diff --git a/mxm_wifiex/wlan_src/Makefile b/mxm_wifiex/wlan_src/Makefile index 3c409c9..645e5d1 100644 --- a/mxm_wifiex/wlan_src/Makefile +++ b/mxm_wifiex/wlan_src/Makefile @@ -111,6 +111,8 @@ CONFIG_USERSPACE_32BIT_OVER_KERNEL_64BIT=n # Select Platform Tools ############################################################################# +MODEXT = ko +ccflags-y += -I$(M)/mlan ccflags-y += -DLINUX KERNELVERSION_X86 := $(shell uname -r) @@ -304,6 +306,7 @@ endif ccflags-y += -Wno-stringop-overflow ccflags-y += -Wno-tautological-compare ccflags-y += -Wno-stringop-truncation +ccflags-y += -Wno-packed-bitfield-compat ############################################################################# # Make Targets @@ -420,11 +423,111 @@ endif endif endif + + + + +MOALOBJS = mlinux/moal_main.o \ + mlinux/moal_ioctl.o \ + mlinux/moal_shim.o \ + mlinux/moal_eth_ioctl.o \ + mlinux/moal_init.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 +ifeq ($(CONFIG_MUSB),y) +MLANOBJS += mlan/mlan_usb.o +endif +ifeq ($(CONFIG_SDIO),y) +MLANOBJS += mlan/mlan_sdio.o +endif +ifeq ($(CONFIG_PCIE),y) +MLANOBJS += mlan/mlan_pcie.o +endif +MLANOBJS += mlan/mlan_11n_aggr.o +MLANOBJS += mlan/mlan_11n_rxreorder.o +MLANOBJS += mlan/mlan_11n.o +MLANOBJS += mlan/mlan_11ac.o +MLANOBJS += mlan/mlan_11ax.o +MLANOBJS += mlan/mlan_11d.o +MLANOBJS += mlan/mlan_11h.o +ifeq ($(CONFIG_STA_SUPPORT),y) +MLANOBJS += mlan/mlan_meas.o +MLANOBJS += mlan/mlan_scan.o \ + mlan/mlan_sta_ioctl.o \ + mlan/mlan_sta_rx.o \ + mlan/mlan_sta_tx.o \ + mlan/mlan_sta_event.o \ + mlan/mlan_sta_cmd.o \ + mlan/mlan_sta_cmdresp.o \ + mlan/mlan_join.o +ifeq ($(CONFIG_STA_WEXT),y) +MOALOBJS += mlinux/moal_priv.o \ + mlinux/moal_wext.o +endif +endif +ifeq ($(CONFIG_UAP_SUPPORT),y) +MLANOBJS += mlan/mlan_uap_ioctl.o +MLANOBJS += mlan/mlan_uap_cmdevent.o +MLANOBJS += mlan/mlan_uap_txrx.o +MOALOBJS += mlinux/moal_uap.o +ifeq ($(CONFIG_UAP_WEXT),y) +MOALOBJS += mlinux/moal_uap_priv.o +MOALOBJS += mlinux/moal_uap_wext.o +endif +endif +ifeq ($(CONFIG_STA_CFG80211),y) +MOALOBJS += mlinux/moal_cfg80211.o +MOALOBJS += mlinux/moal_cfg80211_util.o +MOALOBJS += mlinux/moal_sta_cfg80211.o +endif +ifeq ($(CONFIG_UAP_CFG80211),y) +MOALOBJS += mlinux/moal_cfg80211.o +MOALOBJS += mlinux/moal_cfg80211_util.o +MOALOBJS += mlinux/moal_uap_cfg80211.o +endif + +ifdef CONFIG_PROC_FS +MOALOBJS += mlinux/moal_proc.o +MOALOBJS += mlinux/moal_debug.o +endif + +ifeq ($(CONFIG_MAC80211_SUPPORT),y) +MOALOBJS += mlinux/moal_mac80211.o +MLANOBJS += mlan/mlan_mac80211.o +endif + + + + + + + +obj-m := mlan.o +mlan-objs := $(MLANOBJS) + +ifeq ($(CONFIG_MUSB),y) +MOALOBJS += mlinux/moal_usb.o +endif +ifeq ($(CONFIG_SDIO),y) +MOALOBJS += mlinux/moal_sdio_mmc.o +endif +ifeq ($(CONFIG_PCIE),y) +MOALOBJS += mlinux/moal_pcie.o +endif +obj-m += moal.o +moal-objs := $(MOALOBJS) + # Otherwise we were called directly from the command line; invoke the kernel build system. else default: - $(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS_COMPILE) modules + $(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules endif @@ -448,6 +551,9 @@ build: echo default mkdir $(BINDIR); \ fi + cp -f mlan.$(MODEXT) $(BINDIR)/mlan$(DBG).$(MODEXT) + + cp -f moal.$(MODEXT) $(BINDIR)/moal$(DBG).$(MODEXT) cp -rpf script/load $(BINDIR)/ cp -rpf script/unload $(BINDIR)/ @@ -478,6 +584,8 @@ endif install: default + cp -f mlan.$(MODEXT) $(INSTALLDIR)/mlan$(DBG).$(MODEXT) + cp -f moal.$(MODEXT) $(INSTALLDIR)/moal$(DBG).$(MODEXT) echo $(INSTALLDIR) echo "MX Driver Installed" diff --git a/mxm_wifiex/wlan_src/mlan/mlan.h b/mxm_wifiex/wlan_src/mlan/mlan.h new file mode 100644 index 0000000..d2cd022 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan.h @@ -0,0 +1,37 @@ +/** @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 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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/mxm_wifiex/wlan_src/mlan/mlan_11ac.c b/mxm_wifiex/wlan_src/mlan/mlan_11ac.c new file mode 100644 index 0000000..04aee8d --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11ac.c @@ -0,0 +1,1309 @@ +/** @file mlan_11ac.c + * + * @brief This file contains the functions for station ioctl. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 "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" + +#define NO_NSS_SUPPORT 0x3 + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +t_u16 wlan_convert_mcsmap_to_maxrate(mlan_private *priv, t_u16 bands, + t_u16 mcs_map); +/** + * @brief determine the center frquency center index for bandwidth + * of 80 MHz and 160 MHz + * + * @param pmpriv A pointer to mlan_private structure + * @param band band + * @param pri_chan primary channel + * @param chan_bw channel bandwidth + * + * @return channel center frequency center, if found; O, otherwise + */ + +t_u8 wlan_get_center_freq_idx(mlan_private *pmpriv, t_u16 band, t_u32 pri_chan, + t_u8 chan_bw) +{ + t_u8 center_freq_idx = 0; + + if (band & BAND_AAC) { + switch (pri_chan) { + case 36: + case 40: + case 44: + case 48: + if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 42; + break; + } + /* fall through */ + case 52: + case 56: + case 60: + case 64: + if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 58; + break; + } else if (chan_bw == CHANNEL_BW_160MHZ) { + center_freq_idx = 50; + break; + } + /* fall through */ + case 100: + case 104: + case 108: + case 112: + if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 106; + break; + } + /* fall through */ + case 116: + case 120: + case 124: + case 128: + if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 122; + break; + } else if (chan_bw == CHANNEL_BW_160MHZ) { + center_freq_idx = 114; + break; + } + /* fall through */ + case 132: + case 136: + case 140: + case 144: + if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 138; + break; + } + /* fall through */ + case 149: + case 153: + case 157: + case 161: + if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 155; + break; + } + /* fall through */ + case 165: + case 169: + case 173: + case 177: + if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 171; + break; + } + /* fall through */ + case 184: + case 188: + case 192: + case 196: + if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 190; + break; + } + /* fall through */ + default: /* error. go to the default */ + center_freq_idx = 42; + } + } + return center_freq_idx; +} + +/** + * @brief This function gets the bitmap of nss which supports VHT mcs + * + * @param mcs_map_set VHT mcs map + * + * @return The bitmap of supported nss + */ +static t_u8 wlan_get_nss_vht_mcs(t_u16 mcs_map_set) +{ + t_u8 nss, nss_map = 0; + for (nss = 1; nss <= 8; nss++) { + if (GET_VHTNSSMCS(mcs_map_set, nss) != NO_NSS_SUPPORT) + nss_map |= 1 << (nss - 1); + } + PRINTM(MCMND, "Supported nss bit map:0x%02x\n", nss_map); + return nss_map; +} + +/** + * @brief This function gets the bitmap of nss which supports VHT mcs + * + * @param mcs_map_set VHT mcs map + * + * @return The bitmap of supported nss + */ +static t_u8 wlan_get_nss_num_vht_mcs(t_u16 mcs_map_set) +{ + t_u8 nss, nss_num = 0; + for (nss = 1; nss <= 8; nss++) { + if (GET_VHTNSSMCS(mcs_map_set, nss) != NO_NSS_SUPPORT) + nss_num++; + } + PRINTM(MCMND, "Supported nss:%d\n", nss_num); + return nss_num; +} + +/** + * @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, VHT_capa_t *vht_cap, + t_u8 bands) +{ + t_u32 usr_dot_11ac_dev_cap; + + ENTER(); + + if (bands & BAND_A) + usr_dot_11ac_dev_cap = priv->usr_dot_11ac_dev_cap_a; + else + usr_dot_11ac_dev_cap = priv->usr_dot_11ac_dev_cap_bg; + + vht_cap->vht_cap_info = usr_dot_11ac_dev_cap; + + LEAVE(); +} + +/** + * @brief Set/get 11ac 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_11ac_ioctl_vhtcfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11ac_cfg *cfg = MNULL; + t_u16 cmd_action = 0; + t_u32 usr_vht_cap_info = 0; + t_u32 cfg_value = 0; + t_u32 hw_value = 0; + t_u8 nss = 0; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + t_u16 rx_nss = 0; + t_u16 tx_nss = 0; +#endif + + ENTER(); + +#define VHT_CAP_INFO_BIT_FIELDS \ + (MBIT(4) | MBIT(5) | MBIT(6) | MBIT(7) | MBIT(11) | MBIT(12) | \ + MBIT(19) | MBIT(20) | MBIT(21) | MBIT(22) | MBIT(28) | MBIT(29)) + + cfg = (mlan_ds_11ac_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + /** SET operation */ + /** validate the user input and correct it if necessary */ + if (pmpriv->bss_role == MLAN_BSS_ROLE_STA) { + if (cfg->param.vht_cfg.txrx == 3) { + PRINTM(MERROR, + "Configuration of VHT capabilities for TX/RX 3 is not supported in STA mode!\n"); + return MLAN_STATUS_FAILURE; + } + } + if (pmpriv->bss_role == MLAN_BSS_ROLE_UAP) { + if (cfg->param.vht_cfg.txrx != 3) { + PRINTM(MERROR, + "Configuration of VHT capabilities for TX/RX %d is not supported in UAP mode!\n", + cfg->param.vht_cfg.txrx); + return MLAN_STATUS_FAILURE; + } + } + /** set bit fileds */ + usr_vht_cap_info = VHT_CAP_INFO_BIT_FIELDS & + cfg->param.vht_cfg.vht_cap_info & + pmadapter->hw_dot_11ac_dev_cap; + /** set MAX MPDU LEN field (bit 0 - bit 1) */ + cfg_value = + GET_VHTCAP_MAXMPDULEN(cfg->param.vht_cfg.vht_cap_info); + hw_value = + GET_VHTCAP_MAXMPDULEN(pmadapter->hw_dot_11ac_dev_cap); + SET_VHTCAP_MAXMPDULEN(usr_vht_cap_info, + MIN(cfg_value, hw_value)); + /** set CHAN Width Set field (bit 2 - bit 3) */ + cfg_value = GET_VHTCAP_CHWDSET(cfg->param.vht_cfg.vht_cap_info); + hw_value = GET_VHTCAP_CHWDSET(pmadapter->hw_dot_11ac_dev_cap); + SET_VHTCAP_CHWDSET(usr_vht_cap_info, MIN(cfg_value, hw_value)); + /** set Rx STBC field (bit 8 - bit 10) */ + cfg_value = GET_VHTCAP_RXSTBC(cfg->param.vht_cfg.vht_cap_info); + hw_value = GET_VHTCAP_RXSTBC(pmadapter->hw_dot_11ac_dev_cap); + SET_VHTCAP_RXSTBC(usr_vht_cap_info, MIN(cfg_value, hw_value)); + /** set Steering Number of BFer Ant (bit 13 - bit 15) */ + cfg_value = + GET_VHTCAP_SNBFERANT(cfg->param.vht_cfg.vht_cap_info); + hw_value = GET_VHTCAP_SNBFERANT(pmadapter->hw_dot_11ac_dev_cap); + SET_VHTCAP_SNBFERANT(usr_vht_cap_info, + MIN(cfg_value, hw_value)); + /** set Number of Sounding Dimension (bit 16 - bit 18) */ + cfg_value = + GET_VHTCAP_NUMSNDDM(cfg->param.vht_cfg.vht_cap_info); + hw_value = GET_VHTCAP_NUMSNDDM(pmadapter->hw_dot_11ac_dev_cap); + SET_VHTCAP_NUMSNDDM(usr_vht_cap_info, MIN(cfg_value, hw_value)); + /** set Number of Max AMPDU Length Exponent (bit 23 - bit 25) */ + cfg_value = GET_VHTCAP_MAXAMPDULENEXP( + cfg->param.vht_cfg.vht_cap_info); + hw_value = GET_VHTCAP_MAXAMPDULENEXP( + pmadapter->hw_dot_11ac_dev_cap); + SET_VHTCAP_MAXAMPDULENEXP(usr_vht_cap_info, + MIN(cfg_value, hw_value)); + /** set VHT Link Adaptation Capable (bit 26 - bit 27) */ + cfg_value = + GET_VHTCAP_LINKADPCAP(cfg->param.vht_cfg.vht_cap_info); + hw_value = + GET_VHTCAP_LINKADPCAP(pmadapter->hw_dot_11ac_dev_cap); + SET_VHTCAP_LINKADPCAP(usr_vht_cap_info, + MIN(cfg_value, hw_value)); + /** update the user setting if it is beyond the hw capabiliteis + */ + cfg->param.vht_cfg.vht_cap_info = usr_vht_cap_info; + PRINTM(MINFO, "Set: vht cap info 0x%x\n", usr_vht_cap_info); + + /** update the RX MCS map */ + if (cfg->param.vht_cfg.txrx & MLAN_RADIO_RX) { +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type)) { + if (cfg->param.vht_cfg.band == BAND_SELECT_A) { + rx_nss = GET_RXMCSSUPP( + pmadapter->user_htstream >> 8); + tx_nss = + GET_TXMCSSUPP( + pmadapter->user_htstream >> + 8) & + 0x0f; + } else { + rx_nss = GET_RXMCSSUPP( + pmadapter->user_htstream); + tx_nss = + GET_TXMCSSUPP( + pmadapter->user_htstream) & + 0x0f; + } + } +#endif + /* use the previous user value */ + if (cfg->param.vht_cfg.vht_rx_mcs == 0xffffffff) + cfg->param.vht_cfg.vht_rx_mcs = GET_VHTMCS( + pmpriv->usr_dot_11ac_mcs_support); + for (nss = 1; nss <= 8; nss++) { + cfg_value = GET_VHTNSSMCS( + cfg->param.vht_cfg.vht_rx_mcs, nss); + hw_value = GET_DEVNSSRXMCS( + pmadapter->hw_dot_11ac_mcs_support, + nss); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if ((rx_nss != 0) && (nss > rx_nss)) + cfg_value = NO_NSS_SUPPORT; +#endif + if ((hw_value == NO_NSS_SUPPORT) || + (cfg_value == NO_NSS_SUPPORT)) + SET_VHTNSSMCS( + cfg->param.vht_cfg.vht_rx_mcs, + nss, NO_NSS_SUPPORT); + else + SET_VHTNSSMCS( + cfg->param.vht_cfg.vht_rx_mcs, + nss, MIN(cfg_value, hw_value)); + } + PRINTM(MINFO, "Set: vht rx mcs set 0x%08x\n", + cfg->param.vht_cfg.vht_rx_mcs); + /* use the previous user value */ + if (cfg->param.vht_cfg.vht_tx_mcs == 0xffffffff) + cfg->param.vht_cfg.vht_tx_mcs = GET_VHTMCS( + pmpriv->usr_dot_11ac_mcs_support >> 16); + for (nss = 1; nss <= 8; nss++) { + cfg_value = GET_VHTNSSMCS( + cfg->param.vht_cfg.vht_tx_mcs, nss); + hw_value = GET_DEVNSSTXMCS( + pmadapter->hw_dot_11ac_mcs_support, + nss); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if ((rx_nss != 0) && (nss > rx_nss)) + cfg_value = NO_NSS_SUPPORT; +#endif + if ((hw_value == NO_NSS_SUPPORT) || + (cfg_value == NO_NSS_SUPPORT)) + SET_VHTNSSMCS( + cfg->param.vht_cfg.vht_tx_mcs, + nss, NO_NSS_SUPPORT); + else + SET_VHTNSSMCS( + cfg->param.vht_cfg.vht_tx_mcs, + nss, MIN(cfg_value, hw_value)); + } + PRINTM(MINFO, "Set: vht tx mcs set 0x%08x\n", + cfg->param.vht_cfg.vht_tx_mcs); + if (!cfg->param.vht_cfg.skip_usr_11ac_mcs_cfg) { + RESET_DEVRXMCSMAP( + pmpriv->usr_dot_11ac_mcs_support); + pmpriv->usr_dot_11ac_mcs_support |= GET_VHTMCS( + cfg->param.vht_cfg.vht_rx_mcs); + RESET_DEVTXMCSMAP( + pmpriv->usr_dot_11ac_mcs_support); + pmpriv->usr_dot_11ac_mcs_support |= + (GET_VHTMCS( + cfg->param.vht_cfg.vht_tx_mcs) + << 16); + PRINTM(MINFO, "Set: vht mcs set 0x%08x\n", + pmpriv->usr_dot_11ac_mcs_support); + } else { + PRINTM(MINFO, + "Skipped user 11ac mcs configuration\n"); + cfg->param.vht_cfg.skip_usr_11ac_mcs_cfg = + MFALSE; + } + } + } + + if (pmpriv->bss_role == MLAN_BSS_ROLE_STA) { + if (cfg->param.vht_cfg.txrx & MLAN_RADIO_RX) { + /* maximum VHT configuration used in association */ + if (pioctl_req->action == MLAN_ACT_SET) { + if (cfg->param.vht_cfg.band == BAND_SELECT_BG) + pmpriv->usr_dot_11ac_dev_cap_bg = + usr_vht_cap_info; + else if (cfg->param.vht_cfg.band == + BAND_SELECT_A) + pmpriv->usr_dot_11ac_dev_cap_a = + usr_vht_cap_info; + else { + pmpriv->usr_dot_11ac_dev_cap_bg = + usr_vht_cap_info; + pmpriv->usr_dot_11ac_dev_cap_a = + usr_vht_cap_info; + } + pmpriv->usr_dot_11ac_bw = + cfg->param.vht_cfg.bwcfg; + + } else { + /** GET operation */ + if (cfg->param.vht_cfg.band == BAND_SELECT_BG) { + cfg->param.vht_cfg.vht_cap_info = + pmpriv->usr_dot_11ac_dev_cap_bg; + PRINTM(MINFO, + "Get: vht cap info for 2.4GHz 0x%x\n", + pmpriv->usr_dot_11ac_dev_cap_bg); + } else if (cfg->param.vht_cfg.band == + BAND_SELECT_A) { + cfg->param.vht_cfg.vht_cap_info = + pmpriv->usr_dot_11ac_dev_cap_a; + PRINTM(MINFO, + "Get: vht cap info for 5GHz 0x%x\n", + pmpriv->usr_dot_11ac_dev_cap_a); + } else { + PRINTM(MINFO, + "Get: invalid band selection for vht cap info\n"); + ret = MLAN_STATUS_FAILURE; + } + cfg->param.vht_cfg.bwcfg = + pmpriv->usr_dot_11ac_bw; + cfg->param.vht_cfg.vht_rx_mcs = GET_DEVRXMCSMAP( + pmpriv->usr_dot_11ac_mcs_support); + cfg->param.vht_cfg.vht_tx_mcs = GET_DEVTXMCSMAP( + pmpriv->usr_dot_11ac_mcs_support); + cfg->param.vht_cfg.vht_rx_max_rate = + wlan_convert_mcsmap_to_maxrate( + pmpriv, cfg->param.vht_cfg.band, + cfg->param.vht_cfg.vht_rx_mcs); + cfg->param.vht_cfg.vht_tx_max_rate = + wlan_convert_mcsmap_to_maxrate( + pmpriv, cfg->param.vht_cfg.band, + cfg->param.vht_cfg.vht_tx_mcs); + } + LEAVE(); + return ret; + } + } + + 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_11AC_CFG, cmd_action, 0, + (t_void *)pioctl_req, + (t_void *)&cfg->param.vht_cfg); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set Operating Mode Notification cfg + * + * @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_11ac_ioctl_opermodecfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11ac_cfg *cfg = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u8 hw_bw_160or8080 = 0; + t_u8 hw_rx_nss = 0; + + ENTER(); + + cfg = (mlan_ds_11ac_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + cfg->param.opermode_cfg.bw = pmpriv->usr_dot_11ac_opermode_bw; + cfg->param.opermode_cfg.nss = pmpriv->usr_dot_11ac_opermode_nss; + } else if (pioctl_req->action == MLAN_ACT_SET) { + hw_bw_160or8080 = + GET_VHTCAP_CHWDSET(pmadapter->hw_dot_11ac_dev_cap); + hw_rx_nss = wlan_get_nss_num_vht_mcs( + GET_DEVRXMCSMAP(pmadapter->hw_dot_11ac_mcs_support)); + if ((((cfg->param.opermode_cfg.bw - 1) > BW_80MHZ) && + !hw_bw_160or8080) || + (cfg->param.opermode_cfg.nss > hw_rx_nss)) { + PRINTM(MERROR, + "bw or nss NOT supported. HW support bw_160or8080=%d rx_nss=%d.\n", + hw_bw_160or8080, hw_rx_nss); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmpriv->usr_dot_11ac_opermode_bw = cfg->param.opermode_cfg.bw; + pmpriv->usr_dot_11ac_opermode_nss = cfg->param.opermode_cfg.nss; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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_11ac_ioctl_supported_mcs_set(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + /*mlan_ds_11ac_cfg *cfg= MNULL;*/ + /*int rx_mcs_supp;*/ + /*t_u8 mcs_set[NUM_MCS_SUPP];*/ + + ENTER(); +#if 0 + 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_11ACRXMCSSUPP(pmadapter->usr_dot_11ac_mcs_support); + /* Set MCS */ + 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_CHANWIDTH80(pmadapter->usr_dot_11ac_dev_cap_bg) + || ISSUPP_CHANWIDTH80(pmadapter->usr_dot_11ac_dev_cap_a) + ) + SETHT_MCS32(mcs_set); + + cfg = (mlan_ds_11ac_cfg *)pioctl_req->pbuf; + memcpy_ext(pmadapter, cfg->param.supported_mcs_set, mcs_set, + NUM_MCS_SUPP, sizeof(cfg->param.supported_mcs_set)); + +#endif + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function prints the 802.11ac device capability + * + * @param pmadapter A pointer to mlan_adapter structure + * @param cap Capability value + * + * @return N/A + */ +void wlan_show_dot11acdevcap(pmlan_adapter pmadapter, t_u32 cap) +{ + ENTER(); + + switch (GET_VHTCAP_MAXMPDULEN(cap)) { + case 0x0: + PRINTM(MINFO, + "GET_HW_SPEC: Maximum MSDU length = 3895 octets\n"); + break; + case 0x1: + PRINTM(MINFO, + "GET_HW_SPEC: Maximum MSDU length = 7991 octets\n"); + break; + case 0x2: + PRINTM(MINFO, + "GET_HW_SPEC: Maximum MSDU length = 11454 octets\n"); + break; + default: + PRINTM(MINFO, "Unsupport value\n"); + break; + } + + PRINTM(MINFO, "GET_HW_SPEC: HTC-VHT %s\n", + (ISSUPP_11ACVHTHTCVHT(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: VHT TXOP PS %s\n", + (ISSUPP_11ACVHTTXOPPS(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: MU RX beamformee %s\n", + (ISSUPP_11ACMURXBEAMFORMEE(cap) ? "supported" : + "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: MU TX beamformee %s\n", + (ISSUPP_11ACMUTXBEAMFORMEE(cap) ? "supported" : + "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: SU RX Beamformee %s\n", + (ISSUPP_11ACSUBEAMFORMEE(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: SU TX Beamformer %s\n", + (ISSUPP_11ACSUBEAMFORMER(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Rx STBC %s\n", + (ISSUPP_11ACRXSTBC(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Tx STBC %s\n", + (ISSUPP_11ACTXSTBC(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Short GI %s for 160MHz BW\n", + (ISSUPP_11ACSGI160(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Short GI %s for 80MHz BW\n", + (ISSUPP_11ACSGI80(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: LDPC coding %s\n", + (ISSUPP_11ACLDPC(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Channel BW 20/40/80/160/80+80 MHz %s\n", + (ISSUPP_11ACBW8080(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Channel BW 20/40/80/160 MHz %s\n", + (ISSUPP_11ACBW160(cap) ? "supported" : "not supported")); + + LEAVE(); + return; +} + +/** + * @brief This function prints the 802.11ac device MCS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param support Support value + * + * @return N/A + */ +void wlan_show_dot11acmcssupport(pmlan_adapter pmadapter, t_u32 support) +{ + ENTER(); + + PRINTM(MINFO, "GET_HW_SPEC: MCSs for %2dx%2d MIMO\n", + GET_DEVRXMCSMAP(support), GET_DEVTXMCSMAP(support)); + + LEAVE(); + return; +} + +/** + * @brief This function converts the 2-bit MCS map to the highest long GI + * VHT PPDU data rate + * + * @param priv A pointer to mlan_private structure + * @param bands Supported bands + * @param mcs_map 2-bit MCS map + * + * @return the max data rate for long GI + */ +t_u16 wlan_convert_mcsmap_to_maxrate(mlan_private *priv, t_u16 bands, + t_u16 mcs_map) +{ + t_u8 i; + t_u8 nss; + t_u8 max_mcs; + t_u16 max_rate = 0; + t_u32 usr_vht_cap_info = 0; + t_u32 usr_dot_11n_dev_cap; + + /* tables of the MCS map to the highest data rate (in Mbps) + * supported for long GI */ + t_u16 max_rate_lgi_20MHZ[8][3] = { + {0x41, 0x4E, 0x0}, /* NSS = 1 */ + {0x82, 0x9C, 0x0}, /* NSS = 2 */ + {0xC3, 0xEA, 0x104}, /* NSS = 3 */ + {0x104, 0x138, 0x0}, /* NSS = 4 */ + {0x145, 0x186, 0x0}, /* NSS = 5 */ + {0x186, 0x1D4, 0x208}, /* NSS = 6 */ + {0x1C7, 0x222, 0x0}, /* NSS = 7 */ + {0x208, 0x270, 0x0} /* NSS = 8 */ + }; + + t_u16 max_rate_lgi_40MHZ[8][3] = { + {0x87, 0xA2, 0xB4}, /* NSS = 1 */ + {0x10E, 0x144, 0x168}, /* NSS = 2 */ + {0x195, 0x1E6, 0x21C}, /* NSS = 3 */ + {0x21C, 0x288, 0x2D0}, /* NSS = 4 */ + {0x2A3, 0x32A, 0x384}, /* NSS = 5 */ + {0x32A, 0x3CC, 0x438}, /* NSS = 6 */ + {0x3B1, 0x46E, 0x4EC}, /* NSS = 7 */ + {0x438, 0x510, 0x5A0} /* NSS = 8 */ + }; + + t_u16 max_rate_lgi_80MHZ[8][3] = { + {0x124, 0x15F, 0x186}, /* NSS = 1 */ + {0x249, 0x2BE, 0x30C}, /* NSS = 2 */ + {0x36D, 0x41D, 0x492}, /* NSS = 3 */ + {0x492, 0x57C, 0x618}, /* NSS = 4 */ + {0x5B6, 0x6DB, 0x79E}, /* NSS = 5 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 6 */ + {0x7FF, 0x999, 0xAAA}, /* NSS = 7 */ + {0x924, 0xAF8, 0xC30} /* NSS = 8 */ + }; + t_u16 max_rate_lgi_160MHZ[8][3] = { + {0x249, 0x2BE, 0x30C}, /* NSS = 1 */ + {0x492, 0x57C, 0x618}, /* NSS = 2 */ + {0x6DB, 0x83A, 0x0}, /* NSS = 3 */ + {0x924, 0xAF8, 0xC30}, /* NSS = 4 */ + {0xB6D, 0xDB6, 0xF3C}, /* NSS = 5 */ + {0xDB6, 0x1074, 0x1248}, /* NSS = 6 */ + {0xFFF, 0x1332, 0x1554}, /* NSS = 7 */ + {0x1248, 0x15F0, 0x1860} /* NSS = 8 */ + }; + + if (bands & BAND_AAC) { + usr_vht_cap_info = priv->usr_dot_11ac_dev_cap_a; + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; + } else { + usr_vht_cap_info = priv->usr_dot_11ac_dev_cap_bg; + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; + } + + /* find the max NSS supported */ + nss = 0; + for (i = 0; i < 8; i++) { + max_mcs = (mcs_map >> (2 * i)) & 0x3; + if (max_mcs < 3) + nss = i; + } + + max_mcs = (mcs_map >> (2 * nss)) & 0x3; + /* if max_mcs is 3, nss must be 0 (SS = 1). Thus, max mcs is MCS 9*/ + if (max_mcs >= 3) + max_mcs = 2; + + if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) { + /* support 160 MHz */ + max_rate = max_rate_lgi_160MHZ[nss][max_mcs]; + if (max_mcs >= 1 && max_rate == 0) + /* MCS9 is not supported in NSS6 */ + max_rate = max_rate_lgi_160MHZ[nss][max_mcs - 1]; + + } else { + if (priv->usr_dot_11ac_bw == BW_FOLLOW_VHTCAP) { + max_rate = max_rate_lgi_80MHZ[nss][max_mcs]; + if (max_mcs >= 1 && max_rate == 0) + /* MCS9 is not supported in NSS3 */ + max_rate = max_rate_lgi_80MHZ[nss][max_mcs - 1]; + } else { + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) { + max_rate = max_rate_lgi_40MHZ[nss][max_mcs]; + } else { + max_rate = max_rate_lgi_20MHZ[nss][max_mcs]; + /* MCS9 is not supported in NSS1/2/4/5/7/8 */ + if (max_mcs >= 1 && max_rate == 0) + max_rate = + max_rate_lgi_20MHZ[nss] + [max_mcs - 1]; + } + } + } + PRINTM(MCMND, "max_rate=%dM\n", max_rate); + return max_rate; +} + +/** + * @brief This function fills the VHT cap tlv out put format is LE, not CPU + * + * @param priv A pointer to mlan_private structure + * @param pvht_cap A pointer to MrvlIETypes_HTCap_t structure + * @param bands Band configuration + * @param flag TREU--pvht_cap has the setting for resp + * MFALSE -- pvht_cap is clean + * @param bw_80p80 TRUE -- enable 80p80 + * @return N/A + */ +void wlan_fill_vht_cap_tlv(mlan_private *priv, MrvlIETypes_VHTCap_t *pvht_cap, + t_u16 bands, t_u8 flag, t_u8 bw_80p80) +{ + t_u16 mcs_map_user = 0; + t_u16 mcs_map_resp = 0; + t_u16 mcs_map_result = 0; + t_u16 mcs_user = 0; + t_u16 mcs_resp = 0; + t_u16 nss; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + t_u16 rx_nss = 0, tx_nss = 0; +#endif + ENTER(); + + /* Fill VHT cap info */ + wlan_fill_cap_info(priv, &pvht_cap->vht_cap, bands); + /* clear 80p80 in vht_cap_info */ + if (!bw_80p80) + pvht_cap->vht_cap.vht_cap_info &= ~(MBIT(2) | MBIT(3)); + pvht_cap->vht_cap.vht_cap_info = + wlan_cpu_to_le32(pvht_cap->vht_cap.vht_cap_info); + + /* Fill VHT MCS Set */ + /* rx MCS Set, find the minimum of the user rx mcs and ap rx mcs*/ + mcs_map_resp = mcs_map_user = + GET_DEVRXMCSMAP(priv->usr_dot_11ac_mcs_support); + if (flag) + mcs_map_resp = + wlan_le16_to_cpu(pvht_cap->vht_cap.mcs_sets.rx_mcs_map); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (IS_CARD9098(priv->adapter->card_type) || + IS_CARD9097(priv->adapter->card_type)) { + if (bands & BAND_A) { + rx_nss = GET_RXMCSSUPP(priv->adapter->user_htstream >> + 8); + tx_nss = GET_TXMCSSUPP(priv->adapter->user_htstream >> + 8) & + 0x0f; + } else { + rx_nss = GET_RXMCSSUPP(priv->adapter->user_htstream); + tx_nss = GET_TXMCSSUPP(priv->adapter->user_htstream) & + 0x0f; + } + /** force 1x1 when enable 80P80 */ + if (bw_80p80) + rx_nss = tx_nss = 1; + } +#endif + mcs_map_result = 0; + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if ((rx_nss != 0) && (nss > rx_nss)) + mcs_user = NO_NSS_SUPPORT; +#endif + if ((mcs_user == NO_NSS_SUPPORT) || + (mcs_resp == NO_NSS_SUPPORT)) + SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT); + else + SET_VHTNSSMCS(mcs_map_result, nss, + MIN(mcs_user, mcs_resp)); + } + /* rx MCS map */ + pvht_cap->vht_cap.mcs_sets.rx_mcs_map = + wlan_cpu_to_le16(mcs_map_result); + + /* rx highest rate */ + pvht_cap->vht_cap.mcs_sets.rx_max_rate = + wlan_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + pvht_cap->vht_cap.mcs_sets.rx_max_rate = + wlan_cpu_to_le16(pvht_cap->vht_cap.mcs_sets.rx_max_rate); + + /* tx MCS Set find the minimum of the user tx mcs and ap tx mcs */ + mcs_map_resp = mcs_map_user = + GET_DEVTXMCSMAP(priv->usr_dot_11ac_mcs_support); + if (flag) + mcs_map_resp = + wlan_le16_to_cpu(pvht_cap->vht_cap.mcs_sets.tx_mcs_map); + mcs_map_result = 0; + for (nss = 1; nss <= 8; nss++) { + mcs_user = GET_VHTNSSMCS(mcs_map_user, nss); + mcs_resp = GET_VHTNSSMCS(mcs_map_resp, nss); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if ((tx_nss != 0) && (nss > tx_nss)) + mcs_user = NO_NSS_SUPPORT; +#endif + if ((mcs_user == NO_NSS_SUPPORT) || + (mcs_resp == NO_NSS_SUPPORT)) + SET_VHTNSSMCS(mcs_map_result, nss, NO_NSS_SUPPORT); + else + SET_VHTNSSMCS(mcs_map_result, nss, + MIN(mcs_user, mcs_resp)); + } + + /* tx MCS map */ + pvht_cap->vht_cap.mcs_sets.tx_mcs_map = + wlan_cpu_to_le16(mcs_map_result); + /* tx highest rate */ + pvht_cap->vht_cap.mcs_sets.tx_max_rate = + wlan_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + pvht_cap->vht_cap.mcs_sets.tx_max_rate = + wlan_cpu_to_le16(pvht_cap->vht_cap.mcs_sets.tx_max_rate); + + LEAVE(); + return; +} + +/** + * @brief This function fills the VHT cap tlv out put format is CPU + * + * @param priv A pointer to mlan_private structure + * @param pvht_cap A pointer to MrvlIETypes_HTCap_t structure + * @param bands Band configuration + * + * @return N/A + */ +void wlan_fill_vht_cap_ie(mlan_private *priv, IEEEtypes_VHTCap_t *pvht_cap, + t_u16 bands) +{ + ENTER(); + + pvht_cap->ieee_hdr.element_id = VHT_CAPABILITY; + pvht_cap->ieee_hdr.len = sizeof(VHT_capa_t); + + /* Fill VHT cap info */ + wlan_fill_cap_info(priv, &pvht_cap->vht_cap, bands); + + /* rx MCS map */ + pvht_cap->vht_cap.mcs_sets.rx_mcs_map = + GET_DEVRXMCSMAP(priv->usr_dot_11ac_mcs_support); + + /* rx highest rate */ + pvht_cap->vht_cap.mcs_sets.rx_max_rate = wlan_convert_mcsmap_to_maxrate( + priv, bands, pvht_cap->vht_cap.mcs_sets.rx_mcs_map); + + /* tx MCS map */ + pvht_cap->vht_cap.mcs_sets.tx_mcs_map = + GET_DEVTXMCSMAP(priv->usr_dot_11ac_mcs_support); + /* tx highest rate */ + pvht_cap->vht_cap.mcs_sets.tx_max_rate = wlan_convert_mcsmap_to_maxrate( + priv, bands, pvht_cap->vht_cap.mcs_sets.tx_mcs_map); + + LEAVE(); + return; +} + +/** + * @brief This function would check whether support 80+80Mhz + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSSDescriptor_t structure + * + * @return ret suport 80+80Mhz or not + */ +t_u8 wlan_is_80_80_support(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc) +{ + t_u8 ret = MFALSE; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + t_u16 rx_nss = 0, tx_nss = 0; + IEEEtypes_VHTCap_t *pvht_cap = pbss_desc->pvht_cap; + MrvlIEtypes_He_cap_t *phecap = MNULL; + IEEEtypes_HECap_t *pBsshecap = MNULL; +#endif + + ENTER(); + +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (!IS_CARD9098(pmpriv->adapter->card_type) && + !IS_CARD9097(pmpriv->adapter->card_type)) + return ret; + /** check band A */ + if (!(pbss_desc->bss_band & BAND_A)) + return ret; + + /** check band A antenna setting */ + rx_nss = GET_RXMCSSUPP(pmpriv->adapter->user_htstream >> 8); + tx_nss = GET_TXMCSSUPP(pmpriv->adapter->user_htstream >> 8) & 0x0f; + /** check if support 2*2 */ + if (rx_nss != 2 || tx_nss != 2) + return ret; + /** check if AP support AC 80P80 */ + if (ISSUPP_11ACBW8080(pmpriv->usr_dot_11ac_dev_cap_a) && pvht_cap && + ISSUPP_11ACBW8080(pvht_cap->vht_cap.vht_cap_info)) + ret = MTRUE; + /** check if AP support AX 80P80 */ + if (pbss_desc->phe_cap) { + pBsshecap = (IEEEtypes_HECap_t *)pbss_desc->phe_cap; + phecap = (MrvlIEtypes_He_cap_t *)pmpriv->user_he_cap; + if (ret && (phecap->he_phy_cap[0] & MBIT(4)) && + (pBsshecap->he_phy_cap[0] & MBIT(4))) + ret = MTRUE; + else + ret = MFALSE; + } +#endif + LEAVE(); + return ret; +} + +/** + * @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_11ac_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, + t_u8 **ppbuffer) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + MrvlIETypes_VHTCap_t *pvht_cap; + MrvlIETypes_OperModeNtf_t *pmrvl_oper_mode; + t_u16 mcs_map_user = 0; + t_u16 nss; + int ret_len = 0; + t_u8 bw_80p80 = MFALSE; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + t_u16 rx_nss = 0; +#endif + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + + /* VHT Capabilities IE */ + if (pbss_desc->pvht_cap && + wlan_get_nss_vht_mcs( + pbss_desc->pvht_cap->vht_cap.mcs_sets.rx_mcs_map)) { + pvht_cap = (MrvlIETypes_VHTCap_t *)*ppbuffer; + memset(pmadapter, pvht_cap, 0, sizeof(MrvlIETypes_VHTCap_t)); + pvht_cap->header.type = wlan_cpu_to_le16(VHT_CAPABILITY); + pvht_cap->header.len = sizeof(VHT_capa_t); + memcpy_ext(pmadapter, + (t_u8 *)pvht_cap + sizeof(MrvlIEtypesHeader_t), + (t_u8 *)pbss_desc->pvht_cap + + sizeof(IEEEtypes_Header_t), + pvht_cap->header.len, sizeof(VHT_capa_t)); + bw_80p80 = wlan_is_80_80_support(pmpriv, pbss_desc); + wlan_fill_vht_cap_tlv(pmpriv, pvht_cap, pbss_desc->bss_band, + MTRUE, bw_80p80); + + HEXDUMP("VHT_CAPABILITIES IE", (t_u8 *)pvht_cap, + sizeof(MrvlIETypes_VHTCap_t)); + *ppbuffer += sizeof(MrvlIETypes_VHTCap_t); + ret_len += sizeof(MrvlIETypes_VHTCap_t); + pvht_cap->header.len = wlan_cpu_to_le16(pvht_cap->header.len); + } else { + LEAVE(); + return 0; + } + + /* Operating Mode Notification IE */ + pmrvl_oper_mode = (MrvlIETypes_OperModeNtf_t *)*ppbuffer; + memset(pmadapter, pmrvl_oper_mode, 0, + sizeof(MrvlIETypes_OperModeNtf_t)); + pmrvl_oper_mode->header.type = wlan_cpu_to_le16(OPER_MODE_NTF); + pmrvl_oper_mode->header.len = sizeof(t_u8); + + if (pmpriv->usr_dot_11ac_opermode_bw || + pmpriv->usr_dot_11ac_opermode_nss) { + pmrvl_oper_mode->oper_mode |= + (pmpriv->usr_dot_11ac_opermode_nss - 1) << 4; + pmrvl_oper_mode->oper_mode |= + pmpriv->usr_dot_11ac_opermode_bw - 1; + if (pbss_desc->bss_band & BAND_G) { + if (!(IS_OPER_MODE_20M(pmrvl_oper_mode->oper_mode))) { + if (pbss_desc->pht_cap->ht_cap.ht_cap_info & + MBIT(1)) + SET_OPER_MODE_40M( + pmrvl_oper_mode->oper_mode); + else + SET_OPER_MODE_20M( + pmrvl_oper_mode->oper_mode); + } + } + } else { + /** set default bandwidth:80M*/ + SET_OPER_MODE_80M(pmrvl_oper_mode->oper_mode); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type)) { + if (pbss_desc->bss_band & BAND_A) + rx_nss = GET_RXMCSSUPP( + pmadapter->user_htstream >> 8); + else + rx_nss = + GET_RXMCSSUPP(pmadapter->user_htstream); + } +#endif + mcs_map_user = + GET_DEVRXMCSMAP(pmpriv->usr_dot_11ac_mcs_support); + nss = wlan_get_nss_num_vht_mcs(mcs_map_user); + +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type)) { + PRINTM(MCMND, "rx_nss=%d nss=%d\n", rx_nss, nss); + nss = MIN(rx_nss, nss); + } +#endif + + pmrvl_oper_mode->oper_mode |= (nss - 1) << 4; + + switch (pbss_desc->curr_bandwidth) { + case BW_20MHZ: + SET_OPER_MODE_20M(pmrvl_oper_mode->oper_mode); + break; + case BW_40MHZ: + SET_OPER_MODE_40M(pmrvl_oper_mode->oper_mode); + break; + case BW_80MHZ: + default: + break; + } + } + HEXDUMP("OPER MODE NTF IE", (t_u8 *)pmrvl_oper_mode, + sizeof(MrvlIETypes_OperModeNtf_t)); + *ppbuffer += sizeof(MrvlIETypes_OperModeNtf_t); + ret_len += sizeof(MrvlIETypes_OperModeNtf_t); + pmrvl_oper_mode->header.len = + wlan_cpu_to_le16(pmrvl_oper_mode->header.len); + + LEAVE(); + return ret_len; +} + +/** + * @brief 11ac 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_11ac_cfg_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_11ac_cfg *cfg = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11ac_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_11ac_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + cfg = (mlan_ds_11ac_cfg *)pioctl_req->pbuf; + switch (cfg->sub_command) { + case MLAN_OID_11AC_VHT_CFG: + status = wlan_11ac_ioctl_vhtcfg(pmadapter, pioctl_req); + break; + case MLAN_OID_11AC_CFG_SUPPORTED_MCS_SET: + status = wlan_11ac_ioctl_supported_mcs_set(pmadapter, + pioctl_req); + break; + case MLAN_OID_11AC_OPERMODE_CFG: + status = wlan_11ac_ioctl_opermodecfg(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief This function prepares 11ac 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_11ac_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_11AC_CFG *vhtcfg = &cmd->params.vhtcfg; + mlan_ds_11ac_vht_cfg *vht_cfg = (mlan_ds_11ac_vht_cfg *)pdata_buf; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11AC_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_11AC_CFG) + S_DS_GEN); + vhtcfg->action = wlan_cpu_to_le16(cmd_action); + vhtcfg->band_config = vht_cfg->band & 0xFF; + // block user enable 80MHZ + if (IS_FW_SUPPORT_NO_80MHZ(pmadapter)) + vht_cfg->bwcfg = 0; + + vhtcfg->misc_config = vht_cfg->txrx & 0x3; + if (vhtcfg->misc_config != 2) + vhtcfg->misc_config |= (vht_cfg->bwcfg << 2); + + vhtcfg->vht_cap_info = wlan_cpu_to_le32(vht_cfg->vht_cap_info); + vht_cfg->vht_rx_mcs = wlan_cpu_to_le32(vht_cfg->vht_rx_mcs); + memcpy_ext(pmadapter, &vhtcfg->vht_supp_mcs_set[0], + &vht_cfg->vht_rx_mcs, sizeof(t_u32), sizeof(t_u32)); + vht_cfg->vht_tx_mcs = wlan_cpu_to_le32(vht_cfg->vht_tx_mcs); + memcpy_ext(pmadapter, &vhtcfg->vht_supp_mcs_set[4], + &vht_cfg->vht_tx_mcs, sizeof(t_u32), sizeof(t_u32)); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of 11accfg + * + * @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_11ac_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + mlan_ds_11ac_cfg *cfg = MNULL; + HostCmd_DS_11AC_CFG *vhtcfg = &resp->params.vhtcfg; + + ENTER(); + if (pioctl_buf && + (wlan_le16_to_cpu(vhtcfg->action) == HostCmd_ACT_GEN_GET)) { + cfg = (mlan_ds_11ac_cfg *)pioctl_buf->pbuf; + cfg->param.vht_cfg.band = vhtcfg->band_config; + cfg->param.vht_cfg.txrx = vhtcfg->misc_config & 0x03; + if (cfg->param.vht_cfg.txrx & 0x1) + cfg->param.vht_cfg.bwcfg = + (vhtcfg->misc_config & 0x04) >> 2; + else + cfg->param.vht_cfg.bwcfg = 0; + + cfg->param.vht_cfg.vht_cap_info = + wlan_le32_to_cpu(vhtcfg->vht_cap_info); + memcpy_ext(pmadapter, &cfg->param.vht_cfg.vht_rx_mcs, + &vhtcfg->vht_supp_mcs_set[0], sizeof(t_u32), + sizeof(t_u32)); + cfg->param.vht_cfg.vht_rx_mcs = + wlan_le32_to_cpu(cfg->param.vht_cfg.vht_rx_mcs); + memcpy_ext(pmadapter, &cfg->param.vht_cfg.vht_tx_mcs, + &vhtcfg->vht_supp_mcs_set[4], sizeof(t_u32), + sizeof(t_u32)); + cfg->param.vht_cfg.vht_tx_mcs = + wlan_le32_to_cpu(cfg->param.vht_cfg.vht_tx_mcs); + cfg->param.vht_cfg.vht_rx_max_rate = + wlan_convert_mcsmap_to_maxrate( + pmpriv, cfg->param.vht_cfg.band, + cfg->param.vht_cfg.vht_rx_mcs); + cfg->param.vht_cfg.vht_tx_max_rate = + wlan_convert_mcsmap_to_maxrate( + pmpriv, cfg->param.vht_cfg.band, + cfg->param.vht_cfg.vht_tx_mcs); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +void wlan_update_11ac_cap(mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + + pmpriv->usr_dot_11ac_mcs_support = pmadapter->hw_dot_11ac_mcs_support; + pmpriv->usr_dot_11ac_dev_cap_bg = + pmadapter->hw_dot_11ac_dev_cap & + ~DEFALUT_11AC_CAP_BEAMFORMING_RESET_MASK; + pmpriv->usr_dot_11ac_dev_cap_a = + pmadapter->hw_dot_11ac_dev_cap & + ~DEFALUT_11AC_CAP_BEAMFORMING_RESET_MASK; + pmpriv->usr_dot_11ac_bw = BW_FOLLOW_VHTCAP; +} + +/** + * @brief This function check if 11AC is allowed in bandcfg + * + * @param pmpriv A pointer to mlan_private structure + * @param bss_band bss band + * + * @return 0--not allowed, other value allowed + */ +t_u8 wlan_11ac_bandconfig_allowed(mlan_private *pmpriv, t_u16 bss_band) +{ + if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { + if (bss_band & BAND_G) + return (pmpriv->adapter->adhoc_start_band & BAND_GAC); + else if (bss_band & BAND_A) + return (pmpriv->adapter->adhoc_start_band & BAND_AAC); + } else { + if (bss_band & BAND_G) + return (pmpriv->config_bands & BAND_GAC); + else if (bss_band & BAND_A) + return (pmpriv->config_bands & BAND_AAC); + } + return 0; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11ac.h b/mxm_wifiex/wlan_src/mlan/mlan_11ac.h new file mode 100644 index 0000000..8166106 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11ac.h @@ -0,0 +1,52 @@ +/** @file mlan_11ac.h + * + * @brief This file contains the functions for station ioctl. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 _MLAN_11AC_H_ +#define _MLAN_11AC_H_ + +#include "mlan_11n_aggr.h" +#include "mlan_11n_rxreorder.h" +#include "mlan_wmm.h" + +void wlan_show_dot11acdevcap(pmlan_adapter pmadapter, t_u32 cap); +void wlan_show_dot11acmcssupport(pmlan_adapter pmadapter, t_u32 support); +t_u16 wlan_convert_mcsmap_to_maxrate(mlan_private *priv, t_u16 bands, + t_u16 mcs_map); +void wlan_fill_vht_cap_tlv(mlan_private *priv, MrvlIETypes_VHTCap_t *pvht_cap, + t_u16 bands, t_u8 flag, t_u8 bw_80p80); +void wlan_fill_vht_cap_ie(mlan_private *priv, IEEEtypes_VHTCap_t *pvht_cap, + t_u16 bands); +int wlan_cmd_append_11ac_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, + t_u8 **ppbuffer); +mlan_status wlan_11ac_cfg_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +void wlan_update_11ac_cap(mlan_private *pmpriv); +t_u8 wlan_11ac_bandconfig_allowed(mlan_private *pmpriv, t_u8 bss_band); +t_u8 wlan_is_80_80_support(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc); + +mlan_status wlan_cmd_11ac_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); + +mlan_status wlan_ret_11ac_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +#endif /* _MLAN_11AC_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11ax.c b/mxm_wifiex/wlan_src/mlan/mlan_11ax.c new file mode 100644 index 0000000..628e430 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11ax.c @@ -0,0 +1,904 @@ +/** @file mlan_11ax.c + * + * @brief This file contains the functions for 11ax related features. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_ioctl.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11ax.h" +#include "mlan_11ac.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function prints the 802.11ax HE mac capability + * + * @param pmadapter A pointer to mlan_adapter structure + * @param cap Capability value + * + * @return N/A + */ +void wlan_show_dot11axmaccap(pmlan_adapter pmadapter, t_u32 cap) +{ + ENTER(); + + LEAVE(); + return; +} + +/** + * @brief This function check if AP support TWT Response. + * + * @param pbss_desc A pointer to BSSDescriptor_t structure + * + * @return MTRUE/MFALSE + */ +t_u8 wlan_check_ap_11ax_twt_supported(BSSDescriptor_t *pbss_desc) +{ + if (!pbss_desc->phe_cap) + return MFALSE; + if (!(pbss_desc->phe_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)) + return MFALSE; + if (!pbss_desc->pext_cap) + return MFALSE; + if (!ISSUPP_EXTCAP_EXT_TWT_RESP(pbss_desc->pext_cap->ext_cap)) + return MFALSE; + return MTRUE; +} + +/** + * @brief This function check if we should enable TWT support + * + * @param pbss_desc A pointer to BSSDescriptor_t structure + * + * @return MTRUE/MFALSE + */ +t_u8 wlan_check_11ax_twt_supported(mlan_private *pmpriv, + BSSDescriptor_t *pbss_desc) +{ + MrvlIEtypes_He_cap_t *phecap = + (MrvlIEtypes_He_cap_t *)&pmpriv->user_he_cap; + MrvlIEtypes_He_cap_t *hw_he_cap = + (MrvlIEtypes_He_cap_t *)&pmpriv->adapter->hw_he_cap; + if (pbss_desc && !wlan_check_ap_11ax_twt_supported(pbss_desc)) { + PRINTM(MINFO, "AP don't support twt feature\n"); + return MFALSE; + } + if (pbss_desc) { + if (pbss_desc->bss_band & BAND_A) { + hw_he_cap = (MrvlIEtypes_He_cap_t *)&pmpriv->adapter + ->hw_he_cap; + phecap = (MrvlIEtypes_He_cap_t *)&pmpriv->user_he_cap; + } else { + hw_he_cap = (MrvlIEtypes_He_cap_t *)&pmpriv->adapter + ->hw_2g_he_cap; + phecap = + (MrvlIEtypes_He_cap_t *)&pmpriv->user_2g_he_cap; + } + } + if (!(hw_he_cap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT)) { + PRINTM(MINFO, "FW don't support TWT\n"); + return MFALSE; + } + if (phecap->he_mac_cap[0] & HE_MAC_CAP_TWT_REQ_SUPPORT) + return MTRUE; + PRINTM(MINFO, "USER HE_MAC_CAP don't support TWT\n"); + return MFALSE; +} + +/** + * @brief This function prints the 802.11ax HE PHY cap + * + * @param pmadapter A pointer to mlan_adapter structure + * @param support Support value + * + * @return N/A + */ +void wlan_show_dot11axphycap(pmlan_adapter pmadapter, t_u32 support) +{ + ENTER(); + + LEAVE(); + return; +} + +/** + * @brief This function fills the HE cap tlv out put format is LE, not CPU + * + * @param priv A pointer to mlan_private structure + * @param band 5G or 2.4 G + * @param phe_cap A pointer to MrvlIEtypes_Data_t structure + * @param flag TREU--pvht_cap has the setting for resp + * MFALSE -- pvht_cap is clean + * + * @return bytes added to the phe_cap + */ +t_u16 wlan_fill_he_cap_tlv(mlan_private *pmpriv, t_u8 band, + MrvlIEtypes_Extension_t *phe_cap, t_u8 flag) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + t_u16 len = 0; + + if (!phe_cap) { + LEAVE(); + return 0; + } + if (band & BAND_A) { + memcpy_ext(pmadapter, (t_u8 *)phe_cap, pmpriv->user_he_cap, + pmpriv->user_hecap_len, + sizeof(MrvlIEtypes_He_cap_t)); + len = pmpriv->user_hecap_len; + } else { + memcpy_ext(pmadapter, (t_u8 *)phe_cap, pmpriv->user_2g_he_cap, + pmpriv->user_2g_hecap_len, + sizeof(MrvlIEtypes_He_cap_t)); + len = pmpriv->user_2g_hecap_len; + } + phe_cap->type = wlan_cpu_to_le16(phe_cap->type); + phe_cap->len = wlan_cpu_to_le16(phe_cap->len); + + LEAVE(); + return len; +} + +/** + * @brief This function append the 802_11ax HE capability 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_11ax_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, + t_u8 **ppbuffer) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + MrvlIEtypes_He_cap_t *phecap = MNULL; + int len = 0; + t_u8 bw_80p80 = MFALSE; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + /** check if AP support HE, if not return right away */ + if (!pbss_desc->phe_cap) { + LEAVE(); + return 0; + } + bw_80p80 = wlan_is_80_80_support(pmpriv, pbss_desc); + phecap = (MrvlIEtypes_He_cap_t *)*ppbuffer; + if (pbss_desc->bss_band & BAND_A) { + memcpy_ext(pmadapter, *ppbuffer, pmpriv->user_he_cap, + pmpriv->user_hecap_len, pmpriv->user_hecap_len); + *ppbuffer += pmpriv->user_hecap_len; + len = pmpriv->user_hecap_len; + } else { + memcpy_ext(pmadapter, *ppbuffer, pmpriv->user_2g_he_cap, + pmpriv->user_2g_hecap_len, + pmpriv->user_2g_hecap_len); + *ppbuffer += pmpriv->user_2g_hecap_len; + len = pmpriv->user_2g_hecap_len; + } + phecap->type = wlan_cpu_to_le16(phecap->type); + phecap->len = wlan_cpu_to_le16(phecap->len); + + if (bw_80p80) { + /** configure 2*2 to 1*1 to support 80+80Mhz*/ + /** set 1*1 mcs rate for 80Mhz rx*/ + phecap->he_txrx_mcs_support[0] &= ~(MBIT(0) | MBIT(1)); + /** set 1*1 mcs rate for 80Mhz tx*/ + phecap->he_txrx_mcs_support[3] &= ~(MBIT(0) | MBIT(1)); + /** set 1*1 mcs rate for 160Mhz rx*/ + phecap->he160_txrx_mcs_support[0] &= ~(MBIT(0) | MBIT(1)); + /** set 1*1 mcs rate for 160Mhz tx*/ + phecap->he160_txrx_mcs_support[3] &= ~(MBIT(0) | MBIT(1)); + /** set 1*1 mcs rate for 80+80Mhz rx*/ + phecap->he8080_txrx_mcs_support[0] &= ~(MBIT(0) | MBIT(1)); + /** set 1*1 mcs rate for 80+80Mhz tx*/ + phecap->he8080_txrx_mcs_support[3] &= ~(MBIT(0) | MBIT(1)); + } else { + /** reset BIT3 and BIT4 channel width ,not support 80 + 80*/ + /** not support 160Mhz now, if support,not reset bit3 */ + phecap->he_phy_cap[0] &= ~(MBIT(3) | MBIT(4)); + } + + LEAVE(); + return len; +} + +/** + * @brief This function save the 11ax cap from FW. + * + * @param pmadapater A pointer to mlan_adapter + * @param hw_he_cap A pointer to MrvlIEtypes_Extension_t + * + * @return N/A + */ +void wlan_update_11ax_cap(mlan_adapter *pmadapter, + MrvlIEtypes_Extension_t *hw_he_cap) +{ + MrvlIEtypes_He_cap_t *phe_cap = MNULL; + t_u8 i = 0; + t_u8 he_cap_2g = 0; + + ENTER(); + if ((hw_he_cap->len + sizeof(MrvlIEtypesHeader_t)) > + sizeof(pmadapter->hw_he_cap)) { + PRINTM(MERROR, "hw_he_cap too big, len=%d\n", hw_he_cap->len); + LEAVE(); + return; + } + phe_cap = (MrvlIEtypes_He_cap_t *)hw_he_cap; + if (phe_cap->he_phy_cap[0] & + (AX_2G_40MHZ_SUPPORT | AX_2G_20MHZ_SUPPORT)) { + pmadapter->hw_2g_hecap_len = + hw_he_cap->len + sizeof(MrvlIEtypesHeader_t); + memcpy_ext(pmadapter, pmadapter->hw_2g_he_cap, + (t_u8 *)hw_he_cap, + hw_he_cap->len + sizeof(MrvlIEtypesHeader_t), + sizeof(pmadapter->hw_2g_he_cap)); + pmadapter->fw_bands |= BAND_GAX; + pmadapter->config_bands |= BAND_GAX; + he_cap_2g = MTRUE; + DBG_HEXDUMP(MCMD_D, "2.4G HE capability IE ", + (t_u8 *)pmadapter->hw_2g_he_cap, + pmadapter->hw_2g_hecap_len); + } else { + pmadapter->fw_bands |= BAND_AAX; + pmadapter->config_bands |= BAND_AAX; + pmadapter->hw_hecap_len = + hw_he_cap->len + sizeof(MrvlIEtypesHeader_t); + memcpy_ext(pmadapter, pmadapter->hw_he_cap, (t_u8 *)hw_he_cap, + hw_he_cap->len + sizeof(MrvlIEtypesHeader_t), + sizeof(pmadapter->hw_he_cap)); + DBG_HEXDUMP(MCMD_D, "5G HE capability IE ", + (t_u8 *)pmadapter->hw_he_cap, + pmadapter->hw_hecap_len); + } + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + pmadapter->priv[i]->config_bands = + pmadapter->config_bands; + if (he_cap_2g) { + pmadapter->priv[i]->user_2g_hecap_len = + pmadapter->hw_2g_hecap_len; + memcpy_ext(pmadapter, + pmadapter->priv[i]->user_2g_he_cap, + pmadapter->hw_2g_he_cap, + pmadapter->hw_2g_hecap_len, + sizeof(pmadapter->priv[i] + ->user_2g_he_cap)); + } else { + pmadapter->priv[i]->user_hecap_len = + pmadapter->hw_hecap_len; + memcpy_ext( + pmadapter, + pmadapter->priv[i]->user_he_cap, + pmadapter->hw_he_cap, + pmadapter->hw_hecap_len, + sizeof(pmadapter->priv[i]->user_he_cap)); + } + } + } + LEAVE(); + return; +} + +/** + * @brief This function check if 11AX is allowed in bandcfg + * + * @param pmpriv A pointer to mlan_private structure + * @param bss_band bss band + * + * @return 0--not allowed, other value allowed + */ +t_u16 wlan_11ax_bandconfig_allowed(mlan_private *pmpriv, t_u16 bss_band) +{ + if (!IS_FW_SUPPORT_11AX(pmpriv->adapter)) + return MFALSE; + if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { + if (bss_band & BAND_G) + return (pmpriv->adapter->adhoc_start_band & BAND_GAX); + else if (bss_band & BAND_A) + return (pmpriv->adapter->adhoc_start_band & BAND_AAX); + } else { + if (bss_band & BAND_G) + return (pmpriv->config_bands & BAND_GAX); + else if (bss_band & BAND_A) + return (pmpriv->config_bands & BAND_AAX); + } + return MFALSE; +} + +/** + * @brief Set 11ax 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_11ax_ioctl_hecfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11ax_cfg *cfg = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11ax_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_11ax_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + cfg = (mlan_ds_11ax_cfg *)pioctl_req->pbuf; + + if ((cfg->param.he_cfg.band & MBIT(0)) && + !(pmadapter->fw_bands & BAND_GAX)) { + PRINTM(MERROR, "FW don't support 2.4G AX\n"); + return MLAN_STATUS_FAILURE; + } + if ((cfg->param.he_cfg.band & MBIT(1)) && + !(pmadapter->fw_bands & BAND_AAX)) { + PRINTM(MERROR, "FW don't support 5G AX\n"); + return MLAN_STATUS_FAILURE; + } + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_11AX_CFG, cmd_action, 0, + (t_void *)pioctl_req, + (t_void *)&cfg->param.he_cfg); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief 11ax 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_11ax_cfg_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_11ax_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11ax_cfg *)pioctl_req->pbuf; + switch (cfg->sub_command) { + case MLAN_OID_11AX_HE_CFG: + status = wlan_11ax_ioctl_hecfg(pmadapter, pioctl_req); + break; + case MLAN_OID_11AX_CMD_CFG: + status = wlan_11ax_ioctl_cmd(pmadapter, pioctl_req); + break; + case MLAN_OID_11AX_TWT_CFG: + status = wlan_11ax_ioctl_twtcfg(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief This function prepares 11ax 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_11ax_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_11AX_CFG *axcfg = &cmd->params.axcfg; + mlan_ds_11ax_he_cfg *hecfg = (mlan_ds_11ax_he_cfg *)pdata_buf; + MrvlIEtypes_Extension_t *tlv = MNULL; + t_u8 *pos = MNULL; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11AX_CFG); + cmd->size = sizeof(HostCmd_DS_11AX_CFG) + S_DS_GEN; + + axcfg->action = wlan_cpu_to_le16(cmd_action); + axcfg->band_config = hecfg->band & 0xFF; + + pos = (t_u8 *)axcfg->val; + /**HE Capability */ + if (hecfg->he_cap.len && (hecfg->he_cap.ext_id == HE_CAPABILITY)) { + tlv = (MrvlIEtypes_Extension_t *)pos; + tlv->type = wlan_cpu_to_le16(hecfg->he_cap.id); + tlv->len = wlan_cpu_to_le16(hecfg->he_cap.len); + memcpy_ext(pmadapter, &tlv->ext_id, &hecfg->he_cap.ext_id, + hecfg->he_cap.len, + MRVDRV_SIZE_OF_CMD_BUFFER - cmd->size); + cmd->size += hecfg->he_cap.len + sizeof(MrvlIEtypesHeader_t); + pos += hecfg->he_cap.len + sizeof(MrvlIEtypesHeader_t); + } + + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +/** + * @brief This function handles the command response of 11axcfg + * + * @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_11ax_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + mlan_ds_11ax_cfg *cfg = MNULL; + mlan_ds_11ax_he_capa *hecap = MNULL; + HostCmd_DS_11AX_CFG *axcfg = &resp->params.axcfg; + MrvlIEtypes_Extension_t *tlv = MNULL; + t_u16 left_len = 0, tlv_type = 0, tlv_len = 0; + + ENTER(); + + if (pioctl_buf == MNULL) + goto done; + + cfg = (mlan_ds_11ax_cfg *)pioctl_buf->pbuf; + cfg->param.he_cfg.band = axcfg->band_config; + hecap = (mlan_ds_11ax_he_capa *)&cfg->param.he_cfg.he_cap; + + /* TLV parse */ + left_len = resp->size - sizeof(HostCmd_DS_11AX_CFG) - S_DS_GEN; + tlv = (MrvlIEtypes_Extension_t *)axcfg->val; + + while (left_len > sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if (tlv_type == EXTENSION) { + switch (tlv->ext_id) { + case HE_CAPABILITY: + hecap->id = tlv_type; + hecap->len = tlv_len; + memcpy_ext(pmadapter, (t_u8 *)&hecap->ext_id, + (t_u8 *)&tlv->ext_id, tlv_len, + sizeof(mlan_ds_11ax_he_capa) - + sizeof(MrvlIEtypesHeader_t)); + if (cfg->param.he_cfg.band & MBIT(1)) { + memcpy_ext( + pmadapter, + (t_u8 *)&pmpriv->user_he_cap, + (t_u8 *)tlv, + tlv_len + + sizeof(MrvlIEtypesHeader_t), + sizeof(pmpriv->user_he_cap)); + pmpriv->user_hecap_len = MIN( + tlv_len + + sizeof(MrvlIEtypesHeader_t), + sizeof(pmpriv->user_he_cap)); + PRINTM(MCMND, "user_hecap_len=%d\n", + pmpriv->user_hecap_len); + } else { + memcpy_ext( + pmadapter, + (t_u8 *)&pmpriv->user_2g_he_cap, + (t_u8 *)tlv, + tlv_len + + sizeof(MrvlIEtypesHeader_t), + sizeof(pmpriv->user_2g_he_cap)); + pmpriv->user_2g_hecap_len = MIN( + tlv_len + + sizeof(MrvlIEtypesHeader_t), + sizeof(pmpriv->user_2g_he_cap)); + PRINTM(MCMND, "user_2g_hecap_len=%d\n", + pmpriv->user_2g_hecap_len); + } + break; + default: + break; + } + } + + left_len -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypes_Extension_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + +done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief 11ax 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 + */ +mlan_status wlan_11ax_ioctl_cmd(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_11ax_cmd_cfg *cfg = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11ax_cmd_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_11ax_cmd_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + cfg = (mlan_ds_11ax_cmd_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 */ + status = wlan_prepare_cmd(pmpriv, HostCmd_CMD_11AX_CMD, cmd_action, 0, + (t_void *)pioctl_req, (t_void *)cfg); + if (status == MLAN_STATUS_SUCCESS) + status = MLAN_STATUS_PENDING; + + LEAVE(); + return status; +} + +/** + * @brief This function prepares 11ax 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_11ax_cmd(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_11AX_CMD_CFG *axcmd = &cmd->params.axcmd; + mlan_ds_11ax_cmd_cfg *ds_11ax_cmd = (mlan_ds_11ax_cmd_cfg *)pdata_buf; + mlan_ds_11ax_sr_cmd *sr_cmd = + (mlan_ds_11ax_sr_cmd *)&ds_11ax_cmd->param; + mlan_ds_11ax_beam_cmd *beam_cmd = + (mlan_ds_11ax_beam_cmd *)&ds_11ax_cmd->param; + mlan_ds_11ax_htc_cmd *htc_cmd = + (mlan_ds_11ax_htc_cmd *)&ds_11ax_cmd->param; + mlan_ds_11ax_txop_cmd *txop_cmd = + (mlan_ds_11ax_txop_cmd *)&ds_11ax_cmd->param; + mlan_ds_11ax_txomi_cmd *txomi_cmd = + (mlan_ds_11ax_txomi_cmd *)&ds_11ax_cmd->param; + mlan_ds_11ax_toltime_cmd *toltime_cmd = + (mlan_ds_11ax_toltime_cmd *)&ds_11ax_cmd->param; + MrvlIEtypes_Data_t *tlv = MNULL; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11AX_CMD); + cmd->size = sizeof(HostCmd_DS_11AX_CMD_CFG) + S_DS_GEN; + + axcmd->action = wlan_cpu_to_le16(cmd_action); + axcmd->sub_id = wlan_cpu_to_le16(ds_11ax_cmd->sub_id); + switch (ds_11ax_cmd->sub_id) { + case MLAN_11AXCMD_SR_SUBID: + tlv = (MrvlIEtypes_Data_t *)axcmd->val; + tlv->header.type = wlan_cpu_to_le16(sr_cmd->type); + tlv->header.len = wlan_cpu_to_le16(sr_cmd->len); + memcpy_ext(pmadapter, tlv->data, + &sr_cmd->param.obss_pd_offset.offset, sr_cmd->len, + sr_cmd->len); + cmd->size += sizeof(MrvlIEtypesHeader_t) + sr_cmd->len; + break; + case MLAN_11AXCMD_BEAM_SUBID: + axcmd->val[0] = beam_cmd->value; + cmd->size += sizeof(t_u8); + break; + case MLAN_11AXCMD_HTC_SUBID: + axcmd->val[0] = htc_cmd->value; + cmd->size += sizeof(t_u8); + break; + case MLAN_11AXCMD_TXOPRTS_SUBID: + memcpy_ext(pmadapter, axcmd->val, &txop_cmd->rts_thres, + sizeof(t_u16), sizeof(t_u16)); + cmd->size += sizeof(t_u16); + break; + case MLAN_11AXCMD_TXOMI_SUBID: + memcpy_ext(pmadapter, axcmd->val, &txomi_cmd->omi, + sizeof(t_u16), sizeof(t_u16)); + cmd->size += sizeof(t_u16); + break; + case MLAN_11AXCMD_OBSS_TOLTIME_SUBID: + memcpy_ext(pmadapter, axcmd->val, &toltime_cmd->tol_time, + sizeof(t_u32), sizeof(t_u32)); + cmd->size += sizeof(t_u32); + break; + default: + PRINTM(MERROR, "Unknown subcmd %x\n", ds_11ax_cmd->sub_id); + break; + } + + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of 11axcmd + * + * @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_11ax_cmd(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + mlan_ds_11ax_cmd_cfg *cfg = MNULL; + HostCmd_DS_11AX_CMD_CFG *axcmd = &resp->params.axcmd; + MrvlIEtypes_Data_t *tlv = MNULL; + t_s16 left_len = 0; + t_u16 tlv_type = 0, tlv_len = 0; + + ENTER(); + + if (pioctl_buf == MNULL) + goto done; + + cfg = (mlan_ds_11ax_cmd_cfg *)pioctl_buf->pbuf; + cfg->sub_id = wlan_le16_to_cpu(axcmd->sub_id); + + switch (axcmd->sub_id) { + case MLAN_11AXCMD_SR_SUBID: + /* TLV parse */ + left_len = + resp->size - sizeof(HostCmd_DS_11AX_CMD_CFG) - S_DS_GEN; + // tlv = (MrvlIEtypes_Extension_t *)axcfg->val; + tlv = (MrvlIEtypes_Data_t *)axcmd->val; + while (left_len > sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->header.type); + tlv_len = wlan_le16_to_cpu(tlv->header.len); + memcpy_ext( + pmadapter, + cfg->param.sr_cfg.param.obss_pd_offset.offset, + tlv->data, tlv_len, tlv_len); + left_len -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypes_Data_t + *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + break; + case MLAN_11AXCMD_BEAM_SUBID: + cfg->param.beam_cfg.value = *axcmd->val; + break; + case MLAN_11AXCMD_HTC_SUBID: + cfg->param.htc_cfg.value = *axcmd->val; + break; + case MLAN_11AXCMD_TXOPRTS_SUBID: + memcpy_ext(pmadapter, &cfg->param.txop_cfg.rts_thres, + axcmd->val, sizeof(t_u16), sizeof(t_u16)); + break; + case MLAN_11AXCMD_TXOMI_SUBID: + memcpy_ext(pmadapter, &cfg->param.txomi_cfg.omi, axcmd->val, + sizeof(t_u16), sizeof(t_u16)); + break; + case MLAN_11AXCMD_OBSS_TOLTIME_SUBID: + memcpy_ext(pmadapter, &cfg->param.toltime_cfg.tol_time, + axcmd->val, sizeof(t_u32), sizeof(t_u32)); + break; + default: + PRINTM(MERROR, "Unknown subcmd %x\n", axcmd->sub_id); + break; + } + +done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares TWT cfg command to configure + * setup/teardown + * + * @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 Status returned + */ +mlan_status wlan_cmd_twt_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_TWT_CFG *hostcmd_twtcfg = + (HostCmd_DS_TWT_CFG *)&cmd->params.twtcfg; + mlan_ds_twtcfg *ds_twtcfg = (mlan_ds_twtcfg *)pdata_buf; + hostcmd_twt_setup *twt_setup_params = MNULL; + hostcmd_twt_teardown *twt_teardown_params = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TWT_CFG); + + hostcmd_twtcfg->action = wlan_cpu_to_le16(cmd_action); + hostcmd_twtcfg->sub_id = wlan_cpu_to_le16(ds_twtcfg->sub_id); + + cmd->size = S_DS_GEN + sizeof(hostcmd_twtcfg->action) + + sizeof(hostcmd_twtcfg->sub_id); + + switch (hostcmd_twtcfg->sub_id) { + case MLAN_11AX_TWT_SETUP_SUBID: + twt_setup_params = &hostcmd_twtcfg->param.twt_setup; + memset(pmadapter, twt_setup_params, 0x00, + sizeof(hostcmd_twtcfg->param.twt_setup)); + twt_setup_params->implicit = + ds_twtcfg->param.twt_setup.implicit; + twt_setup_params->announced = + ds_twtcfg->param.twt_setup.announced; + twt_setup_params->trigger_enabled = + ds_twtcfg->param.twt_setup.trigger_enabled; + twt_setup_params->twt_info_disabled = + ds_twtcfg->param.twt_setup.twt_info_disabled; + twt_setup_params->negotiation_type = + ds_twtcfg->param.twt_setup.negotiation_type; + twt_setup_params->twt_wakeup_duration = + ds_twtcfg->param.twt_setup.twt_wakeup_duration; + twt_setup_params->flow_identifier = + ds_twtcfg->param.twt_setup.flow_identifier; + twt_setup_params->hard_constraint = + ds_twtcfg->param.twt_setup.hard_constraint; + twt_setup_params->twt_exponent = + ds_twtcfg->param.twt_setup.twt_exponent; + twt_setup_params->twt_mantissa = wlan_cpu_to_le16( + ds_twtcfg->param.twt_setup.twt_mantissa); + twt_setup_params->twt_request = + ds_twtcfg->param.twt_setup.twt_request; + cmd->size += sizeof(hostcmd_twtcfg->param.twt_setup); + break; + case MLAN_11AX_TWT_TEARDOWN_SUBID: + twt_teardown_params = &hostcmd_twtcfg->param.twt_teardown; + memset(pmadapter, twt_teardown_params, 0x00, + sizeof(hostcmd_twtcfg->param.twt_teardown)); + twt_teardown_params->flow_identifier = + ds_twtcfg->param.twt_teardown.flow_identifier; + twt_teardown_params->negotiation_type = + ds_twtcfg->param.twt_teardown.negotiation_type; + twt_teardown_params->teardown_all_twt = + ds_twtcfg->param.twt_teardown.teardown_all_twt; + cmd->size += sizeof(hostcmd_twtcfg->param.twt_teardown); + break; + default: + PRINTM(MERROR, "Unknown subcmd %x\n", ds_twtcfg->sub_id); + ret = MLAN_STATUS_FAILURE; + break; + } + + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return ret; +} + +/** + * @brief TWT config 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 + */ +mlan_status wlan_11ax_ioctl_twtcfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_twtcfg *cfg = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_twtcfg)) { + PRINTM(MERROR, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_twtcfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + cfg = (mlan_ds_twtcfg *)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_TWT_CFG, cmd_action, 0, + (t_void *)pioctl_req, (t_void *)cfg); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11ax.h b/mxm_wifiex/wlan_src/mlan/mlan_11ax.h new file mode 100644 index 0000000..4e64772 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11ax.h @@ -0,0 +1,58 @@ +/** @file mlan_11ax.h + * + * @brief This file contains the functions for station ioctl. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 _MLAN_11AX_H_ +#define _MLAN_11AX_H_ + +/** device support 2.4G 40MHZ*/ +#define AX_2G_40MHZ_SUPPORT MBIT(1) +/** device support 2.4G 242 tone RUs */ +#define AX_2G_20MHZ_SUPPORT MBIT(5) + +t_u8 wlan_check_11ax_twt_supported(mlan_private *pmpriv, + BSSDescriptor_t *pbss_desc); +mlan_status wlan_11ax_ioctl_twtcfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_twt_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +t_u16 wlan_fill_he_cap_tlv(mlan_private *pmpriv, t_u8 band, + MrvlIEtypes_Extension_t *phe_cap, t_u8 flag); +void wlan_update_11ax_cap(mlan_adapter *pmadapter, + MrvlIEtypes_Extension_t *hw_he_cap); +int wlan_cmd_append_11ax_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, + t_u8 **ppbuffer); +t_u16 wlan_11ax_bandconfig_allowed(mlan_private *pmpriv, t_u16 bss_band); +mlan_status wlan_11ax_cfg_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_11ax_ioctl_cmd(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_11ax_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_11ax_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_cmd_11ax_cmd(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_11ax_cmd(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +#endif /* _MLAN_11AX_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11d.c b/mxm_wifiex/wlan_src/mlan/mlan_11d.c new file mode 100644 index 0000000..a9ac930 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11d.c @@ -0,0 +1,1590 @@ +/** @file mlan_11d.c + * + * @brief This file contains functions for 802.11D. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_11h.h" + +/******************************************************** + Local Variables +********************************************************/ + +#ifdef STA_SUPPORT +/** Region code mapping */ +typedef struct _region_code_mapping { + /** Region */ + t_u8 region[COUNTRY_CODE_LEN]; + /** Code */ + t_u8 code; +} region_code_mapping_t; + +/** Region code mapping table */ +static region_code_mapping_t region_code_mapping[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x20}, /* IC Canada */ + {"SG ", 0x10}, /* Singapore */ + {"EU ", 0x30}, /* ETSI */ + {"AU ", 0x30}, /* Australia */ + {"KR ", 0x30}, /* Republic Of Korea */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ + {"JP ", 0x41}, /* Japan */ + {"CN ", 0x50}, /* China */ + {"JP ", 0xFE}, /* Japan */ + {"JP ", 0xFF}, /* Japan special */ + {"NE ", 0x30}, /* New Zeland */ +}; + +/** Universal region code */ +#define UNIVERSAL_REGION_CODE 0xff +#endif + +/** Default Tx power */ +#define TX_PWR_DEFAULT 10 + +/* 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 */ +}; + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +#ifdef STA_SUPPORT +/** + * @brief This function converts integer code to region string + * + * @param pmadapter A pointer to mlan_adapter structure + * @param code Region code + * + * @return Region string + */ +static t_u8 *wlan_11d_code_2_region(pmlan_adapter pmadapter, t_u8 code) +{ + t_u8 i; + + ENTER(); + + /* Look for code in mapping table */ + for (i = 0; i < NELEMENTS(region_code_mapping); i++) { + if (region_code_mapping[i].code == code) { + LEAVE(); + return region_code_mapping[i].region; + } + } + + LEAVE(); + /* Default is US */ + return region_code_mapping[0].region; +} + +/** + * @brief This function Checks if channel txpwr is learned from AP/IBSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param band Band number + * @param chan Channel number + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return MTRUE or MFALSE + */ +static t_u8 wlan_11d_channel_known(pmlan_adapter pmadapter, t_u8 band, + t_u8 chan, + parsed_region_chan_11d_t *parsed_region_chan) +{ + chan_power_11d_t *pchan_pwr = parsed_region_chan->chan_pwr; + t_u8 no_of_chan = parsed_region_chan->no_of_chan; + t_u8 i = 0; + t_u8 ret = MFALSE; + mlan_private *pmpriv; + + ENTER(); + + HEXDUMP("11D: parsed_region_chan", (t_u8 *)pchan_pwr, + sizeof(chan_power_11d_t) * no_of_chan); + + /* Search channel */ + for (i = 0; i < no_of_chan; i++) { + if (chan == pchan_pwr[i].chan && band == pchan_pwr[i].band) { + PRINTM(MINFO, "11D: Found channel:%d (band:%d)\n", chan, + band); + ret = MTRUE; + + if (band & BAND_A) { + /* If chan is a DFS channel, we need to see an + * AP on it */ + pmpriv = wlan_get_priv(pmadapter, + MLAN_BSS_ROLE_STA); + if (pmpriv && wlan_11h_radar_detect_required( + pmpriv, chan)) { + PRINTM(MINFO, + "11H: DFS channel %d, and ap_seen=%d\n", + chan, pchan_pwr[i].ap_seen); + ret = pchan_pwr[i].ap_seen; + } + } + + LEAVE(); + return ret; + } + } + + PRINTM(MINFO, "11D: Could not find channel:%d (band:%d)\n", chan, band); + LEAVE(); + return ret; +} + +/** + * @brief This function generates parsed_region_chan from Domain Info + * learned from AP/IBSS + * + * @param pmadapter Pointer to mlan_adapter structure + * @param region_chan Pointer to region_chan_t + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return N/A + */ +static t_void wlan_11d_generate_parsed_region_chan( + pmlan_adapter pmadapter, region_chan_t *region_chan, + parsed_region_chan_11d_t *parsed_region_chan) +{ + chan_freq_power_t *cfp; + t_u8 i; + + ENTER(); + + /* Region channel must be provided */ + if (!region_chan) { + PRINTM(MWARN, "11D: region_chan is MNULL\n"); + LEAVE(); + return; + } + + /* Get channel-frequency-power trio */ + cfp = region_chan->pcfp; + if (!cfp) { + PRINTM(MWARN, "11D: cfp equal MNULL\n"); + LEAVE(); + return; + } + + /* Set channel, band and power */ + for (i = 0; i < region_chan->num_cfp; i++, cfp++) { + parsed_region_chan->chan_pwr[i].chan = (t_u8)cfp->channel; + parsed_region_chan->chan_pwr[i].band = region_chan->band; + parsed_region_chan->chan_pwr[i].pwr = (t_u8)cfp->max_tx_power; + PRINTM(MINFO, "11D: Chan[%d] Band[%d] Pwr[%d]\n", + parsed_region_chan->chan_pwr[i].chan, + parsed_region_chan->chan_pwr[i].band, + parsed_region_chan->chan_pwr[i].pwr); + } + parsed_region_chan->no_of_chan = region_chan->num_cfp; + + PRINTM(MINFO, "11D: no_of_chan[%d]\n", parsed_region_chan->no_of_chan); + + LEAVE(); + return; +} + +/** + * @brief This function generates domain_info from parsed_region_chan + * + * @param pmadapter Pointer to mlan_adapter structure + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11d_generate_domain_info(pmlan_adapter pmadapter, + parsed_region_chan_11d_t *parsed_region_chan) +{ + t_u8 no_of_sub_band = 0; + t_u8 no_of_chan = parsed_region_chan->no_of_chan; + t_u8 no_of_parsed_chan = 0; + t_u8 first_chan = 0, next_chan = 0, max_pwr = 0; + t_u8 i, flag = MFALSE; + wlan_802_11d_domain_reg_t *domain_info = &pmadapter->domain_reg; + + ENTER(); + + /* Should be only place that clear domain_reg (besides init) */ + memset(pmadapter, domain_info, 0, sizeof(wlan_802_11d_domain_reg_t)); + + /* Set country code */ + memcpy_ext(pmadapter, domain_info->country_code, + wlan_11d_code_2_region(pmadapter, + (t_u8)pmadapter->region_code), + COUNTRY_CODE_LEN, 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 | BAND_GAC)) { + cfp = channel_freq_power_UN_BG; + cfp_no = NELEMENTS(channel_freq_power_UN_BG); + } else if (band & (BAND_A | BAND_AN | BAND_AAC)) { + cfp = channel_freq_power_UN_AJ; + cfp_no = NELEMENTS(channel_freq_power_UN_AJ); + } else { + PRINTM(MERROR, "11D: Wrong Band[%d]\n", band); + LEAVE(); + return MFALSE; + } + /* Locate the first_chan */ + for (i = 0; i < cfp_no; i++) { + if (cfp && ((cfp + i)->channel == first_chan)) { + PRINTM(MINFO, "11D: first_chan found\n"); + break; + } + } + + if (i < cfp_no) { + /* Check if beyond the boundary */ + if (i + no_of_chan < cfp_no) { + /* Get first_chan + no_of_chan */ + *chan = (t_u8)(cfp + i + no_of_chan)->channel; + LEAVE(); + return MTRUE; + } + } + + LEAVE(); + return MFALSE; +} + +/** + * @brief This function processes the country info present in BSSDescriptor. + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSSDescriptor_t + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_11d_process_country_info(mlan_private *pmpriv, + BSSDescriptor_t *pbss_desc) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + parsed_region_chan_11d_t region_chan; + parsed_region_chan_11d_t *parsed_region_chan = + &pmadapter->parsed_region_chan; + t_u16 i, j, num_chan_added = 0; + + ENTER(); + + memset(pmadapter, ®ion_chan, 0, sizeof(parsed_region_chan_11d_t)); + + /* Parse 11D country info */ + if (wlan_11d_parse_domain_info(pmadapter, &pbss_desc->country_info, + (t_u8)pbss_desc->bss_band, + ®ion_chan) != MLAN_STATUS_SUCCESS) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (parsed_region_chan->no_of_chan != 0) { + /* + * Check if the channel number already exists in the + * chan-power table of parsed_region_chan + */ + for (i = 0; (i < region_chan.no_of_chan && i < MAX_NO_OF_CHAN); + i++) { + for (j = 0; (j < parsed_region_chan->no_of_chan && + j < MAX_NO_OF_CHAN); + j++) { + /* + * Channel already exists, update the tx power + * with new tx power, since country IE is valid + * here. + */ + if (region_chan.chan_pwr[i].chan == + parsed_region_chan->chan_pwr[j] + .chan && + region_chan.chan_pwr[i].band == + parsed_region_chan->chan_pwr[j] + .band) { + parsed_region_chan->chan_pwr[j].pwr = + region_chan.chan_pwr[i].pwr; + break; + } + } + + if (j == parsed_region_chan->no_of_chan && + (j + num_chan_added) < MAX_NO_OF_CHAN) { + /* + * Channel does not exist in the channel power + * table, update this new chan and tx_power + * to the channel power table + */ + parsed_region_chan + ->chan_pwr[parsed_region_chan + ->no_of_chan + + num_chan_added] + .chan = region_chan.chan_pwr[i].chan; + parsed_region_chan + ->chan_pwr[parsed_region_chan + ->no_of_chan + + num_chan_added] + .band = region_chan.chan_pwr[i].band; + parsed_region_chan + ->chan_pwr[parsed_region_chan + ->no_of_chan + + num_chan_added] + .pwr = region_chan.chan_pwr[i].pwr; + parsed_region_chan + ->chan_pwr[parsed_region_chan + ->no_of_chan + + num_chan_added] + .ap_seen = MFALSE; + num_chan_added++; + } + } + parsed_region_chan->no_of_chan += num_chan_added; + } else { + /* Parsed region is empty, copy the first one */ + memcpy_ext(pmadapter, parsed_region_chan, ®ion_chan, + sizeof(parsed_region_chan_11d_t), + 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_ext(pmadapter, pdomain->country_code, country_code, + COUNTRY_CODE_LEN, COUNTRY_CODE_LEN); + pdomain->band = band; + pdomain->no_of_sub_band = num_sub_band; + memcpy_ext(pmadapter, pdomain->sub_band, sub_band_list, + num_sub_band * sizeof(IEEEtypes_SubbandSet_t), + MRVDRV_MAX_SUBBAND_802_11D * 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 && + pmpriv->state_11d.user_enable_11d == ENABLE_11D) ? + MTRUE : + MFALSE; +} + +/** + * @brief This function gets if 11D is enabled in FW + * + * @param pmpriv Pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +t_bool wlan_fw_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; + t_u8 i; + + ENTER(); + PRINTM(MCMND, "11D:Country=%c%c band=%d sub-band=5d\n", + pmadapter->domain_reg.country_code[0], + pmadapter->domain_reg.country_code[1], + pmadapter->domain_reg.band, no_of_sub_band); + for (i = 0; i < no_of_sub_band; i++) { + PRINTM(MCMND, + "11D: first chan=%d no_of_chan=%d, max_tx_pwr=%d\n", + pmadapter->domain_reg.sub_band[i].first_chan, + pmadapter->domain_reg.sub_band[i].no_of_chan, + pmadapter->domain_reg.sub_band[i].max_tx_pwr); + } + + 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_ext(pmadapter, domain->country_code, + pmadapter->domain_reg.country_code, + sizeof(domain->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_ext(pmadapter, domain->sub_band, + pmadapter->domain_reg.sub_band, + no_of_sub_band * sizeof(IEEEtypes_SubbandSet_t), + MRVDRV_MAX_SUBBAND_802_11D * + 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; +} + +/** + * @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 | BAND_AAC)) { + cf = channel_freq_power_UN_AJ; + cnt = NELEMENTS(channel_freq_power_UN_AJ); + } else { + cf = channel_freq_power_UN_BG; + cnt = NELEMENTS(channel_freq_power_UN_BG); + } + + /* Locate channel and return corresponding frequency */ + for (i = 0; i < cnt; i++) { + if (chan == cf[i].channel) + freq = cf[i].freq; + } + + LEAVE(); + return freq; +} + +#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 setups scan channels + * + * @param pmpriv Pointer to mlan_private structure + * @param band Band + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_11d_set_universaltable(mlan_private *pmpriv, t_u8 band) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u16 i = 0; + + ENTER(); + + memset(pmadapter, pmadapter->universal_channel, 0, + sizeof(pmadapter->universal_channel)); + + if (band & (BAND_B | BAND_G | BAND_GN)) + /* If band B, G or N */ + { + /* Set channel-frequency-power */ + pmadapter->universal_channel[i].num_cfp = + NELEMENTS(channel_freq_power_UN_BG); + PRINTM(MINFO, "11D: BG-band num_cfp=%d\n", + pmadapter->universal_channel[i].num_cfp); + + pmadapter->universal_channel[i].pcfp = channel_freq_power_UN_BG; + pmadapter->universal_channel[i].valid = MTRUE; + + /* Set region code */ + pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; + + /* Set band */ + if (band & BAND_GN) + pmadapter->universal_channel[i].band = BAND_G; + else + pmadapter->universal_channel[i].band = + (band & BAND_G) ? BAND_G : BAND_B; + i++; + } + + if (band & (BAND_A | BAND_AN | BAND_AAC)) { + /* If band A */ + + /* Set channel-frequency-power */ + pmadapter->universal_channel[i].num_cfp = + NELEMENTS(channel_freq_power_UN_AJ); + PRINTM(MINFO, "11D: AJ-band num_cfp=%d\n", + pmadapter->universal_channel[i].num_cfp); + + pmadapter->universal_channel[i].pcfp = channel_freq_power_UN_AJ; + + pmadapter->universal_channel[i].valid = MTRUE; + + /* Set region code */ + pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; + + /* Set band */ + pmadapter->universal_channel[i].band = BAND_A; + i++; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function calculates the scan type for channels + * + * @param pmadapter A pointer to mlan_adapter structure + * @param band Band number + * @param chan Chan number + * @param parsed_region_chan Pointer to parsed_region_chan_11d_t + * + * @return PASSIVE if chan is unknown; ACTIVE + * if chan is known + */ +t_u8 wlan_11d_get_scan_type(pmlan_adapter pmadapter, t_u8 band, t_u8 chan, + parsed_region_chan_11d_t *parsed_region_chan) +{ + t_u8 scan_type = MLAN_SCAN_TYPE_PASSIVE; + + ENTER(); + + if (wlan_11d_channel_known(pmadapter, band, chan, parsed_region_chan)) { + /* Channel found */ + PRINTM(MINFO, "11D: Channel found and doing Active Scan\n"); + scan_type = MLAN_SCAN_TYPE_ACTIVE; + } else + PRINTM(MINFO, + "11D: Channel not found and doing Passive Scan\n"); + + LEAVE(); + return scan_type; +} + +/** + * @brief This function clears the parsed region table, if 11D is enabled + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11d_clear_parsedtable(mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wlan_11d_is_enabled(pmpriv)) + memset(pmadapter, &(pmadapter->parsed_region_chan), 0, + sizeof(parsed_region_chan_11d_t)); + else + ret = MLAN_STATUS_FAILURE; + + LEAVE(); + return ret; +} + +/** + * @brief This function generates 11D info from user specified regioncode + * and download to FW + * + * @param pmpriv A pointer to mlan_private structure + * @param band Band to create + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11d_create_dnld_countryinfo(mlan_private *pmpriv, t_u8 band) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + region_chan_t *region_chan; + parsed_region_chan_11d_t parsed_region_chan; + t_u8 j; + + ENTER(); + + /* Only valid if 11D is enabled */ + if (wlan_11d_is_enabled(pmpriv)) { + PRINTM(MINFO, "11D: Band[%d]\n", band); + + /* Update parsed_region_chan; download domain info to FW */ + + /* Find region channel */ + for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++) { + region_chan = &pmadapter->region_channel[j]; + + PRINTM(MINFO, "11D: [%d] region_chan->Band[%d]\n", j, + region_chan->band); + + if (!region_chan || !region_chan->valid || + !region_chan->pcfp) + continue; + switch (region_chan->band) { + case BAND_A: + switch (band) { + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + case BAND_A | BAND_AN | BAND_AAC: + 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: + case BAND_B | BAND_G | BAND_GN | BAND_GAC: + 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_ext(pmadapter, ®ion_chan, + &pmadapter->parsed_region_chan, + sizeof(parsed_region_chan_11d_t), + 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; +} +#endif /* STA_SUPPORT */ + +/** + * @brief This function checks country code and maps it when needed + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pcountry_code Pointer to the country code string + * + * @return Pointer to the mapped country code string + */ +static t_u8 *wlan_11d_map_country_code(pmlan_adapter pmadapter, + t_u8 *pcountry_code) +{ + /* Since firmware can only recognize EU as ETSI domain and there is no + * memory left for some devices to convert it in firmware, driver need + * to convert it before passing country code to firmware through tlv + */ + + if (wlan_is_etsi_country(pmadapter, pcountry_code)) + return ("EU "); + else + return pcountry_code; +} + +/** + * @brief This function sets up domain_reg and downloads CMD to FW + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11d_cfg_domain_info(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11d_domain_info *domain_info = MNULL; + mlan_ds_11d_cfg *cfg_11d = MNULL; + t_u8 cfp_bg = 0, cfp_a = 0; + + ENTER(); + + if (pmadapter->otp_region && pmadapter->otp_region->force_reg) { + PRINTM(MERROR, + "ForceRegionRule is set in the on-chip OTP memory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (!wlan_fw_11d_is_enabled(pmpriv)) + wlan_11d_enable(pmpriv, MNULL, ENABLE_11D); + + cfg_11d = (mlan_ds_11d_cfg *)pioctl_req->pbuf; + domain_info = &cfg_11d->param.domain_info; + memcpy_ext(pmadapter, pmadapter->country_code, + domain_info->country_code, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + wlan_11d_set_domain_info( + pmpriv, domain_info->band, + wlan_11d_map_country_code(pmadapter, domain_info->country_code), + domain_info->no_of_sub_band, + (IEEEtypes_SubbandSet_t *)domain_info->sub_band); + ret = wlan_11d_send_domain_info(pmpriv, pioctl_req); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + /* Update region code and table based on country code */ + if (wlan_misc_country_2_cfp_table_code( + pmadapter, domain_info->country_code, &cfp_bg, &cfp_a)) { + PRINTM(MIOCTL, "Country code %c%c not found!\n", + domain_info->country_code[0], + domain_info->country_code[1]); + goto done; + } + pmadapter->cfp_code_bg = cfp_bg; + pmadapter->cfp_code_a = cfp_a; + if (cfp_a) + pmadapter->region_code = cfp_a; + else if (cfp_bg) + pmadapter->region_code = cfp_bg; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, pmadapter->region_code, + pmadapter->config_bands | + pmadapter->adhoc_start_band)) { + PRINTM(MIOCTL, "Fail to set regiontabl\n"); + goto done; + } +done: + LEAVE(); + return ret; +} + +#if defined(UAP_SUPPORT) +/** + * @brief This function handles domain info data from UAP interface. + * Checks conditions, sets up domain_reg, then downloads CMD. + * + * @param pmpriv A pointer to mlan_private structure + * @param band Band interface is operating on + * @param domain_tlv Pointer to domain_info tlv + * @param pioctl_buf Pointer to the IOCTL buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11d_handle_uap_domain_info(mlan_private *pmpriv, t_u8 band, + t_u8 *domain_tlv, + t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + MrvlIEtypes_DomainParamSet_t *pdomain_tlv; + t_u8 num_sub_band = 0; + t_u8 cfp_bg = 0, cfp_a = 0; + + ENTER(); + + pdomain_tlv = (MrvlIEtypes_DomainParamSet_t *)domain_tlv; + + /* update region code & table based on country string */ + if (wlan_misc_country_2_cfp_table_code( + pmadapter, pdomain_tlv->country_code, &cfp_bg, &cfp_a) == + MLAN_STATUS_SUCCESS) { + pmadapter->cfp_code_bg = cfp_bg; + pmadapter->cfp_code_a = cfp_a; + if (cfp_a) + pmadapter->region_code = cfp_a; + else if (cfp_bg) + pmadapter->region_code = cfp_bg; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, pmadapter->region_code, + pmadapter->config_bands | + pmadapter->adhoc_start_band)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + memcpy_ext(pmadapter, pmadapter->country_code, + pdomain_tlv->country_code, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + num_sub_band = ((pdomain_tlv->header.len - COUNTRY_CODE_LEN) / + sizeof(IEEEtypes_SubbandSet_t)); + + /* TODO: don't just clobber pmadapter->domain_reg. + * Add some checking or merging between STA & UAP domain_info + */ + wlan_11d_set_domain_info(pmpriv, band, pdomain_tlv->country_code, + num_sub_band, pdomain_tlv->sub_band); + ret = wlan_11d_send_domain_info(pmpriv, pioctl_buf); + +done: + LEAVE(); + return ret; +} +#endif diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11h.c b/mxm_wifiex/wlan_src/mlan/mlan_11h.c new file mode 100644 index 0000000..027ba58 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11h.c @@ -0,0 +1,4274 @@ +/** @file mlan_11h.c + * + * @brief This file contains functions for 802.11H. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************* +Change Log: + 03/26/2009: initial version +************************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_ioctl.h" +#include "mlan_11h.h" +#include "mlan_11n.h" +#ifdef UAP_SUPPORT +#include "mlan_uap.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/** Default IBSS DFS recovery interval (in TBTTs); used for adhoc start */ +#define WLAN_11H_DEFAULT_DFS_RECOVERY_INTERVAL 100 + +/** Default 11h power constraint used to offset the maximum transmit power */ +#define WLAN_11H_TPC_POWERCONSTRAINT 0 + +/** 11h TPC Power capability minimum setting, sent in TPC_INFO command to fw */ +#define WLAN_11H_TPC_POWERCAPABILITY_MIN 5 + +/** 11h TPC Power capability maximum setting, sent in TPC_INFO command to fw */ +#define WLAN_11H_TPC_POWERCAPABILITY_MAX 20 + +/** Regulatory requirement for the duration of a channel availability check */ +#define WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION 60000 /* in ms */ + +/** Starting Frequency for 11A band */ +#define START_FREQ_11A_BAND 5000 /* in MHz */ + +/** DFS Channel Move Time */ +#define DFS_CHAN_MOVE_TIME 10 /* in sec */ + +/** Regulatory requirement for the duration of a non-occupancy period */ +#define WLAN_11H_NON_OCCUPANCY_PERIOD 1800 /* in sec (30mins) */ + +/** Maximum allowable age (seconds) on DFS report data */ +#define MAX_DFS_REPORT_USABLE_AGE_SEC (120) /* 2 minutes */ + +/** Minimum delay for CHAN_SW IE to broadcast by FW */ +#define MIN_RDH_CHAN_SW_IE_PERIOD_MSEC (400) /* 4 beacons @ 100ms */ + +/** Maximum delay for CHAN_SW IE to broadcast by FW */ +#define MAX_RDH_CHAN_SW_IE_PERIOD_MSEC (3000) /* 5 beacons @ 600ms */ + +/** Maximum retries on selecting new random channel */ +#define MAX_RANDOM_CHANNEL_RETRIES (20) + +/** Maximum retries on selecting new random non-dfs channel */ +#define MAX_SWITCH_CHANNEL_RETRIES (30) + +/** Value for undetermined priv_curr_idx on first entry to new RDH stage */ +#define RDH_STAGE_FIRST_ENTRY_PRIV_IDX (0xff) + +/** Region codes 0x10, 0x20: channels 1 thru 11 supported */ +static const IEEEtypes_SupportChan_Subband_t wlan_11h_2_4G_region_FCC = {1, 11}; + +/** Region codes 0x30, 0x32, 0x41, 0x50: channels 1 thru 13 supported */ +static const IEEEtypes_SupportChan_Subband_t wlan_11h_2_4G_region_EU = {1, 13}; + +/** Region code 0x40: only channel 14 supported */ +static const IEEEtypes_SupportChan_Subband_t wlan_11h_2_4G_region_JPN40 = {14, + 1}; + +/** JPN sub-band config : Start Channel = 8, NumChans = 3 */ +static const IEEEtypes_SupportChan_Subband_t wlan_11h_JPN_bottom_band = {8, 3}; + +/** U-NII sub-band config : Start Channel = 36, NumChans = 4 */ +static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_lower_band = {36, 4}; + +/** U-NII sub-band config : Start Channel = 52, NumChans = 4 */ +static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_middle_band = {52, + 4}; + +/** U-NII sub-band config : Start Channel = 100, NumChans = 11 */ +static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_mid_upper_band = { + 100, 11}; + +/** U-NII sub-band config : Start Channel = 100, NumChans = 5 */ +static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_mid_upper_band_0 = { + 100, 5}; + +/** U-NII sub-band config : Start Channel = 132, NumChans = 3 */ +static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_mid_upper_band_1 = { + 132, 3}; + +/** 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 NXP + * proprietary format + * + * @param pmadapter Pointer to mlan_adapter + * @param pout_buf Output parameter: Buffer to output NXP formatted IE + * @param pin_ie Pointer to IEEE IE to be converted to NXP 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 NXP 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_ext(pmadapter, ptmp_buf, &mrvl_ie_hdr, sizeof(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_ext(pmadapter, ptmp_buf, pin_ie + 2, pin_ie[1], 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_ext(adapter, pdfs->dfs_owner, priv->curr_addr, + sizeof(pdfs->dfs_owner), sizeof(pdfs->dfs_owner)); + pdfs->dfs_recovery_interval = WLAN_11H_DEFAULT_DFS_RECOVERY_INTERVAL; + + for (; (num_chans < adapter->parsed_region_chan.no_of_chan) && + (num_chans < WLAN_11H_MAX_IBSS_DFS_CHANNELS); + num_chans++) { + pdfs->channel_map[num_chans].channel_number = + adapter->parsed_region_chan.chan_pwr[num_chans].chan; + + /* + * Set the initial map field with a basic measurement + */ + pdfs->channel_map[num_chans].rpt_map = initial_map; + } + + /* + * If we have an established channel map, include it and return + * a valid DFS element + */ + if (num_chans) { + PRINTM(MINFO, "11h: Added %d channels to IBSS DFS Map\n", + num_chans); + + pdfs->element_id = IBSS_DFS; + pdfs->len = (sizeof(pdfs->dfs_owner) + + sizeof(pdfs->dfs_recovery_interval) + + num_chans * sizeof(IEEEtypes_ChannelMap_t)); + + LEAVE(); + return pdfs->len + sizeof(pdfs->len) + sizeof(pdfs->element_id); + } + + /* Ensure the element is zeroed out for an invalid return */ + memset(adapter, pdfs, 0x00, sizeof(IEEEtypes_IBSS_DFS_t)); + + LEAVE(); + return 0; +} +#endif + +/** + * @brief Setup the Supported Channel IE sent in association requests + * + * The Supported Channels IE is required to be sent when the spectrum + * management capability (11h) is enabled. The element contains a + * starting channel and number of channels tuple for each sub-band + * the STA supports. This information is based on the operating region. + * + * @param priv Private driver information structure + * @param band Band in use + * @param psup_chan Output parameter: Pointer to the Supported Chan element + * setup by this function. + * + * @return + * - Length of the returned element in psup_chan output parameter + * - 0 if returned element is not setup + */ +static t_u16 +wlan_11h_set_supp_channels_ie(mlan_private *priv, t_u8 band, + IEEEtypes_SupportedChannels_t *psup_chan) +{ + t_u16 num_subbands = 0; + t_u16 ret_len = 0; + t_u8 cfp_bg, cfp_a; + + ENTER(); + memset(priv->adapter, psup_chan, 0x00, + sizeof(IEEEtypes_SupportedChannels_t)); + + cfp_bg = cfp_a = priv->adapter->region_code; + if (!priv->adapter->region_code) { + /* Invalid region code, use CFP code */ + cfp_bg = priv->adapter->cfp_code_bg; + cfp_a = priv->adapter->cfp_code_a; + } + + if ((band & BAND_B) || (band & BAND_G)) { + /* + * Channels are contiguous in 2.4GHz, usually only one subband. + */ + switch (cfp_bg) { + case 0x10: /* USA FCC */ + case 0x20: /* Canada IC */ + default: + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_FCC; + break; + case 0x30: /* Europe ETSI */ + case 0x41: /* Japan */ + case 0x50: /* China */ + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_EU; + break; + case 0x40: /* Japan */ + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_JPN40; + break; + case 0xff: /* Japan special */ + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_EU; + psup_chan->subband[num_subbands++] = + wlan_11h_2_4G_region_JPN40; + break; + } + } else if (band & BAND_A) { + /* + * Set the supported channel elements based on the region code, + * incrementing num_subbands for each sub-band we append to the + * element. + */ + switch (cfp_a) { + case 0x10: /* USA FCC */ + case 0x20: /* Canada IC */ + case 0x30: /* Europe ETSI */ + default: + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_mid_upper_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + case 0x50: /* China */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + case 0x40: /* Japan */ + case 0x41: /* Japan */ + case 0xff: /* Japan special */ + psup_chan->subband[num_subbands++] = + wlan_11h_JPN_bottom_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_mid_upper_band; + break; + case 0x1: /* Low band (5150-5250 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + break; + case 0x2: /* Lower middle band (5250-5350 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + break; + case 0x3: /* Upper middle band (5470-5725 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_mid_upper_band; + break; + case 0x4: /* High band (5725-5850 MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + case 0x5: /* Low band (5150-5250 MHz) and High band (5725-5850 + MHz) channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + case 0x6: /* Low band (5150-5250 MHz) and Lower middle band + (5250-5350 MHz) and High band (5725-5850 MHz) + channels */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + case 0x7: + /* 36-48 */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_lower_band; + /* 52-64 */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_middle_band; + /* 100-116 */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_mid_upper_band_0; + /* 132-140 */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_mid_upper_band_1; + /* 149-165 */ + psup_chan->subband[num_subbands++] = + wlan_11h_unii_upper_band; + break; + } + } + + /* + * If we have setup any supported subbands in the element, return a + * valid IE along with its size, else return 0. + */ + if (num_subbands) { + psup_chan->element_id = SUPPORTED_CHANNELS; + psup_chan->len = + num_subbands * sizeof(IEEEtypes_SupportChan_Subband_t); + + ret_len = (t_u16)(psup_chan->len + sizeof(psup_chan->len) + + sizeof(psup_chan->element_id)); + + HEXDUMP("11h: SupChan", (t_u8 *)psup_chan, ret_len); + } + + LEAVE(); + return ret_len; +} + +/** + * @brief Prepare CMD_802_11_TPC_ADAPT_REQ firmware command + * + * @param priv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being prepared + * for the firmware + * @param pinfo_buf HostCmd_DS_802_11_TPC_ADAPT_REQ passed as void data block + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_11h_cmd_tpc_request(mlan_private *priv, + HostCmd_DS_COMMAND *pcmd_ptr, + const t_void *pinfo_buf) +{ + ENTER(); + + memcpy_ext(priv->adapter, &pcmd_ptr->params.tpc_req, pinfo_buf, + sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ), + 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_ext(priv->adapter, &pcmd_ptr->params.chan_sw_ann, pch_sw_ann, + sizeof(HostCmd_DS_802_11_CHAN_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) +{ + HostCmd_DS_CHAN_RPT_REQ *pchan_rpt_req = + (HostCmd_DS_CHAN_RPT_REQ *)pinfo_buf; + wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs; + MrvlIEtypes_ChanRpt11hBasic_t *ptlv_basic; + t_bool is_cancel_req = MFALSE; + t_u8 dfs53cfg = priv->adapter->dfs53cfg; + MrvlIEtypes_DfsW53Cfg_t *ptlv_dfs53cfg; + + ENTER(); + + /* + * pchan_rpt_req->millisec_dwell_time would be zero if the chan_rpt_req + * is to cancel current ongoing report + */ + if (pchan_rpt_req->millisec_dwell_time == 0) + is_cancel_req = MTRUE; + + if (pstate_dfs->dfs_check_pending && !is_cancel_req) { + PRINTM(MERROR, + "11h: ChanRptReq - previous CMD_CHAN_REPORT_REQUEST has" + " not returned its result yet (as EVENT_CHANNEL_READY)." + " This command will be dropped.\n"); + LEAVE(); + return MLAN_STATUS_PENDING; + } + + /* Converted to little endian in wlan_11h_cmd_process */ + pcmd_ptr->size = sizeof(HostCmd_DS_CHAN_RPT_REQ) + S_DS_GEN; + + memcpy_ext(priv->adapter, &pcmd_ptr->params.chan_rpt_req, pchan_rpt_req, + sizeof(HostCmd_DS_CHAN_RPT_REQ), + sizeof(HostCmd_DS_CHAN_RPT_REQ)); + pcmd_ptr->params.chan_rpt_req.chan_desc.startFreq = + wlan_cpu_to_le16(pchan_rpt_req->chan_desc.startFreq); + pcmd_ptr->params.chan_rpt_req.millisec_dwell_time = + wlan_cpu_to_le32(pchan_rpt_req->millisec_dwell_time); + + /* if DFS channel, add BASIC report TLV, and set radar bit */ + if (!is_cancel_req && wlan_11h_radar_detect_required( + priv, pchan_rpt_req->chan_desc.chanNum)) { + ptlv_basic = + (MrvlIEtypes_ChanRpt11hBasic_t *)(((t_u8 *)(pcmd_ptr)) + + pcmd_ptr->size); + ptlv_basic->Header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANRPT_11H_BASIC); + ptlv_basic->Header.len = + wlan_cpu_to_le16(sizeof(MeasRptBasicMap_t)); + memset(priv->adapter, &ptlv_basic->map, 0, + sizeof(MeasRptBasicMap_t)); + ptlv_basic->map.radar = 1; + pcmd_ptr->size += sizeof(MrvlIEtypes_ChanRpt11hBasic_t); + } + + if ((priv->adapter->region_code == COUNTRY_CODE_JP_40 || + priv->adapter->region_code == COUNTRY_CODE_JP_FF) && + (dfs53cfg != DFS_W53_DEFAULT_FW)) { + ptlv_dfs53cfg = + (MrvlIEtypes_DfsW53Cfg_t *)(((t_u8 *)(pcmd_ptr)) + + pcmd_ptr->size); + ptlv_dfs53cfg->Header.type = + wlan_cpu_to_le16(TLV_TYPE_DFS_W53_CFG); + ptlv_dfs53cfg->Header.len = wlan_cpu_to_le16(sizeof(t_u8)); + ptlv_dfs53cfg->dfs53cfg = dfs53cfg; + pcmd_ptr->size += sizeof(MrvlIEtypes_DfsW53Cfg_t); + } + + pcmd_ptr->size = wlan_cpu_to_le16(pcmd_ptr->size); + + /* update dfs sturcture. + * dfs_check_pending is set when we receive CMD_RESP == SUCCESS */ + pstate_dfs->dfs_check_pending = MFALSE; + pstate_dfs->dfs_radar_found = MFALSE; + pstate_dfs->dfs_check_priv = MNULL; + + if (!is_cancel_req) + pstate_dfs->dfs_check_channel = + pchan_rpt_req->chan_desc.chanNum; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set the local power capability and constraint TLV + * + * @param ppbuffer The buffer to add these two TLVs + * @param channel Channel to which the power constraint applies + * @param power_constraint Power constraint to be applied on the channel + * @param min_tx_power_capability Min. Tx Power in Power Capability IE + * @param max_tx_power_capability Max. Tx Power in Power Capability IE + * + * @return The len increased + */ +static t_u32 wlan_11h_set_local_power_constraint_tlv( + t_u8 **ppbuffer, t_u8 channel, t_u8 power_constraint, + t_u8 min_tx_power_capability, t_u8 max_tx_power_capability) +{ + MrvlIEtypes_PowerCapability_t *pcap; + MrvlIEtypes_LocalPowerConstraint_t *pconstraint; + t_u8 *start_ptr = MNULL; + + ENTER(); + + /* Null Checks */ + if ((ppbuffer == MNULL) || (((t_u8 *)(*ppbuffer)) == MNULL)) { + LEAVE(); + return 0; + } + + start_ptr = (t_u8 *)(*ppbuffer); + + PRINTM(MINFO, + "11h: Set local power constraint = %d channel=%d min_tx_pwr=%d max_tx_pwr=%d\n", + power_constraint, channel, min_tx_power_capability, + max_tx_power_capability); + + pcap = (MrvlIEtypes_PowerCapability_t *)*ppbuffer; + pcap->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CAPABILITY); + pcap->header.len = wlan_cpu_to_le16(2); + pcap->min_power = min_tx_power_capability; + pcap->max_power = max_tx_power_capability; + *ppbuffer += sizeof(MrvlIEtypesHeader_t) + 2; + + pconstraint = (MrvlIEtypes_LocalPowerConstraint_t *)*ppbuffer; + pconstraint->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CONSTRAINT); + pconstraint->header.len = wlan_cpu_to_le16(2); + pconstraint->chan = channel; + pconstraint->constraint = power_constraint; + *ppbuffer += sizeof(MrvlIEtypesHeader_t) + 2; + + LEAVE(); + return (t_u32)(*ppbuffer - start_ptr); +} + +/** + * @brief Utility function to process a join to an infrastructure BSS + * + * @param priv Private driver information structure + * @param ppbuffer Output parameter: Pointer to the TLV output buffer, + * modified on return to point after the appended 11h TLVs + * @param band Band on which we are joining the BSS + * @param channel Channel on which we are joining the BSS + * @param p11h_bss_info Pointer to the 11h BSS information for this network + * that was parsed out of the scan response. + * + * @return Integer number of bytes appended to the TLV output + * buffer (ppbuffer) + */ +static t_u32 wlan_11h_process_infra_join(mlan_private *priv, t_u8 **ppbuffer, + t_u8 band, t_u32 channel, + wlan_11h_bss_info_t *p11h_bss_info) +{ + MrvlIEtypesHeader_t ie_header; + IEEEtypes_SupportedChannels_t sup_chan_ie; + t_u32 ret_len = 0; + t_u16 sup_chan_len = 0; + + ENTER(); + + /* Null Checks */ + if ((ppbuffer == MNULL) || (((t_u8 *)(*ppbuffer)) == MNULL)) { + LEAVE(); + return 0; + } + + ret_len += wlan_11h_set_local_power_constraint_tlv( + ppbuffer, (t_u8)channel, + (t_u8)p11h_bss_info->power_constraint.local_constraint, + (t_u8)priv->adapter->state_11h.min_tx_power_capability, + (t_u8)priv->adapter->state_11h.max_tx_power_capability); + + /* Setup the Supported Channels IE */ + sup_chan_len = wlan_11h_set_supp_channels_ie(priv, band, &sup_chan_ie); + + /* + * If we returned a valid Supported Channels IE, wrap and append it + */ + if (sup_chan_len) { + /* Wrap the supported channels IE with a passthrough TLV type */ + ie_header.type = wlan_cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = wlan_cpu_to_le16(sup_chan_len); + memcpy_ext(priv->adapter, *ppbuffer, &ie_header, + sizeof(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_ext(priv->adapter, *ppbuffer, &sup_chan_ie, sup_chan_len, + 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_ext(adapter, dfs_elem.dfs_owner, + p11h_bss_info->ibss_dfs.dfs_owner, + sizeof(dfs_elem.dfs_owner), + sizeof(dfs_elem.dfs_owner)); + dfs_elem.dfs_recovery_interval = + p11h_bss_info->ibss_dfs.dfs_recovery_interval; + } + + /* Append the dfs element to the TLV buffer */ + size_appended = wlan_11h_convert_ieee_to_mrvl_ie( + adapter, (t_u8 *)*ppbuffer, (t_u8 *)&dfs_elem); + + HEXDUMP("11h: IBSS-DFS", (t_u8 *)*ppbuffer, size_appended); + *ppbuffer += size_appended; + ret_len += size_appended; + + /* + * Check to see if we are joining a network. Join is indicated by the + * BSS Info pointer being valid (not NULL) + */ + if (p11h_bss_info) { + /* + * If there was a quiet element, include it in + * adhoc join command + */ + if (p11h_bss_info->quiet.element_id == QUIET) { + size_appended = wlan_11h_convert_ieee_to_mrvl_ie( + adapter, (t_u8 *)*ppbuffer, + (t_u8 *)&p11h_bss_info->quiet); + HEXDUMP("11h: Quiet", (t_u8 *)*ppbuffer, size_appended); + *ppbuffer += size_appended; + ret_len += size_appended; + } + + /* Copy the local constraint from the network */ + local_constraint = + p11h_bss_info->power_constraint.local_constraint; + } else { + /* + * If we are the adhoc starter, we can add a quiet element + */ + if (adapter->state_11h.quiet_ie.quiet_period) { + size_appended = wlan_11h_convert_ieee_to_mrvl_ie( + adapter, (t_u8 *)*ppbuffer, + (t_u8 *)&adapter->state_11h.quiet_ie); + HEXDUMP("11h: Quiet", (t_u8 *)*ppbuffer, size_appended); + *ppbuffer += size_appended; + ret_len += size_appended; + } + /* Use the local_constraint configured in the driver state */ + local_constraint = adapter->state_11h.usr_def_power_constraint; + } + + PRINTM(MINFO, "WEILIE 1: ppbuffer = %p\n", *ppbuffer); + + ret_len += wlan_11h_set_local_power_constraint_tlv( + ppbuffer, (t_u8)channel, (t_u8)local_constraint, + (t_u8)priv->adapter->state_11h.min_tx_power_capability, + (t_u8)priv->adapter->state_11h.max_tx_power_capability); + PRINTM(MINFO, "WEILIE 2: ppbuffer = %p\n", *ppbuffer); + + LEAVE(); + return ret_len; +} + +/** + * @brief Return whether the driver has enabled 11h for the interface + * + * Association/Join commands are dynamic in that they enable 11h in the + * driver/firmware when they are detected in the existing BSS. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if 11h is enabled + * - MFALSE otherwise + */ +static t_bool wlan_11h_is_enabled(mlan_private *priv) +{ + ENTER(); + LEAVE(); + return priv->intf_state_11h.is_11h_enabled; +} + +/** + * @brief Return whether the device has activated slave radar detection. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if slave radar detection is enabled in firmware + * - MFALSE otherwise + */ +static t_bool wlan_11h_is_slave_radar_det_active(mlan_private *priv) +{ + ENTER(); + LEAVE(); + return priv->adapter->state_11h.is_slave_radar_det_active; +} +/** + * @brief Return whether the slave interface is active, and on DFS channel. + * priv is assumed to already be a dfs slave interface, doesn't check this. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if priv is slave, and meets both conditions + * - MFALSE otherwise + */ +static t_bool wlan_11h_is_slave_active_on_dfs_chan(mlan_private *priv) +{ + t_bool ret = MFALSE; + + ENTER(); + if ((priv->media_connected == MTRUE) && + (priv->curr_bss_params.band & BAND_A) && + wlan_11h_radar_detect_required( + priv, priv->curr_bss_params.bss_descriptor.channel)) + ret = MTRUE; + + LEAVE(); + return ret; +} + +/** + * @brief Return whether the master interface is active, and on DFS channel. + * priv is assumed to already be a dfs master interface, doesn't check this. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if priv is master, and meets both conditions + * - MFALSE otherwise + */ +static t_bool wlan_11h_is_master_active_on_dfs_chan(mlan_private *priv) +{ + t_bool ret = MFALSE; + + ENTER(); + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + /* Ad-hoc creator */ + if (((priv->media_connected == MTRUE) || + (priv->adhoc_state == ADHOC_STARTING)) && + (priv->adapter->adhoc_start_band & BAND_A) && + wlan_11h_radar_detect_required(priv, priv->adhoc_channel)) + ret = MTRUE; + } else if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + /* UAP */ +#ifdef UAP_SUPPORT + if ((priv->uap_bss_started == MTRUE) && + (priv->uap_state_chan_cb.bandcfg.chanBand == BAND_5GHZ) && + wlan_11h_radar_detect_required( + priv, priv->uap_state_chan_cb.channel)) + ret = MTRUE; +#endif + } + LEAVE(); + return ret; +} + +/** + * @brief Determine if priv is DFS Master interface + * + * @param priv Pointer to mlan_private + * + * @return MTRUE or MFALSE + */ +static t_bool wlan_11h_is_dfs_master(mlan_private *priv) +{ + t_bool ret = MFALSE; + + ENTER(); + /* UAP: all are master */ + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + ret = MTRUE; + + /* STA: only ad-hoc creator is master */ + else if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (priv->bss_mode == MLAN_BSS_MODE_IBSS) && + (priv->adhoc_state == ADHOC_STARTED || + priv->adhoc_state == ADHOC_STARTING)) + ret = MTRUE; + + /* all other cases = slave interface */ + LEAVE(); + return ret; +} + +/* Need this as function to pass to wlan_count_priv_cond() */ +/** + * @brief Determine if priv is DFS Slave interface + * + * @param priv Pointer to mlan_private + * + * @return MTRUE or MFALSE + */ + +static t_bool wlan_11h_is_dfs_slave(mlan_private *priv) +{ + t_bool ret = MFALSE; + ENTER(); + ret = !wlan_11h_is_dfs_master(priv); + LEAVE(); + return ret; +} + +/** + * @brief This function checks if interface is active. + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +t_bool wlan_is_intf_active(mlan_private *pmpriv) +{ + t_bool ret = MFALSE; + ENTER(); + +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + /* + * NOTE: UAP's media_connected == true only after first STA + * associated. Need different variable to tell if UAP + * has been started. + */ + ret = pmpriv->uap_bss_started; + else +#endif + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) + ret = pmpriv->media_connected; + + LEAVE(); + return ret; +} + +/** + * @brief This function gets current radar detect flags + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return 11H MIB setting for radar detect + */ +static t_u32 wlan_11h_get_current_radar_detect_flags(mlan_adapter *pmadapter) +{ + t_u32 radar_det_flags = 0; + + ENTER(); + if (pmadapter->state_11h.is_master_radar_det_active) + radar_det_flags |= MASTER_RADAR_DET_MASK; + if (pmadapter->state_11h.is_slave_radar_det_active) + radar_det_flags |= SLAVE_RADAR_DET_MASK; + + PRINTM(MINFO, "%s: radar_det_state_curr=0x%x\n", __func__, + radar_det_flags); + + LEAVE(); + return radar_det_flags; +} + +/** + * @brief This function checks if radar detect flags have/should be changed. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pnew_state Output param with new state, if return MTRUE. + * + * @return MTRUE (need update) or MFALSE (no change in flags) + */ +static t_bool wlan_11h_check_radar_det_state(mlan_adapter *pmadapter, + t_u32 *pnew_state) +{ + t_u32 radar_det_state_new = 0; + t_bool ret; + + ENTER(); + PRINTM(MINFO, + "%s: master_radar_det_pending=%d, " + " slave_radar_det_pending=%d\n", + __func__, pmadapter->state_11h.master_radar_det_enable_pending, + pmadapter->state_11h.slave_radar_det_enable_pending); + + /* new state comes from evaluating interface states & pending starts */ + if (pmadapter->state_11h.master_radar_det_enable_pending || + (wlan_count_priv_cond(pmadapter, + wlan_11h_is_master_active_on_dfs_chan, + wlan_11h_is_dfs_master) > 0)) + radar_det_state_new |= MASTER_RADAR_DET_MASK; + if (pmadapter->state_11h.slave_radar_det_enable_pending || + (wlan_count_priv_cond(pmadapter, + wlan_11h_is_slave_active_on_dfs_chan, + wlan_11h_is_dfs_slave) > 0)) + radar_det_state_new |= SLAVE_RADAR_DET_MASK; + + PRINTM(MINFO, "%s: radar_det_state_new=0x%x\n", __func__, + radar_det_state_new); + + /* now compare flags with current state */ + ret = (wlan_11h_get_current_radar_detect_flags(pmadapter) != + radar_det_state_new) ? + MTRUE : + MFALSE; + if (ret) + *pnew_state = radar_det_state_new; + + LEAVE(); + return ret; +} + +/** + * @brief generate the channel center frequency index + * + * @param channel_num channel number + * + * @return frenquency index + */ +static t_u8 wlan_11h_get_channel_freq_idx(t_u8 channel_num) +{ + t_u8 index; + t_u8 center_freq[] = {42, 58, 106, 122, 138, 155}; + t_u8 chan_idx, ret = 0; + + chan_idx = channel_num - 100; + + for (index = 0; index < sizeof(center_freq); index++) { + if ((chan_idx >= (center_freq[index] - 6)) && + (chan_idx <= (center_freq[index] + 6))) { + ret = center_freq[index]; + break; + } + } + + return ret; +} + +/** + * @brief Prepare ioctl for add/remove CHAN_SW IE - RADAR_DETECTED event + * handling + * + * @param pmadapter Pointer to mlan_adapter + * @param pioctl_req Pointer to completed mlan_ioctl_req (allocated + * inside) + * @param ppcust_chansw_ie Poniter to customer ie + * @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(mlan_adapter *pmadapter, + mlan_ioctl_req **ppioctl_req, + 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; + IEEEtypes_Header_t *pChanSwWrap_ie = MNULL; + IEEEtypes_WideBWChanSwitch_t *pbwchansw_ie = MNULL; + IEEEtypes_VhtTpcEnvelope_t *pvhttpcEnv_ie = MNULL; + t_u8 index; + mlan_private *pmpriv = MNULL; + + ENTER(); + + if (pmadapter == MNULL || ppioctl_req == MNULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* allocate buffer for mlan_ioctl_req and mlan_ds_misc_cfg */ + /* FYI - will be freed as part of cmd_response handler */ + ret = pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, + sizeof(mlan_ioctl_req) + sizeof(mlan_ds_misc_cfg), MLAN_MEM_DEF, + (t_u8 **)&pioctl_req); + if ((ret != MLAN_STATUS_SUCCESS) || !pioctl_req) { + PRINTM(MERROR, "%s(): Could not allocate ioctl req\n", + __func__); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pds_misc_cfg = (mlan_ds_misc_cfg *)((t_u8 *)pioctl_req + + sizeof(mlan_ioctl_req)); + + /* prepare mlan_ioctl_req */ + memset(pmadapter, pioctl_req, 0x00, sizeof(mlan_ioctl_req)); + pioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + pioctl_req->action = MLAN_ACT_SET; + pioctl_req->pbuf = (t_u8 *)pds_misc_cfg; + pioctl_req->buf_len = sizeof(mlan_ds_misc_cfg); + + /* prepare mlan_ds_misc_cfg */ + memset(pmadapter, pds_misc_cfg, 0x00, sizeof(mlan_ds_misc_cfg)); + pds_misc_cfg->sub_command = MLAN_OID_MISC_CUSTOM_IE; + pds_misc_cfg->param.cust_ie.type = TLV_TYPE_MGMT_IE; + pds_misc_cfg->param.cust_ie.len = (sizeof(custom_ie) - MAX_IE_SIZE); + + /* configure custom_ie api settings */ + pcust_chansw_ie = + (custom_ie *)&pds_misc_cfg->param.cust_ie.ie_data_list[0]; + pcust_chansw_ie->ie_index = 0xffff; /* Auto index */ + pcust_chansw_ie->ie_length = sizeof(IEEEtypes_ChanSwitchAnn_t); + pcust_chansw_ie->mgmt_subtype_mask = + (is_adding_ie) ? MBIT(8) | MBIT(5) /* add IE for BEACON | + PROBE_RSP */ + : + 0; /* remove IE */ + + /* prepare CHAN_SW IE inside ioctl */ + pchansw_ie = (IEEEtypes_ChanSwitchAnn_t *)pcust_chansw_ie->ie_buffer; + pchansw_ie->element_id = CHANNEL_SWITCH_ANN; + pchansw_ie->len = + sizeof(IEEEtypes_ChanSwitchAnn_t) - sizeof(IEEEtypes_Header_t); + pchansw_ie->chan_switch_mode = 1; /* STA should not transmit */ + pchansw_ie->new_channel_num = pmadapter->state_rdh.new_channel; + + pchansw_ie->chan_switch_count = pmadapter->dfs_cs_count; + PRINTM(MCMD_D, "New Channel = %d Channel switch count = %d\n", + pmadapter->state_rdh.new_channel, pchansw_ie->chan_switch_count); + + for (index = 0; index < pmadapter->state_rdh.priv_list_count; index++) { + pmpriv = pmadapter->state_rdh.priv_list[index]; + /*find the first AP interface*/ + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + if (pmpriv->is_11ac_enabled) { + pChanSwWrap_ie = + (IEEEtypes_Header_t + *)((t_u8 *)pchansw_ie + + sizeof(IEEEtypes_ChanSwitchAnn_t)); + pChanSwWrap_ie->element_id = EXT_POWER_CONSTR; + /*will have multiple sub IEs*/ + pChanSwWrap_ie->len = 0; + + /* prepare the Wide Bandwidth Channel Switch IE + * Channel Switch IE */ + pbwchansw_ie = + (IEEEtypes_WideBWChanSwitch_t + *)((t_u8 *)pChanSwWrap_ie + + sizeof(IEEEtypes_Header_t)); + pbwchansw_ie->ieee_hdr.element_id = + BW_CHANNEL_SWITCH; + pbwchansw_ie->ieee_hdr.len = + sizeof(IEEEtypes_WideBWChanSwitch_t) - + sizeof(IEEEtypes_Header_t); + /*fix 80MHZ now*/ + pbwchansw_ie->new_channel_width = + VHT_OPER_CHWD_80MHZ; + pbwchansw_ie->new_channel_center_freq0 = + wlan_11h_get_channel_freq_idx( + pmadapter->state_rdh + .new_channel); + pbwchansw_ie->new_channel_center_freq1 = + wlan_11h_get_channel_freq_idx( + pmadapter->state_rdh + .new_channel); + pChanSwWrap_ie->len += + sizeof(IEEEtypes_WideBWChanSwitch_t); + + /*prepare the VHT Transmit Power Envelope IE*/ + pvhttpcEnv_ie = + (IEEEtypes_VhtTpcEnvelope_t + *)((t_u8 *)pChanSwWrap_ie + + sizeof(IEEEtypes_Header_t) + + sizeof(IEEEtypes_WideBWChanSwitch_t)); + pvhttpcEnv_ie->ieee_hdr.element_id = + VHT_TX_POWER_ENV; + pvhttpcEnv_ie->ieee_hdr.len = + sizeof(IEEEtypes_VhtTpcEnvelope_t) - + sizeof(IEEEtypes_Header_t); + /* Local Max TX Power Count= 3, + * Local TX Power Unit Inter=EIP(0) */ + pvhttpcEnv_ie->tpc_info = 3; + pvhttpcEnv_ie->local_max_tp_20mhz = 0xff; + pvhttpcEnv_ie->local_max_tp_40mhz = 0xff; + pvhttpcEnv_ie->local_max_tp_80mhz = 0xff; + pChanSwWrap_ie->len += + sizeof(IEEEtypes_VhtTpcEnvelope_t); + + pcust_chansw_ie->ie_length += + sizeof(IEEEtypes_WideBWChanSwitch_t) + + sizeof(IEEEtypes_VhtTpcEnvelope_t) + + sizeof(IEEEtypes_Header_t); + + PRINTM(MINFO, + "Append Wide Bandwidth Channel Switch IE\n"); + break; + } + } + } + + pds_misc_cfg->param.cust_ie.len += pcust_chansw_ie->ie_length; + DBG_HEXDUMP(MCMD_D, "11h: custom_ie containing CHAN_SW IE", + (t_u8 *)pcust_chansw_ie, pds_misc_cfg->param.cust_ie.len); + + /* assign output pointer before returning */ + *ppioctl_req = pioctl_req; + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef UAP_SUPPORT +/** Bits 2,3 of band config define the band width */ +#define UAP_BAND_WIDTH_MASK 0x0C + +/** + * @brief Check if start channel 165 is allowed to operate in + * previous uAP channel's band config + * + * @param start_chn Random Start channel choosen after radar detection + * @param uap_band_cfg Private driver uAP band configuration information + * structure + * + * @return MFALSE if the channel is not allowed in given band + */ +static t_bool wlan_11h_is_band_valid(t_u8 start_chn, Band_Config_t uap_band_cfg) +{ + /* if band width is not 20MHZ (either 40 or 80MHz) + * return MFALSE, 165 is not allowed in bands other than 20MHZ + */ + if (start_chn == 165 && (uap_band_cfg.chanWidth != CHAN_BW_20MHZ)) { + return MFALSE; + } + return MTRUE; +} + +/** + * @brief Retrieve a randomly selected starting channel if needed for 11h + * + * If 11h is enabled and 5GHz band is selected in band_config + * return a random channel in A band, else one from BG band. + * + * @param priv Private driver information structure + * @param uap_band_cfg Private driver information structure + * + * @return Starting channel + */ +static t_u8 wlan_11h_get_uap_start_channel(mlan_private *priv, + Band_Config_t uap_band_cfg) +{ + t_u8 start_chn; + mlan_adapter *adapter = priv->adapter; + t_u32 region; + t_u32 rand_entry; + region_chan_t *chn_tbl; + t_u8 rand_tries = 0; + + /*TODO: right now mostly a copy of wlan_11h_get_adhoc_start_channel. + * Improve to be more specfic to UAP, e.g. + * 1. take into account COUNTRY_CODE -> region_code + * 2. check domain_info for value channels + */ + ENTER(); + + /* + * Set start_chn to the Default. + * Used if 11h is disabled or the band + * does not require 11h support. + */ + start_chn = DEFAULT_AD_HOC_CHANNEL; + + /* + * Check that we are looking for a channel in the A Band + */ + if (uap_band_cfg.chanBand == BAND_5GHZ) { + /* + * Set default to the A Band default. + * Used if random selection fails + * or if 11h is not enabled + */ + start_chn = DEFAULT_AD_HOC_CHANNEL_A; + + /* + * Check that 11h is enabled in the driver + */ + if (wlan_11h_is_enabled(priv)) { + /* + * Search the region_channel tables for a channel table + * that is marked for the A Band. + */ + for (region = 0; (region < MAX_REGION_CHANNEL_NUM); + region++) { + chn_tbl = &adapter->region_channel[region]; + + /* Check if table is valid and marked for A Band + */ + if (chn_tbl->valid && + chn_tbl->region == adapter->region_code && + chn_tbl->band & BAND_A) { + /* + * Set the start channel. Get a random + * number and use it to pick an entry + * in the table between 0 and the number + * of channels in the table (NumCFP). + */ + rand_entry = wlan_11h_get_random_num( + adapter) % + chn_tbl->num_cfp; + start_chn = + (t_u8)chn_tbl->pcfp[rand_entry] + .channel; + /* Loop until a non-dfs channel is found + * with compatible band bounded by + * chn_tbl->num_cfp entries in the + * channel table + */ + while (((chn_tbl->pcfp[rand_entry] + .dynamic.flags & + NXP_CHANNEL_DISABLED) || + (wlan_11h_is_channel_under_nop( + adapter, start_chn) || + ((adapter->state_rdh.stage == + RDH_GET_INFO_CHANNEL) && + wlan_11h_radar_detect_required( + priv, start_chn)) || + !(wlan_11h_is_band_valid( + start_chn, + uap_band_cfg)))) && + (++rand_tries < + chn_tbl->num_cfp)) { + rand_entry++; + rand_entry = rand_entry % + chn_tbl->num_cfp; + start_chn = + (t_u8)chn_tbl + ->pcfp[rand_entry] + .channel; + PRINTM(MINFO, + "start chan=%d rand_entry=%d\n", + start_chn, rand_entry); + } + + if (rand_tries == chn_tbl->num_cfp) { + PRINTM(MERROR, + "Failed to get UAP start channel\n"); + start_chn = 0; + } + } + } + } + } + + PRINTM(MCMD_D, "11h: UAP Get Start Channel %d\n", start_chn); + LEAVE(); + return start_chn; +} +#endif /* UAP_SUPPORT */ + +#ifdef DEBUG_LEVEL1 +static const char *DFS_TS_REPR_STRINGS[] = {"", "NOP_start", "CAC_completed"}; +#endif + +/** + * @brief Search for a dfs timestamp in the list with desired channel. + * + * Assumes there will only be one timestamp per channel in the list. + * + * @param pmadapter Pointer to mlan_adapter + * @param channel Channel number + * + * @return Pointer to timestamp if found, or MNULL + */ +static wlan_dfs_timestamp_t * +wlan_11h_find_dfs_timestamp(mlan_adapter *pmadapter, t_u8 channel) +{ + wlan_dfs_timestamp_t *pts = MNULL, *pts_found = MNULL; + + ENTER(); + pts = (wlan_dfs_timestamp_t *)util_peek_list( + pmadapter->pmoal_handle, &pmadapter->state_dfs.dfs_ts_head, + MNULL, MNULL); + + while (pts && pts != (wlan_dfs_timestamp_t *)&pmadapter->state_dfs + .dfs_ts_head) { + PRINTM(MINFO, + "dfs_timestamp(@ %p) - chan=%d, repr=%d(%s)," + " time(sec.usec)=%lu.%06lu\n", + pts, pts->channel, pts->represents, + DFS_TS_REPR_STRINGS[pts->represents], pts->ts_sec, + pts->ts_usec); + + if (pts->channel == channel) { + pts_found = pts; + break; + } + pts = pts->pnext; + } + + LEAVE(); + return pts_found; +} + +/** + * @brief Removes dfs timestamp from list. + * + * @param pmadapter Pointer to mlan_adapter + * @param pdfs_ts Pointer to dfs_timestamp to remove + */ +static t_void wlan_11h_remove_dfs_timestamp(mlan_adapter *pmadapter, + wlan_dfs_timestamp_t *pdfs_ts) +{ + ENTER(); + /* dequeue and delete timestamp */ + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->state_dfs.dfs_ts_head, + (pmlan_linked_list)pdfs_ts, MNULL, MNULL); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pdfs_ts); + LEAVE(); +} + +/** + * @brief Add a dfs timestamp to the list + * + * Assumes there will only be one timestamp per channel in the list, + * and that timestamp modes (represents) are mutually exclusive. + * + * @param pmadapter Pointer to mlan_adapter + * @param repr Timestamp 'represents' value (see _dfs_timestamp_repr_e) + * @param channel Channel number + * + * @return Pointer to timestamp if found, or MNULL + */ +static mlan_status wlan_11h_add_dfs_timestamp(mlan_adapter *pmadapter, + t_u8 repr, t_u8 channel) +{ + wlan_dfs_timestamp_t *pdfs_ts = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + pdfs_ts = wlan_11h_find_dfs_timestamp(pmadapter, channel); + + if (!pdfs_ts) { + /* need to allocate new timestamp */ + ret = pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, sizeof(wlan_dfs_timestamp_t), + MLAN_MEM_DEF, (t_u8 **)&pdfs_ts); + if ((ret != MLAN_STATUS_SUCCESS) || !pdfs_ts) { + PRINTM(MERROR, "%s(): Could not allocate dfs_ts\n", + __func__); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(pmadapter, (t_u8 *)pdfs_ts, 0, + sizeof(wlan_dfs_timestamp_t)); + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->state_dfs.dfs_ts_head, + (pmlan_linked_list)pdfs_ts, MNULL, + MNULL); + pdfs_ts->channel = channel; + } + /* (else, use existing timestamp for channel; see assumptions above) */ + + /* update params */ + pmadapter->callbacks.moal_get_system_time( + pmadapter->pmoal_handle, &pdfs_ts->ts_sec, &pdfs_ts->ts_usec); + pdfs_ts->represents = repr; + + PRINTM(MCMD_D, + "11h: add/update dfs_timestamp - chan=%d, repr=%d(%s)," + " time(sec.usec)=%lu.%06lu\n", + pdfs_ts->channel, pdfs_ts->represents, + DFS_TS_REPR_STRINGS[pdfs_ts->represents], pdfs_ts->ts_sec, + pdfs_ts->ts_usec); + + LEAVE(); + return ret; +} + +/******************************************************** + Global functions +********************************************************/ + +/** + * @brief Return whether the device has activated master radar detection. + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if master radar detection is enabled in firmware + * - MFALSE otherwise + */ +t_bool wlan_11h_is_master_radar_det_active(mlan_private *priv) +{ + ENTER(); + LEAVE(); + return priv->adapter->state_11h.is_master_radar_det_active; +} + +/** + * @brief Configure master radar detection. + * Call wlan_11h_check_update_radar_det_state() afterwards + * to push this to firmware. + * + * @param priv Private driver information structure + * @param enable Whether to enable or disable master radar detection + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * + * @sa wlan_11h_check_update_radar_det_state + */ +mlan_status wlan_11h_config_master_radar_det(mlan_private *priv, t_bool enable) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + + /* Force disable master radar detection on in-AP interfaces */ + if (priv->adapter->dfs_repeater) + enable = MFALSE; + + ENTER(); + if (wlan_11h_is_dfs_master(priv) && + priv->adapter->init_para.dfs_master_radar_det_en) { + priv->adapter->state_11h.master_radar_det_enable_pending = + enable; + ret = MLAN_STATUS_SUCCESS; + } + + LEAVE(); + return ret; +} + +/** + * @brief Configure slave radar detection. + * Call wlan_11h_check_update_radar_det_state() afterwards + * to push this to firmware. + * + * @param priv Private driver information structure + * @param enable Whether to enable or disable slave radar detection + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * + * @sa wlan_11h_check_update_radar_det_state + */ +mlan_status wlan_11h_config_slave_radar_det(mlan_private *priv, t_bool enable) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + + /* Force disable radar detection on STA interfaces */ + if (priv->adapter->dfs_repeater) + enable = MFALSE; + + ENTER(); + if (wlan_11h_is_dfs_slave(priv) && + priv->adapter->init_para.dfs_slave_radar_det_en) { + priv->adapter->state_11h.slave_radar_det_enable_pending = + enable; + ret = MLAN_STATUS_SUCCESS; + } + LEAVE(); + return ret; +} + +/** + * @brief Checks all interfaces and determines if radar_detect flag states + * have/should be changed. If so, sends SNMP_MIB 11H command to FW. + * Call this function on any interface enable/disable/channel change. + * + * @param pmpriv Pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS (update or not) + * or MLAN_STATUS_FAILURE (cmd failure) + * + * @sa wlan_11h_check_radar_det_state + */ +mlan_status wlan_11h_check_update_radar_det_state(mlan_private *pmpriv) +{ + t_u32 new_radar_det_state = 0; + t_u32 mib_11h = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wlan_11h_check_radar_det_state(pmpriv->adapter, + &new_radar_det_state)) { + PRINTM(MCMD_D, "%s: radar_det_state being updated.\n", + __func__); + + mib_11h |= new_radar_det_state; + /* keep priv's existing 11h state */ + if (pmpriv->intf_state_11h.is_11h_active) + mib_11h |= ENABLE_11H_MASK; + + /* Send cmd to FW to enable/disable 11h function in firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, Dot11H_i, MNULL, + &mib_11h); + if (ret) + ret = MLAN_STATUS_FAILURE; + } + + /* updated state sent OR no change, thus no longer pending */ + pmpriv->adapter->state_11h.master_radar_det_enable_pending = MFALSE; + pmpriv->adapter->state_11h.slave_radar_det_enable_pending = MFALSE; + + LEAVE(); + return ret; +} + +/** + * @brief Query 11h firmware enabled state. + * + * Return whether the firmware currently has 11h extensions enabled + * + * @param priv Private driver information structure + * + * @return + * - MTRUE if 11h has been activated in the firmware + * - MFALSE otherwise + * + * @sa wlan_11h_activate + */ +t_bool wlan_11h_is_active(mlan_private *priv) +{ + ENTER(); + LEAVE(); + return priv->intf_state_11h.is_11h_active; +} + +/** + * @brief Enable the transmit interface and record the state. + * + * @param priv Private driver information structure + * + * @return N/A + */ +t_void wlan_11h_tx_enable(mlan_private *priv) +{ + ENTER(); + if (priv->intf_state_11h.tx_disabled) { + if (priv->media_connected == MTRUE) { + wlan_recv_event(priv, MLAN_EVENT_ID_FW_START_TX, MNULL); + priv->intf_state_11h.tx_disabled = MFALSE; + } + } + LEAVE(); +} + +/** + * @brief Disable the transmit interface and record the state. + * + * @param priv Private driver information structure + * + * @return N/A + */ +t_void wlan_11h_tx_disable(mlan_private *priv) +{ + ENTER(); + if (!priv->intf_state_11h.tx_disabled) { + if (priv->media_connected == MTRUE) { + priv->intf_state_11h.tx_disabled = MTRUE; + wlan_recv_event(priv, MLAN_EVENT_ID_FW_STOP_TX, MNULL); + } + } + LEAVE(); +} + +/** + * @brief Enable or Disable the 11h extensions in the firmware + * + * @param priv Private driver information structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param flag Enable 11h if MTRUE, disable otherwise + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11h_activate(mlan_private *priv, t_void *pioctl_buf, + t_bool flag) +{ + t_u32 enable = flag & ENABLE_11H_MASK; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + /* add bits for master/slave radar detect into enable. */ + enable |= wlan_11h_get_current_radar_detect_flags(priv->adapter); + + /* Whenever repeater mode is on make sure + * we do not enable master or slave radar det mode. + * HW will not detect radar in dfs_repeater mode. + */ + if (priv->adapter->dfs_repeater) { + enable &= ~(MASTER_RADAR_DET_MASK | SLAVE_RADAR_DET_MASK); + } + + /* + * Send cmd to FW to enable/disable 11h function in firmware + */ + ret = wlan_prepare_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, Dot11H_i, + (t_void *)pioctl_buf, &enable); + if (ret) + ret = MLAN_STATUS_FAILURE; + else + /* Set boolean flag in driver 11h state */ + priv->intf_state_11h.is_11h_active = flag; + + PRINTM(MINFO, "11h: %s\n", flag ? "Activate" : "Deactivate"); + + LEAVE(); + return ret; +} + +/** + * @brief Initialize the 11h parameters and enable 11h when starting an IBSS + * + * @param adapter mlan_adapter structure + * + * @return N/A + */ +t_void wlan_11h_init(mlan_adapter *adapter) +{ + wlan_11h_device_state_t *pstate_11h = &adapter->state_11h; + IEEEtypes_Quiet_t *pquiet = &adapter->state_11h.quiet_ie; + wlan_dfs_device_state_t *pstate_dfs = &adapter->state_dfs; + wlan_radar_det_hndlg_state_t *pstate_rdh = &adapter->state_rdh; + wlan_dfs_testing_settings_t *pdfs_test = &adapter->dfs_test_params; + + ENTER(); + + /* Initialize 11H struct */ + pstate_11h->usr_def_power_constraint = WLAN_11H_TPC_POWERCONSTRAINT; + pstate_11h->min_tx_power_capability = WLAN_11H_TPC_POWERCAPABILITY_MIN; + pstate_11h->max_tx_power_capability = WLAN_11H_TPC_POWERCAPABILITY_MAX; + + pstate_11h->recvd_chanswann_event = MFALSE; + pstate_11h->master_radar_det_enable_pending = MFALSE; + pstate_11h->slave_radar_det_enable_pending = MFALSE; + pstate_11h->is_master_radar_det_active = MFALSE; + pstate_11h->is_slave_radar_det_active = MFALSE; + + /*Initialize quiet_ie*/ + memset(adapter, pquiet, 0, sizeof(IEEEtypes_Quiet_t)); + pquiet->element_id = QUIET; + pquiet->len = + (sizeof(pquiet->quiet_count) + sizeof(pquiet->quiet_period) + + sizeof(pquiet->quiet_duration) + sizeof(pquiet->quiet_offset)); + + /* Initialize DFS struct */ + pstate_dfs->dfs_check_pending = MFALSE; + pstate_dfs->dfs_radar_found = MFALSE; + pstate_dfs->dfs_check_channel = 0; + pstate_dfs->dfs_report_time_sec = 0; + util_init_list((pmlan_linked_list)&pstate_dfs->dfs_ts_head); + + /* Initialize RDH struct */ + pstate_rdh->stage = RDH_OFF; + pstate_rdh->priv_list_count = 0; + pstate_rdh->priv_curr_idx = 0; + pstate_rdh->curr_channel = 0; + pstate_rdh->new_channel = 0; + memset(adapter, &(pstate_rdh->uap_band_cfg), 0, + sizeof(pstate_rdh->uap_band_cfg)); + pstate_rdh->max_bcn_dtim_ms = 0; + memset(adapter, pstate_rdh->priv_list, 0, + sizeof(pstate_rdh->priv_list)); + + /* Initialize dfs channel switch count */ +#define DFS_CS_COUNT 5 + adapter->dfs_cs_count = DFS_CS_COUNT; + + /* 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; + pdfs_test->cac_restart = 0; + pdfs_test->millisec_dwell_time = 0; + adapter->dfs53cfg = adapter->init_para.dfs53cfg; + + LEAVE(); +} + +/** + * @brief Cleanup for the 11h parameters that allocated memory, etc. + * + * @param adapter mlan_adapter structure + * + * @return N/A + */ +t_void wlan_11h_cleanup(mlan_adapter *adapter) +{ + wlan_dfs_device_state_t *pstate_dfs = &adapter->state_dfs; + wlan_dfs_timestamp_t *pdfs_ts; + + ENTER(); + + /* cleanup dfs_timestamp list */ + pdfs_ts = (wlan_dfs_timestamp_t *)util_peek_list( + adapter->pmoal_handle, &pstate_dfs->dfs_ts_head, MNULL, MNULL); + while (pdfs_ts) { + util_unlink_list(adapter->pmoal_handle, + &pstate_dfs->dfs_ts_head, + (pmlan_linked_list)pdfs_ts, MNULL, MNULL); + adapter->callbacks.moal_mfree(adapter->pmoal_handle, + (t_u8 *)pdfs_ts); + + pdfs_ts = (wlan_dfs_timestamp_t *)util_peek_list( + adapter->pmoal_handle, &pstate_dfs->dfs_ts_head, MNULL, + MNULL); + } + + LEAVE(); +} + +/** + * @brief Initialize the 11h parameters and enable 11h when starting an IBSS + * + * @param pmpriv Pointer to mlan_private structure + * + * @return N/A + */ +t_void wlan_11h_priv_init(mlan_private *pmpriv) +{ + wlan_11h_interface_state_t *pistate_11h = &pmpriv->intf_state_11h; + + ENTER(); + + pistate_11h->is_11h_enabled = MTRUE; + pistate_11h->is_11h_active = MFALSE; + pistate_11h->adhoc_auto_sel_chan = MTRUE; + pistate_11h->tx_disabled = MFALSE; + pistate_11h->dfs_slave_csa_chan = 0; + pistate_11h->dfs_slave_csa_expire_at_sec = 0; + + LEAVE(); +} + +/** + * @brief Retrieve a randomly selected starting channel if needed for 11h + * + * If 11h is enabled and an A-Band channel start band preference + * configured in the driver, the start channel must be random in order + * to meet with + * + * @param priv Private driver information structure + * + * @return Starting channel + */ +t_u8 wlan_11h_get_adhoc_start_channel(mlan_private *priv) +{ + t_u8 start_chn; + mlan_adapter *adapter = priv->adapter; + t_u32 region; + t_u32 rand_entry; + region_chan_t *chn_tbl; + t_u8 rand_tries = 0; + + ENTER(); + + /* + * Set start_chn to the Default. Used if 11h is disabled or the band + * does not require 11h support. + */ + start_chn = DEFAULT_AD_HOC_CHANNEL; + + /* + * Check that we are looking for a channel in the A Band + */ + if ((adapter->adhoc_start_band & BAND_A)) { + /* + * Set default to the A Band default. + * Used if random selection fails + * or if 11h is not enabled + */ + start_chn = DEFAULT_AD_HOC_CHANNEL_A; + + /* + * Check that 11h is enabled in the driver + */ + if (wlan_11h_is_enabled(priv)) { + /* + * Search the region_channel tables for a channel table + * that is marked for the A Band. + */ + for (region = 0; (region < MAX_REGION_CHANNEL_NUM); + region++) { + chn_tbl = &adapter->region_channel[region]; + + /* Check if table is valid and marked for A Band + */ + if (chn_tbl->valid && + chn_tbl->region == adapter->region_code && + chn_tbl->band & BAND_A) { + /* + * Set the start channel. Get a random + * number and use it to pick an entry + * in the table between 0 and the number + * of channels in the table (NumCFP). + */ + do { + rand_entry = + wlan_11h_get_random_num( + adapter) % + chn_tbl->num_cfp; + start_chn = + (t_u8)chn_tbl + ->pcfp[rand_entry] + .channel; + } while ( + (wlan_11h_is_channel_under_nop( + adapter, start_chn) || + ((adapter->state_rdh.stage == + RDH_GET_INFO_CHANNEL) && + wlan_11h_radar_detect_required( + priv, start_chn))) && + (++rand_tries < + MAX_RANDOM_CHANNEL_RETRIES)); + } + } + } + } + + PRINTM(MINFO, "11h: %s: AdHoc Channel set to %u\n", + wlan_11h_is_enabled(priv) ? "Enabled" : "Disabled", start_chn); + + LEAVE(); + return start_chn; +} + +/** + * @brief Retrieve channel closed for operation by Channel Switch Announcement + * + * After receiving CSA, we must not transmit in any form on the original + * channel for a certain duration. This checks the time, and returns + * the channel if valid. + * + * @param priv Private driver information structure + * + * @return Closed channel, else 0 + */ +t_u8 wlan_11h_get_csa_closed_channel(mlan_private *priv) +{ + t_u32 sec, usec; + + ENTER(); + + if (!priv->intf_state_11h.dfs_slave_csa_chan) { + LEAVE(); + return 0; + } + + /* have csa channel, check if expired or not */ + priv->adapter->callbacks.moal_get_system_time( + priv->adapter->pmoal_handle, &sec, &usec); + if (sec > priv->intf_state_11h.dfs_slave_csa_expire_at_sec) { + /* expired: remove channel from blacklist table, and clear vars + */ + wlan_set_chan_blacklist(priv, BAND_A, + priv->intf_state_11h.dfs_slave_csa_chan, + MFALSE); + priv->intf_state_11h.dfs_slave_csa_chan = 0; + priv->intf_state_11h.dfs_slave_csa_expire_at_sec = 0; + } + + LEAVE(); + return priv->intf_state_11h.dfs_slave_csa_chan; +} + +/** + * @brief Check if the current region's regulations require the input channel + * to be scanned for radar. + * + * Based on statically defined requirements for sub-bands per regulatory + * agency requirements. + * + * Used in adhoc start to determine if channel availability check is required + * + * @param priv Private driver information structure + * @param channel Channel to determine radar detection requirements + * + * @return + * - MTRUE if radar detection is required + * - MFALSE otherwise + */ +/** @sa wlan_11h_issue_radar_detect + */ +t_bool wlan_11h_radar_detect_required(mlan_private *priv, t_u8 channel) +{ + t_bool required = MFALSE; + + ENTER(); + + /* + * No checks for 11h or measurement code being enabled is placed here + * since regulatory requirements exist whether we support them or not. + */ + + required = wlan_get_cfp_radar_detect(priv, channel); + + if (!priv->adapter->region_code) { + PRINTM(MINFO, + "11h: Radar detection in CFP code BG:%#x " + ", A:%#x " + "is %srequired for channel %d\n", + priv->adapter->cfp_code_bg, priv->adapter->cfp_code_a, + (required ? "" : "not "), channel); + } else + PRINTM(MINFO, + "11h: Radar detection in region %#02x " + "is %srequired for channel %d\n", + priv->adapter->region_code, (required ? "" : "not "), + channel); + + if (required == MTRUE && priv->media_connected == MTRUE && + priv->curr_bss_params.bss_descriptor.channel == channel) { + required = MFALSE; + + PRINTM(MINFO, "11h: Radar detection not required. " + "Already operating on the channel\n"); + } + + LEAVE(); + return required; +} + +t_s32 wlan_11h_cancel_radar_detect(mlan_private *priv) +{ + t_s32 ret; + HostCmd_DS_CHAN_RPT_REQ chan_rpt_req; + memset(priv->adapter, &chan_rpt_req, 0x00, sizeof(chan_rpt_req)); + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, (t_void *)MNULL, + (t_void *)&chan_rpt_req); + return ret; +} + +/** + * @brief Perform a radar measurement if required on given channel + * + * Check to see if the provided channel requires a channel availability + * check (60 second radar detection measurement). If required, perform + * measurement, stalling calling thread until the measurement completes + * and then report result. + * + * Used when starting an adhoc or AP network. + * + * @param priv Private driver information structure + * @param pioctl_req Pointer to IOCTL request buffer + * @param channel Channel on which to perform radar measurement + * @param bandcfg Channel Band config structure + * + * @return + * - MTRUE if radar measurement request was successfully issued + * - MFALSE if radar detection is not required + * - < 0 for error during radar detection (if performed) + * + * @sa wlan_11h_radar_detect_required + */ +t_s32 wlan_11h_issue_radar_detect(mlan_private *priv, + pmlan_ioctl_req pioctl_req, t_u8 channel, + Band_Config_t bandcfg) +{ + t_s32 ret; + HostCmd_DS_CHAN_RPT_REQ chan_rpt_req; + mlan_adapter *pmadapter = priv->adapter; + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + + ENTER(); + + ret = wlan_11h_radar_detect_required(priv, channel); + if (ret) { + /* Prepare and issue CMD_CHAN_RPT_REQ. */ + memset(priv->adapter, &chan_rpt_req, 0x00, + sizeof(chan_rpt_req)); + + chan_rpt_req.chan_desc.startFreq = START_FREQ_11A_BAND; + + if (pmadapter->chanrpt_param_bandcfg) { + chan_rpt_req.chan_desc.bandcfg = bandcfg; + } else { + *((t_u8 *)&chan_rpt_req.chan_desc.bandcfg) = + (t_u8)bandcfg.chanWidth; + } + + chan_rpt_req.chan_desc.chanNum = channel; + chan_rpt_req.millisec_dwell_time = + WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION; + + /* ETSI new requirement for ch 120, 124 and 128 */ + if (wlan_is_etsi_country(pmadapter, pmadapter->country_code)) { + if (channel == 120 || channel == 124 || + channel == 128) { + chan_rpt_req.millisec_dwell_time = + WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION * + 10; + } + if (channel == 116 && + ((bandcfg.chanWidth == CHAN_BW_40MHZ) || + (bandcfg.chanWidth == CHAN_BW_80MHZ))) { + chan_rpt_req.millisec_dwell_time = + WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION * + 10; + } + } + + /* Save dwell time information to be used later in moal */ + if (pioctl_req) { + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + if (!ds_11hcfg->param.chan_rpt_req.host_based) { + ds_11hcfg->param.chan_rpt_req + .millisec_dwell_time = + chan_rpt_req.millisec_dwell_time; + } + } + + 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; + } + if (priv->adapter->dfs_test_params.cac_restart) { + priv->adapter->dfs_test_params.chan = + chan_rpt_req.chan_desc.chanNum; + if (chan_rpt_req.millisec_dwell_time) + priv->adapter->dfs_test_params + .millisec_dwell_time = + chan_rpt_req.millisec_dwell_time; + else + chan_rpt_req.millisec_dwell_time = + priv->adapter->dfs_test_params + .millisec_dwell_time; + memcpy_ext(priv->adapter, + &priv->adapter->dfs_test_params.bandcfg, + &bandcfg, sizeof(bandcfg), sizeof(bandcfg)); + } + PRINTM(MMSG, + "11h: issuing DFS Radar check for channel=%d." + " Please wait for response...\n", + channel); + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, + (t_void *)&chan_rpt_req); + } + + LEAVE(); + return ret; +} + +/** + * @brief Checks if a radar measurement was performed on channel, + * and if so, whether radar was detected on it. + * + * Used when starting an adhoc network. + * + * @param priv Private driver information structure + * @param chan Channel to check upon + * + * @return + * - MLAN_STATUS_SUCCESS if no radar on channel + * - MLAN_STATUS_FAILURE if radar was found on channel + * - (TBD??) MLAN_STATUS_PENDING if radar report NEEDS TO BE REISSUED + * + * @sa wlan_11h_issue_radar_detect + * @sa wlan_11h_process_start + */ +mlan_status wlan_11h_check_chan_report(mlan_private *priv, t_u8 chan) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs; + t_u32 sec, usec; + + ENTER(); + + /* check report we hold is valid or not */ + priv->adapter->callbacks.moal_get_system_time( + priv->adapter->pmoal_handle, &sec, &usec); + + PRINTM(MINFO, "11h: %s()\n", __func__); + PRINTM(MINFO, "- sec_now=%d, sec_report=%d.\n", sec, + pstate_dfs->dfs_report_time_sec); + PRINTM(MINFO, "- rpt_channel=%d, rpt_radar=%d.\n", + pstate_dfs->dfs_check_channel, pstate_dfs->dfs_radar_found); + + if ((!pstate_dfs->dfs_check_pending) && + (chan == pstate_dfs->dfs_check_channel) && + ((sec - pstate_dfs->dfs_report_time_sec) < + MAX_DFS_REPORT_USABLE_AGE_SEC)) { + /* valid and not out-dated, check if radar */ + if (pstate_dfs->dfs_radar_found) { + PRINTM(MMSG, "Radar was detected on channel %d.\n", + chan); + ret = MLAN_STATUS_FAILURE; + } + } else { + /* When Cache is not valid. This is required during extending + * cache validity during bss_stop + */ + pstate_dfs->dfs_check_channel = 0; + + /*TODO: reissue report request if not pending. + * BUT HOW to make the code wait for it??? + * For now, just fail since we don't have the info. */ + + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} + +/** + * @brief Process an TLV buffer for a pending BSS Adhoc start command. + * + * Activate 11h functionality in the firmware if driver has is enabled + * for 11h (configured by the application via IOCTL). + * + * @param priv Private driver information structure + * @param ppbuffer Output parameter: Pointer to the TLV output buffer, + * modified on return to point after the appended 11h TLVs + * @param pcap_info Pointer to the capability info for the BSS to join + * @param channel Channel on which we are starting the IBSS + * @param p11h_bss_info Input/Output parameter: Pointer to the 11h BSS + * information for this network that we are establishing. + * 11h sensed flag set on output if warranted. + * + * @return + * - MLAN_STATUS_SUCCESS if 11h is disabled + * - Integer number of bytes appended to the TLV output buffer (ppbuffer) + * - < 0 for error (e.g. radar detected on channel) + */ +t_s32 wlan_11h_process_start(mlan_private *priv, t_u8 **ppbuffer, + IEEEtypes_CapInfo_t *pcap_info, t_u32 channel, + wlan_11h_bss_info_t *p11h_bss_info) +{ + mlan_adapter *adapter = priv->adapter; + t_s32 ret = MLAN_STATUS_SUCCESS; + t_bool is_dfs_chan = MFALSE; + + ENTER(); + if (wlan_11h_is_enabled(priv) && + ((adapter->adhoc_start_band & BAND_A))) { + if (!wlan_fw_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_fw_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_ext(priv->adapter, priv->adapter->curr_cmd->pdata_buf, + &resp->params.tpc_req, + sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ), + sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ)); + break; + + case HostCmd_CMD_802_11_TPC_INFO: + HEXDUMP("11h: TPC INFO Rsp Data:", (t_u8 *)resp, + (t_u32)resp->size); + break; + + case HostCmd_CMD_802_11_CHAN_SW_ANN: + PRINTM(MINFO, "11h: Ret ChSwAnn: Sz=%u, Seq=%u, Ret=%u\n", + resp->size, resp->seq_num, resp->result); + break; + + case HostCmd_CMD_CHAN_REPORT_REQUEST: + priv->adapter->state_dfs.dfs_check_priv = priv; + priv->adapter->state_dfs.dfs_check_pending = MTRUE; + + if (resp->params.chan_rpt_req.millisec_dwell_time == 0) { + /* from wlan_11h_ioctl_dfs_chan_report */ + priv->adapter->state_dfs.dfs_check_pending = MFALSE; + priv->adapter->state_dfs.dfs_check_priv = MNULL; + priv->adapter->state_dfs.dfs_check_channel = 0; + PRINTM(MINFO, "11h: Cancelling Chan Report \n"); + } else { + PRINTM(MERROR, + "11h: Ret ChanRptReq. Set dfs_check_pending and wait" + " for EVENT_CHANNEL_REPORT.\n"); + } + + break; + + default: + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief Process an element from a scan response, copy relevant info for 11h + * + * @param pmadapter Pointer to mlan_adapter + * @param p11h_bss_info Output parameter: Pointer to the 11h BSS information + * for the network that is being processed + * @param pelement Pointer to the current IE we are inspecting for 11h + * relevance + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11h_process_bss_elem(mlan_adapter *pmadapter, + wlan_11h_bss_info_t *p11h_bss_info, + const t_u8 *pelement) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 element_len = *((t_u8 *)pelement + 1); + + ENTER(); + switch (*pelement) { + case POWER_CONSTRAINT: + PRINTM(MINFO, "11h: Power Constraint IE Found\n"); + p11h_bss_info->sensed_11h = MTRUE; + memcpy_ext(pmadapter, &p11h_bss_info->power_constraint, + pelement, 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_ext(pmadapter, &p11h_bss_info->power_capability, + pelement, 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_ext(pmadapter, &p11h_bss_info->tpc_report, pelement, + 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_ext(pmadapter, &p11h_bss_info->chan_switch_ann, pelement, + 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_ext(pmadapter, &p11h_bss_info->quiet, pelement, + 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_ext(pmadapter, &p11h_bss_info->ibss_dfs, pelement, + element_len + sizeof(IEEEtypes_Header_t), + sizeof(IEEEtypes_IBSS_DFS_t)); + p11h_bss_info->ibss_dfs.len = + MIN(element_len, (sizeof(IEEEtypes_IBSS_DFS_t) - + sizeof(IEEEtypes_Header_t))); + break; + + case SUPPORTED_CHANNELS: + case TPC_REQUEST: + /* + * These elements are not in beacons/probe responses. + * Included here to cover set of enumerated 11h elements. + */ + break; + + default: + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief Driver handling for CHANNEL_SWITCH_ANN event + * + * @param priv Pointer to mlan_private + * + * @return MLAN_STATUS_SUCCESS, MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +mlan_status wlan_11h_handle_event_chanswann(mlan_private *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef STA_SUPPORT + mlan_deauth_param deauth_param; +#endif + t_u32 sec, usec; +#ifdef UAP_SUPPORT + mlan_adapter *pmadapter = priv->adapter; + int i; + t_u8 radar_detected = MFALSE; + mlan_private *pmpriv = MNULL; +#endif + + ENTER(); +#ifdef UAP_SUPPORT + if (priv->adapter->state_11h.is_master_radar_det_active) { + for (i = 0; i < MIN(pmadapter->priv_num, MLAN_MAX_BSS_NUM); + i++) { + if (pmadapter->priv[i] && + (pmadapter->priv[i]->bss_role == + MLAN_BSS_ROLE_UAP) && + pmadapter->priv[i]->uap_bss_started && + (priv->curr_bss_params.bss_descriptor.channel == + pmadapter->priv[i]->uap_channel)) { + PRINTM(MCMND, + "Receive channel switch Ann event on uap_channel=%d\n", + pmadapter->priv[i]->uap_channel); + radar_detected = MTRUE; + pmpriv = pmadapter->priv[i]; + break; + } + } + if (radar_detected) { + if (!pmpriv->intf_state_11h.is_11h_host) { + if (pmadapter->state_rdh.stage == RDH_OFF) { + pmadapter->state_rdh.stage = + RDH_CHK_INTFS; + wlan_11h_radar_detected_handling( + pmadapter, pmpriv); + if (pmpriv->uap_host_based) + wlan_recv_event( + pmpriv, + MLAN_EVENT_ID_FW_RADAR_DETECTED, + MNULL); + } else { + PRINTM(MEVENT, + "Ignore Event Radar Detected - handling already in progress.\n"); + } + } else { + if (pmpriv->adapter->dfs_test_params + .no_channel_change_on_radar || + pmpriv->adapter->dfs_test_params + .fixed_new_channel_on_radar) { + if (pmadapter->state_rdh.stage == + RDH_OFF || + pmadapter->state_rdh.stage == + RDH_SET_CUSTOM_IE) { + pmadapter->state_rdh.stage = + RDH_CHK_INTFS; + wlan_11h_radar_detected_handling( + pmadapter, pmpriv); + } else + PRINTM(MEVENT, + "Ignore Event Radar Detected - handling already in progress.\n"); + } else { + pmpriv->intf_state_11h.tx_disabled = + MTRUE; + wlan_recv_event( + pmpriv, + MLAN_EVENT_ID_FW_RADAR_DETECTED, + MNULL); + } + } + } + } + if (pmadapter->ecsa_enable) { + t_u8 stop_tx = *(t_u8 *)pmadapter->event_body; + if (stop_tx) + pmadapter->state_rdh.tx_block = MTRUE; + LEAVE(); + return ret; + } +#endif + priv->adapter->state_11h.recvd_chanswann_event = MTRUE; + + /* unlikely: clean up previous csa if still on-going */ + if (priv->intf_state_11h.dfs_slave_csa_chan) { + wlan_set_chan_blacklist(priv, BAND_A, + priv->intf_state_11h.dfs_slave_csa_chan, + MFALSE); + } + + /* record channel and time of occurence */ + priv->intf_state_11h.dfs_slave_csa_chan = + priv->curr_bss_params.bss_descriptor.channel; + priv->adapter->callbacks.moal_get_system_time( + priv->adapter->pmoal_handle, &sec, &usec); + priv->intf_state_11h.dfs_slave_csa_expire_at_sec = + sec + DFS_CHAN_MOVE_TIME; + +#ifdef STA_SUPPORT + /* do directed deauth. recvd_chanswann_event flag will cause different + * reason code */ + PRINTM(MINFO, "11h: handle_event_chanswann() - sending deauth\n"); + memcpy_ext(priv->adapter, deauth_param.mac_addr, + &priv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + deauth_param.reason_code = DEF_DEAUTH_REASON_CODE; + ret = wlan_disconnect(priv, MNULL, &deauth_param); + + /* clear region table so next scan will be all passive */ + PRINTM(MINFO, "11h: handle_event_chanswann() - clear region table\n"); + wlan_11d_clear_parsedtable(priv); + + /* add channel to blacklist table */ + PRINTM(MINFO, + "11h: handle_event_chanswann() - scan blacklist csa channel\n"); + wlan_set_chan_blacklist(priv, BAND_A, + priv->intf_state_11h.dfs_slave_csa_chan, MTRUE); +#endif + + priv->adapter->state_11h.recvd_chanswann_event = MFALSE; + LEAVE(); + return ret; +} + +/** + * @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; + dfs_test->usr_cac_restart = pdfs_test_params->cac_restart; + } 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; + pdfs_test_params->cac_restart = dfs_test->usr_cac_restart; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief 802.11h IOCTL to handle channel NOP status check + * @brief If given channel is under NOP, return a new non-dfs + * @brief channel + * + * @param pmadapter Pointer to mlan_adapter + * @param pioctl_req Pointer to mlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11h_ioctl_get_channel_nop_info(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + t_s32 ret = MLAN_STATUS_FAILURE; + mlan_ds_11h_chan_nop_info *ch_nop_info = MNULL; + + ENTER(); + + if (pioctl_req) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + ch_nop_info = &ds_11hcfg->param.ch_nop_info; + + if (pioctl_req->action == MLAN_ACT_GET) { + ch_nop_info->chan_under_nop = + wlan_11h_is_channel_under_nop( + pmadapter, ch_nop_info->curr_chan); + if (ch_nop_info->chan_under_nop) { + wlan_11h_switch_non_dfs_chan( + pmpriv, &ch_nop_info->new_chan.channel); + if (ch_nop_info->chan_width == CHAN_BW_80MHZ || + ch_nop_info->chan_width == CHAN_BW_40MHZ) + wlan_11h_update_bandcfg( + &ch_nop_info->new_chan.bandcfg, + ch_nop_info->new_chan.channel); + if (ch_nop_info->chan_width == CHAN_BW_80MHZ) + ch_nop_info->new_chan.center_chan = + wlan_get_center_freq_idx( + pmpriv, BAND_AAC, + ch_nop_info->new_chan + .channel, + ch_nop_info->chan_width); + } + } + ret = MLAN_STATUS_SUCCESS; + } + + LEAVE(); + return ret; +} + +/** + * @brief 802.11h DFS Channel Switch Count Configuration + * + * @param pmadapter Pointer to mlan_adapter + * @param pioctl_req Pointer to mlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11h_ioctl_chan_switch_count(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + t_s32 ret = MLAN_STATUS_FAILURE; + + ENTER(); + + if (pioctl_req) { + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + ds_11hcfg->param.cs_count = pmadapter->dfs_cs_count; + } else { + pmadapter->dfs_cs_count = ds_11hcfg->param.cs_count; + } + ret = MLAN_STATUS_SUCCESS; + } + + LEAVE(); + return ret; +} + +/** + * @brief 802.11h DFS chan report + * + * @param priv Pointer to mlan_private + * @param pioctl_req Pointer to mlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11h_ioctl_dfs_chan_report(mlan_private *priv, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + HostCmd_DS_CHAN_RPT_REQ *chan_rpt_req = MNULL; + t_s32 ret; + + ENTER(); + + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + + chan_rpt_req = + (HostCmd_DS_CHAN_RPT_REQ *)&ds_11hcfg->param.chan_rpt_req; + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + (t_void *)chan_rpt_req); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} +/** + * @brief Check if channel is under NOP (Non-Occupancy Period) + * If so, the channel should not be used until the period expires. + * + * @param pmadapter Pointer to mlan_adapter + * @param channel Channel number + * + * @return MTRUE or MFALSE + */ +t_bool wlan_11h_is_channel_under_nop(mlan_adapter *pmadapter, t_u8 channel) +{ + wlan_dfs_timestamp_t *pdfs_ts = MNULL; + t_u32 now_sec, now_usec; + t_bool ret = MFALSE; + ENTER(); + pdfs_ts = wlan_11h_find_dfs_timestamp(pmadapter, channel); + + if (pdfs_ts && (pdfs_ts->channel == channel) && + (pdfs_ts->represents == DFS_TS_REPR_NOP_START)) { + /* found NOP_start timestamp entry on channel */ + pmadapter->callbacks.moal_get_system_time( + pmadapter->pmoal_handle, &now_sec, &now_usec); + 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 { + 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, + t_u8 *radar_chan) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_CHAN_RPT_RSP *pchan_rpt_rsp; + MrvlIEtypes_Data_t *ptlv; + MeasRptBasicMap_t *pmeas_rpt_basic; + t_u8 *pbuffer; + t_s32 evt_len; + t_u16 tlv_len; + t_u32 sec, usec; + wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs; + t_u8 dfs_radar_found = MFALSE; + t_u8 dfs_check_channel = pstate_dfs->dfs_check_channel; + + ENTER(); + pchan_rpt_rsp = (HostCmd_DS_CHAN_RPT_RSP *)&pevent->event_buf; + DBG_HEXDUMP(MCMD_D, "11h: Event ChanRptReady (HostCmd_DS_CHAN_RPT_RSP)", + (t_u8 *)pchan_rpt_rsp, pevent->event_len); + + if (wlan_le32_to_cpu(pchan_rpt_rsp->cmd_result) == + MLAN_CMD_RESULT_SUCCESS) { + pbuffer = (t_u8 *)&pchan_rpt_rsp->tlv_buffer; + evt_len = pevent->event_len; + evt_len -= sizeof(HostCmd_DS_CHAN_RPT_RSP) - + sizeof(pchan_rpt_rsp->tlv_buffer); + + while (evt_len >= sizeof(MrvlIEtypesHeader_t)) { + ptlv = (MrvlIEtypes_Data_t *)pbuffer; + tlv_len = wlan_le16_to_cpu(ptlv->header.len); + + switch (wlan_le16_to_cpu(ptlv->header.type)) { + case TLV_TYPE_CHANRPT_11H_BASIC: + pmeas_rpt_basic = + (MeasRptBasicMap_t *)&ptlv->data; + if (pmeas_rpt_basic->radar) + dfs_radar_found = MTRUE; + break; + default: + break; + } + + pbuffer += (tlv_len + sizeof(ptlv->header)); + evt_len -= (tlv_len + sizeof(ptlv->header)); + evt_len = (evt_len > 0) ? evt_len : 0; + } + } else { + ret = MLAN_STATUS_FAILURE; + } + if (dfs_radar_found) { + PRINTM(MMSG, "RADAR Detected on channel %d!\n", + dfs_check_channel); + /* add channel to NOP list */ + wlan_11h_add_dfs_timestamp(priv->adapter, DFS_TS_REPR_NOP_START, + dfs_check_channel); + } + *radar_chan = dfs_check_channel; + pstate_dfs->dfs_radar_found = dfs_radar_found; + /* Update DFS structure. */ + priv->adapter->callbacks.moal_get_system_time( + priv->adapter->pmoal_handle, &sec, &usec); + pstate_dfs->dfs_report_time_sec = sec; + pstate_dfs->dfs_check_pending = MFALSE; + pstate_dfs->dfs_check_priv = MNULL; + + LEAVE(); + return ret; +} + +/** + * @brief Print debug info in RADAR_DETECTED event + * This event may have the dfs record data appended. + * + * @param priv Pointer to mlan_private + * @param pevent Pointer to mlan_event + * @param radar_chan Pointer to radar channel + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11h_print_event_radar_detected(mlan_private *priv, + mlan_event *pevent, + t_u8 *radar_chan) +{ + wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs; + ENTER(); + *radar_chan = pstate_dfs->dfs_check_channel; + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Check if RADAR_DETECTED handling is blocking data tx + * + * @param pmadapter Pointer to mlan_adapter + * + * @return MTRUE or MFALSE + */ +t_bool wlan_11h_radar_detected_tx_blocked(mlan_adapter *pmadapter) +{ + if (pmadapter->state_rdh.tx_block) + return MTRUE; + switch (pmadapter->state_rdh.stage) { + case RDH_OFF: + case RDH_CHK_INTFS: + case RDH_STOP_TRAFFIC: + return MFALSE; + } + return MTRUE; +} + +/** + * @brief Callback for RADAR_DETECTED event driver handling + * + * @param priv Void pointer to mlan_private + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11h_radar_detected_callback(t_void *priv) +{ + mlan_status ret; + ENTER(); + ret = wlan_11h_radar_detected_handling( + ((mlan_private *)(priv))->adapter, (mlan_private *)priv); + LEAVE(); + return ret; +} + +#ifdef UAP_SUPPORT +/** + * @brief Function for handling sta disconnect event in dfs_repeater mode + * + * @param pmadapter pointer to mlan_adapter + * + * @return NONE + */ +void wlan_dfs_rep_disconnect(mlan_adapter *pmadapter) +{ + mlan_private *priv_list[MLAN_MAX_BSS_NUM]; + mlan_private *pmpriv = MNULL; + t_u8 pcount, i; + + memset(pmadapter, priv_list, 0x00, sizeof(priv_list)); + pcount = wlan_get_privs_by_cond(pmadapter, wlan_is_intf_active, + priv_list); + + /* Stop all the active BSSes */ + for (i = 0; i < pcount; i++) { + pmpriv = priv_list[i]; + + if (GET_BSS_ROLE(pmpriv) != MLAN_BSS_ROLE_UAP) + continue; + + if (wlan_11h_radar_detect_required(pmpriv, + pmadapter->dfsr_channel)) { + wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + } + } +} + +/** + * @brief Function for handling sta BW change event in dfs_repeater mode + * + * @param pmadapter pointer to mlan_adapter + * + * @return NONE + */ +void wlan_dfs_rep_bw_change(mlan_adapter *pmadapter) +{ + mlan_private *priv_list[MLAN_MAX_BSS_NUM]; + mlan_private *pmpriv = MNULL; + t_u8 pcount, i; + + memset(pmadapter, priv_list, 0x00, sizeof(priv_list)); + pcount = wlan_get_privs_by_cond(pmadapter, wlan_is_intf_active, + priv_list); + if (pcount == 1) { + pmpriv = priv_list[0]; + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + PRINTM(MMSG, + "dfs-repeater: BW change detected\n" + "no active priv's, skip event handling.\n"); + return; + } + } + + /* Stop all the active BSSes */ + for (i = 0; i < pcount; i++) { + pmpriv = priv_list[i]; + + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + /* Check if uAPs running on non-dfs channel. If they do + * then there is no need to restart the uAPs + */ + if (!wlan_11h_radar_detect_required( + pmpriv, pmadapter->dfsr_channel)) + return; + + wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + } + } + + /* Start all old active BSSes */ + for (i = 0; i < pcount; i++) { + pmpriv = priv_list[i]; + + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_START, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + } + } +} +#endif + +/** + * @brief Update band config for the new channel + * + * @param uap_band_cfg uap's old channel's band configuration + * @param new_channel new channel that the device is switching to + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +void wlan_11h_update_bandcfg(Band_Config_t *uap_band_cfg, t_u8 new_channel) +{ + t_u8 chan_offset; + ENTER(); + + /* Update the channel offset for 20MHz, 40MHz and 80MHz + * Clear the channel bandwidth for 20MHz + * since channel switch could be happening from 40/80MHz to 20MHz + */ + chan_offset = wlan_get_second_channel_offset(new_channel); + uap_band_cfg->chan2Offset = chan_offset; + + if (!chan_offset) { /* 40MHz/80MHz */ + PRINTM(MCMD_D, "20MHz channel, clear channel bandwidth\n"); + uap_band_cfg->chanWidth = CHAN_BW_20MHZ; + } + LEAVE(); +} + +/** + * @brief Get priv current index -- this is used to enter correct rdh_state + * during radar handling + * + * @param pmpriv Pointer to mlan_private + * @param pstate_rdh Pointer to radar detected state handler + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_11h_get_priv_curr_idx(mlan_private *pmpriv, + wlan_radar_det_hndlg_state_t *pstate_rdh) +{ + t_bool found = MFALSE; + ENTER(); + + PRINTM(MINFO, "%s:pmpriv =%p\n", __func__, pmpriv); + while ((++pstate_rdh->priv_curr_idx) < pstate_rdh->priv_list_count) { + if (pmpriv == + pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]) { + PRINTM(MINFO, "found matching priv: priv_idx=%d\n", + pstate_rdh->priv_curr_idx); + found = MTRUE; + break; + } + } + return (found == MTRUE) ? MLAN_STATUS_SUCCESS : MLAN_STATUS_FAILURE; +} + +/** + * @brief Driver handling for remove customeie + * + * @param pmadapter Pointer to mlan_adapter + * @param pmpriv Pointer to mlan_private + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +mlan_status wlan_11h_remove_custom_ie(mlan_adapter *pmadapter, + mlan_private *pmpriv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + wlan_radar_det_hndlg_state_t *pstate_rdh = &pmadapter->state_rdh; + mlan_ioctl_req *pioctl_req = MNULL; + + ENTER(); + if (pstate_rdh->stage == RDH_SET_CUSTOM_IE) { + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + PRINTM(MMSG, "Removing CHAN_SW IE from interfaces.\n"); + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = + pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]; + if (!wlan_11h_is_dfs_master(pmpriv)) + continue; + ret = wlan_11h_prepare_custom_ie_chansw( + pmadapter, &pioctl_req, MFALSE); + if ((ret != MLAN_STATUS_SUCCESS) || !pioctl_req) { + PRINTM(MERROR, + "%s(): Error in preparing CHAN_SW IE.\n", + __func__); + goto done; + } + + pioctl_req->bss_index = pmpriv->bss_index; + ret = wlan_misc_ioctl_custom_ie_list( + pmadapter, pioctl_req, MFALSE); + if (ret != MLAN_STATUS_SUCCESS && + ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not remove IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, pmpriv, pmpriv->bss_index); + /* TODO: hiow to handle this error case?? + * ignore & continue? */ + } + /* free ioctl buffer memory before we leave */ + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pioctl_req); + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Driver handling for RADAR_DETECTED event + * + * @param pmadapter Pointer to mlan_adapter + * @param pmpriv Pointer to mlan_private + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +mlan_status wlan_11h_radar_detected_handling(mlan_adapter *pmadapter, + mlan_private *pmpriv) +{ +#ifdef DEBUG_LEVEL1 + const char *rdh_stage_str[] = {"RDH_OFF", + "RDH_CHK_INTFS", + "RDH_STOP_TRAFFIC", + "RDH_GET_INFO_CHANNEL", + "RDH_GET_INFO_BEACON_DTIM", + "RDH_SET_CUSTOM_IE", + "RDH_REM_CUSTOM_IE", + "RDH_STOP_INTFS", + "RDH_SET_NEW_CHANNEL", + "RDH_RESTART_INTFS", + "RDH_RESTART_TRAFFIC"}; +#endif + + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 i; + wlan_radar_det_hndlg_state_t *pstate_rdh = &pmadapter->state_rdh; + + ENTER(); + + if (!pmpriv) { + PRINTM(MERROR, "Invalid radar priv -- Exit radar handling\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + switch (pstate_rdh->stage) { + case RDH_CHK_INTFS: + PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]); + + /* get active interfaces */ + memset(pmadapter, pstate_rdh->priv_list, 0x00, + sizeof(pstate_rdh->priv_list)); + pstate_rdh->priv_list_count = wlan_get_privs_by_cond( + pmadapter, wlan_is_intf_active, pstate_rdh->priv_list); + PRINTM(MCMD_D, "%s(): priv_list_count = %d\n", __func__, + pstate_rdh->priv_list_count); + for (i = 0; i < pstate_rdh->priv_list_count; i++) + PRINTM(MINFO, "%s(): priv_list[%d] = %p\n", __func__, + i, pstate_rdh->priv_list[i]); + + if (pstate_rdh->priv_list_count == 0) { + /* no interfaces active... nothing to do */ + PRINTM(MMSG, "11h: Radar Detected - no active priv's," + " skip event handling.\n"); + pstate_rdh->stage = RDH_OFF; + PRINTM(MCMD_D, "%s(): finished - stage(%d)=%s\n", + __func__, pstate_rdh->stage, + rdh_stage_str[pstate_rdh->stage]); + break; /* EXIT CASE */ + } + + /* else: start handling */ + pstate_rdh->curr_channel = 0; + pstate_rdh->new_channel = 0; + memset(pmadapter, &(pstate_rdh->uap_band_cfg), 0, + sizeof(pstate_rdh->uap_band_cfg)); + pstate_rdh->max_bcn_dtim_ms = 0; + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_STOP_TRAFFIC; + /* fall through */ + + case RDH_STOP_TRAFFIC: + PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]); + + PRINTM(MMSG, + "11h: Radar Detected - stopping host tx traffic.\n"); + for (i = 0; i < pstate_rdh->priv_list_count; i++) + wlan_11h_tx_disable(pstate_rdh->priv_list[i]); + + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_GET_INFO_CHANNEL; + /* fall through */ + + case RDH_GET_INFO_CHANNEL: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* here, prefer STA info over UAP info - one less CMD to send */ + if (pstate_rdh->priv_curr_idx == + RDH_STAGE_FIRST_ENTRY_PRIV_IDX) { +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_11h_get_priv_curr_idx(pmpriv, + pstate_rdh); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Unable to locate pmpriv in current active priv_list\n"); + break; /* EXIT CASE */ + } + + /* send cmd to get first UAP's info */ + pmpriv->uap_state_chan_cb.pioctl_req_curr = + MNULL; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_11h_radar_detected_callback; + ret = wlan_uap_get_channel(pmpriv); + break; /* EXIT CASE */ + } else +#endif + { + /* Assume all STAs on same channel, find first + * STA */ + MASSERT(pstate_rdh->priv_list_count > 0); + for (i = 0; i < pstate_rdh->priv_list_count; + i++) { + pmpriv = pstate_rdh->priv_list[i]; + if (GET_BSS_ROLE(pmpriv) == + MLAN_BSS_ROLE_STA) + break; + } + /* STA info kept in driver, just copy */ + pstate_rdh->curr_channel = + pmpriv->curr_bss_params.bss_descriptor + .channel; + } + } +#ifdef UAP_SUPPORT + else if (pstate_rdh->priv_curr_idx < + pstate_rdh->priv_list_count) { + /* repeat entry: UAP return with info */ + pstate_rdh->curr_channel = + pmpriv->uap_state_chan_cb.channel; + pstate_rdh->uap_band_cfg = + pmpriv->uap_state_chan_cb.bandcfg; + PRINTM(MCMD_D, + "%s(): uap_band_cfg=0x%02x curr_chan=%d, curr_idx=%d bss_role=%d\n", + __func__, pstate_rdh->uap_band_cfg, + pstate_rdh->curr_channel, + pstate_rdh->priv_curr_idx, GET_BSS_ROLE(pmpriv)); + } +#endif + + /* add channel to NOP list */ + wlan_11h_add_dfs_timestamp(pmadapter, DFS_TS_REPR_NOP_START, + pstate_rdh->curr_channel); + + /* choose new channel (!= curr channel) and move on */ +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + pstate_rdh->new_channel = + wlan_11h_get_uap_start_channel( + pmpriv, + pmpriv->uap_state_chan_cb.bandcfg); + else +#endif + pstate_rdh->new_channel = + wlan_11h_get_adhoc_start_channel(pmpriv); + + if (!pstate_rdh->new_channel || + (pstate_rdh->new_channel == + pstate_rdh->curr_channel)) { /* report error */ + PRINTM(MERROR, + "%s(): ERROR - Failed to choose new_chan" + " (!= curr_chan) !!\n", + __func__); +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, + MNULL, MNULL); + PRINTM(MERROR, + "STOP UAP and exit radar handling...\n"); + pstate_rdh->stage = RDH_OFF; + break; /* leads to exit case */ + } +#endif + } + if (!pmadapter->dfs_test_params.no_channel_change_on_radar && + pmadapter->dfs_test_params.fixed_new_channel_on_radar) { + PRINTM(MCMD_D, "dfs_testing - user fixed new_chan=%d\n", + pmadapter->dfs_test_params + .fixed_new_channel_on_radar); + pstate_rdh->new_channel = + pmadapter->dfs_test_params + .fixed_new_channel_on_radar; + } + /* applies to DFS with ECSA support */ + if (pmadapter->dfs_test_params.no_channel_change_on_radar) { + pstate_rdh->new_channel = pstate_rdh->curr_channel; + } + PRINTM(MCMD_D, "%s(): curr_chan=%d, new_chan=%d\n", __func__, + pstate_rdh->curr_channel, pstate_rdh->new_channel); + + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_GET_INFO_BEACON_DTIM; + /* fall through */ + + case RDH_GET_INFO_BEACON_DTIM: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + +#ifdef UAP_SUPPORT + /* check all intfs in this stage to find longest period */ + /* UAP intf callback returning with info */ + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count) { + t_u16 bcn_dtim_msec; + pmpriv = + pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]; + PRINTM(MCMD_D, "%s(): uap.bcn_pd=%d, uap.dtim_pd=%d\n", + __func__, + pmpriv->uap_state_chan_cb.beacon_period, + pmpriv->uap_state_chan_cb.dtim_period); + bcn_dtim_msec = + (pmpriv->uap_state_chan_cb.beacon_period * + pmpriv->uap_state_chan_cb.dtim_period); + if (bcn_dtim_msec > pstate_rdh->max_bcn_dtim_ms) + pstate_rdh->max_bcn_dtim_ms = bcn_dtim_msec; + } +#endif + + /* check next intf */ + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = + pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]; + +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + pmpriv->uap_state_chan_cb.pioctl_req_curr = + MNULL; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_11h_radar_detected_callback; + ret = wlan_uap_get_beacon_dtim(pmpriv); + break; /* leads to exit case */ + } else +#endif + { /* get STA info from driver and compare here */ + t_u16 bcn_pd_msec = 100; + t_u16 dtim_pd_msec = 1; + t_u16 bcn_dtim_msec; + + /* adhoc creator */ + if (wlan_11h_is_dfs_master(pmpriv)) { + bcn_pd_msec = pmpriv->beacon_period; + } else { + bcn_pd_msec = pmpriv->curr_bss_params + .bss_descriptor + .beacon_period; + /* if (priv->bss_mode != + * MLAN_BSS_MODE_IBSS) */ + /* TODO: mlan_scan.c needs to parse TLV + * 0x05 (TIM) for dtim_period */ + } + PRINTM(MCMD_D, + "%s(): sta.bcn_pd=%d, sta.dtim_pd=%d\n", + __func__, bcn_pd_msec, dtim_pd_msec); + bcn_dtim_msec = (bcn_pd_msec * dtim_pd_msec); + if (bcn_dtim_msec > pstate_rdh->max_bcn_dtim_ms) + pstate_rdh->max_bcn_dtim_ms = + bcn_dtim_msec; + } + } + + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count) + break; /* EXIT CASE (for UAP) */ + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_SET_CUSTOM_IE; + /* fall through */ + + case RDH_SET_CUSTOM_IE: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* add CHAN_SW IE - Need apply on each interface */ + if (pstate_rdh->priv_curr_idx == + RDH_STAGE_FIRST_ENTRY_PRIV_IDX) { + mlan_ioctl_req *pioctl_req = MNULL; + PRINTM(MMSG, + "11h: Radar Detected - adding CHAN_SW IE to interfaces.\n"); + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = pstate_rdh->priv_list + [pstate_rdh->priv_curr_idx]; + if (!wlan_11h_is_dfs_master(pmpriv)) + continue; + ret = wlan_11h_prepare_custom_ie_chansw( + pmadapter, &pioctl_req, MTRUE); + if ((ret != MLAN_STATUS_SUCCESS) || + !pioctl_req) { + PRINTM(MERROR, + "%s(): Error in preparing CHAN_SW IE.\n", + __func__); + break; /* EXIT CASE */ + } + + pioctl_req->bss_index = pmpriv->bss_index; + ret = wlan_misc_ioctl_custom_ie_list( + pmadapter, pioctl_req, MFALSE); + if (ret != MLAN_STATUS_SUCCESS && + ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not set IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, pmpriv, + pmpriv->bss_index); + /* TODO: how to handle this error case?? + * ignore & continue? */ + } + /* free ioctl buffer memory before we leave */ + pmadapter->callbacks.moal_mfree( + pmadapter->pmoal_handle, + (t_u8 *)pioctl_req); + } + break; /* EXIT CASE */ + } + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_REM_CUSTOM_IE; + /* fall through */ + + case RDH_REM_CUSTOM_IE: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* remove CHAN_SW IE - Need apply on each interface */ + if (pstate_rdh->priv_curr_idx == + RDH_STAGE_FIRST_ENTRY_PRIV_IDX) { + mlan_ioctl_req *pioctl_req = MNULL; + /* + * first entry to this stage, do delay + * DFS requires a minimum of 5 chances for clients to + * hear this IE. Use delay: 5 beacons <= + * (BCN_DTIM_MSEC*5) <= 3 seconds). + */ + t_u16 delay_ms = + MAX(MIN_RDH_CHAN_SW_IE_PERIOD_MSEC, + MIN((4 * pstate_rdh->max_bcn_dtim_ms), + MAX_RDH_CHAN_SW_IE_PERIOD_MSEC)); + PRINTM(MMSG, + "11h: Radar Detected - delay %d ms for FW to" + " broadcast CHAN_SW IE.\n", + delay_ms); + wlan_mdelay(pmadapter, delay_ms); + PRINTM(MMSG, + "11h: Radar Detected - delay over, removing" + " CHAN_SW IE from interfaces.\n"); + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = pstate_rdh->priv_list + [pstate_rdh->priv_curr_idx]; + if (!wlan_11h_is_dfs_master(pmpriv)) + continue; + ret = wlan_11h_prepare_custom_ie_chansw( + pmadapter, &pioctl_req, MFALSE); + if ((ret != MLAN_STATUS_SUCCESS) || + !pioctl_req) { + PRINTM(MERROR, + "%s(): Error in preparing CHAN_SW IE.\n", + __func__); + break; /* EXIT CASE */ + } + + pioctl_req->bss_index = pmpriv->bss_index; + ret = wlan_misc_ioctl_custom_ie_list( + pmadapter, pioctl_req, MFALSE); + if (ret != MLAN_STATUS_SUCCESS && + ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not remove IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, pmpriv, + pmpriv->bss_index); + /* TODO: hiow to handle this error + * case?? ignore & continue? */ + } + /* free ioctl buffer memory before we leave */ + pmadapter->callbacks.moal_mfree( + pmadapter->pmoal_handle, + (t_u8 *)pioctl_req); + } + break; /* EXIT CASE */ + } + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_STOP_INTFS; + /* fall through */ + + case RDH_STOP_INTFS: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* issues one cmd (DEAUTH/ADHOC_STOP/BSS_STOP) to each intf */ + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = + pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]; +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, + MNULL, MNULL); + break; /* leads to exit case */ + } +#endif +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + if (wlan_11h_is_dfs_master(pmpriv)) { + /* Save ad-hoc creator state before stop + * clears it */ + pmpriv->adhoc_state_prev = + pmpriv->adhoc_state; + } + if (pmpriv->media_connected == MTRUE) { + wlan_disconnect(pmpriv, MNULL, MNULL); + break; /* leads to exit case */ + } + } +#endif + } + + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count || + ret == MLAN_STATUS_FAILURE) + break; /* EXIT CASE */ + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_SET_NEW_CHANNEL; + + if (pmadapter->dfs_test_params.no_channel_change_on_radar) { + PRINTM(MCMD_D, + "dfs_testing - no channel change on radar." + " Overwrite new_chan = curr_chan.\n"); + pstate_rdh->new_channel = pstate_rdh->curr_channel; + pstate_rdh->priv_curr_idx = + RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_RESTART_INTFS; + goto rdh_restart_intfs; /* skip next stage */ + } + /* fall through */ + + case RDH_SET_NEW_CHANNEL: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* only set new channel for UAP intfs */ + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = + pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]; +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + pmpriv->uap_state_chan_cb.pioctl_req_curr = + MNULL; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_11h_radar_detected_callback; + + /* DFS only in 5GHz */ + wlan_11h_update_bandcfg( + &pstate_rdh->uap_band_cfg, + pstate_rdh->new_channel); + PRINTM(MCMD_D, + "RDH_SET_NEW_CHANNEL: uAP band config = 0x%x channel=%d\n", + pstate_rdh->uap_band_cfg, + pstate_rdh->new_channel); + + ret = wlan_uap_set_channel( + pmpriv, pstate_rdh->uap_band_cfg, + pstate_rdh->new_channel); + break; /* leads to exit case */ + } +#endif + } + + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count || + ret == MLAN_STATUS_FAILURE) + break; /* EXIT CASE (for UAP) */ + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_RESTART_INTFS; + /* fall through */ + + case RDH_RESTART_INTFS: + rdh_restart_intfs: + PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage], + pstate_rdh->priv_curr_idx); + + /* can only restart master intfs */ + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = + pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]; +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + if (wlan_11h_radar_detect_required( + pmpriv, pstate_rdh->new_channel)) { + /* Radar detection is required for this + channel, make sure 11h is activated + in the firmware */ + ret = wlan_11h_activate(pmpriv, MNULL, + MTRUE); + ret = wlan_11h_config_master_radar_det( + pmpriv, MTRUE); + ret = wlan_11h_check_update_radar_det_state( + pmpriv); + } + ret = wlan_prepare_cmd(pmpriv, + HOST_CMD_APCMD_BSS_START, + HostCmd_ACT_GEN_SET, 0, + MNULL, MNULL); + break; /* leads to exit case */ + } +#endif +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + /* Check previous state to find former + * Ad-hoc creator interface. Set new + * state to Starting, so it'll be seen + * as a DFS master. */ + if (pmpriv->adhoc_state_prev == ADHOC_STARTED) { + pmpriv->adhoc_state = ADHOC_STARTING; + pmpriv->adhoc_state_prev = ADHOC_IDLE; + } + if (wlan_11h_is_dfs_master(pmpriv)) { + /* set new adhoc channel here */ + pmpriv->adhoc_channel = + pstate_rdh->new_channel; + if (wlan_11h_radar_detect_required( + pmpriv, + pstate_rdh->new_channel)) { + /* Radar detection is required + for this channel, make sure + 11h is activated in the + firmware */ + ret = wlan_11h_activate( + pmpriv, MNULL, MTRUE); + if (ret) + break; + ret = wlan_11h_config_master_radar_det( + pmpriv, MTRUE); + if (ret) + break; + ret = wlan_11h_check_update_radar_det_state( + pmpriv); + if (ret) + break; + } + ret = wlan_prepare_cmd( + pmpriv, + HostCmd_CMD_802_11_AD_HOC_START, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmpriv->adhoc_last_start_ssid); + break; /* leads to exit case */ + } + + /* NOTE: DON'T reconnect slave STA intfs - + * infra/adhoc_joiner Do we want to return to + * same AP/network (on radar channel)? If want + * to connect back, depend on either: + * 1. driver's reassoc thread + * 2. wpa_supplicant, or other user-space + * app + */ + } +#endif + } + + if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count || + ret == MLAN_STATUS_FAILURE) + break; /* EXIT CASE (for UAP) */ + /* else */ + pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + pstate_rdh->stage = RDH_RESTART_TRAFFIC; + /* fall through */ + + case RDH_RESTART_TRAFFIC: + PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]); + /* remove custome ie */ + if (pmadapter->ecsa_enable) { + mlan_ioctl_req *pioctl_req = MNULL; + pstate_rdh->priv_curr_idx = + RDH_STAGE_FIRST_ENTRY_PRIV_IDX; + while ((++pstate_rdh->priv_curr_idx) < + pstate_rdh->priv_list_count) { + pmpriv = pstate_rdh->priv_list + [pstate_rdh->priv_curr_idx]; + if (!wlan_11h_is_dfs_master(pmpriv)) + continue; + ret = wlan_11h_prepare_custom_ie_chansw( + pmadapter, &pioctl_req, MFALSE); + if ((ret != MLAN_STATUS_SUCCESS) || + !pioctl_req) { + PRINTM(MERROR, + "%s(): Error in preparing CHAN_SW IE.\n", + __func__); + break; /* EXIT CASE */ + } + + pioctl_req->bss_index = pmpriv->bss_index; + + ret = wlan_misc_ioctl_custom_ie_list( + pmadapter, pioctl_req, MFALSE); + if (ret != MLAN_STATUS_SUCCESS && + ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not remove IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, pmpriv, + pmpriv->bss_index); + /* TODO: hiow to handle this error + * case?? ignore & continue? */ + } + /* free ioctl buffer memory before we leave */ + pmadapter->callbacks.moal_mfree( + pmadapter->pmoal_handle, + (t_u8 *)pioctl_req); + } + } + /* continue traffic for reactivated interfaces */ + PRINTM(MMSG, + "11h: Radar Detected - restarting host tx traffic.\n"); + for (i = 0; i < pstate_rdh->priv_list_count; i++) + wlan_11h_tx_enable(pstate_rdh->priv_list[i]); + + pstate_rdh->stage = RDH_OFF; /* DONE! */ + PRINTM(MCMD_D, "%s(): finished - stage(%d)=%s\n", __func__, + pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]); + + break; + + default: + pstate_rdh->stage = RDH_OFF; /* cancel RDH to unblock Tx packets + */ + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief DFS Event Preprocessing. + * Operates directly on pmadapter variables. + * + * 1. EVENT_RADAR_DETECTED comes from firmware without specific + * bss_num/bss_type. Find it an appropriate interface and + * update event_cause field in event_buf. + * + * @param pmadapter Pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS (update successful) + * or MLAN_STATUS_FAILURE (no change) + */ +mlan_status wlan_11h_dfs_event_preprocessing(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_private *pmpriv = MNULL; + mlan_private *priv_list[MLAN_MAX_BSS_NUM] = {0}; + + ENTER(); + switch (pmadapter->event_cause & EVENT_ID_MASK) { + case EVENT_RADAR_DETECTED: + /* find active intf: prefer dfs_master over dfs_slave */ + if (wlan_get_privs_by_two_cond( + pmadapter, wlan_11h_is_master_active_on_dfs_chan, + wlan_11h_is_dfs_master, MTRUE, priv_list)) { + pmpriv = priv_list[0]; + PRINTM(MINFO, "%s: found dfs_master priv=%p\n", + __func__, pmpriv); + } else if (wlan_get_privs_by_two_cond( + pmadapter, + wlan_11h_is_slave_active_on_dfs_chan, + wlan_11h_is_dfs_slave, MTRUE, priv_list)) { + pmpriv = priv_list[0]; + PRINTM(MINFO, "%s: found dfs_slave priv=%p\n", __func__, + pmpriv); + } else if (pmadapter->state_dfs.dfs_check_pending) { + pmpriv = (mlan_private *)(pmadapter->state_dfs + .dfs_check_priv); + PRINTM(MINFO, "%s: found dfs priv=%p\n", __func__, + pmpriv); + } + + /* update event_cause if we found an appropriate priv */ + if (pmpriv) { + pmlan_buffer pmevbuf = pmadapter->pmlan_buffer_event; + t_u32 new_event_cause = + pmadapter->event_cause & EVENT_ID_MASK; + new_event_cause |= + ((GET_BSS_NUM(pmpriv) & 0xff) << 16) | + ((pmpriv->bss_type & 0xff) << 24); + PRINTM(MINFO, "%s: priv - bss_num=%d, bss_type=%d\n", + __func__, GET_BSS_NUM(pmpriv), pmpriv->bss_type); + memcpy_ext(pmadapter, + pmevbuf->pbuf + pmevbuf->data_offset, + &new_event_cause, sizeof(new_event_cause), + sizeof(new_event_cause)); + ret = MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, + "Failed to find dfs master/slave priv\n"); + ret = MLAN_STATUS_FAILURE; + } + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief try to switch to a non-dfs channel + * + * @param priv Void pointer to mlan_private + * + * @param chan pointer to channel + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING + */ +mlan_status wlan_11h_switch_non_dfs_chan(mlan_private *priv, t_u8 *chan) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u32 i; + t_u32 rand_entry; + t_u8 def_chan; + t_u8 rand_tries = 0; + region_chan_t *chn_tbl = MNULL; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (!pmadapter->dfs_test_params.no_channel_change_on_radar && + pmadapter->dfs_test_params.fixed_new_channel_on_radar) { + PRINTM(MCMD_D, "dfs_testing - user fixed new_chan=%d\n", + pmadapter->dfs_test_params.fixed_new_channel_on_radar); + *chan = pmadapter->dfs_test_params.fixed_new_channel_on_radar; + + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + /*get the channel table first*/ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (pmadapter->region_channel[i].band == BAND_A && + pmadapter->region_channel[i].valid) { + chn_tbl = &pmadapter->region_channel[i]; + break; + } + } + + if (!chn_tbl || !chn_tbl->pcfp) + goto done; + + do { + rand_entry = + wlan_11h_get_random_num(pmadapter) % chn_tbl->num_cfp; + def_chan = (t_u8)chn_tbl->pcfp[rand_entry].channel; + rand_tries++; + } while ((wlan_11h_is_channel_under_nop(pmadapter, def_chan) || + chn_tbl->pcfp[rand_entry].passive_scan_or_radar_detect == + MTRUE) && + (rand_tries < MAX_SWITCH_CHANNEL_RETRIES)); + + /* meet max retries, use the lowest non-dfs channel */ + if (rand_tries == MAX_SWITCH_CHANNEL_RETRIES) { + for (i = 0; i < chn_tbl->num_cfp; i++) { + if (chn_tbl->pcfp[i].passive_scan_or_radar_detect == + MFALSE && + !wlan_11h_is_channel_under_nop( + pmadapter, + (t_u8)chn_tbl->pcfp[i].channel)) { + def_chan = (t_u8)chn_tbl->pcfp[i].channel; + break; + } + } + if (i == chn_tbl->num_cfp) + goto done; + } + + *chan = def_chan; + ret = MLAN_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief set dfs check channel + * + * @param priv Void pointer to mlan_private + * + * @param chan pointer to channel + * + * @return N/A + */ +void wlan_11h_set_dfs_check_chan(mlan_private *priv, t_u8 chan) +{ + wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs; + ENTER(); + pstate_dfs->dfs_check_channel = chan; + PRINTM(MCMND, "Set dfs_check_channel=%d\n", chan); + LEAVE(); +} + +/** + * @brief 802.11h DFS W53 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_w53_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11h_cfg *ds_11hcfg = MNULL; + mlan_ds_11h_dfs_w53_cfg *dfs_w53_cfg = MNULL; + + ENTER(); + + ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + dfs_w53_cfg = &ds_11hcfg->param.dfs_w53_cfg; + + if (pioctl_req->action == MLAN_ACT_GET) { + dfs_w53_cfg->dfs53cfg = pmadapter->dfs53cfg; + } else { + pmadapter->dfs53cfg = dfs_w53_cfg->dfs53cfg; + } + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11h.h b/mxm_wifiex/wlan_src/mlan/mlan_11h.h new file mode 100644 index 0000000..05766a9 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11h.h @@ -0,0 +1,201 @@ +/** @file mlan_11h.h + * + * @brief This header file contains data structures and + * function declarations of 802.11h + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************* +Change Log: + 03/26/2009: initial creation +*************************************************************/ + +#ifndef _MLAN_11H_ +#define _MLAN_11H_ + +/** 11H OID bitmasks */ +#define ENABLE_11H_MASK MBIT(0) +#define MASTER_RADAR_DET_MASK MBIT(1) +#define SLAVE_RADAR_DET_MASK MBIT(2) + +/** DFS Master Radar Detect global enable */ +#define DFS_MASTER_RADAR_DETECT_EN (MTRUE) +/** DFS Slave Radar Detect global enable */ +#define DFS_SLAVE_RADAR_DETECT_EN (MFALSE) + +#define CHANNEL_OFFSET_MASK 0x30 +#define CHANNEL_BANDWIDTH_MASK 0x0C + +/** + * 11H APIs + */ + +/* Is master radar detection enabled in firmware? */ +extern t_bool wlan_11h_is_master_radar_det_active(mlan_private *priv); + +/** Configure master radar detection. + * Need call wlan_11h_check_update_radar_det_state() after. + */ +extern mlan_status wlan_11h_config_master_radar_det(mlan_private *priv, + t_bool enable); + +/** Configure slave radar detection. + * Need call wlan_11h_check_update_radar_det_state() after. + */ +extern mlan_status wlan_11h_config_slave_radar_det(mlan_private *priv, + t_bool enable); + +/** Checks all interfaces and updates radar detect flags if necessary */ +extern mlan_status wlan_11h_check_update_radar_det_state(mlan_private *pmpriv); + +/** Return 1 if 11h is active in the firmware, 0 if it is inactive */ +extern t_bool wlan_11h_is_active(mlan_private *priv); + +/** Enable the tx interface and record the new transmit state */ +extern void wlan_11h_tx_enable(mlan_private *priv); + +/** Disable the tx interface and record the new transmit state */ +extern void wlan_11h_tx_disable(mlan_private *priv); + +/** Activate 11h extensions in the firmware */ +extern mlan_status wlan_11h_activate(mlan_private *priv, t_void *pioctl_buf, + t_bool flag); + +/** Initialize the 11h device structure */ +extern void wlan_11h_init(mlan_adapter *pmadapter); + +/** Cleanup for the 11h device structure */ +extern void wlan_11h_cleanup(mlan_adapter *pmadapter); + +/** Initialize the 11h interface structure */ +extern void wlan_11h_priv_init(mlan_private *pmpriv); + +/** Get an initial random channel to start an adhoc network on */ +extern t_u8 wlan_11h_get_adhoc_start_channel(mlan_private *priv); + +/** Get channel that has been closed via Channel Switch Announcement */ +extern t_u8 wlan_11h_get_csa_closed_channel(mlan_private *priv); + +/** Check if radar detection is required on the specified channel */ +extern t_bool wlan_11h_radar_detect_required(mlan_private *priv, t_u8 channel); + +/** Perform a standard availibility check on the specified channel */ +extern t_s32 wlan_11h_issue_radar_detect(mlan_private *priv, + pmlan_ioctl_req pioctl_req, + t_u8 channel, Band_Config_t bandcfg); + +/** Check previously issued radar report for a channel */ +extern mlan_status wlan_11h_check_chan_report(mlan_private *priv, t_u8 chan); + +/** Add any 11h TLVs necessary to complete an adhoc start command */ +extern t_s32 wlan_11h_process_start(mlan_private *priv, t_u8 **ppbuffer, + IEEEtypes_CapInfo_t *pcap_info, + t_u32 channel, + wlan_11h_bss_info_t *p11h_bss_info); + +/** Add any 11h TLVs necessary to complete a join command (adhoc or infra) */ +extern t_s32 wlan_11h_process_join(mlan_private *priv, t_u8 **ppbuffer, + IEEEtypes_CapInfo_t *pcap_info, t_u8 band, + t_u32 channel, + wlan_11h_bss_info_t *p11h_bss_info); + +/** Complete the firmware command preparation for an 11h command function */ +extern mlan_status wlan_11h_cmd_process(mlan_private *priv, + HostCmd_DS_COMMAND *pcmd_ptr, + const t_void *pinfo_buf); + +/** Process the response of an 11h firmware command */ +extern mlan_status wlan_11h_cmdresp_process(mlan_private *priv, + const HostCmd_DS_COMMAND *resp); + +/** Receive IEs from scan processing and record any needed info for 11h */ +extern mlan_status wlan_11h_process_bss_elem(mlan_adapter *pmadapter, + wlan_11h_bss_info_t *p11h_bss_info, + const t_u8 *pelement); + +/** Handler for EVENT_CHANNEL_SWITCH_ANN */ +extern mlan_status wlan_11h_handle_event_chanswann(mlan_private *priv); + +/** Handler for EVENT_CHANNEL_REPORT_RDY */ +extern mlan_status wlan_11h_handle_event_chanrpt_ready(mlan_private *priv, + mlan_event *pevent, + t_u8 *radar_chan); + +/** Debug output for EVENT_RADAR_DETECTED */ +mlan_status wlan_11h_print_event_radar_detected(mlan_private *priv, + mlan_event *pevent, + t_u8 *radar_chan); + +t_s32 wlan_11h_cancel_radar_detect(mlan_private *priv); +/** Handler for DFS_TESTING IOCTL */ +extern mlan_status wlan_11h_ioctl_dfs_testing(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +extern mlan_status +wlan_11h_ioctl_get_channel_nop_info(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +extern mlan_status wlan_11h_ioctl_dfs_chan_report(mlan_private *priv, + pmlan_ioctl_req pioctl_req); +extern mlan_status wlan_11h_ioctl_chan_switch_count(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +/** get/set dfs w53 cfg */ +mlan_status wlan_11h_ioctl_dfs_w53_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +/** Check if channel is under a NOP duration (should not be used) */ +extern t_bool wlan_11h_is_channel_under_nop(mlan_adapter *pmadapter, + t_u8 channel); + +/** Check if RADAR_DETECTED handling is blocking data tx */ +extern t_bool wlan_11h_radar_detected_tx_blocked(mlan_adapter *pmadapter); + +/** Callback for RADAR_DETECTED (for UAP cmdresp) */ +extern mlan_status wlan_11h_radar_detected_callback(t_void *priv); +/** set dfs check channel */ +void wlan_11h_set_dfs_check_chan(mlan_private *priv, t_u8 chan); + +#ifdef UAP_SUPPORT +/** BW_change event Handler for dfs_repeater */ +void wlan_dfs_rep_bw_change(mlan_adapter *pmadapter); + +/** disconnect event Handler for dfs_repeater */ +void wlan_dfs_rep_disconnect(mlan_adapter *pmadapter); +#endif + +/** Handler for RADAR_DETECTED */ +extern mlan_status wlan_11h_radar_detected_handling(mlan_adapter *pmadapter, + mlan_private *priv); + +mlan_status wlan_11h_remove_custom_ie(mlan_adapter *pmadapter, + mlan_private *pmpriv); + +/** DFS Event pre-processing */ +extern mlan_status wlan_11h_dfs_event_preprocessing(mlan_adapter *pmadapter); + +/** DFS switch to non-DFS channel */ +extern mlan_status wlan_11h_switch_non_dfs_chan(mlan_private *priv, t_u8 *chan); + +extern void wlan_11h_update_bandcfg(Band_Config_t *uap_band_cfg, + t_u8 new_channel); + +/** function checks if interface is active. **/ +extern t_bool wlan_is_intf_active(mlan_private *pmpriv); + +#endif /*_MLAN_11H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11n.c b/mxm_wifiex/wlan_src/mlan/mlan_11n.c new file mode 100644 index 0000000..6be3969 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11n.c @@ -0,0 +1,3092 @@ +/** @file mlan_11n.c + * + * @brief This file contains functions for 11n handling. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11ac.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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + if (((cfg->param.htcap_cfg.htcap & ~IGN_HW_DEV_CAP) & + pmpriv->adapter->hw_dot_11n_dev_cap) != + (cfg->param.htcap_cfg.htcap & ~IGN_HW_DEV_CAP)) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } else { + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BG) { + pmpriv->usr_dot_11n_dev_cap_bg = + cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, + "Set: UsrDot11nCap for 2.4GHz 0x%x\n", + pmpriv->usr_dot_11n_dev_cap_bg); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) { + pmpriv->usr_dot_11n_dev_cap_a = + cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, + "Set: UsrDot11nCap for 5GHz 0x%x\n", + pmpriv->usr_dot_11n_dev_cap_a); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BOTH) { + pmpriv->usr_dot_11n_dev_cap_bg = + cfg->param.htcap_cfg.htcap; + pmpriv->usr_dot_11n_dev_cap_a = + cfg->param.htcap_cfg.htcap; + PRINTM(MINFO, + "Set: UsrDot11nCap for 2.4GHz and 5GHz 0x%x\n", + cfg->param.htcap_cfg.htcap); + } + } + } else { + /* Hardware 11N device capability required */ + if (cfg->param.htcap_cfg.hw_cap_req) + cfg->param.htcap_cfg.htcap = + pmadapter->hw_dot_11n_dev_cap; + else { + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_BG) { + cfg->param.htcap_cfg.htcap = + pmpriv->usr_dot_11n_dev_cap_bg; + PRINTM(MINFO, + "Get: UsrDot11nCap for 2.4GHz 0x%x\n", + cfg->param.htcap_cfg.htcap); + } + if (cfg->param.htcap_cfg.misc_cfg == BAND_SELECT_A) { + cfg->param.htcap_cfg.htcap = + pmpriv->usr_dot_11n_dev_cap_a; + PRINTM(MINFO, + "Get: UsrDot11nCap for 5GHz 0x%x\n", + cfg->param.htcap_cfg.htcap); + } + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable AMSDU AGGR CTRL + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_11n_ioctl_amsdu_aggr_ctrl(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_11n_cfg *cfg = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + cfg->param.stream_cfg = pmpriv->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 + pmpriv->usr_dev_mcs_support = + cfg->param.stream_cfg; + break; + case HT_STREAM_MODE_1X1: + pmpriv->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 Set/get control to coex RX window size configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_11n_ioctl_coex_rx_winsize(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cfg->param.coex_rx_winsize = pmadapter->coex_rx_winsize; + else if (pioctl_req->action == MLAN_ACT_SET) + pmadapter->coex_rx_winsize = (t_u8)cfg->param.coex_rx_winsize; + + LEAVE(); + return ret; +} + +/** + * @brief This function will resend addba request to all + * the peer in the TxBAStreamTbl + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +static void wlan_11n_update_addba_request(mlan_private *priv) +{ + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + wlan_request_ralist_lock(priv); + ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + wlan_release_ralist_lock(priv); + LEAVE(); + return; + } + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + wlan_send_addba(priv, ptx_tbl->tid, ptx_tbl->ra); + ptx_tbl = ptx_tbl->pnext; + } + wlan_release_ralist_lock(priv); + /* Signal MOAL to trigger mlan_main_process */ + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); + LEAVE(); + return; +} + +/** + * @brief Set/get addba parameter + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_11n_ioctl_addba_param(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + t_u32 timeout; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + cfg->param.addba_param.timeout = pmpriv->add_ba_param.timeout; + cfg->param.addba_param.txwinsize = + pmpriv->add_ba_param.tx_win_size; + cfg->param.addba_param.rxwinsize = + pmpriv->add_ba_param.rx_win_size; + cfg->param.addba_param.txamsdu = pmpriv->add_ba_param.tx_amsdu; + cfg->param.addba_param.rxamsdu = pmpriv->add_ba_param.rx_amsdu; + } else { + timeout = pmpriv->add_ba_param.timeout; + pmpriv->add_ba_param.timeout = cfg->param.addba_param.timeout; + pmpriv->add_ba_param.tx_win_size = + cfg->param.addba_param.txwinsize; + + pmpriv->add_ba_param.rx_win_size = + cfg->param.addba_param.rxwinsize; + pmpriv->user_rxwinsize = pmpriv->add_ba_param.rx_win_size; + pmpriv->add_ba_param.tx_amsdu = cfg->param.addba_param.txamsdu; + pmpriv->add_ba_param.rx_amsdu = cfg->param.addba_param.rxamsdu; + if (timeout != pmpriv->add_ba_param.timeout) + wlan_11n_update_addba_request(pmpriv); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function send delba to specific tid + * + * @param priv A pointer to mlan_priv + * @param tid tid + * @return N/A + */ +void wlan_11n_delba(mlan_private *priv, int tid) +{ + RxReorderTbl *rx_reor_tbl_ptr; + + ENTER(); + + rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list( + priv->adapter->pmoal_handle, &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + if (rx_reor_tbl_ptr->tid == tid) { + PRINTM(MIOCTL, "Send delba to tid=%d, " MACSTR "\n", + tid, MAC2STR(rx_reor_tbl_ptr->ta)); + wlan_send_delba(priv, MNULL, tid, rx_reor_tbl_ptr->ta, + 0); + LEAVE(); + return; + } + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return; +} + +/** + * @brief Set/get addba reject set + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_11n_ioctl_addba_reject(pmlan_adapter pmadapter, + 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_ext(pmadapter, cfg->param.addba_reject, + pmpriv->addba_reject, MAX_NUM_TID, MAX_NUM_TID); + } else { + for (i = 0; i < MAX_NUM_TID; i++) { + /* For AMPDU */ + if (cfg->param.addba_reject[i] > + ADDBA_RSP_STATUS_REJECT) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + + pmpriv->addba_reject[i] = cfg->param.addba_reject[i]; + } + if (pmpriv->media_connected == MTRUE) { + for (i = 0; i < MAX_NUM_TID; i++) { + if (cfg->param.addba_reject[i] == + ADDBA_RSP_STATUS_REJECT) { + PRINTM(MIOCTL, + "Receive addba reject: tid=%d\n", + i); + wlan_11n_delba(pmpriv, i); + } + } + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Set/get ibss ampdu param + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_11n_ioctl_ibss_ampdu_param(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + int i = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + PRINTM(MINFO, "Get IBSS AMPDU param\n"); + for (i = 0; i < MAX_NUM_TID; i++) { + cfg->param.ibss_ampdu.ampdu[i] = pmpriv->ibss_ampdu[i]; + cfg->param.ibss_ampdu.addba_reject[i] = + pmpriv->ibss_addba_reject[i]; + } + } else { + for (i = 0; i < MAX_NUM_TID; i++) { + /* For AMPDU RX*/ + if (cfg->param.ibss_ampdu.addba_reject[i] > + ADDBA_RSP_STATUS_REJECT) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + pmpriv->ibss_addba_reject[i] = + cfg->param.ibss_ampdu.addba_reject[i]; + /* For AMPDU TX*/ + if ((cfg->param.ibss_ampdu.ampdu[i] > HIGH_PRIO_TID) && + (cfg->param.ibss_ampdu.ampdu[i] != + BA_STREAM_NOT_ALLOWED)) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + pmpriv->ibss_ampdu[i] = cfg->param.ibss_ampdu.ampdu[i]; + } + PRINTM(MMSG, "IBSS addba reject: %d %d %d %d %d %d %d %d\n", + pmpriv->ibss_addba_reject[0], + pmpriv->ibss_addba_reject[1], + pmpriv->ibss_addba_reject[2], + pmpriv->ibss_addba_reject[3], + pmpriv->ibss_addba_reject[4], + pmpriv->ibss_addba_reject[5], + pmpriv->ibss_addba_reject[6], + pmpriv->ibss_addba_reject[7]); + PRINTM(MMSG, "IBSS ampdu %d %d %d %d %d %d %d %d\n", + pmpriv->ibss_ampdu[0], pmpriv->ibss_ampdu[1], + pmpriv->ibss_ampdu[2], pmpriv->ibss_ampdu[3], + pmpriv->ibss_ampdu[4], pmpriv->ibss_ampdu[5], + pmpriv->ibss_ampdu[6], pmpriv->ibss_ampdu[7]); + } + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Minimum BA Threshold + * + * @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_min_ba_threshold_cfg(pmlan_adapter pmadapter, + 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.min_ba_threshold = pmadapter->min_ba_threshold; + else + pmadapter->min_ba_threshold = cfg->param.min_ba_threshold; + pioctl_req->data_read_written = sizeof(t_u8) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief This function will send DELBA to entries in the priv's + * Tx BA stream table + * + * @param priv A pointer to mlan_private + * @param pioctl_req A pointer to ioctl request buffer + * @param tid TID + * @param peer_address A pointer to peer address + * @param last_tx_ba_to_delete A pointer to the last entry in TxBAStreamTbl + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING + */ +static mlan_status wlan_send_delba_to_entry_in_txbastream_tbl( + pmlan_private priv, pmlan_ioctl_req pioctl_req, t_u8 tid, + t_u8 *peer_address, TxBAStreamTbl *last_tx_ba_to_delete) +{ + pmlan_adapter pmadapter = priv->adapter; + TxBAStreamTbl *tx_ba_stream_tbl_ptr; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0}; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + wlan_request_ralist_lock(priv); + tx_ba_stream_tbl_ptr = + (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!tx_ba_stream_tbl_ptr) { + wlan_release_ralist_lock(priv); + LEAVE(); + return ret; + } + + while (tx_ba_stream_tbl_ptr != + (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (tx_ba_stream_tbl_ptr->ba_status == + BA_STREAM_SETUP_COMPLETE) { + if (((tid == DELBA_ALL_TIDS) || + (tid == tx_ba_stream_tbl_ptr->tid)) && + (!memcmp(pmadapter, peer_address, zero_mac, + MLAN_MAC_ADDR_LENGTH) || + !memcmp(pmadapter, peer_address, + tx_ba_stream_tbl_ptr->ra, + MLAN_MAC_ADDR_LENGTH))) { + if (last_tx_ba_to_delete && + (tx_ba_stream_tbl_ptr == + last_tx_ba_to_delete)) + ret = wlan_send_delba( + priv, pioctl_req, + tx_ba_stream_tbl_ptr->tid, + tx_ba_stream_tbl_ptr->ra, 1); + else + ret = wlan_send_delba( + priv, MNULL, + tx_ba_stream_tbl_ptr->tid, + tx_ba_stream_tbl_ptr->ra, 1); + } + } + tx_ba_stream_tbl_ptr = tx_ba_stream_tbl_ptr->pnext; + } + wlan_release_ralist_lock(priv); + + LEAVE(); + return ret; +} + +/** + * @brief This function will send DELBA to entries in the priv's + * rx reordering table + * + * @param priv A pointer to mlan_private + * @param pioctl_req A pointer to ioctl request buffer + * @param tid TID + * @param peer_address A pointer to peer address + * @param last_rx_ba_to_delete A pointer to the last entry in RxReorderTbl + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING + */ +static mlan_status wlan_send_delba_to_entry_in_reorder_tbl( + pmlan_private priv, pmlan_ioctl_req pioctl_req, t_u8 tid, + t_u8 *peer_address, RxReorderTbl *last_rx_ba_to_delete) +{ + pmlan_adapter pmadapter = priv->adapter; + RxReorderTbl *rx_reor_tbl_ptr; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0}; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list( + pmadapter->pmoal_handle, &priv->rx_reorder_tbl_ptr, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return ret; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + if (rx_reor_tbl_ptr->ba_status == BA_STREAM_SETUP_COMPLETE) { + if (((tid == DELBA_ALL_TIDS) || + (tid == rx_reor_tbl_ptr->tid)) && + (!memcmp(pmadapter, peer_address, zero_mac, + MLAN_MAC_ADDR_LENGTH) || + !memcmp(pmadapter, peer_address, + rx_reor_tbl_ptr->ta, + MLAN_MAC_ADDR_LENGTH))) { + if (last_rx_ba_to_delete && + (rx_reor_tbl_ptr == last_rx_ba_to_delete)) + ret = wlan_send_delba( + priv, pioctl_req, + rx_reor_tbl_ptr->tid, + rx_reor_tbl_ptr->ta, 0); + else + ret = wlan_send_delba( + priv, MNULL, + rx_reor_tbl_ptr->tid, + rx_reor_tbl_ptr->ta, 0); + } + } + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return ret; +} + +/** + * @brief IOCTL to delete BA + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_11n_ioctl_delba(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + TxBAStreamTbl *tx_ba_stream_tbl_ptr, *last_tx_ba_to_delete = MNULL; + RxReorderTbl *rx_reor_tbl_ptr, *last_rx_ba_to_delete = MNULL; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0}; + t_u8 tid, *peer_address; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + tid = cfg->param.del_ba.tid; + peer_address = cfg->param.del_ba.peer_mac_addr; + + PRINTM(MINFO, "DelBA: direction %d, TID %d, peer address " MACSTR "\n", + cfg->param.del_ba.direction, tid, MAC2STR(peer_address)); + + if (cfg->param.del_ba.direction & DELBA_RX) { + rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list( + pmadapter->pmoal_handle, &pmpriv->rx_reorder_tbl_ptr, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + + if (rx_reor_tbl_ptr) { + while (rx_reor_tbl_ptr != + (RxReorderTbl *)&pmpriv->rx_reorder_tbl_ptr) { + if (rx_reor_tbl_ptr->ba_status == + BA_STREAM_SETUP_COMPLETE) { + if (((tid == DELBA_ALL_TIDS) || + (tid == rx_reor_tbl_ptr->tid)) && + (!memcmp(pmadapter, peer_address, + zero_mac, + MLAN_MAC_ADDR_LENGTH) || + !memcmp(pmadapter, peer_address, + rx_reor_tbl_ptr->ta, + MLAN_MAC_ADDR_LENGTH))) { + /* Found RX BA to delete */ + last_rx_ba_to_delete = + rx_reor_tbl_ptr; + } + } + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + } + } + + if ((last_rx_ba_to_delete == MNULL) && + (cfg->param.del_ba.direction & DELBA_TX)) { + wlan_request_ralist_lock(pmpriv); + tx_ba_stream_tbl_ptr = (TxBAStreamTbl *)util_peek_list( + pmadapter->pmoal_handle, &pmpriv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + + if (tx_ba_stream_tbl_ptr) { + while (tx_ba_stream_tbl_ptr != + (TxBAStreamTbl *)&pmpriv->tx_ba_stream_tbl_ptr) { + if (tx_ba_stream_tbl_ptr->ba_status == + BA_STREAM_SETUP_COMPLETE) { + if (((tid == DELBA_ALL_TIDS) || + (tid == + tx_ba_stream_tbl_ptr->tid)) && + (!memcmp(pmadapter, peer_address, + zero_mac, + MLAN_MAC_ADDR_LENGTH) || + !memcmp(pmadapter, peer_address, + tx_ba_stream_tbl_ptr->ra, + MLAN_MAC_ADDR_LENGTH))) { + /* Found TX BA to delete */ + last_tx_ba_to_delete = + tx_ba_stream_tbl_ptr; + } + } + tx_ba_stream_tbl_ptr = + tx_ba_stream_tbl_ptr->pnext; + } + } + wlan_release_ralist_lock(pmpriv); + } + + if (cfg->param.del_ba.direction & DELBA_TX) { + if (last_rx_ba_to_delete) + ret = wlan_send_delba_to_entry_in_txbastream_tbl( + pmpriv, MNULL, tid, peer_address, MNULL); + else + ret = wlan_send_delba_to_entry_in_txbastream_tbl( + pmpriv, pioctl_req, tid, peer_address, + last_tx_ba_to_delete); + } + if (last_rx_ba_to_delete) { + ret = wlan_send_delba_to_entry_in_reorder_tbl( + pmpriv, pioctl_req, tid, peer_address, + last_rx_ba_to_delete); + } + + LEAVE(); + return ret; +} + +/** + * @brief IOCTL to reject addba req + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_11n_ioctl_rejectaddbareq(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_REJECT_ADDBA_REQ, cmd_action, + 0, (t_void *)pioctl_req, + &cfg->param.reject_addba_req); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function will send DELBA to entries in the priv's + * Tx BA stream table + * + * @param priv A pointer to mlan_private + * @param tid TID + * + * @return N/A + */ +static void wlan_send_delba_txbastream_tbl(pmlan_private priv, t_u8 tid) +{ + pmlan_adapter pmadapter = priv->adapter; + TxBAStreamTbl *tx_ba_stream_tbl_ptr; + + ENTER(); + + wlan_request_ralist_lock(priv); + tx_ba_stream_tbl_ptr = + (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!tx_ba_stream_tbl_ptr) { + wlan_release_ralist_lock(priv); + LEAVE(); + return; + } + + while (tx_ba_stream_tbl_ptr != + (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (tx_ba_stream_tbl_ptr->ba_status == + BA_STREAM_SETUP_COMPLETE) { + if (tid == tx_ba_stream_tbl_ptr->tid) { + PRINTM(MIOCTL, + "Tx:Send delba to tid=%d, " MACSTR "\n", + tid, MAC2STR(tx_ba_stream_tbl_ptr->ra)); + wlan_release_ralist_lock(priv); + wlan_send_delba(priv, MNULL, + tx_ba_stream_tbl_ptr->tid, + tx_ba_stream_tbl_ptr->ra, 1); + LEAVE(); + return; + } + } + tx_ba_stream_tbl_ptr = tx_ba_stream_tbl_ptr->pnext; + } + wlan_release_ralist_lock(priv); + + LEAVE(); + return; +} + +/** + * @brief update station list for the new aggr_prio_tbl setting + * + * @param priv A pointer to mlan_private structure + * + * + * @return N/A + */ +void wlan_update_all_stations_ampdu(mlan_private *priv) +{ + sta_node *sta_ptr; + mlan_adapter *pmadapter = priv->adapter; + int i = 0; + + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + sta_ptr = (sta_node *)util_peek_list(pmadapter->pmoal_handle, + &priv->sta_list, MNULL, MNULL); + if (!sta_ptr) { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + LEAVE(); + return; + } + while (sta_ptr != (sta_node *)&priv->sta_list) { + for (i = 0; i < MAX_NUM_TID; i++) { + if (sta_ptr->is_11n_enabled) + sta_ptr->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + } + sta_ptr = sta_ptr->pnext; + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return; +} + +/** + * @brief Set/get aggr_prio_tbl + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_11n_ioctl_aggr_prio_tbl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + int i = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + for (i = 0; i < MAX_NUM_TID; i++) { + cfg->param.aggr_prio_tbl.ampdu[i] = + pmpriv->aggr_prio_tbl[i].ampdu_user; + cfg->param.aggr_prio_tbl.amsdu[i] = + pmpriv->aggr_prio_tbl[i].amsdu; + } + } else { + for (i = 0; i < MAX_NUM_TID; i++) { + /* For AMPDU */ + if ((cfg->param.aggr_prio_tbl.ampdu[i] > + HIGH_PRIO_TID) && + (cfg->param.aggr_prio_tbl.ampdu[i] != + BA_STREAM_NOT_ALLOWED)) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } + + pmpriv->aggr_prio_tbl[i].ampdu_ap = + pmpriv->aggr_prio_tbl[i].ampdu_user = + cfg->param.aggr_prio_tbl.ampdu[i]; + + /* For AMSDU */ + if ((cfg->param.aggr_prio_tbl.amsdu[i] > + HIGH_PRIO_TID && + cfg->param.aggr_prio_tbl.amsdu[i] != + BA_STREAM_NOT_ALLOWED)) { + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + break; + } else { + pmpriv->aggr_prio_tbl[i].amsdu = + cfg->param.aggr_prio_tbl.amsdu[i]; + } + } + if (pmpriv->media_connected == MTRUE) { + for (i = 0; i < MAX_NUM_TID; i++) { + if (cfg->param.aggr_prio_tbl.ampdu[i] == + BA_STREAM_NOT_ALLOWED) { + PRINTM(MIOCTL, + "Receive aggrpriotbl: BA not allowed tid=%d\n", + i); + wlan_send_delba_txbastream_tbl(pmpriv, + i); + } + } + wlan_update_all_stations_ampdu(pmpriv); + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function update all the tx_win_size + * + * @param pmadapter A pointer to mlan_adapter + * + * + * @return N/A + */ +void wlan_update_ampdu_txwinsize(pmlan_adapter pmadapter) +{ + t_u8 i; + t_u32 tx_win_size = 0; + pmlan_private priv = MNULL; + + ENTER(); + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + tx_win_size = priv->add_ba_param.tx_win_size; +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) + priv->add_ba_param.tx_win_size = + MLAN_STA_AMPDU_DEF_TXWINSIZE; +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + priv->add_ba_param.tx_win_size = + MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; +#endif +#ifdef UAP_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_UAP) + priv->add_ba_param.tx_win_size = + MLAN_UAP_AMPDU_DEF_TXWINSIZE; +#endif + if (pmadapter->coex_win_size && + pmadapter->coex_tx_win_size) + priv->add_ba_param.tx_win_size = + pmadapter->coex_tx_win_size; + + if (tx_win_size != priv->add_ba_param.tx_win_size) { + if (priv->media_connected == MTRUE) { + for (i = 0; i < MAX_NUM_TID; i++) + wlan_send_delba_txbastream_tbl( + priv, i); + wlan_recv_event( + priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + } + } + LEAVE(); + return; +} + +/** + * @brief Get supported MCS set + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_11n_ioctl_supported_mcs_set(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_11n_cfg *cfg = MNULL; + int rx_mcs_supp; + t_u8 mcs_set[NUM_MCS_FIELD]; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) { + PRINTM(MERROR, "Set operation is not supported\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + rx_mcs_supp = GET_RXMCSSUPP(pmpriv->usr_dev_mcs_support); + /* Set MCS for 1x1/2x2*/ + memset(pmadapter, (t_u8 *)mcs_set, 0xff, rx_mcs_supp); + /* Clear all the other values */ + memset(pmadapter, (t_u8 *)&mcs_set[rx_mcs_supp], 0, + NUM_MCS_FIELD - rx_mcs_supp); + /* Set MCS32 with 40MHz support */ + if ((ISSUPP_CHANWIDTH40(pmpriv->usr_dot_11n_dev_cap_bg) || + ISSUPP_CHANWIDTH40(pmpriv->usr_dot_11n_dev_cap_a)) && + !(pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && + pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) + SETHT_MCS32(mcs_set); + + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + memcpy_ext(pmadapter, cfg->param.supported_mcs_set, mcs_set, + NUM_MCS_FIELD, NUM_MCS_FIELD); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function checks if the given pointer is valid entry of + * Tx BA Stream table + * + * @param priv Pointer to mlan_private + * @param ptxtblptr Pointer to tx ba stream entry + * + * @return MTRUE or MFALSE + */ +static int wlan_is_txbastreamptr_valid(mlan_private *priv, + TxBAStreamTbl *ptxtblptr) +{ + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + LEAVE(); + return MFALSE; + } + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (ptx_tbl == ptxtblptr) { + LEAVE(); + return MTRUE; + } + ptx_tbl = ptx_tbl->pnext; + } + LEAVE(); + return MFALSE; +} + +/** + * @brief This function will return the pointer to a entry in BA Stream + * table which matches the ba_status requested + * + * @param priv A pointer to mlan_private + * @param ba_status Current status of the BA stream + * + * @return A pointer to first entry matching status in BA stream + * NULL if not found + */ +static TxBAStreamTbl *wlan_11n_get_txbastream_status(mlan_private *priv, + baStatus_e ba_status) +{ + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + LEAVE(); + return MNULL; + } + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (ptx_tbl->ba_status == ba_status) { + LEAVE(); + return ptx_tbl; + } + ptx_tbl = ptx_tbl->pnext; + } + + LEAVE(); + return MNULL; +} + +/******************************************************** + Global Functions +********************************************************/ + +#ifdef STA_SUPPORT +/** + * @brief This function fills the cap info + * + * @param priv A pointer to mlan_private structure + * @param pht_cap A pointer to MrvlIETypes_HTCap_t structure + * @param bands Band configuration + * + * @return N/A + */ +static void wlan_fill_cap_info(mlan_private *priv, HTCap_t *ht_cap, t_u8 bands) +{ + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + if (bands & BAND_A) + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; + + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) + SETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + else + RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + + if (ISSUPP_GREENFIELD(usr_dot_11n_dev_cap)) + SETHT_GREENFIELD(ht_cap->ht_cap_info); + else + RESETHT_GREENFIELD(ht_cap->ht_cap_info); + + if (ISSUPP_SHORTGI20(usr_dot_11n_dev_cap)) + SETHT_SHORTGI20(ht_cap->ht_cap_info); + else + RESETHT_SHORTGI20(ht_cap->ht_cap_info); + + if (ISSUPP_SHORTGI40(usr_dot_11n_dev_cap)) + SETHT_SHORTGI40(ht_cap->ht_cap_info); + else + RESETHT_SHORTGI40(ht_cap->ht_cap_info); + if (ISSUPP_RXSTBC(usr_dot_11n_dev_cap)) + SETHT_RXSTBC(ht_cap->ht_cap_info, 1); + else + RESETHT_RXSTBC(ht_cap->ht_cap_info); + + if (ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap)) + SETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + else + RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + + /** if current channel only allow 20Mhz, we should cler 40Mhz support */ + if (priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && + priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) { + RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + RESETHT_SHORTGI40(ht_cap->ht_cap_info); + RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + } + /* No user config for LDPC coding capability yet */ + if (ISSUPP_RXLDPC(usr_dot_11n_dev_cap)) + SETHT_LDPCCODINGCAP(ht_cap->ht_cap_info); + else + RESETHT_LDPCCODINGCAP(ht_cap->ht_cap_info); + + /* No user config for TX STBC yet */ + if (ISSUPP_TXSTBC(usr_dot_11n_dev_cap)) + SETHT_TXSTBC(ht_cap->ht_cap_info); + else + RESETHT_TXSTBC(ht_cap->ht_cap_info); + + /* No user config for Delayed BACK yet */ + RESETHT_DELAYEDBACK(ht_cap->ht_cap_info); + + /* Need change to support 8k AMSDU receive */ + RESETHT_MAXAMSDU(ht_cap->ht_cap_info); + /* SM power save */ + if (ISSUPP_MIMOPS(priv->adapter->hw_dot_11n_dev_cap)) + RESETHT_SM_POWERSAVE(ht_cap->ht_cap_info); /* Enable HT SMPS*/ + else + SETHT_STATIC_SMPS(ht_cap->ht_cap_info); /* Disable HT SMPS */ + + LEAVE(); +} + +/** + * @brief This function clear the bit in cap info which we don't support + * + * @param priv A pointer to mlan_private structure + * @param pht_cap A pointer to MrvlIETypes_HTCap_t structure + * @param bands Band configuration + * + * @return N/A + */ +static void wlan_reset_cap_info(mlan_private *priv, HTCap_t *ht_cap, t_u8 bands) +{ + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + if (bands & BAND_A) + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; + + if (!ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap)) + RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + + if (!ISSUPP_GREENFIELD(usr_dot_11n_dev_cap)) + RESETHT_GREENFIELD(ht_cap->ht_cap_info); + + if (!ISSUPP_SHORTGI20(usr_dot_11n_dev_cap)) + RESETHT_SHORTGI20(ht_cap->ht_cap_info); + + if (!ISSUPP_SHORTGI40(usr_dot_11n_dev_cap)) + RESETHT_SHORTGI40(ht_cap->ht_cap_info); + if (!ISSUPP_RXSTBC(usr_dot_11n_dev_cap)) + RESETHT_RXSTBC(ht_cap->ht_cap_info); + + if (!ISENABLED_40MHZ_INTOLARENT(usr_dot_11n_dev_cap)) + RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + + /** if current channel only allow 20Mhz, we should cler 40Mhz support */ + if (priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && + priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) { + RESETHT_SUPPCHANWIDTH(ht_cap->ht_cap_info); + RESETHT_SHORTGI40(ht_cap->ht_cap_info); + RESETHT_40MHZ_INTOLARANT(ht_cap->ht_cap_info); + } + /* No user config for LDPC coding capability yet */ + if (!ISSUPP_RXLDPC(usr_dot_11n_dev_cap)) + RESETHT_LDPCCODINGCAP(ht_cap->ht_cap_info); + + /* No user config for TX STBC yet */ + if (!ISSUPP_TXSTBC(usr_dot_11n_dev_cap)) + RESETHT_TXSTBC(ht_cap->ht_cap_info); + + /* No user config for Delayed BACK yet */ + RESETHT_DELAYEDBACK(ht_cap->ht_cap_info); + + /* Need change to support 8k AMSDU receive */ + RESETHT_MAXAMSDU(ht_cap->ht_cap_info); + /* SM power save */ + if (!ISSUPP_MIMOPS(priv->adapter->hw_dot_11n_dev_cap)) + SETHT_STATIC_SMPS(ht_cap->ht_cap_info); /* Disable HT SMPS */ + + LEAVE(); +} + +/** + * @brief This function fills the HT cap tlv + * + * @param priv A pointer to mlan_private structure + * @param pht_cap A pointer to MrvlIETypes_HTCap_t structure + * @param bands Band configuration + * @param fill A flag for fill the htcap info + * + * @return N/A + */ +void wlan_fill_ht_cap_tlv(mlan_private *priv, MrvlIETypes_HTCap_t *pht_cap, + t_u16 bands, t_u8 fill) +{ + mlan_adapter *pmadapter = priv->adapter; + int rx_mcs_supp; + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + if (bands & BAND_A) + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; + + /* Fill HT cap info */ + if (fill) + wlan_fill_cap_info(priv, &pht_cap->ht_cap, bands); + else + wlan_reset_cap_info(priv, &pht_cap->ht_cap, bands); + + pht_cap->ht_cap.ht_cap_info = + wlan_cpu_to_le16(pht_cap->ht_cap.ht_cap_info); + + /* Set ampdu param */ + SETAMPDU_SIZE(pht_cap->ht_cap.ampdu_param, AMPDU_FACTOR_64K); + SETAMPDU_SPACING(pht_cap->ht_cap.ampdu_param, 0); + + rx_mcs_supp = GET_RXMCSSUPP(priv->usr_dev_mcs_support); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type)) { + if (bands & BAND_A) + rx_mcs_supp = MIN( + rx_mcs_supp, + GET_RXMCSSUPP(pmadapter->user_htstream >> 8)); + else + rx_mcs_supp = + MIN(rx_mcs_supp, + GET_RXMCSSUPP(pmadapter->user_htstream)); + } +#endif + memset(pmadapter, (t_u8 *)pht_cap->ht_cap.supported_mcs_set, 0xff, + rx_mcs_supp); + /* Clear all the other values to get the minimum mcs set btw STA and AP + */ + memset(pmadapter, + (t_u8 *)&pht_cap->ht_cap.supported_mcs_set[rx_mcs_supp], 0, + NUM_MCS_FIELD - rx_mcs_supp); + /* Set MCS32 with 40MHz support */ + /* if current channel only support 20MHz, we should not set 40Mz + * supprot*/ + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + !(priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && + priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) + SETHT_MCS32(pht_cap->ht_cap.supported_mcs_set); + + /* Clear RD responder bit */ + RESETHT_EXTCAP_RDG(pht_cap->ht_cap.ht_ext_cap); + pht_cap->ht_cap.ht_ext_cap = + wlan_cpu_to_le16(pht_cap->ht_cap.ht_ext_cap); + + /* Set Tx BF cap */ + pht_cap->ht_cap.tx_bf_cap = wlan_cpu_to_le32(priv->tx_bf_cap); + + LEAVE(); + return; +} + +/** + * @brief This function fills the HT cap ie + * + * @param priv A pointer to mlan_private structure + * @param pht_cap A pointer to IEEEtypes_HTCap_t structure + * @param bands Band configuration + * + * @return N/A + */ +void wlan_fill_ht_cap_ie(mlan_private *priv, IEEEtypes_HTCap_t *pht_cap, + t_u16 bands) +{ + mlan_adapter *pmadapter = priv->adapter; + int rx_mcs_supp; + t_u32 usr_dot_11n_dev_cap; + + ENTER(); + + pht_cap->ieee_hdr.element_id = HT_CAPABILITY; + pht_cap->ieee_hdr.len = sizeof(HTCap_t); + if (bands & BAND_A) + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = priv->usr_dot_11n_dev_cap_bg; + + /* Fill HT cap info */ + wlan_fill_cap_info(priv, &pht_cap->ht_cap, bands); + + /* Set ampdu param */ + SETAMPDU_SIZE(pht_cap->ht_cap.ampdu_param, AMPDU_FACTOR_64K); + SETAMPDU_SPACING(pht_cap->ht_cap.ampdu_param, 0); + + rx_mcs_supp = GET_RXMCSSUPP(priv->usr_dev_mcs_support); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type)) { + if (bands & BAND_A) + rx_mcs_supp = MIN( + rx_mcs_supp, + GET_RXMCSSUPP(pmadapter->user_htstream >> 8)); + else + rx_mcs_supp = + MIN(rx_mcs_supp, + GET_RXMCSSUPP(pmadapter->user_htstream)); + } +#endif + memset(pmadapter, (t_u8 *)pht_cap->ht_cap.supported_mcs_set, 0xff, + rx_mcs_supp); + /* Clear all the other values to get the minimum mcs set btw STA and AP + */ + memset(pmadapter, + (t_u8 *)&pht_cap->ht_cap.supported_mcs_set[rx_mcs_supp], 0, + NUM_MCS_FIELD - rx_mcs_supp); + /* Set MCS32 with 40MHz support */ + /* if current channel only support 20MHz, we should not set 40Mz + * supprot*/ + if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + !(priv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && + priv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) + SETHT_MCS32(pht_cap->ht_cap.supported_mcs_set); + + /* Clear RD responder bit */ + RESETHT_EXTCAP_RDG(pht_cap->ht_cap.ht_ext_cap); + + /* Set Tx BF cap */ + pht_cap->ht_cap.tx_bf_cap = priv->tx_bf_cap; + + LEAVE(); + return; +} +#endif /* STA_SUPPORT */ + +/** + * @brief This function prints the 802.11n device capability + * + * @param pmadapter A pointer to mlan_adapter structure + * @param cap Capability value + * + * @return N/A + */ +void wlan_show_dot11ndevcap(pmlan_adapter pmadapter, t_u32 cap) +{ + ENTER(); + + PRINTM(MINFO, "GET_HW_SPEC: Maximum MSDU length = %s octets\n", + (ISSUPP_MAXAMSDU(cap) ? "7935" : "3839")); + PRINTM(MINFO, "GET_HW_SPEC: Beam forming %s\n", + (ISSUPP_BEAMFORMING(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Greenfield preamble %s\n", + (ISSUPP_GREENFIELD(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: AMPDU %s\n", + (ISSUPP_AMPDU(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: MIMO Power Save %s\n", + (ISSUPP_MIMOPS(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Rx STBC %s\n", + (ISSUPP_RXSTBC(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Tx STBC %s\n", + (ISSUPP_TXSTBC(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Short GI for 40 Mhz %s\n", + (ISSUPP_SHORTGI40(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: Short GI for 20 Mhz %s\n", + (ISSUPP_SHORTGI20(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: LDPC coded packet receive %s\n", + (ISSUPP_RXLDPC(cap) ? "supported" : "not supported")); + + PRINTM(MINFO, "GET_HW_SPEC: Number of Tx BA streams supported = %d\n", + ISSUPP_GETTXBASTREAM(cap)); + PRINTM(MINFO, "GET_HW_SPEC: 40 Mhz channel width %s\n", + (ISSUPP_CHANWIDTH40(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: 20 Mhz channel width %s\n", + (ISSUPP_CHANWIDTH20(cap) ? "supported" : "not supported")); + PRINTM(MINFO, "GET_HW_SPEC: 10 Mhz channel width %s\n", + (ISSUPP_CHANWIDTH10(cap) ? "supported" : "not supported")); + + if (ISSUPP_RXANTENNAA(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna A\n"); + if (ISSUPP_RXANTENNAB(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna B\n"); + if (ISSUPP_RXANTENNAC(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna C\n"); + if (ISSUPP_RXANTENNAD(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Rx antenna D\n"); + if (ISSUPP_TXANTENNAA(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna A\n"); + if (ISSUPP_TXANTENNAB(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna B\n"); + if (ISSUPP_TXANTENNAC(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna C\n"); + if (ISSUPP_TXANTENNAD(cap)) + PRINTM(MINFO, "GET_HW_SPEC: Presence of Tx antenna D\n"); + + LEAVE(); + return; +} + +/** + * @brief This function prints the 802.11n device MCS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param support Support value + * + * @return N/A + */ +void wlan_show_devmcssupport(pmlan_adapter pmadapter, t_u8 support) +{ + ENTER(); + + PRINTM(MINFO, "GET_HW_SPEC: MCSs for %dx%d MIMO\n", + GET_RXMCSSUPP(support), GET_TXMCSSUPP(support)); + + LEAVE(); + return; +} + +/** + * @brief This function handles the command response of + * delete a block ack request + * + * @param priv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *resp) +{ + int tid; + TxBAStreamTbl *ptx_ba_tbl; + HostCmd_DS_11N_DELBA *pdel_ba = + (HostCmd_DS_11N_DELBA *)&resp->params.del_ba; + + ENTER(); + + pdel_ba->del_ba_param_set = wlan_le16_to_cpu(pdel_ba->del_ba_param_set); + pdel_ba->reason_code = wlan_le16_to_cpu(pdel_ba->reason_code); + + tid = pdel_ba->del_ba_param_set >> DELBA_TID_POS; + if (pdel_ba->del_result == BA_RESULT_SUCCESS) { + mlan_11n_delete_bastream_tbl( + priv, tid, pdel_ba->peer_mac_addr, TYPE_DELBA_SENT, + INITIATOR_BIT(pdel_ba->del_ba_param_set), 0); + wlan_request_ralist_lock(priv); + ptx_ba_tbl = wlan_11n_get_txbastream_status( + priv, BA_STREAM_SETUP_INPROGRESS); + wlan_release_ralist_lock(priv); + if (ptx_ba_tbl) + wlan_send_addba(priv, ptx_ba_tbl->tid, ptx_ba_tbl->ra); + } else { /* + * In case of failure, recreate + * the deleted stream in case + * we initiated the ADDBA + */ + if (INITIATOR_BIT(pdel_ba->del_ba_param_set)) { + wlan_request_ralist_lock(priv); + if (!wlan_11n_get_txbastream_tbl( + priv, tid, pdel_ba->peer_mac_addr, MFALSE)) + wlan_11n_create_txbastream_tbl( + priv, pdel_ba->peer_mac_addr, tid, + BA_STREAM_SETUP_INPROGRESS); + ptx_ba_tbl = wlan_11n_get_txbastream_status( + priv, BA_STREAM_SETUP_INPROGRESS); + wlan_release_ralist_lock(priv); + if (ptx_ba_tbl) { + mlan_11n_delete_bastream_tbl( + priv, ptx_ba_tbl->tid, ptx_ba_tbl->ra, + TYPE_DELBA_SENT, MTRUE, 0); + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * add a block ack request + * + * @param priv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_11n_addba_req(mlan_private *priv, HostCmd_DS_COMMAND *resp) +{ + t_u8 tid; + HostCmd_DS_11N_ADDBA_RSP *padd_ba_rsp = + (HostCmd_DS_11N_ADDBA_RSP *)&resp->params.add_ba_rsp; + TxBAStreamTbl *ptx_ba_tbl; + raListTbl *ra_list = MNULL; + int tid_down; + + ENTER(); + + padd_ba_rsp->block_ack_param_set = + wlan_le16_to_cpu(padd_ba_rsp->block_ack_param_set); + padd_ba_rsp->block_ack_tmo = + wlan_le16_to_cpu(padd_ba_rsp->block_ack_tmo); + padd_ba_rsp->ssn = (wlan_le16_to_cpu(padd_ba_rsp->ssn)) & SSN_MASK; + padd_ba_rsp->status_code = wlan_le16_to_cpu(padd_ba_rsp->status_code); + + tid = (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_TID_MASK) >> + BLOCKACKPARAM_TID_POS; + tid_down = wlan_get_wmm_tid_down(priv, tid); + ra_list = wlan_wmm_get_ralist_node(priv, tid_down, + padd_ba_rsp->peer_mac_addr); + if (padd_ba_rsp->status_code == BA_RESULT_SUCCESS) { + ptx_ba_tbl = wlan_11n_get_txbastream_tbl( + priv, tid, padd_ba_rsp->peer_mac_addr, MTRUE); + if (ptx_ba_tbl) { + PRINTM(MCMND, + "ADDBA REQ: " MACSTR + " tid=%d ssn=%d win_size=%d,amsdu=%d\n", + MAC2STR(padd_ba_rsp->peer_mac_addr), tid, + padd_ba_rsp->ssn, + ((padd_ba_rsp->block_ack_param_set & + BLOCKACKPARAM_WINSIZE_MASK) >> + BLOCKACKPARAM_WINSIZE_POS), + padd_ba_rsp->block_ack_param_set & + BLOCKACKPARAM_AMSDU_SUPP_MASK); + ptx_ba_tbl->ba_status = BA_STREAM_SETUP_COMPLETE; + if ((padd_ba_rsp->block_ack_param_set & + BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.tx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != + BA_STREAM_NOT_ALLOWED)) + ptx_ba_tbl->amsdu = MTRUE; + else + ptx_ba_tbl->amsdu = MFALSE; + if (ra_list) { + ra_list->amsdu_in_ampdu = ptx_ba_tbl->amsdu; + ra_list->ba_status = BA_STREAM_SETUP_COMPLETE; + } + } else { + PRINTM(MERROR, "BA stream not created\n"); + } + } else { + if (ra_list) { + ra_list->amsdu_in_ampdu = MFALSE; + ra_list->ba_status = BA_STREAM_NOT_SETUP; + } + mlan_11n_delete_bastream_tbl(priv, tid, + padd_ba_rsp->peer_mac_addr, + TYPE_DELBA_SENT, MTRUE, 0); + if (padd_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) { +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + disable_station_ampdu( + priv, tid, padd_ba_rsp->peer_mac_addr); +#endif /* UAP_SUPPORT */ + priv->aggr_prio_tbl[tid].ampdu_ap = + BA_STREAM_NOT_ALLOWED; + + } else { + if (ra_list) { + ra_list->packet_count = 0; + ra_list->ba_packet_threshold = + wlan_get_random_ba_threshold( + priv->adapter); + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function restore tx_pause flag + * + * @param priv A pointer to mlan_private structure + * @param flag MTRUE/MFALSE; + * + * @return N/A + */ +void wlan_set_tx_pause_flag(mlan_private *priv, t_u8 flag) +{ + mlan_private *pmpriv = MNULL; + t_u8 i; + for (i = 0; i < priv->adapter->priv_num; i++) { + pmpriv = priv->adapter->priv[i]; + if (pmpriv) + pmpriv->tx_pause = flag; + } +} + +/** + * @brief This function prepares command of reconfigure tx buf + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_recfg_tx_buf(mlan_private *priv, HostCmd_DS_COMMAND *cmd, + int cmd_action, void *pdata_buf) +{ + HostCmd_DS_TXBUF_CFG *ptx_buf = &cmd->params.tx_buf; + t_u16 action = (t_u16)cmd_action; + t_u16 buf_size = *((t_u16 *)pdata_buf); + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_TXBUF_CFG) + S_DS_GEN); + ptx_buf->action = wlan_cpu_to_le16(action); + switch (action) { + case HostCmd_ACT_GEN_SET: + PRINTM(MCMND, "set tx_buf = %d\n", buf_size); + ptx_buf->buff_size = wlan_cpu_to_le16(buf_size); + /** stop tx traffic */ + wlan_set_tx_pause_flag(priv, MTRUE); + break; + case HostCmd_ACT_GEN_GET: + default: + ptx_buf->buff_size = 0; + break; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of amsdu aggr control + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_amsdu_aggr_ctrl(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, int cmd_action, + void *pdata_buf) +{ + HostCmd_DS_AMSDU_AGGR_CTRL *pamsdu_ctrl = &cmd->params.amsdu_aggr_ctrl; + t_u16 action = (t_u16)cmd_action; + mlan_ds_11n_amsdu_aggr_ctrl *aa_ctrl = + (mlan_ds_11n_amsdu_aggr_ctrl *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_AMSDU_AGGR_CTRL) + S_DS_GEN); + pamsdu_ctrl->action = wlan_cpu_to_le16(action); + switch (action) { + case HostCmd_ACT_GEN_SET: + pamsdu_ctrl->enable = wlan_cpu_to_le16(aa_ctrl->enable); + pamsdu_ctrl->curr_buf_size = 0; + break; + case HostCmd_ACT_GEN_GET: + default: + pamsdu_ctrl->curr_buf_size = 0; + break; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of amsdu aggr ctrl + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_amsdu_aggr_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, 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(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_11n_cfg *cfg = MNULL; + HostCmd_DS_11N_CFG *htcfg = &resp->params.htcfg; + + ENTER(); + if (pioctl_buf && + (wlan_le16_to_cpu(htcfg->action) == HostCmd_ACT_GEN_GET)) { + cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; + cfg->param.tx_cfg.httxcap = wlan_le16_to_cpu(htcfg->ht_tx_cap); + cfg->param.tx_cfg.httxinfo = + wlan_le16_to_cpu(htcfg->ht_tx_info); + cfg->param.tx_cfg.misc_cfg = + wlan_le16_to_cpu(htcfg->misc_config); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares reject addba req command + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_reject_addba_req(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_REJECT_ADDBA_REQ *preject_addba_req = + &cmd->params.rejectaddbareq; + mlan_ds_reject_addba_req *prejaddbareq = + (mlan_ds_reject_addba_req *)pdata_buf; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_REJECT_ADDBA_REQ); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_REJECT_ADDBA_REQ) + + S_DS_GEN); + preject_addba_req->action = wlan_cpu_to_le16(cmd_action); + preject_addba_req->conditions = + wlan_cpu_to_le32(prejaddbareq->conditions); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of reject addba req + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_reject_addba_req(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_11n_cfg *cfg = MNULL; + HostCmd_DS_REJECT_ADDBA_REQ *preject_addba_req = + &resp->params.rejectaddbareq; + + ENTER(); + if (pioctl_buf && (wlan_le16_to_cpu(preject_addba_req->action) == + HostCmd_ACT_GEN_GET)) { + cfg = (mlan_ds_11n_cfg *)pioctl_buf->pbuf; + cfg->param.reject_addba_req.conditions = + wlan_le32_to_cpu(preject_addba_req->conditions); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares TX BF configuration command + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_tx_bf_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, 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_ext(pmadapter, txbfcfg->body.bf_periodicity.peer_mac, + txbf->body.bf_periodicity[0].peer_mac, + MLAN_MAC_ADDR_LENGTH, 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_ext(pmadapter, + txbfcfg->body.bf_sound_args.peer_mac, + txbf->body.bf_sound[0].peer_mac, + MLAN_MAC_ADDR_LENGTH, 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_ext(pmadapter, txbfcfg->body.tx_bf_peer.peer_mac, + txbf->body.tx_bf_peer[0].peer_mac, + MLAN_MAC_ADDR_LENGTH, 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_ext(pmadapter, txbfcfg->body.bf_snr.peer_mac, + txbf->body.bf_snr[0].peer_mac, + MLAN_MAC_ADDR_LENGTH, 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(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + 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_ext(pmadapter, txbf->body.bf_sound[0].peer_mac, + txbfcfg->body.bf_sound_args.peer_mac, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + txbf->body.bf_sound[0].status = + txbfcfg->body.bf_sound_args.status; + break; + case SET_GET_BF_PERIODICITY: + memcpy_ext(pmadapter, + txbf->body.bf_periodicity->peer_mac, + txbfcfg->body.bf_periodicity.peer_mac, + MLAN_MAC_ADDR_LENGTH, 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_ext(pmadapter, + txbf->body.tx_bf_peer[i].peer_mac, + (t_u8 *)tx_bf_peer->peer_mac, + MLAN_MAC_ADDR_LENGTH, + 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_ext(pmadapter, + txbf->body.bf_snr[i].peer_mac, + (t_u8 *)bf_snr->peer_mac, + MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + txbf->body.bf_snr[i].snr = bf_snr->snr; + bf_snr++; + } + break; + default: + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get second channel offset + * + * @param chan channel num + * @return second channel offset + */ +t_u8 wlan_get_second_channel_offset(int chan) +{ + t_u8 chan2Offset = SEC_CHAN_NONE; + + switch (chan) { + case 36: + case 44: + case 52: + case 60: + case 100: + case 108: + case 116: + case 124: + case 132: + case 140: + case 149: + case 157: + chan2Offset = SEC_CHAN_ABOVE; + break; + case 40: + case 48: + case 56: + case 64: + case 104: + case 112: + case 120: + case 128: + case 136: + case 144: + case 153: + case 161: + chan2Offset = SEC_CHAN_BELOW; + break; + case 165: + /* Special Case: 20Mhz-only Channel */ + chan2Offset = SEC_CHAN_NONE; + break; + } + return chan2Offset; +} + +#ifdef STA_SUPPORT +/** + * @brief validate the channel offset for Infra/Ad-hoc band configuration + * + * @param pmpriv A pointer to mlan_private structure + * @param band band + * @param chan primary channel + * @param chan_bw channel bandwidth + * + * @return channel offset (NO_SEC_CHANNEL, SEC_CHANNEL_ABOVE, + * SEC_CHANNEL_BELOW) + */ +t_u8 wlan_validate_chan_offset(mlan_private *pmpriv, t_u16 band, t_u32 chan, + t_u8 chan_bw) +{ + t_u8 chan_offset; + pmlan_adapter pmadapter = pmpriv->adapter; + + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE) + chan_offset = SEC_CHAN_ABOVE; + else if (chan_bw == CHANNEL_BW_40MHZ_BELOW) + chan_offset = SEC_CHAN_BELOW; + else + chan_offset = SEC_CHAN_NONE; + + /* validation */ + if (chan_offset != SEC_CHAN_NONE) { + if (band & BAND_GN) { + if ((chan == 1) || (chan == 2) || (chan == 3) || + (chan == 4)) + chan_offset = SEC_CHAN_ABOVE; + else if ((chan == 10) || (chan == 11) || (chan == 12) || + (chan == 13)) + chan_offset = SEC_CHAN_BELOW; + + /* check if channel 12 is supported in the region */ + if (!wlan_find_cfp_by_band_and_channel(pmadapter, band, + 12)) + if ((chan == 8) || (chan == 9)) + chan_offset = SEC_CHAN_BELOW; + } else if (band & BAND_AN) + chan_offset = wlan_get_second_channel_offset(chan); + } + return chan_offset; +} + +/** + * @brief This function check if ht40 is allowed in current region + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSSDescriptor_t structure + * + * @return MTRUE/MFALSE + */ +static int wlan_check_chan_width_ht40_by_region(mlan_private *pmpriv, + BSSDescriptor_t *pbss_desc) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + int i = 0; + int cover_pri_chan = MFALSE; + t_u8 pri_chan; + t_u8 chan_offset; + t_u8 num_cfp; + + ENTER(); + + if (pbss_desc->pht_info == MNULL) { + PRINTM(MERROR, "ht_info pointer NULL, force use HT20\n"); + LEAVE(); + return MFALSE; + } + if (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS && + pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS) { + LEAVE(); + return MFALSE; + } + + pri_chan = pbss_desc->pht_info->ht_info.pri_chan; + chan_offset = GET_SECONDARYCHAN(pbss_desc->pht_info->ht_info.field2); + if ((chan_offset == SEC_CHAN_ABOVE) && + (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40PLUS)) { + pmpriv->curr_chan_flags |= + CHAN_FLAGS_NO_HT40MINUS | CHAN_FLAGS_NO_80MHZ; + return MFALSE; + } + if ((chan_offset == SEC_CHAN_BELOW) && + (pmpriv->curr_chan_flags & CHAN_FLAGS_NO_HT40MINUS)) { + pmpriv->curr_chan_flags |= + CHAN_FLAGS_NO_HT40PLUS | CHAN_FLAGS_NO_80MHZ; + return MFALSE; + } + if (pmpriv->curr_chan_flags & CHAN_FLAGS_MAX) + return MTRUE; + + num_cfp = pmadapter->region_channel[0].num_cfp; + + if ((pbss_desc->bss_band & (BAND_B | BAND_G)) && + pmadapter->region_channel[0].valid) { + for (i = 0; i < num_cfp; i++) { + if (pri_chan == + pmadapter->region_channel[0].pcfp[i].channel) { + cover_pri_chan = MTRUE; + break; + } + } + if (!cover_pri_chan) { + PRINTM(MERROR, "Invalid channel, force use HT20\n"); + LEAVE(); + return MFALSE; + } + + if (chan_offset == SEC_CHAN_ABOVE) { + if (pri_chan > num_cfp - 4) { + PRINTM(MERROR, + "Invalid second channel offset, force use HT20\n"); + LEAVE(); + return MFALSE; + } + } + } + LEAVE(); + return MTRUE; +} + +/** + * @brief This function append the 802_11N tlv + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSSDescriptor_t structure + * @param ppbuffer A Pointer to command buffer pointer + * + * @return bytes added to the buffer + */ +int wlan_cmd_append_11n_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, + t_u8 **ppbuffer) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + MrvlIETypes_HTCap_t *pht_cap; + MrvlIEtypes_ChanListParamSet_t *pchan_list; + MrvlIETypes_2040BSSCo_t *p2040_bss_co; + MrvlIETypes_ExtCap_t *pext_cap; + t_u32 usr_dot_11n_dev_cap, orig_usr_dot_11n_dev_cap = 0; + t_u32 usr_vht_cap_info; + t_u8 usr_dot_11ac_bw; + int ret_len = 0; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (pbss_desc == MNULL) { + LEAVE(); + return 0; + } + + if (pbss_desc->bss_band & BAND_A) + usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = pmpriv->usr_dot_11n_dev_cap_bg; + + if (pbss_desc->bss_band & BAND_A) + usr_vht_cap_info = pmpriv->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = pmpriv->usr_dot_11ac_dev_cap_bg; + if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) + usr_dot_11ac_bw = BW_FOLLOW_VHTCAP; + else + usr_dot_11ac_bw = pmpriv->usr_dot_11ac_bw; + if ((pbss_desc->bss_band & (BAND_B | BAND_G | BAND_A)) && + ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + !wlan_check_chan_width_ht40_by_region(pmpriv, pbss_desc)) { + orig_usr_dot_11n_dev_cap = usr_dot_11n_dev_cap; + RESETSUPP_CHANWIDTH40(usr_dot_11n_dev_cap); + RESET_40MHZ_INTOLARENT(usr_dot_11n_dev_cap); + RESETSUPP_SHORTGI40(usr_dot_11n_dev_cap); + pmpriv->usr_dot_11n_dev_cap_bg = usr_dot_11n_dev_cap; + pbss_desc->curr_bandwidth = BW_20MHZ; + } + if (pbss_desc->pht_cap) { + pht_cap = (MrvlIETypes_HTCap_t *)*ppbuffer; + memset(pmadapter, pht_cap, 0, sizeof(MrvlIETypes_HTCap_t)); + pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY); + pht_cap->header.len = sizeof(HTCap_t); + memcpy_ext(pmadapter, + (t_u8 *)pht_cap + sizeof(MrvlIEtypesHeader_t), + (t_u8 *)pbss_desc->pht_cap + + sizeof(IEEEtypes_Header_t), + pht_cap->header.len, pht_cap->header.len); + + pht_cap->ht_cap.ht_cap_info = + wlan_le16_to_cpu(pht_cap->ht_cap.ht_cap_info); + pht_cap->ht_cap.ht_ext_cap = + wlan_le16_to_cpu(pht_cap->ht_cap.ht_ext_cap); + wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pbss_desc->bss_band, + MTRUE); + + /** check if need support 80+80MHZ */ + /** reset the 2 spatial stream rate for 80 + 80 Mhz */ + if (wlan_is_80_80_support(pmpriv, pbss_desc)) + pht_cap->ht_cap.supported_mcs_set[1] = 0; + HEXDUMP("HT_CAPABILITIES IE", (t_u8 *)pht_cap, + sizeof(MrvlIETypes_HTCap_t)); + *ppbuffer += sizeof(MrvlIETypes_HTCap_t); + ret_len += sizeof(MrvlIETypes_HTCap_t); + pht_cap->header.len = wlan_cpu_to_le16(pht_cap->header.len); + } else { + // AP don't support 11N + LEAVE(); + return 0; + } + + if (pbss_desc->pht_info) { + pchan_list = (MrvlIEtypes_ChanListParamSet_t *)*ppbuffer; + memset(pmadapter, pchan_list, 0, + sizeof(MrvlIEtypes_ChanListParamSet_t)); + pchan_list->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_list->header.len = + sizeof(MrvlIEtypes_ChanListParamSet_t) - + sizeof(MrvlIEtypesHeader_t); + pchan_list->chan_scan_param[0].chan_number = + pbss_desc->pht_info->ht_info.pri_chan; + pchan_list->chan_scan_param[0].bandcfg.chanBand = + wlan_band_to_radio_type((t_u8)pbss_desc->bss_band); + /* support the VHT if the network to be join has the VHT + * operation */ + if (ISSUPP_11ACENABLED(pmadapter->fw_cap_info) && + (usr_dot_11ac_bw == BW_FOLLOW_VHTCAP) && + (!(pmpriv->curr_chan_flags & CHAN_FLAGS_NO_80MHZ)) && + wlan_11ac_bandconfig_allowed(pmpriv, pbss_desc->bss_band) && + pbss_desc->pvht_oprat && + pbss_desc->pvht_oprat->chan_width == VHT_OPER_CHWD_80MHZ) { + pchan_list->chan_scan_param[0].bandcfg.chanWidth = + CHAN_BW_80MHZ; + pchan_list->chan_scan_param[0].bandcfg.chan2Offset = + GET_SECONDARYCHAN( + pbss_desc->pht_info->ht_info.field2); + pbss_desc->curr_bandwidth = BW_80MHZ; + } else if (ISSUPP_CHANWIDTH40(usr_dot_11n_dev_cap) && + ISALLOWED_CHANWIDTH40( + pbss_desc->pht_info->ht_info.field2) && + wlan_check_chan_width_ht40_by_region(pmpriv, + pbss_desc)) { + pchan_list->chan_scan_param[0].bandcfg.chan2Offset = + GET_SECONDARYCHAN( + pbss_desc->pht_info->ht_info.field2); + pbss_desc->curr_bandwidth = BW_40MHZ; + pchan_list->chan_scan_param[0].bandcfg.chanWidth = + CHAN_BW_40MHZ; + } + pchan_list->chan_scan_param[0].bandcfg.scanMode = + SCAN_MODE_USER; + HEXDUMP("ChanList", (t_u8 *)pchan_list, + sizeof(MrvlIEtypes_ChanListParamSet_t)); + HEXDUMP("pht_info", (t_u8 *)pbss_desc->pht_info, + sizeof(MrvlIETypes_HTInfo_t) - 2); + *ppbuffer += sizeof(MrvlIEtypes_ChanListParamSet_t); + ret_len += sizeof(MrvlIEtypes_ChanListParamSet_t); + pchan_list->header.len = + wlan_cpu_to_le16(pchan_list->header.len); + } + + if (pbss_desc->pbss_co_2040) { + p2040_bss_co = (MrvlIETypes_2040BSSCo_t *)*ppbuffer; + memset(pmadapter, p2040_bss_co, 0, + sizeof(MrvlIETypes_2040BSSCo_t)); + p2040_bss_co->header.type = wlan_cpu_to_le16(BSSCO_2040); + p2040_bss_co->header.len = sizeof(BSSCo2040_t); + + memcpy_ext(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, 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_ext(pmadapter, + (t_u8 *)pext_cap + sizeof(MrvlIEtypesHeader_t), + (t_u8 *)&pmpriv->ext_cap, sizeof(ExtCap_t), + pext_cap->header.len); + if (!pmadapter->ecsa_enable) + RESET_EXTCAP_EXT_CHANNEL_SWITCH(pext_cap->ext_cap); + else + SET_EXTCAP_EXT_CHANNEL_SWITCH(pext_cap->ext_cap); + + HEXDUMP("Extended Capabilities IE", (t_u8 *)pext_cap, + sizeof(MrvlIETypes_ExtCap_t)); + *ppbuffer += sizeof(MrvlIETypes_ExtCap_t); + ret_len += sizeof(MrvlIETypes_ExtCap_t); + pext_cap->header.len = wlan_cpu_to_le16(pext_cap->header.len); + } else if (wlan_is_ext_capa_support(pmpriv) || + (pmpriv->config_bands & BAND_AAC)) { + wlan_add_ext_capa_info_ie(pmpriv, pbss_desc, ppbuffer); + ret_len += sizeof(MrvlIETypes_ExtCap_t); + } + PRINTM(MCMND, "curr_bandwidth=%d\n", pbss_desc->curr_bandwidth); + if (orig_usr_dot_11n_dev_cap) + pmpriv->usr_dot_11n_dev_cap_bg = orig_usr_dot_11n_dev_cap; + + LEAVE(); + return ret_len; +} + +#endif /* STA_SUPPORT */ + +/** + * @brief 11n configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_11n_cfg_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_11n_cfg *cfg = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11n_cfg)) { + PRINTM(MINFO, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_11n_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + cfg = (mlan_ds_11n_cfg *)pioctl_req->pbuf; + switch (cfg->sub_command) { + case MLAN_OID_11N_CFG_TX: + status = wlan_11n_ioctl_httxcfg(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_HTCAP_CFG: + status = wlan_11n_ioctl_htusrcfg(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_AGGR_PRIO_TBL: + status = wlan_11n_ioctl_aggr_prio_tbl(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_ADDBA_REJECT: + status = wlan_11n_ioctl_addba_reject(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_ADDBA_PARAM: + status = wlan_11n_ioctl_addba_param(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_DELBA: + status = wlan_11n_ioctl_delba(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_REJECT_ADDBA_REQ: + status = wlan_11n_ioctl_rejectaddbareq(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE: + status = wlan_11n_ioctl_max_tx_buf_size(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL: + status = wlan_11n_ioctl_amsdu_aggr_ctrl(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_SUPPORTED_MCS_SET: + status = + wlan_11n_ioctl_supported_mcs_set(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_TX_BF_CAP: + status = wlan_11n_ioctl_tx_bf_cap(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_TX_BF_CFG: + status = wlan_11n_ioctl_tx_bf_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_STREAM_CFG: + status = wlan_11n_ioctl_stream_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_COEX_RX_WINSIZE: + status = wlan_11n_ioctl_coex_rx_winsize(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM: + status = wlan_11n_ioctl_ibss_ampdu_param(pmadapter, pioctl_req); + break; + case MLAN_OID_11N_CFG_MIN_BA_THRESHOLD: + status = wlan_11n_ioctl_min_ba_threshold_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(); + + if (!ptx_tbl || !wlan_is_txbastreamptr_valid(priv, ptx_tbl)) + goto exit; + PRINTM(MINFO, "Delete BA stream table entry: %p\n", ptx_tbl); + util_unlink_list(pmadapter->pmoal_handle, &priv->tx_ba_stream_tbl_ptr, + (pmlan_linked_list)ptx_tbl, MNULL, MNULL); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)ptx_tbl); +exit: + LEAVE(); +} + +/** + * @brief This function will delete all the entries in Tx BA Stream table + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +void wlan_11n_deleteall_txbastream_tbl(mlan_private *priv) +{ + int i; + TxBAStreamTbl *del_tbl_ptr = MNULL; + + ENTER(); + + wlan_request_ralist_lock(priv); + while ((del_tbl_ptr = (TxBAStreamTbl *)util_peek_list( + priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, MNULL, MNULL))) { + wlan_11n_delete_txbastream_tbl_entry(priv, del_tbl_ptr); + } + + util_init_list((pmlan_linked_list)&priv->tx_ba_stream_tbl_ptr); + wlan_release_ralist_lock(priv); + for (i = 0; i < MAX_NUM_TID; ++i) { + priv->aggr_prio_tbl[i].ampdu_ap = + priv->aggr_prio_tbl[i].ampdu_user; + } + + LEAVE(); +} + +/** + * @brief This function will return the pointer to an entry in BA Stream + * table which matches the give RA/TID pair + * + * @param priv A pointer to mlan_private + * @param tid TID to find in reordering table + * @param ra RA to find in reordering table + * @param lock flag for request the spin_lock + * + * @return A pointer to first entry matching RA/TID in BA stream + * NULL if not found + */ +TxBAStreamTbl *wlan_11n_get_txbastream_tbl(mlan_private *priv, int tid, + t_u8 *ra, int lock) +{ + TxBAStreamTbl *ptx_tbl; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (lock) + wlan_request_ralist_lock(priv); + ptx_tbl = (TxBAStreamTbl *)util_peek_list(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + if (lock) + wlan_release_ralist_lock(priv); + LEAVE(); + return MNULL; + } + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + PRINTM(MDAT_D, "get_txbastream_tbl TID %d\n", ptx_tbl->tid); + DBG_HEXDUMP(MDAT_D, "RA", ptx_tbl->ra, MLAN_MAC_ADDR_LENGTH); + + if ((!memcmp(pmadapter, ptx_tbl->ra, ra, + MLAN_MAC_ADDR_LENGTH)) && + (ptx_tbl->tid == tid)) { + if (lock) + wlan_release_ralist_lock(priv); + LEAVE(); + return ptx_tbl; + } + + ptx_tbl = ptx_tbl->pnext; + } + if (lock) + wlan_release_ralist_lock(priv); + LEAVE(); + return MNULL; +} + +/** + * @brief This function will create a entry in tx ba stream table for the + * given RA/TID. + * + * @param priv A pointer to mlan_private + * @param ra RA to find in reordering table + * @param tid TID to find in reordering table + * @param ba_status BA stream status to create the stream with + * + * @return N/A + */ +void wlan_11n_create_txbastream_tbl(mlan_private *priv, t_u8 *ra, int tid, + baStatus_e ba_status) +{ + TxBAStreamTbl *new_node = MNULL; + pmlan_adapter pmadapter = priv->adapter; + raListTbl *ra_list = MNULL; + int tid_down; + + ENTER(); + + PRINTM(MDAT_D, "create_txbastream_tbl TID %d\n", tid); + DBG_HEXDUMP(MDAT_D, "RA", ra, MLAN_MAC_ADDR_LENGTH); + + if (pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, sizeof(TxBAStreamTbl), + MLAN_MEM_DEF, (t_u8 **)&new_node)) { + PRINTM(MERROR, + "wlan_11n_create_txbastream_tbl Failed to allocate new_node\n"); + LEAVE(); + return; + } + tid_down = wlan_get_wmm_tid_down(priv, tid); + ra_list = wlan_wmm_get_ralist_node(priv, tid_down, ra); + if (ra_list) { + ra_list->amsdu_in_ampdu = MFALSE; + ra_list->ba_status = ba_status; + } + util_init_list((pmlan_linked_list)new_node); + + new_node->tid = tid; + new_node->ba_status = ba_status; + memcpy_ext(pmadapter, new_node->ra, ra, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + (pmlan_linked_list)new_node, MNULL, MNULL); + + LEAVE(); +} + +/** + * @brief This function will send a block ack to given tid/ra + * + * @param priv A pointer to mlan_private + * @param tid TID to send the ADDBA + * @param peer_mac MAC address to send the ADDBA + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int wlan_send_addba(mlan_private *priv, int tid, t_u8 *peer_mac) +{ + HostCmd_DS_11N_ADDBA_REQ add_ba_req; + static t_u8 dialog_tok; + mlan_status ret; + + ENTER(); + + PRINTM(MCMND, "Send addba: TID %d\n", tid); + DBG_HEXDUMP(MCMD_D, "Send addba RA", peer_mac, MLAN_MAC_ADDR_LENGTH); + + add_ba_req.block_ack_param_set = (t_u16)( + (tid << BLOCKACKPARAM_TID_POS) | + (priv->add_ba_param.tx_win_size << BLOCKACKPARAM_WINSIZE_POS) | + IMMEDIATE_BLOCK_ACK); + /** enable AMSDU inside AMPDU */ + if (priv->add_ba_param.tx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + add_ba_req.block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK; + add_ba_req.block_ack_tmo = (t_u16)priv->add_ba_param.timeout; + + ++dialog_tok; + + if (dialog_tok == 0) + dialog_tok = 1; + + add_ba_req.dialog_token = dialog_tok; + memcpy_ext(priv->adapter, &add_ba_req.peer_mac_addr, peer_mac, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + + /* We don't wait for the response of this command */ + ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, 0, 0, MNULL, + &add_ba_req); + + LEAVE(); + return ret; +} + +/** + * @brief This function will delete a block ack to given tid/ra + * + * @param priv A pointer to mlan_private + * @param pioctl_req A pointer to ioctl request buffer + * @param tid TID to send the ADDBA + * @param peer_mac MAC address to send the ADDBA + * @param initiator MTRUE if we have initiated ADDBA, MFALSE otherwise + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +int wlan_send_delba(mlan_private *priv, pmlan_ioctl_req pioctl_req, int tid, + t_u8 *peer_mac, int initiator) +{ + HostCmd_DS_11N_DELBA delba; + mlan_status ret; + + ENTER(); + + memset(priv->adapter, &delba, 0, sizeof(delba)); + delba.del_ba_param_set = (tid << DELBA_TID_POS); + + if (initiator) + DELBA_INITIATOR(delba.del_ba_param_set); + else + DELBA_RECIPIENT(delba.del_ba_param_set); + + memcpy_ext(priv->adapter, &delba.peer_mac_addr, peer_mac, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, HostCmd_ACT_GEN_SET, + 0, (t_void *)pioctl_req, (t_void *)&delba); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of + * delete a block ack request + * + * @param priv A pointer to mlan_private structure + * @param del_ba A pointer to command response buffer + * + * @return N/A + */ +void wlan_11n_delete_bastream(mlan_private *priv, t_u8 *del_ba) +{ + HostCmd_DS_11N_DELBA *pdel_ba = (HostCmd_DS_11N_DELBA *)del_ba; + int tid; + + ENTER(); + + DBG_HEXDUMP(MCMD_D, "Delba:", (t_u8 *)pdel_ba, 20); + pdel_ba->del_ba_param_set = wlan_le16_to_cpu(pdel_ba->del_ba_param_set); + pdel_ba->reason_code = wlan_le16_to_cpu(pdel_ba->reason_code); + + tid = pdel_ba->del_ba_param_set >> DELBA_TID_POS; + + mlan_11n_delete_bastream_tbl(priv, tid, pdel_ba->peer_mac_addr, + TYPE_DELBA_RECEIVE, + INITIATOR_BIT(pdel_ba->del_ba_param_set), + pdel_ba->reason_code); + + LEAVE(); +} + +/** + * @brief Get Rx reordering table + * + * @param priv A pointer to mlan_private structure + * @param buf A pointer to rx_reorder_tbl structure + * @return number of rx reorder table entry + */ +int wlan_get_rxreorder_tbl(mlan_private *priv, rx_reorder_tbl *buf) +{ + int i; + rx_reorder_tbl *ptbl = buf; + RxReorderTbl *rx_reorder_tbl_ptr; + int count = 0; + ENTER(); + priv->adapter->callbacks.moal_spin_lock(priv->adapter->pmoal_handle, + priv->rx_reorder_tbl_ptr.plock); + rx_reorder_tbl_ptr = + (RxReorderTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, MNULL, + MNULL); + if (!rx_reorder_tbl_ptr) { + priv->adapter->callbacks.moal_spin_unlock( + priv->adapter->pmoal_handle, + priv->rx_reorder_tbl_ptr.plock); + LEAVE(); + return count; + } + while (rx_reorder_tbl_ptr != + (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + ptbl->tid = (t_u16)rx_reorder_tbl_ptr->tid; + memcpy_ext(priv->adapter, ptbl->ta, rx_reorder_tbl_ptr->ta, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + ptbl->start_win = rx_reorder_tbl_ptr->start_win; + ptbl->win_size = rx_reorder_tbl_ptr->win_size; + ptbl->amsdu = rx_reorder_tbl_ptr->amsdu; + for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) + ptbl->buffer[i] = MTRUE; + else + ptbl->buffer[i] = MFALSE; + } + rx_reorder_tbl_ptr = rx_reorder_tbl_ptr->pnext; + ptbl++; + count++; + if (count >= MLAN_MAX_RX_BASTREAM_SUPPORTED) + break; + } + priv->adapter->callbacks.moal_spin_unlock( + priv->adapter->pmoal_handle, priv->rx_reorder_tbl_ptr.plock); + LEAVE(); + return count; +} + +/** + * @brief Get transmit BA stream table + * + * @param priv A pointer to mlan_private structure + * @param buf A pointer to tx_ba_stream_tbl structure + * @return number of ba stream table entry + */ +int wlan_get_txbastream_tbl(mlan_private *priv, tx_ba_stream_tbl *buf) +{ + TxBAStreamTbl *ptxtbl; + tx_ba_stream_tbl *ptbl = buf; + int count = 0; + t_u32 bastream_max = 0; + + ENTER(); + + wlan_request_ralist_lock(priv); + ptxtbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptxtbl) { + wlan_release_ralist_lock(priv); + LEAVE(); + return count; + } + bastream_max = ISSUPP_GETTXBASTREAM(priv->adapter->hw_dot_11n_dev_cap); + if (bastream_max == 0) + bastream_max = MLAN_MAX_TX_BASTREAM_DEFAULT; + + while (ptxtbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + ptbl->tid = (t_u16)ptxtbl->tid; + PRINTM(MINFO, "tid=%d\n", ptbl->tid); + memcpy_ext(priv->adapter, ptbl->ra, ptxtbl->ra, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + ptbl->amsdu = ptxtbl->amsdu; + ptxtbl = ptxtbl->pnext; + ptbl++; + count++; + if (count >= bastream_max) + break; + } + wlan_release_ralist_lock(priv); + LEAVE(); + return count; +} + +/** + * @brief This function check if 11AC is allowed in bandcfg + * + * @param pmpriv A pointer to mlan_private structure + * @param bss_band bss band + * + * @return 0--not allowed, other value allowed + */ +t_u8 wlan_11n_bandconfig_allowed(mlan_private *pmpriv, t_u8 bss_band) +{ + if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { + if (bss_band & BAND_G) + return (pmpriv->adapter->adhoc_start_band & BAND_GN); + else if (bss_band & BAND_A) + return (pmpriv->adapter->adhoc_start_band & BAND_AN); + } else { + if (bss_band & BAND_G) + return (pmpriv->config_bands & BAND_GN); + else if (bss_band & BAND_A) + return (pmpriv->config_bands & BAND_AN); + } + return 0; +} + +/** + * @brief This function cleans up txbastream_tbl for specific station + * + * @param priv A pointer to mlan_private + * @param ra RA to find in txbastream_tbl + * @return N/A + */ +void wlan_11n_cleanup_txbastream_tbl(mlan_private *priv, t_u8 *ra) +{ + TxBAStreamTbl *ptx_tbl = MNULL; + t_u8 i; + ENTER(); + + wlan_request_ralist_lock(priv); + for (i = 0; i < MAX_NUM_TID; ++i) { + ptx_tbl = wlan_11n_get_txbastream_tbl(priv, i, ra, MFALSE); + if (ptx_tbl) + wlan_11n_delete_txbastream_tbl_entry(priv, ptx_tbl); + } + wlan_release_ralist_lock(priv); + LEAVE(); + return; +} + +void wlan_update_11n_cap(mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + + pmpriv->usr_dev_mcs_support = pmadapter->hw_dev_mcs_support; + pmpriv->usr_dot_11n_dev_cap_bg = + pmadapter->hw_dot_11n_dev_cap & DEFAULT_11N_CAP_MASK_BG; + pmpriv->usr_dot_11n_dev_cap_a = + pmadapter->hw_dot_11n_dev_cap & DEFAULT_11N_CAP_MASK_A; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11n.h b/mxm_wifiex/wlan_src/mlan/mlan_11n.h new file mode 100644 index 0000000..9e3de59 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11n.h @@ -0,0 +1,408 @@ +/** @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 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +/** Prepare 11ncfg command */ +mlan_status wlan_cmd_11n_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +/** Prepare reject addba requst command */ +mlan_status wlan_cmd_reject_addba_req(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); +/** Handle the command response of rejecting addba request */ +mlan_status wlan_ret_reject_addba_req(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +/** Prepare TX BF configuration command */ +mlan_status wlan_cmd_tx_bf_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +/** Handle the command response TX BF configuration */ +mlan_status wlan_ret_tx_bf_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +#ifdef STA_SUPPORT +t_u8 wlan_11n_bandconfig_allowed(mlan_private *pmpriv, t_u8 bss_band); +/** Append the 802_11N tlv */ +int wlan_cmd_append_11n_tlv(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, + t_u8 **ppbuffer); +/** wlan fill HT cap tlv */ +void wlan_fill_ht_cap_tlv(mlan_private *priv, MrvlIETypes_HTCap_t *pht_cap, + t_u16 band, t_u8 fill); +/** wlan fill HT cap IE */ +void wlan_fill_ht_cap_ie(mlan_private *priv, IEEEtypes_HTCap_t *pht_cap, + t_u16 bands); +#endif /* STA_SUPPORT */ +/** Miscellaneous configuration handler */ +mlan_status wlan_11n_cfg_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +/** Delete Tx BA stream table entry */ +void wlan_11n_delete_txbastream_tbl_entry(mlan_private *priv, + TxBAStreamTbl *ptx_tbl); +/** Delete all Tx BA stream table entries */ +void wlan_11n_deleteall_txbastream_tbl(mlan_private *priv); +/** Get Tx BA stream table */ +TxBAStreamTbl *wlan_11n_get_txbastream_tbl(mlan_private *priv, int tid, + t_u8 *ra, int lock); +/** Create Tx BA stream table */ +void wlan_11n_create_txbastream_tbl(mlan_private *priv, t_u8 *ra, int tid, + baStatus_e ba_status); +/** Send ADD BA request */ +int wlan_send_addba(mlan_private *priv, int tid, t_u8 *peer_mac); +/** Send DEL BA request */ +int wlan_send_delba(mlan_private *priv, pmlan_ioctl_req pioctl_req, int tid, + t_u8 *peer_mac, int initiator); +/** This function handles the command response of delete a block ack request*/ +void wlan_11n_delete_bastream(mlan_private *priv, t_u8 *del_ba); +/** get rx reorder table */ +int wlan_get_rxreorder_tbl(mlan_private *priv, rx_reorder_tbl *buf); +/** get tx ba stream table */ +int wlan_get_txbastream_tbl(mlan_private *priv, tx_ba_stream_tbl *buf); +/** send delba */ +void wlan_11n_delba(mlan_private *priv, int tid); +/** update amdpdu tx win size */ +void wlan_update_ampdu_txwinsize(pmlan_adapter pmadapter); +/** Minimum number of AMSDU */ +#define MIN_NUM_AMSDU 2 +/** AMSDU Aggr control cmd resp */ +mlan_status wlan_ret_amsdu_aggr_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +void wlan_set_tx_pause_flag(mlan_private *priv, t_u8 flag); +/** reconfigure tx buf size */ +mlan_status wlan_cmd_recfg_tx_buf(mlan_private *priv, HostCmd_DS_COMMAND *cmd, + int cmd_action, void *pdata_buf); +/** AMSDU aggr control cmd */ +mlan_status wlan_cmd_amsdu_aggr_ctrl(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, int cmd_action, + void *pdata_buf); + +t_u8 wlan_validate_chan_offset(mlan_private *pmpriv, t_u16 band, t_u32 chan, + t_u8 chan_bw); +/** get channel offset */ +t_u8 wlan_get_second_channel_offset(int chan); + +void wlan_update_11n_cap(mlan_private *pmpriv); + +/** clean up txbastream_tbl */ +void wlan_11n_cleanup_txbastream_tbl(mlan_private *priv, t_u8 *ra); +/** + * @brief This function checks whether a station has 11N enabled or not + * + * @param priv A pointer to mlan_private + * @param mac station mac address + * @return MTRUE or MFALSE + */ +static INLINE t_u8 is_station_11n_enabled(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) + return (sta_ptr->is_11n_enabled) ? MTRUE : MFALSE; + return MFALSE; +} + +/** + * @brief This function get station max amsdu size + * + * @param priv A pointer to mlan_private + * @param mac station mac address + * @return max amsdu size statio supported + */ +static INLINE t_u16 get_station_max_amsdu_size(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) + return sta_ptr->max_amsdu; + return 0; +} + +/** + * @brief This function checks whether a station allows AMPDU or not + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param tid TID value for ptr + * @return MTRUE or MFALSE + */ +static INLINE t_u8 is_station_ampdu_allowed(mlan_private *priv, raListTbl *ptr, + int tid) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, ptr->ra); + if (sta_ptr) { + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (priv->sec_info.wapi_enabled && + !sta_ptr->wapi_key_on) + return MFALSE; + } + return (sta_ptr->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? + MTRUE : + MFALSE; + } + return MFALSE; +} + +/** + * @brief This function disable station ampdu for specific tid + * + * @param priv A pointer to mlan_private + * @param tid tid index + * @param ra station mac address + * @return N/A + */ +static INLINE void disable_station_ampdu(mlan_private *priv, t_u8 tid, t_u8 *ra) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, ra); + if (sta_ptr) + sta_ptr->ampdu_sta[tid] = BA_STREAM_NOT_ALLOWED; + return; +} + +/** + * @brief This function reset station ampdu for specific id to user setting. + * + * @param priv A pointer to mlan_private + * @param tid tid index + * @param ra station mac address + * @return N/A + */ +static INLINE void reset_station_ampdu(mlan_private *priv, t_u8 tid, t_u8 *ra) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, ra); + if (sta_ptr) + sta_ptr->ampdu_sta[tid] = priv->aggr_prio_tbl[tid].ampdu_user; + return; +} + +#define IS_BG_RATE (priv->bitmap_rates[0] || priv->bitmap_rates[1]) +/** + * @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) +{ + if ((!priv->is_data_rate_auto) && IS_BG_RATE) + return MFALSE; +#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; +} + +#define BA_RSSI_HIGH_THRESHOLD -70 + +static INLINE void wlan_update_station_del_ba_count(mlan_private *priv, + raListTbl *ptr) +{ + sta_node *sta_ptr = MNULL; + t_s8 rssi; + sta_ptr = wlan_get_station_entry(priv, ptr->ra); + if (sta_ptr) { + rssi = sta_ptr->snr - sta_ptr->nf; + if (rssi > BA_RSSI_HIGH_THRESHOLD) + ptr->del_ba_count = 0; + } + return; +} + +static INLINE void wlan_update_del_ba_count(mlan_private *priv, raListTbl *ptr) +{ + t_s8 rssi; +#ifdef UAP_802_11N +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + return wlan_update_station_del_ba_count(priv, ptr); +#endif /* UAP_SUPPORT */ +#endif /* UAP_802_11N */ + rssi = priv->snr - priv->nf; + if (rssi > BA_RSSI_HIGH_THRESHOLD) + ptr->del_ba_count = 0; +} + +/** + * @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; +#endif + if (priv->amsdu_disable) + return MFALSE; +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + sta_ptr = wlan_get_station_entry(priv, ptr->ra); + if (sta_ptr) { + if (priv->sec_info.wapi_enabled && + !sta_ptr->wapi_key_on) + return MFALSE; + } + } +#endif /* UAP_SUPPORT */ +#define TXRATE_BITMAP_INDEX_MCS0_7 2 + return ((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) && + ((priv->is_data_rate_auto) || + !(((priv->bitmap_rates[TXRATE_BITMAP_INDEX_MCS0_7]) & 0x03) || + IS_BG_RATE))) ? + MTRUE : + MFALSE; +} + +/** + * @brief This function checks whether a BA stream is available or not + * + * @param priv A pointer to mlan_private + * + * @return MTRUE or MFALSE + */ +static INLINE t_u8 wlan_is_bastream_avail(mlan_private *priv) +{ + mlan_private *pmpriv = MNULL; + t_u8 i = 0; + t_u32 bastream_num = 0; + t_u32 bastream_max = 0; + for (i = 0; i < priv->adapter->priv_num; i++) { + pmpriv = priv->adapter->priv[i]; + if (pmpriv) + bastream_num += wlan_wmm_list_len( + (pmlan_list_head)&pmpriv->tx_ba_stream_tbl_ptr); + } + bastream_max = ISSUPP_GETTXBASTREAM(priv->adapter->hw_dot_11n_dev_cap); + if (bastream_max == 0) + bastream_max = MLAN_MAX_TX_BASTREAM_DEFAULT; + return (bastream_num < bastream_max) ? MTRUE : MFALSE; +} + +/** + * @brief This function finds the stream to delete + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param ptr_tid TID value of ptr + * @param ptid A pointer to TID of stream to delete, if return MTRUE + * @param ra RA of stream to delete, if return MTRUE + * + * @return MTRUE or MFALSE + */ +static INLINE t_u8 wlan_find_stream_to_delete(mlan_private *priv, + raListTbl *ptr, int ptr_tid, + int *ptid, t_u8 *ra) +{ + int tid; + t_u8 ret = MFALSE; + TxBAStreamTbl *ptx_tbl; + + ENTER(); + + ptx_tbl = (TxBAStreamTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + MNULL, MNULL); + if (!ptx_tbl) { + LEAVE(); + return ret; + } + + tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; + + while (ptx_tbl != (TxBAStreamTbl *)&priv->tx_ba_stream_tbl_ptr) { + if (tid > priv->aggr_prio_tbl[ptx_tbl->tid].ampdu_user) { + tid = priv->aggr_prio_tbl[ptx_tbl->tid].ampdu_user; + *ptid = ptx_tbl->tid; + memcpy_ext(priv->adapter, ra, ptx_tbl->ra, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + ret = MTRUE; + } + + ptx_tbl = ptx_tbl->pnext; + } + LEAVE(); + return ret; +} + +/** + * @brief This function checks whether 11n is supported + * + * @param priv A pointer to mlan_private + * @param ra Address of the receiver STA + * + * @return MTRUE or MFALSE + */ +static INLINE int 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/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.c b/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.c new file mode 100644 index 0000000..17cd78d --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.c @@ -0,0 +1,603 @@ +/** @file mlan_11n_aggr.c + * + * @brief This file contains functions for 11n Aggregation. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" + +/******************************************************** + 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_ext(pmadapter, amsdu_buf, data, (MLAN_MAC_ADDR_LENGTH)*2, + (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_ext(pmadapter, amsdu_buf + amsdu_buf_offset, &snap, LLC_SNAP_LEN, + LLC_SNAP_LEN); + amsdu_buf_offset += LLC_SNAP_LEN; + + memcpy_ext(pmadapter, amsdu_buf + amsdu_buf_offset, data + dt_offset, + pkt_len - 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; + t_u8 hdr_len = sizeof(Eth803Hdr_t); + + ENTER(); + while (total_pkt_len >= hdr_len) { + /* Length will be in network format, change it to host */ + pkt_len = mlan_ntohs( + (*(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH)))); + if (pkt_len > total_pkt_len) { + PRINTM(MERROR, "Error in packet length.\n"); + break; + } + + pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ? + (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) : + 0; + data += pkt_len + pad + sizeof(Eth803Hdr_t); + total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t); + ++pkt_count; + } + LEAVE(); + return pkt_count; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Deaggregate the received AMSDU packet + * + * @param priv A pointer to mlan_private structure + * @param pmbuf A pointer to aggregated data packet + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_11n_deaggregate_pkt(mlan_private *priv, pmlan_buffer pmbuf) +{ + t_u16 pkt_len; + int total_pkt_len; + t_u8 *data; + mlan_adapter *pmadapter = priv->adapter; + t_u32 max_rx_data_size = MLAN_RX_DATA_BUF_SIZE; + int pad; + mlan_status ret = MLAN_STATUS_FAILURE; + RxPacketHdr_t *prx_pkt; + mlan_buffer *daggr_mbuf = MNULL; + t_u8 rfc1042_eth_hdr[MLAN_MAC_ADDR_LENGTH] = {0xaa, 0xaa, 0x03, + 0x00, 0x00, 0x00}; + t_u8 hdr_len = sizeof(Eth803Hdr_t); + t_u8 eapol_type[2] = {0x88, 0x8e}; + + ENTER(); + + data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + total_pkt_len = pmbuf->data_len; + + /* Sanity test */ +#if defined(USB) + if (IS_USB(pmadapter->card_type) && + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable) { + max_rx_data_size = + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_max; + if (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_mode == + MLAN_USB_AGGR_MODE_NUM) { + max_rx_data_size *= + MAX(MLAN_USB_MAX_PKT_SIZE, + pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_align); + max_rx_data_size = + MAX(max_rx_data_size, MLAN_RX_DATA_BUF_SIZE); + } + } +#endif + if (total_pkt_len > max_rx_data_size) { + PRINTM(MERROR, + "Total packet length greater than tx buffer" + " size %d\n", + total_pkt_len); + goto done; + } + + pmbuf->use_count = wlan_11n_get_num_aggrpkts(data, total_pkt_len); + + // rx_trace 7 + if (pmadapter->tp_state_on) + pmadapter->callbacks.moal_tp_accounting( + pmadapter->pmoal_handle, pmbuf, 7 /*RX_DROP_P3*/); + if (pmadapter->tp_state_drop_point == 7 /*RX_DROP_P3*/) + goto done; + + while (total_pkt_len >= hdr_len) { + prx_pkt = (RxPacketHdr_t *)data; + /* Length will be in network format, change it to host */ + pkt_len = mlan_ntohs( + (*(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH)))); + if (pkt_len > total_pkt_len) { + PRINTM(MERROR, + "Error in packet length: total_pkt_len = %d, pkt_len = %d\n", + total_pkt_len, pkt_len); + ret = MLAN_STATUS_FAILURE; + break; + } + + pad = (((pkt_len + sizeof(Eth803Hdr_t)) & 3)) ? + (4 - ((pkt_len + sizeof(Eth803Hdr_t)) & 3)) : + 0; + + total_pkt_len -= pkt_len + pad + sizeof(Eth803Hdr_t); + + if (memcmp(pmadapter, &prx_pkt->rfc1042_hdr, rfc1042_eth_hdr, + sizeof(rfc1042_eth_hdr)) == 0) { + memmove(pmadapter, data + LLC_SNAP_LEN, data, + (2 * MLAN_MAC_ADDR_LENGTH)); + data += LLC_SNAP_LEN; + pkt_len += sizeof(Eth803Hdr_t) - LLC_SNAP_LEN; + } else { + *(t_u16 *)(data + (2 * MLAN_MAC_ADDR_LENGTH)) = + (t_u16)0; + pkt_len += sizeof(Eth803Hdr_t); + } + daggr_mbuf = wlan_alloc_mlan_buffer(pmadapter, + pkt_len + MLAN_NET_IP_ALIGN, + 0, MOAL_ALLOC_MLAN_BUFFER); + if (daggr_mbuf == MNULL) { + PRINTM(MERROR, "Error allocating daggr mlan_buffer\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + daggr_mbuf->data_offset += MLAN_NET_IP_ALIGN; + 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_ext(pmadapter, + daggr_mbuf->pbuf + daggr_mbuf->data_offset, data, + pkt_len, daggr_mbuf->data_len); + +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_uap_recv_packet(priv, daggr_mbuf); + } else { +#endif /* UAP_SUPPORT */ + /** send EAPOL from AMSDU pkt to firmware */ + if (priv->sec_info.ewpa_enabled && + (!memcmp(pmadapter, + daggr_mbuf->pbuf + + daggr_mbuf->data_offset + + MLAN_ETHER_PKT_TYPE_OFFSET, + eapol_type, sizeof(eapol_type)))) { + ret = wlan_prepare_cmd( + priv, HostCmd_CMD_802_11_EAPOL_PKT, 0, + 0, MNULL, daggr_mbuf); + if (ret == MLAN_STATUS_SUCCESS) + wlan_recv_event( + priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + wlan_free_mlan_buffer(pmadapter, daggr_mbuf); + data += pkt_len + pad; + continue; + } + + ret = pmadapter->callbacks.moal_recv_packet( + pmadapter->pmoal_handle, daggr_mbuf); +#ifdef UAP_SUPPORT + } +#endif /* UAP_SUPPORT */ + switch (ret) { + case MLAN_STATUS_PENDING: + break; + case MLAN_STATUS_FAILURE: + PRINTM(MERROR, "Deaggr, send to moal failed\n"); + daggr_mbuf->status_code = MLAN_ERROR_PKT_INVALID; + /* fall through */ + case MLAN_STATUS_SUCCESS: + wlan_recv_packet_complete(pmadapter, daggr_mbuf, ret); + break; + default: + break; + } + + data += pkt_len + pad; + } + +done: + priv->msdu_in_rx_amsdu_cnt += pmbuf->use_count; + priv->amsdu_rx_cnt++; + /** we should free the aggr buffer after deaggr */ + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + LEAVE(); + return ret; +} + +/** + * @brief Aggregate multiple packets into one single AMSDU packet + * + * @param priv A pointer to mlan_private structure + * @param pra_list Pointer to the RA List table containing the pointers + * to packets. + * @param headroom Any interface specific headroom that may be need. TxPD + * will be formed leaving this headroom. + * @param ptrindex Pointer index + * + * @return Final packet size or MLAN_STATUS_FAILURE + */ +int wlan_11n_aggregate_pkt(mlan_private *priv, raListTbl *pra_list, + int headroom, int ptrindex) +{ + int pkt_size = 0; + pmlan_adapter pmadapter = priv->adapter; + mlan_buffer *pmbuf_aggr, *pmbuf_src; + t_u8 *data; + int pad = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + mlan_tx_param tx_param; +#ifdef STA_SUPPORT + TxPD *ptx_pd = MNULL; +#endif + t_u32 max_amsdu_size = MIN(pra_list->max_amsdu, pmadapter->tx_buf_size); + ENTER(); + + PRINTM(MDAT_D, "Handling Aggr packet\n"); + + pmbuf_src = (pmlan_buffer)util_peek_list( + pmadapter->pmoal_handle, &pra_list->buf_head, MNULL, MNULL); + if (pmbuf_src) { + pmbuf_aggr = wlan_alloc_mlan_buffer(pmadapter, + pmadapter->tx_buf_size, 0, + MOAL_MALLOC_BUFFER); + if (!pmbuf_aggr) { + PRINTM(MERROR, "Error allocating mlan_buffer\n"); + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + data = pmbuf_aggr->pbuf + headroom; + pmbuf_aggr->bss_index = pmbuf_src->bss_index; + pmbuf_aggr->buf_type = pmbuf_src->buf_type; + pmbuf_aggr->priority = pmbuf_src->priority; + pmbuf_aggr->pbuf = data; + pmbuf_aggr->data_offset = 0; + pmbuf_aggr->in_ts_sec = pmbuf_src->in_ts_sec; + pmbuf_aggr->in_ts_usec = pmbuf_src->in_ts_usec; + if (pmbuf_src->flags & MLAN_BUF_FLAG_TCP_ACK) + pmbuf_aggr->flags |= MLAN_BUF_FLAG_TCP_ACK; + + /* Form AMSDU */ + wlan_11n_form_amsdu_txpd(priv, pmbuf_aggr); + pkt_size = sizeof(TxPD); +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + ptx_pd = (TxPD *)pmbuf_aggr->pbuf; +#endif + priv->msdu_in_tx_amsdu_cnt++; + } else { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + goto exit; + } + + while (pmbuf_src && ((pkt_size + (pmbuf_src->data_len + LLC_SNAP_LEN) + + headroom) <= max_amsdu_size)) { + pmbuf_src = + (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle, + &pra_list->buf_head, + MNULL, MNULL); + /* Collects TP statistics */ + if (pmadapter->tp_state_on && (pkt_size > sizeof(TxPD))) + pmadapter->callbacks.moal_tp_accounting( + pmadapter->pmoal_handle, pmbuf_src->pdesc, 3); + 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); + + if (pmbuf_src) { + pkt_size += wlan_11n_form_amsdu_pkt( + pmadapter, (data + pkt_size), + pmbuf_src->pbuf + pmbuf_src->data_offset, + pmbuf_src->data_len, &pad); + + DBG_HEXDUMP(MDAT_D, "pmbuf_src", pmbuf_src, + sizeof(mlan_buffer)); + wlan_write_data_complete(pmadapter, pmbuf_src, + MLAN_STATUS_SUCCESS); + } + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + if (!wlan_is_ralist_valid(priv, pra_list, ptrindex)) { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pmbuf_src = + (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &pra_list->buf_head, MNULL, + MNULL); + priv->msdu_in_tx_amsdu_cnt++; + } + + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + /* Last AMSDU packet does not need padding */ + pkt_size -= pad; + pmbuf_aggr->data_len = pkt_size; + wlan_11n_update_pktlen_amsdu_txpd(priv, pmbuf_aggr); + pmbuf_aggr->data_len += headroom; + pmbuf_aggr->pbuf = data - headroom; + tx_param.next_pkt_len = + ((pmbuf_src) ? pmbuf_src->data_len + sizeof(TxPD) : 0); + /* Collects TP statistics */ + if (pmadapter->tp_state_on) + pmadapter->callbacks.moal_tp_accounting(pmadapter->pmoal_handle, + pmbuf_aggr, 4); + + /* Drop Tx packets at drop point 4 */ + if (pmadapter->tp_state_drop_point == 4) { + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + goto exit; + } else + ret = pmadapter->ops.host_to_card(priv, MLAN_TYPE_DATA, + pmbuf_aggr, &tx_param); + switch (ret) { +#ifdef USB + case MLAN_STATUS_PRESOURCE: + PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n"); + break; +#endif + 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; + if (ptx_pd != MNULL) + 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: + pmbuf_aggr->status_code = MLAN_ERROR_DATA_TX_FAIL; + pmadapter->dbg.num_tx_host_to_card_failure++; + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + goto exit; + case MLAN_STATUS_PENDING: + break; + case MLAN_STATUS_SUCCESS: + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + break; + default: + break; + } + if (ret != MLAN_STATUS_RESOURCE) { + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + if (wlan_is_ralist_valid(priv, pra_list, ptrindex)) { + priv->wmm.packets_out[ptrindex]++; + priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = pra_list; + } + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur = + pmadapter->bssprio_tbl[priv->bss_priority] + .bssprio_cur->pnext; + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + } + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec); + priv->amsdu_tx_cnt++; + +exit: + LEAVE(); + return pkt_size + headroom; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.h b/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.h new file mode 100644 index 0000000..96a2783 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11n_aggr.h @@ -0,0 +1,38 @@ +/** @file mlan_11n_aggr.h + * + * @brief This file contains related macros, enum, and struct + * of 11n aggregation functionalities + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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/mxm_wifiex/wlan_src/mlan/mlan_11n_rxreorder.c b/mxm_wifiex/wlan_src/mlan/mlan_11n_rxreorder.c new file mode 100644 index 0000000..68dbd07 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11n_rxreorder.c @@ -0,0 +1,1497 @@ +/** @file mlan_11n_rxreorder.c + * + * @brief This file contains the handling of RxReordering in wlan + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 11/10/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11n_rxreorder.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function will dispatch amsdu packet and + * forward it to kernel/upper layer + * + * @param priv A pointer to mlan_private + * @param pmbuf A pointer to the received buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_11n_dispatch_amsdu_pkt(mlan_private *priv, + pmlan_buffer pmbuf) +{ + RxPD *prx_pd; + prx_pd = (RxPD *)(pmbuf->pbuf + pmbuf->data_offset); + + ENTER(); + if (prx_pd->rx_pkt_type == PKT_TYPE_AMSDU) { + pmbuf->data_len = prx_pd->rx_pkt_length; + pmbuf->data_offset += prx_pd->rx_pkt_offset; + wlan_11n_deaggregate_pkt(priv, pmbuf); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief This function will process the rx packet and + * forward it to kernel/upper layer + * + * @param priv A pointer to mlan_private + * @param payload A pointer to rx packet payload + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_11n_dispatch_pkt(t_void *priv, t_void *payload) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef STA_SUPPORT + pmlan_adapter pmadapter = ((pmlan_private)priv)->adapter; +#endif + ENTER(); + if (payload == (t_void *)RX_PKT_DROPPED_IN_FW) { + LEAVE(); + return ret; + } +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE((mlan_private *)priv) == MLAN_BSS_ROLE_UAP) { + if (MLAN_STATUS_SUCCESS == + wlan_11n_dispatch_amsdu_pkt((mlan_private *)priv, + (pmlan_buffer)payload)) { + LEAVE(); + return ret; + } + ret = wlan_process_uap_rx_packet(priv, (pmlan_buffer)payload); + LEAVE(); + return ret; + } +#endif /* UAP_SUPPORT */ + +#ifdef STA_SUPPORT + if (MLAN_STATUS_SUCCESS == + wlan_11n_dispatch_amsdu_pkt((mlan_private *)priv, + (pmlan_buffer)payload)) { + LEAVE(); + return ret; + } + ret = wlan_process_rx_packet(pmadapter, (pmlan_buffer)payload); +#endif /* STA_SUPPORT */ + LEAVE(); + return ret; +} + +/** + * @brief This function restarts the reordering timeout timer + * + * @param pmadapter A pointer to mlan_adapter + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * + * @return N/A + */ +static void mlan_11n_rxreorder_timer_restart(pmlan_adapter pmadapter, + RxReorderTbl *rx_reor_tbl_ptr) +{ + t_u16 min_flush_time = 0; + ENTER(); + + if (rx_reor_tbl_ptr->win_size >= 32) + min_flush_time = MIN_FLUSH_TIMER_15_MS; + else + min_flush_time = MIN_FLUSH_TIMER_MS; + + if (rx_reor_tbl_ptr->timer_context.timer_is_set) + pmadapter->callbacks.moal_stop_timer( + pmadapter->pmoal_handle, + rx_reor_tbl_ptr->timer_context.timer); + + pmadapter->callbacks.moal_start_timer( + pmadapter->pmoal_handle, rx_reor_tbl_ptr->timer_context.timer, + MFALSE, (rx_reor_tbl_ptr->win_size * min_flush_time)); + + rx_reor_tbl_ptr->timer_context.timer_is_set = MTRUE; + LEAVE(); +} + +/** + * @brief This function dispatches all the packets in the buffer. + * There could be holes in the buffer. + * + * @param priv A pointer to mlan_private + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * @param start_win Start window + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_11n_dispatch_pkt_until_start_win( + t_void *priv, RxReorderTbl *rx_reor_tbl_ptr, int start_win) +{ + int no_pkt_to_send, i, xchg; + mlan_status ret = MLAN_STATUS_SUCCESS; + void *rx_tmp_ptr = MNULL; + mlan_private *pmpriv = (mlan_private *)priv; + + ENTER(); + + no_pkt_to_send = (start_win > rx_reor_tbl_ptr->start_win) ? + MIN((start_win - rx_reor_tbl_ptr->start_win), + rx_reor_tbl_ptr->win_size) : + rx_reor_tbl_ptr->win_size; + + for (i = 0; i < no_pkt_to_send; ++i) { + pmpriv->adapter->callbacks.moal_spin_lock( + pmpriv->adapter->pmoal_handle, pmpriv->rx_pkt_lock); + rx_tmp_ptr = MNULL; + if (rx_reor_tbl_ptr->rx_reorder_ptr[i]) { + rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i]; + rx_reor_tbl_ptr->rx_reorder_ptr[i] = MNULL; + } + pmpriv->adapter->callbacks.moal_spin_unlock( + pmpriv->adapter->pmoal_handle, pmpriv->rx_pkt_lock); + if (rx_tmp_ptr) + wlan_11n_dispatch_pkt(priv, rx_tmp_ptr); + } + + pmpriv->adapter->callbacks.moal_spin_lock(pmpriv->adapter->pmoal_handle, + pmpriv->rx_pkt_lock); + /* + * We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + xchg = rx_reor_tbl_ptr->win_size - no_pkt_to_send; + for (i = 0; i < xchg; ++i) { + rx_reor_tbl_ptr->rx_reorder_ptr[i] = + rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i]; + rx_reor_tbl_ptr->rx_reorder_ptr[no_pkt_to_send + i] = MNULL; + } + + rx_reor_tbl_ptr->start_win = start_win; + pmpriv->adapter->callbacks.moal_spin_unlock( + pmpriv->adapter->pmoal_handle, pmpriv->rx_pkt_lock); + + LEAVE(); + return ret; +} + +/** + * @brief This function will display the rxReorder table + * + * @param pmadapter A pointer to mlan_adapter structure + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * + * @return N/A + */ +static t_void wlan_11n_display_tbl_ptr(pmlan_adapter pmadapter, + RxReorderTbl *rx_reor_tbl_ptr) +{ + ENTER(); + + DBG_HEXDUMP(MDAT_D, "Reorder ptr", rx_reor_tbl_ptr->rx_reorder_ptr, + sizeof(t_void *) * rx_reor_tbl_ptr->win_size); + + LEAVE(); +} + +/** + * @brief This function will dispatch all packets sequentially + * from start_win until a hole is found and adjust the + * start_win appropriately + * + * @param priv A pointer to mlan_private + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_11n_scan_and_dispatch(t_void *priv, + RxReorderTbl *rx_reor_tbl_ptr) +{ + int i, j, xchg; + mlan_status ret = MLAN_STATUS_SUCCESS; + void *rx_tmp_ptr = MNULL; + mlan_private *pmpriv = (mlan_private *)priv; + + ENTER(); + + for (i = 0; i < rx_reor_tbl_ptr->win_size; ++i) { + pmpriv->adapter->callbacks.moal_spin_lock( + pmpriv->adapter->pmoal_handle, pmpriv->rx_pkt_lock); + if (!rx_reor_tbl_ptr->rx_reorder_ptr[i]) { + pmpriv->adapter->callbacks.moal_spin_unlock( + pmpriv->adapter->pmoal_handle, + pmpriv->rx_pkt_lock); + break; + } + rx_tmp_ptr = rx_reor_tbl_ptr->rx_reorder_ptr[i]; + rx_reor_tbl_ptr->rx_reorder_ptr[i] = MNULL; + pmpriv->adapter->callbacks.moal_spin_unlock( + pmpriv->adapter->pmoal_handle, pmpriv->rx_pkt_lock); + wlan_11n_dispatch_pkt(priv, rx_tmp_ptr); + } + + pmpriv->adapter->callbacks.moal_spin_lock(pmpriv->adapter->pmoal_handle, + pmpriv->rx_pkt_lock); + /* + * We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + if (i > 0) { + xchg = rx_reor_tbl_ptr->win_size - i; + for (j = 0; j < xchg; ++j) { + rx_reor_tbl_ptr->rx_reorder_ptr[j] = + rx_reor_tbl_ptr->rx_reorder_ptr[i + j]; + rx_reor_tbl_ptr->rx_reorder_ptr[i + j] = MNULL; + } + } + + rx_reor_tbl_ptr->start_win = + (rx_reor_tbl_ptr->start_win + i) & (MAX_TID_VALUE - 1); + + pmpriv->adapter->callbacks.moal_spin_unlock( + pmpriv->adapter->pmoal_handle, pmpriv->rx_pkt_lock); + LEAVE(); + return ret; +} + +/** + * @brief This function delete rxreorder table's entry + * and free the memory + * + * @param priv A pointer to mlan_private + * @param rx_reor_tbl_ptr A pointer to structure RxReorderTbl + * + * @return N/A + */ +static t_void wlan_11n_delete_rxreorder_tbl_entry(mlan_private *priv, + RxReorderTbl *rx_reor_tbl_ptr) +{ + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + if (!rx_reor_tbl_ptr) { + LEAVE(); + return; + } + mlan_block_rx_process(pmadapter, MTRUE); + + wlan_11n_dispatch_pkt_until_start_win( + priv, rx_reor_tbl_ptr, + (rx_reor_tbl_ptr->start_win + rx_reor_tbl_ptr->win_size) & + (MAX_TID_VALUE - 1)); + + if (rx_reor_tbl_ptr->timer_context.timer) { + if (rx_reor_tbl_ptr->timer_context.timer_is_set) { + priv->adapter->callbacks.moal_stop_timer( + pmadapter->pmoal_handle, + rx_reor_tbl_ptr->timer_context.timer); + rx_reor_tbl_ptr->timer_context.timer_is_set = MFALSE; + } + priv->adapter->callbacks.moal_free_timer( + pmadapter->pmoal_handle, + rx_reor_tbl_ptr->timer_context.timer); + rx_reor_tbl_ptr->timer_context.timer = MNULL; + } + + PRINTM(MDAT_D, "Delete rx_reor_tbl_ptr: %p\n", rx_reor_tbl_ptr); + util_unlink_list(pmadapter->pmoal_handle, &priv->rx_reorder_tbl_ptr, + (pmlan_linked_list)rx_reor_tbl_ptr, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + + pmadapter->callbacks.moal_mfree( + pmadapter->pmoal_handle, + (t_u8 *)rx_reor_tbl_ptr->rx_reorder_ptr); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)rx_reor_tbl_ptr); + mlan_block_rx_process(pmadapter, MFALSE); + + LEAVE(); +} + +/** + * @brief This function returns the last used sequence number + * + * @param rx_reorder_tbl_ptr A pointer to structure RxReorderTbl + * + * @return Last used sequence number + */ +static int wlan_11n_find_last_seqnum(RxReorderTbl *rx_reorder_tbl_ptr) +{ + int i; + + ENTER(); + for (i = (rx_reorder_tbl_ptr->win_size - 1); i >= 0; --i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { + LEAVE(); + return i; + } + } + LEAVE(); + return -1; +} + +/** + * @brief This function flushes all data + * + * @param priv A pointer to mlan_private structure + * @param rx_reor_tbl_ptr A pointer to RxReorderTbl + * + * @return N/A + */ +static t_void wlan_start_flush_data(mlan_private *priv, + RxReorderTbl *rx_reor_tbl_ptr) +{ + int startWin; + + ENTER(); + wlan_11n_display_tbl_ptr(priv->adapter, rx_reor_tbl_ptr); + + startWin = wlan_11n_find_last_seqnum(rx_reor_tbl_ptr); + if (startWin >= 0) { + PRINTM(MINFO, "Flush data %d\n", startWin); + wlan_11n_dispatch_pkt_until_start_win( + priv, rx_reor_tbl_ptr, + ((rx_reor_tbl_ptr->start_win + startWin + 1) & + (MAX_TID_VALUE - 1))); + } + wlan_11n_display_tbl_ptr(priv->adapter, rx_reor_tbl_ptr); + LEAVE(); +} + +/** + * @brief This function set the flag to flushes data + * + * @param context Reorder context pointer + * + * @return N/A + */ +static t_void wlan_flush_data(t_void *context) +{ + reorder_tmr_cnxt_t *reorder_cnxt = (reorder_tmr_cnxt_t *)context; + ENTER(); + /* Set the flag to flush data */ + reorder_cnxt->priv->adapter->flush_data = MTRUE; + reorder_cnxt->ptr->flush_data = MTRUE; + reorder_cnxt->timer_is_set = MFALSE; + wlan_recv_event(reorder_cnxt->priv, MLAN_EVENT_ID_DRV_DEFER_RX_WORK, + MNULL); + LEAVE(); +} + +/** + * @brief This function will create a entry in rx reordering table for the + * given ta/tid and will initialize it with seq_num, win_size + * + * @param priv A pointer to mlan_private + * @param ta ta to find in reordering table + * @param tid tid to find in reordering table + * @param win_size win_size for the give ta/tid pair. + * @param seq_num Starting sequence number for current entry. + * + * @return N/A + */ +static t_void wlan_11n_create_rxreorder_tbl(mlan_private *priv, t_u8 *ta, + int tid, int win_size, int seq_num) +{ + int i; + pmlan_adapter pmadapter = priv->adapter; + RxReorderTbl *rx_reor_tbl_ptr, *new_node; + sta_node *sta_ptr = MNULL; + t_u16 last_seq = 0; + + ENTER(); + + /* + * If we get a TID, ta pair which is already present dispatch all the + * the packets and move the window size until the ssn + */ + rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl(priv, tid, ta); + if (rx_reor_tbl_ptr) { + PRINTM(MCMND, "%s: delete %p old_size=%d, win_size=%d\n", + __func__, rx_reor_tbl_ptr, rx_reor_tbl_ptr->win_size, + win_size); + wlan_11n_delete_rxreorder_tbl_entry(priv, rx_reor_tbl_ptr); + } + mlan_block_rx_process(pmadapter, MTRUE); + PRINTM(MCMND, "%s: seq_num %d, tid %d, ta " MACSTR ", win_size %d\n", + __func__, seq_num, tid, MAC2STR(ta), win_size); + if (pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(RxReorderTbl), MLAN_MEM_DEF, + (t_u8 **)&new_node)) { + PRINTM(MERROR, "Rx reorder memory allocation failed\n"); + mlan_block_rx_process(pmadapter, MFALSE); + LEAVE(); + return; + } + + util_init_list((pmlan_linked_list)new_node); + if (pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, sizeof(pmlan_buffer) * 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); + mlan_block_rx_process(pmadapter, MFALSE); + LEAVE(); + return; + } + PRINTM(MDAT_D, "Create ReorderPtr: %p start_win=%d last_seq=%d\n", + new_node, new_node->start_win, last_seq); + new_node->timer_context.ptr = new_node; + new_node->timer_context.priv = priv; + new_node->timer_context.timer_is_set = MFALSE; + pmadapter->callbacks.moal_init_timer(pmadapter->pmoal_handle, + &new_node->timer_context.timer, + wlan_flush_data, + &new_node->timer_context); + 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); + new_node->tid = tid; + memcpy_ext(pmadapter, new_node->ta, ta, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + new_node->start_win = seq_num; + new_node->pkt_count = 0; + if (queuing_ra_based(priv)) { + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + sta_ptr = wlan_get_station_entry(priv, ta); + if (sta_ptr) + last_seq = sta_ptr->rx_seq[tid]; + } + PRINTM(MINFO, "UAP/ADHOC:last_seq=%d start_win=%d\n", last_seq, + new_node->start_win); + } else { + last_seq = priv->rx_seq[tid]; + } + new_node->last_seq = last_seq; + new_node->win_size = win_size; + new_node->force_no_drop = MFALSE; + new_node->check_start_win = MTRUE; + new_node->ba_status = BA_STREAM_SETUP_INPROGRESS; + for (i = 0; i < win_size; ++i) + new_node->rx_reorder_ptr[i] = MNULL; + mlan_block_rx_process(pmadapter, MFALSE); + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function will return the pointer to a entry in rx reordering + * table which matches the give TA/TID pair + * + * @param priv A pointer to mlan_private + * @param ta ta to find in reordering table + * @param tid tid to find in reordering table + * + * @return A pointer to structure RxReorderTbl + */ +RxReorderTbl *wlan_11n_get_rxreorder_tbl(mlan_private *priv, int tid, t_u8 *ta) +{ + RxReorderTbl *rx_reor_tbl_ptr; + + ENTER(); + + rx_reor_tbl_ptr = + (RxReorderTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, MNULL, + MNULL); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return MNULL; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + if ((!memcmp(priv->adapter, rx_reor_tbl_ptr->ta, ta, + MLAN_MAC_ADDR_LENGTH)) && + (rx_reor_tbl_ptr->tid == tid)) { + LEAVE(); + return rx_reor_tbl_ptr; + } + + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return MNULL; +} + +/** + * @brief This function prepares command for adding a block ack + * request. + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_11n_addba_req(mlan_private *priv, HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf) +{ + HostCmd_DS_11N_ADDBA_REQ *padd_ba_req = + (HostCmd_DS_11N_ADDBA_REQ *)&cmd->params.add_ba_req; + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_11N_ADDBA_REQ) + S_DS_GEN); + + memcpy_ext(priv->adapter, padd_ba_req, pdata_buf, + sizeof(HostCmd_DS_11N_ADDBA_REQ), + sizeof(HostCmd_DS_11N_ADDBA_REQ)); + padd_ba_req->block_ack_param_set = + wlan_cpu_to_le16(padd_ba_req->block_ack_param_set); + padd_ba_req->block_ack_tmo = + wlan_cpu_to_le16(padd_ba_req->block_ack_tmo); + padd_ba_req->ssn = wlan_cpu_to_le16(padd_ba_req->ssn); + padd_ba_req->add_req_result = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function check if AMPDU Rx allowed + * + * @param priv A pointer to mlan_private structure + * @param tid TID + * + * @return MTRUE/MFALSE + */ +t_u8 wlan_is_addba_reject(mlan_private *priv, t_u8 tid) +{ +#ifdef STA_SUPPORT +#endif + return priv->addba_reject[tid]; +} +/** + * @brief This function prepares command for adding a block ack + * response. + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_11n_addba_rspgen(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, void *pdata_buf) +{ + HostCmd_DS_11N_ADDBA_RSP *padd_ba_rsp = + (HostCmd_DS_11N_ADDBA_RSP *)&cmd->params.add_ba_rsp; + HostCmd_DS_11N_ADDBA_REQ *pevt_addba_req = + (HostCmd_DS_11N_ADDBA_REQ *)pdata_buf; + t_u8 tid = 0; + int win_size = 0; + + ENTER(); + + pevt_addba_req->block_ack_param_set = + wlan_le16_to_cpu(pevt_addba_req->block_ack_param_set); + pevt_addba_req->block_ack_tmo = + wlan_le16_to_cpu(pevt_addba_req->block_ack_tmo); + pevt_addba_req->ssn = wlan_le16_to_cpu(pevt_addba_req->ssn); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_11N_ADDBA_RSP) + S_DS_GEN); + + memcpy_ext(priv->adapter, padd_ba_rsp->peer_mac_addr, + pevt_addba_req->peer_mac_addr, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + padd_ba_rsp->dialog_token = pevt_addba_req->dialog_token; + padd_ba_rsp->block_ack_tmo = + wlan_cpu_to_le16(pevt_addba_req->block_ack_tmo); + padd_ba_rsp->ssn = wlan_cpu_to_le16(pevt_addba_req->ssn); + padd_ba_rsp->add_rsp_result = 0; + + padd_ba_rsp->block_ack_param_set = pevt_addba_req->block_ack_param_set; + tid = (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_TID_MASK) >> + BLOCKACKPARAM_TID_POS; + if (wlan_is_addba_reject(priv, tid) +#ifdef STA_SUPPORT + || ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + priv->wps.session_enable) +#endif +#ifdef UAP_SUPPORT + || ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + (util_scalar_read(priv->adapter->pmoal_handle, + &priv->adapter->pending_bridge_pkts, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock) > + RX_LOW_THRESHOLD)) +#endif + ) + padd_ba_rsp->status_code = + wlan_cpu_to_le16(ADDBA_RSP_STATUS_DECLINED); + else + padd_ba_rsp->status_code = + wlan_cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); + padd_ba_rsp->block_ack_param_set &= ~BLOCKACKPARAM_WINSIZE_MASK; + if (!priv->add_ba_param.rx_amsdu) + /* We do not support AMSDU inside AMPDU, hence reset the bit */ + padd_ba_rsp->block_ack_param_set &= + ~BLOCKACKPARAM_AMSDU_SUPP_MASK; + + padd_ba_rsp->block_ack_param_set |= + (priv->add_ba_param.rx_win_size << BLOCKACKPARAM_WINSIZE_POS); + win_size = (padd_ba_rsp->block_ack_param_set & + BLOCKACKPARAM_WINSIZE_MASK) >> + BLOCKACKPARAM_WINSIZE_POS; + + if (win_size == 0) + padd_ba_rsp->status_code = + wlan_cpu_to_le16(ADDBA_RSP_STATUS_DECLINED); + + padd_ba_rsp->block_ack_param_set = + wlan_cpu_to_le16(padd_ba_rsp->block_ack_param_set); + + if (padd_ba_rsp->status_code == + wlan_cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT)) + wlan_11n_create_rxreorder_tbl(priv, + pevt_addba_req->peer_mac_addr, + tid, win_size, + pevt_addba_req->ssn); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command for deleting a block ack + * request. + * + * @param priv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *cmd, + void *pdata_buf) +{ + HostCmd_DS_11N_DELBA *pdel_ba = + (HostCmd_DS_11N_DELBA *)&cmd->params.del_ba; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_11N_DELBA); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_11N_DELBA) + S_DS_GEN); + + memcpy_ext(priv->adapter, pdel_ba, pdata_buf, + sizeof(HostCmd_DS_11N_DELBA), sizeof(HostCmd_DS_11N_DELBA)); + pdel_ba->del_ba_param_set = wlan_cpu_to_le16(pdel_ba->del_ba_param_set); + pdel_ba->reason_code = wlan_cpu_to_le16(pdel_ba->reason_code); + pdel_ba->del_result = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function will identify if RxReodering is needed for the packet + * and will do the reordering if required before sending it to kernel + * + * @param priv A pointer to mlan_private + * @param seq_num Seqence number of the current packet + * @param tid Tid of the current packet + * @param ta Transmiter address of the current packet + * @param pkt_type Packetype for the current packet (to identify if its a BAR) + * @param payload Pointer to the payload + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status mlan_11n_rxreorder_pkt(void *priv, t_u16 seq_num, t_u16 tid, + t_u8 *ta, t_u8 pkt_type, void *payload) +{ + RxReorderTbl *rx_reor_tbl_ptr; + int prev_start_win, start_win, end_win, win_size; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = ((mlan_private *)priv)->adapter; + + ENTER(); + + rx_reor_tbl_ptr = + wlan_11n_get_rxreorder_tbl((mlan_private *)priv, tid, ta); + if (!rx_reor_tbl_ptr || rx_reor_tbl_ptr->win_size <= 1) { + if (pkt_type != PKT_TYPE_BAR) + wlan_11n_dispatch_pkt(priv, payload); + + LEAVE(); + return ret; + + } else { + if (rx_reor_tbl_ptr->flush_data) { + rx_reor_tbl_ptr->flush_data = MFALSE; + wlan_start_flush_data(priv, rx_reor_tbl_ptr); + } + if ((pkt_type == PKT_TYPE_AMSDU) && !rx_reor_tbl_ptr->amsdu) { + wlan_11n_dispatch_pkt(priv, payload); + LEAVE(); + return ret; + } + if (pkt_type == PKT_TYPE_BAR) + PRINTM(MDAT_D, "BAR "); + if (pkt_type == PKT_TYPE_AMSDU) + PRINTM(MDAT_D, "AMSDU "); + + if (rx_reor_tbl_ptr->check_start_win) { + if (seq_num == rx_reor_tbl_ptr->start_win) + rx_reor_tbl_ptr->check_start_win = MFALSE; + else { + rx_reor_tbl_ptr->pkt_count++; + if (rx_reor_tbl_ptr->pkt_count < + (rx_reor_tbl_ptr->win_size / 2)) { + if (rx_reor_tbl_ptr->last_seq == + seq_num) { + /** drop duplicate packet */ + ret = MLAN_STATUS_FAILURE; + } else { + /** forward the packet to kernel + */ + rx_reor_tbl_ptr->last_seq = + seq_num; + if (pkt_type != PKT_TYPE_BAR) + wlan_11n_dispatch_pkt( + priv, payload); + } + LEAVE(); + return ret; + } + rx_reor_tbl_ptr->check_start_win = MFALSE; + if ((seq_num != rx_reor_tbl_ptr->start_win) && + (rx_reor_tbl_ptr->last_seq != + DEFAULT_SEQ_NUM)) { + end_win = (rx_reor_tbl_ptr->start_win + + rx_reor_tbl_ptr->win_size - + 1) & + (MAX_TID_VALUE - 1); + if (((end_win > + rx_reor_tbl_ptr->start_win) && + (rx_reor_tbl_ptr->last_seq >= + rx_reor_tbl_ptr->start_win) && + (rx_reor_tbl_ptr->last_seq < + end_win)) || + ((end_win < + rx_reor_tbl_ptr->start_win) && + ((rx_reor_tbl_ptr->last_seq >= + rx_reor_tbl_ptr->start_win) || + (rx_reor_tbl_ptr->last_seq < + end_win)))) { + PRINTM(MDAT_D, + "Update start_win: last_seq=%d, start_win=%d seq_num=%d\n", + rx_reor_tbl_ptr->last_seq, + rx_reor_tbl_ptr + ->start_win, + seq_num); + rx_reor_tbl_ptr->start_win = + rx_reor_tbl_ptr + ->last_seq + + 1; + } else if ((seq_num < + rx_reor_tbl_ptr->start_win) && + (seq_num > + rx_reor_tbl_ptr->last_seq)) { + PRINTM(MDAT_D, + "Update start_win: last_seq=%d, start_win=%d seq_num=%d\n", + rx_reor_tbl_ptr->last_seq, + rx_reor_tbl_ptr + ->start_win, + seq_num); + rx_reor_tbl_ptr->start_win = + rx_reor_tbl_ptr + ->last_seq + + 1; + } + } + } + } + if (rx_reor_tbl_ptr->force_no_drop) { + wlan_11n_dispatch_pkt_until_start_win( + priv, rx_reor_tbl_ptr, + (rx_reor_tbl_ptr->start_win + + rx_reor_tbl_ptr->win_size) & + (MAX_TID_VALUE - 1)); + if (pkt_type != PKT_TYPE_BAR) + rx_reor_tbl_ptr->start_win = seq_num; + mlan_11n_rxreorder_timer_restart(pmadapter, + rx_reor_tbl_ptr); + } + + prev_start_win = start_win = rx_reor_tbl_ptr->start_win; + win_size = rx_reor_tbl_ptr->win_size; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + + PRINTM(MDAT_D, "TID %d, TA " MACSTR "\n", tid, MAC2STR(ta)); + PRINTM(MDAT_D, + "1:seq_num %d start_win %d win_size %d end_win %d\n", + seq_num, start_win, win_size, end_win); + /* + * If seq_num is less then starting win then ignore and drop + * the packet + */ + if (rx_reor_tbl_ptr->force_no_drop) { + PRINTM(MDAT_D, "No drop packet\n"); + rx_reor_tbl_ptr->force_no_drop = MFALSE; + } else { + /* Wrap */ + if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { + if (seq_num >= ((start_win + (TWOPOW11)) & + (MAX_TID_VALUE - 1)) && + (seq_num < start_win)) { + if (pkt_type == PKT_TYPE_BAR) + PRINTM(MDAT_D, + "BAR: start_win=%d, end_win=%d, seq_num=%d\n", + start_win, end_win, + seq_num); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else if ((seq_num < start_win) || + (seq_num >= (start_win + (TWOPOW11)))) { + if (pkt_type == PKT_TYPE_BAR) + PRINTM(MDAT_D, + "BAR: start_win=%d, end_win=%d, seq_num=%d\n", + start_win, end_win, seq_num); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* + * If this packet is a BAR we adjust seq_num as + * WinStart = seq_num + */ + if (pkt_type == PKT_TYPE_BAR) + seq_num = ((seq_num + win_size) - 1) & + (MAX_TID_VALUE - 1); + + PRINTM(MDAT_D, + "2:seq_num %d start_win %d win_size %d end_win %d\n", + seq_num, start_win, win_size, end_win); + + if (((end_win < start_win) && (seq_num < start_win) && + (seq_num > end_win)) || + ((end_win > start_win) && + ((seq_num > end_win) || (seq_num < start_win)))) { + end_win = seq_num; + if (((seq_num - win_size) + 1) >= 0) + start_win = (end_win - win_size) + 1; + else + start_win = + (MAX_TID_VALUE - (win_size - seq_num)) + + 1; + ret = wlan_11n_dispatch_pkt_until_start_win( + priv, rx_reor_tbl_ptr, start_win); + if (ret) + goto done; + } + + PRINTM(MDAT_D, + "3:seq_num %d start_win %d win_size %d" + " end_win %d\n", + seq_num, start_win, win_size, end_win); + if (pkt_type != PKT_TYPE_BAR) { + if (seq_num >= start_win) { + if (rx_reor_tbl_ptr->rx_reorder_ptr[seq_num - + start_win]) { + PRINTM(MDAT_D, "Drop Duplicate Pkt\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + rx_reor_tbl_ptr + ->rx_reorder_ptr[seq_num - start_win] = + payload; + } else { /* Wrap condition */ + if (rx_reor_tbl_ptr + ->rx_reorder_ptr[(seq_num + + (MAX_TID_VALUE)) - + start_win]) { + PRINTM(MDAT_D, "Drop Duplicate Pkt\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + rx_reor_tbl_ptr + ->rx_reorder_ptr[(seq_num + + (MAX_TID_VALUE)) - + start_win] = payload; + } + } + + wlan_11n_display_tbl_ptr(pmadapter, rx_reor_tbl_ptr); + + /* + * Dispatch all packets sequentially from start_win until a + * hole is found and adjust the start_win appropriately + */ + ret = wlan_11n_scan_and_dispatch(priv, rx_reor_tbl_ptr); + + wlan_11n_display_tbl_ptr(pmadapter, rx_reor_tbl_ptr); + } + +done: + if (!rx_reor_tbl_ptr->timer_context.timer_is_set || + (prev_start_win != rx_reor_tbl_ptr->start_win)) { + mlan_11n_rxreorder_timer_restart(pmadapter, rx_reor_tbl_ptr); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function will delete an entry for a given tid/ta pair. tid/ta + * are taken from delba_event body + * + * @param priv A pointer to mlan_private + * @param tid tid to send delba + * @param peer_mac MAC address to send delba + * @param type TYPE_DELBA_SENT or TYPE_DELBA_RECEIVE + * @param initiator MTRUE if we are initiator of ADDBA, MFALSE otherwise + * @param reason_code delete ba reason + * + * @return N/A + */ +void mlan_11n_delete_bastream_tbl(mlan_private *priv, int tid, t_u8 *peer_mac, + t_u8 type, int initiator, t_u16 reason_code) +{ + RxReorderTbl *rx_reor_tbl_ptr; + TxBAStreamTbl *ptxtbl; + t_u8 cleanup_rx_reorder_tbl; + raListTbl *ra_list = MNULL; + int tid_down; + + ENTER(); + + if (type == TYPE_DELBA_RECEIVE) + cleanup_rx_reorder_tbl = (initiator) ? MTRUE : MFALSE; + else + cleanup_rx_reorder_tbl = (initiator) ? MFALSE : MTRUE; + + PRINTM(MEVENT, + "delete_bastream_tbl: " MACSTR " tid=%d, type=%d" + "initiator=%d reason=%d\n", + MAC2STR(peer_mac), tid, type, initiator, reason_code); + + if (cleanup_rx_reorder_tbl) { + rx_reor_tbl_ptr = + wlan_11n_get_rxreorder_tbl(priv, tid, peer_mac); + if (!rx_reor_tbl_ptr) { + PRINTM(MWARN, "TID, TA not found in table!\n"); + LEAVE(); + return; + } + wlan_11n_delete_rxreorder_tbl_entry(priv, rx_reor_tbl_ptr); + } else { + wlan_request_ralist_lock(priv); + ptxtbl = wlan_11n_get_txbastream_tbl(priv, tid, peer_mac, + MFALSE); + if (!ptxtbl) { + PRINTM(MWARN, "TID, RA not found in table!\n"); + wlan_release_ralist_lock(priv); + LEAVE(); + return; + } + wlan_11n_delete_txbastream_tbl_entry(priv, ptxtbl); + wlan_release_ralist_lock(priv); + tid_down = wlan_get_wmm_tid_down(priv, tid); + ra_list = wlan_wmm_get_ralist_node(priv, tid_down, peer_mac); + if (ra_list) { + ra_list->amsdu_in_ampdu = MFALSE; + ra_list->ba_status = BA_STREAM_NOT_SETUP; + if (type == TYPE_DELBA_RECEIVE) { + if (reason_code == REASON_CODE_STA_TIMEOUT) + ra_list->del_ba_count = 0; + else + ra_list->del_ba_count++; + ra_list->packet_count = 0; +/** after delba, we will try to set up BA again after sending 1k packets*/ +#define MIN_BA_SETUP_PACKET_REQIRED 1024 + ra_list->ba_packet_threshold = + MIN_BA_SETUP_PACKET_REQIRED + + wlan_get_random_ba_threshold( + priv->adapter); + } + } + } + + LEAVE(); +} + +/** + * @brief This function handles the command response of + * a block ack response + * + * @param priv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_11n_addba_resp(mlan_private *priv, + HostCmd_DS_COMMAND *resp) +{ + HostCmd_DS_11N_ADDBA_RSP *padd_ba_rsp = + (HostCmd_DS_11N_ADDBA_RSP *)&resp->params.add_ba_rsp; + int tid; + RxReorderTbl *rx_reor_tbl_ptr = MNULL; + + ENTER(); + + padd_ba_rsp->status_code = wlan_le16_to_cpu(padd_ba_rsp->status_code); + padd_ba_rsp->block_ack_param_set = + wlan_le16_to_cpu(padd_ba_rsp->block_ack_param_set); + padd_ba_rsp->block_ack_tmo = + wlan_le16_to_cpu(padd_ba_rsp->block_ack_tmo); + padd_ba_rsp->ssn = wlan_le16_to_cpu(padd_ba_rsp->ssn); + + tid = (padd_ba_rsp->block_ack_param_set & BLOCKACKPARAM_TID_MASK) >> + BLOCKACKPARAM_TID_POS; + /* Check if we had rejected the ADDBA, if yes then do not create the + * stream + */ + if (padd_ba_rsp->status_code == BA_RESULT_SUCCESS) { + PRINTM(MCMND, + "ADDBA RSP: " MACSTR + " tid=%d ssn=%d win_size=%d,amsdu=%d\n", + MAC2STR(padd_ba_rsp->peer_mac_addr), tid, + padd_ba_rsp->ssn, + ((padd_ba_rsp->block_ack_param_set & + BLOCKACKPARAM_WINSIZE_MASK) >> + BLOCKACKPARAM_WINSIZE_POS), + padd_ba_rsp->block_ack_param_set & + BLOCKACKPARAM_AMSDU_SUPP_MASK); + + rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl( + priv, tid, padd_ba_rsp->peer_mac_addr); + if (rx_reor_tbl_ptr) { + rx_reor_tbl_ptr->ba_status = BA_STREAM_SETUP_COMPLETE; + if ((padd_ba_rsp->block_ack_param_set & + BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.rx_amsdu) + rx_reor_tbl_ptr->amsdu = MTRUE; + else + rx_reor_tbl_ptr->amsdu = MFALSE; + } + } else { + PRINTM(MCMND, "ADDBA RSP: Failed(" MACSTR " tid=%d)\n", + MAC2STR(padd_ba_rsp->peer_mac_addr), tid); + rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl( + priv, tid, padd_ba_rsp->peer_mac_addr); + if (rx_reor_tbl_ptr) + wlan_11n_delete_rxreorder_tbl_entry(priv, + rx_reor_tbl_ptr); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles ba_stream_timeout event + * + * @param priv A pointer to mlan_private + * @param event A pointer to structure HostCmd_DS_11N_BATIMEOUT + * + * @return N/A + */ +void wlan_11n_ba_stream_timeout(mlan_private *priv, + HostCmd_DS_11N_BATIMEOUT *event) +{ + HostCmd_DS_11N_DELBA delba; + + ENTER(); + + DBG_HEXDUMP(MCMD_D, "Event:", (t_u8 *)event, 20); + + memset(priv->adapter, &delba, 0, sizeof(HostCmd_DS_11N_DELBA)); + memcpy_ext(priv->adapter, delba.peer_mac_addr, event->peer_mac_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + + delba.del_ba_param_set |= (t_u16)event->tid << DELBA_TID_POS; + delba.del_ba_param_set |= (t_u16)event->origninator + << DELBA_INITIATOR_POS; + delba.reason_code = REASON_CODE_STA_TIMEOUT; + wlan_prepare_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, MNULL, &delba); + + LEAVE(); + return; +} + +/** + * @brief This function cleans up reorder tbl + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +void wlan_11n_cleanup_reorder_tbl(mlan_private *priv) +{ + RxReorderTbl *del_tbl_ptr; + + ENTER(); + + while ((del_tbl_ptr = (RxReorderTbl *)util_peek_list( + priv->adapter->pmoal_handle, &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock))) { + wlan_11n_delete_rxreorder_tbl_entry(priv, del_tbl_ptr); + } + + util_init_list((pmlan_linked_list)&priv->rx_reorder_tbl_ptr); + + memset(priv->adapter, priv->rx_seq, 0xff, sizeof(priv->rx_seq)); + LEAVE(); +} + +/** + * @brief This function handle the rxba_sync event + * + * @param priv A pointer to mlan_private + * @param event_buf A pointer to event buf + * @param len event_buf length + * @return N/A + */ +void wlan_11n_rxba_sync_event(mlan_private *priv, t_u8 *event_buf, t_u16 len) +{ + MrvlIEtypes_RxBaSync_t *tlv_rxba = (MrvlIEtypes_RxBaSync_t *)event_buf; + t_u16 tlv_type, tlv_len; + RxReorderTbl *rx_reor_tbl_ptr = MNULL; + t_u8 i, j; + t_u16 seq_num = 0; + int tlv_buf_left = len; + ENTER(); + + DBG_HEXDUMP(MEVT_D, "RXBA_SYNC_EVT", event_buf, len); + while (tlv_buf_left >= sizeof(MrvlIEtypes_RxBaSync_t)) { + tlv_type = wlan_le16_to_cpu(tlv_rxba->header.type); + tlv_len = wlan_le16_to_cpu(tlv_rxba->header.len); + if (tlv_type != TLV_TYPE_RXBA_SYNC) { + PRINTM(MERROR, "Wrong TLV id=0x%x\n", tlv_type); + goto done; + } + tlv_rxba->seq_num = wlan_le16_to_cpu(tlv_rxba->seq_num); + tlv_rxba->bitmap_len = wlan_le16_to_cpu(tlv_rxba->bitmap_len); + PRINTM(MEVENT, MACSTR " tid=%d seq_num=%d bitmap_len=%d\n", + MAC2STR(tlv_rxba->mac), tlv_rxba->tid, tlv_rxba->seq_num, + tlv_rxba->bitmap_len); + rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl( + priv, tlv_rxba->tid, tlv_rxba->mac); + if (!rx_reor_tbl_ptr) { + PRINTM(MEVENT, "Can not find rx_reorder_tbl\n"); + goto done; + } + if (rx_reor_tbl_ptr->force_no_drop) { + PRINTM(MEVENT, "Ignore RXBA_SYNC_EVT in resume\n"); + goto done; + } + for (i = 0; i < tlv_rxba->bitmap_len; i++) { + for (j = 0; j < 8; j++) { + if (tlv_rxba->bitmap[i] & (1 << j)) { + seq_num = (tlv_rxba->seq_num + i * 8 + + j) & + (MAX_TID_VALUE - 1); + PRINTM(MEVENT, + "Fw dropped packet, seq=%d start_win=%d, win_size=%d\n", + seq_num, + rx_reor_tbl_ptr->start_win, + rx_reor_tbl_ptr->win_size); + if (MLAN_STATUS_SUCCESS != + mlan_11n_rxreorder_pkt( + priv, seq_num, + tlv_rxba->tid, + tlv_rxba->mac, 0, + (t_void *) + RX_PKT_DROPPED_IN_FW)) { + PRINTM(MERROR, + "Fail to handle dropped packet, seq=%d\n", + seq_num); + } + } + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv_rxba = + (MrvlIEtypes_RxBaSync_t *)((t_u8 *)tlv_rxba + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } +done: + LEAVE(); + return; +} + +/** + * @brief This function cleans up reorder tbl for specific station + * + * @param priv A pointer to mlan_private + * @param ta ta to find in reordering table + * @return N/A + */ +void wlan_cleanup_reorder_tbl(mlan_private *priv, t_u8 *ta) +{ + RxReorderTbl *rx_reor_tbl_ptr = MNULL; + t_u8 i; + ENTER(); + for (i = 0; i < MAX_NUM_TID; ++i) { + rx_reor_tbl_ptr = wlan_11n_get_rxreorder_tbl(priv, i, ta); + if (rx_reor_tbl_ptr) + wlan_11n_delete_rxreorder_tbl_entry(priv, + rx_reor_tbl_ptr); + } + LEAVE(); + return; +} + +/** + * @brief This function will set force_no_drop flag in rxreorder_tbl. + * + * @param priv A pointer to mlan_private + * @param flag MTRUE/MFALSE + * + * @return N/A + */ +void wlan_set_rxreorder_tbl_no_drop_flag(mlan_private *priv, t_u8 flag) +{ + RxReorderTbl *rx_reor_tbl_ptr; + + ENTER(); + + rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list( + priv->adapter->pmoal_handle, &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + rx_reor_tbl_ptr->force_no_drop = flag; + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return; +} + +/** + * @brief This function update all the rx_reorder_tbl's force_no_drop flag + * + * @param pmadapter A pointer to mlan_adapter + * @param flag MTRUE/MFALSE + * @return N/A + */ +void wlan_update_rxreorder_tbl(pmlan_adapter pmadapter, t_u8 flag) +{ + t_u8 i; + pmlan_private priv = MNULL; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + wlan_set_rxreorder_tbl_no_drop_flag(priv, flag); + } + } + return; +} + +/** + * @brief This function will flush the data in rxreorder_tbl. + * which has flush_data flag on. + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +void wlan_flush_priv_rxreorder_tbl(mlan_private *priv) +{ + RxReorderTbl *rx_reor_tbl_ptr; + + ENTER(); + + rx_reor_tbl_ptr = (RxReorderTbl *)util_peek_list( + priv->adapter->pmoal_handle, &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + if (!rx_reor_tbl_ptr) { + LEAVE(); + return; + } + + while (rx_reor_tbl_ptr != (RxReorderTbl *)&priv->rx_reorder_tbl_ptr) { + if (rx_reor_tbl_ptr->flush_data) { + rx_reor_tbl_ptr->flush_data = MFALSE; + wlan_start_flush_data(priv, rx_reor_tbl_ptr); + } + rx_reor_tbl_ptr = rx_reor_tbl_ptr->pnext; + } + + LEAVE(); + return; +} + +/** + * @brief This function update all the rx_reorder_tbl's force_no_drop flag + * + * @param pmadapter A pointer to mlan_adapter + * @return N/A + */ +void wlan_flush_rxreorder_tbl(pmlan_adapter pmadapter) +{ + t_u8 i; + pmlan_private priv = MNULL; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + wlan_flush_priv_rxreorder_tbl(priv); + } + } + return; +} + +/** + * @brief This function update all the rx_win_size based on coex flag + * + * @param pmadapter A pointer to mlan_adapter + * @param coex_flag coex flag + * + * @return N/A + */ +void wlan_update_ampdu_rxwinsize(pmlan_adapter pmadapter, t_u8 coex_flag) +{ + t_u8 i; + t_u32 rx_win_size = 0; + pmlan_private priv = MNULL; + + ENTER(); + if (!pmadapter->coex_rx_winsize) { + LEAVE(); + return; + } + PRINTM(MEVENT, "Update rxwinsize %d\n", coex_flag); + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + rx_win_size = priv->add_ba_param.rx_win_size; + if (coex_flag == MTRUE) { +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size = + MLAN_STA_COEX_AMPDU_DEF_RXWINSIZE; +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + priv->add_ba_param.rx_win_size = + MLAN_WFD_COEX_AMPDU_DEF_RXWINSIZE; +#endif +#ifdef UAP_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size = + MLAN_UAP_COEX_AMPDU_DEF_RXWINSIZE; +#endif + + } else { + priv->add_ba_param.rx_win_size = + priv->user_rxwinsize; + } + if (pmadapter->coex_win_size && + pmadapter->coex_rx_win_size) + priv->add_ba_param.rx_win_size = + pmadapter->coex_rx_win_size; + if (rx_win_size != priv->add_ba_param.rx_win_size) { + if (priv->media_connected == MTRUE) { + for (i = 0; i < MAX_NUM_TID; i++) + wlan_11n_delba(priv, i); + wlan_recv_event( + priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + } + } + LEAVE(); + return; +} + +/** + * @brief check coex for + * + * @param pmadapter A pointer to mlan_adapter + * + * @return N/A + */ +void wlan_coex_ampdu_rxwinsize(pmlan_adapter pmadapter) +{ + t_u8 i; + pmlan_private priv = MNULL; + t_u8 count = 0; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; +#ifdef STA_SUPPORT + if (GET_BSS_ROLE((mlan_private *)priv) == + MLAN_BSS_ROLE_STA) { + if (priv->media_connected == MTRUE) + count++; + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE((mlan_private *)priv) == + MLAN_BSS_ROLE_UAP) { + if (priv->uap_bss_started) + count++; + } +#endif + } + if (count >= 2) + break; + } + if (count >= 2) + wlan_update_ampdu_rxwinsize(pmadapter, MTRUE); + else + wlan_update_ampdu_rxwinsize(pmadapter, MFALSE); + return; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_11n_rxreorder.h b/mxm_wifiex/wlan_src/mlan/mlan_11n_rxreorder.h new file mode 100644 index 0000000..7cab435 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_11n_rxreorder.h @@ -0,0 +1,104 @@ +/** @file mlan_11n_rxreorder.h + * + * @brief This file contains related macros, enum, and struct + * of 11n RxReordering functionalities + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 11/10/2008: initial version +********************************************************/ + +#ifndef _MLAN_11N_RXREORDER_H_ +#define _MLAN_11N_RXREORDER_H_ + +/** Max value a TID can take = 2^12 = 4096 */ +#define MAX_TID_VALUE (2 << 11) +/** 2^11 = 2048 */ +#define TWOPOW11 (2 << 10) + +/** Tid Mask used for extracting TID from BlockAckParamSet */ +#define BLOCKACKPARAM_TID_MASK 0x3C +/** Tid position in BlockAckParamSet */ +#define BLOCKACKPARAM_TID_POS 2 +/** WinSize Mask used for extracting WinSize from BlockAckParamSet */ +#define BLOCKACKPARAM_WINSIZE_MASK 0xffc0 +/** WinSize Mask used for extracting WinSize from BlockAckParamSet */ +#define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1 +/** WinSize position in BlockAckParamSet */ +#define BLOCKACKPARAM_WINSIZE_POS 6 +/** Position of TID in DelBA Param set */ +#define DELBA_TID_POS 12 +/** Position of INITIATOR in DelBA Param set */ +#define DELBA_INITIATOR_POS 11 +/** Reason code: Requested from peer STA as it does not want to + * use the mechanism */ +#define REASON_CODE_STA_DONT_WANT 37 +/** Reason code: Requested from peer STA due to timeout*/ +#define REASON_CODE_STA_TIMEOUT 39 +/** Type: send delba command */ +#define TYPE_DELBA_SENT 1 +/** Type: recieve delba command */ +#define TYPE_DELBA_RECEIVE 2 +/** Set Initiator Bit */ +#define DELBA_INITIATOR(paramset) (paramset = (paramset | (1 << 11))) +/** Reset Initiator Bit for recipient */ +#define DELBA_RECIPIENT(paramset) (paramset = (paramset & ~(1 << 11))) +/** Immediate block ack */ +#define IMMEDIATE_BLOCK_ACK 0x2 + +/** The request has been declined */ +#define ADDBA_RSP_STATUS_DECLINED 37 +/** ADDBA response status : Reject */ +#define ADDBA_RSP_STATUS_REJECT 1 +/** ADDBA response status : Accept */ +#define ADDBA_RSP_STATUS_ACCEPT 0 + +/** DEFAULT SEQ NUM */ +#define DEFAULT_SEQ_NUM 0xffff + +/** Indicate packet has been dropped in FW */ +#define RX_PKT_DROPPED_IN_FW 0xffffffff + +mlan_status mlan_11n_rxreorder_pkt(void *priv, t_u16 seqNum, t_u16 tid, + t_u8 *ta, t_u8 pkttype, void *payload); +void mlan_11n_delete_bastream_tbl(mlan_private *priv, int tid, + t_u8 *PeerMACAddr, t_u8 type, int initiator, + t_u16 reason_code); +void wlan_11n_ba_stream_timeout(mlan_private *priv, + HostCmd_DS_11N_BATIMEOUT *event); +mlan_status wlan_ret_11n_addba_resp(mlan_private *priv, + HostCmd_DS_COMMAND *resp); +mlan_status wlan_cmd_11n_delba(mlan_private *priv, HostCmd_DS_COMMAND *cmd, + void *pdata_buf); +mlan_status wlan_cmd_11n_addba_rspgen(mlan_private *priv, + HostCmd_DS_COMMAND *cmd, void *pdata_buf); +mlan_status wlan_cmd_11n_addba_req(mlan_private *priv, HostCmd_DS_COMMAND *cmd, + void *pdata_buf); +void wlan_11n_cleanup_reorder_tbl(mlan_private *priv); +RxReorderTbl *wlan_11n_get_rxreorder_tbl(mlan_private *priv, int tid, t_u8 *ta); +void wlan_11n_rxba_sync_event(mlan_private *priv, t_u8 *event_buf, t_u16 len); +void wlan_update_rxreorder_tbl(pmlan_adapter pmadapter, t_u8 flag); +void wlan_flush_rxreorder_tbl(pmlan_adapter pmadapter); +void wlan_coex_ampdu_rxwinsize(pmlan_adapter pmadapter); + +/** clean up reorder_tbl */ +void wlan_cleanup_reorder_tbl(mlan_private *priv, t_u8 *ta); +#endif /* _MLAN_11N_RXREORDER_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_cfp.c b/mxm_wifiex/wlan_src/mlan/mlan_cfp.c new file mode 100644 index 0000000..6ad3cca --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_cfp.c @@ -0,0 +1,3608 @@ +/** + * @file mlan_cfp.c + * + * @brief This file contains WLAN client mode channel, frequency and power + * related code + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************* + * Change Log: + * 04/16/2009: initial version + ************************************************************/ + +#include "mlan.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_join.h" +#include "mlan_main.h" + +/******************************************************** + * Local Variables + ********************************************************/ + +/** 100mW */ +#define WLAN_TX_PWR_DEFAULT 20 +/** 100mW */ +#define WLAN_TX_PWR_00_DEFAULT 20 +/** 100mW */ +#define WLAN_TX_PWR_US_DEFAULT 20 +/** 100mW */ +#define WLAN_TX_PWR_JP_BG_DEFAULT 20 +/** 200mW */ +#define WLAN_TX_PWR_JP_A_DEFAULT 23 +/** 100mW */ +#define WLAN_TX_PWR_FR_100MW 20 +/** 10mW */ +#define WLAN_TX_PWR_FR_10MW 10 +/** 100mW */ +#define WLAN_TX_PWR_EMEA_DEFAULT 20 +/** 2000mW */ +#define WLAN_TX_PWR_CN_2000MW 33 +/** 200mW */ +#define WLAN_TX_PWR_200MW 23 +/** 1000mW */ +#define WLAN_TX_PWR_1000MW 30 +/** 30mW */ +#define WLAN_TX_PWR_SP_30MW 14 +/** 60mW */ +#define WLAN_TX_PWR_SP_60MW 17 +/** 25mW */ +#define WLAN_TX_PWR_25MW 14 +/** 250mW */ +#define WLAN_TX_PWR_250MW 24 + +/** Region code mapping */ +typedef struct _country_code_mapping { + /** Region */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Code for B/G CFP table */ + t_u8 cfp_code_bg; + /** Code for A CFP table */ + t_u8 cfp_code_a; +} country_code_mapping_t; + +#define EU_CFP_CODE_BG 0x30 +#define EU_CFP_CODE_A 0x30 + +/** Region code mapping table */ +static country_code_mapping_t country_code_mapping[] = { + {"WW", 0x00, 0x00}, /* World */ + {"US", 0x10, 0x10}, /* US FCC */ + {"CA", 0x10, 0x20}, /* IC Canada */ + {"SG", 0x10, 0x10}, /* Singapore */ + {"EU", 0x30, 0x30}, /* ETSI */ + {"AU", 0x30, 0x30}, /* Australia */ + {"KR", 0x30, 0x30}, /* Republic Of Korea */ + {"JP", 0xFF, 0x40}, /* Japan */ + {"CN", 0x30, 0x50}, /* China */ + {"BR", 0x01, 0x09}, /* Brazil */ + {"RU", 0x30, 0x0f}, /* Russia */ + {"IN", 0x10, 0x06}, /* India */ + {"MY", 0x30, 0x06}, /* Malaysia */ + {"NZ", 0x30, 0x30}, /* New Zeland */ + {"MX", 0x10, 0x07}, /* Mexico */ +}; + +/** Country code for ETSI */ +static t_u8 eu_country_code_table[][COUNTRY_CODE_LEN] = { + "AL", "AD", "AT", "AU", "BY", "BE", "BA", "BG", "HR", "CY", "CZ", "DK", + "EE", "FI", "FR", "MK", "DE", "GR", "HU", "IS", "IE", "IT", "KR", "LV", + "LI", "LT", "LU", "MT", "MD", "MC", "ME", "NL", "NO", "PL", "RO", "RU", + "SM", "RS", "SI", "SK", "ES", "SE", "CH", "TR", "UA", "UK", "GB", "NZ"}; + +/** + * 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: World Wide Safe */ +static chan_freq_power_t channel_freq_power_00_BG[] = { + {1, 2412, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {2, 2417, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {3, 2422, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {4, 2427, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {5, 2432, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {6, 2437, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {7, 2442, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {8, 2447, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {9, 2452, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {10, 2457, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {11, 2462, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x1c, 0}}, + {12, 2467, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x1f, 0}}, + {13, 2472, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x1f, 0}}}; +/* Format { Channel, Frequency (MHz), MaxTxPower } */ +/** Band: 'B/G', Region: USA FCC/Canada IC */ +static chan_freq_power_t channel_freq_power_US_BG[] = { + {1, 2412, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_US_DEFAULT, MFALSE}}; + +/** Band: 'B/G', Region: Europe ETSI/China */ +static chan_freq_power_t channel_freq_power_EU_BG[] = { + {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}}; + +/** Band: 'B/G', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPN41_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}}; + +/** Band: 'B/G', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPN40_BG[] = { + {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}}; + +/** Band: 'B/G', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPNFE_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MTRUE}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MTRUE}}; + +/** Band : 'B/G', Region: Brazil */ +static chan_freq_power_t channel_freq_power_BR_BG[] = { + {1, 2412, WLAN_TX_PWR_1000MW, MFALSE}, + {2, 2417, WLAN_TX_PWR_1000MW, MFALSE}, + {3, 2422, WLAN_TX_PWR_1000MW, MFALSE}, + {4, 2427, WLAN_TX_PWR_1000MW, MFALSE}, + {5, 2432, WLAN_TX_PWR_1000MW, MFALSE}, + {6, 2437, WLAN_TX_PWR_1000MW, MFALSE}, + {7, 2442, WLAN_TX_PWR_1000MW, MFALSE}, + {8, 2447, WLAN_TX_PWR_1000MW, MFALSE}, + {9, 2452, WLAN_TX_PWR_1000MW, MFALSE}, + {10, 2457, WLAN_TX_PWR_1000MW, MFALSE}, + {11, 2462, WLAN_TX_PWR_1000MW, MFALSE}, + {12, 2467, WLAN_TX_PWR_1000MW, MFALSE}, + {13, 2472, WLAN_TX_PWR_1000MW, MFALSE}, +}; + +/** Band : 'B/G', Region: Special */ +static chan_freq_power_t channel_freq_power_SPECIAL_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {2, 2417, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {3, 2422, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {4, 2427, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {5, 2432, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {6, 2437, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {7, 2442, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {8, 2447, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {9, 2452, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {10, 2457, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {11, 2462, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {12, 2467, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {13, 2472, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}, + {14, 2484, WLAN_TX_PWR_JP_BG_DEFAULT, MFALSE}}; + +/** + * The 2.4GHz CFP tables + */ +static cfp_table_t cfp_table_BG[] = { + { + 0x01, /* Brazil */ + channel_freq_power_BR_BG, + NELEMENTS(channel_freq_power_BR_BG), + }, + { + 0x00, /* World FCC */ + channel_freq_power_00_BG, + NELEMENTS(channel_freq_power_00_BG), + }, + { + 0x10, /* US FCC */ + channel_freq_power_US_BG, + NELEMENTS(channel_freq_power_US_BG), + }, + { + 0x20, /* CANADA IC */ + channel_freq_power_US_BG, + NELEMENTS(channel_freq_power_US_BG), + }, + { + 0x30, /* EU */ + channel_freq_power_EU_BG, + NELEMENTS(channel_freq_power_EU_BG), + }, + { + 0x40, /* JAPAN */ + channel_freq_power_JPN40_BG, + NELEMENTS(channel_freq_power_JPN40_BG), + }, + { + 0x41, /* JAPAN */ + channel_freq_power_JPN41_BG, + NELEMENTS(channel_freq_power_JPN41_BG), + }, + { + 0x50, /* China */ + channel_freq_power_EU_BG, + NELEMENTS(channel_freq_power_EU_BG), + }, + { + 0xfe, /* JAPAN */ + channel_freq_power_JPNFE_BG, + NELEMENTS(channel_freq_power_JPNFE_BG), + }, + { + 0xff, /* Special */ + channel_freq_power_SPECIAL_BG, + NELEMENTS(channel_freq_power_SPECIAL_BG), + }, + /* Add new region here */ +}; + +/** Number of the CFP tables for 2.4GHz */ +#define MLAN_CFP_TABLE_SIZE_BG (NELEMENTS(cfp_table_BG)) + +/* Format { Channel, Frequency (MHz), MaxTxPower, DFS } */ +/** Band: 'A', Region: World Wide Safe */ +static chan_freq_power_t channel_freq_power_00_A[] = { + {36, 5180, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0}}, + {40, 5200, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0}}, + {44, 5220, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0}}, + {48, 5240, WLAN_TX_PWR_00_DEFAULT, MFALSE, {0x10, 0}}, + {52, 5260, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {56, 5280, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {60, 5300, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {64, 5320, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {100, 5500, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {104, 5520, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {108, 5540, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {112, 5560, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {116, 5580, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {120, 5600, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {124, 5620, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {128, 5640, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {132, 5660, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {136, 5680, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {140, 5700, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {144, 5720, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {149, 5745, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {153, 5765, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {157, 5785, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {161, 5805, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}, + {165, 5825, WLAN_TX_PWR_00_DEFAULT, MTRUE, {0x13, 0}}}; +/* Format { Channel, Frequency (MHz), MaxTxPower, DFS } */ +/** Band: 'A', Region: USA FCC */ +static chan_freq_power_t channel_freq_power_A[] = { + {36, 5180, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {100, 5500, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {120, 5600, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {124, 5620, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {128, 5640, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {144, 5720, 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}, + {144, 5720, WLAN_TX_PWR_US_DEFAULT, MTRUE}, + {149, 5745, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_US_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_US_DEFAULT, MFALSE}}; + +/** Band: 'A', Region: Europe ETSI */ +static chan_freq_power_t channel_freq_power_EU_A[] = { + {36, 5180, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {100, 5500, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {120, 5600, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {124, 5620, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {128, 5640, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {149, 5745, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}}; + +/** Band: 'A', Region: Japan */ +static chan_freq_power_t channel_freq_power_JPN_A[] = { + {36, 5180, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_JP_A_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {100, 5500, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {120, 5600, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {124, 5620, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {128, 5640, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}, + {144, 5720, WLAN_TX_PWR_JP_A_DEFAULT, MTRUE}}; + +/** Band: 'A', Region: China */ +static chan_freq_power_t channel_freq_power_CN_A[] = { + {36, 5180, WLAN_TX_PWR_200MW, MFALSE}, + {40, 5200, WLAN_TX_PWR_200MW, MFALSE}, + {44, 5220, WLAN_TX_PWR_200MW, MFALSE}, + {48, 5240, WLAN_TX_PWR_200MW, MFALSE}, + {52, 5260, WLAN_TX_PWR_200MW, MTRUE}, + {56, 5280, WLAN_TX_PWR_200MW, MTRUE}, + {60, 5300, WLAN_TX_PWR_200MW, MTRUE}, + {64, 5320, WLAN_TX_PWR_200MW, MTRUE}, + {149, 5745, WLAN_TX_PWR_CN_2000MW, MFALSE}, + {153, 5765, WLAN_TX_PWR_CN_2000MW, MFALSE}, + {157, 5785, WLAN_TX_PWR_CN_2000MW, MFALSE}, + {161, 5805, WLAN_TX_PWR_CN_2000MW, MFALSE}, + {165, 5825, WLAN_TX_PWR_CN_2000MW, MFALSE}}; + +/** Band: 'A', NULL */ +static chan_freq_power_t channel_freq_power_NULL_A[] = {}; + +/** Band: 'A', Region: Spain/Austria/Brazil */ +static chan_freq_power_t channel_freq_power_SPN2_A[] = { + {36, 5180, WLAN_TX_PWR_200MW, MFALSE}, + {40, 5200, WLAN_TX_PWR_200MW, MFALSE}, + {44, 5220, WLAN_TX_PWR_200MW, MFALSE}, + {48, 5240, WLAN_TX_PWR_200MW, MFALSE}, + {52, 5260, WLAN_TX_PWR_200MW, MTRUE}, + {56, 5280, WLAN_TX_PWR_200MW, MTRUE}, + {60, 5300, WLAN_TX_PWR_200MW, MTRUE}, + {64, 5320, WLAN_TX_PWR_200MW, MTRUE}, +}; + +/** Band: 'A', Region: Brazil */ +static chan_freq_power_t channel_freq_power_BR1_A[] = { + {100, 5500, WLAN_TX_PWR_250MW, MTRUE}, + {104, 5520, WLAN_TX_PWR_250MW, MTRUE}, + {108, 5540, WLAN_TX_PWR_250MW, MTRUE}, + {112, 5560, WLAN_TX_PWR_250MW, MTRUE}, + {116, 5580, WLAN_TX_PWR_250MW, MTRUE}, + {120, 5600, WLAN_TX_PWR_250MW, MTRUE}, + {124, 5620, WLAN_TX_PWR_250MW, MTRUE}, + {128, 5640, WLAN_TX_PWR_250MW, MTRUE}, + {132, 5660, WLAN_TX_PWR_250MW, MTRUE}, + {136, 5680, WLAN_TX_PWR_250MW, MTRUE}, + {140, 5700, WLAN_TX_PWR_250MW, MTRUE}, +}; + +/** Band: 'A', Region: Brazil */ +static chan_freq_power_t channel_freq_power_BR2_A[] = { + {149, 5745, WLAN_TX_PWR_1000MW, MFALSE}, + {153, 5765, WLAN_TX_PWR_1000MW, MFALSE}, + {157, 5785, WLAN_TX_PWR_1000MW, MFALSE}, + {161, 5805, WLAN_TX_PWR_1000MW, MFALSE}, + {165, 5825, WLAN_TX_PWR_1000MW, MFALSE}}; + +/** Band: 'A', Region: Russia */ +static chan_freq_power_t channel_freq_power_RU_A[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_DEFAULT, MFALSE}, + {56, 5280, WLAN_TX_PWR_DEFAULT, MFALSE}, + {60, 5300, WLAN_TX_PWR_DEFAULT, MFALSE}, + {64, 5320, WLAN_TX_PWR_DEFAULT, MFALSE}, + {132, 5660, WLAN_TX_PWR_DEFAULT, MFALSE}, + {136, 5680, WLAN_TX_PWR_DEFAULT, MFALSE}, + {140, 5700, WLAN_TX_PWR_DEFAULT, MFALSE}, + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, +}; + +/** Band: 'A', Region: Mexico */ +static chan_freq_power_t channel_freq_power_MX_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}, + {132, 5660, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_EMEA_DEFAULT, MTRUE}, + {149, 5745, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_EMEA_DEFAULT, MFALSE}}; + +/** Band: 'A', Code: 1, Low band (5150-5250 MHz) channels */ +static chan_freq_power_t channel_freq_power_low_band[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, +}; + +/** Band: 'A', Code: 2, Lower middle band (5250-5350 MHz) channels */ +static chan_freq_power_t channel_freq_power_lower_middle_band[] = { + {52, 5260, WLAN_TX_PWR_DEFAULT, MTRUE}, + {56, 5280, WLAN_TX_PWR_DEFAULT, MTRUE}, + {60, 5300, WLAN_TX_PWR_DEFAULT, MTRUE}, + {64, 5320, WLAN_TX_PWR_DEFAULT, MTRUE}, +}; + +/** Band: 'A', Code: 3, Upper middle band (5470-5725 MHz) channels */ +static chan_freq_power_t channel_freq_power_upper_middle_band[] = { + {100, 5500, WLAN_TX_PWR_DEFAULT, MTRUE}, + {104, 5520, WLAN_TX_PWR_DEFAULT, MTRUE}, + {108, 5540, WLAN_TX_PWR_DEFAULT, MTRUE}, + {112, 5560, WLAN_TX_PWR_DEFAULT, MTRUE}, + {116, 5580, WLAN_TX_PWR_DEFAULT, MTRUE}, + {120, 5600, WLAN_TX_PWR_DEFAULT, MTRUE}, + {124, 5620, WLAN_TX_PWR_DEFAULT, MTRUE}, + {128, 5640, WLAN_TX_PWR_DEFAULT, MTRUE}, + {132, 5660, WLAN_TX_PWR_DEFAULT, MTRUE}, + {136, 5680, WLAN_TX_PWR_DEFAULT, MTRUE}, + {140, 5700, WLAN_TX_PWR_DEFAULT, MTRUE}, +}; + +/** Band: 'A', Code: 4, High band (5725-5850 MHz) channels */ +static chan_freq_power_t channel_freq_power_high_band[] = { + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE}}; + +/** Band: 'A', Code: 5, Low band (5150-5250 MHz) and + * High band (5725-5850 MHz) channels + */ +static chan_freq_power_t channel_freq_power_low_high_band[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE}}; + +/** Band: 'A', Code: 6, Low band (5150-5250 MHz) and + * mid low (5260-5320) and High band (5725-5850 MHz) channels + */ +static chan_freq_power_t channel_freq_power_low_middle_high_band[] = { + {36, 5180, WLAN_TX_PWR_DEFAULT, MFALSE}, + {40, 5200, WLAN_TX_PWR_DEFAULT, MFALSE}, + {44, 5220, WLAN_TX_PWR_DEFAULT, MFALSE}, + {48, 5240, WLAN_TX_PWR_DEFAULT, MFALSE}, + {52, 5260, WLAN_TX_PWR_DEFAULT, MFALSE}, + {56, 5280, WLAN_TX_PWR_DEFAULT, MFALSE}, + {60, 5300, WLAN_TX_PWR_DEFAULT, MFALSE}, + {64, 5320, WLAN_TX_PWR_DEFAULT, MFALSE}, + {149, 5745, WLAN_TX_PWR_DEFAULT, MFALSE}, + {153, 5765, WLAN_TX_PWR_DEFAULT, MFALSE}, + {157, 5785, WLAN_TX_PWR_DEFAULT, MFALSE}, + {161, 5805, WLAN_TX_PWR_DEFAULT, MFALSE}, + {165, 5825, WLAN_TX_PWR_DEFAULT, MFALSE}}; + +/** + * The 5GHz CFP tables + */ +static cfp_table_t cfp_table_A[] = { + {0x1, /* Low band (5150-5250 MHz) channels */ + channel_freq_power_low_band, NELEMENTS(channel_freq_power_low_band)}, + {0x2, /* Lower middle band (5250-5350 MHz) channels */ + channel_freq_power_lower_middle_band, + NELEMENTS(channel_freq_power_lower_middle_band)}, + {0x3, /* Upper middle band (5470-5725 MHz) channels */ + channel_freq_power_upper_middle_band, + NELEMENTS(channel_freq_power_upper_middle_band)}, + {0x4, /* High band (5725-5850 MHz) channels */ + channel_freq_power_high_band, NELEMENTS(channel_freq_power_high_band)}, + {0x5, /* Low band (5150-5250 MHz) and + * High band (5725-5850 MHz) channels + */ + channel_freq_power_low_high_band, + NELEMENTS(channel_freq_power_low_high_band)}, + {0x6, /* Low band (5150-5250 MHz) + * Mid band (5260-5320) and + * High band (5725-5850 MHz) channels + */ + channel_freq_power_low_middle_high_band, + NELEMENTS(channel_freq_power_low_middle_high_band)}, + { + 0x07, /* Mexico */ + channel_freq_power_MX_A, + NELEMENTS(channel_freq_power_MX_A), + }, + + { + 0x09, /* SPAIN/Austria/Brazil */ + channel_freq_power_SPN2_A, + NELEMENTS(channel_freq_power_SPN2_A), + }, + { + 0x0c, /* Brazil */ + channel_freq_power_BR1_A, + NELEMENTS(channel_freq_power_BR1_A), + }, + { + 0x0e, /* Brazil */ + channel_freq_power_BR2_A, + NELEMENTS(channel_freq_power_BR2_A), + }, + { + 0x0f, /* Russia */ + channel_freq_power_RU_A, + NELEMENTS(channel_freq_power_RU_A), + }, + { + 0x00, /* World */ + channel_freq_power_00_A, + NELEMENTS(channel_freq_power_00_A), + }, + { + 0x10, /* US FCC */ + channel_freq_power_A, + NELEMENTS(channel_freq_power_A), + }, + { + 0x20, /* CANADA IC */ + channel_freq_power_CAN_A, + NELEMENTS(channel_freq_power_CAN_A), + }, + { + 0x30, /* EU */ + channel_freq_power_EU_A, + NELEMENTS(channel_freq_power_EU_A), + }, + { + 0x40, /* JAPAN */ + channel_freq_power_JPN_A, + NELEMENTS(channel_freq_power_JPN_A), + }, + { + 0x41, /* JAPAN */ + channel_freq_power_JPN_A, + NELEMENTS(channel_freq_power_JPN_A), + }, + { + 0x50, /* China */ + channel_freq_power_CN_A, + NELEMENTS(channel_freq_power_CN_A), + }, + { + 0xfe, /* JAPAN */ + channel_freq_power_NULL_A, + NELEMENTS(channel_freq_power_NULL_A), + }, + { + 0xff, /* Special */ + channel_freq_power_JPN_A, + NELEMENTS(channel_freq_power_JPN_A), + }, + /* Add new region here */ +}; +/** Number of the CFP tables for 5GHz */ +#define MLAN_CFP_TABLE_SIZE_A (NELEMENTS(cfp_table_A)) + +enum { RATEID_DBPSK1Mbps, //(0) + RATEID_DQPSK2Mbps, //(1) + RATEID_CCK5_5Mbps, //(2) + RATEID_CCK11Mbps, //(3) + RATEID_CCK22Mbps, //(4) + RATEID_OFDM6Mbps, //(5) + RATEID_OFDM9Mbps, //(6) + RATEID_OFDM12Mbps, //(7) + RATEID_OFDM18Mbps, //(8) + RATEID_OFDM24Mbps, //(9) + RATEID_OFDM36Mbps, //(10) + RATEID_OFDM48Mbps, //(11) + RATEID_OFDM54Mbps, //(12) + RATEID_OFDM72Mbps, //(13) +}; + +static const t_u8 rateUnit_500Kbps[] = { + (10 / 5), /* 1Mbps */ + (20 / 5), /* 2Mbps */ + + (55 / 5), /* 5.5Mbps */ + (110 / 5), /* 11Mbps */ + (10 / 5), /* 22Mbps, intentionally set to 1Mbps + * because it's not available + */ + + (60 / 5), /* 6Mbps */ + (90 / 5), /* 9Mbps */ + (120 / 5), /* 12Mbps */ + (180 / 5), /* 18Mbps */ + (240 / 5), /* 24Mbps */ + (360 / 5), /* 36Mbps */ + (480 / 5), /* 48Mbps */ + (540 / 5), /* 54Mbps */ + (60 / 5), /* 72Mbps, intentionally set to 6Mbps + * because it's not available + */ +}; + +typedef struct _rate_map { + /** Rate, in 0.5Mbps */ + t_u32 rate; + /** Mrvl rate id, refer to RATEID_XXX in FW */ + t_u32 id; + /** nss: 0-nss1, 1-nss2 */ + t_u8 nss; +} rate_map; + +/** If user configure to 1x1 or we found peer device only support 1x1, + * then we need skip the nss1 part when map to Mrvl rate. + */ +const rate_map rate_map_table_2x2[] = { + /* LG <--> Mrvl rate idx */ + {2, 0, 0}, // RATEID_DBPSK1Mbps + {4, 1, 0}, // RATEID_DQPSK2Mbps + {11, 2, 0}, // RATEID_CCK5_5Mbps + {22, 3, 0}, // RATEID_CCK11Mbps + {44, 4, 0}, // RATEID_CCK22Mbps + {12, 5, 0}, // RATEID_OFDM6Mbps + {18, 6, 0}, // RATEID_OFDM9Mbps + {24, 7, 0}, // RATEID_OFDM12Mbps + {36, 8, 0}, // RATEID_OFDM18Mbps + {48, 9, 0}, // RATEID_OFDM24Mbps + {72, 10, 0}, // RATEID_OFDM36Mbps + {96, 11, 0}, // RATEID_OFDM48Mbps + {108, 12, 0}, // RATEID_OFDM54Mbps + {144, 13, 0}, // RATEID_OFDM72Mbps + + /* HT bw20 <--> Mrvl rate idx - nss2 */ + {26, 22, 1}, // RATEID_MCS8_13Mbps + {52, 23, 1}, // RATEID_MCS9_26Mbps + {78, 24, 1}, // RATEID_MCS10_39Mbps + {104, 25, 1}, // RATEID_MCS11_52Mbps + {156, 26, 1}, // RATEID_MCS12_78Mbps + {208, 27, 1}, // RATEID_MCS13_104Mbps + {234, 28, 1}, // RATEID_MCS14_117Mbps + {260, 29, 1}, // RATEID_MCS15_130Mbps + /* HT bw20 <--> Mrvl rate idx - nss1 */ + {13, 14, 0}, // RATEID_MCS0_6d5Mbps + {26, 15, 0}, // RATEID_MCS1_13Mbps + {39, 16, 0}, // RATEID_MCS2_19d5Mbps + {52, 17, 0}, // RATEID_MCS3_26Mbps + {78, 18, 0}, // RATEID_MCS4_39Mbps + {104, 19, 0}, // RATEID_MCS5_52Mbps + {117, 20, 0}, // RATEID_MCS6_58d5Mbps + {130, 21, 0}, // RATEID_MCS7_65Mbps + + /* HT bw40<--> Mrvl rate idx - nss2 */ + {54, 39, 1}, // RATEID_MCS8BW40_27Mbps + {108, 40, 1}, // RATEID_MCS9BW40_54Mbps + {162, 41, 1}, // RATEID_MCS10BW40_81Mbps + {216, 42, 1}, // RATEID_MCS11BW40_108Mbps + {324, 43, 1}, // RATEID_MCS12BW40_162Mbps + {432, 44, 1}, // RATEID_MCS13BW40_216Mbps + {486, 45, 1}, // RATEID_MCS14BW40_243Mbps + {540, 46, 1}, // RATEID_MCS15BW40_270Mbps + /* HT bw40<--> Mrvl rate idx - nss1 */ + {12, 30, 0}, // RATEID_MCS32BW40_6Mbps + {27, 31, 0}, // RATEID_MCS0BW40_13d5Mbps + {54, 32, 0}, // RATEID_MCS1BW40_27Mbps + {81, 33, 0}, // RATEID_MCS2BW40_40d5Mbps + {108, 34, 0}, // RATEID_MCS3BW40_54Mbps + {162, 35, 0}, // RATEID_MCS4BW40_81Mbps + {216, 36, 0}, // RATEID_MCS5BW40_108Mbps + {243, 37, 0}, // RATEID_MCS6BW40_121d5Mbps + {270, 38, 0}, // RATEID_MCS7BW40_135Mbps + + /* VHT bw20<--> Mrvl rate idx - nss2 */ + {26, 57, 1}, // RATEID_VHT_MCS0_2SS_BW20 13 Mbps + {52, 58, 1}, // RATEID_VHT_MCS1_2SS_BW20 26 Mbps + {78, 59, 1}, // RATEID_VHT_MCS2_2SS_BW20 39 Mbps + {104, 60, 1}, // RATEID_VHT_MCS3_2SS_BW20 52 Mbps + {156, 61, 1}, // RATEID_VHT_MCS4_2SS_BW20 78 Mbps + {208, 62, 1}, // RATEID_VHT_MCS5_2SS_BW20 104 Mbps + {234, 63, 1}, // RATEID_VHT_MCS6_2SS_BW20 117 Mbps + {260, 64, 1}, // RATEID_VHT_MCS7_2SS_BW20 130 Mbps + {312, 65, 1}, // RATEID_VHT_MCS8_2SS_BW20 156 Mbps + {0, 66, 1}, // RATEID_VHT_MCS9_2SS_BW20 173.3 Mbps(INVALID) + /* VHT bw20<--> Mrvl rate idx - nss1 */ + {13, 47, 0}, // RATEID_VHT_MCS0_1SS_BW20 6.5 Mbps + {26, 48, 0}, // RATEID_VHT_MCS1_1SS_BW20 13 Mbps + {39, 49, 0}, // RATEID_VHT_MCS2_1SS_BW20 19.5 Mbps + {52, 50, 0}, // RATEID_VHT_MCS3_1SS_BW20 26 Mbps + {78, 51, 0}, // RATEID_VHT_MCS4_1SS_BW20 39 Mbps + {104, 52, 0}, // RATEID_VHT_MCS5_1SS_BW20 52 Mbps + {117, 53, 0}, // RATEID_VHT_MCS6_1SS_BW20 58.5 Mbps + {130, 54, 0}, // RATEID_VHT_MCS7_1SS_BW20 65 Mbps + {156, 55, 0}, // RATEID_VHT_MCS8_1SS_BW20 78 Mbps + {0, 56, 0}, // RATEID_VHT_MCS9_1SS_BW20 86.7 Mbps(INVALID) + + /* VHT bw40<--> Mrvl rate idx - nss2 */ + {54, 77, 1}, // RATEID_VHT_MCS0_2SS_BW40 27 Mbps + {108, 78, 1}, // RATEID_VHT_MCS1_2SS_BW40 54 Mbps + {162, 79, 1}, // RATEID_VHT_MCS2_2SS_BW40 81 Mbps + {216, 80, 1}, // RATEID_VHT_MCS3_2SS_BW40 108 Mbps + {324, 81, 1}, // RATEID_VHT_MCS4_2SS_BW40 162 Mbps + {432, 82, 1}, // RATEID_VHT_MCS5_2SS_BW40 216 Mbps + {486, 83, 1}, // RATEID_VHT_MCS6_2SS_BW40 243 Mbps + {540, 84, 1}, // RATEID_VHT_MCS7_2SS_BW40 270 Mbps + {648, 85, 1}, // RATEID_VHT_MCS8_2SS_BW40 324 Mbps + {720, 86, 1}, // RATEID_VHT_MCS9_2SS_BW40 360 Mbps + /* VHT bw40<--> Mrvl rate idx - nss1 */ + {27, 67, 0}, // RATEID_VHT_MCS0_1SS_BW40 13.5 Mbps + {54, 68, 0}, // RATEID_VHT_MCS1_1SS_BW40 27 Mbps + {81, 69, 0}, // RATEID_VHT_MCS2_1SS_BW40 40.5 Mbps + {108, 70, 0}, // RATEID_VHT_MCS3_1SS_BW40 54 Mbps + {162, 71, 0}, // RATEID_VHT_MCS4_1SS_BW40 81 Mbps + {216, 72, 0}, // RATEID_VHT_MCS5_1SS_BW40 108 Mbps + {243, 73, 0}, // RATEID_VHT_MCS6_1SS_BW40 121.5 Mbps + {270, 74, 0}, // RATEID_VHT_MCS7_1SS_BW40 135 Mbps + {324, 75, 0}, // RATEID_VHT_MCS8_1SS_BW40 162 Mbps + {360, 76, 0}, // RATEID_VHT_MCS9_1SS_BW40 180 Mbps + + /* VHT bw80<--> Mrvl rate idx - nss2 */ + {117, 97, 1}, // RATEID_VHT_MCS0_2SS_BW80 58.5 Mbps + {234, 98, 1}, // RATEID_VHT_MCS1_2SS_BW80 117 Mbps + {350, 99, 1}, // RATEID_VHT_MCS2_2SS_BW80 175 Mbps + {468, 100, 1}, // RATEID_VHT_MCS3_2SS_BW80 234 Mbps + {702, 101, 1}, // RATEID_VHT_MCS4_2SS_BW80 351 Mbps + {936, 102, 1}, // RATEID_VHT_MCS5_2SS_BW80 468 Mbps + {1053, 103, 1}, // RATEID_VHT_MCS6_2SS_BW80 526.5 Mbps + {1170, 104, 1}, // RATEID_VHT_MCS7_2SS_BW80 585 Mbps + {1404, 105, 1}, // RATEID_VHT_MCS8_2SS_BW80 702 Mbps + {1560, 106, 1}, // RATEID_VHT_MCS9_2SS_BW80 780 Mbps + /* VHT bw80<--> Mrvl rate idx - nss1 */ + {58, 87, 0}, // RATEID_VHT_MCS0_1SS_BW80 29.3 Mbps, 29.3x2 could + // correspond to 58 + {59, 87, 0}, // RATEID_VHT_MCS0_1SS_BW80 29.3 Mbps, 29.3*2 could + // correspond to 59 too + {117, 88, 0}, // RATEID_VHT_MCS1_1SS_BW80 58.5 Mbps + {175, 89, 0}, // RATEID_VHT_MCS2_1SS_BW80 87.8 Mbps, 87.8x2 could + // correspond to 175 + {176, 89, 0}, // RATEID_VHT_MCS2_1SS_BW80 87.8 Mbps, 87.8x2 could + // correspond to 176 too + {234, 90, 0}, // RATEID_VHT_MCS3_1SS_BW80 117 Mbps + {351, 91, 0}, // RATEID_VHT_MCS4_1SS_BW80 175.5 Mbps + {468, 92, 0}, // RATEID_VHT_MCS5_1SS_BW80 234 Mbps + {526, 93, 0}, // RATEID_VHT_MCS6_1SS_BW80 263.3 Mbps, 263.3x2 could + // correspond to 526 + {527, 93, 0}, // RATEID_VHT_MCS6_1SS_BW80 263.3 Mbps, 263.3x2 could + // correspond to 527 too + {585, 94, 0}, // RATEID_VHT_MCS7_1SS_BW80 292.5 Mbps + {702, 95, 0}, // RATEID_VHT_MCS8_1SS_BW80 351 Mbps + {780, 96, 0}, // RATEID_VHT_MCS9_1SS_BW80 390 Mbps +}; + +/** rate_map_table_1x1 is based on rate_map_table_2x2 and remove nss2 part. + * For the chip who only support 1x1, Mrvl rate idx define is different with 2x2 + * in FW We need redefine a bitrate to Mrvl rate idx table for 1x1 chip. + */ +const rate_map rate_map_table_1x1[] = { + /* LG <--> Mrvl rate idx */ + {2, 0, 0}, // RATEID_DBPSK1Mbps + {4, 1, 0}, // RATEID_DQPSK2Mbps + {11, 2, 0}, // RATEID_CCK5_5Mbps + {22, 3, 0}, // RATEID_CCK11Mbps + {44, 4, 0}, // RATEID_CCK22Mbps + {12, 5, 0}, // RATEID_OFDM6Mbps + {18, 6, 0}, // RATEID_OFDM9Mbps + {24, 7, 0}, // RATEID_OFDM12Mbps + {36, 8, 0}, // RATEID_OFDM18Mbps + {48, 9, 0}, // RATEID_OFDM24Mbps + {72, 10, 0}, // RATEID_OFDM36Mbps + {96, 11, 0}, // RATEID_OFDM48Mbps + {108, 12, 0}, // RATEID_OFDM54Mbps + {144, 13, 0}, // RATEID_OFDM72Mbps + + /* HT bw20 <--> Mrvl rate idx */ + {13, 14, 0}, // RATEID_MCS0_6d5Mbps + {26, 15, 0}, // RATEID_MCS1_13Mbps + {39, 16, 0}, // RATEID_MCS2_19d5Mbps + {52, 17, 0}, // RATEID_MCS3_26Mbps + {78, 18, 0}, // RATEID_MCS4_39Mbps + {104, 19, 0}, // RATEID_MCS5_52Mbps + {117, 20, 0}, // RATEID_MCS6_58d5Mbps + {130, 21, 0}, // RATEID_MCS7_65Mbps + + /* HT bw40<--> Mrvl rate idx */ + {12, 22, 0}, // RATEID_MCS32BW40_6Mbps, for 1x1 start from 22 + {27, 23, 0}, // RATEID_MCS0BW40_13d5Mbps + {54, 24, 0}, // RATEID_MCS1BW40_27Mbps + {81, 25, 0}, // RATEID_MCS2BW40_40d5Mbps + {108, 26, 0}, // RATEID_MCS3BW40_54Mbps + {162, 27, 0}, // RATEID_MCS4BW40_81Mbps + {216, 28, 0}, // RATEID_MCS5BW40_108Mbps + {243, 29, 0}, // RATEID_MCS6BW40_121d5Mbps + {270, 30, 0}, // RATEID_MCS7BW40_135Mbps + + /* VHT bw20<--> Mrvl rate idx */ + {13, 31, 0}, // RATEID_VHT_MCS0_1SS_BW20 6.5 Mbps + {26, 32, 0}, // RATEID_VHT_MCS1_1SS_BW20 13 Mbps + {39, 33, 0}, // RATEID_VHT_MCS2_1SS_BW20 19.5 Mbps + {52, 34, 0}, // RATEID_VHT_MCS3_1SS_BW20 26 Mbps + {78, 35, 0}, // RATEID_VHT_MCS4_1SS_BW20 39 Mbps + {104, 36, 0}, // RATEID_VHT_MCS5_1SS_BW20 52 Mbps + {117, 37, 0}, // RATEID_VHT_MCS6_1SS_BW20 58.5 Mbps + {130, 38, 0}, // RATEID_VHT_MCS7_1SS_BW20 65 Mbps + {156, 39, 0}, // RATEID_VHT_MCS8_1SS_BW20 78 Mbps + {0, 40, 0}, // RATEID_VHT_MCS9_1SS_BW20 86.7 Mbps(INVALID) + + /* VHT bw40<--> Mrvl rate idx */ + {27, 41, 0}, // RATEID_VHT_MCS0_1SS_BW40 13.5 Mbps + {54, 42, 0}, // RATEID_VHT_MCS1_1SS_BW40 27 Mbps + {81, 43, 0}, // RATEID_VHT_MCS2_1SS_BW40 40.5 Mbps + {108, 44, 0}, // RATEID_VHT_MCS3_1SS_BW40 54 Mbps + {162, 45, 0}, // RATEID_VHT_MCS4_1SS_BW40 81 Mbps + {216, 46, 0}, // RATEID_VHT_MCS5_1SS_BW40 108 Mbps + {243, 47, 0}, // RATEID_VHT_MCS6_1SS_BW40 121.5 Mbps + {270, 48, 0}, // RATEID_VHT_MCS7_1SS_BW40 135 Mbps + {324, 49, 0}, // RATEID_VHT_MCS8_1SS_BW40 162 Mbps + {360, 50, 0}, // RATEID_VHT_MCS9_1SS_BW40 180 Mbps + + /* VHT bw80<--> Mrvl rate idx */ + {58, 51, 0}, // RATEID_VHT_MCS0_1SS_BW80 29.3 Mbps, 29.3x2 could + // correspond to 58 + {59, 51, 0}, // RATEID_VHT_MCS0_1SS_BW80 29.3 Mbps, 29.3x2 could + // correspond to 59 too + {117, 52, 0}, // RATEID_VHT_MCS1_1SS_BW80 58.5 Mbps + {175, 53, 0}, // RATEID_VHT_MCS2_1SS_BW80 87.8 Mbps, 87.8x2 could + // correspond to 175 + {176, 53, 0}, // RATEID_VHT_MCS2_1SS_BW80 87.8 Mbps, 87.8x2 could + // correspond to 176 too + {234, 54, 0}, // RATEID_VHT_MCS3_1SS_BW80 117 Mbps + {351, 55, 0}, // RATEID_VHT_MCS4_1SS_BW80 175.5 Mbps + {468, 56, 0}, // RATEID_VHT_MCS5_1SS_BW80 234 Mbps + {526, 57, 0}, // RATEID_VHT_MCS6_1SS_BW80 263.3 Mbps, 263.3x2 could + // correspond to 526 + {527, 57, 0}, // RATEID_VHT_MCS6_1SS_BW80 263.3 Mbps, 263.3x2 could + // correspond to 527 too + {585, 58, 0}, // RATEID_VHT_MCS7_1SS_BW80 292.5 Mbps + {702, 59, 0}, // RATEID_VHT_MCS8_1SS_BW80 351 Mbps + {780, 60, 0}, // RATEID_VHT_MCS9_1SS_BW80 390 Mbps +}; + +/******************************************************** + * Global Variables + ********************************************************/ +/** + * The table to keep region code + */ + +t_u16 region_code_index[MRVDRV_MAX_REGION_CODE] = {0x00, 0x10, 0x20, 0x30, 0x40, + 0x41, 0x50, 0xfe, 0xff}; + +/** The table to keep CFP code for BG */ +t_u16 cfp_code_index_bg[MRVDRV_MAX_CFP_CODE_BG] = {}; +/** The table to keep CFP code for A */ +t_u16 cfp_code_index_a[MRVDRV_MAX_CFP_CODE_A] = {0x1, 0x2, 0x3, 0x4, 0x5}; + +/** + * The rates supported for ad-hoc B mode + */ +t_u8 AdhocRates_B[B_SUPPORTED_RATES] = {0x82, 0x84, 0x8b, 0x96, 0}; + +/** + * The rates supported for ad-hoc G mode + */ +t_u8 AdhocRates_G[G_SUPPORTED_RATES] = {0x8c, 0x12, 0x98, 0x24, 0xb0, + 0x48, 0x60, 0x6c, 0x00}; + +/** + * The rates supported for ad-hoc BG mode + */ +t_u8 AdhocRates_BG[BG_SUPPORTED_RATES] = {0x82, 0x84, 0x8b, 0x96, 0x0c, + 0x12, 0x18, 0x24, 0x30, 0x48, + 0x60, 0x6c, 0x00}; + +/** + * The rates supported in A mode for ad-hoc + */ +t_u8 AdhocRates_A[A_SUPPORTED_RATES] = {0x8c, 0x12, 0x98, 0x24, 0xb0, + 0x48, 0x60, 0x6c, 0x00}; + +/** + * The rates supported in A mode (used for BAND_A) + */ +t_u8 SupportedRates_A[A_SUPPORTED_RATES] = {0x0c, 0x12, 0x18, 0x24, 0xb0, + 0x48, 0x60, 0x6c, 0x00}; + +/** + * The rates supported by the card + */ +t_u16 WlanDataRates[WLAN_SUPPORTED_RATES_EXT] = { + 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, 0x18, 0x24, 0x30, 0x48, + 0x60, 0x6C, 0x90, 0x0D, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, + 0x0C, 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x00}; + +/** + * The rates supported in B mode + */ +t_u8 SupportedRates_B[B_SUPPORTED_RATES] = {0x02, 0x04, 0x0b, 0x16, 0x00}; + +/** + * The rates supported in G mode (BAND_G, BAND_G|BAND_GN) + */ +t_u8 SupportedRates_G[G_SUPPORTED_RATES] = {0x0c, 0x12, 0x18, 0x24, 0x30, + 0x48, 0x60, 0x6c, 0x00}; + +/** + * The rates supported in BG mode (BAND_B|BAND_G, BAND_B|BAND_G|BAND_GN) + */ +t_u8 SupportedRates_BG[BG_SUPPORTED_RATES] = {0x02, 0x04, 0x0b, 0x0c, 0x12, + 0x16, 0x18, 0x24, 0x30, 0x48, + 0x60, 0x6c, 0x00}; + +/** + * The rates supported in N mode + */ +t_u8 SupportedRates_N[N_SUPPORTED_RATES] = {0x02, 0x04, 0}; + +#define MCS_NUM_AX 12 +// for MCS0/MCS1/MCS3/MCS4 have 4 additional DCM=1 value +// note: the value in the table is 2 multiplier of the actual rate +t_u16 ax_mcs_rate_nss1[12][MCS_NUM_AX + 4] = { + {0x90, 0x48, 0x120, 0x90, 0x1B0, 0x240, 0x120, 0x360, 0x1B0, 0x481, + 0x511, 0x5A1, 0x6C1, 0x781, 0x871, 0x962}, /*SG 160M*/ + {0x88, 0x44, 0x110, 0x88, 0x198, 0x220, 0x110, 0x330, 0x198, 0x440, + 0x4C9, 0x551, 0x661, 0x716, 0x7F9, 0x8DC}, /*MG 160M*/ + {0x7A, 0x3D, 0xF5, 0x7A, 0x16F, 0x1EA, 0xF5, 0x2DF, 0x16F, 0x3D4, 0x44E, + 0x4C9, 0x5BE, 0x661, 0x72D, 0x7F9}, /*LG 160M*/ + {0x48, 0x24, 0x90, 0x48, 0xD8, 0x120, 0x90, 0x1B0, 0xD8, 0x240, 0x288, + 0x2D0, 0x360, 0x3C0, 0x438, 0x4B0}, /*SG 80M*/ + {0x44, 0x22, 0x88, 0x44, 0xCC, 0x110, 0x88, 0x198, 0xCC, 0x220, 0x264, + 0x2A8, 0x330, 0x38B, 0x3FC, 0x46E}, /*MG 80M*/ + {0x3D, 0x1E, 0x7A, 0x3D, 0xB7, 0xF5, 0x7A, 0x16F, 0xB7, 0x1EA, 0x227, + 0x264, 0x2DF, 0x330, 0x396, 0x3FC}, /*LG 80M*/ + {0x22, 0x11, 0x44, 0x22, 0x67, 0x89, 0x44, 0xCE, 0x67, 0x113, 0x135, + 0x158, 0x19D, 0x1CA, 0x204, 0x23D}, /*SG 40M*/ + {0x20, 0x10, 0x41, 0x20, 0x61, 0x82, 0x41, 0xC3, 0x61, 0x104, 0x124, + 0x145, 0x186, 0x1B1, 0x1E7, 0x21D}, /*MG 40M*/ + {0x1D, 0xE, 0x3A, 0x1D, 0x57, 0x75, 0x3A, 0xAF, 0x57, 0xEA, 0x107, + 0x124, 0x15F, 0x186, 0x1B6, 0x1E7}, /*LG 40M*/ + {0x11, 0x8, 0x22, 0x11, 0x33, 0x44, 0x22, 0x67, 0x33, 0x89, 0x9A, 0xAC, + 0xCE, 0xE5, 0x102, 0x11E}, /*SG 20M*/ + {0x10, 0x8, 0x20, 0x10, 0x30, 0x41, 0x20, 0x61, 0x30, 0x82, 0x92, 0xA2, + 0xC3, 0xD8, 0xF3, 0x10E}, /*MG 20M*/ + {0xE, 0x7, 0x1D, 0xE, 0x2B, 0x3A, 0x1D, 0x57, 0x2B, 0x75, 0x83, 0x92, + 0xAF, 0xC3, 0xDB, 0xF3} /*LG 20M*/ +}; + +// note: the value in the table is 2 multiplier of the actual rate +t_u16 ax_tone_ru_rate_nss1[9][MCS_NUM_AX + 4] = { + {0x8, 0x4, 0xF, 0x8, 0x17, 0x1E, 0xF, 0x2D, 0x17, 0x3C, 0x44, 0x4B, + 0x5A, 0x64, 0x71, 0x7D}, /*SG 106-tone*/ + {0x7, 0x4, 0xF, 0x7, 0x16, 0x1D, 0xF, 0x2B, 0x16, 0x39, 0x40, 0x47, + 0x55, 0x5F, 0x6B, 0x76}, /*MG 106-tone*/ + {0x7, 0x3, 0xD, 0x6, 0x14, 0x1A, 0xD, 0x27, 0x14, 0x33, 0x3A, 0x40, + 0x4D, 0x55, 0x60, 0x6B}, /*LG 106-tone*/ + {0x4, 0x2, 0x7, 0x4, 0xB, 0xF, 0x7, 0x16, 0xB, 0x1D, 0x20, 0x22, 0x2B, + 0x2F, 0x35, 0x3B}, /*SG 52-tone*/ + {0x4, 0x2, 0x7, 0x4, 0xA, 0xE, 0x7, 0x14, 0xA, 0x1B, 0x1E, 0x22, 0x28, + 0x2D, 0x32, 0x38}, /*MG 52-tone*/ + {0x3, 0x2, 0x6, 0x3, 0x9, 0xC, 0x6, 0x12, 0x9, 0x18, 0x1B, 0x1E, 0x24, + 0x28, 0x2D, 0x32}, /*LG 52-tone*/ + {0x2, 0x1, 0x4, 0x2, 0x6, 0x7, 0x4, 0xB, 0x5, 0xE, 0x10, 0x12, 0x15, + 0x18, 0x1A, 0x1D}, /*SG 26-tone*/ + {0x2, 0x1, 0x4, 0x2, 0x5, 0x6, 0x4, 0xA, 0x5, 0xD, 0xF, 0x11, 0x14, + 0x16, 0x19, 0x1C}, /*MG 26-tone*/ + {0x2, 0x1, 0x3, 0x2, 0x5, 0x6, 0x3, 0x9, 0x4, 0xC, 0xE, 0xF, 0x12, 0x14, + 0x17, 0x19} /*LG 26-tone*/ +}; + +// note: the value in the table is 2 multiplier of the actual rate +t_u16 ax_mcs_rate_nss2[12][MCS_NUM_AX + 4] = { + {0x120, 0x90, 0x240, 0x120, 0x360, 0x481, 0x240, 0x61C, 0x360, 0x901, + 0xA22, 0xB42, 0xD82, 0xF03, 0x10E3, 0x12C3}, /*SG 160M*/ + {0x110, 0x88, 0x220, 0x110, 0x330, 0x440, 0x220, 0x661, 0x330, 0x881, + 0x992, 0xAA2, 0xCAC, 0xE2D, 0xFF3, 0x11B9}, /*MG 160M*/ + {0xF5, 0x7A, 0x1EA, 0xF5, 0x2DF, 0x3D4, 0x1EA, 0x5BE, 0x2DF, 0x7A8, + 0x1134, 0x992, 0xB7C, 0xCC2, 0xE5B, 0xFF3}, /*LG 160M*/ + {0x90, 0x48, 0x120, 0x90, 0x1B0, 0x240, 0x120, 0x360, 0x1B0, 0x481, + 0x511, 0x5A1, 0x6C1, 0x781, 0x871, 0x962}, /*SG 80M*/ + {0x88, 0x44, 0x110, 0x88, 0x198, 0x220, 0x110, 0x330, 0x198, 0x440, + 0x4C9, 0x551, 0x661, 0x716, 0x7F9, 0x8DC}, /*MG 80M*/ + {0x7A, 0x3D, 0xF5, 0x7A, 0x16F, 0x1EA, 0xF5, 0x2DF, 0x16F, 0x3D4, 0x44E, + 0x4C9, 0x5BE, 0x661, 0x72D, 0x7F9}, /*LG 80M*/ + {0x44, 0x22, 0x89, 0x44, 0xCE, 0x113, 0x89, 0x19D, 0xCE, 0x226, 0x26B, + 0x2B0, 0x339, 0x395, 0x408, 0x47B}, /*SG 40M*/ + {0x41, 0x20, 0x82, 0x41, 0xC3, 0x104, 0x82, 0x186, 0xC3, 0x208, 0x249, + 0x28A, 0x30C, 0x362, 0x3CE, 0x43B}, /*MG 40M*/ + {0x3A, 0x1D, 0x75, 0x3A, 0xAF, 0xEA, 0x75, 0x15F, 0xAF, 0x1D4, 0x20E, + 0x249, 0x2BE, 0x30C, 0x36D, 0x3CF}, /*LG 40M*/ + {0x22, 0x11, 0x44, 0x22, 0x67, 0x89, 0x44, 0xCE, 0x67, 0x113, 0x135, + 0x158, 0x19D, 0x1CA, 0x204, 0x23D}, /*SG 20M*/ + {0x20, 0x10, 0x41, 0x20, 0x61, 0x82, 0x41, 0xC3, 0x61, 0x104, 0x124, + 0x145, 0x186, 0x1B1, 0x1E7, 0x21D}, /*MG 20M*/ + {0x1D, 0xE, 0x3A, 0x1D, 0x57, 0x75, 0x3A, 0xAF, 0x57, 0xEA, 0x107, + 0x124, 0x15F, 0x186, 0x1B6, 0x1E7} /*LG 20M*/ +}; + +// note: the value in the table is 2 multiplier of the actual rate +t_u16 ax_tone_ru_rate_nss2[9][MCS_NUM_AX + 4] = { + {0xF, 0x8, 0x1E, 0xF, 0x2D, 0x3C, 0x1E, 0x5A, 0x2D, 0x78, 0x87, 0x96, + 0xB4, 0xC8, 0xE1, 0xFA}, /*SG 106-tone*/ + {0xE, 0x7, 0x1D, 0xE, 0x2B, 0x39, 0x1D, 0x55, 0x2B, 0x72, 0x80, 0x8E, + 0xAA, 0xBD, 0xD5, 0xED}, /*MG 106-tone*/ + {0xD, 0x7, 0x1A, 0xD, 0x27, 0x33, 0x1A, 0x4D, 0x27, 0x66, 0x73, 0x80, + 0x99, 0xAA, 0xC0, 0xD5}, /*LG 106-tone*/ + {0x7, 0x4, 0xF, 0x7, 0x16, 0x1D, 0xF, 0x2A, 0x16, 0x39, 0x40, 0x47, + 0x55, 0x5F, 0x6A, 0x76}, /*SG 52-tone*/ + {0x7, 0x4, 0xE, 0x7, 0x14, 0x1B, 0xE, 0x28, 0x14, 0x36, 0x3C, 0x43, + 0x50, 0x59, 0x64, 0x70}, /*MG 52-tone*/ + {0x6, 0x3, 0xC, 0x6, 0x12, 0x18, 0xC, 0x24, 0x12, 0x30, 0x36, 0x3C, + 0x48, 0x50, 0x5A, 0x64}, /*LG 52-tone*/ + {0x4, 0x2, 0x7, 0x4, 0xB, 0xF, 0x7, 0x16, 0xB, 0x1D, 0x20, 0x22, 0x2B, + 0x2F, 0x35, 0x3B}, /*SG 26-tone*/ + {0x4, 0x2, 0x7, 0x4, 0xA, 0xE, 0x7, 0x14, 0xA, 0x1B, 0x1E, 0x22, 0x28, + 0x2D, 0x32, 0x38}, /*MG 26-tone*/ + {0x3, 0x2, 0x6, 0x3, 0x9, 0xC, 0x6, 0x12, 0x9, 0x18, 0x1B, 0x1E, 0x24, + 0x28, 0x2D, 0x32} /*LG 26-tone*/ +}; + +/******************************************************** + * Local Functions + ********************************************************/ +/** + * @brief Find a character in a string. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param s A pointer to string + * @param c Character to be located + * @param n The length of string + * + * @return A pointer to the first occurrence of c in string, or MNULL if + * c is not found. + */ +static void *wlan_memchr(pmlan_adapter pmadapter, void *s, int c, int n) +{ + const t_u8 *p = (t_u8 *)s; + + ENTER(); + + while (n--) { + if ((t_u8)c == *p++) { + LEAVE(); + return (void *)(p - 1); + } + } + + LEAVE(); + return MNULL; +} + +/** + * @brief This function finds the CFP in + * cfp_table_BG/A based on region/code and band parameter. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param region The region code + * @param band The band + * @param cfp_no A pointer to CFP number + * + * @return A pointer to CFP + */ +static chan_freq_power_t *wlan_get_region_cfp_table(pmlan_adapter pmadapter, + t_u8 region, t_u8 band, + int *cfp_no) +{ + t_u32 i; + t_u8 cfp_bg, cfp_a; + + ENTER(); + + cfp_bg = cfp_a = region; + if (!region) { + /* Invalid region code, use CFP code */ + cfp_bg = pmadapter->cfp_code_bg; + cfp_a = pmadapter->cfp_code_a; + } + + if (band & (BAND_B | BAND_G | BAND_GN | BAND_GAC)) { + /* Return the FW cfp table for requested region code, if + * available. If region is not forced and the requested region + * code is different, simply return the corresponding + * pre-defined table. + */ + if (pmadapter->otp_region && pmadapter->cfp_otp_bg) { + if (pmadapter->otp_region->force_reg || + (cfp_bg == + (t_u8)pmadapter->otp_region->region_code)) { + *cfp_no = pmadapter->tx_power_table_bg_rows; + LEAVE(); + return pmadapter->cfp_otp_bg; + } + } + for (i = 0; i < MLAN_CFP_TABLE_SIZE_BG; i++) { + PRINTM(MINFO, "cfp_table_BG[%d].code=%d\n", i, + cfp_table_BG[i].code); + /* Check if region/code matches for BG bands */ + if (cfp_table_BG[i].code == cfp_bg) { + /* Select by band */ + *cfp_no = cfp_table_BG[i].cfp_no; + LEAVE(); + return cfp_table_BG[i].cfp; + } + } + } + if (band & (BAND_A | BAND_AN | BAND_AAC)) { + /* Return the FW cfp table for requested region code */ + if (pmadapter->otp_region && pmadapter->cfp_otp_a) { + if (pmadapter->otp_region->force_reg || + (cfp_a == + (t_u8)pmadapter->otp_region->region_code)) { + *cfp_no = pmadapter->tx_power_table_a_rows; + LEAVE(); + return pmadapter->cfp_otp_a; + } + } + for (i = 0; i < MLAN_CFP_TABLE_SIZE_A; i++) { + PRINTM(MINFO, "cfp_table_A[%d].code=%d\n", i, + cfp_table_A[i].code); + /* Check if region/code matches for A bands */ + if (cfp_table_A[i].code == cfp_a) { + /* Select by band */ + *cfp_no = cfp_table_A[i].cfp_no; + LEAVE(); + return cfp_table_A[i].cfp; + } + } + } + + if (!region) + PRINTM(MERROR, "Error Band[0x%x] or code[BG:%#x, A:%#x]\n", + band, cfp_bg, cfp_a); + else + PRINTM(MERROR, "Error Band[0x%x] or region[%#x]\n", band, + region); + + LEAVE(); + return MNULL; +} + +/** + * @brief This function copies dynamic CFP elements from one table to another. + * Only copy elements where channel numbers match. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param cfp Destination table + * @param num_cfp Number of elements in dest table + * @param cfp_src Source table + * @param num_cfp_src Number of elements in source table + */ +static t_void wlan_cfp_copy_dynamic(pmlan_adapter pmadapter, + chan_freq_power_t *cfp, t_u8 num_cfp, + chan_freq_power_t *cfp_src, + t_u8 num_cfp_src) +{ + int i, j; + + ENTER(); + + if (cfp == cfp_src) { + LEAVE(); + return; + } + + /* first clear dest dynamic blacklisted entries */ + for (i = 0; i < num_cfp; i++) { + cfp[i].dynamic.blacklist = MFALSE; + cfp[i].dynamic.flags = 0; + } + + /* copy dynamic blacklisted entries from source where channels match */ + if (cfp_src) { + for (i = 0; i < num_cfp; i++) + for (j = 0; j < num_cfp_src; j++) + if (cfp[i].channel == cfp_src[j].channel) { + cfp[i].dynamic.blacklist = + cfp_src[j].dynamic.blacklist; + cfp[i].dynamic.flags = + cfp_src[j].dynamic.flags; + break; + } + } + + LEAVE(); +} + +/******************************************************** + * Global Functions + ********************************************************/ +/** + * @brief This function converts region string to integer code + * + * @param pmadapter A pointer to mlan_adapter structure + * @param country_code Country string + * @param cfp_bg Pointer to buffer + * @param cfp_a Pointer to buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_country_2_cfp_table_code(pmlan_adapter pmadapter, + t_u8 *country_code, t_u8 *cfp_bg, + t_u8 *cfp_a) +{ + t_u8 i; + + ENTER(); + + /* Look for code in mapping table */ + for (i = 0; i < NELEMENTS(country_code_mapping); i++) { + if (!memcmp(pmadapter, country_code_mapping[i].country_code, + country_code, COUNTRY_CODE_LEN - 1)) { + *cfp_bg = country_code_mapping[i].cfp_code_bg; + *cfp_a = country_code_mapping[i].cfp_code_a; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + + /* If still not found, look for code in EU country code table */ + for (i = 0; i < NELEMENTS(eu_country_code_table); i++) { + if (!memcmp(pmadapter, eu_country_code_table[i], country_code, + COUNTRY_CODE_LEN - 1)) { + *cfp_bg = EU_CFP_CODE_BG; + *cfp_a = EU_CFP_CODE_A; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief This function finds if given country code is in EU table + * + * @param pmadapter A pointer to mlan_adapter structure + * @param country_code Country string + * + * @return MTRUE or MFALSE + */ +t_bool wlan_is_etsi_country(pmlan_adapter pmadapter, t_u8 *country_code) +{ + t_u8 i; + + ENTER(); + + /* Look for code in EU country code table */ + for (i = 0; i < NELEMENTS(eu_country_code_table); i++) { + if (!memcmp(pmadapter, eu_country_code_table[i], country_code, + COUNTRY_CODE_LEN - 1)) { + LEAVE(); + return MTRUE; + } + } + + LEAVE(); + return MFALSE; +} + +#define BAND_MASK_5G 0x03 +#define ANTENNA_OFFSET 2 +/** + * @brief This function adjust the antenna index + * + * V16_FW_API: Bit0: ant A, Bit 1:ant B, Bit0 & Bit 1: A+B + * 8887: case1: 0 - 2.4G ant A, 1- 2.4G antB, 2-- 5G ant C + * case2: 0 - 2.4G ant A, 1- 2.4G antB, 0x80- 5G antA, 0x81-5G ant B + * @param priv A pointer to mlan_private structure + * @param prx_pd A pointer to the RxPD structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +t_u8 wlan_adjust_antenna(pmlan_private priv, RxPD *prx_pd) +{ + t_u8 antenna = prx_pd->antenna; +#if defined(SD8887) || defined(SD8987) + t_u32 rx_channel = (prx_pd->rx_info & RXPD_CHAN_MASK) >> 5; +#endif + if (prx_pd->antenna == 0xff) + return 0; + if (priv->adapter->pcard_info->v16_fw_api) { + if ((antenna & MBIT(0)) && (antenna & MBIT(1))) + antenna = 2; + else if (antenna & MBIT(1)) + antenna = 1; + else if (antenna & MBIT(0)) + antenna = 0; + } + +#if defined(SD8887) || defined(SD8987) + if (MFALSE +#ifdef SD8887 + || IS_SD8887(priv->adapter->card_type) +#endif +#ifdef SD8987 + || IS_SD8987(priv->adapter->card_type) +#endif + ) { + if ((priv->adapter->antinfo & ANT_DIVERSITY_2G) && + (priv->adapter->antinfo & ANT_DIVERSITY_5G)) { +#define MAX_2G_CHAN 14 + if (rx_channel > MAX_2G_CHAN) + antenna += ANTENNA_OFFSET; + } + } +#endif + + return antenna; +} + +/** + * @brief This function adjust the rate index + * + * @param priv A pointer to mlan_private structure + * @param rx_rate rx rate + * @param rate_info rate info + * @return rate index + */ +t_u16 wlan_adjust_data_rate(mlan_private *priv, t_u8 rx_rate, t_u8 rate_info) +{ + t_u16 rate_index = 0; + t_u8 bw = 0; + t_u8 nss = 0; + t_bool sgi_enable = 0; + t_u8 gi = 0; +#define MAX_MCS_NUM_AX 12 + +#define MAX_MCS_NUM_SUPP 16 +#define MAX_MCS_NUM_AC 10 +#define RATE_INDEX_MCS0 12 + bw = (rate_info & 0xC) >> 2; + sgi_enable = (rate_info & 0x10) >> 4; + if ((rate_info & 0x3) == 0) { + rate_index = (rx_rate > MLAN_RATE_INDEX_OFDM0) ? rx_rate - 1 : + rx_rate; + } else if ((rate_info & 0x03) == 1) { + rate_index = RATE_INDEX_MCS0 + + MAX_MCS_NUM_SUPP * 2 * sgi_enable + + MAX_MCS_NUM_SUPP * bw + rx_rate; + } else if ((rate_info & 0x3) == 2) { + if (IS_STREAM_2X2(priv->adapter->feature_control)) + nss = rx_rate >> 4; // 0:NSS1, 1:NSS2 + rate_index = RATE_INDEX_MCS0 + MAX_MCS_NUM_SUPP * 4 + + MAX_MCS_NUM_AC * 6 * sgi_enable + + MAX_MCS_NUM_AC * 2 * bw + MAX_MCS_NUM_AC * nss + + (rx_rate & 0x0f); + } else if ((rate_info & 0x3) == 3) { + gi = (rate_info & 0x10) >> 4 | (rate_info & 0x80) >> 6; + if (IS_STREAM_2X2(priv->adapter->feature_control)) + nss = rx_rate >> 4; // 0:NSS1, 1:NSS2 + rate_index = RATE_INDEX_MCS0 + MAX_MCS_NUM_SUPP * 4 + + MAX_MCS_NUM_AC * 12 + MAX_MCS_NUM_AX * 6 * gi + + MAX_MCS_NUM_AX * 2 * bw + MAX_MCS_NUM_AX * nss + + (rx_rate & 0x0f); + } + return rate_index; +} + +#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 tx_rate_info Tx rate info + * @param ext_rate_info Extend tx rate info + * + * @return Data rate or 0 + */ +t_u32 wlan_index_to_data_rate(pmlan_adapter pmadapter, t_u8 index, + t_u8 tx_rate_info, t_u8 ext_rate_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 */ + +#define MCS_NUM_AC 10 + /* NSS 1. note: the value in the table is 2 multiplier of the actual + * rate in other words, it is in the unit of 500 Kbs + */ + t_u16 ac_mcs_rate_nss1[8][MCS_NUM_AC] = { + {0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, 0x492, 0x57C, + 0x618}, /* LG 160M*/ + {0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, 0x514, 0x618, + 0x6C6}, /* SG 160M*/ + {0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, 0x249, 0x2BE, + 0x30C}, /* LG 80M */ + {0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, 0x28A, 0x30C, + 0x363}, /* SG 80M */ + {0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, 0x10E, 0x144, + 0x168}, /* LG 40M */ + {0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, 0x12C, 0x168, + 0x190}, /* SG 40M */ + {0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, + 0x00}, /* LG 20M */ + {0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, + 0x00}, /* SG 20M */ + }; + /* NSS 2. note: the value in the table is 2 multiplier of the actual + * rate + */ + t_u16 ac_mcs_rate_nss2[8][MCS_NUM_AC] = { + {0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, 0x924, 0xAF8, + 0xC30}, /*LG 160M*/ + {0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, 0xA28, 0xC30, + 0xD8B}, /*SG 160M*/ + + {0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, 0x492, 0x57C, + 0x618}, /*LG 80M*/ + {0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, 0x514, 0x618, + 0x6C6}, /*SG 80M*/ + {0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, 0x21C, 0x288, + 0x2D0}, /*LG 40M*/ + {0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, 0x258, 0x2D0, + 0x320}, /*SG 40M*/ + {0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, 0x138, + 0x00}, /*LG 20M*/ + {0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, 0x15B, + 0x00}, /*SG 20M*/ + }; + + t_u32 rate = 0; + t_u8 mcs_index = 0; + t_u8 he_dcm = 0; + t_u8 he_tone = 0; + t_u8 stbc = 0; + + t_u8 bw = 0; + t_u8 gi = 0; + + ENTER(); + + PRINTM(MINFO, "%s:index=%d, tx_rate_info=%d, ext_rate_info=%d\n", + __func__, index, tx_rate_info, ext_rate_info); + + if ((tx_rate_info & 0x3) == MLAN_RATE_FORMAT_VHT) { + /* VHT rate */ + mcs_index = index & 0xF; + + if (mcs_index > 9) + mcs_index = 9; + + /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ + bw = (tx_rate_info & 0xC) >> 2; + /* LGI: gi =0, SGI: gi = 1 */ + gi = (tx_rate_info & 0x10) >> 4; + if ((index >> 4) == 1) { + /* NSS = 2 */ + rate = ac_mcs_rate_nss2[2 * (3 - bw) + gi][mcs_index]; + } else + /* NSS = 1 */ + rate = ac_mcs_rate_nss1[2 * (3 - bw) + gi][mcs_index]; + } else + + if ((tx_rate_info & 0x3) == MLAN_RATE_FORMAT_HE) { + /* VHT rate */ + mcs_index = index & 0xF; + he_dcm = ext_rate_info & MBIT(0); + + if (mcs_index > MCS_NUM_AX - 1) + mcs_index = MCS_NUM_AX - 1; + + /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ + bw = (tx_rate_info & (MBIT(3) | MBIT(2))) >> 2; + /* BIT7:BIT4 0:0= 0.8us,0:1= 0.8us, 1:0=1.6us, 1:1=3.2us or + * 0.8us + */ + gi = (tx_rate_info & MBIT(4)) >> 4 | + (tx_rate_info & MBIT(7)) >> 6; + /* STBC: BIT5 in tx rate info */ + stbc = (tx_rate_info & MBIT(5)) >> 5; + + if (gi > 3) { + PRINTM(MERROR, "Invalid gi value"); + return 0; + } + + if ((gi == 3) && stbc && he_dcm) { + gi = 0; + stbc = 0; + he_dcm = 0; + } + /* map to gi 0:0.8us,1:1.6us 2:3.2us*/ + if (gi > 0) + gi = gi - 1; + + // TODO: hardcode he_tone here, wait for FW value ready. + he_tone = 4; + + // he_tone = (ext_rate_info & 0xE) >> 1; + + if ((index >> 4) == 1) { + switch (mcs_index) { + case 0: + case 1: + // #if 0 + // if (he_tone < 3) { + // rate = + //ax_tone_ru_rate_nss2[3*(2-he_tone)+gi][mcs_index*2 + //+ he_dcm]; + // } else { + // #endif + rate = ax_mcs_rate_nss2[3 * (3 - bw) + gi] + [mcs_index * 2 + he_dcm]; + break; + case 2: + // #if 0 + // if (he_tone < 3) { + // rate = + //ax_tone_ru_rate_nss2[3*(2-he_tone)+gi][mcs_index*2]; + // } else { + // #endif + rate = ax_mcs_rate_nss2[3 * (3 - bw) + gi] + [mcs_index * 2]; + break; + case 3: + case 4: + // #if 0 + // if (he_tone < 3) { + // rate = + //ax_tone_ru_rate_nss2[3*(2-he_tone)+gi][mcs_index*2 + //- 1 + he_dcm]; + // } else { + // #endif + rate = ax_mcs_rate_nss2[3 * (3 - bw) + gi] + [mcs_index * 2 - 1 + + he_dcm]; + break; + + default: + // #if 0 + // if (he_tone < 3) { + // rate = + //ax_tone_ru_rate_nss2[3*(2-he_tone)+gi][mcs_index + //+ 4]; + // } else { + // #endif + rate = ax_mcs_rate_nss2[3 * (3 - bw) + gi] + [mcs_index + 4]; + break; + } + } else { + switch (mcs_index) { + case 0: + case 1: + // #if 0 + // if (he_tone < 3) { + // rate = + //ax_tone_ru_rate_nss1[3*(2-he_tone)+gi][mcs_index*2 + //+ he_dcm]; + // } else { + // #endif + rate = ax_mcs_rate_nss1[3 * (3 - bw) + gi] + [mcs_index * 2 + he_dcm]; + break; + case 2: + // #if 0 + // if (he_tone < 3) { + // rate = + //ax_tone_ru_rate_nss1[3*(2-he_tone)+gi][mcs_index*2]; + // } else { + // #endif + rate = ax_mcs_rate_nss1[3 * (3 - bw) + gi] + [mcs_index * 2]; + break; + case 3: + case 4: + // #if 0 + // if (he_tone < 3) { + // rate = + //ax_tone_ru_rate_nss1[3*(2-he_tone)+gi][mcs_index*2 + //- 1 + he_dcm]; + // } else { + // #endif + rate = ax_mcs_rate_nss1[3 * (3 - bw) + gi] + [mcs_index * 2 - 1 + + he_dcm]; + break; + + default: + // #if 0 + // if (he_tone < 3) { + // rate = + //ax_tone_ru_rate_nss1[3*(2-he_tone)+gi][mcs_index + //+ 4]; + // } else { + // #endif + rate = ax_mcs_rate_nss1[3 * (3 - bw) + gi] + [mcs_index + 4]; + break; + } + } + } else if ((tx_rate_info & 0x3) == MLAN_RATE_FORMAT_HT) { + /* HT rate */ + /* 20M: bw=0, 40M: bw=1 */ + bw = (tx_rate_info & 0xC) >> 2; + /* LGI: gi =0, SGI: gi = 1 */ + gi = (tx_rate_info & 0x10) >> 4; + if (index == MLAN_RATE_BITMAP_MCS0) { + if (gi == 1) + rate = 0x0D; /* MCS 32 SGI rate */ + else + rate = 0x0C; /* MCS 32 LGI rate */ + } else if (index < MCS_NUM_SUPP) { + if (bw <= 1) + rate = mcs_rate[2 * (1 - bw) + gi][index]; + else + rate = WlanDataRates[0]; + } else + rate = WlanDataRates[0]; + } else { + /* 11n non HT rates */ + if (index >= WLAN_SUPPORTED_RATES_EXT) + index = 0; + rate = WlanDataRates[index]; + } + LEAVE(); + return rate; +} + +/** + * @brief Use rate to get the index + * + * @param pmadapter A pointer to mlan_adapter structure + * @param rate Data rate + * + * @return Index or 0 + */ +t_u8 wlan_data_rate_to_index(pmlan_adapter pmadapter, t_u32 rate) +{ + t_u16 *ptr; + + ENTER(); + if (rate) { + ptr = wlan_memchr(pmadapter, WlanDataRates, (t_u8)rate, + sizeof(WlanDataRates)); + if (ptr) { + LEAVE(); + return (t_u8)(ptr - WlanDataRates); + } + } + LEAVE(); + return 0; +} + +/** + * @brief Get active data rates + * + * @param pmpriv A pointer to mlan_private structure + * @param bss_mode The specified BSS mode (Infra/IBSS) + * @param config_bands The specified band configuration + * @param rates The buf to return the active rates + * + * @return The number of Rates + */ +t_u32 wlan_get_active_data_rates(mlan_private *pmpriv, t_u32 bss_mode, + t_u16 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 | BAND_AN | BAND_AAC: + /* Fall Through */ + 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_GN | BAND_GAC: + case BAND_B | BAND_G | BAND_GN | BAND_GAC: + case BAND_G | BAND_GN | BAND_GAC: + case BAND_B | BAND_G: + /* Fall Through */ + case BAND_B: /* Matching BAND_B/G */ + /* Fall Through */ + case BAND_G: + /* Fall Through */ + case 0: + break; + default: + continue; + } + break; + default: + continue; + } + if (channel == FIRST_VALID_CHANNEL) + cfp = &rc->pcfp[0]; + else { + for (i = 0; i < rc->num_cfp; i++) { + if (rc->pcfp[i].channel == channel) { + cfp = &rc->pcfp[i]; + break; + } + } + } + } + + if (!cfp && channel) + PRINTM(MCMND, "%s: can not find cfp by band %d & channel %d\n", + __func__, 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 | BAND_AN | BAND_AAC: + /* Fall Through */ + 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_GN | BAND_GAC: + case BAND_B | BAND_G | BAND_GN | BAND_GAC: + case BAND_G | BAND_GN | BAND_GAC: + case BAND_B | BAND_G: + /* Fall Through */ + case BAND_B: + /* Fall Through */ + case BAND_G: + /* Fall Through */ + 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, "%s: cannot find cfp by band %d & freq %d\n", + __func__, 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_u16 config_bands, WLAN_802_11_RATES rates) +{ + t_u32 k = 0; + + ENTER(); + + if (bss_mode == MLAN_BSS_MODE_INFRA) { + /* Infra. mode */ + switch (config_bands) { + case (t_u8)BAND_B: + PRINTM(MINFO, "Infra Band=%d SupportedRates_B\n", + config_bands); + k = wlan_copy_rates(rates, k, SupportedRates_B, + sizeof(SupportedRates_B)); + break; + case (t_u8)BAND_G: + case BAND_G | BAND_GN: + case BAND_G | BAND_GN | BAND_GAC: + case BAND_G | BAND_GN | BAND_GAC | BAND_GAX: + 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_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC: + case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC | + BAND_GAC: + case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC | + BAND_AAX: + case BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AN | BAND_AAC | + BAND_GAC | BAND_AAX | BAND_GAX: + case BAND_B | BAND_G | BAND_GN: + case BAND_B | BAND_G | BAND_GN | BAND_GAC: + case BAND_B | BAND_G | BAND_GN | BAND_GAC | BAND_GAX: + 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: + case BAND_A | BAND_AN | BAND_AAC: + case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC: + case BAND_A | BAND_AN | BAND_AAC | BAND_AAX: + case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC | BAND_AAX: + 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: + case BAND_GN | BAND_GAC: + case BAND_GN | BAND_GAC | BAND_GAX: + PRINTM(MINFO, "Infra band=%d SupportedRates_N\n", + config_bands); + k = wlan_copy_rates(rates, k, SupportedRates_N, + sizeof(SupportedRates_N)); + break; + } + } else { + /* Ad-hoc mode */ + switch (config_bands) { + case (t_u8)BAND_B: + PRINTM(MINFO, "Band: Adhoc B\n"); + k = wlan_copy_rates(rates, k, AdhocRates_B, + sizeof(AdhocRates_B)); + break; + case (t_u8)BAND_G: + PRINTM(MINFO, "Band: Adhoc G only\n"); + k = wlan_copy_rates(rates, k, AdhocRates_G, + sizeof(AdhocRates_G)); + break; + case BAND_B | BAND_G: + PRINTM(MINFO, "Band: Adhoc BG\n"); + k = wlan_copy_rates(rates, k, AdhocRates_BG, + sizeof(AdhocRates_BG)); + break; + case BAND_A: + case BAND_A | BAND_AN | BAND_AAC: + case BAND_A | BAND_AN | BAND_AAC | BAND_AAX: + + PRINTM(MINFO, "Band: Adhoc A\n"); + k = wlan_copy_rates(rates, k, AdhocRates_A, + sizeof(AdhocRates_A)); + break; + } + } + + LEAVE(); + return k; +} + +#define COUNTRY_ID_US 0 +#define COUNTRY_ID_JP 1 +#define COUNTRY_ID_CN 2 +#define COUNTRY_ID_EU 3 +typedef struct _oper_bw_chan { + /*non-global operating class*/ + t_u8 oper_class; + /*global operating class*/ + t_u8 global_oper_class; + /*bandwidth 0-20M 1-40M 2-80M 3-160M*/ + t_u8 bandwidth; + /*channel list*/ + t_u8 channel_list[13]; +} oper_bw_chan; + +/** oper class table for US*/ +static oper_bw_chan oper_bw_chan_us[] = { + /** non-Global oper class, global oper class, bandwidth, channel list*/ + {1, 115, 0, {36, 40, 44, 48}}, + {2, 118, 0, {52, 56, 60, 64}}, + {3, 124, 0, {149, 153, 157, 161}}, + {4, + 121, + 0, + {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144}}, + {5, 125, 0, {149, 153, 157, 161, 165}}, + {12, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}}, + {22, 116, 1, {36, 44}}, + {23, 119, 1, {52, 60}}, + {24, 122, 1, {100, 108, 116, 124, 132, 140}}, + {25, 126, 1, {149, 157}}, + {26, 126, 1, {149, 157}}, + {27, 117, 1, {40, 48}}, + {28, 120, 1, {56, 64}}, + {29, 123, 1, {104, 112, 120, 128, 136, 144}}, + {30, 127, 1, {153, 161}}, + {31, 127, 1, {153, 161}}, + {32, 83, 1, {1, 2, 3, 4, 5, 6, 7}}, + {33, 84, 1, {5, 6, 7, 8, 9, 10, 11}}, + {128, 128, 2, {42, 58, 106, 122, 138, 155}}, + {129, 129, 3, {50, 114}}, + {130, 130, 2, {42, 58, 106, 122, 138, 155}}, +}; +/** oper class table for EU*/ +static oper_bw_chan oper_bw_chan_eu[] = { + /** non-global oper class,global oper class, bandwidth, channel list*/ + {1, 115, 0, {36, 40, 44, 48}}, + {2, 118, 0, {52, 56, 60, 64}}, + {3, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}, + {4, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {5, 116, 1, {36, 44}}, + {6, 119, 1, {52, 60}}, + {7, 122, 1, {100, 108, 116, 124, 132}}, + {8, 117, 1, {40, 48}}, + {9, 120, 1, {56, 64}}, + {10, 123, 1, {104, 112, 120, 128, 136}}, + {11, 83, 1, {1, 2, 3, 4, 5, 6, 7, 8, 9}}, + {12, 84, 1, {5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {17, 125, 0, {149, 153, 157, 161, 165, 169}}, + {128, 128, 2, {42, 58, 106, 122, 138, 155}}, + {129, 129, 3, {50, 114}}, + {130, 130, 2, {42, 58, 106, 122, 138, 155}}, +}; +/** oper class table for Japan*/ +static oper_bw_chan oper_bw_chan_jp[] = { + /** non-Global oper class,global oper class, bandwidth, channel list*/ + {1, 115, 0, {34, 38, 42, 46, 36, 40, 44, 48}}, + {30, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {31, 82, 0, {14}}, + {32, 118, 0, {52, 56, 60, 64}}, + {33, 118, 0, {52, 56, 60, 64}}, + {34, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}, + {35, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}, + {36, 116, 1, {36, 44}}, + {37, 119, 1, {52, 60}}, + {38, 119, 1, {52, 60}}, + {39, 122, 1, {100, 108, 116, 124, 132}}, + {40, 122, 1, {100, 108, 116, 124, 132}}, + {41, 117, 1, {40, 48}}, + {42, 120, 1, {56, 64}}, + {43, 120, 1, {56, 64}}, + {44, 123, 1, {104, 112, 120, 128, 136}}, + {45, 123, 1, {104, 112, 120, 128, 136}}, + {56, 83, 1, {1, 2, 3, 4, 5, 6, 7, 8, 9}}, + {57, 84, 1, {5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {58, 121, 0, {100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140}}, + {128, 128, 2, {42, 58, 106, 122, 138, 155}}, + {129, 129, 3, {50, 114}}, + {130, 130, 2, {42, 58, 106, 122, 138, 155}}, +}; +/** oper class table for China*/ +static oper_bw_chan oper_bw_chan_cn[] = { + /** non-Global oper class,global oper class, bandwidth, channel list*/ + {1, 115, 0, {36, 40, 44, 48}}, + {2, 118, 0, {52, 56, 60, 64}}, + {3, 125, 0, {149, 153, 157, 161, 165}}, + {4, 116, 1, {36, 44}}, + {5, 119, 1, {52, 60}}, + {6, 126, 1, {149, 157}}, + {7, 81, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {8, 83, 0, {1, 2, 3, 4, 5, 6, 7, 8, 9}}, + {9, 84, 1, {5, 6, 7, 8, 9, 10, 11, 12, 13}}, + {128, 128, 2, {42, 58, 106, 122, 138, 155}}, + {129, 129, 3, {50, 114}}, + {130, 130, 2, {42, 58, 106, 122, 138, 155}}, +}; + +/** + * @brief Get non-global operaing class table according to country + * + * @param pmpriv A pointer to mlan_private structure + * @param arraysize A pointer to table size + * + * @return A pointer to oper_bw_chan + */ +oper_bw_chan *wlan_get_nonglobal_operclass_table(mlan_private *pmpriv, + int *arraysize) +{ + t_u8 country_code[][COUNTRY_CODE_LEN] = {"US", "JP", "CN"}; + int country_id = 0; + oper_bw_chan *poper_bw_chan = MNULL; + + ENTER(); + + for (country_id = 0; country_id < 3; country_id++) + if (!memcmp(pmpriv->adapter, pmpriv->adapter->country_code, + country_code[country_id], COUNTRY_CODE_LEN - 1)) + break; + if (country_id >= 3) + country_id = COUNTRY_ID_US; /*Set default to US*/ + if (wlan_is_etsi_country(pmpriv->adapter, + pmpriv->adapter->country_code)) + country_id = COUNTRY_ID_EU; /** Country in EU */ + + switch (country_id) { + case COUNTRY_ID_US: + poper_bw_chan = oper_bw_chan_us; + *arraysize = sizeof(oper_bw_chan_us); + break; + case COUNTRY_ID_JP: + poper_bw_chan = oper_bw_chan_jp; + *arraysize = sizeof(oper_bw_chan_jp); + break; + case COUNTRY_ID_CN: + poper_bw_chan = oper_bw_chan_cn; + *arraysize = sizeof(oper_bw_chan_cn); + break; + case COUNTRY_ID_EU: + poper_bw_chan = oper_bw_chan_eu; + *arraysize = sizeof(oper_bw_chan_eu); + break; + default: + PRINTM(MERROR, "Country not support!\n"); + break; + } + + LEAVE(); + return poper_bw_chan; +} + +/** + * @brief Check validation of given channel and oper class + * + * @param pmpriv A pointer to mlan_private structure + * @param channel Channel number + * @param oper_class operating class + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_check_operclass_validation(mlan_private *pmpriv, t_u8 channel, + t_u8 oper_class) +{ + int arraysize = 0, i = 0, channum = 0; + oper_bw_chan *poper_bw_chan = MNULL; + t_u8 center_freq_idx = 0; + t_u8 center_freqs[] = {42, 50, 58, 106, 114, 122, 138, 155}; + + ENTER(); + + for (i = 0; i < sizeof(center_freqs); i++) { + if (channel == center_freqs[i]) { + PRINTM(MERROR, "Invalid channel number %d!\n", channel); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + if (oper_class <= 0 || oper_class > 130) { + PRINTM(MERROR, "Invalid operating class!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (oper_class >= 128) { + center_freq_idx = wlan_get_center_freq_idx( + pmpriv, BAND_AAC, channel, CHANNEL_BW_80MHZ); + channel = center_freq_idx; + } + poper_bw_chan = wlan_get_nonglobal_operclass_table(pmpriv, &arraysize); + + if (!poper_bw_chan) { + PRINTM(MCMND, "Operating class table do not find!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + for (i = 0; i < arraysize / sizeof(oper_bw_chan); i++) { + if (poper_bw_chan[i].oper_class == oper_class || + poper_bw_chan[i].global_oper_class == oper_class) { + for (channum = 0; + channum < sizeof(poper_bw_chan[i].channel_list); + channum++) { + if (poper_bw_chan[i].channel_list[channum] && + poper_bw_chan[i].channel_list[channum] == + channel) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + } + } + + PRINTM(MCMND, "Operating class %d do not match channel %d!\n", + oper_class, channel); + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief Get current operating class from channel and bandwidth + * + * @param pmpriv A pointer to mlan_private structure + * @param channel Channel number + * @param bw Bandwidth + * @param oper_class A pointer to current operating class + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_get_curr_oper_class(mlan_private *pmpriv, t_u8 channel, + t_u8 bw, t_u8 *oper_class) +{ + oper_bw_chan *poper_bw_chan = MNULL; + t_u8 center_freq_idx = 0; + t_u8 center_freqs[] = {42, 50, 58, 106, 114, 122, 138, 155}; + int i = 0, arraysize = 0, channum = 0; + + ENTER(); + + poper_bw_chan = wlan_get_nonglobal_operclass_table(pmpriv, &arraysize); + + if (!poper_bw_chan) { + PRINTM(MCMND, "Operating class table do not find!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + for (i = 0; i < sizeof(center_freqs); i++) { + if (channel == center_freqs[i]) { + PRINTM(MERROR, "Invalid channel number %d!\n", channel); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + if (bw == BW_80MHZ) { + center_freq_idx = wlan_get_center_freq_idx( + pmpriv, BAND_AAC, channel, CHANNEL_BW_80MHZ); + channel = center_freq_idx; + } + + for (i = 0; i < arraysize / sizeof(oper_bw_chan); i++) { + if (poper_bw_chan[i].bandwidth == bw) { + for (channum = 0; + channum < sizeof(poper_bw_chan[i].channel_list); + channum++) { + if (poper_bw_chan[i].channel_list[channum] && + poper_bw_chan[i].channel_list[channum] == + channel) { + *oper_class = + poper_bw_chan[i].oper_class; + return MLAN_STATUS_SUCCESS; + } + } + } + } + + PRINTM(MCMND, "Operating class not find!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief Add Supported operating classes IE + * + * @param pmpriv A pointer to mlan_private structure + * @param pptlv_out A pointer to TLV to fill in + * @param curr_oper_class Current operating class + * + * @return Length + */ +int wlan_add_supported_oper_class_ie(mlan_private *pmpriv, t_u8 **pptlv_out, + t_u8 curr_oper_class) +{ + t_u8 oper_class_us[] = {1, 2, 3, 4, 5, 12, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 128, 129, 130}; + t_u8 oper_class_eu[] = {1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 17, 128, 129, 130}; + t_u8 oper_class_jp[] = {1, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 44, + 45, 56, 57, 58, 128, 129, 130}; + t_u8 oper_class_cn[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 128, 129, 130}; + t_u8 country_code[][COUNTRY_CODE_LEN] = {"US", "JP", "CN"}; + int country_id = 0, ret = 0; + MrvlIETypes_SuppOperClass_t *poper_class = MNULL; + + ENTER(); + + for (country_id = 0; country_id < 3; country_id++) + if (!memcmp(pmpriv->adapter, pmpriv->adapter->country_code, + country_code[country_id], COUNTRY_CODE_LEN - 1)) + break; + if (country_id >= 3) + country_id = COUNTRY_ID_US; /*Set default to US*/ + if (wlan_is_etsi_country(pmpriv->adapter, + pmpriv->adapter->country_code)) + country_id = COUNTRY_ID_EU; /** Country in EU */ + poper_class = (MrvlIETypes_SuppOperClass_t *)*pptlv_out; + memset(pmpriv->adapter, poper_class, 0, + sizeof(MrvlIETypes_SuppOperClass_t)); + poper_class->header.type = wlan_cpu_to_le16(REGULATORY_CLASS); + if (country_id == COUNTRY_ID_US) { + poper_class->header.len = sizeof(oper_class_us); + memcpy_ext(pmpriv->adapter, &poper_class->oper_class, + oper_class_us, sizeof(oper_class_us), + poper_class->header.len); + } else if (country_id == COUNTRY_ID_JP) { + poper_class->header.len = sizeof(oper_class_jp); + memcpy_ext(pmpriv->adapter, &poper_class->oper_class, + oper_class_jp, sizeof(oper_class_jp), + poper_class->header.len); + } else if (country_id == COUNTRY_ID_CN) { + poper_class->header.len = sizeof(oper_class_cn); + memcpy_ext(pmpriv->adapter, &poper_class->oper_class, + oper_class_cn, sizeof(oper_class_cn), + poper_class->header.len); + } else if (country_id == COUNTRY_ID_EU) { + poper_class->header.len = sizeof(oper_class_eu); + memcpy_ext(pmpriv->adapter, &poper_class->oper_class, + oper_class_eu, sizeof(oper_class_eu), + poper_class->header.len); + } + poper_class->current_oper_class = curr_oper_class; + poper_class->header.len += sizeof(poper_class->current_oper_class); + DBG_HEXDUMP(MCMD_D, "Operating class", (t_u8 *)poper_class, + sizeof(MrvlIEtypesHeader_t) + poper_class->header.len); + ret = sizeof(MrvlIEtypesHeader_t) + poper_class->header.len; + *pptlv_out += ret; + poper_class->header.len = wlan_cpu_to_le16(poper_class->header.len); + + LEAVE(); + return ret; +} + +/** + * @brief This function sets region table. + * + * @param pmpriv A pointer to mlan_private structure + * @param region The region code + * @param band The band + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_set_regiontable(mlan_private *pmpriv, t_u8 region, t_u8 band) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + int i = 0, j; + chan_freq_power_t *cfp; + int cfp_no; + region_chan_t region_chan_old[MAX_REGION_CHANNEL_NUM]; + t_u8 cfp_code_bg = region; + t_u8 cfp_code_a = region; + + ENTER(); + + memcpy_ext(pmadapter, region_chan_old, pmadapter->region_channel, + sizeof(pmadapter->region_channel), sizeof(region_chan_old)); + memset(pmadapter, pmadapter->region_channel, 0, + sizeof(pmadapter->region_channel)); + + if (band & (BAND_B | BAND_G | BAND_GN)) { + if (pmadapter->cfp_code_bg) + cfp_code_bg = pmadapter->cfp_code_bg; + PRINTM(MCMND, "%s: 2.4G 0x%x\n", __func__, cfp_code_bg); + cfp = wlan_get_region_cfp_table(pmadapter, cfp_code_bg, + BAND_G | BAND_B | BAND_GN, + &cfp_no); + if (cfp) { + pmadapter->region_channel[i].num_cfp = (t_u8)cfp_no; + pmadapter->region_channel[i].pcfp = cfp; + } else { + PRINTM(MERROR, "wrong region code %#x in Band B-G\n", + region); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->region_channel[i].valid = MTRUE; + pmadapter->region_channel[i].region = region; + if (band & BAND_GN) + pmadapter->region_channel[i].band = BAND_G; + else + pmadapter->region_channel[i].band = + (band & BAND_G) ? BAND_G : BAND_B; + + for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++) { + if (region_chan_old[j].band & (BAND_B | BAND_G)) + break; + } + + if ((j < MAX_REGION_CHANNEL_NUM) && + (region_chan_old[j].valid == MTRUE)) { + wlan_cfp_copy_dynamic(pmadapter, cfp, cfp_no, + region_chan_old[j].pcfp, + region_chan_old[j].num_cfp); + } else if (region) { + wlan_cfp_copy_dynamic(pmadapter, cfp, cfp_no, MNULL, 0); + } + i++; + } + if (band & (BAND_A | BAND_AN | BAND_AAC)) { + if (pmadapter->cfp_code_bg) + cfp_code_a = pmadapter->cfp_code_a; + PRINTM(MCMND, "%s: 5G 0x%x\n", __func__, cfp_code_a); + cfp = wlan_get_region_cfp_table(pmadapter, cfp_code_a, BAND_A, + &cfp_no); + if (cfp) { + pmadapter->region_channel[i].num_cfp = (t_u8)cfp_no; + pmadapter->region_channel[i].pcfp = cfp; + } else { + PRINTM(MERROR, "wrong region code %#x in Band A\n", + region); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->region_channel[i].valid = MTRUE; + pmadapter->region_channel[i].region = region; + pmadapter->region_channel[i].band = BAND_A; + + for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++) { + if (region_chan_old[j].band & BAND_A) + break; + } + if ((j < MAX_REGION_CHANNEL_NUM) && region_chan_old[j].valid) { + wlan_cfp_copy_dynamic(pmadapter, cfp, cfp_no, + region_chan_old[j].pcfp, + region_chan_old[j].num_cfp); + } else if (region) { + wlan_cfp_copy_dynamic(pmadapter, cfp, cfp_no, MNULL, 0); + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get if radar detection is enabled or not on a certain channel + * + * @param priv Private driver information structure + * @param chnl Channel to determine radar detection requirements + * + * @return + * - MTRUE if radar detection is required + * - MFALSE otherwise + */ +t_bool wlan_get_cfp_radar_detect(mlan_private *priv, t_u8 chnl) +{ + int i, j; + t_bool required = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /*get the cfp table first*/ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band == BAND_A) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (!pcfp) { + /* This means operation in BAND-A is not support, we can + * just return false here, it's harmless + */ + goto done; + } + + /*get the radar detection requirements according to chan num*/ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chnl) { + required = pcfp[j].passive_scan_or_radar_detect; + break; + } + } + +done: + LEAVE(); + return required; +} + +/** + * @brief Get if scan type is passive or not on a certain channel for b/g band + * + * @param priv Private driver information structure + * @param chnl Channel to determine scan type + * + * @return + * - MTRUE if scan type is passive + * - MFALSE otherwise + */ + +t_bool wlan_bg_scan_type_is_passive(mlan_private *priv, t_u8 chnl) +{ + int i, j; + t_bool passive = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /*get the cfp table first*/ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & (BAND_B | BAND_G)) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (!pcfp) { + /*This means operation in BAND-B or BAND_G is not support, we + * can just return false here + */ + goto done; + } + + /*get the bg scan type according to chan num*/ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chnl) { + passive = pcfp[j].passive_scan_or_radar_detect; + break; + } + } + +done: + LEAVE(); + return passive; +} + +/** + * @brief Get if a channel is NO_IR (passive) or not + * + * @param priv Private driver information structure + * @param band Band to check + * @param chan Channel to check + * + * @return + * - MTRUE if channel is passive + * - MFALSE otherwise + */ + +t_bool wlan_is_chan_passive(mlan_private *priv, t_u8 band, t_u8 chan) +{ + int i, j; + t_bool passive = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /* get the cfp table first */ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & band) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (pcfp) { + /* check table according to chan num */ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chan) { + if (pcfp[j].dynamic.flags & NXP_CHANNEL_PASSIVE) + passive = MTRUE; + break; + } + } + } + + LEAVE(); + return passive; +} + +/** + * @brief Get if a channel is disabled or not + * + * @param priv Private driver information structure + * @param band Band to check + * @param chan Channel to check + * + * @return + * - MTRUE if channel is disabled + * - MFALSE otherwise + */ + +t_bool wlan_is_chan_disabled(mlan_private *priv, t_u8 band, t_u8 chan) +{ + int i, j; + t_bool disabled = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /* get the cfp table first */ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & band) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (pcfp) { + /* check table according to chan num */ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chan) { + if (pcfp[j].dynamic.flags & + NXP_CHANNEL_DISABLED) + disabled = MTRUE; + break; + } + } + } + + LEAVE(); + return disabled; +} + +/** + * @brief Get if a channel is blacklisted or not + * + * @param priv Private driver information structure + * @param band Band to check + * @param chan Channel to check + * + * @return + * - MTRUE if channel is blacklisted + * - MFALSE otherwise + */ + +t_bool wlan_is_chan_blacklisted(mlan_private *priv, t_u8 band, t_u8 chan) +{ + int i, j; + t_bool blacklist = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /*get the cfp table first*/ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & band) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (pcfp) { + /*check table according to chan num*/ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chan) { + blacklist = pcfp[j].dynamic.blacklist; + break; + } + } + } + + LEAVE(); + return blacklist; +} + +/** + * @brief Set a channel as blacklisted or not + * + * @param priv Private driver information structure + * @param band Band to check + * @param chan Channel to check + * @param bl Blacklist if MTRUE + * + * @return + * - MTRUE if channel setting is updated + * - MFALSE otherwise + */ + +t_bool wlan_set_chan_blacklist(mlan_private *priv, t_u8 band, t_u8 chan, + t_bool bl) +{ + int i, j; + t_bool set_bl = MFALSE; + chan_freq_power_t *pcfp = MNULL; + + ENTER(); + + /*get the cfp table first*/ + for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) { + if (priv->adapter->region_channel[i].band & band) { + pcfp = priv->adapter->region_channel[i].pcfp; + break; + } + } + + if (pcfp) { + /*check table according to chan num*/ + for (j = 0; j < priv->adapter->region_channel[i].num_cfp; j++) { + if (pcfp[j].channel == chan) { + pcfp[j].dynamic.blacklist = bl; + set_bl = MTRUE; + break; + } + } + } + + LEAVE(); + return set_bl; +} + +/** + * @brief Convert rateid in IEEE format to MRVL format + * + * @param priv Private driver information structure + * @param IeeeMacRate Rate in terms of IEEE format + * @param pmbuf A pointer to packet buffer + * + * @return + * Rate ID in terms of MRVL format + */ +t_u8 wlan_ieee_rateid_to_mrvl_rateid(mlan_private *priv, t_u16 IeeeMacRate, + t_u8 *dst_mac) +{ + /* Set default rate ID to RATEID_DBPSK1Mbps */ + t_u8 mrvlRATEID = 0; + const rate_map *rate_tbl = rate_map_table_1x1; + t_u32 cnt = sizeof(rate_map_table_1x1) / sizeof(rate_map); + t_u8 skip_nss2 = MTRUE; + t_u32 i = 0; + IEEEtypes_HTCap_t *htcap = MNULL; + t_u8 tx_mcs_supp = GET_TXMCSSUPP(priv->usr_dev_mcs_support); +#ifdef UAP_SUPPORT + psta_node sta_ptr = MNULL; +#endif + + ENTER(); + + if (priv->adapter->hw_dev_mcs_support == HT_STREAM_MODE_2X2) { + rate_tbl = rate_map_table_2x2; + cnt = sizeof(rate_map_table_2x2) / sizeof(rate_map); + } + +#ifdef UAP_SUPPORT + if (priv->bss_role == MLAN_BSS_ROLE_UAP) { + if (!dst_mac) { + LEAVE(); + return mrvlRATEID; + } + sta_ptr = (sta_node *)util_peek_list( + priv->adapter->pmoal_handle, &priv->sta_list, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + if (!sta_ptr) { + LEAVE(); + return mrvlRATEID; + } + while (sta_ptr != (sta_node *)&priv->sta_list) { + if (memcmp(priv->adapter, dst_mac, sta_ptr->mac_addr, + MLAN_MAC_ADDR_LENGTH)) { + htcap = &(sta_ptr->HTcap); + break; + } + sta_ptr = sta_ptr->pnext; + } + } +#endif +#ifdef STA_SUPPORT + if (priv->bss_role == MLAN_BSS_ROLE_STA) + htcap = priv->curr_bss_params.bss_descriptor.pht_cap; +#endif + if (htcap) { + /* If user configure tx to 2x2 and peer device rx is 2x2 */ + if (tx_mcs_supp >= 2 && htcap->ht_cap.supported_mcs_set[1]) + skip_nss2 = MFALSE; + } + + for (i = 0; i < cnt; i++) { + if (rate_tbl[i].nss && skip_nss2) + continue; + if (rate_tbl[i].rate == IeeeMacRate) { + mrvlRATEID = rate_tbl[i].id; + break; + } + } + + return mrvlRATEID; +} + +/** + * @brief Convert rateid in MRVL format to IEEE format + * + * @param IeeeMacRate Rate in terms of MRVL format + * + * @return + * Rate ID in terms of IEEE format + */ +t_u8 wlan_mrvl_rateid_to_ieee_rateid(t_u8 rate) +{ + return rateUnit_500Kbps[rate]; +} + +/** + * @brief Update CFP tables and power tables from FW + * + * @param priv Private driver information structure + * @param buf Pointer to the buffer holding TLV data + * from 0x242 command response. + * @param buf_left bufsize + * + * @return + * None + */ +void wlan_add_fw_cfp_tables(pmlan_private pmpriv, t_u8 *buf, t_u16 buf_left) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + MrvlIEtypesHeader_t *head; + t_u16 tlv; + t_u16 tlv_buf_len; + t_u16 tlv_buf_left; + t_u16 i; + int k = 0, rows, cols; + t_u16 max_tx_pwr_bg = WLAN_TX_PWR_DEFAULT; + t_u16 max_tx_pwr_a = WLAN_TX_PWR_DEFAULT; + t_u8 *tlv_buf; + t_u8 *data; + t_u8 *tmp; + mlan_status ret; + + ENTER(); + + if (!buf) { + PRINTM(MERROR, "CFP table update failed!\n"); + goto out; + } + if (pmadapter->otp_region) + wlan_free_fw_cfp_tables(pmadapter); + pmadapter->tx_power_table_bg_rows = FW_CFP_TABLE_MAX_ROWS_BG; + pmadapter->tx_power_table_bg_cols = FW_CFP_TABLE_MAX_COLS_BG; + pmadapter->tx_power_table_a_rows = FW_CFP_TABLE_MAX_ROWS_A; + pmadapter->tx_power_table_a_cols = FW_CFP_TABLE_MAX_COLS_A; + tlv_buf = (t_u8 *)buf; + tlv_buf_left = buf_left; + + while (tlv_buf_left >= sizeof(*head)) { + head = (MrvlIEtypesHeader_t *)tlv_buf; + tlv = wlan_le16_to_cpu(head->type); + tlv_buf_len = wlan_le16_to_cpu(head->len); + + if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) + break; + data = (t_u8 *)head + sizeof(*head); + + switch (tlv) { + case TLV_TYPE_REGION_INFO: + /* Skip adding fw region info if it already exists or + * if this TLV has no set data + */ + if (*data == 0) + break; + if (pmadapter->otp_region) + break; + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(otp_region_info_t), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->otp_region); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->otp_region) { + PRINTM(MERROR, + "Memory allocation for the otp region info struct failed!\n"); + break; + } + /* Save region info values from OTP in the otp_region + * structure + */ + memcpy_ext(pmadapter, pmadapter->otp_region, data, + sizeof(otp_region_info_t), + sizeof(otp_region_info_t)); + data += sizeof(otp_region_info_t); + /* Get pre-defined cfp tables corresponding to the + * region code in OTP + */ + for (i = 0; i < MLAN_CFP_TABLE_SIZE_BG; i++) { + if (cfp_table_BG[i].code == + pmadapter->otp_region->region_code) { + max_tx_pwr_bg = (cfp_table_BG[i].cfp) + ->max_tx_power; + break; + } + } + for (i = 0; i < MLAN_CFP_TABLE_SIZE_A; i++) { + if (cfp_table_A[i].code == + pmadapter->otp_region->region_code) { + max_tx_pwr_a = (cfp_table_A[i].cfp) + ->max_tx_power; + break; + } + } + /* Update the region code and the country code in + * pmadapter + */ + pmadapter->region_code = + pmadapter->otp_region->region_code; + pmadapter->country_code[0] = + pmadapter->otp_region->country_code[0]; + pmadapter->country_code[1] = + pmadapter->otp_region->country_code[1]; + pmadapter->country_code[2] = '\0'; + pmadapter->domain_reg.country_code[0] = + pmadapter->otp_region->country_code[0]; + pmadapter->domain_reg.country_code[1] = + pmadapter->otp_region->country_code[1]; + pmadapter->domain_reg.country_code[2] = '\0'; + pmadapter->cfp_code_bg = + pmadapter->otp_region->region_code; + pmadapter->cfp_code_a = + pmadapter->otp_region->region_code; + break; + case TLV_TYPE_CHAN_ATTR_CFG: + /* Skip adding fw cfp tables if they already exist or + * if this TLV has no set data + */ + if (*data == 0) + break; + if (pmadapter->cfp_otp_bg || pmadapter->cfp_otp_a) { + break; + } + + ret = pcb->moal_malloc( + pmadapter->pmoal_handle, + pmadapter->tx_power_table_bg_rows * + sizeof(chan_freq_power_t), + MLAN_MEM_DEF, (t_u8 **)&pmadapter->cfp_otp_bg); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->cfp_otp_bg) { + PRINTM(MERROR, + "Memory allocation for storing otp bg table data failed!\n"); + break; + } + /* Save channel usability flags from OTP data in the fw + * cfp bg table and set frequency and max_tx_power + * values + */ + for (i = 0; i < pmadapter->tx_power_table_bg_rows; + i++) { + (pmadapter->cfp_otp_bg + i)->channel = *data; + if (*data == 14) + (pmadapter->cfp_otp_bg + i)->freq = + 2484; + else + (pmadapter->cfp_otp_bg + i)->freq = + 2412 + 5 * (*data - 1); + (pmadapter->cfp_otp_bg + i)->max_tx_power = + max_tx_pwr_bg; + data++; + (pmadapter->cfp_otp_bg + i)->dynamic.flags = + *data; + if (*data & NXP_CHANNEL_DFS) + (pmadapter->cfp_otp_bg + i) + ->passive_scan_or_radar_detect = + MTRUE; + data++; + } + ret = pcb->moal_malloc( + pmadapter->pmoal_handle, + pmadapter->tx_power_table_a_rows * + sizeof(chan_freq_power_t), + MLAN_MEM_DEF, (t_u8 **)&pmadapter->cfp_otp_a); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->cfp_otp_a) { + PRINTM(MERROR, + "Memory allocation for storing otp a table data failed!\n"); + break; + } + /* Save channel usability flags from OTP data in the fw + * cfp a table and set frequency and max_tx_power values + */ + for (i = 0; i < pmadapter->tx_power_table_a_rows; i++) { + (pmadapter->cfp_otp_a + i)->channel = *data; + if (*data < 183) + /* 5GHz channels */ + (pmadapter->cfp_otp_a + i)->freq = + 5035 + 5 * (*data - 7); + else + /* 4GHz channels */ + (pmadapter->cfp_otp_a + i)->freq = + 4915 + 5 * (*data - 183); + (pmadapter->cfp_otp_a + i)->max_tx_power = + max_tx_pwr_a; + data++; + (pmadapter->cfp_otp_a + i)->dynamic.flags = + *data; + if (*data & NXP_CHANNEL_DFS) + (pmadapter->cfp_otp_a + i) + ->passive_scan_or_radar_detect = + MTRUE; + data++; + } + break; + case TLV_TYPE_POWER_TABLE: + /* Skip adding fw power tables if this TLV has no data + * or if they already exists but force reg rule is set + * in the otp + */ + if (*data == 0) + break; + if (pmadapter->otp_region && + pmadapter->otp_region->force_reg && + pmadapter->tx_power_table_bg) + break; + + /* Save the tlv data in power tables for band BG and A + */ + tmp = data; + i = 0; + while ((i < + pmadapter->tx_power_table_bg_rows * + pmadapter->tx_power_table_bg_cols) && + (i < tlv_buf_len) && (*tmp != 36)) { + i++; + tmp++; + } + if (!pmadapter->tx_power_table_bg) { + ret = pcb->moal_malloc( + pmadapter->pmoal_handle, i, + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->tx_power_table_bg); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->tx_power_table_bg) { + PRINTM(MERROR, + "Memory allocation for the BG-band power table failed!\n"); + break; + } + } + memcpy_ext(pmadapter, pmadapter->tx_power_table_bg, + data, i, i); + pmadapter->tx_power_table_bg_size = i; + data += i; + i = 0; + while ((i < pmadapter->tx_power_table_a_rows * + pmadapter->tx_power_table_a_cols) && + (i < (tlv_buf_len - + pmadapter->tx_power_table_bg_size))) { + i++; + } + if (!pmadapter->tx_power_table_a) { + ret = pcb->moal_malloc( + pmadapter->pmoal_handle, i, + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->tx_power_table_a); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->tx_power_table_a) { + PRINTM(MERROR, + "Memory allocation for the A-band power table failed!\n"); + break; + } + } + memcpy_ext(pmadapter, pmadapter->tx_power_table_a, data, + i, i); + pmadapter->tx_power_table_a_size = i; + break; + case TLV_TYPE_POWER_TABLE_ATTR: + pmadapter->tx_power_table_bg_rows = + ((power_table_attr_t *)data)->rows_2g; + pmadapter->tx_power_table_bg_cols = + ((power_table_attr_t *)data)->cols_2g; + pmadapter->tx_power_table_a_rows = + ((power_table_attr_t *)data)->rows_5g; + pmadapter->tx_power_table_a_cols = + ((power_table_attr_t *)data)->cols_5g; + break; + default: + break; + } + tlv_buf += (sizeof(*head) + tlv_buf_len); + tlv_buf_left -= (sizeof(*head) + tlv_buf_len); + } + if (!pmadapter->cfp_otp_bg || !pmadapter->tx_power_table_bg) + goto out; + /* Set remaining flags for BG */ + rows = pmadapter->tx_power_table_bg_rows; + cols = pmadapter->tx_power_table_bg_cols; + + for (i = 0; i < rows; i++) { + k = (i * cols) + 1; + if ((pmadapter->cfp_otp_bg + i)->dynamic.flags & + NXP_CHANNEL_DISABLED) + continue; + + if (pmadapter->tx_power_table_bg[k + MOD_CCK] == 0) + (pmadapter->cfp_otp_bg + i)->dynamic.flags |= + NXP_CHANNEL_NO_CCK; + + if (pmadapter->tx_power_table_bg[k + MOD_OFDM_PSK] == 0 && + pmadapter->tx_power_table_bg[k + MOD_OFDM_QAM16] == 0 && + pmadapter->tx_power_table_bg[k + MOD_OFDM_QAM64] == 0) { + (pmadapter->cfp_otp_bg + i)->dynamic.flags |= + NXP_CHANNEL_NO_OFDM; + } + } + +out: + LEAVE(); +} + +/** + * @brief This function deallocates otp cfp and power tables memory. + * + * @param pmadapter A pointer to mlan_adapter structure + */ +void wlan_free_fw_cfp_tables(mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb; + + ENTER(); + + pcb = &pmadapter->callbacks; + if (pmadapter->otp_region) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->otp_region); + if (pmadapter->cfp_otp_bg) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->cfp_otp_bg); + if (pmadapter->tx_power_table_bg) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->tx_power_table_bg); + pmadapter->otp_region = MNULL; + pmadapter->cfp_otp_bg = MNULL; + pmadapter->tx_power_table_bg = MNULL; + pmadapter->tx_power_table_bg_size = 0; + if (pmadapter->cfp_otp_a) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->cfp_otp_a); + if (pmadapter->tx_power_table_a) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->tx_power_table_a); + pmadapter->cfp_otp_a = MNULL; + pmadapter->tx_power_table_a = MNULL; + pmadapter->tx_power_table_a_size = 0; + LEAVE(); +} + +/** + * @brief Get DFS chan list + * + * @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_get_cfp_table(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_misc_cfg *ds_misc_cfg = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + chan_freq_power_t *cfp = MNULL; + t_u32 cfp_no = 0; + + ENTER(); + if (pioctl_req) { + ds_misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + cfp = wlan_get_region_cfp_table( + pmadapter, pmadapter->region_code, + ds_misc_cfg->param.cfp.band, &cfp_no); + if (cfp) { + ds_misc_cfg->param.cfp.num_chan = cfp_no; + memcpy_ext(pmadapter, + ds_misc_cfg->param.cfp.cfp_tbl, cfp, + cfp_no * sizeof(chan_freq_power_t), + cfp_no * sizeof(chan_freq_power_t)); + } + ret = MLAN_STATUS_SUCCESS; + } + } + LEAVE(); + return ret; +} + +/** + * @brief Get power tables and cfp tables for set region code + * into the IOCTL request buffer + * + * @param pmadapter Private mlan adapter structure + * @param pioctl_req Pointer to the IOCTL request structure + * + * @return success, otherwise fail + * + */ +mlan_status wlan_get_cfpinfo(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + chan_freq_power_t *cfp_bg = MNULL; + t_u32 cfp_no_bg = 0; + chan_freq_power_t *cfp_a = MNULL; + t_u32 cfp_no_a = 0; + t_u8 cfp_code_a = pmadapter->region_code; + t_u8 cfp_code_bg = pmadapter->region_code; + t_u32 len = 0, size = 0; + t_u8 *req_buf, *tmp; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!pioctl_req || !pioctl_req->pbuf) { + PRINTM(MERROR, "MLAN IOCTL information is not present!\n"); + ret = MLAN_STATUS_FAILURE; + goto out; + } + /* Calculate the total response size required to return region, + * country codes, cfp tables and power tables + */ + size = sizeof(pmadapter->country_code) + sizeof(pmadapter->region_code); + /* Add size to store region, country and environment codes */ + size += sizeof(t_u32); + if (pmadapter->cfp_code_bg) + cfp_code_bg = pmadapter->cfp_code_bg; + + /* Get cfp table and its size corresponding to the region code */ + cfp_bg = wlan_get_region_cfp_table(pmadapter, cfp_code_bg, + BAND_G | BAND_B, &cfp_no_bg); + size += cfp_no_bg * sizeof(chan_freq_power_t); + if (pmadapter->cfp_code_a) + cfp_code_a = pmadapter->cfp_code_a; + cfp_a = wlan_get_region_cfp_table(pmadapter, cfp_code_a, BAND_A, + &cfp_no_a); + size += cfp_no_a * sizeof(chan_freq_power_t); + if (pmadapter->otp_region) + size += sizeof(pmadapter->otp_region->environment); + + /* Get power table size */ + if (pmadapter->tx_power_table_bg) { + size += pmadapter->tx_power_table_bg_size; + /* Add size to store table size, rows and cols */ + size += 3 * sizeof(t_u32); + } + if (pmadapter->tx_power_table_a) { + size += pmadapter->tx_power_table_a_size; + size += 3 * sizeof(t_u32); + } + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < size) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->buf_len_needed = size; + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto out; + } + /* Copy the total size of region code, country code and environment + * in first four bytes of the IOCTL request buffer and then copy + * codes respectively in following bytes + */ + req_buf = (t_u8 *)pioctl_req->pbuf; + size = sizeof(pmadapter->country_code) + sizeof(pmadapter->region_code); + if (pmadapter->otp_region) + size += sizeof(pmadapter->otp_region->environment); + tmp = (t_u8 *)&size; + memcpy_ext(pmadapter, req_buf, tmp, sizeof(size), sizeof(size)); + len += sizeof(size); + memcpy_ext(pmadapter, req_buf + len, &pmadapter->region_code, + sizeof(pmadapter->region_code), + sizeof(pmadapter->region_code)); + len += sizeof(pmadapter->region_code); + memcpy_ext(pmadapter, req_buf + len, &pmadapter->country_code, + sizeof(pmadapter->country_code), + sizeof(pmadapter->country_code)); + len += sizeof(pmadapter->country_code); + if (pmadapter->otp_region) { + memcpy_ext(pmadapter, req_buf + len, + &pmadapter->otp_region->environment, + sizeof(pmadapter->otp_region->environment), + sizeof(pmadapter->otp_region->environment)); + len += sizeof(pmadapter->otp_region->environment); + } + /* copy the cfp table size followed by the entire table */ + if (!cfp_bg) + goto out; + size = cfp_no_bg * sizeof(chan_freq_power_t); + memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size)); + len += sizeof(size); + memcpy_ext(pmadapter, req_buf + len, cfp_bg, size, size); + len += size; + if (!cfp_a) + goto out; + size = cfp_no_a * sizeof(chan_freq_power_t); + memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size)); + len += sizeof(size); + memcpy_ext(pmadapter, req_buf + len, cfp_a, size, size); + len += size; + /* Copy the size of the power table, number of rows, number of cols + * and the entire power table + */ + if (!pmadapter->tx_power_table_bg) + goto out; + size = pmadapter->tx_power_table_bg_size; + memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size)); + len += sizeof(size); + + /* No. of rows */ + size = pmadapter->tx_power_table_bg_rows; + memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size)); + len += sizeof(size); + + /* No. of cols */ + size = pmadapter->tx_power_table_bg_size / + pmadapter->tx_power_table_bg_rows; + memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size)); + len += sizeof(size); + memcpy_ext(pmadapter, req_buf + len, pmadapter->tx_power_table_bg, + pmadapter->tx_power_table_bg_size, + pmadapter->tx_power_table_bg_size); + len += pmadapter->tx_power_table_bg_size; + if (!pmadapter->tx_power_table_a) + goto out; + size = pmadapter->tx_power_table_a_size; + memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size)); + len += sizeof(size); + + /* No. of rows */ + size = pmadapter->tx_power_table_a_rows; + memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size)); + len += sizeof(size); + + /* No. of cols */ + size = pmadapter->tx_power_table_a_size / + pmadapter->tx_power_table_a_rows; + memcpy_ext(pmadapter, req_buf + len, tmp, sizeof(size), sizeof(size)); + len += sizeof(size); + memcpy_ext(pmadapter, req_buf + len, pmadapter->tx_power_table_a, + pmadapter->tx_power_table_a_size, + pmadapter->tx_power_table_a_size); + len += pmadapter->tx_power_table_a_size; +out: + if (pioctl_req) + pioctl_req->data_read_written = len; + + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_cmdevt.c b/mxm_wifiex/wlan_src/mlan/mlan_cmdevt.c new file mode 100644 index 0000000..6393ae6 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_cmdevt.c @@ -0,0 +1,8377 @@ +/** + * @file mlan_cmdevt.c + * + * @brief This file contains the handling of CMD/EVENT in MLAN + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11ac.h" +#include "mlan_11ax.h" +#include "mlan_11h.h" +#ifdef SDIO +#include "mlan_sdio.h" +#endif /* SDIO */ +#ifdef PCIE +#include "mlan_pcie.h" +#endif /* PCIE */ +#include "mlan_init.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************* + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +#ifdef STA_SUPPORT +/** + * @brief This function inserts scan command node to scan_pending_q. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * @return N/A + */ +static t_void wlan_queue_scan_cmd(mlan_private *pmpriv, + cmd_ctrl_node *pcmd_node) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + if (pcmd_node == MNULL) + goto done; + pcmd_node->cmd_flag |= CMD_F_SCAN; + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + +done: + LEAVE(); +} + +/** + * @brief Internal function used to flush the scan pending queue + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +void wlan_check_scan_queue(pmlan_adapter pmadapter) +{ + cmd_ctrl_node *pcmd_node = MNULL; + t_u16 num = 0; + + pcmd_node = (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + PRINTM(MERROR, "No pending scan command\n"); + return; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->scan_pending_q) { + num++; + pcmd_node = pcmd_node->pnext; + } + PRINTM(MERROR, "num_pending_scan=%d\n", num); +} +#endif + +/** + * @brief This function will dump the pending commands id + * + * @param pmadapter A pointer to mlan_adapter + * + * @return N/A + */ +static void wlan_dump_pending_commands(pmlan_adapter pmadapter) +{ + cmd_ctrl_node *pcmd_node = MNULL; + HostCmd_DS_COMMAND *pcmd; + + ENTER(); + wlan_request_cmd_lock(pmadapter); + pcmd_node = (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + wlan_release_cmd_lock(pmadapter); + LEAVE(); + return; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->cmd_pending_q) { + pcmd = (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + PRINTM(MERROR, "pending command id: 0x%x ioctl_buf=%p\n", + wlan_le16_to_cpu(pcmd->command), pcmd_node->pioctl_buf); + pcmd_node = pcmd_node->pnext; + } +#ifdef STA_SUPPORT + wlan_check_scan_queue(pmadapter); +#endif + wlan_release_cmd_lock(pmadapter); + LEAVE(); + return; +} + +#define REASON_CODE_NO_CMD_NODE 1 +#define REASON_CODE_CMD_TIMEOUT 2 +#define REASON_CODE_CMD_TO_CARD_FAILURE 3 +#define REASON_CODE_EXT_SCAN_TIMEOUT 4 +/** + * @brief This function dump debug info + * + * @return N/A + */ +t_void wlan_dump_info(mlan_adapter *pmadapter, t_u8 reason) +{ + cmd_ctrl_node *pcmd_node = MNULL; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + t_u8 i; +#ifdef SDIO + t_u8 j; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; +#endif + t_u16 cmd_id, cmd_act; + mlan_private *pmpriv = MNULL; + + ENTER(); + + PRINTM(MERROR, "------------Dump info-----------\n", reason); + switch (reason) { + case REASON_CODE_NO_CMD_NODE: + pmadapter->dbg.num_no_cmd_node++; + PRINTM(MERROR, "No Free command node\n"); + break; + case REASON_CODE_CMD_TIMEOUT: + PRINTM(MERROR, "Commmand Timeout\n"); + break; + case REASON_CODE_CMD_TO_CARD_FAILURE: + PRINTM(MERROR, "Command to card failure\n"); + break; + case REASON_CODE_EXT_SCAN_TIMEOUT: + PRINTM(MERROR, "EXT_SCAN_STATUS event Timeout\n"); + break; + default: + break; + } + if ((reason == REASON_CODE_NO_CMD_NODE) && + (pmadapter->dbg.num_no_cmd_node > 1)) { + if (pmadapter->dbg.num_no_cmd_node >= 5) + wlan_recv_event(wlan_get_priv(pmadapter, + MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DBG_DUMP, MNULL); + LEAVE(); + return; + } + wlan_dump_pending_commands(pmadapter); + if (reason != REASON_CODE_CMD_TIMEOUT) { + if (!pmadapter->curr_cmd) { + PRINTM(MERROR, "CurCmd Empty\n"); + } else { + pcmd_node = pmadapter->curr_cmd; + cmd_id = pmadapter->dbg.last_cmd_id + [pmadapter->dbg.last_cmd_index]; + cmd_act = pmadapter->dbg.last_cmd_act + [pmadapter->dbg.last_cmd_index]; + PRINTM_GET_SYS_TIME(MERROR, &sec, &usec); + PRINTM(MERROR, + "Current cmd id (%lu.%06lu) = 0x%x, act = 0x%x\n", + sec, usec, cmd_id, cmd_act); +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type) && + pcmd_node->cmdbuf) { + t_u8 *pcmd_buf; + pcmd_buf = pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset + + pmadapter->ops.intf_header_len; + for (i = 0; i < 16; i++) + PRINTM(MERROR, "%02x ", *pcmd_buf++); + PRINTM(MERROR, "\n"); + } +#endif + pmpriv = pcmd_node->priv; + if (pmpriv) + PRINTM(MERROR, "BSS type = %d BSS role= %d\n", + pmpriv->bss_type, pmpriv->bss_role); + } + } + PRINTM(MERROR, "mlan_processing =%d\n", pmadapter->mlan_processing); + PRINTM(MERROR, "main_lock_flag =%d\n", pmadapter->main_lock_flag); + PRINTM(MERROR, "main_process_cnt =%d\n", pmadapter->main_process_cnt); + PRINTM(MERROR, "delay_task_flag =%d\n", pmadapter->delay_task_flag); + PRINTM(MERROR, "mlan_rx_processing =%d\n", + pmadapter->mlan_rx_processing); + PRINTM(MERROR, "rx_pkts_queued=%d\n", pmadapter->rx_pkts_queued); + PRINTM(MERROR, "more_task_flag = %d\n", pmadapter->more_task_flag); + PRINTM(MERROR, "num_cmd_timeout = %d\n", pmadapter->num_cmd_timeout); + PRINTM(MERROR, "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); +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + 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); + } +#endif + PRINTM(MERROR, "num_alloc_buffer_failure = %d\n", + pmadapter->dbg.num_alloc_buffer_failure); + PRINTM(MERROR, "num_pkt_dropped = %d\n", + pmadapter->dbg.num_pkt_dropped); + PRINTM(MERROR, "num_no_cmd_node = %d\n", + pmadapter->dbg.num_no_cmd_node); + PRINTM(MERROR, "num_event_deauth = %d\n", + pmadapter->dbg.num_event_deauth); + PRINTM(MERROR, "num_event_disassoc = %d\n", + pmadapter->dbg.num_event_disassoc); + PRINTM(MERROR, "num_event_link_lost = %d\n", + pmadapter->dbg.num_event_link_lost); + PRINTM(MERROR, "num_cmd_deauth = %d\n", pmadapter->dbg.num_cmd_deauth); + PRINTM(MERROR, "num_cmd_assoc_success = %d\n", + pmadapter->dbg.num_cmd_assoc_success); + PRINTM(MERROR, "num_cmd_assoc_failure = %d\n", + pmadapter->dbg.num_cmd_assoc_failure); + PRINTM(MERROR, "num_cons_assoc_failure = %d\n", + pmadapter->dbg.num_cons_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 wakeup_timeout=%d\n", + pmadapter->pm_wakeup_card_req, pmadapter->pm_wakeup_fw_try, + pmadapter->pm_wakeup_timeout); + 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); +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + PRINTM(MERROR, "mp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + pmadapter->pcard_sd->mp_rd_bitmap, + pmadapter->pcard_sd->curr_rd_port); + PRINTM(MERROR, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + pmadapter->pcard_sd->mp_wr_bitmap, + pmadapter->pcard_sd->curr_wr_port); + PRINTM(MERROR, "mp_invalid_update=%d\n", + pmadapter->pcard_sd->mp_invalid_update); + PRINTM(MERROR, "last_recv_wr_bitmap=0x%x last_mp_index=%d\n", + pmadapter->pcard_sd->last_recv_wr_bitmap, + pmadapter->pcard_sd->last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + PRINTM(MERROR, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n", + pmadapter->pcard_sd->last_mp_wr_bitmap[i], + pmadapter->pcard_sd->last_mp_wr_ports[i], + pmadapter->pcard_sd->last_mp_wr_len[i], + pmadapter->pcard_sd->last_curr_wr_port[i]); + for (j = 0; j < mp_aggr_pkt_limit; j++) { + PRINTM(MERROR, "0x%02x ", + pmadapter->pcard_sd->last_mp_wr_info + [i * mp_aggr_pkt_limit + j]); + } + PRINTM(MERROR, "\n"); + } + } +#endif +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type)) { + PRINTM(MERROR, "txbd_rdptr=0x%x txbd_wrptr=0x%x\n", + pmadapter->pcard_pcie->txbd_rdptr, + pmadapter->pcard_pcie->txbd_wrptr); + PRINTM(MERROR, "rxbd_rdptr=0x%x rxbd_wrptr=0x%x\n", + pmadapter->pcard_pcie->rxbd_rdptr, + pmadapter->pcard_pcie->rxbd_wrptr); + PRINTM(MERROR, "evtbd_rdptr=0x%x evt_wrptr=0x%x\n", + pmadapter->pcard_pcie->evtbd_rdptr, + pmadapter->pcard_pcie->evtbd_wrptr); + PRINTM(MERROR, "last_wr_index:%d\n", + pmadapter->pcard_pcie->txbd_wrptr & + (MLAN_MAX_TXRX_BD - 1)); + PRINTM(MERROR, "Tx pkt size:\n"); + for (i = 0; i < MLAN_MAX_TXRX_BD; i++) { + PRINTM(MERROR, "%04d ", + pmadapter->pcard_pcie->last_tx_pkt_size[i]); + if (((i + 1) % 16) == 0) + PRINTM(MERROR, "\n"); + } + } +#endif + for (i = 0; i < pmadapter->priv_num; ++i) { + if (pmadapter->priv[i]) + wlan_dump_ralist(pmadapter->priv[i]); + } + if (reason != REASON_CODE_CMD_TIMEOUT) { + if ((pmadapter->dbg.num_no_cmd_node >= 5) || + (pmadapter->pm_wakeup_card_req && + pmadapter->pm_wakeup_fw_try) || + (reason == REASON_CODE_EXT_SCAN_TIMEOUT)) { + if (pmpriv) + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_DRV_DBG_DUMP, + MNULL); + else + wlan_recv_event( + wlan_get_priv(pmadapter, + MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DBG_DUMP, MNULL); + } + } + PRINTM(MERROR, "-------- Dump info End---------\n", reason); + LEAVE(); + return; +} + +/** + * @brief This function convert a given character to hex + * + * @param chr Character to be converted + * + * @return The converted hex if chr is a valid hex, else 0 + */ +static t_u32 wlan_hexval(t_u8 chr) +{ + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + return 0; +} + +/** + * @brief This function convert a given string to hex + * + * @param a A pointer to string to be converted + * + * @return The converted hex value if param a is a valid hex, else + * 0 + */ +int wlan_atox(t_u8 *a) +{ + int i = 0; + + ENTER(); + + while (wlan_isxdigit(*a)) + i = i * 16 + wlan_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief This function parse cal data from ASCII to hex + * + * @param src A pointer to source data + * @param len Source data length + * @param dst A pointer to a buf to store the parsed data + * + * @return The parsed hex data length + */ +static t_u32 wlan_parse_cal_cfg(t_u8 *src, t_size len, t_u8 *dst) +{ + t_u8 *ptr; + t_u8 *dptr; + + ENTER(); + ptr = src; + dptr = dst; + + while (ptr - src < len) { + if (*ptr && (wlan_isspace(*ptr) || *ptr == '\t')) { + ptr++; + continue; + } + + if (wlan_isxdigit(*ptr)) { + *dptr++ = wlan_atox(ptr); + ptr += 2; + } else { + ptr++; + } + } + LEAVE(); + return dptr - dst; +} + +/** + * @brief This function finds first occurrence of a char in a string + * + * @param s A pointer to the string to be searched + * @param c The character to search for + * + * @return Location of the first occurrence of the char + * if found, else NULL + */ +t_u8 *wlan_strchr(t_u8 *s, int c) +{ + t_u8 *pos = s; + while (*pos != '\0') { + if (*pos == (t_u8)c) + return pos; + pos++; + } + return MNULL; +} + +#define CFG_TYPE_HOSTCMD 0 +#define CFG_TYPE_DPDFILE 1 + +/** + * @brief WOAL parse ASCII format raw data to hex format + * + * @param pmpriv MOAL handle + * @param cfg_type Conf file type + * @param data Source data + * @param size data length + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 wlan_process_hostcmd_cfg(pmlan_private pmpriv, t_u16 cfg_type, + t_u8 *data, t_size size) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = data; + t_u8 *intf_s, *intf_e; + t_u8 *buf = MNULL; + t_u8 *ptr = MNULL; + t_u32 cmd_len = 0; + t_u8 start_raw = MFALSE; + mlan_ds_misc_cmd *hostcmd; + HostCmd_DS_GEN *pcmd = MNULL; + HostCmd_DS_802_11_CFG_DATA *pcfg_cmd = MNULL; + mlan_adapter *pmadapter = MNULL; + mlan_callbacks *pcb = MNULL; + t_u8 hostcmd_flag = MFALSE; + + ENTER(); + if (!pmpriv) { + PRINTM(MERROR, "pmpriv is NULL\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter = pmpriv->adapter; + pcb = (mlan_callbacks *)&pmadapter->callbacks; + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_misc_cmd), MLAN_MEM_DEF, + (t_u8 **)&hostcmd); + if (ret || !hostcmd) { + PRINTM(MERROR, "Could not allocate buffer space!\n"); + LEAVE(); + return ret; + } + buf = hostcmd->cmd; + ptr = buf; + while ((pos - data) < size) { + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos == '#') { /* Line comment */ + while (*pos != '\n') + pos++; + pos++; + } + if ((*pos == '\r' && *(pos + 1) == '\n') || *pos == '\n' || + *pos == '\0') { + pos++; + continue; /* Needn't process this line */ + } + + if (*pos == '}') { + if (cfg_type == CFG_TYPE_DPDFILE && pcmd) { + /* Fill command head for DPD RAW data conf */ + hostcmd->len = ptr - buf; + pcmd->command = + wlan_cpu_to_le16(HostCmd_CMD_CFG_DATA); + pcmd->size = wlan_cpu_to_le16(hostcmd->len); + pcfg_cmd = (HostCmd_DS_802_11_CFG_DATA + *)((t_u8 *)pcmd + S_DS_GEN); + pcfg_cmd->action = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + pcfg_cmd->type = wlan_cpu_to_le16(OID_TYPE_DPD); + pcfg_cmd->data_len = wlan_cpu_to_le16( + hostcmd->len - S_DS_GEN - + sizeof(HostCmd_DS_802_11_CFG_DATA)); + pcmd = MNULL; + pcfg_cmd = MNULL; + } else { + /* For hostcmd data conf */ + cmd_len = *((t_u16 *)(buf + sizeof(t_u16))); + hostcmd->len = cmd_len; + } + ret = wlan_prepare_cmd(pmpriv, 0, 0, 0, MNULL, + (t_void *)hostcmd); + memset(pmadapter, buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + ptr = buf; + start_raw = MFALSE; + pos++; + continue; + } + + if (start_raw == MFALSE) { + intf_s = wlan_strchr(pos, '='); + if (intf_s) { + if (*(intf_s + 1) == '=') + hostcmd_flag = MTRUE; + intf_e = wlan_strchr(intf_s, '{'); + } else + intf_e = MNULL; + + if (intf_s && intf_e) { + start_raw = MTRUE; + pos = intf_e + 1; + /* Reserve command head for DPD RAW data conf */ + if (cfg_type == CFG_TYPE_DPDFILE && + !hostcmd_flag) { + pcmd = (HostCmd_DS_GEN *)ptr; + ptr += S_DS_GEN + + sizeof(HostCmd_DS_802_11_CFG_DATA); + } + continue; + } + } + + if (start_raw) { + /* Raw data block exists */ + while (*pos != '\n') { + if ((*pos <= 'f' && *pos >= 'a') || + (*pos <= 'F' && *pos >= 'A') || + (*pos <= '9' && *pos >= '0')) { + *ptr++ = wlan_atox(pos); + pos += 2; + } else + pos++; + } + } + } + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)hostcmd); + LEAVE(); + return ret; +} + +/** + * @brief This function initializes the command node. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * @param cmd_no cmd id + * @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(pmlan_private pmpriv, cmd_ctrl_node *pcmd_node, + t_u32 cmd_no, t_void *pioctl_buf, + t_void *pdata_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + if (pcmd_node == MNULL) { + LEAVE(); + return; + } + pcmd_node->priv = pmpriv; + pcmd_node->cmd_no = cmd_no; + pcmd_node->pioctl_buf = pioctl_buf; + pcmd_node->pdata_buf = pdata_buf; + +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + pcmd_node->cmdbuf = + wlan_alloc_mlan_buffer(pmadapter, + MRVDRV_SIZE_OF_CMD_BUFFER, 0, + MOAL_MALLOC_BUFFER); + if (!pcmd_node->cmdbuf) { + PRINTM(MERROR, "Failed to allocate cmd_buffer\n"); + LEAVE(); + return; + } + } +#endif /* USB */ +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) + pcmd_node->cmdbuf = pcmd_node->pmbuf; +#endif + + /* 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; +#ifdef USB + if (IS_USB(pmadapter->card_type)) + pcmd_node->cmdbuf->data_offset += MLAN_TYPE_LEN; +#endif +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) + pcmd_node->cmdbuf->data_offset += + pmadapter->ops.intf_header_len; +#endif + + LEAVE(); +} + +/** + * @brief This function gets a free command node if available in + * command free queue. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or MNULL + */ +static cmd_ctrl_node *wlan_get_cmd_node(mlan_adapter *pmadapter) +{ + cmd_ctrl_node *pcmd_node; + + ENTER(); + + if (pmadapter == MNULL) { + LEAVE(); + return MNULL; + } + wlan_request_cmd_lock(pmadapter); + if (util_peek_list(pmadapter->pmoal_handle, &pmadapter->cmd_free_q, + MNULL, MNULL)) { + pcmd_node = (cmd_ctrl_node *)util_dequeue_list( + pmadapter->pmoal_handle, &pmadapter->cmd_free_q, MNULL, + MNULL); + } else { + PRINTM(MERROR, + "GET_CMD_NODE: cmd_ctrl_node is not available\n"); + pcmd_node = MNULL; + } + wlan_release_cmd_lock(pmadapter); + LEAVE(); + return pcmd_node; +} + +/** + * @brief This function cleans command node. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * + * @return N/A + */ +static t_void wlan_clean_cmd_node(pmlan_adapter pmadapter, + cmd_ctrl_node *pcmd_node) +{ + ENTER(); + + if (pcmd_node == MNULL) { + LEAVE(); + return; + } + pcmd_node->cmd_no = 0; + pcmd_node->cmd_flag = 0; + pcmd_node->pioctl_buf = MNULL; + pcmd_node->pdata_buf = MNULL; + +#ifdef USB + if (IS_USB(pmadapter->card_type) && pcmd_node->cmdbuf) { + wlan_free_mlan_buffer(pmadapter, pcmd_node->cmdbuf); + pcmd_node->cmdbuf = MNULL; + } +#endif + + if (pcmd_node->respbuf) { + pmadapter->ops.cmdrsp_complete(pmadapter, pcmd_node->respbuf, + MLAN_STATUS_SUCCESS); + pcmd_node->respbuf = MNULL; + } + + LEAVE(); + return; +} + +#ifdef STA_SUPPORT +/** + * @brief This function will return the pointer to the first entry in + * pending cmd which is scan command + * + * @param pmadapter A pointer to mlan_adapter + * + * @return A pointer to first entry match pioctl_req + */ +static cmd_ctrl_node *wlan_get_pending_scan_cmd(pmlan_adapter pmadapter) +{ + cmd_ctrl_node *pcmd_node = MNULL; + + ENTER(); + + pcmd_node = (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + LEAVE(); + return MNULL; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->cmd_pending_q) { + if (pcmd_node->cmd_flag & CMD_F_SCAN) { + LEAVE(); + return pcmd_node; + } + pcmd_node = pcmd_node->pnext; + } + LEAVE(); + return MNULL; +} +#endif + +/** + * @brief This function will return the pointer to the first entry in + * pending cmd which matches the given pioctl_req + * + * @param pmadapter A pointer to mlan_adapter + * @param pioctl_req A pointer to mlan_ioctl_req buf + * + * @return A pointer to first entry match pioctl_req + */ +static cmd_ctrl_node *wlan_get_pending_ioctl_cmd(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + cmd_ctrl_node *pcmd_node = MNULL; + + ENTER(); + + pcmd_node = (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + LEAVE(); + return MNULL; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->cmd_pending_q) { + if (pcmd_node->pioctl_buf && + (pcmd_node->pioctl_buf == pioctl_req)) { + LEAVE(); + return pcmd_node; + } + pcmd_node = pcmd_node->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief This function will return the pointer to the first entry in + * pending cmd which matches the given bss_index + * + * @param pmadapter A pointer to mlan_adapter + * @param bss_index bss_index + * + * @return A pointer to first entry match pioctl_req + */ +static cmd_ctrl_node *wlan_get_bss_pending_ioctl_cmd(pmlan_adapter pmadapter, + t_u32 bss_index) +{ + cmd_ctrl_node *pcmd_node = MNULL; + mlan_ioctl_req *pioctl_buf = MNULL; + ENTER(); + + pcmd_node = (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (!pcmd_node) { + LEAVE(); + return MNULL; + } + while (pcmd_node != (cmd_ctrl_node *)&pmadapter->cmd_pending_q) { + if (pcmd_node->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + if (pioctl_buf->bss_index == bss_index) { + LEAVE(); + return pcmd_node; + } + } + pcmd_node = pcmd_node->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief This function handles the command response of host_cmd + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_host_cmd(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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_ext(pmpriv->adapter, misc->param.hostcmd.cmd, + (void *)resp, size, MRVDRV_SIZE_OF_CMD_BUFFER); + } + + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, 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_ext(pmpriv->adapter, (void *)cmd, pcmd_ptr->cmd, pcmd_ptr->len, + MRVDRV_SIZE_OF_CMD_BUFFER); + PRINTM(MINFO, "Host command size = %d\n", pcmd_ptr->len); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function get the cmd timeout value + * + * @param cmd_id cmd id + * + * @return timeout value for this command + */ +static t_u16 wlan_get_cmd_timeout(t_u16 cmd_id) +{ + t_u16 timeout; + ENTER(); + switch (cmd_id) { + case HostCmd_CMD_802_11_SCAN: + case HostCmd_CMD_802_11_SCAN_EXT: + timeout = MRVDRV_TIMER_10S * 2; + break; + case HostCmd_CMD_FUNC_INIT: + case HostCmd_CMD_FUNC_SHUTDOWN: + case HostCmd_CMD_802_11_ASSOCIATE: + case HostCmd_CMD_802_11_DEAUTHENTICATE: + case HostCmd_CMD_802_11_DISASSOCIATE: + case HostCmd_CMD_802_11_AD_HOC_START: + case HostCmd_CMD_802_11_AD_HOC_JOIN: + case HostCmd_CMD_802_11_AD_HOC_STOP: + case HostCmd_CMD_11N_ADDBA_REQ: + case HostCmd_CMD_11N_ADDBA_RSP: + case HostCmd_CMD_11N_DELBA: + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + case HostCmd_CMD_SUPPLICANT_PMK: + case HostCmd_CMD_SUPPLICANT_PROFILE: + case HostCmd_CMD_SOFT_RESET: +#ifdef UAP_SUPPORT + case HOST_CMD_APCMD_SYS_RESET: + case HOST_CMD_APCMD_BSS_START: + case HOST_CMD_APCMD_BSS_STOP: + case HOST_CMD_APCMD_STA_DEAUTH: +#endif + case HostCMD_APCMD_ACS_SCAN: + timeout = MRVDRV_TIMER_5S; + break; + default: + timeout = MRVDRV_TIMER_1S * 2; + break; + } + LEAVE(); + return timeout; +} + +/** + * @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(mlan_private *pmpriv, + cmd_ctrl_node *pcmd_node) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_COMMAND *pcmd; + mlan_ioctl_req *pioctl_buf = MNULL; + t_u16 cmd_code; + t_u16 cmd_size; + t_u32 age_ts_usec; +#ifdef USB + t_u32 tmp; +#endif +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + t_u16 timeout = 0; + + ENTER(); + + if (pcmd_node) + if (pcmd_node->pioctl_buf != MNULL) + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + if (!pmadapter || !pcmd_node) { + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pcmd = (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + + /* Sanity test */ + if (pcmd == MNULL || pcmd->size == 0) { + PRINTM(MERROR, + "DNLD_CMD: pcmd is null or command size is zero, " + "Not sending\n"); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Set command sequence number */ + pmadapter->seq_num++; + pcmd->seq_num = wlan_cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO( + pmadapter->seq_num, pcmd_node->priv->bss_num, + pcmd_node->priv->bss_type)); + cmd_code = wlan_le16_to_cpu(pcmd->command); + pcmd_node->cmd_no = cmd_code; + timeout = wlan_get_cmd_timeout(cmd_code); + cmd_size = wlan_le16_to_cpu(pcmd->size); + + pcmd_node->cmdbuf->data_len = cmd_size; + + wlan_request_cmd_lock(pmadapter); + pmadapter->curr_cmd = pcmd_node; + wlan_release_cmd_lock(pmadapter); + + /* Save the last command id and action to debug log */ + pmadapter->dbg.last_cmd_index = + (pmadapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; + pmadapter->dbg.last_cmd_id[pmadapter->dbg.last_cmd_index] = cmd_code; + pmadapter->dbg.last_cmd_act[pmadapter->dbg.last_cmd_index] = + wlan_le16_to_cpu(*(t_u16 *)((t_u8 *)pcmd + S_DS_GEN)); + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->dnld_cmd_in_secs, + &age_ts_usec); + +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + /* Add extra header for USB */ + if (pcmd_node->cmdbuf->data_offset < MLAN_TYPE_LEN) { + PRINTM(MERROR, + "DNLD_CMD: data_offset is too small=%d\n", + pcmd_node->cmdbuf->data_offset); + if (pioctl_buf) + pioctl_buf->status_code = + MLAN_ERROR_CMD_DNLD_FAIL; + + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + if (pmadapter->dbg.last_cmd_index) + pmadapter->dbg.last_cmd_index--; + else + pmadapter->dbg.last_cmd_index = DBG_CMD_NUM - 1; + ret = MLAN_STATUS_FAILURE; + goto done; + } + tmp = wlan_cpu_to_le32(MLAN_USB_TYPE_CMD); + memcpy_ext(pmadapter, (t_u8 *)pcmd - MLAN_TYPE_LEN, + (t_u8 *)&tmp, MLAN_TYPE_LEN, MLAN_TYPE_LEN); + pcmd_node->cmdbuf->data_offset -= MLAN_TYPE_LEN; + pcmd_node->cmdbuf->data_len += MLAN_TYPE_LEN; + } +#endif + + PRINTM_GET_SYS_TIME(MCMND, &sec, &usec); + PRINTM_NETINTF(MCMND, pmpriv); + PRINTM(MCMND, + "DNLD_CMD (%lu.%06lu): 0x%x, act 0x%x, len %d, seqno 0x%x timeout %d\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), timeout); + DBG_HEXDUMP(MCMD_D, "DNLD_CMD", (t_u8 *)pcmd, cmd_size); + +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) { + pcmd_node->cmdbuf->data_offset -= + pmadapter->ops.intf_header_len; + pcmd_node->cmdbuf->data_len += pmadapter->ops.intf_header_len; + } +#endif + + /* Send the command to lower layer */ + ret = pmadapter->ops.host_to_card(pmpriv, MLAN_TYPE_CMD, + pcmd_node->cmdbuf, MNULL); + +#ifdef USB + if (IS_USB(pmadapter->card_type) && (ret == MLAN_STATUS_PENDING)) + pcmd_node->cmdbuf = MNULL; +#endif + + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "DNLD_CMD: Host to Card Failed\n"); + if (pcmd_node->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + pioctl_buf->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + } + + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + if (pmadapter->dbg.last_cmd_index) + pmadapter->dbg.last_cmd_index--; + else + pmadapter->dbg.last_cmd_index = DBG_CMD_NUM - 1; + + pmadapter->dbg.num_cmd_host_to_card_failure++; + wlan_dump_info(pmadapter, REASON_CODE_CMD_TO_CARD_FAILURE); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Clear BSS_NO_BITS from HostCmd */ + cmd_code &= HostCmd_CMD_ID_MASK; + + /* For the command who has no command response, we should return here */ + if (cmd_code == HostCmd_CMD_FW_DUMP_EVENT || + cmd_code == HostCmd_CMD_SOFT_RESET) { + if (pcmd_node->pioctl_buf) { + PRINTM(MMSG, + "CMD(0x%x) has no cmd resp: free curr_cmd and do ioctl_complete\n", + cmd_code); + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, + pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + } + goto done; + } + + /* Setup the timer after transmit command */ + pcb->moal_start_timer(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_timer, MFALSE, timeout); + + 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; +#if defined(SDIO) || defined(PCIE) + t_u16 cmd_len = 0; +#endif + opt_sleep_confirm_buffer *sleep_cfm_buf = + (opt_sleep_confirm_buffer *)(pmadapter->psleep_cfm->pbuf + + pmadapter->psleep_cfm->data_offset); + mlan_buffer *pmbuf = MNULL; + mlan_private *pmpriv = MNULL; + + ENTER(); + + pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!pmpriv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) { + cmd_len = sizeof(OPT_Confirm_Sleep); + pmbuf = pmadapter->psleep_cfm; + } +#endif + pmadapter->seq_num++; + sleep_cfm_buf->ps_cfm_sleep.seq_num = + wlan_cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO( + pmadapter->seq_num, pmpriv->bss_num, pmpriv->bss_type)); + DBG_HEXDUMP(MCMD_D, "SLEEP_CFM", &sleep_cfm_buf->ps_cfm_sleep, + sizeof(OPT_Confirm_Sleep)); + + /* Send sleep confirm command to firmware */ +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + pmbuf = wlan_alloc_mlan_buffer(pmadapter, + sizeof(opt_sleep_confirm_buffer), + 0, MOAL_MALLOC_BUFFER); + + if (!pmbuf) { + PRINTM(MERROR, + "Failed to allocate sleep confirm buffers\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmbuf->buf_type = MLAN_BUF_TYPE_CMD; + pmbuf->data_len = pmadapter->psleep_cfm->data_len; + memcpy_ext(pmadapter, pmbuf->pbuf + pmbuf->data_offset, + pmadapter->psleep_cfm->pbuf + + pmadapter->psleep_cfm->data_offset, + pmadapter->psleep_cfm->data_len, pmbuf->data_len); + } +#endif /* USB */ + +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) + pmadapter->psleep_cfm->data_len = + cmd_len + pmadapter->ops.intf_header_len; +#endif + + if (pmbuf) + ret = pmadapter->ops.host_to_card(pmpriv, MLAN_TYPE_CMD, pmbuf, + MNULL); + +#ifdef USB + if (IS_USB(pmadapter->card_type) && (ret != MLAN_STATUS_PENDING)) + wlan_free_mlan_buffer(pmadapter, pmbuf); +#endif + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "SLEEP_CFM: failed\n"); + pmadapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; + goto done; + } else { + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + pmadapter->ps_state = PS_STATE_SLEEP_CFM; +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + if (!sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl) { + /* Response is not needed for sleep confirm + * command */ + pmadapter->ps_state = PS_STATE_SLEEP; + } else { + pmadapter->ps_state = PS_STATE_SLEEP_CFM; + } + + if (!sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl && + (pmadapter->is_hs_configured && + !pmadapter->sleep_period.period)) { + pmadapter->pm_wakeup_card_req = MTRUE; + wlan_host_sleep_activated_event( + wlan_get_priv(pmadapter, + MLAN_BSS_ROLE_STA), + MTRUE); + } + } +#endif /* STA_SUPPORT */ + + PRINTM_NETINTF(MEVENT, pmpriv); +#define NUM_SC_PER_LINE 16 + if (++i % NUM_SC_PER_LINE == 0) + PRINTM(MEVENT, "+\n"); + else + PRINTM(MEVENT, "+"); + } + +done: + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Event handler + * + * @param priv A pointer to mlan_private structure + * @param event_id Event ID + * @param pmevent Event buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_recv_event(pmlan_private priv, mlan_event_id event_id, + t_void *pmevent) +{ + pmlan_callbacks pcb = MNULL; + + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pcb = &priv->adapter->callbacks; + + if (pmevent) + /* The caller has provided the event. */ + pcb->moal_recv_event(priv->adapter->pmoal_handle, + (pmlan_event)pmevent); + else { + mlan_event mevent; + + memset(priv->adapter, &mevent, 0, sizeof(mlan_event)); + mevent.bss_index = priv->bss_index; + mevent.event_id = event_id; + mevent.event_len = 0; + + pcb->moal_recv_event(priv->adapter->pmoal_handle, &mevent); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function allocates the command buffer and links + * it to command free queue. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_alloc_cmd_buffer(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); + +#if defined(PCIE) || defined(SDIO) + if (!IS_USB(pmadapter->card_type)) { + /* Allocate and initialize command buffers */ + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + pcmd_array[i].pmbuf = wlan_alloc_mlan_buffer( + pmadapter, MRVDRV_SIZE_OF_CMD_BUFFER, 0, + MOAL_MALLOC_BUFFER); + if (!pcmd_array[i].pmbuf) { + PRINTM(MERROR, + "ALLOC_CMD_BUF: Failed to allocate command buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } +#endif + wlan_request_cmd_lock(pmadapter); + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) + wlan_insert_cmd_to_free_q(pmadapter, &pcmd_array[i]); + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function frees the command buffer. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_free_cmd_buffer(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++) { +#ifdef USB + if (IS_USB(pmadapter->card_type) && pcmd_array[i].cmdbuf) { + PRINTM(MINFO, "Free all the USB command buffer.\n"); + wlan_free_mlan_buffer(pmadapter, pcmd_array[i].cmdbuf); + pcmd_array[i].cmdbuf = MNULL; + } +#endif +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type) && 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; + } +#endif + if (pcmd_array[i].respbuf) { +#ifdef USB + if (IS_USB(pmadapter->card_type)) + pmadapter->callbacks.moal_recv_complete( + pmadapter->pmoal_handle, + pcmd_array[i].respbuf, + pmadapter->rx_cmd_ep, + MLAN_STATUS_SUCCESS); +#endif +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) + wlan_free_mlan_buffer(pmadapter, + pcmd_array[i].respbuf); +#endif + pcmd_array[i].respbuf = MNULL; + } + } + /* Release cmd_ctrl_node */ + if (pmadapter->cmd_pool) { + PRINTM(MINFO, "Free command pool.\n"); + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->cmd_pool); + pmadapter->cmd_pool = MNULL; + } + +done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles events generated by firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_process_event(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + pmlan_buffer pmbuf = pmadapter->pmlan_buffer_event; + t_u32 eventcause = pmadapter->event_cause; +#ifdef DEBUG_LEVEL1 + t_u32 in_ts_sec = 0, in_ts_usec = 0; +#endif + ENTER(); + + /* Save the last event to debug log */ + pmadapter->dbg.last_event_index = + (pmadapter->dbg.last_event_index + 1) % DBG_CMD_NUM; + pmadapter->dbg.last_event[pmadapter->dbg.last_event_index] = + (t_u16)eventcause; + + if ((eventcause & EVENT_ID_MASK) == EVENT_RADAR_DETECTED) { + if (wlan_11h_dfs_event_preprocessing(pmadapter) == + MLAN_STATUS_SUCCESS) { + memcpy_ext(pmadapter, (t_u8 *)&eventcause, + pmbuf->pbuf + pmbuf->data_offset, + sizeof(eventcause), sizeof(eventcause)); + } else { + priv = wlan_get_priv_by_id( + pmadapter, EVENT_GET_BSS_NUM(eventcause), + EVENT_GET_BSS_TYPE(eventcause)); + if (priv) + PRINTM_NETINTF(MEVENT, priv); + PRINTM(MERROR, "Error processing DFS Event: 0x%x\n", + eventcause); + goto done; + } + } + /* Get BSS number and corresponding priv */ + priv = wlan_get_priv_by_id(pmadapter, EVENT_GET_BSS_NUM(eventcause), + EVENT_GET_BSS_TYPE(eventcause)); + if (!priv) + priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Clear BSS_NO_BITS from event */ + eventcause &= EVENT_ID_MASK; + pmadapter->event_cause = eventcause; + + if (pmbuf) { + pmbuf->bss_index = priv->bss_index; + memcpy_ext(pmadapter, pmbuf->pbuf + pmbuf->data_offset, + (t_u8 *)&eventcause, sizeof(eventcause), + sizeof(eventcause)); + } + + if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE && + eventcause != EVENT_FW_DUMP_INFO) { + PRINTM_GET_SYS_TIME(MEVENT, &in_ts_sec, &in_ts_usec); + PRINTM_NETINTF(MEVENT, priv); + PRINTM(MEVENT, "%lu.%06lu : Event: 0x%x\n", in_ts_sec, + in_ts_usec, eventcause); + } + + ret = priv->ops.process_event(priv); +done: + pmadapter->event_cause = 0; + pmadapter->pmlan_buffer_event = MNULL; + if (pmbuf) + pmadapter->ops.event_complete(pmadapter, pmbuf, + MLAN_STATUS_SUCCESS); + + 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(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(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(mlan_private *pmpriv, t_u16 cmd_no, + t_u16 cmd_action, t_u32 cmd_oid, + t_void *pioctl_buf, t_void *pdata_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = MNULL; + cmd_ctrl_node *pcmd_node = MNULL; + HostCmd_DS_COMMAND *cmd_ptr = MNULL; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + ENTER(); + + if (!pmpriv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter = pmpriv->adapter; + + /* Sanity test */ + if (!pmadapter || pmadapter->surprise_removed) { + PRINTM(MERROR, "PREP_CMD: Card is Removed\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_FW_NOT_READY; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (pmadapter->hw_status == WlanHardwareStatusReset) { + if ((cmd_no != HostCmd_CMD_FUNC_INIT) +#ifdef PCIE + && (cmd_no != HostCmd_CMD_PCIE_HOST_BUF_DETAILS) +#endif + ) { + PRINTM(MERROR, "PREP_CMD: FW is in reset state\n"); + if (pioctl_req) + pioctl_req->status_code = + MLAN_ERROR_FW_NOT_READY; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* Get a new command node */ + pcmd_node = wlan_get_cmd_node(pmadapter); + + if (pcmd_node == MNULL) { + PRINTM(MERROR, "PREP_CMD: No free cmd node\n"); + wlan_dump_info(pmadapter, REASON_CODE_NO_CMD_NODE); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + /** reset num no cmd node */ + pmadapter->dbg.num_no_cmd_node = 0; + + /* Initialize the command node */ + wlan_init_cmd_node(pmpriv, pcmd_node, cmd_no, pioctl_buf, pdata_buf); + + if (pcmd_node->cmdbuf == MNULL) { + PRINTM(MERROR, "PREP_CMD: No free cmd buf\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + cmd_ptr = (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + cmd_ptr->command = cmd_no; + cmd_ptr->result = 0; + + /* Prepare command */ + if (cmd_no) + ret = pmpriv->ops.prepare_cmd(pmpriv, cmd_no, cmd_action, + cmd_oid, pioctl_buf, pdata_buf, + cmd_ptr); + else { + ret = wlan_cmd_host_cmd(pmpriv, cmd_ptr, pdata_buf); + pcmd_node->cmd_flag |= CMD_F_HOSTCMD; + } + + /* Return error, since the command preparation failed */ + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "PREP_CMD: Command 0x%x preparation failed\n", + cmd_no); + pcmd_node->pioctl_buf = MNULL; + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_DNLD_FAIL; + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + wlan_request_cmd_lock(pmadapter); + /* Send command */ +#ifdef STA_SUPPORT + if (cmd_no == HostCmd_CMD_802_11_SCAN || + cmd_no == HostCmd_CMD_802_11_SCAN_EXT) { + if (cmd_no == HostCmd_CMD_802_11_SCAN_EXT && + pmadapter->ext_scan && pmadapter->ext_scan_enh && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, + MFALSE); + } else + wlan_queue_scan_cmd(pmpriv, pcmd_node); + } else { +#endif + if ((cmd_no == HostCmd_CMD_802_11_HS_CFG_ENH) && + (cmd_action == HostCmd_ACT_GEN_SET) && + (pmadapter->hs_cfg.conditions == HOST_SLEEP_CFG_CANCEL)) + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, + MFALSE); + else + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, + MTRUE); +#ifdef STA_SUPPORT + } +#endif + wlan_release_cmd_lock(pmadapter); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function inserts command node to cmd_free_q + * after cleaning it. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * + * @return N/A + */ +t_void wlan_insert_cmd_to_free_q(mlan_adapter *pmadapter, + cmd_ctrl_node *pcmd_node) +{ + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + mlan_ioctl_req *pioctl_req = MNULL; + ENTER(); + + if (pcmd_node == MNULL) + goto done; + if (pcmd_node->pioctl_buf) { + pioctl_req = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + if (pioctl_req->status_code != MLAN_ERROR_NO_ERROR) + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_req, + MLAN_STATUS_FAILURE); + else + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_req, + MLAN_STATUS_SUCCESS); + } + /* Clean the node */ + wlan_clean_cmd_node(pmadapter, pcmd_node); + + /* Insert node into cmd_free_q */ + util_enqueue_list_tail(pmadapter->pmoal_handle, &pmadapter->cmd_free_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); +done: + LEAVE(); +} + +/** + * @brief This function queues the command to cmd list. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pcmd_node A pointer to cmd_ctrl_node structure + * @param add_tail Specify if the cmd needs to be queued in the header or + * tail + * + * @return N/A + */ +t_void wlan_insert_cmd_to_pending_q(mlan_adapter *pmadapter, + cmd_ctrl_node *pcmd_node, t_u32 add_tail) +{ + HostCmd_DS_COMMAND *pcmd = MNULL; + t_u16 command; + + ENTER(); + + if (pcmd_node == MNULL) { + PRINTM(MERROR, "QUEUE_CMD: pcmd_node is MNULL\n"); + goto done; + } + + pcmd = (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + + command = wlan_le16_to_cpu(pcmd->command); + + /* Exit_PS command needs to be queued in the header always. */ + if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { + HostCmd_DS_802_11_PS_MODE_ENH *pm = &pcmd->params.psmode_enh; + if (wlan_le16_to_cpu(pm->action) == DIS_AUTO_PS) { + if (pmadapter->ps_state != PS_STATE_AWAKE) + add_tail = MFALSE; + } + } + + if (add_tail) { + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, + MNULL); + } else { + util_enqueue_list_head(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, + MNULL); + } + + PRINTM_NETINTF(MCMND, pcmd_node->priv); + PRINTM(MCMND, "QUEUE_CMD: cmd=0x%x is queued\n", command); + +done: + LEAVE(); + return; +} + +/** + * @brief This function executes next command in command + * pending queue. It will put firmware back to PS mode + * if applicable. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_exec_next_cmd(mlan_adapter *pmadapter) +{ + mlan_private *priv = MNULL; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_COMMAND *pcmd; + + ENTER(); + + /* Sanity test */ + if (pmadapter == MNULL) { + PRINTM(MERROR, "EXEC_NEXT_CMD: pmadapter is MNULL\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Check if already in processing */ + if (pmadapter->curr_cmd) { + PRINTM(MERROR, + "EXEC_NEXT_CMD: there is command in processing!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + wlan_request_cmd_lock(pmadapter); + /* Check if any command is pending */ + pcmd_node = (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + + if (pcmd_node) { + pcmd = (HostCmd_DS_COMMAND *)(pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset); + priv = pcmd_node->priv; + + if (pmadapter->ps_state != PS_STATE_AWAKE) { + PRINTM(MERROR, + "Cannot send command in sleep state, this should not happen\n"); + wlan_release_cmd_lock(pmadapter); + goto done; + } + + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + wlan_release_cmd_lock(pmadapter); + ret = wlan_dnld_cmd_to_fw(priv, pcmd_node); + priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + /* Any command sent to the firmware when host is in sleep mode, + * should de-configure host sleep */ + /* We should skip the host sleep configuration command itself + * though */ + if (priv && (pcmd->command != + wlan_cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { + if (pmadapter->hs_activated == MTRUE) { + pmadapter->is_hs_configured = MFALSE; + wlan_host_sleep_activated_event(priv, MFALSE); + } + } + goto done; + } else { + wlan_release_cmd_lock(pmadapter); + } + ret = MLAN_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_process_cmdresp(mlan_adapter *pmadapter) +{ + HostCmd_DS_COMMAND *resp = MNULL; + mlan_private *pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + mlan_private *pmpriv_next = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 orig_cmdresp_no; + t_u16 cmdresp_no; + t_u16 cmdresp_result; + mlan_ioctl_req *pioctl_buf = MNULL; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + t_u32 i; + + ENTER(); + + 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; + } + + 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); + orig_cmdresp_no = wlan_le16_to_cpu(resp->command); + cmdresp_no = (orig_cmdresp_no & HostCmd_CMD_ID_MASK); + if (pmadapter->curr_cmd->cmd_no != cmdresp_no) { + PRINTM(MERROR, "cmdresp error: cmd=0x%x cmd_resp=0x%x\n", + pmadapter->curr_cmd->cmd_no, cmdresp_no); + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->dnld_cmd_in_secs = 0; + /* 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; + } + pmadapter->num_cmd_timeout = 0; + wlan_request_cmd_lock(pmadapter); + if (pmadapter->curr_cmd->cmd_flag & CMD_F_CANCELED) { + cmd_ctrl_node *free_cmd = pmadapter->curr_cmd; + pmadapter->curr_cmd = MNULL; + PRINTM(MCMND, "CMD_RESP: 0x%x been canceled!\n", + wlan_le16_to_cpu(resp->command)); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + wlan_insert_cmd_to_free_q(pmadapter, free_cmd); + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_FAILURE; + goto done; + } else { + wlan_release_cmd_lock(pmadapter); + } + if (pmadapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + /* Copy original response back to response buffer */ + if (pmpriv) + wlan_ret_host_cmd(pmpriv, resp, pioctl_buf); + } + 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); + if (!pmpriv) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + 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; + + PRINTM_GET_SYS_TIME(MCMND, &sec, &usec); + PRINTM_NETINTF(MCMND, pmadapter->curr_cmd->priv); + PRINTM(MCMND, + "CMD_RESP (%lu.%06lu): 0x%x, result %d, len %d, seqno 0x%x\n", + sec, usec, orig_cmdresp_no, cmdresp_result, resp->size, + resp->seq_num); + + if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { + PRINTM(MERROR, "CMD_RESP: Invalid response to command!\n"); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_FW_CMDRESP; + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (pmadapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + pmadapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; + if ((cmdresp_result == HostCmd_RESULT_OK) && + (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) + ret = wlan_ret_802_11_hs_cfg(pmpriv, resp, pioctl_buf); + } else { + /* handle response */ + ret = pmpriv->ops.process_cmdresp(pmpriv, cmdresp_no, resp, + pioctl_buf); + } + + /* Check init command response */ + if (pmadapter->hw_status == WlanHardwareStatusInitializing || + pmadapter->hw_status == WlanHardwareStatusGetHwSpec) { + if (ret == MLAN_STATUS_FAILURE) { +#ifdef 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; + } +#ifdef STA_SUPPORT +#ifdef PCIE + /* init adma write pointer */ + if (IS_PCIE(pmadapter->card_type) && + cmdresp_no == HostCmd_CMD_FUNC_SHUTDOWN && + pmadapter->pwarm_reset_ioctl_req) { +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) +#endif + wlan_pcie_init_fw(pmadapter); + } +#endif +#endif + } + + wlan_request_cmd_lock(pmadapter); + if (pmadapter->curr_cmd) { + cmd_ctrl_node *free_cmd = pmadapter->curr_cmd; + pioctl_buf = (mlan_ioctl_req *)pmadapter->curr_cmd->pioctl_buf; + pmadapter->curr_cmd = MNULL; + if (pioctl_buf && (ret == MLAN_STATUS_SUCCESS)) + pioctl_buf->status_code = MLAN_ERROR_NO_ERROR; + else if (pioctl_buf && (ret == MLAN_STATUS_FAILURE) && + !pioctl_buf->status_code) + pioctl_buf->status_code = MLAN_ERROR_CMD_RESP_FAIL; + + /* Clean up and put current command back to cmd_free_q */ + wlan_insert_cmd_to_free_q(pmadapter, free_cmd); + } + wlan_release_cmd_lock(pmadapter); + + if ((pmadapter->hw_status == WlanHardwareStatusInitializing) && + (pmadapter->last_init_cmd == cmdresp_no)) { + i = pmpriv->bss_index + 1; + while (i < pmadapter->priv_num && + (!(pmpriv_next = pmadapter->priv[i]) || + pmpriv_next->bss_virtual)) + i++; + if (!pmpriv_next || i >= pmadapter->priv_num) { +#ifdef STA_SUPPORT + if (pmadapter->pwarm_reset_ioctl_req) { + /* warm reset complete */ + pmadapter->hw_status = WlanHardwareStatusReady; + pcb->moal_ioctl_complete( + pmadapter->pmoal_handle, + pmadapter->pwarm_reset_ioctl_req, + MLAN_STATUS_SUCCESS); + pmadapter->pwarm_reset_ioctl_req = MNULL; + goto done; + } +#endif + pmadapter->hw_status = WlanHardwareStatusInitdone; + } else { + /* Issue init commands for the next interface */ + ret = pmpriv_next->ops.init_cmd(pmpriv_next, MFALSE); + } + } else if ((pmadapter->hw_status == WlanHardwareStatusGetHwSpec) && + (HostCmd_CMD_GET_HW_SPEC == cmdresp_no)) { + pmadapter->hw_status = WlanHardwareStatusGetHwSpecdone; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the timeout of command sending. + * It will re-send the same command again. + * + * @param function_context A pointer to function_context + * @return N/A + */ +t_void wlan_cmd_timeout_func(t_void *function_context) +{ + mlan_adapter *pmadapter = (mlan_adapter *)function_context; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_ioctl_req *pioctl_buf = MNULL; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif +#if defined(SDIO) || defined(PCIE) + t_u8 i; +#endif + mlan_private *pmpriv = MNULL; + + ENTER(); + + pmadapter->cmd_timer_is_set = MFALSE; + if (!pmadapter->curr_cmd) { + if (pmadapter->ext_scan && pmadapter->ext_scan_enh && + pmadapter->scan_processing) { + PRINTM(MMSG, "Ext scan enh timeout\n"); + pmadapter->ext_scan_timeout = MTRUE; + wlan_dump_info(pmadapter, REASON_CODE_EXT_SCAN_TIMEOUT); + goto exit; + } + PRINTM(MWARN, "CurCmd Empty\n"); + goto exit; + } + pmadapter->num_cmd_timeout++; + pcmd_node = pmadapter->curr_cmd; + if (pcmd_node->pioctl_buf != MNULL) { + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + pioctl_buf->status_code = MLAN_ERROR_CMD_TIMEOUT; + } + + pmadapter->dbg.timeout_cmd_id = + pmadapter->dbg.last_cmd_id[pmadapter->dbg.last_cmd_index]; + pmadapter->dbg.timeout_cmd_act = + pmadapter->dbg.last_cmd_act[pmadapter->dbg.last_cmd_index]; + PRINTM_GET_SYS_TIME(MERROR, &sec, &usec); + PRINTM(MERROR, "Timeout cmd id (%lu.%06lu) = 0x%x, act = 0x%x\n", sec, + usec, pmadapter->dbg.timeout_cmd_id, + pmadapter->dbg.timeout_cmd_act); +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type) && pcmd_node->cmdbuf) { + t_u8 *pcmd_buf; + pcmd_buf = pcmd_node->cmdbuf->pbuf + + pcmd_node->cmdbuf->data_offset + + pmadapter->ops.intf_header_len; + for (i = 0; i < 16; i++) + PRINTM(MERROR, "%02x ", *pcmd_buf++); + PRINTM(MERROR, "\n"); + } +#endif +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type)) + pmadapter->ops.debug_dump(pmadapter); +#endif + pmpriv = pcmd_node->priv; + if (pmpriv) + PRINTM(MERROR, "BSS type = %d BSS role= %d\n", pmpriv->bss_type, + pmpriv->bss_role); + wlan_dump_info(pmadapter, REASON_CODE_CMD_TIMEOUT); + + if (pmadapter->hw_status == WlanHardwareStatusInitializing || + pmadapter->hw_status == WlanHardwareStatusGetHwSpec) + wlan_init_fw_complete(pmadapter); + else { + /* Signal MOAL to perform extra handling for debugging */ + if (pmpriv) { + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DBG_DUMP, + MNULL); + } else { + wlan_recv_event(wlan_get_priv(pmadapter, + MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DBG_DUMP, MNULL); + } + } + +exit: + LEAVE(); + return; +} + +#ifdef STA_SUPPORT +/** + * @brief Internal function used to flush the scan pending queue + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +t_void wlan_flush_scan_queue(pmlan_adapter pmadapter) +{ + cmd_ctrl_node *pcmd_node = MNULL; + + ENTER(); + + wlan_request_cmd_lock(pmadapter); + while ((pcmd_node = (cmd_ctrl_node *)util_peek_list( + pmadapter->pmoal_handle, &pmadapter->scan_pending_q, + MNULL, MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + pcmd_node->pioctl_buf = MNULL; + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + + pmadapter->scan_processing = MFALSE; + wlan_release_cmd_lock(pmadapter); + + LEAVE(); +} + +/** + * @brief Cancel pending SCAN ioctl cmd. + * + * @param pmadapter A pointer to mlan_adapter + * @param pioctl_req A pointer to pmlan_ioctl_req + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING + */ +mlan_status wlan_cancel_pending_scan_cmd(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_ioctl_req *pioctl_buf = MNULL; + pmlan_private priv = MNULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + PRINTM(MIOCTL, "Cancel scan command\n"); + wlan_request_cmd_lock(pmadapter); + /* IOCTL will be completed, avoid calling IOCTL complete again from + * EVENT/CMDRESP */ + if (pmadapter->pscan_ioctl_req) { + pioctl_buf = pmadapter->pscan_ioctl_req; + priv = pmadapter->priv[pioctl_buf->bss_index]; + pmadapter->pscan_ioctl_req = MNULL; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + } + + if (pmadapter->curr_cmd && pmadapter->curr_cmd->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pmadapter->curr_cmd->pioctl_buf; + if (pioctl_buf->req_id == MLAN_IOCTL_SCAN) { + PRINTM(MIOCTL, "wlan_cancel_scan: current command\n"); + pcmd_node = pmadapter->curr_cmd; + pcmd_node->pioctl_buf = MNULL; + pcmd_node->cmd_flag |= CMD_F_CANCELED; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_buf, + MLAN_STATUS_FAILURE); + } + } + while ((pcmd_node = wlan_get_pending_scan_cmd(pmadapter)) != MNULL) { + PRINTM(MIOCTL, + "wlan_cancel_scan: find scan command in cmd_pending_q\n"); + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + wlan_release_cmd_lock(pmadapter); + if (pmadapter->scan_processing && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + if (priv) { + if (MLAN_STATUS_FAILURE == + wlan_prepare_cmd(priv, HostCmd_CMD_802_11_SCAN_EXT, + HostCmd_ACT_GEN_SET, 0, pioctl_req, + MNULL)) + PRINTM(MERROR, "Failed to prepare command"); + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + status = MLAN_STATUS_PENDING; + } + } else + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); + LEAVE(); + return status; +} +#endif + +/** + * @brief Cancel all pending cmd. + * + * @param pmadapter A pointer to mlan_adapter + * @param flag MTRUE/MFALSE + * + * @return N/A + */ +t_void wlan_cancel_all_pending_cmd(pmlan_adapter pmadapter, t_u8 flag) +{ + cmd_ctrl_node *pcmd_node = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_ioctl_req *pioctl_buf = MNULL; +#ifdef STA_SUPPORT + pmlan_private priv = MNULL; +#endif + ENTER(); + /* Cancel current cmd */ + wlan_request_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + /* IOCTL will be completed, avoid calling IOCTL complete again from + * EVENT/CMDRESP */ + if (pmadapter->pscan_ioctl_req) { + pioctl_buf = pmadapter->pscan_ioctl_req; + priv = pmadapter->priv[pioctl_buf->bss_index]; + pmadapter->pscan_ioctl_req = MNULL; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + } +#endif + if (pmadapter->curr_cmd && flag) { + pcmd_node = pmadapter->curr_cmd; + 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; + } + pmadapter->curr_cmd = MNULL; + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + + /* Cancel all pending command */ + while ((pcmd_node = (cmd_ctrl_node *)util_peek_list( + pmadapter->pmoal_handle, &pmadapter->cmd_pending_q, + MNULL, MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + if (pcmd_node->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_buf, + MLAN_STATUS_FAILURE); + pcmd_node->pioctl_buf = MNULL; + } + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + wlan_release_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + if (pmadapter->scan_processing && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + if (priv) { + if (MLAN_STATUS_FAILURE == + wlan_prepare_cmd(priv, HostCmd_CMD_802_11_SCAN_EXT, + HostCmd_ACT_GEN_SET, 0, MNULL, + MNULL)) + PRINTM(MERROR, "Failed to prepare command"); + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } else + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); +#endif + LEAVE(); +} + +/** + * @brief Cancel specific bss's pending ioctl cmd. + * + * @param pmadapter A pointer to mlan_adapter + * @param bss_index BSS index + * + * @return N/A + */ +t_void wlan_cancel_bss_pending_cmd(pmlan_adapter pmadapter, t_u32 bss_index) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_ioctl_req *pioctl_buf = MNULL; +#ifdef STA_SUPPORT + t_u8 flash_scan = MFALSE; +#endif +#ifdef STA_SUPPORT + pmlan_private priv = MNULL; +#endif + ENTER(); + + PRINTM(MIOCTL, "MOAL Cancel BSS IOCTL: bss_index=%d\n", (int)bss_index); + wlan_request_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + if (pmadapter->pscan_ioctl_req && + (pmadapter->pscan_ioctl_req->bss_index == bss_index)) { + /* IOCTL will be completed, avoid calling IOCTL complete again + * from EVENT/CMDRESP */ + flash_scan = MTRUE; + pioctl_buf = pmadapter->pscan_ioctl_req; + priv = pmadapter->priv[pioctl_buf->bss_index]; + pmadapter->pscan_ioctl_req = MNULL; + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + } +#endif + if (pmadapter->curr_cmd && pmadapter->curr_cmd->pioctl_buf) { + pioctl_buf = (mlan_ioctl_req *)pmadapter->curr_cmd->pioctl_buf; + if (pioctl_buf->bss_index == bss_index) { + pcmd_node = pmadapter->curr_cmd; + pcmd_node->pioctl_buf = MNULL; + pcmd_node->cmd_flag |= CMD_F_CANCELED; +#ifdef STA_SUPPORT + if (pioctl_buf->req_id == MLAN_IOCTL_SCAN) + flash_scan = MTRUE; +#endif + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_buf, + MLAN_STATUS_FAILURE); + } + } + while ((pcmd_node = wlan_get_bss_pending_ioctl_cmd( + pmadapter, bss_index)) != MNULL) { + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + pioctl_buf = (mlan_ioctl_req *)pcmd_node->pioctl_buf; + pcmd_node->pioctl_buf = MNULL; +#ifdef STA_SUPPORT + if (pioctl_buf->req_id == MLAN_IOCTL_SCAN) + flash_scan = MTRUE; +#endif + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + wlan_release_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + if (flash_scan) { + if (pmadapter->scan_processing && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + if (priv) { + if (MLAN_STATUS_FAILURE == + wlan_prepare_cmd( + priv, HostCmd_CMD_802_11_SCAN_EXT, + HostCmd_ACT_GEN_SET, 0, MNULL, + MNULL)) + PRINTM(MERROR, + "failed to prepare command"); + wlan_recv_event( + priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } else + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); + } +#endif + LEAVE(); + return; +} + +/** + * @brief Cancel pending ioctl cmd. + * + * @param pmadapter A pointer to mlan_adapter + * @param pioctl_req A pointer to mlan_ioctl_req buf + * + * @return N/A + */ +t_void wlan_cancel_pending_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + cmd_ctrl_node *pcmd_node = MNULL; + t_u8 find = MFALSE; +#ifdef STA_SUPPORT + pmlan_private priv = MNULL; +#endif + + ENTER(); + + PRINTM(MIOCTL, "MOAL Cancel IOCTL: 0x%x sub_id=0x%x action=%d\n", + pioctl_req->req_id, *((t_u32 *)pioctl_req->pbuf), + (int)pioctl_req->action); + + wlan_request_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + /* IOCTL will be completed, avoid calling IOCTL complete again from + * EVENT/CMDRESP */ + if (pmadapter->pscan_ioctl_req == pioctl_req) { + priv = pmadapter->priv[pioctl_req->bss_index]; + pmadapter->pscan_ioctl_req = MNULL; + find = MTRUE; + } +#endif + if ((pmadapter->curr_cmd) && + (pmadapter->curr_cmd->pioctl_buf == pioctl_req)) { + pcmd_node = pmadapter->curr_cmd; + pcmd_node->pioctl_buf = MNULL; + pcmd_node->cmd_flag |= CMD_F_CANCELED; + find = MTRUE; + } + + while ((pcmd_node = wlan_get_pending_ioctl_cmd(pmadapter, + pioctl_req)) != MNULL) { + util_unlink_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + (pmlan_linked_list)pcmd_node, MNULL, MNULL); + pcmd_node->pioctl_buf = MNULL; + find = MTRUE; + wlan_insert_cmd_to_free_q(pmadapter, pcmd_node); + } + wlan_release_cmd_lock(pmadapter); +#ifdef STA_SUPPORT + if (pioctl_req->req_id == MLAN_IOCTL_SCAN) { + if (pmadapter->scan_processing && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + if (priv) { + if (MLAN_STATUS_FAILURE == + wlan_prepare_cmd( + priv, HostCmd_CMD_802_11_SCAN_EXT, + HostCmd_ACT_GEN_SET, 0, MNULL, + MNULL)) + PRINTM(MERROR, + "Failed to prepare command"); + wlan_recv_event( + priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } else + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); + } +#endif + if (find) { + pioctl_req->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_req, + MLAN_STATUS_FAILURE); + } + + LEAVE(); + return; +} + +/** + * @brief This function convert mlan_wifi_rate to wifi_rate. + * + * @param pmpriv A pointer to mlan_private structure + * @param pmlan_rate A pointer to mlan_wifi_rate structure + * @param prate A pointer to wifi_rate + * + * @return N/A + */ +t_void wlan_fill_hal_wifi_rate(pmlan_private pmpriv, mlan_wifi_rate *pmlan_rate, + wifi_rate *prate) +{ + t_u8 index = 0; + t_u8 rate_info = 0; + + ENTER(); + + prate->preamble = pmlan_rate->preamble; + prate->nss = pmlan_rate->nss; + prate->bw = pmlan_rate->bw; + prate->rateMcsIdx = pmlan_rate->rateMcsIdx; + prate->reserved = 0; + prate->bitrate = wlan_le32_to_cpu(pmlan_rate->bitrate); + + if (!prate->bitrate) { + index = prate->rateMcsIdx; + index |= prate->nss << 4; + if (prate->preamble == WIFI_PREAMBLE_HT) + rate_info = MLAN_RATE_FORMAT_HT; + else if (prate->preamble == WIFI_PREAMBLE_VHT) + rate_info = MLAN_RATE_FORMAT_VHT; + else + rate_info = MLAN_RATE_FORMAT_LG; + rate_info |= prate->bw << 2; + PRINTM(MCMND, "index=0x%x rate_info=0x%x\n", index, rate_info); + /** For rateMcsIdx, OFDM/CCK rate code would be as per ieee std + * in the units of 0.5mbps. HT/VHT it would be mcs index */ + /** For bitrate, in 100kbps */ + if (rate_info == MLAN_RATE_FORMAT_LG) + prate->bitrate = prate->rateMcsIdx * 5; + else + prate->bitrate = + wlan_index_to_data_rate(pmpriv->adapter, index, + rate_info, 0) * + 5; + PRINTM(MCMND, "bitrate(in 100kbps)=%d\n", prate->bitrate); + } + + LEAVE(); +} + +/** + * @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(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + 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_ext(pmpriv->adapter, info->param.ver_ext.version_str, + ver_ext->version_str, sizeof(char) * 128, + sizeof(char) * MLAN_MAX_VER_STR_LEN); + } + 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(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + 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 && + !pmadapter->keep_wakeup && !wlan_is_tx_pending(pmadapter) && + !IS_CARD_RX_RCVD(pmadapter)) { + wlan_dnld_sleep_confirm_cmd(pmadapter); + } else { + PRINTM(MCMND, "Delay Sleep Confirm (%s%s%s%s)\n", + (pmadapter->cmd_sent) ? "D" : "", + (pmadapter->curr_cmd) ? "C" : "", + (wlan_is_tx_pending(pmadapter)) ? "T" : "", + (IS_CARD_RX_RCVD(pmadapter)) ? "R" : ""); + } + + LEAVE(); +} + +/** + * @brief This function sends the HS_ACTIVATED event to the application + * + * @param priv A pointer to mlan_private structure + * @param activated MTRUE if activated, MFALSE if de-activated + * + * @return N/A + */ +t_void wlan_host_sleep_activated_event(pmlan_private priv, t_u8 activated) +{ + ENTER(); + + if (!priv) { + LEAVE(); + return; + } + + if (activated) { + if (priv->adapter->is_hs_configured) { + priv->adapter->hs_activated = MTRUE; + wlan_update_rxreorder_tbl(priv->adapter, MTRUE); + PRINTM(MEVENT, "hs_activated\n"); + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_HS_ACTIVATED, + MNULL); + } else + PRINTM(MWARN, "hs_activated: HS not configured !!!\n"); + } else { + PRINTM(MEVENT, "hs_deactived\n"); + priv->adapter->hs_activated = MFALSE; + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_HS_DEACTIVATED, MNULL); + } + + LEAVE(); + return; +} + +/** + * @brief This function sends the HS_WAKEUP event to the application + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + */ +t_void wlan_host_sleep_wakeup_event(pmlan_private priv) +{ + ENTER(); + + if (priv->adapter->is_hs_configured) + wlan_recv_event(priv, MLAN_EVENT_ID_FW_HS_WAKEUP, MNULL); + else + PRINTM(MWARN, "hs_wakeup: Host Sleep not configured !!!\n"); + + LEAVE(); +} + +/** + * @brief This function handles the command response of hs_cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_802_11_hs_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(); + + phs_cfg->params.hs_config.conditions = + wlan_le32_to_cpu(phs_cfg->params.hs_config.conditions); + phs_cfg->action = wlan_le16_to_cpu(phs_cfg->action); + PRINTM(MCMND, + "CMD_RESP: HS_CFG cmd reply result=%#x," + " action=0x%x conditions=0x%x gpio=0x%x gap=0x%x\n", + resp->result, phs_cfg->action, + phs_cfg->params.hs_config.conditions, + phs_cfg->params.hs_config.gpio, phs_cfg->params.hs_config.gap); + if ((phs_cfg->action == HS_ACTIVATE && + !pmadapter->pcard_info->supp_ps_handshake) || + pmadapter->pcard_info->supp_ps_handshake) { + /* clean up curr_cmd to allow suspend */ + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_NO_ERROR; + /* Clean up and put current command back to cmd_free_q */ + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + if (!pmadapter->pcard_info->supp_ps_handshake) { + wlan_host_sleep_activated_event(pmpriv, MTRUE); + goto done; + } + } + if (phs_cfg->params.hs_config.conditions != HOST_SLEEP_CFG_CANCEL) { + pmadapter->is_hs_configured = MTRUE; + if (pmadapter->pcard_info->supp_ps_handshake) + wlan_host_sleep_activated_event(pmpriv, 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) + pmadapter->ops.wakeup_card(pmadapter, MTRUE); + 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; + pmlan_private pmpriv; + + 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); + + pmpriv = + wlan_get_priv_by_id(pmadapter, HostCmd_GET_BSS_NO(cmd->seq_num), + HostCmd_GET_BSS_TYPE(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 (!pmpriv) + pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + 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_NETINTF(MEVENT, pmpriv); + 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, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_u16 ps_bitmap, 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->delay_to_ps = + wlan_cpu_to_le16(pmadapter->delay_to_ps); + ps_mode->mode = + wlan_cpu_to_le16(pmadapter->enhanced_ps_mode); + } + if (ps_bitmap & BITMAP_BCN_TMO) { + MrvlIEtypes_bcn_timeout_t *bcn_tmo_tlv = + (MrvlIEtypes_bcn_timeout_t *)tlv; + mlan_ds_bcn_timeout *bcn_tmo = + (mlan_ds_bcn_timeout *)pdata_buf; + bcn_tmo_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_BCN_TIMEOUT); + bcn_tmo_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_bcn_timeout_t) - + sizeof(MrvlIEtypesHeader_t)); + bcn_tmo_tlv->bcn_miss_tmo_window = + wlan_cpu_to_le16(bcn_tmo->bcn_miss_tmo_window); + bcn_tmo_tlv->bcn_miss_tmo_period = + wlan_cpu_to_le16(bcn_tmo->bcn_miss_tmo_period); + bcn_tmo_tlv->bcn_rq_tmo_window = + wlan_cpu_to_le16(bcn_tmo->bcn_rq_tmo_window); + bcn_tmo_tlv->bcn_rq_tmo_period = + wlan_cpu_to_le16(bcn_tmo->bcn_rq_tmo_period); + cmd_size += sizeof(MrvlIEtypes_bcn_timeout_t); + tlv += sizeof(MrvlIEtypes_bcn_timeout_t); + + psmode_enh->params.auto_ps.ps_bitmap = wlan_cpu_to_le16( + (ps_bitmap & (~BITMAP_BCN_TMO)) | + BITMAP_STA_PS); + } + if (ps_bitmap & BITMAP_AUTO_DS) { + MrvlIEtypes_auto_ds_param_t *auto_ps_tlv = + (MrvlIEtypes_auto_ds_param_t *)tlv; + auto_ds_param *auto_ds = &auto_ps_tlv->param; + t_u16 idletime = 0; + auto_ps_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); + auto_ps_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_auto_ds_param_t) - + sizeof(MrvlIEtypesHeader_t)); + cmd_size += sizeof(MrvlIEtypes_auto_ds_param_t); + tlv += sizeof(MrvlIEtypes_auto_ds_param_t); + if (pdata_buf) + idletime = + ((mlan_ds_auto_ds *)pdata_buf)->idletime; + auto_ds->deep_sleep_timeout = + wlan_cpu_to_le16(idletime); + } +#if defined(UAP_SUPPORT) + if (pdata_buf && + (ps_bitmap & (BITMAP_UAP_INACT_PS | BITMAP_UAP_DTIM_PS))) { + mlan_ds_ps_mgmt *ps_mgmt = (mlan_ds_ps_mgmt *)pdata_buf; + MrvlIEtypes_sleep_param_t *sleep_tlv = MNULL; + MrvlIEtypes_inact_sleep_param_t *inact_tlv = MNULL; + if (ps_mgmt->flags & PS_FLAG_SLEEP_PARAM) { + sleep_tlv = (MrvlIEtypes_sleep_param_t *)tlv; + sleep_tlv->header.type = wlan_cpu_to_le16( + TLV_TYPE_AP_SLEEP_PARAM); + sleep_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_sleep_param_t) - + sizeof(MrvlIEtypesHeader_t)); + sleep_tlv->ctrl_bitmap = wlan_cpu_to_le32( + ps_mgmt->sleep_param.ctrl_bitmap); + sleep_tlv->min_sleep = wlan_cpu_to_le32( + ps_mgmt->sleep_param.min_sleep); + sleep_tlv->max_sleep = wlan_cpu_to_le32( + ps_mgmt->sleep_param.max_sleep); + cmd_size += sizeof(MrvlIEtypes_sleep_param_t); + tlv += sizeof(MrvlIEtypes_sleep_param_t); + } + if (ps_mgmt->flags & PS_FLAG_INACT_SLEEP_PARAM) { + inact_tlv = + (MrvlIEtypes_inact_sleep_param_t *)tlv; + inact_tlv->header.type = wlan_cpu_to_le16( + TLV_TYPE_AP_INACT_SLEEP_PARAM); + inact_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_inact_sleep_param_t) - + sizeof(MrvlIEtypesHeader_t)); + inact_tlv->inactivity_to = wlan_cpu_to_le32( + ps_mgmt->inact_param.inactivity_to); + inact_tlv->min_awake = wlan_cpu_to_le32( + ps_mgmt->inact_param.min_awake); + inact_tlv->max_awake = wlan_cpu_to_le32( + ps_mgmt->inact_param.max_awake); + cmd_size += + sizeof(MrvlIEtypes_inact_sleep_param_t); + tlv += sizeof(MrvlIEtypes_inact_sleep_param_t); + } + } +#endif + cmd->size = wlan_cpu_to_le16(cmd_size); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of ps_mode_enh + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_enh_power_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_ds_rate *rate = MNULL; + ENTER(); + + pmpriv->tx_rate = resp->params.tx_rate.tx_rate; + pmpriv->tx_rate_info = resp->params.tx_rate.tx_rate_info; + if ((pmpriv->tx_rate_info & 0x3) == MLAN_RATE_FORMAT_HE) + pmpriv->ext_tx_rate_info = + resp->params.tx_rate.ext_tx_rate_info; + else + pmpriv->ext_tx_rate_info = 0; + + if (!pmpriv->is_data_rate_auto) { + pmpriv->data_rate = + wlan_index_to_data_rate(pmadapter, pmpriv->tx_rate, + pmpriv->tx_rate_info, + pmpriv->ext_tx_rate_info); + } + + if (pioctl_buf) { + rate = (mlan_ds_rate *)pioctl_buf->pbuf; + if (rate->sub_command == MLAN_OID_RATE_CFG) { + if (rate->param.rate_cfg.rate_type == MLAN_RATE_INDEX) { + if ((pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_VHT || + ((pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_HE)) + + /* VHT rate */ + rate->param.rate_cfg.rate = + (pmpriv->tx_rate) & 0xF; + else if ((pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_HT) + /* HT rate */ + rate->param.rate_cfg.rate = + pmpriv->tx_rate + + MLAN_RATE_INDEX_MCS0; + else + /* LG rate */ + /* For HostCmd_CMD_802_11_TX_RATE_QUERY, + * there is a hole (0x4) in rate table + * between HR/DSSS and OFDM rates, + * so minus 1 for OFDM rate index */ + rate->param.rate_cfg.rate = + (pmpriv->tx_rate > + MLAN_RATE_INDEX_OFDM0) ? + pmpriv->tx_rate - 1 : + pmpriv->tx_rate; + } else { + /* rate_type = MLAN_RATE_VALUE */ + rate->param.rate_cfg.rate = + wlan_index_to_data_rate( + pmadapter, pmpriv->tx_rate, + pmpriv->tx_rate_info, + pmpriv->ext_tx_rate_info); + } + } else if (rate->sub_command == MLAN_OID_GET_DATA_RATE) { + /* Tx rate info */ + if ((pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_VHT || + (pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_HE) { + /* AX/VHT rate */ + rate->param.data_rate.tx_rate_format = + pmpriv->tx_rate_info & 0x3; + rate->param.data_rate.tx_ht_bw = + (pmpriv->tx_rate_info & 0xC) >> 2; + if ((pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_HE) + rate->param.data_rate.tx_ht_gi = + (pmpriv->tx_rate_info & 0x10) >> + 4 | + (pmpriv->tx_rate_info & 0x80) >> + 6; + else + rate->param.data_rate.tx_ht_gi = + (pmpriv->tx_rate_info & 0x10) >> + 4; + rate->param.data_rate.tx_nss = + ((pmpriv->tx_rate) >> 4) & 0x03; + rate->param.data_rate.tx_mcs_index = + (pmpriv->tx_rate) & 0xF; + if ((pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_VHT || + (pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_HE) + rate->param.data_rate.tx_data_rate = + wlan_index_to_data_rate( + pmadapter, + pmpriv->tx_rate, + pmpriv->tx_rate_info, + pmpriv->ext_tx_rate_info); + } else if ((pmpriv->tx_rate_info & 0x3) == + MLAN_RATE_FORMAT_HT) { + /* HT rate */ + rate->param.data_rate.tx_rate_format = + MLAN_RATE_FORMAT_HT; + rate->param.data_rate.tx_ht_bw = + (pmpriv->tx_rate_info & 0xC) >> 2; + + rate->param.data_rate.tx_ht_gi = + (pmpriv->tx_rate_info & 0x10) >> 4; + rate->param.data_rate.tx_mcs_index = + pmpriv->tx_rate; + rate->param.data_rate.tx_data_rate = + wlan_index_to_data_rate( + pmadapter, pmpriv->tx_rate, + pmpriv->tx_rate_info, + pmpriv->ext_tx_rate_info); + } else { + /* LG rate */ + rate->param.data_rate.tx_rate_format = + MLAN_RATE_FORMAT_LG; + /* For HostCmd_CMD_802_11_TX_RATE_QUERY, + * there is a hole in rate table + * between HR/DSSS and OFDM rates, + * so minus 1 for OFDM rate index */ + rate->param.data_rate.tx_data_rate = + (pmpriv->tx_rate > + MLAN_RATE_INDEX_OFDM0) ? + pmpriv->tx_rate - 1 : + pmpriv->tx_rate; + } + + /* Rx rate info */ + if ((pmpriv->rxpd_rate_info & 0x3) == + MLAN_RATE_FORMAT_VHT || + (pmpriv->rxpd_rate_info & 0x3) == + MLAN_RATE_FORMAT_HE) { + /* VHT rate */ + rate->param.data_rate.rx_rate_format = + pmpriv->rxpd_rate_info & 0x3; + rate->param.data_rate.rx_ht_bw = + (pmpriv->rxpd_rate_info & 0xC) >> 2; + if ((pmpriv->rxpd_rate_info & 0x3) == + MLAN_RATE_FORMAT_HE) + rate->param.data_rate.rx_ht_gi = + (pmpriv->rxpd_rate_info & + 0x10) >> + 4 | + (pmpriv->rxpd_rate_info & + 0x80) >> + 6; + else + rate->param.data_rate.rx_ht_gi = + (pmpriv->rxpd_rate_info & + 0x10) >> + 4; + rate->param.data_rate.rx_nss = + ((pmpriv->rxpd_rate) >> 4) & 0x3; + rate->param.data_rate.rx_mcs_index = + (pmpriv->rxpd_rate) & 0xF; + if ((pmpriv->rxpd_rate_info & 0x3) == + MLAN_RATE_FORMAT_VHT || + (pmpriv->rxpd_rate_info & 0x3) == + MLAN_RATE_FORMAT_HE) + rate->param.data_rate.rx_data_rate = + wlan_index_to_data_rate( + pmadapter, + pmpriv->rxpd_rate, + pmpriv->rxpd_rate_info, + pmpriv->rxpd_rx_info); + } else if ((pmpriv->rxpd_rate_info & 0x3) == + MLAN_RATE_FORMAT_HT) { + /* HT rate */ + rate->param.data_rate.rx_rate_format = + MLAN_RATE_FORMAT_HT; + rate->param.data_rate.rx_ht_bw = + (pmpriv->rxpd_rate_info & 0xC) >> 2; + rate->param.data_rate.rx_ht_gi = + (pmpriv->rxpd_rate_info & 0x10) >> 4; + rate->param.data_rate.rx_mcs_index = + pmpriv->rxpd_rate; + rate->param.data_rate.rx_data_rate = + wlan_index_to_data_rate( + pmadapter, pmpriv->rxpd_rate, + pmpriv->rxpd_rate_info, 0); + } else { + /* LG rate */ + rate->param.data_rate.rx_rate_format = + MLAN_RATE_FORMAT_LG; + /* For rate index in RxPD, + * there is a hole in rate table + * between HR/DSSS and OFDM rates, + * so minus 1 for OFDM rate index */ + rate->param.data_rate.rx_data_rate = + (pmpriv->rxpd_rate > + MLAN_RATE_INDEX_OFDM0) ? + pmpriv->rxpd_rate - 1 : + pmpriv->rxpd_rate; + } + } + pioctl_buf->data_read_written = + sizeof(mlan_data_rate) + MLAN_SUB_COMMAND_SIZE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of robustcoex. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_robustcoex(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u16 *pdata_buf) +{ + HostCmd_DS_802_11_ROBUSTCOEX *rbstcx = &cmd->params.robustcoexparams; + mlan_ds_misc_robustcoex_params *robustcoex_params = MNULL; + MrvlIEtypes_RobustcoexSourceGPIO_t *tlv = + (MrvlIEtypes_RobustcoexSourceGPIO_t *)(rbstcx->tlv_buf); + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_ROBUSTCOEX); + cmd->size = sizeof(HostCmd_DS_802_11_ROBUSTCOEX) + S_DS_GEN; + rbstcx->action = wlan_cpu_to_le16(cmd_action); + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + robustcoex_params = (mlan_ds_misc_robustcoex_params *)pdata_buf; + if (robustcoex_params->method == ROBUSTCOEX_GPIO_CFG) { + cmd->size += sizeof(MrvlIEtypes_RobustcoexSourceGPIO_t); + tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ROBUSTCOEX); + tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_RobustcoexSourceGPIO_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv->enable = (t_u8)robustcoex_params->enable; + tlv->gpio_num = (t_u8)robustcoex_params->gpio_num; + tlv->gpio_polarity = + (t_u8)robustcoex_params->gpio_polarity; + } + break; + case HostCmd_ACT_GEN_GET: + default: + break; + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#if defined(PCIE) +/** + * @brief This function enables SSU support. + * + * @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_ssu(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u16 *pdata_buf) +{ + HostCmd_DS_SSU_CFG *ssu_cfg_cmd = &cmd->params.ssu_params; + mlan_ds_ssu_params *ssu_params = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SSU); + cmd->size = sizeof(HostCmd_DS_SSU_CFG) + S_DS_GEN; + ssu_cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + case HostCmd_ACT_GEN_SET_DEFAULT: + ssu_params = (mlan_ds_ssu_params *)pdata_buf; + ssu_cfg_cmd->nskip = wlan_cpu_to_le32(ssu_params->nskip); + ssu_cfg_cmd->nsel = wlan_cpu_to_le32(ssu_params->nsel); + ssu_cfg_cmd->adcdownsample = + wlan_cpu_to_le32(ssu_params->adcdownsample); + ssu_cfg_cmd->mask_adc_pkt = + wlan_cpu_to_le32(ssu_params->mask_adc_pkt); + ssu_cfg_cmd->out_16bits = + wlan_cpu_to_le32(ssu_params->out_16bits); + ssu_cfg_cmd->spec_pwr_enable = + wlan_cpu_to_le32(ssu_params->spec_pwr_enable); + ssu_cfg_cmd->rate_deduction = + wlan_cpu_to_le32(ssu_params->rate_deduction); + ssu_cfg_cmd->n_pkt_avg = + wlan_cpu_to_le32(ssu_params->n_pkt_avg); + /* Initialize PCIE ring buffer */ + ret = wlan_alloc_ssu_pcie_buf(pmadapter); + if (MLAN_STATUS_SUCCESS != ret) { + PRINTM(MERROR, + "Failed to allocate PCIE host buffers for SSU\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + ssu_cfg_cmd->buffer_base_addr[0] = + wlan_cpu_to_le32((t_u32)pmadapter->ssu_buf->buf_pa); + ssu_cfg_cmd->buffer_base_addr[1] = wlan_cpu_to_le32( + (t_u32)((t_u64)(pmadapter->ssu_buf->buf_pa >> 32))); + ssu_cfg_cmd->buffer_pool_size = + wlan_cpu_to_le32(MLAN_SSU_BUF_SIZE); + break; + case HostCmd_ACT_GEN_GET: + default: + break; + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function prepares command of dmcs config. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_dmcs_config(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_DMCS_CFG *dmcs = &cmd->params.dmcs; + mlan_ds_misc_mapping_policy *dmcs_params = MNULL; + t_u8 *mapping_policy = (t_u8 *)dmcs->tlv_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_DMCS_CONFIG); + cmd->size = sizeof(HostCmd_DS_DMCS_CFG) + S_DS_GEN; + dmcs->action = wlan_cpu_to_le16(cmd_action); + dmcs_params = (mlan_ds_misc_mapping_policy *)pdata_buf; + dmcs->subcmd = wlan_cpu_to_le16(dmcs_params->subcmd); + switch (dmcs->subcmd) { + case 0: + cmd->size += sizeof(t_u8); + *mapping_policy = dmcs_params->mapping_policy; + break; + case 1: + default: + break; + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of dmcs config + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ret_dmcs_config(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_DMCS_CFG *dmcs = &resp->params.dmcs; + MrvlIEtypes_DmcsStatus_t *dmcs_status; + mlan_ds_misc_cfg *cfg = MNULL; + t_u16 tlv_buf_left = 0; + t_u16 tlv_type = 0, tlv_len = 0; + MrvlIEtypesHeader_t *tlv = MNULL; + int i = 0; + + ENTER(); + if (pioctl_buf) { + cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)dmcs + + sizeof(HostCmd_DS_DMCS_CFG)); + tlv_buf_left = + resp->size - (sizeof(HostCmd_DS_DMCS_CFG) + 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 while processing DMCS status tlv, bytes_left < TLV len\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + switch (tlv_type) { + case TLV_TYPE_DMCS_STATUS: + dmcs_status = (MrvlIEtypes_DmcsStatus_t *)tlv; + cfg->param.dmcs_status.mapping_policy = + dmcs_status->mapping_policy; + memset(pmpriv->adapter, + &cfg->param.dmcs_status.radio_status, 0, + sizeof(dmcsStatus_t)); + for (i = 0; i < MAX_NUM_MAC; i++) { + memcpy_ext( + pmpriv->adapter, + &cfg->param.dmcs_status + .radio_status[i], + &dmcs_status->radio_status[i], + sizeof(dmcsStatus_t), + sizeof(dmcsStatus_t)); + } + break; + default: + break; + } + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t + *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + pioctl_buf->data_read_written = + sizeof(mlan_ds_misc_dmcs_status); + } + LEAVE(); + return ret; +} + +/** + * @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 + * @param pioctl_buf A pointer to ioctl buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_tx_rate_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_TX_RATE_CFG *rate_cfg = + (HostCmd_DS_TX_RATE_CFG *)&(cmd->params.tx_rate_cfg); + MrvlRateScope_t *rate_scope; + MrvlRateDropPattern_t *rate_drop; + MrvlIETypes_rate_setting_t *rate_setting_tlv; + mlan_ds_rate *ds_rate = MNULL; + t_u16 *pbitmap_rates = (t_u16 *)pdata_buf; + + t_u32 i; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TX_RATE_CFG); + + rate_cfg->action = wlan_cpu_to_le16(cmd_action); + + rate_scope = (MrvlRateScope_t *)rate_cfg->tlv_buf; + rate_scope->type = wlan_cpu_to_le16(TLV_TYPE_RATE_SCOPE); + rate_scope->length = wlan_cpu_to_le16(sizeof(MrvlRateScope_t) - + sizeof(MrvlIEtypesHeader_t)); + if (pbitmap_rates != MNULL) { + rate_scope->hr_dsss_rate_bitmap = + wlan_cpu_to_le16(pbitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = + wlan_cpu_to_le16(pbitmap_rates[1]); + for (i = 0; i < NELEMENTS(rate_scope->ht_mcs_rate_bitmap); i++) + rate_scope->ht_mcs_rate_bitmap[i] = + wlan_cpu_to_le16(pbitmap_rates[2 + i]); + for (i = 0; i < NELEMENTS(rate_scope->vht_mcs_rate_bitmap); i++) + rate_scope->vht_mcs_rate_bitmap[i] = wlan_cpu_to_le16( + pbitmap_rates + [2 + + NELEMENTS( + rate_scope->ht_mcs_rate_bitmap) + + i]); + if (IS_FW_SUPPORT_11AX(pmpriv->adapter)) { + for (i = 0; + i < NELEMENTS(rate_scope->he_mcs_rate_bitmap); i++) + rate_scope->he_mcs_rate_bitmap + [i] = wlan_cpu_to_le16( + pbitmap_rates + [2 + + NELEMENTS( + rate_scope + ->ht_mcs_rate_bitmap) + + NELEMENTS( + rate_scope + ->vht_mcs_rate_bitmap) + + i]); + } else { + rate_scope->length = wlan_cpu_to_le16( + sizeof(MrvlRateScope_t) - + sizeof(rate_scope->he_mcs_rate_bitmap) - + sizeof(MrvlIEtypesHeader_t)); + } + } else { + rate_scope->hr_dsss_rate_bitmap = + wlan_cpu_to_le16(pmpriv->bitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = + wlan_cpu_to_le16(pmpriv->bitmap_rates[1]); + for (i = 0; i < NELEMENTS(rate_scope->ht_mcs_rate_bitmap); i++) + rate_scope->ht_mcs_rate_bitmap[i] = + wlan_cpu_to_le16(pmpriv->bitmap_rates[2 + i]); + for (i = 0; i < NELEMENTS(rate_scope->vht_mcs_rate_bitmap); i++) + rate_scope->vht_mcs_rate_bitmap[i] = wlan_cpu_to_le16( + pmpriv->bitmap_rates + [2 + + NELEMENTS( + rate_scope->ht_mcs_rate_bitmap) + + i]); + if (IS_FW_SUPPORT_11AX(pmpriv->adapter)) { + for (i = 0; + i < NELEMENTS(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->he_mcs_rate_bitmap + [i] = wlan_cpu_to_le16( + pmpriv->bitmap_rates + [2 + + NELEMENTS( + rate_scope + ->ht_mcs_rate_bitmap) + + NELEMENTS( + rate_scope + ->vht_mcs_rate_bitmap) + + i]); + } else { + rate_scope->length = wlan_cpu_to_le16( + sizeof(MrvlRateScope_t) - + sizeof(rate_scope->he_mcs_rate_bitmap) - + sizeof(MrvlIEtypesHeader_t)); + } + } + + rate_drop = + (MrvlRateDropPattern_t *)((t_u8 *)rate_scope + + wlan_le16_to_cpu(rate_scope->length) + + sizeof(MrvlIEtypesHeader_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) + rate_scope->length + + sizeof(MrvlIEtypesHeader_t) + sizeof(MrvlRateDropPattern_t)); + if (pioctl_buf && pmpriv->adapter->pcard_info->v17_fw_api) { + ds_rate = (mlan_ds_rate *)pioctl_buf->pbuf; + rate_setting_tlv = (MrvlIETypes_rate_setting_t + *)((t_u8 *)rate_drop + + sizeof(MrvlRateDropPattern_t)); + rate_setting_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_TX_RATE_CFG); + rate_setting_tlv->header.len = wlan_cpu_to_le16( + sizeof(rate_setting_tlv->rate_setting)); + rate_setting_tlv->rate_setting = + wlan_cpu_to_le16(ds_rate->param.rate_cfg.rate_setting); + PRINTM(MCMND, "he rate setting = %d\n", + rate_setting_tlv->rate_setting); + cmd->size = wlan_cpu_to_le16( + S_DS_GEN + sizeof(HostCmd_DS_TX_RATE_CFG) + + rate_scope->length + sizeof(MrvlIEtypesHeader_t) + + sizeof(MrvlRateDropPattern_t) + + sizeof(MrvlIETypes_rate_setting_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(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_ds_rate *ds_rate = MNULL; + HostCmd_DS_TX_RATE_CFG *prate_cfg = MNULL; + MrvlRateScope_t *prate_scope; + MrvlIEtypesHeader_t *head = MNULL; + t_u16 tlv, tlv_buf_len = 0; + t_u8 *tlv_buf; + t_u32 i; + t_s32 index; + mlan_status ret = MLAN_STATUS_SUCCESS; + MrvlIETypes_rate_setting_t *rate_setting_tlv = MNULL; + t_u16 rate_setting = 0xffff; + + ENTER(); + + if (resp == MNULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + prate_cfg = (HostCmd_DS_TX_RATE_CFG *)&(resp->params.tx_rate_cfg); + + tlv_buf = (t_u8 *)prate_cfg->tlv_buf; + if (tlv_buf) { + tlv_buf_len = resp->size - + (sizeof(HostCmd_DS_TX_RATE_CFG) + S_DS_GEN); + tlv_buf_len = wlan_le16_to_cpu(tlv_buf_len); + } + + while (tlv_buf && tlv_buf_len > 0) { + tlv = (*tlv_buf); + tlv = tlv | (*(tlv_buf + 1) << 8); + + switch (tlv) { + case TLV_TYPE_RATE_SCOPE: + prate_scope = (MrvlRateScope_t *)tlv_buf; + pmpriv->bitmap_rates[0] = wlan_le16_to_cpu( + prate_scope->hr_dsss_rate_bitmap); + pmpriv->bitmap_rates[1] = + wlan_le16_to_cpu(prate_scope->ofdm_rate_bitmap); + for (i = 0; + i < NELEMENTS(prate_scope->ht_mcs_rate_bitmap); + i++) + pmpriv->bitmap_rates[2 + i] = wlan_le16_to_cpu( + prate_scope->ht_mcs_rate_bitmap[i]); + for (i = 0; + i < NELEMENTS(prate_scope->vht_mcs_rate_bitmap); + i++) + pmpriv->bitmap_rates + [2 + + sizeof(prate_scope->ht_mcs_rate_bitmap) / + sizeof(t_u16) + + i] = + wlan_le16_to_cpu( + prate_scope + ->vht_mcs_rate_bitmap[i]); + if (IS_FW_SUPPORT_11AX(pmadapter)) { + for (i = 0; + i < + NELEMENTS(prate_scope->he_mcs_rate_bitmap); + i++) + pmpriv->bitmap_rates + [2 + + sizeof(prate_scope + ->ht_mcs_rate_bitmap) / + sizeof(t_u16) + + sizeof(prate_scope + ->vht_mcs_rate_bitmap) / + sizeof(t_u16) + + i] = + wlan_le16_to_cpu( + prate_scope + ->he_mcs_rate_bitmap + [i]); + } + break; + case TLV_TYPE_TX_RATE_CFG: + rate_setting_tlv = + (MrvlIETypes_rate_setting_t *)tlv_buf; + rate_setting = rate_setting_tlv->rate_setting; + 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 + sizeof(MrvlIEtypesHeader_t)); + } + + pmpriv->is_data_rate_auto = wlan_is_rate_auto(pmpriv); + + if (pmpriv->is_data_rate_auto) { + pmpriv->data_rate = 0; + } else { + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + } + + if (pioctl_buf) { + ds_rate = (mlan_ds_rate *)pioctl_buf->pbuf; + if (ds_rate == MNULL) { + PRINTM(MERROR, "Request buffer not found!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (pmpriv->is_data_rate_auto) { + ds_rate->param.rate_cfg.is_rate_auto = MTRUE; + ds_rate->param.rate_cfg.rate_format = + MLAN_RATE_FORMAT_AUTO; + } else { + ds_rate->param.rate_cfg.is_rate_auto = MFALSE; + /* check the LG rate */ + index = wlan_get_rate_index( + pmadapter, &pmpriv->bitmap_rates[0], 4); + if (index != -1) { + if ((index >= MLAN_RATE_BITMAP_OFDM0) && + (index <= MLAN_RATE_BITMAP_OFDM7)) + index -= (MLAN_RATE_BITMAP_OFDM0 - + MLAN_RATE_INDEX_OFDM0); + ds_rate->param.rate_cfg.rate_format = + MLAN_RATE_FORMAT_LG; + ds_rate->param.rate_cfg.rate = index; + } + /* check the HT rate */ + index = wlan_get_rate_index( + pmadapter, &pmpriv->bitmap_rates[2], 16); + if (index != -1) { + ds_rate->param.rate_cfg.rate_format = + MLAN_RATE_FORMAT_HT; + ds_rate->param.rate_cfg.rate = index; + } + /* check the VHT rate */ + index = wlan_get_rate_index( + pmadapter, &pmpriv->bitmap_rates[10], 16); + if (index != -1) { + ds_rate->param.rate_cfg.rate_format = + MLAN_RATE_FORMAT_VHT; + ds_rate->param.rate_cfg.rate = index % 16; + ds_rate->param.rate_cfg.nss = index / 16; + ds_rate->param.rate_cfg.nss += MLAN_RATE_NSS1; + } + /* check the HE rate */ + if (IS_FW_SUPPORT_11AX(pmadapter)) { + index = wlan_get_rate_index( + pmadapter, &pmpriv->bitmap_rates[18], + 16); + if (index != -1) { + ds_rate->param.rate_cfg.rate_format = + MLAN_RATE_FORMAT_HE; + ds_rate->param.rate_cfg.rate = + index % 16; + ds_rate->param.rate_cfg.nss = + index / 16; + ds_rate->param.rate_cfg.nss += + MLAN_RATE_NSS1; + } + } + ds_rate->param.rate_cfg.rate_setting = rate_setting; + PRINTM(MINFO, "Rate index is %d\n", + ds_rate->param.rate_cfg.rate); + } + for (i = 0; i < MAX_BITMAP_RATES_SIZE; i++) { + ds_rate->param.rate_cfg.bitmap_rates[i] = + pmpriv->bitmap_rates[i]; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function issues adapter specific commands + * to initialize firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +mlan_status wlan_adapter_get_hw_spec(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); +#if defined(SDIO) + /* + * This should be issued in the very first to config + * SDIO_GPIO interrupt mode. + */ + if (IS_SD(pmadapter->card_type) && + (wlan_set_sdio_gpio_int(priv) != MLAN_STATUS_SUCCESS)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif + +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type) && + (MLAN_STATUS_SUCCESS != wlan_set_pcie_buf_config(priv))) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_FUNC_INIT, HostCmd_ACT_GEN_SET, + 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + /** DPD data dnld cmd prepare */ + if ((pmadapter->pdpd_data) && (pmadapter->dpd_data_len > 0)) { + ret = wlan_process_hostcmd_cfg(priv, CFG_TYPE_DPDFILE, + pmadapter->pdpd_data, + pmadapter->dpd_data_len); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->pdpd_data = MNULL; + pmadapter->dpd_data_len = 0; + } + if ((pmadapter->ptxpwr_data) && (pmadapter->txpwr_data_len > 0)) { + ret = wlan_process_hostcmd_cfg(priv, CFG_TYPE_HOSTCMD, + pmadapter->ptxpwr_data, + pmadapter->txpwr_data_len); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->ptxpwr_data = MNULL; + pmadapter->txpwr_data_len = 0; + } + if (!pmadapter->pdpd_data && + (pmadapter->dpd_data_len == UNKNOW_DPD_LENGTH)) { + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_GET, OID_TYPE_DPD, 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(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, OID_TYPE_CAL, MNULL, + MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->pcal_data = MNULL; + pmadapter->cal_data_len = 0; + } + /* Get FW region and cfp tables */ + if (pmadapter->init_para.fw_region) { + ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REGION_CFG, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + /* + * Get HW spec + */ + ret = wlan_prepare_cmd(priv, HostCmd_CMD_GET_HW_SPEC, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = MLAN_STATUS_PENDING; +done: + LEAVE(); + return ret; +} + +/** + * @brief This function issues adapter specific commands + * to initialize firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +mlan_status wlan_adapter_init_cmd(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 + +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + } +#endif + + 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 + +#ifdef 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; + } + } + + if (pmadapter->init_para.indrstcfg != 0xffffffff) { + mlan_ds_ind_rst_cfg ind_rst_cfg; + ind_rst_cfg.ir_mode = pmadapter->init_para.indrstcfg & 0xff; + ind_rst_cfg.gpio_pin = + (pmadapter->init_para.indrstcfg & 0xff00) >> 8; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_INDEPENDENT_RESET_CFG, + HostCmd_ACT_GEN_SET, 0, MNULL, + (t_void *)&ind_rst_cfg); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + if (pmadapter->inact_tmo) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmadapter->inact_tmo); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_RF_ANTENNA, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = MLAN_STATUS_PENDING; +done: + LEAVE(); + return ret; +} + +#ifdef RX_PACKET_COALESCE +mlan_status wlan_cmd_rx_pkt_coalesce_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + mlan_ds_misc_rx_packet_coalesce *rx_pkt_cfg = + (mlan_ds_misc_rx_packet_coalesce *)pdata_buf; + HostCmd_DS_RX_PKT_COAL_CFG *prx_coal_cfg = + (HostCmd_DS_RX_PKT_COAL_CFG *)&cmd->params.rx_pkt_coal_cfg; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RX_PKT_COALESCE_CFG); + prx_coal_cfg->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + prx_coal_cfg->packet_threshold = + wlan_cpu_to_le32(rx_pkt_cfg->packet_threshold); + prx_coal_cfg->delay = wlan_cpu_to_le16(rx_pkt_cfg->delay); + PRINTM(MCMND, + "Set RX coal config: packet threshold=%d delay=%d\n", + rx_pkt_cfg->packet_threshold, rx_pkt_cfg->delay); + cmd->size = wlan_cpu_to_le16( + S_DS_GEN + sizeof(HostCmd_DS_RX_PKT_COAL_CFG)); + } else { + cmd->size = wlan_cpu_to_le16(S_DS_GEN + sizeof(cmd_action)); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of RX_PACKET_COAL_CFG + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_rx_pkt_coalesce_cfg(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *pcfg = MNULL; + const HostCmd_DS_RX_PKT_COAL_CFG *presp_cfg = + &resp->params.rx_pkt_coal_cfg; + + ENTER(); + + if (pioctl_buf) { + pcfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + pcfg->param.rx_coalesce.packet_threshold = + wlan_le32_to_cpu(presp_cfg->packet_threshold); + pcfg->param.rx_coalesce.delay = + wlan_le16_to_cpu(presp_cfg->delay); + PRINTM(MCMND, + "Get rx pkt coalesce info: packet threshold=%d delay=%d\n", + pcfg->param.rx_coalesce.packet_threshold, + pcfg->param.rx_coalesce.delay); + pioctl_buf->buf_len = sizeof(mlan_ds_misc_rx_packet_coalesce); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#endif + +/** + * @brief This function download the vdll block. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param block A pointer to VDLL block + * @param block_len The VDLL block length + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_download_vdll_block(mlan_adapter *pmadapter, t_u8 *block, + t_u16 block_len) +{ + mlan_status status = MLAN_STATUS_FAILURE; + mlan_status ret = MLAN_STATUS_PENDING; +#if defined(SDIO) || defined(PCIE) + pvdll_dnld_ctrl ctrl = &pmadapter->vdll_ctrl; +#endif + mlan_buffer *pmbuf = MNULL; + mlan_private *pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + HostCmd_DS_GEN *cmd_hdr = MNULL; + t_u16 msg_len = block_len + sizeof(HostCmd_DS_GEN); +#ifdef USB + t_u32 tmp; +#endif + ENTER(); +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) { + pmbuf = ctrl->cmd_buf; + if (pmbuf) + pmbuf->data_offset += pmadapter->ops.intf_header_len; + } +#endif +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + pmbuf = wlan_alloc_mlan_buffer(pmadapter, + MRVDRV_SIZE_OF_CMD_BUFFER, 0, + MOAL_MALLOC_BUFFER); + if (pmbuf) { + tmp = wlan_cpu_to_le32(MLAN_USB_TYPE_VDLL); + memcpy_ext(pmadapter, + (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset), + (t_u8 *)&tmp, MLAN_TYPE_LEN, MLAN_TYPE_LEN); + pmbuf->data_offset += MLAN_TYPE_LEN; + } + } +#endif + if (!pmbuf) { + PRINTM(MERROR, "dnld vdll: Fail to alloc vdll buf"); + goto done; + } + cmd_hdr = (HostCmd_DS_GEN *)(pmbuf->pbuf + pmbuf->data_offset); + cmd_hdr->command = wlan_cpu_to_le16(HostCmd_CMD_VDLL); + cmd_hdr->seq_num = wlan_cpu_to_le16(0xFF00); + cmd_hdr->size = wlan_cpu_to_le16(msg_len); + + pmadapter->callbacks.moal_memcpy_ext(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(HostCmd_DS_GEN), + block, block_len, block_len); + + pmbuf->data_len = msg_len; + +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) { + pmbuf->data_offset -= pmadapter->ops.intf_header_len; + pmbuf->data_len += pmadapter->ops.intf_header_len; + } +#endif +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + pmbuf->data_offset -= MLAN_TYPE_LEN; + pmbuf->data_len += MLAN_TYPE_LEN; + } +#endif + PRINTM_NETINTF(MCMND, pmpriv); + PRINTM(MCMND, "DNLD_VDLL : block_len=%d\n", block_len); + + ret = pmadapter->ops.host_to_card(pmpriv, MLAN_TYPE_VDLL, pmbuf, MNULL); + + if (ret == MLAN_STATUS_FAILURE) + PRINTM(MERROR, "DNLD_VDLL: Host to Card Failed\n"); + else + status = MLAN_STATUS_SUCCESS; + +done: + if ((ret == MLAN_STATUS_FAILURE) || (ret == MLAN_STATUS_SUCCESS)) { +#ifdef USB + if (IS_USB(pmadapter->card_type)) + wlan_free_mlan_buffer(pmadapter, pmbuf); +#endif + } + LEAVE(); + return status; +} + +/** + * @brief The function Get the VDLL image from moal + * + * @param pmadapter A pointer to mlan_adapter structure + * @param offset offset + * + * @return MLAN_STATUS_SUCCESS + * + */ +static mlan_status wlan_get_vdll_image(pmlan_adapter pmadapter, t_u32 vdll_len) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + vdll_dnld_ctrl *ctrl = &pmadapter->vdll_ctrl; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (ctrl->vdll_mem) { + PRINTM(MCMND, "VDLL mem is not empty: %p len=%d\n", + ctrl->vdll_mem, ctrl->vdll_len); + goto done; + } + if (pcb->moal_vmalloc && pcb->moal_vfree) + status = pcb->moal_vmalloc(pmadapter->pmoal_handle, vdll_len, + (t_u8 **)&ctrl->vdll_mem); + else + status = pcb->moal_malloc(pmadapter->pmoal_handle, vdll_len, + MLAN_MEM_DEF, + (t_u8 **)&ctrl->vdll_mem); + + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "VDLL: Fail to alloc vdll memory"); + goto done; + } + + if (MLAN_STATUS_SUCCESS != + pcb->moal_get_vdll_data(pmadapter->pmoal_handle, vdll_len, + ctrl->vdll_mem)) { + PRINTM(MERROR, "VDLL: firmware image not available\n"); + status = MLAN_STATUS_FAILURE; + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)ctrl->vdll_mem); + else + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)ctrl->vdll_mem); + ctrl->vdll_mem = MNULL; + ctrl->vdll_len = 0; + goto done; + } + /*allocate a memory to store all VDLL images*/ + ctrl->vdll_len = vdll_len; + PRINTM(MMSG, "VDLL image: len=%d\n", ctrl->vdll_len); +done: + LEAVE(); + return status; +} + +/** + * @brief This function handle the multi_chan info event + * + * @param pmpriv A pointer to mlan_private structure + * @param pevent A pointer to event buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_process_vdll_event(pmlan_private pmpriv, pmlan_buffer pevent) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + vdll_ind *ind = MNULL; + t_u32 offset = 0; + t_u16 block_len = 0; + mlan_adapter *pmadapter = pmpriv->adapter; + vdll_dnld_ctrl *ctrl = &pmadapter->vdll_ctrl; + + ENTER(); + ind = (vdll_ind *)(pevent->pbuf + pevent->data_offset + + sizeof(mlan_event_id)); + switch (wlan_le16_to_cpu(ind->type)) { + case VDLL_IND_TYPE_REQ: + offset = wlan_le32_to_cpu(ind->offset); + block_len = wlan_le16_to_cpu(ind->block_len); + PRINTM(MEVENT, "VDLL_IND: type=%d offset = 0x%x, len = %d\n", + wlan_le16_to_cpu(ind->type), offset, block_len); + if (offset <= ctrl->vdll_len) { + block_len = MIN(block_len, ctrl->vdll_len - offset); + if (!pmadapter->cmd_sent) { + status = wlan_download_vdll_block( + pmadapter, ctrl->vdll_mem + offset, + block_len); + if (status) + PRINTM(MERROR, + "Fail to download VDLL block\n"); + } else { + PRINTM(MCMND, + "cmd_sent=1, delay download VDLL block\n"); + ctrl->pending_block_len = block_len; + ctrl->pending_block = ctrl->vdll_mem + offset; + } + } else { + PRINTM(MERROR, + "Invalid VDLL req: offset=0x%x, len=%d, vdll_len=%d\n", + offset, block_len, ctrl->vdll_len); + } + break; + + case VDLL_IND_TYPE_OFFSET: + offset = wlan_le32_to_cpu(ind->offset); + PRINTM(MEVENT, "VDLL_IND (OFFSET): offset=0x%x\n", offset); + wlan_get_vdll_image(pmadapter, offset); + break; + default: + PRINTM(MERROR, "unknow vdll ind type=%d\n", ind->type); + break; + } + LEAVE(); + return status; +} + +/** + * @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(pmlan_private pmpriv, 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_ext(pmpriv->adapter, hw_spec->permanent_addr, pmpriv->curr_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef SDIO +/** + * @brief This function prepares command of sdio rx aggr command. + * + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action: GET or SET + * @param pdata_buf A pointer to new setting buf + + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_sdio_rx_aggr_cfg(HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_SDIO_SP_RX_AGGR_CFG *cfg = &pcmd->params.sdio_rx_aggr; + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG); + pcmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_SDIO_SP_RX_AGGR_CFG) + + S_DS_GEN); + cfg->action = cmd_action; + if (cmd_action == HostCmd_ACT_GEN_SET) + cfg->enable = *(t_u8 *)pdata_buf; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sdio rx aggr command + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ret_sdio_rx_aggr_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_SDIO_SP_RX_AGGR_CFG *cfg = &resp->params.sdio_rx_aggr; + + pmadapter->pcard_sd->sdio_rx_aggr_enable = cfg->enable; + pmadapter->pcard_sd->sdio_rx_block_size = + wlan_le16_to_cpu(cfg->sdio_block_size); + PRINTM(MMSG, "SDIO rx aggr: %d block_size=%d\n", cfg->enable, + pmadapter->pcard_sd->sdio_rx_block_size); + if (!pmadapter->pcard_sd->sdio_rx_block_size) + pmadapter->pcard_sd->sdio_rx_aggr_enable = MFALSE; + if (pmadapter->pcard_sd->sdio_rx_aggr_enable) { + pmadapter->pcard_sd->max_sp_rx_size = SDIO_CMD53_MAX_SIZE; + wlan_re_alloc_sdio_rx_mpa_buffer(pmadapter); + } + return MLAN_STATUS_SUCCESS; +} +#endif + +/** + * @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(pmlan_private pmpriv, HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action, t_u32 cmd_oid, + 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 = 0; + t_u32 data_offset; + t_u8 *temp_pcmd = (t_u8 *)pcmd; + + ENTER(); + + data_offset = S_DS_GEN + sizeof(HostCmd_DS_802_11_CFG_DATA); + + if ((cmd_oid == OID_TYPE_CAL) && (pmadapter->pcal_data) && + (pmadapter->cal_data_len > 0)) { + len = wlan_parse_cal_cfg((t_u8 *)pmadapter->pcal_data, + pmadapter->cal_data_len, + (t_u8 *)(temp_pcmd + data_offset)); + } + + pcfg_data->action = cmd_action; + pcfg_data->type = cmd_oid; + pcfg_data->data_len = len; + + pcmd->command = HostCmd_CMD_CFG_DATA; + pcmd->size = pcfg_data->data_len + data_offset; + + pcmd->command = wlan_cpu_to_le16(pcmd->command); + pcmd->size = wlan_cpu_to_le16(pcmd->size); + + pcfg_data->action = wlan_cpu_to_le16(pcfg_data->action); + pcfg_data->type = wlan_cpu_to_le16(pcfg_data->type); + pcfg_data->data_len = wlan_cpu_to_le16(pcfg_data->data_len); + + 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; + t_u8 event_buf[100]; + mlan_cmdresp_event *pevent = (mlan_cmdresp_event *)event_buf; + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_CFG_DATA *pcfg_data = &resp->params.cfg_data; + t_u16 action; + t_u16 type; + + ENTER(); + + if (resp->result != HostCmd_RESULT_OK) { + PRINTM(MERROR, "CFG data cmd resp failed\n"); + ret = MLAN_STATUS_FAILURE; + } + + if (!pmadapter->pdpd_data && + (pmadapter->dpd_data_len == UNKNOW_DPD_LENGTH) && + pmadapter->hw_status == WlanHardwareStatusGetHwSpec) { + action = wlan_le16_to_cpu(pcfg_data->action); + type = wlan_le16_to_cpu(pcfg_data->type); + if (action == HostCmd_ACT_GEN_GET && (type == OID_TYPE_DPD)) { + pcfg_data->action = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_STORE_HOST_CMD_RESP; + pevent->resp = (t_u8 *)resp; + pevent->event_len = wlan_le16_to_cpu(resp->size); + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_STORE_HOST_CMD_RESP, + (mlan_event *)pevent); + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of mac_control. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action Command action + * @param pdata_buf A pointer to command information buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_mac_control(pmlan_private pmpriv, HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_MAC_CONTROL *pmac = &pcmd->params.mac_ctrl; + t_u32 action = *((t_u32 *)pdata_buf); + + ENTER(); + + if (cmd_action != HostCmd_ACT_GEN_SET) { + PRINTM(MERROR, "wlan_cmd_mac_control(): support SET only.\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_MAC_CONTROL); + pcmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_MAC_CONTROL) + S_DS_GEN); + pmac->action = wlan_cpu_to_le32(action); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of mac_control + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_mac_control(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + ENTER(); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of get_hw_spec + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ret_get_hw_spec(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + t_void *pioctl_buf) +{ + HostCmd_DS_GET_HW_SPEC *hw_spec = &resp->params.hw_spec; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 i; + t_u16 left_len; + t_u16 tlv_type = 0; + t_u16 tlv_len = 0; + MrvlIEtypes_fw_ver_info_t *api_rev = MNULL; + t_u16 api_id = 0; + MrvlIEtypesHeader_t *tlv = MNULL; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + MrvlIEtypes_Max_Conn_t *tlv_max_conn = MNULL; + MrvlIEtypes_Extension_t *ext_tlv = MNULL; + MrvlIEtypes_fw_cap_info_t *fw_cap_tlv = MNULL; + + ENTER(); + + pmadapter->fw_cap_info = wlan_le32_to_cpu(hw_spec->fw_cap_info); + pmadapter->fw_cap_info &= pmadapter->init_para.dev_cap_mask; + + PRINTM(MMSG, "fw_cap_info=0x%x, dev_cap_mask=0x%x\n", + wlan_le32_to_cpu(hw_spec->fw_cap_info), + pmadapter->init_para.dev_cap_mask); +#ifdef STA_SUPPORT + if (IS_SUPPORT_MULTI_BANDS(pmadapter)) + pmadapter->fw_bands = (t_u8)GET_FW_DEFAULT_BANDS(pmadapter); + else + pmadapter->fw_bands = BAND_B; + + if ((pmadapter->fw_bands & BAND_A) && (pmadapter->fw_bands & BAND_GN)) + pmadapter->fw_bands |= BAND_AN; + if (!(pmadapter->fw_bands & BAND_G) && (pmadapter->fw_bands & BAND_GN)) + pmadapter->fw_bands &= ~BAND_GN; + + 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_AN) { + pmadapter->config_bands |= BAND_AN; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->config_bands |= + BAND_AN; + } + } + if (pmadapter->fw_bands & BAND_AAC) { + pmadapter->config_bands |= BAND_AAC; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->config_bands |= + BAND_AAC; + } + } + if (pmadapter->fw_bands & BAND_GAC) { + pmadapter->config_bands |= BAND_GAC; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->config_bands |= + BAND_GAC; + } + } + pmadapter->adhoc_start_band = BAND_A; + pmpriv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A; + } else if (pmadapter->fw_bands & BAND_G) { + pmadapter->adhoc_start_band = BAND_G | BAND_B; + pmpriv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + } else if (pmadapter->fw_bands & BAND_B) { + pmadapter->adhoc_start_band = BAND_B; + pmpriv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + } +#endif /* STA_SUPPORT */ + + pmadapter->fw_release_number = + wlan_le32_to_cpu(hw_spec->fw_release_number); + pmadapter->number_of_antenna = + wlan_le16_to_cpu(hw_spec->number_of_antenna) & 0x00ff; + pmadapter->antinfo = + (wlan_le16_to_cpu(hw_spec->number_of_antenna) & 0xff00) >> 8; + PRINTM(MCMND, "num_ant=%d, antinfo=0x%x\n", + pmadapter->number_of_antenna, pmadapter->antinfo); + + PRINTM(MINFO, "GET_HW_SPEC: fw_release_number- 0x%X\n", + pmadapter->fw_release_number); + PRINTM(MINFO, "GET_HW_SPEC: Permanent addr- " MACSTR "\n", + MAC2STR(hw_spec->permanent_addr)); + PRINTM(MINFO, "GET_HW_SPEC: hw_if_version=0x%X version=0x%X\n", + wlan_le16_to_cpu(hw_spec->hw_if_version), + wlan_le16_to_cpu(hw_spec->version)); + + if (pmpriv->curr_addr[0] == 0xff) + memmove(pmadapter, pmpriv->curr_addr, hw_spec->permanent_addr, + MLAN_MAC_ADDR_LENGTH); + memmove(pmadapter, pmadapter->permanent_addr, hw_spec->permanent_addr, + MLAN_MAC_ADDR_LENGTH); + pmadapter->hw_dot_11n_dev_cap = + wlan_le32_to_cpu(hw_spec->dot_11n_dev_cap); + pmadapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_update_11n_cap(pmadapter->priv[i]); + } + + wlan_show_dot11ndevcap(pmadapter, pmadapter->hw_dot_11n_dev_cap); + wlan_show_devmcssupport(pmadapter, pmadapter->hw_dev_mcs_support); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + pmadapter->user_htstream = pmadapter->hw_dev_mcs_support; + /** separate stream config for 2.4G and 5G, will be changed according to + * antenna cfg*/ + if (pmadapter->fw_bands & BAND_A) + pmadapter->user_htstream |= (pmadapter->user_htstream << 8); + PRINTM(MCMND, "user_htstream=0x%x\n", pmadapter->user_htstream); +#endif + + if (ISSUPP_BEAMFORMING(pmadapter->hw_dot_11n_dev_cap)) { + PRINTM(MCMND, "Enable Beamforming\n"); + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->tx_bf_cap = + pmadapter->pcard_info + ->default_11n_tx_bf_cap; + } + } + pmadapter->hw_dot_11ac_dev_cap = + wlan_le32_to_cpu(hw_spec->Dot11acDevCap); + pmadapter->hw_dot_11ac_mcs_support = + wlan_le32_to_cpu(hw_spec->Dot11acMcsSupport); + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_update_11ac_cap(pmadapter->priv[i]); + } + wlan_show_dot11acdevcap(pmadapter, pmadapter->hw_dot_11ac_dev_cap); + wlan_show_dot11acmcssupport(pmadapter, + pmadapter->hw_dot_11ac_mcs_support); + +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + pmadapter->pcard_sd->mp_end_port = + wlan_le16_to_cpu(hw_spec->mp_end_port); + + for (i = 1; i <= (unsigned)(MAX_PORT - + pmadapter->pcard_sd->mp_end_port); + i++) + pmadapter->pcard_sd->mp_data_port_mask &= + ~(1 << (MAX_PORT - i)); + } +#endif + + pmadapter->max_mgmt_ie_index = + wlan_le16_to_cpu(hw_spec->mgmt_buf_count); + PRINTM(MCMND, "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->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; + + pmadapter->region_code = wlan_le16_to_cpu(hw_spec->region_code); + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (pmadapter->region_code == region_code_index[i]) + break; + } + /* If it's unidentified region code, use the default */ + if (i >= MRVDRV_MAX_REGION_CODE) { + pmadapter->region_code = MRVDRV_DEFAULT_REGION_CODE; + PRINTM(MWARN, + "unidentified region code, use the default (0x%02x)\n", + MRVDRV_DEFAULT_REGION_CODE); + } + /* Synchronize CFP code with region code */ + pmadapter->cfp_code_bg = pmadapter->region_code; + pmadapter->cfp_code_a = pmadapter->region_code; + + if (pmadapter->fw_cap_info & ENHANCE_EXT_SCAN_ENABLE) + pmadapter->ext_scan_enh = MTRUE; + +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + if ((pmadapter->fw_cap_info & SDIO_SP_RX_AGGR_ENABLE) && + pmadapter->pcard_sd->sdio_rx_aggr_enable) { + t_u8 sdio_sp_rx_aggr = MTRUE; + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_SDIO_SP_RX_AGGR_CFG, + HostCmd_ACT_GEN_SET, 0, MNULL, + &sdio_sp_rx_aggr); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else { + pmadapter->pcard_sd->sdio_rx_aggr_enable = MFALSE; + PRINTM(MCMND, "FW: SDIO rx aggr disabled 0x%x\n", + pmadapter->fw_cap_info); + } + } +#endif + + if (wlan_set_regiontable(pmpriv, (t_u8)pmadapter->region_code, + pmadapter->fw_bands)) { + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } +#ifdef STA_SUPPORT + if (wlan_11d_set_universaltable(pmpriv, pmadapter->fw_bands)) { + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif /* STA_SUPPORT */ + if (pmadapter->fw_cap_info & FW_CAPINFO_ECSA) { + t_u8 ecsa_enable = MTRUE; + pmadapter->ecsa_enable = MTRUE; + PRINTM(MCMND, "pmadapter->ecsa_enable=%d\n", + pmadapter->ecsa_enable); + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, ECSAEnable_i, MNULL, + &ecsa_enable); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (pmadapter->fw_cap_info & FW_CAPINFO_GET_LOG) { + pmadapter->getlog_enable = MTRUE; + PRINTM(MCMND, "pmadapter->getlog_enable=%d\n", + pmadapter->getlog_enable); + } + + left_len = resp->size - sizeof(HostCmd_DS_GET_HW_SPEC) - S_DS_GEN; + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)hw_spec + + sizeof(HostCmd_DS_GET_HW_SPEC)); + while (left_len > sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + switch (tlv_type) { + case TLV_TYPE_FW_VER_INFO: + api_rev = (MrvlIEtypes_fw_ver_info_t *)tlv; + api_id = wlan_le16_to_cpu(api_rev->api_id); + switch (api_id) { + case FW_API_VER_ID: + pmadapter->fw_ver = api_rev->major_ver; + pmadapter->fw_min_ver = api_rev->minor_ver; + PRINTM(MCMND, "fw ver=%d.%d\n", + api_rev->major_ver, api_rev->minor_ver); + break; + case UAP_FW_API_VER_ID: + pmadapter->uap_fw_ver = api_rev->major_ver; + PRINTM(MCMND, "uap fw ver=%d.%d\n", + api_rev->major_ver, api_rev->minor_ver); + break; + case CHANRPT_API_VER_ID: + pmadapter->chanrpt_param_bandcfg = + api_rev->minor_ver; + PRINTM(MCMND, "chanrpt api ver=%d.%d\n", + api_rev->major_ver, api_rev->minor_ver); + break; + default: + break; + } + break; + case TLV_TYPE_MAX_CONN: + tlv_max_conn = (MrvlIEtypes_Max_Conn_t *)tlv; + PRINTM(MMSG, "max_p2p_conn = %d, max_sta_conn = %d\n", + tlv_max_conn->max_p2p_conn, + tlv_max_conn->max_sta_conn); + if (tlv_max_conn->max_p2p_conn && + tlv_max_conn->max_sta_conn) + pmadapter->max_sta_conn = + MIN(tlv_max_conn->max_sta_conn, + tlv_max_conn->max_p2p_conn); + else if (tlv_max_conn->max_sta_conn) + pmadapter->max_sta_conn = + tlv_max_conn->max_sta_conn; + else if (tlv_max_conn->max_p2p_conn) + pmadapter->max_sta_conn = + tlv_max_conn->max_p2p_conn; + else + pmadapter->max_sta_conn = 0; + break; + case TLV_TYPE_EXTENSION_ID: + ext_tlv = (MrvlIEtypes_Extension_t *)tlv; + if (ext_tlv->ext_id == HE_CAPABILITY) { + ext_tlv->type = tlv_type; + ext_tlv->len = tlv_len; + wlan_update_11ax_cap( + pmadapter, + (MrvlIEtypes_Extension_t *)ext_tlv); + } + + break; + case TLV_TYPE_FW_CAP_INFO: + fw_cap_tlv = (MrvlIEtypes_fw_cap_info_t *)tlv; + pmadapter->fw_cap_info = + wlan_le32_to_cpu(fw_cap_tlv->fw_cap_info); + pmadapter->fw_cap_ext = + wlan_le32_to_cpu(fw_cap_tlv->fw_cap_ext); + PRINTM(MCMND, "fw_cap_info=0x%x fw_cap_ext=0x%x\n", + pmadapter->fw_cap_info, pmadapter->fw_cap_ext); + break; + default: + break; + } + left_len -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of radio_control. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_802_11_radio_control(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, 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_ext(pmpriv->adapter, &radio_ctl, pdata_buf, sizeof(t_u32), + sizeof(radio_ctl)); + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_RADIO_CONTROL *pradio_ctrl = + (HostCmd_DS_802_11_RADIO_CONTROL *)&resp->params.radio; + mlan_ds_radio_cfg *radio_cfg = MNULL; + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + pmadapter->radio_on = wlan_le16_to_cpu(pradio_ctrl->control); + if (pioctl_buf) { + radio_cfg = (mlan_ds_radio_cfg *)pioctl_buf->pbuf; + radio_cfg->param.radio_on_off = (t_u32)pmadapter->radio_on; + pioctl_buf->data_read_written = sizeof(mlan_ds_radio_cfg); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of remain_on_channel. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_remain_on_channel(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_REMAIN_ON_CHANNEL *remain_channel = + &resp->params.remain_on_chan; + mlan_ds_radio_cfg *radio_cfg = MNULL; + + ENTER(); + if (pioctl_buf) { + radio_cfg = (mlan_ds_radio_cfg *)pioctl_buf->pbuf; + radio_cfg->param.remain_chan.status = remain_channel->status; + radio_cfg->param.remain_chan.bandcfg = remain_channel->bandcfg; + radio_cfg->param.remain_chan.channel = remain_channel->channel; + radio_cfg->param.remain_chan.remain_period = + wlan_le32_to_cpu(remain_channel->remain_period); + pioctl_buf->data_read_written = sizeof(mlan_ds_radio_cfg); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief This function prepares command of wifi direct mode. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_wifi_direct_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_WIFI_DIRECT_MODE *wfd_mode = &resp->params.wifi_direct_mode; + mlan_ds_bss *bss = MNULL; + + ENTER(); + if (pioctl_buf) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + bss->param.wfd_mode = wlan_le16_to_cpu(wfd_mode->mode); + pioctl_buf->data_read_written = sizeof(mlan_ds_bss); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of p2p_params_config. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_p2p_params_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG *p2p_config = + &cmd->params.p2p_params_config; + mlan_ds_wifi_direct_config *cfg = + (mlan_ds_wifi_direct_config *)pdata_buf; + MrvlIEtypes_NoA_setting_t *pnoa_tlv = MNULL; + MrvlIEtypes_OPP_PS_setting_t *popp_ps_tlv = MNULL; + t_u8 *tlv = MNULL; + ENTER(); + + cmd->size = sizeof(HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG) + S_DS_GEN; + cmd->command = wlan_cpu_to_le16(HOST_CMD_P2P_PARAMS_CONFIG); + p2p_config->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + tlv = (t_u8 *)p2p_config->tlv_buf; + if (cfg->flags & WIFI_DIRECT_NOA) { + pnoa_tlv = (MrvlIEtypes_NoA_setting_t *)tlv; + pnoa_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WIFI_DIRECT_NOA); + pnoa_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_NoA_setting_t) - + sizeof(MrvlIEtypesHeader_t)); + pnoa_tlv->enable = cfg->noa_enable; + pnoa_tlv->index = wlan_cpu_to_le16(cfg->index); + pnoa_tlv->noa_count = cfg->noa_count; + pnoa_tlv->noa_duration = + wlan_cpu_to_le32(cfg->noa_duration); + pnoa_tlv->noa_interval = + wlan_cpu_to_le32(cfg->noa_interval); + cmd->size += sizeof(MrvlIEtypes_NoA_setting_t); + tlv += sizeof(MrvlIEtypes_NoA_setting_t); + PRINTM(MCMND, + "Set NOA: enable=%d index=%d, count=%d, duration=%d interval=%d\n", + cfg->noa_enable, cfg->index, cfg->noa_count, + (int)cfg->noa_duration, (int)cfg->noa_interval); + } + if (cfg->flags & WIFI_DIRECT_OPP_PS) { + popp_ps_tlv = (MrvlIEtypes_OPP_PS_setting_t *)tlv; + popp_ps_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WIFI_DIRECT_OPP_PS); + popp_ps_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_OPP_PS_setting_t) - + sizeof(MrvlIEtypesHeader_t)); + + popp_ps_tlv->enable = cfg->ct_window; + popp_ps_tlv->enable |= cfg->opp_ps_enable << 7; + cmd->size += sizeof(MrvlIEtypes_OPP_PS_setting_t); + PRINTM(MCMND, "Set OPP_PS: enable=%d ct_win=%d\n", + cfg->opp_ps_enable, cfg->ct_window); + } + } else if (cmd_action == HostCmd_ACT_GEN_GET) { + tlv = (t_u8 *)p2p_config->tlv_buf; + if (cfg->flags & WIFI_DIRECT_NOA) { + pnoa_tlv = (MrvlIEtypes_NoA_setting_t *)tlv; + pnoa_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WIFI_DIRECT_NOA); + pnoa_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_NoA_setting_t) - + sizeof(MrvlIEtypesHeader_t)); + cmd->size += sizeof(MrvlIEtypes_NoA_setting_t); + tlv += sizeof(MrvlIEtypes_NoA_setting_t); + } + + if (cfg->flags & WIFI_DIRECT_OPP_PS) { + popp_ps_tlv = (MrvlIEtypes_OPP_PS_setting_t *)tlv; + popp_ps_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WIFI_DIRECT_OPP_PS); + popp_ps_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_OPP_PS_setting_t) - + sizeof(MrvlIEtypesHeader_t)); + cmd->size += sizeof(MrvlIEtypes_OPP_PS_setting_t); + } + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of p2p_params_config + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_p2p_params_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG *p2p_config = + &resp->params.p2p_params_config; + mlan_ds_misc_cfg *cfg = MNULL; + MrvlIEtypes_NoA_setting_t *pnoa_tlv = MNULL; + MrvlIEtypes_OPP_PS_setting_t *popp_ps_tlv = MNULL; + MrvlIEtypesHeader_t *tlv = MNULL; + t_u16 tlv_buf_left = 0; + t_u16 tlv_type = 0; + t_u16 tlv_len = 0; + + ENTER(); + if (wlan_le16_to_cpu(p2p_config->action) == HostCmd_ACT_GEN_GET) { + if (pioctl_buf) { + cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + tlv = (MrvlIEtypesHeader_t *)(p2p_config->tlv_buf); + tlv_buf_left = + resp->size - + (sizeof(HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG) + + S_DS_GEN); + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if (tlv_buf_left < + (tlv_len + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, + "Error processing p2p param config TLVs, bytes left < TLV length\n"); + break; + } + switch (tlv_type) { + case TLV_TYPE_WIFI_DIRECT_NOA: + pnoa_tlv = (MrvlIEtypes_NoA_setting_t *) + tlv; + cfg->param.p2p_config.flags |= + WIFI_DIRECT_NOA; + cfg->param.p2p_config.noa_enable = + pnoa_tlv->enable; + cfg->param.p2p_config.index = + wlan_le16_to_cpu( + pnoa_tlv->index); + cfg->param.p2p_config.noa_count = + pnoa_tlv->noa_count; + cfg->param.p2p_config.noa_duration = + wlan_le32_to_cpu( + pnoa_tlv->noa_duration); + cfg->param.p2p_config.noa_interval = + wlan_le32_to_cpu( + pnoa_tlv->noa_interval); + PRINTM(MCMND, + "Get NOA: enable=%d index=%d, count=%d, duration=%d interval=%d\n", + cfg->param.p2p_config.noa_enable, + cfg->param.p2p_config.index, + cfg->param.p2p_config.noa_count, + (int)cfg->param.p2p_config + .noa_duration, + (int)cfg->param.p2p_config + .noa_interval); + break; + case TLV_TYPE_WIFI_DIRECT_OPP_PS: + popp_ps_tlv = + (MrvlIEtypes_OPP_PS_setting_t *) + tlv; + cfg->param.p2p_config.flags |= + WIFI_DIRECT_OPP_PS; + cfg->param.p2p_config.opp_ps_enable = + (popp_ps_tlv->enable & 0x80) >> + 7; + cfg->param.p2p_config.ct_window = + popp_ps_tlv->enable & 0x7f; + PRINTM(MCMND, + "Get OPP_PS: enable=%d ct_win=%d\n", + cfg->param.p2p_config + .opp_ps_enable, + cfg->param.p2p_config.ct_window); + break; + default: + break; + } + tlv_buf_left -= + tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t + *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + pioctl_buf->data_read_written = + sizeof(mlan_ds_wifi_direct_config); + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function prepares command of mimo switch configuration. + * + * @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_802_11_mimo_switch(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf) +{ + HostCmd_DS_MIMO_SWITCH *mimo_switch_cmd = &cmd->params.mimo_switch; + mlan_ds_mimo_switch *pmimo_switch = (mlan_ds_mimo_switch *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_MIMO_SWITCH); + cmd->size = + wlan_cpu_to_le16((sizeof(HostCmd_DS_MIMO_SWITCH)) + S_DS_GEN); + mimo_switch_cmd->txpath_antmode = pmimo_switch->txpath_antmode; + mimo_switch_cmd->rxpath_antmode = pmimo_switch->rxpath_antmode; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of hs wakeup reason. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_hs_wakeup_reason(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf) +{ + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_HS_WAKEUP_REASON); + cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_HS_WAKEUP_REASON)) + + S_DS_GEN); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * hs wakeup reason + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_hs_wakeup_reason(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_HS_WAKEUP_REASON *hs_wakeup_reason = + (HostCmd_DS_HS_WAKEUP_REASON *)&resp->params.hs_wakeup_reason; + mlan_ds_pm_cfg *pm_cfg = MNULL; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_buf->pbuf; + pm_cfg->param.wakeup_reason.hs_wakeup_reason = + wlan_le16_to_cpu(hs_wakeup_reason->wakeup_reason); + pioctl_buf->data_read_written = sizeof(mlan_ds_pm_cfg); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of tx_rx_pkt_stats + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @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 + */ +mlan_status wlan_cmd_tx_rx_pkt_stats(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + pmlan_ioctl_req pioctl_buf, + t_void *pdata_buf) +{ + HostCmd_DS_TX_RX_HISTOGRAM *ptx_rx_histogram = + &cmd->params.tx_rx_histogram; + mlan_ds_misc_tx_rx_histogram *ptx_rx_pkt_stats = + (mlan_ds_misc_tx_rx_histogram *)pdata_buf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!ptx_rx_pkt_stats) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cmd->command = wlan_cpu_to_le16(HOST_CMD_TX_RX_PKT_STATS); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_TX_RX_HISTOGRAM) + S_DS_GEN); + + ptx_rx_histogram->enable = ptx_rx_pkt_stats->enable; + ptx_rx_histogram->action = wlan_cpu_to_le16(ptx_rx_pkt_stats->action); +done: + LEAVE(); + return ret; +} +/** + * @brief This function handles the command response of tx_rx_pkt_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_tx_rx_pkt_stats(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_TX_RX_HISTOGRAM *ptx_rx_histogram = + &resp->params.tx_rx_histogram; + mlan_ds_misc_cfg *info; + t_u16 cmdsize = wlan_le16_to_cpu(resp->size), length; + t_u32 *pos, count = 0; + + ENTER(); + + if (pioctl_buf) { + ptx_rx_histogram->action = + wlan_le16_to_cpu(ptx_rx_histogram->action); + info = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + length = + cmdsize - S_DS_GEN - sizeof(HostCmd_DS_TX_RX_HISTOGRAM); + if (length > 0) { + info->param.tx_rx_histogram.size = length; + memcpy_ext(pmpriv->adapter, + info->param.tx_rx_histogram.value, + (t_u8 *)ptx_rx_histogram + + sizeof(HostCmd_DS_TX_RX_HISTOGRAM), + length, info->param.tx_rx_histogram.size); + pos = (t_u32 *)info->param.tx_rx_histogram.value; + while (length - 4 * count) { + *pos = wlan_le32_to_cpu(*pos); + pos += 4; + count++; + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/* + * @brief This function prepares command of cwmode control. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_cw_mode_ctrl(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_CW_MODE_CTRL *cwmode_ctrl = &cmd->params.cwmode; + mlan_ds_cw_mode_ctrl *cw_mode = (mlan_ds_cw_mode_ctrl *)pdata_buf; + ENTER(); + cmd->size = + wlan_cpu_to_le16((sizeof(HostCmd_DS_CW_MODE_CTRL)) + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_CW_MODE_CTRL); + cwmode_ctrl->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + cwmode_ctrl->mode = cw_mode->mode; + cwmode_ctrl->channel = cw_mode->channel; + cwmode_ctrl->chanInfo = cw_mode->chanInfo; + cwmode_ctrl->txPower = wlan_cpu_to_le16(cw_mode->txPower); + cwmode_ctrl->rateInfo = wlan_cpu_to_le32(cw_mode->rateInfo); + cwmode_ctrl->pktLength = wlan_cpu_to_le16(cw_mode->pktLength); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/* + * @brief This function handles the command response of cwmode_ctrl + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_cw_mode_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CW_MODE_CTRL *cwmode_resp = &resp->params.cwmode; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + if (pioctl_buf) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc->param.cwmode.mode = cwmode_resp->mode; + misc->param.cwmode.channel = cwmode_resp->channel; + misc->param.cwmode.chanInfo = cwmode_resp->chanInfo; + misc->param.cwmode.txPower = + wlan_le16_to_cpu(cwmode_resp->txPower); + misc->param.cwmode.rateInfo = + wlan_le32_to_cpu(cwmode_resp->rateInfo); + ; + misc->param.cwmode.pktLength = + wlan_le16_to_cpu(cwmode_resp->pktLength); + ; + pioctl_buf->data_read_written = sizeof(mlan_ds_misc_cfg); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of rf_antenna. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_802_11_rf_antenna(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, 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; + typedef struct _HostCmd_DS_802_11_RF_ANTENNA_1X1 { + /** Action */ + t_u16 action; + /** Antenna or 0xffff (diversity) */ + t_u16 antenna_mode; + /** Evaluate time */ + t_u16 evaluate_time; + /** Current antenna */ + t_u16 current_antenna; + } HostCmd_DS_802_11_RF_ANTENNA_1X1; + HostCmd_DS_802_11_RF_ANTENNA_1X1 *pantenna_1x1 = + (HostCmd_DS_802_11_RF_ANTENNA_1X1 *)&cmd->params.antenna; + mlan_ds_ant_cfg_1x1 *ant_cfg_1x1 = (mlan_ds_ant_cfg_1x1 *)pdata_buf; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_RF_ANTENNA); + if (!IS_STREAM_2X2(pmpriv->adapter->feature_control)) + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_802_11_RF_ANTENNA_1X1) + S_DS_GEN); + else + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_802_11_RF_ANTENNA) + S_DS_GEN); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + if (IS_STREAM_2X2(pmpriv->adapter->feature_control)) { + 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_1x1->action = + wlan_cpu_to_le16(HostCmd_ACT_SET_BOTH); + pantenna_1x1->antenna_mode = + wlan_cpu_to_le16((t_u16)ant_cfg_1x1->antenna); + pantenna_1x1->evaluate_time = wlan_cpu_to_le16( + (t_u16)ant_cfg_1x1->evaluate_time); + } + } else { + if (IS_STREAM_2X2(pmpriv->adapter->feature_control)) { + pantenna->action_tx = + wlan_cpu_to_le16(HostCmd_ACT_GET_TX); + pantenna->action_rx = + wlan_cpu_to_le16(HostCmd_ACT_GET_RX); + } else { + pantenna_1x1->action = + wlan_cpu_to_le16(HostCmd_ACT_GET_BOTH); + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of rf_antenna + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_802_11_rf_antenna(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + mlan_adapter *pmadapter = pmpriv->adapter; +#endif + typedef struct _HostCmd_DS_802_11_RF_ANTENNA_1X1 { + /** Action */ + t_u16 action; + /** Antenna or 0xffff (diversity) */ + t_u16 antenna_mode; + /** Evaluate time */ + t_u16 evaluate_time; + /** Current antenna */ + t_u16 current_antenna; + } HostCmd_DS_802_11_RF_ANTENNA_1X1; + HostCmd_DS_802_11_RF_ANTENNA_1X1 *pantenna_1x1 = + (HostCmd_DS_802_11_RF_ANTENNA_1X1 *)&resp->params.antenna; + t_u16 ant_mode = wlan_le16_to_cpu(pantenna_1x1->antenna_mode); + t_u16 evaluate_time = wlan_le16_to_cpu(pantenna_1x1->evaluate_time); + t_u16 current_antenna = wlan_le16_to_cpu(pantenna_1x1->current_antenna); + mlan_ds_radio_cfg *radio = MNULL; + + ENTER(); + + if (IS_STREAM_2X2(pmpriv->adapter->feature_control)) { + PRINTM(MCMND, + "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 defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type)) { + tx_ant_mode &= 0x0303; + rx_ant_mode &= 0x0303; + /** 2G antcfg TX */ + if (tx_ant_mode & 0x00FF) { + pmadapter->user_htstream &= ~0xF0; + pmadapter->user_htstream |= + (bitcount(tx_ant_mode & 0x00FF) << 4); + } + /* 5G antcfg tx */ + if (tx_ant_mode & 0xFF00) { + pmadapter->user_htstream &= ~0xF000; + pmadapter->user_htstream |= + (bitcount(tx_ant_mode & 0xFF00) << 12); + } + /* 2G antcfg RX */ + if (rx_ant_mode & 0x00FF) { + pmadapter->user_htstream &= ~0xF; + pmadapter->user_htstream |= + bitcount(rx_ant_mode & 0x00FF); + } + /* 5G antcfg RX */ + if (rx_ant_mode & 0xFF00) { + pmadapter->user_htstream &= ~0xF00; + pmadapter->user_htstream |= + (bitcount(rx_ant_mode & 0xFF00) << 8); + } + PRINTM(MCMND, + "user_htstream=0x%x, tx_antenna=0x%x rx_antenna=0x%x\n", + pmadapter->user_htstream, tx_ant_mode, + rx_ant_mode); + } +#endif + } else + PRINTM(MINFO, + "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x, Evaluate time = %d, Current antenna = %d\n", + wlan_le16_to_cpu(pantenna_1x1->action), ant_mode, + evaluate_time, current_antenna); + + if (pioctl_buf) { + radio = (mlan_ds_radio_cfg *)pioctl_buf->pbuf; + if (IS_STREAM_2X2(pmpriv->adapter->feature_control)) { + radio->param.ant_cfg.tx_antenna = tx_ant_mode; + radio->param.ant_cfg.rx_antenna = rx_ant_mode; + } else { + radio->param.ant_cfg_1x1.antenna = ant_mode; + radio->param.ant_cfg_1x1.evaluate_time = evaluate_time; + radio->param.ant_cfg_1x1.current_antenna = + current_antenna; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of reg_access. + * + * @param priv A pointer to mlan_priv register. + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_reg_access(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + mlan_ds_reg_rw *reg_rw; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + MrvlIEtypes_Reg_type_t *tlv; + mlan_adapter *pmadapter = pmpriv->adapter; +#endif + + 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); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + if ((reg_rw->type == MLAN_REG_MAC2) && + (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type))) { + tlv = (MrvlIEtypes_Reg_type_t + *)((t_u8 *)cmd + + sizeof(HostCmd_DS_MAC_REG_ACCESS) + + S_DS_GEN); + tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_REG_ACCESS_CTRL); + tlv->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv->type = MLAN_REG_MAC2; + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_MAC_REG_ACCESS) + S_DS_GEN + + sizeof(MrvlIEtypes_Reg_type_t)); + } +#endif + 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; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + if ((reg_rw->type == MLAN_REG_BBP2) && + (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type))) { + tlv = (MrvlIEtypes_Reg_type_t + *)((t_u8 *)cmd + + sizeof(HostCmd_DS_BBP_REG_ACCESS) + + S_DS_GEN); + tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_REG_ACCESS_CTRL); + tlv->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv->type = MLAN_REG_BBP2; + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_BBP_REG_ACCESS) + S_DS_GEN + + sizeof(MrvlIEtypes_Reg_type_t)); + } +#endif + 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; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + if ((reg_rw->type == MLAN_REG_RF2) && + (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type))) { + tlv = (MrvlIEtypes_Reg_type_t + *)((t_u8 *)cmd + + sizeof(HostCmd_DS_RF_REG_ACCESS) + + S_DS_GEN); + tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_REG_ACCESS_CTRL); + tlv->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv->type = MLAN_REG_RF2; + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_RF_REG_ACCESS) + S_DS_GEN + + sizeof(MrvlIEtypes_Reg_type_t)); + } +#endif + break; + } + case HostCmd_CMD_CAU_REG_ACCESS: { + HostCmd_DS_RF_REG_ACCESS *cau_reg; + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_RF_REG_ACCESS) + + S_DS_GEN); + cau_reg = (HostCmd_DS_RF_REG_ACCESS *)&cmd->params.rf_reg; + cau_reg->action = wlan_cpu_to_le16(cmd_action); + cau_reg->offset = wlan_cpu_to_le16((t_u16)reg_rw->offset); + cau_reg->value = (t_u8)reg_rw->value; + break; + } + case HostCmd_CMD_TARGET_ACCESS: { + HostCmd_DS_TARGET_ACCESS *target; + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_TARGET_ACCESS) + + S_DS_GEN); + target = (HostCmd_DS_TARGET_ACCESS *)&cmd->params.target; + target->action = wlan_cpu_to_le16(cmd_action); + target->csu_target = wlan_cpu_to_le16(MLAN_CSU_TARGET_PSU); + target->address = wlan_cpu_to_le16((t_u16)reg_rw->offset); + target->data = (t_u8)reg_rw->value; + break; + } + case HostCmd_CMD_802_11_EEPROM_ACCESS: { + mlan_ds_read_eeprom *rd_eeprom = + (mlan_ds_read_eeprom *)pdata_buf; + HostCmd_DS_802_11_EEPROM_ACCESS *cmd_eeprom = + (HostCmd_DS_802_11_EEPROM_ACCESS *)&cmd->params.eeprom; + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_802_11_EEPROM_ACCESS) + S_DS_GEN); + cmd_eeprom->action = wlan_cpu_to_le16(cmd_action); + cmd_eeprom->offset = wlan_cpu_to_le16(rd_eeprom->offset); + cmd_eeprom->byte_count = + wlan_cpu_to_le16(rd_eeprom->byte_count); + cmd_eeprom->value = 0; + break; + } + case HostCmd_CMD_BCA_REG_ACCESS: { + HostCmd_DS_BCA_REG_ACCESS *bca_reg; + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_BCA_REG_ACCESS) + + S_DS_GEN); + bca_reg = (HostCmd_DS_BCA_REG_ACCESS *)&cmd->params.bca_reg; + bca_reg->action = wlan_cpu_to_le16(cmd_action); + bca_reg->offset = wlan_cpu_to_le16((t_u16)reg_rw->offset); + bca_reg->value = wlan_cpu_to_le32(reg_rw->value); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + if ((reg_rw->type == MLAN_REG_BCA2) && + (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type))) { + tlv = (MrvlIEtypes_Reg_type_t + *)((t_u8 *)cmd + + sizeof(HostCmd_DS_BCA_REG_ACCESS) + + S_DS_GEN); + tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_REG_ACCESS_CTRL); + tlv->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv->type = MLAN_REG_BCA2; + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_BCA_REG_ACCESS) + S_DS_GEN + + sizeof(MrvlIEtypes_Reg_type_t)); + } +#endif + break; + } + default: + LEAVE(); + return MLAN_STATUS_FAILURE; + } + cmd->command = wlan_cpu_to_le16(cmd->command); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of reg_access + * + * @param pmadapter A pointer to mlan_adapter structure + * @param type The type of reg access (MAC, BBP or RF) + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ret_reg_access(mlan_adapter *pmadapter, t_u16 type, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_ds_reg_rw *reg_rw = MNULL; + + ENTER(); + + if (pioctl_buf) { + reg_mem = (mlan_ds_reg_mem *)pioctl_buf->pbuf; + reg_rw = ®_mem->param.reg_rw; + switch (type) { + case HostCmd_CMD_MAC_REG_ACCESS: { + HostCmd_DS_MAC_REG_ACCESS *reg; + reg = (HostCmd_DS_MAC_REG_ACCESS *)&resp->params.mac_reg; + reg_rw->offset = (t_u32)wlan_le16_to_cpu(reg->offset); + reg_rw->value = wlan_le32_to_cpu(reg->value); + break; + } + case HostCmd_CMD_BBP_REG_ACCESS: { + HostCmd_DS_BBP_REG_ACCESS *reg; + reg = (HostCmd_DS_BBP_REG_ACCESS *)&resp->params.bbp_reg; + reg_rw->offset = (t_u32)wlan_le16_to_cpu(reg->offset); + reg_rw->value = (t_u32)reg->value; + break; + } + + case HostCmd_CMD_RF_REG_ACCESS: { + HostCmd_DS_RF_REG_ACCESS *reg; + reg = (HostCmd_DS_RF_REG_ACCESS *)&resp->params.rf_reg; + reg_rw->offset = (t_u32)wlan_le16_to_cpu(reg->offset); + reg_rw->value = (t_u32)reg->value; + break; + } + case HostCmd_CMD_CAU_REG_ACCESS: { + HostCmd_DS_RF_REG_ACCESS *reg; + reg = (HostCmd_DS_RF_REG_ACCESS *)&resp->params.rf_reg; + reg_rw->offset = (t_u32)wlan_le16_to_cpu(reg->offset); + reg_rw->value = (t_u32)reg->value; + break; + } + case HostCmd_CMD_TARGET_ACCESS: { + HostCmd_DS_TARGET_ACCESS *reg; + reg = (HostCmd_DS_TARGET_ACCESS *)&resp->params.target; + reg_rw->offset = (t_u32)wlan_le16_to_cpu(reg->address); + reg_rw->value = (t_u32)reg->data; + break; + } + case HostCmd_CMD_802_11_EEPROM_ACCESS: { + mlan_ds_read_eeprom *eeprom = ®_mem->param.rd_eeprom; + HostCmd_DS_802_11_EEPROM_ACCESS *cmd_eeprom = + (HostCmd_DS_802_11_EEPROM_ACCESS *)&resp->params + .eeprom; + cmd_eeprom->byte_count = + wlan_le16_to_cpu(cmd_eeprom->byte_count); + PRINTM(MINFO, "EEPROM read len=%x\n", + cmd_eeprom->byte_count); + if (eeprom->byte_count < cmd_eeprom->byte_count) { + eeprom->byte_count = 0; + PRINTM(MINFO, + "EEPROM read return length is too big\n"); + pioctl_buf->status_code = + MLAN_ERROR_CMD_RESP_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + eeprom->offset = wlan_le16_to_cpu(cmd_eeprom->offset); + eeprom->byte_count = cmd_eeprom->byte_count; + if (eeprom->byte_count > 0) { + memcpy_ext(pmadapter, &eeprom->value, + &cmd_eeprom->value, + eeprom->byte_count, MAX_EEPROM_DATA); + HEXDUMP("EEPROM", (char *)&eeprom->value, + MIN(MAX_EEPROM_DATA, + eeprom->byte_count)); + } + break; + } + case HostCmd_CMD_BCA_REG_ACCESS: { + HostCmd_DS_BCA_REG_ACCESS *reg; + reg = (HostCmd_DS_BCA_REG_ACCESS *)&resp->params.bca_reg; + reg_rw->offset = (t_u32)wlan_le16_to_cpu(reg->offset); + reg_rw->value = wlan_le32_to_cpu(reg->value); + break; + } + default: + pioctl_buf->status_code = MLAN_ERROR_CMD_RESP_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of mem_access. + * + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_mem_access(HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + mlan_ds_mem_rw *mem_rw = (mlan_ds_mem_rw *)pdata_buf; + HostCmd_DS_MEM_ACCESS *mem_access = + (HostCmd_DS_MEM_ACCESS *)&cmd->params.mem; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_MEM_ACCESS); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_MEM_ACCESS) + S_DS_GEN); + + mem_access->action = wlan_cpu_to_le16(cmd_action); + mem_access->addr = wlan_cpu_to_le32(mem_rw->addr); + mem_access->value = wlan_cpu_to_le32(mem_rw->value); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of mem_access + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_mem_access(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_ds_mem_rw *mem_rw = MNULL; + HostCmd_DS_MEM_ACCESS *mem = (HostCmd_DS_MEM_ACCESS *)&resp->params.mem; + + ENTER(); + + if (pioctl_buf) { + reg_mem = (mlan_ds_reg_mem *)pioctl_buf->pbuf; + mem_rw = ®_mem->param.mem_rw; + + mem_rw->addr = wlan_le32_to_cpu(mem->addr); + mem_rw->value = wlan_le32_to_cpu(mem->value); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * + * @brief This function handles coex events generated by firmware + * + * @param priv A pointer to mlan_private structure + * @param pevent A pointer to event buf + * + * @return N/A + */ +void wlan_bt_coex_wlan_param_update_event(pmlan_private priv, + pmlan_buffer pevent) +{ + pmlan_adapter pmadapter = priv->adapter; + MrvlIEtypesHeader_t *tlv = MNULL; + MrvlIETypes_BtCoexAggrWinSize_t *pCoexWinsize = MNULL; + MrvlIEtypes_BtCoexScanTime_t *pScantlv = MNULL; + t_s32 len = pevent->data_len - sizeof(t_u32); + t_u8 *pCurrent_ptr = pevent->pbuf + pevent->data_offset + sizeof(t_u32); + t_u16 tlv_type, tlv_len; + + ENTER(); + + while (len >= sizeof(MrvlIEtypesHeader_t)) { + tlv = (MrvlIEtypesHeader_t *)pCurrent_ptr; + tlv_len = wlan_le16_to_cpu(tlv->len); + tlv_type = wlan_le16_to_cpu(tlv->type); + if ((tlv_len + sizeof(MrvlIEtypesHeader_t)) > len) + break; + switch (tlv_type) { + case TLV_BTCOEX_WL_AGGR_WINSIZE: + pCoexWinsize = (MrvlIETypes_BtCoexAggrWinSize_t *)tlv; + pmadapter->coex_win_size = pCoexWinsize->coex_win_size; + pmadapter->coex_tx_win_size = pCoexWinsize->tx_win_size; + pmadapter->coex_rx_win_size = pCoexWinsize->rx_win_size; + wlan_coex_ampdu_rxwinsize(pmadapter); + wlan_update_ampdu_txwinsize(pmadapter); + break; + case TLV_BTCOEX_WL_SCANTIME: + pScantlv = (MrvlIEtypes_BtCoexScanTime_t *)tlv; + pmadapter->coex_scan = pScantlv->coex_scan; + pmadapter->coex_min_scan_time = + wlan_le16_to_cpu(pScantlv->min_scan_time); + pmadapter->coex_max_scan_time = + wlan_le16_to_cpu(pScantlv->max_scan_time); + break; + default: + break; + } + len -= tlv_len + sizeof(MrvlIEtypesHeader_t); + pCurrent_ptr += tlv_len + sizeof(MrvlIEtypesHeader_t); + } + PRINTM(MEVENT, + "coex_scan=%d min_scan=%d coex_win=%d, tx_win=%d rx_win=%d\n", + pmadapter->coex_scan, pmadapter->coex_min_scan_time, + pmadapter->coex_win_size, pmadapter->coex_tx_win_size, + pmadapter->coex_rx_win_size); + + LEAVE(); +} + +/** + * @brief This function prepares command of supplicant pmk + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_802_11_supplicant_pmk(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + MrvlIEtypes_PMK_t *ppmk_tlv = MNULL; + MrvlIEtypes_Passphrase_t *ppassphrase_tlv = MNULL; + MrvlIEtypes_SAE_Password_t *psae_password_tlv = MNULL; + MrvlIEtypes_SsIdParamSet_t *pssid_tlv = MNULL; + MrvlIEtypes_Bssid_t *pbssid_tlv = MNULL; + HostCmd_DS_802_11_SUPPLICANT_PMK *pesupplicant_psk = + &cmd->params.esupplicant_psk; + t_u8 *ptlv_buffer = (t_u8 *)pesupplicant_psk->tlv_buffer; + mlan_ds_sec_cfg *sec = (mlan_ds_sec_cfg *)pdata_buf; + mlan_ds_passphrase *psk = MNULL; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + t_u8 ssid_flag = 0, bssid_flag = 0, pmk_flag = 0, passphrase_flag = 0; + t_u8 sae_password_flag = 0; + + ENTER(); + psk = (mlan_ds_passphrase *)&sec->param.passphrase; + + /* + * 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=nxp + * 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=nxp/: 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=nxp + */ + + /* -1 is for t_u8 TlvBuffer[1] as this should not be included */ + cmd->size = sizeof(HostCmd_DS_802_11_SUPPLICANT_PMK) + S_DS_GEN - 1; + if (psk && memcmp(pmpriv->adapter, (t_u8 *)&psk->bssid, zero_mac, + sizeof(zero_mac))) { + pbssid_tlv = (MrvlIEtypes_Bssid_t *)ptlv_buffer; + pbssid_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_BSSID); + pbssid_tlv->header.len = MLAN_MAC_ADDR_LENGTH; + memcpy_ext(pmpriv->adapter, pbssid_tlv->bssid, + (t_u8 *)&psk->bssid, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + ptlv_buffer += + (pbssid_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (pbssid_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + pbssid_tlv->header.len = + wlan_cpu_to_le16(pbssid_tlv->header.len); + bssid_flag = 1; + } + if (psk && (psk->psk_type == MLAN_PSK_PMK)) { + ppmk_tlv = (MrvlIEtypes_PMK_t *)ptlv_buffer; + ppmk_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_PMK); + ppmk_tlv->header.len = MLAN_MAX_KEY_LENGTH; + memcpy_ext(pmpriv->adapter, ppmk_tlv->pmk, psk->psk.pmk.pmk, + MLAN_MAX_KEY_LENGTH, MLAN_MAX_KEY_LENGTH); + ptlv_buffer += + (ppmk_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (ppmk_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + ppmk_tlv->header.len = wlan_cpu_to_le16(ppmk_tlv->header.len); + pmk_flag = 1; + } + if (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_ext(pmpriv->adapter, (t_u8 *)pssid_tlv->ssid, + (t_u8 *)psk->ssid.ssid, psk->ssid.ssid_len, + MLAN_MAX_SSID_LENGTH); + ptlv_buffer += + (pssid_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + cmd->size += + (pssid_tlv->header.len + sizeof(MrvlIEtypesHeader_t)); + pssid_tlv->header.len = wlan_cpu_to_le16(pssid_tlv->header.len); + ssid_flag = 1; + } + if (psk->psk_type == MLAN_PSK_PASSPHRASE) { + ppassphrase_tlv = (MrvlIEtypes_Passphrase_t *)ptlv_buffer; + ppassphrase_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_PASSPHRASE); + ppassphrase_tlv->header.len = + (t_u16)MIN(MLAN_MAX_PASSPHRASE_LENGTH, + psk->psk.passphrase.passphrase_len); + memcpy_ext(pmpriv->adapter, ppassphrase_tlv->passphrase, + psk->psk.passphrase.passphrase, + psk->psk.passphrase.passphrase_len, + MLAN_MAX_PASSPHRASE_LENGTH); + ptlv_buffer += (ppassphrase_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += (ppassphrase_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + ppassphrase_tlv->header.len = + wlan_cpu_to_le16(ppassphrase_tlv->header.len); + passphrase_flag = 1; + } + if (psk->psk_type == MLAN_PSK_SAE_PASSWORD) { + psae_password_tlv = (MrvlIEtypes_SAE_Password_t *)ptlv_buffer; + psae_password_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_SAE_PASSWORD); + psae_password_tlv->header.len = + (t_u16)MIN(MLAN_MAX_SAE_PASSWORD_LENGTH, + psk->psk.sae_password.sae_password_len); + memcpy_ext(pmpriv->adapter, psae_password_tlv->sae_password, + psk->psk.sae_password.sae_password, + psk->psk.sae_password.sae_password_len, + MLAN_MAX_SAE_PASSWORD_LENGTH); + ptlv_buffer += (psae_password_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += (psae_password_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + psae_password_tlv->header.len = + wlan_cpu_to_le16(psae_password_tlv->header.len); + sae_password_flag = 1; + } + if ((cmd_action == HostCmd_ACT_GEN_SET) && + ((ssid_flag || bssid_flag) && (!pmk_flag && !passphrase_flag) && + (!pmk_flag && !sae_password_flag))) { + PRINTM(MERROR, + "Invalid case,ssid/bssid present without pmk, passphrase or sae password\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SUPPLICANT_PMK); + pesupplicant_psk->action = wlan_cpu_to_le16(cmd_action); + pesupplicant_psk->cache_result = 0; + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Handle the supplicant pmk response + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS MLAN_STATUS_FAILURE + */ +mlan_status wlan_ret_802_11_supplicant_pmk(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_SUPPLICANT_PMK *supplicant_pmk_resp = + &resp->params.esupplicant_psk; + mlan_ds_sec_cfg *sec_buf = MNULL; + mlan_ds_sec_cfg *sec = MNULL; + mlan_adapter *pmadapter = pmpriv->adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + MrvlIEtypes_PMK_t *ppmk_tlv = MNULL; + MrvlIEtypes_Passphrase_t *passphrase_tlv = MNULL; + MrvlIEtypes_SAE_Password_t *psae_password_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) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_sec_cfg), + MLAN_MEM_DEF, (t_u8 **)&sec_buf); + if (ret || !sec_buf) { + PRINTM(MERROR, "Could not allocate sec_buf!\n"); + LEAVE(); + return ret; + } + 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) && + (tlv != TLV_TYPE_SAE_PASSWORD)) + 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_ext( + pmpriv->adapter, + sec->param.passphrase.ssid.ssid, + pssid_tlv->ssid, + pssid_tlv->header.len, + MLAN_MAX_SSID_LENGTH); + 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_ext(pmpriv->adapter, + &sec->param.passphrase.bssid, + pbssid_tlv->bssid, + MLAN_MAC_ADDR_LENGTH, + 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_ext( + pmpriv->adapter, + sec->param.passphrase.psk + .passphrase.passphrase, + passphrase_tlv->passphrase, + passphrase_tlv->header.len, + MLAN_MAX_PASSPHRASE_LENGTH); + tlv_buf += passphrase_tlv->header.len + + sizeof(MrvlIEtypesHeader_t); + tlv_buf_len -= + (passphrase_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + break; + case TLV_TYPE_SAE_PASSWORD: + psae_password_tlv = + (MrvlIEtypes_SAE_Password_t *) + tlv_buf; + psae_password_tlv->header + .len = wlan_le16_to_cpu( + psae_password_tlv->header.len); + sec->param.passphrase.psk_type = + MLAN_PSK_SAE_PASSWORD; + sec->param.passphrase.psk.sae_password + .sae_password_len = + psae_password_tlv->header.len; + memcpy_ext( + pmpriv->adapter, + sec->param.passphrase.psk + .sae_password + .sae_password, + psae_password_tlv->sae_password, + psae_password_tlv->header.len, + MLAN_MAX_SAE_PASSWORD_LENGTH); + tlv_buf += + psae_password_tlv->header.len + + sizeof(MrvlIEtypesHeader_t); + tlv_buf_len -= + (psae_password_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_ext(pmpriv->adapter, + sec->param.passphrase.psk.pmk + .pmk, + ppmk_tlv->pmk, + ppmk_tlv->header.len, + MLAN_MAX_KEY_LENGTH); + tlv_buf += ppmk_tlv->header.len + + sizeof(MrvlIEtypesHeader_t); + tlv_buf_len -= + (ppmk_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + break; + } + } +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA && + ((mlan_ds_bss *)pioctl_buf->pbuf)->sub_command == + MLAN_OID_BSS_FIND_BSS) { + wlan_set_ewpa_mode(pmpriv, + &sec->param.passphrase); + ret = wlan_find_bss(pmpriv, pioctl_buf); + } +#endif + + } else if (action == HostCmd_ACT_GEN_SET) { + PRINTM(MINFO, "Esupp PMK set: enable ewpa query\n"); + pmpriv->ewpa_query = MTRUE; + } + if (sec_buf) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)sec_buf); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of independent reset. + * + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_ind_rst_cfg(HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + mlan_ds_ind_rst_cfg *pdata_ind_rst = (mlan_ds_ind_rst_cfg *)pdata_buf; + HostCmd_DS_INDEPENDENT_RESET_CFG *ind_rst_cfg = + (HostCmd_DS_INDEPENDENT_RESET_CFG *)&cmd->params.ind_rst_cfg; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_INDEPENDENT_RESET_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_INDEPENDENT_RESET_CFG) + + S_DS_GEN); + + ind_rst_cfg->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + ind_rst_cfg->ir_mode = pdata_ind_rst->ir_mode; + ind_rst_cfg->gpio_pin = pdata_ind_rst->gpio_pin; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of independent reset + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_ind_rst_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc = MNULL; + const HostCmd_DS_INDEPENDENT_RESET_CFG *ind_rst_cfg = + (HostCmd_DS_INDEPENDENT_RESET_CFG *)&resp->params.ind_rst_cfg; + + ENTER(); + + if (pioctl_buf) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + + if (wlan_le16_to_cpu(ind_rst_cfg->action) == + HostCmd_ACT_GEN_GET) { + misc->param.ind_rst_cfg.ir_mode = ind_rst_cfg->ir_mode; + misc->param.ind_rst_cfg.gpio_pin = + ind_rst_cfg->gpio_pin; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of ps inactivity timeout. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_ps_inactivity_timeout(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + t_u16 timeout = *((t_u16 *)pdata_buf); + HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT *ps_inact_tmo = + (HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT *)&cmd->params + .ps_inact_tmo; + + ENTER(); + + cmd->command = + wlan_cpu_to_le16(HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT); + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT) + S_DS_GEN); + + ps_inact_tmo->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) + ps_inact_tmo->inact_tmo = wlan_cpu_to_le16(timeout); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of HostCmd_CMD_GET_TSF + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_get_tsf(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action) +{ + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_GET_TSF); + cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_TSF)) + S_DS_GEN); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of HostCmd_CMD_GET_TSF + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_get_tsf(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc_cfg = MNULL; + HostCmd_DS_TSF *tsf_pointer = (HostCmd_DS_TSF *)&resp->params.tsf; + + ENTER(); + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.misc_tsf = wlan_le64_to_cpu(tsf_pointer->tsf); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of chan_region_cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_chan_region_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u16 action; + HostCmd_DS_CHAN_REGION_CFG *reg = MNULL; + t_u8 *tlv_buf = MNULL; + t_u16 tlv_buf_left; + mlan_ds_misc_cfg *misc_cfg = MNULL; + mlan_ds_misc_chnrgpwr_cfg *cfg = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + reg = (HostCmd_DS_CHAN_REGION_CFG *)&resp->params.reg_cfg; + if (!reg) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + action = wlan_le16_to_cpu(reg->action); + if (action != HostCmd_ACT_GEN_GET) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + tlv_buf = (t_u8 *)reg + sizeof(*reg); + tlv_buf_left = wlan_le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*reg); + + /* Add FW cfp tables and region info */ + wlan_add_fw_cfp_tables(pmpriv, tlv_buf, tlv_buf_left); + + if (!pioctl_buf) + goto done; + + if (!pioctl_buf->pbuf) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + + if (misc_cfg->sub_command == MLAN_OID_MISC_GET_REGIONPWR_CFG) { + cfg = (mlan_ds_misc_chnrgpwr_cfg *)&( + misc_cfg->param.rgchnpwr_cfg); + cfg->length = wlan_le16_to_cpu(resp->size); + memcpy_ext(pmpriv->adapter, cfg->chnrgpwr_buf, (t_u8 *)resp, + cfg->length, sizeof(cfg->chnrgpwr_buf)); + } else { + memset(pmpriv->adapter, &misc_cfg->param.custom_reg_domain, 0, + sizeof(mlan_ds_custom_reg_domain)); + if (pmadapter->otp_region) + memcpy_ext(pmpriv->adapter, + &misc_cfg->param.custom_reg_domain.region, + pmadapter->otp_region, + sizeof(otp_region_info_t), + sizeof(otp_region_info_t)); + if (pmadapter->cfp_otp_bg) { + misc_cfg->param.custom_reg_domain.num_bg_chan = + pmadapter->tx_power_table_bg_rows; + memcpy_ext(pmpriv->adapter, + (t_u8 *)misc_cfg->param.custom_reg_domain + .cfp_tbl, + (t_u8 *)pmadapter->cfp_otp_bg, + pmadapter->tx_power_table_bg_rows * + sizeof(chan_freq_power_t), + pmadapter->tx_power_table_bg_rows * + sizeof(chan_freq_power_t)); + } + if (pmadapter->cfp_otp_a) { + misc_cfg->param.custom_reg_domain.num_a_chan = + pmadapter->tx_power_table_a_rows; + memcpy_ext(pmpriv->adapter, + (t_u8 *)misc_cfg->param.custom_reg_domain + .cfp_tbl + + pmadapter->tx_power_table_bg_rows * + sizeof(chan_freq_power_t), + (t_u8 *)pmadapter->cfp_otp_a, + pmadapter->tx_power_table_a_rows * + sizeof(chan_freq_power_t), + pmadapter->tx_power_table_a_rows * + sizeof(chan_freq_power_t)); + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of packet aggragation + * + * @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_packet_aggr_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_PACKET_AGGR_CTRL *aggr_ctrl = &cmd->params.aggr_ctrl; + mlan_ds_misc_aggr_ctrl *aggr = (mlan_ds_misc_aggr_ctrl *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_PACKET_AGGR_CTRL); + aggr_ctrl->action = wlan_cpu_to_le16(cmd_action); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_PACKET_AGGR_CTRL) + + S_DS_GEN); + aggr_ctrl->aggr_enable = 0; + + if (aggr->tx.enable) + aggr_ctrl->aggr_enable |= MBIT(0); + aggr_ctrl->aggr_enable = wlan_cpu_to_le16(aggr_ctrl->aggr_enable); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * packet aggregation + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_packet_aggr_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc = MNULL; + HostCmd_DS_PACKET_AGGR_CTRL *aggr_ctrl = &resp->params.aggr_ctrl; + mlan_ds_misc_aggr_ctrl *aggr = MNULL; +#if defined(USB) + mlan_adapter *pmadapter = pmpriv->adapter; + t_u8 i; + t_u8 change = 0; + usb_tx_aggr_params *pusb_tx_aggr = MNULL; +#endif + + ENTER(); + + aggr_ctrl->aggr_enable = wlan_le16_to_cpu(aggr_ctrl->aggr_enable); + aggr_ctrl->tx_aggr_max_size = + wlan_le16_to_cpu(aggr_ctrl->tx_aggr_max_size); + aggr_ctrl->tx_aggr_max_num = + wlan_le16_to_cpu(aggr_ctrl->tx_aggr_max_num); + aggr_ctrl->tx_aggr_align = wlan_le16_to_cpu(aggr_ctrl->tx_aggr_align); + PRINTM(MCMND, "enable=0x%x, tx_size=%d, tx_num=%d, tx_align=%d\n", + aggr_ctrl->aggr_enable, aggr_ctrl->tx_aggr_max_size, + aggr_ctrl->tx_aggr_max_num, aggr_ctrl->tx_aggr_align); +#if defined(USB) + if (IS_USB(pmadapter->card_type)) { + if (aggr_ctrl->aggr_enable & MBIT(0)) { + if (!pmadapter->pcard_usb->usb_tx_aggr[0] + .aggr_ctrl.enable) { + pmadapter->pcard_usb->usb_tx_aggr[0] + .aggr_ctrl.enable = MTRUE; + change = MTRUE; + } + + } else { + if (pmadapter->pcard_usb->usb_tx_aggr[0] + .aggr_ctrl.enable) { + pmadapter->pcard_usb->usb_tx_aggr[0] + .aggr_ctrl.enable = MFALSE; + change = MTRUE; + } + } + pmadapter->pcard_usb->usb_tx_aggr[0].aggr_ctrl.aggr_mode = + MLAN_USB_AGGR_MODE_LEN_V2; + pmadapter->pcard_usb->usb_tx_aggr[0].aggr_ctrl.aggr_align = + aggr_ctrl->tx_aggr_align; + pmadapter->pcard_usb->usb_tx_aggr[0].aggr_ctrl.aggr_max = + aggr_ctrl->tx_aggr_max_size; + pmadapter->pcard_usb->usb_tx_aggr[0].aggr_ctrl.aggr_tmo = + MLAN_USB_TX_AGGR_TIMEOUT_MSEC * 1000; + if (change) { + wlan_reset_usb_tx_aggr(pmadapter); + for (i = 0; i < pmadapter->priv_num; ++i) { + if (pmadapter->priv[i]) { + pusb_tx_aggr = + wlan_get_usb_tx_aggr_params( + pmadapter, + pmadapter->priv[i] + ->port); + if (pusb_tx_aggr && + pusb_tx_aggr->aggr_ctrl.aggr_mode == + MLAN_USB_AGGR_MODE_LEN_V2) + pmadapter->priv[i]->intf_hr_len = + MLAN_USB_TX_AGGR_HEADER; + else + pmadapter->priv[i]->intf_hr_len = + USB_INTF_HEADER_LEN; + } + } + } + } +#endif + if (pioctl_buf) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + aggr = (mlan_ds_misc_aggr_ctrl *)&(misc->param.aggr_params); + if (aggr_ctrl->aggr_enable & MBIT(0)) + aggr->tx.enable = MTRUE; + else + aggr->tx.enable = MFALSE; + aggr->tx.aggr_align = aggr_ctrl->tx_aggr_align; + aggr->tx.aggr_max_size = aggr_ctrl->tx_aggr_max_size; + aggr->tx.aggr_max_num = aggr_ctrl->tx_aggr_max_num; +#if defined(USB) + if (IS_USB(pmadapter->card_type)) + aggr->tx.aggr_tmo = pmadapter->pcard_usb->usb_tx_aggr[0] + .aggr_ctrl.aggr_tmo; +#endif + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function sends fw dump event command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd Hostcmd ID + * @param cmd_action Command action + * @param pdata_buf A void pointer to information buffer + * @return N/A + */ +mlan_status wlan_cmd_fw_dump_event(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_FW_DUMP_EVENT); + cmd->size = S_DS_GEN; + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of get link layer statistics. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * + */ +mlan_status wlan_cmd_802_11_link_statistic(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_get_info *info = (mlan_ds_get_info *)(pioctl_buf->pbuf); + HostCmd_DS_802_11_LINK_STATISTIC *ll_stat = + &cmd->params.get_link_statistic; + wifi_link_layer_params *ll_params = MNULL; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_LINK_STATS); + cmd->size = wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_DS_802_11_LINK_STATISTIC)); + ll_stat->action = wlan_cpu_to_le16(cmd_action); + + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + ll_params = + (wifi_link_layer_params *)info->param.link_statistic; + ll_stat->mpdu_size_threshold = + wlan_cpu_to_le32(ll_params->mpdu_size_threshold); + ll_stat->aggressive_statistics_gathering = wlan_cpu_to_le32( + ll_params->aggressive_statistics_gathering); + break; + case HostCmd_ACT_GEN_GET: + /** ll_stat->stat_type = wlan_cpu_to_le16(stat_type); */ + break; + default: + break; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function fill link layer statistic from firmware + * + * @param priv A pointer to + * mlan_private structure + * @param link_statistic_ioctl_buf, A pointer to fill ioctl buffer + * @param resp A pointer to + * HostCmd_DS_COMMAND + * + * @return MLAN_STATUS_SUCCESS + */ +static void wlan_fill_link_statistic(mlan_private *priv, + char *link_statistic_ioctl_buf, + HostCmd_DS_COMMAND *resp) +{ + char *link_statistic = link_statistic_ioctl_buf; + wifi_radio_stat *radio_stat = MNULL; + wifi_iface_stat *iface_stat = MNULL; + mlan_wifi_iface_stat *fw_ifaceStat = MNULL; + mlan_wifi_radio_stat *fw_radioStat = MNULL; + t_u32 num_radio = 0; + int i = 0, chan_idx = 0, peerIdx = 0, rate_idx = 0; + t_u16 left_len = 0, tlv_type = 0, tlv_len = 0; + MrvlIEtypesHeader_t *tlv = MNULL; + HostCmd_DS_802_11_LINK_STATISTIC *plink_stat = + (HostCmd_DS_802_11_LINK_STATISTIC *)&resp->params + .get_link_statistic; + + /* TLV parse */ + left_len = resp->size - sizeof(HostCmd_DS_802_11_LINK_STATISTIC) - + S_DS_GEN; + tlv = (MrvlIEtypesHeader_t *)(plink_stat->value); + DBG_HEXDUMP(MCMD_D, "tlv:", (void *)tlv, 1024); + while (left_len > sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + switch (tlv_type) { + case TLV_TYPE_LL_STAT_IFACE: + fw_ifaceStat = (mlan_wifi_iface_stat + *)((t_u8 *)tlv + + sizeof(MrvlIEtypesHeader_t)); + break; + case TLV_TYPE_LL_STAT_RADIO: + fw_radioStat = (mlan_wifi_radio_stat + *)((t_u8 *)tlv + + sizeof(MrvlIEtypesHeader_t)); + num_radio = MAX_RADIO; + break; + default: + break; + } + left_len -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + + if (!fw_ifaceStat || !fw_radioStat) { + PRINTM(MERROR, "!fw_ifaceStat || !fw_radioStat\n"); + return; + } + + *((t_u32 *)link_statistic) = num_radio; + link_statistic += sizeof(num_radio); + + /* Fill radio stats array*/ + for (i = 0; i < num_radio; i++) { + radio_stat = (wifi_radio_stat *)link_statistic; + link_statistic += sizeof(wifi_radio_stat); + + radio_stat->radio = wlan_le32_to_cpu(fw_radioStat[i].radio); + + radio_stat->on_time = wlan_le32_to_cpu(fw_radioStat[i].on_time); + radio_stat->tx_time = wlan_le32_to_cpu(fw_radioStat[i].tx_time); + radio_stat->reserved0 = + wlan_le32_to_cpu(fw_radioStat[i].reserved0); + radio_stat->rx_time = wlan_le32_to_cpu(fw_radioStat[i].rx_time); + radio_stat->on_time_scan = + wlan_le32_to_cpu(fw_radioStat[i].on_time_scan); + radio_stat->on_time_nbd = + wlan_le32_to_cpu(fw_radioStat[i].on_time_nbd); + radio_stat->on_time_gscan = + wlan_le32_to_cpu(fw_radioStat[i].on_time_gscan); + radio_stat->on_time_roam_scan = + wlan_le32_to_cpu(fw_radioStat[i].on_time_roam_scan); + radio_stat->on_time_pno_scan = + wlan_le32_to_cpu(fw_radioStat[i].on_time_pno_scan); + radio_stat->on_time_hs20 = + wlan_le32_to_cpu(fw_radioStat[i].on_time_hs20); + + radio_stat->num_channels = + wlan_le32_to_cpu(fw_radioStat[i].num_channels); + for (chan_idx = 0; chan_idx < radio_stat->num_channels; + chan_idx++) { + if (radio_stat->num_channels > MAX_NUM_CHAN) { + radio_stat->num_channels = + wlan_le32_to_cpu(MAX_NUM_CHAN); + PRINTM(MERROR, + "%s : radio_stat->num_channels=%d\n", + __func__, radio_stat->num_channels); + break; + } + radio_stat->channels[chan_idx].channel.width = + wlan_le32_to_cpu(fw_radioStat[i] + .channels[chan_idx] + .channel.width); + radio_stat->channels[chan_idx].channel.center_freq = + wlan_le32_to_cpu(fw_radioStat[i] + .channels[chan_idx] + .channel.center_freq); + radio_stat->channels[chan_idx].channel.center_freq0 = + wlan_le32_to_cpu(fw_radioStat[i] + .channels[chan_idx] + .channel.center_freq0); + radio_stat->channels[chan_idx].channel.center_freq1 = + wlan_le32_to_cpu(fw_radioStat[i] + .channels[chan_idx] + .channel.center_freq1); + + radio_stat->channels[chan_idx] + .on_time = wlan_le32_to_cpu( + fw_radioStat[i].channels[chan_idx].on_time); + radio_stat->channels[chan_idx].cca_busy_time = + wlan_le32_to_cpu(fw_radioStat[i] + .channels[chan_idx] + .cca_busy_time); + } + } + + /* Fill iface stats*/ + iface_stat = (wifi_iface_stat *)link_statistic; + + /* get wifi_interface_link_layer_info in driver, not in firmware */ + if (priv->bss_role == MLAN_BSS_ROLE_STA) { + iface_stat->info.mode = MLAN_INTERFACE_STA; + if (priv->media_connected) + iface_stat->info.state = MLAN_ASSOCIATING; + else + iface_stat->info.state = MLAN_DISCONNECTED; + iface_stat->info.roaming = MLAN_ROAMING_IDLE; + iface_stat->info.capabilities = MLAN_CAPABILITY_QOS; + memcpy_ext(priv->adapter, iface_stat->info.ssid, + priv->curr_bss_params.bss_descriptor.ssid.ssid, + MLAN_MAX_SSID_LENGTH, MLAN_MAX_SSID_LENGTH); + memcpy_ext(priv->adapter, iface_stat->info.bssid, + priv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + } else { + iface_stat->info.mode = MLAN_INTERFACE_SOFTAP; + iface_stat->info.capabilities = MLAN_CAPABILITY_QOS; + } + memcpy_ext(priv->adapter, iface_stat->info.mac_addr, priv->curr_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + memcpy_ext(priv->adapter, iface_stat->info.ap_country_str, + priv->adapter->country_code, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + memcpy_ext(priv->adapter, iface_stat->info.country_str, + priv->adapter->country_code, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + + iface_stat->beacon_rx = wlan_le32_to_cpu(fw_ifaceStat->beacon_rx); + iface_stat->average_tsf_offset = + wlan_le64_to_cpu(fw_ifaceStat->average_tsf_offset); + iface_stat->leaky_ap_detected = + wlan_le32_to_cpu(fw_ifaceStat->leaky_ap_detected); + iface_stat->leaky_ap_avg_num_frames_leaked = + wlan_le32_to_cpu(fw_ifaceStat->leaky_ap_avg_num_frames_leaked); + iface_stat->leaky_ap_guard_time = + wlan_le32_to_cpu(fw_ifaceStat->leaky_ap_guard_time); + + /* Value of iface_stat should be Reaccumulate by each peer */ + iface_stat->mgmt_rx = wlan_le32_to_cpu(fw_ifaceStat->mgmt_rx); + iface_stat->mgmt_action_rx = + wlan_le32_to_cpu(fw_ifaceStat->mgmt_action_rx); + iface_stat->mgmt_action_tx = + wlan_le32_to_cpu(fw_ifaceStat->mgmt_action_tx); + + iface_stat->rssi_mgmt = wlan_le32_to_cpu(fw_ifaceStat->rssi_mgmt); + iface_stat->rssi_data = wlan_le32_to_cpu(fw_ifaceStat->rssi_data); + iface_stat->rssi_ack = wlan_le32_to_cpu(fw_ifaceStat->rssi_ack); + + for (i = WMM_AC_BK; i <= WMM_AC_VO; i++) { + iface_stat->ac[i].ac = i; + iface_stat->ac[i].tx_mpdu = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].tx_mpdu); + iface_stat->ac[i].rx_mpdu = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].rx_mpdu); + iface_stat->ac[i].tx_mcast = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].tx_mcast); + iface_stat->ac[i].rx_mcast = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].rx_mcast); + iface_stat->ac[i].rx_ampdu = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].rx_ampdu); + iface_stat->ac[i].tx_ampdu = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].tx_ampdu); + iface_stat->ac[i].mpdu_lost = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].mpdu_lost); + iface_stat->ac[i].retries = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].retries); + iface_stat->ac[i].retries_short = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].retries_short); + iface_stat->ac[i].retries_long = + wlan_le32_to_cpu(fw_ifaceStat->ac[i].retries_long); + iface_stat->ac[i].contention_time_min = wlan_le32_to_cpu( + fw_ifaceStat->ac[i].contention_time_min); + iface_stat->ac[i].contention_time_max = wlan_le32_to_cpu( + fw_ifaceStat->ac[i].contention_time_max); + iface_stat->ac[i].contention_time_avg = wlan_le32_to_cpu( + fw_ifaceStat->ac[i].contention_time_avg); + iface_stat->ac[i].contention_num_samples = wlan_le32_to_cpu( + fw_ifaceStat->ac[i].contention_num_samples); + } + + /* LL_STAT V3: STA-solution: support maxium 1 peers for AP*/ + iface_stat->num_peers = wlan_le32_to_cpu(fw_ifaceStat->num_peers); + for (peerIdx = 0; peerIdx < iface_stat->num_peers; peerIdx++) { + iface_stat->peer_info[peerIdx].type = + fw_ifaceStat->peer_info[peerIdx].type; + memcpy_ext(priv->adapter, + iface_stat->peer_info[peerIdx].peer_mac_address, + fw_ifaceStat->peer_info[peerIdx].peer_mac_address, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + iface_stat->peer_info[peerIdx].capabilities = wlan_le32_to_cpu( + fw_ifaceStat->peer_info[peerIdx].capabilities); + iface_stat->peer_info[peerIdx].num_rate = wlan_le32_to_cpu( + fw_ifaceStat->peer_info[peerIdx].num_rate); + + PRINTM(MINFO, + "bitrate tx_mpdu rx_mpdu mpdu_lost retries retries_short retries_long\n"); + for (rate_idx = 0; + rate_idx < iface_stat->peer_info[peerIdx].num_rate; + rate_idx++) { + wlan_fill_hal_wifi_rate(priv, + &fw_ifaceStat + ->peer_info[peerIdx] + .rate_stats[rate_idx] + .rate, + &iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .rate); + + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .tx_mpdu = wlan_le32_to_cpu( + fw_ifaceStat->peer_info[peerIdx] + .rate_stats[rate_idx] + .tx_mpdu); + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .rx_mpdu = wlan_le32_to_cpu( + fw_ifaceStat->peer_info[peerIdx] + .rate_stats[rate_idx] + .rx_mpdu); + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .mpdu_lost = wlan_le32_to_cpu( + fw_ifaceStat->peer_info[peerIdx] + .rate_stats[rate_idx] + .mpdu_lost); + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .retries = wlan_le32_to_cpu( + fw_ifaceStat->peer_info[peerIdx] + .rate_stats[rate_idx] + .retries); + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .retries_short = wlan_le32_to_cpu( + fw_ifaceStat->peer_info[peerIdx] + .rate_stats[rate_idx] + .retries_short); + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .retries_long = wlan_le32_to_cpu( + fw_ifaceStat->peer_info[peerIdx] + .rate_stats[rate_idx] + .retries_long); + PRINTM(MCMND, + "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .rate.bitrate, + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .tx_mpdu, + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .rx_mpdu, + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .mpdu_lost, + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .retries, + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .retries_short, + iface_stat->peer_info[peerIdx] + .rate_stats[rate_idx] + .retries_long); + } + } +} + +/** + * @brief This function handles the command response of get_link_statistic + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_get_link_statistic(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_get_info *info; + t_u8 *link_statistic = MNULL; + t_u16 action = wlan_le16_to_cpu(resp->params.get_link_statistic.action); + + ENTER(); + + if (pioctl_buf) { + info = (mlan_ds_get_info *)pioctl_buf->pbuf; + link_statistic = info->param.link_statistic; + + switch (action) { + case HostCmd_ACT_GEN_GET: + wlan_fill_link_statistic(pmpriv, link_statistic, resp); + break; + case HostCmd_ACT_GEN_SET: + case HostCmd_ACT_GEN_REMOVE: + /* nothing to do */ + break; + default: + break; + } + /* Indicate ioctl complete */ + pioctl_buf->data_read_written = + BUF_MAXLEN + MLAN_SUB_COMMAND_SIZE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function sends boot sleep configure command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd Hostcmd ID + * @param cmd_action Command action + * @param pdata_buf A void pointer to information buffer + * @return MLAN_STATUS_SUCCESS/ MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_boot_sleep(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_BOOT_SLEEP *boot_sleep = MNULL; + t_u16 enable = *(t_u16 *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_BOOT_SLEEP); + boot_sleep = &cmd->params.boot_sleep; + boot_sleep->action = wlan_cpu_to_le16(cmd_action); + boot_sleep->enable = wlan_cpu_to_le16(enable); + + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_BOOT_SLEEP); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of boot sleep cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_boot_sleep(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_BOOT_SLEEP *boot_sleep = &resp->params.boot_sleep; + mlan_ds_misc_cfg *cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + + ENTER(); + + cfg->param.boot_sleep = wlan_le16_to_cpu(boot_sleep->enable); + PRINTM(MCMND, "boot sleep cfg status %u", cfg->param.boot_sleep); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + +/** + * @brief This function handles send crypto command + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd Hostcmd ID + * @param cmd_action Command action + * @param pdata_buf A void pointer to information buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_crypto(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u16 *pdata_buf) +{ + HostCmd_DS_CRYPTO *cry_cmd = &cmd->params.crypto_cmd; + mlan_ds_sup_cfg *cfg = (mlan_ds_sup_cfg *)pdata_buf; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + subcmd_prf_hmac_sha1_t *prf_hmac_sha1 = MNULL; + subcmd_hmac_sha1_t *hmac_sha1 = MNULL; + subcmd_hmac_sha256_t *hmac_sha256 = MNULL; + subcmd_sha256_t *sha256 = MNULL; + subcmd_rijndael_t *rijndael = MNULL; + subcmd_rc4_t *rc4 = MNULL; + subcmd_md5_t *md5 = MNULL; + subcmd_mrvl_f_t *mrvl_f = MNULL; + subcmd_sha256_kdf_t *sha256_kdf = MNULL; + t_u8 *ptlv = MNULL; + t_u8 tlv_bitmap = 0; + t_u32 i = 0; +#endif + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_CRYPTO); + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_CRYPTO); + cry_cmd->action = wlan_cpu_to_le16(cmd_action); + cry_cmd->subCmdCode = cfg->sub_command; + switch (cfg->sub_command) { +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + case HostCmd_CMD_CRYPTO_SUBCMD_PRF_HMAC_SHA1: + tlv_bitmap = BIT_TLV_TYPE_CRYPTO_KEY | + BIT_TLV_TYPE_CRYPTO_KEY_PREFIX | + BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK; + /* set subcmd start */ + prf_hmac_sha1 = (subcmd_prf_hmac_sha1_t *)cry_cmd->subCmd; + prf_hmac_sha1->output_len = cfg->output_len; + /* set tlv start */ + ptlv = prf_hmac_sha1->tlv; + cmd->size += sizeof(subcmd_prf_hmac_sha1_t); + break; + case HostCmd_CMD_CRYPTO_SUBCMD_HMAC_SHA1: + tlv_bitmap = BIT_TLV_TYPE_CRYPTO_KEY | + BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK; + /* set subcmd start */ + hmac_sha1 = (subcmd_hmac_sha1_t *)cry_cmd->subCmd; + hmac_sha1->output_len = cfg->output_len; + hmac_sha1->data_blks_nr = cfg->data_blks_nr; + /* set tlv start */ + ptlv = hmac_sha1->tlv; + cmd->size += sizeof(subcmd_hmac_sha1_t); + break; + case HostCmd_CMD_CRYPTO_SUBCMD_HMAC_SHA256: + tlv_bitmap = BIT_TLV_TYPE_CRYPTO_KEY | + BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK; + /* set subcmd start */ + hmac_sha256 = (subcmd_hmac_sha256_t *)cry_cmd->subCmd; + hmac_sha256->output_len = cfg->output_len; + hmac_sha256->data_blks_nr = cfg->data_blks_nr; + /* set tlv start */ + ptlv = hmac_sha256->tlv; + cmd->size += sizeof(subcmd_hmac_sha256_t); + break; + case HostCmd_CMD_CRYPTO_SUBCMD_SHA256: + tlv_bitmap = BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK; + /* set subcmd start */ + sha256 = (subcmd_sha256_t *)cry_cmd->subCmd; + sha256->output_len = cfg->output_len; + sha256->data_blks_nr = cfg->data_blks_nr; + /* set tlv start */ + ptlv = sha256->tlv; + cmd->size += sizeof(subcmd_sha256_t); + break; + case HostCmd_CMD_CRYPTO_SUBCMD_RIJNDAEL: + tlv_bitmap = BIT_TLV_TYPE_CRYPTO_KEY | + BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK; + /* set subcmd start */ + rijndael = (subcmd_rijndael_t *)cry_cmd->subCmd; + rijndael->sub_action_code = cfg->sub_action_code; + rijndael->output_len = cfg->output_len; + /* set tlv start */ + ptlv = rijndael->tlv; + cmd->size += sizeof(subcmd_rijndael_t); + break; + case HostCmd_CMD_CRYPTO_SUBCMD_RC4: + tlv_bitmap = BIT_TLV_TYPE_CRYPTO_KEY | + BIT_TLV_TYPE_CRYPTO_KEY_IV | + BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK; + /* set subcmd start */ + rc4 = (subcmd_rc4_t *)cry_cmd->subCmd; + rc4->skip_bytes = cfg->skip_bytes; + rc4->output_len = cfg->output_len; + /* set tlv start */ + ptlv = rc4->tlv; + cmd->size += sizeof(subcmd_rc4_t); + break; + case HostCmd_CMD_CRYPTO_SUBCMD_MD5: + tlv_bitmap = BIT_TLV_TYPE_CRYPTO_KEY | + BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK; + /* set subcmd start */ + md5 = (subcmd_md5_t *)cry_cmd->subCmd; + md5->output_len = cfg->output_len; + /* set tlv start */ + ptlv = md5->tlv; + cmd->size += sizeof(subcmd_md5_t); + break; + case HostCmd_CMD_CRYPTO_SUBCMD_MRVL_F: + tlv_bitmap = BIT_TLV_TYPE_CRYPTO_KEY | + BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK; + /* set subcmd start */ + mrvl_f = (subcmd_mrvl_f_t *)cry_cmd->subCmd; + mrvl_f->iterations = cfg->iteration; + mrvl_f->count = cfg->count; + mrvl_f->output_len = cfg->output_len; + /* set tlv start */ + ptlv = mrvl_f->tlv; + cmd->size += sizeof(subcmd_mrvl_f_t); + break; + case HostCmd_CMD_CRYPTO_SUBCMD_SHA256_KDF: + tlv_bitmap = BIT_TLV_TYPE_CRYPTO_KEY | + BIT_TLV_TYPE_CRYPTO_KEY_PREFIX | + BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK; + /* set subcmd start */ + sha256_kdf = (subcmd_sha256_kdf_t *)cry_cmd->subCmd; + sha256_kdf->output_len = cfg->output_len; + /* set tlv start */ + ptlv = sha256_kdf->tlv; + cmd->size += sizeof(subcmd_sha256_kdf_t); + break; +#endif + default: + break; + } +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + /* add tlv */ + if (tlv_bitmap & BIT_TLV_TYPE_CRYPTO_KEY) { + ((MrvlIEParamSet_t *)ptlv)->Type = + wlan_cpu_to_le16(TLV_TYPE_CRYPTO_KEY); + ((MrvlIEParamSet_t *)ptlv)->Length = + wlan_cpu_to_le16(cfg->key_len); + memcpy_ext(pmpriv->adapter, + (t_u8 *)ptlv + sizeof(MrvlIEParamSet_t), cfg->key, + cfg->key_len, cfg->key_len); + cmd->size += cfg->key_len + sizeof(MrvlIEParamSet_t); + ptlv += cfg->key_len + sizeof(MrvlIEParamSet_t); + } + + if (tlv_bitmap & BIT_TLV_TYPE_CRYPTO_KEY_PREFIX) { + ((MrvlIEParamSet_t *)ptlv)->Type = + wlan_cpu_to_le16(TLV_TYPE_CRYPTO_KEY_PREFIX); + ((MrvlIEParamSet_t *)ptlv)->Length = + wlan_cpu_to_le16(cfg->key_prefix_len); + memcpy_ext(pmpriv->adapter, ptlv + sizeof(MrvlIEParamSet_t), + cfg->key_prefix, cfg->key_prefix_len, + cfg->key_prefix_len); + cmd->size += cfg->key_prefix_len + sizeof(MrvlIEParamSet_t); + ptlv += cfg->key_prefix_len + sizeof(MrvlIEParamSet_t); + } + + if (tlv_bitmap & BIT_TLV_TYPE_CRYPTO_KEY_IV) { + ((MrvlIEParamSet_t *)ptlv)->Type = + wlan_cpu_to_le16(TLV_TYPE_CRYPTO_KEY_IV); + ((MrvlIEParamSet_t *)ptlv)->Length = + wlan_cpu_to_le16(cfg->key_iv_len); + memcpy_ext(pmpriv->adapter, ptlv + sizeof(MrvlIEParamSet_t), + cfg->key_iv, cfg->key_iv_len, cfg->key_iv_len); + cmd->size += cfg->key_iv_len + sizeof(MrvlIEParamSet_t); + ptlv += cfg->key_iv_len + sizeof(MrvlIEParamSet_t); + } + + if (tlv_bitmap & BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK) { + t_u16 data_blk_len = 0; + t_u8 *pdata_blk = MNULL; + for (i = 0; i < cfg->data_blks_nr; i++) { + data_blk_len = *(cfg->key_data_blk_len + i); + pdata_blk = *(cfg->key_data_blk + i); + ((MrvlIEParamSet_t *)ptlv)->Type = + wlan_cpu_to_le16(TLV_TYPE_CRYPTO_KEY_DATA_BLK); + ((MrvlIEParamSet_t *)ptlv)->Length = + wlan_cpu_to_le16(data_blk_len); + memcpy_ext(pmpriv->adapter, + ptlv + sizeof(MrvlIEParamSet_t), pdata_blk, + data_blk_len, data_blk_len); + cmd->size += data_blk_len + sizeof(MrvlIEParamSet_t); + ptlv += data_blk_len + sizeof(MrvlIEParamSet_t); + } + } +#endif + HEXDUMP("HostCmd_DS_COMMAND wlan_cmd_crypto", cmd, cmd->size); + + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of crypto command + * + * @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_crypto(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CRYPTO *crypto_cmd = &resp->params.crypto_cmd; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (pmlan_callbacks)&pmadapter->callbacks; + mlan_ds_sup_cfg *cfg = (mlan_ds_sup_cfg *)pioctl_buf->pbuf; +#endif + + ENTER(); +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + if (!cfg) { + PRINTM(MERROR, "wlan_ret_crypto cfg is null \n"); + goto done; + } + if (resp->result == HostCmd_RESULT_OK) { + /* copy the result */ + memcpy_ext(pmpriv->adapter, cfg->output, + (t_u8 *)crypto_cmd + sizeof(HostCmd_DS_CRYPTO) + + sizeof(cfg->output_len), + cfg->output_len, cfg->output_len); + } + + /* Prevent the ioctl from completing when the cmd is freed */ + if (cfg->call_back) { + pmadapter->curr_cmd->pioctl_buf = MNULL; + /* trigger wait q */ + pcb->moal_notify_hostcmd_complete(pmadapter->pmoal_handle, + pmpriv->bss_index); + } +#endif +done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif + +/** + * @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 + */ +mlan_status wlan_cmd_802_11_mac_address(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + 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_ext(pmpriv->adapter, cmd->params.mac_addr.mac_addr, + pmpriv->curr_addr, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + /* HEXDUMP("SET_CMD: MAC ADDRESS-", priv->CurrentAddr, 6); */ + } + 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 + */ +mlan_status wlan_ret_802_11_mac_address(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_MAC_ADDRESS *pmac_addr = &resp->params.mac_addr; + mlan_ds_bss *bss = MNULL; + + ENTER(); + + memcpy_ext(pmpriv->adapter, pmpriv->curr_addr, pmac_addr->mac_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + + PRINTM(MINFO, "MAC address: " MACSTR "\n", MAC2STR(pmpriv->curr_addr)); + if (pioctl_buf) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + memcpy_ext(pmpriv->adapter, &bss->param.mac_addr, + pmpriv->curr_addr, MLAN_MAC_ADDR_LENGTH, + 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 prepares command of Rx abort 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_rxabortcfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_CMD_RX_ABORT_CFG *cfg_cmd = + (HostCmd_DS_CMD_RX_ABORT_CFG *)&cmd->params.rx_abort_cfg; + mlan_ds_misc_rx_abort_cfg *cfg = (mlan_ds_misc_rx_abort_cfg *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RX_ABORT_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_CMD_RX_ABORT_CFG) + + S_DS_GEN); + cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + cfg_cmd->enable = (t_u8)cfg->enable; + cfg_cmd->rssi_threshold = (t_s8)cfg->rssi_threshold; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of Rx Abort 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_rxabortcfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CMD_RX_ABORT_CFG *cfg_cmd = + (HostCmd_DS_CMD_RX_ABORT_CFG *)&resp->params.rx_abort_cfg; + mlan_ds_misc_cfg *misc_cfg = MNULL; + + ENTER(); + + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.rx_abort_cfg.enable = (t_u8)cfg_cmd->enable; + misc_cfg->param.rx_abort_cfg.rssi_threshold = + (t_s8)cfg_cmd->rssi_threshold; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of Rx abort cfg ext + * + * @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_rxabortcfg_ext(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_CMD_RX_ABORT_CFG_EXT *cfg_cmd = + (HostCmd_DS_CMD_RX_ABORT_CFG_EXT *)&cmd->params.rx_abort_cfg_ext; + mlan_ds_misc_rx_abort_cfg_ext *cfg = + (mlan_ds_misc_rx_abort_cfg_ext *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RX_ABORT_CFG_EXT); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_CMD_RX_ABORT_CFG_EXT) + + S_DS_GEN); + cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + cfg_cmd->enable = (t_u8)cfg->enable; + cfg_cmd->rssi_margin = (t_s8)cfg->rssi_margin; + cfg_cmd->ceil_rssi_threshold = (t_s8)cfg->ceil_rssi_threshold; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of Rx Abort Cfg ext + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_rxabortcfg_ext(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CMD_RX_ABORT_CFG_EXT *cfg_cmd = + (HostCmd_DS_CMD_RX_ABORT_CFG_EXT *)&resp->params + .rx_abort_cfg_ext; + mlan_ds_misc_cfg *misc_cfg = MNULL; + + ENTER(); + + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.rx_abort_cfg_ext.enable = cfg_cmd->enable; + misc_cfg->param.rx_abort_cfg_ext.rssi_margin = + cfg_cmd->rssi_margin; + misc_cfg->param.rx_abort_cfg_ext.ceil_rssi_threshold = + cfg_cmd->ceil_rssi_threshold; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of Dot11mc unassoc ftm 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_dot11mc_unassoc_ftm_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_CMD_DOT11MC_UNASSOC_FTM_CFG *cfg_cmd = + (HostCmd_DS_CMD_DOT11MC_UNASSOC_FTM_CFG *)&cmd->params + .dot11mc_unassoc_ftm_cfg; + mlan_ds_misc_dot11mc_unassoc_ftm_cfg *cfg = + (mlan_ds_misc_dot11mc_unassoc_ftm_cfg *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_DOT11MC_UNASSOC_FTM_CFG); + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_CMD_DOT11MC_UNASSOC_FTM_CFG) + S_DS_GEN); + cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + cfg_cmd->state = wlan_cpu_to_le16(cfg->state); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of Dot11mc unassoc ftm 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_dot11mc_unassoc_ftm_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CMD_DOT11MC_UNASSOC_FTM_CFG *cfg_cmd = + (HostCmd_DS_CMD_DOT11MC_UNASSOC_FTM_CFG *)&resp->params + .dot11mc_unassoc_ftm_cfg; + mlan_ds_misc_cfg *misc_cfg = MNULL; + + ENTER(); + + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.dot11mc_unassoc_ftm_cfg.state = + wlan_le16_to_cpu(cfg_cmd->state); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of Tx ampdu prot 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_tx_ampdu_prot_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_CMD_TX_AMPDU_PROT_MODE *cfg_cmd = + (HostCmd_DS_CMD_TX_AMPDU_PROT_MODE *)&cmd->params + .tx_ampdu_prot_mode; + mlan_ds_misc_tx_ampdu_prot_mode *cfg = + (mlan_ds_misc_tx_ampdu_prot_mode *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_TX_AMPDU_PROT_MODE); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_CMD_TX_AMPDU_PROT_MODE) + + S_DS_GEN); + cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + cfg_cmd->mode = wlan_cpu_to_le16(cfg->mode); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of Tx ampdu prot 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_tx_ampdu_prot_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CMD_TX_AMPDU_PROT_MODE *cfg_cmd = + (HostCmd_DS_CMD_TX_AMPDU_PROT_MODE *)&resp->params + .tx_ampdu_prot_mode; + mlan_ds_misc_cfg *misc_cfg = MNULL; + + ENTER(); + + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.tx_ampdu_prot_mode.mode = + wlan_le16_to_cpu(cfg_cmd->mode); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of Rate Adapt 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_rate_adapt_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_CMD_RATE_ADAPT_CFG *cfg_cmd = + (HostCmd_DS_CMD_RATE_ADAPT_CFG *)&cmd->params.rate_adapt_cfg; + mlan_ds_misc_rate_adapt_cfg *cfg = + (mlan_ds_misc_rate_adapt_cfg *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RATE_ADAPT_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_CMD_RATE_ADAPT_CFG) + + S_DS_GEN); + cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + cfg_cmd->sr_rateadapt = (t_u8)cfg->sr_rateadapt; + cfg_cmd->ra_low_thresh = (t_u8)cfg->ra_low_thresh; + cfg_cmd->ra_high_thresh = (t_u8)cfg->ra_high_thresh; + cfg_cmd->ra_interval = wlan_cpu_to_le16(cfg->ra_interval); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of Rate Adapt 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_rate_adapt_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CMD_RATE_ADAPT_CFG *cfg_cmd = + (HostCmd_DS_CMD_RATE_ADAPT_CFG *)&resp->params.rate_adapt_cfg; + mlan_ds_misc_cfg *misc_cfg = MNULL; + + ENTER(); + + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.rate_adapt_cfg.sr_rateadapt = + (t_u8)cfg_cmd->sr_rateadapt; + misc_cfg->param.rate_adapt_cfg.ra_low_thresh = + (t_u8)cfg_cmd->ra_low_thresh; + misc_cfg->param.rate_adapt_cfg.ra_high_thresh = + (t_u8)cfg_cmd->ra_high_thresh; + misc_cfg->param.rate_adapt_cfg.ra_interval = + wlan_le16_to_cpu(cfg_cmd->ra_interval); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of CCK Desense 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_cck_desense_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_CMD_CCK_DESENSE_CFG *cfg_cmd = + (HostCmd_DS_CMD_CCK_DESENSE_CFG *)&cmd->params.cck_desense_cfg; + mlan_ds_misc_cck_desense_cfg *cfg = + (mlan_ds_misc_cck_desense_cfg *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_CCK_DESENSE_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_CMD_CCK_DESENSE_CFG) + + S_DS_GEN); + cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + cfg_cmd->mode = wlan_cpu_to_le16(cfg->mode); + cfg_cmd->margin = (t_s8)cfg->margin; + cfg_cmd->ceil_thresh = (t_s8)cfg->ceil_thresh; + cfg_cmd->num_on_intervals = (t_u8)cfg->num_on_intervals; + cfg_cmd->num_off_intervals = (t_u8)cfg->num_off_intervals; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of CCK Desense 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_cck_desense_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CMD_CCK_DESENSE_CFG *cfg_cmd = + (HostCmd_DS_CMD_CCK_DESENSE_CFG *)&resp->params.cck_desense_cfg; + mlan_ds_misc_cfg *misc_cfg = MNULL; + + ENTER(); + + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.cck_desense_cfg.mode = + wlan_le16_to_cpu(cfg_cmd->mode); + misc_cfg->param.cck_desense_cfg.margin = (t_s8)cfg_cmd->margin; + misc_cfg->param.cck_desense_cfg.ceil_thresh = + (t_s8)cfg_cmd->ceil_thresh; + misc_cfg->param.cck_desense_cfg.num_on_intervals = + (t_u8)cfg_cmd->num_on_intervals; + misc_cfg->param.cck_desense_cfg.num_off_intervals = + (t_u8)cfg_cmd->num_off_intervals; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function sends dynamic bandwidth command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd Hostcmd ID + * @param cmd_action Command action + * @param pdata_buf A void pointer to information buffer + * @return N/A + */ +mlan_status wlan_cmd_config_dyn_bw(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_DYN_BW *dyn_bw_cmd = &cmd->params.dyn_bw; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_DYN_BW); + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_DYN_BW); + dyn_bw_cmd->action = wlan_cpu_to_le16(cmd_action); + dyn_bw_cmd->dyn_bw = wlan_cpu_to_le16(*(t_u16 *)pdata_buf); + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of dyn_bw + * + * @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_dyn_bw(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *cfg = MNULL; + HostCmd_DS_DYN_BW *dyn_bw = &resp->params.dyn_bw; + + ENTER(); + if (pioctl_buf && + (wlan_le16_to_cpu(dyn_bw->action) == HostCmd_ACT_GEN_GET)) { + cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + cfg->param.dyn_bw = wlan_le16_to_cpu(dyn_bw->dyn_bw); + PRINTM(MCMND, "Get dynamic bandwidth 0x%x\n", + cfg->param.dyn_bw); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of CHAN_TRPC_CONFIG + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_get_chan_trpc_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_CHANNEL_TRPC_CONFIG *trpc_cfg = &cmd->params.ch_trpc_config; + mlan_ds_misc_chan_trpc_cfg *cfg = + (mlan_ds_misc_chan_trpc_cfg *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CHANNEL_TRPC_CONFIG); + trpc_cfg->action = wlan_cpu_to_le16(cmd_action); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_CHANNEL_TRPC_CONFIG) + + S_DS_GEN); + trpc_cfg->sub_band = wlan_cpu_to_le16(cfg->sub_band); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +/** + * @brief This function prepares command of LOW_POWER_MODE_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 or MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_set_get_low_power_mode_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_LOW_POWER_MODE_CFG *lpm_cfg = &cmd->params.lpm_cfg; + t_u16 lpm = *(t_u16 *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_LOW_POWER_MODE_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_LOW_POWER_MODE_CFG) + + S_DS_GEN); + lpm_cfg->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) + lpm_cfg->lpm = wlan_cpu_to_le16(lpm); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of low power mode + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + + */ +mlan_status wlan_ret_set_get_low_power_mode_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_power_cfg *cfg = MNULL; + HostCmd_DS_LOW_POWER_MODE_CFG *lpm_cfg = &resp->params.lpm_cfg; + + ENTER(); + + if (pioctl_buf && + (wlan_le16_to_cpu(lpm_cfg->action) == HostCmd_ACT_GEN_GET)) { + cfg = (mlan_ds_power_cfg *)pioctl_buf->pbuf; + cfg->param.lpm = wlan_le16_to_cpu(lpm_cfg->lpm); + PRINTM(MCMND, "Get low power mode %d\n", cfg->param.lpm); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +/** + * @brief This function handles the command response of + * packet aggregation + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_get_chan_trpc_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc = MNULL; + HostCmd_DS_CHANNEL_TRPC_CONFIG *trpc_cfg = &resp->params.ch_trpc_config; + mlan_ds_misc_chan_trpc_cfg *cfg = MNULL; + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + if (pioctl_buf) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + cfg = (mlan_ds_misc_chan_trpc_cfg *)&(misc->param.trpc_cfg); + cfg->sub_band = wlan_le16_to_cpu(trpc_cfg->sub_band); + cfg->length = wlan_le16_to_cpu(resp->size); + memcpy_ext(pmadapter, cfg->trpc_buf, (t_u8 *)resp, cfg->length, + sizeof(cfg->trpc_buf)); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of RANGE_EXT + * + * @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_range_ext(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_RANGE_EXT *range_ext = &cmd->params.range_ext; + t_u8 mode = *(t_u8 *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_RANGE_EXT); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_RANGE_EXT) + S_DS_GEN); + range_ext->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) + range_ext->mode = mode; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of RANGE_EXT + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_range_ext(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc_cfg = MNULL; + HostCmd_DS_RANGE_EXT *range_ext = &resp->params.range_ext; + + ENTER(); + + if (pioctl_buf && + (wlan_le16_to_cpu(range_ext->action) == HostCmd_ACT_GEN_GET)) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.range_ext_mode = range_ext->mode; + PRINTM(MCMND, "Get range ext mode %d\n", + misc_cfg->param.range_ext_mode); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_decl.h b/mxm_wifiex/wlan_src/mlan/mlan_decl.h new file mode 100644 index 0000000..48169d7 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_decl.h @@ -0,0 +1,1988 @@ +/** @file mlan_decl.h + * + * @brief This file declares the generic data structures and APIs. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 _MLAN_DECL_H_ +#define _MLAN_DECL_H_ + +/** MLAN release version */ +#define MLAN_RELEASE_VERSION "214" + +/** Re-define generic data types for MLAN/MOAL */ +/** Signed char (1-byte) */ +typedef signed char t_s8, *t_ps8; +/** Unsigned char (1-byte) */ +typedef unsigned char t_u8, *t_pu8; +/** Signed short (2-bytes) */ +typedef short t_s16, *t_ps16; +/** Unsigned short (2-bytes) */ +typedef unsigned short t_u16, *t_pu16; +/** Signed long (4-bytes) */ +typedef int t_s32, *t_ps32; +/** Unsigned long (4-bytes) */ +typedef unsigned int t_u32, *t_pu32; +/** Signed long long 8-bytes) */ +typedef long long t_s64, *t_ps64; +/** Unsigned long long 8-bytes) */ +typedef unsigned long long t_u64, *t_pu64; +/** Void pointer (4-bytes) */ +typedef void t_void, *t_pvoid; +/** 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) + +#ifndef MACSTR +/** MAC address security format */ +#define MACSTR "%02x:XX:XX:XX:%02x:%02x" +#endif + +#ifndef MAC2STR +/** MAC address security print arguments */ +#define MAC2STR(a) (a)[0], (a)[4], (a)[5] +#endif + +#ifndef FULL_MACSTR +#define FULL_MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif +#ifndef FULL_MAC2STR +#define FULL_MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#endif + +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) (((p) + ((a)-1)) & ~((a)-1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** Return the byte offset of a field in the given structure */ +#define MLAN_FIELD_OFFSET(type, field) ((t_u32)(t_ptr) & (((type *)0)->field)) +/** Return aligned offset */ +#define OFFSET_ALIGN_ADDR(p, a) (t_u32)(ALIGN_ADDR(p, a) - (t_ptr)p) + +#if defined(WIFI_DIRECT_SUPPORT) +/** Maximum BSS numbers */ +#define MLAN_MAX_BSS_NUM (16) +#else +/** Maximum BSS numbers */ +#define MLAN_MAX_BSS_NUM (2) +#endif + +/** NET IP alignment */ +#define MLAN_NET_IP_ALIGN 2 + +/** DMA alignment */ +/* SDIO3.0 Inrevium Adapter require 32 bit DMA alignment */ +#define DMA_ALIGNMENT 32 + +/** max size of TxPD */ +#define MAX_TXPD_SIZE 32 + +/** Minimum data header length */ +#define MLAN_MIN_DATA_HEADER_LEN (DMA_ALIGNMENT + MAX_TXPD_SIZE) + +/** rx data header length */ +#define MLAN_RX_HEADER_LEN MLAN_MIN_DATA_HEADER_LEN + +/** This is current limit on Maximum Tx AMPDU allowed */ +#define MLAN_MAX_TX_BASTREAM_SUPPORTED 16 +#define MLAN_MAX_TX_BASTREAM_DEFAULT 2 +/** This is current limit on Maximum Rx AMPDU allowed */ +#define MLAN_MAX_RX_BASTREAM_SUPPORTED 16 + +#ifdef STA_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_STA_AMPDU_DEF_TXWINSIZE 64 +/** Default Win size attached during ADDBA response */ +#define MLAN_STA_AMPDU_DEF_RXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_STA_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_UAP_AMPDU_DEF_TXWINSIZE 64 +/** Default Win size attached during ADDBA response */ +#define MLAN_UAP_AMPDU_DEF_RXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_UAP_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif /* UAP_SUPPORT */ + +#ifdef WIFI_DIRECT_SUPPORT +/** WFD use the same window size for tx/rx */ +#define MLAN_WFD_AMPDU_DEF_TXRXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_WFD_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif + +/** Block ack timeout value */ +#define MLAN_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff +/** Maximum Tx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_TXWINSIZE 0x3ff +/** Maximum Rx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_RXWINSIZE 0x3ff + +/** Rate index for HR/DSSS 0 */ +#define MLAN_RATE_INDEX_HRDSSS0 0 +/** Rate index for HR/DSSS 3 */ +#define MLAN_RATE_INDEX_HRDSSS3 3 +/** Rate index for OFDM 0 */ +#define MLAN_RATE_INDEX_OFDM0 4 +/** Rate index for OFDM 7 */ +#define MLAN_RATE_INDEX_OFDM7 11 +/** Rate index for MCS 0 */ +#define MLAN_RATE_INDEX_MCS0 0 +/** Rate index for MCS 2 */ +#define MLAN_RATE_INDEX_MCS2 2 +/** Rate index for MCS 4 */ +#define MLAN_RATE_INDEX_MCS4 4 +/** Rate index for MCS 7 */ +#define MLAN_RATE_INDEX_MCS7 7 +/** Rate index for MCS 9 */ +#define MLAN_RATE_INDEX_MCS9 9 +/** Rate index for MCS11 */ +#define MLAN_RATE_INDEX_MCS11 11 +/** Rate index for MCS15 */ +#define MLAN_RATE_INDEX_MCS15 15 +/** Rate index for MCS 32 */ +#define MLAN_RATE_INDEX_MCS32 32 +/** Rate index for MCS 127 */ +#define MLAN_RATE_INDEX_MCS127 127 +#define MLAN_RATE_NSS1 1 +#define MLAN_RATE_NSS2 2 + +/** 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 +#define MLAN_RATE_BITMAP_NSS1_MCS0 160 +#define MLAN_RATE_BITMAP_NSS1_MCS9 169 +#define MLAN_RATE_BITMAP_NSS2_MCS0 176 +#define MLAN_RATE_BITMAP_NSS2_MCS9 185 + +/** MU beamformer */ +#define DEFALUT_11AC_CAP_BEAMFORMING_RESET_MASK (MBIT(19)) + +/** Size of rx data buffer 4096+256 */ +#define MLAN_RX_DATA_BUF_SIZE 4352 + +/** Size of command buffer */ +/** because cal_data_size 2.4 k */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (4 * 1024) +/** Size of rx command buffer */ +#define MLAN_RX_CMD_BUF_SIZE MRVDRV_SIZE_OF_CMD_BUFFER +/** Upload size */ +#define WLAN_UPLD_SIZE MRVDRV_SIZE_OF_CMD_BUFFER + +#if defined(PCIE) +#define MLAN_SSU_MAX_PKT_SIZE (283 * 4) +#define MLAN_SSU_HEADER_SIZE 256 +/** + * Size of DMA buffer to collect 10ms SSU data: + * 2500 spectral packets, plus header + */ +#define MLAN_SSU_BUF_SIZE_1MS (MLAN_SSU_MAX_PKT_SIZE * 250) +#define MLAN_SSU_BUF_SIZE (MLAN_SSU_HEADER_SIZE + MLAN_SSU_BUF_SIZE_1MS * 10) +#define MLAN_SSU_BUF_SIZE_HOST (MLAN_SSU_BUF_SIZE) +#endif + +/** driver initial the fw reset */ +#define FW_RELOAD_SDIO_INBAND_RESET 1 +/** out band reset trigger reset, no interface re-emulation */ +#define FW_RELOAD_NO_EMULATION 2 +/** out band reset with interface re-emulation */ +#define FW_RELOAD_WITH_EMULATION 3 +#ifdef PCIE +/** pcie card reset */ +#define FW_RELOAD_PCIE_RESET 4 +#endif + +#ifdef USB +#define MLAN_USB_BLOCK_SIZE (512) +#define MLAN_USB_AGGR_MODE_NUM (0) +#define MLAN_USB_AGGR_MODE_LEN (1) +#define MLAN_USB_AGGR_MODE_LEN_V2 (2) +#define MLAN_USB_TX_AGGR_MAX_LEN (16000) +#define MLAN_USB_TX_AGGR_MAX_NUM 10 +#define MLAN_USB_TX_AGGR_V2_ALIGN 4 +#define MLAN_USB_TX_AGGR_HEADER 4 +#define MLAN_USB_MAX_PKT_SIZE (MLAN_USB_BLOCK_SIZE * 4) + +#define MLAN_USB_RX_ALIGN_SIZE MLAN_USB_BLOCK_SIZE +#define MLAN_USB_RX_MAX_AGGR_NUM (8) +#define MLAN_USB_RX_DEAGGR_TIMEOUT_USEC (200) + +#define MLAN_USB_TX_AGGR_ALIGN (MLAN_USB_BLOCK_SIZE * 4) +#define MLAN_USB_TX_MAX_AGGR_NUM (8) +#define MLAN_USB_TX_MAX_AGGR_SIZE \ + (MLAN_USB_BLOCK_SIZE * 4 * MLAN_USB_TX_MAX_AGGR_NUM) +#define MLAN_USB_TX_MIN_AGGR_TIMEOUT (1) +#define MLAN_USB_TX_MAX_AGGR_TIMEOUT (4) +#define MLAN_USB_TX_AGGR_TIMEOUT_MSEC MLAN_USB_TX_MIN_AGGR_TIMEOUT +#define MLAN_USB_TX_AGGR_TIMEOUT_DYN (0xFFFF) +#endif /*USB*/ + +/** MLAN MAC Address Length */ +#define MLAN_MAC_ADDR_LENGTH (6) +/** MLAN 802.11 MAC Address */ +typedef t_u8 mlan_802_11_mac_addr[MLAN_MAC_ADDR_LENGTH]; + +/** MLAN Maximum SSID Length */ +#define MLAN_MAX_SSID_LENGTH (32) + +/** RTS/FRAG related defines */ +/** Minimum RTS value */ +#define MLAN_RTS_MIN_VALUE (0) +/** Maximum RTS value */ +#define MLAN_RTS_MAX_VALUE (2347) +/** Minimum FRAG value */ +#define MLAN_FRAG_MIN_VALUE (256) +/** Maximum FRAG value */ +#define MLAN_FRAG_MAX_VALUE (2346) + +/** Minimum tx retry count */ +#define MLAN_TX_RETRY_MIN (0) +/** Maximum tx retry count */ +#define MLAN_TX_RETRY_MAX (14) + +/** max Wmm AC queues */ +#define MAX_AC_QUEUES 4 + +#ifdef SDIO +/** 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 MLAN_RX_DATA_BUF_SIZE +/** SDIO MP aggr pkt limit */ +#define SDIO_MP_AGGR_DEF_PKT_LIMIT (16) +/** max SDIO MP aggr pkt limit */ +#define SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX (16) + +/** SDIO IO Port mask */ +#define MLAN_SDIO_IO_PORT_MASK 0xfffff +/** SDIO Block/Byte mode mask */ +#define MLAN_SDIO_BYTE_MODE_MASK 0x80000000 +#endif /* SDIO */ + +/** SD Interface */ +#define INTF_SD MBIT(0) +#define IS_SD(ct) (ct & (INTF_SD << 8)) +/** PCIE Interface */ +#define INTF_PCIE MBIT(1) +#define IS_PCIE(ct) (ct & (INTF_PCIE << 8)) +/** USB Interface */ +#define INTF_USB MBIT(2) +#define IS_USB(ct) (ct & (INTF_USB << 8)) + +/** 8887 card type */ +#define CARD_TYPE_8887 0x01 +/** 8897 card type */ +#define CARD_TYPE_8897 0x02 +/** 8977 card type */ +#define CARD_TYPE_8977 0x03 +/** 8997 card type */ +#define CARD_TYPE_8997 0x04 +/** 8987 card type */ +#define CARD_TYPE_8987 0x05 +/** 9098 card type */ +#define CARD_TYPE_9098 0x06 +/** 9097 card type */ +#define CARD_TYPE_9097 0x07 +/** 8978 card type */ +#define CARD_TYPE_8978 0x08 + +/** 9098 A0 reverion num */ +#define CHIP_9098_REV_A0 1 +#define CHIP_9098_REV_A1 2 +/** 9097 CHIP REV */ +#define CHIP_9097_REV_B0 1 + +#define INTF_MASK 0xff +#define CARD_TYPE_MASK 0xff + +#ifdef SDIO +/** SD8887 card type */ +#define CARD_TYPE_SD8887 (CARD_TYPE_8887 | (INTF_SD << 8)) +/** SD8897 card type */ +#define CARD_TYPE_SD8897 (CARD_TYPE_8897 | (INTF_SD << 8)) +/** SD8977 card type */ +#define CARD_TYPE_SD8977 (CARD_TYPE_8977 | (INTF_SD << 8)) +/** SD8978 card type */ +#define CARD_TYPE_SD8978 (CARD_TYPE_8978 | (INTF_SD << 8)) +/** SD8997 card type */ +#define CARD_TYPE_SD8997 (CARD_TYPE_8997 | (INTF_SD << 8)) +/** SD8987 card type */ +#define CARD_TYPE_SD8987 (CARD_TYPE_8987 | (INTF_SD << 8)) +/** SD9097 card type */ +#define CARD_TYPE_SD9097 (CARD_TYPE_9097 | (INTF_SD << 8)) +/** SD9098 card type */ +#define CARD_TYPE_SD9098 (CARD_TYPE_9098 | (INTF_SD << 8)) + +#define IS_SD8887(ct) (CARD_TYPE_SD8887 == (ct)) +#define IS_SD8897(ct) (CARD_TYPE_SD8897 == (ct)) +#define IS_SD8977(ct) (CARD_TYPE_SD8977 == (ct)) +#define IS_SD8978(ct) (CARD_TYPE_SD8978 == (ct)) +#define IS_SD8997(ct) (CARD_TYPE_SD8997 == (ct)) +#define IS_SD8987(ct) (CARD_TYPE_SD8987 == (ct)) +#define IS_SD9097(ct) (CARD_TYPE_SD9097 == (ct)) +#define IS_SD9098(ct) (CARD_TYPE_SD9098 == (ct)) + +/** SD8887 Card */ +#define CARD_SD8887 "SD8887" +/** SD8897 Card */ +#define CARD_SD8897 "SD8897" +/** SD8977 Card */ +#define CARD_SD8977 "SD8977" +/** SD8978 Card */ +#define CARD_SD8978 "SD8978" +/** SD8997 Card */ +#define CARD_SD8997 "SD8997" +/** SD8987 Card */ +#define CARD_SD8987 "SD8987" +/** SD9097 Card */ +#define CARD_SD9097 "SD9097" +/** SD9098 Card */ +#define CARD_SD9098 "SD9098" +#endif + +#ifdef PCIE +/** PCIE8897 card type */ +#define CARD_TYPE_PCIE8897 (CARD_TYPE_8897 | (INTF_PCIE << 8)) +/** PCIE8997 card type */ +#define CARD_TYPE_PCIE8997 (CARD_TYPE_8997 | (INTF_PCIE << 8)) +/** PCIE9097 card type */ +#define CARD_TYPE_PCIE9097 (CARD_TYPE_9097 | (INTF_PCIE << 8)) +/** PCIE9098 card type */ +#define CARD_TYPE_PCIE9098 (CARD_TYPE_9098 | (INTF_PCIE << 8)) + +#define IS_PCIE8897(ct) (CARD_TYPE_PCIE8897 == (ct)) +#define IS_PCIE8997(ct) (CARD_TYPE_PCIE8997 == (ct)) +#define IS_PCIE9097(ct) (CARD_TYPE_PCIE9097 == (ct)) +#define IS_PCIE9098(ct) (CARD_TYPE_PCIE9098 == (ct)) + +/** PCIE8897 Card */ +#define CARD_PCIE8897 "PCIE8897" +/** PCIE8997 Card */ +#define CARD_PCIE8997 "PCIE8997" +/** PCIE9097 Card */ +#define CARD_PCIE9097 "PCIE9097" +/** PCIE9000S Card */ +#define CARD_PCIE9000S "PCIE9000S" +/** PCIE9098 Card */ +#define CARD_PCIE9098 "PCIE9098" +#endif + +#ifdef USB +/** USB8897 card type */ +#define CARD_TYPE_USB8897 (CARD_TYPE_8897 | (INTF_USB << 8)) +/** USB8997 card type */ +#define CARD_TYPE_USB8997 (CARD_TYPE_8997 | (INTF_USB << 8)) +/** USB8978 card type */ +#define CARD_TYPE_USB8978 (CARD_TYPE_8978 | (INTF_USB << 8)) +/** USB9098 card type */ +#define CARD_TYPE_USB9098 (CARD_TYPE_9098 | (INTF_USB << 8)) +/** USB9097 card type */ +#define CARD_TYPE_USB9097 (CARD_TYPE_9097 | (INTF_USB << 8)) + +#define IS_USB8897(ct) (CARD_TYPE_USB8897 == (ct)) +#define IS_USB8997(ct) (CARD_TYPE_USB8997 == (ct)) +#define IS_USB8978(ct) (CARD_TYPE_USB8978 == (ct)) +#define IS_USB9098(ct) (CARD_TYPE_USB9098 == (ct)) +#define IS_USB9097(ct) (CARD_TYPE_USB9097 == (ct)) + +/** USB8897 Card */ +#define CARD_USB8897 "USB8897" +/** USB8997 Card */ +#define CARD_USB8997 "USB8997" +/** USB8978 Card */ +#define CARD_USB8978 "USB8978" +/** USB9098 Card */ +#define CARD_USB9098 "USB9098" +/** USB9097 Card */ +#define CARD_USB9097 "USB9097" +#endif + +#define IS_CARD8887(ct) (CARD_TYPE_8887 == ((ct)&0xf)) +#define IS_CARD8897(ct) (CARD_TYPE_8897 == ((ct)&0xf)) +#define IS_CARD8977(ct) (CARD_TYPE_8977 == ((ct)&0xf)) +#define IS_CARD8997(ct) (CARD_TYPE_8997 == ((ct)&0xf)) +#define IS_CARD8987(ct) (CARD_TYPE_8987 == ((ct)&0xf)) +#define IS_CARD9098(ct) (CARD_TYPE_9098 == ((ct)&0xf)) +#define IS_CARD9097(ct) (CARD_TYPE_9097 == ((ct)&0xf)) + +typedef struct _card_type_entry { + t_u16 card_type; + t_u16 func_id; + char *name; +} card_type_entry; + +#if defined(SDIO) || defined(PCIE) +/** Max retry number of IO write */ +#define MAX_WRITE_IOMEM_RETRY 2 +#endif /* SDIO || PCIE */ + +#ifdef PCIE +typedef enum { + PCIE_INT_MODE_LEGACY = 0, + PCIE_INT_MODE_MSI, + PCIE_INT_MODE_MSIX, + PCIE_INT_MODE_MAX, +} PCIE_INT_MODE; +#endif /* PCIE */ + +/** 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 USB +/** Buffer flag for deaggregated rx packet */ +#define MLAN_BUF_FLAG_RX_DEAGGR MBIT(5) + +/** Buffer flag for sleep confirm resp packet */ +#define MLAN_BUF_FLAG_SLEEPCFM_RESP MBIT(6) + +/** Buffer flag for USB TX AGGR */ +#define MLAN_BUF_FLAG_USB_TX_AGGR MBIT(7) +#endif + +/** Buffer flag for TCP_ACK */ +#define MLAN_BUF_FLAG_TCP_ACK MBIT(9) + +/** Buffer flag for TX_STATUS */ +#define MLAN_BUF_FLAG_TX_STATUS MBIT(10) + +/** Buffer flag for NULL data packet */ +#define MLAN_BUF_FLAG_NULL_PKT MBIT(12) +/** Buffer flag for Diag pkt */ +#define MLAN_BUF_FLAG_DIAG_BUF MBIT(13) + +#define MLAN_BUF_FLAG_TX_CTRL MBIT(14) + +#ifdef DEBUG_LEVEL1 +/** Debug level bit definition */ +#define MMSG MBIT(0) +#define MFATAL MBIT(1) +#define MERROR MBIT(2) +#define MDATA MBIT(3) +#define MCMND MBIT(4) +#define MEVENT MBIT(5) +#define MINTR MBIT(6) +#define MIOCTL MBIT(7) + +#define MREG_D MBIT(9) + +#define MMPA_D MBIT(15) +#define MDAT_D MBIT(16) +#define MCMD_D MBIT(17) +#define MEVT_D MBIT(18) +#define MFW_D MBIT(19) +#define MIF_D MBIT(20) + +#define MENTRY MBIT(28) +#define MWARN MBIT(29) +#define MINFO MBIT(30) +#define MHEX_DUMP MBIT(31) +#endif /* DEBUG_LEVEL1 */ + +/** Memory allocation type: DMA */ +#define MLAN_MEM_DMA MBIT(0) + +/** Default memory allocation flag */ +#define MLAN_MEM_DEF 0 + +/** mlan_status */ +typedef enum _mlan_status { + MLAN_STATUS_FAILURE = 0xffffffff, + MLAN_STATUS_SUCCESS = 0, + MLAN_STATUS_PENDING, + MLAN_STATUS_RESOURCE, +#ifdef USB + /* Status pending and no resource */ + MLAN_STATUS_PRESOURCE, +#endif + MLAN_STATUS_COMPLETE, + MLAN_STATUS_FILE_ERR, +} mlan_status; + +/** mlan_error_code */ +typedef enum _mlan_error_code { + /** No error */ + MLAN_ERROR_NO_ERROR = 0, + /** Firmware/device errors below (MSB=0) */ + MLAN_ERROR_FW_NOT_READY = 0x00000001, + MLAN_ERROR_FW_BUSY = 0x00000002, + MLAN_ERROR_FW_CMDRESP = 0x00000003, + MLAN_ERROR_DATA_TX_FAIL = 0x00000004, + MLAN_ERROR_DATA_RX_FAIL = 0x00000005, + /** Driver errors below (MSB=1) */ + MLAN_ERROR_PKT_SIZE_INVALID = 0x80000001, + MLAN_ERROR_PKT_TIMEOUT = 0x80000002, + MLAN_ERROR_PKT_INVALID = 0x80000003, + MLAN_ERROR_CMD_INVALID = 0x80000004, + MLAN_ERROR_CMD_TIMEOUT = 0x80000005, + MLAN_ERROR_CMD_DNLD_FAIL = 0x80000006, + MLAN_ERROR_CMD_CANCEL = 0x80000007, + MLAN_ERROR_CMD_RESP_FAIL = 0x80000008, + MLAN_ERROR_CMD_ASSOC_FAIL = 0x80000009, + MLAN_ERROR_CMD_SCAN_FAIL = 0x8000000A, + MLAN_ERROR_IOCTL_INVALID = 0x8000000B, + MLAN_ERROR_IOCTL_FAIL = 0x8000000C, + MLAN_ERROR_EVENT_UNKNOWN = 0x8000000D, + MLAN_ERROR_INVALID_PARAMETER = 0x8000000E, + MLAN_ERROR_NO_MEM = 0x8000000F, + /** More to add */ +} mlan_error_code; + +/** mlan_buf_type */ +typedef enum _mlan_buf_type { + MLAN_BUF_TYPE_CMD = 1, + MLAN_BUF_TYPE_DATA, + MLAN_BUF_TYPE_EVENT, + MLAN_BUF_TYPE_RAW_DATA, +#ifdef SDIO + MLAN_BUF_TYPE_SPA_DATA, +#endif +} mlan_buf_type; + +#ifdef USB +/** mlan_usb_ep */ +typedef enum _mlan_usb_ep { + MLAN_USB_EP_CTRL = 0, + MLAN_USB_EP_CMD_EVENT = 1, + MLAN_USB_EP_DATA = 2, + MLAN_USB_EP_DATA_CH2 = 3, + MLAN_USB_EP_CMD_EVENT_IF2 = 4, + MLAN_USB_EP_DATA_IF2 = 5, + MLAN_USB_EP_DATA_CH2_IF2 = 6, +} mlan_usb_ep; + +/** Timeout in milliseconds for usb_bulk_msg function */ +#define MLAN_USB_BULK_MSG_TIMEOUT 100 +#endif /* USB */ + +/** 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 mask */ +#define BSS_ROLE_MASK (MBIT(0) | MBIT(1)) + +/** Get BSS role */ +#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_MASK) + +/** mlan_data_frame_type */ +typedef enum _mlan_data_frame_type { + MLAN_DATA_FRAME_TYPE_ETH_II = 0, + MLAN_DATA_FRAME_TYPE_802_11, +} mlan_data_frame_type; + +/** mlan_event_id */ +typedef enum _mlan_event_id { + /* Event generated by firmware (MSB=0) */ + MLAN_EVENT_ID_FW_UNKNOWN = 0x00000001, + MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED = 0x00000002, + MLAN_EVENT_ID_FW_ADHOC_LINK_LOST = 0x00000003, + MLAN_EVENT_ID_FW_DISCONNECTED = 0x00000004, + MLAN_EVENT_ID_FW_MIC_ERR_UNI = 0x00000005, + MLAN_EVENT_ID_FW_MIC_ERR_MUL = 0x00000006, + MLAN_EVENT_ID_FW_BCN_RSSI_LOW = 0x00000007, + MLAN_EVENT_ID_FW_BCN_RSSI_HIGH = 0x00000008, + MLAN_EVENT_ID_FW_BCN_SNR_LOW = 0x00000009, + MLAN_EVENT_ID_FW_BCN_SNR_HIGH = 0x0000000A, + MLAN_EVENT_ID_FW_MAX_FAIL = 0x0000000B, + MLAN_EVENT_ID_FW_DATA_RSSI_LOW = 0x0000000C, + MLAN_EVENT_ID_FW_DATA_RSSI_HIGH = 0x0000000D, + MLAN_EVENT_ID_FW_DATA_SNR_LOW = 0x0000000E, + MLAN_EVENT_ID_FW_DATA_SNR_HIGH = 0x0000000F, + MLAN_EVENT_ID_FW_LINK_QUALITY = 0x00000010, + MLAN_EVENT_ID_FW_PORT_RELEASE = 0x00000011, + MLAN_EVENT_ID_FW_PRE_BCN_LOST = 0x00000012, + MLAN_EVENT_ID_FW_DEBUG_INFO = 0x00000013, + MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE = 0x0000001A, + MLAN_EVENT_ID_FW_HS_WAKEUP = 0x0000001B, + MLAN_EVENT_ID_FW_BG_SCAN = 0x0000001D, + MLAN_EVENT_ID_FW_BG_SCAN_STOPPED = 0x0000001E, + MLAN_EVENT_ID_FW_WEP_ICV_ERR = 0x00000020, + MLAN_EVENT_ID_FW_STOP_TX = 0x00000021, + MLAN_EVENT_ID_FW_START_TX = 0x00000022, + MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN = 0x00000023, + MLAN_EVENT_ID_FW_RADAR_DETECTED = 0x00000024, + MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY = 0x00000025, + MLAN_EVENT_ID_FW_BW_CHANGED = 0x00000026, + MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED = 0x0000002B, + +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_UAP_FW_BSS_START = 0x0000002C, + MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE = 0x0000002D, + MLAN_EVENT_ID_UAP_FW_BSS_IDLE = 0x0000002E, + MLAN_EVENT_ID_UAP_FW_MIC_COUNTERMEASURES = 0x0000002F, + MLAN_EVENT_ID_UAP_FW_STA_CONNECT = 0x00000030, + MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT = 0x00000031, +#endif + + MLAN_EVENT_ID_FW_DUMP_INFO = 0x00000033, + + MLAN_EVENT_ID_FW_TX_STATUS = 0x00000034, + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE = 0x00000036, +#if defined(PCIE) + MLAN_EVENT_ID_SSU_DUMP_FILE = 0x00000039, +#endif /* SSU_SUPPORT */ + /* Event generated by MLAN driver (MSB=1) */ + MLAN_EVENT_ID_DRV_CONNECTED = 0x80000001, + MLAN_EVENT_ID_DRV_DEFER_HANDLING = 0x80000002, + MLAN_EVENT_ID_DRV_HS_ACTIVATED = 0x80000003, + MLAN_EVENT_ID_DRV_HS_DEACTIVATED = 0x80000004, + MLAN_EVENT_ID_DRV_MGMT_FRAME = 0x80000005, + MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM = 0x80000006, + MLAN_EVENT_ID_DRV_PASSTHRU = 0x80000007, + MLAN_EVENT_ID_DRV_SCAN_REPORT = 0x80000009, + MLAN_EVENT_ID_DRV_MEAS_REPORT = 0x8000000A, + MLAN_EVENT_ID_DRV_ASSOC_FAILURE_REPORT = 0x8000000B, + MLAN_EVENT_ID_DRV_REPORT_STRING = 0x8000000F, + MLAN_EVENT_ID_DRV_DBG_DUMP = 0x80000012, + MLAN_EVENT_ID_DRV_BGSCAN_RESULT = 0x80000013, + MLAN_EVENT_ID_DRV_FLUSH_RX_WORK = 0x80000015, + MLAN_EVENT_ID_DRV_DEFER_RX_WORK = 0x80000016, + MLAN_EVENT_ID_DRV_FT_RESPONSE = 0x80000018, + MLAN_EVENT_ID_DRV_FLUSH_MAIN_WORK = 0x80000019, +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_DRV_UAP_CHAN_INFO = 0x80000020, +#endif + MLAN_EVENT_ID_DRV_ASSOC_FAILURE_LOGGER = 0x80000026, + MLAN_EVENT_ID_DRV_ASSOC_SUCC_LOGGER = 0x80000027, + MLAN_EVENT_ID_DRV_DISCONNECT_LOGGER = 0x80000028, + MLAN_EVENT_ID_DRV_WIFI_STATUS = 0x80000029, + MLAN_EVENT_ID_STORE_HOST_CMD_RESP = 0x80000030, +} mlan_event_id; + +/** Data Structures */ +/** mlan_image data structure */ +typedef struct _mlan_fw_image { + /** Firmware image buffer pointer */ + t_u8 *pfw_buf; + /** Firmware image length */ + t_u32 fw_len; + /** Firmware reload flag */ + t_u8 fw_reload; +} mlan_fw_image, *pmlan_fw_image; + +/** MrvlIEtypesHeader_t */ +typedef MLAN_PACK_START struct _MrvlIEtypesHeader { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; +} MLAN_PACK_END MrvlIEtypesHeader_t; + +/** MrvlExtIEtypesHeader_t */ +typedef MLAN_PACK_START struct _MrvlExtIEtypesHeader { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; + /** ext id */ + t_u8 ext_id; +} MLAN_PACK_END MrvlExtIEtypesHeader_t; + +/** MrvlIEtypes_Data_t */ +typedef MLAN_PACK_START struct _MrvlExtIEtypes_Data_t { + /** Header */ + MrvlExtIEtypesHeader_t header; + /** Data */ + t_u8 data[]; +} MLAN_PACK_END MrvlExtIEtypes_Data_t; + +/** MrvlIEtypes_Data_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Data_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Data */ + t_u8 data[]; +} MLAN_PACK_END MrvlIEtypes_Data_t; + +#define OID_TYPE_CAL 0x2 +#define OID_TYPE_DPD 0xa +#define UNKNOW_DPD_LENGTH 0xffffffff + +/** Custom data structure */ +typedef struct _mlan_init_param { + /** DPD data buffer pointer */ + t_u8 *pdpd_data_buf; + /** DPD data length */ + t_u32 dpd_data_len; + /** region txpowerlimit cfg data buffer pointer */ + t_u8 *ptxpwr_data_buf; + /** region txpowerlimit cfg data length */ + t_u32 txpwr_data_len; + /** Cal data buffer pointer */ + t_u8 *pcal_data_buf; + /** Cal data length */ + t_u32 cal_data_len; + /** Other custom data */ +} mlan_init_param, *pmlan_init_param; + +/** channel type */ +enum mlan_channel_type { + CHAN_NO_HT, + CHAN_HT20, + CHAN_HT40MINUS, + CHAN_HT40PLUS, + CHAN_VHT80 +}; + +/** channel band */ +enum { BAND_2GHZ = 0, + BAND_5GHZ = 1, + BAND_4GHZ = 2, +}; + +/** channel offset */ +enum { SEC_CHAN_NONE = 0, + SEC_CHAN_ABOVE = 1, + SEC_CHAN_5MHZ = 2, + SEC_CHAN_BELOW = 3 }; + +/** channel bandwidth */ +enum { CHAN_BW_20MHZ = 0, + CHAN_BW_10MHZ, + CHAN_BW_40MHZ, + CHAN_BW_80MHZ, +}; + +/** scan mode */ +enum { SCAN_MODE_MANUAL = 0, + SCAN_MODE_ACS, + SCAN_MODE_USER, +}; + +/** max cac time 10 minutes */ +#define MAX_CAC_DWELL_TIME 600000 +/** default cac time 60 seconds */ +#define DEF_CAC_DWELL_TIME 60000 +/** start freq for 5G */ +#define START_FREQ_11A_BAND 5000 +/** DFS state */ +typedef enum _dfs_state_t { + /** Channel can be used, CAC (Channel Availability Check) must be done + before using it */ + DFS_USABLE = 0, + /** Channel is not available, radar was detected */ + DFS_UNAVAILABLE = 1, + /** Channel is Available, CAC is done and is free of radar */ + DFS_AVAILABLE = 2, +} dfs_state_t; + +typedef enum _dfs_w53_cfg_t { + /** DFS W53 Default Fw Value */ + DFS_W53_DEFAULT_FW = 0, + /** DFS W53 New W53 Rules/Standard */ + DFS_W53_NEW = 1, + /** DFS W53 Old W53 Rules/Standard */ + DFS_W53_OLD = 2 +} dfs_w53_cfg_t; + +/** Band_Config_t */ +typedef MLAN_PACK_START struct _Band_Config_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=user*/ + t_u8 scanMode : 2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset : 2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth : 2; + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand : 2; +#else + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand : 2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth : 2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset : 2; + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=Adoption mode*/ + t_u8 scanMode : 2; +#endif +} MLAN_PACK_END Band_Config_t; + +/** channel_band_t */ +typedef MLAN_PACK_START struct _chan_band_info { + /** Band Configuration */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** 11n flag */ + t_u8 is_11n_enabled; + /** center channel */ + t_u8 center_chan; + /** dfs channel flag */ + t_u8 is_dfs_chan; +} MLAN_PACK_END chan_band_info, *pchan_band_info; + +/** Channel usability flags */ +#define NXP_CHANNEL_NO_OFDM MBIT(9) +#define NXP_CHANNEL_NO_CCK MBIT(8) +#define NXP_CHANNEL_DISABLED MBIT(7) +/* BIT 5/6 resevered for FW */ +#define NXP_CHANNEL_NOHT160 MBIT(4) +#define NXP_CHANNEL_NOHT80 MBIT(3) +#define NXP_CHANNEL_NOHT40 MBIT(2) +#define NXP_CHANNEL_DFS MBIT(1) +#define NXP_CHANNEL_PASSIVE MBIT(0) + +/** CFP dynamic (non-const) elements */ +typedef struct _cfp_dyn_t { + /** extra flags to specify channel usability + * bit 9 : if set, channel is non-OFDM + * bit 8 : if set, channel is non-CCK + * bit 7 : if set, channel is disabled + * bit 5/6 resevered for FW + * bit 4 : if set, 160MHz on channel is disabled + * bit 3 : if set, 80MHz on channel is disabled + * bit 2 : if set, 40MHz on channel is disabled + * bit 1 : if set, channel is DFS channel + * bit 0 : if set, channel is passive + */ + t_u16 flags; + /** TRUE: Channel is blacklisted (do not use) */ + t_bool blacklist; +} cfp_dyn_t; + +/** Chan-Freq-TxPower mapping table*/ +typedef struct _chan_freq_power_t { + /** Channel Number */ + t_u16 channel; + /** Frequency of this Channel */ + t_u32 freq; + /** Max allowed Tx power level */ + t_u16 max_tx_power; + /** TRUE:radar detect required for BAND A or passive scan for BAND B/G; + * FALSE:radar detect not required for BAND A or active scan for BAND + * B/G*/ + t_bool passive_scan_or_radar_detect; + /** Elements associated to cfp that change at run-time */ + cfp_dyn_t dynamic; +} chan_freq_power_t; + +/** 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[]; +} mlan_event, *pmlan_event; + +/** mlan_cmdresp_event data structure */ +typedef struct _mlan_cmdresp_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; + /** resp buffer pointer */ + t_u8 *resp; +} mlan_cmdresp_event, *pmlan_cmdresp_event; + +/** csi event data structure */ + +/** mlan_ioctl_req data structure */ +typedef struct _mlan_ioctl_req { + /** Pointer to previous mlan_ioctl_req */ + struct _mlan_ioctl_req *pprev; + /** Pointer to next mlan_ioctl_req */ + struct _mlan_ioctl_req *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Request id */ + t_u32 req_id; + /** Action: set or get */ + t_u32 action; + /** Pointer to buffer */ + t_u8 *pbuf; + /** Length of buffer */ + t_u32 buf_len; + /** Length of the data read/written in buffer */ + t_u32 data_read_written; + /** Length of buffer needed */ + t_u32 buf_len_needed; + /** Reserved for MOAL module */ + t_ptr reserved_1; +} mlan_ioctl_req, *pmlan_ioctl_req; + +/** txpower structure */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl : 1; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign : 1; + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val : 6; +#else + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val : 6; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign : 1; + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl : 1; +#endif +} MLAN_PACK_END tx_power_t; +/* pkt_txctrl */ +typedef MLAN_PACK_START struct _pkt_txctrl { + /**Data rate in unit of 0.5Mbps */ + t_u16 data_rate; + /*Channel number to transmit the frame */ + t_u8 channel; + /** Bandwidth to transmit the frame*/ + t_u8 bw; + /** Power to be used for transmission*/ + union { + tx_power_t tp; + t_u8 val; + } tx_power; + /** Retry time of tx transmission*/ + t_u8 retry_limit; +} MLAN_PACK_END pkt_txctrl, *ppkt_txctrl; + +/** pkt_rxinfo */ +typedef MLAN_PACK_START struct _pkt_rxinfo { + /** Data rate of received paccket*/ + t_u16 data_rate; + /** Channel on which packet was received*/ + t_u8 channel; + /** Rx antenna*/ + t_u8 antenna; + /** Rx Rssi*/ + t_u8 rssi; +} MLAN_PACK_END pkt_rxinfo, *ppkt_rxinfo; + +/** mlan_buffer data structure */ +typedef struct _mlan_buffer { + /** Pointer to previous mlan_buffer */ + struct _mlan_buffer *pprev; + /** Pointer to next mlan_buffer */ + struct _mlan_buffer *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** Flags for this buffer */ + t_u32 flags; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Buffer descriptor, e.g. skb in Linux */ + t_void *pdesc; + /** Pointer to buffer */ + t_u8 *pbuf; +#ifdef PCIE + /** Physical address of the pbuf pointer */ + t_u64 buf_pa; + t_u32 total_pcie_buf_len; +#endif + /** Offset to data */ + t_u32 data_offset; + /** Data length */ + t_u32 data_len; + /** Buffer type: data, cmd, event etc. */ + mlan_buf_type buf_type; + + /** Fields below are valid for data packet only */ + /** QoS priority */ + t_u32 priority; + /** Time stamp when packet is received (seconds) */ + t_u32 in_ts_sec; + /** Time stamp when packet is received (micro seconds) */ + t_u32 in_ts_usec; + /** Time stamp when packet is processed (seconds) */ + t_u32 out_ts_sec; + /** Time stamp when packet is processed (micro seconds) */ + t_u32 out_ts_usec; + /** tx_seq_num */ + t_u32 tx_seq_num; + + /** Fields below are valid for MLAN module only */ + /** Pointer to parent mlan_buffer */ + struct _mlan_buffer *pparent; + /** Use count for this buffer */ + t_u32 use_count; + union { + pkt_txctrl tx_info; + pkt_rxinfo rx_info; + } u; +} mlan_buffer, *pmlan_buffer, **ppmlan_buffer; + +/** mlan_fw_info data structure */ +typedef struct _mlan_hw_info { + t_u32 fw_cap; +} mlan_hw_info, *pmlan_hw_info; + +/** mlan_bss_attr data structure */ +typedef struct _mlan_bss_attr { + /** BSS type */ + t_u32 bss_type; + /** Data frame type: Ethernet II, 802.11, etc. */ + t_u32 frame_type; + /** The BSS is active (non-0) or not (0). */ + t_u32 active; + /** BSS Priority */ + t_u32 bss_priority; + /** BSS number */ + t_u32 bss_num; + /** The BSS is virtual */ + t_u32 bss_virtual; +} mlan_bss_attr, *pmlan_bss_attr; + +/** bss tbl data structure */ +typedef struct _mlan_bss_tbl { + /** BSS Attributes */ + mlan_bss_attr bss_attr[MLAN_MAX_BSS_NUM]; +} mlan_bss_tbl, *pmlan_bss_tbl; + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/** Type enumeration for the command result */ +typedef MLAN_PACK_START enum _mlan_cmd_result_e { + MLAN_CMD_RESULT_SUCCESS = 0, + MLAN_CMD_RESULT_FAILURE = 1, + MLAN_CMD_RESULT_TIMEOUT = 2, + MLAN_CMD_RESULT_INVALID_DATA = 3 +} MLAN_PACK_END mlan_cmd_result_e; + +/** Type enumeration of WMM AC_QUEUES */ +typedef MLAN_PACK_START enum _mlan_wmm_ac_e { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VO +} MLAN_PACK_END mlan_wmm_ac_e; + +/** Type enumeration for the action field in the Queue Config command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_config_action_e { + MLAN_WMM_QUEUE_CONFIG_ACTION_GET = 0, + MLAN_WMM_QUEUE_CONFIG_ACTION_SET = 1, + MLAN_WMM_QUEUE_CONFIG_ACTION_DEFAULT = 2, + MLAN_WMM_QUEUE_CONFIG_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_config_action_e; + +/** Type enumeration for the action field in the queue stats command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_stats_action_e { + MLAN_WMM_STATS_ACTION_START = 0, + MLAN_WMM_STATS_ACTION_STOP = 1, + MLAN_WMM_STATS_ACTION_GET_CLR = 2, + MLAN_WMM_STATS_ACTION_SET_CFG = 3, /* Not currently used */ + MLAN_WMM_STATS_ACTION_GET_CFG = 4, /* Not currently used */ + MLAN_WMM_STATS_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_stats_action_e; + +/** + * @brief IOCTL structure for a Traffic stream status. + * + */ +typedef MLAN_PACK_START struct { + /** TSID: Range: 0->7 */ + t_u8 tid; + /** TSID specified is valid */ + t_u8 valid; + /** AC TSID is active on */ + t_u8 access_category; + /** UP specified for the TSID */ + t_u8 user_priority; + /** Power save mode for TSID: 0 (legacy), 1 (UAPSD) */ + t_u8 psb; + /** Upstream(0), Downlink(1), Bidirectional(3) */ + t_u8 flow_dir; + /** Medium time granted for the TSID */ + t_u16 medium_time; +} MLAN_PACK_END wlan_ioctl_wmm_ts_status_t, + /** Type definition of mlan_ds_wmm_ts_status for + MLAN_OID_WMM_CFG_TS_STATUS */ + mlan_ds_wmm_ts_status, *pmlan_ds_wmm_ts_status; + +/** Max Ie length */ +#define MAX_IE_SIZE 256 + +/** custom IE */ +typedef MLAN_PACK_START struct _custom_ie { + /** IE Index */ + t_u16 ie_index; + /** Mgmt Subtype Mask */ + t_u16 mgmt_subtype_mask; + /** IE Length */ + t_u16 ie_length; + /** IE buffer */ + t_u8 ie_buffer[MAX_IE_SIZE]; +} MLAN_PACK_END custom_ie; + +/** Max IE index to FW */ +#define MAX_MGMT_IE_INDEX_TO_FW 4 +/** Max IE index per BSS */ +#define MAX_MGMT_IE_INDEX 26 + +/** custom IE info */ +typedef MLAN_PACK_START struct _custom_ie_info { + /** size of buffer */ + t_u16 buf_size; + /** no of buffers of buf_size */ + t_u16 buf_count; +} MLAN_PACK_END custom_ie_info; + +/** TLV buffer : Max Mgmt IE */ +typedef MLAN_PACK_START struct _tlvbuf_max_mgmt_ie { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** No of tuples */ + t_u16 count; + /** custom IE info tuples */ + custom_ie_info info[MAX_MGMT_IE_INDEX]; +} MLAN_PACK_END tlvbuf_max_mgmt_ie; + +/** TLV buffer : custom IE */ +typedef MLAN_PACK_START struct _tlvbuf_custom_ie { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** IE data */ + custom_ie ie_data_list[MAX_MGMT_IE_INDEX_TO_FW]; + /** Max mgmt IE TLV */ + tlvbuf_max_mgmt_ie max_mgmt_ie; +} MLAN_PACK_END mlan_ds_misc_custom_ie; + +/** channel width */ +typedef enum wifi_channel_width { + WIFI_CHAN_WIDTH_20 = 0, + WIFI_CHAN_WIDTH_40 = 1, + WIFI_CHAN_WIDTH_80 = 2, + WIFI_CHAN_WIDTH_160 = 3, + WIFI_CHAN_WIDTH_80P80 = 4, + WIFI_CHAN_WIDTH_5 = 5, + WIFI_CHAN_WIDTH_10 = 6, + WIFI_CHAN_WIDTH_INVALID = -1 +} wifi_channel_width_t; + +/** channel information */ +typedef struct { + /** channel width (20, 40, 80, 80+80, 160) */ + wifi_channel_width_t width; + /** primary 20 MHz channel */ + int center_freq; + /** center frequency (MHz) first segment */ + int center_freq0; + /** center frequency (MHz) second segment */ + int center_freq1; +} wifi_channel_info; + +/** wifi rate */ +typedef struct { + /** 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */ + t_u32 preamble : 3; + /** 0:1x1, 1:2x2, 3:3x3, 4:4x4 */ + t_u32 nss : 2; + /** 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz */ + t_u32 bw : 3; + /** OFDM/CCK rate code would be as per ieee std in the units of 0.5mbps + */ + /** HT/VHT it would be mcs index */ + t_u32 rateMcsIdx : 8; + /** reserved */ + t_u32 reserved : 16; + /** units of 100 Kbps */ + t_u32 bitrate; +} wifi_rate; + +/** wifi Preamble type */ +typedef enum { + WIFI_PREAMBLE_LEGACY = 0x1, + WIFI_PREAMBLE_HT = 0x2, + WIFI_PREAMBLE_VHT = 0x4 +} wifi_preamble; + +/** timeval */ +typedef struct { + /** Time (seconds) */ + t_u32 time_sec; + /** Time (micro seconds) */ + t_u32 time_usec; +} wifi_timeval; + +#define MAX_NUM_RATE 32 +#define MAX_RADIO 2 +#define MAX_NUM_CHAN 1 +#define VHT_NUM_SUPPORT_MCS 10 +#define MCS_NUM_SUPP 16 + +#define BUF_MAXLEN 4096 +/** connection state */ +typedef enum { + MLAN_DISCONNECTED = 0, + MLAN_AUTHENTICATING = 1, + MLAN_ASSOCIATING = 2, + MLAN_ASSOCIATED = 3, + /** if done by firmware/driver */ + MLAN_EAPOL_STARTED = 4, + /** if done by firmware/driver */ + MLAN_EAPOL_COMPLETED = 5, +} mlan_connection_state; +/** roam state */ +typedef enum { + MLAN_ROAMING_IDLE = 0, + MLAN_ROAMING_ACTIVE = 1, +} mlan_roam_state; +/** interface mode */ +typedef enum { + MLAN_INTERFACE_STA = 0, + MLAN_INTERFACE_SOFTAP = 1, + MLAN_INTERFACE_IBSS = 2, + MLAN_INTERFACE_P2P_CLIENT = 3, + MLAN_INTERFACE_P2P_GO = 4, + MLAN_INTERFACE_NAN = 5, + MLAN_INTERFACE_MESH = 6, +} mlan_interface_mode; + +/** set for QOS association */ +#define MLAN_CAPABILITY_QOS 0x00000001 +/** set for protected association (802.11 beacon frame control protected bit + * set) */ +#define MLAN_CAPABILITY_PROTECTED 0x00000002 +/** set if 802.11 Extended Capabilities element interworking bit is set */ +#define MLAN_CAPABILITY_INTERWORKING 0x00000004 +/** set for HS20 association */ +#define MLAN_CAPABILITY_HS20 0x00000008 +/** set is 802.11 Extended Capabilities element UTF-8 SSID bit is set */ +#define MLAN_CAPABILITY_SSID_UTF8 0x00000010 +/** set is 802.11 Country Element is present */ +#define MLAN_CAPABILITY_COUNTRY 0x00000020 + +/** link layer status */ +typedef struct { + /** interface mode */ + mlan_interface_mode mode; + /** interface mac address (self) */ + t_u8 mac_addr[6]; + /** connection state (valid for STA, CLI only) */ + mlan_connection_state state; + /** roaming state */ + mlan_roam_state roaming; + /** WIFI_CAPABILITY_XXX (self) */ + t_u32 capabilities; + /** null terminated SSID */ + t_u8 ssid[33]; + /** bssid */ + t_u8 bssid[6]; + /** country string advertised by AP */ + t_u8 ap_country_str[3]; + /** country string for this association */ + t_u8 country_str[3]; +} mlan_interface_link_layer_info, *mlan_interface_handle; + +/** channel statistics */ +typedef struct { + /** channel */ + wifi_channel_info channel; + /** msecs the radio is awake (32 bits number accruing over time) */ + t_u32 on_time; + /** msecs the CCA register is busy (32 bits number accruing over time) + */ + t_u32 cca_busy_time; +} wifi_channel_stat; + +#define timeval_to_msec(timeval) \ + (t_u64)((t_u64)(timeval.time_sec) * 1000 + \ + (t_u64)(timeval.time_usec) / 1000) +#define timeval_to_usec(timeval) \ + (t_u64)((t_u64)(timeval.time_sec) * 1000 * 1000 + \ + (t_u64)(timeval.time_usec)) +#define is_zero_timeval(timeval) \ + ((timeval.time_sec == 0) && (timeval.time_usec == 0)) + +/** radio statistics */ +typedef struct { + /** wifi radio (if multiple radio supported) */ + int radio; + /** msecs the radio is awake (32 bits number accruing over time) */ + t_u32 on_time; + /** msecs the radio is transmitting (32 bits number accruing over time) + */ + t_u32 tx_time; + /** TBD: num_tx_levels: number of radio transmit power levels */ + t_u32 reserved0; + /** TBD: tx_time_per_levels: pointer to an array of radio transmit per + * power levels in msecs accured over time */ + /* t_u32 *reserved1;*/ + /** msecs the radio is in active receive (32 bits number accruing over + * time) */ + t_u32 rx_time; + /** msecs the radio is awake due to all scan (32 bits number accruing + * over time) */ + t_u32 on_time_scan; + /** msecs the radio is awake due to NAN (32 bits number accruing over + * time) */ + t_u32 on_time_nbd; + /** msecs the radio is awake due to G?scan (32 bits number accruing over + * time) */ + t_u32 on_time_gscan; + /** msecs the radio is awake due to roam?scan (32 bits number accruing + * over time) */ + t_u32 on_time_roam_scan; + /** msecs the radio is awake due to PNO scan (32 bits number accruing + * over time) */ + t_u32 on_time_pno_scan; + /** msecs the radio is awake due to HS2.0 scans and GAS exchange (32 + * bits number accruing over time) */ + t_u32 on_time_hs20; + /** number of channels */ + t_u32 num_channels; + /** channel statistics */ + wifi_channel_stat channels[MAX_NUM_CHAN]; +} wifi_radio_stat; + +/** per rate statistics */ +typedef struct { + /** rate information */ + wifi_rate rate; + /** number of successfully transmitted data pkts (ACK rcvd) */ + t_u32 tx_mpdu; + /** number of received data pkts */ + t_u32 rx_mpdu; + /** number of data packet losses (no ACK) */ + t_u32 mpdu_lost; + /** total number of data pkt retries */ + t_u32 retries; + /** number of short data pkt retries */ + t_u32 retries_short; + /** number of long data pkt retries */ + t_u32 retries_long; +} wifi_rate_stat; + +/** wifi peer type */ +typedef enum { + WIFI_PEER_STA, + WIFI_PEER_AP, + WIFI_PEER_P2P_GO, + WIFI_PEER_P2P_CLIENT, + WIFI_PEER_NAN, + WIFI_PEER_TDLS, + WIFI_PEER_INVALID, +} wifi_peer_type; + +/** per peer statistics */ +typedef struct { + /** peer type (AP, TDLS, GO etc.) */ + wifi_peer_type type; + /** mac address */ + t_u8 peer_mac_address[6]; + /** peer WIFI_CAPABILITY_XXX */ + t_u32 capabilities; + /** number of rates */ + t_u32 num_rate; + /** per rate statistics, number of entries = num_rate */ + wifi_rate_stat rate_stats[]; +} wifi_peer_info; + +/** per access category statistics */ +typedef struct { + /** access category (VI, VO, BE, BK) */ + mlan_wmm_ac_e ac; + /** number of successfully transmitted unicast data pkts (ACK rcvd) */ + t_u32 tx_mpdu; + /** number of received unicast mpdus */ + t_u32 rx_mpdu; + /** number of succesfully transmitted multicast data packets */ + /** STA case: implies ACK received from AP for the unicast packet in + * which mcast pkt was sent */ + t_u32 tx_mcast; + /** number of received multicast data packets */ + t_u32 rx_mcast; + /** number of received unicast a-mpdus */ + t_u32 rx_ampdu; + /** number of transmitted unicast a-mpdus */ + t_u32 tx_ampdu; + /** number of data pkt losses (no ACK) */ + t_u32 mpdu_lost; + /** total number of data pkt retries */ + t_u32 retries; + /** number of short data pkt retries */ + t_u32 retries_short; + /** number of long data pkt retries */ + t_u32 retries_long; + /** data pkt min contention time (usecs) */ + t_u32 contention_time_min; + /** data pkt max contention time (usecs) */ + t_u32 contention_time_max; + /** data pkt avg contention time (usecs) */ + t_u32 contention_time_avg; + /** num of data pkts used for contention statistics */ + t_u32 contention_num_samples; +} wifi_wmm_ac_stat; + +/** interface statistics */ +typedef struct { + /** wifi interface */ + /* wifi_interface_handle iface;*/ + /** current state of the interface */ + mlan_interface_link_layer_info info; + /** access point beacon received count from connected AP */ + t_u32 beacon_rx; + /** Average beacon offset encountered (beacon_TSF - TBTT) + * the average_tsf_offset field is used so as to calculate the + * typical beacon contention time on the channel as well may be + * used to debug beacon synchronization and related power consumption + * issue + */ + t_u64 average_tsf_offset; + /** indicate that this AP typically leaks packets beyond the driver + * guard time */ + t_u32 leaky_ap_detected; + /** average number of frame leaked by AP after frame with PM bit set was + * ACK'ed by AP */ + t_u32 leaky_ap_avg_num_frames_leaked; + /** Guard time currently in force (when implementing IEEE power + * management based on frame control PM bit), How long driver waits + * before shutting down the radio and after receiving an ACK for a data + * frame with PM bit set) + */ + t_u32 leaky_ap_guard_time; + /** access point mgmt frames received count from connected AP (including + * Beacon) */ + t_u32 mgmt_rx; + /** action frames received count */ + t_u32 mgmt_action_rx; + /** action frames transmit count */ + t_u32 mgmt_action_tx; + /** access Point Beacon and Management frames RSSI (averaged) */ + t_s32 rssi_mgmt; + /** access Point Data Frames RSSI (averaged) from connected AP */ + t_s32 rssi_data; + /** access Point ACK RSSI (averaged) from connected AP */ + t_s32 rssi_ack; + /** per ac data packet statistics */ + wifi_wmm_ac_stat ac[MAX_AC_QUEUES]; + /** number of peers */ + t_u32 num_peers; + /** per peer statistics */ + wifi_peer_info peer_info[]; +} wifi_iface_stat; + +/** link layer stat configuration params */ +typedef struct { + /** threshold to classify the pkts as short or long */ + t_u32 mpdu_size_threshold; + /** wifi statistics bitmap */ + t_u32 aggressive_statistics_gathering; +} wifi_link_layer_params; + +/** wifi statistics bitmap */ +#define WIFI_STATS_RADIO 0x00000001 /** all radio statistics */ +#define WIFI_STATS_RADIO_CCA \ + 0x00000002 /** cca_busy_time (within radio statistics) */ +#define WIFI_STATS_RADIO_CHANNELS \ + 0x00000004 /** all channel statistics (within radio statistics) */ +#define WIFI_STATS_RADIO_SCAN \ + 0x00000008 /** all scan statistics (within radio statistics) */ +#define WIFI_STATS_IFACE 0x00000010 /** all interface statistics */ +#define WIFI_STATS_IFACE_TXRATE \ + 0x00000020 /** all tx rate statistics (within interface statistics) */ +#define WIFI_STATS_IFACE_AC \ + 0x00000040 /** all ac statistics (within interface statistics) */ +#define WIFI_STATS_IFACE_CONTENTION \ + 0x00000080 /** all contention (min, max, avg) statistics (within ac \ + statisctics) */ + +/** station stats */ +typedef struct _sta_stats { + t_u64 last_rx_in_msec; +} sta_stats; + +#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)(t_void *pmoal_handle, t_u32 offset, + t_u32 len, t_u8 *pbuf); + mlan_status (*moal_get_vdll_data)(t_void *pmoal_handle, t_u32 len, + t_u8 *pbuf); + /** moal_get_hw_spec_complete */ + mlan_status (*moal_get_hw_spec_complete)(t_void *pmoal_handle, + mlan_status status, + pmlan_hw_info phw, + pmlan_bss_tbl ptbl); + /** moal_init_fw_complete */ + mlan_status (*moal_init_fw_complete)(t_void *pmoal_handle, + mlan_status status); + /** moal_shutdown_fw_complete */ + mlan_status (*moal_shutdown_fw_complete)(t_void *pmoal_handle, + mlan_status status); + /** moal_send_packet_complete */ + mlan_status (*moal_send_packet_complete)(t_void *pmoal_handle, + pmlan_buffer pmbuf, + mlan_status status); + /** moal_recv_complete */ + mlan_status (*moal_recv_complete)(t_void *pmoal_handle, + pmlan_buffer pmbuf, t_u32 port, + mlan_status status); + /** moal_recv_packet */ + mlan_status (*moal_recv_packet)(t_void *pmoal_handle, + pmlan_buffer pmbuf); + /** moal_recv_event */ + mlan_status (*moal_recv_event)(t_void *pmoal_handle, + pmlan_event pmevent); + /** moal_ioctl_complete */ + mlan_status (*moal_ioctl_complete)(t_void *pmoal_handle, + pmlan_ioctl_req pioctl_req, + mlan_status status); + + /** moal_alloc_mlan_buffer */ + mlan_status (*moal_alloc_mlan_buffer)(t_void *pmoal_handle, t_u32 size, + ppmlan_buffer pmbuf); + /** moal_free_mlan_buffer */ + mlan_status (*moal_free_mlan_buffer)(t_void *pmoal_handle, + pmlan_buffer pmbuf); + +#ifdef USB + /** moal_write_data_async */ + mlan_status (*moal_write_data_async)(t_void *pmoal_handle, + pmlan_buffer pmbuf, t_u32 port); +#endif /* USB */ +#if defined(SDIO) || defined(PCIE) + /** moal_write_reg */ + mlan_status (*moal_write_reg)(t_void *pmoal_handle, t_u32 reg, + t_u32 data); + /** moal_read_reg */ + mlan_status (*moal_read_reg)(t_void *pmoal_handle, t_u32 reg, + t_u32 *data); +#endif /* SDIO || PCIE */ + /** moal_write_data_sync */ + mlan_status (*moal_write_data_sync)(t_void *pmoal_handle, + pmlan_buffer pmbuf, t_u32 port, + t_u32 timeout); + /** moal_read_data_sync */ + mlan_status (*moal_read_data_sync)(t_void *pmoal_handle, + pmlan_buffer pmbuf, t_u32 port, + t_u32 timeout); + /** moal_malloc */ + mlan_status (*moal_malloc)(t_void *pmoal_handle, t_u32 size, t_u32 flag, + t_u8 **ppbuf); + /** moal_mfree */ + mlan_status (*moal_mfree)(t_void *pmoal_handle, t_u8 *pbuf); + /** moal_vmalloc */ + mlan_status (*moal_vmalloc)(t_void *pmoal_handle, t_u32 size, + t_u8 **ppbuf); + /** moal_vfree */ + mlan_status (*moal_vfree)(t_void *pmoal_handle, t_u8 *pbuf); +#ifdef PCIE + /** moal_malloc_consistent */ + mlan_status (*moal_malloc_consistent)(t_void *pmoal_handle, t_u32 size, + t_u8 **ppbuf, t_u64 *pbuf_pa); + /** moal_mfree_consistent */ + mlan_status (*moal_mfree_consistent)(t_void *pmoal_handle, t_u32 size, + t_u8 *pbuf, t_u64 buf_pa); + /** moal_map_memory */ + mlan_status (*moal_map_memory)(t_void *pmoal_handle, t_u8 *pbuf, + t_u64 *pbuf_pa, t_u32 size, t_u32 flag); + /** moal_unmap_memory */ + mlan_status (*moal_unmap_memory)(t_void *pmoal_handle, t_u8 *pbuf, + t_u64 buf_pa, t_u32 size, t_u32 flag); +#endif /* PCIE */ + /** moal_memset */ + t_void *(*moal_memset)(t_void *pmoal_handle, t_void *pmem, t_u8 byte, + t_u32 num); + /** moal_memcpy */ + t_void *(*moal_memcpy)(t_void *pmoal_handle, t_void *pdest, + const t_void *psrc, t_u32 num); + /** moal_memcpy_ext */ + t_void *(*moal_memcpy_ext)(t_void *pmoal_handle, t_void *pdest, + const t_void *psrc, t_u32 num, + t_u32 dest_size); + /** moal_memmove */ + t_void *(*moal_memmove)(t_void *pmoal_handle, t_void *pdest, + const t_void *psrc, t_u32 num); + /** moal_memcmp */ + t_s32 (*moal_memcmp)(t_void *pmoal_handle, const t_void *pmem1, + const t_void *pmem2, t_u32 num); + /** moal_udelay */ + t_void (*moal_udelay)(t_void *pmoal_handle, t_u32 udelay); + /** moal_usleep_range */ + t_void (*moal_usleep_range)(t_void *pmoal_handle, t_u32 min_delay, + t_u32 max_delay); + /** moal_get_boot_ktime */ + mlan_status (*moal_get_boot_ktime)(t_void *pmoal_handle, t_u64 *pnsec); + /** moal_get_system_time */ + mlan_status (*moal_get_system_time)(t_void *pmoal_handle, t_u32 *psec, + t_u32 *pusec); + /** moal_init_timer*/ + mlan_status (*moal_init_timer)(t_void *pmoal_handle, t_void **pptimer, + IN t_void (*callback)(t_void *pcontext), + t_void *pcontext); + /** moal_free_timer */ + mlan_status (*moal_free_timer)(t_void *pmoal_handle, t_void *ptimer); + /** moal_start_timer*/ + mlan_status (*moal_start_timer)(t_void *pmoal_handle, t_void *ptimer, + t_u8 periodic, t_u32 msec); + /** moal_stop_timer*/ + mlan_status (*moal_stop_timer)(t_void *pmoal_handle, t_void *ptimer); + /** moal_init_lock */ + mlan_status (*moal_init_lock)(t_void *pmoal_handle, t_void **pplock); + /** moal_free_lock */ + mlan_status (*moal_free_lock)(t_void *pmoal_handle, t_void *plock); + /** moal_spin_lock */ + mlan_status (*moal_spin_lock)(t_void *pmoal_handle, t_void *plock); + /** moal_spin_unlock */ + mlan_status (*moal_spin_unlock)(t_void *pmoal_handle, t_void *plock); + /** moal_print */ + t_void (*moal_print)(t_void *pmoal_handle, t_u32 level, char *pformat, + IN...); + /** moal_print_netintf */ + t_void (*moal_print_netintf)(t_void *pmoal_handle, t_u32 bss_index, + t_u32 level); + /** moal_assert */ + t_void (*moal_assert)(t_void *pmoal_handle, t_u32 cond); + + /** moal_hist_data_add */ + t_void (*moal_hist_data_add)(t_void *pmoal_handle, t_u32 bss_index, + t_u16 rx_rate, t_s8 snr, t_s8 nflr, + t_u8 antenna); +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + mlan_status (*moal_wait_hostcmd_complete)(t_void *pmoal_handle, + t_u32 bss_index); + mlan_status (*moal_notify_hostcmd_complete)(t_void *pmoal_handle, + t_u32 bss_index); +#endif + void (*moal_tp_accounting)(t_void *pmoal_handle, t_void *buf, + t_u32 drop_point); + void (*moal_tp_accounting_rx_param)(t_void *pmoal_handle, + unsigned int type, + unsigned int rsvd1); + +} mlan_callbacks, *pmlan_callbacks; + +/** Parameter unchanged, use MLAN default setting */ +#define ROBUSTCOEX_GPIO_UNCHANGED 0 +/** Parameter enabled, override MLAN default setting */ +#define ROBUSTCOEX_GPIO_CFG 1 + +#if defined(SDIO) +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 +/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 + */ +#define GPIO_INT_NEW_MODE 255 +#endif + +/** 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 + +/** Control bit for stream 2X2 */ +#define FEATURE_CTRL_STREAM_2X2 MBIT(0) +/** Control bit for DFS support */ +#define FEATURE_CTRL_DFS_SUPPORT MBIT(1) +#ifdef USB +/** Control bit for winner check & not wait for FW ready event */ +#define FEATURE_CTRL_USB_NEW_INIT MBIT(2) +#endif +/** Default feature control */ +#define FEATURE_CTRL_DEFAULT 0xffffffff +/** Check if stream 2X2 enabled */ +#define IS_STREAM_2X2(x) ((x)&FEATURE_CTRL_STREAM_2X2) +/** Check if DFS support enabled */ +#define IS_DFS_SUPPORT(x) ((x)&FEATURE_CTRL_DFS_SUPPORT) +#ifdef USB +/** Check if winner check & not wait for FW ready event */ +#define IS_USB_NEW_INIT(x) ((x)&FEATURE_CTRL_USB_NEW_INIT) +#endif + +/* +#define DRV_MODE_NAN MBIT(4) +#define DRV_MODE_11P MBIT(5) +#define DRV_MODE_MAC80211 MBIT(6) +#define DRV_MODE_DFS MBIT(7)*/ +#define DRV_MODE_MASK (MBIT(4) | MBIT(5) | MBIT(6) | MBIT(7)) + +/** 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 +#if defined(SDIO) + /** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ + t_u32 int_mode; + /** GPIO interrupt pin number */ + t_u32 gpio_pin; +#endif +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif + /** allocate fixed buffer size for scan beacon buffer*/ + t_u32 fixed_beacon_buffer; + /** SDIO MPA Tx */ + t_u32 mpa_tx_cfg; + /** SDIO MPA Rx */ + t_u32 mpa_rx_cfg; +#ifdef SDIO + /** SDIO Single port rx aggr */ + t_u8 sdio_rx_aggr_enable; + /* see blk_queue_max_segment_size */ + t_u32 max_seg_size; + /* see blk_queue_max_segments */ + t_u16 max_segs; +#endif + /** Auto deep sleep */ + t_u32 auto_ds; + /** IEEE PS mode */ + t_u32 ps_mode; + /** Max Tx buffer size */ + t_u32 max_tx_buf; +#if defined(STA_SUPPORT) + /** 802.11d configuration */ + t_u32 cfg_11d; +#endif + /** Feature control bitmask */ + t_u32 feature_control; + /** enable/disable rx work */ + t_u8 rx_work; + /** dev cap mask */ + t_u32 dev_cap_mask; + /** oob independent reset */ + t_u32 indrstcfg; + /** dtim interval */ + t_u16 multi_dtim; + /** IEEE ps inactivity timeout value */ + t_u16 inact_tmo; + /** card type */ + t_u16 card_type; + /** card rev */ + t_u8 card_rev; + /** Host sleep wakeup interval */ + t_u32 hs_wake_interval; + /** GPIO to indicate wakeup source */ + t_u8 indication_gpio; + /** Dynamic MIMO-SISO switch for hscfg*/ + t_u8 hs_mimo_switch; +#ifdef USB + /** Tx CMD endpoint address */ + t_u8 tx_cmd_ep; + /** Rx CMD/EVT endpoint address */ + t_u8 rx_cmd_ep; + + /** Rx data endpoint address */ + t_u8 rx_data_ep; + /** Tx data endpoint address */ + t_u8 tx_data_ep; +#endif + /** fw region */ + t_bool fw_region; + /** passive to active scan */ + t_u8 passive_to_active_scan; + /** uap max supported station per chip */ + t_u8 uap_max_sta; + /** drv mode */ + t_u32 drv_mode; + /** dfs w53 cfg */ + t_u8 dfs53cfg; +} mlan_device, *pmlan_device; + +/** MLAN API function prototype */ +#define MLAN_API + +/** Registration */ +MLAN_API mlan_status mlan_register(pmlan_device pmdevice, + t_void **ppmlan_adapter); + +/** Un-registration */ +MLAN_API mlan_status mlan_unregister(t_void *pmlan_adapter); + +/** Firmware Downloading */ +MLAN_API mlan_status mlan_dnld_fw(t_void *pmlan_adapter, pmlan_fw_image pmfw); + +/** Custom data pass API */ +MLAN_API mlan_status mlan_set_init_param(t_void *pmlan_adapter, + pmlan_init_param pparam); + +/** Firmware Initialization */ +MLAN_API mlan_status mlan_init_fw(t_void *pmlan_adapter); + +/** Firmware Shutdown */ +MLAN_API mlan_status mlan_shutdown_fw(t_void *pmlan_adapter); + +/** Main Process */ +MLAN_API mlan_status mlan_main_process(t_void *pmlan_adapter); + +/** Rx process */ +mlan_status mlan_rx_process(t_void *pmlan_adapter, t_u8 *rx_pkts); + +/** Packet Transmission */ +MLAN_API mlan_status mlan_send_packet(t_void *pmlan_adapter, + pmlan_buffer pmbuf); + +#ifdef USB +/** mlan_write_data_async_complete */ +MLAN_API mlan_status mlan_write_data_async_complete(t_void *pmlan_adapter, + pmlan_buffer pmbuf, + t_u32 port, + mlan_status status); + +/** Packet Reception */ +MLAN_API mlan_status mlan_recv(t_void *pmlan_adapter, pmlan_buffer pmbuf, + t_u32 port); +#endif /* USB */ + +/** Packet Reception complete callback */ +MLAN_API mlan_status mlan_recv_packet_complete(t_void *pmlan_adapter, + pmlan_buffer pmbuf, + mlan_status status); + +#if defined(SDIO) || defined(PCIE) +/** interrupt handler */ +MLAN_API mlan_status mlan_interrupt(t_u16 msg_id, t_void *pmlan_adapter); + +#if defined(SYSKT) +/** GPIO IRQ callback function */ +MLAN_API t_void mlan_hs_callback(t_void *pctx); +#endif /* SYSKT_MULTI || SYSKT */ +#endif /* SDIO || PCIE */ + +MLAN_API t_void mlan_pm_wakeup_card(t_void *pmlan_adapter, t_u8 keep_wakeup); + +MLAN_API t_u8 mlan_is_main_process_running(t_void *adapter); +#ifdef PCIE +MLAN_API t_void mlan_set_int_mode(t_void *adapter, t_u32 int_mode, + t_u8 func_num); +#endif +/** mlan ioctl */ +MLAN_API mlan_status mlan_ioctl(t_void *pmlan_adapter, + pmlan_ioctl_req pioctl_req); +/** mlan select wmm queue */ +MLAN_API t_u8 mlan_select_wmm_queue(t_void *pmlan_adapter, t_u8 bss_num, + t_u8 tid); + +#endif /* !_MLAN_DECL_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_fw.h b/mxm_wifiex/wlan_src/mlan/mlan_fw.h new file mode 100644 index 0000000..2011e24 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_fw.h @@ -0,0 +1,7410 @@ +/** @file mlan_fw.h + * + * @brief This file contains firmware specific defines. + * structures and declares global function prototypes used + * in MLAN module. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 */ +#ifdef USB +#define USB_INTF_HEADER_LEN 0 +#endif /* USB */ +#ifdef SDIO +#define SDIO_INTF_HEADER_LEN 4 +#endif /* SDIO */ +#ifdef PCIE +#define PCIE_INTF_HEADER_LEN 4 +#endif /* PCIE */ + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +#define WPA_GCMP_KEY_LEN 32 + +#define WPA_CCMP_256_KEY_LEN 32 + +/** Ethernet header */ +typedef MLAN_PACK_START struct { + /** Ethernet header destination address */ + t_u8 dest_addr[MLAN_MAC_ADDR_LENGTH]; + /** Ethernet header source address */ + t_u8 src_addr[MLAN_MAC_ADDR_LENGTH]; + /** Ethernet header length */ + t_u16 h803_len; + +} MLAN_PACK_END Eth803Hdr_t; + +/** RFC 1042 header */ +typedef MLAN_PACK_START struct { + /** LLC DSAP */ + t_u8 llc_dsap; + /** LLC SSAP */ + t_u8 llc_ssap; + /** LLC CTRL */ + t_u8 llc_ctrl; + /** SNAP OUI */ + t_u8 snap_oui[3]; + /** SNAP type */ + t_u16 snap_type; + +} MLAN_PACK_END Rfc1042Hdr_t; + +/** Rx packet header */ +typedef MLAN_PACK_START struct { + /** Etherner header */ + Eth803Hdr_t eth803_hdr; + /** RFC 1042 header */ + Rfc1042Hdr_t rfc1042_hdr; + +} MLAN_PACK_END RxPacketHdr_t; + +/** Rates supported in band B */ +#define B_SUPPORTED_RATES 5 +/** Rates supported in band G */ +#define G_SUPPORTED_RATES 9 +/** Rates supported in band BG */ +#define BG_SUPPORTED_RATES 13 + +/** Setup the number of rates passed in the driver/firmware API */ +#define A_SUPPORTED_RATES 9 + +/** CapInfo Short Slot Time Disabled */ +/* #define SHORT_SLOT_TIME_DISABLED(CapInfo) + * ((IEEEtypes_CapInfo_t)(CapInfo).short_slot_time = 0) */ +#define SHORT_SLOT_TIME_DISABLED(CapInfo) (CapInfo &= ~MBIT(10)) +/** CapInfo Short Slot Time Enabled */ +#define SHORT_SLOT_TIME_ENABLED(CapInfo) (CapInfo |= MBIT(10)) +/** CapInfo Spectrum Mgmt Disabled */ +#define SPECTRUM_MGMT_DISABLED(CapInfo) (CapInfo &= ~MBIT(8)) +/** CapInfo Spectrum Mgmt Enabled */ +#define SPECTRUM_MGMT_ENABLED(CapInfo) (CapInfo |= MBIT(8)) +/** CapInfo Radio Measurement Disabled */ +#define RADIO_MEASUREMENT_DISABLED(CapInfo) (CapInfo &= ~MBIT(12)) +/** CapInfo Radio Measurement Enabled */ +#define RADIO_MEASUREMENT_ENABLED(CapInfo) (CapInfo |= MBIT(12)) + +/** Setup the number of rates passed in the driver/firmware API */ +#define HOSTCMD_SUPPORTED_RATES 14 + +/** Rates supported in band N */ +#define N_SUPPORTED_RATES 3 +#ifdef STA_SUPPORT +/** All bands (B, G, N, AAC, GAC) */ +#define ALL_802_11_BANDS \ + (BAND_A | BAND_B | BAND_G | BAND_GN | BAND_AAC | BAND_GAC) +#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) | MBIT(12) | MBIT(13)) +#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 */ +/* need to shift bit 12 and bit 13 in fw_cap_info from the firmware + * to bit 13 and 14 for 11ac so that bit 11 is for GN, bit 12 for AN, + * bit 13 for GAC, and bit 14 for AAC, in order to be compatible with + * the band capability defined in the driver after right shift of 8 bits */ +#define GET_FW_DEFAULT_BANDS(_adapter) \ + (((((_adapter->fw_cap_info & 0x3000) << 1) | \ + (_adapter->fw_cap_info & ~0xF000)) >> \ + 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 +/** Length of WEP 40 bit key */ +#define WEP_40_BIT_LEN 5 +/** Length of WEP 104 bit key */ +#define WEP_104_BIT_LEN 13 + +/** Key information enabled */ +#define KEY_INFO_ENABLED 0x01 +/** KEY_TYPE_ID */ +typedef enum _KEY_TYPE_ID { + /** Key type : WEP */ + KEY_TYPE_ID_WEP = 0, + /** Key type : TKIP */ + KEY_TYPE_ID_TKIP = 1, + /** Key type : AES */ + KEY_TYPE_ID_AES = 2, + KEY_TYPE_ID_WAPI = 3, + KEY_TYPE_ID_AES_CMAC = 4, + /** Key type : GCMP */ + KEY_TYPE_ID_GCMP = 5, + /** Key type : GCMP_256 */ + KEY_TYPE_ID_GCMP_256 = 6, + /** Key type : CCMP_256 */ + KEY_TYPE_ID_CCMP_256 = 7, +} KEY_TYPE_ID; + +/** Key Info flag for multicast key */ +#define KEY_INFO_MCAST_KEY 0x01 +/** Key Info flag for unicast key */ +#define KEY_INFO_UCAST_KEY 0x02 + +/** KEY_INFO_WEP*/ +typedef enum _KEY_INFO_WEP { + KEY_INFO_WEP_MCAST = 0x01, + KEY_INFO_WEP_UNICAST = 0x02, + KEY_INFO_WEP_ENABLED = 0x04 +} KEY_INFO_WEP; + +/** KEY_INFO_TKIP */ +typedef enum _KEY_INFO_TKIP { + KEY_INFO_TKIP_MCAST = 0x01, + KEY_INFO_TKIP_UNICAST = 0x02, + KEY_INFO_TKIP_ENABLED = 0x04 +} KEY_INFO_TKIP; + +/** KEY_INFO_AES*/ +typedef enum _KEY_INFO_AES { + KEY_INFO_AES_MCAST = 0x01, + KEY_INFO_AES_UNICAST = 0x02, + KEY_INFO_AES_ENABLED = 0x04, + KEY_INFO_AES_MCAST_IGTK = 0x400, +} KEY_INFO_AES; + +/** WPA AES key length */ +#define WPA_AES_KEY_LEN 16 +/** WPA TKIP key length */ +#define WPA_TKIP_KEY_LEN 32 +/** WPA AES IGTK key length */ +#define CMAC_AES_KEY_LEN 16 +/** IGTK key length */ +#define WPA_IGTK_KEY_LEN 16 + +/** WAPI key length */ +#define WAPI_KEY_LEN 50 +/** KEY_INFO_WAPI*/ +typedef enum _KEY_INFO_WAPI { + KEY_INFO_WAPI_MCAST = 0x01, + KEY_INFO_WAPI_UNICAST = 0x02, + KEY_INFO_WAPI_ENABLED = 0x04 +} KEY_INFO_WAPI; + +/** Maximum ethernet frame length sans FCS */ +#define MV_ETH_FRAME_LEN 1514 + +#if defined(SDIO) || defined(PCIE) +/** Length of SNAP header */ +#define MRVDRV_SNAP_HEADER_LEN 8 + +/** The number of times to try when polling for status bits */ +#define MAX_POLL_TRIES 100 + +/** The number of times to try when waiting for downloaded firmware to + become active when multiple interface is present */ +#define MAX_MULTI_INTERFACE_POLL_TRIES 150 +/** The number of times to try when waiting for downloaded firmware to + become active. (polling the scratch register). */ +#define MAX_FIRMWARE_POLL_TRIES 100 + +/** FW fill in rx_len with extra 204 bytes */ +#define EXTRA_LEN 256 + +/** 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) +#endif /* SDIO || PCIE */ + +#ifdef SDIO +/* Macros in interface module */ +/** Firmware ready */ +#define SDIO_FIRMWARE_READY 0xfedc +#endif /* SDIO */ + +#ifdef PCIE +/* Macros in interface module */ +/** Firmware ready */ +#define PCIE_FIRMWARE_READY 0xfedcba00 +#endif + +/** 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 + +/** Terminating TLV Type */ +#define MRVL_TERMINATE_TLV_ID 0xffff + +/** TLV type : SSID */ +#define TLV_TYPE_SSID 0x0000 +/** TLV type : Rates */ +#define TLV_TYPE_RATES 0x0001 +/** TLV type : PHY FH */ +#define TLV_TYPE_PHY_FH 0x0002 +/** TLV type : PHY DS */ +#define TLV_TYPE_PHY_DS 0x0003 +/** TLV type : CF */ +#define TLV_TYPE_CF 0x0004 +/** TLV type : IBSS */ +#define TLV_TYPE_IBSS 0x0006 + +/** TLV type : Domain */ +#define TLV_TYPE_DOMAIN 0x0007 + +/** TLV type : Power constraint */ +#define TLV_TYPE_POWER_CONSTRAINT 0x0020 + +/** TLV type : Power capability */ +#define TLV_TYPE_POWER_CAPABILITY 0x0021 + +#define TLV_TYPE_HT_CAPABILITY 0x002d + +#define TLV_TYPE_EXTENSION_ID 0x00ff + +/**TLV type : Host MLME Flag*/ +#define TLV_TYPE_HOST_MLME (PROPRIETARY_TLV_BASE_ID + 307) + +/** TLV type : Vendor Specific IE */ +#define TLV_TYPE_VENDOR_SPECIFIC_IE 0x00dd + +/** TLV type : Key material */ +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0x00) /* 0x0100 */ +/** TLV type : Channel list */ +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 0x01) /* 0x0101 */ +/** TLV type : Number of probes */ +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 0x02) /* 0x0102 */ +/** TLV type : Beacon RSSI low */ +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 0x04) /* 0x0104 */ +/** TLV type : Beacon SNR low */ +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 0x05) /* 0x0105 */ +/** TLV type : Fail count */ +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 0x06) /* 0x0106 */ +/** TLV type : BCN miss */ +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 0x07) /* 0x0107 */ +/** TLV type : LED behavior */ +#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 0x09) /* 0x0109 */ +/** TLV type : Passthrough */ +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 0x0a) /* 0x010a */ +/** TLV type : Power TBL 2.4 Ghz */ +#define TLV_TYPE_POWER_TBL_2_4GHZ \ + (PROPRIETARY_TLV_BASE_ID + 0x0c) /* 0x010c \ + */ +/** TLV type : Power TBL 5 GHz */ +#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 0x0d) /* 0x010d */ +/** TLV type : WMM queue status */ +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 0x10) /* 0x0110 */ +/** TLV type : Wildcard SSID */ +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 0x12) /* 0x0112 */ +/** TLV type : TSF timestamp */ +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 0x13) /* 0x0113 */ +/** TLV type : Beacon RSSI high */ +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 0x16) /* 0x0116 */ +/** TLV type : Beacon SNR high */ +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 0x17) /* 0x0117 */ +/** TLV type : Start BG scan later */ +#define TLV_TYPE_STARTBGSCANLATER \ + (PROPRIETARY_TLV_BASE_ID + 0x1e) /* 0x011e \ + */ +/** TLV type: BG scan repeat count */ +#define TLV_TYPE_REPEAT_COUNT (PROPRIETARY_TLV_BASE_ID + 0xb0) /* 0x01b0 */ +/** TLV type : Authentication type */ +#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 0x1f) /* 0x011f */ +/** TLV type : BSSID */ +#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 0x23) /* 0x0123 */ + +/** TLV type : Link Quality */ +#define TLV_TYPE_LINK_QUALITY (PROPRIETARY_TLV_BASE_ID + 0x24) /* 0x0124 */ + +/** TLV type : Data RSSI low */ +#define TLV_TYPE_RSSI_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 0x26) /* 0x0126 */ +/** TLV type : Data SNR low */ +#define TLV_TYPE_SNR_LOW_DATA (PROPRIETARY_TLV_BASE_ID + 0x27) /* 0x0127 */ +/** TLV type : Data RSSI high */ +#define TLV_TYPE_RSSI_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 0x28) /* 0x0128 */ +/** TLV type : Data SNR high */ +#define TLV_TYPE_SNR_HIGH_DATA (PROPRIETARY_TLV_BASE_ID + 0x29) /* 0x0129 */ + +/** TLV type : Channel band list */ +#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 0x2a) /* 0x012a */ + +/** TLV type : Security Cfg */ +#define TLV_TYPE_SECURITY_CFG (PROPRIETARY_TLV_BASE_ID + 0x3a) /* 0x013a */ + +/** TLV type : Passphrase */ +#define TLV_TYPE_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 0x3c) /* 0x013c */ +/** TLV type : SAE Password */ +#define TLV_TYPE_SAE_PASSWORD (PROPRIETARY_TLV_BASE_ID + 0x141) /* 0x0241 */ +/** 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: key param v2 */ +#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 0x9C) /* 0x019C */ + +/** TLV type: ps params in hs */ +#define TLV_TYPE_PS_PARAMS_IN_HS (PROPRIETARY_TLV_BASE_ID + 0xB5) /* 0x01b5 */ +/** TLV type: hs wake hold off */ +#define TLV_TYPE_HS_WAKE_HOLDOFF (PROPRIETARY_TLV_BASE_ID + 0xB6) /* 0x01b6 */ +/** TLV type: wake up source */ +#define TLV_TYPE_HS_WAKEUP_SOURCE_GPIO \ + (PROPRIETARY_TLV_BASE_ID + 0x105) /* 0x0205 */ +/** TLV type: management filter */ +#define TLV_TYPE_MGMT_FRAME_WAKEUP \ + (PROPRIETARY_TLV_BASE_ID + 0x116) /* 0x0216 */ +/** TLV type: extend wakeup source */ +#define TLV_TYPE_WAKEUP_EXTEND (PROPRIETARY_TLV_BASE_ID + 0x118) /* 0x0218 */ +/** TLV type: HS antenna mode */ +#define TLV_TYPE_HS_ANTMODE (PROPRIETARY_TLV_BASE_ID + 0x119) /* 0x0219 */ + +/** TLV type: robustcoex mode */ +#define TLV_TYPE_ROBUSTCOEX (PROPRIETARY_TLV_BASE_ID + 0x11B) /* 0x021B */ + +#define TLV_TYPE_DMCS_STATUS (PROPRIETARY_TLV_BASE_ID + 0x13A) /* 0x023A */ + +/** TLV type : HT Capabilities */ +#define TLV_TYPE_HT_CAP (PROPRIETARY_TLV_BASE_ID + 0x4a) /* 0x014a */ +/** TLV type : HT Information */ +#define TLV_TYPE_HT_INFO (PROPRIETARY_TLV_BASE_ID + 0x4b) /* 0x014b */ +/** TLV type : Secondary Channel Offset */ +#define TLV_SECONDARY_CHANNEL_OFFSET \ + (PROPRIETARY_TLV_BASE_ID + 0x4c) /* 0x014c */ +/** TLV type : 20/40 BSS Coexistence */ +#define TLV_TYPE_2040BSS_COEXISTENCE \ + (PROPRIETARY_TLV_BASE_ID + 0x4d) /* 0x014d */ +/** TLV type : Overlapping BSS Scan Parameters */ +#define TLV_TYPE_OVERLAP_BSS_SCAN_PARAM \ + (PROPRIETARY_TLV_BASE_ID + 0x4e) /* 0x014e */ +/** TLV type : Extended capabilities */ +#define TLV_TYPE_EXTCAP (PROPRIETARY_TLV_BASE_ID + 0x4f) /* 0x014f */ +/** TLV type : Set of MCS values that STA desires to use within the BSS */ +#define TLV_TYPE_HT_OPERATIONAL_MCS_SET \ + (PROPRIETARY_TLV_BASE_ID + 0x50) /* 0x0150 */ +/** TLV ID : Management Frame */ +#define TLV_TYPE_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 0x68) /* 0x0168 */ +/** TLV type : RXBA_SYNC */ +#define TLV_TYPE_RXBA_SYNC (PROPRIETARY_TLV_BASE_ID + 0x99) /* 0x0199 */ + +#ifdef WIFI_DIRECT_SUPPORT +/** TLV type : AP PSK */ +#define TLV_TYPE_UAP_PSK (PROPRIETARY_TLV_BASE_ID + 0xa8) /* 0x01a8 */ +/** TLV type : p2p NOA */ +#define TLV_TYPE_WIFI_DIRECT_NOA (PROPRIETARY_TLV_BASE_ID + 0x83) +/** TLV type : p2p opp ps */ +#define TLV_TYPE_WIFI_DIRECT_OPP_PS (PROPRIETARY_TLV_BASE_ID + 0x84) +#endif /* WIFI_DIRECT_SUPPORT */ + +/** TLV : 20/40 coex config */ +#define TLV_TYPE_2040_BSS_COEX_CONTROL \ + (PROPRIETARY_TLV_BASE_ID + 0x98) /* 0x0198 */ + +/** TLV type : aggr win size */ +#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 0xca) +/** TLV type : scan time */ +#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 0Xcb) +/** TLV type : Ewpa_eapol_pkt */ +#define TLV_TYPE_EAPOL_PKT (PROPRIETARY_TLV_BASE_ID + 0xcf) + +#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 0x9a) + +/** TLV type : EES Configuration */ +#define TLV_TYPE_EES_CFG (PROPRIETARY_TLV_BASE_ID + 0xda) +/** TLV type : EES Network Configuration */ +#define TLV_TYPE_EES_NET_CFG (PROPRIETARY_TLV_BASE_ID + 0xdb) + +#define TLV_TYPE_LL_STAT_IFACE (PROPRIETARY_TLV_BASE_ID + 300) +#define TLV_TYPE_LL_STAT_RADIO (PROPRIETARY_TLV_BASE_ID + 301) + +/** TLV type: fw cap info */ +#define TLV_TYPE_FW_CAP_INFO (PROPRIETARY_TLV_BASE_ID + 318) + +/** ADDBA TID mask */ +#define ADDBA_TID_MASK (MBIT(2) | MBIT(3) | MBIT(4) | MBIT(5)) +/** DELBA TID mask */ +#define DELBA_TID_MASK (MBIT(12) | MBIT(13) | MBIT(14) | MBIT(15)) +/** ADDBA Starting Sequence Number Mask */ +#define SSN_MASK 0xfff0 + +/** Block Ack result status */ +/** Block Ack Result : Success */ +#define BA_RESULT_SUCCESS 0x0 +/** Block Ack Result : Execution failure */ +#define BA_RESULT_FAILURE 0x1 +/** Block Ack Result : Timeout */ +#define BA_RESULT_TIMEOUT 0x2 +/** Block Ack Result : Data invalid */ +#define BA_RESULT_DATA_INVALID 0x3 + +/** Get the baStatus (NOT_SETUP, COMPLETE, IN_PROGRESS) + * in Tx BA stream table */ +#define IS_BASTREAM_SETUP(ptr) (ptr->ba_status) + +/** An AMPDU/AMSDU could be disallowed for certain TID. 0xff means + * no aggregation is enabled for the assigned TID */ +#define BA_STREAM_NOT_ALLOWED 0xff + +#ifdef STA_SUPPORT +#endif + +/** Test if 11n is enabled by checking the HTCap IE */ +#define IS_11N_ENABLED(priv) \ + ((priv->config_bands & BAND_GN || priv->config_bands & BAND_AN) && \ + priv->curr_bss_params.bss_descriptor.pht_cap && \ + !priv->curr_bss_params.bss_descriptor.disable_11n) +/** 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 +/** 12K buf size */ +#define MLAN_TX_DATA_BUF_SIZE_12K 12288 +/** Max Rx AMPDU Size */ +#define MAX_RX_AMPDU_SIZE_64K 0x03 +/** Non green field station */ +#define NON_GREENFIELD_STAS 0x04 + +/** Greenfield support */ +#define HWSPEC_GREENFIELD_SUPP MBIT(29) +/** RX STBC support */ +#define HWSPEC_RXSTBC_SUPP MBIT(26) +/** ShortGI @ 40Mhz support */ +#define HWSPEC_SHORTGI40_SUPP MBIT(24) +/** ShortGI @ 20Mhz support */ +#define HWSPEC_SHORTGI20_SUPP MBIT(23) +/** RX LDPC support */ +#define HWSPEC_LDPC_SUPP MBIT(22) +/** Channel width 40Mhz support */ +#define HWSPEC_CHANBW40_SUPP MBIT(17) +/** 40Mhz intolarent enable */ +#define CAPINFO_40MHZ_INTOLARENT MBIT(8) + +/** Default 11n capability mask for 2.4GHz */ +#define DEFAULT_11N_CAP_MASK_BG \ + (HWSPEC_SHORTGI20_SUPP | HWSPEC_RXSTBC_SUPP | HWSPEC_LDPC_SUPP) +/** Default 11n capability mask for 5GHz */ +#define DEFAULT_11N_CAP_MASK_A \ + (HWSPEC_CHANBW40_SUPP | HWSPEC_SHORTGI20_SUPP | \ + HWSPEC_SHORTGI40_SUPP | HWSPEC_RXSTBC_SUPP | HWSPEC_LDPC_SUPP) + +/** Default 11n TX BF capability 2X2 chip **/ +#define DEFAULT_11N_TX_BF_CAP_2X2 0x19E74618 +/** Default 11n TX BF capability 1X1 chip **/ +#define DEFAULT_11N_TX_BF_CAP_1X1 0x19E74608 + +/** Bits to ignore in hw_dev_cap as these bits are set in get_hw_spec */ +#define IGN_HW_DEV_CAP (CAPINFO_40MHZ_INTOLARENT) + +/** HW_SPEC FwCapInfo */ +#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & MBIT(11)) + +/** HW_SPEC Dot11nDevCap : MAX AMSDU supported */ +#define ISSUPP_MAXAMSDU(Dot11nDevCap) (Dot11nDevCap & MBIT(31)) +/** HW_SPEC Dot11nDevCap : Beamforming support */ +#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & MBIT(30)) +/** HW_SPEC Dot11nDevCap : Green field support */ +#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & MBIT(29)) +/** HW_SPEC Dot11nDevCap : AMPDU support */ +#define ISSUPP_AMPDU(Dot11nDevCap) (Dot11nDevCap & MBIT(28)) +/** HW_SPEC Dot11nDevCap : MIMO PS support */ +#define ISSUPP_MIMOPS(Dot11nDevCap) (Dot11nDevCap & MBIT(27)) +/** HW_SPEC Dot11nDevCap : Rx STBC support */ +#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & MBIT(26)) +/** HW_SPEC Dot11nDevCap : Tx STBC support */ +#define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & MBIT(25)) +/** HW_SPEC Dot11nDevCap : Short GI @ 40Mhz support */ +#define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & MBIT(24)) +/** HW_SPEC Dot11nDevCap : Reset Short GI @ 40Mhz support */ +#define RESETSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap &= ~MBIT(24)) +/** HW_SPEC Dot11nDevCap : Short GI @ 20Mhz support */ +#define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & MBIT(23)) +/** HW_SPEC Dot11nDevCap : Rx LDPC support */ +#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & MBIT(22)) +/** HW_SPEC Dot11nDevCap : Number of TX BA streams supported */ +#define ISSUPP_GETTXBASTREAM(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF) +/** HW_SPEC Dot11nDevCap : Channel BW support @ 40Mhz support */ +#define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & MBIT(17)) +/** HW_SPEC Dot11nDevCap : Channel BW support @ 20Mhz support */ +#define ISSUPP_CHANWIDTH20(Dot11nDevCap) (Dot11nDevCap & MBIT(16)) +/** HW_SPEC Dot11nDevCap : Channel BW support @ 10Mhz support */ +#define ISSUPP_CHANWIDTH10(Dot11nDevCap) (Dot11nDevCap & MBIT(15)) +/** Dot11nUsrCap : 40Mhz intolarance enabled */ +#define ISENABLED_40MHZ_INTOLARENT(Dot11nDevCap) (Dot11nDevCap & MBIT(8)) +/** Dot11nUsrCap : Reset 40Mhz intolarance enabled */ +#define RESET_40MHZ_INTOLARENT(Dot11nDevCap) (Dot11nDevCap &= ~MBIT(8)) +/** HW_SPEC Dot11nDevCap : Rx AntennaD support */ +#define ISSUPP_RXANTENNAD(Dot11nDevCap) (Dot11nDevCap & MBIT(7)) +/** HW_SPEC Dot11nDevCap : Rx AntennaC support */ +#define ISSUPP_RXANTENNAC(Dot11nDevCap) (Dot11nDevCap & MBIT(6)) +/** HW_SPEC Dot11nDevCap : Rx AntennaB support */ +#define ISSUPP_RXANTENNAB(Dot11nDevCap) (Dot11nDevCap & MBIT(5)) +/** HW_SPEC Dot11nDevCap : Rx AntennaA support */ +#define ISSUPP_RXANTENNAA(Dot11nDevCap) (Dot11nDevCap & MBIT(4)) +/** HW_SPEC Dot11nDevCap : Tx AntennaD support */ +#define ISSUPP_TXANTENNAD(Dot11nDevCap) (Dot11nDevCap & MBIT(3)) +/** HW_SPEC Dot11nDevCap : Tx AntennaC support */ +#define ISSUPP_TXANTENNAC(Dot11nDevCap) (Dot11nDevCap & MBIT(2)) +/** HW_SPEC Dot11nDevCap : Tx AntennaB support */ +#define ISSUPP_TXANTENNAB(Dot11nDevCap) (Dot11nDevCap & MBIT(1)) +/** HW_SPEC Dot11nDevCap : Tx AntennaA support */ +#define ISSUPP_TXANTENNAA(Dot11nDevCap) (Dot11nDevCap & MBIT(0)) + +/** HW_SPEC Dot11nDevCap : Set support of channel bw @ 40Mhz */ +#define SETSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap |= MBIT(17)) +/** HW_SPEC Dot11nDevCap : Reset support of channel bw @ 40Mhz */ +#define RESETSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap &= ~MBIT(17)) + +/** DevMCSSupported : Tx MCS supported */ +#define GET_TXMCSSUPP(DevMCSSupported) (DevMCSSupported >> 4) +/** DevMCSSupported : Rx MCS supported */ +#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) + +/** GET HTCapInfo : Supported Channel BW */ +#define GETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo & MBIT(1)) +/** GET HTCapInfo : Support for Greenfield */ +#define GETHT_GREENFIELD(HTCapInfo) (HTCapInfo & MBIT(4)) +/** GET HTCapInfo : Support for Short GI @ 20Mhz */ +#define GETHT_SHORTGI20(HTCapInfo) (HTCapInfo & MBIT(5)) +/** GET HTCapInfo : Support for Short GI @ 40Mhz */ +#define GETHT_SHORTGI40(HTCapInfo) (HTCapInfo & MBIT(6)) +/** GET HTCapInfo : Support for Tx STBC */ +#define GETHT_TXSTBC(HTCapInfo) (HTCapInfo & MBIT(7)) + +/** GET HTCapInfo : Support for Rx STBC */ +#define GETHT_RXSTBC(HTCapInfo) ((HTCapInfo >> 8) & 0x03) +/** GET HTCapInfo : Support for Delayed ACK */ +#define GETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo & MBIT(10)) +/** GET HTCapInfo : Support for Max AMSDU */ +#define GETHT_MAXAMSDU(HTCapInfo) (HTCapInfo & MBIT(11)) + +/** GET HTCapInfo : Support 40Mhz Intolarence */ +#define GETHT_40MHZ_INTOLARANT(HTCapInfo) (HTCapInfo & MBIT(14)) + +/** SET HTCapInfo : Set support for LDPC coding capability */ +#define SETHT_LDPCCODINGCAP(HTCapInfo) (HTCapInfo |= MBIT(0)) +/** SET HTCapInfo : Set support for Channel BW */ +#define SETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo |= MBIT(1)) +/** SET HTCapInfo : Set support for Greenfield */ +#define SETHT_GREENFIELD(HTCapInfo) (HTCapInfo |= MBIT(4)) +/** SET HTCapInfo : Set support for Short GI @ 20Mhz */ +#define SETHT_SHORTGI20(HTCapInfo) (HTCapInfo |= MBIT(5)) +/** SET HTCapInfo : Set support for Short GI @ 40Mhz */ +#define SETHT_SHORTGI40(HTCapInfo) (HTCapInfo |= MBIT(6)) +/** SET HTCapInfo : Set support for Tx STBC */ +#define SETHT_TXSTBC(HTCapInfo) (HTCapInfo |= MBIT(7)) +/** SET HTCapInfo : Set support for Rx STBC */ +#define SETHT_RXSTBC(HTCapInfo, value) (HTCapInfo |= (value << 8)) +/** SET HTCapInfo : Set support for delayed block ack */ +#define SETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo |= MBIT(10)) +/** SET HTCapInfo : Set support for Max size AMSDU */ +#define SETHT_MAXAMSDU(HTCapInfo) (HTCapInfo |= MBIT(11)) +/** SET HTCapInfo : Set support for DSSS/CCK Rates @ 40Mhz */ +#define SETHT_DSSSCCK40(HTCapInfo) (HTCapInfo |= MBIT(12)) +/** SET HTCapInfo : Enable 40Mhz Intolarence */ +#define SETHT_40MHZ_INTOLARANT(HTCapInfo) (HTCapInfo |= MBIT(14)) +/** SET HTCapInfo : Disable Static SM power save */ +#define SETHT_STATIC_SMPS(HTCapInfo) ((HTCapInfo) |= (MBIT(2) | MBIT(3))) + +/** RESET HTCapInfo : Set support for LDPC coding capability */ +#define RESETHT_LDPCCODINGCAP(HTCapInfo) (HTCapInfo &= ~MBIT(0)) +/** RESET HTCapInfo : Set support for Channel BW */ +#define RESETHT_SUPPCHANWIDTH(HTCapInfo) (HTCapInfo &= ~MBIT(1)) +/** RESET HTCapInfo : Set support for Greenfield */ +#define RESETHT_GREENFIELD(HTCapInfo) (HTCapInfo &= ~MBIT(4)) +/** RESET HTCapInfo : Set support for Short GI @ 20Mhz */ +#define RESETHT_SHORTGI20(HTCapInfo) (HTCapInfo &= ~MBIT(5)) +/** RESET HTCapInfo : Set support for Short GI @ 40Mhz */ +#define RESETHT_SHORTGI40(HTCapInfo) (HTCapInfo &= ~MBIT(6)) +/** RESET HTCapInfo : Set support for Tx STBC */ +#define RESETHT_TXSTBC(HTCapInfo) (HTCapInfo &= ~MBIT(7)) +/** RESET HTCapInfo : Set support for Rx STBC */ +#define RESETHT_RXSTBC(HTCapInfo) (HTCapInfo &= ~(0x03 << 8)) +/** RESET HTCapInfo : Set support for delayed block ack */ +#define RESETHT_DELAYEDBACK(HTCapInfo) (HTCapInfo &= ~MBIT(10)) +/** RESET HTCapInfo : Set support for Max size AMSDU */ +#define RESETHT_MAXAMSDU(HTCapInfo) (HTCapInfo &= ~MBIT(11)) +/** RESET HTCapInfo : Disable 40Mhz Intolarence */ +#define RESETHT_40MHZ_INTOLARANT(HTCapInfo) (HTCapInfo &= ~MBIT(14)) +/** RESET HTCapInfo: Enable SM power save */ +#define RESETHT_SM_POWERSAVE(HTCapInfo) ((HTCapInfo) &= ~(MBIT(2) | MBIT(3))) +/** RESET HTExtCap : Clear RD Responder bit */ +#define RESETHT_EXTCAP_RDG(HTExtCap) (HTExtCap &= ~MBIT(11)) +/** SET MCS32 */ +#define SETHT_MCS32(x) (x[4] |= 1) +/** Set mcs set defined bit */ +#define SETHT_MCS_SET_DEFINED(x) (x[12] |= 1) +/** Set the highest Rx data rate */ +#define SETHT_RX_HIGHEST_DT_SUPP(x, y) ((*(t_u16 *)(x + 10)) = y) +/** AMPDU factor size */ +#define AMPDU_FACTOR_64K 0x03 +/** Set AMPDU size in A-MPDU paramter field */ +#define SETAMPDU_SIZE(x, y) \ + do { \ + x = x & ~0x03; \ + x |= y & 0x03; \ + } while (0) /** Set AMPDU spacing in A-MPDU paramter field */ +#define SETAMPDU_SPACING(x, y) \ + do { \ + x = x & ~0x1c; \ + x |= (y & 0x07) << 2; \ + } while (0) + +/** RadioType : Support for Band A */ +#define ISSUPP_BANDA(FwCapInfo) (FwCapInfo & MBIT(10)) +/** RadioType : Support for 40Mhz channel BW */ +#define ISALLOWED_CHANWIDTH40(Field2) (Field2 & MBIT(2)) +/** RadioType : Set support 40Mhz channel */ +#define SET_CHANWIDTH40(Field2) (Field2 |= MBIT(2)) +/** RadioType : Reset support 40Mhz channel */ +#define RESET_CHANWIDTH40(Field2) (Field2 &= ~(MBIT(0) | MBIT(1) | MBIT(2))) +/** RadioType : Get secondary channel */ +#define GET_SECONDARYCHAN(Field2) (Field2 & (MBIT(0) | MBIT(1))) + +/** ExtCap : Support for FILS */ +#define ISSUPP_EXTCAP_FILS(ext_cap) (ext_cap.FILS) +/** ExtCap : Set support FILS */ +#define SET_EXTCAP_FILS(ext_cap) (ext_cap.FILS = 1) +/** ExtCap : Reset support FILS */ +#define RESET_EXTCAP_FILS(ext_cap) (ext_cap.FILS = 0) + +/** ExtCap : Support for TDLS */ +#define ISSUPP_EXTCAP_TDLS(ext_cap) (ext_cap.TDLSSupport) +/** ExtCap : Set support TDLS */ +#define SET_EXTCAP_TDLS(ext_cap) (ext_cap.TDLSSupport = 1) +/** ExtCap : Reset support TDLS */ +#define RESET_EXTCAP_TDLS(ext_cap) (ext_cap.TDLSSupport = 0) +/** ExtCap : Support for TDLS UAPSD */ +#define ISSUPP_EXTCAP_TDLS_UAPSD(ext_cap) (ext_cap.TDLSPeerUAPSDSupport) +/** ExtCap : Set support TDLS UAPSD */ +#define SET_EXTCAP_TDLS_UAPSD(ext_cap) (ext_cap.TDLSPeerUAPSDSupport = 1) +/** ExtCap : Reset support TDLS UAPSD */ +#define RESET_EXTCAP_TDLS_UAPSD(ext_cap) (ext_cap.TDLSPeerUAPSDSupport = 0) +/** ExtCap : Support for TDLS CHANNEL SWITCH */ +#define ISSUPP_EXTCAP_TDLS_CHAN_SWITCH(ext_cap) (ext_cap.TDLSChannelSwitching) +/** ExtCap : Set support TDLS CHANNEL SWITCH */ +#define SET_EXTCAP_TDLS_CHAN_SWITCH(ext_cap) (ext_cap.TDLSChannelSwitching = 1) +/** ExtCap : Reset support TDLS CHANNEL SWITCH */ +#define RESET_EXTCAP_TDLS_CHAN_SWITCH(ext_cap) \ + (ext_cap.TDLSChannelSwitching = 0) +/** ExtCap : Set support Multi BSSID */ +#define SET_EXTCAP_MULTI_BSSID(ext_cap) (ext_cap.MultipleBSSID = 1) +/** ExtCap : Support for Interworking */ +#define ISSUPP_EXTCAP_INTERWORKING(ext_cap) (ext_cap.Interworking) +/** ExtCap : Set support Interworking */ +#define SET_EXTCAP_INTERWORKING(ext_cap) (ext_cap.Interworking = 1) +/** ExtCap : Reset support Interworking */ +#define RESET_EXTCAP_INTERWORKING(ext_cap) (ext_cap.Interworking = 0) +/** ExtCap : Support for Operation Mode Notification */ +#define ISSUPP_EXTCAP_OPERMODENTF(ext_cap) (ext_cap.OperModeNtf) +/** ExtCap : Set support Operation Mode Notification */ +#define SET_EXTCAP_OPERMODENTF(ext_cap) (ext_cap.OperModeNtf = 1) +/** ExtCap : Reset support Operation Mode Notification */ +#define RESET_EXTCAP_OPERMODENTF(ext_cap) (ext_cap.OperModeNtf = 0) +/** ExtCap : Support for QosMap */ +#define ISSUPP_EXTCAP_QOS_MAP(ext_cap) (ext_cap.Qos_Map) +/** ExtCap : Set Support QosMap */ +#define SET_EXTCAP_QOS_MAP(ext_cap) (ext_cap.Qos_Map = 1) +/** ExtCap : Reset support QosMap */ +#define RESET_EXTCAP_QOS_MAP(ext_cap) (ext_cap.Qos_Map = 0) +/** ExtCap : Support for BSS_Transition */ +#define ISSUPP_EXTCAP_BSS_TRANSITION(ext_cap) (ext_cap.BSS_Transition) +/** ExtCap : Set Support BSS_Transition */ +#define SET_EXTCAP_BSS_TRANSITION(ext_cap) (ext_cap.BSS_Transition = 1) +/** ExtCap : Reset support BSS_Transition */ +#define RESET_EXTCAP_BSS_TRANSITION(ext_cap) (ext_cap.BSS_Transition = 0) + +/** ExtCap : Support for TDLS wider bandwidth */ +#define ISSUPP_EXTCAP_TDLS_WIDER_BANDWIDTH(ext_cap) (ext_cap.TDLSWildBandwidth) +/** ExtCap : Set support TDLS wider bandwidth */ +#define SET_EXTCAP_TDLS_WIDER_BANDWIDTH(ext_cap) (ext_cap.TDLSWildBandwidth = 1) +/** ExtCap : Reset support TDLS wider bandwidth */ +#define RESET_EXTCAP_TDLS_WIDER_BANDWIDTH(ext_cap) \ + (ext_cap.TDLSWildBandwidth = 0) + +/** ExtCap : Support for extend channel switch */ +#define ISSUPP_EXTCAP_EXT_CHANNEL_SWITCH(ext_cap) (ext_cap.ExtChanSwitching) +/** ExtCap : Set support Ext Channel Switch */ +#define SET_EXTCAP_EXT_CHANNEL_SWITCH(ext_cap) (ext_cap.ExtChanSwitching = 1) +/** ExtCap: Set Timing Measurement */ +#define SET_EXTCAP_EXT_TIMING_MEASUREMENT(ext_cap) \ + (ext_cap.TimingMeasurement = 1) +/** ExtCap : Reset support Ext Channel Switch */ +#define RESET_EXTCAP_EXT_CHANNEL_SWITCH(ext_cap) (ext_cap.ExtChanSwitching = 0) + +/** ExtCap : Support for TWT RESP */ +#define ISSUPP_EXTCAP_EXT_TWT_RESP(ext_cap) (ext_cap.TWTResp) +/** ExtCap : Set support Ext TWT_REQ */ +#define SET_EXTCAP_TWT_REQ(ext_cap) (ext_cap.TWTReq = 1) +/** ExtCap : ReSet support Ext TWT REQ */ +#define RESET_EXTCAP_TWT_REQ(ext_cap) (ext_cap.TWTReq = 0) + +/** LLC/SNAP header len */ +#define LLC_SNAP_LEN 8 + +/** bandwidth following HTCAP */ +#define BW_FOLLOW_HTCAP 0 +/** bandwidth following VHTCAP */ +#define BW_FOLLOW_VHTCAP 1 + +/** HW_SPEC FwCapInfo */ +#define HWSPEC_11ACSGI80_SUPP MBIT(5) +#define HWSPEC_11ACRXSTBC_SUPP MBIT(8) + +#define ISSUPP_11ACENABLED(FwCapInfo) (FwCapInfo & (MBIT(12) | MBIT(13))) + +#define ISSUPP_11AC2GENABLED(FwCapInfo) (FwCapInfo & MBIT(12)) +#define ISSUPP_11AC5GENABLED(FwCapInfo) (FwCapInfo & MBIT(13)) + +/** HW_SPEC Dot11acDevCap : HTC-VHT supported */ +#define ISSUPP_11ACVHTHTCVHT(Dot11acDevCap) (Dot11acDevCap & MBIT(22)) +/** HW_SPEC Dot11acDevCap : VHT TXOP PS support */ +#define ISSUPP_11ACVHTTXOPPS(Dot11acDevCap) (Dot11acDevCap & MBIT(21)) +/** HW_SPEC Dot11acDevCap : MU RX beamformee support */ +#define ISSUPP_11ACMURXBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap & MBIT(20)) +/** HW_SPEC Dot11acDevCap : MU TX beamformee support */ +#define ISSUPP_11ACMUTXBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap & MBIT(19)) +/** HW_SPEC Dot11acDevCap : SU Beamformee support */ +#define ISSUPP_11ACSUBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap & MBIT(12)) +/** HW_SPEC Dot11acDevCap : SU Beamformer support */ +#define ISSUPP_11ACSUBEAMFORMER(Dot11acDevCap) (Dot11acDevCap & MBIT(11)) +/** HW_SPEC Dot11acDevCap : Rx STBC support */ +#define ISSUPP_11ACRXSTBC(Dot11acDevCap) (Dot11acDevCap & MBIT(8)) +/** HW_SPEC Dot11acDevCap : Tx STBC support */ +#define ISSUPP_11ACTXSTBC(Dot11acDevCap) (Dot11acDevCap & MBIT(7)) +/** HW_SPEC Dot11acDevCap : Short GI support for 160MHz BW */ +#define ISSUPP_11ACSGI160(Dot11acDevCap) (Dot11acDevCap & MBIT(6)) +/** HW_SPEC Dot11acDevCap : Short GI support for 80MHz BW */ +#define ISSUPP_11ACSGI80(Dot11acDevCap) (Dot11acDevCap & MBIT(5)) +/** HW_SPEC Dot11acDevCap : LDPC coding support */ +#define ISSUPP_11ACLDPC(Dot11acDevCap) (Dot11acDevCap & MBIT(4)) +/** HW_SPEC Dot11acDevCap : Channel BW 20/40/80/160/80+80 MHz support */ +#define ISSUPP_11ACBW8080(Dot11acDevCap) (Dot11acDevCap & MBIT(3)) +/** HW_SPEC Dot11acDevCap : Channel BW 20/40/80/160 MHz support */ +#define ISSUPP_11ACBW160(Dot11acDevCap) (Dot11acDevCap & MBIT(2)) + +/** Set VHT Cap Info: Max MPDU length */ +#define SET_VHTCAP_MAXMPDULEN(VHTCapInfo, value) (VHTCapInfo |= (value & 0x03)) +/** SET VHT CapInfo: Supported Channel Width SET (2 bits)*/ +#define SET_VHTCAP_CHWDSET(VHTCapInfo, value) \ + (VHTCapInfo |= ((value & 0x3) << 2)) +/** SET VHT CapInfo: Rx STBC (3 bits) */ +#define SET_VHTCAP_RXSTBC(VHTCapInfo, value) \ + (VHTCapInfo |= ((value & 0x7) << 8)) +/** SET VHT CapInfo: Commpressed Steering Num of BFer Ant Supported (3 bits) */ +#define SET_VHTCAP_SNBFERANT(VHTCapInfo, value) \ + (VHTCapInfo |= ((value & 0x7) << 13)) +/** SET VHT CapInfo: Num of Sounding Dimensions (3 bits) */ +#define SET_VHTCAP_NUMSNDDM(VHTCapInfo, value) \ + (VHTCapInfo |= ((value & 0x7) << 16)) +/** SET VHT CapInfo: Max AMPDU Length Exponent (3 bits) */ +#define SET_VHTCAP_MAXAMPDULENEXP(VHTCapInfo, value) \ + (VHTCapInfo |= ((value & 0x7) << 23)) +/** SET VHT CapInfo: VHT Link Adaptation Capable (2 bits) */ +#define SET_VHTCAP_LINKADPCAP(VHTCapInfo, value) \ + (VHTCapInfo |= ((value & 0x3) << 26)) + +/** HW_SPEC Dot11acDevCap : ReSet VHT Link Adapation Capable */ +#define RESET_11ACVHTLINKCAPA(Dot11acDevCap, value) (Dot11acDevCap &= ~(0x03)) +/** HW_SPEC Dot11acDevCap : ReSet Maximum AMPDU Length Exponent */ +#define RESET_11ACAMPDULENEXP(Dot11acDevCap, value) (Dot11acDevCap &= ~(0x07)) +/** HW_SPEC Dot11acDevCap : ReSet support of HTC-VHT */ +#define RESET_11ACVHTHTCVHT(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(22)) +/** HW_SPEC Dot11acDevCap : ReSet support of VHT TXOP PS */ +#define RESET_11ACVHTTXOPPS(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(21)) +/** HW_SPEC Dot11acDevCap : ReSet support of MU RX beamformee */ +#define RESET_11ACMURXBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(20)) +/** HW_SPEC Dot11acDevCap : ReSet support of MU TX beamformee */ +#define RESET_11ACMUTXBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(19)) +/** HW_SPEC Dot11acDevCap : ReSet Number of Sounding Dimensions */ +#define RESET_11ACSOUNDINGNUM(Dot11acDevCap) (Dot11acDevCap &= ~((0x07) << 16)) +/** HW_SPEC Dot11acDevCap : ReSet Compressed Steering Number + * of Beamformer Antenna */ +#define RESET_11ACBFANTNUM(Dot11acDevCap) (Dot11acDevCap &= ~((0x07) << 13)) +/** HW_SPEC Dot11acDevCap : ReSet support of SU Beamformee */ +#define RESET_11ACSUBEAMFORMEE(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(12)) +/** HW_SPEC Dot11acDevCap : ReSet support of SU Beamformer */ +#define RESET_11ACSUBEAMFORMER(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(11)) +/** HW_SPEC Dot11acDevCap : ReSet support of Rx STBC */ +#define RESET_11ACRXSTBC(Dot11acDevCap) (Dot11acDevCap &= ~((0x07) << 8)) +/** HW_SPEC Dot11acDevCap : ReSet support of Tx STBC */ +#define RESET_11ACTXSTBC(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(7)) +/** HW_SPEC Dot11acDevCap : ReSet support of Short GI support for 160MHz BW */ +#define RESET_11ACSGI160(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(6)) +/** HW_SPEC Dot11acDevCap : ReSet support of Short GI support for 80MHz BW */ +#define RESET_11ACSGI80(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(5)) +/** HW_SPEC Dot11acDevCap : ReSet support of LDPC coding */ +#define RESET_11ACLDPC(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(4)) +/** HW_SPEC Dot11acDevCap : ReSet support of + * Channel BW 20/40/80/160/80+80 MHz */ +#define RESET_11ACBW8080(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(3)) +/** HW_SPEC Dot11acDevCap : ReSet support of + * Channel BW 20/40/80/160 MHz */ +#define RESET_11ACBW160(Dot11acDevCap) (Dot11acDevCap &= ~MBIT(2)) +/** HW_SPEC Dot11acDevCap : ReSet Max MPDU length */ +#define RESET_11ACMAXMPDULEN(Dot11acDevCap) (Dot11acDevCap &= ~(0x03)) + +/** Default 11ac capability mask for 2.4GHz */ +#define DEFAULT_11AC_CAP_MASK_BG \ + (HWSPEC_11ACSGI80_SUPP | HWSPEC_11ACRXSTBC_SUPP) +/** Default 11ac capability mask for 5GHz */ +#define DEFAULT_11AC_CAP_MASK_A (HWSPEC_11ACSGI80_SUPP | HWSPEC_11ACRXSTBC_SUPP) +/** GET VHT CapInfo : MAX MPDU Length */ +#define GET_VHTCAP_MAXMPDULEN(VHTCapInfo) (VHTCapInfo & 0x3) +/** GET VHT CapInfo: Supported Channel Width SET (2 bits)*/ +#define GET_VHTCAP_CHWDSET(VHTCapInfo) ((VHTCapInfo >> 2) & 0x3) +/** GET VHT CapInfo: Rx STBC (3 bits) */ +#define GET_VHTCAP_RXSTBC(VHTCapInfo) ((VHTCapInfo >> 8) & 0x7) +/** GET VHT CapInfo: Compressed Steering Num of BFer Ant Supported (3 bits) */ +#define GET_VHTCAP_SNBFERANT(VHTCapInfo) ((VHTCapInfo >> 13) & 0x7) +/** GET VHT CapInfo: Num of Sounding Dimensions (3 bits) */ +#define GET_VHTCAP_NUMSNDDM(VHTCapInfo) ((VHTCapInfo >> 16) & 0x7) +/** GET VHT CapInfo: Max AMPDU Length Exponent (3 bits) */ +#define GET_VHTCAP_MAXAMPDULENEXP(VHTCapInfo) ((VHTCapInfo >> 23) & 0x7) +/** GET VHT CapInfo: VHT Link Adaptation Capable (2 bits) */ +#define GET_VHTCAP_LINKADPCAP(VHTCapInfo) ((VHTCapInfo >> 26) & 0x3) +/**SET OPERATING MODE:Channel Width:80M*/ +#define SET_OPER_MODE_80M(oper_mode) \ + (oper_mode = (oper_mode & ~MBIT(0)) | MBIT(1)) +/**SET OPERATING MODE:Channel Width:40M*/ +#define SET_OPER_MODE_40M(oper_mode) \ + (oper_mode = (oper_mode & ~MBIT(1)) | MBIT(0)) +/**SET OPERATING MODE:Channel Width:20M*/ +#define SET_OPER_MODE_20M(oper_mode) (oper_mode &= ~(0x03)) +#define IS_OPER_MODE_20M(oper_mode) (((oper_mode) & (MBIT(0) | MBIT(1))) == 0) +/**SET OPERATING MODE:Rx NSS:2*/ +#define SET_OPER_MODE_2NSS(oper_mode) \ + (oper_mode = (oper_mode & ~(MBIT(5) | MBIT(6))) | MBIT(4)) +/**SET OPERATING MODE:Rx NSS:1*/ +#define SET_OPER_MODE_1NSS(oper_mode) \ + (oper_mode &= ~(MBIT(4) | MBIT(5) | MBIT(6))) + +#define GET_VHTMCS(MCSMapSet) (MCSMapSet & 0xFFFF) +#define GET_VHTNSSMCS(MCSMapSet, nss) ((MCSMapSet >> (2 * (nss - 1))) & 0x3) +#define RET_VHTNSSMCS(MCSMapSet, nss) ((MCSMapSet >> (2 * (nss - 1))) & 0x3) +#define SET_VHTNSSMCS(MCSMapSet, nss, value) \ + (MCSMapSet |= (value & 0x3) << (2 * (nss - 1))) + +/** DevMCSSupported : Tx MCS supported */ +#define GET_DEVTXMCSMAP(DevMCSMap) (DevMCSMap >> 16) +#define GET_DEVNSSTXMCS(DevMCSMap, nss) \ + ((DevMCSMap >> (2 * (nss - 1) + 16)) & 0x3) +#define SET_DEVNSSTXMCS(DevMCSMap, nss, value) \ + (DevMCSMap |= (value & 0x3) << (2 * (nss - 1) + 16)) +#define RESET_DEVTXMCSMAP(DevMCSMap) (DevMCSMap &= 0xFFFF) +/** DevMCSSupported : Rx MCS supported */ +#define GET_DEVRXMCSMAP(DevMCSMap) (DevMCSMap & 0xFFFF) +#define GET_DEVNSSRXMCS(DevMCSMap, nss) ((DevMCSMap >> (2 * (nss - 1))) & 0x3) +#define SET_DEVNSSRXMCS(DevMCSMap, nss, value) \ + (DevMCSMap |= (value & 0x3) << (2 * (nss - 1))) +#define RESET_DEVRXMCSMAP(DevMCSMap) (DevMCSMap &= 0xFFFF0000) + +/** TLV type : Rate scope */ +#define TLV_TYPE_RATE_DROP_PATTERN \ + (PROPRIETARY_TLV_BASE_ID + 0x51) /* 0x0151 \ + */ +/** TLV type : Rate drop pattern */ +#define TLV_TYPE_RATE_DROP_CONTROL \ + (PROPRIETARY_TLV_BASE_ID + 0x52) /* 0x0152 \ + */ +/** TLV type : Rate scope */ +#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 0x53) /* 0x0153 */ + +/** TLV type : Power group */ +#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 0x54) /* 0x0154 */ + +/** Modulation class for DSSS Rates */ +#define MOD_CLASS_HR_DSSS 0x03 +/** Modulation class for OFDM Rates */ +#define MOD_CLASS_OFDM 0x07 +/** Modulation class for HT Rates */ +#define MOD_CLASS_HT 0x08 +/** Modulation class for VHT Rates */ +#define MOD_CLASS_VHT 0x09 +/** HT bandwidth 20 MHz */ +#define HT_BW_20 0 +/** HT bandwidth 40 MHz */ +#define HT_BW_40 1 +/** HT bandwidth 80 MHz */ +#define HT_BW_80 2 + +/** TLV type : TX RATE CFG, rename from TLV_TYPE_GI_LTF_SIZE to include CMD and + * HE ER SU settings to this tlv */ +#define TLV_TYPE_TX_RATE_CFG (PROPRIETARY_TLV_BASE_ID + 319) /* 0x023f */ + +/** 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 : DFS W53 Configuration */ +#define TLV_TYPE_DFS_W53_CFG (PROPRIETARY_TLV_BASE_ID + 0x145) // + 325 +#ifdef OPCHAN +/** TLV type : OpChannel control */ +#define TLV_TYPE_OPCHAN_CONTROL_DESC \ + (PROPRIETARY_TLV_BASE_ID + 0x79) /* 0x0179 */ +/** TLV type : OpChannel channel group control */ +#define TLV_TYPE_OPCHAN_CHANGRP_CTRL \ + (PROPRIETARY_TLV_BASE_ID + 0x7a) /* 0x017a */ +#endif + +/** TLV type : Action frame */ +#define TLV_TYPE_IEEE_ACTION_FRAME \ + (PROPRIETARY_TLV_BASE_ID + 0x8c) /* 0x018c \ + */ + +/** TLV type : SCAN channel gap */ +#define TLV_TYPE_SCAN_CHANNEL_GAP \ + (PROPRIETARY_TLV_BASE_ID + 0xc5) /* 0x01c5 \ + */ +/** TLV type : Channel statistics */ +#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 0xc6) /* 0x01c6 */ +/** TLV type : BSS_MODE */ +#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 0xce) /* 0x01ce */ + +/** Firmware Host Command ID Constants */ +/** Host Command ID : Get hardware specifications */ +#define HostCmd_CMD_GET_HW_SPEC 0x0003 +/** Host Command ID : 802.11 scan */ +#define HostCmd_CMD_802_11_SCAN 0x0006 +/** Host Command ID : 802.11 get log */ +#define HostCmd_CMD_802_11_GET_LOG 0x000b + +/** Host Command id: GET_TX_RX_PKT_STATS */ +#define HOST_CMD_TX_RX_PKT_STATS 0x008d + +/** Host Command ID : 802.11 get/set link layer statistic */ +#define HostCmd_CMD_802_11_LINK_STATS 0x0256 + +/** Host Command ID : MAC multicast address */ +#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 +/** Host Command ID : 802.11 EEPROM access */ +#define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059 +/** Host Command ID : 802.11 associate */ +#define HostCmd_CMD_802_11_ASSOCIATE 0x0012 + +/** Host Command ID : 802.11 SNMP MIB */ +#define HostCmd_CMD_802_11_SNMP_MIB 0x0016 +/** Host Command ID : MAC register access */ +#define HostCmd_CMD_MAC_REG_ACCESS 0x0019 +/** Host Command ID : BBP register access */ +#define HostCmd_CMD_BBP_REG_ACCESS 0x001a +/** Host Command ID : RF register access */ +#define HostCmd_CMD_RF_REG_ACCESS 0x001b + +/** Host Command ID : 802.11 radio control */ +#define HostCmd_CMD_802_11_RADIO_CONTROL 0x001c +/** Host Command ID : 802.11 RF channel */ +#define HostCmd_CMD_802_11_RF_CHANNEL 0x001d +/** Host Command ID : 802.11 RF Tx power */ +#define HostCmd_CMD_802_11_RF_TX_POWER 0x001e + +/** Host Command ID : 802.11 RF antenna */ +#define HostCmd_CMD_802_11_RF_ANTENNA 0x0020 + +/** Host Command ID : 802.11 deauthenticate */ +#define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 +/** Host Command ID: 802.11 disassoicate */ +#define HostCmd_CMD_802_11_DISASSOCIATE 0x0026 +/** Host Command ID : MAC control */ +#define HostCmd_CMD_MAC_CONTROL 0x0028 +/** Host Command ID : 802.11 Ad-Hoc start */ +#define HostCmd_CMD_802_11_AD_HOC_START 0x002b +/** Host Command ID : 802.11 Ad-Hoc join */ +#define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c + +/** Host Command ID: CW Mode */ +#define HostCmd_CMD_CW_MODE_CTRL 0x0239 +/** Host Command ID : 802.11 key material */ +#define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e + +/** Host Command ID : 802.11 Ad-Hoc stop */ +#define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040 + +/** Host Command ID : 802.22 MAC address */ +#define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D + +/** Host Command ID : WMM Traffic Stream Status */ +#define HostCmd_CMD_WMM_TS_STATUS 0x005d + +/** Host Command ID : 802.11 D domain information */ +#define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b + +/*This command gets/sets the Transmit Rate-based Power Control (TRPC) channel + * configuration.*/ +#define HostCmd_CHANNEL_TRPC_CONFIG 0x00fb + +/** Host Command ID : 802.11 TPC information */ +#define HostCmd_CMD_802_11_TPC_INFO 0x005f +/** Host Command ID : 802.11 TPC adapt req */ +#define HostCmd_CMD_802_11_TPC_ADAPT_REQ 0x0060 +/** Host Command ID : 802.11 channel SW ann */ +#define HostCmd_CMD_802_11_CHAN_SW_ANN 0x0061 + +/** Host Command ID : Measurement request */ +#define HostCmd_CMD_MEASUREMENT_REQUEST 0x0062 +/** Host Command ID : Measurement report */ +#define HostCmd_CMD_MEASUREMENT_REPORT 0x0063 + +/** Host Command ID : 802.11 sleep parameters */ +#define HostCmd_CMD_802_11_SLEEP_PARAMS 0x0066 + +/** Host Command ID : 802.11 ps inactivity timeout */ +#define HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT 0x0067 + +/** Host Command ID : 802.11 sleep period */ +#define HostCmd_CMD_802_11_SLEEP_PERIOD 0x0068 + +/** Host Command ID: 802.11 BG scan config */ +#define HostCmd_CMD_802_11_BG_SCAN_CONFIG 0x006b +/** Host Command ID : 802.11 BG scan query */ +#define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c + +/** Host Command ID : WMM ADDTS req */ +#define HostCmd_CMD_WMM_ADDTS_REQ 0x006E +/** Host Command ID : WMM DELTS req */ +#define HostCmd_CMD_WMM_DELTS_REQ 0x006F +/** Host Command ID : WMM queue configuration */ +#define HostCmd_CMD_WMM_QUEUE_CONFIG 0x0070 +/** Host Command ID : 802.11 get status */ +#define HostCmd_CMD_WMM_GET_STATUS 0x0071 + +/** Host Command ID : 802.11 subscribe event */ +#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 + +/** Host Command ID : 802.11 Tx rate query */ +#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f +/** Host Command ID :Get timestamp value */ +#define HostCmd_CMD_GET_TSF 0x0080 + +/** Host Command ID : WMM queue stats */ +#define HostCmd_CMD_WMM_QUEUE_STATS 0x0081 + +/** Host Command ID : KEEP ALIVE command */ +#define HostCmd_CMD_AUTO_TX 0x0082 + +/** 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 + +#if defined(SDIO) +/** Host Command ID : SDIO GPIO interrupt configuration */ +#define HostCmd_CMD_SDIO_GPIO_INT_CONFIG 0x0088 +#endif + +/** Host Command ID : Mfg command */ +#define HostCmd_CMD_MFG_COMMAND 0x0089 +/** Host Command ID : Inactivity timeout ext */ +#define HostCmd_CMD_INACTIVITY_TIMEOUT_EXT 0x008a + +/** Host Command ID : DBGS configuration */ +#define HostCmd_CMD_DBGS_CFG 0x008b +/** Host Command ID : Get memory */ +#define HostCmd_CMD_GET_MEM 0x008c + +/** Host Command ID : Cal data dnld */ +#define HostCmd_CMD_CFG_DATA 0x008f + +/** Host Command ID : SDIO pull control */ +#define HostCmd_CMD_SDIO_PULL_CTRL 0x0093 + +/** Host Command ID : ECL system clock configuration */ +#define HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG 0x0094 + +/** Host Command ID : Extended version */ +#define HostCmd_CMD_VERSION_EXT 0x0097 + +/** Host Command ID : MEF configuration */ +#define HostCmd_CMD_MEF_CFG 0x009a +/** Host Command ID : 802.11 RSSI INFO*/ +#define HostCmd_CMD_RSSI_INFO 0x00a4 +/** Host Command ID : Function initialization */ +#define HostCmd_CMD_FUNC_INIT 0x00a9 +/** Host Command ID : Function shutdown */ +#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa + +/** Host Command ID : Robustcoex */ +#define HostCmd_CMD_802_11_ROBUSTCOEX 0x00e0 + +/** Host Command ID :EAPOL PKT */ +#define HostCmd_CMD_802_11_EAPOL_PKT 0x012e + +/** Host Command ID :MIMO SWITCH **/ +#define HostCmd_CMD_802_11_MIMO_SWITCH 0x0235 + +/** Host Command ID : 802.11 RSSI INFO EXT*/ +#define HostCmd_CMD_RSSI_INFO_EXT 0x0237 + +#ifdef RX_PACKET_COALESCE +/** TLV ID for RX pkt coalesce config */ +#define TLV_TYPE_RX_PKT_COAL_CONFIG (PROPRIETARY_TLV_BASE_ID + 0xC9) +#endif + +/** Host Command ID : Channel report request */ +#define HostCmd_CMD_CHAN_REPORT_REQUEST 0x00dd + +/** Host Command ID: SUPPLICANT_PMK */ +#define HostCmd_CMD_SUPPLICANT_PMK 0x00c4 +/** Host Command ID: SUPPLICANT_PROFILE */ +#define HostCmd_CMD_SUPPLICANT_PROFILE 0x00c5 + +/** Host Command ID : Add Block Ack Request */ +#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce +/** Host Command ID : Delete a Block Ack Request */ +#define HostCmd_CMD_11N_CFG 0x00cd +/** Host Command ID : Add Block Ack Response */ +#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf +/** Host Command ID : Delete a Block Ack Request */ +#define HostCmd_CMD_11N_DELBA 0x00d0 +/** Host Command ID: Configure Tx Buf size */ +#define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 +/** Host Command ID: AMSDU Aggr Ctrl */ +#define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df +/** Host Command ID: 11AC config */ +#define HostCmd_CMD_11AC_CFG 0x0112 +/** 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 + +#define HostCmd_CMD_802_11_BAND_STEERING 0x026f + +#ifdef SDIO +/** Host Command ID : SDIO single port RX aggr */ +#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223 + +/** fw_cap_info bit16 for sdio sp rx aggr flag*/ +#define SDIO_SP_RX_AGGR_ENABLE MBIT(16) + +#endif + +/* fw_cap_info bit18 for ecsa support*/ +#define FW_CAPINFO_ECSA MBIT(18) + +/* fw_cap_info bit20 for get log*/ +#define FW_CAPINFO_GET_LOG MBIT(20) + +/** fw_cap_info bit22 for embedded supplicant support*/ +#define FW_CAPINFO_SUPPLICANT_SUPPORT MBIT(21) + +/** fw_cap_info bit23 for embedded authenticator support*/ +#define FW_CAPINFO_AUTH_SUPPORT MBIT(22) + +/** fw_cap_info bit25 for adhoc support*/ +#define FW_CAPINFO_ADHOC_SUPPORT MBIT(25) +/** Check if adhoc is supported by firmware */ +#define IS_FW_SUPPORT_ADHOC(_adapter) \ + (_adapter->fw_cap_info & FW_CAPINFO_ADHOC_SUPPORT) + +/** Check if supplicant is supported by firmware */ +#define IS_FW_SUPPORT_SUPPLICANT(_adapter) \ + (_adapter->fw_cap_info & FW_CAPINFO_SUPPLICANT_SUPPORT) + +/** Check if authenticator is supported by firmware */ +#define IS_FW_SUPPORT_AUTHENTICATOR(_adapter) \ + (_adapter->fw_cap_info & FW_CAPINFO_AUTH_SUPPORT) + +/** Ext fw cap info bit0 only 1x1 5G is available */ +#define FW_CAPINFO_EXT_5G_1X1_ONLY MBIT(0) +/** Ext fw cap info bit1 1x1 5G is not available */ +#define FW_CAPINFO_EXT_NO_5G_1X1 MBIT(1) +/** Ext fw cap info bit 2 only 1x1 2G is available */ +#define FW_CAPINFO_EXT_2G_1X1_ONLY MBIT(2) +/**Ext fw cap info bit3 1x1 2G is not available */ +#define FW_CAPINFO_EXT_NO_2G_1X1 MBIT(3) +/** Ext fw cap info bit4 1x1 + 1x1 5G mode is unavailable */ +#define FW_CAPINFO_EXT_NO_5G_1X1_PLUS_1X1 MBIT(4) +/** Ext fw cap info bit5 80 + 80 MHz capability disabled */ +#define FW_CAPINFO_EXT_NO_80MHz_PLUS_80MHz MBIT(5) +/** Ext fw cap info bit6 1024 QAM is disabled */ +#define FW_CAPINFO_EXT_NO_1024_QAM MBIT(6) +/** FW cap info bit 7 11AX */ +#define FW_CAPINFO_EXT_802_11AX MBIT(7) +/** FW cap info bit 8: 80MHZ disabled */ +#define FW_CAPINFO_EXT_NO_80MHZ MBIT(8) + +/** Check if 5G 1x1 only is supported by firmware */ +#define IS_FW_SUPPORT_5G_1X1_ONLY(_adapter) \ + (_adapter->fw_cap_ext & FW_CAPINFO_EXT_5G_1X1_ONLY) +/** Check if 5G 1x1 is unavailable in firmware */ +#define IS_FW_SUPPORT_NO_5G_1X1(_adapter) \ + (_adapter->fw_cap_ext & FW_CAPINFO_EXT_NO_5G_1X1) +/** Check if 2G 1x1 only is supported by firmware */ +#define IS_FW_SUPPORT_2G_1X1_ONLY(_adapter) \ + (_adapter->fw_cap_ext & FW_CAPINFO_EXT_2G_1X1_ONLY) +/** Check if 2G 1x1 is unavailable in firmware */ +#define IS_FW_SUPPORT_NO_2G_1X1(_adapter) \ + (_adapter->fw_cap_ext & FW_CAPINFO_EXT_NO_2G_1X1) +/** Check if 5G 1x1 + 1x1 mode is disabled in firmware */ +#define IS_FW_SUPPORT_NO_5G_1X1_PLUS_1X1(_adapter) \ + (_adapter->fw_cap_ext & FW_CAPINFO_EXT_NO_5G_1X1_PLUS_1X1) +/** Check if 80 + 80MHz is disabled in firmware */ +#define IS_FW_SUPPORT_NO_80MHz_PLUS_80MHz(_adapter) \ + (_adapter->fw_cap_ext & FW_CAPINFO_EXT_NO_80MHz_PLUS_80MHz) +/** Check if 1024 QAM disabled in firmware */ +#define IS_FW_SUPPORT_NO_1024_QAM(_adapter) \ + (_adapter->fw_cap_ext & FW_CAPINFO_EXT_NO_1024_QAM) +/** Check if 80MHZ disabled in firmware */ +#define IS_FW_SUPPORT_NO_80MHZ(_adapter) \ + (_adapter->fw_cap_ext & FW_CAPINFO_EXT_NO_80MHZ) + +/** FW cap info TLV */ +typedef MLAN_PACK_START struct _MrvlIEtypes_fw_cap_info_t { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; + /** Fw cap info bitmap */ + t_u32 fw_cap_info; + /** Extended fw cap info bitmap */ + t_u32 fw_cap_ext; +} MLAN_PACK_END MrvlIEtypes_fw_cap_info_t, *pMrvlIEtypes_fw_cap_info_t; + +/** Check if 11AX is supported by firmware */ +#define IS_FW_SUPPORT_11AX(_adapter) \ + (_adapter->fw_cap_ext & FW_CAPINFO_EXT_802_11AX) + +typedef MLAN_PACK_START struct _MrvlIEtypes_Extension_t { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; + /** Element id extension */ + t_u8 ext_id; + /** payload */ + t_u8 data[]; +} MLAN_PACK_END MrvlIEtypes_Extension_t, *pMrvlIEtypes_Extension_t; + +/* HE MAC Capabilities Information field BIT 1 for TWT Req */ +#define HE_MAC_CAP_TWT_REQ_SUPPORT MBIT(1) +/* HE MAC Capabilities Information field BIT 2 for TWT Resp*/ +#define HE_MAC_CAP_TWT_RESP_SUPPORT MBIT(2) +typedef MLAN_PACK_START struct _MrvlIEtypes_He_cap_t { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; + /** Element id extension */ + t_u8 ext_id; + /** he mac capability info */ + t_u8 he_mac_cap[6]; + /** he phy capability info */ + t_u8 he_phy_cap[11]; + /** he txrx mcs support , size would be 4 or 8 or 12 */ + t_u8 he_txrx_mcs_support[4]; + /** 160Mhz tx rx mcs support*/ + t_u8 he160_txrx_mcs_support[4]; + /** 80+80 Mhz tx rx mcs suport */ + t_u8 he8080_txrx_mcs_support[4]; + /** PPE Thresholds (optional) */ + t_u8 val[20]; +} MLAN_PACK_END MrvlIEtypes_He_cap_t, *pMrvlIEtypes_he_cap_t; + +#ifdef RX_PACKET_COALESCE +/** Host Command ID : Rx packet coalescing configuration */ +#define HostCmd_CMD_RX_PKT_COALESCE_CFG 0x012c +#endif + +/** Host Command ID : Extended scan support */ +#define HostCmd_CMD_802_11_SCAN_EXT 0x0107 + +/** Host Command ID : Forward mgmt frame */ +#define HostCmd_CMD_RX_MGMT_IND 0x010c + +#ifdef PCIE +/** Host Command ID: Host buffer description */ +#define HostCmd_CMD_PCIE_HOST_BUF_DETAILS 0x00fa +#endif + +/** Host Command ID : Set BSS_MODE */ +#define HostCmd_CMD_SET_BSS_MODE 0x00f7 + +#ifdef UAP_SUPPORT +/** Host Command id: SYS_INFO */ +#define HOST_CMD_APCMD_SYS_INFO 0x00ae +/** Host Command id: sys_reset */ +#define HOST_CMD_APCMD_SYS_RESET 0x00af +/** Host Command id: SYS_CONFIGURE */ +#define HOST_CMD_APCMD_SYS_CONFIGURE 0x00b0 +/** Host Command id: BSS_START */ +#define HOST_CMD_APCMD_BSS_START 0x00b1 +/** Host Command id: BSS_STOP */ +#define HOST_CMD_APCMD_BSS_STOP 0x00b2 +/** Host Command id: sta_list */ +#define HOST_CMD_APCMD_STA_LIST 0x00b3 +/** Host Command id: STA_DEAUTH */ +#define HOST_CMD_APCMD_STA_DEAUTH 0x00b5 + +/** Host Command id: REPORT_MIC */ +#define HOST_CMD_APCMD_REPORT_MIC 0x00ee +/** Host Command id: UAP_OPER_CTRL */ +#define HOST_CMD_APCMD_OPER_CTRL 0x0233 +#endif /* UAP_SUPPORT */ + +/** Host Command id: PMIC CONFIGURE*/ +#define HOST_CMD_PMIC_CONFIGURE 0x23E + +/** Host Command ID: Tx data pause */ +#define HostCmd_CMD_CFG_TX_DATA_PAUSE 0x0103 + +#ifdef WIFI_DIRECT_SUPPORT +/** Host Command ID: P2P PARAMS CONFIG */ +#define HOST_CMD_P2P_PARAMS_CONFIG 0x00ea +/** Host Command ID: WIFI_DIRECT_MODE_CONFIG */ +#define HOST_CMD_WIFI_DIRECT_MODE_CONFIG 0x00eb +#endif + +/** Host Command ID: Remain On Channel */ +#define HostCmd_CMD_802_11_REMAIN_ON_CHANNEL 0x010d + +#define HostCmd_CMD_COALESCE_CFG 0x010a + +/** Host Command ID: GTK REKEY OFFLOAD CFG */ +#define HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG 0x010f + +/** Host Command ID : OTP user data */ +#define HostCmd_CMD_OTP_READ_USER_DATA 0x0114 + +/** Host Command ID: HS wakeup reason */ +#define HostCmd_CMD_HS_WAKEUP_REASON 0x0116 + +/** Host Command ID: reject addba request */ +#define HostCmd_CMD_REJECT_ADDBA_REQ 0x0119 + +#define HostCmd_CMD_FW_DUMP_EVENT 0x0125 + +#define HostCMD_CONFIG_LOW_POWER_MODE 0x0128 + +/** Host Command ID : Target device access */ +#define HostCmd_CMD_TARGET_ACCESS 0x012a + +/** Host Command ID: BCA device access */ +#define HostCmd_CMD_BCA_REG_ACCESS 0x0272 + +/** Host Command ID: DFS repeater mode */ +#define HostCmd_DFS_REPEATER_MODE 0x012b + +/** Host Command ID: ACS scan */ +#define HostCMD_APCMD_ACS_SCAN 0x0224 + +/** Host Command ID: Get sensor temp*/ +#define HostCmd_DS_GET_SENSOR_TEMP 0x0227 + +/** Host Command ID : Configure ADHOC_OVER_IP parameters */ +#define HostCmd_CMD_WMM_PARAM_CONFIG 0x023a + +#ifdef STA_SUPPORT +/** Host Command ID : set/get sta configure */ +#define HostCmd_CMD_STA_CONFIGURE 0x023f +#endif + +/** Host Command ID : GPIO independent reset configure */ +#define HostCmd_CMD_INDEPENDENT_RESET_CFG 0x0243 + +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) +/* TLV type: reg type */ +#define TLV_TYPE_REG_ACCESS_CTRL (PROPRIETARY_TLV_BASE_ID + 0x13C) /* 0x023c*/ +/** MrvlIEtypes_Reg_type_t*/ +typedef MLAN_PACK_START struct _MrvlIEtypes_Reg_type_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** type: 0x81/0x82/0x83 */ + t_u8 type; +} MLAN_PACK_END MrvlIEtypes_Reg_type_t; + +#endif +#define HostCmd_CMD_CHAN_REGION_CFG 0x0242 +/* mod_grp */ +typedef enum _mod_grp { + MOD_CCK, // 0 + MOD_OFDM_PSK, // 1 + MOD_OFDM_QAM16, // 2 + MOD_OFDM_QAM64, // 3 + MOD_HT_20_PSK, // 4 + MOD_HT_20_QAM16, // 5 + MOD_HT_20_QAM64, // 6 + MOD_HT_40_PSK, // 7 + MOD_HT_40_QAM16, // 8 + MOD_HT_40_QAM64, // 9 +#ifdef STREAM_2x2 + MOD_HT2_20_PSK, // 10 + MOD_HT2_20_QAM16, // 11 + MOD_HT2_20_QAM64, // 12 + MOD_HT2_40_PSK, // 13 + MOD_HT2_40_QAM16, // 14 + MOD_HT2_40_QAM64, // 15 +#endif + + MOD_VHT_20_QAM256, // 16 + MOD_VHT_40_QAM256, // 17 + MOD_VHT_80_PSK, // 18 + MOD_VHT_80_QAM16, // 19 + MOD_VHT_80_QAM64, // 20 + MOD_VHT_80_QAM256, // 21 +#ifdef STREAM_2x2 + MOD_VHT2_20_QAM256, // 22 + MOD_VHT2_40_QAM256, // 23 + MOD_VHT2_80_PSK, // 24 + MOD_VHT2_80_QAM16, // 25 + MOD_VHT2_80_QAM64, // 26 + MOD_VHT2_80_QAM256, // 27 +#endif +} mod_grp; + +typedef MLAN_PACK_START struct _power_table_attr { + t_u8 rows_2g; + t_u8 cols_2g; + t_u8 rows_5g; + t_u8 cols_5g; +} MLAN_PACK_END power_table_attr_t; + +#define FW_CFP_TABLE_MAX_ROWS_BG 14 +#define FW_CFP_TABLE_MAX_COLS_BG 17 + +#define FW_CFP_TABLE_MAX_ROWS_A 39 +#define FW_CFP_TABLE_MAX_COLS_A 29 + +#define HostCmd_CMD_DYN_BW 0x0252 + +#define HostCmd_CMD_BOOT_SLEEP 0x0258 + +#define HostCmd_CMD_RX_ABORT_CFG 0x0261 +#define HostCmd_CMD_RX_ABORT_CFG_EXT 0x0262 +#define HostCmd_CMD_TX_AMPDU_PROT_MODE 0x0263 +#define HostCmd_CMD_RATE_ADAPT_CFG 0x0264 +#define HostCmd_CMD_CCK_DESENSE_CFG 0x0265 + +#define HostCmd_CMD_VDLL 0x0240 +#if defined(PCIE) +#define HostCmd_CMD_SSU 0x0259 +#endif + +#define HostCmd_CMD_DMCS_CONFIG 0x0260 + +/** Host Command ID: 11AX config */ +#define HostCmd_CMD_11AX_CFG 0x0266 +/** Host Command ID: 11AX command */ +#define HostCmd_CMD_11AX_CMD 0x026d +/** Host Command ID: Range ext command */ +#define HostCmd_CMD_RANGE_EXT 0x0274 +/** Host Command ID: TWT cfg command */ +#define HostCmd_CMD_TWT_CFG 0x0270 + +#define HostCmd_CMD_LOW_POWER_MODE_CFG 0x026e +#define HostCmd_CMD_UAP_BEACON_STUCK_CFG 0x0271 +#define HostCmd_CMD_ARB_CONFIG 0x0273 +#define HostCmd_CMD_DOT11MC_UNASSOC_FTM_CFG 0x0275 + +/** 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 : Set Default */ +#define HostCmd_ACT_GEN_SET_DEFAULT 0x0002 +/** General purpose action : Get_Current */ +#define HostCmd_ACT_GEN_GET_CURRENT 0x0003 +/** General purpose action : Remove */ +#define HostCmd_ACT_GEN_REMOVE 0x0004 +/** General purpose action : Reset */ +#define HostCmd_ACT_GEN_RESET 0x0005 + +/** Host command action : Set Rx */ +#define HostCmd_ACT_SET_RX 0x0001 +/** Host command action : Set Tx */ +#define HostCmd_ACT_SET_TX 0x0002 +/** Host command action : Set both Rx and Tx */ +#define HostCmd_ACT_SET_BOTH 0x0003 +/** Host command action : Get Rx */ +#define HostCmd_ACT_GET_RX 0x0004 +/** Host command action : Get Tx */ +#define HostCmd_ACT_GET_TX 0x0008 +/** Host command action : Get both Rx and Tx */ +#define HostCmd_ACT_GET_BOTH 0x000c + +/** General Result Code*/ +/** General result code OK */ +#define HostCmd_RESULT_OK 0x0000 +/** Genenral error */ +#define HostCmd_RESULT_ERROR 0x0001 +/** Command is not valid */ +#define HostCmd_RESULT_NOT_SUPPORT 0x0002 +/** Command is pending */ +#define HostCmd_RESULT_PENDING 0x0003 +/** System is busy (command ignored) */ +#define HostCmd_RESULT_BUSY 0x0004 +/** Data buffer is not big enough */ +#define HostCmd_RESULT_PARTIAL_DATA 0x0005 + +/* Define action or option for HostCmd_CMD_MAC_CONTROL */ +/** MAC action : Rx on */ +#define HostCmd_ACT_MAC_RX_ON 0x0001 +/** MAC action : Tx on */ +#define HostCmd_ACT_MAC_TX_ON 0x0002 +/** MAC action : WEP enable */ +#define HostCmd_ACT_MAC_WEP_ENABLE 0x0008 +/** MAC action : EthernetII enable */ +#define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010 +/** MAC action : Promiscous mode enable */ +#define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 +/** MAC action : All multicast enable */ +#define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 +/** MAC action : RTS/CTS enable */ +#define HostCmd_ACT_MAC_RTS_CTS_ENABLE 0x0200 +/** MAC action : Strict protection enable */ +#define HostCmd_ACT_MAC_STRICT_PROTECTION_ENABLE 0x0400 +/** MAC action : Force 11n protection disable */ +#define HostCmd_ACT_MAC_FORCE_11N_PROTECTION_OFF 0x0800 +/** MAC action : Ad-Hoc G protection on */ +#define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000 +/** MAC action : Static-Dynamic BW enable */ +#define HostCmd_ACT_MAC_STATIC_DYNAMIC_BW_ENABLE MBIT(16) +/** MAC action : Dynamic BW */ +#define HostCmd_ACT_MAC_DYNAMIC_BW MBIT(17) + +/* Define action or option for HostCmd_CMD_802_11_SCAN */ +/** Scan type : BSS */ +#define HostCmd_BSS_MODE_BSS 0x0001 +/** Scan type : IBSS */ +#define HostCmd_BSS_MODE_IBSS 0x0002 +/** Scan type : Any */ +#define HostCmd_BSS_MODE_ANY 0x0003 + +/** Define bitmap conditions for HOST_SLEEP_CFG : GPIO FF */ +#define HOST_SLEEP_CFG_GPIO_FF 0xff +/** Define bitmap conditions for HOST_SLEEP_CFG : GAP FF */ +#define HOST_SLEEP_CFG_GAP_FF 0xff + +/** Buffer Constants */ +/** Number of command buffers */ +#define MRVDRV_NUM_OF_CMD_BUFFER 40 +/** Maximum number of BSS Descriptors */ +#define MRVDRV_MAX_BSSID_LIST 200 + +/** Host command flag in command */ +#define CMD_F_HOSTCMD (1 << 0) +/** command cancel flag in command */ +#define CMD_F_CANCELED (1 << 1) +/** scan command flag */ +#define CMD_F_SCAN (1 << 2) + +/** Host Command ID bit mask (bit 11:0) */ +#define HostCmd_CMD_ID_MASK 0x0fff + +/** Host Command Sequence number mask (bit 7:0) */ +#define HostCmd_SEQ_NUM_MASK 0x00ff + +/** Host Command BSS number mask (bit 11:8) */ +#define HostCmd_BSS_NUM_MASK 0x0f00 + +/** Host Command BSS type mask (bit 15:12) */ +#define HostCmd_BSS_TYPE_MASK 0xf000 + +/** Set BSS information to Host Command */ +#define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) \ + ((((seq)&0x00ff) | (((num)&0x000f) << 8)) | (((type)&0x000f) << 12)) + +/** Get Sequence Number from Host Command (bit 7:0) */ +#define HostCmd_GET_SEQ_NO(seq) ((seq)&HostCmd_SEQ_NUM_MASK) + +/** Get BSS number from Host Command (bit 11:8) */ +#define HostCmd_GET_BSS_NO(seq) (((seq)&HostCmd_BSS_NUM_MASK) >> 8) + +/** Get BSS type from Host Command (bit 15:12) */ +#define HostCmd_GET_BSS_TYPE(seq) (((seq)&HostCmd_BSS_TYPE_MASK) >> 12) + +/** Card Event definition : Dummy host wakeup signal */ +#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 +/** Card Event definition : Link lost */ +#define EVENT_LINK_LOST 0x00000003 +/** Card Event definition : Link sensed */ +#define EVENT_LINK_SENSED 0x00000004 +/** Card Event definition : MIB changed */ +#define EVENT_MIB_CHANGED 0x00000006 +/** Card Event definition : Init done */ +#define EVENT_INIT_DONE 0x00000007 +/** Card Event definition : Deauthenticated */ +#define EVENT_DEAUTHENTICATED 0x00000008 +/** Card Event definition : Disassociated */ +#define EVENT_DISASSOCIATED 0x00000009 +/** Card Event definition : Power save awake */ +#define EVENT_PS_AWAKE 0x0000000a +/** Card Event definition : Power save sleep */ +#define EVENT_PS_SLEEP 0x0000000b +/** Card Event definition : MIC error multicast */ +#define EVENT_MIC_ERR_MULTICAST 0x0000000d +/** Card Event definition : MIC error unicast */ +#define EVENT_MIC_ERR_UNICAST 0x0000000e + +/** Card Event definition : Ad-Hoc BCN lost */ +#define EVENT_ADHOC_BCN_LOST 0x00000011 + +/** Card Event definition : Stop Tx */ +#define EVENT_STOP_TX 0x00000013 +/** Card Event definition : Start Tx */ +#define EVENT_START_TX 0x00000014 +/** Card Event definition : Channel switch */ +#define EVENT_CHANNEL_SWITCH 0x00000015 + +/** Card Event definition : MEAS report ready */ +#define EVENT_MEAS_REPORT_RDY 0x00000016 + +/** Card Event definition : WMM status change */ +#define EVENT_WMM_STATUS_CHANGE 0x00000017 + +/** Card Event definition : BG scan report */ +#define EVENT_BG_SCAN_REPORT 0x00000018 +/** Card Event definition : BG scan stopped */ +#define EVENT_BG_SCAN_STOPPED 0x00000065 + +/** Card Event definition : Beacon RSSI low */ +#define EVENT_RSSI_LOW 0x00000019 +/** Card Event definition : Beacon SNR low */ +#define EVENT_SNR_LOW 0x0000001a +/** Card Event definition : Maximum fail */ +#define EVENT_MAX_FAIL 0x0000001b +/** Card Event definition : Beacon RSSI high */ +#define EVENT_RSSI_HIGH 0x0000001c +/** Card Event definition : Beacon SNR high */ +#define EVENT_SNR_HIGH 0x0000001d + +/** Card Event definition : IBSS coalsced */ +#define EVENT_IBSS_COALESCED 0x0000001e + +/** Event definition : IBSS station connected */ +#define EVENT_IBSS_STATION_CONNECT 0x00000020 +/** Event definition : IBSS station dis-connected */ +#define EVENT_IBSS_STATION_DISCONNECT 0x00000021 + +/** Card Event definition : Data RSSI low */ +#define EVENT_DATA_RSSI_LOW 0x00000024 +/** Card Event definition : Data SNR low */ +#define EVENT_DATA_SNR_LOW 0x00000025 +/** Card Event definition : Data RSSI high */ +#define EVENT_DATA_RSSI_HIGH 0x00000026 +/** Card Event definition : Data SNR high */ +#define EVENT_DATA_SNR_HIGH 0x00000027 + +/** Card Event definition : Link Quality */ +#define EVENT_LINK_QUALITY 0x00000028 + +/** Card Event definition : Port release event */ +#define EVENT_PORT_RELEASE 0x0000002b + +/** Card Event definition : Pre-Beacon Lost */ +#define EVENT_PRE_BEACON_LOST 0x00000031 + +#define EVENT_WATCHDOG_TMOUT 0x00000032 + +/** Card Event definition : Add BA event */ +#define EVENT_ADDBA 0x00000033 +/** Card Event definition : Del BA event */ +#define EVENT_DELBA 0x00000034 +/** Card Event definition: BA stream timeout*/ +#define EVENT_BA_STREAM_TIMEOUT 0x00000037 + +/** Card Event definition : AMSDU aggr control */ +#define EVENT_AMSDU_AGGR_CTRL 0x00000042 + +/** Card Event definition: WEP ICV error */ +#define EVENT_WEP_ICV_ERR 0x00000046 + +/** Card Event definition : Host sleep enable */ +#define EVENT_HS_ACT_REQ 0x00000047 + +/** Card Event definition : BW changed */ +#define EVENT_BW_CHANGE 0x00000048 + +#ifdef WIFI_DIRECT_SUPPORT +/** WIFIDIRECT generic event */ +#define EVENT_WIFIDIRECT_GENERIC_EVENT 0x00000049 +/** WIFIDIRECT service discovery event */ +#define EVENT_WIFIDIRECT_SERVICE_DISCOVERY 0x0000004a +#endif +/** Remain on Channel expired event */ +#define EVENT_REMAIN_ON_CHANNEL_EXPIRED 0x0000005f + +#define EVENT_MEF_HOST_WAKEUP 0x0000004f + +/** Card Event definition: Channel switch pending announcment */ +#define EVENT_CHANNEL_SWITCH_ANN 0x00000050 + +/** Event definition: Radar Detected by card */ +#define EVENT_RADAR_DETECTED 0x00000053 + +/** Event definition: Radar Detected by card */ +#define EVENT_CHANNEL_REPORT_RDY 0x00000054 + +/** Event definition: Scan results through event */ +#define EVENT_EXT_SCAN_REPORT 0x00000058 +/** Enhance ext scan done event */ +#define EVENT_EXT_SCAN_STATUS_REPORT 0x0000007f + +/** Event definition : FW debug information */ +#define EVENT_FW_DEBUG_INFO 0x00000063 + +/** Event definition: RXBA_SYNC */ +#define EVENT_RXBA_SYNC 0x00000059 + +#ifdef UAP_SUPPORT +/** Event ID: STA deauth */ +#define EVENT_MICRO_AP_STA_DEAUTH 0x0000002c +/** Event ID: STA assoicated */ +#define EVENT_MICRO_AP_STA_ASSOC 0x0000002d +/** Event ID: BSS started */ +#define EVENT_MICRO_AP_BSS_START 0x0000002e +/** Event ID: BSS idle event */ +#define EVENT_MICRO_AP_BSS_IDLE 0x00000043 +/** Event ID: BSS active event */ +#define EVENT_MICRO_AP_BSS_ACTIVE 0x00000044 + +/** Event ID: MIC countermeasures event */ +#define EVENT_MICRO_AP_MIC_COUNTERMEASURES 0x0000004c +#endif /* UAP_SUPPORT */ + +/** Event ID: TX data pause event */ +#define EVENT_TX_DATA_PAUSE 0x00000055 + +/** Event ID: SAD Report */ +#define EVENT_SAD_REPORT 0x00000066 + +/** Event ID: Tx status */ +#define EVENT_TX_STATUS_REPORT 0x00000074 + +#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0x00000076 + +#if defined(PCIE) +#define EVENT_SSU_DUMP_DMA 0x0000008C +#endif + +#define EVENT_VDLL_IND 0x00000081 +#define EVENT_EXCEED_MAX_P2P_CONN 0x00000089 + +#define EVENT_FW_HANG_REPORT 0x0000008F + +#define EVENT_FW_DUMP_INFO 0x00000073 +/** Event ID mask */ +#define EVENT_ID_MASK 0xffff + +/** BSS number mask */ +#define BSS_NUM_MASK 0xf + +/** Get BSS number from event cause (bit 23:16) */ +#define EVENT_GET_BSS_NUM(event_cause) (((event_cause) >> 16) & BSS_NUM_MASK) + +/** Get BSS type from event cause (bit 31:24) */ +#define EVENT_GET_BSS_TYPE(event_cause) (((event_cause) >> 24) & 0x00ff) + +/** Event_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*/ +#if defined(STA_SUPPORT) +/** Pairwise Cipher Suite length */ +#define PAIRWISE_CIPHER_SUITE_LEN 4 +/** AKM Suite length */ +#define AKM_SUITE_LEN 4 +/** MFPC bit in RSN capability */ +#define MFPC_BIT 7 +/** MFPR bit in RSN capability */ +#define MFPR_BIT 6 +#endif +/** Bit mask for TxPD status field for null packet */ +#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 +/** Bit mask for TxPD status field for last packet */ +#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 + +/** Bit mask for TxPD flags field for Tx status report */ +#define MRVDRV_TxPD_FLAGS_TX_PACKET_STATUS MBIT(5) + +/** Packet type: 802.11 */ +#define PKT_TYPE_802DOT11 0x05 +#define PKT_TYPE_MGMT_FRAME 0xE5 +/** Packet type: AMSDU */ +#define PKT_TYPE_AMSDU 0xE6 +/** Packet type: BAR */ +#define PKT_TYPE_BAR 0xE7 + +/** Packet type: debugging */ +#define PKT_TYPE_DEBUG 0xEF + +/** channel number at bit 5-13 */ +#define RXPD_CHAN_MASK 0x3FE0 +/** Rate control mask 15-23 */ +#define TXPD_RATE_MASK 0xff8000 +/** enable bw ctrl in TxPD */ +#define TXPD_BW_ENABLE MBIT(20) +/** enable tx power ctrl in TxPD */ +#define TXPD_TXPW_ENABLE MBIT(7) +/** sign of power */ +#define TXPD_TXPW_NEGATIVE MBIT(6) +/** Enable Rate ctrl in TxPD */ +#define TXPD_TXRATE_ENABLE MBIT(15) +/** enable retry limit in TxPD */ +#define TXPD_RETRY_ENABLE MBIT(12) + +/** TxPD descriptor */ +typedef MLAN_PACK_START struct _TxPD { + /** BSS type */ + t_u8 bss_type; + /** BSS number */ + t_u8 bss_num; + /** Tx packet length */ + t_u16 tx_pkt_length; + /** Tx packet offset */ + t_u16 tx_pkt_offset; + /** Tx packet type */ + t_u16 tx_pkt_type; + /** Tx Control */ + t_u32 tx_control; + /** Pkt Priority */ + t_u8 priority; + /** Transmit Pkt Flags*/ + t_u8 flags; + /** Amount of time the packet has been queued + * in the driver (units = 2ms)*/ + t_u8 pkt_delay_2ms; + /** reserved */ + t_u8 reserved; + /** Tx Control */ + t_u32 tx_control_1; +} MLAN_PACK_END TxPD, *PTxPD; + +/** RxPD Descriptor */ +typedef MLAN_PACK_START struct _RxPD { + /** BSS type */ + t_u8 bss_type; + /** BSS number */ + t_u8 bss_num; + /** Rx Packet Length */ + t_u16 rx_pkt_length; + /** Rx Pkt offset */ + t_u16 rx_pkt_offset; + /** Rx packet type */ + t_u16 rx_pkt_type; + /** Sequence number */ + t_u16 seq_num; + /** Packet Priority */ + t_u8 priority; + /** Rx Packet Rate */ + t_u8 rx_rate; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10 + * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01 BW80 = 10 BW160 + * = 11 [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1 [Bit 5] STBC + * support Enabled = 1 [Bit 6] LDPC support Enabled = 1 [Bit 7] [Bit4, + * Bit7] AX Guard interval, 00, 01, 10 */ + t_u8 rate_info; + /** Reserved */ + t_u8 reserved[3]; + /** TDLS flags, bit 0: 0=InfraLink, 1=DirectLink */ + t_u8 flags; + /**For SD8887 antenna info: 0 = 2.4G antenna a; 1 = 2.4G antenna b; 3 = + * 5G antenna; 0xff = invalid value */ + t_u8 antenna; + /* [31:0] ToA of the rx packet, [63:32] ToD of the ack for the rx packet + * Both ToA and ToD are in nanoseconds */ + t_u64 toa_tod_tstamps; + /** rx info */ + t_u32 rx_info; +} MLAN_PACK_END RxPD, *PRxPD; + +/** IEEEtypes_FrameCtl_t*/ +#ifdef BIG_ENDIAN_SUPPORT +typedef MLAN_PACK_START struct _IEEEtypes_FrameCtl_t { + /** Order */ + t_u8 order : 1; + /** Wep */ + t_u8 wep : 1; + /** More Data */ + t_u8 more_data : 1; + /** Power Mgmt */ + t_u8 pwr_mgmt : 1; + /** Retry */ + t_u8 retry : 1; + /** More Frag */ + t_u8 more_frag : 1; + /** From DS */ + t_u8 from_ds : 1; + /** To DS */ + t_u8 to_ds : 1; + /** Sub Type */ + t_u8 sub_type : 4; + /** Type */ + t_u8 type : 2; + /** Protocol Version */ + t_u8 protocol_version : 2; +} MLAN_PACK_END IEEEtypes_FrameCtl_t; +#else +typedef MLAN_PACK_START struct _IEEEtypes_FrameCtl_t { + /** Protocol Version */ + t_u8 protocol_version : 2; + /** Type */ + t_u8 type : 2; + /** Sub Type */ + t_u8 sub_type : 4; + /** To DS */ + t_u8 to_ds : 1; + /** From DS */ + t_u8 from_ds : 1; + /** More Frag */ + t_u8 more_frag : 1; + /** Retry */ + t_u8 retry : 1; + /** Power Mgmt */ + t_u8 pwr_mgmt : 1; + /** More Data */ + t_u8 more_data : 1; + /** Wep */ + t_u8 wep : 1; + /** Order */ + t_u8 order : 1; +} MLAN_PACK_END IEEEtypes_FrameCtl_t; +#endif + +/** MrvlIETypes_MgmtFrameSet_t */ +typedef MLAN_PACK_START struct _MrvlIETypes_MgmtFrameSet_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** Frame Control */ + IEEEtypes_FrameCtl_t frame_control; + /* t_u8 frame_contents[]; */ +} MLAN_PACK_END MrvlIETypes_MgmtFrameSet_t; + +/** Beacon */ +typedef MLAN_PACK_START struct _IEEEtypes_Beacon_t { + /** time stamp */ + t_u8 time_stamp[8]; + /** beacon interval */ + t_u16 beacon_interval; + /** cap info */ + t_u16 cap_info; +} MLAN_PACK_END IEEEtypes_Beacon_t; + +/** Fixed size of station association event */ +#define ASSOC_EVENT_FIX_SIZE 12 + +/** MrvlIEtypes_channel_band_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_channel_band_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Band Configuration */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; +} MLAN_PACK_END MrvlIEtypes_channel_band_t; + +#ifdef UAP_SUPPORT +/** 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[]; */ +} 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[]; */ +} 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_7 : 1; + /** First passive scan then active scan */ + t_u8 passive_to_active_scan : 1; + /** First channel in scan */ + t_u8 first_chan : 1; + /** Enable hidden ssid report */ + t_u8 hidden_ssid_report : 1; + /** Enable probe response timeout */ + t_u8 rsp_timeout_en : 1; + /** Multidomain scan mode */ + t_u8 multidomain_scan : 1; + /** Disble channel filtering flag */ + t_u8 disable_chan_filt : 1; + /** Channel scan mode passive flag */ + t_u8 passive_scan : 1; +#else + /** Channel scan mode passive flag */ + t_u8 passive_scan : 1; + /** Disble channel filtering flag */ + t_u8 disable_chan_filt : 1; + /** Multidomain scan mode */ + t_u8 multidomain_scan : 1; + /** Enable probe response timeout */ + t_u8 rsp_timeout_en : 1; + /** Enable hidden ssid report */ + t_u8 hidden_ssid_report : 1; + /** First channel in scan */ + t_u8 first_chan : 1; + /** First passive scan then active scan */ + t_u8 passive_to_active_scan : 1; + /** Reserved */ + t_u8 reserved_7 : 1; +#endif +} MLAN_PACK_END ChanScanMode_t; + +/** ChanScanParamSet_t */ +typedef MLAN_PACK_START struct _ChanScanParamSet_t { + /** Channel scan parameter : band config */ + Band_Config_t bandcfg; + /** Channel scan parameter : Channel number */ + t_u8 chan_number; + /** Channel scan parameter : Channel scan mode */ + ChanScanMode_t chan_scan_mode; + /** Channel scan parameter : Minimum scan time */ + t_u16 min_scan_time; + /** Channel scan parameter : Maximum scan time */ + t_u16 max_scan_time; +} MLAN_PACK_END ChanScanParamSet_t; + +/** MrvlIEtypes_ChanListParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ChanListParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Channel scan parameters */ + ChanScanParamSet_t chan_scan_param[1]; +} MLAN_PACK_END MrvlIEtypes_ChanListParamSet_t; + +/** MrvlIEtypes_EESParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_EESParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** EES scan mode */ + t_u16 ees_mode; + /** EES report condition */ + t_u16 report_cond; + /** EES High Period scan interval */ + t_u16 high_period; + /** EES High Period scan count */ + t_u16 high_period_count; + /** EES Medium Period scan interval */ + t_u16 mid_period; + /** EES Medium Period scan count */ + t_u16 mid_period_count; + /** EES Low Period scan interval */ + t_u16 low_period; + /** EES Low Period scan count */ + t_u16 low_period_count; +} MLAN_PACK_END MrvlIEtypes_EESParamSet_t; + +/** MrvlIEtype_EESNetworkCfg_t */ +typedef MLAN_PACK_START struct _MrvlIEtype_EESNetworkCfg_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Number of networks in the list */ + t_u8 network_count; + /** Maximum number of connection */ + t_u8 max_conn_count; + /** Black List Exp */ + t_u8 black_list_exp; +} MLAN_PACK_END MrvlIEtype_EESNetworkCfg_t; + +/** ChanBandParamSet_t */ +typedef struct _ChanBandParamSet_t { + /** Channel scan parameter : band config */ + Band_Config_t bandcfg; + /** Channel number */ + t_u8 chan_number; +} ChanBandParamSet_t; + +/** MrvlIEtypes_ChanBandListParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ChanBandListParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Channel Band parameters */ + ChanBandParamSet_t chan_band_param[1]; +} MLAN_PACK_END MrvlIEtypes_ChanBandListParamSet_t; + +/** MrvlIEtypes_RatesParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RatesParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Rates */ + t_u8 rates[1]; +} MLAN_PACK_END MrvlIEtypes_RatesParamSet_t; + +/** _MrvlIEtypes_Bssid_List_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Bssid_List_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END MrvlIEtypes_Bssid_List_t; + +/** MrvlIEtypes_SsIdParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_SsIdParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** SSID */ + t_u8 ssid[1]; +} MLAN_PACK_END MrvlIEtypes_SsIdParamSet_t; + +/**MrvlIEtypes_AssocType_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_HostMlme_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Authentication type */ + t_u8 host_mlme; +} MLAN_PACK_END MrvlIEtypes_HostMlme_t; + +/** MrvlIEtypes_NumProbes_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_NumProbes_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Number of probes */ + t_u16 num_probes; +} MLAN_PACK_END MrvlIEtypes_NumProbes_t; + +/** MrvlIEtypes_WildCardSsIdParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_WildCardSsIdParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Maximum SSID length */ + t_u8 max_ssid_length; + /** SSID */ + t_u8 ssid[1]; +} MLAN_PACK_END MrvlIEtypes_WildCardSsIdParamSet_t; + +/**TSF data size */ +#define TSF_DATA_SIZE 8 +/** Table of TSF values returned in the scan result */ +typedef MLAN_PACK_START struct _MrvlIEtypes_TsfTimestamp_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** the length of each TSF data is 8 bytes, could be multiple TSF here + */ + t_u8 tsf_data[1]; +} MLAN_PACK_END MrvlIEtypes_TsfTimestamp_t; + +/** CfParamSet_t */ +typedef MLAN_PACK_START struct _CfParamSet_t { + /** CF parameter : Count */ + t_u8 cfp_cnt; + /** CF parameter : Period */ + t_u8 cfp_period; + /** CF parameter : Duration */ + t_u16 cfp_max_duration; + /** CF parameter : Duration remaining */ + t_u16 cfp_duration_remaining; +} MLAN_PACK_END CfParamSet_t; + +/** IbssParamSet_t */ +typedef MLAN_PACK_START struct _IbssParamSet_t { + /** ATIM window value */ + t_u16 atim_window; +} MLAN_PACK_END IbssParamSet_t; + +/** MrvlIEtypes_SsParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_SsParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** CF/IBSS parameters sets */ + union { + /** CF parameter set */ + CfParamSet_t cf_param_set[1]; + /** IBSS parameter set */ + IbssParamSet_t ibss_param_set[1]; + } cf_ibss; +} MLAN_PACK_END MrvlIEtypes_SsParamSet_t; + +/** FhParamSet_t */ +typedef MLAN_PACK_START struct _FhParamSet_t { + /** FH parameter : Dwell time */ + t_u16 dwell_time; + /** FH parameter : Hop set */ + t_u8 hop_set; + /** FH parameter : Hop pattern */ + t_u8 hop_pattern; + /** FH parameter : Hop index */ + t_u8 hop_index; +} MLAN_PACK_END FhParamSet_t; + +/** DsParamSet_t */ +typedef MLAN_PACK_START struct _DsParamSet_t { + /** Current channel number */ + t_u8 current_chan; +} MLAN_PACK_END DsParamSet_t; + +/** MrvlIEtypes_PhyParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_PhyParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** FH/DS parameters */ + union { + /** FH parameter set */ + FhParamSet_t fh_param_set[1]; + /** DS parameter set */ + DsParamSet_t ds_param_set[1]; + } fh_ds; +} MLAN_PACK_END MrvlIEtypes_PhyParamSet_t; + +/* Auth type to be used in the Authentication portion of an Assoc seq */ +/** MrvlIEtypes_AuthType_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_AuthType_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Authentication type */ + t_u16 auth_type; +} MLAN_PACK_END MrvlIEtypes_AuthType_t; + +/** MrvlIEtypes_ScanChanGap_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ScanChanGap_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Time gap in units to TUs to be used between + * two consecutive channels scan */ + t_u16 gap; +} MLAN_PACK_END MrvlIEtypes_ScanChanGap_t; + +/** channel statictics */ +typedef MLAN_PACK_START struct _chan_statistics_t { + /** channle number */ + t_u8 chan_num; + /** band info */ + Band_Config_t bandcfg; + /** flags */ + t_u8 flags; + /** noise */ + t_s8 noise; + /** total network */ + t_u16 total_networks; + /** scan duration */ + t_u16 cca_scan_duration; + /** busy duration */ + t_u16 cca_busy_duration; +} MLAN_PACK_END chan_statistics_t; + +/** channel statictics tlv */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ChannelStats_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** channel statictics */ + chan_statistics_t chanStat[]; +} MLAN_PACK_END MrvlIEtypes_ChannelStats_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[]; +} MLAN_PACK_END MrvlIEtypes_RsnParamSet_t; + +/** MrvlIEtypes_SecurityCfg_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_SecurityCfg_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** enable 11w */ + t_u8 use_mfp; +} MLAN_PACK_END MrvlIEtypes_SecurityCfg_t; + +/** Host Command ID : _HostCmd_DS_BEACON_STUCK_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_BEACON_STUCK_CFG { + /** ACT_GET/ACT_SET */ + t_u8 action; + /** No of beacon interval after which firmware will check if beacon Tx + * is going fine */ + t_u8 beacon_stuck_detect_count; + /** Upon performing MAC reset, no of beacon interval after which + * firmware will check if recovery was successful */ + t_u8 recovery_confirm_count; +} MLAN_PACK_END HostCmd_DS_BEACON_STUCK_CFG; + +/** Key Info flag for multicast key */ +#define KEY_INFO_MCAST_KEY 0x01 +/** Key Info flag for unicast key */ +#define KEY_INFO_UCAST_KEY 0x02 +/** Key Info flag for enable key */ +#define KEY_INFO_ENABLE_KEY 0x04 +/** Key Info flag for default key */ +#define KEY_INFO_DEFAULT_KEY 0x08 +/** Key Info flag for TX key */ +#define KEY_INFO_TX_KEY 0x10 +/** Key Info flag for RX key */ +#define KEY_INFO_RX_KEY 0x20 +#define KEY_INFO_CMAC_AES_KEY 0x400 +/** PN size for WPA/WPA2 */ +#define WPA_PN_SIZE 8 +/** PN size for PMF IGTK */ +#define IGTK_PN_SIZE 8 +/** WAPI KEY size */ +#define WAPI_KEY_SIZE 32 +/** key params fix size */ +#define KEY_PARAMS_FIXED_LEN 10 +/** key index mask */ +#define KEY_INDEX_MASK 0xf + +/** wep_param */ +typedef MLAN_PACK_START struct _wep_param_t { + /** key_len */ + t_u16 key_len; + /** wep key */ + t_u8 key[MAX_WEP_KEY_SIZE]; +} MLAN_PACK_END wep_param_t; + +/** tkip_param */ +typedef MLAN_PACK_START struct _tkip_param { + /** Rx packet num */ + t_u8 pn[WPA_PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** tkip key */ + t_u8 key[WPA_TKIP_KEY_LEN]; +} MLAN_PACK_END tkip_param; + +/** aes_param */ +typedef MLAN_PACK_START struct _aes_param { + /** Rx packet num */ + t_u8 pn[WPA_PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** aes key */ + t_u8 key[WPA_AES_KEY_LEN]; +} MLAN_PACK_END aes_param; + +/** wapi_param */ +typedef MLAN_PACK_START struct _wapi_param { + /** Rx packet num */ + t_u8 pn[PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** wapi key */ + t_u8 key[WAPI_KEY_SIZE]; +} MLAN_PACK_END wapi_param; + +/** cmac_aes_param */ +typedef MLAN_PACK_START struct _cmac_aes_param { + /** IGTK pn */ + t_u8 ipn[IGTK_PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** aes key */ + t_u8 key[CMAC_AES_KEY_LEN]; +} MLAN_PACK_END cmac_aes_param; + +/** gmac_param */ +typedef MLAN_PACK_START struct _gcmp_param { + /** GCMP pn */ + t_u8 pn[WPA_PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** aes key */ + t_u8 key[WPA_GCMP_KEY_LEN]; +} MLAN_PACK_END gcmp_param; + +/** ccmp256_param */ +typedef MLAN_PACK_START struct _ccmp256_param { + /** CCMP pn */ + t_u8 pn[WPA_PN_SIZE]; + /** key_len */ + t_u16 key_len; + /** ccmp256 key */ + t_u8 key[WPA_CCMP_256_KEY_LEN]; +} MLAN_PACK_END ccmp_256_param; + +/** MrvlIEtype_KeyParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtype_KeyParamSetV2_t { + /** Type ID */ + t_u16 type; + /** Length of Payload */ + t_u16 length; + /** mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** key index */ + t_u8 key_idx; + /** Type of Key: WEP=0, TKIP=1, AES=2, WAPI=3 AES_CMAC=4 */ + t_u8 key_type; + /** Key Control Info specific to a key_type_id */ + t_u16 key_info; + union { + /** wep key param */ + wep_param_t wep; + /** tkip key param */ + tkip_param tkip; + /** aes key param */ + aes_param aes; + /** wapi key param */ + wapi_param wapi; + /** IGTK key param */ + cmac_aes_param cmac_aes; + /** gcmp key param */ + gcmp_param gcmp; + /** ccmp 256 key parameters */ + ccmp_256_param ccmp256; + } key_params; +} MLAN_PACK_END MrvlIEtype_KeyParamSetV2_t; + +/** HostCmd_DS_802_11_KEY_MATERIAL */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_KEY_MATERIAL { + /** Action */ + t_u16 action; + /** Key parameter set */ + MrvlIEtype_KeyParamSetV2_t key_param_set; +} MLAN_PACK_END HostCmd_DS_802_11_KEY_MATERIAL; + +/** HostCmd_DS_GTK_REKEY_PARAMS */ +typedef MLAN_PACK_START struct _HostCmd_DS_GTK_REKEY_PARAMS { + /** Action */ + t_u16 action; + /** Key confirmation key */ + t_u8 kck[MLAN_KCK_LEN]; + /** Key encryption key */ + t_u8 kek[MLAN_KEK_LEN]; + /** Replay counter low 32 bit */ + t_u32 replay_ctr_low; + /** Replay counter high 32 bit */ + t_u32 replay_ctr_high; +} MLAN_PACK_END HostCmd_DS_GTK_REKEY_PARAMS; + +/** Data structure of WMM QoS information */ +typedef MLAN_PACK_START struct _WmmQosInfo_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; + /** Reserved */ + t_u8 reserved : 3; + /** Parameter set count */ + t_u8 para_set_count : 4; +#else + /** Parameter set count */ + t_u8 para_set_count : 4; + /** Reserved */ + t_u8 reserved : 3; + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END WmmQosInfo_t, *pWmmQosInfo_t; + +/** Data structure of WMM ECW */ +typedef MLAN_PACK_START struct _WmmEcw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max : 4; + /** Minimum Ecw */ + t_u8 ecw_min : 4; +#else + /** Minimum Ecw */ + t_u8 ecw_min : 4; + /** Maximum Ecw */ + t_u8 ecw_max : 4; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END WmmEcw_t, *pWmmEcw_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef MLAN_PACK_START struct _WmmAciAifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved : 1; + /** Aci */ + t_u8 aci : 2; + /** Acm */ + t_u8 acm : 1; + /** Aifsn */ + t_u8 aifsn : 4; +#else + /** Aifsn */ + t_u8 aifsn : 4; + /** Acm */ + t_u8 acm : 1; + /** Aci */ + t_u8 aci : 2; + /** Reserved */ + t_u8 reserved : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END WmmAciAifsn_t, *pWmmAciAifsn_t; + +/** Data structure of WMM AC parameters */ +typedef MLAN_PACK_START struct _WmmAcParameters_t { + WmmAciAifsn_t aci_aifsn; /**< AciAifSn */ + WmmEcw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} MLAN_PACK_END WmmAcParameters_t, *pWmmAcParameters_t; + +/** Data structure of WMM parameter */ +typedef MLAN_PACK_START struct _WmmParameter_t { + /** OuiType: 00:50:f2:02 */ + t_u8 ouitype[4]; + /** Oui subtype: 01 */ + t_u8 ouisubtype; + /** version: 01 */ + t_u8 version; + /** QoS information */ + t_u8 qos_info; + /** Reserved */ + t_u8 reserved; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + WmmAcParameters_t ac_params[MAX_AC_QUEUES]; +} MLAN_PACK_END WmmParameter_t, *pWmmParameter_t; + +/** Data structure of Host command WMM_PARAM_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_WMM_PARAM_CONFIG { + /** action */ + t_u16 action; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + WmmAcParameters_t ac_params[MAX_AC_QUEUES]; +} MLAN_PACK_END HostCmd_DS_WMM_PARAM_CONFIG; + +/* Definition of firmware host command */ +/** HostCmd_DS_GEN */ +typedef MLAN_PACK_START struct _HostCmd_DS_GEN { + /** Command */ + t_u16 command; + /** Size */ + t_u16 size; + /** Sequence number */ + t_u16 seq_num; + /** Result */ + t_u16 result; +} MLAN_PACK_END HostCmd_DS_GEN + + ; + +/** Size of HostCmd_DS_GEN */ +#define S_DS_GEN sizeof(HostCmd_DS_GEN) + +/** mod_group_setting */ +typedef MLAN_PACK_START struct _mod_group_setting { + /** modulation group */ + t_u8 mod_group; + /** power */ + t_u8 power; +} MLAN_PACK_END mod_group_setting; + +/** MrvlIETypes_ChanTRPCConfig_t */ +typedef MLAN_PACK_START struct _MrvlIETypes_ChanTRPCConfig_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** start freq */ + t_u16 start_freq; + /* channel width */ + t_u8 width; + /** channel number */ + t_u8 chan_num; + /** mode groups */ + mod_group_setting mod_group[]; +} MLAN_PACK_END MrvlIETypes_ChanTRPCConfig_t; + +/* HostCmd_DS_CHANNEL_TRPC_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_CHANNEL_TRPC_CONFIG { + /** action */ + t_u16 action; + /** 0/1/2/3 */ + t_u16 sub_band; + /** chan TRPC config */ + MrvlIETypes_ChanTRPCConfig_t tlv[]; +} MLAN_PACK_END HostCmd_DS_CHANNEL_TRPC_CONFIG; + +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; + +#define MAX_NUM_STACK_BYTES 100 +/** mef stack struct*/ +typedef MLAN_PACK_START struct _mef_stack { + /** length of byte*/ + t_u16 sp; + /** data of filter items*/ + t_u8 byte[MAX_NUM_STACK_BYTES]; +} MLAN_PACK_END mef_stack; + +/** mef entry struct */ +typedef MLAN_PACK_START struct _mef_entry_header { + /**mode:1->hostsleep;2->non hostsleep mode*/ + t_u8 mode; + /**action=0->discard and not wake host + * action=1->discard and wake host + * action=3->allow and wake host*/ + t_u8 action; +} MLAN_PACK_END mef_entry_header; + +/** mef op struct is to help to generate mef data*/ +typedef MLAN_PACK_START struct _mef_op { + /** operand_type*/ + t_u8 operand_type; + /** reserved*/ + t_u8 rsvd[3]; + /** data */ + t_u8 val[MAX_NUM_BYTE_SEQ + 1]; +} MLAN_PACK_END mef_op; + +/* HostCmd_DS_802_11_SLEEP_PERIOD */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SLEEP_PERIOD { + /** ACT_GET/ACT_SET */ + t_u16 action; + + /** Sleep Period in msec */ + t_u16 sleep_pd; +} MLAN_PACK_END HostCmd_DS_802_11_SLEEP_PERIOD; + +/* HostCmd_DS_802_11_SLEEP_PARAMS */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SLEEP_PARAMS { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** Sleep clock error in ppm */ + t_u16 error; + /** Wakeup offset in usec */ + t_u16 offset; + /** Clock stabilization time in usec */ + t_u16 stable_time; + /** Control periodic calibration */ + t_u8 cal_control; + /** Control the use of external sleep clock */ + t_u8 external_sleep_clk; + /** Reserved field, should be set to zero */ + t_u16 reserved; +} MLAN_PACK_END HostCmd_DS_802_11_SLEEP_PARAMS; + +/** Sleep response control */ +typedef enum _sleep_resp_ctrl { + RESP_NOT_NEEDED = 0, + RESP_NEEDED, +} sleep_resp_ctrl; + +/** Structure definition for the new ieee power save parameters*/ +typedef MLAN_PACK_START struct __ps_param { + /** Null packet interval */ + t_u16 null_pkt_interval; + /** Num dtims */ + t_u16 multiple_dtims; + /** becaon miss interval */ + t_u16 bcn_miss_timeout; + /** local listen interval */ + t_u16 local_listen_interval; + /** Adhoc awake period */ + t_u16 adhoc_wake_period; + /** mode - (0x01 - firmware to automatically choose PS_POLL or NULL + * mode, 0x02 - PS_POLL, 0x03 - NULL mode ) + */ + t_u16 mode; + /** Delay to PS in milliseconds */ + t_u16 delay_to_ps; +} MLAN_PACK_END ps_param; + +/** Structure definition for the new auto deep sleep command */ +typedef MLAN_PACK_START struct __auto_ds_param { + /** Deep sleep inactivity timeout */ + t_u16 deep_sleep_timeout; +} MLAN_PACK_END auto_ds_param; + +/** Structure definition for sleep confirmation in the new ps command */ +typedef MLAN_PACK_START struct __sleep_confirm_param { + /** response control 0x00 - response not needed, 0x01 - response needed + */ + t_u16 resp_ctrl; +} MLAN_PACK_END sleep_confirm_param; + +/** bitmap for get auto deepsleep */ +#define BITMAP_AUTO_DS 0x01 +/** bitmap for sta power save */ +#define BITMAP_STA_PS 0x10 +/** bitmap for beacon timeout */ +#define BITMAP_BCN_TMO 0x20 +/** bitmap for uap inactivity based PS */ +#define BITMAP_UAP_INACT_PS 0x100 +/** bitmap for uap DTIM PS */ +#define BITMAP_UAP_DTIM_PS 0x200 +/** Structure definition for the new ieee power save parameters*/ +typedef MLAN_PACK_START struct _auto_ps_param { + /** bitmap for enable power save mode */ + t_u16 ps_bitmap; + /* auto deep sleep parameter, + * sta power save parameter + * uap inactivity parameter + * uap DTIM parameter */ +} MLAN_PACK_END auto_ps_param; + +/** fix size for auto ps */ +#define AUTO_PS_FIX_SIZE 4 + +/** TLV type : auto ds param */ +#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 0x71) /* 0x0171 */ +/** TLV type : ps param */ +#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 0x72) /* 0x0172 */ +/** TLV type : beacon timeout */ +#define TLV_TYPE_BCN_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 0x11F) /* 0x011F */ + +/** MrvlIEtypes_auto_ds_param_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_auto_ds_param_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** auto ds param */ + auto_ds_param param; +} MLAN_PACK_END MrvlIEtypes_auto_ds_param_t; + +/** MrvlIEtypes_ps_param_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ps_param_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** ps param */ + ps_param param; +} MLAN_PACK_END MrvlIEtypes_ps_param_t; + +/** MrvlIEtypes_bcn_timeout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_bcn_timeout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Beacon miss timeout period window */ + t_u16 bcn_miss_tmo_window; + /** Beacon miss timeout period */ + t_u16 bcn_miss_tmo_period; + /** Beacon reacquire timeout period window */ + t_u16 bcn_rq_tmo_window; + /** Beacon reacquire timeout period */ + t_u16 bcn_rq_tmo_period; +} MLAN_PACK_END MrvlIEtypes_bcn_timeout_t; + +/** Structure definition for low power mode cfg command */ +typedef MLAN_PACK_START struct _HostCmd_DS_LOW_POWER_MODE_CFG { + /** Action */ + t_u16 action; + /** Low power mode */ + t_u16 lpm; +} MLAN_PACK_END HostCmd_DS_LOW_POWER_MODE_CFG; + +/** Structure definition for new power save command */ +typedef MLAN_PACK_START struct _HostCmd_DS_PS_MODE_ENH { + /** Action */ + t_u16 action; + /** Data speciifc to action */ + /* For IEEE power save data will be as + * UINT16 mode (0x01 - firmware to automatically choose PS_POLL or NULL + * mode, 0x02 - PS_POLL, 0x03 - NULL mode ) UINT16 NullpacketInterval + * UINT16 NumDtims + * UINT16 BeaconMissInterval + * UINT16 locallisteninterval + * UINT16 adhocawakeperiod */ + + /* For auto deep sleep */ + /* UINT16 Deep sleep inactivity timeout*/ + + /* For PS sleep confirm + * UINT16 responeCtrl - 0x00 - reponse from fw not needed, 0x01 - + * response from fw is needed */ + + union { + /** PS param definition */ + ps_param opt_ps; + /** Auto ds param definition */ + auto_ds_param auto_ds; + /** Sleep comfirm param definition */ + sleep_confirm_param sleep_cfm; + /** bitmap for get PS info and Disable PS mode */ + t_u16 ps_bitmap; + /** auto ps param */ + auto_ps_param auto_ps; + } params; +} MLAN_PACK_END HostCmd_DS_802_11_PS_MODE_ENH; + +/** FW VERSION tlv */ +#define TLV_TYPE_FW_VER_INFO (PROPRIETARY_TLV_BASE_ID + 0xC7) /* 0x1C7 */ + +/** MrvlIEtypes_fw_ver_info_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_fw_ver_info_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** API id */ + t_u16 api_id; + /** major version */ + t_u8 major_ver; + /** minor version */ + t_u8 minor_ver; +} MLAN_PACK_END MrvlIEtypes_fw_ver_info_t; + +/** API ID */ +enum API_VER_ID { + KEY_API_VER_ID = 1, + FW_API_VER_ID = 2, + UAP_FW_API_VER_ID = 3, + CHANRPT_API_VER_ID = 4, +}; + +/** FW AP V15 */ +#define HOST_API_VERSION_V15 15 +/** FW minor version 1 */ +#define FW_MINOR_VERSION_1 1 + +/** UAP FW version 2 */ +#define UAP_FW_VERSION_2 0x2 + +/** HostCMD_DS_APCMD_ACS_SCAN */ +typedef MLAN_PACK_START struct _HostCMD_DS_APCMD_ACS_SCAN { + /** band */ + Band_Config_t bandcfg; + /** channel */ + t_u8 chan; +} MLAN_PACK_END HostCMD_DS_APCMD_ACS_SCAN; + +/** HostCmd_DS_GET_HW_SPEC */ +typedef MLAN_PACK_START struct _HostCmd_DS_GET_HW_SPEC { + /** HW Interface version number */ + t_u16 hw_if_version; + /** HW version number */ + t_u16 version; + /** Reserved field */ + t_u16 reserved; + /** Max no of Multicast address */ + t_u16 num_of_mcast_adr; + /** MAC address */ + t_u8 permanent_addr[MLAN_MAC_ADDR_LENGTH]; + /** Region Code */ + t_u16 region_code; + /** Number of antenna used */ + t_u16 number_of_antenna; + /** FW release number, example 0x1234=1.2.3.4 */ + t_u32 fw_release_number; + /** Reserved field */ + t_u32 reserved_1; + /** Reserved field */ + t_u32 reserved_2; + /** Reserved field */ + t_u32 reserved_3; + /** FW/HW Capability */ + t_u32 fw_cap_info; + /** 802.11n Device Capabilities */ + t_u32 dot_11n_dev_cap; + /** MIMO abstraction of MCSs supported by device */ + t_u8 dev_mcs_support; + /** Valid end port at init */ + t_u16 mp_end_port; + /** mgmt IE buffer count */ + t_u16 mgmt_buf_count; + /** Reserved */ + t_u32 reserved_8; + /** Reserved */ + t_u32 reserved_9; + /** 802.11ac Device Capabilities */ + t_u32 Dot11acDevCap; + /** MCSs supported by 802.11ac device */ + t_u32 Dot11acMcsSupport; +} MLAN_PACK_END HostCmd_DS_GET_HW_SPEC; + +#ifdef SDIO +/* HostCmd_DS_SDIO_SP_RX_AGGR_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_SDIO_SP_RX_AGGR_CFG { + t_u8 action; + t_u8 enable; + t_u16 sdio_block_size; +} MLAN_PACK_END HostCmd_DS_SDIO_SP_RX_AGGR_CFG; +#endif + +/** HostCmd_DS_802_11_CFG_DATA */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_CFG_DATA { + /** Action */ + t_u16 action; + /** Type */ + t_u16 type; + /** Data length */ + t_u16 data_len; + /** Data */ +} MLAN_PACK_END HostCmd_DS_802_11_CFG_DATA; + +/** HostCmd_DS_CMD_802_11_RSSI_INFO_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RSSI_INFO_EXT { + /** Action */ + t_u16 action; + /** Parameter used for exponential averaging for Data */ + t_u16 ndata; + /** Parameter used for exponential averaging for Beacon */ + t_u16 nbcn; + /** Last RSSI beacon TSF(only for Get action) */ + t_u64 tsfbcn; + /** TLV info**/ + t_u8 *tlv_buf[]; +} MLAN_PACK_END HostCmd_DS_802_11_RSSI_INFO_EXT; + +/** TLV rssi info */ +#define TLV_TYPE_RSSI_INFO (PROPRIETARY_TLV_BASE_ID + 0xe5) /* 0x01E5 */ + +/** MrvlIEtypes_eapol_pkt_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RSSI_EXT_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Path ID + [Bit1:Bit0] = [0:1]: path A + [Bit1:Bit0] = [1:0]: path B + [Bit1:Bit0] = [1:1]: combined signal of path A and path B + [Bit7:Bit2] : Reserved + **/ + t_u16 path_id; + /** Last Data RSSI in dBm */ + t_s16 data_rssi_last; + /** Last Data NF in dBm */ + t_s16 data_nf_last; + /** AVG DATA RSSI in dBm */ + t_s16 data_rssi_avg; + /** AVG DATA NF in dBm */ + t_s16 data_nf_avg; + /** Last BEACON RSSI in dBm */ + t_s16 bcn_rssi_last; + /** Last BEACON NF in dBm */ + t_s16 bcn_nf_last; + /** AVG BEACON RSSI in dBm */ + t_s16 bcn_rssi_avg; + /** AVG BEACON NF in dBm */ + t_s16 bcn_nf_avg; +} MLAN_PACK_END MrvlIEtypes_RSSI_EXT_t; + +/** HostCmd_DS_CMD_802_11_RSSI_INFO */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RSSI_INFO { + /** Action */ + t_u16 action; + /** Parameter used for exponential averaging for Data */ + t_u16 ndata; + /** Parameter used for exponential averaging for Beacon */ + t_u16 nbcn; + /** Reserved field 0 */ + t_u16 reserved[9]; + /** Reserved field 1 */ + t_u64 reserved_1; +} MLAN_PACK_END HostCmd_DS_802_11_RSSI_INFO; + +/** HostCmd_DS_802_11_RSSI_INFO_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RSSI_INFO_RSP { + /** Action */ + t_u16 action; + /** Parameter used for exponential averaging for Data */ + t_u16 ndata; + /** Parameter used for exponential averaging for beacon */ + t_u16 nbcn; + /** Last Data RSSI in dBm */ + t_s16 data_rssi_last; + /** Last Data NF in dBm */ + t_s16 data_nf_last; + /** AVG DATA RSSI in dBm */ + t_s16 data_rssi_avg; + /** AVG DATA NF in dBm */ + t_s16 data_nf_avg; + /** Last BEACON RSSI in dBm */ + t_s16 bcn_rssi_last; + /** Last BEACON NF in dBm */ + t_s16 bcn_nf_last; + /** AVG BEACON RSSI in dBm */ + t_s16 bcn_rssi_avg; + /** AVG BEACON NF in dBm */ + t_s16 bcn_nf_avg; + /** Last RSSI Beacon TSF */ + t_u64 tsf_bcn; +} MLAN_PACK_END HostCmd_DS_802_11_RSSI_INFO_RSP; + +/** HostCmd_DS_802_11_MAC_ADDRESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_MAC_ADDRESS { + /** Action */ + t_u16 action; + /** MAC address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END HostCmd_DS_802_11_MAC_ADDRESS; + +/** HostCmd_DS_MAC_CONTROL */ +typedef MLAN_PACK_START struct _HostCmd_DS_MAC_CONTROL { + /** Action */ + t_u32 action; +} MLAN_PACK_END HostCmd_DS_MAC_CONTROL; + +/** HostCmd_DS_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; + +#if defined(SDIO) +/** 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; +#endif /* GPIO_SDIO_INT_CTRL */ + +typedef MLAN_PACK_START struct _HostCmd_DS_SDIO_PULL_CTRL { + /** Action */ + t_u16 action; + /** The delay of pulling up in us */ + t_u16 pull_up; + /** The delay of pulling down in us */ + t_u16 pull_down; +} MLAN_PACK_END HostCmd_DS_SDIO_PULL_CTRL; + +/** HostCmd_DS_802_11_GET_LOG */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_GET_LOG { + /** Number of multicast transmitted frames */ + t_u32 mcast_tx_frame; + /** Number of failures */ + t_u32 failed; + /** Number of retries */ + t_u32 retry; + /** Number of multiretries */ + t_u32 multiretry; + /** Number of duplicate frames */ + t_u32 frame_dup; + /** Number of RTS success */ + t_u32 rts_success; + /** Number of RTS failure */ + t_u32 rts_failure; + /** Number of acknowledgement failure */ + t_u32 ack_failure; + /** Number of fragmented packets received */ + t_u32 rx_frag; + /** Number of multicast frames received */ + t_u32 mcast_rx_frame; + /** FCS error */ + t_u32 fcs_error; + /** Number of transmitted frames */ + t_u32 tx_frame; + /** Reserved field */ + t_u32 reserved; + /** Number of WEP icv error for each key */ + t_u32 wep_icv_err_cnt[4]; + /** Beacon received count */ + t_u32 bcn_rcv_cnt; + /** Beacon missed count */ + t_u32 bcn_miss_cnt; + /** Tx frag count */ + t_u32 tx_frag_cnt; + /** Qos Tx frag count */ + t_u32 qos_tx_frag_cnt[8]; + /** Qos failed count */ + t_u32 qos_failed_cnt[8]; + /** Qos retry count */ + t_u32 qos_retry_cnt[8]; + /** Qos multi retry count */ + t_u32 qos_multi_retry_cnt[8]; + /** Qos frame dup count */ + t_u32 qos_frm_dup_cnt[8]; + /** Qos rts success count */ + t_u32 qos_rts_suc_cnt[8]; + /** Qos rts failure count */ + t_u32 qos_rts_failure_cnt[8]; + /** Qos ack failure count */ + t_u32 qos_ack_failure_cnt[8]; + /** Qos Rx frag count */ + t_u32 qos_rx_frag_cnt[8]; + /** Qos Tx frame count */ + t_u32 qos_tx_frm_cnt[8]; + /** Qos discarded frame count */ + t_u32 qos_discarded_frm_cnt[8]; + /** Qos mpdus Rx count */ + t_u32 qos_mpdus_rx_cnt[8]; + /** Qos retry rx count */ + t_u32 qos_retries_rx_cnt[8]; + /** CMAC ICV errors count */ + t_u32 cmacicv_errors; + /** CMAC replays count */ + t_u32 cmac_replays; + /** mgmt CCMP replays count */ + t_u32 mgmt_ccmp_replays; + /** TKIP ICV errors count */ + t_u32 tkipicv_errors; + /** TKIP replays count */ + t_u32 tkip_replays; + /** CCMP decrypt errors count */ + t_u32 ccmp_decrypt_errors; + /** CCMP replays count */ + t_u32 ccmp_replays; + /** Tx amsdu count */ + t_u32 tx_amsdu_cnt; + /** failed amsdu count */ + t_u32 failed_amsdu_cnt; + /** retry amsdu count */ + t_u32 retry_amsdu_cnt; + /** multi-retry amsdu count */ + t_u32 multi_retry_amsdu_cnt; + /** Tx octets in amsdu count */ + t_u64 tx_octets_in_amsdu_cnt; + /** amsdu ack failure count */ + t_u32 amsdu_ack_failure_cnt; + /** Rx amsdu count */ + t_u32 rx_amsdu_cnt; + /** Rx octets in amsdu count */ + t_u64 rx_octets_in_amsdu_cnt; + /** Tx ampdu count */ + t_u32 tx_ampdu_cnt; + /** tx mpdus in ampdu count */ + t_u32 tx_mpdus_in_ampdu_cnt; + /** tx octets in ampdu count */ + t_u64 tx_octets_in_ampdu_cnt; + /** ampdu Rx count */ + t_u32 ampdu_rx_cnt; + /** mpdu in Rx ampdu count */ + t_u32 mpdu_in_rx_ampdu_cnt; + /** Rx octets ampdu count */ + t_u64 rx_octets_in_ampdu_cnt; + /** ampdu delimiter CRC error count */ + t_u32 ampdu_delimiter_crc_error_cnt; + /** Rx Stuck Related Info*/ + /** Rx Stuck Issue count */ + t_u32 rx_stuck_issue_cnt[2]; + /** Rx Stuck Recovery count */ + t_u32 rx_stuck_recovery_cnt; + /** Rx Stuck TSF */ + t_u64 rx_stuck_tsf[2]; + /** Tx Watchdog Recovery Related Info */ + /** Tx Watchdog Recovery count */ + t_u32 tx_watchdog_recovery_cnt; + /** Tx Watchdog TSF */ + t_u64 tx_watchdog_tsf[2]; + /** Channel Switch Related Info */ + /** Channel Switch Announcement Sent */ + t_u32 channel_switch_ann_sent; + /** Channel Switch State */ + t_u32 channel_switch_state; + /** Register Class */ + t_u32 reg_class; + /** Channel Number */ + t_u32 channel_number; + /** Channel Switch Mode */ + t_u32 channel_switch_mode; +} MLAN_PACK_END HostCmd_DS_802_11_GET_LOG; + +/* maln wifi rate */ +typedef MLAN_PACK_START struct _mlan_wifi_rate { + /** 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */ + t_u8 preamble; + /** 0:1x1, 1:2x2, 3:3x3, 4:4x4 */ + t_u8 nss; + /** 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz */ + t_u8 bw; + /** OFDM/CCK rate code would be as per ieee std in the units of 0.5mbps + */ + /** HT/VHT it would be mcs index */ + t_u8 rateMcsIdx; + /** units of 100 Kbps */ + t_u32 bitrate; +} MLAN_PACK_START mlan_wifi_rate; + +/** channel information */ +typedef MLAN_PACK_START struct { + /** channel width (20, 40, 80, 80+80, 160) */ + t_u32 width; + /** primary 20 MHz channel */ + t_u32 center_freq; + /** center frequency (MHz) first segment */ + t_u32 center_freq0; + /** center frequency (MHz) second segment */ + t_u32 center_freq1; +} MLAN_PACK_END mlan_wifi_channel_info; + +/** channel statistics */ +typedef MLAN_PACK_START struct { + /** channel */ + mlan_wifi_channel_info channel; + /** msecs the radio is awake (32 bits number accruing over time) */ + t_u32 on_time; + /** msecs the CCA register is busy (32 bits number accruing over time) + */ + t_u32 cca_busy_time; +} MLAN_PACK_END mlan_wifi_channel_stat; + +/** radio statistics */ +typedef MLAN_PACK_START struct { + /** supported wifi in case of multi radio */ + t_u32 radio; + /** msecs the radio is awake */ + t_u32 on_time; + /** msecs the radio is transmitting */ + t_u32 tx_time; + /** TBD: num_tx_levels: number of radio transmit power levels */ + t_u32 reserved0; + /** TBD: tx_time_per_levels: pointer to an array of radio transmit per + * power levels in msecs accured over time */ + t_u32 reserved1; + /** msecs the radio is in active receive */ + t_u32 rx_time; + /** msecs the radio is awake due to all scan */ + t_u32 on_time_scan; + /** msecs the radio is awake due to NAN */ + t_u32 on_time_nbd; + /** msecs the radio is awake due to G?scan */ + t_u32 on_time_gscan; + /** msecs the radio is awake due to roam?scan */ + t_u32 on_time_roam_scan; + /** msecs the radio is awake due to PNO scan */ + t_u32 on_time_pno_scan; + /** msecs the radio is awake due to HS2.0 scans and GAS exchange */ + t_u32 on_time_hs20; + /** number of channels */ + t_u32 num_channels; + /** channel statistics */ + mlan_wifi_channel_stat channels[MAX_NUM_CHAN]; // support only 1 + // channel, so keep it. +} MLAN_PACK_END mlan_wifi_radio_stat; + +/** per rate statistics */ +typedef MLAN_PACK_START struct { + /** rate information */ + mlan_wifi_rate rate; + /** number of successfully transmitted data pkts (ACK rcvd) */ + t_u32 tx_mpdu; + /** number of received data pkts */ + t_u32 rx_mpdu; + /** number of data packet losses (no ACK) */ + t_u32 mpdu_lost; + /** total number of data pkt retries */ + t_u32 retries; + /** number of short data pkt retries */ + t_u32 retries_short; + /** number of long data pkt retries */ + t_u32 retries_long; +} MLAN_PACK_END mlan_wifi_rate_stat; + +/** per peer statistics */ +typedef MLAN_PACK_START struct { + /** peer type (AP, TDLS, GO etc.) */ + t_u8 type; + /** mac address */ + t_u8 peer_mac_address[6]; + /** peer WIFI_CAPABILITY_XXX */ + t_u32 capabilities; + /** number of rates */ + t_u32 num_rate; + /** per rate statistics, number of entries = num_rate */ + mlan_wifi_rate_stat rate_stats[]; +} MLAN_PACK_END mlan_wifi_peer_info; + +/* per access category statistics */ +typedef MLAN_PACK_START struct { + /** access category (VI, VO, BE, BK) */ + t_u32 ac; + /** number of successfully transmitted unicast data pkts (ACK rcvd) */ + t_u32 tx_mpdu; + /** number of received unicast mpdus */ + t_u32 rx_mpdu; + /** number of succesfully transmitted multicast data packets */ + /** STA case: implies ACK received from AP for the unicast packet in + * which mcast pkt was sent */ + t_u32 tx_mcast; + /** number of received multicast data packets */ + t_u32 rx_mcast; + /** number of received unicast a-mpdus */ + t_u32 rx_ampdu; + /** number of transmitted unicast a-mpdus */ + t_u32 tx_ampdu; + /** number of data pkt losses (no ACK) */ + t_u32 mpdu_lost; + /** total number of data pkt retries */ + t_u32 retries; + /** number of short data pkt retries */ + t_u32 retries_short; + /** number of long data pkt retries */ + t_u32 retries_long; + /** data pkt min contention time (usecs) */ + t_u32 contention_time_min; + /** data pkt max contention time (usecs) */ + t_u32 contention_time_max; + /** data pkt avg contention time (usecs) */ + t_u32 contention_time_avg; + /** num of data pkts used for contention statistics */ + t_u32 contention_num_samples; +} MLAN_PACK_END mlan_wifi_wmm_ac_stat; + +/** interface statistics */ +typedef MLAN_PACK_START struct { + /** access point beacon received count from connected AP */ + t_u32 beacon_rx; + /** Average beacon offset encountered (beacon_TSF - TBTT) + * the average_tsf_offset field is used so as to calculate the + * typical beacon contention time on the channel as well may be + * used to debug beacon synchronization and related power consumption + * issue + */ + t_u64 average_tsf_offset; + /** indicate that this AP typically leaks packets beyond the driver + * guard time */ + t_u32 leaky_ap_detected; + /** average number of frame leaked by AP after frame with PM bit set was + * ACK'ed by AP */ + t_u32 leaky_ap_avg_num_frames_leaked; + /** Guard time currently in force (when implementing IEEE power + * management based on frame control PM bit), How long driver waits + * before shutting down the radio and after receiving an ACK for a data + * frame with PM bit set) + */ + t_u32 leaky_ap_guard_time; + /** access point mgmt frames received count from connected AP (including + * Beacon) */ + t_u32 mgmt_rx; + /** action frames received count */ + t_u32 mgmt_action_rx; + /** action frames transmit count */ + t_u32 mgmt_action_tx; + /** access Point Beacon and Management frames RSSI (averaged) */ + t_u32 rssi_mgmt; + /** access Point Data Frames RSSI (averaged) from connected AP */ + t_u32 rssi_data; + /** access Point ACK RSSI (averaged) from connected AP */ + t_u32 rssi_ack; + /** per ac data packet statistics */ + mlan_wifi_wmm_ac_stat ac[MAX_AC_QUEUES]; + /** number of peers */ + t_u32 num_peers; + /** per peer statistics */ + mlan_wifi_peer_info peer_info[]; +} MLAN_PACK_END mlan_wifi_iface_stat; + +/** MrvlIETypes_llStatIface_t */ +typedef MLAN_PACK_START struct _MrvlIETypes_llStatIface_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** Frame Control */ + mlan_wifi_iface_stat ifaceStat; + /* t_u8 frame_contents[]; */ +} MLAN_PACK_END MrvlIETypes_llStatIface_t; + +/** MrvlIETypes_llStatRadio_t */ +typedef MLAN_PACK_START struct _MrvlIETypes_llStatRadio_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** Frame Control */ + mlan_wifi_radio_stat radioStat[MAX_RADIO]; + /* t_u8 frame_contents[]; */ +} MLAN_PACK_END MrvlIETypes_llStatRadio_t; + +#define TYPE_IFACE_STAT MBIT(0) +#define TYPE_RADIO_STAT MBIT(1) +#define TYPE_PEER_INFO MBIT(2) +/** HostCmd_DS_802_11_LINK_STATISTIC */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_LINK_STATISTIC { + /** Action : HostCmd_ACT_GEN_GET/SET/REMOVE */ + t_u16 action; + /** statistic which would be get in action HostCmd_ACT_GEN_GET : + * TYPE_IFACE_STAT/RADIO_STAT/PEER_INFO */ + t_u16 stat_type; + /* threshold to classify the pkts as short or long, packet size < + * mpdu_size_threshold => short */ + t_u32 mpdu_size_threshold; + /* set for field debug mode. Driver should collect all statistics + * regardless of performance impact. */ + t_u32 aggressive_statistics_gathering; + /** Value */ + t_u8 value[]; +} MLAN_PACK_END HostCmd_DS_802_11_LINK_STATISTIC; + +/**_HostCmd_TX_RATE_QUERY */ +typedef MLAN_PACK_START struct _HostCmd_TX_RATE_QUERY { + /** Tx rate */ + t_u8 tx_rate; + /** Tx Rate Info: + * [Bit 0-1] tx rate formate: LG = 0, HT = 1, VHT = 2 + * [Bit 2-3] HT/VHT Bandwidth: BW20 = 0, BW40 = 1, BW80 = 2, BW160 = 3 + * [Bit 4] HT/VHT Guard Interval: LGI = 0, SGI = 1 + * [Bit4,Bit7] AX Guard Interval: 00, 01, 02 */ + t_u8 tx_rate_info; + /** + * BIT0: DCM + * BIT3-BIT1: tone mode + ** 000: 26 tone + ** 001: 52 tone + ** 010: 106 tone + ** 011: 242 tone + ** 100: 484 tone + ** 101: 996 tone + * BIT7-BIT4: resvd + **/ + t_u8 ext_tx_rate_info; +} MLAN_PACK_END HostCmd_TX_RATE_QUERY; + +typedef MLAN_PACK_START struct _hs_config_param { + /** bit0=1: broadcast data + * bit1=1: unicast data + * bit2=1: mac events + * bit3=1: multicast data + */ + t_u32 conditions; + /** GPIO pin or 0xff for interface */ + t_u8 gpio; + /** gap in milliseconds or or 0xff for special setting when + * GPIO is used to wakeup host + */ + t_u8 gap; +} MLAN_PACK_END hs_config_param; + +/** HS Action 0x0001 - Configure enhanced host sleep mode, + * 0x0002 - Activate enhanced host sleep mode + */ +typedef enum _Host_Sleep_Action { + HS_CONFIGURE = 0x0001, + HS_ACTIVATE = 0x0002, +} Host_Sleep_Action; + +/** Structure definition for activating enhanced hs */ +typedef MLAN_PACK_START struct __hs_activate_param { + /** response control 0x00 - response not needed, 0x01 - response needed + */ + t_u16 resp_ctrl; +} MLAN_PACK_END hs_activate_param; + +/** HostCmd_DS_802_11_HS_CFG_ENH */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_HS_CFG_ENH { + /** Action 0x0001 - Configure enhanced host sleep mode, + * 0x0002 - Activate enhanced host sleep mode + */ + t_u16 action; + + union { + /** Configure enhanced hs */ + hs_config_param hs_config; + /** Activate enhanced hs */ + hs_activate_param hs_activate; + } params; +} MLAN_PACK_END HostCmd_DS_802_11_HS_CFG_ENH; + +/** HostCmd_CMD_802_11_ROBUSTCOEX */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_ROBUSTCOEX { + /** Action */ + t_u16 action; + /** RSVD */ + t_u16 rsvd; + t_u8 tlv_buf[]; +} MLAN_PACK_END HostCmd_DS_802_11_ROBUSTCOEX; + +/** HostCmd_CMD_DMCS_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_DMCS_CFG { + /** Action */ + t_u16 action; + /** SubCmd of DMCS */ + t_u16 subcmd; + t_u8 tlv_buf[]; +} MLAN_PACK_END HostCmd_DS_DMCS_CFG; + +#if defined(PCIE) +/** HostCmd_CMD_SSU */ +typedef MLAN_PACK_START struct _HostCmd_DS_SSU_CFG { + /** Action */ + t_u16 action; + /** # of FFT sample to skip */ + t_u32 nskip; + /** # of FFT sample selected to dump */ + t_u32 nsel; + /** Down sample ADC input for buffering */ + t_u32 adcdownsample; + /** Mask Out ADC Data From Spectral Packet */ + t_u32 mask_adc_pkt; + /** Enable 16-Bit FFT Output Data Precision in Spectral Packet */ + t_u32 out_16bits; + /** Enable power spectrum in dB for spectral packet */ + t_u32 spec_pwr_enable; + /** Enable Spectral Packet Rate Reduction in dB output format */ + t_u32 rate_deduction; + /** # of Spectral packets over which spectral data to be averaged */ + t_u32 n_pkt_avg; + /** ret: Calculated fft length in dw */ + t_u32 fft_len; + /** ret: Calculated adc length in dw */ + t_u32 adc_len; + /** ret: Calculated record length in dw */ + t_u32 rec_len; + /** Mapped address of DMA buffer */ + t_u32 buffer_base_addr[2]; + /** Total size of allocated buffer for SSU DMA */ + t_u32 buffer_pool_size; + /** ret: Calculated buffer numbers */ + t_u32 number_of_buffers; + /** ret: Calculated buffer size in byte for each descriptor */ + t_u32 buffer_size; +} MLAN_PACK_END HostCmd_DS_SSU_CFG; +#endif + +/** SNMP_MIB_INDEX */ +typedef enum _SNMP_MIB_INDEX { + OpRateSet_i = 1, + DtimPeriod_i = 3, + RtsThresh_i = 5, + ShortRetryLim_i = 6, + LongRetryLim_i = 7, + FragThresh_i = 8, + Dot11D_i = 9, + Dot11H_i = 10, + WwsMode_i = 17, + Thermal_i = 34, + NullPktPeriod_i = 37, + SignalextEnable_i = 41, + ECSAEnable_i = 42, + StopDeauth_i = 44, +} SNMP_MIB_INDEX; + +/** max SNMP buf size */ +#define MAX_SNMP_BUF_SIZE 128 + +#ifdef UAP_SUPPORT +/** HostCmd_CMD_802_11_SNMP_MIB */ +typedef MLAN_PACK_START struct _HostCmd_DS_UAP_802_11_SNMP_MIB { + /** SNMP query type */ + t_u16 query_type; + /** snmp oid buf */ + t_u8 snmp_data[]; +} MLAN_PACK_END HostCmd_DS_UAP_802_11_SNMP_MIB; +#endif + +/** 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]; + t_u16 vht_mcs_rate_bitmap[8]; + t_u16 he_mcs_rate_bitmap[8]; +} MLAN_PACK_END MrvlRateScope_t; + +/** MrvlRateDropPattern_t */ +typedef MLAN_PACK_START struct _MrvlRateDropPattern_t { + /** Header Type */ + t_u16 type; + /** Header Length */ + t_u16 length; + /** Rate Drop Mode */ + t_u32 rate_drop_mode; + /* MrvlRateDropControl_t RateDropControl[]; */ +} MLAN_PACK_END MrvlRateDropPattern_t; + +typedef MLAN_PACK_START struct _MrvlIETypes_rate_setting_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Rate Setting */ + t_u16 rate_setting; +} MLAN_PACK_END MrvlIETypes_rate_setting_t; + +/** HostCmd_DS_TX_RATE_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_TX_RATE_CFG { + /** Action */ + t_u16 action; + t_u16 reserved_1; + /* MrvlRateScope_t RateScope; + * MrvlRateDropPattern_t RateDrop; */ + t_u8 tlv_buf[]; +} 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[]*/ + t_u8 tlv_buf[]; +} MLAN_PACK_END HostCmd_DS_TXPWR_CFG; + +/** HostCmd_CMD_802_11_RF_TX_POWER */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RF_TX_POWER { + /** Action */ + t_u16 action; + /** Current power level */ + t_s16 current_level; + /** Maximum power */ + t_s8 max_power; + /** Minimum power */ + t_s8 min_power; +} MLAN_PACK_END HostCmd_DS_802_11_RF_TX_POWER; + +/** Connection type infra */ +#define CONNECTION_TYPE_INFRA 0 +/** Connection type adhoc */ +#define CONNECTION_TYPE_ADHOC 1 +#ifdef WIFI_DIRECT_SUPPORT +/** BSS Mode: WIFIDIRECT Client */ +#define BSS_MODE_WIFIDIRECT_CLIENT 0 +/** BSS Mode: WIFIDIRECT GO */ +#define BSS_MODE_WIFIDIRECT_GO 2 +#endif +/** HostCmd_DS_SET_BSS_MODE */ +typedef MLAN_PACK_START struct _HostCmd_DS_SET_BSS_MODE { + /** connection type */ + t_u8 con_type; +} MLAN_PACK_END HostCmd_DS_SET_BSS_MODE; + +/** 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; +/** VHT Capabilities element */ +typedef MLAN_PACK_START struct _MrvlIETypes_VHTCap_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** VHTCap struct */ + VHT_capa_t vht_cap; +} MLAN_PACK_END MrvlIETypes_VHTCap_t; + +/** HostCmd_DS_REMAIN_ON_CHANNEL */ +typedef MLAN_PACK_START struct _HostCmd_DS_REMAIN_ON_CHANNEL { + /** Action 0-GET, 1-SET, 4 CLEAR*/ + t_u16 action; + /** Not used set to zero */ + t_u8 status; + /** Not used set to zero */ + t_u8 reserved; + /** Band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** remain time: Unit ms*/ + t_u32 remain_period; +} MLAN_PACK_END HostCmd_DS_REMAIN_ON_CHANNEL; + +#ifdef WIFI_DIRECT_SUPPORT +/** HostCmd_DS_WIFI_DIRECT_MODE */ +typedef MLAN_PACK_START struct _HostCmd_DS_WIFI_DIRECT_MODE { + /** Action 0-GET, 1-SET*/ + t_u16 action; + /**0:disable 1:listen 2:GO 3:p2p client 4:find 5:stop find*/ + t_u16 mode; +} MLAN_PACK_END HostCmd_DS_WIFI_DIRECT_MODE; + +/** MrvlIEtypes_NoA_setting_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_NoA_setting_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** enable/disable */ + t_u8 enable; + /** index */ + t_u16 index; + /** NoA count */ + t_u8 noa_count; + /** NoA duration */ + t_u32 noa_duration; + /** NoA interval */ + t_u32 noa_interval; +} MLAN_PACK_END MrvlIEtypes_NoA_setting_t; + +/** MrvlIEtypes_NoA_setting_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_OPP_PS_setting_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** enable/disable && ct_window */ + t_u8 enable; +} MLAN_PACK_END MrvlIEtypes_OPP_PS_setting_t; + +/** HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG { + /** Action 0-GET, 1-SET */ + t_u16 action; + /** MrvlIEtypes_NoA_setting_t + * MrvlIEtypes_OPP_PS_setting_t + */ + t_u8 tlv_buf[]; +} MLAN_PACK_END HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG; +#endif + +MLAN_PACK_START struct coalesce_filt_field_param { + t_u8 operation; + t_u8 operand_len; + t_u16 offset; + t_u8 operand_byte_stream[4]; +} MLAN_PACK_END; + +MLAN_PACK_START struct coalesce_receive_filt_rule { + MrvlIEtypesHeader_t header; + t_u8 num_of_fields; + t_u8 pkt_type; + t_u16 max_coalescing_delay; + struct coalesce_filt_field_param params[]; +} MLAN_PACK_END; + +/** HostCmd_DS_COALESCE_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_COALESCE_CONFIG { + /** Action 0-GET, 1-SET */ + t_u16 action; + t_u16 num_of_rules; + struct coalesce_receive_filt_rule rule[]; +} MLAN_PACK_END HostCmd_DS_COALESCE_CONFIG; + +/** TLV type : FW support max connection TLV */ +#define TLV_TYPE_MAX_CONN (PROPRIETARY_TLV_BASE_ID + 0x117) /* 0x0217 */ +/** MrvlIEtypes_Max_Conn_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Max_Conn_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** FW support max P2P connection */ + t_u8 max_p2p_conn; + /** FW support max STA connection */ + t_u8 max_sta_conn; +} MLAN_PACK_END MrvlIEtypes_Max_Conn_t; + +/** exceed max p2p connection event */ +typedef MLAN_PACK_START struct _event_exceed_max_p2p_conn { + /** Event ID */ + t_u16 event_id; + /** BSS index number for multiple BSS support */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** When exceed max, the mac address who request p2p connect */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END event_exceed_max_p2p_conn; + +#ifdef STA_SUPPORT + +/** + * @brief Structure used internally in the wlan driver to configure a scan. + * + * Sent to the command process module to configure the firmware + * scan command prepared by wlan_cmd_802_11_scan. + * + * @sa wlan_scan_networks + * + */ +typedef MLAN_PACK_START struct _wlan_scan_cmd_config { + /** + * BSS Type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. Valid settings are: + * + * - MLAN_SCAN_MODE_BSS (infrastructure) + * - MLAN_SCAN_MODE_IBSS (adhoc) + * - MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_mode; + + /** + * Specific BSSID used to filter scan results in the firmware + */ + t_u8 specific_bssid[MLAN_MAC_ADDR_LENGTH]; + + /** + * Length of TLVs sent in command starting at tlvBuffer + */ + t_u32 tlv_buf_len; + + /** + * SSID TLV(s) and ChanList TLVs to be sent in the firmware command + * + * TLV_TYPE_CHANLIST, MrvlIEtypes_ChanListParamSet_t + * TLV_TYPE_SSID, MrvlIEtypes_SsIdParamSet_t + */ + t_u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored here */ +} MLAN_PACK_END wlan_scan_cmd_config; + +/** + * Sructure to retrieve the scan table + */ +typedef MLAN_PACK_START struct { + /** + * - Zero based scan entry to start retrieval in command request + * - Number of scans entries returned in command response + */ + t_u32 scan_number; + /** + * Buffer marker for multiple wlan_ioctl_get_scan_table_entry + * structures. Each struct is padded to the nearest 32 bit boundary. + */ + t_u8 scan_table_entry_buf[1]; +} MLAN_PACK_END wlan_get_scan_table_info; + +/** Generic structure defined for parsing WPA/RSN IEs for GTK/PTK OUIs */ +typedef MLAN_PACK_START struct { + /** Group key oui */ + t_u8 GrpKeyOui[4]; + /** Number of PTKs */ + t_u8 PtkCnt[2]; + /** Ptk body starts here */ + t_u8 PtkBody[4]; +} MLAN_PACK_END IEBody; +#endif /* STA_SUPPORT */ + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for HostCmd_CMD_802_11_SCAN + */ +/** HostCmd_DS_802_11_SCAN */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SCAN { + /** BSS mode */ + t_u8 bss_mode; + /** BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** TLV buffer */ + t_u8 tlv_buffer[1]; + /** MrvlIEtypes_SsIdParamSet_t SsIdParamSet; + * MrvlIEtypes_ChanListParamSet_t ChanListParamSet; + * MrvlIEtypes_RatesParamSet_t OpRateSet; + */ +} MLAN_PACK_END HostCmd_DS_802_11_SCAN; + +/** fw_cap_info bit to indicate enhance ext scan type */ +#define ENHANCE_EXT_SCAN_ENABLE MBIT(19) +/** mlan_event_scan_result data structure */ +typedef MLAN_PACK_START struct _mlan_event_scan_result { + /** Event ID */ + t_u16 event_id; + /** BSS index number for multiple BSS support */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** More event available or not */ + t_u8 more_event; + /** Reserved */ + t_u8 reserved[3]; + /** Size of the response buffer */ + t_u16 buf_size; + /** Number of BSS in scan response */ + t_u8 num_of_set; +} MLAN_PACK_END mlan_event_scan_result, *pmlan_event_scan_result; + +/** ext scan status report event */ +typedef MLAN_PACK_START struct _mlan_event_scan_status { + /** Event ID */ + t_u16 event_id; + /** BSS index number for multiple BSS support */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** scan status */ + t_u8 scan_status; + /** result */ + t_u16 buf_len; + /** event buf */ + t_u8 event_buf[]; +} MLAN_PACK_END mlan_event_scan_status, *pmlan_event_scan_status; + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for HostCmd_CMD_802_11_SCAN_EXT + */ +/** HostCmd_DS_802_11_SCAN_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SCAN_EXT { + /** Scan type for ext scan + * 0: default type: cmd resp after ext scan report event + * 1: enhanced type: cmd resp before ext scan report event + * 2: scan cancelled: cancel scan during scan processing + */ + t_u8 ext_scan_type; + /** Reserved */ + t_u8 reserved[3]; + /** TLV buffer */ + t_u8 tlv_buffer[1]; + /** MrvlIEtypes_Bssid_List_t BssIdList; + * MrvlIEtypes_SsIdParamSet_t SSIDParamSet; + * MrvlIEtypes_ChanListParamSet_t ChanListParamSet; + * MrvlIEtypes_RatesParamSet_t OpRateSet; + * MrvlIEtypes_NumProbes_t NumProbes; + * MrvlIEtypes_WildCardSsIdParamSet_t WildCardSSIDParamSet; + * MrvlIEtypes_BssMode_t BssMode; + */ +} MLAN_PACK_END HostCmd_DS_802_11_SCAN_EXT; + +/** MrvlIEtypes_BssMode */ +typedef MLAN_PACK_START struct _MrvlIEtypes_BssMode_t { + /** Header */ + MrvlIEtypesHeader_t header; + /* INFRA/IBSS/AUTO */ + t_u8 bss_mode; +} MLAN_PACK_END MrvlIEtypes_BssMode_t; + +/** BSS scan Rsp */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Bss_Scan_Rsp_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** BSSID of the BSS descriptor */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Beacon/Probe response buffer */ + t_u8 frame_body[1]; +} MLAN_PACK_END MrvlIEtypes_Bss_Scan_Rsp_t; + +typedef MLAN_PACK_START struct _MrvlIEtypes_Bss_Scan_Info_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** RSSI for scan entry */ + t_s16 rssi; + /** Channel ANPI */ + t_s16 anpi; + /** Channel load (parts per 255) */ + t_u8 cca_busy_fraction; + /** Band */ + Band_Config_t bandcfg; + /** Channel */ + t_u8 channel; + /** Reserved */ + t_u8 reserved; + /** TSF data */ + t_u64 tsf; +} MLAN_PACK_END MrvlIEtypes_Bss_Scan_Info_t; + +/** HostCmd_DS_RX_MGMT_IND */ +typedef MLAN_PACK_START struct _HostCmd_DS_RX_MGMT_IND { + /** Action */ + t_u16 action; + /** Mgmt frame subtype mask */ + t_u32 mgmt_subtype_mask; +} MLAN_PACK_END HostCmd_DS_RX_MGMT_IND; + +/** HostCmd_DS_802_11_SCAN_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_SCAN_RSP { + /** Size of BSS descriptor */ + t_u16 bss_descript_size; + /** Numner of sets */ + t_u8 number_of_sets; + /** BSS descriptor and TLV buffer */ + t_u8 bss_desc_and_tlv_buffer[1]; +} MLAN_PACK_END HostCmd_DS_802_11_SCAN_RSP; + +/** HostCmd_DS_802_11_BG_SCAN_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_BG_SCAN_CONFIG { + /** action */ + t_u16 action; + /** 0: disable, 1: enable */ + t_u8 enable; + /** bss type */ + t_u8 bss_type; + /** num of channel per scan */ + t_u8 chan_per_scan; + /** reserved field */ + t_u8 reserved; + /** reserved field */ + t_u16 reserved1; + /** interval between consecutive scans */ + t_u32 scan_interval; + /** reserved field */ + t_u32 reserved2; + /** condition to trigger report to host */ + t_u32 report_condition; + /** reserved field */ + t_u16 reserved3; +} MLAN_PACK_END HostCmd_DS_802_11_BG_SCAN_CONFIG; + +/** HostCmd_DS_802_11_BG_SCAN_QUERY */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_BG_SCAN_QUERY { + /** Flush */ + t_u8 flush; +} MLAN_PACK_END HostCmd_DS_802_11_BG_SCAN_QUERY; + +/** HostCmd_DS_802_11_BG_SCAN_QUERY_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_BG_SCAN_QUERY_RSP { + /** Report condition */ + t_u32 report_condition; + /** Scan response */ + HostCmd_DS_802_11_SCAN_RSP scan_resp; +} MLAN_PACK_END HostCmd_DS_802_11_BG_SCAN_QUERY_RSP; + +/** MrvlIEtypes_StartLater_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_StartLater_t { + /** Header */ + MrvlIEtypesHeader_t header; + /* 0 - BGScan start immediately, 1 - BGScan will start later after "Scan + * Interval" */ + t_u16 value; +} MLAN_PACK_END MrvlIEtypes_StartLater_t; + +/** MrvlIEtypes_RepeatCount_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RepeatCount_t { + /** Header */ + MrvlIEtypesHeader_t header; + /* Repeat count */ + t_u16 repeat_count; +} MLAN_PACK_END MrvlIEtypes_RepeatCount_t; + +/** MrvlIEtypes_DomainParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_DomainParamSet { + /** Header */ + MrvlIEtypesHeader_t header; + /** Country code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** Set of subbands */ + IEEEtypes_SubbandSet_t sub_band[1]; +} MLAN_PACK_END MrvlIEtypes_DomainParamSet_t; + +/** HostCmd_DS_802_11D_DOMAIN_INFO */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11D_DOMAIN_INFO { + /** Action */ + t_u16 action; + /** Domain parameter set */ + MrvlIEtypes_DomainParamSet_t domain; +} MLAN_PACK_END HostCmd_DS_802_11D_DOMAIN_INFO; + +/** HostCmd_DS_802_11D_DOMAIN_INFO_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11D_DOMAIN_INFO_RSP { + /** Action */ + t_u16 action; + /** Domain parameter set */ + MrvlIEtypes_DomainParamSet_t domain; +} MLAN_PACK_END HostCmd_DS_802_11D_DOMAIN_INFO_RSP; + +/** HostCmd_DS_11N_ADDBA_REQ */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_ADDBA_REQ { + /** Result of the ADDBA Request Operation */ + t_u8 add_req_result; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Dialog Token */ + t_u8 dialog_token; + /** Block Ack Parameter Set */ + t_u16 block_ack_param_set; + /** Block Act Timeout Value */ + t_u16 block_ack_tmo; + /** Starting Sequence Number */ + t_u16 ssn; +} MLAN_PACK_END HostCmd_DS_11N_ADDBA_REQ; + +/** HostCmd_DS_11N_ADDBA_RSP */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_ADDBA_RSP { + /** Result of the ADDBA Response Operation */ + t_u8 add_rsp_result; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Dialog Token */ + t_u8 dialog_token; + /** Status Code */ + t_u16 status_code; + /** Block Ack Parameter Set */ + t_u16 block_ack_param_set; + /** Block Act Timeout Value */ + t_u16 block_ack_tmo; + /** Starting Sequence Number */ + t_u16 ssn; +} MLAN_PACK_END HostCmd_DS_11N_ADDBA_RSP; + +/** HostCmd_DS_11N_DELBA */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_DELBA { + /** Result of the ADDBA Request Operation */ + t_u8 del_result; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Delete Block Ack Parameter Set */ + t_u16 del_ba_param_set; + /** Reason Code sent for DELBA */ + t_u16 reason_code; + /** Reserved */ + t_u8 reserved; +} MLAN_PACK_END HostCmd_DS_11N_DELBA; + +/** HostCmd_DS_11N_BATIMEOUT */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_BATIMEOUT { + /** TID */ + t_u8 tid; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Delete Block Ack Parameter Set */ + t_u8 origninator; +} MLAN_PACK_END HostCmd_DS_11N_BATIMEOUT; + +/** HostCmd_DS_11N_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_11N_CFG { + /** Action */ + t_u16 action; + /** HTTxCap */ + t_u16 ht_tx_cap; + /** HTTxInfo */ + t_u16 ht_tx_info; + /** Misc configuration */ + t_u16 misc_config; +} MLAN_PACK_END HostCmd_DS_11N_CFG; + +/** HostCmd_DS_11N_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_REJECT_ADDBA_REQ { + /** Action */ + t_u16 action; + /** Bit0 : host sleep activated + * Bit1 : auto reconnect enabled + * Others : reserved + */ + t_u32 conditions; +} MLAN_PACK_END HostCmd_DS_REJECT_ADDBA_REQ; + +/** HostCmd_DS_TXBUF_CFG*/ +typedef MLAN_PACK_START struct _HostCmd_DS_TXBUF_CFG { + /** Action */ + t_u16 action; + /** Buffer Size */ + t_u16 buff_size; + /** End Port_for Multiport */ + t_u16 mp_end_port; + /** Reserved */ + t_u16 reserved3; +} MLAN_PACK_END HostCmd_DS_TXBUF_CFG; + +/** HostCmd_DS_AMSDU_AGGR_CTRL */ +typedef MLAN_PACK_START struct _HostCmd_DS_AMSDU_AGGR_CTRL { + /** Action */ + t_u16 action; + /** Enable */ + t_u16 enable; + /** Get the current Buffer Size valid */ + t_u16 curr_buf_size; +} MLAN_PACK_END HostCmd_DS_AMSDU_AGGR_CTRL; + +/** HostCmd_DS_11AC_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_11AC_CFG { + /** Action */ + t_u16 action; + /** BandConfig */ + t_u8 band_config; + /** Misc Configuration */ + t_u8 misc_config; + /** VHT Capability Info */ + t_u32 vht_cap_info; + /** VHT Support MCS Set */ + t_u8 vht_supp_mcs_set[VHT_MCS_SET_LEN]; +} MLAN_PACK_END HostCmd_DS_11AC_CFG; + +/** HostCmd_DS_11ACTXBUF_CFG*/ +typedef MLAN_PACK_START struct _HostCmd_DS_11ACTXBUF_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_11ACTXBUF_CFG; + +/** HostCmd_DS_11AX_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_11AX_CFG { + /** Action */ + t_u16 action; + /** BandConfig */ + t_u8 band_config; + /** TLV for HE capability or HE operation */ + t_u8 val[]; +} MLAN_PACK_END HostCmd_DS_11AX_CFG; + +/** HostCmd_DS_11AX_CMD_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_11AX_CMD_CFG { + /** Action */ + t_u16 action; + /** CMD_SUBID */ + t_u16 sub_id; + /** TLV or value for cmd */ + t_u8 val[]; +} MLAN_PACK_END HostCmd_DS_11AX_CMD_CFG; + +/** HostCmd_DS_RANGE_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_RANGE_EXT { + /** Action */ + t_u16 action; + /** Range ext mode */ + t_u8 mode; +} MLAN_PACK_END HostCmd_DS_RANGE_EXT; + +/** Type definition of hostcmd_twt_setup */ +typedef struct MLAN_PACK_START _hostcmd_twt_setup { + /** Implicit, 0: TWT session is explicit, 1: Session is implicit */ + t_u8 implicit; + /** Announced, 0: Unannounced, 1: Announced TWT */ + t_u8 announced; + /** Trigger Enabled, 0: Non-Trigger enabled, 1: Trigger enabled TWT */ + t_u8 trigger_enabled; + /** TWT Information Disabled, 0: TWT info enabled, 1: TWT info disabled + */ + t_u8 twt_info_disabled; + /** Negotiation Type, 0: Future Individual TWT SP start time, 1: Next + * Wake TBTT time */ + t_u8 negotiation_type; + /** TWT Wakeup Duration, time after which the TWT requesting STA can + * transition to doze state */ + t_u8 twt_wakeup_duration; + /** Flow Identifier. Range: [0-7]*/ + t_u8 flow_identifier; + /** Hard Constraint, 0: FW can tweak the TWT setup parameters if it is + *rejected by AP. + ** 1: Firmware should not tweak any parameters. */ + t_u8 hard_constraint; + /** TWT Exponent, Range: [0-63] */ + t_u8 twt_exponent; + /** TWT Mantissa Range: [0-sizeof(UINT16)] */ + t_u16 twt_mantissa; + /** TWT Request Type, 0: REQUEST_TWT, 1: SUGGEST_TWT*/ + t_u8 twt_request; + /** TWT Setup State. Set to 0 by driver, filled by FW in response*/ + t_u8 twt_setup_state; + /** Reserved, set to 0. */ + t_u8 reserved[2]; +} MLAN_PACK_END hostcmd_twt_setup, *phostcmd_twt_setup; + +/** Type definition of hostcmd_twt_teardown */ +typedef struct MLAN_PACK_START _hostcmd_twt_teardown { + /** TWT Flow Identifier. Range: [0-7] */ + t_u8 flow_identifier; + /** Negotiation Type. 0: Future Individual TWT SP start time, 1: Next + * Wake TBTT time */ + t_u8 negotiation_type; + /** Tear down all TWT. 1: To teardown all TWT, 0 otherwise */ + t_u8 teardown_all_twt; + /** TWT Teardown State. Set to 0 by driver, filled by FW in response */ + t_u8 twt_teardown_state; + /** Reserved, set to 0. */ + t_u8 reserved[3]; +} MLAN_PACK_END hostcmd_twt_teardown, *phostcmd_twt_teardown; + +/** HostCmd_DS_TWT_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_TWT_CFG { + /** Action */ + t_u16 action; + /** CMD_SUBID */ + t_u16 sub_id; + /** TWT Setup/Teardown configuration parameters */ + union { + /** TWT Setup config for Sub ID: MLAN_11AX_TWT_SETUP_SUBID */ + hostcmd_twt_setup twt_setup; + /** TWT Teardown config for Sub ID: MLAN_11AX_TWT_TEARDOWN_SUBID + */ + hostcmd_twt_teardown twt_teardown; + } param; +} MLAN_PACK_END HostCmd_DS_TWT_CFG; + +/** HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG { + /** Action */ + t_u16 action; + /** Current system clock */ + t_u16 cur_sys_clk; + /** Clock type */ + t_u16 sys_clk_type; + /** Length of clocks */ + t_u16 sys_clk_len; + /** System clocks */ + t_u16 sys_clk[16]; +} MLAN_PACK_END HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG; + +/** MrvlIEtypes_WmmParamSet_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_WmmParamSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** WMM IE */ + t_u8 wmm_ie[1]; +} MLAN_PACK_END MrvlIEtypes_WmmParamSet_t; + +/** MrvlIEtypes_WmmQueueStatus_t */ +typedef MLAN_PACK_START struct { + /** Header */ + MrvlIEtypesHeader_t header; + /** Queue index */ + t_u8 queue_index; + /** Disabled flag */ + t_u8 disabled; + /** Medium time allocation in 32us units*/ + t_u16 medium_time; + /** Flow required flag */ + t_u8 flow_required; + /** Flow created flag */ + t_u8 flow_created; + /** Reserved */ + t_u32 reserved; +} MLAN_PACK_END MrvlIEtypes_WmmQueueStatus_t; + +/** Size of a TSPEC. Used to allocate necessary buffer space in commands */ +#define WMM_TSPEC_SIZE 63 + +/** Extra IE bytes allocated in messages for appended IEs after a TSPEC */ +#define WMM_ADDTS_EXTRA_IE_BYTES 256 + +/** Extra TLV bytes allocated in messages for configuring WMM Queues */ +#define WMM_QUEUE_CONFIG_EXTRA_TLV_BYTES 64 + +/** Number of bins in the histogram for the HostCmd_DS_WMM_QUEUE_STATS */ +#define WMM_STATS_PKTS_HIST_BINS 7 + +/** + * @brief Firmware command structure to retrieve the firmware WMM status. + * + * Used to retrieve the status of each WMM AC Queue in TLV + * format (MrvlIEtypes_WmmQueueStatus_t) as well as the current WMM + * parameter IE advertised by the AP. + * + * Used in response to a EVENT_WMM_STATUS_CHANGE event signaling + * a QOS change on one of the ACs or a change in the WMM Parameter in + * the Beacon. + * + * TLV based command, byte arrays used for max sizing purpose. There are no + * arguments sent in the command, the TLVs are returned by the firmware. + */ +typedef MLAN_PACK_START struct { + /** Queue status TLV */ + t_u8 queue_status_tlv[sizeof(MrvlIEtypes_WmmQueueStatus_t) * + MAX_AC_QUEUES]; + /** WMM parameter TLV */ + t_u8 wmm_param_tlv[sizeof(IEEEtypes_WmmParameter_t) + 2]; +} MLAN_PACK_END HostCmd_DS_WMM_GET_STATUS; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_ADDTS_REQ firmware command + */ +typedef MLAN_PACK_START struct { + mlan_cmd_result_e command_result; /**< Command result */ + t_u32 timeout_ms; /**< Timeout value in milliseconds */ + t_u8 dialog_token; /**< Dialog token */ + t_u8 ieee_status_code; /**< IEEE status code */ + t_u8 tspec_data[WMM_TSPEC_SIZE]; /**< TSPEC data */ + t_u8 addts_extra_ie_buf[WMM_ADDTS_EXTRA_IE_BYTES]; /**< Extra IE buffer + */ +} MLAN_PACK_END HostCmd_DS_WMM_ADDTS_REQ; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_DELTS_REQ firmware command + */ +typedef MLAN_PACK_START struct { + mlan_cmd_result_e command_result; /**< Command result */ + t_u8 dialog_token; /**< Dialog token */ + t_u8 ieee_reason_code; /**< IEEE reason code */ + t_u8 tspec_data[WMM_TSPEC_SIZE]; /**< TSPEC data */ +} MLAN_PACK_END HostCmd_DS_WMM_DELTS_REQ; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_QUEUE_CONFIG firmware cmd + * + * Set/Get/Default the Queue parameters for a specific AC in the firmware. + */ +typedef MLAN_PACK_START struct { + mlan_wmm_queue_config_action_e action; /**< Set, Get, or Default */ + mlan_wmm_ac_e access_category; /**< WMM_AC_BK(0) to WMM_AC_VO(3) */ + /** @brief MSDU lifetime expiry per 802.11e + * + * - Ignored if 0 on a set command + * - Set to the 802.11e specified 500 TUs when defaulted + */ + t_u16 msdu_lifetime_expiry; + t_u8 tlv_buffer[WMM_QUEUE_CONFIG_EXTRA_TLV_BYTES]; /**< Not supported */ +} MLAN_PACK_END HostCmd_DS_WMM_QUEUE_CONFIG; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_QUEUE_STATS firmware cmd + * + * Turn statistical collection on/off for a given AC or retrieve the + * accumulated stats for an AC and clear them in the firmware. + */ +typedef MLAN_PACK_START struct { + mlan_wmm_queue_stats_action_e action; /**< Start, Stop, or Get */ +#ifdef BIG_ENDIAN_SUPPORT + t_u8 select_bin : 7; /**< WMM_AC_BK(0) to WMM_AC_VO(3), or TID */ + t_u8 select_is_userpri : 1; /**< Set if select_bin is UP, Clear for AC + */ +#else + t_u8 select_is_userpri : 1; /**< Set if select_bin is UP, Clear for AC + */ + t_u8 select_bin : 7; /**< WMM_AC_BK(0) to WMM_AC_VO(3), or TID */ +#endif + t_u16 pkt_count; /**< Number of successful packets transmitted */ + t_u16 pkt_loss; /**< Packets lost; not included in pktCount */ + t_u32 avg_queue_delay; /**< Average Queue delay in microsec */ + t_u32 avg_tx_delay; /**< Average Transmission delay in microsec */ + t_u16 used_time; /**< Calc used time - units of 32 microsec */ + t_u16 policed_time; /**< Calc policed time - units of 32 microsec */ + /** @brief Queue Delay Histogram; number of packets per queue delay + * range + * + * [0] - 0ms <= delay < 5ms + * [1] - 5ms <= delay < 10ms + * [2] - 10ms <= delay < 20ms + * [3] - 20ms <= delay < 30ms + * [4] - 30ms <= delay < 40ms + * [5] - 40ms <= delay < 50ms + * [6] - 50ms <= delay < msduLifetime (TUs) + */ + t_u16 delay_histogram[WMM_STATS_PKTS_HIST_BINS]; + /** Reserved */ + t_u16 reserved_1; +} MLAN_PACK_END HostCmd_DS_WMM_QUEUE_STATS; + +/** + * @brief Command structure for the HostCmd_CMD_WMM_TS_STATUS firmware cmd + * + * Query the firmware to get the status of the WMM Traffic Streams + */ +typedef MLAN_PACK_START struct { + /** TSID: Range: 0->7 */ + t_u8 tid; + /** TSID specified is valid */ + t_u8 valid; + /** AC TSID is active on */ + t_u8 access_category; + /** UP specified for the TSID */ + t_u8 user_priority; + /** Power save mode for TSID: 0 (legacy), 1 (UAPSD) */ + t_u8 psb; + /** Uplink(1), Downlink(2), Bidirectional(3) */ + t_u8 flow_dir; + /** Medium time granted for the TSID */ + t_u16 medium_time; +} MLAN_PACK_END HostCmd_DS_WMM_TS_STATUS; + +/** Firmware status for a specific AC */ +typedef MLAN_PACK_START struct { + /** Disabled flag */ + t_u8 disabled; + /** Flow required flag */ + t_u8 flow_required; + /** Flow created flag */ + t_u8 flow_created; +} MLAN_PACK_END WmmAcStatus_t; + +/** Local Power Capability */ +typedef MLAN_PACK_START struct _MrvlIEtypes_PowerCapability_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Minmum power */ + t_s8 min_power; + /** Maximum power */ + t_s8 max_power; +} MLAN_PACK_END MrvlIEtypes_PowerCapability_t; + +/** HT Information element */ +typedef MLAN_PACK_START struct _MrvlIETypes_HTInfo_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** HTInfo struct */ + HTInfo_t ht_info; +} MLAN_PACK_END MrvlIETypes_HTInfo_t; + +/** 20/40 BSS Coexistence element */ +typedef MLAN_PACK_START struct _MrvlIETypes_2040BSSCo_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** BSSCo2040_t struct */ + BSSCo2040_t bss_co_2040; +} MLAN_PACK_END MrvlIETypes_2040BSSCo_t; + +/** Extended Capabilities element */ +typedef MLAN_PACK_START struct _MrvlIETypes_ExtCap_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** ExtCap_t struct */ + ExtCap_t ext_cap; +} MLAN_PACK_END MrvlIETypes_ExtCap_t; + +/** Supported operating classes element */ +typedef MLAN_PACK_START struct _MrvlIETypes_SuppOperClass_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Current operationg class **/ + t_u8 current_oper_class; + /** Operating class list */ + t_u8 oper_class[1]; +} MLAN_PACK_END MrvlIETypes_SuppOperClass_t; + +/** Oper_class channel bandwidth element */ +typedef MLAN_PACK_START struct _MrvlIEtypes_chan_bw_oper_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** channel oper bandwidth*/ + mlan_ds_bw_chan_oper ds_chan_bw_oper; +} MLAN_PACK_END MrvlIEtypes_chan_bw_oper_t; + +/** Qos Info */ +typedef MLAN_PACK_START struct _MrvlIETypes_qosinfo_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** qos_info*/ + t_u8 qos_info; +} MLAN_PACK_END MrvlIETypes_qosinfo_t; + +/** Overlapping BSS Scan Parameters element */ +typedef MLAN_PACK_START struct _MrvlIETypes_OverlapBSSScanParam_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** OBSSScanParam_t struct */ + OBSSScanParam_t obss_scan_param; +} MLAN_PACK_END MrvlIETypes_OverlapBSSScanParam_t; + +/** Set of MCS values that STA desires to use within the BSS */ +typedef MLAN_PACK_START struct _MrvlIETypes_HTOperationalMCSSet_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** Bitmap indicating MCSs that STA desires to use within the BSS */ + t_u8 ht_operational_mcs_bitmap[16]; +} MLAN_PACK_END MrvlIETypes_HTOperationalMCSSet_t; + +/** VHT Operations IE */ +typedef MLAN_PACK_START struct _MrvlIETypes_VHTOprat_t { + /** Header */ + MrvlIEtypesHeader_t header; + + t_u8 chan_width; + t_u8 chan_center_freq_1; + t_u8 chan_center_freq_2; + /** Basic MCS set map, each 2 bits stands for a Nss */ + t_u16 basic_MCS_map; +} MLAN_PACK_END MrvlIETypes_VHTOprat_t; + +/** VHT Transmit Power Envelope IE */ +typedef MLAN_PACK_START struct _MrvlIETypes_VHTtxpower_t { + /** Header */ + MrvlIEtypesHeader_t header; + + t_u8 max_tx_power; + t_u8 chan_center_freq; + t_u8 chan_width; +} MLAN_PACK_END MrvlIETypes_VHTtxpower_t; + +/** Extended Power Constraint IE */ +typedef MLAN_PACK_START struct _MrvlIETypes_ExtPwerCons_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** channel width */ + t_u8 chan_width; + /** local power constraint */ + t_u8 local_power_cons; +} MLAN_PACK_END MrvlIETypes_ExtPwerCons_t; + +/** Extended BSS Load IE */ +typedef MLAN_PACK_START struct _MrvlIETypes_ExtBSSload_t { + /** Header */ + MrvlIEtypesHeader_t header; + + t_u8 MU_MIMO_capa_count; + t_u8 stream_underutilization; + t_u8 VHT40_util; + t_u8 VHT80_util; + t_u8 VHT160_util; +} MLAN_PACK_END MrvlIETypes_ExtBSSload_t; + +/** Quiet Channel IE */ +typedef MLAN_PACK_START struct _MrvlIETypes_QuietChan_t { + /** Header */ + MrvlIEtypesHeader_t header; + + t_u8 AP_quiet_mode; + t_u8 quiet_count; + t_u8 quiet_period; + t_u16 quiet_dur; + t_u16 quiet_offset; +} MLAN_PACK_END MrvlIETypes_QuietChan_t; + +/** Wide Bandwidth Channel Switch IE */ +typedef MLAN_PACK_START struct _MrvlIETypes_BWSwitch_t { + /** Header */ + MrvlIEtypesHeader_t header; + + t_u8 new_chan_width; + t_u8 new_chan_center_freq_1; + t_u8 new_chan_center_freq_2; +} MLAN_PACK_END MrvlIETypes_BWSwitch_t; + +/** AID IE */ +typedef MLAN_PACK_START struct _MrvlIETypes_AID_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** AID number */ + t_u16 AID; +} MLAN_PACK_END MrvlIETypes_AID_t; + +/** Operating Mode Notification IE */ +typedef MLAN_PACK_START struct _MrvlIETypes_OperModeNtf_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** operating mdoe */ + t_u8 oper_mode; +} MLAN_PACK_END MrvlIETypes_OperModeNtf_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; + +/** MrvlIEtypes_SAE_Password_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_SAE_Password_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** SAE Password */ + char sae_password[1]; +} MLAN_PACK_END MrvlIEtypes_SAE_Password_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; + +/* 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; + +/** RFType */ +typedef MLAN_PACK_START struct _RFType_t { + /** band info */ + Band_Config_t bandcfg; + /** reserved */ + t_u8 reserved; +} MLAN_PACK_END RFType_t; + +/** HostCmd_CMD_802_11_RF_CHANNEL */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RF_CHANNEL { + /** Action */ + t_u16 action; + /** Current channel */ + t_u16 current_channel; + /** RF type */ + RFType_t rf_type; + /** Reserved field */ + t_u16 reserved; +#ifdef STA_SUPPORT + /** Reserved */ + t_u8 reserved_1[32]; +#else /* STA_SUPPORT */ + /** List of channels */ + t_u8 channel_list[32]; +#endif /* !STA_SUPPORT */ +} MLAN_PACK_END HostCmd_DS_802_11_RF_CHANNEL; + +/** HostCmd_DS_VERSION_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_VERSION_EXT { + /** Selected version string */ + t_u8 version_str_sel; + /** Version string */ + char version_str[128]; +} MLAN_PACK_END HostCmd_DS_VERSION_EXT; + +#define TLV_TYPE_CHAN_ATTR_CFG (PROPRIETARY_TLV_BASE_ID + 237) +#define TLV_TYPE_REGION_INFO (PROPRIETARY_TLV_BASE_ID + 238) +#define TLV_TYPE_POWER_TABLE (PROPRIETARY_TLV_BASE_ID + 262) +#define TLV_TYPE_POWER_TABLE_ATTR (PROPRIETARY_TLV_BASE_ID + 317) +/** HostCmd_DS_CHAN_REGION_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_CHAN_REGION_CFG { + /** Action */ + t_u16 action; +} MLAN_PACK_END HostCmd_DS_CHAN_REGION_CFG; + +/** HostCmd_CMD_CW_MODE_CTRL */ +typedef MLAN_PACK_START struct _HostCmd_DS_CW_MODE_CTRL { + /** Action for CW Tone Control */ + t_u16 action; + /** Mode of Operation 0: Disbale 1: Tx Continuous Packet 2: Tx + * Continuous Wave */ + t_u8 mode; + /** channel */ + t_u8 channel; + /** channel info*/ + t_u8 chanInfo; + /** Tx Power level in dBm */ + t_u16 txPower; + /** Packet Length */ + t_u16 pktLength; + /** bit rate Info */ + t_u32 rateInfo; +} MLAN_PACK_END HostCmd_DS_CW_MODE_CTRL; + +/** HostCmd_CMD_802_11_RF_ANTENNA */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_RF_ANTENNA { + /** Action 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_BCA_REG_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_BCA_REG_ACCESS { + /** Action */ + t_u16 action; + /** BCA register offset */ + t_u16 offset; + /** BCA register value */ + t_u32 value; +} MLAN_PACK_END HostCmd_DS_BCA_REG_ACCESS; + +/** HostCmd_CMD_BBP_REG_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_BBP_REG_ACCESS { + /** Acion */ + t_u16 action; + /** BBP register offset */ + t_u16 offset; + /** BBP register value */ + t_u8 value; + /** Reserved field */ + t_u8 reserved[3]; +} MLAN_PACK_END HostCmd_DS_BBP_REG_ACCESS; + +/** HostCmd_CMD_RF_REG_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_RF_REG_ACCESS { + /** Action */ + t_u16 action; + /** RF register offset */ + t_u16 offset; + /** RF register value */ + t_u8 value; + /** Reserved field */ + t_u8 reserved[3]; +} MLAN_PACK_END HostCmd_DS_RF_REG_ACCESS; + +/** HostCmd_DS_802_11_EEPROM_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_EEPROM_ACCESS { + /** Action */ + t_u16 action; + + /** multiple 4 */ + t_u16 offset; + /** Number of bytes */ + t_u16 byte_count; + /** Value */ + t_u8 value; +} MLAN_PACK_END HostCmd_DS_802_11_EEPROM_ACCESS; + +/** HostCmd_DS_MEM_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_MEM_ACCESS { + /** Action */ + t_u16 action; + /** Reserved field */ + t_u16 reserved; + /** Address */ + t_u32 addr; + /** Value */ + t_u32 value; +} MLAN_PACK_END HostCmd_DS_MEM_ACCESS; + +/** HostCmd_DS_TARGET_ACCESS */ +typedef MLAN_PACK_START struct _HostCmd_DS_TARGET_ACCESS { + /** Action */ + t_u16 action; + /** CSU Target Device. 1: CSU, 2: PSU */ + t_u16 csu_target; + /** Target Device Address */ + t_u16 address; + /** Data */ + t_u8 data; +} MLAN_PACK_END HostCmd_DS_TARGET_ACCESS; + +/** HostCmd_DS_SUBSCRIBE_EVENT */ +typedef MLAN_PACK_START struct _HostCmd_DS_SUBSCRIBE_EVENT { + /** Action */ + t_u16 action; + /** Bitmap of subscribed events */ + t_u16 event_bitmap; +} MLAN_PACK_END HostCmd_DS_SUBSCRIBE_EVENT; + +/** HostCmd_DS_OTP_USER_DATA */ +typedef MLAN_PACK_START struct _HostCmd_DS_OTP_USER_DATA { + /** Action */ + t_u16 action; + /** Reserved field */ + t_u16 reserved; + /** User data length */ + t_u16 user_data_length; + /** User data */ + t_u8 user_data[1]; +} MLAN_PACK_END HostCmd_DS_OTP_USER_DATA; + +/** HostCmd_CMD_HS_WAKEUP_REASON */ +typedef MLAN_PACK_START struct _HostCmd_DS_HS_WAKEUP_REASON { + /** wakeupReason: + * 0: unknown + * 1: Broadcast data matched + * 2: Multicast data matched + * 3: Unicast data matched + * 4: Maskable event matched + * 5. Non-maskable event matched + * 6: Non-maskable condition matched (EAPoL rekey) + * 7: Magic pattern matched + * Others: reserved. (set to 0) */ + t_u16 wakeup_reason; +} MLAN_PACK_END HostCmd_DS_HS_WAKEUP_REASON; + +/** MrvlIEtypes_HsWakeHoldoff_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_HsWakeHoldoff_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Minimum delay between HsActive and HostWake (in msec) */ + t_u16 min_wake_holdoff; +} MLAN_PACK_END MrvlIEtypes_HsWakeHoldoff_t; + +/** MrvlIEtypes_PsParamsInHs_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_PsParamsInHs_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Host sleep wake interval(in msec) */ + t_u32 hs_wake_interval; + /** Host sleep inactivity timeout (in msec) */ + t_u32 hs_inactivity_timeout; +} MLAN_PACK_END MrvlIEtypes_PsParamsInHs_t; + +/** MrvlIEtypes_WakeupSourceGPIO_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_WakeupSourceGPIO_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** GPIO for indication of wakeup source */ + t_u8 ind_gpio; + /** Level on ind_gpio for normal wakeup source */ + t_u8 level; +} MLAN_PACK_END MrvlIEtypes_WakeupSourceGPIO_t; + +/** MrvlIEtypes_RobustcoexSourceGPIO_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_RobustcoexSourceGPIO_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** GPIO cfg for external bt request */ + t_u8 enable; + /** GPIO number */ + t_u8 gpio_num; + /** GPIO Polarity */ + t_u8 gpio_polarity; +} MLAN_PACK_END MrvlIEtypes_RobustcoexSourceGPIO_t; + +#define MAX_NUM_MAC 2 + +typedef MLAN_PACK_START struct _dmcs_chan_status { + /** Channel number */ + t_u8 channel; + /** Number of AP on this channel */ + t_u8 ap_count; + /** Number of STA on this channel*/ + t_u8 sta_count; +} MLAN_PACK_END dmcs_chan_status; + +typedef MLAN_PACK_START struct _dmcs_status { + /** radio ID */ + t_u8 radio_id; + /** Running mode + ** 0 - Idle + ** 1 - DBC + ** 2 - DRCS + */ + t_u8 running_mode; + /** Channel status of this radio */ + dmcs_chan_status chan_status[2]; +} MLAN_PACK_END dmcs_status; + +/** MrvlIEtypes_DmcsConfig_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_DmcsConfig_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Mapping policy */ + t_u8 mapping_policy; + /** Radio status of DMCS */ + dmcs_status radio_status[MAX_NUM_MAC]; +} MLAN_PACK_END MrvlIEtypes_DmcsStatus_t; + +#define ANTMODE_FW_DECISION 0xff +/** MrvlIEtypes_HS_Antmode_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_HS_Antmode_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Tx Path antenna mode*/ + t_u8 txpath_antmode; + /** Rx Path antenna mode */ + t_u8 rxpath_antmode; +} MLAN_PACK_END MrvlIEtypes_HS_Antmode_t; + +typedef MLAN_PACK_START struct _MrvlIEtypes_WakeupExtend_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Events that will be forced ignore **/ + t_u32 event_force_ignore; + /** Events that will use extend gap to inform host*/ + t_u32 event_use_ext_gap; + /** Extend gap*/ + t_u8 ext_gap; + /** GPIO wave level*/ + t_u8 gpio_wave; +} MLAN_PACK_END MrvlIEtypes_WakeupExtend_t; + +#define EVENT_MANAGEMENT_FRAME_WAKEUP 136 +typedef MLAN_PACK_START struct _mgmt_frame_filter { + /** action - bitmap + ** On matching rx'd pkt and filter during NON_HOSTSLEEP mode: + ** Action[1]=0 Discard + ** Action[1]=1 Allow + ** Note that default action on non-match is "Allow". + ** + ** On matching rx'd pkt and filter during HOSTSLEEP mode: + ** Action[1:0]=00 Discard and Not Wake host + ** Action[1:0]=01 Discard and Wake host + ** Action[1:0]=10 Invalid + ** Note that default action on non-match is "Discard and Not Wake + *host". + **/ + t_u8 action; + /** Frame type(p2p...) + ** type=0: invalid + ** type=1: p2p + ** type=0xff: management frames(assoc req/rsp, probe req/rsp,...) + ** type=others: reserved + **/ + t_u8 type; + /** Frame mask according to each type + ** When type=1 for p2p, frame-mask have following define: + ** Bit Frame + ** 0 GO Negotiation Request + ** 1 GO Negotiation Response + ** 2 GO Negotiation Confirmation + ** 3 P2P Invitation Request + ** 4 P2P Invitation Response + ** 5 Device Discoverability Request + ** 6 Device Discoverability Response + ** 7 Provision Discovery Request + ** 8 Provision Discovery Response + ** 9 Notice of Absence + ** 10 P2P Presence Request + ** 11 P2P Presence Response + ** 12 GO Discoverability Request + ** 13-31 Reserved + ** + ** When type=others, frame-mask is reserved. + **/ + t_u32 frame_mask; +} MLAN_PACK_END mgmt_frame_filter; + +#define MAX_MGMT_FRAME_FILTER 2 +/** MrvlIEtypes_MgmtFrameFilter_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_MgmtFrameFilter_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** management frame filters */ + mgmt_frame_filter filter[MAX_MGMT_FRAME_FILTER]; +} MLAN_PACK_END MrvlIEtypes_MgmtFrameFilter_t; + +/** HostCmd_DS_INACTIVITY_TIMEOUT_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_INACTIVITY_TIMEOUT_EXT { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** uS, 0 means 1000uS(1ms) */ + t_u16 timeout_unit; + /** Inactivity timeout for unicast data */ + t_u16 unicast_timeout; + /** Inactivity timeout for multicast data */ + t_u16 mcast_timeout; + /** Timeout for additional RX traffic after Null PM1 packet exchange */ + t_u16 ps_entry_timeout; + /** Reserved to further expansion */ + t_u16 reserved; +} MLAN_PACK_END HostCmd_DS_INACTIVITY_TIMEOUT_EXT; + +/** HostCmd_DS_INDEPENDENT_RESET_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_INDEPENDENT_RESET_CFG { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** out band independent reset */ + t_u8 ir_mode; + /** gpio pin */ + t_u8 gpio_pin; +} MLAN_PACK_END HostCmd_DS_INDEPENDENT_RESET_CFG; + +/** HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT */ +typedef MLAN_PACK_START struct _HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** ps inactivity timeout value */ + t_u16 inact_tmo; +} MLAN_PACK_END HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT; + +/** TLV type : STA Mac address */ +#define TLV_TYPE_STA_MAC_ADDRESS (PROPRIETARY_TLV_BASE_ID + 0x20) /* 0x0120 */ + +#define TLV_TYPE_RANDOM_MAC (PROPRIETARY_TLV_BASE_ID + 0xEC) /*0x01EC*/ + +/** MrvlIEtypes_MacAddr_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_MacAddr_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** mac address */ + t_u8 mac[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END MrvlIEtypes_MacAddr_t; + +/** Assoc Request */ +#define SUBTYPE_ASSOC_REQUEST 0 +/** ReAssoc Request */ +#define SUBTYPE_REASSOC_REQUEST 2 +/** Probe Resp */ +#define SUBTYPE_PROBE_RESP 5 +/** Disassoc Request */ +#define SUBTYPE_DISASSOC 10 +/** Auth Request */ +#define SUBTYPE_AUTH 11 +/** Deauth Request */ +#define SUBTYPE_DEAUTH 12 +/** Action frame */ +#define SUBTYPE_ACTION 13 +/** beacon */ +#define SUBTYPE_BEACON 8 + +#ifdef UAP_SUPPORT +/** TLV type : AP Channel band Config */ +#define TLV_TYPE_UAP_CHAN_BAND_CONFIG \ + (PROPRIETARY_TLV_BASE_ID + 0x2a) /* 0x012a */ +/** TLV type : AP Mac address */ +#define TLV_TYPE_UAP_MAC_ADDRESS (PROPRIETARY_TLV_BASE_ID + 0x2b) /* 0x012b */ +/** TLV type : AP Beacon period */ +#define TLV_TYPE_UAP_BEACON_PERIOD \ + (PROPRIETARY_TLV_BASE_ID + 0x2c) /* 0x012c \ + */ +/** TLV type : AP DTIM period */ +#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 0x2d) /* 0x012d */ +/** TLV type : AP Tx power */ +#define TLV_TYPE_UAP_TX_POWER (PROPRIETARY_TLV_BASE_ID + 0x2f) /* 0x012f */ +/** TLV type : AP SSID broadcast control */ +#define TLV_TYPE_UAP_BCAST_SSID_CTL \ + (PROPRIETARY_TLV_BASE_ID + 0x30) /* 0x0130 */ +/** TLV type : AP Preamble control */ +#define TLV_TYPE_UAP_PREAMBLE_CTL \ + (PROPRIETARY_TLV_BASE_ID + 0x31) /* 0x0131 \ + */ +/** TLV type : AP Antenna control */ +#define TLV_TYPE_UAP_ANTENNA_CTL (PROPRIETARY_TLV_BASE_ID + 0x32) /* 0x0132 */ +/** TLV type : AP RTS threshold */ +#define TLV_TYPE_UAP_RTS_THRESHOLD \ + (PROPRIETARY_TLV_BASE_ID + 0x33) /* 0x0133 \ + */ +/** TLV type : AP Tx data rate */ +#define TLV_TYPE_UAP_TX_DATA_RATE \ + (PROPRIETARY_TLV_BASE_ID + 0x35) /* 0x0135 \ + */ +/** TLV type: AP Packet forwarding control */ +#define TLV_TYPE_UAP_PKT_FWD_CTL (PROPRIETARY_TLV_BASE_ID + 0x36) /* 0x0136 */ +/** TLV type: STA information */ +#define TLV_TYPE_UAP_STA_INFO (PROPRIETARY_TLV_BASE_ID + 0x37) /* 0x0137 */ +/** TLV type: AP STA MAC address filter */ +#define TLV_TYPE_UAP_STA_MAC_ADDR_FILTER \ + (PROPRIETARY_TLV_BASE_ID + 0x38) /* 0x0138 */ +/** TLV type: AP STA ageout timer */ +#define TLV_TYPE_UAP_STA_AGEOUT_TIMER \ + (PROPRIETARY_TLV_BASE_ID + 0x39) /* 0x0139 */ +/** TLV type: AP WEP keys */ +#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 0x3b) /* 0x013b */ +/** TLV type: AP WPA passphrase */ +#define TLV_TYPE_UAP_WPA_PASSPHRASE \ + (PROPRIETARY_TLV_BASE_ID + 0x3c) /* 0x013c */ +/** TLV type: AP protocol */ +#define TLV_TYPE_UAP_ENCRYPT_PROTOCOL \ + (PROPRIETARY_TLV_BASE_ID + 0x40) /* 0x0140 */ +/** TLV type: AP AKMP */ +#define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 0x41) /* 0x0141 */ +/** TLV type: AP Fragment threshold */ +#define TLV_TYPE_UAP_FRAG_THRESHOLD \ + (PROPRIETARY_TLV_BASE_ID + 0x46) /* 0x0146 */ +/** TLV type: AP Group rekey timer */ +#define TLV_TYPE_UAP_GRP_REKEY_TIME \ + (PROPRIETARY_TLV_BASE_ID + 0x47) /* 0x0147 */ +/**TLV type : AP Max Station number */ +#define TLV_TYPE_UAP_MAX_STA_CNT (PROPRIETARY_TLV_BASE_ID + 0x55) /* 0x0155 */ +/**TLV type : AP Max Station number per chip */ +#define TLV_TYPE_UAP_MAX_STA_CNT_PER_CHIP \ + (PROPRIETARY_TLV_BASE_ID + 0x140) /* 0x0240 */ +/**TLV type : AP Retry limit */ +#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 0x5d) /* 0x015d */ +/** TLV type : AP MCBC data rate */ +#define TLV_TYPE_UAP_MCBC_DATA_RATE \ + (PROPRIETARY_TLV_BASE_ID + 0x62) /* 0x0162 */ +/**TLV type: AP RSN replay protection */ +#define TLV_TYPE_UAP_RSN_REPLAY_PROTECT \ + (PROPRIETARY_TLV_BASE_ID + 0x64) /* 0x0164 */ +/**TLV type: AP mgmt IE passthru mask */ +#define TLV_TYPE_UAP_MGMT_IE_PASSTHRU_MASK \ + (PROPRIETARY_TLV_BASE_ID + 0x70) /* 0x0170 */ + +/**TLV type: AP pairwise handshake timeout */ +#define TLV_TYPE_UAP_EAPOL_PWK_HSK_TIMEOUT \ + (PROPRIETARY_TLV_BASE_ID + 0x75) /* 0x0175 */ +/**TLV type: AP pairwise handshake retries */ +#define TLV_TYPE_UAP_EAPOL_PWK_HSK_RETRIES \ + (PROPRIETARY_TLV_BASE_ID + 0x76) /* 0x0176 */ +/**TLV type: AP groupwise handshake timeout */ +#define TLV_TYPE_UAP_EAPOL_GWK_HSK_TIMEOUT \ + (PROPRIETARY_TLV_BASE_ID + 0x77) /* 0x0177 */ +/**TLV type: AP groupwise handshake retries */ +#define TLV_TYPE_UAP_EAPOL_GWK_HSK_RETRIES \ + (PROPRIETARY_TLV_BASE_ID + 0x78) /* 0x0178 */ +/** TLV type: AP PS STA ageout timer */ +#define TLV_TYPE_UAP_PS_STA_AGEOUT_TIMER \ + (PROPRIETARY_TLV_BASE_ID + 0x7b) /* 0x017b */ +/** TLV type : Pairwise Cipher */ +#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 0x91) /* 0x0191 */ +/** TLV type : Group Cipher */ +#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 0x92) /* 0x0192 */ +/** TLV type : BSS Status */ +#define TLV_TYPE_BSS_STATUS (PROPRIETARY_TLV_BASE_ID + 0x93) /* 0x0193 */ +/** TLV type : AP WMM params */ +#define TLV_TYPE_AP_WMM_PARAM (PROPRIETARY_TLV_BASE_ID + 0xd0) /* 0x01d0 */ +/** TLV type : AP Tx beacon rate */ +#define TLV_TYPE_UAP_TX_BEACON_RATE \ + (PROPRIETARY_TLV_BASE_ID + 288) /* 0x0220 \ + */ + +/** MrvlIEtypes_beacon_period_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_beacon_period_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** beacon period */ + t_u16 beacon_period; +} MLAN_PACK_END MrvlIEtypes_beacon_period_t; + +/** MrvlIEtypes_dtim_period_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_dtim_period_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** DTIM period */ + t_u8 dtim_period; +} MLAN_PACK_END MrvlIEtypes_dtim_period_t; + +/** MrvlIEtypes_tx_rate_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_tx_rate_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** tx data rate */ + t_u16 tx_data_rate; +} MLAN_PACK_END MrvlIEtypes_tx_rate_t; + +/** MrvlIEtypes_mcbc_rate_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_mcbc_rate_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** mcbc data rate */ + t_u16 mcbc_data_rate; +} MLAN_PACK_END MrvlIEtypes_mcbc_rate_t; + +/** MrvlIEtypes_tx_power_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_tx_power_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** tx power */ + t_u8 tx_power; +} MLAN_PACK_END MrvlIEtypes_tx_power_t; + +/** MrvlIEtypes_bcast_ssid_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_bcast_ssid_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** bcast ssid control*/ + t_u8 bcast_ssid_ctl; +} MLAN_PACK_END MrvlIEtypes_bcast_ssid_t; + +/** MrvlIEtypes_antenna_mode_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_antenna_mode_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** which antenna */ + t_u8 which_antenna; + /** antenna mode*/ + t_u8 antenna_mode; +} MLAN_PACK_END MrvlIEtypes_antenna_mode_t; + +/** MrvlIEtypes_pkt_forward_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_pkt_forward_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** pkt foward control */ + t_u8 pkt_forward_ctl; +} MLAN_PACK_END MrvlIEtypes_pkt_forward_t; + +/** MrvlIEtypes_max_sta_count_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_max_sta_count_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** max station count */ + t_u16 max_sta_count; +} MLAN_PACK_END MrvlIEtypes_max_sta_count_t; + +/** MrvlIEtypes_uap_max_sta_cnt */ +typedef MLAN_PACK_START struct _MrvlIEtypes_uap_max_sta_cnt_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** max station count */ + t_u16 uap_max_sta; +} MLAN_PACK_END MrvlIEtypes_uap_max_sta_cnt_t; + +/** MrvlIEtypes_sta_ageout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_sta_ageout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** station age out timer */ + t_u32 sta_ageout_timer; +} MLAN_PACK_END MrvlIEtypes_sta_ageout_t; + +/** MrvlIEtypes_rts_threshold_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_rts_threshold_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** rts threshold */ + t_u16 rts_threshold; +} MLAN_PACK_END MrvlIEtypes_rts_threshold_t; + +/** MrvlIEtypes_frag_threshold_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_frag_threshold_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** frag threshold */ + t_u16 frag_threshold; +} MLAN_PACK_END MrvlIEtypes_frag_threshold_t; + +/** MrvlIEtypes_retry_limit_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_retry_limit_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** retry limit */ + t_u8 retry_limit; +} MLAN_PACK_END MrvlIEtypes_retry_limit_t; + +/** MrvlIEtypes_eapol_pwk_hsk_timeout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_pwk_hsk_timeout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** pairwise update timeout in milliseconds */ + t_u32 pairwise_update_timeout; +} MLAN_PACK_END MrvlIEtypes_eapol_pwk_hsk_timeout_t; + +/** MrvlIEtypes_eapol_pwk_hsk_retries_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_pwk_hsk_retries_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** pairwise handshake retries */ + t_u32 pwk_retries; +} MLAN_PACK_END MrvlIEtypes_eapol_pwk_hsk_retries_t; + +/** MrvlIEtypes_eapol_gwk_hsk_timeout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_gwk_hsk_timeout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** groupwise update timeout in milliseconds */ + t_u32 groupwise_update_timeout; +} MLAN_PACK_END MrvlIEtypes_eapol_gwk_hsk_timeout_t; + +/** MrvlIEtypes_eapol_gwk_hsk_retries_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_gwk_hsk_retries_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** groupwise handshake retries */ + t_u32 gwk_retries; +} MLAN_PACK_END MrvlIEtypes_eapol_gwk_hsk_retries_t; + +/** MrvlIEtypes_mgmt_ie_passthru_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_mgmt_ie_passthru_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** mgmt IE mask value */ + t_u32 mgmt_ie_mask; +} MLAN_PACK_END MrvlIEtypes_mgmt_ie_passthru_t; + +/** MrvlIEtypes_mac_filter_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_mac_filter_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Filter mode */ + t_u8 filter_mode; + /** Number of STA MACs */ + t_u8 count; + /** STA MAC addresses buffer */ + t_u8 mac_address[1]; +} MLAN_PACK_END MrvlIEtypes_mac_filter_t; + +/** MrvlIEtypes_auth_type_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_auth_type_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Authentication type */ + t_u8 auth_type; +} MLAN_PACK_END MrvlIEtypes_auth_type_t; + +/** MrvlIEtypes_encrypt_protocol_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_encrypt_protocol_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** encryption protocol */ + t_u16 protocol; +} MLAN_PACK_END MrvlIEtypes_encrypt_protocol_t; + +/** MrvlIEtypes_pwk_cipher_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_pwk_cipher_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** protocol */ + t_u16 protocol; + /** pairwise cipher */ + t_u8 pairwise_cipher; + /** reserved */ + t_u8 reserved; +} MLAN_PACK_END MrvlIEtypes_pwk_cipher_t; + +/** MrvlIEtypes_gwk_cipher_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_gwk_cipher_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** group cipher */ + t_u8 group_cipher; + /** reserved */ + t_u8 reserved; +} MLAN_PACK_END MrvlIEtypes_gwk_cipher_t; + +/** MrvlIEtypes_akmp_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_akmp_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** key management */ + t_u16 key_mgmt; + /** key management operation */ + t_u16 key_mgmt_operation; +} MLAN_PACK_END MrvlIEtypes_akmp_t; + +/** MrvlIEtypes_passphrase_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_passphrase_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** passphrase */ + t_u8 passphrase[1]; +} MLAN_PACK_END MrvlIEtypes_passphrase_t; + +/** MrvlIEtypes_rsn_replay_prot_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_rsn_replay_prot_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** rsn replay proection */ + t_u8 rsn_replay_prot; +} MLAN_PACK_END MrvlIEtypes_rsn_replay_prot_t; + +/** MrvlIEtypes_group_rekey_time_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_group_rekey_time_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** group key rekey time */ + t_u32 gk_rekey_time; +} MLAN_PACK_END MrvlIEtypes_group_rekey_time_t; + +/** MrvlIEtypes_wep_key_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_wep_key_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** key index */ + t_u8 key_index; + /** is default */ + t_u8 is_default; + /** key data */ + t_u8 key[1]; +} MLAN_PACK_END MrvlIEtypes_wep_key_t; + +/** MrvlIEtypes_bss_status_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_bss_status_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** BSS status, READ only */ + t_u16 bss_status; +} MLAN_PACK_END MrvlIEtypes_bss_status_t; + +/** MrvlIEtypes_preamble_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_preamble_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** preamble type, READ only */ + t_u8 preamble_type; +} MLAN_PACK_END MrvlIEtypes_preamble_t; + +/** MrvlIEtypes_wmm_parameter_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_wmm_parameter_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** WMM parameter */ + WmmParameter_t wmm_para; +} MLAN_PACK_END MrvlIEtypes_wmm_parameter_t; + +/** SNMP_MIB_UAP_INDEX */ +typedef enum _SNMP_MIB_UAP_INDEX { + tkip_mic_failures = 0x0b, + ccmp_decrypt_errors = 0x0c, + wep_undecryptable_count = 0x0d, + wep_icv_error_count = 0x0e, + decrypt_failure_count = 0xf, + dot11_failed_count = 0x12, + dot11_retry_count = 0x13, + dot11_multi_retry_count = 0x14, + dot11_frame_dup_count = 0x15, + dot11_rts_success_count = 0x16, + dot11_rts_failure_count = 0x17, + dot11_ack_failure_count = 0x18, + dot11_rx_fragment_count = 0x19, + dot11_mcast_rx_frame_count = 0x1a, + dot11_fcs_error_count = 0x1b, + dot11_tx_frame_count = 0x1c, + dot11_rsna_tkip_cm_invoked = 0x1d, + dot11_rsna_4way_hshk_failures = 0x1e, + dot11_mcast_tx_count = 0x1f, +} SNMP_MIB_UAP_INDEX; + +/** MrvlIEtypes_snmp_oid_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_snmp_oid_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** data */ + t_u32 data; +} MLAN_PACK_END MrvlIEtypes_snmp_oid_t; + +/** HostCmd_SYS_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_SYS_CONFIG { + /** CMD Action GET/SET*/ + t_u16 action; + /** Tlv buffer */ + t_u8 tlv_buffer[1]; +} MLAN_PACK_END HostCmd_DS_SYS_CONFIG; + +/** HostCmd_SYS_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_SYS_INFO { + /** sys info */ + t_u8 sys_info[64]; +} MLAN_PACK_END HostCmd_DS_SYS_INFO; + +/** HostCmd_DS_STA_DEAUTH */ +typedef MLAN_PACK_START struct _HostCmd_DS_STA_DEAUTH { + /** mac address */ + t_u8 mac[MLAN_MAC_ADDR_LENGTH]; + /** reason code */ + t_u16 reason; +} MLAN_PACK_END HostCmd_DS_STA_DEAUTH; + +/** HostCmd_DS_REPORT_MIC */ +typedef MLAN_PACK_START struct _HostCmd_DS_REPORT_MIC { + /** mac address */ + t_u8 mac[MLAN_MAC_ADDR_LENGTH]; +} MLAN_PACK_END HostCmd_DS_REPORT_MIC; + +/** HostCmd_UAP_OPER_CTRL */ +typedef MLAN_PACK_START struct _HostCmd_DS_UAP_OPER_CTRL { + /** CMD Action GET/SET*/ + t_u16 action; + /** control*/ + t_u16 ctrl; + /**channel operation*/ + t_u16 chan_opt; + /**channel band tlv*/ + MrvlIEtypes_channel_band_t channel_band; +} MLAN_PACK_END HostCmd_DS_UAP_OPER_CTRL; + +/** Host Command id: POWER_MGMT */ +#define HOST_CMD_POWER_MGMT_EXT 0x00ef +/** TLV type: AP Sleep param */ +#define TLV_TYPE_AP_SLEEP_PARAM (PROPRIETARY_TLV_BASE_ID + 0x6a) /* 0x016a */ +/** TLV type: AP Inactivity Sleep param */ +#define TLV_TYPE_AP_INACT_SLEEP_PARAM \ + (PROPRIETARY_TLV_BASE_ID + 0x6b) /* 0x016b */ + +/** MrvlIEtypes_sleep_param_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_sleep_param_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** control bitmap */ + t_u32 ctrl_bitmap; + /** min_sleep */ + t_u32 min_sleep; + /** max_sleep */ + t_u32 max_sleep; +} MLAN_PACK_END MrvlIEtypes_sleep_param_t; + +/** MrvlIEtypes_inact_sleep_param_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_inact_sleep_param_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** inactivity timeout */ + t_u32 inactivity_to; + + /** min_awake */ + t_u32 min_awake; + /** max_awake */ + t_u32 max_awake; +} MLAN_PACK_END MrvlIEtypes_inact_sleep_param_t; + +/** HostCmd_DS_POWER_MGMT */ +typedef MLAN_PACK_START struct _HostCmd_DS_POWER_MGMT_EXT { + /** CMD Action Get/Set*/ + t_u16 action; + /** power mode */ + t_u16 power_mode; +} MLAN_PACK_END HostCmd_DS_POWER_MGMT_EXT; + +/** MrvlIEtypes_ps_sta_ageout_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_ps_sta_ageout_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** station age out timer */ + t_u32 ps_sta_ageout_timer; +} MLAN_PACK_END MrvlIEtypes_ps_sta_ageout_t; + +/** MrvlIEtypes_sta_info_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_sta_info_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** STA MAC address */ + t_u8 mac_address[MLAN_MAC_ADDR_LENGTH]; + /** Power Mgmt status */ + t_u8 power_mgmt_status; + /** RSSI */ + t_s8 rssi; + /** ie_buf */ + t_u8 ie_buf[]; +} 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[]; */ + t_u8 tlv_buf[]; +} 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 */ + +/** HostCmd_DS_TX_RX_HISTOGRAM */ +typedef MLAN_PACK_START struct _HostCmd_DS_TX_RX_HISTOGRAM { + /** Enable or disable */ + t_u8 enable; + /** Choose to get TX, RX or both */ + t_u16 action; +} MLAN_PACK_END HostCmd_DS_TX_RX_HISTOGRAM; + +/** 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; + +/**BT coexit scan time setting*/ +typedef MLAN_PACK_START struct _MrvlIEtypes_BtCoexScanTime_t { + /** Header */ + MrvlIEtypesHeader_t header; + /**coex scan state 0: disable 1: enable*/ + t_u8 coex_scan; + /**reserved*/ + t_u8 reserved; + /**min scan time*/ + t_u16 min_scan_time; + /**max scan time*/ + t_u16 max_scan_time; +} MLAN_PACK_END MrvlIEtypes_BtCoexScanTime_t; + +/**BT coexit aggr win size */ +typedef MLAN_PACK_START struct _MrvlIETypes_BtCoexAggrWinSize_t { + /** Header */ + MrvlIEtypesHeader_t header; + /**winsize 0: restore default winsize, 1: use below winsize */ + t_u8 coex_win_size; + /**tx win size*/ + t_u8 tx_win_size; + /**rx win size*/ + t_u8 rx_win_size; + /**reserved*/ + t_u8 reserved; +} MLAN_PACK_END MrvlIETypes_BtCoexAggrWinSize_t; + +/** MrvlIEtypes_eapol_pkt_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_eapol_pkt_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** eapol pkt buf */ + t_u8 pkt_buf[]; +} MLAN_PACK_END MrvlIEtypes_eapol_pkt_t; + +/** HostCmd_DS_EAPOL_PKT */ +typedef MLAN_PACK_START struct _HostCmd_DS_EAPOL_PKT { + /** Action */ + t_u16 action; + /** TLV buffer */ + MrvlIEtypes_eapol_pkt_t tlv_eapol; +} MLAN_PACK_END HostCmd_DS_EAPOL_PKT; + +/** HostCmd_DS_OXYGEN_MIMO_SWITCH */ +typedef MLAN_PACK_START struct _HostCmd_DS_MIMO_SWITCH { + /** Tx path antanne mode */ + t_u8 txpath_antmode; + /** Rx path antanne mode */ + t_u8 rxpath_antmode; +} MLAN_PACK_END HostCmd_DS_MIMO_SWITCH; + +#ifdef RX_PACKET_COALESCE +typedef MLAN_PACK_START struct _HostCmd_DS_RX_PKT_COAL_CFG { + /** Action */ + t_u16 action; + /** Packet threshold */ + t_u32 packet_threshold; + /** Timeout */ + t_u16 delay; +} MLAN_PACK_END HostCmd_DS_RX_PKT_COAL_CFG; +#endif + +/** HostCmd_DS_DYN_BW */ +typedef MLAN_PACK_START struct _HostCmd_DS_DYN_BW { + /** Action */ + t_u16 action; + /** Dynamic bandwidth */ + t_u16 dyn_bw; +} MLAN_PACK_END HostCmd_DS_DYN_BW; + +/** Host Command ID : Packet aggregation CTRL */ +#define HostCmd_CMD_PACKET_AGGR_CTRL 0x0251 + +/** HostCmd_DS_PACKET_AGGR_CTRL */ +typedef MLAN_PACK_START struct _HostCmd_DS_PACKET_AGGR_AGGR_CTRL { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** enable aggregation, BIT(0) TX, BIT(1)RX */ + t_u16 aggr_enable; + /** Tx aggregation alignment */ + t_u16 tx_aggr_max_size; + /** Tx aggregation max packet number */ + t_u16 tx_aggr_max_num; + /** Tx aggregation alignment */ + t_u16 tx_aggr_align; +} MLAN_PACK_END HostCmd_DS_PACKET_AGGR_CTRL; + +#ifdef USB +/** Host Command ID : Packet aggregation over host interface */ +#define HostCmd_CMD_PACKET_AGGR_OVER_HOST_INTERFACE 0x0117 + +/** TLV ID : USB Aggregation parameters */ +#define MRVL_USB_AGGR_PARAM_TLV_ID \ + (PROPRIETARY_TLV_BASE_ID + 0xB1) /* 0x1B1 \ + */ + +/** TLV size : USB Aggregation parameters, except header */ +#define MRVL_USB_AGGR_PARAM_TLV_LEN (14) + +/** VHT Operations IE */ +typedef MLAN_PACK_START struct _MrvlIETypes_USBAggrParam_t { + /** Header */ + MrvlIEtypesHeader_t header; + + /** Enable */ + t_u16 enable; + /** Rx aggregation mode */ + t_u16 rx_aggr_mode; + /** Rx aggregation alignment */ + t_u16 rx_aggr_align; + /** Rx aggregation max packet/size */ + t_u16 rx_aggr_max; + /** Rx aggrgation timeout, in microseconds */ + t_u16 rx_aggr_tmo; + /** Tx aggregation mode */ + t_u16 tx_aggr_mode; + /** Tx aggregation alignment */ + t_u16 tx_aggr_align; +} MLAN_PACK_END MrvlIETypes_USBAggrParam_t; + +/** HostCmd_DS_PACKET_AGGR_OVER_HOST_INTERFACE */ +typedef MLAN_PACK_START struct _HostCmd_DS_PACKET_AGGR_OVER_HOST_INTERFACE { + /** ACT_GET/ACT_SET */ + t_u16 action; + /** + * Host interface aggregation control TLV(s) to be sent in the firmware + * command + * + * TLV_USB_AGGR_PARAM, MrvlIETypes_USBAggrParam_t + */ + t_u8 tlv_buf[1]; +} MLAN_PACK_END HostCmd_DS_PACKET_AGGR_OVER_HOST_INTERFACE; +#endif /* USB */ + +/** HostCmd_CONFIG_LOW_PWR_MODE */ +typedef MLAN_PACK_START struct _HostCmd_CONFIG_LOW_PWR_MODE { + /** Enable LPM */ + t_u8 enable; +} MLAN_PACK_END HostCmd_CONFIG_LOW_PWR_MODE; + +/** HostCmd_CMD_GET_TSF */ +typedef MLAN_PACK_START struct _HostCmd_DS_TSF { + /** tsf value*/ + t_u64 tsf; +} MLAN_PACK_END HostCmd_DS_TSF; +/* WLAN_GET_TSF*/ + +typedef struct _HostCmd_DS_DFS_REPEATER_MODE { + /** Set or Get */ + t_u16 action; + /** 1 on or 0 off */ + t_u16 mode; +} HostCmd_DS_DFS_REPEATER_MODE; + +/** HostCmd_DS_BOOT_SLEEP */ +typedef MLAN_PACK_START struct _HostCmd_DS_BOOT_SLEEP { + /** Set or Get */ + t_u16 action; + /** 1 on or 0 off */ + t_u16 enable; +} MLAN_PACK_END HostCmd_DS_BOOT_SLEEP; + +/** + * @brief 802.11h Local Power Constraint NXP extended TLV + */ +typedef MLAN_PACK_START struct { + MrvlIEtypesHeader_t header; /**< NXP TLV header: ID/Len */ + t_u8 chan; /**< Channel local constraint applies to */ + + /** Power constraint included in beacons + * and used by fw to offset 11d info + */ + t_u8 constraint; + +} MLAN_PACK_END MrvlIEtypes_LocalPowerConstraint_t; + +/* + * + * Data structures for driver/firmware command processing + * + */ + +/** TPC Info structure sent in CMD_802_11_TPC_INFO command to firmware */ +typedef MLAN_PACK_START struct { + /**< Local constraint */ + MrvlIEtypes_LocalPowerConstraint_t local_constraint; + /**< Power Capability */ + MrvlIEtypes_PowerCapability_t power_cap; + +} MLAN_PACK_END HostCmd_DS_802_11_TPC_INFO; + +/** TPC Request structure sent in CMD_802_11_TPC_ADAPT_REQ + * command to firmware + */ +typedef MLAN_PACK_START struct { + t_u8 dest_mac[MLAN_MAC_ADDR_LENGTH]; /**< Destination STA address */ + t_u16 timeout; /**< Response timeout in ms */ + t_u8 rate_index; /**< IEEE Rate index to send request */ + +} MLAN_PACK_END HostCmd_TpcRequest; + +/** TPC Response structure received from the + * CMD_802_11_TPC_ADAPT_REQ command + */ +typedef MLAN_PACK_START struct { + t_u8 tpc_ret_code; /**< Firmware command result status code */ + t_s8 tx_power; /**< Reported TX Power from the TPC Report element */ + t_s8 link_margin; /**< Reported link margin from the TPC Report element + */ + t_s8 rssi; /**< RSSI of the received TPC Report frame */ + +} MLAN_PACK_END HostCmd_TpcResponse; + +/** CMD_802_11_TPC_ADAPT_REQ substruct. + * Union of the TPC request and response + */ +typedef MLAN_PACK_START union { + HostCmd_TpcRequest req; /**< Request struct sent to firmware */ + HostCmd_TpcResponse resp; /**< Response struct received from firmware */ + +} MLAN_PACK_END HostCmd_DS_802_11_TPC_ADAPT_REQ; + +/** CMD_802_11_CHAN_SW_ANN firmware command substructure */ +typedef MLAN_PACK_START struct { + t_u8 switch_mode; /**< Set to 1 for a quiet switch request, no STA tx */ + t_u8 new_chan; /**< Requested new channel */ + t_u8 switch_count; /**< Number of TBTTs until the switch is to occur */ +} MLAN_PACK_END HostCmd_DS_802_11_CHAN_SW_ANN; + +/** + * @brief Enumeration of measurement types, including max supported + * enum for 11h/11k + */ +typedef MLAN_PACK_START enum _MeasType_t { + WLAN_MEAS_BASIC = 0, /**< 11h: Basic */ + WLAN_MEAS_NUM_TYPES, /**< Number of enumerated measurements */ + WLAN_MEAS_11H_MAX_TYPE = WLAN_MEAS_BASIC, /**< Max 11h measurement */ + +} MLAN_PACK_END MeasType_t; + +/** + * @brief Mode octet of the measurement request element (7.3.2.21) + */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /**< Reserved */ + t_u8 rsvd5_7 : 3; + /**< 11k: duration spec. for meas. is mandatory */ + t_u8 duration_mandatory : 1; + /**< 11h: en/disable report rcpt. of spec. type */ + t_u8 report : 1; + /**< 11h: en/disable requests of specified type */ + t_u8 request : 1; + /**< 11h: enable report/request bits */ + t_u8 enable : 1; + /**< 11k: series or parallel with previous meas */ + t_u8 parallel : 1; +#else + /**< 11k: series or parallel with previous meas */ + t_u8 parallel : 1; + /**< 11h: enable report/request bits */ + t_u8 enable : 1; + /**< 11h: en/disable requests of specified type */ + t_u8 request : 1; + /**< 11h: en/disable report rcpt. of spec. type */ + t_u8 report : 1; + /**< 11k: duration spec. for meas. is mandatory */ + t_u8 duration_mandatory : 1; + /**< Reserved */ + t_u8 rsvd5_7 : 3; +#endif /* BIG_ENDIAN_SUPPORT */ + +} MLAN_PACK_END MeasReqMode_t; + +/** + * @brief Common measurement request structure (7.3.2.21.1 to 7.3.2.21.3) + */ +typedef MLAN_PACK_START struct { + t_u8 channel; /**< Channel to measure */ + t_u64 start_time; /**< TSF Start time of measurement (0 for immediate) + */ + t_u16 duration; /**< TU duration of the measurement */ + +} MLAN_PACK_END MeasReqCommonFormat_t; + +/** + * @brief Basic measurement request structure (7.3.2.21.1) + */ +typedef MeasReqCommonFormat_t MeasReqBasic_t; + +/** + * @brief CCA measurement request structure (7.3.2.21.2) + */ +typedef MeasReqCommonFormat_t MeasReqCCA_t; + +/** + * @brief RPI measurement request structure (7.3.2.21.3) + */ +typedef MeasReqCommonFormat_t MeasReqRPI_t; + +/** + * @brief Union of the availble measurement request types. Passed in the + * driver/firmware interface. + */ +typedef union { + MeasReqBasic_t basic; /**< Basic measurement request */ + MeasReqCCA_t cca; /**< CCA measurement request */ + MeasReqRPI_t rpi; /**< RPI measurement request */ + +} MeasRequest_t; + +/** + * @brief Mode octet of the measurement report element (7.3.2.22) + */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u8 rsvd3_7 : 5; /**< Reserved */ + t_u8 refused : 1; /**< Measurement refused */ + t_u8 incapable : 1; /**< Incapable of performing measurement */ + t_u8 late : 1; /**< Start TSF time missed for measurement */ +#else + t_u8 late : 1; /**< Start TSF time missed for measurement */ + t_u8 incapable : 1; /**< Incapable of performing measurement */ + t_u8 refused : 1; /**< Measurement refused */ + t_u8 rsvd3_7 : 5; /**< Reserved */ +#endif /* BIG_ENDIAN_SUPPORT */ + +} MLAN_PACK_END MeasRptMode_t; + +/** + * @brief Basic measurement report (7.3.2.22.1) + */ +typedef MLAN_PACK_START struct { + t_u8 channel; /**< Channel to measured */ + t_u64 start_time; /**< Start time (TSF) of measurement */ + t_u16 duration; /**< Duration of measurement in TUs */ + MeasRptBasicMap_t map; /**< Basic measurement report */ + +} MLAN_PACK_END MeasRptBasic_t; + +/** + * @brief CCA measurement report (7.3.2.22.2) + */ +typedef MLAN_PACK_START struct { + t_u8 channel; /**< Channel to measured */ + t_u64 start_time; /**< Start time (TSF) of measurement */ + t_u16 duration; /**< Duration of measurement in TUs */ + t_u8 busy_fraction; /**< Fractional duration CCA indicated chan busy */ + +} MLAN_PACK_END MeasRptCCA_t; + +/** + * @brief RPI measurement report (7.3.2.22.3) + */ +typedef MLAN_PACK_START struct { + t_u8 channel; /**< Channel to measured */ + t_u64 start_time; /**< Start time (TSF) of measurement */ + t_u16 duration; /**< Duration of measurement in TUs */ + t_u8 density[8]; /**< RPI Density histogram report */ + +} MLAN_PACK_END MeasRptRPI_t; + +/** + * @brief Union of the availble measurement report types. Passed in the + * driver/firmware interface. + */ +typedef union { + MeasRptBasic_t basic; /**< Basic measurement report */ + MeasRptCCA_t cca; /**< CCA measurement report */ + MeasRptRPI_t rpi; /**< RPI measurement report */ + +} MeasReport_t; + +/** + * @brief Structure passed to firmware to perform a measurement + */ +typedef MLAN_PACK_START struct { + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; /**< Reporting STA address */ + t_u8 dialog_token; /**< Measurement dialog toke */ + MeasReqMode_t req_mode; /**< Report mode */ + MeasType_t meas_type; /**< Measurement type */ + MeasRequest_t req; /**< Measurement request data */ + +} MLAN_PACK_END HostCmd_DS_MEASUREMENT_REQUEST, + *pHostCmd_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, *pHostCmd_DS_MEASUREMENT_REPORT; + +typedef MLAN_PACK_START struct { + t_u16 startFreq; + Band_Config_t bandcfg; + t_u8 chanNum; + +} MLAN_PACK_END MrvlChannelDesc_t; + +#ifdef OPCHAN +typedef MLAN_PACK_START struct { + MrvlIEtypesHeader_t header; /**< Header */ + + MrvlChannelDesc_t chanDesc; + + t_u16 controlFlags; + t_u16 reserved; + + t_u8 actPower; + t_u8 mdMinPower; + t_u8 mdMaxPower; + t_u8 mdPower; + +} MLAN_PACK_END MrvlIEtypes_ChanControlDesc_t; + +typedef MLAN_PACK_START struct { + MrvlIEtypesHeader_t header; /**< Header */ + + t_u16 chanGroupBitmap; + ChanScanMode_t scanMode; + t_u8 numChan; + + MrvlChannelDesc_t chanDesc[50]; + +} MLAN_PACK_END MrvlIEtypes_ChanGroupControl_t; + +typedef MLAN_PACK_START struct { + t_u16 action; /**< CMD Action Get/Set*/ + + t_u8 tlv_buffer[1]; + +} MLAN_PACK_END HostCmd_DS_OPCHAN_CONFIG; + +typedef MLAN_PACK_START struct { + t_u16 action; /**< CMD Action Get/Set*/ + + t_u8 tlv_buffer[1]; + +} MLAN_PACK_END HostCmd_DS_OPCHAN_CHANGROUP_CONFIG; + +#define HostCmd_CMD_OPCHAN_CONFIG 0x00f8 +#define HostCmd_CMD_OPCHAN_CHANGROUP_CONFIG 0x00f9 +#endif + +typedef MLAN_PACK_START struct { + MrvlIEtypesHeader_t Header; /**< Header */ + + MeasRptBasicMap_t map; /**< IEEE 802.11h basic meas report */ +} MLAN_PACK_END MrvlIEtypes_ChanRpt11hBasic_t; + +/* MrvlIEtypes_DfsW53Cfg_t*/ +typedef MLAN_PACK_START struct { + /* header */ + MrvlIEtypesHeader_t Header; + /** df53cfg vlue*/ + t_u8 dfs53cfg; +} MLAN_PACK_END MrvlIEtypes_DfsW53Cfg_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; + +#ifdef PCIE +/** PCIE dual descriptor for data/event */ +typedef MLAN_PACK_START struct _dual_desc_buf { + /** buf size */ + t_u16 len; + /** buffer descriptor flags */ + t_u16 flags; + /** pkt size */ + t_u16 pkt_size; + /** reserved */ + t_u16 reserved; + /** Physical address of the buffer */ + t_u64 paddr; +} MLAN_PACK_END adma_dual_desc_buf, *padma_dual_desc_buf; + +#if defined(PCIE8997) || defined(PCIE8897) +/** PCIE ring buffer description for DATA */ +typedef MLAN_PACK_START struct _mlan_pcie_data_buf { + /** Buffer descriptor flags */ + t_u16 flags; + /** Offset of fragment/pkt to start of ip header */ + t_u16 offset; + /** Fragment length of the buffer */ + t_u16 frag_len; + /** Length of the buffer */ + t_u16 len; + /** Physical address of the buffer */ + t_u64 paddr; + /** Reserved */ + t_u32 reserved; +} MLAN_PACK_END mlan_pcie_data_buf, *pmlan_pcie_data_buf; + +/** PCIE ring buffer description for EVENT */ +typedef MLAN_PACK_START struct _mlan_pcie_evt_buf { + /** Physical address of the buffer */ + t_u64 paddr; + /** Length of the buffer */ + t_u16 len; + /** Buffer descriptor flags */ + t_u16 flags; +} MLAN_PACK_END mlan_pcie_evt_buf, *pmlan_pcie_evt_buf; + +/** PCIE host buffer configuration */ +typedef MLAN_PACK_START struct _HostCmd_DS_PCIE_HOST_BUF_DETAILS { + /** TX buffer descriptor ring address */ + t_u32 txbd_addr_lo; + t_u32 txbd_addr_hi; + /** TX buffer descriptor ring count */ + t_u32 txbd_count; + + /** RX buffer descriptor ring address */ + t_u32 rxbd_addr_lo; + t_u32 rxbd_addr_hi; + /** RX buffer descriptor ring count */ + t_u32 rxbd_count; + + /** Event buffer descriptor ring address */ + t_u32 evtbd_addr_lo; + t_u32 evtbd_addr_hi; + /** Event buffer descriptor ring count */ + t_u32 evtbd_count; +} HostCmd_DS_PCIE_HOST_BUF_DETAILS; +#endif +#endif + +typedef MLAN_PACK_START struct _HostCmd_DS_SENSOR_TEMP { + t_u32 temperature; +} MLAN_PACK_END HostCmd_DS_SENSOR_TEMP; + +#ifdef STA_SUPPORT +typedef MLAN_PACK_START struct _HostCmd_DS_STA_CONFIGURE { + /** Action Set or get */ + t_u16 action; + /** Tlv buffer */ + t_u8 tlv_buffer[]; + /**MrvlIEtypes_channel_band_t band_channel; */ +} MLAN_PACK_END HostCmd_DS_STA_CONFIGURE; +#endif + +/** HostCmd_DS_AUTO_TX structure */ +typedef MLAN_PACK_START struct _HostCmd_DS_AUTO_TX { + /** Action Set or get */ + t_u16 action; + /** Tlv buffer */ + t_u8 tlv_buffer[]; +} MLAN_PACK_END HostCmd_DS_AUTO_TX; + +#define OID_CLOUD_KEEP_ALIVE 0 +#define EVENT_CLOUD_KEEP_ALIVE_RETRY_FAIL 133 +/** TLV for cloud keep alive control info */ +#define TLV_TYPE_CLOUD_KEEP_ALIVE \ + (PROPRIETARY_TLV_BASE_ID + 0x102) /* 0x0100 + 258 */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Cloud_Keep_Alive_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** ID for cloud keep alive */ + t_u8 keep_alive_id; + /** Enable/disable for this ID */ + t_u8 enable; + /** TLV buffer */ + t_u8 tlv[]; +} MLAN_PACK_END MrvlIEtypes_Cloud_Keep_Alive_t; + +/** TLV for cloud keep alive control info */ +#define TLV_TYPE_KEEP_ALIVE_CTRL \ + (PROPRIETARY_TLV_BASE_ID + 0x103) /* 0x0100 + 259 */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Keep_Alive_Ctrl_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** period to send keep alive packet */ + t_u32 snd_interval; + /** period to send retry packet */ + t_u16 retry_interval; + /** count to send retry packet */ + t_u16 retry_count; +} MLAN_PACK_END MrvlIEtypes_Keep_Alive_Ctrl_t; + +/** TLV for cloud keep alive packet */ +#define TLV_TYPE_KEEP_ALIVE_PKT \ + (PROPRIETARY_TLV_BASE_ID + 0x104) /* 0x0100 + 260 */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Keep_Alive_Pkt_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Ethernet Header */ + Eth803Hdr_t eth_header; + /** packet buffer*/ + t_u8 ip_packet[]; +} MLAN_PACK_END MrvlIEtypes_Keep_Alive_Pkt_t; + +/** TLV to indicate firmware only keep probe response while scan */ +#define TLV_TYPE_ONLYPROBERESP (PROPRIETARY_TLV_BASE_ID + 0xE9) /* 0x01E9 */ +typedef MLAN_PACK_START struct _MrvlIEtypes_OnlyProberesp_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** only keep probe response */ + t_u8 proberesp_only; +} MLAN_PACK_END MrvlIEtypes_OnlyProberesp_t; + +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) +#define HostCmd_CMD_CRYPTO 0x025e + +#define HostCmd_CMD_CRYPTO_SUBCMD_PRF_HMAC_SHA1 (0x1) +#define HostCmd_CMD_CRYPTO_SUBCMD_HMAC_SHA1 (0x2) +#define HostCmd_CMD_CRYPTO_SUBCMD_HMAC_SHA256 (0x3) +#define HostCmd_CMD_CRYPTO_SUBCMD_SHA256 (0x4) +#define HostCmd_CMD_CRYPTO_SUBCMD_RIJNDAEL (0x5) +#define HostCmd_CMD_CRYPTO_SUBCMD_RC4 (0x6) +#define HostCmd_CMD_CRYPTO_SUBCMD_MD5 (0x7) +#define HostCmd_CMD_CRYPTO_SUBCMD_MRVL_F (0x8) +#define HostCmd_CMD_CRYPTO_SUBCMD_SHA256_KDF (0x9) + +#define TLV_TYPE_CRYPTO_KEY (PROPRIETARY_TLV_BASE_ID + 308) +#define TLV_TYPE_CRYPTO_KEY_IV (PROPRIETARY_TLV_BASE_ID + 309) +#define TLV_TYPE_CRYPTO_KEY_PREFIX (PROPRIETARY_TLV_BASE_ID + 310) +#define TLV_TYPE_CRYPTO_KEY_DATA_BLK (PROPRIETARY_TLV_BASE_ID + 311) + +/** MrvlIEParamSet_t */ +typedef MLAN_PACK_START struct { + /** Type */ + t_u16 Type; + /** Length */ + t_u16 Length; +} MLAN_PACK_END MrvlIEParamSet_t; + +/** HostCmd_DS_CRYPTO */ +typedef MLAN_PACK_START struct _HostCmd_DS_CRYPTO { + /** action */ + t_u16 action; + /** subCmdCode */ + t_u8 subCmdCode; + /** subCmd start */ + t_u8 subCmd[]; +} MLAN_PACK_END HostCmd_DS_CRYPTO; + +/** subcmd_prf_hmac_sha1 used by prf_hmac_sha1, md5 and sha256_kdf */ +typedef MLAN_PACK_START struct _subcmd_prf_hmac_sha1 { + /** output_len */ + t_u16 output_len; + /** tlv start */ + t_u8 tlv[]; +} MLAN_PACK_END subcmd_prf_hmac_sha1_t, subcmd_md5_t, subcmd_sha256_kdf_t; + +/** subcmd_hmac_sha1 used by hmac_sha1, hmac_sha256, sha256 */ +typedef MLAN_PACK_START struct _subcmd_hmac_sha1 { + /** output_len */ + t_u16 output_len; + /** number of data blocks */ + t_u16 data_blks_nr; + /** tlv start */ + t_u8 tlv[]; +} MLAN_PACK_END subcmd_hmac_sha1_t, subcmd_hmac_sha256_t, subcmd_sha256_t; + +/** subcmd_rijndael, used by rijndael */ +typedef MLAN_PACK_START struct _subcmd_rijndael { + /** output_len */ + t_u16 output_len; + /** sub action code */ + t_u8 sub_action_code; + /** tlv start */ + t_u8 tlv[]; +} MLAN_PACK_END subcmd_rijndael_t; + +/** subcmd_rc4, used by rc4 */ +typedef MLAN_PACK_START struct _subcmd_rc4 { + /** output_len */ + t_u16 output_len; + /** skip bytes */ + t_u16 skip_bytes; + /** tlv start */ + t_u8 tlv[]; +} MLAN_PACK_END subcmd_rc4_t; + +/** subcmd_mrvf_f, used by mrvl_f*/ +typedef MLAN_PACK_START struct _subcmd_mrvf_f { + /** output_len */ + t_u16 output_len; + /** iterations */ + t_u32 iterations; + /** count */ + t_u32 count; + /** tlv start */ + t_u8 tlv[]; +} MLAN_PACK_END subcmd_mrvl_f_t; + +#endif + +#ifdef UAP_SUPPORT +/** action add station */ +#define HostCmd_ACT_ADD_STA 0x1 +/** remove station */ +#define HostCmd_ACT_REMOVE_STA 0x0 +/** HostCmd_DS_ADD_STATION */ +typedef MLAN_PACK_START struct _HostCmd_DS_ADD_STATION { + /** 1 -add, 0 --delete */ + t_u16 action; + /** aid */ + t_u16 aid; + /** peer_mac */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Listen Interval */ + int listen_interval; + /** Capability Info */ + t_u16 cap_info; + /** tlv start */ + t_u8 tlv[]; +} MLAN_PACK_END HostCmd_DS_ADD_STATION; + +/** Host Command ID : Add New Station */ +#define HostCmd_CMD_ADD_NEW_STATION 0x025f +/** TLV id: station flag */ +#define TLV_TYPE_UAP_STA_FLAGS (PROPRIETARY_TLV_BASE_ID + 313) +/**MrvlIEtypes_Sta_Flag_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_StaFlag_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** station flag */ + t_u32 sta_flags; +} MLAN_PACK_END MrvlIEtypes_StaFlag_t; +#endif + +/** Host Command ID : _HostCmd_DS_BAND_STEERING */ +typedef MLAN_PACK_START struct _HostCmd_DS_BAND_STEERING { + /** ACT_GET/ACT_SET */ + t_u8 action; + /** State */ + t_u8 state; + /** probe requests to be blocked on 2g */ + t_u8 block_2g_prb_req; + /** limit the btm request sent to STA at */ + t_u8 max_btm_req_allowed; +} MLAN_PACK_END HostCmd_DS_BAND_STEERING; + +/** HostCmd_CMD_RX_ABORT_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_CMD_RX_ABORT_CFG { + /** Action */ + t_u16 action; + /** Enable/disable rx abort on weak pkt rssi */ + t_u8 enable; + /** rx weak rssi pkt threshold */ + t_s8 rssi_threshold; +} MLAN_PACK_END HostCmd_DS_CMD_RX_ABORT_CFG; +/** HostCmd_CMD_RX_ABORT_CFG_EXT */ +typedef MLAN_PACK_START struct _HostCmd_DS_CMD_RX_ABORT_CFG_EXT { + /** Action */ + t_u16 action; + /** Enable/disable dyn rx abort on weak pkt rssi */ + t_u8 enable; + /** specify rssi margin */ + t_s8 rssi_margin; + /** specify ceil rssi threshold */ + t_s8 ceil_rssi_threshold; +} MLAN_PACK_END HostCmd_DS_CMD_RX_ABORT_CFG_EXT; + +/** HostCmd_CMD_ARB_CONFIG */ +typedef MLAN_PACK_START struct _HostCmd_DS_CMD_ARB_CONFIG { + /** Action */ + t_u16 action; + /** 0-4 */ + t_u32 arb_mode; + /** 1: use FW enhancement, 0: use FW default */ + t_u32 reserved; +} MLAN_PACK_END HostCmd_DS_CMD_ARB_CONFIG; + +/** HostCmd_DS_CMD_TX_AMPDU_PROT_MODE */ +typedef MLAN_PACK_START struct _HostCmd_DS_CMD_TX_AMPDU_PROT_MODE { + /** Action */ + t_u16 action; + /** Prot mode */ + t_u16 mode; +} MLAN_PACK_END HostCmd_DS_CMD_TX_AMPDU_PROT_MODE; + +/** HostCmd_DS_CMD_DOT11MC_UNASSOC_FTM_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_CMD_DOT11MC_UNASSOC_FTM_CFG { + /** Action */ + t_u16 action; + /** Cfg state */ + t_u16 state; +} MLAN_PACK_END HostCmd_DS_CMD_DOT11MC_UNASSOC_FTM_CFG; + +/** HostCmd_CMD_RATE_ADAPT_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_CMD_RATE_ADAPT_CFG { + /** Action */ + t_u16 action; + /** SR Rateadapt*/ + t_u8 sr_rateadapt; + /** set low threshold */ + t_u8 ra_low_thresh; + /** set high threshold */ + t_u8 ra_high_thresh; + /** set interval */ + t_u16 ra_interval; +} MLAN_PACK_END HostCmd_DS_CMD_RATE_ADAPT_CFG; + +/** HostCmd_CMD_CCK_DESENSE_CFG */ +typedef MLAN_PACK_START struct _HostCmd_DS_CMD_CCK_DESENSE_CFG { + /** Action */ + t_u16 action; + /** cck desense mode: 0:disable 1:normal 2:dynamic */ + t_u16 mode; + /** specify rssi margin */ + t_s8 margin; + /** specify ceil rssi threshold */ + t_s8 ceil_thresh; + /** cck desense "on" interval count */ + t_u8 num_on_intervals; + /** cck desense "off" interval count */ + t_u8 num_off_intervals; +} MLAN_PACK_END HostCmd_DS_CMD_CCK_DESENSE_CFG; + +/** 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; +#ifdef SDIO + HostCmd_DS_SDIO_SP_RX_AGGR_CFG sdio_rx_aggr; +#endif + /** 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; + /** Get link layer statistic */ + HostCmd_DS_802_11_LINK_STATISTIC get_link_statistic; + /** RSSI information */ + HostCmd_DS_802_11_RSSI_INFO_EXT rssi_info_ext; + /** RSSI information */ + HostCmd_DS_802_11_RSSI_INFO rssi_info; + /** RSSI information response */ + HostCmd_DS_802_11_RSSI_INFO_RSP rssi_info_rsp; + /** SNMP MIB */ + HostCmd_DS_802_11_SNMP_MIB smib; +#ifdef UAP_SUPPORT + /** UAP SNMP MIB */ + HostCmd_DS_UAP_802_11_SNMP_MIB uap_smib; +#endif + /** Radio control */ + HostCmd_DS_802_11_RADIO_CONTROL radio; + /** RF channel */ + HostCmd_DS_802_11_RF_CHANNEL rf_channel; + /** Tx rate query */ + HostCmd_TX_RATE_QUERY tx_rate; + /** Tx rate configuration */ + HostCmd_DS_TX_RATE_CFG tx_rate_cfg; + /** Tx power configuration */ + HostCmd_DS_TXPWR_CFG txp_cfg; + /** RF Tx power configuration */ + HostCmd_DS_802_11_RF_TX_POWER txp; + + /** RF antenna */ + HostCmd_DS_802_11_RF_ANTENNA antenna; + + /** CW Mode: Tx CW Level control */ + HostCmd_DS_CW_MODE_CTRL cwmode; + /** Enhanced power save command */ + HostCmd_DS_802_11_PS_MODE_ENH psmode_enh; + HostCmd_DS_802_11_HS_CFG_ENH opt_hs_cfg; + /** Scan */ + HostCmd_DS_802_11_SCAN scan; + /** Extended Scan */ + HostCmd_DS_802_11_SCAN_EXT ext_scan; + + /** Mgmt frame subtype mask */ + HostCmd_DS_RX_MGMT_IND rx_mgmt_ind; + /** Scan response */ + HostCmd_DS_802_11_SCAN_RSP scan_resp; + + HostCmd_DS_802_11_BG_SCAN_CONFIG bg_scan_config; + HostCmd_DS_802_11_BG_SCAN_QUERY bg_scan_query; + HostCmd_DS_802_11_BG_SCAN_QUERY_RSP bg_scan_query_resp; + HostCmd_DS_SUBSCRIBE_EVENT subscribe_event; + HostCmd_DS_OTP_USER_DATA otp_user_data; + /** Associate */ + HostCmd_DS_802_11_ASSOCIATE associate; + + /** Associate response */ + HostCmd_DS_802_11_ASSOCIATE_RSP associate_rsp; + /** Deauthenticate */ + HostCmd_DS_802_11_DEAUTHENTICATE deauth; + /** Ad-Hoc start */ + HostCmd_DS_802_11_AD_HOC_START adhoc_start; + /** Ad-Hoc start result */ + HostCmd_DS_802_11_AD_HOC_START_RESULT adhoc_start_result; + /** Ad-Hoc join result */ + HostCmd_DS_802_11_AD_HOC_JOIN_RESULT adhoc_join_result; + /** Ad-Hoc join */ + HostCmd_DS_802_11_AD_HOC_JOIN adhoc_join; + /** Domain information */ + HostCmd_DS_802_11D_DOMAIN_INFO domain_info; + /** Domain information response */ + HostCmd_DS_802_11D_DOMAIN_INFO_RSP domain_info_resp; + HostCmd_DS_802_11_TPC_ADAPT_REQ tpc_req; + HostCmd_DS_802_11_TPC_INFO tpc_info; + HostCmd_DS_802_11_CHAN_SW_ANN chan_sw_ann; + HostCmd_DS_CHAN_RPT_REQ chan_rpt_req; + HostCmd_DS_MEASUREMENT_REQUEST meas_req; + HostCmd_DS_MEASUREMENT_REPORT meas_rpt; + /** Add BA request */ + HostCmd_DS_11N_ADDBA_REQ add_ba_req; + /** Add BA response */ + HostCmd_DS_11N_ADDBA_RSP add_ba_rsp; + /** Delete BA entry */ + HostCmd_DS_11N_DELBA del_ba; + /** Tx buffer configuration */ + HostCmd_DS_TXBUF_CFG tx_buf; + /** AMSDU Aggr Ctrl configuration */ + HostCmd_DS_AMSDU_AGGR_CTRL amsdu_aggr_ctrl; + /** 11n configuration */ + HostCmd_DS_11N_CFG htcfg; + /** reject addba req conditions configuration */ + HostCmd_DS_REJECT_ADDBA_REQ rejectaddbareq; + /* RANDYTODO need add more */ + /** HostCmd_DS_11AC_CFG */ + HostCmd_DS_11AC_CFG vhtcfg; + /** HostCmd_DS_11ACTXBUF_CFG*/ + HostCmd_DS_11ACTXBUF_CFG ac_tx_buf; + /** 11n configuration */ + HostCmd_DS_TX_BF_CFG tx_bf_cfg; + /** WMM status get */ + HostCmd_DS_WMM_GET_STATUS get_wmm_status; + /** WMM ADDTS */ + HostCmd_DS_WMM_ADDTS_REQ add_ts; + /** WMM DELTS */ + HostCmd_DS_WMM_DELTS_REQ del_ts; + /** WMM set/get queue config */ + HostCmd_DS_WMM_QUEUE_CONFIG queue_config; + /** WMM param config*/ + HostCmd_DS_WMM_PARAM_CONFIG param_config; + /** WMM on/of/get queue statistics */ + HostCmd_DS_WMM_QUEUE_STATS queue_stats; + /** WMM get traffic stream status */ + HostCmd_DS_WMM_TS_STATUS ts_status; + /** Key material */ + HostCmd_DS_802_11_KEY_MATERIAL key_material; + /** GTK Rekey parameters */ + HostCmd_DS_GTK_REKEY_PARAMS gtk_rekey; + /** E-Supplicant PSK */ + HostCmd_DS_802_11_SUPPLICANT_PMK esupplicant_psk; + /** E-Supplicant profile */ + HostCmd_DS_802_11_SUPPLICANT_PROFILE esupplicant_profile; + /** Extended version */ + HostCmd_DS_VERSION_EXT verext; + /** Adhoc Coalescing */ + HostCmd_DS_802_11_IBSS_STATUS ibss_coalescing; + /** Mgmt IE list configuration */ + HostCmd_DS_MGMT_IE_LIST_CFG mgmt_ie_list; + /** System clock configuration */ + HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG sys_clock_cfg; + /** MAC register access */ + HostCmd_DS_MAC_REG_ACCESS mac_reg; + /** BBP register access */ + HostCmd_DS_BBP_REG_ACCESS bbp_reg; + /** RF register access */ + HostCmd_DS_RF_REG_ACCESS rf_reg; + /** EEPROM register access */ + HostCmd_DS_802_11_EEPROM_ACCESS eeprom; + /** Memory access */ + HostCmd_DS_MEM_ACCESS mem; + /** Target device access */ + HostCmd_DS_TARGET_ACCESS target; + /** BCA register access */ + HostCmd_DS_BCA_REG_ACCESS bca_reg; + /** Inactivity timeout extend */ + HostCmd_DS_INACTIVITY_TIMEOUT_EXT inactivity_to; +#ifdef UAP_SUPPORT + HostCmd_DS_SYS_CONFIG sys_config; + HostCmd_DS_SYS_INFO sys_info; + HostCmd_DS_STA_DEAUTH sta_deauth; + HostCmd_DS_STA_LIST sta_list; + HostCmd_DS_POWER_MGMT_EXT pm_cfg; + HostCmd_DS_REPORT_MIC report_mic; + HostCmd_DS_UAP_OPER_CTRL uap_oper_ctrl; +#endif /* UAP_SUPPORT */ + HostCmd_DS_TX_RX_HISTOGRAM tx_rx_histogram; + + /** Sleep period command */ + HostCmd_DS_802_11_SLEEP_PERIOD sleep_pd; + /** Sleep params command */ + HostCmd_DS_802_11_SLEEP_PARAMS sleep_param; + +#ifdef SDIO + /** SDIO GPIO interrupt config command */ + HostCmd_DS_SDIO_GPIO_INT_CONFIG sdio_gpio_int; + HostCmd_DS_SDIO_PULL_CTRL sdio_pull_ctl; +#endif + HostCmd_DS_SET_BSS_MODE bss_mode; + HostCmd_DS_CMD_TX_DATA_PAUSE tx_data_pause; +#if defined(PCIE) +#if defined(PCIE8997) || defined(PCIE8897) + HostCmd_DS_PCIE_HOST_BUF_DETAILS pcie_host_spec; +#endif +#endif + HostCmd_DS_REMAIN_ON_CHANNEL remain_on_chan; +#ifdef WIFI_DIRECT_SUPPORT + HostCmd_DS_WIFI_DIRECT_MODE wifi_direct_mode; + HostCmd_DS_WIFI_DIRECT_PARAM_CONFIG p2p_params_config; +#endif + HostCmd_DS_COALESCE_CONFIG coalesce_config; + HostCmd_DS_HS_WAKEUP_REASON hs_wakeup_reason; + HostCmd_DS_PACKET_AGGR_CTRL aggr_ctrl; +#ifdef USB + HostCmd_DS_PACKET_AGGR_OVER_HOST_INTERFACE packet_aggr; +#endif + HostCmd_CONFIG_LOW_PWR_MODE low_pwr_mode_cfg; + HostCmd_DS_TSF tsf; + HostCmd_DS_DFS_REPEATER_MODE dfs_repeater; +#ifdef RX_PACKET_COALESCE + HostCmd_DS_RX_PKT_COAL_CFG rx_pkt_coal_cfg; +#endif + HostCmd_DS_EAPOL_PKT eapol_pkt; + HostCmd_DS_SENSOR_TEMP temp_sensor; + HostCMD_DS_APCMD_ACS_SCAN acs_scan; + HostCmd_DS_MIMO_SWITCH mimo_switch; +#ifdef STA_SUPPORT + HostCmd_DS_STA_CONFIGURE sta_cfg; +#endif + /** GPIO Independent reset configure */ + HostCmd_DS_INDEPENDENT_RESET_CFG ind_rst_cfg; + HostCmd_DS_802_11_PS_INACTIVITY_TIMEOUT ps_inact_tmo; + HostCmd_DS_CHAN_REGION_CFG reg_cfg; + HostCmd_DS_AUTO_TX auto_tx; + HostCmd_DS_DYN_BW dyn_bw; + HostCmd_DS_802_11_ROBUSTCOEX robustcoexparams; + HostCmd_DS_DMCS_CFG dmcs; +#if defined(PCIE) + HostCmd_DS_SSU_CFG ssu_params; +#endif + /** boot sleep configure */ + HostCmd_DS_BOOT_SLEEP boot_sleep; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + /** crypto cmd */ + HostCmd_DS_CRYPTO crypto_cmd; +#endif +#ifdef UAP_SUPPORT + /** Add station cmd */ + HostCmd_DS_ADD_STATION sta_info; +#endif + /** HostCmd_DS_11AX_CFG */ + HostCmd_DS_11AX_CFG axcfg; + /** HostCmd_DS_11AX_CMD_CFG */ + HostCmd_DS_11AX_CMD_CFG axcmd; + HostCmd_DS_RANGE_EXT range_ext; + /** HostCmd_DS_TWT_CFG */ + HostCmd_DS_TWT_CFG twtcfg; + + HostCmd_DS_CMD_RX_ABORT_CFG rx_abort_cfg; + HostCmd_DS_CMD_RX_ABORT_CFG_EXT rx_abort_cfg_ext; + HostCmd_DS_CMD_TX_AMPDU_PROT_MODE tx_ampdu_prot_mode; + HostCmd_DS_CMD_RATE_ADAPT_CFG rate_adapt_cfg; + HostCmd_DS_CMD_CCK_DESENSE_CFG cck_desense_cfg; + /** trpc_config */ + HostCmd_DS_CHANNEL_TRPC_CONFIG ch_trpc_config; + HostCmd_DS_LOW_POWER_MODE_CFG lpm_cfg; + HostCmd_DS_BAND_STEERING band_steer_info; + HostCmd_DS_BEACON_STUCK_CFG beacon_stuck_cfg; + struct mfg_cmd_generic_cfg mfg_generic_cfg; + struct mfg_cmd_tx_cont mfg_tx_cont; + struct mfg_cmd_tx_frame2 mfg_tx_frame2; + HostCmd_DS_CMD_ARB_CONFIG arb_cfg; + HostCmd_DS_CMD_DOT11MC_UNASSOC_FTM_CFG dot11mc_unassoc_ftm_cfg; + } params; +} MLAN_PACK_END HostCmd_DS_COMMAND, *pHostCmd_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_u32 hdr; + /** 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; + +/** req host side download vdll block */ +#define VDLL_IND_TYPE_REQ 0 +/** notify vdll start offset in firmware image */ +#define VDLL_IND_TYPE_OFFSET 1 + +/** vdll indicate event structure */ +typedef MLAN_PACK_START struct _vdll_ind { + /*VDLL ind type*/ + t_u16 type; + /*reserved*/ + t_u16 reserved; + /*indicate the offset downloaded so far*/ + t_u32 offset; + /*VDLL block size*/ + t_u16 block_len; +} MLAN_PACK_END vdll_ind, *pvdll_ind; +#ifdef PRAGMA_PACK +#pragma pack(pop) +#endif + +#endif /* !_MLAN_FW_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_ieee.h b/mxm_wifiex/wlan_src/mlan/mlan_ieee.h new file mode 100644 index 0000000..03b5fd4 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_ieee.h @@ -0,0 +1,1888 @@ +/** @file mlan_ieee.h + * + * @brief This file contains IEEE information element related + * definitions used in MLAN and MOAL module. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/****************************************************** +Change log: + 11/03/2008: initial version +******************************************************/ + +#ifndef _MLAN_IEEE_H_ +#define _MLAN_IEEE_H_ + +/** FIX IES size in beacon buffer */ +#define WLAN_802_11_FIXED_IE_SIZE 12 +/** WLAN supported rates */ +#define WLAN_SUPPORTED_RATES 14 + +/** WLAN supported rates extension */ +#define WLAN_SUPPORTED_RATES_EXT 60 + +/** Enumeration definition*/ +/** WLAN_802_11_NETWORK_TYPE */ +typedef enum _WLAN_802_11_NETWORK_TYPE { + Wlan802_11FH, + Wlan802_11DS, + /* Defined as upper bound*/ + Wlan802_11NetworkTypeMax +} WLAN_802_11_NETWORK_TYPE; + +#ifdef BIG_ENDIAN_SUPPORT +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x3000 +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc)&0xF000) >> 12) +#else +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x000C +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc)&0x00F0) >> 4) +#endif + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/* Reason codes */ +#define IEEE_80211_REASONCODE_UNSPECIFIED 1 + +typedef enum _IEEEtypes_Ext_ElementId_e { + HE_CAPABILITY = 35, + HE_OPERATION = 36 +} IEEEtypes_Ext_ElementId_e; + +/** IEEE Type definitions */ +typedef MLAN_PACK_START enum _IEEEtypes_ElementId_e { + SSID = 0, + SUPPORTED_RATES = 1, + + FH_PARAM_SET = 2, + DS_PARAM_SET = 3, + CF_PARAM_SET = 4, + + IBSS_PARAM_SET = 6, + COUNTRY_INFO = 7, + POWER_CONSTRAINT = 32, + POWER_CAPABILITY = 33, + TPC_REQUEST = 34, + TPC_REPORT = 35, + CHANNEL_SWITCH_ANN = 37, + EXTEND_CHANNEL_SWITCH_ANN = 60, + QUIET = 40, + IBSS_DFS = 41, + SUPPORTED_CHANNELS = 36, + REGULATORY_CLASS = 59, + HT_CAPABILITY = 45, + QOS_INFO = 46, + HT_OPERATION = 61, + MULTI_BSSID = 71, + BSSCO_2040 = 72, + OVERLAPBSSSCANPARAM = 74, + NONTX_BSSID_CAP = 83, + MBSSID_INDEX = 85, + EXT_CAPABILITY = 127, + /*IEEE802.11r*/ + MOBILITY_DOMAIN = 54, + FAST_BSS_TRANSITION = 55, + TIMEOUT_INTERVAL = 56, + RIC = 57, + QOS_MAPPING = 110, + VHT_CAPABILITY = 191, + VHT_OPERATION = 192, + EXT_BSS_LOAD = 193, + BW_CHANNEL_SWITCH = 194, + VHT_TX_POWER_ENV = 195, + EXT_POWER_CONSTR = 196, + AID_INFO = 197, + QUIET_CHAN = 198, + OPER_MODE_NTF = 199, + + ERP_INFO = 42, + + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + WMM_IE = VENDOR_SPECIFIC_221, + + WPS_IE = VENDOR_SPECIFIC_221, + /* WPA */ + WPA_IE = VENDOR_SPECIFIC_221, + /* WPA2 */ + RSN_IE = 48, + VS_IE = VENDOR_SPECIFIC_221, + WAPI_IE = 68, + FRAGMENT = 242, + EXTENSION = 255 +} MLAN_PACK_END IEEEtypes_ElementId_e; + +/** IEEE IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_Header_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; +} MLAN_PACK_END IEEEtypes_Header_t, *pIEEEtypes_Header_t; + +/** Vendor specific IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorHeader_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** OUI */ + t_u8 oui[3]; + /** OUI type */ + t_u8 oui_type; + /** OUI subtype */ + t_u8 oui_subtype; + /** Version */ + t_u8 version; +} MLAN_PACK_END IEEEtypes_VendorHeader_t, *pIEEEtypes_VendorHeader_t; + +/** Vendor specific IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorSpecific_t { + /** Vendor specific IE header */ + IEEEtypes_VendorHeader_t vend_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_VendorHeader_t)]; +} MLAN_PACK_END IEEEtypes_VendorSpecific_t, *pIEEEtypes_VendorSpecific_t; + +/** IEEE IE */ +typedef MLAN_PACK_START struct _IEEEtypes_Generic_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_Header_t)]; +} MLAN_PACK_END IEEEtypes_Generic_t, *pIEEEtypes_Generic_t; + +/**ft capability policy*/ +typedef MLAN_PACK_START struct _IEEEtypes_FtCapPolicy_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved : 6; + /** RIC support */ + t_u8 ric : 1; + /** FT over the DS capable */ + t_u8 ft_over_ds : 1; +#else + /** FT over the DS capable */ + t_u8 ft_over_ds : 1; + /** RIC support */ + t_u8 ric : 1; + /** Reserved */ + t_u8 reserved : 6; +#endif +} MLAN_PACK_END IEEEtypes_FtCapPolicy_t; + +/** Mobility domain IE */ +typedef MLAN_PACK_START struct _IEEEtypes_MobilityDomain_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Mobility Domain ID */ + t_u16 mdid; + /** FT Capability policy */ + t_u8 ft_cap; +} MLAN_PACK_END IEEEtypes_MobilityDomain_t; + +/**FT MIC Control*/ +typedef MLAN_PACK_START struct _IEEEtypes_FT_MICControl_t { + /** reserved */ + t_u8 reserved; + /** element count */ + t_u8 element_count; +} MLAN_PACK_END IEEEtypes_FT_MICControl_t; + +/** FTIE MIC LEN */ +#define FTIE_MIC_LEN 16 + +/**FT IE*/ +typedef MLAN_PACK_START struct _IEEEtypes_FastBssTransElement_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** mic control */ + IEEEtypes_FT_MICControl_t mic_control; + /** mic */ + t_u8 mic[FTIE_MIC_LEN]; + /** ANonce */ + t_u8 a_nonce[32]; + /** SNonce */ + t_u8 s_nonce[32]; + /** sub element */ + t_u8 sub_element[1]; +} MLAN_PACK_END IEEEtypes_FastBssTransElement_t; + +/*Category for FT*/ +#define FT_CATEGORY 6 +/** FT ACTION request */ +#define FT_ACTION_REQUEST 1 +/** FT ACTION response */ +#define FT_ACTION_RESPONSE 2 + +/*FT response and FT ack*/ +typedef MLAN_PACK_START struct { + /** category */ + t_u8 category; + /** action */ + t_u8 action; + /** sta address */ + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + /** target ap address */ + t_u8 target_ap_addr[MLAN_MAC_ADDR_LENGTH]; + /** status code */ + t_u16 status_code; + /** varible */ + t_u8 variable[]; +} MLAN_PACK_END IEEEtypes_Ft_action_response; + +/**FT request */ +typedef MLAN_PACK_START struct { + /** category */ + t_u8 category; + /** action */ + t_u8 action; + /** sta address */ + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + /** target ap address */ + t_u8 target_ap_addr[MLAN_MAC_ADDR_LENGTH]; + /** varible */ + t_u8 variable[]; +} MLAN_PACK_END IEEEtypes_Ft_action_request; + +/** auth frame body*/ +typedef MLAN_PACK_START struct { + /** auth alg */ + t_u16 auth_alg; + /** auth transaction */ + t_u16 auth_transaction; + /** status code */ + t_u16 status_code; + /** variable */ + t_u8 variable[]; +} MLAN_PACK_END IEEEtypes_Auth_framebody; + +/** associate request frame */ +typedef MLAN_PACK_START struct { + t_u16 capab_info; + t_u16 listen_interval; + /** followed by SSID and Supported rates */ + t_u8 variablep[]; +} MLAN_PACK_END IEEEtypes_assoc_req; + +/*Mgmt frame*/ +typedef MLAN_PACK_START struct { + /** frame control */ + t_u16 frame_control; + /** duration */ + t_u16 duration; + /** dest address */ + t_u8 da[MLAN_MAC_ADDR_LENGTH]; + /** source address */ + t_u8 sa[MLAN_MAC_ADDR_LENGTH]; + /** bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** seq control */ + t_u16 seq_ctrl; + /** address 4 */ + t_u8 addr4[MLAN_MAC_ADDR_LENGTH]; + union { + IEEEtypes_Auth_framebody auth; + IEEEtypes_assoc_req assoc_req; + IEEEtypes_Ft_action_response ft_resp; + IEEEtypes_Ft_action_request ft_req; + } u; +} MLAN_PACK_END IEEE80211_MGMT; + +/** TLV header */ +typedef MLAN_PACK_START struct _TLV_Generic_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; +} MLAN_PACK_END TLV_Generic_t, *pTLV_Generic_t; + +/** Capability information mask */ +#define CAPINFO_MASK (~(MBIT(15) | MBIT(14) | MBIT(11) | MBIT(9))) + +/** Capability Bit Map*/ +#ifdef BIG_ENDIAN_SUPPORT +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t { + t_u8 rsrvd1 : 2; + t_u8 dsss_ofdm : 1; + t_u8 radio_measurement : 1; + t_u8 rsvrd2 : 1; + t_u8 short_slot_time : 1; + t_u8 rsrvd3 : 1; + t_u8 spectrum_mgmt : 1; + t_u8 chan_agility : 1; + t_u8 pbcc : 1; + t_u8 short_preamble : 1; + t_u8 privacy : 1; + t_u8 cf_poll_rqst : 1; + t_u8 cf_pollable : 1; + t_u8 ibss : 1; + t_u8 ess : 1; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#else +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t { + /** Capability Bit Map : ESS */ + t_u8 ess : 1; + /** Capability Bit Map : IBSS */ + t_u8 ibss : 1; + /** Capability Bit Map : CF pollable */ + t_u8 cf_pollable : 1; + /** Capability Bit Map : CF poll request */ + t_u8 cf_poll_rqst : 1; + /** Capability Bit Map : privacy */ + t_u8 privacy : 1; + /** Capability Bit Map : Short preamble */ + t_u8 short_preamble : 1; + /** Capability Bit Map : PBCC */ + t_u8 pbcc : 1; + /** Capability Bit Map : Channel agility */ + t_u8 chan_agility : 1; + /** Capability Bit Map : Spectrum management */ + t_u8 spectrum_mgmt : 1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd3 : 1; + /** Capability Bit Map : Short slot time */ + t_u8 short_slot_time : 1; + /** Capability Bit Map : APSD */ + t_u8 Apsd : 1; + /** Capability Bit Map : Reserved */ + t_u8 rsvrd2 : 1; + /** Capability Bit Map : DSS OFDM */ + t_u8 dsss_ofdm : 1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd1 : 2; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#endif /* BIG_ENDIAN_SUPPORT */ + +/** IEEEtypes_Ssid_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Ssid_t { + /** SSID: Element ID */ + t_u8 element_id; + /** SSID : Length */ + t_u8 len; + /** ssid */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; +} MLAN_PACK_END IEEEtypes_Ssid_t, *pIEEEtypes_Ssid_t; + +/** IEEEtypes_CfParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_CfParamSet_t { + /** CF peremeter : Element ID */ + t_u8 element_id; + /** CF peremeter : Length */ + t_u8 len; + /** CF peremeter : Count */ + t_u8 cfp_cnt; + /** CF peremeter : Period */ + t_u8 cfp_period; + /** CF peremeter : Maximum duration */ + t_u16 cfp_max_duration; + /** CF peremeter : Remaining duration */ + t_u16 cfp_duration_remaining; +} MLAN_PACK_END IEEEtypes_CfParamSet_t, *pIEEEtypes_CfParamSet_t; + +/** IEEEtypes_IbssParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_IbssParamSet_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ATIM window value in milliseconds */ + t_u16 atim_window; +} MLAN_PACK_END IEEEtypes_IbssParamSet_t, *pIEEEtypes_IbssParamSet_t; + +/** IEEEtypes_SsParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_SsParamSet_t { + /** SS parameter : CF parameter set */ + IEEEtypes_CfParamSet_t cf_param_set; + /** SS parameter : IBSS parameter set */ + IEEEtypes_IbssParamSet_t ibss_param_set; +} MLAN_PACK_END IEEEtypes_SsParamSet_t, *pIEEEtypes_SsParamSet_t; + +/** IEEEtypes_FhParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_FhParamSet_t { + /** FH parameter : Element ID */ + t_u8 element_id; + /** FH parameter : Length */ + t_u8 len; + /** FH parameter : Dwell time in milliseconds */ + t_u16 dwell_time; + /** FH parameter : Hop set */ + t_u8 hop_set; + /** FH parameter : Hop pattern */ + t_u8 hop_pattern; + /** FH parameter : Hop index */ + t_u8 hop_index; +} MLAN_PACK_END IEEEtypes_FhParamSet_t, *pIEEEtypes_FhParamSet_t; + +/** IEEEtypes_DsParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_DsParamSet_t { + /** DS parameter : Element ID */ + t_u8 element_id; + /** DS parameter : Length */ + t_u8 len; + /** DS parameter : Current channel */ + t_u8 current_chan; +} MLAN_PACK_END IEEEtypes_DsParamSet_t, *pIEEEtypes_DsParamSet_t; + +/** IEEEtypes_PhyParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_PhyParamSet_t { + /** FH parameter set */ + IEEEtypes_FhParamSet_t fh_param_set; + /** DS parameter set */ + IEEEtypes_DsParamSet_t ds_param_set; +} MLAN_PACK_END IEEEtypes_PhyParamSet_t, *pIEEEtypes_PhyParamSet_t; + +/** IEEEtypes_ERPInfo_t */ +typedef MLAN_PACK_START struct _IEEEtypes_ERPInfo_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ERP flags */ + t_u8 erp_flags; +} MLAN_PACK_END IEEEtypes_ERPInfo_t, *pIEEEtypes_ERPInfo_t; + +/** IEEEtypes_AId_t */ +typedef t_u16 IEEEtypes_AId_t; + +/** IEEEtypes_StatusCode_t */ +typedef t_u16 IEEEtypes_StatusCode_t; + +/** Fixed size in assoc_resp */ +#define ASSOC_RESP_FIXED_SIZE 6 + +/** IEEEtypes_SeqCtl_t */ +typedef MLAN_PACK_START struct _IEEEtypes_SeqCtl_t { + /** Fragment Number */ + t_u16 FragNum : 4; + /** Sequence Number */ + t_u16 SeqNum : 12; +} MLAN_PACK_END IEEEtypes_SeqCtl_t; + +/** IEEEtypes_MgmtHdr_t */ +typedef MLAN_PACK_START struct _IEEEtypes_MgmtHdr_t { + /** FrmCtl*/ + t_u16 FrmCtl; + /** Duration*/ + t_u16 Duration; + /** Destination Addr*/ + t_u8 DestAddr[6]; + /** Source Addr*/ + t_u8 SrcAddr[6]; + /** BSSID */ + t_u8 BssId[6]; + /** IEEEtypes_SeqCtl_t */ + IEEEtypes_SeqCtl_t SeqCtl; +} MLAN_PACK_END IEEEtypes_MgmtHdr_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 +/** AKM: PSK SHA256 */ +#define RSN_AKM_PSK_SHA256 6 + +/** AKM: PSK SHA256 */ +#define RSN_AKM_SAE 8 +/** AKM: PSK SHA256 */ +#define RSN_AKM_OWE 18 + +#if defined(STA_SUPPORT) +/** Pairwise Cipher Suite length */ +#define PAIRWISE_CIPHER_SUITE_LEN 4 +/** AKM Suite length */ +#define AKM_SUITE_LEN 4 +/** MFPC bit in RSN capability */ +#define MFPC_BIT 7 +/** MFPR bit in RSN capability */ +#define MFPR_BIT 6 +/** PMF ORing mask */ +#define PMF_MASK 0x00c0 +#endif + +/** wpa_suite_t */ +typedef MLAN_PACK_START struct _wpa_suite_t { + /** OUI */ + t_u8 oui[3]; + /** tyep */ + t_u8 type; +} MLAN_PACK_END wpa_suite, wpa_suite_mcast_t; + +/** wpa_suite_ucast_t */ +typedef MLAN_PACK_START struct { + /* count */ + t_u16 count; + /** wpa_suite list */ + wpa_suite list[1]; +} MLAN_PACK_END wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t; + +/** IEEEtypes_Rsn_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Rsn_t { + /** Rsn : Element ID */ + t_u8 element_id; + /** Rsn : Length */ + t_u8 len; + /** Rsn : version */ + t_u16 version; + /** Rsn : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Rsn : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Rsn_t, *pIEEEtypes_Rsn_t; + +/** IEEEtypes_Wpa_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Wpa_t { + /** Wpa : Element ID */ + t_u8 element_id; + /** Wpa : Length */ + t_u8 len; + /** Wpa : oui */ + t_u8 oui[4]; + /** version */ + t_u16 version; + /** Wpa : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Wpa : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Wpa_t, *pIEEEtypes_Wpa_t; + +/** Data structure of WMM QoS information */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmQosInfo_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; + /** Reserved */ + t_u8 reserved : 3; + /** Parameter set count */ + t_u8 para_set_count : 4; +#else + /** Parameter set count */ + t_u8 para_set_count : 4; + /** Reserved */ + t_u8 reserved : 3; + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmQosInfo_t, *pIEEEtypes_WmmQosInfo_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAciAifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved : 1; + /** Aci */ + t_u8 aci : 2; + /** Acm */ + t_u8 acm : 1; + /** Aifsn */ + t_u8 aifsn : 4; +#else + /** Aifsn */ + t_u8 aifsn : 4; + /** Acm */ + t_u8 acm : 1; + /** Aci */ + t_u8 aci : 2; + /** Reserved */ + t_u8 reserved : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmAciAifsn_t, *pIEEEtypes_WmmAciAifsn_t; + +/** Data structure of WMM ECW */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmEcw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max : 4; + /** Minimum Ecw */ + t_u8 ecw_min : 4; +#else + /** Minimum Ecw */ + t_u8 ecw_min : 4; + /** Maximum Ecw */ + t_u8 ecw_max : 4; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmEcw_t, *pIEEEtypes_WmmEcw_t; + +/** Data structure of WMM AC parameters */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAcParameters_t { + IEEEtypes_WmmAciAifsn_t aci_aifsn; /**< AciAifSn */ + IEEEtypes_WmmEcw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} MLAN_PACK_END IEEEtypes_WmmAcParameters_t, *pIEEEtypes_WmmAcParameters_t; + +/** Data structure of WMM Info IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmInfo_t { + /** + * WMM Info IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [7] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [0] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + +} MLAN_PACK_END IEEEtypes_WmmInfo_t, *pIEEEtypes_WmmInfo_t; + +/** Data structure of WMM parameter IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmParameter_t { + /** + * WMM Parameter IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [24] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [1] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + /** Reserved */ + t_u8 reserved; + + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + IEEEtypes_WmmAcParameters_t ac_params[MAX_AC_QUEUES]; +} MLAN_PACK_END IEEEtypes_WmmParameter_t, *pIEEEtypes_WmmParameter_t; + +/** Enumerator for TSPEC direction */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_Direction_e { + + TSPEC_DIR_UPLINK = 0, + TSPEC_DIR_DOWNLINK = 1, + /* 2 is a reserved value */ + TSPEC_DIR_BIDIRECT = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_Direction_e; + +/** Enumerator for TSPEC PSB */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_PSB_e { + + TSPEC_PSB_LEGACY = 0, + TSPEC_PSB_TRIG = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_PSB_e; + +/** Enumerator for TSPEC Ack Policy */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e { + + TSPEC_ACKPOLICY_NORMAL = 0, + TSPEC_ACKPOLICY_NOACK = 1, + /* 2 is reserved */ + TSPEC_ACKPOLICY_BLOCKACK = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e; + +/** Enumerator for TSPEC Trafffice type */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e { + + TSPEC_TRAFFIC_APERIODIC = 0, + TSPEC_TRAFFIC_PERIODIC = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e; + +/** Data structure of WMM TSPEC information */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u8 Reserved17_23 : 7; /* ! Reserved */ + t_u8 Schedule : 1; + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy : 2; + t_u8 UserPri : 3; /* ! 802.1d User Priority */ + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior : 1; /* ! + Legacy/Trigg + */ + t_u8 Aggregation : 1; /* ! Reserved */ + t_u8 AccessPolicy2 : 1; /* ! */ + t_u8 AccessPolicy1 : 1; /* ! */ + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction : 2; + t_u8 TID : 4; /* ! Unique identifier */ + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType : 1; +#else + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType : 1; + t_u8 TID : 4; /* ! Unique identifier */ + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction : 2; + t_u8 AccessPolicy1 : 1; /* ! */ + t_u8 AccessPolicy2 : 1; /* ! */ + t_u8 Aggregation : 1; /* ! Reserved */ + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior : 1; /* ! + Legacy/Trigg + */ + t_u8 UserPri : 3; /* ! 802.1d User Priority */ + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy : 2; + t_u8 Schedule : 1; + t_u8 Reserved17_23 : 7; /* ! Reserved */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_t; + +/** Data structure of WMM TSPEC Nominal Size */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Fixed : 1; /* ! 1: Fixed size given in Size, 0: Var, size is + nominal */ + t_u16 Size : 15; /* ! Nominal size in octets */ +#else + t_u16 Size : 15; /* ! Nominal size in octets */ + t_u16 Fixed : 1; /* ! 1: Fixed size given in Size, 0: Var, size is + nominal */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_NomMSDUSize_t; + +/** Data structure of WMM TSPEC SBWA */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Whole : 3; /* ! Whole portion */ + t_u16 Fractional : 13; /* ! Fractional portion */ +#else + t_u16 Fractional : 13; /* ! Fractional portion */ + t_u16 Whole : 3; /* ! Whole portion */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_SBWA; + +/** Data structure of WMM TSPEC Body */ +typedef MLAN_PACK_START struct { + 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; + +#define MAX_DSCP_EXCEPTION_NUM 21 +/** DSCP Range */ +typedef struct MLAN_PACK_START _DSCP_Exception_t { + /* DSCP value 0 to 63 or ff */ + t_u8 dscp_value; + /* user priority 0-7*/ + t_u8 user_priority; +} MLAN_PACK_END DSCP_Exception_t, *pDSCP_Exception_t; + +/** DSCP Range */ +typedef struct MLAN_PACK_START _DSCP_Range_t { + /* DSCP low value */ + t_u8 dscp_low_value; + /* DSCP high value */ + t_u8 dscp_high_value; +} MLAN_PACK_END DSCP_Range_t, *pDSCP_Range_t; + +/** Overlapping BSS Scan Parameters Data */ +typedef struct MLAN_PACK_START _OverlapBSSScanParam_t { + /** OBSS Scan Passive Dwell in milliseconds */ + t_u16 obss_scan_passive_dwell; + /** OBSS Scan Active Dwell in milliseconds */ + t_u16 obss_scan_active_dwell; + /** BSS Channel Width Trigger Scan Interval in seconds */ + t_u16 bss_chan_width_trigger_scan_int; + /** OBSS Scan Passive Total Per Channel */ + t_u16 obss_scan_passive_total; + /** OBSS Scan Active Total Per Channel */ + t_u16 obss_scan_active_total; + /** BSS Width Channel Transition Delay Factor */ + t_u16 bss_width_chan_trans_delay; + /** OBSS Scan Activity Threshold */ + t_u16 obss_scan_active_threshold; +} MLAN_PACK_END OBSSScanParam_t, *pOBSSScanParam_t; + +/** HT Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTCap struct */ + HTCap_t ht_cap; +} MLAN_PACK_END IEEEtypes_HTCap_t, *pIEEEtypes_HTCap_t; + +/** HT Information IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTInfo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTInfo struct */ + HTInfo_t ht_info; +} MLAN_PACK_END IEEEtypes_HTInfo_t, *pIEEEtypes_HTInfo_t; + +/** 20/40 BSS Coexistence IE */ +typedef MLAN_PACK_START struct _IEEEtypes_2040BSSCo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** BSSCo2040_t struct */ + BSSCo2040_t bss_co_2040; +} MLAN_PACK_END IEEEtypes_2040BSSCo_t, *pIEEEtypes_2040BSSCo_t; + +/** Extended Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_ExtCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** ExtCap_t struct */ + ExtCap_t ext_cap; +} MLAN_PACK_END IEEEtypes_ExtCap_t, *pIEEEtypes_ExtCap_t; + +/** Overlapping BSS Scan Parameters IE */ +typedef MLAN_PACK_START struct _IEEEtypes_OverlapBSSScanParam_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** OBSSScanParam_t struct */ + OBSSScanParam_t obss_scan_param; +} MLAN_PACK_END IEEEtypes_OverlapBSSScanParam_t, + *pIEEEtypes_OverlapBSSScanParam_t; + +/** VHT MCS rate set field, refer to 802.11ac */ +typedef MLAN_PACK_START struct _VHT_MCS_set { + t_u16 rx_mcs_map; + t_u16 rx_max_rate; /* bit 29-31 reserved */ + t_u16 tx_mcs_map; + t_u16 tx_max_rate; /* bit 61-63 reserved */ +} MLAN_PACK_END VHT_MCS_set_t, *pVHT_MCS_set_t; + +/** VHT Capabilities info field, reference 802.11ac D1.4 p89 */ +typedef MLAN_PACK_START struct _VHT_capa { +#if 0 +#ifdef BIG_ENDIAN_SUPPORT + t_u8 mpdu_max_len:2; + t_u8 chan_width:2; + t_u8 rx_LDPC:1; + t_u8 sgi_80:1; + t_u8 sgi_160:1; + t_u8 tx_STBC:1; + t_u8 rx_STBC:3; + t_u8 SU_beamformer_capa:1; + t_u8 SU_beamformee_capa:1; + t_u8 beamformer_ante_num:3; + t_u8 sounding_dim_num:3; + t_u8 MU_beamformer_capa:1; + t_u8 MU_beamformee_capa:1; + t_u8 VHT_TXOP_ps:1; + t_u8 HTC_VHT_capa:1; + t_u8 max_ampdu_len:3; + t_u8 link_apapt_capa:2; + t_u8 reserved_1:4; +#else + t_u8 reserved_1:4; + t_u8 link_apapt_capa:2; + t_u8 max_ampdu_len:3; + t_u8 HTC_VHT_capa:1; + t_u8 VHT_TXOP_ps:1; + t_u8 MU_beamformee_capa:1; + t_u8 MU_beamformer_capa:1; + t_u8 sounding_dim_num:3; + t_u8 beamformer_ante_num:3; + t_u8 SU_beamformee_capa:1; + t_u8 SU_beamformer_capa:1; + t_u8 rx_STBC:3; + t_u8 tx_STBC:1; + t_u8 sgi_160:1; + t_u8 sgi_80:1; + t_u8 rx_LDPC:1; + t_u8 chan_width:2; + t_u8 mpdu_max_len:2; +#endif /* BIG_ENDIAN_SUPPORT */ +#endif + t_u32 vht_cap_info; + VHT_MCS_set_t mcs_sets; +} MLAN_PACK_END VHT_capa_t, *pVHT_capa_t; + +/** VHT Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VHTCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + VHT_capa_t vht_cap; +} MLAN_PACK_END IEEEtypes_VHTCap_t, *pIEEEtypes_VHTCap_t; + +#define VHT_CAP_CHWD_80MHZ 0 +#define VHT_CAP_CHWD_160MHZ 1 +#define VHT_CAP_CHWD_80_80MHZ 2 + +/** VHT Operations IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VHTOprat_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 chan_width; + t_u8 chan_center_freq_1; + t_u8 chan_center_freq_2; + /** Basic MCS set map, each 2 bits stands for a Nss */ + t_u16 basic_MCS_map; +} MLAN_PACK_END IEEEtypes_VHTOprat_t, *pIEEEtypes_VHTOprat_t; + +#define VHT_OPER_CHWD_20_40MHZ 0 +#define VHT_OPER_CHWD_80MHZ 1 +#define VHT_OPER_CHWD_160MHZ 2 +#define VHT_OPER_CHWD_80_80MHZ 3 + +/** VHT Transmit Power Envelope IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VHTtxpower_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 max_tx_power; + t_u8 chan_center_freq; + t_u8 chan_width; +} MLAN_PACK_END IEEEtypes_VHTtxpower_t, *pIEEEtypes_VHTtxpower_t; + +/** Extended Power Constraint IE */ +typedef MLAN_PACK_START struct _IEEEtypes_ExtPwerCons_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** channel width */ + t_u8 chan_width; + /** local power constraint */ + t_u8 local_power_cons; +} MLAN_PACK_END IEEEtypes_ExtPwerCons_t, *pIEEEtypes_ExtPwerCons_t; + +/** Extended BSS Load IE */ +typedef MLAN_PACK_START struct _IEEEtypes_ExtBSSload_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 MU_MIMO_capa_count; + t_u8 stream_underutilization; + t_u8 VHT40_util; + t_u8 VHT80_util; + t_u8 VHT160_util; +} MLAN_PACK_END IEEEtypes_ExtBSSload_t, *pIEEEtypes_ExtBSSload_t; + +/** Quiet Channel IE */ +typedef MLAN_PACK_START struct _IEEEtypes_QuietChan_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 AP_quiet_mode; + t_u8 quiet_count; + t_u8 quiet_period; + t_u16 quiet_dur; + t_u16 quiet_offset; +} MLAN_PACK_END IEEEtypes_QuietChan_t, *pIEEEtypes_QuietChan_t; + +/** Wide Bandwidth Channel Switch IE */ +typedef MLAN_PACK_START struct _IEEEtypes_BWSwitch_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 new_chan_width; + t_u8 new_chan_center_freq_1; + t_u8 new_chan_center_freq_2; +} MLAN_PACK_END IEEEtypes_BWSwitch_t, *pIEEEtypes_BWSwitch_t; + +/** AID IE */ +typedef MLAN_PACK_START struct _IEEEtypes_AID_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** AID number */ + t_u16 AID; +} MLAN_PACK_END IEEEtypes_AID_t, *pIEEEtypes_AID_t; + +/** Operating Mode Notificaton IE */ +typedef MLAN_PACK_START struct _IEEEtypes_OperModeNtf_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Operating Mode */ + t_u8 oper_mode; +} MLAN_PACK_END IEEEtypes_OperModeNtf_t, *pIEEEtypes_OperModeNtf_t; + +typedef MLAN_PACK_START struct _IEEEtypes_Extension_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Element id extension */ + t_u8 ext_id; + /** payload */ + t_u8 data[]; +} MLAN_PACK_END IEEEtypes_Extension_t, *pIEEEtypes_Extension_t; + +typedef MLAN_PACK_START struct _IEEEtypes_HECap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Element id extension */ + t_u8 ext_id; + /** he mac capability info */ + t_u8 he_mac_cap[6]; + /** he phy capability info */ + t_u8 he_phy_cap[11]; + /** he txrx mcs support , size would be 4 or 8 or 12 */ + t_u8 he_txrx_mcs_support[4]; + /** PPE Thresholds (optional) */ +} MLAN_PACK_END IEEEtypes_HECap_t, *pIEEEtypes_HECap_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; + +/** default channel switch count */ +#define DEF_CHAN_SWITCH_COUNT 5 + +/* 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; + +/** data structure for extended channel switch */ +typedef MLAN_PACK_START struct { + /** IEEE element ID = 60 */ + t_u8 element_id; + /** Element length after id and len, set to 4 */ + t_u8 len; + /** STA should not transmit any frames if 1 */ + t_u8 chan_switch_mode; + /** Operate class # that AP/IBSS is moving to */ + t_u8 new_oper_class; + /** Channel # that AP/IBSS is moving to */ + t_u8 new_channel_num; + /** of TBTTs before channel switch */ + t_u8 chan_switch_count; +} MLAN_PACK_END IEEEtypes_ExtChanSwitchAnn_t; + +/* IEEE Wide Bandwidth Channel Switch Element */ +/** + * 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 wide bandwidth chan + * switch element. + */ +typedef MLAN_PACK_START struct { + /** Generic IE header IEEE Element ID = 194*/ + IEEEtypes_Header_t ieee_hdr; + t_u8 new_channel_width; + t_u8 new_channel_center_freq0; + t_u8 new_channel_center_freq1; +} MLAN_PACK_END IEEEtypes_WideBWChanSwitch_t; + +/* IEEE VHT Transmit Power Envelope Element */ +/** + * Provided in beacons and probe responses. Used to advertise the max + * TX power in sepeate bandwidth and as a sub element of Channel Switch + * Wrapper IE. + */ +typedef MLAN_PACK_START struct { + /** Generic IE header IEEE Element ID = 195*/ + IEEEtypes_Header_t ieee_hdr; + t_u8 tpc_info; /**< Transmit Power Information>*/ + t_u8 local_max_tp_20mhz; /**< Local Maximum Transmit Power for 20 MHZ>*/ + t_u8 local_max_tp_40mhz; /**< Local Maximum Transmit Power for 40 MHZ>*/ + t_u8 local_max_tp_80mhz; /**< Local Maximum Transmit Power for 80 MHZ>*/ +} MLAN_PACK_END IEEEtypes_VhtTpcEnvelope_t; + +/* IEEE Quiet Period Element (7.3.2.23) */ +/** + * Provided in beacons and probe responses. Indicates times during + * which the STA should not be transmitting data. Only starting STAs in + * an IBSS and APs are allowed to originate a quiet element. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 40 */ + t_u8 len; /**< Element length after id and len */ + t_u8 quiet_count; /**< Number of TBTTs until beacon with the quiet + period */ + t_u8 quiet_period; /**< Regular quiet period, # of TBTTS between periods + */ + t_u16 quiet_duration; /**< Duration of the quiet period in TUs */ + t_u16 quiet_offset; /**< Offset in TUs from the TBTT for the quiet + period */ + +} MLAN_PACK_END IEEEtypes_Quiet_t; + +/** +*** @brief Map octet of the basic measurement report (7.3.2.22.1) +**/ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /**< Reserved */ + t_u8 rsvd5_7 : 3; + /**< Channel is unmeasured */ + t_u8 unmeasured : 1; + /**< Radar detected on channel */ + t_u8 radar : 1; + /**< Unidentified signal found on channel */ + t_u8 unidentified_sig : 1; + /**< OFDM preamble detected on channel */ + t_u8 ofdm_preamble : 1; + /**< At least one valid MPDU received on channel */ + t_u8 bss : 1; +#else + /**< At least one valid MPDU received on channel */ + t_u8 bss : 1; + /**< OFDM preamble detected on channel */ + t_u8 ofdm_preamble : 1; + /**< Unidentified signal found on channel */ + t_u8 unidentified_sig : 1; + /**< Radar detected on channel */ + t_u8 radar : 1; + /**< Channel is unmeasured */ + t_u8 unmeasured : 1; + /**< Reserved */ + t_u8 rsvd5_7 : 3; +#endif /* BIG_ENDIAN_SUPPORT */ + +} MLAN_PACK_END MeasRptBasicMap_t; + +/* IEEE DFS Channel Map field (7.3.2.24) */ +/** + * Used to list supported channels and provide a octet "map" field which + * contains a basic measurement report for that channel in the + * IEEEtypes_IBSS_DFS_t element + */ +typedef MLAN_PACK_START struct { + t_u8 channel_number; /**< Channel number */ + MeasRptBasicMap_t rpt_map; /**< Basic measurement report for the channel + */ + +} MLAN_PACK_END IEEEtypes_ChannelMap_t; + +/* IEEE IBSS DFS Element (7.3.2.24) */ +/** + * IBSS DFS element included in ad hoc beacons and probe responses. + * Provides information regarding the IBSS DFS Owner as well as the + * originating STAs supported channels and basic measurement results. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 41 */ + t_u8 len; /**< Element length after id and len */ + t_u8 dfs_owner[MLAN_MAC_ADDR_LENGTH]; /**< DFS Owner STA Address */ + t_u8 dfs_recovery_interval; /**< DFS Recovery time in TBTTs */ + + /** Variable length map field, one Map entry for each supported channel + */ + IEEEtypes_ChannelMap_t channel_map[WLAN_11H_MAX_IBSS_DFS_CHANNELS]; + +} MLAN_PACK_END IEEEtypes_IBSS_DFS_t; + +/* 802.11h BSS information kept for each BSSID received in scan results */ +/** + * IEEE BSS information needed from scan results for later processing in + * join commands + */ +typedef struct { + t_u8 sensed_11h; /**< Capability bit set or 11h IE found in this BSS */ + + IEEEtypes_PowerConstraint_t power_constraint; /**< Power Constraint IE + */ + IEEEtypes_PowerCapability_t power_capability; /**< Power Capability IE + */ + IEEEtypes_TPCReport_t tpc_report; /**< TPC Report IE */ + IEEEtypes_ChanSwitchAnn_t chan_switch_ann; /**< Channel Switch + Announcement IE */ + IEEEtypes_Quiet_t quiet; /**< Quiet IE */ + IEEEtypes_IBSS_DFS_t ibss_dfs; /**< IBSS DFS Element IE */ + +} wlan_11h_bss_info_t; + +/** action code for 20/40 BSS Coexsitence Management frame */ +#define BSS_20_40_COEX 0 + +#ifdef STA_SUPPORT +/** Macro for maximum size of scan response buffer */ +#define MAX_SCAN_RSP_BUF (16 * 1024) + +/** Maximum number of channels that can be sent in user scan config */ +#define WLAN_USER_SCAN_CHAN_MAX 50 +/** Maximum length of SSID list */ +#define MRVDRV_MAX_SSID_LIST_LENGTH 10 + +/** Maximum length of BSSID list */ +#define MAX_BSSID_FILTER_LIST 5 + +/** Scan all the channels in specified band */ +#define BAND_SPECIFIED 0x80 + +/** + * IOCTL SSID List sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Used to specify SSID specific filters as well as SSID pattern matching + * filters for scan result processing in firmware. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_ssid { + /** SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH + 1]; + /** Maximum length of SSID */ + t_u8 max_len; +} MLAN_PACK_END wlan_user_scan_ssid; + +/** + * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Multiple instances of this structure are included in the IOCTL command + * to configure a instance of a scan on the specific channel. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_chan { + /** Channel Number to scan */ + t_u8 chan_number; + /** Radio type: 'B/G' Band = 0, 'A' Band = 1 */ + t_u8 radio_type; + /** Scan type: Active = 1, Passive = 2 */ + t_u8 scan_type; + /** Reserved */ + t_u8 reserved; + /** Scan duration in milliseconds; if 0 default used */ + t_u32 scan_time; +} MLAN_PACK_END wlan_user_scan_chan; + +/** channel statictics */ +typedef MLAN_PACK_START struct _ChanStatistics_t { + /** channle number */ + t_u8 chan_num; + /** band info */ + Band_Config_t bandcfg; + /** flags */ + t_u8 flags; + /** noise */ + t_s8 noise; + /** total network */ + t_u16 total_networks; + /** scan duration */ + t_u16 cca_scan_duration; + /** busy duration */ + t_u16 cca_busy_duration; + /** min rss */ + t_u8 min_rss; + /** max rssi */ + t_u8 max_rss; +} MLAN_PACK_END ChanStatistics_t; + +/** Enhance ext scan type defination */ +typedef enum _MLAN_EXT_SCAN_TYPE { + EXT_SCAN_DEFAULT, + EXT_SCAN_ENHANCE, + EXT_SCAN_CANCEL, +} MLAN_EXT_SCAN_TYPE; + +/** + * Input structure to configure an immediate scan cmd to firmware + * + * Specifies a number of parameters to be used in general for the scan + * as well as a channel list (wlan_user_scan_chan) for each scan period + * desired. + */ +typedef MLAN_PACK_START struct { + /** + * Flag set to keep the previous scan table intact + * + * If set, the scan results will accumulate, replacing any previous + * matched entries for a BSS with the new scan data + */ + t_u8 keep_previous_scan; + /** + * BSS mode to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. Valid settings are: + * + * - MLAN_SCAN_MODE_BSS (infrastructure) + * - MLAN_SCAN_MODE_IBSS (adhoc) + * - MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_mode; + /** + * Configure the number of probe requests for active chan scans + */ + t_u8 num_probes; + /** + * @brief ssid filter flag + */ + t_u8 ssid_filter; + /** + * @brief BSSID filter sent in the firmware command to limit the + * results + */ + t_u8 specific_bssid[MLAN_MAC_ADDR_LENGTH]; + /** + * SSID filter list used in the to limit the scan results + */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** + * Variable number (fixed maximum) of channels to scan up + */ + wlan_user_scan_chan chan_list[WLAN_USER_SCAN_CHAN_MAX]; + /** scan channel gap */ + t_u16 scan_chan_gap; + /** scan type: 0 legacy, 1: enhance scan*/ + t_u8 ext_scan_type; + /** flag to filer only probe response */ + t_u8 proberesp_only; + t_u8 random_mac[MLAN_MAC_ADDR_LENGTH]; + /** Number of BSSIDs to be filtered */ + t_u8 bssid_num; + /** BSSID filter list used in the to limit the scan results */ + mlan_802_11_mac_addr bssid_list[MAX_BSSID_FILTER_LIST]; +} MLAN_PACK_END wlan_user_scan_cfg; + +/** Default scan interval in millisecond*/ +#define DEFAULT_BGSCAN_INTERVAL 30000 + +/** action get all, except pps/uapsd config */ +#define BG_SCAN_ACT_GET 0x0000 +/** action set all, except pps/uapsd config */ +#define BG_SCAN_ACT_SET 0x0001 +/** action get pps/uapsd config */ +#define BG_SCAN_ACT_GET_PPS_UAPSD 0x0100 +/** action set pps/uapsd config */ +#define BG_SCAN_ACT_SET_PPS_UAPSD 0x0101 +/** action set all */ +#define BG_SCAN_ACT_SET_ALL 0xff01 +/** ssid match */ +#define BG_SCAN_SSID_MATCH 0x0001 +/** ssid match and RSSI exceeded */ +#define BG_SCAN_SSID_RSSI_MATCH 0x0004 +/**wait for all channel scan to complete to report scan result*/ +#define BG_SCAN_WAIT_ALL_CHAN_DONE 0x80000000 +/** Maximum number of channels that can be sent in bg scan config */ +#define WLAN_BG_SCAN_CHAN_MAX 38 + +/** Enumeration definition */ +/** EES MODE */ +typedef enum { + /** EES MODE: LOW */ + EES_MODE_LOW = 0, + /** EES MODE: MID */ + EES_MODE_MID, + /** EES MODE: HIGH */ + EES_MODE_HIGH, + /** EES MODE: OFF */ + EES_MODE_OFF, + /** EES MODE: LOOP */ + EES_MODE_LOOP = 15, +} ees_modes; + +/** EES Maximum SSID */ +#define EES_MAX_SSIDS 2 + +/** ees_ssid_config */ +typedef MLAN_PACK_START struct { + /** SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH + 1]; + /** Maximum length of SSID */ + t_u8 max_len; + /** PairCipher */ + t_u8 pair_cipher; + /** GroupCipher */ + t_u8 group_cipher; +} MLAN_PACK_END ees_ssid_config; + +/** + * Input structure to configure bs scan cmd to firmware + */ +typedef MLAN_PACK_START struct { + /** action */ + t_u16 action; + /** enable/disable */ + t_u8 enable; + /** BSS type: + * MLAN_SCAN_MODE_BSS (infrastructure) + * MLAN_SCAN_MODE_IBSS (adhoc) + * MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_type; + /** number of channel scanned during each scan */ + t_u8 chan_per_scan; + /** interval between consecutive scan */ + t_u32 scan_interval; + /** bit 0: ssid match bit 1: ssid match and SNR exceeded + * bit 2: ssid match and RSSI exceeded + * bit 31: wait for all channel scan to complete to report scan result + */ + t_u32 report_condition; + /* Configure the number of probe requests for active chan scans */ + t_u8 num_probes; + /** RSSI threshold */ + t_u8 rssi_threshold; + /** SNR threshold */ + t_u8 snr_threshold; + /** repeat count */ + t_u16 repeat_count; + /** start later flag */ + t_u16 start_later; + /** SSID filter list used in the to limit the scan results */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** Variable number (fixed maximum) of channels to scan up */ + wlan_user_scan_chan chan_list[WLAN_BG_SCAN_CHAN_MAX]; + /** scan channel gap */ + t_u16 scan_chan_gap; + /** Enable EES configuration */ + t_u8 config_ees; + /** EES scan mode */ + t_u16 ees_mode; + /** EES report condition */ + t_u16 report_cond; + /** EES High Period scan interval */ + t_u16 high_period; + /** EES High Period scan count */ + t_u16 high_period_count; + /** EES Medium Period scan interval */ + t_u16 mid_period; + /** EES Medium Period scan count */ + t_u16 mid_period_count; + /** EES Low Period scan interval */ + t_u16 low_period; + /** EES Low Period scan count */ + t_u16 low_period_count; + /** Number of networks in the list */ + t_u8 network_count; + /** Maximum number of connection count */ + t_u8 max_conn_count; + /** Black List Exp */ + t_u8 black_list_exp; + /** Array of ees config struct */ + ees_ssid_config ees_ssid_cfg[EES_MAX_SSIDS]; + t_u8 random_mac[MLAN_MAC_ADDR_LENGTH]; +} 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 load */ + t_u8 chan_load; + /** Channel */ + t_u32 channel; + + /** Freq */ + t_u32 freq; + + /** Beacon period */ + t_u16 beacon_period; + + /** ATIM window */ + t_u32 atim_window; + + /** ERP flags */ + t_u8 erp_flags; + + /** Type of network in use */ + WLAN_802_11_NETWORK_TYPE network_type_use; + + /** Network infrastructure mode */ + t_u32 bss_mode; + + /** Network supported rates */ + WLAN_802_11_RATES supported_rates; + + /** Supported data rates */ + t_u8 data_rates[WLAN_SUPPORTED_RATES]; + + /** Current channel bandwidth + * 0 : 20MHZ + * 1 : 40MHZ + * 2 : 80MHZ + * 3 : 160MHZ + */ + t_u8 curr_bandwidth; + + /** Network band. + * BAND_B(0x01): 'b' band + * BAND_G(0x02): 'g' band + * BAND_A(0X04): 'a' band + */ + t_u16 bss_band; + + /** TSF timestamp from the current firmware TSF */ + t_u64 network_tsf; + + /** TSF value included in the beacon/probe response */ + t_u8 time_stamp[8]; + + /** PHY parameter set */ + IEEEtypes_PhyParamSet_t phy_param_set; + + /** SS parameter set */ + IEEEtypes_SsParamSet_t ss_param_set; + + /** Capability information */ + IEEEtypes_CapInfo_t cap_info; + + /** WMM IE */ + IEEEtypes_WmmParameter_t wmm_ie; + + /** 802.11h BSS information */ + wlan_11h_bss_info_t wlan_11h_bss_info; + + /** Indicate disabling 11n when associate with AP */ + t_u8 disable_11n; + /** 802.11n BSS information */ + /** HT Capabilities IE */ + IEEEtypes_HTCap_t *pht_cap; + /** HT Capabilities Offset */ + t_u16 ht_cap_offset; + /** HT Information IE */ + IEEEtypes_HTInfo_t *pht_info; + /** HT Information Offset */ + t_u16 ht_info_offset; + /** 20/40 BSS Coexistence IE */ + IEEEtypes_2040BSSCo_t *pbss_co_2040; + /** 20/40 BSS Coexistence Offset */ + t_u16 bss_co_2040_offset; + /** Extended Capabilities IE */ + IEEEtypes_ExtCap_t *pext_cap; + /** Extended Capabilities Offset */ + t_u16 ext_cap_offset; + /** Overlapping BSS Scan Parameters IE */ + IEEEtypes_OverlapBSSScanParam_t *poverlap_bss_scan_param; + /** Overlapping BSS Scan Parameters Offset */ + t_u16 overlap_bss_offset; + + /** VHT Capabilities IE */ + IEEEtypes_VHTCap_t *pvht_cap; + /** VHT Capabilities IE offset */ + t_u16 vht_cap_offset; + /** VHT Operations IE */ + IEEEtypes_VHTOprat_t *pvht_oprat; + /** VHT Operations IE offset */ + t_u16 vht_oprat_offset; + /** VHT Transmit Power Envelope IE */ + IEEEtypes_VHTtxpower_t *pvht_txpower; + /** VHT Transmit Power Envelope IE offset */ + t_u16 vht_txpower_offset; + /** Extended Power Constraint IE */ + IEEEtypes_ExtPwerCons_t *pext_pwer; + /** Extended Power Constraint IE offset */ + t_u16 ext_pwer_offset; + /** Extended BSS Load IE */ + IEEEtypes_ExtBSSload_t *pext_bssload; + /** Extended BSS Load IE offset */ + t_u16 ext_bssload_offset; + /** Quiet Channel IE */ + IEEEtypes_QuietChan_t *pquiet_chan; + /** Quiet Channel IE offset */ + t_u16 quiet_chan_offset; + /** Operating Mode Notification IE */ + IEEEtypes_OperModeNtf_t *poper_mode; + /** Operating Mode Notification IE offset */ + t_u16 oper_mode_offset; + /** HE Capability IE */ + IEEEtypes_HECap_t *phe_cap; + /** HE Capability IE offset */ + t_u16 he_cap_offset; + /** HE operation IE */ + IEEEtypes_Extension_t *phe_oprat; + /** HE operation IE offset */ + t_u16 he_oprat_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 + /* Hotspot 2.0 OSEN AKM IE*/ + IEEEtypes_Generic_t *posen_ie; + /** osen IE offset in the beacon buffer */ + t_u16 osen_offset; + /* Mobility domain IE */ + IEEEtypes_MobilityDomain_t *pmd_ie; + /** Mobility domain IE offset in the beacon buffer */ + t_u16 md_offset; + + /** Pointer to the returned scan response */ + t_u8 *pbeacon_buf; + /** Length of the stored scan response */ + t_u32 beacon_buf_size; + /** Max allocated size for updated scan response */ + t_u32 beacon_buf_size_max; + +} BSSDescriptor_t, *pBSSDescriptor_t; + +#endif /* !_MLAN_IEEE_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_init.c b/mxm_wifiex/wlan_src/mlan/mlan_init.c new file mode 100644 index 0000000..e81299d --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_init.c @@ -0,0 +1,1959 @@ +/** @file mlan_init.c + * + * @brief This file contains the initialization for FW + * and HW. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11ac.h" +#include "mlan_11h.h" +#include "mlan_meas.h" +#ifdef SDIO +#include "mlan_sdio.h" +#endif +#ifdef PCIE +#include "mlan_pcie.h" +#endif /* PCIE */ +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) +#include "hostsa_init.h" +#endif +#include "mlan_11ax.h" + +/******************************************************** + Global Variables +********************************************************/ +extern pmlan_operations mlan_ops[]; +/******************************************************* + Local Functions +********************************************************/ + +/** + * @brief This function adds a BSS priority table + * + * @param priv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_add_bsspriotbl(pmlan_private priv) +{ + pmlan_adapter pmadapter = priv->adapter; + mlan_bssprio_node *pbssprio = MNULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + status = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_bssprio_node), + MLAN_MEM_DEF, + (t_u8 **)&pbssprio); + if (status) { + PRINTM(MERROR, "Failed to allocate bsspriotbl\n"); + LEAVE(); + return status; + } + + pbssprio->priv = priv; + + util_init_list((pmlan_linked_list)pbssprio); + + if (!pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur) + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur = + pbssprio; + + util_enqueue_list_tail( + pmadapter->pmoal_handle, + &pmadapter->bssprio_tbl[priv->bss_priority].bssprio_head, + (pmlan_linked_list)pbssprio, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + + LEAVE(); + return status; +} + +/** + * @brief This function deletes the BSS priority table + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + */ +static t_void wlan_delete_bsspriotbl(pmlan_private priv) +{ + int i; + pmlan_adapter pmadapter = priv->adapter; + mlan_bssprio_node *pbssprio_node = MNULL, *ptmp_node = MNULL, + **ppcur = MNULL; + pmlan_list_head phead; + + ENTER(); + + for (i = 0; i < pmadapter->priv_num; ++i) { + phead = &pmadapter->bssprio_tbl[i].bssprio_head; + ppcur = &pmadapter->bssprio_tbl[i].bssprio_cur; + PRINTM(MINFO, + "Delete BSS priority table, index = %d, i = %d, phead = %p, pcur = %p\n", + priv->bss_index, i, phead, *ppcur); + if (*ppcur) { + pbssprio_node = (mlan_bssprio_node *)util_peek_list( + pmadapter->pmoal_handle, phead, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + while (pbssprio_node && + ((pmlan_list_head)pbssprio_node != phead)) { + ptmp_node = pbssprio_node->pnext; + if (pbssprio_node->priv == priv) { + PRINTM(MINFO, + "Delete node, pnode = %p, pnext = %p\n", + pbssprio_node, ptmp_node); + util_unlink_list( + pmadapter->pmoal_handle, phead, + (pmlan_linked_list)pbssprio_node, + pmadapter->callbacks + .moal_spin_lock, + pmadapter->callbacks + .moal_spin_unlock); + pmadapter->callbacks.moal_mfree( + pmadapter->pmoal_handle, + (t_u8 *)pbssprio_node); + } + pbssprio_node = ptmp_node; + } + *ppcur = (mlan_bssprio_node *)phead; + } + } + + LEAVE(); +} + +/** + * @brief The function handles VDLL init + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * + */ +static mlan_status vdll_init(pmlan_adapter pmadapter) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + vdll_dnld_ctrl *ctrl = &pmadapter->vdll_ctrl; + + ENTER(); + memset(pmadapter, ctrl, 0, sizeof(vdll_dnld_ctrl)); +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) { + ctrl->cmd_buf = + wlan_alloc_mlan_buffer(pmadapter, + MRVDRV_SIZE_OF_CMD_BUFFER, 0, + MOAL_MALLOC_BUFFER); + if (!ctrl->cmd_buf) { + PRINTM(MERROR, + "vdll init: fail to alloc command buffer"); + status = MLAN_STATUS_FAILURE; + } + } +#endif + LEAVE(); + return status; +} +/** + * @brief The function handles VDLL deinit + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * + */ +static t_void vdll_deinit(pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + if (pmadapter->vdll_ctrl.vdll_mem != MNULL) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->vdll_ctrl.vdll_mem); + else + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->vdll_ctrl.vdll_mem); + pmadapter->vdll_ctrl.vdll_mem = MNULL; + pmadapter->vdll_ctrl.vdll_len = 0; + } +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type) && + pmadapter->vdll_ctrl.cmd_buf != MNULL) { + wlan_free_mlan_buffer(pmadapter, pmadapter->vdll_ctrl.cmd_buf); + pmadapter->vdll_ctrl.cmd_buf = MNULL; + } +#endif + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function allocates buffer for the members of adapter + * structure like command buffer and BSSID list. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_allocate_adapter(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef STA_SUPPORT + t_u32 beacon_buffer_size; + t_u32 buf_size; + BSSDescriptor_t *ptemp_scan_table = MNULL; + t_u8 chan_2g[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; + t_u8 chan_5g[] = {12, 16, 34, 38, 42, 46, 36, 40, 44, 48, 52, + 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, + 132, 136, 140, 144, 149, 153, 157, 161, 165}; +#endif +#ifdef SDIO + t_u32 max_mp_regs = 0; + t_u32 mp_tx_aggr_buf_size = 0; + t_u32 mp_rx_aggr_buf_size = 0; +#endif + + ENTER(); + +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + max_mp_regs = pmadapter->pcard_sd->reg->max_mp_regs; + mp_tx_aggr_buf_size = SDIO_MP_AGGR_BUF_SIZE_MAX; + mp_rx_aggr_buf_size = SDIO_MP_AGGR_BUF_SIZE_MAX; + } +#endif + +#ifdef STA_SUPPORT + /* Allocate buffer to store the BSSID list */ + buf_size = sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST; + if (pmadapter->callbacks.moal_vmalloc && + pmadapter->callbacks.moal_vfree) + ret = pmadapter->callbacks.moal_vmalloc( + pmadapter->pmoal_handle, buf_size, + (t_u8 **)&ptemp_scan_table); + else + ret = pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, buf_size, MLAN_MEM_DEF, + (t_u8 **)&ptemp_scan_table); + if (ret != MLAN_STATUS_SUCCESS || !ptemp_scan_table) { + PRINTM(MERROR, "Failed to allocate scan table\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->pscan_table = ptemp_scan_table; + + if (pmadapter->fixed_beacon_buffer) + beacon_buffer_size = MAX_SCAN_BEACON_BUFFER; + else + beacon_buffer_size = DEFAULT_SCAN_BEACON_BUFFER; + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + beacon_buffer_size, MLAN_MEM_DEF, + (t_u8 **)&pmadapter->bcn_buf); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->bcn_buf) { + PRINTM(MERROR, "Failed to allocate bcn buf\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->bcn_buf_size = beacon_buffer_size; + + pmadapter->num_in_chan_stats = sizeof(chan_2g); + pmadapter->num_in_chan_stats += sizeof(chan_5g); + buf_size = sizeof(ChanStatistics_t) * pmadapter->num_in_chan_stats; + if (pmadapter->callbacks.moal_vmalloc && + pmadapter->callbacks.moal_vfree) + ret = pmadapter->callbacks.moal_vmalloc( + pmadapter->pmoal_handle, buf_size, + (t_u8 **)&pmadapter->pchan_stats); + else + ret = pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, buf_size, MLAN_MEM_DEF, + (t_u8 **)&pmadapter->pchan_stats); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pchan_stats) { + PRINTM(MERROR, "Failed to allocate channel statistics\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#endif + + /* Allocate command buffer */ + ret = wlan_alloc_cmd_buffer(pmadapter); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to allocate command buffer\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + ret = pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, max_mp_regs + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->pcard_sd->mp_regs_buf); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->pcard_sd->mp_regs_buf) { + PRINTM(MERROR, "Failed to allocate mp_regs_buf\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->pcard_sd->mp_regs = (t_u8 *)ALIGN_ADDR( + pmadapter->pcard_sd->mp_regs_buf, DMA_ALIGNMENT); + + ret = pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, MAX_SUPPORT_AMSDU_SIZE, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->pcard_sd->rx_buffer); + + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->pcard_sd->rx_buffer) { + PRINTM(MERROR, "Failed to allocate receive buffer\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->pcard_sd->rx_buf = (t_u8 *)ALIGN_ADDR( + pmadapter->pcard_sd->rx_buffer, DMA_ALIGNMENT); + + pmadapter->pcard_sd->max_sp_tx_size = MAX_SUPPORT_AMSDU_SIZE; + pmadapter->pcard_sd->max_sp_rx_size = MAX_SUPPORT_AMSDU_SIZE; + ret = wlan_alloc_sdio_mpa_buffers( + pmadapter, mp_tx_aggr_buf_size, mp_rx_aggr_buf_size); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Failed to allocate sdio mp-a buffers\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#ifdef DEBUG_LEVEL1 + if (mlan_drvdbg & MMPA_D) { + pmadapter->pcard_sd->mpa_buf_size = + SDIO_MP_DBG_NUM * SDIO_MP_AGGR_DEF_PKT_LIMIT * + MLAN_SDIO_BLOCK_SIZE; + if (pmadapter->callbacks.moal_vmalloc && + pmadapter->callbacks.moal_vfree) + ret = pmadapter->callbacks.moal_vmalloc( + pmadapter->pmoal_handle, + pmadapter->pcard_sd->mpa_buf_size, + (t_u8 **)&pmadapter->pcard_sd->mpa_buf); + else + ret = pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, + pmadapter->pcard_sd->mpa_buf_size, + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->pcard_sd->mpa_buf); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->pcard_sd->mpa_buf) { + PRINTM(MERROR, "Failed to allocate mpa buf\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } +#endif + } +#endif + + pmadapter->psleep_cfm = + wlan_alloc_mlan_buffer(pmadapter, + sizeof(opt_sleep_confirm_buffer), 0, + MOAL_MALLOC_BUFFER); + +#ifdef PCIE + /* Initialize PCIE ring buffer */ + if (IS_PCIE(pmadapter->card_type)) { + ret = wlan_alloc_pcie_ring_buf(pmadapter); + if (MLAN_STATUS_SUCCESS != ret) { + PRINTM(MERROR, + "Failed to allocate PCIE host buffers\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } +#endif /* PCIE */ + + vdll_init(pmadapter); + 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; +#ifdef USB + pusb_tx_aggr_params pusb_tx_aggr = MNULL; +#endif + + ENTER(); + + priv->media_connected = MFALSE; + memset(pmadapter, priv->curr_addr, 0xff, MLAN_MAC_ADDR_LENGTH); + +#ifdef STA_SUPPORT + priv->pkt_tx_ctrl = 0; + priv->bss_mode = MLAN_BSS_MODE_INFRA; + priv->data_rate = 0; /* Initially indicate the rate as auto */ + priv->is_data_rate_auto = MTRUE; + priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; + priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; + + priv->sec_info.wep_status = Wlan802_11WEPDisabled; + priv->sec_info.authentication_mode = MLAN_AUTH_MODE_AUTO; + priv->sec_info.encryption_mode = MLAN_ENCRYPTION_MODE_NONE; + for (i = 0; i < MRVL_NUM_WEP_KEY; i++) + memset(pmadapter, &priv->wep_key[i], 0, sizeof(mrvl_wep_key_t)); + priv->wep_key_curr_index = 0; + priv->ewpa_query = MFALSE; + priv->curr_pkt_filter = HostCmd_ACT_MAC_STATIC_DYNAMIC_BW_ENABLE | + HostCmd_ACT_MAC_RTS_CTS_ENABLE | + HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | + HostCmd_ACT_MAC_ETHERNETII_ENABLE; + + priv->beacon_period = MLAN_BEACON_INTERVAL; + priv->pattempted_bss_desc = MNULL; + memset(pmadapter, &priv->gtk_rekey, 0, + sizeof(mlan_ds_misc_gtk_rekey_data)); + memset(pmadapter, &priv->curr_bss_params, 0, + sizeof(priv->curr_bss_params)); + priv->listen_interval = MLAN_DEFAULT_LISTEN_INTERVAL; + + memset(pmadapter, &priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); + priv->assoc_rsp_size = 0; + + wlan_11d_priv_init(priv); + wlan_11h_priv_init(priv); + +#ifdef UAP_SUPPORT + priv->uap_bss_started = MFALSE; + priv->uap_host_based = MFALSE; + memset(pmadapter, &priv->uap_state_chan_cb, 0, + sizeof(priv->uap_state_chan_cb)); +#endif +#ifdef 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->atim_window = 0; + priv->adhoc_state = ADHOC_IDLE; + priv->tx_power_level = 0; + priv->max_tx_power_level = 0; + priv->min_tx_power_level = 0; + priv->tx_rate = 0; + priv->rxpd_rate_info = 0; + priv->rx_pkt_info = MFALSE; + /* refer to V15 CMD_TX_RATE_QUERY */ + priv->rxpd_vhtinfo = 0; + priv->rxpd_rate = 0; + priv->data_rssi_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->data_nf_last = 0; + priv->bcn_rssi_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->bcn_nf_last = 0; + priv->sec_info.ewpa_enabled = MFALSE; + priv->sec_info.wpa_enabled = MFALSE; + priv->sec_info.wpa2_enabled = MFALSE; + memset(pmadapter, &priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + memset(pmadapter, &priv->aes_key, 0, sizeof(priv->aes_key)); + priv->wpa_ie_len = 0; + priv->wpa_is_gtk_set = MFALSE; +#if defined(STA_SUPPORT) + priv->pmfcfg.mfpc = 0; + priv->pmfcfg.mfpr = 0; +#endif + priv->sec_info.wapi_enabled = MFALSE; + priv->wapi_ie_len = 0; + priv->sec_info.wapi_key_on = MFALSE; + + memset(pmadapter, &priv->wps, 0, sizeof(priv->wps)); + memset(pmadapter, &priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); + priv->gen_ie_buf_len = 0; +#endif /* STA_SUPPORT */ + priv->wmm_required = MTRUE; + priv->wmm_enabled = MFALSE; + priv->wmm_qosinfo = 0; +#ifdef STA_SUPPORT + priv->pcurr_bcn_buf = MNULL; + priv->curr_bcn_size = 0; + memset(pmadapter, &priv->ext_cap, 0, sizeof(priv->ext_cap)); + + SET_EXTCAP_OPERMODENTF(priv->ext_cap); + SET_EXTCAP_QOS_MAP(priv->ext_cap); + /* Save default Extended Capability */ + memcpy_ext(priv->adapter, &priv->def_ext_cap, &priv->ext_cap, + sizeof(priv->ext_cap), sizeof(priv->def_ext_cap)); +#endif /* STA_SUPPORT */ + + for (i = 0; i < MAX_NUM_TID; i++) + priv->addba_reject[i] = ADDBA_RSP_STATUS_ACCEPT; + priv->addba_reject[6] = ADDBA_RSP_STATUS_REJECT; + priv->addba_reject[7] = ADDBA_RSP_STATUS_REJECT; + memcpy_ext(priv->adapter, priv->ibss_addba_reject, priv->addba_reject, + sizeof(priv->addba_reject), sizeof(priv->ibss_addba_reject)); + priv->max_amsdu = 0; +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) { + priv->add_ba_param.tx_win_size = MLAN_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = MLAN_STA_AMPDU_DEF_RXWINSIZE; + } +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + priv->add_ba_param.tx_win_size = MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; + priv->add_ba_param.rx_win_size = MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; + } +#endif +#ifdef UAP_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + priv->add_ba_param.tx_win_size = MLAN_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = MLAN_UAP_AMPDU_DEF_RXWINSIZE; + priv->aggr_prio_tbl[6].ampdu_user = + priv->aggr_prio_tbl[7].ampdu_user = + BA_STREAM_NOT_ALLOWED; + } +#endif + priv->user_rxwinsize = priv->add_ba_param.rx_win_size; + + priv->port_ctrl_mode = MTRUE; + priv->port_open = MFALSE; + + priv->intf_hr_len = pmadapter->ops.intf_header_len; +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + pusb_tx_aggr = + wlan_get_usb_tx_aggr_params(pmadapter, priv->port); + if (pusb_tx_aggr && pusb_tx_aggr->aggr_ctrl.aggr_mode == + MLAN_USB_AGGR_MODE_LEN_V2) { + priv->intf_hr_len = MLAN_USB_TX_AGGR_HEADER; + } + priv->port = pmadapter->tx_data_ep; + } +#endif + ret = wlan_add_bsspriotbl(priv); +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + hostsa_init(priv); +#endif + + LEAVE(); + return ret; +} + +/** + * @brief This function initializes the adapter structure + * and sets default values to the members of adapter. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +t_void wlan_init_adapter(pmlan_adapter pmadapter) +{ + opt_sleep_confirm_buffer *sleep_cfm_buf = MNULL; +#ifdef USB + t_s32 i = 0; +#endif + ENTER(); + + if (pmadapter->psleep_cfm) { + sleep_cfm_buf = (opt_sleep_confirm_buffer + *)(pmadapter->psleep_cfm->pbuf + + pmadapter->psleep_cfm->data_offset); + } +#ifdef MFG_CMD_SUPPORT + if (pmadapter->init_para.mfg_mode == MLAN_INIT_PARA_DISABLED) + pmadapter->mfg_mode = MFALSE; + else + pmadapter->mfg_mode = pmadapter->init_para.mfg_mode; +#endif + +#ifdef STA_SUPPORT + pmadapter->pwarm_reset_ioctl_req = MNULL; +#endif + pmadapter->cmd_sent = MFALSE; +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + pmadapter->pcard_sd->int_mode = pmadapter->init_para.int_mode; + pmadapter->pcard_sd->gpio_pin = pmadapter->init_para.gpio_pin; + pmadapter->data_sent = MTRUE; + pmadapter->pcard_sd->mp_rd_bitmap = 0; + pmadapter->pcard_sd->mp_wr_bitmap = 0; + pmadapter->pcard_sd->curr_rd_port = 0; + pmadapter->pcard_sd->curr_wr_port = 0; + pmadapter->pcard_sd->mp_data_port_mask = + pmadapter->pcard_sd->reg->data_port_mask; + pmadapter->pcard_sd->mp_invalid_update = 0; + memset(pmadapter, pmadapter->pcard_sd->mp_update, 0, + sizeof(pmadapter->pcard_sd->mp_update)); + pmadapter->pcard_sd->mpa_tx.buf_len = 0; + pmadapter->pcard_sd->mpa_tx.pkt_cnt = 0; + pmadapter->pcard_sd->mpa_tx.start_port = 0; + + if (!pmadapter->init_para.mpa_tx_cfg) + pmadapter->pcard_sd->mpa_tx.enabled = MFALSE; + else if (pmadapter->init_para.mpa_tx_cfg == + MLAN_INIT_PARA_DISABLED) + pmadapter->pcard_sd->mpa_tx.enabled = MFALSE; + else + pmadapter->pcard_sd->mpa_tx.enabled = MTRUE; + pmadapter->pcard_sd->mpa_tx.pkt_aggr_limit = + SDIO_MP_AGGR_DEF_PKT_LIMIT; + + pmadapter->pcard_sd->mpa_rx.buf_len = 0; + pmadapter->pcard_sd->mpa_rx.pkt_cnt = 0; + pmadapter->pcard_sd->mpa_rx.start_port = 0; + + if (!pmadapter->init_para.mpa_rx_cfg) + pmadapter->pcard_sd->mpa_rx.enabled = MFALSE; + else if (pmadapter->init_para.mpa_rx_cfg == + MLAN_INIT_PARA_DISABLED) + pmadapter->pcard_sd->mpa_rx.enabled = MFALSE; + else + pmadapter->pcard_sd->mpa_rx.enabled = MTRUE; + pmadapter->pcard_sd->mpa_rx.pkt_aggr_limit = + SDIO_MP_AGGR_DEF_PKT_LIMIT; + } +#endif + + pmadapter->rx_pkts_queued = 0; + pmadapter->cmd_resp_received = MFALSE; + pmadapter->event_received = MFALSE; + pmadapter->data_received = MFALSE; + + pmadapter->cmd_timer_is_set = MFALSE; + + /* PnP and power profile */ + pmadapter->surprise_removed = MFALSE; + /* FW hang report */ + pmadapter->fw_hang_report = MFALSE; + + if (!pmadapter->init_para.ps_mode) { + pmadapter->ps_mode = DEFAULT_PS_MODE; + } else if (pmadapter->init_para.ps_mode == MLAN_INIT_PARA_DISABLED) + pmadapter->ps_mode = Wlan802_11PowerModeCAM; + else + pmadapter->ps_mode = Wlan802_11PowerModePSP; + pmadapter->ps_state = PS_STATE_AWAKE; + pmadapter->need_to_wakeup = MFALSE; + +#ifdef STA_SUPPORT + pmadapter->scan_block = MFALSE; + /* Scan type */ + pmadapter->scan_type = MLAN_SCAN_TYPE_ACTIVE; + /* Scan mode */ + pmadapter->scan_mode = HostCmd_BSS_MODE_ANY; + /* Scan time */ + pmadapter->specific_scan_time = MRVDRV_SPECIFIC_SCAN_CHAN_TIME; + pmadapter->active_scan_time = MRVDRV_ACTIVE_SCAN_CHAN_TIME; + pmadapter->passive_scan_time = MRVDRV_PASSIVE_SCAN_CHAN_TIME; + if (!pmadapter->init_para.passive_to_active_scan) + pmadapter->passive_to_active_scan = MLAN_PASS_TO_ACT_SCAN_EN; + else if (pmadapter->init_para.passive_to_active_scan == + MLAN_INIT_PARA_DISABLED) + pmadapter->passive_to_active_scan = MLAN_PASS_TO_ACT_SCAN_DIS; + else + pmadapter->passive_to_active_scan = MLAN_PASS_TO_ACT_SCAN_EN; + + pmadapter->scan_chan_gap = 0; + pmadapter->num_in_scan_table = 0; + memset(pmadapter, pmadapter->pscan_table, 0, + (sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST)); + pmadapter->active_scan_triggered = MFALSE; + pmadapter->ext_scan = MTRUE; + pmadapter->scan_probes = DEFAULT_PROBES; + + memset(pmadapter, pmadapter->bcn_buf, 0, pmadapter->bcn_buf_size); + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + + pmadapter->radio_on = RADIO_ON; + if (!pmadapter->multiple_dtim) + pmadapter->multiple_dtim = MRVDRV_DEFAULT_MULTIPLE_DTIM; + + pmadapter->local_listen_interval = 0; /* default value in firmware will + be used */ +#endif /* STA_SUPPORT */ + + pmadapter->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 = + pmadapter->pcard_info->max_tx_buf_size; + 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; + +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) { + pmadapter->pcard_usb->usb_tx_aggr[i].aggr_ctrl.enable = + MFALSE; + pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_ctrl.aggr_mode = + MLAN_USB_AGGR_MODE_LEN_V2; + pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_ctrl.aggr_align = + MLAN_USB_TX_AGGR_V2_ALIGN; + pmadapter->pcard_usb->usb_tx_aggr[i].aggr_ctrl.aggr_max = + MLAN_USB_TX_AGGR_MAX_LEN; + pmadapter->pcard_usb->usb_tx_aggr[i].aggr_ctrl.aggr_tmo = + MLAN_USB_TX_AGGR_TIMEOUT_MSEC * 1000; + + pmadapter->pcard_usb->usb_tx_aggr[i].pmbuf_aggr = MNULL; + pmadapter->pcard_usb->usb_tx_aggr[i].aggr_len = 0; + pmadapter->pcard_usb->usb_tx_aggr[i].hold_timeout_msec = + MLAN_USB_TX_AGGR_TIMEOUT_MSEC; + pmadapter->pcard_usb->usb_tx_aggr[i].port = + pmadapter->tx_data_ep; + pmadapter->pcard_usb->usb_tx_aggr[i].phandle = + (t_void *)pmadapter; + } + + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable = MFALSE; + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_mode = + MLAN_USB_AGGR_MODE_NUM; + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_align = + MLAN_USB_RX_ALIGN_SIZE; + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_max = + MLAN_USB_RX_MAX_AGGR_NUM; + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_tmo = + MLAN_USB_RX_DEAGGR_TIMEOUT_USEC; + + pmadapter->pcard_usb->fw_usb_aggr = MTRUE; + } +#endif + + pmadapter->is_hs_configured = MFALSE; + pmadapter->hs_cfg.conditions = HOST_SLEEP_DEF_COND; + pmadapter->hs_cfg.gpio = HOST_SLEEP_DEF_GPIO; + pmadapter->hs_cfg.gap = HOST_SLEEP_DEF_GAP; + pmadapter->hs_activated = MFALSE; + pmadapter->min_wake_holdoff = HOST_SLEEP_DEF_WAKE_HOLDOFF; + pmadapter->hs_inactivity_timeout = HOST_SLEEP_DEF_INACTIVITY_TIMEOUT; + + memset(pmadapter, pmadapter->event_body, 0, + sizeof(pmadapter->event_body)); + pmadapter->hw_dot_11n_dev_cap = 0; + pmadapter->hw_dev_mcs_support = 0; + pmadapter->coex_rx_winsize = 1; +#ifdef STA_SUPPORT + pmadapter->chan_bandwidth = 0; +#endif /* STA_SUPPORT */ + + pmadapter->min_ba_threshold = MIN_BA_THRESHOLD; + pmadapter->hw_dot_11ac_dev_cap = 0; + pmadapter->hw_dot_11ac_mcs_support = 0; + pmadapter->max_sta_conn = 0; + /* Initialize 802.11d */ + wlan_11d_init(pmadapter); + + wlan_11h_init(pmadapter); + + wlan_wmm_init(pmadapter); + wlan_init_wmm_param(pmadapter); + pmadapter->bypass_pkt_count = 0; + if (pmadapter->psleep_cfm) { + pmadapter->psleep_cfm->buf_type = MLAN_BUF_TYPE_CMD; + pmadapter->psleep_cfm->data_len = sizeof(OPT_Confirm_Sleep); + memset(pmadapter, &sleep_cfm_buf->ps_cfm_sleep, 0, + sizeof(OPT_Confirm_Sleep)); + sleep_cfm_buf->ps_cfm_sleep.command = + wlan_cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); + sleep_cfm_buf->ps_cfm_sleep.size = + wlan_cpu_to_le16(sizeof(OPT_Confirm_Sleep)); + sleep_cfm_buf->ps_cfm_sleep.result = 0; + sleep_cfm_buf->ps_cfm_sleep.action = + wlan_cpu_to_le16(SLEEP_CONFIRM); + sleep_cfm_buf->ps_cfm_sleep.sleep_cfm.resp_ctrl = + wlan_cpu_to_le16(RESP_NEEDED); +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + sleep_cfm_buf->hdr = + wlan_cpu_to_le32(MLAN_USB_TYPE_CMD); + pmadapter->psleep_cfm->data_len += MLAN_TYPE_LEN; + } +#endif + } + 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; + memcpy_ext(pmadapter, pmadapter->country_code, + MRVDRV_DEFAULT_COUNTRY_CODE, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + pmadapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; + +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type)) { + pmadapter->pcard_pcie->txbd_wrptr = 0; + pmadapter->pcard_pcie->txbd_rdptr = 0; + pmadapter->pcard_pcie->rxbd_rdptr = 0; + pmadapter->pcard_pcie->evtbd_rdptr = 0; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + pmadapter->pcard_pcie->rxbd_wrptr = + pmadapter->pcard_pcie->reg + ->txrx_rw_ptr_rollover_ind; + pmadapter->pcard_pcie->evtbd_wrptr = + EVT_RW_PTR_ROLLOVER_IND; + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + pmadapter->pcard_pcie->rxbd_wrptr = MLAN_MAX_TXRX_BD; + pmadapter->pcard_pcie->evtbd_wrptr = MLAN_MAX_EVT_BD; + } +#endif + } +#endif + LEAVE(); + return; +} + +/** + * @brief This function intializes the lock variables and + * the list heads for interface + * + * @param pmadapter A pointer to a mlan_adapter structure + * @param start_index start index of mlan private + * + * @return MLAN_STATUS_SUCCESS -- on success, + * otherwise MLAN_STATUS_FAILURE + * + */ +mlan_status wlan_init_priv_lock_list(pmlan_adapter pmadapter, t_u8 start_index) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_s32 i = 0; + t_u32 j = 0; + for (i = start_index; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + if (pcb->moal_init_lock(pmadapter->pmoal_handle, + &priv->rx_pkt_lock) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + if (pcb->moal_init_lock(pmadapter->pmoal_handle, + &priv->wmm.ra_list_spinlock) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } +#ifdef STA_SUPPORT + if (pcb->moal_init_lock(pmadapter->pmoal_handle, + &priv->curr_bcn_buf_lock) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } +#endif + } + } + for (i = start_index; i < pmadapter->priv_num; ++i) { + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->bssprio_tbl[i].bssprio_head, + MTRUE, pmadapter->callbacks.moal_init_lock); + pmadapter->bssprio_tbl[i].bssprio_cur = MNULL; + } + + for (i = start_index; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + for (j = 0; j < MAX_NUM_TID; ++j) { + util_init_list_head( + (t_void *)pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[j].ra_list, + MTRUE, + priv->adapter->callbacks.moal_init_lock); + } + util_init_list_head( + (t_void *)pmadapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, MTRUE, + pmadapter->callbacks.moal_init_lock); + util_init_list_head( + (t_void *)pmadapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, MTRUE, + pmadapter->callbacks.moal_init_lock); + util_scalar_init((t_void *)pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, 0, + priv->wmm.ra_list_spinlock, + pmadapter->callbacks.moal_init_lock); + util_scalar_init((t_void *)pmadapter->pmoal_handle, + &priv->wmm.highest_queued_prio, + HIGH_PRIO_TID, + priv->wmm.ra_list_spinlock, + pmadapter->callbacks.moal_init_lock); + util_init_list_head( + (t_void *)pmadapter->pmoal_handle, + &priv->sta_list, MTRUE, + pmadapter->callbacks.moal_init_lock); + /* Initialize bypass_txq */ + util_init_list_head( + (t_void *)pmadapter->pmoal_handle, + &priv->bypass_txq, MTRUE, + pmadapter->callbacks.moal_init_lock); + } + } +error: + LEAVE(); + return ret; +} + +/** + * @brief This function intializes the lock variables and + * the list heads. + * + * @param pmadapter A pointer to a mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS -- on success, + * otherwise MLAN_STATUS_FAILURE + * + */ +mlan_status wlan_init_lock_list(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; +#if defined(USB) + t_s32 i = 0; +#endif + ENTER(); + + if (pcb->moal_init_lock(pmadapter->pmoal_handle, + &pmadapter->pmlan_lock) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) { + if (pcb->moal_init_lock(pmadapter->pmoal_handle, + &pmadapter->pint_lock) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + } +#endif + if (pcb->moal_init_lock(pmadapter->pmoal_handle, + &pmadapter->pmain_proc_lock) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + if (pcb->moal_init_lock(pmadapter->pmoal_handle, + &pmadapter->prx_proc_lock) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + if (pcb->moal_init_lock(pmadapter->pmoal_handle, + &pmadapter->pmlan_cmd_lock) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } +#if defined(USB) + if (IS_USB(pmadapter->card_type)) { + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) { + if (pcb->moal_init_lock(pmadapter->pmoal_handle, + &pmadapter->pcard_usb + ->usb_tx_aggr[i] + .paggr_lock) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + } + } +#endif + + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, MTRUE, + pmadapter->callbacks.moal_init_lock); + util_scalar_init((t_void *)pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, 0, MNULL, + pmadapter->callbacks.moal_init_lock); + /* Initialize cmd_free_q */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->cmd_free_q, MTRUE, + pmadapter->callbacks.moal_init_lock); + /* Initialize cmd_pending_q */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, MTRUE, + pmadapter->callbacks.moal_init_lock); + /* Initialize scan_pending_q */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, MTRUE, + pmadapter->callbacks.moal_init_lock); + + /* Initialize ioctl_pending_q */ + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, MTRUE, + pmadapter->callbacks.moal_init_lock); + +error: + LEAVE(); + return ret; +} + +/** + * @brief This function releases the lock variables + * + * @param pmadapter A pointer to a mlan_adapter structure + * + * @return None + * + */ +t_void wlan_free_lock_list(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 defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type) && pmadapter->pint_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->pint_lock); +#endif + if (pmadapter->prx_proc_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + if (pmadapter->pmain_proc_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + if (pmadapter->pmlan_cmd_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_lock); +#if defined(USB) + if (IS_USB(pmadapter->card_type)) { + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) { + if (pmadapter->pcard_usb->usb_tx_aggr[i].paggr_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + pmadapter->pcard_usb + ->usb_tx_aggr[i] + .paggr_lock); + } + } +#endif + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + if (priv->rx_pkt_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + priv->rx_pkt_lock); + if (priv->wmm.ra_list_spinlock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); +#ifdef STA_SUPPORT + if (priv->curr_bcn_buf_lock) + pcb->moal_free_lock(pmadapter->pmoal_handle, + priv->curr_bcn_buf_lock); +#endif + } + } + + /* Free lists */ + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, pcb->moal_free_lock); + + util_scalar_free((t_void *)pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, pcb->moal_free_lock); + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->cmd_free_q, + pmadapter->callbacks.moal_free_lock); + + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + pmadapter->callbacks.moal_free_lock); + + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, + pmadapter->callbacks.moal_free_lock); + + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + pmadapter->callbacks.moal_free_lock); + + for (i = 0; i < pmadapter->priv_num; i++) + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &pmadapter->bssprio_tbl[i].bssprio_head, + pcb->moal_free_lock); + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + util_free_list_head( + (t_void *)pmadapter->pmoal_handle, + &priv->sta_list, + priv->adapter->callbacks.moal_free_lock); + util_free_list_head( + (t_void *)pmadapter->pmoal_handle, + &priv->bypass_txq, + pmadapter->callbacks.moal_free_lock); + for (j = 0; j < MAX_NUM_TID; ++j) + util_free_list_head( + (t_void *)priv->adapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[j].ra_list, + priv->adapter->callbacks.moal_free_lock); + util_free_list_head( + (t_void *)priv->adapter->pmoal_handle, + &priv->tx_ba_stream_tbl_ptr, + priv->adapter->callbacks.moal_free_lock); + util_free_list_head( + (t_void *)priv->adapter->pmoal_handle, + &priv->rx_reorder_tbl_ptr, + priv->adapter->callbacks.moal_free_lock); + util_scalar_free( + (t_void *)priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + priv->adapter->callbacks.moal_free_lock); + util_scalar_free( + (t_void *)priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, + priv->adapter->callbacks.moal_free_lock); + } + } + + LEAVE(); + return; +} + +/** + * @brief This function intializes the timers + * + * @param pmadapter A pointer to a mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS -- on success, + * otherwise MLAN_STATUS_FAILURE + * + */ +mlan_status wlan_init_timer(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; +#if defined(USB) + t_s32 i = 0; +#endif + 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; + } +#if defined(USB) + if (IS_USB(pmadapter->card_type)) { + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) { + if (pcb->moal_init_timer( + pmadapter->pmoal_handle, + &pmadapter->pcard_usb->usb_tx_aggr[i] + .paggr_hold_timer, + wlan_usb_tx_aggr_timeout_func, + &pmadapter->pcard_usb->usb_tx_aggr[i]) != + MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + } + } +#endif + if (pcb->moal_init_timer(pmadapter->pmoal_handle, + &pmadapter->pwakeup_fw_timer, + wlan_wakeup_card_timeout_func, + pmadapter) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->wakeup_fw_timer_is_set = MFALSE; +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(pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; +#if defined(USB) + t_s32 i = 0; +#endif + ENTER(); + + if (pmadapter->pmlan_cmd_timer) + pcb->moal_free_timer(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_timer); +#if defined(USB) + if (IS_USB(pmadapter->card_type)) { + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) { + if (pmadapter->pcard_usb->usb_tx_aggr[i] + .paggr_hold_timer) + pcb->moal_free_timer(pmadapter->pmoal_handle, + pmadapter->pcard_usb + ->usb_tx_aggr[i] + .paggr_hold_timer); + } + } +#endif + + if (pmadapter->pwakeup_fw_timer) + pcb->moal_free_timer(pmadapter->pmoal_handle, + pmadapter->pwakeup_fw_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(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef PCIE + pmlan_private priv = pmadapter->priv[0]; +#endif + ENTER(); + /* Initialize adapter structure */ + wlan_init_adapter(pmadapter); +#ifdef MFG_CMD_SUPPORT + if (pmadapter->mfg_mode != MTRUE) { +#endif + wlan_adapter_get_hw_spec(pmadapter); +#ifdef MFG_CMD_SUPPORT + } +#ifdef PCIE + else if (IS_PCIE(pmadapter->card_type)) { + if (MLAN_STATUS_SUCCESS != wlan_set_pcie_buf_config(priv)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +#endif /* PCIE */ +#endif /* MFG_CMD_SUPPORT */ + if (wlan_is_cmd_pending(pmadapter)) { + /* Send the first command in queue and return */ + if (mlan_main_process(pmadapter) == MLAN_STATUS_FAILURE) + ret = MLAN_STATUS_FAILURE; + else + ret = MLAN_STATUS_PENDING; +#if defined(MFG_CMD_SUPPORT) && defined(PCIE) + if (IS_PCIE(pmadapter->card_type) && pmadapter->mfg_mode) { + ret = MLAN_STATUS_SUCCESS; + } +#endif + } +#ifdef PCIE +done: +#endif +#ifdef MFG_CMD_SUPPORT + if (pmadapter->mfg_mode == MTRUE) { + pmadapter->hw_status = WlanHardwareStatusInitializing; + ret = wlan_get_hw_spec_complete(pmadapter); + } +#endif + LEAVE(); + return ret; +} + +/** + * @brief This function udpate hw spec info to each interface + * + * @param pmadapter A pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS, MLAN_STATUS_PENDING or + * MLAN_STATUS_FAILURE + */ +void wlan_update_hw_spec(pmlan_adapter pmadapter) +{ + t_u32 i; + + ENTER(); + +#ifdef STA_SUPPORT + if (IS_SUPPORT_MULTI_BANDS(pmadapter)) + pmadapter->fw_bands = (t_u8)GET_FW_DEFAULT_BANDS(pmadapter); + else + pmadapter->fw_bands = BAND_B; + + if ((pmadapter->fw_bands & BAND_A) && (pmadapter->fw_bands & BAND_GN)) + pmadapter->fw_bands |= BAND_AN; + if (!(pmadapter->fw_bands & BAND_G) && (pmadapter->fw_bands & BAND_GN)) + pmadapter->fw_bands &= ~BAND_GN; + + 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_AN) { + pmadapter->config_bands |= BAND_AN; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->config_bands |= + BAND_AN; + } + } + if (pmadapter->fw_bands & BAND_AAC) { + pmadapter->config_bands |= BAND_AAC; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->config_bands |= + BAND_AAC; + } + } + pmadapter->adhoc_start_band = BAND_A; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->adhoc_channel = + DEFAULT_AD_HOC_CHANNEL_A; + } + } else if (pmadapter->fw_bands & BAND_G) { + pmadapter->adhoc_start_band = BAND_G | BAND_B; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->adhoc_channel = + DEFAULT_AD_HOC_CHANNEL; + } + } else if (pmadapter->fw_bands & BAND_B) { + pmadapter->adhoc_start_band = BAND_B; + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->adhoc_channel = + DEFAULT_AD_HOC_CHANNEL; + } + } +#endif /* STA_SUPPORT */ + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]->curr_addr[0] == 0xff) + memmove(pmadapter, pmadapter->priv[i]->curr_addr, + pmadapter->permanent_addr, + MLAN_MAC_ADDR_LENGTH); + } + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_update_11n_cap(pmadapter->priv[i]); + } + if (ISSUPP_BEAMFORMING(pmadapter->hw_dot_11n_dev_cap)) { + PRINTM(MCMND, "Enable Beamforming\n"); + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + pmadapter->priv[i]->tx_bf_cap = + pmadapter->pcard_info + ->default_11n_tx_bf_cap; + } + } + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_update_11ac_cap(pmadapter->priv[i]); + } + if (IS_FW_SUPPORT_11AX(pmadapter)) { + if (pmadapter->hw_2g_hecap_len) { + pmadapter->fw_bands |= BAND_GAX; + pmadapter->config_bands |= BAND_GAX; + } + if (pmadapter->hw_hecap_len) { + pmadapter->fw_bands |= BAND_AAX; + pmadapter->config_bands |= BAND_AAX; + } + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + pmadapter->priv[i]->config_bands = + pmadapter->config_bands; + pmadapter->priv[i]->user_2g_hecap_len = + pmadapter->hw_2g_hecap_len; + memcpy_ext(pmadapter, + pmadapter->priv[i]->user_2g_he_cap, + pmadapter->hw_2g_he_cap, + pmadapter->hw_2g_hecap_len, + sizeof(pmadapter->priv[i] + ->user_2g_he_cap)); + pmadapter->priv[i]->user_hecap_len = + pmadapter->hw_hecap_len; + memcpy_ext( + pmadapter, + pmadapter->priv[i]->user_he_cap, + pmadapter->hw_he_cap, + pmadapter->hw_hecap_len, + sizeof(pmadapter->priv[i]->user_he_cap)); + } + } + } + LEAVE(); + return; +} + +/** + * @brief This function initializes firmware for interface + * + * @param pmadapter A pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS, MLAN_STATUS_PENDING or + * MLAN_STATUS_FAILURE + */ +mlan_status wlan_init_priv_fw(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = MNULL; + t_u8 i = 0; + + ENTER(); + + wlan_init_priv_lock_list(pmadapter, 1); + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + priv = pmadapter->priv[i]; + + /* Initialize private structure */ + ret = wlan_init_priv(priv); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } +#ifdef MFG_CMD_SUPPORT + if (pmadapter->mfg_mode != MTRUE) { +#endif + wlan_update_hw_spec(pmadapter); + /* Issue firmware initialize commands for first BSS, + * for other interfaces it will be called after getting + * the last init command response of previous interface + */ + priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + ret = priv->ops.init_cmd(priv, MTRUE); + if (ret == MLAN_STATUS_FAILURE) + goto done; +#ifdef MFG_CMD_SUPPORT + } +#endif /* MFG_CMD_SUPPORT */ + + if (wlan_is_cmd_pending(pmadapter)) { + /* Send the first command in queue and return */ + if (mlan_main_process(pmadapter) == MLAN_STATUS_FAILURE) + ret = MLAN_STATUS_FAILURE; + else + ret = MLAN_STATUS_PENDING; +#if defined(MFG_CMD_SUPPORT) && defined(PCIE) + if (IS_PCIE(pmadapter->card_type) && pmadapter->mfg_mode) { + ret = MLAN_STATUS_SUCCESS; + pmadapter->hw_status = WlanHardwareStatusReady; + } +#endif + } 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; +#if defined(USB) + t_s32 i = 0; +#endif + ENTER(); + + if (!pmadapter) { + PRINTM(MERROR, "The adapter is NULL\n"); + LEAVE(); + return; + } + + wlan_cancel_all_pending_cmd(pmadapter, MTRUE); + /* 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; + } +#if defined(USB) + if (IS_USB(pmadapter->card_type)) { + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) { + if (pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_hold_timer_is_set) { + /* Cancel usb_tx_aggregation timeout timer */ + pcb->moal_stop_timer(pmadapter->pmoal_handle, + pmadapter->pcard_usb + ->usb_tx_aggr[i] + .paggr_hold_timer); + pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_hold_timer_is_set = MFALSE; + } + } + } +#endif + if (pmadapter->wakeup_fw_timer_is_set) { + /* Cancel wakeup card timer */ + pcb->moal_stop_timer(pmadapter->pmoal_handle, + pmadapter->pwakeup_fw_timer); + pmadapter->wakeup_fw_timer_is_set = MFALSE; + } + wlan_free_fw_cfp_tables(pmadapter); +#ifdef STA_SUPPORT + PRINTM(MINFO, "Free ScanTable\n"); + if (pmadapter->pscan_table) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pscan_table); + else + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pscan_table); + pmadapter->pscan_table = MNULL; + } + if (pmadapter->pchan_stats) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pchan_stats); + else + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pchan_stats); + pmadapter->pchan_stats = MNULL; + } + if (pmadapter->bcn_buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->bcn_buf); + pmadapter->bcn_buf = MNULL; + } +#endif + + wlan_11h_cleanup(pmadapter); + +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + if (pmadapter->pcard_sd->mp_regs_buf) { + pcb->moal_mfree( + pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_sd->mp_regs_buf); + pmadapter->pcard_sd->mp_regs_buf = MNULL; + pmadapter->pcard_sd->mp_regs = MNULL; + } + if (pmadapter->pcard_sd->rx_buffer) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_sd->rx_buffer); + pmadapter->pcard_sd->rx_buffer = MNULL; + pmadapter->pcard_sd->rx_buf = MNULL; + } + wlan_free_sdio_mpa_buffers(pmadapter); +#ifdef DEBUG_LEVEL1 + if (pmadapter->pcard_sd->mpa_buf) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree( + pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_sd->mpa_buf); + else + pcb->moal_mfree( + pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_sd->mpa_buf); + pmadapter->pcard_sd->mpa_buf = MNULL; + pmadapter->pcard_sd->mpa_buf_size = 0; + } +#endif + } +#endif + + wlan_free_mlan_buffer(pmadapter, pmadapter->psleep_cfm); + pmadapter->psleep_cfm = MNULL; + +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type)) { + /* Free ssu dma buffer just in case */ + wlan_free_ssu_pcie_buf(pmadapter); + /* Free PCIE ring buffers */ + wlan_free_pcie_ring_buf(pmadapter); + } +#endif + + /* Free timers */ + wlan_free_timer(pmadapter); + + /* Free lock variables */ + wlan_free_lock_list(pmadapter); + +#ifdef SDIO + if (pmadapter->pcard_sd) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_sd); + pmadapter->pcard_sd = MNULL; + } +#endif +#ifdef PCIE + if (pmadapter->pcard_pcie) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_pcie); + pmadapter->pcard_pcie = MNULL; + } +#endif +#ifdef USB + if (pmadapter->pcard_usb) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_usb); + pmadapter->pcard_usb = MNULL; + } +#endif + vdll_deinit(pmadapter); + + LEAVE(); + return; +} + +/** + * @brief This function frees the structure of priv + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +t_void wlan_free_priv(mlan_private *pmpriv) +{ + ENTER(); + wlan_clean_txrx(pmpriv); + wlan_delete_bsspriotbl(pmpriv); + +#ifdef STA_SUPPORT + wlan_free_curr_bcn(pmpriv); +#endif /* STA_SUPPORT */ + +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + hostsa_cleanup(pmpriv); +#endif /*EMBEDDED AUTHENTICATOR*/ + + wlan_delete_station_list(pmpriv); + LEAVE(); +} + +/** + * @brief This function init interface based on pmadapter's bss_attr table + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +mlan_status wlan_init_interface(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = MNULL; + t_u8 i = 0; + t_u32 j = 0; + + ENTER(); + + pcb = &pmadapter->callbacks; + for (i = 0; i < MLAN_MAX_BSS_NUM; i++) { + if (pmadapter->bss_attr[i].active == MTRUE) { + if (!pmadapter->priv[i]) { + /* For valid bss_attr, allocate memory for + * private structure */ + if (pcb->moal_vmalloc && pcb->moal_vfree) + ret = pcb->moal_vmalloc( + pmadapter->pmoal_handle, + sizeof(mlan_private), + (t_u8 **)&pmadapter->priv[i]); + else + ret = pcb->moal_malloc( + pmadapter->pmoal_handle, + sizeof(mlan_private), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->priv[i]); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->priv[i]) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + pmadapter->priv_num++; + memset(pmadapter, pmadapter->priv[i], 0, + sizeof(mlan_private)); + } + pmadapter->priv[i]->adapter = pmadapter; + + /* Save bss_type, frame_type & bss_priority */ + pmadapter->priv[i]->bss_type = + (t_u8)pmadapter->bss_attr[i].bss_type; + pmadapter->priv[i]->frame_type = + (t_u8)pmadapter->bss_attr[i].frame_type; + pmadapter->priv[i]->bss_priority = + (t_u8)pmadapter->bss_attr[i].bss_priority; + if (pmadapter->bss_attr[i].bss_type == + MLAN_BSS_TYPE_STA) + pmadapter->priv[i]->bss_role = + MLAN_BSS_ROLE_STA; + else if (pmadapter->bss_attr[i].bss_type == + MLAN_BSS_TYPE_UAP) + pmadapter->priv[i]->bss_role = + MLAN_BSS_ROLE_UAP; +#ifdef WIFI_DIRECT_SUPPORT + else if (pmadapter->bss_attr[i].bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) { + pmadapter->priv[i]->bss_role = + MLAN_BSS_ROLE_STA; + if (pmadapter->bss_attr[i].bss_virtual) + pmadapter->priv[i]->bss_virtual = MTRUE; + } +#endif + /* Save bss_index and bss_num */ + pmadapter->priv[i]->bss_index = i; + pmadapter->priv[i]->bss_num = + (t_u8)pmadapter->bss_attr[i].bss_num; + + /* init function table */ + for (j = 0; mlan_ops[j]; j++) { + if (mlan_ops[j]->bss_role == + GET_BSS_ROLE(pmadapter->priv[i])) { + memcpy_ext(pmadapter, + &pmadapter->priv[i]->ops, + mlan_ops[j], + sizeof(mlan_operations), + sizeof(mlan_operations)); + break; + } + } + } + } + /*wmm init*/ + wlan_wmm_init(pmadapter); + /* Initialize firmware, may return PENDING */ + ret = wlan_init_priv_fw(pmadapter); + PRINTM(MINFO, "wlan_init_priv_fw returned ret=0x%x\n", ret); +error: + LEAVE(); + return ret; +} + +/** + * @brief The cmdresp handler calls this function for init_fw_complete callback + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * The firmware initialization callback succeeded. + */ +mlan_status wlan_get_hw_spec_complete(pmlan_adapter pmadapter) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_hw_info info; + mlan_bss_tbl bss_tbl; + + ENTER(); +#ifdef MFG_CMD_SUPPORT + if (pmadapter->mfg_mode != MTRUE) { +#endif + /* Check if hardware is ready */ + if (pmadapter->hw_status != WlanHardwareStatusInitializing) + status = MLAN_STATUS_FAILURE; + else { + memset(pmadapter, &info, 0, sizeof(info)); + info.fw_cap = pmadapter->fw_cap_info; + memset(pmadapter, &bss_tbl, 0, sizeof(bss_tbl)); + memcpy_ext(pmadapter, bss_tbl.bss_attr, + pmadapter->bss_attr, sizeof(mlan_bss_tbl), + sizeof(mlan_bss_tbl)); + } + /* Invoke callback */ + ret = pcb->moal_get_hw_spec_complete(pmadapter->pmoal_handle, + status, &info, &bss_tbl); + if (ret == MLAN_STATUS_SUCCESS && status == MLAN_STATUS_SUCCESS) + memcpy_ext(pmadapter, pmadapter->bss_attr, + bss_tbl.bss_attr, sizeof(mlan_bss_tbl), + sizeof(mlan_bss_tbl)); + else { + pmadapter->hw_status = WlanHardwareStatusNotReady; + wlan_init_fw_complete(pmadapter); + } +#ifdef MFG_CMD_SUPPORT + } +#endif + if (pmadapter->hw_status == WlanHardwareStatusInitializing) + ret = wlan_init_interface(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief The cmdresp handler calls this function for init_fw_complete callback + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * The firmware initialization callback succeeded. + */ +mlan_status wlan_init_fw_complete(pmlan_adapter pmadapter) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_private *pmpriv = MNULL; + + ENTER(); + + /* Check if hardware is ready */ + if (pmadapter->hw_status != WlanHardwareStatusReady) + status = MLAN_STATUS_FAILURE; + + /* Reconfigure wmm parameter*/ + if (status == MLAN_STATUS_SUCCESS) { + pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_STA); + if (pmpriv) + status = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_WMM_PARAM_CONFIG, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmadapter->ac_params); + } + /* Invoke callback */ + ret = pcb->moal_init_fw_complete(pmadapter->pmoal_handle, status); + LEAVE(); + return ret; +} + +/** + * @brief The cmdresp handler calls this function + * for shutdown_fw_complete callback + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * The firmware shutdown callback succeeded. + */ +mlan_status wlan_shutdown_fw_complete(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/mxm_wifiex/wlan_src/mlan/mlan_init.h b/mxm_wifiex/wlan_src/mlan/mlan_init.h new file mode 100644 index 0000000..f32731b --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_init.h @@ -0,0 +1,125 @@ +/** @file mlan_init.h + * + * @brief This file defines the FW initialization data + * structures. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 2312 +/** Rx buffer size for firmware download*/ +#define FW_DNLD_RX_BUF_SIZE 2048 +/** Max firmware retry */ +#define MAX_FW_RETRY 3 + +/** Firmware has last block */ +#define FW_HAS_LAST_BLOCK 0x00000004 +/** CMD id for CMD4 */ +#define FW_CMD_4 0x00000004 +/** CMD id for CMD6 */ +#define FW_CMD_6 0x00000006 +/** CMD id for CMD7 */ +#define FW_CMD_7 0x00000007 +/** CMD id for CMD10 */ +#define FW_CMD_10 0x0000000a + +/** Firmware data transmit size */ +#define FW_DATA_XMIT_SIZE (sizeof(FWHeader) + DataLength + sizeof(t_u32)) + +/** FWHeader */ +typedef MLAN_PACK_START struct _FWHeader { + /** FW download command */ + t_u32 dnld_cmd; + /** FW base address */ + t_u32 base_addr; + /** FW data length */ + t_u32 data_length; + /** FW CRC */ + t_u32 crc; +} MLAN_PACK_END FWHeader; + +/** FWData */ +typedef MLAN_PACK_START struct _FWData { + /** FW data header */ + FWHeader fw_header; + /** FW data sequence number */ + t_u32 seq_num; + /** FW data buffer */ + t_u8 data[1]; +} MLAN_PACK_END FWData; + +/** FWSyncHeader */ +typedef MLAN_PACK_START struct _FWSyncHeader { + /** FW sync header command */ + t_u32 cmd; + /** FW sync header sequence number */ + t_u32 seq_num; + /** Extended header */ + t_u32 magic; + /** Chip rev */ + t_u32 chip_rev; + /** Strap */ + t_u32 strap; + /** Status */ + t_u32 status; + /** Offset */ + t_u32 offset; +} MLAN_PACK_END FWSyncHeader; + +/** FW Sync pkt */ +typedef MLAN_PACK_START struct _FWSyncPkt { + /** pkt type */ + t_u32 pkt_type; + /** FW sync header command */ + t_u32 cmd; + /** FW sync header sequence number */ + t_u32 seq_num; + /** chip rev */ + t_u32 chip_rev; + /** fw status */ + t_u32 fw_ready; +} MLAN_PACK_END FWSyncPkt; + +#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); \ + (x)->status = wlan_le32_to_cpu((x)->status); \ + (x)->offset = wlan_le32_to_cpu((x)->offset); \ + } +#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/mxm_wifiex/wlan_src/mlan/mlan_ioctl.h b/mxm_wifiex/wlan_src/mlan/mlan_ioctl.h new file mode 100644 index 0000000..2315b2a --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_ioctl.h @@ -0,0 +1,5159 @@ +/** @file mlan_ioctl.h + * + * @brief This file declares the IOCTL data structures and APIs. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/****************************************************** +Change log: + 11/07/2008: initial version +******************************************************/ + +#ifndef _MLAN_IOCTL_H_ +#define _MLAN_IOCTL_H_ + +/** Enumeration for IOCTL request ID */ +enum _mlan_ioctl_req_id { + /* Scan Group */ + MLAN_IOCTL_SCAN = 0x00010000, + MLAN_OID_SCAN_NORMAL = 0x00010001, + MLAN_OID_SCAN_SPECIFIC_SSID = 0x00010002, + MLAN_OID_SCAN_USER_CONFIG = 0x00010003, + MLAN_OID_SCAN_CONFIG = 0x00010004, + MLAN_OID_SCAN_GET_CURRENT_BSS = 0x00010005, + MLAN_OID_SCAN_CANCEL = 0x00010006, + MLAN_OID_SCAN_TABLE_FLUSH = 0x0001000A, + MLAN_OID_SCAN_BGSCAN_CONFIG = 0x0001000B, + /* BSS Configuration Group */ + MLAN_IOCTL_BSS = 0x00020000, + MLAN_OID_BSS_START = 0x00020001, + MLAN_OID_BSS_STOP = 0x00020002, + MLAN_OID_BSS_MODE = 0x00020003, + MLAN_OID_BSS_CHANNEL = 0x00020004, + MLAN_OID_BSS_CHANNEL_LIST = 0x00020005, + MLAN_OID_BSS_MAC_ADDR = 0x00020006, + MLAN_OID_BSS_MULTICAST_LIST = 0x00020007, + MLAN_OID_BSS_FIND_BSS = 0x00020008, + MLAN_OID_IBSS_BCN_INTERVAL = 0x00020009, + MLAN_OID_IBSS_ATIM_WINDOW = 0x0002000A, + MLAN_OID_IBSS_CHANNEL = 0x0002000B, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_BSS_CONFIG = 0x0002000C, + MLAN_OID_UAP_DEAUTH_STA = 0x0002000D, + MLAN_OID_UAP_BSS_RESET = 0x0002000E, +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + MLAN_OID_BSS_ROLE = 0x0002000F, +#endif +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_WIFI_DIRECT_MODE = 0x00020010, +#endif +#ifdef STA_SUPPORT + MLAN_OID_BSS_LISTEN_INTERVAL = 0x00020011, +#endif + MLAN_OID_BSS_REMOVE = 0x00020014, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_CFG_WMM_PARAM = 0x00020015, +#endif + MLAN_OID_BSS_11D_CHECK_CHANNEL = 0x00020016, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_ACS_SCAN = 0x00020017, + MLAN_OID_UAP_SCAN_CHANNELS = 0x00020018, + MLAN_OID_UAP_CHANNEL = 0x00020019, + MLAN_OID_UAP_OPER_CTRL = 0x0002001A, +#endif +#ifdef STA_SUPPORT + MLAN_OID_BSS_CHAN_INFO = 0x0002001B, +#endif +#ifdef UAP_SUPPORT + MLAN_OID_UAP_ADD_STATION = 0x0002001C, +#endif + + MLAN_OID_BSS_FIND_BSSID = 0x0002001D, + + /* Radio Configuration Group */ + MLAN_IOCTL_RADIO_CFG = 0x00030000, + MLAN_OID_RADIO_CTRL = 0x00030001, + MLAN_OID_BAND_CFG = 0x00030002, + MLAN_OID_ANT_CFG = 0x00030003, + MLAN_OID_REMAIN_CHAN_CFG = 0x00030004, + MLAN_OID_MIMO_SWITCH = 0x00030005, + + /* SNMP MIB Group */ + MLAN_IOCTL_SNMP_MIB = 0x00040000, + MLAN_OID_SNMP_MIB_RTS_THRESHOLD = 0x00040001, + MLAN_OID_SNMP_MIB_FRAG_THRESHOLD = 0x00040002, + MLAN_OID_SNMP_MIB_RETRY_COUNT = 0x00040003, + MLAN_OID_SNMP_MIB_DOT11D = 0x00040004, +#if defined(UAP_SUPPORT) + MLAN_OID_SNMP_MIB_DOT11H = 0x00040005, +#endif + MLAN_OID_SNMP_MIB_DTIM_PERIOD = 0x00040006, + MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE = 0x00040007, + MLAN_OID_SNMP_MIB_CTRL_DEAUTH = 0x00040008, + + /* Status Information Group */ + MLAN_IOCTL_GET_INFO = 0x00050000, + MLAN_OID_GET_STATS = 0x00050001, + MLAN_OID_GET_SIGNAL = 0x00050002, + MLAN_OID_GET_FW_INFO = 0x00050003, + MLAN_OID_GET_VER_EXT = 0x00050004, + MLAN_OID_GET_BSS_INFO = 0x00050005, + MLAN_OID_GET_DEBUG_INFO = 0x00050006, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_STA_LIST = 0x00050007, +#endif + MLAN_OID_GET_SIGNAL_EXT = 0x00050008, + MLAN_OID_LINK_STATS = 0x00050009, + MLAN_OID_GET_UAP_STATS_LOG = 0x0005000A, + /* Security Configuration Group */ + MLAN_IOCTL_SEC_CFG = 0x00060000, + MLAN_OID_SEC_CFG_AUTH_MODE = 0x00060001, + MLAN_OID_SEC_CFG_ENCRYPT_MODE = 0x00060002, + MLAN_OID_SEC_CFG_WPA_ENABLED = 0x00060003, + MLAN_OID_SEC_CFG_ENCRYPT_KEY = 0x00060004, + MLAN_OID_SEC_CFG_PASSPHRASE = 0x00060005, + MLAN_OID_SEC_CFG_EWPA_ENABLED = 0x00060006, + MLAN_OID_SEC_CFG_ESUPP_MODE = 0x00060007, + MLAN_OID_SEC_CFG_WAPI_ENABLED = 0x00060009, + MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED = 0x0006000A, +#ifdef UAP_SUPPORT + MLAN_OID_SEC_CFG_REPORT_MIC_ERR = 0x0006000B, +#endif + MLAN_OID_SEC_QUERY_KEY = 0x0006000C, + + /* Rate Group */ + MLAN_IOCTL_RATE = 0x00070000, + MLAN_OID_RATE_CFG = 0x00070001, + MLAN_OID_GET_DATA_RATE = 0x00070002, + MLAN_OID_SUPPORTED_RATES = 0x00070003, + + /* Power Configuration Group */ + MLAN_IOCTL_POWER_CFG = 0x00080000, + MLAN_OID_POWER_CFG = 0x00080001, + MLAN_OID_POWER_CFG_EXT = 0x00080002, + MLAN_OID_POWER_LOW_POWER_MODE = 0x00080003, + + /* Power Management Configuration Group */ + MLAN_IOCTL_PM_CFG = 0x00090000, + MLAN_OID_PM_CFG_IEEE_PS = 0x00090001, + MLAN_OID_PM_CFG_HS_CFG = 0x00090002, + MLAN_OID_PM_CFG_INACTIVITY_TO = 0x00090003, + MLAN_OID_PM_CFG_DEEP_SLEEP = 0x00090004, + MLAN_OID_PM_CFG_SLEEP_PD = 0x00090005, + MLAN_OID_PM_CFG_PS_CFG = 0x00090006, + MLAN_OID_PM_CFG_SLEEP_PARAMS = 0x00090008, +#ifdef UAP_SUPPORT + MLAN_OID_PM_CFG_PS_MODE = 0x00090009, +#endif /* UAP_SUPPORT */ + MLAN_OID_PM_INFO = 0x0009000A, + MLAN_OID_PM_HS_WAKEUP_REASON = 0x0009000B, + MLAN_OID_PM_MGMT_FILTER = 0x0009000C, + MLAN_OID_PM_CFG_BCN_TIMEOUT = 0x0009000D, + + /* WMM Configuration Group */ + MLAN_IOCTL_WMM_CFG = 0x000A0000, + MLAN_OID_WMM_CFG_ENABLE = 0x000A0001, + MLAN_OID_WMM_CFG_QOS = 0x000A0002, + MLAN_OID_WMM_CFG_ADDTS = 0x000A0003, + MLAN_OID_WMM_CFG_DELTS = 0x000A0004, + MLAN_OID_WMM_CFG_QUEUE_CONFIG = 0x000A0005, + MLAN_OID_WMM_CFG_QUEUE_STATS = 0x000A0006, + MLAN_OID_WMM_CFG_QUEUE_STATUS = 0x000A0007, + MLAN_OID_WMM_CFG_TS_STATUS = 0x000A0008, + + /* WPS Configuration Group */ + MLAN_IOCTL_WPS_CFG = 0x000B0000, + MLAN_OID_WPS_CFG_SESSION = 0x000B0001, + + /* 802.11n Configuration Group */ + MLAN_IOCTL_11N_CFG = 0x000C0000, + MLAN_OID_11N_CFG_TX = 0x000C0001, + MLAN_OID_11N_HTCAP_CFG = 0x000C0002, + MLAN_OID_11N_CFG_ADDBA_REJECT = 0x000C0003, + MLAN_OID_11N_CFG_AGGR_PRIO_TBL = 0x000C0004, + MLAN_OID_11N_CFG_ADDBA_PARAM = 0x000C0005, + MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE = 0x000C0006, + MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL = 0x000C0007, + MLAN_OID_11N_CFG_SUPPORTED_MCS_SET = 0x000C0008, + MLAN_OID_11N_CFG_TX_BF_CAP = 0x000C0009, + MLAN_OID_11N_CFG_TX_BF_CFG = 0x000C000A, + MLAN_OID_11N_CFG_STREAM_CFG = 0x000C000B, + MLAN_OID_11N_CFG_DELBA = 0x000C000C, + MLAN_OID_11N_CFG_REJECT_ADDBA_REQ = 0x000C000D, + MLAN_OID_11N_CFG_COEX_RX_WINSIZE = 0x000C000E, + MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM = 0x000C0010, + MLAN_OID_11N_CFG_MIN_BA_THRESHOLD = 0x000C0011, + + /* 802.11d Configuration Group */ + MLAN_IOCTL_11D_CFG = 0x000D0000, +#ifdef STA_SUPPORT + MLAN_OID_11D_CFG_ENABLE = 0x000D0001, + MLAN_OID_11D_CLR_CHAN_TABLE = 0x000D0002, +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + MLAN_OID_11D_DOMAIN_INFO = 0x000D0003, +#endif + MLAN_OID_11D_DOMAIN_INFO_EXT = 0x000D0004, + + /* Register Memory Access Group */ + MLAN_IOCTL_REG_MEM = 0x000E0000, + MLAN_OID_REG_RW = 0x000E0001, + MLAN_OID_EEPROM_RD = 0x000E0002, + MLAN_OID_MEM_RW = 0x000E0003, + + /* Multi-Radio Configuration Group */ + MLAN_IOCTL_MFR_CFG = 0x00100000, + /* 802.11h Configuration Group */ + MLAN_IOCTL_11H_CFG = 0x00110000, + MLAN_OID_11H_CHANNEL_CHECK = 0x00110001, + MLAN_OID_11H_LOCAL_POWER_CONSTRAINT = 0x00110002, + MLAN_OID_11H_DFS_TESTING = 0x00110003, + MLAN_OID_11H_CHAN_REPORT_REQUEST = 0x00110004, + MLAN_OID_11H_CHAN_SWITCH_COUNT = 0x00110005, + MLAN_OID_11H_CHAN_NOP_INFO = 0x00110006, + MLAN_OID_11H_DFS_W53_CFG = 0x00110008, + + /* 802.11n Configuration Group RANDYTODO for value assign */ + MLAN_IOCTL_11AC_CFG = 0x00120000, + MLAN_OID_11AC_VHT_CFG = 0x00120001, + MLAN_OID_11AC_CFG_SUPPORTED_MCS_SET = 0x00120002, + MLAN_OID_11AC_OPERMODE_CFG = 0x00120003, + + /* 802.11ax Configuration Group */ + MLAN_IOCTL_11AX_CFG = 0x00170000, + MLAN_OID_11AX_HE_CFG = 0x00170001, + MLAN_OID_11AX_CMD_CFG = 0x00170002, + MLAN_OID_11AX_TWT_CFG = 0x00170003, + + /* Miscellaneous Configuration Group */ + MLAN_IOCTL_MISC_CFG = 0x00200000, + MLAN_OID_MISC_GEN_IE = 0x00200001, + MLAN_OID_MISC_REGION = 0x00200002, + MLAN_OID_MISC_WARM_RESET = 0x00200003, +#ifdef SDIO + MLAN_OID_MISC_SDIO_MPA_CTRL = 0x00200006, +#endif + MLAN_OID_MISC_HOST_CMD = 0x00200007, + MLAN_OID_MISC_SYS_CLOCK = 0x00200009, + MLAN_OID_MISC_SOFT_RESET = 0x0020000A, + MLAN_OID_MISC_WWS = 0x0020000B, + MLAN_OID_MISC_ASSOC_RSP = 0x0020000C, + MLAN_OID_MISC_INIT_SHUTDOWN = 0x0020000D, + MLAN_OID_MISC_CUSTOM_IE = 0x0020000F, + MLAN_OID_MISC_TX_DATAPAUSE = 0x00200012, + MLAN_OID_MISC_IP_ADDR = 0x00200013, + MLAN_OID_MISC_MAC_CONTROL = 0x00200014, + MLAN_OID_MISC_MEF_CFG = 0x00200015, + MLAN_OID_MISC_CFP_CODE = 0x00200016, + MLAN_OID_MISC_COUNTRY_CODE = 0x00200017, + MLAN_OID_MISC_THERMAL = 0x00200018, + MLAN_OID_MISC_RX_MGMT_IND = 0x00200019, + MLAN_OID_MISC_SUBSCRIBE_EVENT = 0x0020001A, +#ifdef DEBUG_LEVEL1 + MLAN_OID_MISC_DRVDBG = 0x0020001B, +#endif + MLAN_OID_MISC_HOTSPOT_CFG = 0x0020001C, + MLAN_OID_MISC_OTP_USER_DATA = 0x0020001D, +#ifdef USB + MLAN_OID_MISC_USB_AGGR_CTRL = 0x0020001F, +#endif + MLAN_OID_MISC_TXCONTROL = 0x00200020, +#ifdef STA_SUPPORT + MLAN_OID_MISC_EXT_CAP_CFG = 0x00200021, +#endif +#if defined(STA_SUPPORT) + MLAN_OID_MISC_PMFCFG = 0x00200022, +#endif +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_MISC_WIFI_DIRECT_CONFIG = 0x00200025, +#endif + MLAN_OID_MISC_LOW_PWR_MODE = 0x00200029, + MLAN_OID_MISC_MEF_FLT_CFG = 0x0020002A, + MLAN_OID_MISC_DFS_REAPTER_MODE = 0x0020002B, +#ifdef RX_PACKET_COALESCE + MLAN_OID_MISC_RX_PACKET_COALESCE = 0x0020002C, +#endif + MLAN_OID_MISC_COALESCE_CFG = 0x0020002E, + MLAN_OID_MISC_GET_SENSOR_TEMP = 0x00200030, + MLAN_OID_MISC_GTK_REKEY_OFFLOAD = 0x00200037, + MLAN_OID_MISC_OPER_CLASS = 0x00200038, + MLAN_OID_MISC_PMIC_CFG = 0x00200039, + MLAN_OID_MISC_IND_RST_CFG = 0x00200040, + MLAN_OID_MISC_GET_TSF = 0x00200045, + MLAN_OID_MISC_GET_CHAN_REGION_CFG = 0x00200046, + MLAN_OID_MISC_CLOUD_KEEP_ALIVE = 0x00200048, + MLAN_OID_MISC_OPER_CLASS_CHECK = 0x00200049, + + MLAN_OID_MISC_CWMODE_CTRL = 0x00200051, + MLAN_OID_MISC_AGGR_CTRL = 0x00200052, + MLAN_OID_MISC_DYN_BW = 0x00200053, + MLAN_OID_MISC_FW_DUMP_EVENT = 0x00200054, + MLAN_OID_MISC_PER_PKT_CFG = 0x00200055, + + MLAN_OID_MISC_ROBUSTCOEX = 0x00200056, + MLAN_OID_MISC_GET_TX_RX_HISTOGRAM = 0x00200057, + MLAN_OID_MISC_CFP_INFO = 0x00200060, + MLAN_OID_MISC_BOOT_SLEEP = 0x00200061, +#if defined(PCIE) + MLAN_OID_MISC_SSU = 0x00200062, +#endif + MLAN_OID_MISC_DMCS_CONFIG = 0x00200065, + MLAN_OID_MISC_RX_ABORT_CFG = 0x00200066, + MLAN_OID_MISC_RX_ABORT_CFG_EXT = 0x00200067, + MLAN_OID_MISC_TX_AMPDU_PROT_MODE = 0x00200068, + MLAN_OID_MISC_RATE_ADAPT_CFG = 0x00200069, + MLAN_OID_MISC_CCK_DESENSE_CFG = 0x00200070, + MLAN_OID_MISC_GET_CHAN_TRPC_CFG = 0x00200072, + MLAN_OID_MISC_BAND_STEERING = 0x00200073, + MLAN_OID_MISC_GET_REGIONPWR_CFG = 0x00200074, + MLAN_OID_MISC_RF_TEST_GENERIC = 0x00200075, + MLAN_OID_MISC_RF_TEST_TX_CONT = 0x00200076, + MLAN_OID_MISC_RF_TEST_TX_FRAME = 0x00200077, + MLAN_OID_MISC_ARB_CONFIG = 0x00200078, + MLAN_OID_MISC_BEACON_STUCK = 0x00200079, + MLAN_OID_MISC_CFP_TABLE = 0x0020007A, + MLAN_OID_MISC_RANGE_EXT = 0x0020007B, + MLAN_OID_MISC_DOT11MC_UNASSOC_FTM_CFG = 0x0020007C, + MLAN_OID_MISC_TP_STATE = 0x0020007D, +}; + +/** Sub command size */ +#define MLAN_SUB_COMMAND_SIZE 4 + +/** Enumeration for the action of IOCTL request */ +enum _mlan_act_ioctl { + MLAN_ACT_SET = 1, + MLAN_ACT_GET, + MLAN_ACT_CANCEL, + MLAN_ACT_CLEAR, + MLAN_ACT_RESET, + MLAN_ACT_DEFAULT +}; + +/** 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, + MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE +}; + +/** Enumeration for passive to active scan */ +enum _mlan_pass_to_act_scan { + MLAN_PASS_TO_ACT_SCAN_UNCHANGED = 0, + MLAN_PASS_TO_ACT_SCAN_EN, + MLAN_PASS_TO_ACT_SCAN_DIS +}; + +/** Max number of supported rates */ +#define MLAN_SUPPORTED_RATES 32 + +/** Mrvl Proprietary Tlv base */ +#define PROPRIETARY_TLV_BASE_ID 0x100 + +/** 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 +/** Max gap time between 2 scan in milliseconds */ +#define MRVDRV_MAX_SCAN_CHAN_GAP_TIME 500 + +/** Maximum number of probes to send on each channel */ +#define MAX_PROBES 5 + +/** 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; + /** channel load */ + t_u8 chan_load; + /** 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; + +typedef MLAN_PACK_START struct _tx_status_event { + /** packet type */ + t_u8 packet_type; + /** tx_token_id */ + t_u8 tx_token_id; + /** 0--success, 1--fail, 2--watchdogtimeout */ + t_u8 status; +} MLAN_PACK_END tx_status_event; + +/** + * Sructure to retrieve the scan table + */ +typedef struct { + /** + * - Zero based scan entry to start retrieval in command request + * - Number of scans entries returned in command response + */ + t_u32 scan_number; + /** + * Buffer marker for multiple wlan_ioctl_get_scan_table_entry + * structures. Each struct is padded to the nearest 32 bit boundary. + */ + t_u8 scan_table_entry_buf[1]; +} wlan_ioctl_get_scan_table_info; + +/** + * Structure passed in the wlan_ioctl_get_scan_table_info for each + * BSS returned in the WLAN_GET_SCAN_RESP IOCTL + */ +typedef struct _wlan_ioctl_get_scan_table_entry { + /** + * Fixed field length included in the response. + * + * Length value is included so future fixed fields can be added to the + * response without breaking backwards compatibility. Use the length + * to find the offset for the bssInfoLength field, not a sizeof() + * calc. + */ + t_u32 fixed_field_length; + + /** + * Length of the BSS Information (probe resp or beacon) that + * follows after the fixed_field_length + */ + t_u32 bss_info_length; + + /** + * Always present, fixed length data fields for the BSS + */ + wlan_get_scan_table_fixed fixed_fields; + + /* + * Probe response or beacon scanned for the BSS. + * + * Field layout: + * - TSF 8 octets + * - Beacon Interval 2 octets + * - Capability Info 2 octets + * + * - IEEE Infomation Elements; variable number & length per 802.11 spec + */ + /* t_u8 bss_info_buffer[]; */ +} wlan_ioctl_get_scan_table_entry; + +/** Type definition of mlan_scan_time_params */ +typedef struct _mlan_scan_time_params { + /** Scan channel time for specific scan in milliseconds */ + t_u32 specific_scan_time; + /** Scan channel time for active scan in milliseconds */ + t_u32 active_scan_time; + /** Scan channel time for passive scan in milliseconds */ + t_u32 passive_scan_time; +} mlan_scan_time_params, *pmlan_scan_time_params; + +/** Type definition of mlan_user_scan */ +typedef struct _mlan_user_scan { + /** Length of scan_cfg_buf */ + t_u32 scan_cfg_len; + /** Buffer of scan config */ + t_u8 scan_cfg_buf[1]; +} mlan_user_scan, *pmlan_user_scan; + +/** Type definition of mlan_scan_req */ +typedef struct _mlan_scan_req { + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan type */ + t_u32 scan_type; + /** SSID */ + mlan_802_11_ssid scan_ssid; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; +} mlan_scan_req, *pmlan_scan_req; + +/** Type defnition of mlan_scan_resp */ +typedef struct _mlan_scan_resp { + /** Number of scan result */ + t_u32 num_in_scan_table; + /** Scan table */ + t_u8 *pscan_table; + /* Age in seconds */ + t_u32 age_in_secs; + /** channel statstics */ + t_u8 *pchan_stats; + /** Number of records in the chan_stats */ + t_u32 num_in_chan_stats; +} mlan_scan_resp, *pmlan_scan_resp; + +#define EXT_SCAN_TYPE_ENH 2 +/** Type definition of mlan_scan_cfg */ +typedef struct _mlan_scan_cfg { + /** Scan type */ + t_u32 scan_type; + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan probe */ + t_u32 scan_probe; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** First passive scan then active scan */ + t_u8 passive_to_active_scan; + /** Ext_scan: 0 disable, 1: enable, 2: enhance scan*/ + t_u32 ext_scan; + /** scan channel gap */ + t_u32 scan_chan_gap; +} mlan_scan_cfg, *pmlan_scan_cfg; + +/** Type defnition of mlan_ds_scan for MLAN_IOCTL_SCAN */ +typedef struct _mlan_ds_scan { + /** Sub-command */ + t_u32 sub_command; + /** Scan request/response */ + union { + /** Scan request */ + mlan_scan_req scan_req; + /** Scan response */ + mlan_scan_resp scan_resp; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; + /** Scan config parameters */ + mlan_scan_cfg scan_cfg; + } param; +} mlan_ds_scan, *pmlan_ds_scan; + +/*-----------------------------------------------------------------*/ +/** BSS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for BSS mode */ +enum _mlan_bss_mode { + MLAN_BSS_MODE_INFRA = 1, + MLAN_BSS_MODE_IBSS, + MLAN_BSS_MODE_AUTO +}; + +/** Maximum key length */ +#define MLAN_MAX_KEY_LENGTH 32 + +/** Maximum atim window in milliseconds */ +#define MLAN_MAX_ATIM_WINDOW 50 + +/** Minimum beacon interval */ +#define MLAN_MIN_BEACON_INTERVAL 20 +/** Maximum beacon interval */ +#define MLAN_MAX_BEACON_INTERVAL 1000 +/** Default beacon interval */ +#define MLAN_BEACON_INTERVAL 100 + +/** Receive all packets */ +#define MLAN_PROMISC_MODE 1 +/** Receive multicast packets in multicast list */ +#define MLAN_MULTICAST_MODE 2 +/** Receive all multicast packets */ +#define MLAN_ALL_MULTI_MODE 4 + +/** Maximum size of multicast list */ +#define MLAN_MAX_MULTICAST_LIST_SIZE 32 + +/** mlan_multicast_list data structure for MLAN_OID_BSS_MULTICAST_LIST */ +typedef struct _mlan_multicast_list { + /** Multicast mode */ + t_u32 mode; + /** Number of multicast addresses in the list */ + t_u32 num_multicast_addr; + /** Multicast address list */ + mlan_802_11_mac_addr mac_list[MLAN_MAX_MULTICAST_LIST_SIZE]; +} mlan_multicast_list, *pmlan_multicast_list; + +/** Max channel */ +#define MLAN_MAX_CHANNEL 165 +/** Maximum number of channels in table */ +#define MLAN_MAX_CHANNEL_NUM 128 + +/** Channel/frequence for MLAN_OID_BSS_CHANNEL */ +typedef struct _chan_freq { + /** Channel Number */ + t_u32 channel; + /** Frequency of this Channel */ + t_u32 freq; +} chan_freq; + +/** mlan_chan_list data structure for MLAN_OID_BSS_CHANNEL_LIST */ +typedef struct _mlan_chan_list { + /** Number of channel */ + t_u32 num_of_chan; + /** Channel-Frequency table */ + chan_freq cf[MLAN_MAX_CHANNEL_NUM]; +} mlan_chan_list; + +/* This channel is disabled.*/ +#define CHAN_FLAGS_DISABLED MBIT(0) +/* do not initiate radiation, this includes sending probe requests or beaconing + */ +#define CHAN_FLAGS_NO_IR MBIT(1) +/* Radar detection is required on this channel */ +#define CHAN_FLAGS_RADAR MBIT(3) +/* extension channel above this channel is not permitted */ +#define CHAN_FLAGS_NO_HT40PLUS MBIT(4) +/* extension channel below this channel is not permitted */ +#define CHAN_FLAGS_NO_HT40MINUS MBIT(5) +/* OFDM is not allowed on this channel */ +#define CHAN_FLAGS_NO_OFDM MBIT(6) +/** 80Mhz can not used on this channel */ +#define CHAN_FLAGS_NO_80MHZ MBIT(7) +/** 180Mhz can not used on this channel */ +#define CHAN_FLAGS_NO_160MHZ MBIT(8) +/* Only indoor use is permitted on this channel */ +#define CHAN_FLAGS_INDOOR_ONLY MBIT(9) +/* IR operation is allowed on this channel if it's + * connected concurrently to a BSS on the same channel on + * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz + * band), and IEEE80211_CHAN_RADAR is not set */ +#define CHAN_FLAGS_IR_CONCURRENT MBIT(10) +/* 20 MHz operation is not allowed on this channel */ +#define CHAN_FLAGS_20MHZ MBIT(11) +/* 10 MHz operation is not allowed on this channel */ +#define CHAN_FLAGS_NO_10MHZ MBIT(12) +/** This channel's flag is valid */ +#define CHAN_FLAGS_MAX MBIT(31) + +/** Maximum response buffer length */ +#define ASSOC_RSP_BUF_SIZE 500 + +/** Type definition of mlan_ds_misc_assoc_rsp for MLAN_OID_MISC_ASSOC_RSP */ +typedef struct _mlan_ds_misc_assoc_rsp { + /** Associate response buffer */ + t_u8 assoc_resp_buf[ASSOC_RSP_BUF_SIZE]; + /** Response buffer length */ + t_u32 assoc_resp_len; +} mlan_ds_misc_assoc_rsp, *pmlan_ds_misc_assoc_rsp; + +/** mlan_ssid_bssid data structure for + * MLAN_OID_BSS_START and MLAN_OID_BSS_FIND_BSS + */ +typedef struct _mlan_ssid_bssid { + /** SSID */ + mlan_802_11_ssid ssid; + /** BSSID */ + mlan_802_11_mac_addr bssid; + /** index in BSSID list, start from 1 */ + t_u32 idx; + /** Receive signal strength in dBm */ + t_s32 rssi; + /**channel*/ + t_u16 channel; + /**mobility domain value*/ + t_u16 ft_md; + /**ft capability*/ + t_u8 ft_cap; + /**band*/ + t_u16 bss_band; + /** channel flag */ + t_u32 channel_flags; + /** host mlme flag*/ + t_u8 host_mlme; + /** assoicate resp frame/ie from firmware */ + mlan_ds_misc_assoc_rsp assoc_rsp; +} mlan_ssid_bssid, *pmlan_ssid_bssid; + +/** Data structure of WMM ECW */ +typedef struct _wmm_ecw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max : 4; + /** Minimum Ecw */ + t_u8 ecw_min : 4; +#else + /** Minimum Ecw */ + t_u8 ecw_min : 4; + /** Maximum Ecw */ + t_u8 ecw_max : 4; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_ecw_t, *pwmm_ecw_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef struct _wmm_aci_aifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved : 1; + /** Aci */ + t_u8 aci : 2; + /** Acm */ + t_u8 acm : 1; + /** Aifsn */ + t_u8 aifsn : 4; +#else + /** Aifsn */ + t_u8 aifsn : 4; + /** Acm */ + t_u8 acm : 1; + /** Aci */ + t_u8 aci : 2; + /** Reserved */ + t_u8 reserved : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_aci_aifsn_t, *pwmm_aci_aifsn_t; + +/** Data structure of WMM AC parameters */ +typedef struct _wmm_ac_parameters_t { + wmm_aci_aifsn_t aci_aifsn; /**< AciAifSn */ + wmm_ecw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} wmm_ac_parameters_t, *pwmm_ac_parameters_t; + +/** mlan_deauth_param */ +typedef struct _mlan_deauth_param { + /** STA mac addr */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** deauth reason */ + t_u16 reason_code; +} mlan_deauth_param; + +#ifdef UAP_SUPPORT +/** UAP FLAG: Host based */ +#define UAP_FLAG_HOST_BASED MBIT(0) +/** UAP FLAG: Host mlme */ +#define UAP_FLAG_HOST_MLME MBIT(1) + +/** 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 64 +/** Maximum RTS threshold */ +#define MAX_RTS_THRESHOLD 2347 +/** Maximum fragmentation threshold */ +#define MAX_FRAG_THRESHOLD 2346 +/** Minimum fragmentation threshold */ +#define MIN_FRAG_THRESHOLD 256 +/** data rate 54 M */ +#define DATA_RATE_54M 108 +/** Maximum value of bcast_ssid_ctl */ +#define MAX_BCAST_SSID_CTL 2 +/** antenna A */ +#define ANTENNA_MODE_A 0 +/** antenna B */ +#define ANTENNA_MODE_B 1 +/** transmit antenna */ +#define TX_ANTENNA 1 +/** receive antenna */ +#define RX_ANTENNA 0 +/** Maximum stage out time */ +#define MAX_STAGE_OUT_TIME 864000 +/** Minimum stage out time */ +#define MIN_STAGE_OUT_TIME 50 +/** Maximum Retry Limit */ +#define MAX_RETRY_LIMIT 14 + +/** Maximum group key timer in seconds */ +#define MAX_GRP_TIMER 86400 + +/** Maximum value of 4 byte configuration */ +#define MAX_VALID_DWORD 0x7FFFFFFF /* (1 << 31) - 1 */ + +/** default UAP BAND 2.4G */ +#define DEFAULT_UAP_BAND 0 +/** default UAP channel 6 */ +#define DEFAULT_UAP_CHANNEL 6 + +/** Maximum data rates */ +#define MAX_DATA_RATES 14 + +/** auto data rate */ +#define DATA_RATE_AUTO 0 + +/**filter mode: disable */ +#define MAC_FILTER_MODE_DISABLE 0 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_ALLOW_MAC 1 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_BLOCK_MAC 2 +/** Maximum mac filter num */ +#define MAX_MAC_FILTER_NUM 64 + +/* 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 +/** WPA3 SAE */ +#define PROTOCOL_WPA3_SAE 0x100 + +/** Key_mgmt_psk */ +#define KEY_MGMT_NONE 0x04 +/** Key_mgmt_none */ +#define KEY_MGMT_PSK 0x02 +/** Key_mgmt_eap */ +#define KEY_MGMT_EAP 0x01 +/** Key_mgmt_psk_sha256 */ +#define KEY_MGMT_PSK_SHA256 0x100 +/** Key_mgmt_sae */ +#define KEY_MGMT_SAE 0x400 +/** Key_mgmt_owe */ +#define KEY_MGMT_OWE 0x200 + +/** TKIP */ +#define CIPHER_TKIP 0x04 +/** AES CCMP */ +#define CIPHER_AES_CCMP 0x08 + +/** Valid cipher bitmap */ +#define VALID_CIPHER_BITMAP 0x0c + +/** Packet forwarding to be done by FW or host */ +#define PKT_FWD_FW_BIT 0x01 +/** Intra-BSS broadcast packet forwarding allow bit */ +#define PKT_FWD_INTRA_BCAST 0x02 +/** Intra-BSS unicast packet forwarding allow bit */ +#define PKT_FWD_INTRA_UCAST 0x04 +/** Inter-BSS unicast packet forwarding allow bit */ +#define PKT_FWD_INTER_UCAST 0x08 +/** Intra-BSS unicast packet */ +#define PKT_INTRA_UCAST 0x01 +/** Inter-BSS unicast packet */ +#define PKT_INTER_UCAST 0x02 +/** Enable Host PKT forwarding */ +#define PKT_FWD_ENABLE_BIT 0x01 + +/** Channel List Entry */ +typedef struct _channel_list { + /** Channel Number */ + t_u8 chan_number; + /** Band Config */ + Band_Config_t bandcfg; +} scan_chan_list; + +/** mac_filter data structure */ +typedef struct _mac_filter { + /** mac filter mode */ + t_u16 filter_mode; + /** mac adress count */ + t_u16 mac_count; + /** mac address list */ + mlan_802_11_mac_addr mac_list[MAX_MAC_FILTER_NUM]; +} mac_filter; + +/** wpa parameter */ +typedef struct _wpa_param { + /** Pairwise cipher WPA */ + t_u8 pairwise_cipher_wpa; + /** Pairwise cipher WPA2 */ + t_u8 pairwise_cipher_wpa2; + /** group cipher */ + t_u8 group_cipher; + /** RSN replay protection */ + t_u8 rsn_protection; + /** passphrase length */ + t_u32 length; + /** passphrase */ + t_u8 passphrase[64]; + /**group key rekey time in seconds */ + t_u32 gk_rekey_time; +} wpa_param; + +/** wep key */ +typedef struct _wep_key { + /** key index 0-3 */ + t_u8 key_index; + /** is default */ + t_u8 is_default; + /** length */ + t_u16 length; + /** key data */ + t_u8 key[26]; +} wep_key; + +/** wep param */ +typedef struct _wep_param { + /** key 0 */ + wep_key key0; + /** key 1 */ + wep_key key1; + /** key 2 */ + wep_key key2; + /** key 3 */ + wep_key key3; +} wep_param; + +/** Data structure of WMM QoS information */ +typedef struct _wmm_qos_info_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; + /** Reserved */ + t_u8 reserved : 3; + /** Parameter set count */ + t_u8 para_set_count : 4; +#else + /** Parameter set count */ + t_u8 para_set_count : 4; + /** Reserved */ + t_u8 reserved : 3; + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_qos_info_t, *pwmm_qos_info_t; + +/** Data structure of WMM parameter IE */ +typedef struct _wmm_parameter_t { + /** OuiType: 00:50:f2:02 */ + t_u8 ouitype[4]; + /** Oui subtype: 01 */ + t_u8 ouisubtype; + /** version: 01 */ + t_u8 version; + /** QoS information */ + t_u8 qos_info; + /** Reserved */ + t_u8 reserved; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; +} wmm_parameter_t, *pwmm_parameter_t; + +/** MAX BG channel */ +#define MAX_BG_CHANNEL 14 +/** mlan_bss_param + * Note: For each entry you must enter an invalid value + * in the MOAL function woal_set_sys_config_invalid_data(). + * Otherwise for a valid data an unwanted TLV will be + * added to that command. + */ +typedef struct _mlan_uap_bss_param { + /** AP mac addr */ + mlan_802_11_mac_addr mac_addr; + /** SSID */ + mlan_802_11_ssid ssid; + /** Broadcast ssid control */ + t_u8 bcast_ssid_ctl; + /** Radio control: on/off */ + t_u8 radio_ctl; + /** dtim period */ + t_u8 dtim_period; + /** beacon period */ + t_u16 beacon_period; + /** rates */ + t_u8 rates[MAX_DATA_RATES]; + /** Tx data rate */ + t_u16 tx_data_rate; + /** Tx beacon rate */ + t_u16 tx_beacon_rate; + /** multicast/broadcast data rate */ + t_u16 mcbc_data_rate; + /** Tx power level in dBm */ + t_u8 tx_power_level; + /** Tx antenna */ + t_u8 tx_antenna; + /** Rx antenna */ + t_u8 rx_antenna; + /** packet forward control */ + t_u8 pkt_forward_ctl; + /** max station count */ + t_u16 max_sta_count; + /** mac filter */ + mac_filter filter; + /** station ageout timer in unit of 100ms */ + t_u32 sta_ageout_timer; + /** PS station ageout timer in unit of 100ms */ + t_u32 ps_sta_ageout_timer; + /** RTS threshold */ + t_u16 rts_threshold; + /** fragmentation threshold */ + t_u16 frag_threshold; + /** retry_limit */ + t_u16 retry_limit; + /** pairwise update timeout in milliseconds */ + t_u32 pairwise_update_timeout; + /** pairwise handshake retries */ + t_u32 pwk_retries; + /** groupwise update timeout in milliseconds */ + t_u32 groupwise_update_timeout; + /** groupwise handshake retries */ + t_u32 gwk_retries; + /** preamble type */ + t_u8 preamble_type; + /** band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** auth mode */ + t_u16 auth_mode; + /** encryption protocol */ + t_u16 protocol; + /** key managment type */ + t_u16 key_mgmt; + /** wep param */ + wep_param wep_cfg; + /** wpa param */ + wpa_param wpa_cfg; + /** Mgmt IE passthru mask */ + t_u32 mgmt_ie_passthru_mask; + /* + * 11n HT Cap HTCap_t ht_cap + */ + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; + /** Enable 2040 Coex */ + t_u8 enable_2040coex; + /** key management operation */ + t_u16 key_mgmt_operation; + /** BSS status */ + t_u16 bss_status; +#ifdef WIFI_DIRECT_SUPPORT + /* pre shared key */ + t_u8 psk[MLAN_MAX_KEY_LENGTH]; +#endif /* WIFI_DIRECT_SUPPORT */ + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; + /** Wmm parameters */ + wmm_parameter_t wmm_para; + + /** uap host based config */ + t_u32 uap_host_based_config; +} mlan_uap_bss_param, *pmlan_uap_bss_param; + +/** mlan_uap_scan_channels */ +typedef struct _mlan_uap_scan_channels { + /** flag for remove nop channel*/ + t_u8 remove_nop_channel; + /** num of removed channel */ + t_u8 num_remvoed_channel; + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; +} mlan_uap_scan_channels; + +/** mlan_uap_oper_ctrl */ +typedef struct _mlan_uap_oper_ctrl { + /** control value + * 0: do nothing, + * 2: uap stops and restarts automaticaly + */ + t_u16 ctrl_value; + /** channel opt + * 1: uap restart on default 2.4G/channel 6 + * 2: uap restart on the band/channel configured by driver previously + * 3: uap restart on the band/channel specified by band_cfg and channel + */ + t_u16 chan_opt; + /** band cfg 0 + * 0: 20Mhz 2: 40 Mhz 3: 80Mhz + */ + t_u8 band_cfg; + /** channel */ + t_u8 channel; +} mlan_uap_oper_ctrl; + +/** mlan_uap_acs_scan */ +typedef struct _mlan_uap_acs_scan { + /** band */ + Band_Config_t bandcfg; + /** channel */ + t_u8 chan; +} mlan_uap_acs_scan; + +/** station is authorized (802.1X) */ +#define STA_FLAG_AUTHORIZED MBIT(1) +/** Station is capable of receiving frames with short barker preamble */ +#define STA_FLAG_SHORT_PREAMBLE MBIT(2) +/** station is WME/QoS capable */ +#define STA_FLAG_WME MBIT(3) +/** station uses management frame protection */ +#define STA_FLAG_MFP MBIT(4) +/** station is authenticated */ +#define STA_FLAG_AUTHENTICATED MBIT(5) +/** station is a TDLS peer */ +#define STA_FLAG_TDLS_PEER MBIT(6) +/** station is associated */ +#define STA_FLAG_ASSOCIATED MBIT(7) +/** mlan_ds_sta_info */ +typedef struct _mlan_ds_sta_info { + /** aid */ + t_u16 aid; + /** peer_mac */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Listen Interval */ + int listen_interval; + /** Capability Info */ + t_u16 cap_info; + /** station flag */ + t_u32 sta_flags; + /** tlv len */ + t_u16 tlv_len; + /** tlv start */ + t_u8 tlv[]; +} mlan_ds_sta_info; +#endif + +#ifdef WIFI_DIRECT_SUPPORT +/** mode: disable wifi direct */ +#define WIFI_DIRECT_MODE_DISABLE 0 +/** mode: listen */ +#define WIFI_DIRECT_MODE_LISTEN 1 +/** mode: GO */ +#define WIFI_DIRECT_MODE_GO 2 +/** mode: client */ +#define WIFI_DIRECT_MODE_CLIENT 3 +/** mode: find */ +#define WIFI_DIRECT_MODE_FIND 4 +/** mode: stop find */ +#define WIFI_DIRECT_MODE_STOP_FIND 5 +#endif + +/** Type definition of mlan_ds_bss for MLAN_IOCTL_BSS */ +typedef struct _mlan_ds_bss { + /** Sub-command */ + t_u32 sub_command; + /** BSS parameter */ + union { + /** SSID-BSSID for MLAN_OID_BSS_START */ + mlan_ssid_bssid ssid_bssid; + /** BSSID for MLAN_OID_BSS_STOP */ + mlan_802_11_mac_addr bssid; + /** BSS mode for MLAN_OID_BSS_MODE */ + t_u32 bss_mode; + /** BSS channel/frequency for MLAN_OID_BSS_CHANNEL */ + chan_freq bss_chan; + /** BSS channel list for MLAN_OID_BSS_CHANNEL_LIST */ + mlan_chan_list chanlist; + /** MAC address for MLAN_OID_BSS_MAC_ADDR */ + mlan_802_11_mac_addr mac_addr; + /** Multicast list for MLAN_OID_BSS_MULTICAST_LIST */ + mlan_multicast_list multicast_list; + /** Beacon interval for MLAN_OID_IBSS_BCN_INTERVAL */ + t_u32 bcn_interval; + /** ATIM window for MLAN_OID_IBSS_ATIM_WINDOW */ + t_u32 atim_window; + /** deauth param for MLAN_OID_BSS_STOP & MLAN_OID_UAP_DEAUTH_STA + */ + mlan_deauth_param deauth_param; +#ifdef UAP_SUPPORT + /** host based flag for MLAN_OID_BSS_START */ + t_u8 host_based; + /** BSS param for AP mode for MLAN_OID_UAP_BSS_CONFIG */ + mlan_uap_bss_param bss_config; + /** AP Wmm parameters for MLAN_OID_UAP_CFG_WMM_PARAM */ + wmm_parameter_t ap_wmm_para; + /** ap scan channels for MLAN_OID_UAP_SCAN_CHANNELS*/ + mlan_uap_scan_channels ap_scan_channels; + /** ap channel for MLAN_OID_UAP_CHANNEL*/ + chan_band_info ap_channel; + /** ap operation control for MLAN_OID_UAP_OPER_CTRL*/ + mlan_uap_oper_ctrl ap_oper_ctrl; + /** AP acs scan MLAN_OID_UAP_ACS_SCAN */ + mlan_uap_acs_scan ap_acs_scan; +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + /** BSS role for MLAN_OID_BSS_ROLE */ + t_u8 bss_role; +#endif +#ifdef WIFI_DIRECT_SUPPORT + /** wifi direct mode for MLAN_OID_WIFI_DIRECT_MODE */ + t_u16 wfd_mode; +#endif +#ifdef STA_SUPPORT + /** Listen interval for MLAN_OID_BSS_LISTEN_INTERVAL */ + t_u16 listen_interval; + /** STA channel info for MLAN_OID_BSS_CHAN_INFO */ + chan_band_info sta_channel; +#endif +#ifdef UAP_SUPPORT + /** STA info for MLAN_OID_UAP_ADD_STATION */ + mlan_ds_sta_info sta_info; +#endif + } param; +} mlan_ds_bss, *pmlan_ds_bss; + +/* OTP Region info */ +typedef MLAN_PACK_START struct _otp_region_info { + t_u8 country_code[2]; + t_u8 region_code; + t_u8 environment; + t_u16 force_reg : 1; + t_u16 reserved : 15; +} MLAN_PACK_END otp_region_info_t; + +/** Type definition of mlan_ds_custom_reg_domain */ +typedef struct _mlan_ds_custom_reg_domain { + otp_region_info_t region; + /** num of 2g channels in custom_reg_domain */ + t_u8 num_bg_chan; + /** num of 5g channels in custom_reg_domain */ + t_u8 num_a_chan; + /** cfp table */ + chan_freq_power_t cfp_tbl[]; +} mlan_ds_custom_reg_domain; +/*-----------------------------------------------------------------*/ +/** Radio Control Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for band */ +enum _mlan_band_def { + BAND_B = 1, + BAND_G = 2, + BAND_A = 4, + BAND_GN = 8, + BAND_AN = 16, + BAND_GAC = 32, + BAND_AAC = 64, + BAND_GAX = 256, + BAND_AAX = 512, + +}; + +/** Channel bandwidth */ +#define CHANNEL_BW_20MHZ 0 +#define CHANNEL_BW_40MHZ_ABOVE 1 +#define CHANNEL_BW_40MHZ_BELOW 3 +/** secondary channel is 80Mhz bandwidth for 11ac */ +#define CHANNEL_BW_80MHZ 4 +#define CHANNEL_BW_160MHZ 5 + +/** RF antenna selection */ +#define RF_ANTENNA_MASK(n) ((1 << (n)) - 1) +/** RF antenna auto select */ +#define RF_ANTENNA_AUTO 0xFFFF + +/** Type definition of mlan_ds_band_cfg for MLAN_OID_BAND_CFG */ +typedef struct _mlan_ds_band_cfg { + /** Infra band */ + t_u32 config_bands; + /** Ad-hoc start band */ + t_u32 adhoc_start_band; + /** Ad-hoc start channel */ + t_u32 adhoc_channel; + /** 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; +/** Type definition of mlan_ds_mimo_switch for MLAN_OID_MIMO_SWITCH */ +typedef struct _mlan_ds_mimo_switch { + /** Tx antenna mode */ + t_u8 txpath_antmode; + /** Rx antenna mode */ + t_u8 rxpath_antmode; +} mlan_ds_mimo_switch, *pmlan_ds_mimo_switch; +/** Type definition of mlan_ds_ant_cfg_1x1 for MLAN_OID_ANT_CFG */ +typedef struct _mlan_ds_ant_cfg_1x1 { + /** Antenna mode */ + t_u32 antenna; + /** Evaluate time */ + t_u16 evaluate_time; + /** Current antenna */ + t_u16 current_antenna; +} mlan_ds_ant_cfg_1x1, *pmlan_ds_ant_cfg_1x1; + +/** Type definition of mlan_ds_remain_chan for MLAN_OID_REMAIN_CHAN_CFG */ +typedef struct _mlan_ds_remain_chan { + /** remove flag */ + t_u16 remove; + /** status */ + t_u8 status; + /** Band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** remain time: Unit ms*/ + t_u32 remain_period; +} mlan_ds_remain_chan, *pmlan_ds_remain_chan; + +/** Type definition of mlan_ds_radio_cfg for MLAN_IOCTL_RADIO_CFG */ +typedef struct _mlan_ds_radio_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Radio control parameter */ + union { + /** Radio on/off for MLAN_OID_RADIO_CTRL */ + t_u32 radio_on_off; + /** Band info for MLAN_OID_BAND_CFG */ + mlan_ds_band_cfg band_cfg; + /** Antenna info for MLAN_OID_ANT_CFG */ + mlan_ds_ant_cfg ant_cfg; + /** Antenna mode for MLAN_OID_MIMO_SWITCH */ + mlan_ds_mimo_switch mimo_switch_cfg; + /** Antenna info for MLAN_OID_ANT_CFG */ + mlan_ds_ant_cfg_1x1 ant_cfg_1x1; + /** remain on channel for MLAN_OID_REMAIN_CHAN_CFG */ + mlan_ds_remain_chan remain_chan; + } param; +} mlan_ds_radio_cfg, *pmlan_ds_radio_cfg; + +enum COALESCE_OPERATION { + RECV_FILTER_MATCH_TYPE_EQ = 0x80, + RECV_FILTER_MATCH_TYPE_NE, +}; + +enum COALESCE_PACKET_TYPE { + PACKET_TYPE_UNICAST = 1, + PACKET_TYPE_MULTICAST = 2, + PACKET_TYPE_BROADCAST = 3 +}; + +#define COALESCE_MAX_RULES 8 +#define COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ +#define COALESCE_MAX_FILTERS 4 +#define MAX_COALESCING_DELAY 100 /* in msecs */ +#define MAX_PATTERN_LEN 20 +#define MAX_OFFSET_LEN 100 + +struct filt_field_param { + t_u8 operation; + t_u8 operand_len; + t_u16 offset; + t_u8 operand_byte_stream[COALESCE_MAX_BYTESEQ]; +}; + +struct coalesce_rule { + t_u16 max_coalescing_delay; + t_u8 num_of_fields; + t_u8 pkt_type; + struct filt_field_param params[COALESCE_MAX_FILTERS]; +}; + +typedef struct _mlan_ds_coalesce_cfg { + t_u16 num_of_rules; + struct coalesce_rule rule[COALESCE_MAX_RULES]; +} mlan_ds_coalesce_cfg; + +/*-----------------------------------------------------------------*/ +/** SNMP MIB Group */ +/*-----------------------------------------------------------------*/ +/** Type definition of mlan_ds_snmp_mib for MLAN_IOCTL_SNMP_MIB */ +typedef struct _mlan_ds_snmp_mib { + /** Sub-command */ + t_u32 sub_command; + /** SNMP MIB parameter */ + union { + /** RTS threshold for MLAN_OID_SNMP_MIB_RTS_THRESHOLD */ + t_u32 rts_threshold; + /** Fragment threshold for MLAN_OID_SNMP_MIB_FRAG_THRESHOLD */ + t_u32 frag_threshold; + /** Retry count for MLAN_OID_SNMP_MIB_RETRY_COUNT */ + t_u32 retry_count; + /** OID value for MLAN_OID_SNMP_MIB_DOT11D/H */ + t_u32 oid_value; + /** DTIM period for MLAN_OID_SNMP_MIB_DTIM_PERIOD */ + t_u32 dtim_period; + /** Singal_ext Enable for MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE */ + t_u8 signalext_enable; + /** Control deauth when uap switch channel */ + t_u8 deauthctrl; + } param; +} mlan_ds_snmp_mib, *pmlan_ds_snmp_mib; + +/*-----------------------------------------------------------------*/ +/** Status Information Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for ad-hoc status */ +enum _mlan_adhoc_status { + ADHOC_IDLE, + ADHOC_STARTED, + ADHOC_JOINED, + ADHOC_COALESCED, + ADHOC_STARTING +}; + +typedef struct _mlan_ds_get_stats_org { + /** Statistics counter */ + /** Multicast transmitted frame count */ + t_u32 mcast_tx_frame; + /** Failure count */ + t_u32 failed; + /** Retry count */ + t_u32 retry; + /** Multi entry count */ + t_u32 multi_retry; + /** Duplicate frame count */ + t_u32 frame_dup; + /** RTS success count */ + t_u32 rts_success; + /** RTS failure count */ + t_u32 rts_failure; + /** Ack failure count */ + t_u32 ack_failure; + /** Rx fragmentation count */ + t_u32 rx_frag; + /** Multicast Tx frame count */ + t_u32 mcast_rx_frame; + /** FCS error count */ + t_u32 fcs_error; + /** Tx frame count */ + t_u32 tx_frame; + /** WEP ICV error count */ + t_u32 wep_icv_error[4]; + /** beacon recv count */ + t_u32 bcn_rcv_cnt; + /** beacon miss count */ + t_u32 bcn_miss_cnt; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; +} mlan_ds_get_stats_org; + +/** Type definition of mlan_ds_get_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_get_stats { + /** Statistics counter */ + /** Multicast transmitted frame count */ + t_u32 mcast_tx_frame; + /** Failure count */ + t_u32 failed; + /** Retry count */ + t_u32 retry; + /** Multi entry count */ + t_u32 multi_retry; + /** Duplicate frame count */ + t_u32 frame_dup; + /** RTS success count */ + t_u32 rts_success; + /** RTS failure count */ + t_u32 rts_failure; + /** Ack failure count */ + t_u32 ack_failure; + /** Rx fragmentation count */ + t_u32 rx_frag; + /** Multicast Tx frame count */ + t_u32 mcast_rx_frame; + /** FCS error count */ + t_u32 fcs_error; + /** Tx frame count */ + t_u32 tx_frame; + /** WEP ICV error count */ + t_u32 wep_icv_error[4]; + /** beacon recv count */ + t_u32 bcn_rcv_cnt; + /** beacon miss count */ + t_u32 bcn_miss_cnt; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; + + /** Tx frag count */ + t_u32 tx_frag_cnt; + /** Qos Tx frag count */ + t_u32 qos_tx_frag_cnt[8]; + /** Qos failed count */ + t_u32 qos_failed_cnt[8]; + /** Qos retry count */ + t_u32 qos_retry_cnt[8]; + /** Qos multi retry count */ + t_u32 qos_multi_retry_cnt[8]; + /** Qos frame dup count */ + t_u32 qos_frm_dup_cnt[8]; + /** Qos rts success count */ + t_u32 qos_rts_suc_cnt[8]; + /** Qos rts failure count */ + t_u32 qos_rts_failure_cnt[8]; + /** Qos ack failure count */ + t_u32 qos_ack_failure_cnt[8]; + /** Qos Rx frag count */ + t_u32 qos_rx_frag_cnt[8]; + /** Qos Tx frame count */ + t_u32 qos_tx_frm_cnt[8]; + /** Qos discarded frame count */ + t_u32 qos_discarded_frm_cnt[8]; + /** Qos mpdus Rx count */ + t_u32 qos_mpdus_rx_cnt[8]; + /** Qos retry rx count */ + t_u32 qos_retries_rx_cnt[8]; + /** CMAC ICV errors count */ + t_u32 cmacicv_errors; + /** CMAC replays count */ + t_u32 cmac_replays; + /** mgmt CCMP replays count */ + t_u32 mgmt_ccmp_replays; + /** TKIP ICV errors count */ + t_u32 tkipicv_errors; + /** TKIP replays count */ + t_u32 tkip_replays; + /** CCMP decrypt errors count */ + t_u32 ccmp_decrypt_errors; + /** CCMP replays count */ + t_u32 ccmp_replays; + /** Tx amsdu count */ + t_u32 tx_amsdu_cnt; + /** failed amsdu count */ + t_u32 failed_amsdu_cnt; + /** retry amsdu count */ + t_u32 retry_amsdu_cnt; + /** multi-retry amsdu count */ + t_u32 multi_retry_amsdu_cnt; + /** Tx octets in amsdu count */ + t_u64 tx_octets_in_amsdu_cnt; + /** amsdu ack failure count */ + t_u32 amsdu_ack_failure_cnt; + /** Rx amsdu count */ + t_u32 rx_amsdu_cnt; + /** Rx octets in amsdu count */ + t_u64 rx_octets_in_amsdu_cnt; + /** Tx ampdu count */ + t_u32 tx_ampdu_cnt; + /** tx mpdus in ampdu count */ + t_u32 tx_mpdus_in_ampdu_cnt; + /** tx octets in ampdu count */ + t_u64 tx_octets_in_ampdu_cnt; + /** ampdu Rx count */ + t_u32 ampdu_rx_cnt; + /** mpdu in Rx ampdu count */ + t_u32 mpdu_in_rx_ampdu_cnt; + /** Rx octets ampdu count */ + t_u64 rx_octets_in_ampdu_cnt; + /** ampdu delimiter CRC error count */ + t_u32 ampdu_delimiter_crc_error_cnt; + /** Rx Stuck Related Info*/ + /** Rx Stuck Issue count */ + t_u32 rx_stuck_issue_cnt[2]; + /** Rx Stuck Recovery count */ + t_u32 rx_stuck_recovery_cnt; + /** Rx Stuck TSF */ + t_u64 rx_stuck_tsf[2]; + /** Tx Watchdog Recovery Related Info */ + /** Tx Watchdog Recovery count */ + t_u32 tx_watchdog_recovery_cnt; + /** Tx Watchdog TSF */ + t_u64 tx_watchdog_tsf[2]; + /** Channel Switch Related Info */ + /** Channel Switch Announcement Sent */ + t_u32 channel_switch_ann_sent; + /** Channel Switch State */ + t_u32 channel_switch_state; + /** Register Class */ + t_u32 reg_class; + /** Channel Number */ + t_u32 channel_number; + /** Channel Switch Mode */ + t_u32 channel_switch_mode; +} 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 +#define MAX_PATH_NUM 3 +/** path A */ +#define PATH_A 0x01 +/** path B */ +#define PATH_B 0x02 +/** path AB */ +#define PATH_AB 0x03 +/** ALL the path */ +#define PATH_ALL 0 +/** Type definition of mlan_ds_get_signal for MLAN_OID_GET_SIGNAL */ +typedef struct _mlan_ds_get_signal { + /** Selector of get operation */ + /* + * Bit0: Last Beacon RSSI, Bit1: Average Beacon RSSI, + * Bit2: Last Data RSSI, Bit3: Average Data RSSI, + * Bit4: Last Beacon SNR, Bit5: Average Beacon SNR, + * Bit6: Last Data SNR, Bit7: Average Data SNR, + * Bit8: Last Beacon NF, Bit9: Average Beacon NF, + * Bit10: Last Data NF, Bit11: Average Data NF + * + * Bit0: PATH A + * Bit1: PATH B + */ + t_u16 selector; + + /** RSSI */ + /** RSSI of last beacon */ + t_s16 bcn_rssi_last; + /** RSSI of beacon average */ + t_s16 bcn_rssi_avg; + /** RSSI of last data packet */ + t_s16 data_rssi_last; + /** RSSI of data packet average */ + t_s16 data_rssi_avg; + + /** SNR */ + /** SNR of last beacon */ + t_s16 bcn_snr_last; + /** SNR of beacon average */ + t_s16 bcn_snr_avg; + /** SNR of last data packet */ + t_s16 data_snr_last; + /** SNR of data packet average */ + t_s16 data_snr_avg; + + /** NF */ + /** NF of last beacon */ + t_s16 bcn_nf_last; + /** NF of beacon average */ + t_s16 bcn_nf_avg; + /** NF of last data packet */ + t_s16 data_nf_last; + /** NF of data packet average */ + t_s16 data_nf_avg; +} mlan_ds_get_signal, *pmlan_ds_get_signal; + +/** bit for 2.4 G antenna diversity */ +#define ANT_DIVERSITY_2G MBIT(3) +/** bit for 5 G antenna diversity */ +#define ANT_DIVERSITY_5G MBIT(7) + +/** mlan_fw_info data structure for MLAN_OID_GET_FW_INFO */ +typedef struct _mlan_fw_info { + /** Firmware version */ + t_u32 fw_ver; + /** MAC address */ + mlan_802_11_mac_addr mac_addr; + /** 802.11n device capabilities */ + t_u32 hw_dot_11n_dev_cap; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; + /** user's MCS setting */ + t_u8 usr_dev_mcs_support; + /** 802.11ac device capabilities */ + t_u32 hw_dot_11ac_dev_cap; + /** 802.11ac device Capabilities for 2.4GHz */ + t_u32 usr_dot_11ac_dev_cap_bg; + /** 802.11ac device Capabilities for 5GHz */ + t_u32 usr_dot_11ac_dev_cap_a; + /** length of hw he capability */ + t_u8 hw_hecap_len; + /** 802.11ax HE capability */ + t_u8 hw_he_cap[54]; + /** length of hw 2.4G he capability */ + t_u8 hw_2g_hecap_len; + /** 802.11ax 2.4G HE capability */ + t_u8 hw_2g_he_cap[54]; + /** 802.11ac device support for MIMO abstraction of MCSs */ + t_u32 hw_dot_11ac_mcs_support; + /** User conf 802.11ac device support for MIMO abstraction of MCSs */ + t_u32 usr_dot_11ac_mcs_support; + /** fw supported band */ + t_u16 fw_bands; + /** region code */ + t_u16 region_code; + /** force_reg */ + t_u8 force_reg; + /** ECSA support */ + t_u8 ecsa_enable; + /** Get log support */ + t_u8 getlog_enable; + /** FW support for embedded supplicant */ + t_u8 fw_supplicant_support; + /** ant info */ + t_u8 antinfo; + /** max AP associated sta count supported by fw */ + t_u8 max_ap_assoc_sta; + /** Bandwidth not support 80Mhz */ + t_u8 prohibit_80mhz; +} mlan_fw_info, *pmlan_fw_info; + +/** Version string buffer length */ +#define MLAN_MAX_VER_STR_LEN 128 + +/** mlan_ver_ext data structure for MLAN_OID_GET_VER_EXT */ +typedef struct _mlan_ver_ext { + /** Selected version string */ + t_u32 version_str_sel; + /** Version string */ + char version_str[MLAN_MAX_VER_STR_LEN]; +} mlan_ver_ext, *pmlan_ver_ext; + +#ifdef BIG_ENDIAN_SUPPORT +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t { + /** Extended Capabilities value */ + t_u8 rsvdBit79 : 1; /* bit 79 */ + t_u8 TWTResp : 1; /* bit 78 */ + t_u8 TWTReq : 1; /* bit 77 */ + t_u8 rsvdBit76 : 1; /* bit 76 */ + t_u8 rsvdBit75 : 1; /* bit 75 */ + t_u8 rsvdBit74 : 1; /* bit 74 */ + t_u8 rsvdBit73 : 1; /* bit 73 */ + t_u8 FILS : 1; /* bit 72 */ + t_u8 FTMI : 1; /* bit 71 */ + t_u8 FTMR : 1; /* bit 70 */ + t_u8 CAQ : 1; /* bit 69 */ + t_u8 rsvdBit68 : 1; /* bit 68 */ + t_u8 NCC : 1; /* bit 67 */ + t_u8 rsvdBit66 : 1; /* bit 66 */ + t_u8 chanSchedMgnt : 1; /* bit 65 */ + t_u8 MaxAMSDU1 : 1; /* bit 64 */ + t_u8 MaxAMSDU0 : 1; /* bit 63 */ + t_u8 OperModeNtf : 1; /* bit 62 */ + t_u8 TDLSWildBandwidth : 1; /* bit 61 */ + t_u8 rsvdBit60 : 1; /* bit 60 */ + t_u8 rsvdBit59 : 1; /* bit 59 */ + t_u8 rsvdBit58 : 1; /* bit 58 */ + t_u8 rsvdBit57 : 1; /* bit 57 */ + t_u8 rsvdBit56 : 1; /* bit 56 */ + t_u8 rsvdBit55 : 1; /* bit 55 */ + t_u8 rsvdBit54 : 1; /* bit 54 */ + t_u8 rsvdBit53 : 1; /* bit 53 */ + t_u8 rsvdBit52 : 1; /* bit 52 */ + t_u8 rsvdBit51 : 1; /* bit 51 */ + t_u8 rsvdBit50 : 1; /* bit 50 */ + t_u8 rsvdBit49 : 1; /* bit 49 */ + t_u8 rsvdBit48 : 1; /* bit 48 */ + t_u8 rsvdBit47 : 1; /* bit 47 */ + t_u8 rsvdBit46 : 1; /* bit 46 */ + t_u8 rsvdBit45 : 1; /* bit 45 */ + t_u8 rsvdBit44 : 1; /* bit 44 */ + t_u8 rsvdBit43 : 1; /* bit 43 */ + t_u8 rsvdBit42 : 1; /* bit 42 */ + t_u8 rsvdBit41 : 1; /* bit 41 */ + t_u8 rsvdBit40 : 1; /* bit 40 */ + t_u8 TDLSChlSwitchProhib : 1; /* bit 39 */ + t_u8 TDLSProhibited : 1; /* bit 38 */ + t_u8 TDLSSupport : 1; /* bit 37 */ + t_u8 MSGCF_Capa : 1; /* bit 36 */ + t_u8 Reserved35 : 1; /* bit 35 */ + t_u8 SSPN_Interface : 1; /* bit 34 */ + t_u8 EBR : 1; /* bit 33 */ + t_u8 Qos_Map : 1; /* bit 32 */ + t_u8 Interworking : 1; /* bit 31 */ + t_u8 TDLSChannelSwitching : 1; /* bit 30 */ + t_u8 TDLSPeerPSMSupport : 1; /* bit 29 */ + t_u8 TDLSPeerUAPSDSupport : 1; /* bit 28 */ + t_u8 UTC : 1; /* bit 27 */ + t_u8 DMS : 1; /* bit 26 */ + t_u8 SSID_List : 1; /* bit 25 */ + t_u8 ChannelUsage : 1; /* bit 24 */ + t_u8 TimingMeasurement : 1; /* bit 23 */ + t_u8 MultipleBSSID : 1; /* bit 22 */ + t_u8 AC_StationCount : 1; /* bit 21 */ + t_u8 QoSTrafficCap : 1; /* bit 20 */ + t_u8 BSS_Transition : 1; /* bit 19 */ + t_u8 TIM_Broadcast : 1; /* bit 18 */ + t_u8 WNM_Sleep : 1; /* bit 17 */ + t_u8 TFS : 1; /* bit 16 */ + t_u8 GeospatialLocation : 1; /* bit 15 */ + t_u8 CivicLocation : 1; /* bit 14 */ + t_u8 CollocatedIntf : 1; /* bit 13 */ + t_u8 ProxyARPService : 1; /* bit 12 */ + t_u8 FMS : 1; /* bit 11 */ + t_u8 LocationTracking : 1; /* bit 10 */ + t_u8 MulticastDiagnostics : 1; /* bit 9 */ + t_u8 Diagnostics : 1; /* bit 8 */ + t_u8 Event : 1; /* bit 7 */ + t_u8 SPSMP_Support : 1; /* bit 6 */ + t_u8 Reserved5 : 1; /* bit 5 */ + t_u8 PSMP_Capable : 1; /* bit 4 */ + t_u8 RejectUnadmFrame : 1; /* bit 3 */ + t_u8 ExtChanSwitching : 1; /* bit 2 */ + t_u8 Reserved1 : 1; /* bit 1 */ + t_u8 BSS_CoexistSupport : 1; /* bit 0 */ +} MLAN_PACK_END ExtCap_t, *pExtCap_t; +#else +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t { + /** Extended Capabilities value */ + t_u8 BSS_CoexistSupport : 1; /* bit 0 */ + t_u8 Reserved1 : 1; /* bit 1 */ + t_u8 ExtChanSwitching : 1; /* bit 2 */ + t_u8 RejectUnadmFrame : 1; /* bit 3 */ + t_u8 PSMP_Capable : 1; /* bit 4 */ + t_u8 Reserved5 : 1; /* bit 5 */ + t_u8 SPSMP_Support : 1; /* bit 6 */ + t_u8 Event : 1; /* bit 7 */ + t_u8 Diagnostics : 1; /* bit 8 */ + t_u8 MulticastDiagnostics : 1; /* bit 9 */ + t_u8 LocationTracking : 1; /* bit 10 */ + t_u8 FMS : 1; /* bit 11 */ + t_u8 ProxyARPService : 1; /* bit 12 */ + t_u8 CollocatedIntf : 1; /* bit 13 */ + t_u8 CivicLocation : 1; /* bit 14 */ + t_u8 GeospatialLocation : 1; /* bit 15 */ + t_u8 TFS : 1; /* bit 16 */ + t_u8 WNM_Sleep : 1; /* bit 17 */ + t_u8 TIM_Broadcast : 1; /* bit 18 */ + t_u8 BSS_Transition : 1; /* bit 19 */ + t_u8 QoSTrafficCap : 1; /* bit 20 */ + t_u8 AC_StationCount : 1; /* bit 21 */ + t_u8 MultipleBSSID : 1; /* bit 22 */ + t_u8 TimingMeasurement : 1; /* bit 23 */ + t_u8 ChannelUsage : 1; /* bit 24 */ + t_u8 SSID_List : 1; /* bit 25 */ + t_u8 DMS : 1; /* bit 26 */ + t_u8 UTC : 1; /* bit 27 */ + t_u8 TDLSPeerUAPSDSupport : 1; /* bit 28 */ + t_u8 TDLSPeerPSMSupport : 1; /* bit 29 */ + t_u8 TDLSChannelSwitching : 1; /* bit 30 */ + t_u8 Interworking : 1; /* bit 31 */ + t_u8 Qos_Map : 1; /* bit 32 */ + t_u8 EBR : 1; /* bit 33 */ + t_u8 SSPN_Interface : 1; /* bit 34 */ + t_u8 Reserved35 : 1; /* bit 35 */ + t_u8 MSGCF_Capa : 1; /* bit 36 */ + t_u8 TDLSSupport : 1; /* bit 37 */ + t_u8 TDLSProhibited : 1; /* bit 38 */ + t_u8 TDLSChlSwitchProhib : 1; /* bit 39 */ + t_u8 rsvdBit40 : 1; /* bit 40 */ + t_u8 rsvdBit41 : 1; /* bit 41 */ + t_u8 rsvdBit42 : 1; /* bit 42 */ + t_u8 rsvdBit43 : 1; /* bit 43 */ + t_u8 rsvdBit44 : 1; /* bit 44 */ + t_u8 rsvdBit45 : 1; /* bit 45 */ + t_u8 rsvdBit46 : 1; /* bit 46 */ + t_u8 rsvdBit47 : 1; /* bit 47 */ + t_u8 rsvdBit48 : 1; /* bit 48 */ + t_u8 rsvdBit49 : 1; /* bit 49 */ + t_u8 rsvdBit50 : 1; /* bit 50 */ + t_u8 rsvdBit51 : 1; /* bit 51 */ + t_u8 rsvdBit52 : 1; /* bit 52 */ + t_u8 rsvdBit53 : 1; /* bit 53 */ + t_u8 rsvdBit54 : 1; /* bit 54 */ + t_u8 rsvdBit55 : 1; /* bit 55 */ + t_u8 rsvdBit56 : 1; /* bit 56 */ + t_u8 rsvdBit57 : 1; /* bit 57 */ + t_u8 rsvdBit58 : 1; /* bit 58 */ + t_u8 rsvdBit59 : 1; /* bit 59 */ + t_u8 rsvdBit60 : 1; /* bit 60 */ + t_u8 TDLSWildBandwidth : 1; /* bit 61 */ + t_u8 OperModeNtf : 1; /* bit 62 */ + t_u8 MaxAMSDU0 : 1; /* bit 63 */ + t_u8 MaxAMSDU1 : 1; /* bit 64 */ + t_u8 chanSchedMgnt : 1; /* bit 65 */ + t_u8 rsvdBit66 : 1; /* bit 66 */ + t_u8 NCC : 1; /* bit 67 */ + t_u8 rsvdBit68 : 1; /* bit 68 */ + t_u8 CAQ : 1; /* bit 69 */ + t_u8 FTMR : 1; /* bit 70 */ + t_u8 FTMI : 1; /* bit 71 */ + t_u8 FILS : 1; /* bit 72 */ + t_u8 rsvdBit73 : 1; /* bit 73 */ + t_u8 rsvdBit74 : 1; /* bit 74 */ + t_u8 rsvdBit75 : 1; /* bit 75 */ + t_u8 rsvdBit76 : 1; /* bit 76 */ + t_u8 TWTReq : 1; /* bit 77 */ + t_u8 TWTResp : 1; /* bit 78 */ + t_u8 rsvdBit79 : 1; /* bit 79 */ +} MLAN_PACK_END ExtCap_t, *pExtCap_t; +#endif + +/** ExtCap : TDLS prohibited */ +#define IS_EXTCAP_TDLS_PROHIBITED(ext_cap) (ext_cap.TDLSProhibited) +/** ExtCap : TDLS channel switch prohibited */ +#define IS_EXTCAP_TDLS_CHLSWITCHPROHIB(ext_cap) (ext_cap.TDLSChlSwitchProhib) + +/** mlan_bss_info data structure for MLAN_OID_GET_BSS_INFO */ +typedef struct _mlan_bss_info { + /** BSS mode */ + t_u32 bss_mode; + /** SSID */ + mlan_802_11_ssid ssid; + /** Table index */ + t_u32 scan_table_idx; + /** Channel */ + t_u32 bss_chan; + /** Band */ + t_u8 bss_band; + /** Region code */ + t_u32 region_code; + /** Connection status */ + t_u32 media_connected; + /** Radio on */ + t_u32 radio_on; + /** Max power level in dBm */ + t_s32 max_power_level; + /** Min power level in dBm */ + t_s32 min_power_level; + /** Adhoc state */ + t_u32 adhoc_state; + /** NF of last beacon */ + t_s32 bcn_nf_last; + /** wep status */ + t_u32 wep_status; + /** scan block status */ + t_u8 scan_block; + /** Host Sleep configured flag */ + t_u32 is_hs_configured; + /** Deep Sleep flag */ + t_u32 is_deep_sleep; + /** BSSID */ + mlan_802_11_mac_addr bssid; +#ifdef STA_SUPPORT + /** Capability Info */ + t_u16 capability_info; + /** Beacon Interval */ + t_u16 beacon_interval; + /** Listen Interval */ + t_u16 listen_interval; + /** Association Id */ + t_u16 assoc_id; + /** AP/Peer supported rates */ + t_u8 peer_supp_rates[MLAN_SUPPORTED_RATES]; + /** extend capability for AP */ + ExtCap_t ext_cap; +#endif /* STA_SUPPORT */ + /** Mobility Domain ID */ + t_u16 mdid; + /** FT Capability policy */ + t_u8 ft_cap; + /** 11h active */ + t_bool is_11h_active; + /** dfs check channel */ + t_u8 dfs_check_channel; +} mlan_bss_info, *pmlan_bss_info; + +/** MAXIMUM number of TID */ +#define MAX_NUM_TID 8 + +/** Max RX Win size */ +#define MAX_RX_WINSIZE 64 + +/** rx_reorder_tbl */ +typedef struct { + /** TID */ + t_u16 tid; + /** TA */ + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + /** Start window */ + t_u32 start_win; + /** Window size */ + t_u32 win_size; + /** amsdu flag */ + t_u8 amsdu; + /** buffer status */ + t_u32 buffer[MAX_RX_WINSIZE]; +} rx_reorder_tbl; + +/** tx_ba_stream_tbl */ +typedef struct { + /** TID */ + t_u16 tid; + /** RA */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** amsdu flag */ + t_u8 amsdu; +} tx_ba_stream_tbl; + +/** Debug command number */ +#define DBG_CMD_NUM 10 + +#ifdef SDIO +/** sdio mp debug number */ +#define SDIO_MP_DBG_NUM 10 +#endif + +#ifdef PCIE +#define MLAN_MAX_TXRX_BD 0x20 +#endif + +/** Maximum size of IEEE Information Elements */ +#define IEEE_MAX_IE_SIZE 256 + +/** max ralist num */ +#define MLAN_MAX_RALIST_NUM 8 +/** ralist info */ +typedef struct _ralist_info { + /** RA list buffer */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** total packets in RA list */ + t_u16 total_pkts; + /** tid num */ + t_u8 tid; + /** tx_pause flag */ + t_u8 tx_pause; +} ralist_info, *pralist_info; + +/** mlan_debug_info data structure for MLAN_OID_GET_DEBUG_INFO */ +typedef struct _mlan_debug_info { + /* WMM AC_BK count */ + t_u32 wmm_ac_bk; + /* WMM AC_BE count */ + t_u32 wmm_ac_be; + /* WMM AC_VI count */ + t_u32 wmm_ac_vi; + /* WMM AC_VO count */ + t_u32 wmm_ac_vo; + /** Corresponds to max_tx_buf_size member of mlan_adapter*/ + t_u32 max_tx_buf_size; + /** Corresponds to tx_buf_size member of mlan_adapter*/ + t_u32 tx_buf_size; + /** Corresponds to curr_tx_buf_size member of mlan_adapter*/ + t_u32 curr_tx_buf_size; + /** Tx table num */ + t_u32 tx_tbl_num; + /** Tx ba stream table */ + tx_ba_stream_tbl tx_tbl[MLAN_MAX_TX_BASTREAM_SUPPORTED]; + /** Rx table num */ + t_u32 rx_tbl_num; + /** Rx reorder table*/ + rx_reorder_tbl rx_tbl[MLAN_MAX_RX_BASTREAM_SUPPORTED]; + /** ralist num */ + t_u32 ralist_num; + /** ralist info */ + ralist_info ralist[MLAN_MAX_RALIST_NUM]; + /** Corresponds to ps_mode member of mlan_adapter */ + t_u16 ps_mode; + /** Corresponds to ps_state member of mlan_adapter */ + t_u32 ps_state; +#ifdef STA_SUPPORT + /** Corresponds to is_deep_sleep member of mlan_adapter */ + t_u8 is_deep_sleep; +#endif /** STA_SUPPORT */ + /** Corresponds to pm_wakeup_card_req member of mlan_adapter */ + t_u8 pm_wakeup_card_req; + /** Corresponds to pm_wakeup_fw_try member of mlan_adapter */ + t_u32 pm_wakeup_fw_try; + /** time stamp when host try to wake up firmware */ + t_u32 pm_wakeup_in_secs; + /** wake up timeout happened */ + t_u32 pm_wakeup_timeout; + /** Corresponds to is_hs_configured member of mlan_adapter */ + t_u8 is_hs_configured; + /** Corresponds to hs_activated member of mlan_adapter */ + t_u8 hs_activated; + /** Corresponds to pps_uapsd_mode member of mlan_adapter */ + t_u16 pps_uapsd_mode; + /** Corresponds to sleep_period.period member of mlan_adapter */ + t_u16 sleep_pd; + /** Corresponds to wmm_qosinfo member of mlan_private */ + t_u8 qos_cfg; + /** Corresponds to tx_lock_flag member of mlan_adapter */ + t_u8 tx_lock_flag; + /** Corresponds to port_open member of mlan_private */ + t_u8 port_open; + /** bypass pkt count */ + t_u16 bypass_pkt_count; + /** Corresponds to scan_processing member of mlan_adapter */ + t_u32 scan_processing; + /** Corresponds to mlan_processing member of mlan_adapter */ + t_u32 mlan_processing; + /** Corresponds to main_lock_flag member of mlan_adapter */ + t_u32 main_lock_flag; + /** Corresponds to main_process_cnt member of mlan_adapter */ + t_u32 main_process_cnt; + /** Corresponds to delay_task_flag member of mlan_adapter */ + t_u32 delay_task_flag; + /** mlan_rx_processing */ + t_u32 mlan_rx_processing; + /** rx pkts queued */ + t_u32 rx_pkts_queued; + /** Number of host to card command failures */ + t_u32 num_cmd_host_to_card_failure; + /** Number of host to card sleep confirm failures */ + t_u32 num_cmd_sleep_cfm_host_to_card_failure; + /** Number of host to card Tx failures */ + t_u32 num_tx_host_to_card_failure; + /** Number of allocate buffer failure */ + t_u32 num_alloc_buffer_failure; + /** Number of pkt dropped */ + t_u32 num_pkt_dropped; +#ifdef SDIO + /** Number of card to host command/event failures */ + t_u32 num_cmdevt_card_to_host_failure; + /** Number of card to host Rx failures */ + t_u32 num_rx_card_to_host_failure; + /** Number of interrupt read failures */ + t_u32 num_int_read_failure; + /** Last interrupt status */ + t_u32 last_int_status; + /** number of interrupt receive */ + t_u32 num_of_irq; + /** flag for sdio rx aggr */ + t_u8 sdio_rx_aggr; + /** FW update port number */ + t_u32 mp_update[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX * 2]; + /** Invalid port update count */ + t_u32 mp_invalid_update; + /** Number of packets tx aggr */ + t_u32 mpa_tx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** no more packets count*/ + t_u32 mpa_sent_last_pkt; + /** no write_ports count */ + t_u32 mpa_sent_no_ports; + /** last recv wr_bitmap */ + t_u32 last_recv_wr_bitmap; + /** last mp_wr_bitmap */ + t_u32 last_mp_wr_bitmap[SDIO_MP_DBG_NUM]; + /** last ports for cmd53 write data */ + t_u32 last_mp_wr_ports[SDIO_MP_DBG_NUM]; + /** last len for cmd53 write data */ + t_u32 last_mp_wr_len[SDIO_MP_DBG_NUM]; + /** last curr_wr_port */ + t_u8 last_curr_wr_port[SDIO_MP_DBG_NUM]; + /** length info for cmd53 write data */ + t_u16 last_mp_wr_info[SDIO_MP_DBG_NUM * SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** last mp_index */ + t_u8 last_mp_index; + /** buffer for mp debug */ + t_u8 *mpa_buf; + /** length info for mp buf size */ + t_u32 mpa_buf_size; + /** Number of packets rx aggr */ + t_u32 mpa_rx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** mp aggr_pkt limit */ + t_u8 mp_aggr_pkt_limit; +#endif + /** Number of deauthentication events */ + t_u32 num_event_deauth; + /** Number of disassosiation events */ + t_u32 num_event_disassoc; + /** Number of link lost events */ + t_u32 num_event_link_lost; + /** Number of deauthentication commands */ + t_u32 num_cmd_deauth; + /** Number of association comamnd successes */ + t_u32 num_cmd_assoc_success; + /** Number of association command failures */ + t_u32 num_cmd_assoc_failure; + /** Number of consecutive association failures */ + t_u32 num_cons_assoc_failure; + + /** Number of command timeouts */ + t_u32 num_cmd_timeout; + /** Timeout command ID */ + t_u16 timeout_cmd_id; + /** Timeout command action */ + t_u16 timeout_cmd_act; + /** List of last command IDs */ + t_u16 last_cmd_id[DBG_CMD_NUM]; + /** List of last command actions */ + t_u16 last_cmd_act[DBG_CMD_NUM]; + /** Last command index */ + t_u16 last_cmd_index; + /** List of last command response IDs */ + t_u16 last_cmd_resp_id[DBG_CMD_NUM]; + /** Last command response index */ + t_u16 last_cmd_resp_index; + /** List of last events */ + t_u16 last_event[DBG_CMD_NUM]; + /** Last event index */ + t_u16 last_event_index; + /** Number of no free command node */ + t_u16 num_no_cmd_node; + /** pending command id */ + t_u16 pending_cmd; + /** time stamp for dnld last cmd */ + t_u32 dnld_cmd_in_secs; + /** Corresponds to data_sent member of mlan_adapter */ + t_u8 data_sent; + /** Corresponds to cmd_sent member of mlan_adapter */ + t_u8 cmd_sent; + /** SDIO multiple port read bitmap */ + t_u32 mp_rd_bitmap; + /** SDIO multiple port write bitmap */ + t_u32 mp_wr_bitmap; + /** Current available port for read */ + t_u8 curr_rd_port; + /** Current available port for write */ + t_u8 curr_wr_port; +#ifdef PCIE + /** PCIE txbd read pointer */ + t_u32 txbd_rdptr; + /** PCIE txbd write pointer */ + t_u32 txbd_wrptr; + /** PCIE rxbd read pointer */ + t_u32 rxbd_rdptr; + /** PCIE rxbd write pointer */ + t_u32 rxbd_wrptr; + /** PCIE eventbd read pointer */ + t_u32 eventbd_rdptr; + /** PCIE eventbd write pointer */ + t_u32 eventbd_wrptr; + /** Last pkt size in transmit */ + t_u32 last_tx_pkt_size[MLAN_MAX_TXRX_BD]; + /** txbd ring vbase */ + t_u8 *txbd_ring_vbase; + /** txbd ring size */ + t_u32 txbd_ring_size; + /** rxbd ring vbase */ + t_u8 *rxbd_ring_vbase; + /** rxbd ring size */ + t_u32 rxbd_ring_size; + /** evtbd ring vbase */ + t_u8 *evtbd_ring_vbase; + /** evtbd ring size */ + t_u32 evtbd_ring_size; +#endif + /** 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 + /** FW hang report */ + t_u8 fw_hang_report; + /** mlan_adapter pointer */ + t_void *mlan_adapter; + /** mlan_adapter_size */ + t_u32 mlan_adapter_size; + /** mlan_priv vector */ + t_void *mlan_priv[MLAN_MAX_BSS_NUM]; + /** mlan_priv_size */ + t_u32 mlan_priv_size[MLAN_MAX_BSS_NUM]; + /** mlan_priv_num */ + t_u8 mlan_priv_num; +} mlan_debug_info, *pmlan_debug_info; + +#ifdef UAP_SUPPORT +/** Maximum number of clients supported by AP */ +#define MAX_NUM_CLIENTS MAX_STA_COUNT + +/** station info */ +typedef struct _sta_info { + /** STA MAC address */ + t_u8 mac_address[MLAN_MAC_ADDR_LENGTH]; + /** Power mgmt status */ + t_u8 power_mgmt_status; + /** RSSI */ + t_s8 rssi; + /** station bandmode */ + t_u16 bandmode; + /** station stats */ + sta_stats stats; + /** ie length */ + t_u16 ie_len; + /** ie buffer */ + t_u8 ie_buf[]; +} sta_info; + +/** mlan_ds_sta_list structure for MLAN_OID_UAP_STA_LIST */ +typedef struct _mlan_ds_sta_list { + /** station count */ + t_u16 sta_count; + /** station list */ + sta_info info[MAX_NUM_CLIENTS]; +} mlan_ds_sta_list, *pmlan_ds_sta_list; +#endif + +/** Type definition of mlan_ds_get_info for MLAN_IOCTL_GET_INFO */ +typedef struct _mlan_ds_get_info { + /** Sub-command */ + t_u32 sub_command; + + /** Status information parameter */ + union { + /** Signal information for MLAN_OID_GET_SIGNAL */ + mlan_ds_get_signal signal; + /** Signal path id for MLAN_OID_GET_SIGNAL_EXT */ + t_u16 path_id; + /** Signal information for MLAN_OID_GET_SIGNAL_EXT */ + mlan_ds_get_signal signal_ext[MAX_PATH_NUM]; + /** Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_get_stats stats; + /** Statistics information for MLAN_OID_LINK_STATS*/ + t_u8 link_statistic[1]; + /** Firmware information for MLAN_OID_GET_FW_INFO */ + mlan_fw_info fw_info; + /** Extended version information for MLAN_OID_GET_VER_EXT */ + mlan_ver_ext ver_ext; + /** BSS information for MLAN_OID_GET_BSS_INFO */ + mlan_bss_info bss_info; + /** Debug information for MLAN_OID_GET_DEBUG_INFO */ + t_u8 debug_info[1]; +#ifdef UAP_SUPPORT + /** UAP Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_uap_stats ustats; + /** UAP station list for MLAN_OID_UAP_STA_LIST */ + mlan_ds_sta_list sta_list; +#endif + } param; +} mlan_ds_get_info, *pmlan_ds_get_info; + +/*-----------------------------------------------------------------*/ +/** Security Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for authentication mode */ +enum _mlan_auth_mode { + MLAN_AUTH_MODE_OPEN = 0x00, + MLAN_AUTH_MODE_SHARED = 0x01, + MLAN_AUTH_MODE_FT = 0x02, + MLAN_AUTH_MODE_SAE = 0x03, + MLAN_AUTH_MODE_NETWORKEAP = 0x80, + MLAN_AUTH_MODE_AUTO = 0xFF, +}; + +/**Enumeration for AssocAgent authentication mode, sync from FW.*/ +typedef enum { + AssocAgentAuth_Open, + AssocAgentAuth_Shared, + AssocAgentAuth_FastBss, + AssocAgentAuth_FastBss_Skip, + AssocAgentAuth_Network_EAP, + AssocAgentAuth_Wpa3Sae, + AssocAgentAuth_Auto, +} AssocAgentAuthType_e; + +/** Enumeration for encryption mode */ +enum _mlan_encryption_mode { + MLAN_ENCRYPTION_MODE_NONE = 0, + MLAN_ENCRYPTION_MODE_WEP40 = 1, + MLAN_ENCRYPTION_MODE_TKIP = 2, + MLAN_ENCRYPTION_MODE_CCMP = 3, + MLAN_ENCRYPTION_MODE_WEP104 = 4, + MLAN_ENCRYPTION_MODE_GCMP = 5, + MLAN_ENCRYPTION_MODE_GCMP_256 = 6, + MLAN_ENCRYPTION_MODE_CCMP_256 = 7, +}; + +/** Enumeration for PSK */ +enum _mlan_psk_type { + MLAN_PSK_PASSPHRASE = 1, + MLAN_PSK_PMK, + MLAN_PSK_CLEAR, + MLAN_PSK_QUERY, + MLAN_PSK_SAE_PASSWORD, +}; + +/** 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 +/** Minimum sae_password length */ +#define MLAN_MIN_SAE_PASSWORD_LENGTH 8 +/** Maximum sae_password length */ +#define MLAN_MAX_SAE_PASSWORD_LENGTH 255 +/** PMK length */ +#define MLAN_PMK_HEXSTR_LENGTH 64 +/* A few details needed for WEP (Wireless Equivalent Privacy) */ +/** 104 bits */ +#define MAX_WEP_KEY_SIZE 13 +/** 40 bits RC4 - WEP */ +#define MIN_WEP_KEY_SIZE 5 +/** packet number size */ +#define PN_SIZE 16 +/** max seq size of wpa/wpa2 key */ +#define SEQ_MAX_SIZE 8 + +/** key flag for tx_seq */ +#define KEY_FLAG_TX_SEQ_VALID 0x00000001 +/** key flag for rx_seq */ +#define KEY_FLAG_RX_SEQ_VALID 0x00000002 +/** key flag for group key */ +#define KEY_FLAG_GROUP_KEY 0x00000004 +/** key flag for tx */ +#define KEY_FLAG_SET_TX_KEY 0x00000008 +/** key flag for mcast IGTK */ +#define KEY_FLAG_AES_MCAST_IGTK 0x00000010 +/** key flag for remove key */ +#define KEY_FLAG_REMOVE_KEY 0x80000000 +/** key flag for GCMP */ +#define KEY_FLAG_GCMP 0x00000020 +/** key flag for GCMP_256 */ +#define KEY_FLAG_GCMP_256 0x00000040 +/** key flag for ccmp 256 */ +#define KEY_FLAG_CCMP_256 0x00000080 + +/** 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 definition of mlan_sae_password_t */ +typedef struct _mlan_sae_password_t { + /** Length of SAE Password */ + t_u32 sae_password_len; + /** SAE Password */ + t_u8 sae_password[MLAN_MAX_SAE_PASSWORD_LENGTH]; +} mlan_sae_password_t; + +/** Type defnition of mlan_pmk_t */ +typedef struct _mlan_pmk_t { + /** PMK */ + t_u8 pmk[MLAN_MAX_KEY_LENGTH]; +} mlan_pmk_t; + +/** Embedded supplicant RSN type: No RSN */ +#define RSN_TYPE_NO_RSN MBIT(0) +/** Embedded supplicant RSN type: WPA */ +#define RSN_TYPE_WPA MBIT(3) +/** Embedded supplicant RSN type: WPA-NONE */ +#define RSN_TYPE_WPANONE MBIT(4) +/** Embedded supplicant RSN type: WPA2 */ +#define RSN_TYPE_WPA2 MBIT(5) +/** Embedded supplicant RSN type: RFU */ +#define RSN_TYPE_VALID_BITS \ + (RSN_TYPE_NO_RSN | RSN_TYPE_WPA | RSN_TYPE_WPANONE | RSN_TYPE_WPA2) + +/** Embedded supplicant cipher type: TKIP */ +#define EMBED_CIPHER_TKIP MBIT(2) +/** Embedded supplicant cipher type: AES */ +#define EMBED_CIPHER_AES MBIT(3) +/** Embedded supplicant cipher type: RFU */ +#define EMBED_CIPHER_VALID_BITS (EMBED_CIPHER_TKIP | EMBED_CIPHER_AES) + +/** Type definition of mlan_ds_passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ +typedef struct _mlan_ds_passphrase { + /** SSID may be used */ + mlan_802_11_ssid ssid; + /** BSSID may be used */ + mlan_802_11_mac_addr bssid; + /** Flag for passphrase or pmk used */ + t_u16 psk_type; + /** Passphrase or PMK */ + union { + /** Passphrase */ + mlan_passphrase_t passphrase; + /** SAE Password */ + mlan_sae_password_t sae_password; + /** 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; +#ifdef UAP_SUPPORT + t_u8 sta_mac[MLAN_MAC_ADDR_LENGTH]; +#endif + } param; +} mlan_ds_sec_cfg, *pmlan_ds_sec_cfg; + +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) +#define BIT_TLV_TYPE_CRYPTO_KEY (1 << 0) +#define BIT_TLV_TYPE_CRYPTO_KEY_IV (1 << 1) +#define BIT_TLV_TYPE_CRYPTO_KEY_PREFIX (1 << 2) +#define BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK (1 << 3) + +/** Type definition of mlan_ds_sup_cfg */ +typedef struct _mlan_ds_sup_cfg { + /** Sub-command */ + t_u8 sub_command; + /** output length */ + t_u16 output_len; + /** number of data blks */ + t_u16 data_blks_nr; + /** sub action code */ + t_u8 sub_action_code; + /** skip bytes */ + t_u16 skip_bytes; + /** iteration */ + t_u32 iteration; + /** count */ + t_u32 count; + /** pointer to output */ + t_u8 *output; + /** key length */ + t_u16 key_len; + /** pointer to key */ + t_u8 *key; + /** key iv length */ + t_u16 key_iv_len; + /** pointer to key iv */ + t_u8 *key_iv; + /** key prefix length */ + t_u16 key_prefix_len; + /** pointer to key prefix */ + t_u8 *key_prefix; + /** pointer to data blk length array */ + t_u32 *key_data_blk_len; + /** pointer to key data blk pointer array */ + t_u8 **key_data_blk; + /** callback */ + t_u8 call_back; +} mlan_ds_sup_cfg, *pmlan_ds_sup_cfg; +#endif + +/*-----------------------------------------------------------------*/ +/** Rate Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for rate type */ +enum _mlan_rate_type { MLAN_RATE_INDEX, MLAN_RATE_VALUE, MLAN_RATE_BITMAP }; + +/** Enumeration for rate format */ +enum _mlan_rate_format { + MLAN_RATE_FORMAT_LG = 0, + MLAN_RATE_FORMAT_HT, + MLAN_RATE_FORMAT_VHT, + MLAN_RATE_FORMAT_HE, + MLAN_RATE_FORMAT_AUTO = 0xFF, +}; + +/** Max bitmap rates size */ +#define MAX_BITMAP_RATES_SIZE 26 + +/** Type definition of mlan_rate_cfg_t for MLAN_OID_RATE_CFG */ +typedef struct _mlan_rate_cfg_t { + /** Fixed rate: 0, auto rate: 1 */ + t_u32 is_rate_auto; + /** Rate type. 0: index; 1: value; 2: bitmap */ + t_u32 rate_type; + /** Rate/MCS index or rate value if fixed rate */ + t_u32 rate; + /** Rate Bitmap */ + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + /** NSS */ + t_u32 nss; + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 rate_format; + /** Rate Setting */ + t_u16 rate_setting; +} mlan_rate_cfg_t; + +/** HT channel bandwidth */ +typedef enum _mlan_ht_bw { + MLAN_HT_BW20, + MLAN_HT_BW40, + /** VHT channel bandwidth */ + MLAN_VHT_BW80, + MLAN_VHT_BW160, +} mlan_ht_bw; + +/** HT guard interval */ +typedef enum _mlan_ht_gi { + MLAN_HT_LGI, + MLAN_HT_SGI, +} mlan_ht_gi; + +typedef enum _mlan_vht_stbc { + MLAN_VHT_STBC, + MLAN_VHT_NO_STBC, +} mlan_vht_stbc; + +typedef enum _mlan_vht_ldpc { + MLAN_VHT_LDPC, + MLAN_VHT_NO_LDPC, +} mlan_vht_ldpc; + +/** Band and BSS mode */ +typedef struct _mlan_band_data_rate { + /** Band configuration */ + t_u8 config_bands; + /** BSS mode (Infra or IBSS) */ + t_u8 bss_mode; +} mlan_band_data_rate; + +/** Type definition of mlan_data_rate for MLAN_OID_GET_DATA_RATE */ +typedef struct _mlan_data_rate { + /** Tx data rate */ + t_u32 tx_data_rate; + /** Rx data rate */ + t_u32 rx_data_rate; + + /** Tx channel bandwidth */ + t_u32 tx_ht_bw; + /** Tx guard interval */ + t_u32 tx_ht_gi; + /** Rx channel bandwidth */ + t_u32 rx_ht_bw; + /** Rx guard interval */ + t_u32 rx_ht_gi; + /** MCS index */ + t_u32 tx_mcs_index; + t_u32 rx_mcs_index; + /** NSS */ + t_u32 tx_nss; + t_u32 rx_nss; + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 tx_rate_format; + t_u32 rx_rate_format; +} mlan_data_rate; + +/** Type definition of mlan_ds_rate for MLAN_IOCTL_RATE */ +typedef struct _mlan_ds_rate { + /** Sub-command */ + t_u32 sub_command; + /** Rate configuration parameter */ + union { + /** Rate configuration for MLAN_OID_RATE_CFG */ + mlan_rate_cfg_t rate_cfg; + /** Data rate for MLAN_OID_GET_DATA_RATE */ + mlan_data_rate data_rate; + /** Supported rates for MLAN_OID_SUPPORTED_RATES */ + t_u8 rates[MLAN_SUPPORTED_RATES]; + /** Band/BSS mode for getting supported rates */ + mlan_band_data_rate rate_band_cfg; + } param; +} mlan_ds_rate, *pmlan_ds_rate; + +/*-----------------------------------------------------------------*/ +/** Power Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** Type definition of mlan_power_cfg_t for MLAN_OID_POWER_CFG */ +typedef struct _mlan_power_cfg_t { + /** Is power auto */ + t_u32 is_power_auto; + /** Power level in dBm */ + t_s32 power_level; +} mlan_power_cfg_t; + +/** max power table size */ +#define MAX_POWER_TABLE_SIZE 128 +#define TX_PWR_CFG_AUTO_CTRL_OFF 0xFF +#define MAX_POWER_GROUP 64 +/** Type definition of mlan_power group info */ +typedef struct mlan_power_group { + /** rate format (LG: 0, HT: 1, VHT: 2, no auto ctrl: 0xFF) */ + t_u32 rate_format; + /** bandwidth (LG: 20 MHz, HT: 20/40 MHz, VHT: 80/160/80+80 MHz) */ + t_u8 bandwidth; + /** NSS */ + t_u32 nss; + /** LG: first rate index, HT/VHT: first MCS */ + t_u8 first_rate_ind; + /** LG: last rate index, HT/VHT: last MCS */ + t_u8 last_rate_ind; + /** minmum tx power (dBm) */ + t_s8 power_min; + /** maximum tx power (dBm) */ + t_s8 power_max; + /** tx power step (dB) */ + t_s8 power_step; +} mlan_power_group; + +/** Type definition of mlan_power_cfg_ext for MLAN_OID_POWER_CFG_EXT */ +typedef struct _mlan_power_cfg_ext { + /** number of power_groups */ + t_u32 num_pwr_grp; + /** array of power groups */ + mlan_power_group power_group[MAX_POWER_GROUP]; +} mlan_power_cfg_ext; + +/** Type definition of mlan_ds_power_cfg for MLAN_IOCTL_POWER_CFG */ +typedef struct _mlan_ds_power_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Power configuration parameter */ + union { + /** Power configuration for MLAN_OID_POWER_CFG */ + mlan_power_cfg_t power_cfg; + /** Extended power configuration for MLAN_OID_POWER_CFG_EXT */ + mlan_power_cfg_ext power_ext; + /** Low power mode for MLAN_OID_POWER_LOW_POWER_MODE */ + t_u16 lpm; + } param; +} mlan_ds_power_cfg, *pmlan_ds_power_cfg; + +/** Type definition of mlan_ds_band_steer_cfg for MLAN_IOCTL_POWER_CFG */ +typedef struct _mlan_ds_band_steer_cfg { + /** Set/Get */ + t_u8 action; + /** enable/disable band steering*/ + t_u8 state; + /** Probe Response will be blocked to 2G channel for first + * block_2g_prb_req probe requests*/ + t_u8 block_2g_prb_req; + /** When band steering is enabled, limit the btm request sent to STA at + * */ + t_u8 max_btm_req_allowed; +} mlan_ds_band_steer_cfg, *pmlan_ds_band_steer_cfg; + +/** Type definition of mlan_ds_beacon_stuck_param_cfg for MLAN_IOCTL_POWER_CFG + */ +typedef struct _mlan_ds_beacon_stuck_param_cfg { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u8 action; + /** No of beacon interval after which firmware will check if beacon Tx + * is going fine */ + t_u8 beacon_stuck_detect_count; + /** Upon performing MAC reset, no of beacon interval after which + * firmware will check if recovery was successful */ + t_u8 recovery_confirm_count; +} mlan_ds_beacon_stuck_param_cfg, *pmlan_ds_beacon_stuck_param_cfg; + +/*-----------------------------------------------------------------*/ +/** Power Management Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Host sleep config conditions : Cancel */ +#define HOST_SLEEP_CFG_CANCEL 0xffffffff + +/** Host sleep config condition: broadcast data */ +#define HOST_SLEEP_COND_BROADCAST_DATA MBIT(0) +/** Host sleep config condition: unicast data */ +#define HOST_SLEEP_COND_UNICAST_DATA MBIT(1) +/** Host sleep config condition: mac event */ +#define HOST_SLEEP_COND_MAC_EVENT MBIT(2) +/** Host sleep config condition: multicast data */ +#define HOST_SLEEP_COND_MULTICAST_DATA MBIT(3) +/** Host sleep config condition: IPV6 packet */ +#define HOST_SLEEP_COND_IPV6_PACKET MBIT(31) + +/** Host sleep config conditions: Default */ +#define HOST_SLEEP_DEF_COND 0 + +/** Host sleep config GPIO : Default */ +#define HOST_SLEEP_DEF_GPIO 0xff +/** Host sleep config gap : Default */ +#define HOST_SLEEP_DEF_GAP 200 +/** Host sleep config min wake holdoff */ +#define HOST_SLEEP_DEF_WAKE_HOLDOFF 0; +/** Host sleep config inactivity timeout */ +#define HOST_SLEEP_DEF_INACTIVITY_TIMEOUT 10; + +/** Type definition of mlan_ds_hs_cfg for MLAN_OID_PM_CFG_HS_CFG */ +typedef struct _mlan_ds_hs_cfg { + /** MTRUE to invoke the HostCmd, MFALSE otherwise */ + t_u32 is_invoke_hostcmd; + /** Host sleep config condition */ + /** Bit0: broadcast data + * Bit1: unicast data + * Bit2: mac event + * Bit3: multicast data + */ + t_u32 conditions; + /** GPIO pin or 0xff for interface */ + t_u32 gpio; + /** Gap in milliseconds or or 0xff for special + * setting when GPIO is used to wakeup host + */ + t_u32 gap; + /** Host sleep wake interval */ + t_u32 hs_wake_interval; + /** Parameter type for indication gpio*/ + t_u8 param_type_ind; + /** GPIO pin for indication wakeup source */ + t_u32 ind_gpio; + /** Level on ind_gpio pin for indication normal wakeup source */ + t_u32 level; + /** Parameter type for extend hscfg*/ + t_u8 param_type_ext; + /** Events that will be forced ignore*/ + t_u32 event_force_ignore; + /** Events that will use extend gap to inform host*/ + t_u32 event_use_ext_gap; + /** Ext gap*/ + t_u8 ext_gap; + /** GPIO wave level for extend hscfg*/ + t_u8 gpio_wave; +} mlan_ds_hs_cfg, *pmlan_ds_hs_cfg; + +#define MAX_MGMT_FRAME_FILTER 2 +typedef struct _mlan_mgmt_frame_wakeup { + /** action - bitmap + ** On matching rx'd pkt and filter during NON_HOSTSLEEP mode: + ** Action[1]=0 Discard + ** Action[1]=1 Allow + ** Note that default action on non-match is "Allow". + ** + ** On matching rx'd pkt and filter during HOSTSLEEP mode: + ** Action[1:0]=00 Discard and Not Wake host + ** Action[1:0]=01 Discard and Wake host + ** Action[1:0]=10 Invalid + ** Note that default action on non-match is "Discard and Not Wake + *host". + **/ + t_u32 action; + /** Frame type(p2p, tdls...) + ** type=0: invalid + ** type=1: p2p + ** type=others: reserved + **/ + t_u32 type; + /** Frame mask according to each type + ** When type=1 for p2p, frame-mask have following define: + ** Bit Frame + ** 0 GO Negotiation Request + ** 1 GO Negotiation Response + ** 2 GO Negotiation Confirmation + ** 3 P2P Invitation Request + ** 4 P2P Invitation Response + ** 5 Device Discoverability Request + ** 6 Device Discoverability Response + ** 7 Provision Discovery Request + ** 8 Provision Discovery Response + ** 9 Notice of Absence + ** 10 P2P Presence Request + ** 11 P2P Presence Response + ** 12 GO Discoverability Request + ** 13-31 Reserved + ** + ** When type=others, frame-mask is reserved. + **/ + t_u32 frame_mask; +} mlan_mgmt_frame_wakeup, *pmlan_mgmt_frame_wakeup; + +/** Enable deep sleep mode */ +#define DEEP_SLEEP_ON 1 +/** Disable deep sleep mode */ +#define DEEP_SLEEP_OFF 0 + +/** Default idle time in milliseconds for auto deep sleep */ +#define DEEP_SLEEP_IDLE_TIME 100 + +typedef struct _mlan_ds_auto_ds { + /** auto ds mode, 0 - disable, 1 - enable */ + t_u16 auto_ds; + /** auto ds idle time in milliseconds */ + t_u16 idletime; +} mlan_ds_auto_ds; + +/** Type definition of mlan_ds_inactivity_to + * for MLAN_OID_PM_CFG_INACTIVITY_TO + */ +typedef struct _mlan_ds_inactivity_to { + /** Timeout unit in microsecond, 0 means 1000us (1ms) */ + t_u32 timeout_unit; + /** Inactivity timeout for unicast data */ + t_u32 unicast_timeout; + /** Inactivity timeout for multicast data */ + t_u32 mcast_timeout; + /** Timeout for additional Rx traffic after Null PM1 packet exchange */ + t_u32 ps_entry_timeout; +} mlan_ds_inactivity_to, *pmlan_ds_inactivity_to; + +/** Minimum sleep period in milliseconds */ +#define MIN_SLEEP_PERIOD 10 +/** Maximum sleep period in milliseconds */ +#define MAX_SLEEP_PERIOD 60 +/** Special setting for UPSD certification tests */ +#define SLEEP_PERIOD_RESERVED_FF 0xFF + +/** PS null interval disable */ +#define PS_NULL_DISABLE (-1) + +/** Local listen interval disable */ +#define MRVDRV_LISTEN_INTERVAL_DISABLE (-1) +/** Minimum listen interval */ +#define MRVDRV_MIN_LISTEN_INTERVAL 0 + +/** Minimum multiple DTIM */ +#define MRVDRV_MIN_MULTIPLE_DTIM 0 +/** Maximum multiple DTIM */ +#define MRVDRV_MAX_MULTIPLE_DTIM 5 +/** Ignore multiple DTIM */ +#define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe +/** Match listen interval to closest DTIM */ +#define MRVDRV_MATCH_CLOSEST_DTIM 0xfffd + +/** Minimum 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; + /** Beacon miss timeout in milliseconds */ + t_u32 bcn_miss_timeout; + /** Delay to PS in milliseconds */ + t_s32 delay_to_ps; + /** PS mode */ + t_u32 ps_mode; +} mlan_ds_ps_cfg, *pmlan_ds_ps_cfg; + +/** Type definition of mlan_ds_sleep_params for MLAN_OID_PM_CFG_SLEEP_PARAMS */ +typedef struct _mlan_ds_sleep_params { + /** Error */ + t_u32 error; + /** Offset in microseconds */ + t_u32 offset; + /** Stable time in microseconds */ + t_u32 stable_time; + /** Calibration control */ + t_u32 cal_control; + /** External sleep clock */ + t_u32 ext_sleep_clk; + /** Reserved */ + t_u32 reserved; +} mlan_ds_sleep_params, *pmlan_ds_sleep_params; + +/** sleep_param */ +typedef struct _ps_sleep_param { + /** control bitmap */ + t_u32 ctrl_bitmap; + /** minimum sleep period (micro second) */ + t_u32 min_sleep; + /** maximum sleep period (micro second) */ + t_u32 max_sleep; +} ps_sleep_param; + +/** inactivity sleep_param */ +typedef struct _inact_sleep_param { + /** inactivity timeout (micro second) */ + t_u32 inactivity_to; + /** miniumu awake period (micro second) */ + t_u32 min_awake; + /** maximum awake period (micro second) */ + t_u32 max_awake; +} inact_sleep_param; + +/** flag for ps mode */ +#define PS_FLAG_PS_MODE 1 +/** flag for sleep param */ +#define PS_FLAG_SLEEP_PARAM 2 +/** flag for inactivity sleep param */ +#define PS_FLAG_INACT_SLEEP_PARAM 4 + +/** Enable Robust Coex mode */ +#define ROBUSTCOEX_GPIOCFG_ENABLE 1 +/** Disable Robust Coex mode */ +#define ROBUSTCOEX_GPIOCFG_DISABLE 0 + +/** Disable power mode */ +#define PS_MODE_DISABLE 0 +/** Enable periodic dtim ps */ +#define PS_MODE_PERIODIC_DTIM 1 +/** Enable inactivity ps */ +#define PS_MODE_INACTIVITY 2 +/** FW wake up method interface */ +#define FW_WAKEUP_METHOD_INTERFACE 1 +/** FW wake up method gpio */ +#define FW_WAKEUP_METHOD_GPIO 2 +/** mlan_ds_ps_mgmt */ +typedef struct _mlan_ds_ps_mgmt { + /** flags for valid field */ + t_u16 flags; + /** power mode */ + t_u16 ps_mode; + /** sleep param */ + ps_sleep_param sleep_param; + /** inactivity sleep param */ + inact_sleep_param inact_param; +} mlan_ds_ps_mgmt; + +/** mlan_ds_ps_info */ +typedef struct _mlan_ds_ps_info { + /** suspend allowed flag */ + t_u32 is_suspend_allowed; +} mlan_ds_ps_info; + +/** Type definition of mlan_ds_wakeup_reason for MLAN_OID_PM_HS_WAKEUP_REASON */ +typedef struct _mlan_ds_hs_wakeup_reason { + t_u16 hs_wakeup_reason; +} mlan_ds_hs_wakeup_reason; + +/** Type definition of mlan_ds_ps_cfg for MLAN_OID_PM_CFG_PS_CFG */ +typedef struct _mlan_ds_bcn_timeout { + /** Beacon miss timeout period window */ + t_u16 bcn_miss_tmo_window; + /** Beacon miss timeout period */ + t_u16 bcn_miss_tmo_period; + /** Beacon reacquire timeout period window */ + t_u16 bcn_rq_tmo_window; + /** Beacon reacquire timeout period */ + t_u16 bcn_rq_tmo_period; +} mlan_ds_bcn_timeout, *pmlan_ds_bcn_timeout; + +/** Type definition of mlan_ds_pm_cfg for MLAN_IOCTL_PM_CFG */ +typedef struct _mlan_ds_pm_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Power management parameter */ + union { + /** Power saving mode for MLAN_OID_PM_CFG_IEEE_PS */ + t_u32 ps_mode; + /** Host Sleep configuration for MLAN_OID_PM_CFG_HS_CFG */ + mlan_ds_hs_cfg hs_cfg; + /** Deep sleep mode for MLAN_OID_PM_CFG_DEEP_SLEEP */ + mlan_ds_auto_ds auto_deep_sleep; + /** Inactivity timeout for MLAN_OID_PM_CFG_INACTIVITY_TO */ + mlan_ds_inactivity_to inactivity_to; + /** Sleep period for MLAN_OID_PM_CFG_SLEEP_PD */ + t_u32 sleep_period; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_CFG */ + mlan_ds_ps_cfg ps_cfg; + /** PS configuration parameters for MLAN_OID_PM_CFG_SLEEP_PARAMS + */ + mlan_ds_sleep_params sleep_params; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_MODE */ + mlan_ds_ps_mgmt ps_mgmt; + /** power info for MLAN_OID_PM_INFO */ + mlan_ds_ps_info ps_info; + /** hs wakeup reason for MLAN_OID_PM_HS_WAKEUP_REASON */ + mlan_ds_hs_wakeup_reason wakeup_reason; + /** config manamgement frame for hs wakeup */ + mlan_mgmt_frame_wakeup mgmt_filter[MAX_MGMT_FRAME_FILTER]; + /** Beacon timout parameters for MLAN_OID_PM_CFG_BCN_TIMEOUT */ + mlan_ds_bcn_timeout bcn_timeout; + } param; +} mlan_ds_pm_cfg, *pmlan_ds_pm_cfg; + +#ifdef RX_PACKET_COALESCE +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + + t_u32 pkt_threshold; /** Packet threshold */ + t_u16 delay; /** Timeout value in milliseconds */ +} wlan_ioctl_rx_pkt_coalesce_config_t; +#endif + +/*-----------------------------------------------------------------*/ +/** WMM Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** WMM TSpec size */ +#define MLAN_WMM_TSPEC_SIZE 63 +/** WMM Add TS extra IE bytes */ +#define MLAN_WMM_ADDTS_EXTRA_IE_BYTES 256 +/** WMM statistics for packets hist bins */ +#define MLAN_WMM_STATS_PKTS_HIST_BINS 7 +/** Maximum number of AC QOS queues available */ +#define MLAN_WMM_MAX_AC_QUEUES 4 + +/** + * @brief IOCTL structure to send an ADDTS request and retrieve the response. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an ADDTS management frame with an appropriate TSPEC IE as well + * as any additional IEs appended in the ADDTS Action frame. + * + * @sa woal_wmm_addts_req_ioctl + */ +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + + t_u32 timeout_ms; /**< Timeout value in milliseconds */ + t_u8 ieee_status_code; /**< IEEE status code */ + + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE /**< TSPEC to send in the ADDTS */ + + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; /**< Extra IE buf*/ +} wlan_ioctl_wmm_addts_req_t; + +/** + * @brief IOCTL structure to send a DELTS request. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an DELTS management frame with an appropriate TSPEC IE. + * + * @sa woal_wmm_delts_req_ioctl + */ +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + t_u8 ieee_reason_code; /**< IEEE reason code sent, unused for WMM */ + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; /**< TSPEC to send in the DELTS */ +} wlan_ioctl_wmm_delts_req_t; + +/** + * @brief IOCTL structure to configure a specific AC Queue's parameters + * + * IOCTL structure from the application layer relayed to firmware to + * get, set, or default the WMM AC queue parameters. + * + * - msdu_lifetime_expiry is ignored if set to 0 on a set command + * + * @sa woal_wmm_queue_config_ioctl + */ +typedef struct { + mlan_wmm_queue_config_action_e action; /**< Set, Get, or Default */ + mlan_wmm_ac_e access_category; /**< WMM_AC_BK(0) to WMM_AC_VO(3) */ + t_u16 msdu_lifetime_expiry; /**< lifetime expiry in TUs */ + t_u8 supported_rates[10]; /**< Not supported yet */ +} wlan_ioctl_wmm_queue_config_t; + +/** + * @brief IOCTL structure to start, stop, and get statistics for a WMM AC + * + * IOCTL structure from the application layer relayed to firmware to + * start or stop statistical collection for a given AC. Also used to + * retrieve and clear the collected stats on a given AC. + * + * @sa woal_wmm_queue_stats_ioctl + */ +typedef struct { + /** Action of Queue Config : Start, Stop, or Get */ + mlan_wmm_queue_stats_action_e action; + /** User Priority */ + t_u8 user_priority; + /** Number of successful packets transmitted */ + t_u16 pkt_count; + /** Packets lost; not included in pkt_count */ + t_u16 pkt_loss; + /** Average Queue delay in microseconds */ + t_u32 avg_queue_delay; + /** Average Transmission delay in microseconds */ + t_u32 avg_tx_delay; + /** Calculated used time in units of 32 microseconds */ + t_u16 used_time; + /** Calculated policed time in units of 32 microseconds */ + t_u16 policed_time; + /** Queue Delay Histogram; number of packets per queue delay range + * + * [0] - 0ms <= delay < 5ms + * [1] - 5ms <= delay < 10ms + * [2] - 10ms <= delay < 20ms + * [3] - 20ms <= delay < 30ms + * [4] - 30ms <= delay < 40ms + * [5] - 40ms <= delay < 50ms + * [6] - 50ms <= delay < msduLifetime (TUs) + */ + t_u16 delay_histogram[MLAN_WMM_STATS_PKTS_HIST_BINS]; +} wlan_ioctl_wmm_queue_stats_t, + /** Type definition of mlan_ds_wmm_queue_stats + * for MLAN_OID_WMM_CFG_QUEUE_STATS + */ + mlan_ds_wmm_queue_stats, *pmlan_ds_wmm_queue_stats; + +/** + * @brief IOCTL sub structure for a specific WMM AC Status + */ +typedef struct { + /** WMM Acm */ + t_u8 wmm_acm; + /** Flow required flag */ + t_u8 flow_required; + /** Flow created flag */ + t_u8 flow_created; + /** Disabled flag */ + t_u8 disabled; +} wlan_ioctl_wmm_queue_status_ac_t; + +/** + * @brief IOCTL structure to retrieve the WMM AC Queue status + * + * IOCTL structure from the application layer to retrieve: + * - ACM bit setting for the AC + * - Firmware status (flow required, flow created, flow disabled) + * + * @sa woal_wmm_queue_status_ioctl + */ +typedef struct { + /** WMM AC queue status */ + wlan_ioctl_wmm_queue_status_ac_t ac_status[MLAN_WMM_MAX_AC_QUEUES]; +} wlan_ioctl_wmm_queue_status_t, + /** Type definition of mlan_ds_wmm_queue_status + * for MLAN_OID_WMM_CFG_QUEUE_STATUS + */ + mlan_ds_wmm_queue_status, *pmlan_ds_wmm_queue_status; + +/** Type definition of mlan_ds_wmm_addts for MLAN_OID_WMM_CFG_ADDTS */ +typedef struct _mlan_ds_wmm_addts { + /** Result of ADDTS request */ + mlan_cmd_result_e result; + /** Timeout value in milliseconds */ + t_u32 timeout; + /** IEEE status code */ + t_u32 status_code; + /** Dialog token */ + t_u8 dialog_tok; + /** TSPEC data length */ + t_u32 ie_data_len; + /** TSPEC to send in the ADDTS + buffering for any extra IEs */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; +} mlan_ds_wmm_addts, *pmlan_ds_wmm_addts; + +/** Type definition of mlan_ds_wmm_delts for MLAN_OID_WMM_CFG_DELTS */ +typedef struct _mlan_ds_wmm_delts { + /** Result of DELTS request */ + mlan_cmd_result_e result; + /** IEEE status code */ + t_u32 status_code; + /** TSPEC data length */ + t_u8 ie_data_len; + /** TSPEC to send in the DELTS */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; +} mlan_ds_wmm_delts, *pmlan_ds_wmm_delts; + +/** Type definition of mlan_ds_wmm_queue_config + * for MLAN_OID_WMM_CFG_QUEUE_CONFIG + */ +typedef struct _mlan_ds_wmm_queue_config { + /** Action of Queue Config : Set, Get, or Default */ + mlan_wmm_queue_config_action_e action; + /** WMM Access Category: WMM_AC_BK(0) to WMM_AC_VO(3) */ + mlan_wmm_ac_e access_category; + /** Lifetime expiry in TUs */ + t_u16 msdu_lifetime_expiry; + /** Reserve for future use */ + t_u8 reserved[10]; +} mlan_ds_wmm_queue_config, *pmlan_ds_wmm_queue_config; + +/** Type definition of mlan_ds_wmm_cfg for MLAN_IOCTL_WMM_CFG */ +typedef struct _mlan_ds_wmm_cfg { + /** Sub-command */ + t_u32 sub_command; + /** WMM configuration parameter */ + union { + /** WMM enable for MLAN_OID_WMM_CFG_ENABLE */ + t_u32 wmm_enable; + /** QoS configuration for MLAN_OID_WMM_CFG_QOS */ + t_u8 qos_cfg; + /** WMM add TS for MLAN_OID_WMM_CFG_ADDTS */ + mlan_ds_wmm_addts addts; + /** WMM delete TS for MLAN_OID_WMM_CFG_DELTS */ + mlan_ds_wmm_delts delts; + /** WMM queue configuration for MLAN_OID_WMM_CFG_QUEUE_CONFIG */ + mlan_ds_wmm_queue_config q_cfg; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, + * WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATS */ + mlan_ds_wmm_queue_stats q_stats; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATUS */ + mlan_ds_wmm_queue_status q_status; + /** WMM TS status for MLAN_OID_WMM_CFG_TS_STATUS */ + mlan_ds_wmm_ts_status ts_status; + } param; +} mlan_ds_wmm_cfg, *pmlan_ds_wmm_cfg; + +/*-----------------------------------------------------------------*/ +/** WPS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for WPS session */ +enum _mlan_wps_status { + MLAN_WPS_CFG_SESSION_START = 1, + MLAN_WPS_CFG_SESSION_END = 0 +}; + +/** Type definition of mlan_ds_wps_cfg for MLAN_IOCTL_WPS_CFG */ +typedef struct _mlan_ds_wps_cfg { + /** Sub-command */ + t_u32 sub_command; + /** WPS configuration parameter */ + union { + /** WPS session for MLAN_OID_WPS_CFG_SESSION */ + t_u32 wps_session; + } param; +} mlan_ds_wps_cfg, *pmlan_ds_wps_cfg; + +/*-----------------------------------------------------------------*/ +/** 802.11n Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum MCS */ +#define NUM_MCS_FIELD 16 + +/** 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 +/** TX Sounding*/ +#define TX_SOUNDING_CFG 0x05 + +/* Maximum number of peer MAC and status/SNR tuples */ +#define MAX_PEER_MAC_TUPLES 10 + +/** Any new subcommand structure should be declare here */ + +/** bf global cfg args */ +typedef struct _mlan_bf_global_cfg_args { + /** Global enable/disable bf */ + t_u8 bf_enbl; + /** Global enable/disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; + /** SNR Threshold */ + t_u8 snr_threshold; + /** Sounding interval in milliseconds */ + t_u16 sounding_interval; + /** BF mode */ + t_u8 bf_mode; + /** Reserved */ + t_u8 reserved; +} mlan_bf_global_cfg_args; + +/** trigger sounding args */ +typedef struct _mlan_trigger_sound_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Status */ + t_u8 status; +} mlan_trigger_sound_args; + +/** bf periodicity args */ +typedef struct _mlan_bf_periodicity_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Current Tx BF Interval in milliseconds */ + t_u16 interval; + /** Status */ + t_u8 status; +} mlan_bf_periodicity_args; + +/** tx bf peer args */ +typedef struct _mlan_tx_bf_peer_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Reserved */ + t_u16 reserved; + /** Enable/Disable Beamforming */ + t_u8 bf_enbl; + /** Enable/Disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; +} mlan_tx_bf_peer_args; + +/** SNR threshold args */ +typedef struct _mlan_snr_thr_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** SNR for peer */ + t_u8 snr; +} mlan_snr_thr_args; + +/** Type definition of mlan_ds_11n_tx_bf_cfg for MLAN_OID_11N_CFG_TX_BF_CFG */ +typedef struct _mlan_ds_11n_tx_bf_cfg { + /** BF Action */ + t_u16 bf_action; + /** Action */ + t_u16 action; + /** Number of peers */ + t_u32 no_of_peers; + union { + mlan_bf_global_cfg_args bf_global_cfg; + mlan_trigger_sound_args bf_sound[MAX_PEER_MAC_TUPLES]; + mlan_bf_periodicity_args bf_periodicity[MAX_PEER_MAC_TUPLES]; + mlan_tx_bf_peer_args tx_bf_peer[MAX_PEER_MAC_TUPLES]; + mlan_snr_thr_args bf_snr[MAX_PEER_MAC_TUPLES]; + } body; +} mlan_ds_11n_tx_bf_cfg, *pmlan_ds_11n_tx_bf_cfg; + +/** Type definition of mlan_ds_11n_amsdu_aggr_ctrl for + * MLAN_OID_11N_AMSDU_AGGR_CTRL*/ +typedef struct _mlan_ds_11n_amsdu_aggr_ctrl { + /** Enable/Disable */ + t_u16 enable; + /** Current AMSDU size valid */ + t_u16 curr_buf_size; +} mlan_ds_11n_amsdu_aggr_ctrl, *pmlan_ds_11n_amsdu_aggr_ctrl; + +/** Type definition of mlan_ds_11n_aggr_prio_tbl + * for MLAN_OID_11N_CFG_AGGR_PRIO_TBL + */ +typedef struct _mlan_ds_11n_aggr_prio_tbl { + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** amsdu priority table */ + t_u8 amsdu[MAX_NUM_TID]; +} mlan_ds_11n_aggr_prio_tbl, *pmlan_ds_11n_aggr_prio_tbl; + +/** DelBA All TIDs */ +#define DELBA_ALL_TIDS 0xff +/** DelBA Tx */ +#define DELBA_TX MBIT(0) +/** DelBA Rx */ +#define DELBA_RX MBIT(1) + +/** Type definition of mlan_ds_11n_delba for MLAN_OID_11N_CFG_DELBA */ +typedef struct _mlan_ds_11n_delba { + /** TID */ + t_u8 tid; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Direction (Tx: bit 0, Rx: bit 1) */ + t_u8 direction; +} mlan_ds_11n_delba, *pmlan_ds_11n_delba; + +/** Type definition of mlan_ds_delba for MLAN_OID_11N_CFG_REJECT_ADDBA_REQ */ +typedef struct _mlan_ds_reject_addba_req { + /** Bit0 : host sleep activated + * Bit1 : auto reconnect enabled + * Others : reserved + */ + t_u32 conditions; +} mlan_ds_reject_addba_req, *pmlan_ds_reject_addba_req; + +/** Type definition of mlan_ds_ibss_ampdu_param */ +typedef struct _mlan_ds_ibss_ampdu_param { + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** rx amdpdu setting */ + t_u8 addba_reject[MAX_NUM_TID]; +} mlan_ds_ibss_ampdu_param, *pmlan_ds_ibss_ampdu_param; + +/** Type definition of mlan_ds_11n_cfg for MLAN_IOCTL_11N_CFG */ +typedef struct _mlan_ds_11n_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11n configuration parameter */ + union { + /** Tx param for 11n for MLAN_OID_11N_CFG_TX */ + mlan_ds_11n_tx_cfg tx_cfg; + /** Aggr priority table for MLAN_OID_11N_CFG_AGGR_PRIO_TBL */ + mlan_ds_11n_aggr_prio_tbl aggr_prio_tbl; + /** Add BA param for MLAN_OID_11N_CFG_ADDBA_PARAM */ + mlan_ds_11n_addba_param addba_param; + /** Add BA Reject paramters for MLAN_OID_11N_CFG_ADDBA_REJECT */ + t_u8 addba_reject[MAX_NUM_TID]; + /** Tx buf size for MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE */ + t_u32 tx_buf_size; + /** HT cap info configuration for MLAN_OID_11N_HTCAP_CFG */ + mlan_ds_11n_htcap_cfg htcap_cfg; + /** Tx param for 11n for MLAN_OID_11N_AMSDU_AGGR_CTRL */ + mlan_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[NUM_MCS_FIELD]; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Transmit Beamforming configuration */ + mlan_ds_11n_tx_bf_cfg tx_bf; + /** HT stream configuration */ + t_u32 stream_cfg; + /** DelBA for MLAN_OID_11N_CFG_DELBA */ + mlan_ds_11n_delba del_ba; + /** Reject Addba Req for MLAN_OID_11N_CFG_REJECT_ADDBA_REQ */ + mlan_ds_reject_addba_req reject_addba_req; + /** Control coex RX window size configuration */ + t_u32 coex_rx_winsize; + /** aggrprirotity table for MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM */ + mlan_ds_ibss_ampdu_param ibss_ampdu; + /** Minimum BA Threshold for MLAN_OID_11N_CFG_MIN_BA_THRESHOLD + */ + t_u8 min_ba_threshold; + } param; +} mlan_ds_11n_cfg, *pmlan_ds_11n_cfg; + +#define NUM_MCS_SUPP 20 +#define VHT_MCS_SET_LEN 8 + +/** Type definition of mlan_ds_11ac_vhtcap_cfg for MLAN_OID_11AC_VHTCAP_CFG */ +typedef struct _mlan_ds_11ac_vhtcap_cfg { + /** HT Capability information */ + t_u32 vhtcap; + /** Band selection */ + t_u32 misc_cfg; + /** Hardware HT cap information required */ + t_u32 hw_cap_req; +} mlan_ds_11ac_vhtcap_cfg, *pmlan_ds_11ac_vhtcap_cfg; + +/** Type definition of mlan_ds_11ac_tx_cfg for MLAN_OID_11AC_CFG_TX */ +typedef struct _mlan_ds_11ac_tx_cfg { + /** Band selection */ + t_u8 band_cfg; + /** misc configuration */ + t_u8 misc_cfg; + /** HTTxCap */ + t_u16 vhttxcap; + /** HTTxInfo */ + t_u16 vhttxinfo; +} mlan_ds_11ac_tx_cfg, *pmlan_ds_11ac_tx_cfg; + +/** Tx */ +#define MLAN_RADIO_TX MBIT(0) +/** Rx */ +#define MLAN_RADIO_RX MBIT(1) +/** Tx & Rx */ +#define MLAN_RADIO_TXRX (MLAN_RADIO_TX | MLAN_RADIO_RX) + +/** Type definition of mlan_ds_11ac_tx_cfg for MLAN_OID_11AC_CFG */ +typedef struct _mlan_ds_11ac_vht_cfg { + /** Band selection (1: 2.4G, 2: 5 G, 3: both 2.4G and 5G) */ + t_u32 band; + /** TxRx (1: Tx, 2: Rx, 3: both Tx and Rx) */ + t_u32 txrx; + /** BW CFG (0: 11N CFG, 1: vhtcap) */ + t_u32 bwcfg; + /** VHT capabilities. */ + t_u32 vht_cap_info; + /** VHT Tx mcs */ + t_u32 vht_tx_mcs; + /** VHT Rx mcs */ + t_u32 vht_rx_mcs; + /** VHT rx max rate */ + t_u16 vht_rx_max_rate; + /** VHT max tx rate */ + t_u16 vht_tx_max_rate; + /** Skip usr 11ac mcs cfg */ + t_bool skip_usr_11ac_mcs_cfg; +} mlan_ds_11ac_vht_cfg, *pmlan_ds_11ac_vht_cfg; + +/** Type definition of mlan_ds_11ac_tx_cfg for MLAN_OID_11AC_CFG */ +typedef struct _mlan_ds_11ac_opermode_cfg { + /** channel width: 1-20MHz, 2-40MHz, 3-80MHz, 4-160MHz or 80+80MHz */ + t_u8 bw; + /** Rx NSS */ + t_u8 nss; +} mlan_ds_11ac_opermode_cfg, *pmlan_ds_11ac_opermode_cfg; + +/** Type definition of mlan_ds_11ac_cfg for MLAN_IOCTL_11AC_CFG */ +typedef struct _mlan_ds_11ac_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11n configuration parameter */ + union { + /** VHT configuration for MLAN_OID_11AC_VHT_CFG */ + mlan_ds_11ac_vht_cfg vht_cfg; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[NUM_MCS_SUPP]; + /** Oper mode configuration for MLAN_OID_11AC_OPERMODE_CFG */ + mlan_ds_11ac_opermode_cfg opermode_cfg; + } param; +} mlan_ds_11ac_cfg, *pmlan_ds_11ac_cfg; + +/** Type definition of mlan_ds_11ax_he_capa for MLAN_OID_11AX_HE_CFG */ +typedef MLAN_PACK_START struct _mlan_ds_11ax_he_capa { + /** tlv id of he capability */ + t_u16 id; + /** length of the payload */ + t_u16 len; + /** extension id */ + t_u8 ext_id; + /** he mac capability info */ + t_u8 he_mac_cap[6]; + /** he phy capability info */ + t_u8 he_phy_cap[11]; + /** he txrx mcs support for 80MHz */ + t_u8 he_txrx_mcs_support[4]; + /** val for txrx mcs 160Mhz or 80+80, and PPE thresholds */ + t_u8 val[28]; +} MLAN_PACK_END mlan_ds_11ax_he_capa, *pmlan_ds_11ax_he_capa; + +/** Type definition of mlan_ds_11ax_he_cfg for MLAN_OID_11AX_HE_CFG */ +typedef struct _mlan_ds_11ax_he_cfg { + /** band, BIT0:2.4G, BIT1:5G*/ + t_u8 band; + /** mlan_ds_11ax_he_capa */ + mlan_ds_11ax_he_capa he_cap; +} mlan_ds_11ax_he_cfg, *pmlan_ds_11ax_he_cfg; +/** Type definition of mlan_ds_11as_cfg for MLAN_IOCTL_11AX_CFG */ +typedef struct _mlan_ds_11ax_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11n configuration parameter */ + union { + /** HE configuration for MLAN_OID_11AX_HE_CFG */ + mlan_ds_11ax_he_cfg he_cfg; + } param; +} mlan_ds_11ax_cfg, *pmlan_ds_11ax_cfg; + +#define MLAN_11AXCMD_CFG_ID_SR_OBSS_PD_OFFSET 1 +#define MLAN_11AXCMD_CFG_ID_SR_ENABLE 2 +#define MLAN_11AXCMD_CFG_ID_BEAM_CHANGE 3 +#define MLAN_11AXCMD_CFG_ID_HTC_ENABLE 4 +#define MLAN_11AXCMD_CFG_ID_TXOP_RTS 5 +#define MLAN_11AXCMD_CFG_ID_TX_OMI 6 +#define MLAN_11AXCMD_CFG_ID_OBSSNBRU_TOLTIME 7 + +#define MLAN_11AXCMD_SR_SUBID 0x102 +#define MLAN_11AXCMD_BEAM_SUBID 0x103 +#define MLAN_11AXCMD_HTC_SUBID 0x104 +#define MLAN_11AXCMD_TXOMI_SUBID 0x105 +#define MLAN_11AXCMD_OBSS_TOLTIME_SUBID 0x106 +#define MLAN_11AXCMD_TXOPRTS_SUBID 0x108 + +#define MLAN_11AX_TWT_SETUP_SUBID 0x114 +#define MLAN_11AX_TWT_TEARDOWN_SUBID 0x115 + +#define MRVL_DOT11AX_ENABLE_SR_TLV_ID (PROPRIETARY_TLV_BASE_ID + 322) +#define MRVL_DOT11AX_OBSS_PD_OFFSET_TLV_ID (PROPRIETARY_TLV_BASE_ID + 323) + +/** Type definition of mlan_11axcmdcfg_obss_pd_offset for MLAN_OID_11AX_CMD_CFG + */ +typedef struct MLAN_PACK_START _mlan_11axcmdcfg_obss_pd_offset { + /** */ + t_u8 offset[2]; +} MLAN_PACK_END mlan_11axcmdcfg_obss_pd_offset; + +/** Type definition of mlan_11axcmdcfg_sr_control for MLAN_OID_11AX_CMD_CFG */ +typedef struct MLAN_PACK_START _mlan_11axcmdcfg_sr_control { + /** 1 enable, 0 disable */ + t_u8 control; +} MLAN_PACK_END mlan_11axcmdcfg_sr_control; + +/** Type definition of mlan_ds_11ax_sr_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct MLAN_PACK_START _mlan_ds_11ax_sr_cmd { + /** type*/ + t_u16 type; + /** length of TLV */ + t_u16 len; + /** value */ + union { + mlan_11axcmdcfg_obss_pd_offset obss_pd_offset; + mlan_11axcmdcfg_sr_control sr_control; + } param; +} MLAN_PACK_END mlan_ds_11ax_sr_cmd, *pmlan_ds_11ax_sr_cmd; + +/** Type definition of mlan_ds_11ax_beam_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_beam_cmd { + /** command value: 1 is disable, 0 is enable*/ + t_u8 value; +} mlan_ds_11ax_beam_cmd, *pmlan_ds_11ax_beam_cmd; + +/** Type definition of mlan_ds_11ax_htc_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_htc_cmd { + /** command value: 1 is enable, 0 is disable*/ + t_u8 value; +} mlan_ds_11ax_htc_cmd, *pmlan_ds_11ax_htc_cmd; + +/** Type definition of mlan_ds_11ax_htc_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_txop_cmd { + /** Two byte rts threshold value of which only 10 bits, bit 0 to bit 9 + * are valid */ + t_u16 rts_thres; +} mlan_ds_11ax_txop_cmd, *pmlan_ds_11ax_txop_cmd; + +/** Type definition of mlan_ds_11ax_htc_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_txomi_cmd { + /* 11ax spec 9.2.4.6a.2 OM Control 12 bits. Bit 0 to bit 11 */ + t_u16 omi; +} mlan_ds_11ax_txomi_cmd, *pmlan_ds_11ax_txomi_cmd; + +/** Type definition of mlan_ds_11ax_toltime_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_toltime_cmd { + /* OBSS Narrow Bandwidth RU Tolerance Time */ + t_u32 tol_time; +} mlan_ds_11ax_toltime_cmd, *pmlan_ds_11ax_toltime_cmd; + +/** Type definition of mlan_ds_11ax_cmd_cfg for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_cmd_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Sub-id */ + t_u32 sub_id; + /** 802.11n configuration parameter */ + union { + /** SR configuration for MLAN_11AXCMD_SR_SUBID */ + mlan_ds_11ax_sr_cmd sr_cfg; + /** Beam configuration for MLAN_11AXCMD_BEAM_SUBID */ + mlan_ds_11ax_beam_cmd beam_cfg; + /** HTC configuration for MLAN_11AXCMD_HTC_SUBID */ + mlan_ds_11ax_htc_cmd htc_cfg; + /** txop RTS configuration for MLAN_11AXCMD_TXOPRTS_SUBID */ + mlan_ds_11ax_txop_cmd txop_cfg; + /** tx omi configuration for MLAN_11AXCMD_TXOMI_SUBID */ + mlan_ds_11ax_txomi_cmd txomi_cfg; + /** OBSS tolerance time configuration for + * MLAN_11AXCMD_TOLTIME_SUBID */ + mlan_ds_11ax_toltime_cmd toltime_cfg; + } param; +} mlan_ds_11ax_cmd_cfg, *pmlan_ds_11ax_cmd_cfg; + +/** Type definition of mlan_ds_twt_setup for MLAN_OID_11AX_TWT_CFG */ +typedef struct MLAN_PACK_START _mlan_ds_twt_setup { + /** Implicit, 0: TWT session is explicit, 1: Session is implicit */ + t_u8 implicit; + /** Announced, 0: Unannounced, 1: Announced TWT */ + t_u8 announced; + /** Trigger Enabled, 0: Non-Trigger enabled, 1: Trigger enabled TWT */ + t_u8 trigger_enabled; + /** TWT Information Disabled, 0: TWT info enabled, 1: TWT info disabled + */ + t_u8 twt_info_disabled; + /** Negotiation Type, 0: Future Individual TWT SP start time, 1: Next + * Wake TBTT time */ + t_u8 negotiation_type; + /** TWT Wakeup Duration, time after which the TWT requesting STA can + * transition to doze state */ + t_u8 twt_wakeup_duration; + /** Flow Identifier. Range: [0-7]*/ + t_u8 flow_identifier; + /** Hard Constraint, 0: FW can tweak the TWT setup parameters if it is + *rejected by AP. + ** 1: Firmware should not tweak any parameters. */ + t_u8 hard_constraint; + /** TWT Exponent, Range: [0-63] */ + t_u8 twt_exponent; + /** TWT Mantissa Range: [0-sizeof(UINT16)] */ + t_u16 twt_mantissa; + /** TWT Request Type, 0: REQUEST_TWT, 1: SUGGEST_TWT*/ + t_u8 twt_request; +} MLAN_PACK_END mlan_ds_twt_setup, *pmlan_ds_twt_setup; + +/** Type definition of mlan_ds_twt_teardown for MLAN_OID_11AX_TWT_CFG */ +typedef struct MLAN_PACK_START _mlan_ds_twt_teardown { + /** TWT Flow Identifier. Range: [0-7] */ + t_u8 flow_identifier; + /** Negotiation Type. 0: Future Individual TWT SP start time, 1: Next + * Wake TBTT time */ + t_u8 negotiation_type; + /** Tear down all TWT. 1: To teardown all TWT, 0 otherwise */ + t_u8 teardown_all_twt; +} MLAN_PACK_END mlan_ds_twt_teardown, *pmlan_ds_twt_teardown; + +/** Type definition of mlan_ds_twtcfg for MLAN_OID_11AX_TWT_CFG */ +typedef struct MLAN_PACK_START _mlan_ds_twtcfg { + /** Sub-command */ + t_u32 sub_command; + /** Sub-id */ + t_u32 sub_id; + /** TWT Setup/Teardown configuration parameter */ + union { + /** TWT Setup config for Sub ID: MLAN_11AX_TWT_SETUP_SUBID */ + mlan_ds_twt_setup twt_setup; + /** TWT Teardown config for Sub ID: MLAN_11AX_TWT_TEARDOWN_SUBID + */ + mlan_ds_twt_teardown twt_teardown; + } param; +} MLAN_PACK_END mlan_ds_twtcfg, *pmlan_ds_twtcfg; + +/** Country code length */ +#define COUNTRY_CODE_LEN 3 + +/*-----------------------------------------------------------------*/ +/** 802.11d Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum subbands for 11d */ +#define MRVDRV_MAX_SUBBAND_802_11D 83 + +/** 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; + +/** 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; +#endif /* STA_SUPPORT */ + /** Domain info for MLAN_OID_11D_DOMAIN_INFO_EXT */ + mlan_ds_11d_domain_info domain_info; +#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 CSU target device type */ +enum _mlan_csu_target_type { + MLAN_CSU_TARGET_CAU = 1, + MLAN_CSU_TARGET_PSU, +}; + +/** Enumeration for register type */ +enum _mlan_reg_type { + MLAN_REG_MAC = 1, + MLAN_REG_BBP, + MLAN_REG_RF, + MLAN_REG_CAU = 5, + MLAN_REG_PSU = 6, + MLAN_REG_BCA = 7, +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + MLAN_REG_MAC2 = 0x81, + MLAN_REG_BBP2 = 0x82, + MLAN_REG_RF2 = 0x83, + MLAN_REG_BCA2 = 0x87 +#endif +}; + +/** 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 */ +/*-----------------------------------------------------------------*/ +/** Type definition of mlan_ds_11h_dfs_testing for MLAN_OID_11H_DFS_TESTING */ +typedef struct _mlan_ds_11h_dfs_testing { + /** User-configured CAC period in milliseconds, 0 to use default */ + t_u32 usr_cac_period_msec; + /** User-configured NOP period in seconds, 0 to use default */ + t_u16 usr_nop_period_sec; + /** User-configured skip channel change, 0 to disable */ + t_u8 usr_no_chan_change; + /** User-configured fixed channel to change to, 0 to use random channel + */ + t_u8 usr_fixed_new_chan; + /** User-configured cac restart */ + t_u8 usr_cac_restart; +} mlan_ds_11h_dfs_testing, *pmlan_ds_11h_dfs_testing; + +/** Type definition of mlan_ds_11h_dfs_testing for MLAN_OID_11H_CHAN_NOP_INFO */ +typedef struct _mlan_ds_11h_chan_nop_info { + /** current channel */ + t_u8 curr_chan; + /** channel_width */ + t_u8 chan_width; + /** flag for chan under nop */ + t_bool chan_under_nop; + /** chan_ban_info for new channel */ + chan_band_info new_chan; +} mlan_ds_11h_chan_nop_info, *pmlan_ds_11h_chan_nop_info; + +typedef struct _mlan_ds_11h_chan_rep_req { + t_u16 startFreq; + Band_Config_t bandcfg; + t_u8 chanNum; + t_u32 millisec_dwell_time; /**< Channel dwell time in milliseconds */ + t_u8 host_based; +} mlan_ds_11h_chan_rep_req; + +typedef struct _mlan_ds_11h_dfs_w53_cfg { + /** dfs w53 cfg */ + t_u8 dfs53cfg; +} mlan_ds_11h_dfs_w53_cfg; + +/** 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; + /** User-configuation for MLAN_OID_11H_DFS_TESTING */ + mlan_ds_11h_dfs_testing dfs_testing; + /** channel NOP information for MLAN_OID_11H_CHAN_NOP_INFO */ + mlan_ds_11h_chan_nop_info ch_nop_info; + /** channel report req for MLAN_OID_11H_CHAN_REPORT_REQUEST */ + mlan_ds_11h_chan_rep_req chan_rpt_req; + /** channel switch count for MLAN_OID_11H_CHAN_SWITCH_COUNT*/ + t_s8 cs_count; + mlan_ds_11h_dfs_w53_cfg dfs_w53_cfg; + } param; +} mlan_ds_11h_cfg, *pmlan_ds_11h_cfg; + +/*-----------------------------------------------------------------*/ +/** Miscellaneous Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** CMD buffer size */ +#define MLAN_SIZE_OF_CMD_BUFFER (3 * 1024) + +/** 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, +}; + +/** 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; + +#ifdef SDIO +/** 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[MRVDRV_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; + +/** Type definition of mlan_ds_misc_rx_abort_cfg + * for MLAN_OID_MISC_RX_ABORT_CFG + */ +typedef struct _mlan_ds_misc_rx_abort_cfg { + /** enable/disable rx abort */ + t_u8 enable; + /** Rx weak RSSI pkt threshold */ + t_s8 rssi_threshold; +} mlan_ds_misc_rx_abort_cfg; + +/** Type definition of mlan_ds_misc_rx_abort_cfg_ext + * for MLAN_OID_MISC_RX_ABORT_CFG_EXT + */ +typedef struct _mlan_ds_misc_rx_abort_cfg_ext { + /** enable/disable dynamic rx abort */ + t_u8 enable; + /** rssi margin */ + t_s8 rssi_margin; + /** specify ceil rssi threshold */ + t_s8 ceil_rssi_threshold; +} mlan_ds_misc_rx_abort_cfg_ext; + +/** Type definition of mlan_ds_misc_rx_abort_cfg_ext + * for MLAN_OID_MISC_TX_AMDPU_PROT_MODE + */ +typedef struct _mlan_ds_misc_tx_ampdu_prot_mode { + /** set prot mode */ + t_u16 mode; +} mlan_ds_misc_tx_ampdu_prot_mode; + +/** Type definition of mlan_ds_misc_dot11mc_unassoc_ftm_cfg + * for MLAN_OID_MISC_DOT11MC_UNASSOC_FTM_CFG + */ +typedef struct _mlan_ds_misc_dot11mc_unassoc_ftm_cfg { + /** set the state */ + t_u16 state; +} mlan_ds_misc_dot11mc_unassoc_ftm_cfg; + +#define RATEADAPT_ALGO_LEGACY 0 +#define RATEADAPT_ALGO_SR 1 + +/** Type definition of mlan_ds_misc_rate_adapt_cfg + * for MLAN_OID_MISC_RATE_ADAPT_CFG + */ +typedef struct _mlan_ds_misc_rate_adapt_cfg { + /** SR Rateadapt */ + t_u8 sr_rateadapt; + /** set low threshold */ + t_u8 ra_low_thresh; + /** set high threshold */ + t_u8 ra_high_thresh; + /** set interval */ + t_u16 ra_interval; +} mlan_ds_misc_rate_adapt_cfg; + +/** Type definition of mlan_ds_misc_cck_desense_cfg + * for MLAN_OID_MISC_CCK_DESENSE_CFG + */ +typedef struct _mlan_ds_misc_cck_desense_cfg { + /** cck desense mode: 0:disable 1:normal 2:dynamic */ + t_u16 mode; + /** specify rssi margin */ + t_s8 margin; + /** specify ceil rssi threshold */ + t_s8 ceil_thresh; + /** cck desense "on" interval count */ + t_u8 num_on_intervals; + /** cck desense "off" interval count */ + t_u8 num_off_intervals; +} mlan_ds_misc_cck_desense_cfg; + +/** IP address length */ +#define IPADDR_LEN (16) +/** Max number of ip */ +#define MAX_IPADDR (4) +/** IP address type - NONE*/ +#define IPADDR_TYPE_NONE (0) +/** IP address type - IPv4*/ +#define IPADDR_TYPE_IPV4 (1) +/** IP operation remove */ +#define MLAN_IPADDR_OP_IP_REMOVE (0) +/** IP operation ARP response */ +#define MLAN_IPADDR_OP_AUTO_ARP_RESP MBIT(1) + +/** Type definition of mlan_ds_misc_ipaddr_cfg for MLAN_OID_MISC_IP_ADDR */ +typedef struct _mlan_ds_misc_ipaddr_cfg { + /** Operation code */ + t_u32 op_code; + /** IP address type */ + t_u32 ip_addr_type; + /** Number of IP */ + t_u32 ip_addr_num; + /** IP address */ + t_u8 ip_addr[MAX_IPADDR][IPADDR_LEN]; +} mlan_ds_misc_ipaddr_cfg; + +/* MEF configuration disable */ +#define MEF_CFG_DISABLE 0 +/* MEF configuration Rx filter enable */ +#define MEF_CFG_RX_FILTER_ENABLE 1 +/* MEF configuration auto ARP response */ +#define MEF_CFG_AUTO_ARP_RESP 2 +/* MEF configuration host command */ +#define MEF_CFG_HOSTCMD 0xFFFF + +/** Type definition of mlan_ds_misc_mef_cfg for MLAN_OID_MISC_MEF_CFG */ +typedef struct _mlan_ds_misc_mef_cfg { + /** Sub-ID for operation */ + t_u32 sub_id; + /** Parameter according to sub-ID */ + union { + /** MEF command buffer for MEF_CFG_HOSTCMD */ + mlan_ds_misc_cmd cmd_buf; + } param; +} mlan_ds_misc_mef_cfg; + +/** Type definition of mlan_ds_misc_cfp_code for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_misc_cfp_code { + /** CFP table code for 2.4GHz */ + t_u32 cfp_code_bg; + /** CFP table code for 5GHz */ + t_u32 cfp_code_a; +} mlan_ds_misc_cfp_code; + +/** Type definition of mlan_ds_misc_arb_cfg + * for MLAN_OID_MISC_ARB_CFG + */ +typedef struct _mlan_ds_misc_arb_cfg { + /** arb mode 0-4 */ + t_u32 arb_mode; +} mlan_ds_misc_arb_cfg; + +/** Type definition of mlan_ds_misc_tp_state + * for MLAN_OID_MISC_TP_STATE + */ +typedef struct _mlan_ds_misc_tp_state { + /** TP account mode 0-disable 1-enable */ + t_u32 on; + /** Packet drop point */ + t_u32 drop_point; +} mlan_ds_misc_tp_state; + +/** Type definition of mlan_ds_misc_country_code + * for MLAN_OID_MISC_COUNTRY_CODE + */ +typedef struct _mlan_ds_misc_country_code { + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; +} mlan_ds_misc_country_code; + +/** action for set */ +#define SUBSCRIBE_EVT_ACT_BITWISE_SET 0x0002 +/** action for clear */ +#define SUBSCRIBE_EVT_ACT_BITWISE_CLR 0x0003 +/** BITMAP for subscribe event rssi low */ +#define SUBSCRIBE_EVT_RSSI_LOW MBIT(0) +/** BITMAP for subscribe event snr low */ +#define SUBSCRIBE_EVT_SNR_LOW MBIT(1) +/** BITMAP for subscribe event max fail */ +#define SUBSCRIBE_EVT_MAX_FAIL MBIT(2) +/** BITMAP for subscribe event beacon missed */ +#define SUBSCRIBE_EVT_BEACON_MISSED MBIT(3) +/** BITMAP for subscribe event rssi high */ +#define SUBSCRIBE_EVT_RSSI_HIGH MBIT(4) +/** BITMAP for subscribe event snr high */ +#define SUBSCRIBE_EVT_SNR_HIGH MBIT(5) +/** BITMAP for subscribe event data rssi low */ +#define SUBSCRIBE_EVT_DATA_RSSI_LOW MBIT(6) +/** BITMAP for subscribe event data snr low */ +#define SUBSCRIBE_EVT_DATA_SNR_LOW MBIT(7) +/** BITMAP for subscribe event data rssi high */ +#define SUBSCRIBE_EVT_DATA_RSSI_HIGH MBIT(8) +/** BITMAP for subscribe event data snr high */ +#define SUBSCRIBE_EVT_DATA_SNR_HIGH MBIT(9) +/** BITMAP for subscribe event link quality */ +#define SUBSCRIBE_EVT_LINK_QUALITY MBIT(10) +/** BITMAP for subscribe event pre_beacon_lost */ +#define SUBSCRIBE_EVT_PRE_BEACON_LOST MBIT(11) +/** default PRE_BEACON_MISS_COUNT */ +#define DEFAULT_PRE_BEACON_MISS 30 + +/** Type definition of mlan_ds_subscribe_evt for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_subscribe_evt { + /** evt action */ + t_u16 evt_action; + /** bitmap for subscribe event */ + t_u16 evt_bitmap; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 low_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 low_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 low_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 low_snr_freq; + /** Failure count threshold */ + t_u8 failure_count; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 failure_count_freq; + /** num of missed beacons */ + t_u8 beacon_miss; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 beacon_miss_freq; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 high_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 high_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 high_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 high_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_low_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_low_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_low_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_low_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_high_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_high_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_high_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_high_snr_freq; + /* Link SNR threshold (dB)*/ + t_u16 link_snr; + /* Link SNR frequency */ + t_u16 link_snr_freq; + /* Second minimum rate value as per the rate table below */ + t_u16 link_rate; + /* Second minimum rate frequency */ + t_u16 link_rate_freq; + /* Tx latency value (us) */ + t_u16 link_tx_latency; + /* Tx latency frequency */ + t_u16 link_tx_lantency_freq; + /* Number of pre missed beacons */ + t_u8 pre_beacon_miss; +} mlan_ds_subscribe_evt; + +/** Max OTP user data length */ +#define MAX_OTP_USER_DATA_LEN 252 + +/** Type definition of mlan_ds_misc_otp_user_data + * for MLAN_OID_MISC_OTP_USER_DATA + */ +typedef struct _mlan_ds_misc_otp_user_data { + /** Reserved */ + t_u16 reserved; + /** OTP user data length */ + t_u16 user_data_length; + /** User data buffer */ + t_u8 user_data[MAX_OTP_USER_DATA_LEN]; +} mlan_ds_misc_otp_user_data; + +typedef struct _aggr_ctrl { + /** Enable */ + t_u16 enable; + /** Aggregation alignment */ + t_u16 aggr_align; + /** Aggregation max size */ + t_u16 aggr_max_size; + /** Aggregation max packet number */ + t_u16 aggr_max_num; + /** Aggrgation timeout, in microseconds */ + t_u16 aggr_tmo; +} aggr_ctrl; + +/** Type definition of mlan_ds_misc_aggr_ctrl + * for MLAN_OID_MISC_AGGR_CTRL + */ +typedef struct _mlan_ds_misc_aggr_ctrl { + /** Tx aggregation control */ + aggr_ctrl tx; +} mlan_ds_misc_aggr_ctrl; + +#ifdef USB +typedef struct _usb_aggr_ctrl { + /** Enable */ + t_u16 enable; + /** Aggregation mode */ + t_u16 aggr_mode; + /** Aggregation alignment */ + t_u16 aggr_align; + /** Aggregation max packet/size */ + t_u16 aggr_max; + /** Aggrgation timeout, in microseconds */ + t_u16 aggr_tmo; +} usb_aggr_ctrl; + +/** Type definition of mlan_ds_misc_usb_aggr_ctrl + * for MLAN_OID_MISC_USB_AGGR_CTRL + */ +typedef struct _mlan_ds_misc_usb_aggr_ctrl { + /** Tx aggregation control */ + usb_aggr_ctrl tx_aggr_ctrl; + /** Rx deaggregation control */ + usb_aggr_ctrl rx_deaggr_ctrl; +} mlan_ds_misc_usb_aggr_ctrl; +#endif + +#ifdef WIFI_DIRECT_SUPPORT +/** flag for NOA */ +#define WIFI_DIRECT_NOA 1 +/** flag for OPP_PS */ +#define WIFI_DIRECT_OPP_PS 2 +/** Type definition of mlan_ds_wifi_direct_config + * for MLAN_OID_MISC_WIFI_DIRECT_CONFIG + */ +typedef struct _mlan_ds_wifi_direct_config { + /** flags for NOA/OPP_PS */ + t_u8 flags; + /** NoA enable/disable */ + t_u8 noa_enable; + /** index */ + t_u16 index; + /** NoA count */ + t_u8 noa_count; + /** NoA duration */ + t_u32 noa_duration; + /** NoA interval */ + t_u32 noa_interval; + /** opp ps enable/disable */ + t_u8 opp_ps_enable; + /** CT window value */ + t_u8 ct_window; +} mlan_ds_wifi_direct_config; +#endif + +#if defined(STA_SUPPORT) +typedef struct _mlan_ds_misc_pmfcfg { + /** Management Frame Protection Capable */ + t_u8 mfpc; + /** Management Frame Protection Required */ + t_u8 mfpr; +} mlan_ds_misc_pmfcfg; +#endif + +#define MAX_SSID_NUM 16 +#define MAX_AP_LIST 8 + +#ifdef RX_PACKET_COALESCE +typedef struct _mlan_ds_misc_rx_packet_coalesce { + /** packet threshold */ + t_u32 packet_threshold; + /** timeout value */ + t_u16 delay; +} mlan_ds_misc_rx_packet_coalesce; +#endif + +typedef struct _mlan_ds_misc_dfs_repeater { + /** Set or Get */ + t_u16 action; + /** 1 on or 0 off */ + t_u16 mode; +} mlan_ds_misc_dfs_repeater; + +#define WOWLAN_MAX_PATTERN_LEN 20 +#define WOWLAN_MAX_OFFSET_LEN 50 +#define MAX_NUM_FILTERS 10 +#define MEF_MODE_HOST_SLEEP (1 << 0) +#define MEF_MODE_NON_HOST_SLEEP (1 << 1) +#define MEF_ACTION_WAKE (1 << 0) +#define MEF_ACTION_ALLOW (1 << 1) +#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3 +#define MEF_AUTO_ARP 0x10 +#define MEF_AUTO_PING 0x20 +#define MEF_NS_RESP 0x40 +#define MEF_MAGIC_PKT 0x80 +#define CRITERIA_BROADCAST BIT(0) +#define CRITERIA_UNICAST BIT(1) +#define CRITERIA_MULTICAST BIT(3) + +#define MAX_NUM_ENTRIES 8 +#define MAX_NUM_BYTE_SEQ 6 +#define MAX_NUM_MASK_SEQ 6 + +#define OPERAND_DNUM 1 +#define OPERAND_BYTE_SEQ 2 + +#define MAX_OPERAND 0x40 +#define TYPE_BYTE_EQ (MAX_OPERAND + 1) +#define TYPE_DNUM_EQ (MAX_OPERAND + 2) +#define TYPE_BIT_EQ (MAX_OPERAND + 3) + +#define RPN_TYPE_AND (MAX_OPERAND + 4) +#define RPN_TYPE_OR (MAX_OPERAND + 5) + +#define ICMP_OF_IP_PROTOCOL 0x01 +#define TCP_OF_IP_PROTOCOL 0x06 +#define UDP_OF_IP_PROTOCOL 0x11 + +#define IPV4_PKT_OFFSET 20 +#define IP_PROTOCOL_OFFSET 31 +#define PORT_PROTOCOL_OFFSET 44 + +#define FILLING_TYPE MBIT(0) +#define FILLING_PATTERN MBIT(1) +#define FILLING_OFFSET MBIT(2) +#define FILLING_NUM_BYTES MBIT(3) +#define FILLING_REPEAT MBIT(4) +#define FILLING_BYTE_SEQ MBIT(5) +#define FILLING_MASK_SEQ MBIT(6) + +/** Type definition of filter_item + * Support three match methods: + * <1>Byte comparison type=0x41 + * <2>Decimal comparison type=0x42 + * <3>Bit comparison type=0x43 + */ +typedef struct _mef_filter_t { + /** flag*/ + t_u32 fill_flag; + /** BYTE 0X41; Decimal 0X42; Bit 0x43*/ + t_u16 type; + /** value*/ + t_u32 pattern; + /** offset*/ + t_u16 offset; + /** number of bytes*/ + t_u16 num_bytes; + /** repeat*/ + t_u16 repeat; + /** byte number*/ + t_u8 num_byte_seq; + /** array*/ + t_u8 byte_seq[MAX_NUM_BYTE_SEQ]; + /** mask numbers*/ + t_u8 num_mask_seq; + /** array*/ + t_u8 mask_seq[MAX_NUM_MASK_SEQ]; +} mef_filter_t; + +typedef struct _mef_entry_t { + /** mode: bit0--hostsleep mode; bit1--non hostsleep mode */ + t_u8 mode; + /** action: 0--discard and not wake host; + 1--discard and wake host; + 3--allow and wake host;*/ + t_u8 action; + /** filter number */ + t_u8 filter_num; + /** filter array*/ + mef_filter_t filter_item[MAX_NUM_FILTERS]; + /** rpn array*/ + t_u8 rpn[MAX_NUM_FILTERS]; +} mef_entry_t; + +/** Type definition of mlan_ds_nvflt_mef_entry + *for MLAN_OID_MISC_MEF_FLT_CFG + */ +typedef struct _mlan_ds_misc_mef_flt_cfg { + /** Type of action*/ + int mef_act_type; + /** NV Filter Criteria*/ + t_u32 criteria; + /** NV MEF entry*/ + mef_entry_t mef_entry; +} mlan_ds_misc_mef_flt_cfg; + +/** Enumeration for action type*/ +enum _mlan_act_mef_act_type { + MEF_ACT_ADD = 1, + MEF_ACT_ENABLE, + MEF_ACT_DISABLE, + MEF_ACT_CANCEL, + MEF_ACT_AUTOARP, + MEF_ACT_WOWLAN, + MEF_ACT_IPV6_NS, +}; + +typedef struct _mlan_ds_sensor_temp { + t_u32 temperature; +} mlan_ds_sensor_temp; + +#define MLAN_KCK_LEN 16 +#define MLAN_KEK_LEN 16 +#define MLAN_REPLAY_CTR_LEN 8 +/** mlan_ds_misc_gtk_rekey_data */ +typedef struct _mlan_ds_misc_gtk_rekey_data { + /** key encryption key */ + t_u8 kek[MLAN_KEK_LEN]; + /** key confirmation key */ + t_u8 kck[MLAN_KCK_LEN]; + /** replay counter */ + t_u8 replay_ctr[MLAN_REPLAY_CTR_LEN]; +} mlan_ds_misc_gtk_rekey_data; +typedef struct _mlan_ds_bw_chan_oper { + /* bandwidth 20:20M 40:40M 80:80M*/ + t_u8 bandwidth; + /* channel number */ + t_u8 channel; + /* Non-global operating class */ + t_u8 oper_class; +} mlan_ds_bw_chan_oper; + +typedef struct _mlan_ds_ind_rst_cfg { + /** Set or Get */ + t_u16 action; + /** oob mode enable/ disable */ + t_u8 ir_mode; + /** gpio pin */ + t_u8 gpio_pin; +} mlan_ds_ind_rst_cfg; + +#define MKEEP_ALIVE_IP_PKT_MAX 256 +typedef struct _mlan_ds_misc_keep_alive { + t_u8 mkeep_alive_id; + t_u8 enable; + /** enable/disable tcp reset*/ + t_u8 reset; + /**True means saved in driver, false means not saved or download*/ + t_u8 cached; + t_u32 send_interval; + t_u16 retry_interval; + t_u16 retry_count; + t_u8 dst_mac[MLAN_MAC_ADDR_LENGTH]; + t_u8 src_mac[MLAN_MAC_ADDR_LENGTH]; + t_u16 pkt_len; + t_u8 packet[MKEEP_ALIVE_IP_PKT_MAX]; + /** Ethernet type */ + t_u16 ether_type; +} mlan_ds_misc_keep_alive, *pmlan_ds_misc_keep_alive; + +/** TX and RX histogram statistic parameters*/ +typedef MLAN_PACK_START struct _mlan_ds_misc_tx_rx_histogram { + /** Enable or disable get tx/rx histogram statistic */ + t_u8 enable; + /** Choose to get TX, RX or both histogram statistic */ + t_u16 action; + /** Size of Tx/Rx info */ + t_u16 size; + /** Store Tx/Rx info */ + t_u8 value[1]; +} MLAN_PACK_END mlan_ds_misc_tx_rx_histogram; + +typedef MLAN_PACK_START struct _mlan_ds_cw_mode_ctrl { + /** Mode of Operation 0: Disable 1: Tx Continuous Packet 2: Tx + * Continuous Wave */ + t_u8 mode; + /*channel*/ + t_u8 channel; + /* channel info*/ + t_u8 chanInfo; + /** Tx Power level in dBm */ + t_u16 txPower; + /** Packet Length */ + t_u16 pktLength; + /** bit rate Info */ + t_u32 rateInfo; +} MLAN_PACK_END mlan_ds_cw_mode_ctrl; + +#define RX_PKT_INFO MBIT(1) +/** Struct for per-packet configuration */ +typedef struct _mlan_per_pkt_cfg { + /** Type ID*/ + t_u16 type; + /** Length of payload*/ + t_u16 len; + /** Tx/Rx per-packet control */ + t_u8 tx_rx_control; + /** Number of ethernet types in ether_type array */ + t_u8 proto_type_num; + /** Array of ether_type for per-packet control */ + t_u16 ether_type[]; +} mlan_per_pkt_cfg; + +/** Type definition of mlan_ds_misc_robustcoex_params for MLAN_IOCTL_MISC_CFG */ +typedef struct _mlan_ds_misc_robustcoex_params { + t_u16 method; + /** enable/disable robustcoex gpio cfg */ + t_u8 enable; + /** Number of GPIO */ + t_u8 gpio_num; + /** Polarity of GPIO */ + t_u8 gpio_polarity; +} mlan_ds_misc_robustcoex_params; + +#if defined(PCIE) +typedef struct _mlan_ds_ssu_params { + t_u32 nskip; + t_u32 nsel; + t_u32 adcdownsample; + t_u32 mask_adc_pkt; + t_u32 out_16bits; + t_u32 spec_pwr_enable; + t_u32 rate_deduction; + t_u32 n_pkt_avg; +} mlan_ds_ssu_params; +#endif + +#define MAX_NUM_MAC 2 +/** Type definition of mlan_ds_misc_mapping_policy */ +typedef struct _mlan_ds_misc_mapping_policy { + /** Enable/disable dynamic mapping */ + t_u16 subcmd; + /** Mapping policy */ + t_u8 mapping_policy; +} mlan_ds_misc_mapping_policy, *pmlan_ds_misc_mapping_policy; + +typedef struct _dmcsChanStatus_t { + /** Channel number */ + t_u8 channel; + /** Number of ap on this channel */ + t_u8 ap_count; + /** Number of sta on this channel */ + t_u8 sta_count; +} dmcsChanStatus_t, *pdmcsChanStatus_t; + +typedef struct _dmcsStatus_t { + /** Radio ID */ + t_u8 radio_id; + /** Running mode + ** 0 - Idle + ** 1 - DBC + ** 2 - DRCS + */ + t_u8 running_mode; + /** Current channel status */ + dmcsChanStatus_t chan_status[2]; +} dmcsStatus_t, *pdmcsStatus_t; + +/** Type definition of mlan_ds_misc_dmcs_status */ +typedef struct _mlan_ds_misc_dmcs_status { + t_u8 mapping_policy; + dmcsStatus_t radio_status[MAX_NUM_MAC]; +} mlan_ds_misc_dmcs_status, *pmlan_ds_misc_dmcs_status; + +/** Type definition of mlan_ds_misc_chan_trpc_cfg for + * MLAN_OID_MISC_GET_CHAN_TRPC_CFG */ +typedef struct _mlan_ds_misc_chan_trpc_cfg { + /** sub_band */ + t_u16 sub_band; + /** length */ + t_u16 length; + /** buf */ + t_u8 trpc_buf[2048]; +} mlan_ds_misc_chan_trpc_cfg; + +#define MFG_CMD_SET_TEST_MODE 1 +#define MFG_CMD_UNSET_TEST_MODE 0 +#define MFG_CMD_TX_ANT 0x1004 +#define MFG_CMD_RX_ANT 0x1005 +#define MFG_CMD_TX_CONT 0x1009 +#define MFG_CMD_RF_CHAN 0x100A +#define MFG_CMD_CLR_RX_ERR 0x1010 +#define MFG_CMD_TX_FRAME 0x1021 +#define MFG_CMD_RFPWR 0x1033 +#define MFG_CMD_RF_BAND_AG 0x1034 +#define MFG_CMD_RF_CHANNELBW 0x1044 +/** MFG CMD generic cfg */ +struct MLAN_PACK_START mfg_cmd_generic_cfg { + /** MFG command code */ + t_u32 mfg_cmd; + /** Action */ + t_u16 action; + /** Device ID */ + t_u16 device_id; + /** MFG Error code */ + t_u32 error; + /** value 1 */ + t_u32 data1; + /** value 2 */ + t_u32 data2; + /** value 3 */ + t_u32 data3; +} MLAN_PACK_END; + +/** MFG CMD Tx Frame 2 */ +struct MLAN_PACK_START mfg_cmd_tx_frame2 { + /** MFG command code */ + t_u32 mfg_cmd; + /** Action */ + t_u16 action; + /** Device ID */ + t_u16 device_id; + /** MFG Error code */ + t_u32 error; + /** enable */ + t_u32 enable; + /** data_rate */ + t_u32 data_rate; + /** frame pattern */ + t_u32 frame_pattern; + /** frame length */ + t_u32 frame_length; + /** BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Adjust burst sifs */ + t_u16 adjust_burst_sifs; + /** Burst sifs in us*/ + t_u32 burst_sifs_in_us; + /** short preamble */ + t_u32 short_preamble; + /** active sub channel */ + t_u32 act_sub_ch; + /** short GI */ + t_u32 short_gi; + /** Adv coding */ + t_u32 adv_coding; + /** Tx beamforming */ + t_u32 tx_bf; + /** HT Greenfield Mode*/ + t_u32 gf_mode; + /** STBC */ + t_u32 stbc; + /** power id */ + t_u32 rsvd[2]; +} MLAN_PACK_END; + +/* MFG CMD Tx Continuous */ +struct MLAN_PACK_START mfg_cmd_tx_cont { + /** MFG command code */ + t_u32 mfg_cmd; + /** Action */ + t_u16 action; + /** Device ID */ + t_u16 device_id; + /** MFG Error code */ + t_u32 error; + /** enable Tx*/ + t_u32 enable_tx; + /** Continuous Wave mode */ + t_u32 cw_mode; + /** payload pattern */ + t_u32 payload_pattern; + /** CS Mode */ + t_u32 cs_mode; + /** active sub channel */ + t_u32 act_sub_ch; + /** Tx rate */ + t_u32 tx_rate; + /** power id */ + t_u32 rsvd; +} MLAN_PACK_END; + +typedef struct _mlan_ds_misc_chnrgpwr_cfg { + /** length */ + t_u16 length; + /** chnrgpwr buf */ + t_u8 chnrgpwr_buf[2048]; +} mlan_ds_misc_chnrgpwr_cfg; + +/** dfs chan list for MLAN_OID_MISC_CFP_TABLE */ +typedef struct _mlan_ds_misc_cfp_tbl { + /** band */ + t_u8 band; + /** num chan */ + t_u8 num_chan; + /** cfp table */ + chan_freq_power_t cfp_tbl[]; +} mlan_ds_misc_cfp_tbl; + +/** 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; +#ifdef SDIO + /** SDIO MP-A Ctrl command for MLAN_OID_MISC_SDIO_MPA_CTRL */ + mlan_ds_misc_sdio_mpa_ctrl mpa_ctrl; +#endif + /** Hostcmd for MLAN_OID_MISC_HOST_CMD */ + mlan_ds_misc_cmd hostcmd; + /** System clock for MLAN_OID_MISC_SYS_CLOCK */ + mlan_ds_misc_sys_clock sys_clock; + /** WWS set/get for MLAN_OID_MISC_WWS */ + t_u32 wws_cfg; + /** Get associate response for MLAN_OID_MISC_ASSOC_RSP */ + mlan_ds_misc_assoc_rsp assoc_resp; + /** Function init/shutdown for MLAN_OID_MISC_INIT_SHUTDOWN */ + t_u32 func_init_shutdown; + /** Custom IE for MLAN_OID_MISC_CUSTOM_IE */ + mlan_ds_misc_custom_ie cust_ie; + /** Config dynamic bandwidth*/ + t_u16 dyn_bw; + /** Tx data pause for MLAN_OID_MISC_TX_DATAPAUSE */ + mlan_ds_misc_tx_datapause tx_datapause; + /** IP address configuration */ + mlan_ds_misc_ipaddr_cfg ipaddr_cfg; + /** MAC control for MLAN_OID_MISC_MAC_CONTROL */ + t_u32 mac_ctrl; + /** MEF configuration for MLAN_OID_MISC_MEF_CFG */ + mlan_ds_misc_mef_cfg mef_cfg; + /** CFP code for MLAN_OID_MISC_CFP_CODE */ + mlan_ds_misc_cfp_code cfp_code; + /** Country code for MLAN_OID_MISC_COUNTRY_CODE */ + mlan_ds_misc_country_code country_code; + /** Thermal reading for MLAN_OID_MISC_THERMAL */ + t_u32 thermal; + /** Mgmt subtype mask for MLAN_OID_MISC_RX_MGMT_IND */ + t_u32 mgmt_subtype_mask; + /** subscribe event for MLAN_OID_MISC_SUBSCRIBE_EVENT */ + mlan_ds_subscribe_evt subscribe_event; +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif + /** Hotspot config param set */ + t_u32 hotspot_cfg; +#ifdef STA_SUPPORT + ExtCap_t ext_cap; +#endif + mlan_ds_misc_otp_user_data otp_user_data; +#ifdef USB + /** USB aggregation parameters for MLAN_OID_MISC_USB_AGGR_CTRL + */ + mlan_ds_misc_usb_aggr_ctrl usb_aggr_params; +#endif + mlan_ds_misc_aggr_ctrl aggr_params; + /** Tx control */ + t_u32 tx_control; +#if defined(STA_SUPPORT) + mlan_ds_misc_pmfcfg pmfcfg; +#endif +#ifdef WIFI_DIRECT_SUPPORT + mlan_ds_wifi_direct_config p2p_config; +#endif + mlan_ds_coalesce_cfg coalesce_cfg; + t_u8 low_pwr_mode; + /** MEF-FLT-CONFIG for MLAN_OID_MISC_NV_FLT_CFG */ + mlan_ds_misc_mef_flt_cfg mef_flt_cfg; + mlan_ds_misc_dfs_repeater dfs_repeater; +#ifdef RX_PACKET_COALESCE + mlan_ds_misc_rx_packet_coalesce rx_coalesce; +#endif + /** FW reload flag */ + t_u8 fw_reload; + mlan_ds_sensor_temp sensor_temp; + /** GTK rekey data */ + mlan_ds_misc_gtk_rekey_data gtk_rekey; + mlan_ds_bw_chan_oper bw_chan_oper; + mlan_ds_ind_rst_cfg ind_rst_cfg; + t_u64 misc_tsf; + mlan_ds_custom_reg_domain custom_reg_domain; + mlan_ds_misc_keep_alive keep_alive; + mlan_ds_misc_tx_rx_histogram tx_rx_histogram; + mlan_ds_cw_mode_ctrl cwmode; + /** Tx/Rx per-packet control */ + t_u8 txrx_pkt_ctrl; + mlan_ds_misc_robustcoex_params robustcoexparams; +#if defined(PCIE) + mlan_ds_ssu_params ssu_params; +#endif + /** boot sleep enable or disable */ + t_u16 boot_sleep; + /** Mapping Policy */ + mlan_ds_misc_mapping_policy dmcs_policy; + mlan_ds_misc_dmcs_status dmcs_status; + mlan_ds_misc_rx_abort_cfg rx_abort_cfg; + mlan_ds_misc_rx_abort_cfg_ext rx_abort_cfg_ext; + mlan_ds_misc_tx_ampdu_prot_mode tx_ampdu_prot_mode; + mlan_ds_misc_rate_adapt_cfg rate_adapt_cfg; + mlan_ds_misc_cck_desense_cfg cck_desense_cfg; + mlan_ds_misc_chan_trpc_cfg trpc_cfg; + mlan_ds_misc_chnrgpwr_cfg rgchnpwr_cfg; + + mlan_ds_band_steer_cfg band_steer_cfg; + mlan_ds_beacon_stuck_param_cfg beacon_stuck_cfg; + struct mfg_cmd_generic_cfg mfg_generic_cfg; + struct mfg_cmd_tx_cont mfg_tx_cont; + struct mfg_cmd_tx_frame2 mfg_tx_frame2; + mlan_ds_misc_arb_cfg arb_cfg; + mlan_ds_misc_cfp_tbl cfp; + t_u8 range_ext_mode; + mlan_ds_misc_dot11mc_unassoc_ftm_cfg dot11mc_unassoc_ftm_cfg; + mlan_ds_misc_tp_state tp_state; + } param; +} mlan_ds_misc_cfg, *pmlan_ds_misc_cfg; + +/** Hotspot status enable */ +#define HOTSPOT_ENABLED MBIT(0) +/** Hotspot status disable */ +#define HOTSPOT_DISABLED MFALSE +/** Keep Hotspot2.0 compatible in mwu and wpa_supplicant */ +#define HOTSPOT_BY_SUPPLICANT MBIT(1) + +/** Reason codes */ +#define MLAN_REASON_UNSPECIFIED 1 +#define MLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define MLAN_REASON_DEAUTH_LEAVING 3 +#define MLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define MLAN_REASON_DISASSOC_AP_BUSY 5 +#define MLAN_REASON_CLASS2_FRAME_FROM_NOAUTH_STA 6 +#define MLAN_REASON_CLASS3_FRAME_FROM_NOASSOC_STA 7 +#define MLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define MLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 +#endif /* !_MLAN_IOCTL_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_join.c b/mxm_wifiex/wlan_src/mlan/mlan_join.c new file mode 100644 index 0000000..359e031 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_join.c @@ -0,0 +1,2621 @@ +/** @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 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11ac.h" +#include "mlan_11ax.h" +#include "mlan_11h.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif +/******************************************************** + Local Constants +********************************************************/ + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Append a generic IE as a pass through TLV to a TLV buffer. + * + * This function is called from the network join command prep. routine. + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a pass through TLV type to the request. + * + * @param priv A pointer to mlan_private structure + * @param ppbuffer pointer to command buffer pointer + * + * @return bytes added to the buffer + */ +static int wlan_cmd_append_generic_ie(mlan_private *priv, t_u8 **ppbuffer) +{ + int ret_len = 0; + MrvlIEtypesHeader_t ie_header; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + + /* + * If there is a generic ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->gen_ie_buf_len) { + PRINTM(MINFO, "append generic IE %d to %p\n", + priv->gen_ie_buf_len, *ppbuffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = wlan_cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = wlan_cpu_to_le16(priv->gen_ie_buf_len); + memcpy_ext(priv->adapter, *ppbuffer, &ie_header, + sizeof(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_ext(priv->adapter, *ppbuffer, priv->gen_ie_buf, + priv->gen_ie_buf_len, priv->gen_ie_buf_len); + + /* Increment the return size and the return buffer pointer param + */ + *ppbuffer += priv->gen_ie_buf_len; + ret_len += priv->gen_ie_buf_len; + + /* Reset the generic IE buffer */ + priv->gen_ie_buf_len = 0; + } + + /* return the length appended to the buffer */ + LEAVE(); + return ret_len; +} + +/** + * @brief Append IE as a pass through TLV to a TLV buffer. + * + * This routine appends IE as a pass through TLV type to the request. + * + * @param priv A pointer to mlan_private structure + * @param ie A pointer to IE buffer + * @param ppbuffer pointer to command buffer pointer + * + * @return bytes added to the buffer + */ +static int wlan_cmd_append_pass_through_ie(mlan_private *priv, + IEEEtypes_Generic_t *ie, + t_u8 **ppbuffer) +{ + int ret_len = 0; + MrvlIEtypesHeader_t ie_header; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (ie->ieee_hdr.len) { + PRINTM(MINFO, "append generic IE %d to %p\n", ie->ieee_hdr.len, + *ppbuffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = wlan_cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = wlan_cpu_to_le16(ie->ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + memcpy_ext(priv->adapter, *ppbuffer, &ie_header, + sizeof(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_ext(priv->adapter, *ppbuffer, ie, + ie->ieee_hdr.len + sizeof(IEEEtypes_Header_t), + ie->ieee_hdr.len + sizeof(IEEEtypes_Header_t)); + + /* Increment the return size and the return buffer pointer param + */ + *ppbuffer += ie->ieee_hdr.len + sizeof(IEEEtypes_Header_t); + ret_len += ie->ieee_hdr.len + sizeof(IEEEtypes_Header_t); + } + /* return the length appended to the buffer */ + LEAVE(); + return ret_len; +} + +/** + * @brief Append TSF tracking info from the scan table for the target AP + * + * This function is called from the network join command prep. routine. + * The TSF table TSF sent to the firmware contains two TSF values: + * - the TSF of the target AP from its previous beacon/probe response + * - the TSF timestamp of our local MAC at the time we observed the + * beacon/probe response. + * + * The firmware uses the timestamp values to set an initial TSF value + * in the MAC for the new association after a reassociation attempt. + * + * @param pmpriv A pointer to mlan_private structure + * @param ppbuffer A pointer to command buffer pointer + * @param pbss_desc A pointer to the BSS Descriptor from the scan table of + * the AP we are trying to join + * + * @return bytes added to the buffer + */ +static int wlan_cmd_append_tsf_tlv(mlan_private *pmriv, t_u8 **ppbuffer, + BSSDescriptor_t *pbss_desc) +{ + MrvlIEtypes_TsfTimestamp_t tsf_tlv; + t_u64 tsf_val; + + ENTER(); + + /* Null Checks */ + if (ppbuffer == MNULL) { + LEAVE(); + return 0; + } + if (*ppbuffer == MNULL) { + LEAVE(); + return 0; + } + + memset(pmriv->adapter, &tsf_tlv, 0x00, + sizeof(MrvlIEtypes_TsfTimestamp_t)); + + tsf_tlv.header.type = wlan_cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); + tsf_tlv.header.len = wlan_cpu_to_le16(2 * sizeof(tsf_val)); + + memcpy_ext(pmriv->adapter, *ppbuffer, &tsf_tlv, sizeof(tsf_tlv.header), + 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_ext(pmriv->adapter, *ppbuffer, &tsf_val, sizeof(tsf_val), + sizeof(tsf_val)); + *ppbuffer += sizeof(tsf_val); + + memcpy_ext(pmriv->adapter, &tsf_val, pbss_desc->time_stamp, + sizeof(pbss_desc->time_stamp), sizeof(tsf_val)); + + PRINTM(MINFO, "ASSOC: TSF offset calc: %016llx - %016llx\n", tsf_val, + pbss_desc->network_tsf); + + memcpy_ext(pmriv->adapter, *ppbuffer, &tsf_val, sizeof(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(mlan_private *pmpriv, t_u8 *rate1, + t_u32 rate1_size, t_u8 *rate2, + 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_ext(pmpriv->adapter, tmp, rate1, rate1_size, 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(mlan_private *pmpriv, + BSSDescriptor_t *pbss_desc, + t_u8 *pout_rates, + t_u32 *pout_rates_size) +{ + t_u8 card_rates[WLAN_SUPPORTED_RATES]; + t_u32 card_rates_size = 0; + ENTER(); + /* Copy AP supported rates */ + memcpy_ext(pmpriv->adapter, pout_rates, pbss_desc->supported_rates, + WLAN_SUPPORTED_RATES, WLAN_SUPPORTED_RATES); + + if ((pmpriv->adapter->region_code == COUNTRY_CODE_JP_40 || + pmpriv->adapter->region_code == COUNTRY_CODE_JP_FF) && + (pbss_desc->phy_param_set.ds_param_set.current_chan == 14)) { + /* Special Case: For Japan, 11G rates on CH14 are not allowed*/ + card_rates_size = wlan_get_supported_rates( + pmpriv, pmpriv->bss_mode, BAND_B, card_rates); + } else { + /* Get the STA supported rates */ + card_rates_size = + wlan_get_supported_rates(pmpriv, pmpriv->bss_mode, + pmpriv->config_bands, + card_rates); + } + /* Get the common rates between AP and STA supported rates */ + if (wlan_get_common_rates(pmpriv, pout_rates, WLAN_SUPPORTED_RATES, + card_rates, card_rates_size)) { + *pout_rates_size = 0; + PRINTM(MERROR, "wlan_get_common_rates failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + *pout_rates_size = + MIN(wlan_strlen((char *)pout_rates), WLAN_SUPPORTED_RATES); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Update the scan entry TSF timestamps to reflect a new association + * + * @param pmpriv A pointer to mlan_private structure + * @param pnew_bss_desc A pointer to the newly associated AP's scan table entry + * + * @return N/A + */ +static t_void wlan_update_tsf_timestamps(mlan_private *pmpriv, + 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_ext(pmpriv->adapter, &new_tsf_base, pnew_bss_desc->time_stamp, + sizeof(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_ext(priv->adapter, *ppbuffer, &ie_header, + sizeof(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_ext(priv->adapter, *ppbuffer, priv->wapi_ie, + priv->wapi_ie_len, 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; +} + +/** + * @brief Append a osen 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 osen 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_osen_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 osen ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->osen_ie_len) { + PRINTM(MCMND, "append osen ie %d to %p\n", priv->osen_ie_len, + *ppbuffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = wlan_cpu_to_le16(TLV_TYPE_VENDOR_SPECIFIC_IE); + ie_header.len = wlan_cpu_to_le16(priv->osen_ie[1]); + memcpy_ext(priv->adapter, *ppbuffer, &ie_header, + sizeof(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 osen IE buffer to the output buffer, advance pointer + */ + memcpy_ext(priv->adapter, *ppbuffer, &priv->osen_ie[2], + priv->osen_ie[1], priv->osen_ie[1]); + + /* Increment the return size and the return buffer pointer param + */ + *ppbuffer += priv->osen_ie[1]; + retlen += priv->osen_ie[1]; + } + /* return the length appended to the buffer */ + LEAVE(); + return retlen; +} + +/** + * @brief This function get the rsn_cap from RSN ie buffer. + * + * @param pmpriv A pointer to mlan_private structure + * + * @param data A pointer to rsn_ie data after IE header + * @param return rsn_cap + */ +t_u16 wlan_get_rsn_cap(t_u8 *data) +{ + t_u16 rsn_cap = 0; + t_u16 *ptr; + t_u16 pairwise_cipher_count = 0; + t_u16 akm_suite_count = 0; + /* rsn_cap = data + 2 bytes version + 4 bytes + * group_cipher_suite + 2 bytes pairwise_cipher_count + + * pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN + 2 bytes + * akm_suite_count + akm_suite_count * AKM_SUITE_LEN + */ + ptr = (t_u16 *)(data + sizeof(t_u16) + 4 * sizeof(t_u8)); + pairwise_cipher_count = wlan_le16_to_cpu(*ptr); + ptr = (t_u16 *)(data + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN); + akm_suite_count = wlan_le16_to_cpu(*ptr); + ptr = (t_u16 *)(data + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16) + akm_suite_count * AKM_SUITE_LEN); + rsn_cap = wlan_le16_to_cpu(*ptr); + PRINTM(MCMND, "rsn_cap=0x%x\n", rsn_cap); + return rsn_cap; +} + +/** + * @brief This function check if we should enable 11w + * + * @param pmpriv A pointer to mlan_private structure + * + * @param BSSDescriptor_t A pointer to BSSDescriptor_t data structure + * @param return MTRUE/MFALSE + */ +t_u8 wlan_use_mfp(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc) +{ + t_u16 ap_rsn_cap = 0; + t_u16 sta_rsn_cap = 0; + t_u8 ap_mfpc, ap_mfpr; + t_u8 sta_mfpc, sta_mfpr; + + if (pmpriv->wpa_ie[0] != RSN_IE) + return 0; + sta_rsn_cap = wlan_get_rsn_cap(pmpriv->wpa_ie + 2); + if (!pbss_desc->prsn_ie) + return 0; + ap_rsn_cap = wlan_get_rsn_cap(pbss_desc->prsn_ie->data); + ap_mfpc = ((ap_rsn_cap & (0x1 << MFPC_BIT)) == (0x1 << MFPC_BIT)); + ap_mfpr = ((ap_rsn_cap & (0x1 << MFPR_BIT)) == (0x1 << MFPR_BIT)); + sta_mfpc = ((sta_rsn_cap & (0x1 << MFPC_BIT)) == (0x1 << MFPC_BIT)); + sta_mfpr = ((sta_rsn_cap & (0x1 << MFPR_BIT)) == (0x1 << MFPR_BIT)); + if (!ap_mfpc && !ap_mfpr) + return MFALSE; + if (!sta_mfpc && !sta_mfpr) + return MFALSE; + return MTRUE; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function updates RSN IE in the association request. + * + * @param pmpriv A pointer to mlan_private structure + * + * @param ptlv_rsn_ie A pointer to rsn_ie TLV + */ +int wlan_update_rsn_ie(mlan_private *pmpriv, + MrvlIEtypes_RsnParamSet_t *ptlv_rsn_ie) +{ + t_u16 *prsn_cap; + t_u16 *ptr; + t_u16 *akm_suite_count_ptr; + t_u16 pmf_mask = 0x00; + t_u8 *temp; + t_u16 pairwise_cipher_count = 0; + t_u16 akm_suite_count = 0; + t_u16 temp_akm_suite_count = 0; + int found = 0; + t_u8 sha_256_oui[4] = {0x00, 0x0f, 0xac, 0x06}; + t_u8 sae_oui[4] = {0x00, 0x0f, 0xac, 0x08}; + mlan_adapter *pmadapter = pmpriv->adapter; + + int ap_mfpc = 0, ap_mfpr = 0, ret = MLAN_STATUS_SUCCESS; + + pmf_mask = (((pmpriv->pmfcfg.mfpc << MFPC_BIT) | + (pmpriv->pmfcfg.mfpr << MFPR_BIT)) | + (~PMF_MASK)); + /* prsn_cap = prsn_ie->rsn_ie + 2 bytes version + 4 bytes + * group_cipher_suite + 2 bytes pairwise_cipher_count + + * pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN + 2 bytes + * akm_suite_count + akm_suite_count * AKM_SUITE_LEN + */ + ptr = (t_u16 *)(ptlv_rsn_ie->rsn_ie + sizeof(t_u16) + 4 * sizeof(t_u8)); + pairwise_cipher_count = wlan_le16_to_cpu(*ptr); + ptr = (t_u16 *)(ptlv_rsn_ie->rsn_ie + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN); + temp_akm_suite_count = wlan_le16_to_cpu(*ptr); + akm_suite_count = wlan_le16_to_cpu(*ptr); + /* Save pointer to akm_suite_count in RSN IE to update it later */ + akm_suite_count_ptr = ptr; + temp = ptlv_rsn_ie->rsn_ie + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16); + /* ptr now points to the 1st AKM suite */ + if (temp_akm_suite_count > 1) { + while (temp_akm_suite_count) { + if (pmpriv->sec_info.authentication_mode == + MLAN_AUTH_MODE_SAE) { + if (!memcmp(pmadapter, temp, sae_oui, + AKM_SUITE_LEN)) { + found = 1; + break; + } + } else if (!memcmp(pmadapter, temp, sha_256_oui, + AKM_SUITE_LEN)) { + found = 1; + break; + } + temp += AKM_SUITE_LEN; + temp_akm_suite_count--; + } + if (found) { + /* Copy SHA256 as AKM suite */ + memcpy_ext(pmadapter, + ptlv_rsn_ie->rsn_ie + + (sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * + PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16)), + temp, AKM_SUITE_LEN, AKM_SUITE_LEN); + /* Shift remaining bytes of RSN IE after this */ + memmove(pmadapter, + ptlv_rsn_ie->rsn_ie + + (sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * + PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16) + AKM_SUITE_LEN), + ptlv_rsn_ie->rsn_ie + + (sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * + PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16) + + akm_suite_count * AKM_SUITE_LEN), + ptlv_rsn_ie->header.len - + (sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * + PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16) + + akm_suite_count * AKM_SUITE_LEN)); + ptlv_rsn_ie->header.len = + ptlv_rsn_ie->header.len - + (akm_suite_count - 1) * AKM_SUITE_LEN; + /* Update akm suite count */ + akm_suite_count = 1; + *akm_suite_count_ptr = akm_suite_count; + } + } + ptr = (t_u16 *)(ptlv_rsn_ie->rsn_ie + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + + pairwise_cipher_count * PAIRWISE_CIPHER_SUITE_LEN + + sizeof(t_u16) + akm_suite_count * AKM_SUITE_LEN); + prsn_cap = ptr; + + ap_mfpc = ((*prsn_cap & (0x1 << MFPC_BIT)) == (0x1 << MFPC_BIT)); + ap_mfpr = ((*prsn_cap & (0x1 << MFPR_BIT)) == (0x1 << MFPR_BIT)); + + if ((!ap_mfpc && !ap_mfpr && pmpriv->pmfcfg.mfpr) || + ((!ap_mfpc) && ap_mfpr) || + (ap_mfpc && ap_mfpr && (!pmpriv->pmfcfg.mfpc))) { + PRINTM(MERROR, + "Mismatch in PMF config of STA and AP, can't associate to AP\n"); + return MLAN_STATUS_FAILURE; + } + if ((pmpriv->pmfcfg.mfpr && pmpriv->pmfcfg.mfpc) || + pmpriv->pmfcfg.mfpc) { + *prsn_cap |= PMF_MASK; + *prsn_cap &= pmf_mask; + } + + return ret; +} + +/** + * @brief This function is to find FT AKM in RSN. + * + * @param pmpriv A pointer to mlan_private structure + * + * @param rsn_ie A pointer to rsn_ie + * + */ +t_u8 wlan_ft_akm_is_used(mlan_private *pmpriv, t_u8 *rsn_ie) +{ + t_u8 *temp; + t_u16 count; + t_u16 pairwise_cipher_count = 0; + t_u16 akm_suite_count = 0; + t_u8 found = 0; + t_u8 rsn_ft_1x_oui[4] = {0x00, 0x0f, 0xac, 0x03}; + t_u8 rsn_ft_psk_oui[4] = {0x00, 0x0f, 0xac, 0x04}; + t_u8 rsn_ft_sae_oui[4] = {0x00, 0x0f, 0xac, 0x09}; + mlan_adapter *pmadapter = pmpriv->adapter; + + ENTER(); + + if (!rsn_ie) + goto done; + + if (rsn_ie[0] != RSN_IE) + goto done; + + /* 2 bytes header + 2 bytes version + 4 bytes group_cipher_suite + + * 2 bytes pairwise_cipher_count + pairwise_cipher_count * + * PAIRWISE_CIPHER_SUITE_LEN (4) + 2 bytes akm_suite_count + + * akm_suite_count * AKM_SUITE_LEN (4) + */ + count = *(t_u16 *)(rsn_ie + 2 + 2 + 4 * sizeof(t_u8)); + pairwise_cipher_count = wlan_le16_to_cpu(count); + count = *(t_u16 *)(rsn_ie + 2 + 2 + 4 * sizeof(t_u8) + sizeof(t_u16) + + pairwise_cipher_count * 4); + akm_suite_count = wlan_le16_to_cpu(count); + temp = (t_u8 *)(rsn_ie + 2 + sizeof(t_u16) + 4 * sizeof(t_u8) + + sizeof(t_u16) + pairwise_cipher_count * 4 + + sizeof(t_u16)); + + while (akm_suite_count) { + if (!memcmp(pmadapter, temp, rsn_ft_1x_oui, + sizeof(rsn_ft_1x_oui)) || + !memcmp(pmadapter, temp, rsn_ft_psk_oui, + sizeof(rsn_ft_psk_oui)) || + !memcmp(pmadapter, temp, rsn_ft_sae_oui, + sizeof(rsn_ft_sae_oui))) { + found = 1; + break; + } + temp += 4; + akm_suite_count--; + } + +done: + LEAVE(); + return found; +} + +/** + * @brief This function is to find specific IE. + * + * @param ie A pointer to ie buffer + * @param ie_len Length of ie buffer + * @param ie_type Type of ie that wants to be found in ie buffer + * + * @return MFALSE if not found; MTURE if found + */ +t_u8 wlan_find_ie(t_u8 *ie, t_u8 ie_len, t_u8 ie_type) +{ + IEEEtypes_Header_t *pheader = MNULL; + t_u8 *pos = MNULL; + t_s8 ret_len; + t_u8 ret = MFALSE; + + ENTER(); + + pos = (t_u8 *)ie; + ret_len = ie_len; + while (ret_len >= 2) { + pheader = (IEEEtypes_Header_t *)pos; + if (pheader->len + sizeof(IEEEtypes_Header_t) > ret_len) { + PRINTM(MMSG, "invalid IE length = %d left len %d\n", + pheader->len, ret_len); + break; + } + if (pheader->element_id == ie_type) { + ret = MTRUE; + break; + } + ret_len -= pheader->len + sizeof(IEEEtypes_Header_t); + pos += pheader->len + sizeof(IEEEtypes_Header_t); + } + + LEAVE(); + return ret; +} + +/** + * @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(mlan_private *pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_ASSOCIATE *passo = &cmd->params.associate; + BSSDescriptor_t *pbss_desc; + MrvlIEtypes_SsIdParamSet_t *pssid_tlv; + MrvlIEtypes_PhyParamSet_t *pphy_tlv; + MrvlIEtypes_SsParamSet_t *pss_tlv; + MrvlIEtypes_RatesParamSet_t *prates_tlv; + MrvlIEtypes_AuthType_t *pauth_tlv; + MrvlIEtypes_RsnParamSet_t *prsn_ie_tlv = MNULL; + MrvlIEtypes_SecurityCfg_t *psecurity_cfg_ie = MNULL; + MrvlIEtypes_ChanListParamSet_t *pchan_tlv; + WLAN_802_11_RATES rates; + t_u32 rates_size; + t_u16 tmp_cap; + t_u8 *pos; +#ifdef DRV_EMBEDDED_SUPPLICANT + void *rsn_wpa_ie_tmp = MNULL; +#endif + t_u8 ft_akm = 0; + t_u8 oper_class; + t_u8 oper_class_flag = MFALSE; + MrvlIEtypes_HostMlme_t *host_mlme_tlv = MNULL; + + 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; + /* clear assoc_rsp_size */ + pmpriv->assoc_rsp_size = 0; + + memcpy_ext(pmadapter, passo->peer_sta_addr, pbss_desc->mac_address, + sizeof(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_ext(pmadapter, pssid_tlv->ssid, pbss_desc->ssid.ssid, + pssid_tlv->header.len, 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_ext(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), + 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_ext(pmadapter, &pmpriv->curr_bss_params.data_rates, rates, + rates_size, WLAN_SUPPORTED_RATES); + + /* 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_ext(pmadapter, prates_tlv->rates, rates, rates_size, rates_size); + pos += sizeof(prates_tlv->header) + rates_size; + PRINTM(MINFO, "ASSOC_CMD: Rates size = %d\n", rates_size); + + /* Add the Authentication type to be used for Auth frames if needed */ + if ((pmpriv->sec_info.authentication_mode != MLAN_AUTH_MODE_AUTO)) { + pauth_tlv = (MrvlIEtypes_AuthType_t *)pos; + pauth_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_AUTH_TYPE); + pauth_tlv->header.len = sizeof(pauth_tlv->auth_type); + if ((pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled) || + (pmpriv->sec_info.authentication_mode == + MLAN_AUTH_MODE_NETWORKEAP)) + pauth_tlv->auth_type = wlan_cpu_to_le16( + (t_u16)pmpriv->sec_info.authentication_mode); + else if (pmpriv->sec_info.authentication_mode == + MLAN_AUTH_MODE_FT) + pauth_tlv->auth_type = + wlan_cpu_to_le16(AssocAgentAuth_FastBss_Skip); + else if (pmpriv->sec_info.authentication_mode == + MLAN_AUTH_MODE_SAE) + pauth_tlv->auth_type = + wlan_cpu_to_le16(AssocAgentAuth_Wpa3Sae); + else + pauth_tlv->auth_type = + wlan_cpu_to_le16(MLAN_AUTH_MODE_OPEN); + pos += sizeof(pauth_tlv->header) + pauth_tlv->header.len; + pauth_tlv->header.len = wlan_cpu_to_le16(pauth_tlv->header.len); + } + + if (IS_SUPPORT_MULTI_BANDS(pmadapter) && + (pbss_desc->bss_band & pmpriv->config_bands) && + !(ISSUPP_11NENABLED(pmadapter->fw_cap_info) && + (!pbss_desc->disable_11n) && + (pmpriv->config_bands & BAND_GN || + pmpriv->config_bands & BAND_AN) && + (pbss_desc->pht_cap))) { + /* Append a channel TLV for the channel the attempted AP was + * found on */ + pchan_tlv = (MrvlIEtypes_ChanListParamSet_t *)pos; + pchan_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_tlv->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t)); + + memset(pmadapter, pchan_tlv->chan_scan_param, 0x00, + sizeof(ChanScanParamSet_t)); + pchan_tlv->chan_scan_param[0].chan_number = + (pbss_desc->phy_param_set.ds_param_set.current_chan); + PRINTM(MINFO, "Assoc: TLV Chan = %d\n", + pchan_tlv->chan_scan_param[0].chan_number); + + pchan_tlv->chan_scan_param[0].bandcfg.chanBand = + wlan_band_to_radio_type((t_u8)pbss_desc->bss_band); + + PRINTM(MINFO, "Assoc: TLV Bandcfg = %x\n", + pchan_tlv->chan_scan_param[0].bandcfg); + pos += sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + } + if (!pmpriv->wps.session_enable) { + if ((pmpriv->sec_info.wpa_enabled || + pmpriv->sec_info.wpa2_enabled)) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + /* WPA_IE or RSN_IE */ + prsn_ie_tlv->header.type = (t_u16)pmpriv->wpa_ie[0]; + prsn_ie_tlv->header.type = + prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = + wlan_cpu_to_le16(prsn_ie_tlv->header.type); + prsn_ie_tlv->header.len = (t_u16)pmpriv->wpa_ie[1]; + prsn_ie_tlv->header.len = + prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= + (sizeof(pmpriv->wpa_ie) - 2)) + memcpy_ext(pmadapter, prsn_ie_tlv->rsn_ie, + &pmpriv->wpa_ie[2], + prsn_ie_tlv->header.len, + prsn_ie_tlv->header.len); + else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + HEXDUMP("ASSOC_CMD: RSN IE", (t_u8 *)prsn_ie_tlv, + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len); + pos += sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len; + prsn_ie_tlv->header.len = + wlan_cpu_to_le16(prsn_ie_tlv->header.len); + /** parse rsn ie to find whether ft akm is used*/ + ft_akm = wlan_ft_akm_is_used(pmpriv, pmpriv->wpa_ie); + /* Append PMF Configuration coming from cfg80211 layer + */ + psecurity_cfg_ie = (MrvlIEtypes_SecurityCfg_t *)pos; + psecurity_cfg_ie->header.type = + wlan_cpu_to_le16(TLV_TYPE_SECURITY_CFG); + + pmpriv->curr_bss_params.use_mfp = + wlan_use_mfp(pmpriv, pbss_desc); + PRINTM(MCMND, "use_mfp=%d\n", + pmpriv->curr_bss_params.use_mfp); + + if (!pmpriv->curr_bss_params.use_mfp) + psecurity_cfg_ie->use_mfp = MFALSE; + else + psecurity_cfg_ie->use_mfp = MTRUE; + psecurity_cfg_ie->header.len = sizeof(t_u8); + pos += sizeof(psecurity_cfg_ie->header) + + psecurity_cfg_ie->header.len; + } +#ifdef DRV_EMBEDDED_SUPPLICANT + else if (supplicantIsEnabled(pmpriv->psapriv)) { + supplicantClrEncryptKey(pmpriv->psapriv); + + if (pbss_desc->prsn_ie) + rsn_wpa_ie_tmp = pbss_desc->prsn_ie; + else if (pbss_desc->pwpa_ie) + rsn_wpa_ie_tmp = pbss_desc->pwpa_ie; + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + pos += supplicantFormatRsnWpaTlv( + pmpriv->psapriv, rsn_wpa_ie_tmp, prsn_ie_tlv); + } +#endif + else if (pmpriv->sec_info.ewpa_enabled) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + if (pbss_desc->pwpa_ie) { + prsn_ie_tlv->header.type = + (t_u16)(*(pbss_desc->pwpa_ie)) + .vend_hdr.element_id; + prsn_ie_tlv->header.type = + prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = wlan_cpu_to_le16( + prsn_ie_tlv->header.type); + prsn_ie_tlv->header.len = + (t_u16)(*(pbss_desc->pwpa_ie)) + .vend_hdr.len; + prsn_ie_tlv->header.len = + prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= + (sizeof(pmpriv->wpa_ie))) { + memcpy_ext(pmadapter, + prsn_ie_tlv->rsn_ie, + &((*(pbss_desc->pwpa_ie)) + .vend_hdr.oui[0]), + prsn_ie_tlv->header.len, + 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_ext(pmadapter, + prsn_ie_tlv->rsn_ie, + &((*(pbss_desc->prsn_ie)) + .data[0]), + prsn_ie_tlv->header.len, + prsn_ie_tlv->header.len); + ret = wlan_update_rsn_ie(pmpriv, + prsn_ie_tlv); + if (ret != MLAN_STATUS_SUCCESS) { + goto done; + } + } else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + HEXDUMP("ASSOC_CMD: RSN IE", + (t_u8 *)prsn_ie_tlv, + sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len); + pos += sizeof(prsn_ie_tlv->header) + + prsn_ie_tlv->header.len; + prsn_ie_tlv->header.len = wlan_cpu_to_le16( + prsn_ie_tlv->header.len); + } + } + } + + if (ISSUPP_11NENABLED(pmadapter->fw_cap_info) && + (!pbss_desc->disable_11n) && + wlan_11n_bandconfig_allowed(pmpriv, pbss_desc->bss_band)) + wlan_cmd_append_11n_tlv(pmpriv, pbss_desc, &pos); + else if ((pmpriv->hotspot_cfg & HOTSPOT_ENABLED) && + !(pmpriv->hotspot_cfg & HOTSPOT_BY_SUPPLICANT)) + wlan_add_ext_capa_info_ie(pmpriv, pbss_desc, &pos); + if (pmpriv->adapter->ecsa_enable) { + oper_class_flag = + wlan_find_ie(pmpriv->gen_ie_buf, pmpriv->gen_ie_buf_len, + REGULATORY_CLASS); + if (!oper_class_flag) { + if (MLAN_STATUS_SUCCESS == + wlan_get_curr_oper_class( + pmpriv, + pbss_desc->phy_param_set.ds_param_set + .current_chan, + pbss_desc->curr_bandwidth, &oper_class)) + wlan_add_supported_oper_class_ie(pmpriv, &pos, + oper_class); + } + } + if (ISSUPP_11ACENABLED(pmadapter->fw_cap_info) && + (!pbss_desc->disable_11n) && + wlan_11ac_bandconfig_allowed(pmpriv, pbss_desc->bss_band)) + wlan_cmd_append_11ac_tlv(pmpriv, pbss_desc, &pos); + + if ((IS_FW_SUPPORT_11AX(pmadapter)) && (!pbss_desc->disable_11n) && + wlan_11ax_bandconfig_allowed(pmpriv, pbss_desc->bss_band)) + wlan_cmd_append_11ax_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); + + if (pmpriv->sec_info.osen_enabled && pmpriv->osen_ie_len) + wlan_cmd_append_osen_ie(pmpriv, &pos); + + wlan_cmd_append_generic_ie(pmpriv, &pos); + + if (pbss_desc->pmd_ie) + wlan_cmd_append_pass_through_ie( + pmpriv, (IEEEtypes_Generic_t *)pbss_desc->pmd_ie, &pos); + wlan_cmd_append_tsf_tlv(pmpriv, &pos, pbss_desc); + + if (pmpriv->curr_bss_params.host_mlme) { + host_mlme_tlv = (MrvlIEtypes_HostMlme_t *)pos; + host_mlme_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_HOST_MLME); + host_mlme_tlv->header.len = + wlan_cpu_to_le16(sizeof(host_mlme_tlv->host_mlme)); + host_mlme_tlv->host_mlme = MTRUE; + pos += sizeof(host_mlme_tlv->header) + + host_mlme_tlv->header.len; + } + + 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_ext(pmadapter, &tmp_cap, &pbss_desc->cap_info, + sizeof(passo->cap_info), sizeof(tmp_cap)); + + 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_ext(pmadapter, &passo->cap_info, &tmp_cap, sizeof(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(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *pioctl_req = (mlan_ioctl_req *)pioctl_buf; + IEEEtypes_AssocRsp_t *passoc_rsp; + BSSDescriptor_t *pbss_desc; + t_u8 enable_data = MTRUE; + t_u8 event_buf[100]; + mlan_event *pevent = (mlan_event *)event_buf; + t_u8 cur_mac[MLAN_MAC_ADDR_LENGTH]; + t_u8 media_connected = pmpriv->media_connected; + mlan_adapter *pmadapter = pmpriv->adapter; + assoc_logger_data *assoc_succ; + mlan_ds_bss *bss; + + ENTER(); + + if (pmpriv->curr_bss_params.host_mlme) + passoc_rsp = + (IEEEtypes_AssocRsp_t *)((t_u8 *)(&resp->params) + + sizeof(IEEEtypes_MgmtHdr_t)); + else + + passoc_rsp = (IEEEtypes_AssocRsp_t *)&resp->params; + passoc_rsp->status_code = wlan_le16_to_cpu(passoc_rsp->status_code); + if (pmpriv->media_connected == MTRUE) + memcpy_ext(pmpriv->adapter, cur_mac, + pmpriv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + + HEXDUMP("ASSOC_RESP:", (t_u8 *)&resp->params, (resp->size - S_DS_GEN)); + + pmpriv->assoc_rsp_size = + MIN(resp->size - S_DS_GEN, sizeof(pmpriv->assoc_rsp_buf)); + + memcpy_ext(pmpriv->adapter, pmpriv->assoc_rsp_buf, &resp->params, + pmpriv->assoc_rsp_size, pmpriv->assoc_rsp_size); + + if (pioctl_req != MNULL) { + bss = (mlan_ds_bss *)pioctl_req->pbuf; + bss->param.ssid_bssid.assoc_rsp.assoc_resp_len = + pmpriv->assoc_rsp_size; + memcpy_ext(pmpriv->adapter, + bss->param.ssid_bssid.assoc_rsp.assoc_resp_buf, + pmpriv->assoc_rsp_buf, pmpriv->assoc_rsp_size, + ASSOC_RSP_BUF_SIZE); + } + if (passoc_rsp->status_code) { + if (pmpriv->media_connected == MTRUE) { + if (pmpriv->port_ctrl_mode == MTRUE) + pmpriv->port_open = pmpriv->prior_port_status; + if (!memcmp(pmpriv->adapter, cur_mac, + pmpriv->pattempted_bss_desc->mac_address, + MLAN_MAC_ADDR_LENGTH)) + wlan_reset_connect_state(pmpriv, MTRUE); + else + wlan_recv_event( + pmpriv, + MLAN_EVENT_ID_DRV_ASSOC_FAILURE_REPORT, + MNULL); + } else + wlan_reset_connect_state(pmpriv, MTRUE); + pmpriv->adapter->dbg.num_cmd_assoc_failure++; + pmpriv->adapter->dbg.num_cons_assoc_failure++; + PRINTM(MERROR, + "ASSOC_RESP: Association Failed, " + "status code = %d, error = 0x%x, a_id = 0x%x\n", + passoc_rsp->status_code, + wlan_le16_to_cpu(*(t_u16 *)&passoc_rsp->capability), + wlan_le16_to_cpu(passoc_rsp->a_id)); + memset(pmadapter, event_buf, 0, sizeof(event_buf)); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_ASSOC_FAILURE_LOGGER; + pevent->event_len = sizeof(passoc_rsp->status_code); + memcpy_ext(pmpriv->adapter, (t_u8 *)pevent->event_buf, + &passoc_rsp->status_code, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_ASSOC_FAILURE_LOGGER, + pevent); + + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + pmpriv->media_connected = MTRUE; + + pmpriv->adapter->pps_uapsd_mode = MFALSE; + pmpriv->adapter->tx_lock_flag = MFALSE; + pmpriv->adapter->delay_null_pkt = MFALSE; + + /* Set the attempted BSSID Index to current */ + pbss_desc = pmpriv->pattempted_bss_desc; + + PRINTM(MCMND, "ASSOC_RESP: %-32s (a_id = 0x%x)\n", pbss_desc->ssid.ssid, + wlan_le16_to_cpu(passoc_rsp->a_id)); + /* Restore default extended capabilities */ + memcpy_ext(pmpriv->adapter, &pmpriv->ext_cap, &pmpriv->def_ext_cap, + sizeof(pmpriv->ext_cap), sizeof(pmpriv->ext_cap)); + /* Make a copy of current BSSID descriptor */ + memcpy_ext(pmpriv->adapter, &pmpriv->curr_bss_params.bss_descriptor, + pbss_desc, sizeof(BSSDescriptor_t), sizeof(BSSDescriptor_t)); + + /* Update curr_bss_params */ + pmpriv->curr_bss_params.bss_descriptor.channel = + pbss_desc->phy_param_set.ds_param_set.current_chan; + + pmpriv->curr_bss_params.band = (t_u8)pbss_desc->bss_band; + + /* Store current channel for further reference. + * This would save one extra call to get current + * channel when disconnect/bw_ch event is raised. + */ + pmpriv->adapter->dfsr_channel = + pmpriv->curr_bss_params.bss_descriptor.channel; + + /* + * Adjust the timestamps in the scan table to be relative to the newly + * associated AP's TSF + */ + wlan_update_tsf_timestamps(pmpriv, pbss_desc); + + if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) + pmpriv->curr_bss_params.wmm_enabled = MTRUE; + else + pmpriv->curr_bss_params.wmm_enabled = MFALSE; + + if ((pmpriv->wmm_required || + (pbss_desc->pht_cap && + (pbss_desc->pht_cap->ieee_hdr.element_id == HT_CAPABILITY))) && + pmpriv->curr_bss_params.wmm_enabled) + pmpriv->wmm_enabled = MTRUE; + else + pmpriv->wmm_enabled = MFALSE; + + pmpriv->curr_bss_params.wmm_uapsd_enabled = MFALSE; + + if (pmpriv->wmm_enabled == MTRUE) + pmpriv->curr_bss_params.wmm_uapsd_enabled = + pbss_desc->wmm_ie.qos_info.qos_uapsd; + + PRINTM(MINFO, "ASSOC_RESP: curr_pkt_filter is 0x%x\n", + pmpriv->curr_pkt_filter); + if (pmpriv->sec_info.wpa_enabled || pmpriv->sec_info.wpa2_enabled) + pmpriv->wpa_is_gtk_set = MFALSE; + if (pmpriv->wmm_enabled) + /* Don't re-enable carrier until we get the WMM_GET_STATUS event + */ + enable_data = MFALSE; + else + /* Since WMM is not enabled, setup the queues with the defaults + */ + wlan_wmm_setup_queues(pmpriv); + + if (enable_data) + PRINTM(MINFO, "Post association, re-enabling data flow\n"); + + /* Reset SNR/NF/RSSI values */ + pmpriv->data_rssi_last = 0; + pmpriv->data_nf_last = 0; + pmpriv->data_rssi_avg = 0; + pmpriv->data_nf_avg = 0; + pmpriv->bcn_rssi_last = 0; + pmpriv->bcn_nf_last = 0; + pmpriv->bcn_rssi_avg = 0; + pmpriv->bcn_nf_avg = 0; + pmpriv->rxpd_rate = 0; + pmpriv->rxpd_rate_info = 0; + /* Reset mib statistics*/ + pmpriv->amsdu_rx_cnt = 0; + pmpriv->amsdu_tx_cnt = 0; + pmpriv->msdu_in_rx_amsdu_cnt = 0; + pmpriv->msdu_in_tx_amsdu_cnt = 0; + if (pbss_desc->pvht_cap && pbss_desc->pht_cap) { + if (GET_VHTCAP_MAXMPDULEN( + pbss_desc->pvht_cap->vht_cap.vht_cap_info) == 2) + pmpriv->max_amsdu = MLAN_TX_DATA_BUF_SIZE_12K; + else if (GET_VHTCAP_MAXMPDULEN( + pbss_desc->pvht_cap->vht_cap.vht_cap_info) == + 1) + pmpriv->max_amsdu = MLAN_TX_DATA_BUF_SIZE_8K; + else + pmpriv->max_amsdu = MLAN_TX_DATA_BUF_SIZE_4K; + } else 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++; + pmpriv->adapter->dbg.num_cons_assoc_failure = 0; + 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_ext(pmpriv->adapter, (t_u8 *)pevent->event_buf, + (t_u8 *)pmpriv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH, pevent->event_len); + + /* Add the ra_list here for infra mode as there will be only 1 ra always + */ + if (media_connected) { + /** replace ralist's mac address with new mac address */ + if (0 == + wlan_ralist_update( + pmpriv, cur_mac, + pmpriv->curr_bss_params.bss_descriptor.mac_address)) + wlan_ralist_add(pmpriv, + pmpriv->curr_bss_params.bss_descriptor + .mac_address); + wlan_11n_cleanup_reorder_tbl(pmpriv); + wlan_11n_deleteall_txbastream_tbl(pmpriv); + + } else + wlan_ralist_add( + pmpriv, + pmpriv->curr_bss_params.bss_descriptor.mac_address); + + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_CONNECTED, pevent); + + /* Send OBSS scan param to the application if available */ + wlan_2040_coex_event(pmpriv); + wlan_coex_ampdu_rxwinsize(pmpriv->adapter); + + if (!pmpriv->sec_info.wpa_enabled && !pmpriv->sec_info.wpa2_enabled && + !pmpriv->sec_info.ewpa_enabled && !pmpriv->sec_info.wapi_enabled && + !pmpriv->wps.session_enable && !pmpriv->sec_info.osen_enabled +#ifdef DRV_EMBEDDED_SUPPLICANT + && !supplicantIsEnabled(pmpriv->psapriv) +#endif + ) { + /* We are in Open/WEP mode, open port immediately */ + if (pmpriv->port_ctrl_mode == MTRUE) { + pmpriv->port_open = MTRUE; + PRINTM(MINFO, "ASSOC_RESP: port_status = OPEN\n"); + } + } + if (pmpriv->sec_info.wpa_enabled || pmpriv->sec_info.wpa2_enabled || + pmpriv->sec_info.ewpa_enabled || pmpriv->sec_info.wapi_enabled || + pmpriv->wps.session_enable || pmpriv->sec_info.osen_enabled +#ifdef DRV_EMBEDDED_SUPPLICANT + || (supplicantIsEnabled(pmpriv->psapriv)) +#endif + ) + pmpriv->adapter->scan_block = MTRUE; + +#ifdef DRV_EMBEDDED_SUPPLICANT + supplicantInitSession( + pmpriv->psapriv, + (t_u8 *)&pmpriv->curr_bss_params.bss_descriptor.ssid.ssid, + pmpriv->curr_bss_params.bss_descriptor.ssid.ssid_len, + (t_u8 *)&pmpriv->curr_bss_params.bss_descriptor.mac_address, + (t_u8 *)&pmpriv->curr_addr); +#endif + + pevent = (mlan_event *)event_buf; + memset(pmadapter, event_buf, 0, sizeof(event_buf)); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_ASSOC_SUCC_LOGGER; + pevent->event_len = sizeof(assoc_logger_data); + assoc_succ = (assoc_logger_data *)pevent->event_buf; + memcpy_ext(pmpriv->adapter, (t_u8 *)assoc_succ->bssid, + pbss_desc->mac_address, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + memcpy_ext(pmpriv->adapter, (t_u8 *)assoc_succ->oui, + pbss_desc->mac_address, MLAN_MAC_ADDR_LENGTH / 2, + MLAN_MAC_ADDR_LENGTH / 2); + memcpy_ext(pmpriv->adapter, (t_u8 *)assoc_succ->ssid, + pbss_desc->ssid.ssid, pbss_desc->ssid.ssid_len, + MLAN_MAX_SSID_LENGTH); + assoc_succ->rssi = pbss_desc->rssi; + assoc_succ->channel = pbss_desc->channel; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_ASSOC_SUCC_LOGGER, pevent); + +done: + /* Need to indicate IOCTL complete */ + if (pioctl_req != MNULL) { + if (ret != MLAN_STATUS_SUCCESS) { + if (passoc_rsp->status_code) + pioctl_req->status_code = + (wlan_le16_to_cpu(*(t_u16 *)&passoc_rsp + ->capability) + << 16) + + passoc_rsp->status_code; + else + pioctl_req->status_code = + MLAN_ERROR_CMD_ASSOC_FAIL; + } else { + pioctl_req->status_code = MLAN_ERROR_NO_ERROR; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of ad_hoc_start. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer cast of mlan_802_11_ssid structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_802_11_ad_hoc_start(mlan_private *pmpriv, + HostCmd_DS_COMMAND *cmd, + 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; + /* 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_ext(pmadapter, padhoc_start->ssid, + ((mlan_802_11_ssid *)pdata_buf)->ssid, + ((mlan_802_11_ssid *)pdata_buf)->ssid_len, + MLAN_MAX_SSID_LENGTH); + + PRINTM(MINFO, "ADHOC_S_CMD: SSID = %s\n", padhoc_start->ssid); + + memset(pmadapter, pbss_desc->ssid.ssid, 0, MLAN_MAX_SSID_LENGTH); + memcpy_ext(pmadapter, pbss_desc->ssid.ssid, + ((mlan_802_11_ssid *)pdata_buf)->ssid, + ((mlan_802_11_ssid *)pdata_buf)->ssid_len, + MLAN_MAX_SSID_LENGTH); + + 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_ext(pmadapter, &pbss_desc->phy_param_set, + &padhoc_start->phy_param_set, + sizeof(IEEEtypes_PhyParamSet_t), + 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_ext(pmadapter, &pbss_desc->ss_param_set, + &padhoc_start->ss_param_set, sizeof(IEEEtypes_SsParamSet_t), + 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->sec_info.wpa_enabled || pmpriv->sec_info.ewpa_enabled) { +/** Ad-Hoc capability privacy on */ +#define AD_HOC_CAP_PRIVACY_ON 1 + PRINTM(MINFO, "ADHOC_S_CMD: wep_status set, Privacy to WEP\n"); + pbss_desc->privacy = Wlan802_11PrivFilter8021xWEP; + padhoc_start->cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } else { + PRINTM(MWARN, "ADHOC_S_CMD: wep_status NOT set, Setting " + "Privacy to ACCEPT ALL\n"); + pbss_desc->privacy = Wlan802_11PrivFilterAcceptAll; + } + + memset(pmadapter, padhoc_start->DataRate, 0, + sizeof(padhoc_start->DataRate)); + + if ((pmpriv->adapter->region_code == COUNTRY_CODE_JP_40 || + pmpriv->adapter->region_code == COUNTRY_CODE_JP_FF) && + (pbss_desc->phy_param_set.ds_param_set.current_chan == 14)) { + wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, BAND_B, + padhoc_start->DataRate); + } else { + wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, + pmadapter->adhoc_start_band, + padhoc_start->DataRate); + } + + if ((pmadapter->adhoc_start_band & BAND_G) && + (pmpriv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmpriv->curr_pkt_filter); + + if (ret) { + PRINTM(MERROR, + "ADHOC_S_CMD: G Protection config failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + /* Find the last non zero */ + for (i = 0; + i < sizeof(padhoc_start->DataRate) && padhoc_start->DataRate[i]; + i++) + /* XXX Do not delete no-operation line */ + ; + + pmpriv->curr_bss_params.num_of_rates = i; + + /* Copy the ad-hoc creating rates into Current BSS rate structure */ + memcpy_ext(pmadapter, &pmpriv->curr_bss_params.data_rates, + &padhoc_start->DataRate, + pmpriv->curr_bss_params.num_of_rates, WLAN_SUPPORTED_RATES); + + PRINTM(MINFO, "ADHOC_S_CMD: Rates=%02x %02x %02x %02x\n", + padhoc_start->DataRate[0], padhoc_start->DataRate[1], + padhoc_start->DataRate[2], padhoc_start->DataRate[3]); + + PRINTM(MINFO, "ADHOC_S_CMD: AD HOC Start command is ready\n"); + + if (IS_SUPPORT_MULTI_BANDS(pmadapter)) { + /* Append a channel TLV */ + pchan_tlv = (MrvlIEtypes_ChanListParamSet_t *)pos; + pchan_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_tlv->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t)); + + memset(pmadapter, pchan_tlv->chan_scan_param, 0x00, + sizeof(ChanScanParamSet_t)); + pchan_tlv->chan_scan_param[0].chan_number = + (t_u8)pmpriv->curr_bss_params.bss_descriptor.channel; + + PRINTM(MINFO, "ADHOC_S_CMD: TLV Chan = %d\n", + pchan_tlv->chan_scan_param[0].chan_number); + + pchan_tlv->chan_scan_param[0].bandcfg.chanBand = + wlan_band_to_radio_type(pmpriv->curr_bss_params.band); + PRINTM(MINFO, "ADHOC_S_CMD: TLV Bandcfg = %x\n", + pchan_tlv->chan_scan_param[0].bandcfg); + pos += sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + cmd_append_size += + sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + } + + if (wlan_11d_create_dnld_countryinfo(pmpriv, + pmpriv->curr_bss_params.band)) { + PRINTM(MERROR, "ADHOC_S_CMD: dnld_countryinfo_11d failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* + * Call 11h start API to add any 11h flags/elements as TLV parameters + */ + append_size_11h = + wlan_11h_process_start(pmpriv, &pos, &padhoc_start->cap, + pmpriv->adhoc_channel, + &pbss_desc->wlan_11h_bss_info); + if (append_size_11h >= 0) + cmd_append_size += append_size_11h; + else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (pmpriv->sec_info.ewpa_enabled) { + memcpy_ext(pmadapter, pmpriv->wpa_ie, wpa_ie, sizeof(wpa_ie), + sizeof(pmpriv->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_ext(pmadapter, prsn_ie_tlv->rsn_ie, + &pmpriv->wpa_ie[2], prsn_ie_tlv->header.len, + 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); + } + + 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_ext(pmadapter, &tmp_cap, &padhoc_start->cap, sizeof(t_u16), + sizeof(tmp_cap)); + + 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_ext(pmadapter, &padhoc_start->cap, &tmp_cap, sizeof(t_u16), + sizeof(padhoc_start->cap)); + + 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(mlan_private *pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_AD_HOC_JOIN *padhoc_join = &cmd->params.adhoc_join; + BSSDescriptor_t *pbss_desc = (BSSDescriptor_t *)pdata_buf; + MrvlIEtypes_ChanListParamSet_t *pchan_tlv; + MrvlIEtypes_RsnParamSet_t *prsn_ie_tlv; + t_u32 cmd_append_size = 0; + t_u16 tmp_cap; + t_u32 i, rates_size = 0; + t_u32 curr_pkt_filter; + t_u8 *pos = (t_u8 *)padhoc_join + sizeof(HostCmd_DS_802_11_AD_HOC_JOIN); + + ENTER(); + +/** Use G protection */ +#define USE_G_PROTECTION 0x02 + if (pbss_desc->erp_flags & USE_G_PROTECTION) { + curr_pkt_filter = pmpriv->curr_pkt_filter | + HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, MNULL, + &curr_pkt_filter); + if (ret) { + PRINTM(MERROR, + "ADHOC_J_CMD: G Protection config failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + pmpriv->pattempted_bss_desc = pbss_desc; + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN); + + padhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS; + + padhoc_join->bss_descriptor.beacon_period = + wlan_cpu_to_le16(pbss_desc->beacon_period); + + memcpy_ext(pmadapter, &padhoc_join->bss_descriptor.bssid, + &pbss_desc->mac_address, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + + memcpy_ext(pmadapter, &padhoc_join->bss_descriptor.ssid, + &pbss_desc->ssid.ssid, pbss_desc->ssid.ssid_len, + MLAN_MAX_SSID_LENGTH); + + memcpy_ext(pmadapter, &padhoc_join->bss_descriptor.phy_param_set, + &pbss_desc->phy_param_set, sizeof(IEEEtypes_PhyParamSet_t), + sizeof(IEEEtypes_PhyParamSet_t)); + + padhoc_join->bss_descriptor.phy_param_set.fh_param_set.dwell_time = + wlan_cpu_to_le16(padhoc_join->bss_descriptor.phy_param_set + .fh_param_set.dwell_time); + + memcpy_ext(pmadapter, &padhoc_join->bss_descriptor.ss_param_set, + &pbss_desc->ss_param_set, sizeof(IEEEtypes_SsParamSet_t), + sizeof(IEEEtypes_SsParamSet_t)); + padhoc_join->bss_descriptor.ss_param_set.ibss_param_set.atim_window = 0; + padhoc_join->bss_descriptor.ss_param_set.ibss_param_set.atim_window = + wlan_cpu_to_le16(padhoc_join->bss_descriptor.ss_param_set + .ibss_param_set.atim_window); + + memcpy_ext(pmadapter, &tmp_cap, &pbss_desc->cap_info, + sizeof(IEEEtypes_CapInfo_t), 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_ext(pmadapter, &padhoc_join->bss_descriptor.cap, &tmp_cap, + sizeof(IEEEtypes_CapInfo_t), sizeof(IEEEtypes_CapInfo_t)); + + /* Information on BSSID descriptor passed to FW */ + PRINTM(MINFO, "ADHOC_J_CMD: BSSID = " MACSTR ", SSID = %s\n", + MAC2STR(padhoc_join->bss_descriptor.bssid), + padhoc_join->bss_descriptor.ssid); + + for (i = 0; i < WLAN_SUPPORTED_RATES && pbss_desc->supported_rates[i]; + i++) + /* XXX Do not delete no-operation line */ + ; + rates_size = i; + + /* Copy Data Rates from the Rates recorded in scan response */ + memset(pmadapter, padhoc_join->bss_descriptor.data_rates, 0, + sizeof(padhoc_join->bss_descriptor.data_rates)); + memcpy_ext(pmadapter, padhoc_join->bss_descriptor.data_rates, + pbss_desc->supported_rates, rates_size, + WLAN_SUPPORTED_RATES); + + 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_ext(pmadapter, &pmpriv->curr_bss_params.data_rates, + pbss_desc->supported_rates, rates_size, + WLAN_SUPPORTED_RATES); + + /* 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->sec_info.wpa_enabled || pmpriv->sec_info.ewpa_enabled) + padhoc_join->bss_descriptor.cap.privacy = AD_HOC_CAP_PRIVACY_ON; + + if (IS_SUPPORT_MULTI_BANDS(pmadapter)) { + /* Append a channel TLV */ + pchan_tlv = (MrvlIEtypes_ChanListParamSet_t *)pos; + pchan_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_tlv->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t)); + + memset(pmadapter, pchan_tlv->chan_scan_param, 0x00, + sizeof(ChanScanParamSet_t)); + pchan_tlv->chan_scan_param[0].chan_number = + (pbss_desc->phy_param_set.ds_param_set.current_chan); + PRINTM(MINFO, "ADHOC_J_CMD: TLV Chan = %d\n", + pchan_tlv->chan_scan_param[0].chan_number); + + pchan_tlv->chan_scan_param[0].bandcfg.chanBand = + wlan_band_to_radio_type((t_u8)pbss_desc->bss_band); + + PRINTM(MINFO, "ADHOC_J_CMD: TLV Bandcfg = %x\n", + pchan_tlv->chan_scan_param[0].bandcfg); + pos += sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + cmd_append_size += + sizeof(pchan_tlv->header) + sizeof(ChanScanParamSet_t); + } + + if (wlan_11d_create_dnld_countryinfo(pmpriv, + (t_u8)pbss_desc->bss_band)) { + PRINTM(MERROR, "Dnld_countryinfo_11d failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (wlan_11d_parse_dnld_countryinfo(pmpriv, + pmpriv->pattempted_bss_desc)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* + * Call 11h join API after capability bits are set so + * adhoc/infra 11h behavior can be properly triggered. + * pos modified if data is appended + */ + cmd_append_size += wlan_11h_process_join( + pmpriv, &pos, &padhoc_join->bss_descriptor.cap, + (t_u8)pbss_desc->bss_band, pbss_desc->channel, + &pbss_desc->wlan_11h_bss_info); + + if (pmpriv->sec_info.wpa_enabled) { + prsn_ie_tlv = (MrvlIEtypes_RsnParamSet_t *)pos; + /* WPA_IE or RSN_IE */ + prsn_ie_tlv->header.type = (t_u16)pmpriv->wpa_ie[0]; + prsn_ie_tlv->header.type = prsn_ie_tlv->header.type & 0x00FF; + prsn_ie_tlv->header.type = + wlan_cpu_to_le16(prsn_ie_tlv->header.type); + prsn_ie_tlv->header.len = (t_u16)pmpriv->wpa_ie[1]; + prsn_ie_tlv->header.len = prsn_ie_tlv->header.len & 0x00FF; + if (prsn_ie_tlv->header.len <= (sizeof(pmpriv->wpa_ie) - 2)) + memcpy_ext(pmadapter, prsn_ie_tlv->rsn_ie, + &pmpriv->wpa_ie[2], prsn_ie_tlv->header.len, + 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_ext(pmadapter, prsn_ie_tlv->rsn_ie, + &((*(pbss_desc->pwpa_ie)) + .vend_hdr.oui[0]), + prsn_ie_tlv->header.len, + 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_ext(pmadapter, prsn_ie_tlv->rsn_ie, + &((*(pbss_desc->prsn_ie)).data[0]), + prsn_ie_tlv->header.len, + 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); + } + } + + 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_ext(pmadapter, &tmp_cap, &padhoc_join->bss_descriptor.cap, + sizeof(IEEEtypes_CapInfo_t), sizeof(IEEEtypes_CapInfo_t)); + tmp_cap = wlan_cpu_to_le16(tmp_cap); + + memcpy_ext(pmadapter, &padhoc_join->bss_descriptor.cap, &tmp_cap, + sizeof(IEEEtypes_CapInfo_t), 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(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *pioctl_req = (mlan_ioctl_req *)pioctl_buf; + HostCmd_DS_802_11_AD_HOC_START_RESULT *padhoc_start_result = + &resp->params.adhoc_start_result; + HostCmd_DS_802_11_AD_HOC_JOIN_RESULT *padhoc_join_result = + &resp->params.adhoc_join_result; + BSSDescriptor_t *pbss_desc; + t_u16 command = resp->command; + t_u8 result = 0; + t_u8 event_buf[100]; + mlan_event *pevent = (mlan_event *)event_buf; + int ie_len = 0; + IEEEtypes_WmmParameter_t *pwmm_param_ie = MNULL; + mlan_adapter *pmadapter = pmpriv->adapter; + const t_u8 wmm_oui[4] = {0x00, 0x50, 0xf2, 0x02}; + + ENTER(); + + pmpriv->wmm_enabled = MFALSE; + if (command == HostCmd_CMD_802_11_AD_HOC_START) { + result = padhoc_start_result->result; + ie_len = resp->size - + (sizeof(HostCmd_DS_802_11_AD_HOC_START_RESULT) + + S_DS_GEN); + pwmm_param_ie = + (IEEEtypes_WmmParameter_t + *)((t_u8 *)resp + + (sizeof(HostCmd_DS_802_11_AD_HOC_START_RESULT) + + S_DS_GEN)); + } else { + result = padhoc_join_result->result; + ie_len = resp->size - + (sizeof(HostCmd_DS_802_11_AD_HOC_JOIN_RESULT) + + S_DS_GEN); + pwmm_param_ie = + (IEEEtypes_WmmParameter_t + *)((t_u8 *)resp + + (sizeof(HostCmd_DS_802_11_AD_HOC_JOIN_RESULT) + + S_DS_GEN)); + } + + pbss_desc = pmpriv->pattempted_bss_desc; + + /* + * Join result code 0 --> SUCCESS + */ + if (result) { + PRINTM(MERROR, "ADHOC_RESP Failed 0x%x\n", result); + if (pmpriv->media_connected == MTRUE) + wlan_reset_connect_state(pmpriv, MTRUE); + if (pmpriv->adhoc_state == ADHOC_STARTING) + pmpriv->adhoc_state = ADHOC_IDLE; + + memset(pmpriv->adapter, &pmpriv->curr_bss_params.bss_descriptor, + 0x00, sizeof(BSSDescriptor_t)); + + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + pmpriv->media_connected = MTRUE; + + if (command == HostCmd_CMD_802_11_AD_HOC_START) { + PRINTM(MINFO, "ADHOC_S_RESP %s\n", pbss_desc->ssid.ssid); + + /* Update the created network descriptor with the new BSSID */ + memcpy_ext(pmpriv->adapter, pbss_desc->mac_address, + padhoc_start_result->bssid, MLAN_MAC_ADDR_LENGTH, + 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_ext(pmpriv->adapter, + &pmpriv->curr_bss_params.bss_descriptor, pbss_desc, + sizeof(BSSDescriptor_t), sizeof(BSSDescriptor_t)); + + pmpriv->adhoc_state = ADHOC_JOINED; + } + + /** process wmm ie */ + if (ie_len >= sizeof(IEEEtypes_VendorHeader_t)) { + if ((pwmm_param_ie->vend_hdr.element_id == + VENDOR_SPECIFIC_221) && + !memcmp(pmadapter, pwmm_param_ie->vend_hdr.oui, wmm_oui, + sizeof(wmm_oui)) && + (pwmm_param_ie->vend_hdr.len + 2 == ie_len)) { + DBG_HEXDUMP(MCMD_D, "WMM Param", (t_u8 *)pwmm_param_ie, + ie_len); + memcpy_ext(pmpriv->adapter, + (t_u8 *)&pmpriv->curr_bss_params + .bss_descriptor.wmm_ie, + pwmm_param_ie, + (pwmm_param_ie->vend_hdr.len + 2), + sizeof(IEEEtypes_WmmParameter_t)); + pmpriv->wmm_enabled = MTRUE; + wlan_wmm_setup_queue_priorities(pmpriv, pwmm_param_ie); + wlan_wmm_setup_ac_downgrade(pmpriv); + } + } + /* Since WMM is not enabled, setup the queues with the defaults */ + if (!pmpriv->wmm_enabled) + wlan_wmm_setup_queues(pmpriv); + + PRINTM(MINFO, "ADHOC_RESP: Channel = %d\n", pmpriv->adhoc_channel); + PRINTM(MINFO, "ADHOC_RESP: BSSID = " MACSTR "\n", + MAC2STR(pmpriv->curr_bss_params.bss_descriptor.mac_address)); + + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_CONNECTED; + pevent->event_len = MLAN_MAC_ADDR_LENGTH; + memcpy_ext(pmpriv->adapter, (t_u8 *)pevent->event_buf, + (t_u8 *)pmpriv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH, 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(mlan_private *pmpriv, t_void *pioctl_buf, + 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_ext(pmpriv->adapter, ¤t_bssid, + &pmpriv->curr_bss_params.bss_descriptor.mac_address, + sizeof(current_bssid), 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(mlan_private *pmpriv, t_void *pioctl_buf, + 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_ext(pmpriv->adapter, &pmpriv->adhoc_last_start_ssid, + padhoc_ssid, sizeof(mlan_802_11_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(mlan_private *pmpriv, t_void *pioctl_buf, + BSSDescriptor_t *pbss_desc) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + ENTER(); + + PRINTM(MINFO, "wlan_adhoc_join: CurBss.ssid =%s\n", + pmpriv->curr_bss_params.bss_descriptor.ssid.ssid); + PRINTM(MINFO, "wlan_adhoc_join: CurBss.ssid_len =%u\n", + pmpriv->curr_bss_params.bss_descriptor.ssid.ssid_len); + PRINTM(MINFO, "wlan_adhoc_join: ssid =%s\n", pbss_desc->ssid.ssid); + PRINTM(MINFO, "wlan_adhoc_join: ssid len =%u\n", + pbss_desc->ssid.ssid_len); + + /* Check if the requested SSID is already joined */ + if (pmpriv->curr_bss_params.bss_descriptor.ssid.ssid_len && + !wlan_ssid_cmp(pmadapter, &pbss_desc->ssid, + &pmpriv->curr_bss_params.bss_descriptor.ssid) && + (pmpriv->curr_bss_params.bss_descriptor.bss_mode == + MLAN_BSS_MODE_IBSS)) { + PRINTM(MINFO, + "ADHOC_J_CMD: New ad-hoc SSID is the same as current, " + "not attempting to re-join\n"); + + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MINFO, "curr_bss_params.channel = %d\n", + pmpriv->curr_bss_params.bss_descriptor.channel); + PRINTM(MINFO, "curr_bss_params.band = %d\n", + pmpriv->curr_bss_params.band); + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_AD_HOC_JOIN, + HostCmd_ACT_GEN_SET, 0, pioctl_buf, pbss_desc); + + LEAVE(); + return ret; +} + +/** + * @brief Send Deauthentication Request or Stop the AdHoc network depending on + * mode + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_req A pointer to mlan_ioctl_req structure + * @param deauth_param A pointer to mlan_deauth_param structure + * + * @return MLAN_STATUS_SUCCESS--success, MLAN_STATUS_FAILURE--fail, + * MLAN_STATUS_PENDING--pending + */ +mlan_status wlan_disconnect(mlan_private *pmpriv, mlan_ioctl_req *pioctl_req, + mlan_deauth_param *deauth_param) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_deauth_param local_param = {.mac_addr = {0, 0, 0, 0, 0, 0}, + .reason_code = DEF_DEAUTH_REASON_CODE}; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + + ENTER(); + + if (deauth_param) + memcpy_ext(pmpriv->adapter, &local_param, deauth_param, + sizeof(*deauth_param), sizeof(local_param)); + if (pmpriv->media_connected == MTRUE) { + if (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) { + if (!deauth_param || + !memcmp(pmpriv->adapter, deauth_param->mac_addr, + zero_mac, sizeof(zero_mac))) + memcpy_ext(pmpriv->adapter, + local_param.mac_addr, + (t_u8 *)&pmpriv->curr_bss_params + .bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); +#ifdef WIFI_DIRECT_SUPPORT + if (pmpriv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + ret = wlan_prepare_cmd( + pmpriv, HostCmd_CMD_802_11_DISASSOCIATE, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, &local_param); + else +#endif + ret = wlan_prepare_cmd( + pmpriv, + HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, &local_param); + + if (ret == MLAN_STATUS_SUCCESS && pioctl_req) + ret = MLAN_STATUS_PENDING; + + } else if (pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) { + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_AD_HOC_STOP, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS && pioctl_req) + ret = MLAN_STATUS_PENDING; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Convert band to radio type used in channel TLV + * + * @param band Band enumeration to convert to a channel TLV radio type + * + * @return Radio type designator for use in a channel TLV + */ +t_u8 wlan_band_to_radio_type(t_u8 band) +{ + t_u8 ret_radio_type; + + ENTER(); + + switch (band) { + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + case BAND_A | BAND_AN | BAND_AAC: + ret_radio_type = BAND_5GHZ; + break; + case BAND_B: + case BAND_G: + case BAND_B | BAND_G: + default: + ret_radio_type = BAND_2GHZ; + break; + } + + LEAVE(); + return ret_radio_type; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_join.h b/mxm_wifiex/wlan_src/mlan/mlan_join.h new file mode 100644 index 0000000..99d6027 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_join.h @@ -0,0 +1,42 @@ +/** @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 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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/mxm_wifiex/wlan_src/mlan/mlan_main.h b/mxm_wifiex/wlan_src/mlan/mlan_main.h new file mode 100644 index 0000000..2e3019f --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_main.h @@ -0,0 +1,4137 @@ +/** @file mlan_main.h + * + * @brief This file defines the private and adapter data + * structures and declares global function prototypes used + * in MLAN module. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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)(t_pvoid pmoal_handle, t_u32 level, + char *pformat, IN...); + +extern mlan_status (*get_sys_time_callback)(t_void *pmoal_handle, t_u32 *psec, + t_u32 *pusec); + +extern t_u32 mlan_drvdbg; + +#ifdef DEBUG_LEVEL2 +#define PRINTM_MINFO(msg...) \ + do { \ + if ((mlan_drvdbg & MINFO) && (print_callback)) \ + print_callback(MNULL, MINFO, msg); \ + } while (0) +#define PRINTM_MWARN(msg...) \ + do { \ + if ((mlan_drvdbg & MWARN) && (print_callback)) \ + print_callback(MNULL, MWARN, msg); \ + } while (0) +#define PRINTM_MENTRY(msg...) \ + do { \ + if ((mlan_drvdbg & MENTRY) && (print_callback)) \ + print_callback(MNULL, MENTRY, msg); \ + } while (0) +#define PRINTM_GET_SYS_TIME(level, psec, pusec) \ + do { \ + if ((level & mlan_drvdbg) && (get_sys_time_callback)) \ + get_sys_time_callback(MNULL, psec, pusec); \ + } while (0) + +/** Hexdump for level-2 debugging */ +#define HEXDUMP(x, y, z) \ + do { \ + if ((mlan_drvdbg & (MHEX_DUMP | MINFO)) && (print_callback)) \ + print_callback(MNULL, MHEX_DUMP | MINFO, x, y, z); \ + } while (0) + +#else + +#define PRINTM_MINFO(msg...) \ + do { \ + } while (0) +#define PRINTM_MWARN(msg...) \ + do { \ + } while (0) +#define PRINTM_MENTRY(msg...) \ + do { \ + } while (0) + +#define PRINTM_GET_SYS_TIME(level, psec, pusec) \ + do { \ + if ((level & mlan_drvdbg) && (get_sys_time_callback) && \ + (level != MINFO) && (level != MWARN)) \ + get_sys_time_callback(MNULL, psec, pusec); \ + } while (0) + +/** Hexdump for debugging */ +#define HEXDUMP(x, y, z) \ + do { \ + } while (0) + +#endif /* DEBUG_LEVEL2 */ + +#define PRINTM_MFW_D(msg...) \ + do { \ + if ((mlan_drvdbg & MFW_D) && (print_callback)) \ + print_callback(MNULL, MFW_D, msg); \ + } while (0) +#define PRINTM_MCMD_D(msg...) \ + do { \ + if ((mlan_drvdbg & MCMD_D) && (print_callback)) \ + print_callback(MNULL, MCMD_D, msg); \ + } while (0) +#define PRINTM_MDAT_D(msg...) \ + do { \ + if ((mlan_drvdbg & MDAT_D) && (print_callback)) \ + print_callback(MNULL, MDAT_D, msg); \ + } while (0) +#define PRINTM_MIF_D(msg...) \ + do { \ + if ((mlan_drvdbg & MIF_D) && (print_callback)) \ + print_callback(MNULL, MIF_D, msg); \ + } while (0) + +#define PRINTM_MIOCTL(msg...) \ + do { \ + if ((mlan_drvdbg & MIOCTL) && (print_callback)) \ + print_callback(MNULL, MIOCTL, msg); \ + } while (0) +#define PRINTM_MINTR(msg...) \ + do { \ + if ((mlan_drvdbg & MINTR) && (print_callback)) \ + print_callback(MNULL, MINTR, msg); \ + } while (0) +#define PRINTM_MEVENT(msg...) \ + do { \ + if ((mlan_drvdbg & MEVENT) && (print_callback)) \ + print_callback(MNULL, MEVENT, msg); \ + } while (0) +#define PRINTM_MCMND(msg...) \ + do { \ + if ((mlan_drvdbg & MCMND) && (print_callback)) \ + print_callback(MNULL, MCMND, msg); \ + } while (0) +#define PRINTM_MDATA(msg...) \ + do { \ + if ((mlan_drvdbg & MDATA) && (print_callback)) \ + print_callback(MNULL, MDATA, msg); \ + } while (0) +#define PRINTM_MERROR(msg...) \ + do { \ + if ((mlan_drvdbg & MERROR) && (print_callback)) \ + print_callback(MNULL, MERROR, msg); \ + } while (0) +#define PRINTM_MFATAL(msg...) \ + do { \ + if ((mlan_drvdbg & MFATAL) && (print_callback)) \ + print_callback(MNULL, MFATAL, msg); \ + } while (0) +#define PRINTM_MMSG(msg...) \ + do { \ + if ((mlan_drvdbg & MMSG) && (print_callback)) \ + print_callback(MNULL, MMSG, msg); \ + } while (0) + +#define PRINTM(level, msg...) PRINTM_##level((char *)msg) + +/** Log debug message */ +#ifdef __GNUC__ +#define PRINTM_NETINTF(level, pmpriv) \ + do { \ + if ((mlan_drvdbg & level) && pmpriv && \ + pmpriv->adapter->callbacks.moal_print_netintf) \ + pmpriv->adapter->callbacks.moal_print_netintf( \ + pmpriv->adapter->pmoal_handle, \ + pmpriv->bss_index, level); \ + } while (0) +#endif /* __GNUC__ */ + +/** Max hex dump data length */ +#define MAX_DATA_DUMP_LEN 64 + +/** Debug hexdump for level-1 debugging */ +#define DBG_HEXDUMP(level, x, y, z) \ + do { \ + if ((mlan_drvdbg & level) && print_callback) \ + print_callback(MNULL, MHEX_DUMP | level, x, y, z); \ + } while (0) + +#else /* DEBUG_LEVEL1 */ + +#define PRINTM(level, msg...) \ + do { \ + } while (0) + +#define PRINTM_NETINTF(level, pmpriv) \ + do { \ + } while (0) + +/** Debug hexdump for level-1 debugging */ +#define DBG_HEXDUMP(level, x, y, z) \ + do { \ + } while (0) + +/** Hexdump for debugging */ +#define HEXDUMP(x, y, z) \ + do { \ + } while (0) + +#define PRINTM_GET_SYS_TIME(level, psec, pusec) \ + do { \ + } while (0) + +#endif /* DEBUG_LEVEL1 */ + +/* Reason Code 3: STA is leaving (or has left) IBSS or ESS */ +#define DEF_DEAUTH_REASON_CODE (0x3) + +/** Log entry point for debugging */ +#define ENTER() \ + do { \ + PRINTM(MENTRY, "Enter: %s\n", __func__); \ + } while (0) + +/** Log exit point for debugging */ +#define LEAVE() \ + do { \ + PRINTM(MENTRY, "Leave: %s\n", __func__); \ + } while (0) + +/** Find minimum */ +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/** Find maximum */ +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#ifdef memset +#undef memset +#endif +/** Memset routine */ +#define memset(adapter, s, c, len) \ + (adapter->callbacks.moal_memset(adapter->pmoal_handle, s, c, len)) + +#ifdef memmove +#undef memmove +#endif +/** Memmove routine */ +#define memmove(adapter, dest, src, len) \ + (adapter->callbacks.moal_memmove(adapter->pmoal_handle, dest, src, len)) + +#ifdef memcpy +#undef memcpy +#endif +/** Memcpy routine */ +#define memcpy(adapter, to, from, len) \ + (adapter->callbacks.moal_memcpy(adapter->pmoal_handle, to, from, len)) +/* memcpy_ext rountine */ +#define memcpy_ext(adapter, to, from, len, size) \ + (adapter->callbacks.moal_memcpy_ext(adapter->pmoal_handle, to, from, \ + len, size)) + +#ifdef memcmp +#undef memcmp +#endif +/** Memcmp routine */ +#define memcmp(adapter, s1, s2, len) \ + (adapter->callbacks.moal_memcmp(adapter->pmoal_handle, s1, s2, len)) + +/** Find number of elements */ +#ifndef NELEMENTS +#define NELEMENTS(x) (sizeof(x) / sizeof(x[0])) +#endif + +/** SWAP: swap t_u8 */ +#define SWAP_U8(a, b) \ + { \ + t_u8 t; \ + t = a; \ + a = b; \ + b = t; \ + } + +/** SWAP: swap t_u8 */ +#define SWAP_U16(a, b) \ + { \ + t_u16 t; \ + t = a; \ + a = b; \ + b = t; \ + } + +/** MLAN MNULL pointer */ +#define MNULL (0) + +/** 16 bits byte swap */ +#define swap_byte_16(x) \ + ((t_u16)((((t_u16)(x)&0x00ffU) << 8) | (((t_u16)(x)&0xff00U) >> 8))) + +/** 32 bits byte swap */ +#define swap_byte_32(x) \ + ((t_u32)((((t_u32)(x)&0x000000ffUL) << 24) | \ + (((t_u32)(x)&0x0000ff00UL) << 8) | \ + (((t_u32)(x)&0x00ff0000UL) >> 8) | \ + (((t_u32)(x)&0xff000000UL) >> 24))) + +/** 64 bits byte swap */ +#define swap_byte_64(x) \ + ((t_u64)((t_u64)(((t_u64)(x)&0x00000000000000ffULL) << 56) | \ + (t_u64)(((t_u64)(x)&0x000000000000ff00ULL) << 40) | \ + (t_u64)(((t_u64)(x)&0x0000000000ff0000ULL) << 24) | \ + (t_u64)(((t_u64)(x)&0x00000000ff000000ULL) << 8) | \ + (t_u64)(((t_u64)(x)&0x000000ff00000000ULL) >> 8) | \ + (t_u64)(((t_u64)(x)&0x0000ff0000000000ULL) >> 24) | \ + (t_u64)(((t_u64)(x)&0x00ff000000000000ULL) >> 40) | \ + (t_u64)(((t_u64)(x)&0xff00000000000000ULL) >> 56))) + +#ifdef BIG_ENDIAN_SUPPORT +/** Convert ulong n/w to host */ +#define mlan_ntohl(x) x +/** Convert host ulong to n/w */ +#define mlan_htonl(x) x +/** Convert n/w to host */ +#define mlan_ntohs(x) x +/** Convert host to n/w */ +#define mlan_htons(x) x +/** Convert from 16 bit little endian format to CPU format */ +#define wlan_le16_to_cpu(x) swap_byte_16(x) +/** Convert from 32 bit little endian format to CPU format */ +#define wlan_le32_to_cpu(x) swap_byte_32(x) +/** Convert from 64 bit little endian format to CPU format */ +#define wlan_le64_to_cpu(x) swap_byte_64(x) +/** Convert to 16 bit little endian format from CPU format */ +#define wlan_cpu_to_le16(x) swap_byte_16(x) +/** Convert to 32 bit little endian format from CPU format */ +#define wlan_cpu_to_le32(x) swap_byte_32(x) +/** Convert to 64 bit little endian format from CPU format */ +#define wlan_cpu_to_le64(x) swap_byte_64(x) + +/** Convert TxPD to little endian format from CPU format */ +#define endian_convert_TxPD(x) \ + { \ + (x)->tx_pkt_length = wlan_cpu_to_le16((x)->tx_pkt_length); \ + (x)->tx_pkt_offset = wlan_cpu_to_le16((x)->tx_pkt_offset); \ + (x)->tx_pkt_type = wlan_cpu_to_le16((x)->tx_pkt_type); \ + (x)->tx_control = wlan_cpu_to_le32((x)->tx_control); \ + (x)->tx_control_1 = wlan_cpu_to_le32((x)->tx_control_1); \ + } +/** Convert RxPD from little endian format to CPU format */ +#define endian_convert_RxPD(x) \ + { \ + (x)->rx_pkt_length = wlan_le16_to_cpu((x)->rx_pkt_length); \ + (x)->rx_pkt_offset = wlan_le16_to_cpu((x)->rx_pkt_offset); \ + (x)->rx_pkt_type = wlan_le16_to_cpu((x)->rx_pkt_type); \ + (x)->seq_num = wlan_le16_to_cpu((x)->seq_num); \ + (x)->rx_info = wlan_le32_to_cpu((x)->rx_info); \ + } + +#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)(t_void *pmoal_handle, t_u32 cond); + +/** Assertion */ +#define MASSERT(cond) \ + do { \ + if (!(cond)) { \ + PRINTM(MFATAL, "ASSERT: %s: %i\n", __func__, \ + __LINE__); \ + if (assert_callback) { \ + assert_callback(MNULL, (t_ptr)(cond)); \ + } else { \ + do { \ + } while (1); \ + } \ + } \ + } while (0) + +/** Maximum event buffer size */ +#define MAX_EVENT_SIZE (3 * 1024) + +/** 60 seconds */ +#define MRVDRV_TIMER_60S 60000 +/** 10 seconds */ +#define MRVDRV_TIMER_10S 10000 +/** 5 seconds */ +#define MRVDRV_TIMER_5S 5000 +/** 3 seconds */ +#define MRVDRV_TIMER_3S 3000 +/** 1 second */ +#define MRVDRV_TIMER_1S 1000 + +/** Maximum size of multicast list */ +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +/** Maximum size of channel */ +#define MRVDRV_MAX_CHANNEL_SIZE 14 +/** Maximum length of SSID */ +#define MRVDRV_MAX_SSID_LENGTH 32 +/** WEP list macros & data structures */ +/** Size of key buffer in bytes */ +#define MRVL_KEY_BUFFER_SIZE_IN_BYTE 16 +/** Maximum length of WPA key */ +#define MRVL_MAX_KEY_WPA_KEY_LENGTH 32 + +/** Default listen interval */ +#define MLAN_DEFAULT_LISTEN_INTERVAL 20 + +/** Maximum number of region codes */ +#define MRVDRV_MAX_REGION_CODE 9 + +/** Maximum number of CFP codes for BG */ +#define MRVDRV_MAX_CFP_CODE_BG 0 +/** Maximum number of CFP codes for A */ +#define MRVDRV_MAX_CFP_CODE_A 5 + +/** high rx pending packets */ +#define HIGH_RX_PENDING 1000 +/** low rx pending packets */ +#define LOW_RX_PENDING 800 + +/** Default region code */ +#define MRVDRV_DEFAULT_REGION_CODE 0x10 +/** Default country code */ +#define MRVDRV_DEFAULT_COUNTRY_CODE "US" + +/** Japan country code */ +#define COUNTRY_CODE_JP_40 0x40 +/** Japan special country code */ +#define COUNTRY_CODE_JP_FF 0xFF + +/** Default factor for calculating beacon average */ +#define DEFAULT_BCN_AVG_FACTOR 8 +/** Default factor for calculating data average */ +#define DEFAULT_DATA_AVG_FACTOR 8 + +/** The first valid channel for use */ +#define FIRST_VALID_CHANNEL 0xff +/** Default Ad-Hoc channel */ +#define DEFAULT_AD_HOC_CHANNEL 6 +/** Default Ad-Hoc channel A */ +#define DEFAULT_AD_HOC_CHANNEL_A 36 + +/** Number of WEP keys */ +#define MRVL_NUM_WEP_KEY (4) + +/** Default multiple DTIM */ +#define MRVDRV_DEFAULT_MULTIPLE_DTIM 1 + +/** Default beacon missing timeout */ +#define DEFAULT_BCN_MISS_TIMEOUT 10 + +/** Maximum buffer space for beacons retrieved from scan responses */ +#define MAX_SCAN_BEACON_BUFFER 49152 +/** Default buffer space for beacons retrieved from scan responses */ +#define DEFAULT_SCAN_BEACON_BUFFER 4096 + +/** + * @brief Buffer pad space for newly allocated beacons/probe responses + * + * Beacons are typically 6 bytes longer than an equivalent probe response. + * For each scan response stored, allocate an extra byte pad at the end to + * allow easy expansion to store a beacon in the same memory a probe response + * previously contained + */ +#define SCAN_BEACON_ENTRY_PAD 6 + +/** Scan time specified in the channel TLV + * for each channel for passive scans + */ +#define MRVDRV_PASSIVE_SCAN_CHAN_TIME 200 + +/** Scan time specified in the channel TLV + * for each channel for active scans + */ +#define MRVDRV_ACTIVE_SCAN_CHAN_TIME 200 + +/** Scan time specified in the channel TLV + * for each channel for specific scans + */ +#define MRVDRV_SPECIFIC_SCAN_CHAN_TIME 110 + +/** + * Max total scan time in milliseconds + * The total scan time should be less than scan command timeout value (20s) + */ +#define MRVDRV_MAX_TOTAL_SCAN_TIME (MRVDRV_TIMER_10S * 2 - MRVDRV_TIMER_1S) + +/** Offset for GTK as it has version to skip past for GTK */ +#define RSN_GTK_OUI_OFFSET 2 + +/** If OUI is not found */ +#define MLAN_OUI_NOT_PRESENT 0 +/** If OUI is found */ +#define MLAN_OUI_PRESENT 1 + +/** Is cmd_resp, event or data packet received? */ +#define IS_CARD_RX_RCVD(adapter) \ + (adapter->cmd_resp_received || adapter->event_received || \ + adapter->data_received) +#ifdef USB +/** Type length */ +#define MLAN_TYPE_LEN 4 +/** Type Command */ +#define MLAN_USB_TYPE_CMD 0xF00DFACE +/** Type VDLL */ +#define MLAN_USB_TYPE_VDLL 0xF00DC0DE +/** Type Data */ +#define MLAN_USB_TYPE_DATA 0xBEADC0DE +/** Type Event */ +#define MLAN_USB_TYPE_EVENT 0xBEEFFACE +#endif /* USB */ +/** Type command */ +#define MLAN_TYPE_CMD 1 +/** Type data */ +#define MLAN_TYPE_DATA 0 +/** Type event */ +#define MLAN_TYPE_EVENT 3 +/** Type vdll */ +#define MLAN_TYPE_VDLL 4 +#ifdef SDIO +/** Type single port aggr data */ +#define MLAN_TYPE_SPA_DATA 10 +/** OFFSET of 512 block number */ +#define OFFSET_OF_BLOCK_NUMBER 15 +/** OFFSET of SDIO Header */ +#define OFFSET_OF_SDIO_HEADER 28 +/** sdio max rx size for cmd53, 255 * 256, reserve 1 block for DMA alignment */ +#define SDIO_CMD53_MAX_SIZE 65280 +#define MAX_SUPPORT_AMSDU_SIZE 4096 +/** Maximum numbfer of registers to read for multiple port */ +#if defined(SD8887) || defined(SD8997) || defined(SD8977) || \ + defined(SD8987) || defined(SD9098) || defined(SD9097) || \ + defined(SD8978) +#define MAX_MP_REGS 196 +#else +/* upto 0xB7 */ +#define MAX_MP_REGS 184 +#endif +/** Maximum port */ +#define MAX_PORT 32 + +/** max MP REGS */ +#define MAX_MP_REGS_MAX (196) + +/** Multi port TX aggregation buffer size */ +#define SDIO_MP_TX_AGGR_DEF_BUF_SIZE (65280) /* 64K - 256 */ + +/** Multi port RX aggregation buffer size */ +#define SDIO_MP_RX_AGGR_DEF_BUF_SIZE (65280) /* 64K - 256 */ + +#endif /* SDIO */ + +/** Minimum BA threshold */ +#define MIN_BA_THRESHOLD 16 + +/** High threshold at which to start drop packets */ +#define RX_HIGH_THRESHOLD 1024 +/** Low threshold to allow Rx BA */ +#define RX_LOW_THRESHOLD 128 + +#define MFG_CMD_SET_TEST_MODE 1 +#define MFG_CMD_UNSET_TEST_MODE 0 +#define MFG_CMD_TX_ANT 0x1004 +#define MFG_CMD_RX_ANT 0x1005 +#define MFG_CMD_TX_CONT 0x1009 +#define MFG_CMD_RF_CHAN 0x100A +#define MFG_CMD_CLR_RX_ERR 0x1010 +#define MFG_CMD_TX_FRAME 0x1021 +#define MFG_CMD_RF_BAND_AG 0x1034 +#define MFG_CMD_RF_CHANNELBW 0x1044 + +/** Debug command number */ +#define DBG_CMD_NUM 10 + +/** Info for debug purpose */ +typedef struct _wlan_dbg { + /** Number of host to card command failures */ + t_u32 num_cmd_host_to_card_failure; + /** Number of host to card sleep confirm failures */ + t_u32 num_cmd_sleep_cfm_host_to_card_failure; + /** Number of host to card Tx failures */ + t_u32 num_tx_host_to_card_failure; + /** Number of card to host command/event failures */ + t_u32 num_cmdevt_card_to_host_failure; + /** Number of card to host Rx failures */ + t_u32 num_rx_card_to_host_failure; + /** Number of interrupt read failures */ + t_u32 num_int_read_failure; + /** Last interrupt status */ + t_u32 last_int_status; + /** Number of allocate buffer failure */ + t_u32 num_alloc_buffer_failure; + /** Number of pkt dropped */ + t_u32 num_pkt_dropped; + /** Number of deauthentication events */ + t_u32 num_event_deauth; + /** Number of disassosiation events */ + t_u32 num_event_disassoc; + /** Number of link lost events */ + t_u32 num_event_link_lost; + /** Number of deauthentication commands */ + t_u32 num_cmd_deauth; + /** Number of association comamnd successes */ + t_u32 num_cmd_assoc_success; + /** Number of association command failures */ + t_u32 num_cmd_assoc_failure; + /** Number of consecutive association command failures */ + t_u32 num_cons_assoc_failure; + + /** Timeout command ID */ + t_u16 timeout_cmd_id; + /** Timeout command action */ + t_u16 timeout_cmd_act; + /** List of last command IDs */ + t_u16 last_cmd_id[DBG_CMD_NUM]; + /** List of last command actions */ + t_u16 last_cmd_act[DBG_CMD_NUM]; + /** Last command index */ + t_u16 last_cmd_index; + /** List of last command response IDs */ + t_u16 last_cmd_resp_id[DBG_CMD_NUM]; + /** Last command response index */ + t_u16 last_cmd_resp_index; + /** List of last events */ + t_u16 last_event[DBG_CMD_NUM]; + /** Last event index */ + t_u16 last_event_index; + /** Number of no free command node */ + t_u16 num_no_cmd_node; +} wlan_dbg; + +/** Hardware status codes */ +typedef enum _WLAN_HARDWARE_STATUS { + WlanHardwareStatusReady, + WlanHardwareStatusGetHwSpec, + WlanHardwareStatusGetHwSpecdone, + WlanHardwareStatusInitializing, + WlanHardwareStatusInitdone, + WlanHardwareStatusReset, + WlanHardwareStatusClosing, + WlanHardwareStatusNotReady +} WLAN_HARDWARE_STATUS; + +/** WLAN_802_11_POWER_MODE */ +typedef enum _WLAN_802_11_POWER_MODE { + Wlan802_11PowerModeCAM, + Wlan802_11PowerModePSP +} WLAN_802_11_POWER_MODE; + +/** tx param */ +typedef struct _mlan_tx_param { + /** next packet length */ + t_u32 next_pkt_len; +} mlan_tx_param; + +/** PS_STATE */ +typedef enum _PS_STATE { + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP_CFM, + PS_STATE_SLEEP +} PS_STATE; + +/** Minimum flush timer for win size of 1 is 50 ms */ +#define MIN_FLUSH_TIMER_MS 50 +/** Minimum flush timer for win size of 1 is 15 ms */ +#define MIN_FLUSH_TIMER_15_MS 15 + +/** Tx BA stream table */ +typedef struct _TxBAStreamTbl TxBAStreamTbl; + +/** Add BA parameter data structure */ +typedef struct { + /** Window size for initiator */ + t_u32 tx_win_size; + /** Window size for receiver */ + t_u32 rx_win_size; + /** Block ack timeout */ + t_u32 timeout; + /** amsdu support for ADDBA request */ + t_u8 tx_amsdu; + /** amsdu support for ADDBA response */ + t_u8 rx_amsdu; +} add_ba_param_t; + +/** Tx aggregation data structure */ +typedef struct _txAggr_t { + /** AMPDU user */ + t_u8 ampdu_user; + /** AMPDU AP */ + t_u8 ampdu_ap; + /** AMSDU */ + t_u8 amsdu; +} tx_aggr_t; + +/** del ba threshold */ +#define DEL_BA_THRESHOLD 10 +/** BA stream status */ +typedef enum _baStatus_e { + BA_STREAM_NOT_SETUP = 0, + BA_STREAM_SETUP_INPROGRESS, + BA_STREAM_SETUP_COMPLETE +} baStatus_e; + +/** RA list table */ +typedef struct _raListTbl raListTbl, *praListTbl; + +/** RA list table */ +struct _raListTbl { + /** Pointer to previous node */ + raListTbl *pprev; + /** Pointer to next node */ + raListTbl *pnext; + /** Buffer list head */ + mlan_list_head buf_head; + /** RA list buffer */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** total packets in RA list */ + t_u16 total_pkts; + /** packets received */ + t_u16 packet_count; + /** packet count threshold to setup BA */ + t_u8 ba_packet_threshold; + /** is 11n enabled */ + t_u8 is_11n_enabled; + /** max amsdu size */ + t_u16 max_amsdu; + /** BA stream status */ + baStatus_e ba_status; + /** del ba count */ + t_u8 del_ba_count; + /** amsdu in ampdu flag */ + t_u8 amsdu_in_ampdu; + /** tx_pause flag */ + t_u8 tx_pause; +}; + +/** TID table */ +typedef struct _tidTbl { + /** RA list head */ + mlan_list_head ra_list; + /** Current RA list */ + raListTbl *ra_list_curr; +} tid_tbl_t; + +/** Highest priority setting for a packet (uses voice AC) */ +#define WMM_HIGHEST_PRIORITY 7 +/** Highest priority TID */ +#define HIGH_PRIO_TID 7 +/** Lowest priority TID */ +#define LOW_PRIO_TID 0 +/** No packet priority (< lowest) */ +#define NO_PKT_PRIO_TID -1 + +/** Max driver packet delay in msec */ +#define WMM_DRV_DELAY_MAX 510 + +/** Struct of WMM DESC */ +typedef struct _wmm_desc { + /** TID table */ + tid_tbl_t tid_tbl_ptr[MAX_NUM_TID]; + /** Packets out */ + t_u32 packets_out[MAX_NUM_TID]; + /** Packets queued */ + t_u32 pkts_queued[MAX_NUM_TID]; + /** Packets paused */ + t_u32 pkts_paused[MAX_NUM_TID]; + /** Spin lock to protect ra_list */ + t_void *ra_list_spinlock; + + /** AC status */ + WmmAcStatus_t ac_status[MAX_AC_QUEUES]; + /** AC downgraded values */ + mlan_wmm_ac_e ac_down_graded_vals[MAX_AC_QUEUES]; + + /** Max driver packet delay sent to the firmware for expiry eval */ + t_u32 drv_pkt_delay_max; + + /** WMM queue priority table */ + t_u8 queue_priority[MAX_AC_QUEUES]; + /** User priority packet transmission control */ + t_u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ + + /** Number of transmit packets queued */ + mlan_scalar tx_pkts_queued; + /** Tracks highest priority with a packet queued */ + mlan_scalar highest_queued_prio; +} wmm_desc_t; + +/** Security structure */ +typedef struct _wlan_802_11_security_t { + /** WPA enabled flag */ + t_u8 wpa_enabled; + /** E-Supplicant enabled flag */ + t_u8 ewpa_enabled; + /** WPA2 enabled flag */ + t_u8 wpa2_enabled; + /** WAPI enabled flag */ + t_u8 wapi_enabled; + /** WAPI key on flag */ + t_u8 wapi_key_on; + /** WEP status */ + WLAN_802_11_WEP_STATUS wep_status; + /** Authentication mode */ + t_u32 authentication_mode; + /** Encryption mode */ + t_u32 encryption_mode; + /** Hotspot OSEN enabled */ + t_u8 osen_enabled; +} 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]; + /** Host MLME flag*/ + t_u8 host_mlme; + t_u8 use_mfp; +} 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 + +/** Region-band mapping table */ +typedef struct _region_chan_t { + /** TRUE if this entry is valid */ + t_u8 valid; + /** Region code for US, Japan ... */ + t_u8 region; + /** Band B/G/A, used for BAND_CONFIG cmd */ + t_u8 band; + /** Actual No. of elements in the array below */ + t_u8 num_cfp; + /** chan-freq-txpower mapping table */ + chan_freq_power_t *pcfp; +} region_chan_t; + +/** State of 11d */ +typedef enum _state_11d_t { + DISABLE_11D = 0, + ENABLE_11D = 1, +} state_11d_t; + +#define DEFAULT_11D_STATE DISABLE_11D + +/** Domain regulatory information */ +typedef struct _wlan_802_11d_domain_reg { + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** band that channels in sub_band belong to */ + t_u8 band; + /** No. of subband in below */ + t_u8 no_of_sub_band; + /** Subband data to send/last sent */ + IEEEtypes_SubbandSet_t sub_band[MRVDRV_MAX_SUBBAND_802_11D]; +} wlan_802_11d_domain_reg_t; + +/** Data for state machine */ +typedef struct _wlan_802_11d_state { + /** True for enabling 11D */ + state_11d_t enable_11d; + /** True for user enabling 11D */ + state_11d_t user_enable_11d; +} wlan_802_11d_state_t; + +/** 802.11h State information kept in the 'mlan_private' driver structure */ +typedef struct { + /** Indicate 11h is enabled from host */ + t_bool is_11h_host; + /** Indicates whether 11h is enabled in the driver */ + t_bool is_11h_enabled; + /** Indicates whether 11h is active in the firmware */ + t_bool is_11h_active; + /** Master device using automatic channel select */ + t_bool adhoc_auto_sel_chan; + /** Set when driver receives a STOP TX event from fw */ + t_bool tx_disabled; + /** Channel that ChanSwAnn was received for, non-zero = active */ + t_u8 dfs_slave_csa_chan; + /** Expiry for above variable, seconds in system time */ + t_u32 dfs_slave_csa_expire_at_sec; +} wlan_11h_interface_state_t; + +#if defined(UAP_SUPPORT) +/** UAP get info callback state kept in the 'mlan_private' driver structure */ +typedef struct { + /** UAP internal callback after wlan_uap_get_channel */ + /** (parameter is really pointer to mlan_private) */ + mlan_status (*get_chan_callback)(t_void *); + /** current ioctl_req (to be completed in callback) */ + pmlan_ioctl_req pioctl_req_curr; + /** band config from MrvlIEtypes_channel_band_t */ + Band_Config_t bandcfg; + /** channel from MrvlIEtypes_channel_band_t */ + t_u8 channel; + /** beacon period (in msec) from MrvlIEtypes_beacon_period_t */ + t_u16 beacon_period; + /** dtim period (no unit) from MrvlIEtypes_dtim_period_t */ + t_u8 dtim_period; +} wlan_uap_get_info_cb_t; +#endif + +/** Data structure for WPS information */ +typedef struct { + /** WPS IE */ + IEEEtypes_VendorSpecific_t wps_ie; + /** Session enable flag */ + t_u8 session_enable; +} wps_t; + +/** mlan_operations data structure */ +typedef struct _mlan_operations { + /** cmd init handler */ + mlan_status (*init_cmd)(t_void *priv, t_u8 first_bss); + /** ioctl handler */ + mlan_status (*ioctl)(t_void *adapter, pmlan_ioctl_req pioctl_req); + /** cmd handler */ + mlan_status (*prepare_cmd)(t_void *priv, t_u16 cmd_no, t_u16 cmd_action, + t_u32 cmd_oid, t_void *pioctl_buf, + t_void *pdata_buf, t_void *pcmd_buf); + /** cmdresp handler */ + mlan_status (*process_cmdresp)(t_void *priv, t_u16 cmdresp_no, + t_void *pcmd_buf, t_void *pioctl); + /** rx handler */ + mlan_status (*process_rx_packet)(t_void *adapter, pmlan_buffer pmbuf); + /** event handler */ + mlan_status (*process_event)(t_void *priv); + /** txpd handler */ + t_void *(*process_txpd)(t_void *priv, pmlan_buffer pmbuf); + /** BSS role */ + mlan_bss_role bss_role; +} mlan_operations, *pmlan_operations; + +/** Private structure for MLAN */ +typedef struct _mlan_private { + /** Pointer to mlan_adapter */ + struct _mlan_adapter *adapter; + /** BSS index */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** BSS role */ + t_u8 bss_role; + /** BSS virtual flag */ + t_u8 bss_virtual; + /** BSS Priority */ + t_u8 bss_priority; + /** BSS number */ + t_u8 bss_num; + /** Frame type */ + t_u8 frame_type; + /** MAC address information */ + t_u8 curr_addr[MLAN_MAC_ADDR_LENGTH]; + /** Media connection status */ + t_bool media_connected; + + /** Current packet filter */ + t_u32 curr_pkt_filter; + /** Infrastructure mode */ + t_u32 bss_mode; + + /** Tx packet control */ + t_u32 pkt_tx_ctrl; + + /** Tx power level */ + t_s16 tx_power_level; + /** Maximum Tx power level */ + t_s8 max_tx_power_level; + /** Minimum Tx power level */ + t_s8 min_tx_power_level; + /** Tx rate */ + t_u8 tx_rate; + t_u8 tx_rate_info; + /*HE tx tone mode and DCM info*/ + t_u8 ext_tx_rate_info; + /*HE rx tone mode and DCM info*/ + t_u8 rxpd_rx_info; + /** rxpd_htinfo */ + t_u8 rxpd_rate_info; + /** max amsdu size */ + t_u16 max_amsdu; + /** amsdu disable flag */ + t_u8 amsdu_disable; + /** 802.11n Device Capabilities for 2.4GHz */ + t_u32 usr_dot_11n_dev_cap_bg; + /** 802.11n Device Capabilities for 5GHz */ + t_u32 usr_dot_11n_dev_cap_a; + /** MIMO abstraction of MCSs supported by device */ + t_u8 usr_dev_mcs_support; +#ifdef UAP_SUPPORT + /** UAP 11n flag */ + t_u8 is_11n_enabled; +#endif /* UAP_SUPPORT */ + /** UAP 11ac flag */ + t_u8 is_11ac_enabled; + /** UAP 11ax flag */ + t_u8 is_11ax_enabled; + /** tx vht_info */ + t_u8 tx_vhtinfo; + /** rxpd_vhtinfo */ + t_u8 rxpd_vhtinfo; + /** 802.11ac Device Capabilities for 2.4GHz */ + t_u32 usr_dot_11ac_dev_cap_bg; + /** 802.11ac Device Capabilities for 5GHz */ + t_u32 usr_dot_11ac_dev_cap_a; + /** MIMO abstraction of MCSs supported by device */ + t_u32 usr_dot_11ac_mcs_support; + /** user dot 11ac_BW */ + t_u8 usr_dot_11ac_bw; + /** user dot 11ac_opermode_BW */ + t_u8 usr_dot_11ac_opermode_bw; + /** user dot 11ac_opermode_nss */ + t_u8 usr_dot_11ac_opermode_nss; + /** length of hw he capability */ + t_u8 user_hecap_len; + /** user configured 802.11ax HE capability */ + t_u8 user_he_cap[54]; + /** length of hw he capability */ + t_u8 user_2g_hecap_len; + /** user configured 802.11ax HE capability */ + t_u8 user_2g_he_cap[54]; + /** dropped pkts */ + t_u32 num_drop_pkts; +#ifdef UAP_SUPPORT + /** packet forward control */ + t_u8 pkt_fwd; +#endif + /** TX beamforming capability */ + t_u32 tx_bf_cap; + /** Rx PD rate */ + t_u8 rxpd_rate; + /** 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; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** Last data RSSI */ + t_s16 data_rssi_last; + /** Last data Noise Floor */ + t_s16 data_nf_last; + /** Average data RSSI */ + t_s16 data_rssi_avg; + /** Averag data Noise Floor */ + t_s16 data_nf_avg; + /** Last beacon RSSI */ + t_s16 bcn_rssi_last; + /** Last beacon Noise Floor */ + t_s16 bcn_nf_last; + /** Average beacon RSSI */ + t_s16 bcn_rssi_avg; + /** Average beacon Noise Floor */ + t_s16 bcn_nf_avg; + /** Attempted BSS descriptor */ + BSSDescriptor_t *pattempted_bss_desc; + + /** GTK rekey data*/ + mlan_ds_misc_gtk_rekey_data gtk_rekey; + + /** Current SSID/BSSID related parameters*/ + current_bss_params_t curr_bss_params; + /** current channel flags */ + t_u32 curr_chan_flags; + /** User selected bands */ + t_u16 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; +#ifdef UAP_SUPPORT + /** Whether UAP interface has started */ + t_bool uap_bss_started; + /** Whether UAP interface start from hostapd */ + t_bool uap_host_based; + /**UAP operating channel*/ + t_u8 uap_channel; + /** state variable for UAP Get Info callback */ + wlan_uap_get_info_cb_t uap_state_chan_cb; +#endif /* UAP_SUPPORT */ + + /** Security related */ + /** Encryption parameter */ + wlan_802_11_security_t sec_info; + /** WEP keys */ + mrvl_wep_key_t wep_key[MRVL_NUM_WEP_KEY]; + /** Current WEP key index */ + t_u16 wep_key_curr_index; + /** EWPA query 0: disable, 1: enable */ + t_u8 ewpa_query; + /** Encryption Key*/ + t_u8 wpa_ie[256]; + /** WPA IE length */ + t_u8 wpa_ie_len; + /** GTK set flag */ + t_u8 wpa_is_gtk_set; + /** AES key material */ + mlan_ds_encrypt_key aes_key; +#if defined(STA_SUPPORT) + /* Mgmt Frame Protection config */ + mlan_ds_misc_pmfcfg pmfcfg; +#endif + /** WAPI IE */ + t_u8 wapi_ie[256]; + /** WAPI IE length */ + t_u8 wapi_ie_len; + /** OSEN IE */ + t_u8 osen_ie[256]; + /** OSEN IE length */ + t_u8 osen_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; + /** 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]; + /** Pointer to the priorities for AMSDU/AMPDU table*/ + t_u8 ibss_ampdu[MAX_NUM_TID]; + /** Pointer to the priorities for AMSDU/AMPDU table*/ + t_u8 ibss_addba_reject[MAX_NUM_TID]; + /** Struct to store ADDBA parameters */ + add_ba_param_t add_ba_param; + /** user rx_win_size */ + t_u32 user_rxwinsize; + /** last rx_seq */ + t_u16 rx_seq[MAX_NUM_TID]; + /** Pointer to the Receive Reordering table*/ + mlan_list_head rx_reorder_tbl_ptr; + /** Lock for Rx packets */ + t_void *rx_pkt_lock; + +#ifdef STA_SUPPORT + /** Buffer to store the association response for application retrieval + */ + t_u8 assoc_rsp_buf[MRVDRV_ASSOC_RSP_BUF_SIZE]; + /** Length of the data stored in assoc_rsp_buf */ + t_u32 assoc_rsp_size; + + /** Generic IEEE IEs passed from the application to be inserted into the + * association request to firmware + */ + t_u8 gen_ie_buf[MRVDRV_GENIE_BUF_SIZE]; + /** Length of the data stored in gen_ie_buf */ + t_u8 gen_ie_buf_len; + + /** disconnect reason code*/ + t_u16 disconnect_reason_code; + t_u8 *pcurr_bcn_buf; + t_u32 curr_bcn_size; + t_void *curr_bcn_buf_lock; + + /** WPS */ + wps_t wps; +#endif /* STA_SUPPORT */ + + /** function table */ + mlan_operations ops; + /** tx pause flag */ + t_u8 tx_pause; + /** Port Control mode */ + t_u8 port_ctrl_mode; + + /** Port open flag */ + t_u8 port_open; + + /** Port open flag state at time of association attempt */ + t_u8 prior_port_status; + /** Bypass TX queue */ + mlan_list_head bypass_txq; + /** IP address operation */ + t_u32 op_code; + /** IP address */ + t_u8 ip_addr[IPADDR_LEN]; + t_u32 hotspot_cfg; +#ifdef STA_SUPPORT + ExtCap_t ext_cap; + ExtCap_t def_ext_cap; +#endif + /** interface header len */ + t_u8 intf_hr_len; +#ifdef USB + /** USB data port */ + t_u32 port; +#endif +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + t_void *psapriv; +#endif + /** rx per packet info */ + t_u8 rx_pkt_info; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; +} mlan_private, *pmlan_private; + +typedef struct _assoc_logger { + /** vendor specific */ + t_u8 oui[3]; + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; + t_s32 rssi; + t_u32 channel; +} assoc_logger_data; + +/** Tx BA stream table */ +struct _TxBAStreamTbl { + /** TxBAStreamTbl previous node */ + TxBAStreamTbl *pprev; + /** TxBAStreamTbl next node */ + TxBAStreamTbl *pnext; + /** TID */ + int tid; + /** RA */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** BA stream status */ + baStatus_e ba_status; + t_u8 amsdu; +}; + +/** RX reorder table */ +typedef struct _RxReorderTbl RxReorderTbl; + +typedef struct { + /** Timer for flushing */ + t_void *timer; + /** Timer set flag */ + t_u8 timer_is_set; + /** RxReorderTbl ptr */ + RxReorderTbl *ptr; + /** Priv pointer */ + mlan_private *priv; +} reorder_tmr_cnxt_t; + +/** RX reorder table */ +struct _RxReorderTbl { + /** RxReorderTbl previous node */ + RxReorderTbl *pprev; + /** RxReorderTbl next node */ + RxReorderTbl *pnext; + /** TID */ + int tid; + /** TA */ + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + /** Start window */ + int start_win; + /** last_seq */ + int last_seq; + /** Window size */ + int win_size; + /** Pointer to pointer to RxReorderTbl */ + t_void **rx_reorder_ptr; + /** Timer context */ + reorder_tmr_cnxt_t timer_context; + /** BA stream status */ + baStatus_e ba_status; + t_u8 amsdu; + /** no packet drop flag for rx_reorder_tbl */ + t_u8 force_no_drop; + /** flag for check start win */ + t_u8 check_start_win; + /** pkt receive after BA setup */ + t_u8 pkt_count; + /** flush data flag */ + t_u8 flush_data; +}; + +/** BSS priority node */ +typedef struct _mlan_bssprio_node mlan_bssprio_node; + +/** BSS priority node */ +struct _mlan_bssprio_node { + /** Pointer to previous node */ + mlan_bssprio_node *pprev; + /** Pointer to next node */ + mlan_bssprio_node *pnext; + /** Pointer to priv */ + pmlan_private priv; +}; + +/** BSS priority table */ +typedef struct _mlan_bssprio_tbl mlan_bssprio_tbl; + +/** BSS priority table */ +struct _mlan_bssprio_tbl { + /** BSS priority list head */ + mlan_list_head bssprio_head; + /** Current priority node */ + mlan_bssprio_node *bssprio_cur; +}; + +/** cmd_ctrl_node */ +typedef struct _cmd_ctrl_node cmd_ctrl_node; + +/** _cmd_ctrl_node */ +struct _cmd_ctrl_node { + /** Pointer to previous node */ + cmd_ctrl_node *pprev; + /** Pointer to next node */ + cmd_ctrl_node *pnext; + /** Pointer to priv */ + pmlan_private priv; + /** Command number */ + t_u32 cmd_no; + /** 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; +#if defined(PCIE) || defined(SDIO) + /** pre_allocated mlan_buffer for cmd */ + mlan_buffer *pmbuf; +#endif +}; + +/** station node */ +typedef struct _sta_node sta_node, *psta_node; + +/** station node*/ +struct _sta_node { + /** previous node */ + sta_node *pprev; + /** next node */ + sta_node *pnext; + /** station mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** wmm flag */ + t_u8 is_wmm_enabled; + /** 11n flag */ + t_u8 is_11n_enabled; + /** AMPDU STA */ + t_u8 ampdu_sta[MAX_NUM_TID]; + /** last rx_seq */ + t_u16 rx_seq[MAX_NUM_TID]; + /** max amsdu size */ + t_u16 max_amsdu; + /** HT cap */ + IEEEtypes_HTCap_t HTcap; + /** 11ac flag */ + t_u8 is_11ac_enabled; + /** UAP 11ax flag */ + t_u8 is_11ax_enabled; + /** SNR */ + t_s8 snr; + /** Noise Floor */ + t_s8 nf; + /** peer capability */ + t_u16 capability; + /** wapi key on off flag */ + t_u8 wapi_key_on; + /** tx pause status */ + t_u8 tx_pause; + /** station band mode */ + t_u16 bandmode; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + t_void *cm_connectioninfo; +#endif + sta_stats stats; +}; + +/** 802.11h State information kept in the 'mlan_adapter' driver structure */ +typedef struct { + /** Min TX Power capability sent to FW for 11h use and fw power control + */ + t_s8 min_tx_power_capability; + /** Max TX Power capability sent to FW for 11h use and fw power control + */ + t_s8 max_tx_power_capability; + /** User provisioned local power constraint sent in association requests + */ + t_s8 usr_def_power_constraint; + /** Received CHANNEL_SWITCH_ANN event */ + t_bool recvd_chanswann_event; + /** Indicates an interface wants to enable master radar detection */ + t_bool master_radar_det_enable_pending; + /** Indicates an interface wants to enable slave radar detection */ + t_bool slave_radar_det_enable_pending; + /** Indicates whether master radar detection active in the firmware */ + t_bool is_master_radar_det_active; + /** Indicates whether slave radar detection active in the firmware */ + t_bool is_slave_radar_det_active; + /** Quiet IE */ + IEEEtypes_Quiet_t quiet_ie; +} wlan_11h_device_state_t; + +/** Enumeration for DFS Timestamp represents field */ +enum _dfs_timestamp_repr_e { + /** Ignore entry */ + DFS_TS_REPR_NOT_IN_USE = 0, + /** NOP (Non-Occupancy Period) start time */ + DFS_TS_REPR_NOP_START = 1, + /** CAC (Channel Availability Check) completion time */ + DFS_TS_REPR_CAC_COMPLETION +}; + +/** DFS Timestamp type used for marking NOP/CAC events */ +typedef struct _wlan_dfs_timestamp_t wlan_dfs_timestamp_t; + +/** DFS Timestamp type used for marking NOP/CAC events */ +struct _wlan_dfs_timestamp_t { + /** Pointer to previous node */ + wlan_dfs_timestamp_t *pprev; + /** Pointer to next node */ + wlan_dfs_timestamp_t *pnext; + /** WLAN Channel number */ + t_u8 channel; + /** What this timestamp represents */ + t_u8 represents; + /** reserved field */ + t_u16 reserved; + /** timestamp - seconds */ + t_u32 ts_sec; + /** timestamp - microseconds */ + t_u32 ts_usec; +}; + +/** DFS State information kept in the 'mlan_adapter' driver structure */ +typedef struct { + /** Indicates whether DFS channel check is occurring in firmware */ + t_bool dfs_check_pending; + /** Indicates whether DFS channel check found radar */ + t_bool dfs_radar_found; + /** Channel radar is being checked on. BAND_A is assumed. */ + t_u8 dfs_check_channel; + /** point to the priv which start the DFS check */ + t_void *dfs_check_priv; + /** Timestamp when we got last report, + * to determine if data is old or not. + */ + t_u32 dfs_report_time_sec; + /** List for holding dfs_timestamps for NOP/CAC events */ + mlan_list_head dfs_ts_head; +} wlan_dfs_device_state_t; + +/** Enumeration for mlan_ds_11h_radar_det_hndlg stages */ +enum _mlan_ds_11h_rdh_stages { + RDH_OFF = 0, + RDH_CHK_INTFS = 1, + RDH_STOP_TRAFFIC, + RDH_GET_INFO_CHANNEL, + RDH_GET_INFO_BEACON_DTIM, + RDH_SET_CUSTOM_IE, + RDH_REM_CUSTOM_IE, + RDH_STOP_INTFS, + RDH_SET_NEW_CHANNEL, + RDH_RESTART_INTFS, + RDH_RESTART_TRAFFIC +}; + +/** State info for Radar Detected Handling kept in 'mlan_adapter' */ +typedef struct { + /** Stage (of Operation) */ + t_u8 stage; + /** Number of interfaces to handle */ + t_u8 priv_list_count; + /** Index of interface in process (used by some stages) */ + t_u8 priv_curr_idx; + /** Current Channel (to leave) */ + t_u8 curr_channel; + /** New Channel (to switch to) */ + t_u8 new_channel; + /** UAP band_config */ + Band_Config_t uap_band_cfg; + /** BEACON*DTIM period (in msec; max of STA/UAP) */ + t_u16 max_bcn_dtim_ms; + /** tx block flag */ + t_u8 tx_block; + /** List of interfaces to handle */ + mlan_private *priv_list[MLAN_MAX_BSS_NUM]; +} wlan_radar_det_hndlg_state_t; + +/** DFS/RDH testing exception settings kept in 'mlan_adapter' */ +typedef struct { + /** user-configured CAC period (in msec) */ + t_u32 user_cac_period_msec; + /** user-configured NOP period (in sec) */ + t_u16 user_nop_period_sec; + /** user-configured skip channel change on radar */ + t_bool no_channel_change_on_radar; + /** user-configured new channel to change to on radar */ + t_u8 fixed_new_channel_on_radar; + /** user-configured cac restart */ + t_u8 cac_restart; + /** cac channel */ + t_u8 chan; + /** band cfg */ + Band_Config_t bandcfg; + /** cac time */ + t_u32 millisec_dwell_time; +} wlan_dfs_testing_settings_t; + +/** + * @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 +/** + * @brief Link buffer into aggregate head buffer + * + * @param pmbuf_aggr Pointer to aggregation buffer + * @param pmbuf Pointer to buffer to copy + */ +static inline t_void wlan_link_buf_to_aggr(pmlan_buffer pmbuf_aggr, + pmlan_buffer pmbuf) +{ + /* link new buf at end of list */ + pmbuf->pnext = pmbuf_aggr; + pmbuf->pprev = pmbuf_aggr->pprev; + pmbuf->pparent = pmbuf_aggr; + pmbuf_aggr->pprev->pnext = pmbuf; + pmbuf_aggr->pprev = pmbuf; + pmbuf_aggr->use_count++; +} + +/** data structure for SDIO MPA TX */ +typedef struct _sdio_mpa_tx { + /** allocated buf for tx aggreation */ + t_u8 *head_ptr; + /** multiport tx aggregation buffer pointer */ + t_u8 *buf; + /** multiport tx aggregation buffer length */ + t_u32 buf_len; + /** multiport tx aggregation packet count */ + t_u32 pkt_cnt; + /** multiport tx aggregation ports */ + t_u32 ports; + /** multiport tx aggregation starting port */ + t_u16 start_port; + /** multiport tx aggregation enable/disable flag */ + t_u8 enabled; + /** multiport tx aggregation buffer size */ + t_u32 buf_size; + /** multiport tx aggregation pkt aggr limit */ + t_u32 pkt_aggr_limit; + /** multiport write info */ + t_u16 mp_wr_info[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** multiport rx aggregation mbuf array */ + pmlan_buffer mbuf_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; +} sdio_mpa_tx; + +/** data structure for SDIO MPA RX */ +typedef struct _sdio_mpa_rx { + /** allocated buf for rx aggreation */ + t_u8 *head_ptr; + /** multiport rx aggregation buffer pointer */ + t_u8 *buf; + /** multiport rx aggregation buffer length */ + t_u32 buf_len; + /** multiport rx aggregation packet count */ + t_u32 pkt_cnt; + /** multiport rx aggregation ports */ + t_u32 ports; + /** multiport rx aggregation starting port */ + t_u16 start_port; + + /** multiport rx aggregation mbuf array */ + pmlan_buffer mbuf_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** multiport rx aggregation pkt len array */ + t_u32 len_arr[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + + /** 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 + +#ifdef USB +/** data structure for USB Rx Deaggregation */ +typedef struct _usb_rx_deaggr_params { + /** Rx aggregation control */ + usb_aggr_ctrl aggr_ctrl; +} usb_rx_deaggr_params; + +#define MAX_USB_TX_PORT_NUM 1 +/** data structure for USB Tx Aggregation */ +typedef struct _usb_tx_aggr_params { + /** Tx aggregation control */ + usb_aggr_ctrl aggr_ctrl; + /** allocated pmbuf for tx aggreation */ + pmlan_buffer pmbuf_aggr; + /** packet len used in pmbuf_aggr */ + t_u32 aggr_len; + /** usb_tx_aggr timer */ + t_void *paggr_hold_timer; + /** usb_tx_aggr timer set flag */ + t_u8 aggr_hold_timer_is_set; + /** Timeout duration in milliseconds to wait for aggregation */ + t_u32 hold_timeout_msec; + /** lock for transmission */ + t_void *paggr_lock; + /** port for data transmission */ + t_u32 port; + /** pointer to moal_adatper structure */ + t_void *phandle; +} usb_tx_aggr_params, *pusb_tx_aggr_params; +#endif + +/** Type definition of mef_entry*/ +typedef struct _mef_cfg { + /** criteria*/ + t_u32 criteria; + /** entry num*/ + t_u16 entry_num; + /** entry pointer*/ + mef_entry_t *pentry; +} mef_cfg; + +/** Type definition of mef_entry*/ +typedef struct _mef_entry { + /** Num for wowlan entry*/ + int num_wowlan_entry; + /** Num for IPv6 neighbor solicitation message offload */ + int num_ipv6_ns_offload; + + /** criteria*/ + t_u32 criteria; + /** MEF CFG Array to store etted_entry_bitmap; + * Caution: 0-3 is for NVIDIA WHITE/BLACK list entries + * Caution: 4 is for NVIDIA ping entry + * Caution: 5 is for Auto Arp Entry + * Caution: 6 is for wowlan Entry + * Caution: 7 is for IPv6 Neighbor Solicitation offload Entry + */ + mef_entry_t entry[MAX_NUM_ENTRIES]; +} mef_entry; + +/** vdll_dnld_ctrl structure */ +typedef struct _vdll_dnld_ctrl { + /** pending VDLL block */ + t_u8 *pending_block; + /* pending VDLL block len */ + t_u16 pending_block_len; + /** memory for VDLL fw image */ + t_u8 *vdll_mem; + /** VDLL fw image len */ + t_u32 vdll_len; +#if defined(SDIO) || defined(PCIE) + /** mlan_buffer for VDLL download */ + mlan_buffer *cmd_buf; +#endif +} vdll_dnld_ctrl, *pvdll_dnld_ctrl; + +/** mlan_init_para structure */ +typedef struct _mlan_init_para { +#ifdef MFG_CMD_SUPPORT + /** MFG mode */ + t_u32 mfg_mode; +#endif +#ifdef SDIO + /** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ + t_u32 int_mode; + /** GPIO interrupt pin number */ + t_u32 gpio_pin; + /** SDIO MPA Tx */ + t_u32 mpa_tx_cfg; + /** SDIO MPA Rx */ + t_u32 mpa_rx_cfg; +#endif + /** Auto deep sleep */ + t_u32 auto_ds; + /** IEEE PS mode */ + t_u32 ps_mode; + /** Max Tx buffer size */ + t_u32 max_tx_buf; + /** 802.11d configuration */ + t_u32 cfg_11d; + /** 802.11H DFS Master Radar Detect */ + t_u32 dfs_master_radar_det_en; + /** 802.11H DFS Slave Radar Detect */ + t_u32 dfs_slave_radar_det_en; + /** dev cap mask */ + t_u32 dev_cap_mask; + /** oob independent reset mode */ + t_u32 indrstcfg; + /** fw region */ + t_bool fw_region; + /** passive to active scan */ + t_u8 passive_to_active_scan; + /** uap max sta */ + t_u8 uap_max_sta; + /** dfs w53 cfg */ + t_u8 dfs53cfg; +} mlan_init_para, *pmlan_init_para; + +#ifdef SDIO +typedef struct _mlan_sdio_card_reg { + t_u8 start_rd_port; + t_u8 start_wr_port; + t_u8 base_0_reg; + t_u8 base_1_reg; + t_u8 poll_reg; + t_u8 host_int_enable; + t_u8 host_int_status; + t_u8 status_reg_0; + t_u8 status_reg_1; + t_u8 sdio_int_mask; + t_u32 data_port_mask; + t_u8 max_mp_regs; + t_u8 rd_bitmap_l; + t_u8 rd_bitmap_u; + t_u8 rd_bitmap_1l; + t_u8 rd_bitmap_1u; + t_u8 wr_bitmap_l; + t_u8 wr_bitmap_u; + t_u8 wr_bitmap_1l; + t_u8 wr_bitmap_1u; + t_u8 rd_len_p0_l; + t_u8 rd_len_p0_u; + t_u8 card_config_2_1_reg; + t_u8 cmd_config_0; + t_u8 cmd_config_1; + t_u8 cmd_config_2; + t_u8 cmd_config_3; + t_u8 cmd_rd_len_0; + t_u8 cmd_rd_len_1; + t_u8 cmd_rd_len_2; + t_u8 cmd_rd_len_3; + t_u8 io_port_0_reg; + t_u8 io_port_1_reg; + t_u8 io_port_2_reg; + t_u8 host_int_rsr_reg; + t_u8 host_int_mask_reg; + t_u8 host_int_status_reg; + t_u8 host_restart_reg; + t_u8 card_to_host_event_reg; + t_u8 host_interrupt_mask_reg; + t_u8 card_interrupt_status_reg; + t_u8 card_interrupt_rsr_reg; + t_u8 card_revision_reg; + t_u8 card_ocr_0_reg; + t_u8 card_ocr_1_reg; + t_u8 card_ocr_3_reg; + t_u8 card_config_reg; + t_u8 card_misc_cfg_reg; + t_u8 debug_0_reg; + t_u8 debug_1_reg; + t_u8 debug_2_reg; + t_u8 debug_3_reg; + t_u32 fw_reset_reg; + t_u8 fw_reset_val; + t_u8 fw_dnld_offset_0_reg; + t_u8 fw_dnld_offset_1_reg; + t_u8 fw_dnld_offset_2_reg; + t_u8 fw_dnld_offset_3_reg; + t_u8 fw_dnld_status_0_reg; + t_u8 fw_dnld_status_1_reg; + t_u8 winner_check_reg; +} mlan_sdio_card_reg, *pmlan_sdio_card_reg; + +typedef struct _mlan_sdio_card { + const mlan_sdio_card_reg *reg; + + /** IO port */ + t_u32 ioport; + /** number of interrupt receive */ + t_u32 num_of_irq; + /** max SDIO single port tx size */ + t_u16 max_sp_tx_size; + /** max SDIO single port rx size */ + t_u16 max_sp_rx_size; + /** SDIO multiple port read bitmap */ + t_u32 mp_rd_bitmap; + /** SDIO multiple port write bitmap */ + t_u32 mp_wr_bitmap; + /** SDIO end port from txbufcfg */ + t_u16 mp_end_port; + /** SDIO port mask calculated based on txbufcfg end port */ + t_u32 mp_data_port_mask; + /** Current available port for read */ + t_u8 curr_rd_port; + /** Current available port for write */ + t_u8 curr_wr_port; + /** FW update port number */ + t_u32 mp_update[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX * 2]; + /** Invalid port update count */ + t_u32 mp_invalid_update; + /** Array to store values of SDIO multiple port group registers */ + t_u8 *mp_regs; + /** allocated buf to read SDIO multiple port group registers */ + t_u8 *mp_regs_buf; + /** buffer to handle receive packet */ + t_u8 *rx_buf; + /** allocated buf for receive */ + t_u8 *rx_buffer; + /* see blk_queue_max_segment_size */ + t_u32 max_seg_size; + /* see blk_queue_max_segments */ + t_u16 max_segs; + + /** data structure for SDIO MPA TX */ + sdio_mpa_tx mpa_tx; + /** packet number for tx aggr */ + t_u32 mpa_tx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** no more packets count*/ + t_u32 mpa_sent_last_pkt; + /** no write_ports count */ + t_u32 mpa_sent_no_ports; + /** last wr_bitmap from FW */ + t_u32 last_recv_wr_bitmap; + /** last mp_wr_bitmap */ + t_u32 last_mp_wr_bitmap[SDIO_MP_DBG_NUM]; + /** last ports for cmd53 write data */ + t_u32 last_mp_wr_ports[SDIO_MP_DBG_NUM]; + /** last length for cmd53 write data */ + t_u32 last_mp_wr_len[SDIO_MP_DBG_NUM]; + /** length info for cmd53 write data */ + t_u16 last_mp_wr_info[SDIO_MP_DBG_NUM * SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** last curr_wr_port */ + t_u8 last_curr_wr_port[SDIO_MP_DBG_NUM]; + + /** buffer for mp debug */ + t_u8 *mpa_buf; + /** length info for mp buf size */ + t_u32 mpa_buf_size; + + /** last mp_index */ + t_u8 last_mp_index; + + /** data structure for SDIO MPA RX */ + sdio_mpa_rx mpa_rx; + /** packet number for tx aggr */ + t_u32 mpa_rx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + + /** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ + t_u32 int_mode; + /** GPIO interrupt pin number */ + t_u32 gpio_pin; + + /** flag for sdio rx aggr */ + t_bool sdio_rx_aggr_enable; + /** fw rx block size */ + t_u16 sdio_rx_block_size; +} mlan_sdio_card, *pmlan_sdio_card; +#endif + +#ifdef PCIE +/** 8 Event buffer ring */ +#define MLAN_MAX_EVT_BD 0x08 +/** 32 entry will mapping to 5*/ +#define TX_RX_NUM_DESC 5 +/** 8 entry will mapping to 3 */ +#define EVT_NUM_DESC 3 +typedef struct _mlan_pcie_card_reg { + /* TX buffer description rd pointer */ + t_u32 reg_txbd_rdptr; + /* TX buffer description wr pointer */ + t_u32 reg_txbd_wrptr; + /* RX buffer description rd pointer */ + t_u32 reg_rxbd_rdptr; + /* RX buffer description wr pointer */ + t_u32 reg_rxbd_wrptr; + /** evtbd rdptr register */ + t_u32 reg_evtbd_rdptr; + /** evtbd wrptr register */ + t_u32 reg_evtbd_wrptr; + /** host int mask register */ + t_u16 reg_host_int_mask; + /** host int status mask register*/ + t_u16 reg_host_int_status_mask; + /** host int status register */ + t_u16 reg_host_int_status; + /** host int status clr selection */ + t_u32 reg_host_int_clr_sel; + /** cpu int event register */ + t_u16 reg_cpu_int_event; + /** ip revision register */ + t_u16 reg_ip_rev; + /** revision id register */ + t_u32 reg_rev_id; + /** driver ready register */ + t_u16 reg_drv_ready; + /** cpu int status register */ + t_u16 reg_cpu_int_status; + /** scratch 0 register */ + t_u16 reg_scratch_0; + /** scratch 1 register */ + t_u16 reg_scratch_1; + /** scratch 2 register */ + t_u16 reg_scratch_2; + /** scratch 3 register */ + t_u16 reg_scratch_3; + /** scratch 6 register */ + t_u16 reg_scratch_6; + /** scratch 7 register */ + t_u16 reg_scratch_7; + /** host interrupt mask*/ + t_u32 host_intr_mask; + /** data send interrupt for host*/ + t_u32 host_intr_dnld_done; + /** Data receive interrupt for host */ + t_u32 host_intr_upld_rdy; + /** Command sent interrupt for host */ + t_u32 host_intr_cmd_done; + /** Event ready interrupt for host */ + t_u32 host_intr_event_rdy; + t_u32 host_intr_cmd_dnld; + /* TX/RX buffer description mask */ + t_u32 txrx_rw_ptr_mask; + /* TX/RX buffer description wrap mask */ + t_u32 txrx_rw_ptr_wrap_mask; + /* TX/RX buffer description indication */ + t_u32 txrx_rw_ptr_rollover_ind; + /** ADMA feature */ + t_u8 use_adma; + /** write to clear interrupt status flag */ + t_u8 msi_int_wr_clr; +} mlan_pcie_card_reg, *pmlan_pcie_card_reg; + +typedef struct _mlan_pcie_card { + const mlan_pcie_card_reg *reg; + /** PCIE interrupt modes 0: Legacy, 1: MSI, 2:MSI-X */ + t_u32 pcie_int_mode; + /** PCIE function number */ + t_u8 func_num; + /** pending num of tx ring buffer in firmware */ + t_u8 txbd_pending; + /** Write pointer for TXBD ring */ + t_u32 txbd_wrptr; + /** Shadow copy of TXBD read pointer */ + t_u32 txbd_rdptr; + /** TXBD ring size */ + t_u32 txbd_ring_size; + /** Lock for protecting the TX ring */ + t_void *tx_ring_lock; + /** Virtual base address of txbd_ring */ + t_u8 *txbd_ring_vbase; + /** Physical base address of txbd_ring */ + t_u64 txbd_ring_pbase; + /** Ring of buffer descriptors for TX */ + t_void *txbd_ring[MLAN_MAX_TXRX_BD]; + /** A list of mlan_buffer objects used for data tx */ + mlan_buffer *tx_buf_list[MLAN_MAX_TXRX_BD]; + /** Flush indicator for txbd_ring */ + t_bool txbd_flush; + + /** Shadow copy of RXBD write pointer */ + t_u32 rxbd_wrptr; + /** RxBD read pointer */ + t_u32 rxbd_rdptr; + /** RXBD ring size */ + t_u32 rxbd_ring_size; + /** A spinlock for rxbd_ring */ + t_void *rx_ring_lock; + /** Virtual base address of rxbd_ring */ + t_u8 *rxbd_ring_vbase; + /** Physical base address of rxbd_ring */ + t_u64 rxbd_ring_pbase; + /** Ring of buffer descriptors for RX */ + t_void *rxbd_ring[MLAN_MAX_TXRX_BD]; + /** A list of mlan_buffer objects used for data rx */ + mlan_buffer *rx_buf_list[MLAN_MAX_TXRX_BD]; + + /** Shadow copy of cmdrsp/evt write pointer */ + t_u32 evtbd_wrptr; + /** Read pointer for cmdrsp/evt ring */ + t_u32 evtbd_rdptr; + /** Size of the cmdrsp/evt ring */ + t_u32 evtbd_ring_size; + /** Virtual base address of evtbd_bd_ring */ + t_u8 *evtbd_ring_vbase; + /** Physical base address of evtbd_bd_ring */ + t_u64 evtbd_ring_pbase; + /** Ring of buffer descriptors for EVENT */ + t_void *evtbd_ring[MLAN_MAX_EVT_BD]; + /** A list of mlan_buffer objects used for EVENT */ + mlan_buffer *evt_buf_list[MLAN_MAX_EVT_BD]; + + /** Command buffer */ + mlan_buffer *cmd_buf; + /** Command response buffer */ + mlan_buffer *cmdrsp_buf; + /** Command buffer */ + mlan_buffer *vdll_cmd_buf; + /** last tx_pkt_size */ + t_u32 last_tx_pkt_size[MLAN_MAX_TXRX_BD]; +} mlan_pcie_card, *pmlan_pcie_card; +#endif + +#ifdef USB +typedef struct _mlan_usb_card { + /** data structure for USB Rx Deaggregation */ + usb_rx_deaggr_params usb_rx_deaggr; + /** data structure for USB Tx Aggregation */ + usb_tx_aggr_params usb_tx_aggr[MAX_USB_TX_PORT_NUM]; + /** USB sggregation supported by FW */ + t_u8 fw_usb_aggr; + +} mlan_usb_card, *pmlan_usb_card; + +#endif + +typedef struct _mlan_card_info { + /** Max Tx buffer size */ + t_u32 max_tx_buf_size; + /** support V16_FW_API */ + t_u8 v16_fw_api; + /** support V17_FW_API */ + t_u8 v17_fw_api; + /** suppress PS handshake */ + t_u8 supp_ps_handshake; + /** DEFAULT_11N_TX_BF_CAP */ + t_u32 default_11n_tx_bf_cap; +} mlan_card_info, *pmlan_card_info; + +typedef struct _mlan_adapter mlan_adapter, *pmlan_adapter; + +/**Adapter_operations data structure*/ +typedef struct _adapter_operations { + /**firmware download handler*/ + mlan_status (*dnld_fw)(pmlan_adapter pmadapter, pmlan_fw_image pmfw); + /**interrupt handler*/ + mlan_status (*interrupt)(t_u16 msg_id, pmlan_adapter pmadapter); + /**INT process handler*/ + mlan_status (*process_int_status)(pmlan_adapter pmadapter); + /**host to card handler*/ + mlan_status (*host_to_card)(pmlan_private pmpriv, t_u8 type, + mlan_buffer *pmbuf, + mlan_tx_param *tx_param); + /*wakeup card*/ + mlan_status (*wakeup_card)(pmlan_adapter pmadapter, t_u8 timeout); + /*reset the PM setting of card*/ + mlan_status (*reset_card)(pmlan_adapter adapter); + /** Handle event/cmd complete*/ + mlan_status (*event_complete)(mlan_adapter *pmlan_adapter, + pmlan_buffer pmbuf, mlan_status status); + /** Handle complete receiving data */ + mlan_status (*data_complete)(mlan_adapter *pmlan_adapter, + pmlan_buffer pmbuf, mlan_status status); + /** Handle command response complete */ + mlan_status (*cmdrsp_complete)(mlan_adapter *pmlan_adapter, + pmlan_buffer pmbuf, mlan_status status); + /** Handle rx packet */ + mlan_status (*handle_rx_packet)(mlan_adapter *pmadapter, + pmlan_buffer pmbuf); + /** handle dump interface specific info */ + mlan_status (*debug_dump)(mlan_adapter *pmadapter); + /**Interface header length*/ + t_u32 intf_header_len; +} mlan_adapter_operations; + +/** Adapter data structure for MLAN */ +typedef struct _mlan_adapter { + /** MOAL handle structure */ + t_void *pmoal_handle; + /** BSS Attributes */ + mlan_bss_attr bss_attr[MLAN_MAX_BSS_NUM]; + /** Private pointer */ + pmlan_private priv[MLAN_MAX_BSS_NUM]; + /** Total number of Priv number */ + t_u8 priv_num; + /** Priority table for bss */ + mlan_bssprio_tbl bssprio_tbl[MLAN_MAX_BSS_NUM]; + /** Callback table */ + mlan_callbacks callbacks; + /** Init parameters */ + mlan_init_para init_para; + /** mlan_lock for init/shutdown */ + t_void *pmlan_lock; + /** main_proc_lock for main_process */ + t_void *pmain_proc_lock; + /** mlan_processing */ + t_u32 mlan_processing; + /** main_process_cnt */ + t_u32 main_process_cnt; + /** mlan_rx_processing */ + t_u32 mlan_rx_processing; + /** rx_proc_lock for main_rx_process */ + t_void *prx_proc_lock; + /** more_rx_task_flag */ + t_u32 more_rx_task_flag; + /** rx work enable flag */ + t_u8 rx_work_flag; + /* number of rx pkts queued */ + t_u16 rx_pkts_queued; + /** more task flag */ + t_u32 more_task_flag; + /** delay task flag */ + t_u32 delay_task_flag; + /** Max tx buf size */ + t_u16 max_tx_buf_size; + /** Tx buf size */ + t_u16 tx_buf_size; + /** current tx buf size in fw */ + t_u16 curr_tx_buf_size; + /** flush data flag */ + t_u8 flush_data; + /** STATUS variables */ + WLAN_HARDWARE_STATUS hw_status; + /** PnP SUPPORT */ + t_u8 surprise_removed; + /** FW hang report */ + t_u8 fw_hang_report; + + /** ECSA support */ + t_u8 ecsa_enable; + + /** Get log support */ + t_u8 getlog_enable; + + /** Radio on flag */ + t_u16 radio_on; + + /** Firmware release number */ + t_u32 fw_release_number; + /** firmware version */ + t_u8 fw_ver; + /** firmware minor version */ + t_u8 fw_min_ver; + /** uap firmware version */ + t_u8 uap_fw_ver; + /** mac address retrun from get_hw_spec */ + t_u8 permanent_addr[MLAN_MAC_ADDR_LENGTH]; + /** Number of antenna used */ + t_u16 number_of_antenna; + /** antenna info */ + t_u8 antinfo; + /** Firmware capability information */ + t_u32 fw_cap_info; + /** Extended firmware capability information */ + t_u32 fw_cap_ext; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + /** High byte for 5G, low byte for 2G, like 0x2211 0x22 for 5G, 0x11 for + * 2G */ + t_u16 user_htstream; +#endif + /** vdll ctrl */ + vdll_dnld_ctrl vdll_ctrl; +#if defined(SDIO) || defined(PCIE) + /** pint_lock for interrupt handling */ + t_void *pint_lock; + /** Interrupt status */ + t_u32 ireg; +#endif + /** card type */ + t_u16 card_type; + /** card rev */ + t_u8 card_rev; + const mlan_card_info *pcard_info; +#ifdef SDIO + pmlan_sdio_card pcard_sd; +#endif +#ifdef PCIE + pmlan_pcie_card pcard_pcie; +#endif +#ifdef USB + pmlan_usb_card pcard_usb; +#endif + + /** Event cause */ + t_u32 event_cause; + /** Event buffer */ + pmlan_buffer pmlan_buffer_event; + /** Upload length */ + t_u32 upld_len; + /** Upload buffer*/ + t_u8 upld_buf[WLAN_UPLD_SIZE]; + /** Data sent: + * TRUE - Data is sent to fw, no Tx Done received + * FALSE - Tx done received for previous Tx + */ + t_u8 data_sent; + /** CMD sent: + * TRUE - CMD is sent to fw, no CMD Done received + * FALSE - CMD done received for previous CMD + */ + t_u8 cmd_sent; + /** CMD Response received: + * TRUE - CMD is response is received from fw, and yet to process + * FALSE - No cmd response to process + */ + t_u8 cmd_resp_received; + /** Event received: + * TRUE - Event received from fw, and yet to process + * FALSE - No events to process + */ + t_u8 event_received; + + /** Data received: + * TRUE - Data received from fw + * FALSE - No Data received + */ + t_u8 data_received; + + /** Command-related variables */ + /** Command sequence number */ + t_u16 seq_num; + /** Command controller nodes */ + cmd_ctrl_node *cmd_pool; + /** Current Command */ + cmd_ctrl_node *curr_cmd; + /** mlan_lock for command */ + t_void *pmlan_cmd_lock; + /** Number of command timeouts */ + t_u32 num_cmd_timeout; + /** Last init fw command id */ + t_u16 last_init_cmd; + /** Command timer */ + t_void *pmlan_cmd_timer; + /** Command timer set flag */ + t_u8 cmd_timer_is_set; + /** time stamp for command dnld */ + t_u32 dnld_cmd_in_secs; + + /** Command Queues */ + /** Free command buffers */ + mlan_list_head cmd_free_q; + /** Pending command buffers */ + mlan_list_head cmd_pending_q; + /** Command queue for scanning */ + mlan_list_head scan_pending_q; + /** ioctl pending queue */ + mlan_list_head ioctl_pending_q; + /** pending_ioctl flag */ + t_u8 pending_ioctl; + pmlan_private pending_disconnect_priv; + /** mlan_processing */ + t_u32 scan_processing; + /** ext_scan enh support flag */ + t_u8 ext_scan_enh; + /** scan type: 0 legacy, 1: enhance scan*/ + t_u8 ext_scan_type; + /** ext scan timeout */ + t_u8 ext_scan_timeout; + /** coex scan flag */ + t_u8 coex_scan; + /** coex min scan time */ + t_u8 coex_min_scan_time; + /** coex max scan time */ + t_u8 coex_max_scan_time; + /** coex win size flag */ + t_u8 coex_win_size; + /** coex amdpdu tx win size */ + t_u8 coex_tx_win_size; + /** coex ampdu rx win size */ + t_u8 coex_rx_win_size; + /** Region code */ + t_u16 region_code; + /** Region Channel data */ + region_chan_t region_channel[MAX_REGION_CHANNEL_NUM]; + /** CFP table code for 2.4GHz */ + t_u8 cfp_code_bg; + /** CFP table code for 5GHz */ + t_u8 cfp_code_a; + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; + /** Minimum BA Threshold */ + t_u8 min_ba_threshold; +#ifdef STA_SUPPORT + /** Universal Channel data */ + region_chan_t universal_channel[MAX_REGION_CHANNEL_NUM]; + /** Parsed region channel */ + parsed_region_chan_11d_t parsed_region_chan; +#endif /* STA_SUPPORT */ + /** 11D and Domain Regulatory Data */ + wlan_802_11d_domain_reg_t domain_reg; + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** FSM variable for 11h support */ + wlan_11h_device_state_t state_11h; + /** FSM variable for DFS support */ + wlan_dfs_device_state_t state_dfs; + /** FSM variable for RDH support */ + wlan_radar_det_hndlg_state_t state_rdh; + /** variable to configure dfs channel switch count */ + t_s8 dfs_cs_count; + /** User configured settings for DFS testing */ + wlan_dfs_testing_settings_t dfs_test_params; + /** dfs w53 cfg */ + t_u8 dfs53cfg; + /** FSM variable for MEAS support */ + wlan_meas_state_t state_meas; + /** Scan table */ + BSSDescriptor_t *pscan_table; + /** scan age in secs */ + t_u32 age_in_secs; + /** Active scan for hidden ssid triggered */ + t_u8 active_scan_triggered; + /** channel statstics */ + ChanStatistics_t *pchan_stats; + /** Number of records in the chan_stats */ + t_u32 num_in_chan_stats; + /** index of chan stats */ + t_u32 idx_chan_stats; + t_u8 bgscan_reported; + + /** Number of records in the scan table */ + t_u32 num_in_scan_table; + /** Scan probes */ + t_u16 scan_probes; + + /** Scan type */ + t_u8 scan_type; + /** Scan mode */ + t_u32 scan_mode; + /** Specific scan time */ + t_u16 specific_scan_time; + /** Active scan time */ + t_u16 active_scan_time; + /** Passive scan time */ + t_u16 passive_scan_time; + /** Passive scan to active scan */ + t_u8 passive_to_active_scan; + /** scan channel gap time */ + t_u16 scan_chan_gap; + /** Scan block flag */ + t_u8 scan_block; + /** Extended scan or legacy scan */ + t_u8 ext_scan; + t_u16 bcn_buf_size; + /** Beacon buffer */ + t_u8 *bcn_buf; + /** Pointer to valid beacon buffer end */ + t_u8 *pbcn_buf_end; + /** allocate fixed scan beacon buffer size*/ + t_u32 fixed_beacon_buffer; + + /** F/W supported bands */ + t_u16 fw_bands; + /** User selected band to start adhoc network */ + t_u16 adhoc_start_band; + /** User selected bands */ + t_u16 config_bands; + /** Pointer to channel list last sent to the firmware for scanning */ + ChanScanParamSet_t *pscan_channels; + + /** Tx lock flag */ + t_u8 tx_lock_flag; + /** Rx lock flag */ + t_u8 rx_lock_flag; + /** main lock flag */ + t_u8 main_lock_flag; +#ifdef USB + /** Tx CMD endpoint address */ + t_u8 tx_cmd_ep; + /** Rx CMD/EVT endpoint address */ + t_u8 rx_cmd_ep; + /** Rx data endpoint address */ + t_u8 rx_data_ep; + /** Tx data endpoint address */ + t_u8 tx_data_ep; +#endif + + /** 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; + /** keep_wakeup */ + t_u8 keep_wakeup; + + /** Multiple DTIM */ + t_u16 multiple_dtim; + /** Local listen interval */ + t_u16 local_listen_interval; + /** Null packet interval */ + t_u16 null_pkt_interval; + + /** IEEE ps inactivity timout value */ + t_u16 inact_tmo; + /** Power save confirm sleep command buffer */ + pmlan_buffer psleep_cfm; + /** Beacon miss timeout */ + t_u16 bcn_miss_time_out; + + /** Deep Sleep flag */ + t_u8 is_deep_sleep; + /** Idle time */ + t_u16 idle_time; + /** Auto Deep Sleep enabled at init time */ + t_u8 init_auto_ds; + + /** delay null pkt flag */ + t_u8 delay_null_pkt; + /** Delay to PS in milliseconds */ + t_u16 delay_to_ps; + /** Enhanced PS mode */ + t_u16 enhanced_ps_mode; + /** Device wakeup required flag */ + t_u8 pm_wakeup_card_req; + + /** Gen NULL pkg */ + t_u16 gen_null_pkt; + + /** PPS/UAPSD mode flag */ + t_u16 pps_uapsd_mode; + /** Number of wakeup tries */ + t_u32 pm_wakeup_fw_try; + /** time stamp when host try to wake up firmware */ + t_u32 pm_wakeup_in_secs; + /** Card wakeup timer */ + t_void *pwakeup_fw_timer; + /** Card wakeup timer */ + t_u8 wakeup_fw_timer_is_set; + /** Number of wake up timeouts */ + t_u32 pm_wakeup_timeout; + + /** 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; + /** mef_flt_cfg_mef configuration */ + mef_entry entry_cfg; + /** Event body */ + t_u8 event_body[MAX_EVENT_SIZE]; + /** 802.11n device capabilities */ + t_u32 hw_dot_11n_dev_cap; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; +#ifdef STA_SUPPORT + /** Adhoc Secondary Channel Bandwidth */ + t_u8 chan_bandwidth; +#endif /* STA_SUPPORT */ + + /** 802.11ac device capabilities */ + t_u32 hw_dot_11ac_dev_cap; + /** 802.11ac device support for MIMO abstraction of MCSs */ + t_u32 hw_dot_11ac_mcs_support; + /** length of hw he capability */ + t_u8 hw_hecap_len; + /** 802.11ax HE capability */ + t_u8 hw_he_cap[54]; + /** length of hw 2.4G he capability */ + t_u8 hw_2g_hecap_len; + /** 802.11ax 2.4G HE capability */ + t_u8 hw_2g_he_cap[54]; + /** max mgmt IE index in device */ + t_u16 max_mgmt_ie_index; + /** Head of Rx data queue */ + mlan_list_head rx_data_queue; +#ifdef MFG_CMD_SUPPORT + t_u32 mfg_mode; +#endif + /** Debug */ + wlan_dbg dbg; + + /** RX pending for forwarding packets */ + mlan_scalar pending_bridge_pkts; + + /** Minimum delay between HsActive and HostWake (in msec) */ + t_u16 min_wake_holdoff; + /** Host sleep wake interval(in msec) */ + t_u32 hs_wake_interval; + /** Host sleep inactivity timeout (in msec) */ + t_u32 hs_inactivity_timeout; + /** Parameter type for indication gpio*/ + t_u8 param_type_ind; + /** GPIO pin for indication wakeup source */ + t_u32 ind_gpio; + /** Level on ind_gpio pin for indication normal wakeup source */ + t_u32 level; + /** Parameter type for extend hscfg*/ + t_u8 param_type_ext; + /** Events that will be forced ignore */ + t_u32 event_force_ignore; + /** Events that will use extend gap to inform host*/ + t_u32 event_use_ext_gap; + /** Extend gap*/ + t_u8 ext_gap; + /** GPIO wave level for extend hscfg */ + t_u8 gpio_wave; + /** Dynamic MIMO-SISO switch for hscfg*/ + t_u8 hs_mimo_switch; + /** management frame wakeup filter config */ + mlan_mgmt_frame_wakeup mgmt_filter[MAX_MGMT_FRAME_FILTER]; + /** Bypass TX queue pkt count */ + t_u16 bypass_pkt_count; +#ifdef STA_SUPPORT + /** warm-reset IOCTL request buffer pointer */ + pmlan_ioctl_req pwarm_reset_ioctl_req; +#endif + /** SCAN IOCTL request buffer pointer */ + pmlan_ioctl_req pscan_ioctl_req; + /** DPD data pointer */ + t_u8 *pdpd_data; + /** DPD data length */ + t_u32 dpd_data_len; + /** region txpowerlimit cfg data buf pointer */ + t_u8 *ptxpwr_data; + /** region txpowerlimit cfg data len */ + t_u32 txpwr_data_len; + /** Cal data pointer */ + t_u8 *pcal_data; + /** Cal data length */ + t_u32 cal_data_len; + /** Feature control bitmask */ + t_u32 feature_control; + + /** Control coex RX window size configuration */ + t_u8 coex_rx_winsize; + t_bool dfs_repeater; + t_u32 dfsr_channel; + t_u8 chanrpt_param_bandcfg; +#if defined(PCIE) + mlan_buffer *ssu_buf; +#endif + /** maximum sta connection */ + t_u8 max_sta_conn; + otp_region_info_t *otp_region; + chan_freq_power_t *cfp_otp_bg; + t_u8 *tx_power_table_bg; + t_u32 tx_power_table_bg_size; + t_u8 tx_power_table_bg_rows; + t_u8 tx_power_table_bg_cols; + chan_freq_power_t *cfp_otp_a; + t_u8 *tx_power_table_a; + t_u32 tx_power_table_a_size; + t_u8 tx_power_table_a_rows; + t_u8 tx_power_table_a_cols; + /**mlan adapter operations*/ + mlan_adapter_operations ops; +#ifdef DRV_EMBEDDED_AUTHENTICATOR + /** authenticator_priv */ + pmlan_private authenticator_priv; +#endif + /** TP accounting mode 1-enable 0-disable */ + t_u32 tp_state_on; + /** Packet drop point */ + t_u32 tp_state_drop_point; +} mlan_adapter, *pmlan_adapter; + +/** Check if stream 2X2 enabled */ +#define IS_STREAM_2X2(x) ((x)&FEATURE_CTRL_STREAM_2X2) +/** Check if DFS support enabled */ +#define IS_DFS_SUPPORT(x) ((x)&FEATURE_CTRL_DFS_SUPPORT) +#ifdef USB +/** Check if winner check & not wait for FW ready event */ +#define IS_USB_NEW_INIT(x) ((x)&FEATURE_CTRL_USB_NEW_INIT) +#endif + +/** Ethernet packet type for EAPOL */ +#define MLAN_ETHER_PKT_TYPE_EAPOL (0x888E) +#define MLAN_ETHER_PKT_TYPE_ARP (0x0806) +/** 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(pmlan_adapter pmadapter); +mlan_status wlan_init_priv_lock_list(pmlan_adapter pmadapter, t_u8 start_index); +t_void wlan_free_lock_list(pmlan_adapter pmadapter); +mlan_status wlan_init_timer(pmlan_adapter pmadapter); +t_void wlan_free_timer(pmlan_adapter pmadapter); + +/* Function prototype */ +/** Initialize firmware */ +mlan_status wlan_init_fw(pmlan_adapter pmadapter); + +/** get hw spec complete */ +mlan_status wlan_get_hw_spec_complete(pmlan_adapter pmadapter); + +/** Initialize firmware complete */ +mlan_status wlan_init_fw_complete(pmlan_adapter pmadapter); + +/** Shutdown firmware complete */ +mlan_status wlan_shutdown_fw_complete(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(pmlan_adapter pmadapter); + +/** Initialize mlan_private structure */ +mlan_status wlan_init_priv(pmlan_private priv); + +mlan_status wlan_download_vdll_block(mlan_adapter *pmadapter, t_u8 *block, + t_u16 block_len); +mlan_status wlan_process_vdll_event(pmlan_private pmpriv, pmlan_buffer pevent); +/** Process event */ +mlan_status wlan_process_event(pmlan_adapter pmadapter); + +/** Prepare command */ +mlan_status wlan_prepare_cmd(pmlan_private priv, t_u16 cmd_no, t_u16 cmd_action, + t_u32 cmd_oid, t_void *pioctl_buf, + t_void *pdata_buf); + +/** cmd timeout handler */ +t_void wlan_cmd_timeout_func(t_void *function_context); + +/** + * @brief check if Tx pending + * + * @param pmadapter Pointer to mlan_adapter + * @return MTRUE/MFALSE; + */ +static inline t_u8 wlan_is_tx_pending(mlan_adapter *pmadapter) +{ +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type) && + pmadapter->pcard_pcie->txbd_pending) + return MTRUE; +#endif + return MFALSE; +} + +/** process host cmd */ +mlan_status wlan_misc_ioctl_host_cmd(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +/** process init/shutdown cmd*/ +mlan_status wlan_misc_ioctl_init_shutdown(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +/** process debug info */ +mlan_status wlan_get_info_debug_info(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** Set/Get BSS role */ +mlan_status wlan_bss_ioctl_bss_role(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +#endif + +#if defined(PCIE) +mlan_status wlan_misc_ssu(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req); +#endif + +mlan_status wlan_set_ewpa_mode(mlan_private *priv, pmlan_ds_passphrase psec_pp); +mlan_status wlan_find_bss(mlan_private *pmpriv, pmlan_ioctl_req pioctl_req); + +/* block main process */ +void mlan_block_main_process(mlan_adapter *pmadapter, t_u8 block); +/* block rx process */ +void mlan_block_rx_process(mlan_adapter *pmadapter, t_u8 block); +/** check pending command */ +int wlan_check_pending_cmd(mlan_adapter *pmadapter); +/** Allocate memory for adapter structure members */ +mlan_status wlan_allocate_adapter(pmlan_adapter pmadapter); +/** Free adapter */ +t_void wlan_free_adapter(pmlan_adapter pmadapter); +/** Free priv */ +t_void wlan_free_priv(mlan_private *pmpriv); +/** Allocate command buffer */ +mlan_status wlan_alloc_cmd_buffer(mlan_adapter *pmadapter); +/** Free command buffer */ +mlan_status wlan_free_cmd_buffer(mlan_adapter *pmadapter); +/** Request command lock */ +t_void wlan_request_cmd_lock(mlan_adapter *pmadapter); +/** Release command lock */ +t_void wlan_release_cmd_lock(mlan_adapter *pmadapter); +#ifdef STA_SUPPORT +/** Flush the scan pending queue */ +t_void wlan_flush_scan_queue(pmlan_adapter pmadapter); +mlan_status wlan_cancel_pending_scan_cmd(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +#endif +/**Cancel pending command */ +t_void wlan_cancel_all_pending_cmd(pmlan_adapter pmadapter, t_u8 flag); +/**Cancel pending ioctl */ +t_void wlan_cancel_pending_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +/**Cancel bss pending ioctl */ +t_void wlan_cancel_bss_pending_cmd(pmlan_adapter pmadapter, t_u32 bss_index); + +/** Insert command to free queue */ +t_void wlan_insert_cmd_to_free_q(mlan_adapter *pmadapter, + cmd_ctrl_node *pcmd_node); + +/** Insert command to pending queue */ +t_void wlan_insert_cmd_to_pending_q(mlan_adapter *pmadapter, + cmd_ctrl_node *pcmd_node, 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); + +#ifdef SDIO +mlan_status wlan_alloc_sdio_mpa_buffers(mlan_adapter *pmadapter, + t_u32 mpa_tx_buf_size, + t_u32 mpa_rx_buf_size); + +mlan_status wlan_free_sdio_mpa_buffers(mlan_adapter *pmadapter); +#endif + +/** Process write data complete */ +mlan_status wlan_write_data_complete(pmlan_adapter pmlan_adapter, + pmlan_buffer pmbuf, mlan_status status); + +#ifdef USB +mlan_status wlan_usb_deaggr_rx_pkt(pmlan_adapter pmadapter, pmlan_buffer pmbuf); + +/** + * @brief This function resets USB Tx Aggregation buffers + * + * @param pmadapter A pointer to mlan_adapter + * + * @return N/A + */ +static INLINE t_void wlan_reset_usb_tx_aggr(pmlan_adapter pmadapter) +{ + t_s32 i = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) { + pcb->moal_spin_lock( + pmadapter->pmoal_handle, + pmadapter->pcard_usb->usb_tx_aggr[i].paggr_lock); + if (pmadapter->pcard_usb->usb_tx_aggr[i].aggr_hold_timer_is_set) { + pcb->moal_stop_timer(pmadapter->pmoal_handle, + pmadapter->pcard_usb + ->usb_tx_aggr[i] + .paggr_hold_timer); + pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_hold_timer_is_set = MFALSE; + } + if (pmadapter->pcard_usb->usb_tx_aggr[i].aggr_ctrl.enable && + pmadapter->pcard_usb->usb_tx_aggr[i].pmbuf_aggr != MNULL) { + wlan_write_data_complete( + pmadapter, + pmadapter->pcard_usb->usb_tx_aggr[i].pmbuf_aggr, + MLAN_STATUS_FAILURE); /* did not get sent */ + pmadapter->pcard_usb->usb_tx_aggr[i].pmbuf_aggr = MNULL; + pmadapter->pcard_usb->usb_tx_aggr[i].aggr_len = 0; + } + pcb->moal_spin_unlock( + pmadapter->pmoal_handle, + pmadapter->pcard_usb->usb_tx_aggr[i].paggr_lock); + } + LEAVE(); +} + +/** + * @brief This function get usb_tx_aggr_params + * + * @param pmadapter A pointer to mlan_adapter + * @param port port for TX + * + * @return A pointer to usb_tx_aggr_params + */ +static INLINE usb_tx_aggr_params * +wlan_get_usb_tx_aggr_params(pmlan_adapter pmadapter, t_u32 port) +{ + int i; + ENTER(); + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) { + if (pmadapter->pcard_usb->usb_tx_aggr[i].aggr_ctrl.enable && + pmadapter->pcard_usb->usb_tx_aggr[i].port == port) + return &pmadapter->pcard_usb->usb_tx_aggr[i]; + } + LEAVE(); + return MNULL; +} + +t_void wlan_usb_tx_aggr_timeout_func(t_void *function_context); +mlan_status wlan_usb_host_to_card_aggr(pmlan_adapter pmadapter, + pmlan_buffer pmbuf, + mlan_tx_param *tx_param, + usb_tx_aggr_params *aggr_params); +#endif + +/** Process receive packet complete */ +mlan_status wlan_recv_packet_complete(pmlan_adapter pmadapter, + pmlan_buffer pmbuf, mlan_status status); +/** Clean Tx Rx queues */ +t_void wlan_clean_txrx(pmlan_private priv); + +t_void wlan_add_buf_bypass_txqueue(mlan_adapter *pmadapter, pmlan_buffer pmbuf); +t_void wlan_process_bypass_tx(mlan_adapter *pmadapter); +t_void wlan_cleanup_bypass_txq(pmlan_private priv); +t_u8 wlan_bypass_tx_list_empty(mlan_adapter *pmadapter); + +/** Check if this is the last packet */ +t_u8 wlan_check_last_packet_indication(pmlan_private priv); + +#define MOAL_ALLOC_MLAN_BUFFER (0) +#define MOAL_MALLOC_BUFFER (1) + +#ifdef PCIE +/* This defines the direction arg to the DMA mapping routines. */ +#define PCI_DMA_BIDIRECTIONAL 0 +#define PCI_DMA_TODEVICE 1 +#define PCI_DMA_FROMDEVICE 2 +#define PCI_DMA_NONE 3 +#endif + +/** 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, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_u16 ps_bitmap, t_void *pdata_buf); +/** handle command resp for enhanced power save mode */ +mlan_status wlan_ret_enh_power_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +/** handle commnand for cfg data */ +mlan_status wlan_cmd_cfg_data(pmlan_private pmpriv, HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action, t_u32 cmd_oid, + t_void *pdata_buf); +/** handle command resp for cfg data */ +mlan_status wlan_ret_cfg_data(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + 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); + +t_void wlan_wakeup_card_timeout_func(void *function_context); + +mlan_status wlan_process_802dot11_mgmt_pkt(mlan_private *priv, t_u8 *payload, + t_u32 payload_len, RxPD *prx_pd); + +mlan_status wlan_pm_ioctl_hscfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_radio_ioctl_remain_chan_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_remain_on_channel(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_remain_on_channel(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +#ifdef WIFI_DIRECT_SUPPORT +mlan_status wlan_bss_ioctl_wifi_direct_mode(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_wifi_direct_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_ret_wifi_direct_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_cmd_p2p_params_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_p2p_params_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_misc_p2p_config(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +#endif +/** get ralist info */ +int wlan_get_ralist_info(mlan_private *priv, pralist_info buf); +/** dump ralist */ +void wlan_dump_ralist(mlan_private *priv); + +/** get pm info */ +mlan_status wlan_get_pm_info(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_bss_ioctl_bss_remove(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_radio_ioctl_mimo_switch_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_802_11_mimo_switch(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf); + +mlan_status wlan_misc_per_pkt_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_config_mgmt_filter(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_get_hs_wakeup_reason(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_hs_wakeup_reason(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf); + +mlan_status wlan_ret_hs_wakeup_reason(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_get_tx_rx_histogram(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_tx_rx_pkt_stats(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + pmlan_ioctl_req pioctl_buf, + t_void *pdata_buf); +mlan_status wlan_ret_tx_rx_pkt_stats(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_radio_ioctl_radio_ctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_radio_ioctl_ant_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_tx_rate_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_ret_tx_rate_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_rate_ioctl_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_ret_802_11_tx_rate_query(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_rate_ioctl_get_data_rate(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +t_void wlan_host_sleep_activated_event(pmlan_private priv, t_u8 activated); +/** Handles the command response of hs_cfg */ +mlan_status wlan_ret_802_11_hs_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +/** Sends HS_WAKEUP event to applications */ +t_void wlan_host_sleep_wakeup_event(pmlan_private priv); + +/** Prepares command of robustcoex */ +mlan_status wlan_cmd_robustcoex(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u16 *pdata_buf); +/** Set Robustcoex gpiocfg */ +mlan_status wlan_misc_robustcoex(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +/** Set mapping policy/get DMCS status */ +mlan_status wlan_misc_dmcs_config(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +/** Prepares command of DMCS config */ +mlan_status wlan_cmd_dmcs_config(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +/** Handles command response of DMCS config */ +mlan_status wlan_ret_dmcs_config(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +#if defined(PCIE) +mlan_status wlan_cmd_ssu(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u16 *pdata_buf); +#endif + +/** send get hw spec command to firmware */ +mlan_status wlan_adapter_get_hw_spec(pmlan_adapter pmadapter); +/** send adapter specific init cmd to firmware */ +mlan_status wlan_adapter_init_cmd(pmlan_adapter pmadapter); +/** get/set bandcfg */ +mlan_status wlan_radio_ioctl_band_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +#ifdef RX_PACKET_COALESCE +mlan_status wlan_cmd_rx_pkt_coalesce_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_rx_pkt_coalesce_cfg(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +#endif + +#ifdef STA_SUPPORT +/** warm reset */ +mlan_status wlan_misc_ioctl_warm_reset(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +/** Process received packet */ +mlan_status wlan_process_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf); +/** ioctl handler for station mode */ +mlan_status wlan_ops_sta_ioctl(t_void *adapter, pmlan_ioctl_req pioctl_req); + +/** cmd handler for station mode */ +mlan_status wlan_ops_sta_prepare_cmd(t_void *priv, t_u16 cmd_no, + t_u16 cmd_action, t_u32 cmd_oid, + t_void *pioctl_buf, t_void *pdata_buf, + t_void *pcmd_buf); + +/** cmdresp handler for station mode */ +mlan_status wlan_ops_sta_process_cmdresp(t_void *priv, t_u16 cmdresp_no, + t_void *pcmd_buf, t_void *pioctl); + +/** rx handler for station mode */ +mlan_status wlan_ops_sta_process_rx_packet(t_void *adapter, pmlan_buffer pmbuf); + +/** event handler for station mode */ +mlan_status wlan_ops_sta_process_event(t_void *priv); + +/** fill txpd for station mode */ +t_void *wlan_ops_sta_process_txpd(t_void *priv, pmlan_buffer pmbuf); + +/** send init cmd to firmware for station mode */ +mlan_status wlan_ops_sta_init_cmd(t_void *priv, t_u8 first_bss); + +/** Flush the scan table */ +mlan_status wlan_flush_scan_table(pmlan_adapter pmadapter); + +/** Scan for networks */ +mlan_status wlan_scan_networks(mlan_private *pmpriv, t_void *pioctl_buf, + wlan_user_scan_cfg *puser_scan_in); + +/** Scan for specific SSID */ +mlan_status wlan_scan_specific_ssid(mlan_private *pmpriv, t_void *pioctl_buf, + mlan_802_11_ssid *preq_ssid); + +/** Scan command handler */ +mlan_status wlan_cmd_802_11_scan(pmlan_private pmpriv, HostCmd_DS_COMMAND *pcmd, + t_void *pdata_buf); + +/** Handler for scan command response */ +mlan_status wlan_ret_802_11_scan(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + t_void *pioctl_buf); + +/** Extended scan command handler */ +mlan_status wlan_cmd_802_11_scan_ext(pmlan_private pmpriv, + HostCmd_DS_COMMAND *pcmd, + t_void *pdata_buf); +/** Handler for extended scan command response */ +mlan_status wlan_ret_802_11_scan_ext(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + t_void *pioctl_buf); +/** Handler event for extended scan report */ +mlan_status wlan_handle_event_ext_scan_report(mlan_private *pmpriv, + mlan_buffer *pmbuf); +mlan_status wlan_handle_event_ext_scan_status(mlan_private *pmpriv, + mlan_buffer *pmbuf); + +/** check network compatibility */ +t_s32 wlan_is_network_compatible(mlan_private *pmpriv, t_u32 index, t_u32 mode); + +/** Find an SSID in a list */ +t_s32 wlan_find_ssid_in_list(pmlan_private pmpriv, mlan_802_11_ssid *ssid, + t_u8 *bssid, t_u32 mode); + +/** Find a BSSID in a list */ +t_s32 wlan_find_bssid_in_list(mlan_private *pmpriv, t_u8 *bssid, t_u32 mode); + +/** Find best network */ +mlan_status wlan_find_best_network(mlan_private *pmpriv, + mlan_ssid_bssid *preq_ssid_bssid); + +/** Compare two SSIDs */ +t_s32 wlan_ssid_cmp(pmlan_adapter pmadapter, mlan_802_11_ssid *ssid1, + mlan_802_11_ssid *ssid2); + +/** Associate */ +mlan_status wlan_associate(mlan_private *pmpriv, IN t_void *pioctl_buf, + IN BSSDescriptor_t *pBSSDesc); + +/** Associate command handler */ +mlan_status wlan_cmd_802_11_associate(mlan_private *pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf); + +/** Handler for association command response */ +mlan_status wlan_ret_802_11_associate(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + t_void *pioctl_buf); + +/** Reset connected state */ +t_void wlan_reset_connect_state(pmlan_private priv, 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(t_u8 band); +/** convert radio_type to band */ +t_u8 radio_type_to_band(t_u8 chanBand); + +/** Disconnect */ +mlan_status wlan_disconnect(mlan_private *pmpriv, mlan_ioctl_req *pioctl_req, + mlan_deauth_param *deauth_param); + +/** Ad-Hoc start */ +mlan_status wlan_adhoc_start(mlan_private *pmpriv, t_void *pioctl_buf, + mlan_802_11_ssid *padhoc_ssid); + +/** Ad-Hoc join */ +mlan_status wlan_adhoc_join(mlan_private *pmpriv, t_void *pioctl_buf, + BSSDescriptor_t *pBSSDesc); + +/** Ad-Hoc start command handler */ +mlan_status wlan_cmd_802_11_ad_hoc_start(mlan_private *pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf); + +/** Ad-Hoc command handler */ +mlan_status wlan_cmd_802_11_ad_hoc_join(mlan_private *pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf); + +/** Handler for Ad-Hoc commands */ +mlan_status wlan_ret_802_11_ad_hoc(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + t_void *pioctl_buf); + +/** Handler for bgscan query commands */ +mlan_status wlan_cmd_802_11_bg_scan_query(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd, + t_void *pdata_buf); +/** Handler for bgscan config command */ +mlan_status wlan_cmd_bgscan_config(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd, t_void *pdata_buf); +/** Hander for bgscan config command response */ +mlan_status wlan_ret_bgscan_config(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_ret_802_11_bgscan_query(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + 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(mlan_private *pmpriv); +/* Free a beacon buffer of the current bss descriptor */ +t_void wlan_free_curr_bcn(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 rate_info, t_u8 ext_rate_info); +/** Get active data rates */ +t_u32 wlan_get_active_data_rates(mlan_private *pmpriv, t_u32 bss_mode, + t_u16 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_u16 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); +mlan_status wlan_cmd_rxabortcfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_rxabortcfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_cmd_rxabortcfg_ext(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_ret_rxabortcfg_ext(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_cmd_tx_ampdu_prot_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_tx_ampdu_prot_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_cmd_dot11mc_unassoc_ftm_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_ret_dot11mc_unassoc_ftm_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_rate_adapt_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_ret_rate_adapt_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_cmd_cck_desense_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); + +mlan_status wlan_ret_cck_desense_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_arb_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_arb_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_misc_ioctl_rxabortcfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_misc_ioctl_rxabortcfg_ext(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_misc_ioctl_tx_ampdu_prot_mode(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_misc_ioctl_dot11mc_unassoc_ftm_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_misc_ioctl_rate_adapt_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_misc_ioctl_cck_desense_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_mfg(pmlan_private pmpriv, pHostCmd_DS_COMMAND cmd, + t_u16 cmd_action, t_pvoid pdata_buf); +mlan_status wlan_ret_mfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_misc_ioctl_rf_test_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_misc_ioctl_range_ext(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_misc_ioctl_arb_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_misc_ioctl_tp_state(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +/* CFP related functions */ +/** Region code index table */ +extern t_u16 region_code_index[MRVDRV_MAX_REGION_CODE]; +/** The table to keep CFP code for BG */ +extern t_u16 cfp_code_index_bg[MRVDRV_MAX_CFP_CODE_BG]; +/** The table to keep CFP code for A */ +extern t_u16 cfp_code_index_a[MRVDRV_MAX_CFP_CODE_A]; + +/** Set region table */ +mlan_status wlan_set_regiontable(mlan_private *pmpriv, t_u8 region, t_u8 band); +/** Get radar detection requirements*/ +t_bool wlan_get_cfp_radar_detect(mlan_private *priv, t_u8 chnl); +/** check if scan type is passive for b/g band*/ +t_bool wlan_bg_scan_type_is_passive(mlan_private *priv, t_u8 chnl); +/** check if channel is NO_IR (passive) */ +t_bool wlan_is_chan_passive(mlan_private *priv, t_u8 band, t_u8 chan); +/** check if channel is disabled */ +t_bool wlan_is_chan_disabled(mlan_private *priv, t_u8 band, t_u8 chan); +/** check if channel is blacklisted */ +t_bool wlan_is_chan_blacklisted(mlan_private *priv, t_u8 band, t_u8 chan); +/** set blacklist setting for a channel */ +t_bool wlan_set_chan_blacklist(mlan_private *priv, t_u8 band, t_u8 chan, + t_bool bl); + +/* 802.11D related functions */ +/** Initialize 11D */ +t_void wlan_11d_priv_init(mlan_private *pmpriv); +/** Initialize 11D */ +t_void wlan_11d_init(mlan_adapter *pmadapter); +/** Enable 11D */ +mlan_status wlan_11d_enable(mlan_private *pmpriv, t_void *pioctl_buf, + state_11d_t flag); +/** Get if 11D is enabled */ +t_bool wlan_11d_is_enabled(mlan_private *pmpriv); +/** Get if FW 11D is enabled */ +t_bool wlan_fw_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); +/** Convert channel to frequency */ +t_u32 wlan_11d_chan_2_freq(pmlan_adapter pmadapter, t_u8 chan, t_u8 band); +#ifdef STA_SUPPORT +/** 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); +#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 +/** Configure 11D domain info command */ +mlan_status wlan_11d_cfg_domain_info(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req); + +/** This function converts region string to CFP table code */ +mlan_status wlan_misc_country_2_cfp_table_code(pmlan_adapter pmadapter, + t_u8 *country_code, t_u8 *cfp_bg, + t_u8 *cfp_a); + +/** This function finds if given country code is in EU table */ +t_bool wlan_is_etsi_country(pmlan_adapter pmadapter, t_u8 *country_code); + +/** check if station list is empty */ +t_u8 wlan_is_station_list_empty(mlan_private *priv); +/** get station node */ +sta_node *wlan_get_station_entry(mlan_private *priv, t_u8 *mac); +/** delete station list */ +t_void wlan_delete_station_list(pmlan_private priv); +/** delete station entry */ +t_void wlan_delete_station_entry(mlan_private *priv, t_u8 *mac); +/** add station entry */ +sta_node *wlan_add_station_entry(mlan_private *priv, t_u8 *mac); +/** process uap rx packet */ + +void wlan_check_sta_capability(pmlan_private priv, pmlan_buffer pevent, + sta_node *sta_ptr); +/** find specific ie */ +t_u8 *wlan_get_specific_ie(pmlan_private priv, t_u8 *ie_buf, t_u8 ie_len, + IEEEtypes_ElementId_e id, t_u8 ext_id); +t_u8 wlan_is_wmm_ie_present(pmlan_adapter pmadapter, t_u8 *pbuf, t_u16 buf_len); + +/** + * @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 INLINE int wlan_is_tx_pause(mlan_private *priv, t_u8 *ra) +{ + sta_node *sta_ptr = MNULL; + sta_ptr = wlan_get_station_entry(priv, ra); + if (sta_ptr) + return sta_ptr->tx_pause; + return MFALSE; +} +t_u16 wlan_update_ralist_tx_pause(pmlan_private priv, t_u8 *mac, t_u8 tx_pause); + +#ifdef UAP_SUPPORT +mlan_status wlan_process_uap_rx_packet(mlan_private *priv, 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(mlan_private *priv, pmlan_buffer pmbuf); +#endif /* UAP_SUPPORT */ + +mlan_status wlan_misc_ioctl_custom_ie_list(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req, + t_bool send_ioctl); + +mlan_status wlan_cmd_get_hw_spec(pmlan_private pmpriv, + HostCmd_DS_COMMAND *pcmd); +mlan_status wlan_ret_get_hw_spec(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + t_void *pioctl_buf); +#ifdef SDIO +mlan_status wlan_cmd_sdio_rx_aggr_cfg(HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_sdio_rx_aggr_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp); +#endif + +mlan_status wlan_misc_ioctl_mac_control(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_mac_control(pmlan_private pmpriv, HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_mac_control(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_cw_mode_ctrl(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_cw_mode_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_802_11_radio_control(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_802_11_radio_control(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_802_11_rf_antenna(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_802_11_rf_antenna(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_ret_reg_access(mlan_adapter *pmadapter, t_u16 type, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_ret_mem_access(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_reg_mem_ioctl_reg_rw(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_reg_mem_ioctl_read_eeprom(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_reg_mem_ioctl_mem_rw(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_reg_access(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_cmd_mem_access(HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_cmd_802_11_mac_address(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action); +mlan_status wlan_ret_802_11_mac_address(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_get_info_ver_ext(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_ioctl_link_statistic(mlan_private *pmpriv, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_802_11_link_statistic(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_ret_get_link_statistic(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_reg_rx_mgmt_ind(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +#ifdef DEBUG_LEVEL1 +mlan_status wlan_set_drvdbg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +#endif + +mlan_status wlan_misc_hotspot_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +#ifdef STA_SUPPORT +mlan_status wlan_misc_ext_capa_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +t_u32 wlan_is_ext_capa_support(mlan_private *pmpriv); +#endif + +#ifdef STA_SUPPORT +void wlan_add_ext_capa_info_ie(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, + t_u8 **pptlv_out); +#endif + +mlan_status wlan_cmd_boot_sleep(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); + +mlan_status wlan_ret_boot_sleep(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) +mlan_status wlan_cmd_crypto(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u16 *pdata_buf); + +mlan_status wlan_ret_crypto(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +#endif + +#define BW_20MHZ 0 +#define BW_40MHZ 1 +#define BW_80MHZ 2 +#define BW_160MHZ 3 +int wlan_add_supported_oper_class_ie(mlan_private *pmpriv, t_u8 **pptlv_out, + t_u8 curr_oper_class); +mlan_status wlan_get_curr_oper_class(mlan_private *pmpriv, t_u8 channel, + t_u8 bw, t_u8 *oper_class); +mlan_status wlan_check_operclass_validation(mlan_private *pmpriv, t_u8 channel, + t_u8 oper_class); +mlan_status wlan_misc_ioctl_operclass_validation(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req); +mlan_status wlan_misc_ioctl_oper_class(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req); + +t_u16 wlan_adjust_data_rate(mlan_private *priv, t_u8 rx_rate, t_u8 rate_info); +t_u8 wlan_adjust_antenna(pmlan_private priv, RxPD *prx_pd); + +mlan_status wlan_misc_otp_user_data(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +#ifdef USB +mlan_status wlan_misc_ioctl_usb_aggr_ctrl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +#endif + +mlan_status wlan_misc_ioctl_aggr_ctrl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_packet_aggr_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_ret_packet_aggr_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +mlan_status wlan_misc_ioctl_txcontrol(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_region(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +#ifdef RX_PACKET_COALESCE +mlan_status wlan_misc_ioctl_rx_pkt_coalesce_config(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +#endif + +void wlan_bt_coex_wlan_param_update_event(pmlan_private priv, + pmlan_buffer pevent); + +mlan_status wlan_misc_ioctl_dfs_repeater_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +t_bool wlan_check_interface_active(mlan_adapter *pmadapter); + +mlan_status wlan_misc_ioctl_coalesce_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_low_pwr_mode(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_pmic_configure(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_cwmode_ctrl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_set_mef_entry(mlan_private *pmpriv, pmlan_adapter pmadapter, + mef_cfg *pmef); +mlan_status wlan_process_mef_cfg_cmd(mlan_private *pmpriv, + pmlan_adapter pmadapter); +mlan_status wlan_misc_ioctl_mef_flt_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_ind_rst_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_ind_rst_cfg(HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_ret_ind_rst_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_802_11_supplicant_pmk(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); + +mlan_status wlan_ret_802_11_supplicant_pmk(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_sec_ioctl_passphrase(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_get_tsf(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +void wlan_add_fw_cfp_tables(pmlan_private pmpriv, t_u8 *buf, t_u16 buf_left); + +void wlan_free_fw_cfp_tables(mlan_adapter *pmadapter); + +mlan_status wlan_misc_chan_reg_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_get_cfp_table(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_get_cfpinfo(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_cmd_get_tsf(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action); +mlan_status wlan_ret_get_tsf(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +t_u8 wlan_ft_akm_is_used(mlan_private *pmpriv, t_u8 *rsn_ie); + +mlan_status wlan_get_rgchnpwr_cfg(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req); +mlan_status wlan_get_chan_trpc_cfg(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req); +mlan_status wlan_cmd_get_chan_trpc_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_get_chan_trpc_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_ps_inactivity_timeout(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); + +t_u8 wlan_ieee_rateid_to_mrvl_rateid(mlan_private *priv, t_u16 IeeeMacRate, + t_u8 *dst_mac); +t_u8 wlan_mrvl_rateid_to_ieee_rateid(t_u8 rate); + +t_u8 wlan_get_center_freq_idx(mlan_private *pmpriv, t_u16 band, t_u32 pri_chan, + t_u8 chan_bw); + +mlan_status wlan_ret_chan_region_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_misc_ioctl_fw_dump_event(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req); +mlan_status wlan_cmd_fw_dump_event(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); + +mlan_status wlan_misc_bootsleep(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); + +mlan_status wlan_misc_ioctl_dyn_bw(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req); +mlan_status wlan_cmd_config_dyn_bw(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_ret_dyn_bw(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_power_ioctl_set_get_lpm(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +mlan_status wlan_cmd_set_get_low_power_mode_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_ret_set_get_low_power_mode_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_cmd_range_ext(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf); +mlan_status wlan_ret_range_ext(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +/** + * @brief RA based queueing + * + * @param priv A pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +static INLINE t_u8 queuing_ra_based(pmlan_private priv) +{ + /* + * Currently we assume if we are in Infra, then DA=RA. This might not be + * true in the future + */ + if ((priv->bss_mode == MLAN_BSS_MODE_INFRA) && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA)) + return MFALSE; + + return MTRUE; +} + +/** + * @brief Copy Rates + * + * @param dest A pointer to Dest Buf + * @param pos The position for copy + * @param src A pointer to Src Buf + * @param len The len of Src Buf + * + * @return Number of Rates copied + */ +static INLINE t_u32 wlan_copy_rates(t_u8 *dest, t_u32 pos, t_u8 *src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= sizeof(WLAN_802_11_RATES)) + break; + dest[pos] = src[i]; + } + + return pos; +} + +/** + * @brief strlen + * + * @param str A pointer to string + * + * @return Length of string + */ +static INLINE t_u32 wlan_strlen(const char *str) +{ + t_u32 i; + + for (i = 0; str[i] != 0; i++) + ; + + return i; +} + +/** + * @brief iscdigit + * + * @param chr A char + * + * @return Non zero if chr is a hex, else 0 + */ +static INLINE t_u32 wlan_isxdigit(t_u8 chr) +{ + return (chr <= 'f' && chr >= 'a') || (chr <= 'F' && chr >= 'A') || + (chr <= '9' && chr >= '0'); +} + +/** + * @brief isspace + * + * @param A chr + * + * @return Non zero if chr is space etc, else 0 + */ +static INLINE t_u32 wlan_isspace(t_u8 chr) +{ + return chr <= ' ' && (chr == ' ' || (chr <= 13 && chr >= 9)); +} + +/** delay unit */ +typedef enum _delay_unit { + USEC, + MSEC, + SEC, +} t_delay_unit; + +/** delay function */ +t_void wlan_delay_func(mlan_adapter *pmadapter, t_u32 delay, t_delay_unit u); + +/** delay function wrapper */ +#define wlan_delay(p, n) wlan_delay_func(p, n, SEC) +/** delay function wrapper */ +#define wlan_mdelay(p, n) wlan_delay_func(p, n, MSEC) +/** delay function wrapper */ +#define wlan_udelay(p, n) wlan_delay_func(p, n, USEC) + +/** + * @brief This function check if there are pending cmd + * in cmd pending Q + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MTRUE -- cmd pending + * MFALSE -- no pending cmd + */ +static INLINE int wlan_is_cmd_pending(mlan_adapter *pmadapter) +{ + int ret; + cmd_ctrl_node *pcmd_node = MNULL; + wlan_request_cmd_lock(pmadapter); + pcmd_node = (cmd_ctrl_node *)util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, + MNULL, MNULL); + if (pcmd_node) + ret = MTRUE; + else + ret = MFALSE; + wlan_release_cmd_lock(pmadapter); + return ret; +} + +/** Get BSS number from priv */ +#define GET_BSS_NUM(priv) ((priv)->bss_num) + +/** + * @brief This function returns priv based on the BSS num and BSS type + * + * @param pmadapter A pointer to mlan_adapter + * @param bss_num BSS number + * @param bss_type BSS type + * + * @return Pointer to mlan_private + */ +static INLINE mlan_private *wlan_get_priv_by_id(mlan_adapter *pmadapter, + t_u32 bss_num, t_u32 bss_type) +{ + int i; + + for (i = 0; i < MIN(pmadapter->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (pmadapter->priv[i]) { + if ((pmadapter->priv[i]->bss_num == bss_num) && + (pmadapter->priv[i]->bss_type == bss_type)) + return pmadapter->priv[i]; + } + } + return MNULL; +} + +/** + * @brief This function returns first available priv + * based on the BSS role + * + * @param pmadapter A pointer to mlan_adapter + * @param bss_role BSS role or MLAN_BSS_ROLE_ANY + * + * @return Pointer to mlan_private + */ +static INLINE mlan_private *wlan_get_priv(mlan_adapter *pmadapter, + mlan_bss_role bss_role) +{ + int i; + + for (i = 0; i < MIN(pmadapter->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (pmadapter->priv[i]) { + if (bss_role == MLAN_BSS_ROLE_ANY || + GET_BSS_ROLE(pmadapter->priv[i]) == bss_role) + return pmadapter->priv[i]; + } + } + return MNULL; +} + +/** + * @brief This function counts the number of occurences for a certain + * condition among privs. Which privs are checked can be configured + * via a second condition. + * + * @param pmadapter A pointer to mlan_adapter + * @param count_cond Function pointer to condition to count on privs + * @param check_cond Function pointer to condition to decide whether priv + * should be counted or not. Use MNULL to check all privs. + * + * @return Count of privs where count_cond returned MTRUE. + */ +static INLINE int +wlan_count_priv_cond(mlan_adapter *pmadapter, + t_bool (*count_cond)(pmlan_private pmpriv), + t_bool (*check_cond)(pmlan_private pmpriv)) +{ + pmlan_private pmpriv; + int count = 0; + int i; + + if (pmadapter == MNULL || count_cond == MNULL) + return 0; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { + if ((check_cond == MNULL) || + (check_cond && check_cond(pmpriv))) { + if (count_cond(pmpriv)) + count++; + } + } + } + + return count; +} + +/** + * @brief This function runs a procedure on each priv. + * Which privs it is run on can be configured via a condition. + * + * @param pmadapter A pointer to mlan_adapter + * @param operation Function pointer to produedure to operate on priv + * @param check_cond Function pointer to condition to decide whether priv + * operated on or not. Use MNULL to run on all privs. + * + * @return Number of privs that operation was run on. + */ +static INLINE int +wlan_do_task_on_privs(mlan_adapter *pmadapter, + t_void (*operation)(pmlan_private pmpriv), + t_bool (*check_cond)(pmlan_private pmpriv)) +{ + pmlan_private pmpriv; + int count = 0; + int i; + + if (pmadapter == MNULL || operation == MNULL) + return 0; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { + if ((check_cond == MNULL) || + (check_cond && check_cond(pmpriv))) { + operation(pmpriv); + count++; + } + } + } + + return count; +} + +/** + * @brief This function builds a list of privs that test for a condition + * This is useful if you need to do a number of operations on the same set + * of privs. For one-off tasks, the above two functions might be better. + * + * @param pmadapter A pointer to mlan_adapter + * @param check_cond Function pointer to condition to decide whether priv + * should be placed in the list. + * @param ppriv_list Output param. Externally supplied array of mlan_private* + * to hold priv's that test positive with check_cond. + * Array size should be at least pmadapter->priv_num. + * + * @return Number of privs in ppriv_list + * + * @sa wlan_count_priv_cond + */ +static INLINE int +wlan_get_privs_by_cond(mlan_adapter *pmadapter, + t_bool (*check_cond)(pmlan_private pmpriv), + mlan_private **ppriv_list) +{ + pmlan_private pmpriv; + int count = 0; + int i; + + if (pmadapter == MNULL || check_cond == MNULL || ppriv_list == MNULL) + return 0; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { + if (check_cond(pmpriv)) + ppriv_list[count++] = pmpriv; + } + } + + return count; +} + +/** + * @brief This function builds a list of privs that test against two conditions + * This is useful if you need to do a number of operations on the same set + * of privs. Can choose whether both conditions (AND) or either condition (OR) + * is required. + * + * @param pmadapter A pointer to mlan_adapter + * @param check_cond Function pointer to condition to decide whether priv + * should be placed in the list. + * @param check_cond_2 Function pointer to second condition to check. + * @param and_conditions If MTRUE, both conditions must be met (AND), + * else either condition can be met (OR). + * @param ppriv_list Output param. Externally supplied array of + * mlan_private* to hold priv's that test positive with check_cond. Array size + * should be at least pmadapter->priv_num. + * + * @return Number of privs in ppriv_list + * + * @sa wlan_count_priv_cond, wlan_get_privs_by_cond + */ +static INLINE int +wlan_get_privs_by_two_cond(mlan_adapter *pmadapter, + t_bool (*check_cond)(pmlan_private pmpriv), + t_bool (*check_cond_2)(pmlan_private pmpriv), + t_bool and_conditions, mlan_private **ppriv_list) +{ + pmlan_private pmpriv; + int count = 0; + int i; + + if (pmadapter == MNULL || check_cond == MNULL || + check_cond_2 == MNULL || ppriv_list == MNULL) + return 0; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { + if (and_conditions) { + if (check_cond(pmpriv) && check_cond_2(pmpriv)) + ppriv_list[count++] = pmpriv; + } else { + if (check_cond(pmpriv) || check_cond_2(pmpriv)) + ppriv_list[count++] = pmpriv; + } + } + } + + return count; +} +#endif /* !_MLAN_MAIN_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_meas.c b/mxm_wifiex/wlan_src/mlan/mlan_meas.c new file mode 100644 index 0000000..38d16af --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_meas.c @@ -0,0 +1,465 @@ +/** + * @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 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************* +Change Log: + 03/24/2009: initial version +************************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_ioctl.h" +#include "mlan_meas.h" + +/** Default measurement duration when not provided by the application */ +#define WLAN_MEAS_DEFAULT_MEAS_DURATION 1000U /* TUs */ + +#ifdef DEBUG_LEVEL2 +/** String descriptions of the different measurement enums. Debug display */ +static const char *meas_type_str[WLAN_MEAS_NUM_TYPES] = { + "basic", +}; + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Retrieve the measurement string representation of a meas_type enum + * Used for debug display only + * + * @param meas_type Measurement type enumeration input for string lookup + * + * @return Constant string representing measurement type + */ +static const char *wlan_meas_get_meas_type_str(MeasType_t meas_type) +{ + if (meas_type <= WLAN_MEAS_11H_MAX_TYPE) + return meas_type_str[meas_type]; + + return "Invld"; +} +#endif + +/** + * @brief Debug print display of the input measurement request + * + * @param pmeas_req Pointer to the measurement request to display + * + * @return N/A + */ +static void +wlan_meas_dump_meas_req(const HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req) +{ + ENTER(); + + PRINTM(MINFO, "Meas: Req: ------------------------------\n"); + + PRINTM(MINFO, "Meas: Req: mac_addr: " MACSTR "\n", + MAC2STR(pmeas_req->mac_addr)); + + PRINTM(MINFO, "Meas: Req: dlgTkn: %d\n", pmeas_req->dialog_token); + PRINTM(MINFO, "Meas: Req: mode: dm[%c] rpt[%c] req[%c]\n", + pmeas_req->req_mode.duration_mandatory ? 'X' : ' ', + pmeas_req->req_mode.report ? 'X' : ' ', + pmeas_req->req_mode.request ? 'X' : ' '); + PRINTM(MINFO, "Meas: Req: : en[%c] par[%c]\n", + pmeas_req->req_mode.enable ? 'X' : ' ', + pmeas_req->req_mode.parallel ? 'X' : ' '); +#ifdef DEBUG_LEVEL2 + PRINTM(MINFO, "Meas: Req: measTyp: %s\n", + wlan_meas_get_meas_type_str(pmeas_req->meas_type)); +#endif + + switch (pmeas_req->meas_type) { + case WLAN_MEAS_BASIC: + /* Lazy cheat, fields of bas, cca, rpi union match on the + * request */ + PRINTM(MINFO, "Meas: Req: chan: %u\n", + pmeas_req->req.basic.channel); + PRINTM(MINFO, "Meas: Req: strt: %llu\n", + wlan_le64_to_cpu(pmeas_req->req.basic.start_time)); + PRINTM(MINFO, "Meas: Req: dur: %u\n", + wlan_le16_to_cpu(pmeas_req->req.basic.duration)); + break; + default: + PRINTM(MINFO, "Meas: Req: \n"); + break; + } + + PRINTM(MINFO, "Meas: Req: ------------------------------\n"); + LEAVE(); +} + +/** + * @brief Debug print display of the input measurement report + * + * @param pmeas_rpt Pointer to measurement report to display + * + * @return N/A + */ +static void +wlan_meas_dump_meas_rpt(const HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt) +{ + MeasType_t type; + ENTER(); + + PRINTM(MINFO, "Meas: Rpt: ------------------------------\n"); + PRINTM(MINFO, "Meas: Rpt: mac_addr: " MACSTR "\n", + MAC2STR(pmeas_rpt->mac_addr)); + + PRINTM(MINFO, "Meas: Rpt: dlgTkn: %d\n", pmeas_rpt->dialog_token); + + PRINTM(MINFO, "Meas: Rpt: rptMode: (%x): Rfs[%c] ICp[%c] Lt[%c]\n", + *(t_u8 *)&pmeas_rpt->rpt_mode, + pmeas_rpt->rpt_mode.refused ? 'X' : ' ', + pmeas_rpt->rpt_mode.incapable ? 'X' : ' ', + pmeas_rpt->rpt_mode.late ? 'X' : ' '); +#ifdef DEBUG_LEVEL2 + PRINTM(MINFO, "Meas: Rpt: measTyp: %s\n", + wlan_meas_get_meas_type_str(pmeas_rpt->meas_type)); +#endif + + type = wlan_le32_to_cpu(pmeas_rpt->meas_type); + switch (type) { + case WLAN_MEAS_BASIC: + PRINTM(MINFO, "Meas: Rpt: chan: %u\n", + pmeas_rpt->rpt.basic.channel); + PRINTM(MINFO, "Meas: Rpt: strt: %llu\n", + wlan_le64_to_cpu(pmeas_rpt->rpt.basic.start_time)); + PRINTM(MINFO, "Meas: Rpt: dur: %u\n", + wlan_le16_to_cpu(pmeas_rpt->rpt.basic.duration)); + PRINTM(MINFO, "Meas: Rpt: bas: (%x): unmsd[%c], radar[%c]\n", + *(t_u8 *)&(pmeas_rpt->rpt.basic.map), + pmeas_rpt->rpt.basic.map.unmeasured ? 'X' : ' ', + pmeas_rpt->rpt.basic.map.radar ? 'X' : ' '); + PRINTM(MINFO, "Meas: Rpt: bas: unidSig[%c] ofdm[%c] bss[%c]\n", + pmeas_rpt->rpt.basic.map.unidentified_sig ? 'X' : ' ', + pmeas_rpt->rpt.basic.map.ofdm_preamble ? 'X' : ' ', + pmeas_rpt->rpt.basic.map.bss ? 'X' : ' '); + break; + default: + PRINTM(MINFO, "Meas: Rpt: \n"); + break; + } + + PRINTM(MINFO, "Meas: Rpt: ------------------------------\n"); + LEAVE(); +} + +/** + * @brief Retrieve a measurement report from the firmware + * + * Callback from command processing when a measurement report is received + * from the firmware. Perform the following when a report is received: + * + * -# Debug displays the report if compiled with the appropriate flags + * -# If we are pending on a specific measurement report token, and it + * matches the received report's token, store the report and wake up + * any pending threads + * + * @param pmpriv Private driver information structure + * @param resp HostCmd_DS_COMMAND struct returned from the firmware command + * passing a HostCmd_DS_MEASUREMENT_REPORT structure. + * + * @return MLAN_STATUS_SUCCESS + */ +static int wlan_meas_cmdresp_get_report(mlan_private *pmpriv, + const HostCmd_DS_COMMAND *resp) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + const HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt = &resp->params.meas_rpt; + + ENTER(); + + PRINTM(MINFO, "Meas: Rpt: %#x-%u, Seq=%u, Ret=%u\n", resp->command, + resp->size, resp->seq_num, resp->result); + + /* Debug displays the measurement report */ + wlan_meas_dump_meas_rpt(pmeas_rpt); + + /* + * Check if we are pending on a measurement report and it matches + * the dialog token of the received report: + */ + if (pmadapter->state_meas.meas_rpt_pend_on && + pmadapter->state_meas.meas_rpt_pend_on == pmeas_rpt->dialog_token) { + PRINTM(MINFO, "Meas: Rpt: RCV'd Pend on meas #%d\n", + pmadapter->state_meas.meas_rpt_pend_on); + + /* Clear the pending report indicator */ + pmadapter->state_meas.meas_rpt_pend_on = 0; + + /* Copy the received report into the measurement state for + * retrieval */ + memcpy_ext(pmadapter, &pmadapter->state_meas.meas_rpt_returned, + pmeas_rpt, + sizeof(pmadapter->state_meas.meas_rpt_returned), + 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_ext(pmpriv->adapter, &pcmd_ptr->params.meas_req, pmeas_req, + sizeof(pcmd_ptr->params.meas_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_ext(pmpriv->adapter, pcmd_ptr->params.meas_rpt.mac_addr, + pmpriv->curr_addr, + sizeof(pcmd_ptr->params.meas_rpt.mac_addr), + sizeof(pcmd_ptr->params.meas_rpt.mac_addr)); + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + Global functions +********************************************************/ + +/** + * @brief Send the input measurement request to the firmware. + * + * If the dialog token in the measurement request is set to 0, the function + * will use an local static auto-incremented token in the measurement + * request. This ensures the dialog token is always set. + * + * If wait_for_resp_timeout is set, the function will block its return on + * a timeout or returned measurement report that matches the requests + * dialog token. + * + * @param pmpriv Private driver information structure + * @param pmeas_req Pointer to the measurement request to send + * @param wait_for_resp_timeout Timeout value of the measurement request + * in ms. + * @param pioctl_req Pointer to IOCTL request buffer + * @param pmeas_rpt Output parameter: Pointer for the resulting + * measurement report + * + * @return + * - 0 for success + * - -ETIMEDOUT if the measurement report does not return before + * the timeout expires + * - Error return from wlan_prepare_cmd routine otherwise + */ +int wlan_meas_util_send_req(mlan_private *pmpriv, + HostCmd_DS_MEASUREMENT_REQUEST *pmeas_req, + t_u32 wait_for_resp_timeout, + pmlan_ioctl_req pioctl_req, + HostCmd_DS_MEASUREMENT_REPORT *pmeas_rpt) +{ + static t_u8 auto_dialog_tok; + wlan_meas_state_t *pmeas_state = &pmpriv->adapter->state_meas; + int ret; + + ENTER(); + + /* If dialogTok was set to 0 or not provided, autoset */ + pmeas_req->dialog_token = + (pmeas_req->dialog_token ? pmeas_req->dialog_token : + ++auto_dialog_tok); + + /* Check for rollover of the dialog token. Avoid using 0 as a token */ + pmeas_req->dialog_token = + (pmeas_req->dialog_token ? pmeas_req->dialog_token : 1); + + /* + * If the request is to pend waiting for the result, set the dialog + * token of this measurement request in the state structure. The + * measurement report handling routines can then check the incoming + * measurement reports for a match with this dialog token. + */ + if (wait_for_resp_timeout) { + pmeas_state->meas_rpt_pend_on = pmeas_req->dialog_token; + PRINTM(MINFO, "Meas: Req: START Pend on meas #%d\n", + pmeas_req->dialog_token); + } + + /* Send the measurement request to the firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MEASUREMENT_REQUEST, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + (void *)pmeas_req); + + LEAVE(); + return ret; +} + +/** + * @brief Prepare the HostCmd_DS_Command structure for a measurement command. + * + * Use the Command field to determine if the command being set up is for + * 11h and call one of the local command handlers accordingly for: + * + * - HostCmd_CMD_MEASUREMENT_REQUEST + * - HostCmd_CMD_MEASUREMENT_REPORT + * + * @param pmpriv Private driver information structure + * @param pcmd_ptr Output parameter: Pointer to the command being prepared + * for the firmware + * @param pinfo_buf Void buffer passthrough with data necessary for a + * specific command type + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * + */ +int wlan_meas_cmd_process(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd_ptr, + const void *pinfo_buf) +{ + int ret = MLAN_STATUS_SUCCESS; + + ENTER(); + switch (pcmd_ptr->command) { + case HostCmd_CMD_MEASUREMENT_REQUEST: + ret = wlan_meas_cmd_request(pmpriv, pcmd_ptr, pinfo_buf); + break; + case HostCmd_CMD_MEASUREMENT_REPORT: + ret = wlan_meas_cmd_get_report(pmpriv, pcmd_ptr); + break; + default: + ret = MLAN_STATUS_FAILURE; + } + + pcmd_ptr->command = wlan_cpu_to_le16(pcmd_ptr->command); + pcmd_ptr->size = wlan_cpu_to_le16(pcmd_ptr->size); + LEAVE(); + return ret; +} + +/** + * @brief Handle the command response from the firmware for a measurement + * command + * + * Use the Command field to determine if the command response being + * is for meas. Call the local command response handler accordingly for: + * + * - HostCmd_CMD_802_MEASUREMENT_REQUEST + * - HostCmd_CMD_802_MEASUREMENT_REPORT + * + * @param pmpriv Private driver information structure + * @param resp HostCmd_DS_COMMAND struct returned from the firmware command + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int wlan_meas_cmdresp_process(mlan_private *pmpriv, + const HostCmd_DS_COMMAND *resp) +{ + int ret = MLAN_STATUS_SUCCESS; + + ENTER(); + switch (resp->command) { + case HostCmd_CMD_MEASUREMENT_REQUEST: + PRINTM(MINFO, "Meas: Req Resp: Sz=%u, Seq=%u, Ret=%u\n", + resp->size, resp->seq_num, resp->result); + break; + case HostCmd_CMD_MEASUREMENT_REPORT: + ret = wlan_meas_cmdresp_get_report(pmpriv, resp); + break; + default: + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_meas.h b/mxm_wifiex/wlan_src/mlan/mlan_meas.h new file mode 100644 index 0000000..c69f418 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_meas.h @@ -0,0 +1,55 @@ +/** + * @file mlan_meas.h + * + * @brief Interface for the measurement module implemented in mlan_meas.c + * + * Driver interface functions and type declarations for the measurement module + * implemented in mlan_meas.c + * + * @sa mlan_meas.c + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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(pmlan_private pmpriv, + pHostCmd_DS_MEASUREMENT_REQUEST pmeas_req, + t_u32 wait_for_resp_timeout, + pmlan_ioctl_req pioctl_req, + pHostCmd_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/mxm_wifiex/wlan_src/mlan/mlan_misc.c b/mxm_wifiex/wlan_src/mlan/mlan_misc.c new file mode 100644 index 0000000..a6208fa --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_misc.c @@ -0,0 +1,5866 @@ +/** + * @file mlan_misc.c + * + * @brief This file include miscellaneous functions for MLAN module + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11ac.h" +#include "mlan_11ax.h" +#ifdef UAP_SUPPORT +#include "mlan_uap.h" +#endif +#ifdef DRV_EMBEDDED_AUTHENTICATOR +#include "authenticator_api.h" +#endif +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +extern pmlan_operations mlan_ops[]; +#endif +extern t_u8 ac_to_tid[4][2]; + +/******************************************************** + Local Functions +********************************************************/ +#if defined(PCIE) || defined(SDIO) +/** + * @brief Check pending irq + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MTRUE/MFALSE; + */ +t_u8 wlan_pending_interrupt(pmlan_adapter pmadapter) +{ + if (!IS_USB(pmadapter->card_type) && pmadapter->ireg) + return MTRUE; + return MFALSE; +} +#endif + +/** Custom IE auto index and mask */ +#define MLAN_CUSTOM_IE_AUTO_IDX_MASK 0xffff +/** Custom IE mask for delete operation */ +#define MLAN_CUSTOM_IE_DELETE_MASK 0 +/** Custom IE mask for create new index */ +#define MLAN_CUSTOM_IE_NEW_MASK 0x8000 +/** Custom IE header size */ +#define MLAN_CUSTOM_IE_HDR_SIZE (sizeof(custom_ie) - MAX_IE_SIZE) + +/** + * @brief Check if current custom IE index is used on other interfaces. + * + * @param pmpriv A pointer to mlan_private structure + * @param idx index to check for in use + * + * @return MLAN_STATUS_SUCCESS --unused, otherwise used. + */ +static mlan_status wlan_is_custom_ie_index_unused(pmlan_private pmpriv, + 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(pmlan_private pmpriv, + pmlan_ioctl_req pioctl_req, + t_u16 mask, custom_ie *ie_data, + t_u16 *idx) +{ + t_u16 index = 0, insert = MFALSE; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + /* Determine the index where the IE needs to be inserted */ + while (!insert) { + while (index < MIN(pmpriv->adapter->max_mgmt_ie_index, + MAX_MGMT_IE_INDEX)) { + if (pmpriv->mgmt_ie[index].mgmt_subtype_mask == + MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + index++; + continue; + } + if (pmpriv->mgmt_ie[index].mgmt_subtype_mask == mask) { + /* Duplicate IE should be avoided */ + if (pmpriv->mgmt_ie[index].ie_length) { + if (!memcmp(pmpriv->adapter, + pmpriv->mgmt_ie[index] + .ie_buffer, + ie_data->ie_buffer, + pmpriv->mgmt_ie[index] + .ie_length)) { + PRINTM(MINFO, + "IE with the same mask exists at index %d mask=0x%x\n", + index, mask); + *idx = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + goto done; + } + } + /* Check if enough space is available */ + if (pmpriv->mgmt_ie[index].ie_length + + ie_data->ie_length > + MAX_IE_SIZE) { + index++; + continue; + } + insert = MTRUE; + break; + } + index++; + } + if (!insert) { + for (index = 0; + index < MIN(pmpriv->adapter->max_mgmt_ie_index, + MAX_MGMT_IE_INDEX); + index++) { + if (pmpriv->mgmt_ie[index].ie_length == 0) { + /* + * Check if this index is in use + * by other interface If yes, + * move ahead to next index + */ + if (MLAN_STATUS_SUCCESS == + wlan_is_custom_ie_index_unused( + pmpriv, index)) { + insert = MTRUE; + break; + } else { + PRINTM(MINFO, + "Skipping IE index %d in use.\n", + index); + } + } + } + } + if (index == pmpriv->adapter->max_mgmt_ie_index && !insert) { + PRINTM(MERROR, "Failed to Set the IE buffer\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + *idx = index; +done: + LEAVE(); + return ret; +} + +/** + * @brief Delete custom IE + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_req A pointer to ioctl request buffer + * @param ie_data a pointer to custom_ie structure + * @param idx index supplied + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ + +static mlan_status wlan_custom_ioctl_auto_delete(pmlan_private pmpriv, + pmlan_ioctl_req pioctl_req, + custom_ie *ie_data, 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_ext(pmpriv->adapter, del_ie, ie_data->ie_buffer, + ie_data->ie_length, MAX_IE_SIZE); + del_len = MIN(MAX_IE_SIZE - 1, ie_data->ie_length); + + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == idx) + ie_data->ie_index = 0; + + for (index = 0; + index < MIN(pmadapter->max_mgmt_ie_index, MAX_MGMT_IE_INDEX); + index++) { + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK != idx && + idx < MAX_MGMT_IE_INDEX) + 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_ext(pmpriv->adapter, ie, + pmpriv->mgmt_ie[index].ie_buffer, + cnt, MAX_IE_SIZE); + if (pmpriv->mgmt_ie[index].ie_length > + (cnt + del_len)) + memcpy_ext( + pmpriv->adapter, &ie[cnt], + &pmpriv->mgmt_ie[index].ie_buffer + [MIN((MAX_IE_SIZE - 1), + (cnt + del_len))], + (pmpriv->mgmt_ie[index] + .ie_length - + (cnt + del_len)), + MAX_IE_SIZE - cnt); + memset(pmpriv->adapter, + &pmpriv->mgmt_ie[index].ie_buffer, 0, + sizeof(pmpriv->mgmt_ie[index].ie_buffer)); + memcpy_ext(pmpriv->adapter, + &pmpriv->mgmt_ie[index].ie_buffer, + ie, + pmpriv->mgmt_ie[index].ie_length - + del_len, + MAX_IE_SIZE); + pmpriv->mgmt_ie[index].ie_length -= del_len; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == idx) + /* set a bit to indicate caller about + * update */ + ie_data->ie_index |= + (((t_u16)1) << index); + insert = MTRUE; + tmp_ie = pmpriv->mgmt_ie[index].ie_buffer; + tmp_len = pmpriv->mgmt_ie[index].ie_length; + cnt = 0; + continue; + } + tmp_ie++; + tmp_len--; + cnt++; + } + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK != idx) + break; + } + if (index == pmadapter->max_mgmt_ie_index && !insert) { + PRINTM(MERROR, "Failed to Clear IE buffer\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + ret = MLAN_STATUS_FAILURE; + } + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_get_info *info; + mlan_debug_info *debug_info = MNULL; + t_u32 i; + t_u8 *ptid; + + ENTER(); + + info = (mlan_ds_get_info *)pioctl_req->pbuf; + debug_info = (mlan_debug_info *)info->param.debug_info; + + if (pioctl_req->action == MLAN_ACT_GET) { + ptid = ac_to_tid[WMM_AC_BK]; + debug_info->wmm_ac_bk = pmpriv->wmm.packets_out[ptid[0]] + + pmpriv->wmm.packets_out[ptid[1]]; + ptid = ac_to_tid[WMM_AC_BE]; + debug_info->wmm_ac_be = pmpriv->wmm.packets_out[ptid[0]] + + pmpriv->wmm.packets_out[ptid[1]]; + ptid = ac_to_tid[WMM_AC_VI]; + debug_info->wmm_ac_vi = pmpriv->wmm.packets_out[ptid[0]] + + pmpriv->wmm.packets_out[ptid[1]]; + ptid = ac_to_tid[WMM_AC_VO]; + debug_info->wmm_ac_vo = pmpriv->wmm.packets_out[ptid[0]] + + pmpriv->wmm.packets_out[ptid[1]]; + debug_info->max_tx_buf_size = (t_u32)pmadapter->max_tx_buf_size; + debug_info->tx_buf_size = (t_u32)pmadapter->tx_buf_size; + debug_info->curr_tx_buf_size = + (t_u32)pmadapter->curr_tx_buf_size; + debug_info->rx_tbl_num = + wlan_get_rxreorder_tbl(pmpriv, debug_info->rx_tbl); + debug_info->tx_tbl_num = + wlan_get_txbastream_tbl(pmpriv, debug_info->tx_tbl); + debug_info->ralist_num = + wlan_get_ralist_info(pmpriv, debug_info->ralist); + debug_info->ps_mode = pmadapter->ps_mode; + debug_info->ps_state = pmadapter->ps_state; +#ifdef STA_SUPPORT + debug_info->is_deep_sleep = pmadapter->is_deep_sleep; +#endif /* STA_SUPPORT */ + debug_info->pm_wakeup_card_req = pmadapter->pm_wakeup_card_req; + debug_info->pm_wakeup_fw_try = pmadapter->pm_wakeup_fw_try; + debug_info->pm_wakeup_in_secs = pmadapter->pm_wakeup_in_secs; + debug_info->pm_wakeup_timeout = pmadapter->pm_wakeup_timeout; + debug_info->is_hs_configured = pmadapter->is_hs_configured; + debug_info->hs_activated = pmadapter->hs_activated; + debug_info->pps_uapsd_mode = pmadapter->pps_uapsd_mode; + debug_info->sleep_pd = pmadapter->sleep_period.period; + debug_info->qos_cfg = pmpriv->wmm_qosinfo; + debug_info->tx_lock_flag = pmadapter->tx_lock_flag; + debug_info->port_open = pmpriv->port_open; + debug_info->bypass_pkt_count = pmadapter->bypass_pkt_count; + debug_info->scan_processing = pmadapter->scan_processing; + debug_info->mlan_processing = pmadapter->mlan_processing; + debug_info->main_lock_flag = pmadapter->main_lock_flag; + debug_info->main_process_cnt = pmadapter->main_process_cnt; + debug_info->delay_task_flag = pmadapter->delay_task_flag; + debug_info->num_cmd_host_to_card_failure = + pmadapter->dbg.num_cmd_host_to_card_failure; + debug_info->num_cmd_sleep_cfm_host_to_card_failure = + pmadapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; + debug_info->num_tx_host_to_card_failure = + pmadapter->dbg.num_tx_host_to_card_failure; + debug_info->num_alloc_buffer_failure = + pmadapter->dbg.num_alloc_buffer_failure; + debug_info->num_pkt_dropped = pmadapter->dbg.num_pkt_dropped; + + debug_info->num_event_deauth = pmadapter->dbg.num_event_deauth; + debug_info->num_event_disassoc = + pmadapter->dbg.num_event_disassoc; + debug_info->num_event_link_lost = + pmadapter->dbg.num_event_link_lost; + debug_info->num_cmd_deauth = pmadapter->dbg.num_cmd_deauth; + debug_info->num_cmd_assoc_success = + pmadapter->dbg.num_cmd_assoc_success; + debug_info->num_cmd_assoc_failure = + pmadapter->dbg.num_cmd_assoc_failure; + debug_info->num_cmd_timeout = pmadapter->num_cmd_timeout; + debug_info->timeout_cmd_id = pmadapter->dbg.timeout_cmd_id; + debug_info->timeout_cmd_act = pmadapter->dbg.timeout_cmd_act; + memcpy_ext(pmadapter, debug_info->last_cmd_id, + pmadapter->dbg.last_cmd_id, + sizeof(pmadapter->dbg.last_cmd_id), + sizeof(debug_info->last_cmd_id)); + memcpy_ext(pmadapter, debug_info->last_cmd_act, + pmadapter->dbg.last_cmd_act, + sizeof(pmadapter->dbg.last_cmd_act), + sizeof(debug_info->last_cmd_act)); + debug_info->last_cmd_index = pmadapter->dbg.last_cmd_index; + memcpy_ext(pmadapter, debug_info->last_cmd_resp_id, + pmadapter->dbg.last_cmd_resp_id, + sizeof(pmadapter->dbg.last_cmd_resp_id), + sizeof(debug_info->last_cmd_resp_id)); + debug_info->last_cmd_resp_index = + pmadapter->dbg.last_cmd_resp_index; + memcpy_ext(pmadapter, debug_info->last_event, + pmadapter->dbg.last_event, + sizeof(pmadapter->dbg.last_event), + sizeof(debug_info->last_event)); + debug_info->last_event_index = pmadapter->dbg.last_event_index; + debug_info->num_no_cmd_node = pmadapter->dbg.num_no_cmd_node; + debug_info->pending_cmd = + (pmadapter->curr_cmd) ? + pmadapter->dbg.last_cmd_id + [pmadapter->dbg.last_cmd_index] : + 0; + debug_info->dnld_cmd_in_secs = pmadapter->dnld_cmd_in_secs; +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + debug_info->num_cmdevt_card_to_host_failure = + pmadapter->dbg.num_cmdevt_card_to_host_failure; + debug_info->num_rx_card_to_host_failure = + pmadapter->dbg.num_rx_card_to_host_failure; + debug_info->num_int_read_failure = + pmadapter->dbg.num_int_read_failure; + debug_info->last_int_status = + pmadapter->dbg.last_int_status; + debug_info->mp_rd_bitmap = + pmadapter->pcard_sd->mp_rd_bitmap; + debug_info->mp_wr_bitmap = + pmadapter->pcard_sd->mp_wr_bitmap; + debug_info->curr_rd_port = + pmadapter->pcard_sd->curr_rd_port; + debug_info->curr_wr_port = + pmadapter->pcard_sd->curr_wr_port; + debug_info->mp_invalid_update = + pmadapter->pcard_sd->mp_invalid_update; + debug_info->num_of_irq = + pmadapter->pcard_sd->num_of_irq; + memcpy_ext(pmadapter, debug_info->mp_update, + pmadapter->pcard_sd->mp_update, + sizeof(pmadapter->pcard_sd->mp_update), + sizeof(debug_info->mp_update)); + memcpy_ext(pmadapter, debug_info->mpa_tx_count, + pmadapter->pcard_sd->mpa_tx_count, + sizeof(pmadapter->pcard_sd->mpa_tx_count), + sizeof(debug_info->mpa_tx_count)); + debug_info->mpa_sent_last_pkt = + pmadapter->pcard_sd->mpa_sent_last_pkt; + debug_info->mpa_sent_no_ports = + pmadapter->pcard_sd->mpa_sent_no_ports; + debug_info->last_recv_wr_bitmap = + pmadapter->pcard_sd->last_recv_wr_bitmap; + debug_info->last_mp_index = + pmadapter->pcard_sd->last_mp_index; + memcpy_ext( + pmadapter, debug_info->last_mp_wr_bitmap, + pmadapter->pcard_sd->last_mp_wr_bitmap, + sizeof(pmadapter->pcard_sd->last_mp_wr_bitmap), + sizeof(debug_info->last_mp_wr_bitmap)); + memcpy_ext( + pmadapter, debug_info->last_mp_wr_ports, + pmadapter->pcard_sd->last_mp_wr_ports, + sizeof(pmadapter->pcard_sd->last_mp_wr_ports), + sizeof(debug_info->last_mp_wr_ports)); + memcpy_ext(pmadapter, debug_info->last_mp_wr_len, + pmadapter->pcard_sd->last_mp_wr_len, + sizeof(pmadapter->pcard_sd->last_mp_wr_len), + sizeof(debug_info->last_mp_wr_len)); + memcpy_ext(pmadapter, debug_info->last_mp_wr_info, + pmadapter->pcard_sd->last_mp_wr_info, + sizeof(pmadapter->pcard_sd->last_mp_wr_info), + sizeof(debug_info->last_mp_wr_info)); + memcpy_ext( + pmadapter, debug_info->last_curr_wr_port, + pmadapter->pcard_sd->last_curr_wr_port, + sizeof(pmadapter->pcard_sd->last_curr_wr_port), + sizeof(debug_info->last_curr_wr_port)); + debug_info->mpa_buf = pmadapter->pcard_sd->mpa_buf; + debug_info->mpa_buf_size = + pmadapter->pcard_sd->mpa_buf_size; + debug_info->sdio_rx_aggr = + pmadapter->pcard_sd->sdio_rx_aggr_enable; + memcpy_ext(pmadapter, debug_info->mpa_rx_count, + pmadapter->pcard_sd->mpa_rx_count, + sizeof(pmadapter->pcard_sd->mpa_rx_count), + sizeof(debug_info->mpa_rx_count)); + debug_info->mp_aggr_pkt_limit = + SDIO_MP_AGGR_DEF_PKT_LIMIT; + } +#endif +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type)) { + debug_info->txbd_rdptr = + pmadapter->pcard_pcie->txbd_rdptr; + debug_info->txbd_wrptr = + pmadapter->pcard_pcie->txbd_wrptr; + debug_info->rxbd_rdptr = + pmadapter->pcard_pcie->rxbd_rdptr; + debug_info->rxbd_wrptr = + pmadapter->pcard_pcie->rxbd_wrptr; + debug_info->eventbd_rdptr = + pmadapter->pcard_pcie->evtbd_rdptr; + debug_info->eventbd_wrptr = + pmadapter->pcard_pcie->evtbd_wrptr; + debug_info->txbd_ring_vbase = + pmadapter->pcard_pcie->txbd_ring_vbase; + debug_info->txbd_ring_size = + pmadapter->pcard_pcie->txbd_ring_size; + debug_info->rxbd_ring_vbase = + pmadapter->pcard_pcie->rxbd_ring_vbase; + debug_info->rxbd_ring_size = + pmadapter->pcard_pcie->rxbd_ring_size; + debug_info->evtbd_ring_vbase = + pmadapter->pcard_pcie->evtbd_ring_vbase; + debug_info->evtbd_ring_size = + pmadapter->pcard_pcie->evtbd_ring_size; + memcpy_ext( + pmadapter, debug_info->last_tx_pkt_size, + pmadapter->pcard_pcie->last_tx_pkt_size, + sizeof(pmadapter->pcard_pcie->last_tx_pkt_size), + sizeof(debug_info->last_tx_pkt_size)); + } +#endif + debug_info->data_sent = pmadapter->data_sent; + debug_info->cmd_sent = pmadapter->cmd_sent; + debug_info->cmd_resp_received = pmadapter->cmd_resp_received; + debug_info->tx_pkts_queued = + util_scalar_read(pmadapter->pmoal_handle, + &pmpriv->wmm.tx_pkts_queued, MNULL, + MNULL); +#ifdef UAP_SUPPORT + debug_info->num_bridge_pkts = + util_scalar_read(pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + debug_info->num_drop_pkts = pmpriv->num_drop_pkts; +#endif + debug_info->fw_hang_report = pmadapter->fw_hang_report; + debug_info->mlan_processing = pmadapter->mlan_processing; + debug_info->mlan_rx_processing = pmadapter->mlan_rx_processing; + debug_info->rx_pkts_queued = pmadapter->rx_pkts_queued; + debug_info->mlan_adapter = pmadapter; + debug_info->mlan_adapter_size = sizeof(mlan_adapter); + debug_info->mlan_priv_num = pmadapter->priv_num; + for (i = 0; i < pmadapter->priv_num; i++) { + debug_info->mlan_priv[i] = pmadapter->priv[i]; + debug_info->mlan_priv_size[i] = sizeof(mlan_private); + } + } + + pioctl_req->data_read_written = + sizeof(mlan_debug_info) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get the MAC control configuration. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status wlan_misc_ioctl_mac_control(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) { + misc->param.mac_ctrl = pmpriv->curr_pkt_filter; + } else { + pmpriv->curr_pkt_filter = misc->param.mac_ctrl; + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MAC_CONTROL, + cmd_action, 0, (t_void *)pioctl_req, + &misc->param.mac_ctrl); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} + +/** + * @brief This timer function handles wakeup card timeout. + * + * @param function_context A pointer to function_context + * @return N/A + */ +t_void wlan_wakeup_card_timeout_func(void *function_context) +{ + pmlan_adapter pmadapter = (pmlan_adapter)function_context; + + ENTER(); + + PRINTM(MERROR, "%s: ps_state=%d\n", __FUNCTION__, pmadapter->ps_state); + if (pmadapter->ps_state != PS_STATE_AWAKE) { + PRINTM(MERROR, "Wakeup card timeout!\n"); + pmadapter->pm_wakeup_timeout++; + wlan_recv_event(wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DBG_DUMP, MNULL); + } + pmadapter->wakeup_fw_timer_is_set = MFALSE; + + LEAVE(); +} + +/** + * @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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pm = MNULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 prev_cond = 0; + + ENTER(); + + pm = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + + switch (pioctl_req->action) { + case MLAN_ACT_SET: +#ifdef STA_SUPPORT + if (pmadapter->pps_uapsd_mode) { + PRINTM(MINFO, + "Host Sleep IOCTL is blocked in UAPSD/PPS mode\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } +#endif /* STA_SUPPORT */ + if (pm->param.hs_cfg.is_invoke_hostcmd == MTRUE) { + if (pm->param.hs_cfg.conditions == + HOST_SLEEP_CFG_CANCEL) { + if (pmadapter->is_hs_configured == MFALSE) { + /* Already cancelled */ + break; + } + /* Save previous condition */ + prev_cond = pmadapter->hs_cfg.conditions; + pmadapter->hs_cfg.conditions = + pm->param.hs_cfg.conditions; + } else if (pmadapter->hs_cfg.conditions == + HOST_SLEEP_CFG_CANCEL) { + /* Return failure if no parameters for HS enable + */ + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + status = MLAN_STATUS_FAILURE; + break; + } + status = wlan_prepare_cmd( + pmpriv, HostCmd_CMD_802_11_HS_CFG_ENH, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + (t_void *)(&pmadapter->hs_cfg)); + if (status == MLAN_STATUS_SUCCESS) + status = MLAN_STATUS_PENDING; + if (pm->param.hs_cfg.conditions == + HOST_SLEEP_CFG_CANCEL) { + /* Restore previous condition */ + pmadapter->hs_cfg.conditions = prev_cond; + } + } else { + pmadapter->hs_cfg.conditions = + pm->param.hs_cfg.conditions; + pmadapter->hs_cfg.gpio = (t_u8)pm->param.hs_cfg.gpio; + pmadapter->hs_cfg.gap = (t_u8)pm->param.hs_cfg.gap; + pmadapter->param_type_ind = + (t_u8)pm->param.hs_cfg.param_type_ind; + pmadapter->ind_gpio = (t_u8)pm->param.hs_cfg.ind_gpio; + pmadapter->level = (t_u8)pm->param.hs_cfg.level; + pmadapter->param_type_ext = + (t_u8)pm->param.hs_cfg.param_type_ext; + pmadapter->event_force_ignore = + pm->param.hs_cfg.event_force_ignore; + pmadapter->event_use_ext_gap = + pm->param.hs_cfg.event_use_ext_gap; + pmadapter->ext_gap = pm->param.hs_cfg.ext_gap; + pmadapter->gpio_wave = pm->param.hs_cfg.gpio_wave; + pmadapter->hs_wake_interval = + pm->param.hs_cfg.hs_wake_interval; + } + break; + case MLAN_ACT_GET: + pm->param.hs_cfg.conditions = pmadapter->hs_cfg.conditions; + pm->param.hs_cfg.gpio = pmadapter->hs_cfg.gpio; + pm->param.hs_cfg.gap = pmadapter->hs_cfg.gap; + pm->param.hs_cfg.param_type_ind = pmadapter->param_type_ind; + pm->param.hs_cfg.ind_gpio = pmadapter->ind_gpio; + pm->param.hs_cfg.level = pmadapter->level; + pm->param.hs_cfg.param_type_ext = pmadapter->param_type_ext; + pm->param.hs_cfg.event_force_ignore = + pmadapter->event_force_ignore; + pm->param.hs_cfg.event_use_ext_gap = + pmadapter->event_use_ext_gap; + pm->param.hs_cfg.ext_gap = pmadapter->ext_gap; + pm->param.hs_cfg.gpio_wave = pmadapter->gpio_wave; + pm->param.hs_cfg.hs_wake_interval = pmadapter->hs_wake_interval; + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief Set Robustcoex gpiocfg + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +mlan_status wlan_misc_robustcoex(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action; + mlan_ds_misc_cfg *robust_coex_cfg = + (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_ROBUSTCOEX, + cmd_action, 0, (t_void *)pioctl_req, + &robust_coex_cfg->param.robustcoexparams); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +/** + * @brief Set/get DMCS config + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_dmcs_config(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action; + mlan_ds_misc_cfg *dmcs_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_DMCS_CONFIG, cmd_action, 0, + (t_void *)pioctl_req, + &dmcs_cfg->param.dmcs_policy); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +#if defined(PCIE) +/** + * @brief Enable SSU support + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +mlan_status wlan_misc_ssu(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = HostCmd_ACT_GEN_GET; + mlan_ds_misc_cfg *ssu_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else if (pioctl_req->action == MLAN_ACT_DEFAULT) + cmd_action = HostCmd_ACT_GEN_SET_DEFAULT; + else if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_SSU, cmd_action, 0, + (t_void *)pioctl_req, + &ssu_cfg->param.ssu_params); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} +#endif + +/** + * @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(); + +#ifdef SDIO + /* make sure that the data length is at least SDIO block size */ + if (IS_SD(pmadapter->card_type)) + data_len = (data_len + MLAN_SDIO_BLOCK_SIZE - 1) / + MLAN_SDIO_BLOCK_SIZE * MLAN_SDIO_BLOCK_SIZE; +#endif + + /* head_room is not implemented for malloc mlan buffer */ + + switch (malloc_flag) { + case MOAL_MALLOC_BUFFER: + buf_size = sizeof(mlan_buffer) + data_len + DMA_ALIGNMENT; + ret = pcb->moal_malloc(pmadapter->pmoal_handle, buf_size, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmbuf); + if ((ret != MLAN_STATUS_SUCCESS) || !pmbuf) { + pmbuf = MNULL; + goto exit; + } + memset(pmadapter, pmbuf, 0, sizeof(mlan_buffer)); + + pmbuf->pdesc = MNULL; + /* Align address */ + pmbuf->pbuf = (t_u8 *)ALIGN_ADDR( + (t_u8 *)pmbuf + sizeof(mlan_buffer), DMA_ALIGNMENT); + pmbuf->data_offset = 0; + pmbuf->data_len = data_len; + pmbuf->flags |= MLAN_BUF_FLAG_MALLOC_BUF; + break; + + case MOAL_ALLOC_MLAN_BUFFER: + /* use moal_alloc_mlan_buffer, head_room supported */ + ret = pcb->moal_alloc_mlan_buffer( + pmadapter->pmoal_handle, + data_len + DMA_ALIGNMENT + head_room, &pmbuf); + if ((ret != MLAN_STATUS_SUCCESS) || !pmbuf) { + PRINTM(MERROR, "Failed to allocate 'mlan_buffer'\n"); + goto exit; + } + pmbuf->data_offset = head_room; + tmp_buf = (t_u8 *)ALIGN_ADDR(pmbuf->pbuf + pmbuf->data_offset, + DMA_ALIGNMENT); + pmbuf->data_offset += + (t_u32)(tmp_buf - (pmbuf->pbuf + pmbuf->data_offset)); + pmbuf->data_len = data_len; + pmbuf->flags = 0; + break; + } + +exit: + LEAVE(); + return pmbuf; +} + +/** + * @brief This function frees a mlan_buffer. + * + * @param pmadapter Pointer to mlan_adapter + * @param pmbuf Pointer to mlan_buffer + * + * @return N/A + */ +t_void wlan_free_mlan_buffer(mlan_adapter *pmadapter, pmlan_buffer pmbuf) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + + if (pcb && pmbuf) { + if (pmbuf->flags & MLAN_BUF_FLAG_BRIDGE_BUF) + util_scalar_decrement( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + if (pmbuf->flags & MLAN_BUF_FLAG_MALLOC_BUF) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pmbuf); + else + pcb->moal_free_mlan_buffer(pmadapter->pmoal_handle, + pmbuf); + } + + LEAVE(); + return; +} + +/** + * @brief Delay function implementation + * + * @param pmadapter A pointer to mlan_adapter structure + * @param delay Delay value + * @param u Units of delay (sec, msec or usec) + * + * @return N/A + */ +t_void wlan_delay_func(mlan_adapter *pmadapter, t_u32 delay, t_delay_unit u) +{ + t_u32 now_tv_sec, now_tv_usec; + t_u32 upto_tv_sec, upto_tv_usec; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (pcb->moal_udelay) { + if (u == SEC) + delay *= 1000000; + else if (u == MSEC) + delay *= 1000; + pcb->moal_udelay(pmadapter->pmoal_handle, delay); + } else { + pcb->moal_get_system_time(pmadapter->pmoal_handle, &upto_tv_sec, + &upto_tv_usec); + + switch (u) { + case SEC: + upto_tv_sec += delay; + break; + case MSEC: + delay *= 1000; + /* fall through */ + case USEC: + upto_tv_sec += (delay / 1000000); + upto_tv_usec += (delay % 1000000); + break; + } + + do { + pcb->moal_get_system_time(pmadapter->pmoal_handle, + &now_tv_sec, &now_tv_usec); + if (now_tv_sec > upto_tv_sec) { + LEAVE(); + return; + } + + if ((now_tv_sec == upto_tv_sec) && + (now_tv_usec >= upto_tv_usec)) { + LEAVE(); + return; + } + } while (MTRUE); + } + + LEAVE(); + return; +} + +/** + * @brief BSS remove + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, MLAN_STATUS_FAILURE + */ +mlan_status wlan_bss_ioctl_bss_remove(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + ENTER(); + wlan_cancel_bss_pending_cmd(pmadapter, pioctl_req->bss_index); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief Set/Get BSS role + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, MLAN_STATUS_FAILURE + */ +mlan_status wlan_bss_ioctl_bss_role(pmlan_adapter pmadapter, + 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; +#ifdef USB + pmlan_callbacks pcb = &pmadapter->callbacks; + pmlan_buffer pmbuf; +#endif +#if defined(WIFI_DIRECT_SUPPORT) + t_u8 bss_mode; +#endif + t_u8 i, global_band = 0; + int j; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + bss->param.bss_role = GET_BSS_ROLE(pmpriv); + } else { + if (GET_BSS_ROLE(pmpriv) == bss->param.bss_role) { + PRINTM(MIOCTL, "BSS ie already in the desired role!\n"); + goto done; + } + mlan_block_rx_process(pmadapter, MTRUE); + /** Switch BSS role */ + wlan_free_priv(pmpriv); + +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + while ((pmbuf = (pmlan_buffer)util_dequeue_list( + pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, + pcb->moal_spin_lock, + pcb->moal_spin_unlock))) { + pcb->moal_recv_complete(pmadapter->pmoal_handle, + pmbuf, + pmadapter->rx_data_ep, + MLAN_STATUS_FAILURE); + } + } +#endif + pmpriv->bss_role = bss->param.bss_role; + if (pmpriv->bss_type == MLAN_BSS_TYPE_UAP) + pmpriv->bss_type = MLAN_BSS_TYPE_STA; + else if (pmpriv->bss_type == MLAN_BSS_TYPE_STA) + pmpriv->bss_type = MLAN_BSS_TYPE_UAP; + /* Initialize private structures */ + wlan_init_priv(pmpriv); + mlan_block_rx_process(pmadapter, MFALSE); + /* Initialize function table */ + for (j = 0; mlan_ops[j]; j++) { + if (mlan_ops[j]->bss_role == GET_BSS_ROLE(pmpriv)) { + memcpy_ext(pmadapter, &pmpriv->ops, mlan_ops[j], + sizeof(mlan_operations), + sizeof(mlan_operations)); + } + } + + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i] && + GET_BSS_ROLE(pmadapter->priv[i]) == + MLAN_BSS_ROLE_STA) + global_band |= pmadapter->priv[i]->config_bands; + } + + if (global_band != pmadapter->config_bands) { + if (wlan_set_regiontable( + pmpriv, (t_u8)pmadapter->region_code, + global_band | + pmadapter->adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (wlan_11d_set_universaltable( + pmpriv, + global_band | + pmadapter->adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->config_bands = global_band; + } + + /* Issue commands to initialize firmware */ +#if defined(WIFI_DIRECT_SUPPORT) + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) + bss_mode = BSS_MODE_WIFIDIRECT_CLIENT; + else + bss_mode = BSS_MODE_WIFIDIRECT_GO; + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, MNULL, + &bss_mode); + if (ret) + goto done; +#endif + ret = pmpriv->ops.init_cmd(pmpriv, MFALSE); + if (ret == MLAN_STATUS_FAILURE) + goto done; + + /* Issue dummy Get command to complete the ioctl */ + memset(pmadapter, &dummy, 0, sizeof(HostCmd_DS_VERSION_EXT)); + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_VERSION_EXT, + HostCmd_ACT_GEN_GET, 0, + (t_void *)pioctl_req, (t_void *)&dummy); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set the custom IE + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * @param send_ioctl Flag to indicate if ioctl should be sent with cmd + * (MTRUE if from moal/user, MFALSE if internal) + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_custom_ie_list(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req, + t_bool send_ioctl) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + custom_ie *ie_data = MNULL; + t_u16 cmd_action = 0, index, mask, i, len, app_data_len; + t_s32 ioctl_len; + t_u8 *tmp_ie; + + ENTER(); + + if ((misc->param.cust_ie.len == 0) || + (misc->param.cust_ie.len == sizeof(t_u16))) { + pioctl_req->action = MLAN_ACT_GET; + /* Get the IE */ + cmd_action = HostCmd_ACT_GEN_GET; + } else { + /* ioctl_len : ioctl length from application, start with + * misc->param.cust_ie.len and reach upto 0 */ + ioctl_len = misc->param.cust_ie.len; + + /* app_data_len : length from application, start with 0 + * and reach upto ioctl_len */ + app_data_len = sizeof(MrvlIEtypesHeader_t); + misc->param.cust_ie.len = 0; + + while (ioctl_len > 0) { + ie_data = (custom_ie *)(((t_u8 *)&misc->param.cust_ie) + + app_data_len); + ioctl_len -= + (ie_data->ie_length + MLAN_CUSTOM_IE_HDR_SIZE); + app_data_len += + (ie_data->ie_length + MLAN_CUSTOM_IE_HDR_SIZE); + + index = ie_data->ie_index; + mask = ie_data->mgmt_subtype_mask; + + /* Need to be Autohandled */ + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == index) { + /* Automatic Deletion */ + if (mask == MLAN_CUSTOM_IE_DELETE_MASK) { + ret = wlan_custom_ioctl_auto_delete( + pmpriv, pioctl_req, ie_data, + index); + /* if IE to delete is not found, return + * error */ + if (ret == MLAN_STATUS_FAILURE) + goto done; + index = ie_data->ie_index; + memset(pmadapter, ie_data, 0, + sizeof(custom_ie) * + MAX_MGMT_IE_INDEX_TO_FW); + len = 0; + for (i = 0; + i < pmadapter->max_mgmt_ie_index; + i++) { + /* Check if index is updated + * before sending to FW */ + if (index & ((t_u16)1) << i) { + memcpy_ext( + pmadapter, + (t_u8 *)ie_data + + len, + &i, + sizeof(ie_data->ie_index), + sizeof(ie_data->ie_index)); + len += sizeof( + ie_data->ie_index); + memcpy_ext( + pmadapter, + (t_u8 *)ie_data + + len, + &pmpriv->mgmt_ie[i] + .mgmt_subtype_mask, + sizeof(ie_data->mgmt_subtype_mask), + sizeof(ie_data->mgmt_subtype_mask)); + len += sizeof( + ie_data->mgmt_subtype_mask); + memcpy_ext( + pmadapter, + (t_u8 *)ie_data + + len, + &pmpriv->mgmt_ie[i] + .ie_length, + sizeof(ie_data->ie_length), + sizeof(ie_data->ie_length)); + len += sizeof( + ie_data->ie_length); + if (pmpriv->mgmt_ie[i] + .ie_length) { + memcpy_ext( + pmadapter, + (t_u8 *)ie_data + + len, + &pmpriv->mgmt_ie[i] + .ie_buffer, + pmpriv->mgmt_ie[i] + .ie_length, + pmpriv->mgmt_ie[i] + .ie_length); + len += pmpriv->mgmt_ie[i] + .ie_length; + } + } + } + misc->param.cust_ie.len += len; + pioctl_req->action = MLAN_ACT_SET; + cmd_action = HostCmd_ACT_GEN_SET; + } else { /* Automatic Addition */ + if (MLAN_STATUS_FAILURE == + wlan_custom_ioctl_get_autoidx( + pmpriv, pioctl_req, mask, + ie_data, &index)) { + PRINTM(MERROR, + "Failed to Set the IE buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + mask &= ~MLAN_CUSTOM_IE_NEW_MASK; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == + index || + index >= MAX_MGMT_IE_INDEX) { + ret = MLAN_STATUS_SUCCESS; + goto done; + } + tmp_ie = (t_u8 *)&pmpriv->mgmt_ie[index] + .ie_buffer; + memcpy_ext( + pmadapter, + tmp_ie + pmpriv->mgmt_ie[index] + .ie_length, + &ie_data->ie_buffer, + ie_data->ie_length, + 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_ext( + pmadapter, &ie_data->ie_buffer, + &pmpriv->mgmt_ie[index] + .ie_buffer, + pmpriv->mgmt_ie[index].ie_length, + MAX_IE_SIZE); + misc->param.cust_ie.len += + pmpriv->mgmt_ie[index] + .ie_length + + MLAN_CUSTOM_IE_HDR_SIZE; + } + } else { + if (index >= pmadapter->max_mgmt_ie_index || + index >= MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid custom IE index %d\n", + index); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Set/Clear the IE and save it */ + if (ie_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK && + ie_data->ie_length) { + PRINTM(MINFO, "Clear the IE buffer\n"); + ret = wlan_custom_ioctl_auto_delete( + pmpriv, pioctl_req, ie_data, + index); + /* if IE to delete is not found, return + * error */ + if (ret == MLAN_STATUS_FAILURE) + goto done; + memset(pmadapter, ie_data, 0, + sizeof(custom_ie) * + MAX_MGMT_IE_INDEX_TO_FW); + memcpy_ext( + pmadapter, (t_u8 *)ie_data, + &pmpriv->mgmt_ie[index], + pmpriv->mgmt_ie[index].ie_length + + MLAN_CUSTOM_IE_HDR_SIZE, + pmpriv->mgmt_ie[index].ie_length + + MLAN_CUSTOM_IE_HDR_SIZE); + } else { + /* + * Check if this index is being used on + * any other interfaces. If yes, then + * the request needs to be rejected. + */ + ret = wlan_is_custom_ie_index_unused( + pmpriv, index); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, + "IE index is used by other interface.\n"); + PRINTM(MERROR, + "Set or delete on index %d is not allowed.\n", + index); + pioctl_req->status_code = + MLAN_ERROR_IOCTL_FAIL; + goto done; + } + PRINTM(MINFO, "Set the IE buffer\n"); + if (ie_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK) + ie_data->ie_length = 0; + else { + if ((pmpriv->mgmt_ie[index] + .mgmt_subtype_mask == + ie_data->mgmt_subtype_mask) && + (pmpriv->mgmt_ie[index] + .ie_length == + ie_data->ie_length) && + !memcmp(pmpriv->adapter, + pmpriv->mgmt_ie[index] + .ie_buffer, + ie_data->ie_buffer, + ie_data->ie_length)) { + PRINTM(MIOCTL, + "same custom ie already configured!\n"); + if (ioctl_len <= 0 && + misc->param.cust_ie + .len == + 0) { + goto done; + } else { + /* remove + * matching IE + * from app + * buffer */ + app_data_len -= + ie_data->ie_length + + MLAN_CUSTOM_IE_HDR_SIZE; + memmove(pmadapter, + (t_u8 *)ie_data, + ie_data->ie_buffer + + ie_data->ie_length, + ioctl_len); + continue; + } + } + } + memset(pmadapter, + &pmpriv->mgmt_ie[index], 0, + sizeof(custom_ie)); + memcpy_ext(pmadapter, + &pmpriv->mgmt_ie[index], + ie_data, sizeof(custom_ie), + sizeof(custom_ie)); + } + + misc->param.cust_ie.len += + pmpriv->mgmt_ie[index].ie_length + + MLAN_CUSTOM_IE_HDR_SIZE; + pioctl_req->action = MLAN_ACT_SET; + cmd_action = HostCmd_ACT_GEN_SET; + } + } + } + + /* Send command to firmware */ + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) { + ret = wlan_prepare_cmd( + pmpriv, HostCmd_CMD_MGMT_IE_LIST, cmd_action, 0, + (send_ioctl) ? (t_void *)pioctl_req : MNULL, + &misc->param.cust_ie); + } +#ifdef UAP_SUPPORT + else if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) { + ret = wlan_prepare_cmd( + pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, cmd_action, 0, + (send_ioctl) ? (t_void *)pioctl_req : MNULL, + (send_ioctl) ? MNULL : &misc->param.cust_ie); + } +#endif + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; +done: + LEAVE(); + return ret; +} + +/** + * @brief Read/write adapter register + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_reg_mem_ioctl_reg_rw(pmlan_adapter pmadapter, + 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: +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + case MLAN_REG_MAC2: +#endif + cmd_no = HostCmd_CMD_MAC_REG_ACCESS; + break; + case MLAN_REG_BBP: +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + case MLAN_REG_BBP2: +#endif + cmd_no = HostCmd_CMD_BBP_REG_ACCESS; + break; + case MLAN_REG_RF: +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + case MLAN_REG_RF2: +#endif + cmd_no = HostCmd_CMD_RF_REG_ACCESS; + break; + case MLAN_REG_CAU: + cmd_no = HostCmd_CMD_CAU_REG_ACCESS; + break; + case MLAN_REG_PSU: + cmd_no = HostCmd_CMD_TARGET_ACCESS; + break; + case MLAN_REG_BCA: +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + case MLAN_REG_BCA2: +#endif + cmd_no = HostCmd_CMD_BCA_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 *)®_mem->param.reg_rw); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Read the EEPROM contents of the card + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_reg_mem_ioctl_read_eeprom(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + reg_mem = (mlan_ds_reg_mem *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_EEPROM_ACCESS, + cmd_action, 0, (t_void *)pioctl_req, + (t_void *)®_mem->param.rd_eeprom); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Read/write memory of device + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_reg_mem_ioctl_mem_rw(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + reg_mem = (mlan_ds_reg_mem *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0, + (t_void *)pioctl_req, + (t_void *)®_mem->param.mem_rw); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function will check if station list is empty + * + * @param priv A pointer to mlan_private + * + * @return MFALSE/MTRUE + */ +t_u8 wlan_is_station_list_empty(mlan_private *priv) +{ + ENTER(); + if (!(util_peek_list(priv->adapter->pmoal_handle, &priv->sta_list, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock))) { + LEAVE(); + return MTRUE; + } + LEAVE(); + return MFALSE; +} + +/** + * @brief This function will return the pointer to station entry in station + * list table which matches the give mac address + * + * @param priv A pointer to mlan_private + * @param mac mac address to find in station list table + * + * @return A pointer to structure sta_node + */ +sta_node *wlan_get_station_entry(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr; + + ENTER(); + + if (!mac) { + LEAVE(); + return MNULL; + } + sta_ptr = (sta_node *)util_peek_list( + priv->adapter->pmoal_handle, &priv->sta_list, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + + while (sta_ptr && (sta_ptr != (sta_node *)&priv->sta_list)) { + if (!memcmp(priv->adapter, sta_ptr->mac_addr, mac, + MLAN_MAC_ADDR_LENGTH)) { + LEAVE(); + return sta_ptr; + } + sta_ptr = sta_ptr->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief This function will add a pointer to station entry in station list + * table with the give mac address, if it does not exist already + * + * @param priv A pointer to mlan_private + * @param mac mac address to find in station list table + * + * @return A pointer to structure sta_node + */ +sta_node *wlan_add_station_entry(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr = MNULL; + mlan_adapter *pmadapter = priv->adapter; + + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) + goto done; + if (priv->adapter->callbacks.moal_malloc(priv->adapter->pmoal_handle, + sizeof(sta_node), MLAN_MEM_DEF, + (t_u8 **)&sta_ptr)) { + PRINTM(MERROR, "Failed to allocate memory for station node\n"); + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + LEAVE(); + return MNULL; + } + memset(priv->adapter, sta_ptr, 0, sizeof(sta_node)); + memcpy_ext(priv->adapter, sta_ptr->mac_addr, mac, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + util_enqueue_list_tail(priv->adapter->pmoal_handle, &priv->sta_list, + (pmlan_linked_list)sta_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + IsAuthenticatorEnabled(priv->psapriv)) + authenticator_init_client(priv->psapriv, + &sta_ptr->cm_connectioninfo, mac); +#endif +done: + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return sta_ptr; +} + +/** + * @brief This function will delete a station entry from station list + * + * + * @param priv A pointer to mlan_private + * @param mac station's mac address + * + * @return N/A + */ +t_void wlan_delete_station_entry(mlan_private *priv, t_u8 *mac) +{ + sta_node *sta_ptr = MNULL; + mlan_adapter *pmadapter = priv->adapter; + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + sta_ptr = wlan_get_station_entry(priv, mac); + if (sta_ptr) { +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + IsAuthenticatorEnabled(priv->psapriv)) + authenticator_free_client(priv->psapriv, + sta_ptr->cm_connectioninfo); +#endif + util_unlink_list(priv->adapter->pmoal_handle, &priv->sta_list, + (pmlan_linked_list)sta_ptr, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + priv->adapter->callbacks.moal_mfree(priv->adapter->pmoal_handle, + (t_u8 *)sta_ptr); + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return; +} + +/** + * @brief Clean up wapi station list + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +t_void wlan_delete_station_list(pmlan_private priv) +{ + sta_node *sta_ptr; + + ENTER(); + while ((sta_ptr = (sta_node *)util_dequeue_list( + priv->adapter->pmoal_handle, &priv->sta_list, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock))) { +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + IsAuthenticatorEnabled(priv->psapriv)) + authenticator_free_client(priv->psapriv, + sta_ptr->cm_connectioninfo); +#endif + priv->adapter->callbacks.moal_mfree(priv->adapter->pmoal_handle, + (t_u8 *)sta_ptr); + } + LEAVE(); + return; +} + +/** + * @brief Set mimo switch 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_mimo_switch_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_radio_cfg *radio_cfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_MIMO_SWITCH, 0, 0, + (t_void *)pioctl_req, + &(radio_cfg->param.mimo_switch_cfg)); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get extended version information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_get_info_ver_ext(pmlan_adapter pmadapter, + 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; +} + +/** + * @brief This function convert mlan_wifi_rate to wifi_rate. + * + * @param pmpriv A pointer to mlan_private structure + * @param rateStats wifi_rate_stat array + * @param pnum_rate A pointer to num_rate + * + * @return N/A + */ +t_void wlan_fill_hal_wifi_rate_in_host(pmlan_private pmpriv, + OUT wifi_rate_stat rateStats[], + t_u32 *pnumRate) +{ + t_u32 total_num_rate = 0; + t_u32 mcs_idx = 0; + t_u8 index = 0; + t_u8 rate_info = 0; + + ENTER(); + + /* HT MCS */ + for (mcs_idx = 0; mcs_idx < MCS_NUM_SUPP; mcs_idx++) { + /* 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */ + rateStats[total_num_rate].rate.preamble = 2; + /* 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz */ + rateStats[total_num_rate].rate.bw = 0; + rateStats[total_num_rate].rate.rateMcsIdx = mcs_idx; + index = rateStats[total_num_rate].rate.rateMcsIdx; + rate_info = MLAN_RATE_FORMAT_HT | + (rateStats[total_num_rate].rate.bw << 2); + rateStats[total_num_rate].rate.bitrate = + wlan_index_to_data_rate(pmpriv->adapter, index, + rate_info, 0) * + 5; + PRINTM(MINFO, "HT[%d] index=0x%x rate_info=0x%x bitrate=0x%x\n", + total_num_rate, index, rate_info, + rateStats[total_num_rate].rate.bitrate / 5); + + /* Get Tx mpdu */ + rateStats[total_num_rate].tx_mpdu = 0; + rateStats[total_num_rate].rx_mpdu = 0; + + /* Todo: mpdu_lost/retries*, need extend GetTxRxRateInfo */ + rateStats[total_num_rate].mpdu_lost = 0xC1; + rateStats[total_num_rate].retries = 0xC2; + rateStats[total_num_rate].retries_short = 0xC3; + rateStats[total_num_rate].retries_long = 0xC4; + + total_num_rate++; + } + + /* VHT MCS */ + for (mcs_idx = 0; mcs_idx < VHT_NUM_SUPPORT_MCS; mcs_idx++) { + /* 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */ + rateStats[total_num_rate].rate.preamble = 3; + /* 0:1x1, 1:2x2, 3:3x3, 4:4x4 */ + rateStats[total_num_rate].rate.nss = 0; + /* 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz */ + rateStats[total_num_rate].rate.bw = 0; + rateStats[total_num_rate].rate.rateMcsIdx = mcs_idx; + /* nss 2 ? bw 20MHZ ? */ + index = rateStats[total_num_rate].rate.rateMcsIdx | + (rateStats[total_num_rate].rate.nss << 4); + rate_info = MLAN_RATE_FORMAT_VHT | + (rateStats[total_num_rate].rate.bw << 2); + rateStats[total_num_rate].rate.bitrate = + wlan_index_to_data_rate(pmpriv->adapter, index, + rate_info, 0) * + 5; + PRINTM(MINFO, + "VHT[%d] index=0x%x rate_info=0x%x bitrate=0x%x\n", + total_num_rate, index, rate_info, + rateStats[total_num_rate].rate.bitrate / 5); + + rateStats[total_num_rate].tx_mpdu = 0; + rateStats[total_num_rate].rx_mpdu = 0; + + /* Todo: mpdu_lost/retries*, need extend GetTxRxRateInfo */ + rateStats[total_num_rate].mpdu_lost = 0xC1; + rateStats[total_num_rate].retries = 0xC2; + rateStats[total_num_rate].retries_short = 0xC3; + rateStats[total_num_rate].retries_long = 0xC4; + + total_num_rate++; + } + + *pnumRate = total_num_rate; + + LEAVE(); +} + +/** + * @brief This function fill link layer statistic from firmware + * + * @param priv A pointer to + * mlan_private structure + * @param link_statistic_ioctl_buf, A pointer to fill ioctl buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static void wlan_fill_link_statistic_in_host(mlan_private *priv, + char *link_statistic_ioctl_buf) +{ + char *link_statistic = link_statistic_ioctl_buf; + wifi_radio_stat *radio_stat = MNULL; + wifi_iface_stat *iface_stat = MNULL; + t_u32 num_radio = MAX_RADIO; + int i = 0, chan_idx = 0; + t_u32 num_peers = 0; + sta_node *sta_ptr = MNULL; +#ifdef WMM + t_u8 *ptid = MNULL; +#endif + + ENTER(); + + *((t_u32 *)link_statistic) = num_radio; + link_statistic += sizeof(num_radio); + + /* Fill radio stats array */ + for (i = 0; i < num_radio; i++) { + radio_stat = (wifi_radio_stat *)link_statistic; + link_statistic += sizeof(wifi_radio_stat); + + radio_stat->radio = 0xF0; + + radio_stat->on_time = 0; + radio_stat->tx_time = 0; + radio_stat->reserved0 = 0; + radio_stat->rx_time = 0; + radio_stat->on_time_scan = 0; + radio_stat->on_time_nbd = 0; + radio_stat->on_time_gscan = 0; + radio_stat->on_time_roam_scan = 0; + radio_stat->on_time_pno_scan = 0; + radio_stat->on_time_hs20 = 0; + + radio_stat->num_channels = 1; + for (chan_idx = 0; chan_idx < radio_stat->num_channels; + chan_idx++) { + if (radio_stat->num_channels > MAX_NUM_CHAN) { + radio_stat->num_channels = MAX_NUM_CHAN; + PRINTM(MERROR, + "%s : radio_stat->num_channels=%d\n", + __func__, radio_stat->num_channels); + break; + } + + if (priv->bss_role == MLAN_BSS_ROLE_STA) { + if (priv->media_connected) { + radio_stat->channels[chan_idx] + .channel.width = + priv->curr_bss_params + .bss_descriptor + .curr_bandwidth; + radio_stat->channels[chan_idx] + .channel.center_freq = + priv->curr_bss_params + .bss_descriptor.freq; + radio_stat->channels[chan_idx] + .channel.center_freq0 = 0; + radio_stat->channels[chan_idx] + .channel.center_freq1 = 0; + } + } else { + radio_stat->channels[chan_idx].channel.width = + priv->uap_state_chan_cb.bandcfg + .chanWidth; + radio_stat->channels[chan_idx] + .channel + .center_freq = wlan_11d_chan_2_freq( + priv->adapter, + priv->uap_state_chan_cb.channel, + (priv->uap_state_chan_cb.channel > 14) ? + BAND_A : + BAND_G); + radio_stat->channels[chan_idx] + .channel.center_freq0 = 0; + radio_stat->channels[chan_idx] + .channel.center_freq1 = 0; + } + radio_stat->channels[chan_idx].on_time = 0xE3; + radio_stat->channels[chan_idx].cca_busy_time = 0xE4; + } + } + + /* Fill iface stats*/ + iface_stat = (wifi_iface_stat *)link_statistic; + + /* get wifi_interface_link_layer_info in driver, not in firmware */ + if (priv->bss_role == MLAN_BSS_ROLE_STA) { + iface_stat->info.mode = MLAN_INTERFACE_STA; + if (priv->media_connected) + iface_stat->info.state = MLAN_ASSOCIATING; + else + iface_stat->info.state = MLAN_DISCONNECTED; + iface_stat->info.roaming = MLAN_ROAMING_IDLE; + iface_stat->info.capabilities = MLAN_CAPABILITY_QOS; + memcpy_ext(priv->adapter, iface_stat->info.ssid, + priv->curr_bss_params.bss_descriptor.ssid.ssid, + MLAN_MAX_SSID_LENGTH, MLAN_MAX_SSID_LENGTH); + memcpy_ext(priv->adapter, iface_stat->info.bssid, + priv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + } else { + iface_stat->info.mode = MLAN_INTERFACE_SOFTAP; + iface_stat->info.capabilities = MLAN_CAPABILITY_QOS; + } + memcpy_ext(priv->adapter, iface_stat->info.mac_addr, priv->curr_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + memcpy_ext(priv->adapter, iface_stat->info.ap_country_str, + priv->adapter->country_code, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + memcpy_ext(priv->adapter, iface_stat->info.country_str, + priv->adapter->country_code, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + + iface_stat->beacon_rx = 0; + iface_stat->average_tsf_offset = 0; + iface_stat->leaky_ap_detected = 0; + iface_stat->leaky_ap_avg_num_frames_leaked = 0; + iface_stat->leaky_ap_guard_time = 0; + + /* Value of iface_stat should be Reaccumulate by each peer */ + iface_stat->mgmt_rx = 0; + iface_stat->mgmt_action_rx = 0; + iface_stat->mgmt_action_tx = 0; + + iface_stat->rssi_mgmt = 0; + iface_stat->rssi_data = 0; + iface_stat->rssi_ack = 0; + +#ifdef WMM + for (i = WMM_AC_BK; i <= WMM_AC_VO; i++) { + iface_stat->ac[i].ac = i; + ptid = ac_to_tid[i]; + iface_stat->ac[i].tx_mpdu = priv->wmm.packets_out[ptid[0]] + + priv->wmm.packets_out[ptid[1]]; + iface_stat->ac[i].rx_mpdu = 0; + iface_stat->ac[i].tx_mcast = 0; + iface_stat->ac[i].rx_mcast = 0; + iface_stat->ac[i].rx_ampdu = 0; + iface_stat->ac[i].tx_ampdu = 0; + iface_stat->ac[i].mpdu_lost = 0; + iface_stat->ac[i].retries = 0; + iface_stat->ac[i].retries_short = 0; + iface_stat->ac[i].retries_long = 0; + iface_stat->ac[i].contention_time_min = 0; + iface_stat->ac[i].contention_time_max = 0; + iface_stat->ac[i].contention_time_avg = 0; + iface_stat->ac[i].contention_num_samples = 0; + } +#endif + + if (priv->bss_role == MLAN_BSS_ROLE_STA) { + if (priv->media_connected) { + iface_stat->peer_info[0].type = WIFI_PEER_AP; + memcpy_ext( + priv->adapter, + iface_stat->peer_info[0].peer_mac_address, + priv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + iface_stat->peer_info[0].capabilities = + MLAN_CAPABILITY_QOS; + wlan_fill_hal_wifi_rate_in_host( + priv, iface_stat->peer_info[0].rate_stats, + &(iface_stat->peer_info[0].num_rate)); + num_peers = 1; + } + } else { + sta_ptr = (sta_node *)util_peek_list( + priv->adapter->pmoal_handle, &priv->sta_list, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + if (sta_ptr) { + while (sta_ptr != (sta_node *)&priv->sta_list) { + iface_stat->peer_info[num_peers].type = + WIFI_PEER_STA; + memcpy_ext(priv->adapter, + iface_stat->peer_info[num_peers] + .peer_mac_address, + sta_ptr->mac_addr, + MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + iface_stat->peer_info[num_peers].capabilities = + MLAN_CAPABILITY_QOS; + wlan_fill_hal_wifi_rate_in_host( + priv, + iface_stat->peer_info[num_peers] + .rate_stats, + &(iface_stat->peer_info[num_peers] + .num_rate)); + num_peers++; + + sta_ptr = sta_ptr->pnext; + } + } + } + iface_stat->num_peers = num_peers; + + LEAVE(); +} + +/** + * @brief Set/Get link layer 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 + */ +mlan_status wlan_ioctl_link_statistic(mlan_private *pmpriv, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_get_info *info = MNULL; + t_u8 *link_statistic = MNULL; + + ENTER(); + + /* Check 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; + } + + /** We will not send HostCmd_CMD_802_11_LINK_STATS to FW */ + if (pioctl_req->action == MLAN_ACT_GET) { + info = (mlan_ds_get_info *)pioctl_req->pbuf; + link_statistic = info->param.link_statistic; + /** Get the LL STATS from driver */ + wlan_fill_link_statistic_in_host(pmpriv, link_statistic); + DBG_HEXDUMP( + MCMD_D, + "wlan_ioctl_link_statistic() link_statistic in host", + (t_u8 *)link_statistic, 800); + } + ret = MLAN_STATUS_SUCCESS; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get TX/RX histogram statistic + * + * @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_tx_rx_histogram(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_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; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_TX_RX_PKT_STATS, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + &(pmisc->param.tx_rx_histogram)); + + 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(pmlan_adapter pmadapter, pmlan_ioctl_req pioctl_req) +{ + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Set driver debug bit masks */ + mlan_drvdbg = misc->param.drvdbg; + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Rx mgmt frame forward register + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_reg_rx_mgmt_ind(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Set passthru mask for mgmt frame */ + pmpriv->mgmt_frame_passthru_mask = misc->param.mgmt_subtype_mask; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_RX_MGMT_IND, + pioctl_req->action, 0, (t_void *)pioctl_req, + &misc->param.mgmt_subtype_mask); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function processes the 802.11 mgmt Frame + * + * @param priv A pointer to mlan_private + * + * @param payload A pointer to the received buffer + * @param payload_len Length of the received buffer + * @param prx_pd A pointer to RxPD + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_process_802dot11_mgmt_pkt(mlan_private *priv, t_u8 *payload, + t_u32 payload_len, RxPD *prx_pd) +{ + pmlan_adapter pmadapter = priv->adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + wlan_802_11_header *pieee_pkt_hdr = MNULL; + t_u16 sub_type = 0; + t_u8 *event_buf = MNULL; + mlan_event *pevent = MNULL; + t_u8 unicast = 0; + t_u8 broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + IEEE80211_MGMT *mgmt = MNULL; + t_u8 category = 0; + t_u8 action_code = 0; +#ifdef UAP_SUPPORT + sta_node *sta_ptr = MNULL; + MrvlIETypes_MgmtFrameSet_t *tlv; + pmlan_buffer pmbuf; +#endif + + ENTER(); + if (payload_len > (MAX_EVENT_SIZE - sizeof(mlan_event))) { + PRINTM(MERROR, "Dropping large mgmt frame,len =%d\n", + payload_len); + LEAVE(); + return ret; + } + /* Check packet type-subtype and compare with mgmt_passthru_mask + * If event is needed to host, just eventify it */ + pieee_pkt_hdr = (wlan_802_11_header *)payload; + sub_type = IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(pieee_pkt_hdr->frm_ctl); + if (((1 << sub_type) & priv->mgmt_frame_passthru_mask) == 0) { + PRINTM(MINFO, "Dropping mgmt frame for subtype %d snr=%d.\n", + sub_type, prx_pd->snr); + LEAVE(); + return ret; + } + switch (sub_type) { + case SUBTYPE_ASSOC_REQUEST: + case SUBTYPE_REASSOC_REQUEST: +#ifdef UAP_SUPPORT + if (priv->uap_host_based & UAP_FLAG_HOST_MLME) { + PRINTM_NETINTF(MMSG, priv); + if (!memcmp(pmadapter, pieee_pkt_hdr->addr3, + priv->curr_addr, MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MMSG, + "wlan: HostMlme MICRO_AP_STA_ASSOC " MACSTR + "\n", + MAC2STR(pieee_pkt_hdr->addr2)); + mgmt = (IEEE80211_MGMT *)payload; + sta_ptr = wlan_add_station_entry( + priv, pieee_pkt_hdr->addr2); + if (sta_ptr) { + sta_ptr->capability = wlan_le16_to_cpu( + mgmt->u.assoc_req.capab_info); + pmbuf = wlan_alloc_mlan_buffer( + pmadapter, payload_len, 0, + MOAL_MALLOC_BUFFER); + if (pmbuf) { + PRINTM(MCMND, + "check sta capability\n"); + pmbuf->data_len = + ASSOC_EVENT_FIX_SIZE; + tlv = (MrvlIETypes_MgmtFrameSet_t + *)(pmbuf->pbuf + + pmbuf->data_offset + + pmbuf->data_len); + tlv->type = wlan_cpu_to_le16( + TLV_TYPE_MGMT_FRAME); + tlv->len = sizeof( + IEEEtypes_FrameCtl_t); + memcpy_ext( + pmadapter, + (t_u8 *)&tlv + ->frame_control, + &pieee_pkt_hdr->frm_ctl, + sizeof(IEEEtypes_FrameCtl_t), + sizeof(IEEEtypes_FrameCtl_t)); + pmbuf->data_len += sizeof( + MrvlIETypes_MgmtFrameSet_t); + memcpy_ext( + pmadapter, + pmbuf->pbuf + + pmbuf->data_offset + + pmbuf->data_len, + payload + + sizeof(wlan_802_11_header), + payload_len - + sizeof(wlan_802_11_header), + payload_len - + sizeof(wlan_802_11_header)); + pmbuf->data_len += + payload_len - + sizeof(wlan_802_11_header); + tlv->len += + payload_len - + sizeof(wlan_802_11_header); + tlv->len = wlan_cpu_to_le16( + tlv->len); + DBG_HEXDUMP( + MCMD_D, "assoc_req", + pmbuf->pbuf + + pmbuf->data_offset, + pmbuf->data_len); + wlan_check_sta_capability( + priv, pmbuf, sta_ptr); + wlan_free_mlan_buffer(pmadapter, + pmbuf); + } + } + } else { + PRINTM(MMSG, + "wlan: Drop MICRO_AP_STA_ASSOC " MACSTR + " from unknown BSSID " MACSTR "\n", + MAC2STR(pieee_pkt_hdr->addr2), + MAC2STR(pieee_pkt_hdr->addr3)); + } + } + unicast = MTRUE; + break; +#endif + case SUBTYPE_AUTH: + unicast = MTRUE; + PRINTM_NETINTF(MMSG, priv); + PRINTM(MMSG, "wlan: HostMlme Auth received from " MACSTR "\n", + MAC2STR(pieee_pkt_hdr->addr2)); + break; + case SUBTYPE_PROBE_RESP: + unicast = MTRUE; + break; + case SUBTYPE_DISASSOC: + case SUBTYPE_DEAUTH: + if (memcmp(pmadapter, pieee_pkt_hdr->addr1, broadcast, + MLAN_MAC_ADDR_LENGTH)) + unicast = MTRUE; +#ifdef UAP_SUPPORT + if (priv->uap_host_based & UAP_FLAG_HOST_MLME) { + if (!memcmp(pmadapter, pieee_pkt_hdr->addr3, + priv->curr_addr, MLAN_MAC_ADDR_LENGTH)) { + PRINTM_NETINTF(MMSG, priv); + PRINTM(MMSG, + "wlan: HostMlme Deauth Receive from " MACSTR + "\n", + MAC2STR(pieee_pkt_hdr->addr2)); + } + } +#endif + if (priv->bss_role == MLAN_BSS_ROLE_STA) { + if (priv->curr_bss_params.host_mlme) { + if (memcmp(pmadapter, pieee_pkt_hdr->addr2, + (t_u8 *)priv->curr_bss_params + .bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MINFO, + "Dropping mgmt frame from others: type=%d " MACSTR + "\n", + sub_type, + MAC2STR(pieee_pkt_hdr->addr2)); + LEAVE(); + return ret; + } + PRINTM_NETINTF(MMSG, priv); + PRINTM(MMSG, + "wlan: HostMlme Disconnected: sub_type=%d\n", + sub_type); + pmadapter->pending_disconnect_priv = priv; + wlan_recv_event( + priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + break; + case SUBTYPE_ACTION: + category = *(payload + sizeof(wlan_802_11_header)); + action_code = *(payload + sizeof(wlan_802_11_header) + 1); + if (category == IEEE_MGMT_ACTION_CATEGORY_BLOCK_ACK) { + PRINTM(MINFO, + "Drop BLOCK ACK action frame: action_code=%d\n", + action_code); + LEAVE(); + return ret; + } + if ((category == IEEE_MGMT_ACTION_CATEGORY_PUBLIC) && + (action_code == BSS_20_40_COEX)) { + PRINTM(MINFO, + "Drop 20/40 BSS Coexistence Management frame\n"); + LEAVE(); + return ret; + } + if (memcmp(pmadapter, pieee_pkt_hdr->addr1, broadcast, + MLAN_MAC_ADDR_LENGTH)) + unicast = MTRUE; + break; + default: + break; + } + if (unicast == MTRUE) { + if (memcmp(pmadapter, pieee_pkt_hdr->addr1, priv->curr_addr, + MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MINFO, + "Dropping mgmt frame for others: type=%d " MACSTR + "\n", + sub_type, MAC2STR(pieee_pkt_hdr->addr1)); + LEAVE(); + return ret; + } + } + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, MAX_EVENT_SIZE, + MLAN_MEM_DEF, &event_buf); + if ((ret != MLAN_STATUS_SUCCESS) || !event_buf) { + PRINTM(MERROR, "Could not allocate buffer for event buf\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pevent = (pmlan_event)event_buf; + pevent->bss_index = priv->bss_index; + mgmt = (IEEE80211_MGMT *)payload; + if (!priv->curr_bss_params.host_mlme && sub_type == SUBTYPE_ACTION && + mgmt->u.ft_resp.category == FT_CATEGORY && + mgmt->u.ft_resp.action == FT_ACTION_RESPONSE && + mgmt->u.ft_resp.status_code == 0) { + PRINTM(MCMND, "FT Action response received\n"); +#define FT_ACTION_HEAD_LEN (24 + 6 + 16) + pevent->event_id = MLAN_EVENT_ID_DRV_FT_RESPONSE; + pevent->event_len = + payload_len + MLAN_MAC_ADDR_LENGTH - FT_ACTION_HEAD_LEN; + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + &mgmt->u.ft_resp.target_ap_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + memcpy_ext(pmadapter, + (t_u8 *)(pevent->event_buf + MLAN_MAC_ADDR_LENGTH), + payload + FT_ACTION_HEAD_LEN, + payload_len - FT_ACTION_HEAD_LEN, + pevent->event_len - MLAN_MAC_ADDR_LENGTH); + } else if (!priv->curr_bss_params.host_mlme && + sub_type == SUBTYPE_AUTH && + mgmt->u.auth.auth_alg == MLAN_AUTH_MODE_FT && + mgmt->u.auth.auth_transaction == 2 && + mgmt->u.auth.status_code == 0) { + PRINTM(MCMND, "FT auth response received \n"); +#define AUTH_PACKET_LEN (24 + 6 + 6) + pevent->event_id = MLAN_EVENT_ID_DRV_FT_RESPONSE; + pevent->event_len = + payload_len + MLAN_MAC_ADDR_LENGTH - AUTH_PACKET_LEN; + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, mgmt->sa, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + memcpy_ext(pmadapter, + (t_u8 *)(pevent->event_buf + MLAN_MAC_ADDR_LENGTH), + payload + AUTH_PACKET_LEN, + payload_len - AUTH_PACKET_LEN, + pevent->event_len - MLAN_MAC_ADDR_LENGTH); + } else { + pevent->event_id = MLAN_EVENT_ID_DRV_MGMT_FRAME; + pevent->event_len = payload_len + sizeof(pevent->event_id); + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + (t_u8 *)&pevent->event_id, sizeof(pevent->event_id), + pevent->event_len); + memcpy_ext( + pmadapter, + (t_u8 *)(pevent->event_buf + sizeof(pevent->event_id)), + payload, payload_len, payload_len); + } + wlan_recv_event(priv, pevent->event_id, pevent); + if (event_buf) + pcb->moal_mfree(pmadapter->pmoal_handle, event_buf); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef STA_SUPPORT +/** + * @brief Extended capabilities configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_ext_capa_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (MLAN_ACT_GET == pioctl_req->action) + memcpy_ext(pmpriv->adapter, &misc->param.ext_cap, + &pmpriv->def_ext_cap, sizeof(misc->param.ext_cap), + sizeof(misc->param.ext_cap)); + else if (MLAN_ACT_SET == pioctl_req->action) { + memcpy_ext(pmpriv->adapter, &pmpriv->ext_cap, + &misc->param.ext_cap, sizeof(misc->param.ext_cap), + sizeof(pmpriv->ext_cap)); + /* Save default Extended Capability */ + memcpy_ext(pmpriv->adapter, &pmpriv->def_ext_cap, + &pmpriv->ext_cap, sizeof(pmpriv->ext_cap), + sizeof(pmpriv->def_ext_cap)); + if (pmpriv->config_bands & BAND_AAC) + SET_EXTCAP_OPERMODENTF(pmpriv->ext_cap); + } + + LEAVE(); + return ret; +} + +/** + * @brief Check whether Extended Capabilities IE support + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MTRUE or MFALSE; + */ +t_u32 wlan_is_ext_capa_support(mlan_private *pmpriv) +{ + ENTER(); + + if (ISSUPP_EXTCAP_TDLS(pmpriv->ext_cap) || + ISSUPP_EXTCAP_INTERWORKING(pmpriv->ext_cap) || + ISSUPP_EXTCAP_BSS_TRANSITION(pmpriv->ext_cap) || + ISSUPP_EXTCAP_QOS_MAP(pmpriv->ext_cap) || + ISSUPP_EXTCAP_OPERMODENTF(pmpriv->ext_cap)) { + LEAVE(); + return MTRUE; + } else { + LEAVE(); + return MFALSE; + } +} +#endif + +/** + * @brief Set hotspot enable/disable + * + * @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_hotspot_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (MLAN_ACT_GET == pioctl_req->action) + misc->param.hotspot_cfg = pmpriv->hotspot_cfg; + else if (MLAN_ACT_SET == pioctl_req->action) + pmpriv->hotspot_cfg = misc->param.hotspot_cfg; + + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Add Extended Capabilities IE + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_desc A pointer to BSSDescriptor_t structure + * @param pptlv_out A pointer to TLV to fill in + * + * @return N/A + */ +void wlan_add_ext_capa_info_ie(mlan_private *pmpriv, BSSDescriptor_t *pbss_desc, + t_u8 **pptlv_out) +{ + MrvlIETypes_ExtCap_t *pext_cap = MNULL; + + ENTER(); + + pext_cap = (MrvlIETypes_ExtCap_t *)*pptlv_out; + memset(pmpriv->adapter, pext_cap, 0, sizeof(MrvlIETypes_ExtCap_t)); + pext_cap->header.type = wlan_cpu_to_le16(EXT_CAPABILITY); + pext_cap->header.len = wlan_cpu_to_le16(sizeof(ExtCap_t)); + if (pmpriv->adapter->ecsa_enable) + SET_EXTCAP_EXT_CHANNEL_SWITCH(pmpriv->ext_cap); + else + RESET_EXTCAP_EXT_CHANNEL_SWITCH(pmpriv->ext_cap); + if (wlan_check_11ax_twt_supported(pmpriv, pbss_desc)) + SET_EXTCAP_TWT_REQ(pmpriv->ext_cap); + memcpy_ext(pmpriv->adapter, &pext_cap->ext_cap, &pmpriv->ext_cap, + sizeof(pmpriv->ext_cap), sizeof(pext_cap->ext_cap)); + *pptlv_out += sizeof(MrvlIETypes_ExtCap_t); + + LEAVE(); +} +#endif + +/** + * @brief Get OTP user data + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_otp_user_data(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + + if (misc->param.otp_user_data.user_data_length > + MAX_OTP_USER_DATA_LEN) { + PRINTM(MERROR, "Invalid OTP user data length\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return ret; + } + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_OTP_READ_USER_DATA, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + &misc->param.otp_user_data); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function will search for the specific ie + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * @param sta_ptr A pointer to sta_node + * + * @return N/A + */ +void wlan_check_sta_capability(pmlan_private priv, pmlan_buffer pevent, + sta_node *sta_ptr) +{ + t_u16 tlv_type, tlv_len; + t_u16 frame_control, frame_sub_type = 0; + t_u8 *assoc_req_ie = MNULL; + t_u8 ie_len = 0, assoc_ie_len = 0; + IEEEtypes_HTCap_t *pht_cap = MNULL; + IEEEtypes_VHTCap_t *pvht_cap = MNULL; + IEEEtypes_Extension_t *phe_cap = MNULL; +#ifdef UAP_SUPPORT + t_u8 *ext_rate = MNULL, *erp = MNULL; +#endif + + int tlv_buf_left = pevent->data_len - ASSOC_EVENT_FIX_SIZE; + MrvlIEtypesHeader_t *tlv = + (MrvlIEtypesHeader_t *)(pevent->pbuf + pevent->data_offset + + ASSOC_EVENT_FIX_SIZE); + MrvlIETypes_MgmtFrameSet_t *mgmt_tlv = MNULL; + + ENTER(); + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_MGMT_FRAME) { + mgmt_tlv = (MrvlIETypes_MgmtFrameSet_t *)tlv; + memcpy_ext(priv->adapter, &frame_control, + (t_u8 *)&(mgmt_tlv->frame_control), + sizeof(frame_control), + sizeof(frame_control)); + frame_sub_type = IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE( + frame_control); + if ((mgmt_tlv->frame_control.type == 0) && + ((frame_sub_type == SUBTYPE_BEACON) +#ifdef UAP_SUPPORT + || (frame_sub_type == SUBTYPE_ASSOC_REQUEST) || + (frame_sub_type == SUBTYPE_REASSOC_REQUEST) +#endif + )) { + if (frame_sub_type == SUBTYPE_BEACON) + assoc_ie_len = + sizeof(IEEEtypes_Beacon_t); +#ifdef UAP_SUPPORT + else if (frame_sub_type == + SUBTYPE_ASSOC_REQUEST) + assoc_ie_len = + sizeof(IEEEtypes_AssocRqst_t); + else if (frame_sub_type == + SUBTYPE_REASSOC_REQUEST) + assoc_ie_len = + sizeof(IEEEtypes_ReAssocRqst_t); +#endif + ie_len = tlv_len - + sizeof(IEEEtypes_FrameCtl_t) - + assoc_ie_len; + assoc_req_ie = + (t_u8 *)tlv + + sizeof(MrvlIETypes_MgmtFrameSet_t) + + assoc_ie_len; + sta_ptr->is_wmm_enabled = + wlan_is_wmm_ie_present(priv->adapter, + assoc_req_ie, + ie_len); + PRINTM(MCMND, "STA: is_wmm_enabled=%d\n", + sta_ptr->is_wmm_enabled); + pht_cap = (IEEEtypes_HTCap_t *) + wlan_get_specific_ie(priv, assoc_req_ie, + ie_len, + HT_CAPABILITY, 0); + if (pht_cap) { + PRINTM(MCMND, "STA supports 11n\n"); + sta_ptr->is_11n_enabled = MTRUE; + memcpy_ext(priv->adapter, + (t_u8 *)&sta_ptr->HTcap, + pht_cap, + sizeof(IEEEtypes_HTCap_t), + sizeof(IEEEtypes_HTCap_t)); + if (GETHT_MAXAMSDU(wlan_le16_to_cpu( + pht_cap->ht_cap + .ht_cap_info))) + sta_ptr->max_amsdu = + MLAN_TX_DATA_BUF_SIZE_8K; + else + sta_ptr->max_amsdu = + MLAN_TX_DATA_BUF_SIZE_4K; + } else { + PRINTM(MCMND, + "STA doesn't support 11n\n"); + } + pvht_cap = (IEEEtypes_VHTCap_t *) + wlan_get_specific_ie(priv, assoc_req_ie, + ie_len, + VHT_CAPABILITY, 0); + if (pvht_cap && + (priv->is_11ac_enabled == MTRUE)) { + PRINTM(MCMND, "STA supports 11ac\n"); + sta_ptr->is_11ac_enabled = MTRUE; + if (GET_VHTCAP_MAXMPDULEN(wlan_le32_to_cpu( + pvht_cap->vht_cap + .vht_cap_info)) == + 2) + sta_ptr->max_amsdu = + MLAN_TX_DATA_BUF_SIZE_12K; + else if (GET_VHTCAP_MAXMPDULEN(wlan_le32_to_cpu( + pvht_cap->vht_cap + .vht_cap_info)) == + 1) + 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 11ac\n"); + } + phe_cap = (IEEEtypes_Extension_t *) + wlan_get_specific_ie(priv, assoc_req_ie, + ie_len, EXTENSION, + HE_CAPABILITY); + if (phe_cap && + (priv->is_11ax_enabled == MTRUE)) { + PRINTM(MCMND, "STA supports 11ax\n"); + sta_ptr->is_11ax_enabled = MTRUE; + } else { + PRINTM(MCMND, + "STA doesn't support 11ax\n"); + } +#ifdef UAP_SUPPORT + /* Note: iphone6 does not have ERP_INFO */ + ext_rate = wlan_get_specific_ie( + priv, assoc_req_ie, ie_len, + EXTENDED_SUPPORTED_RATES, 0); + erp = wlan_get_specific_ie(priv, assoc_req_ie, + ie_len, ERP_INFO, 0); + if (!ext_rate) + PRINTM(MCMND, + "STA doesn't support EXTENDED_SUPPORTED_RATES\n"); + if (!erp) + PRINTM(MCMND, + "STA doesn't support ERP_INFO\n"); + if (sta_ptr->is_11ax_enabled) { + if (priv->uap_channel <= 14) + sta_ptr->bandmode = BAND_GAX; + else + sta_ptr->bandmode = BAND_AAX; + } else if (sta_ptr->is_11ac_enabled) { + if (priv->uap_channel <= 14) + sta_ptr->bandmode = BAND_GAC; + else + sta_ptr->bandmode = BAND_AAC; + } else if (sta_ptr->is_11n_enabled) { + if (priv->uap_channel <= 14) + sta_ptr->bandmode = BAND_GN; + else + sta_ptr->bandmode = BAND_AN; + } else if (ext_rate || erp) { + if (priv->uap_channel <= 14) + sta_ptr->bandmode = BAND_G; + else + sta_ptr->bandmode = BAND_A; + } else + sta_ptr->bandmode = BAND_B; +#endif +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IsAuthenticatorEnabled(priv->psapriv)) + authenticator_get_sta_security_info( + priv->psapriv, + sta_ptr->cm_connectioninfo, + assoc_req_ie, ie_len); +#endif + break; + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + LEAVE(); + + return; +} + +/** + * @brief check if WMM ie present. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pbuf A pointer to IE buffer + * @param buf_len IE buffer len + * + * @return MTRUE/MFALSE + */ +t_u8 wlan_is_wmm_ie_present(pmlan_adapter pmadapter, t_u8 *pbuf, t_u16 buf_len) +{ + t_u16 bytes_left = buf_len; + IEEEtypes_ElementId_e element_id; + t_u8 *pcurrent_ptr = pbuf; + t_u8 element_len; + t_u16 total_ie_len; + IEEEtypes_VendorSpecific_t *pvendor_ie; + const t_u8 wmm_oui[4] = {0x00, 0x50, 0xf2, 0x02}; + t_u8 find_wmm_ie = MFALSE; + + ENTER(); + + /* Process variable IE */ + while (bytes_left >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + + if (bytes_left < total_ie_len) { + PRINTM(MERROR, "InterpretIE: Error in processing IE, " + "bytes left < IE length\n"); + bytes_left = 0; + continue; + } + switch (element_id) { + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; + if (!memcmp(pmadapter, pvendor_ie->vend_hdr.oui, + wmm_oui, sizeof(wmm_oui))) { + find_wmm_ie = MTRUE; + PRINTM(MINFO, "find WMM IE\n"); + } + break; + default: + break; + } + pcurrent_ptr += element_len + 2; + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + if (find_wmm_ie) + break; + } + + LEAVE(); + return find_wmm_ie; +} + +/** + * @brief This function will search for the specific ie + * + * + * @param priv A pointer to mlan_private + * @param ie_buf A pointer to ie_buf + * @param ie_len total ie length + * @param id ie's id + * @param ext_id ie's extension 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_u8 ext_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; + t_u8 element_eid; + + ENTER(); + + DBG_HEXDUMP(MDAT_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); + element_eid = *((t_u8 *)pcurrent_ptr + 2); + 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 ((!ext_id && element_id == id) || + (id == EXTENSION && element_id == id && + ext_id == element_eid)) { + PRINTM(MCMND, "Find IE: id=%d ext_id=%d\n", id, ext_id); + DBG_HEXDUMP(MCMD_D, "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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pm_cfg = MNULL; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + pm_cfg->param.ps_info.is_suspend_allowed = MTRUE; + wlan_request_cmd_lock(pmadapter); + if (util_peek_list(pmadapter->pmoal_handle, &pmadapter->cmd_pending_q, + MNULL, MNULL) || + pmadapter->curr_cmd || !wlan_bypass_tx_list_empty(pmadapter) || + !wlan_wmm_lists_empty(pmadapter) +#if defined(SDIO) || defined(PCIE) + || wlan_pending_interrupt(pmadapter) +#endif + ) { + pm_cfg->param.ps_info.is_suspend_allowed = MFALSE; +#if defined(SDIO) || defined(PCIE) + PRINTM(MIOCTL, + "PM: cmd_pending_q=%p,curr_cmd=%p,wmm_list_empty=%d, by_pass=%d irq_pending=%d\n", + util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, MNULL, MNULL), + pmadapter->curr_cmd, wlan_wmm_lists_empty(pmadapter), + wlan_bypass_tx_list_empty(pmadapter), + wlan_pending_interrupt(pmadapter)); +#else + PRINTM(MIOCTL, + "PM: cmd_pending_q=%p,curr_cmd=%p,wmm_list_empty=%d, by_pass=%d\n", + util_peek_list(pmadapter->pmoal_handle, + &pmadapter->cmd_pending_q, MNULL, MNULL), + pmadapter->curr_cmd, wlan_wmm_lists_empty(pmadapter), + wlan_bypass_tx_list_empty(pmadapter)); +#endif + } + wlan_release_cmd_lock(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief Get hs wakeup reason + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +mlan_status wlan_get_hs_wakeup_reason(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + pmlan_ds_pm_cfg pm_cfg = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_HS_WAKEUP_REASON, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + &pm_cfg->param.wakeup_reason); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get radio status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_radio_ioctl_radio_ctl(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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 = MNULL; + mlan_ds_ant_cfg_1x1 *ant_cfg_1x1 = MNULL; + + ENTER(); + + radio_cfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + if (IS_STREAM_2X2(pmadapter->feature_control)) + ant_cfg = &radio_cfg->param.ant_cfg; + + if (pioctl_req->action == MLAN_ACT_SET) { + /* User input validation */ + if (IS_STREAM_2X2(pmadapter->feature_control)) { +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (IS_CARD9098(pmadapter->card_type) || + IS_CARD9097(pmadapter->card_type)) { + ant_cfg->tx_antenna &= 0x0303; + ant_cfg->rx_antenna &= 0x0303; + /** 2G antcfg TX */ + if (ant_cfg->tx_antenna & 0x00FF) { + pmadapter->user_htstream &= ~0xF0; + pmadapter->user_htstream |= + (bitcount(ant_cfg->tx_antenna & + 0x00FF) + << 4); + } + /* 5G antcfg tx */ + if (ant_cfg->tx_antenna & 0xFF00) { + pmadapter->user_htstream &= ~0xF000; + pmadapter->user_htstream |= + (bitcount(ant_cfg->tx_antenna & + 0xFF00) + << 12); + } + /* 2G antcfg RX */ + if (ant_cfg->rx_antenna & 0x00FF) { + pmadapter->user_htstream &= ~0xF; + pmadapter->user_htstream |= bitcount( + ant_cfg->rx_antenna & 0x00FF); + } + /* 5G antcfg RX */ + if (ant_cfg->rx_antenna & 0xFF00) { + pmadapter->user_htstream &= ~0xF00; + pmadapter->user_htstream |= + (bitcount(ant_cfg->rx_antenna & + 0xFF00) + << 8); + } + PRINTM(MCMND, + "user_htstream=0x%x, tx_antenna=0x%x >rx_antenna=0x%x\n", + pmadapter->user_htstream, + ant_cfg->tx_antenna, + ant_cfg->rx_antenna); + } else { +#endif + + ant_cfg->tx_antenna &= 0x0003; + ant_cfg->rx_antenna &= 0x0003; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + } +#endif + if (!ant_cfg->tx_antenna || + bitcount(ant_cfg->tx_antenna & 0x00FF) > + pmadapter->number_of_antenna || + bitcount(ant_cfg->tx_antenna & 0xFF00) > + pmadapter->number_of_antenna) { + PRINTM(MERROR, + "Invalid TX antenna setting: 0x%x\n", + ant_cfg->tx_antenna); + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (ant_cfg->rx_antenna) { + if (bitcount(ant_cfg->rx_antenna & 0x00FF) > + pmadapter->number_of_antenna || + bitcount(ant_cfg->rx_antenna & 0xFF00) > + pmadapter->number_of_antenna) { + PRINTM(MERROR, + "Invalid RX antenna setting: 0x%x\n", + ant_cfg->rx_antenna); + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } else + ant_cfg->rx_antenna = ant_cfg->tx_antenna; + } else if (!radio_cfg->param.ant_cfg_1x1.antenna || + ((radio_cfg->param.ant_cfg_1x1.antenna != + RF_ANTENNA_AUTO) && + (radio_cfg->param.ant_cfg_1x1.antenna & 0xFFFC))) { + PRINTM(MERROR, "Invalid antenna setting\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + cmd_action = HostCmd_ACT_GEN_SET; + } else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Cast it to t_u16, antenna mode for command + * HostCmd_CMD_802_11_RF_ANTENNA requires 2 bytes */ + if (!IS_STREAM_2X2(pmadapter->feature_control)) + ant_cfg_1x1 = &radio_cfg->param.ant_cfg_1x1; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_RF_ANTENNA, + cmd_action, 0, (t_void *)pioctl_req, + (IS_STREAM_2X2(pmadapter->feature_control)) ? + (t_void *)ant_cfg : + (t_void *)ant_cfg_1x1); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get rate bitmap + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_rate_ioctl_get_rate_bitmap(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set rate bitmap + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_rate_ioctl_set_rate_bitmap(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_rate *ds_rate = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 *bitmap_rates = MNULL; + + ENTER(); + + ds_rate = (mlan_ds_rate *)pioctl_req->pbuf; + bitmap_rates = ds_rate->param.rate_cfg.bitmap_rates; + + PRINTM(MINFO, + "RateBitmap=%04x%04x%04x%04x%04x%04x%04x%04x" + "%04x%04x%04x%04x%04x%04x%04x%04x%04x%04x, " + "IsRateAuto=%d, DataRate=%d\n", + bitmap_rates[17], bitmap_rates[16], bitmap_rates[15], + bitmap_rates[14], bitmap_rates[13], bitmap_rates[12], + bitmap_rates[11], bitmap_rates[10], bitmap_rates[9], + bitmap_rates[8], bitmap_rates[7], bitmap_rates[6], + bitmap_rates[5], bitmap_rates[4], bitmap_rates[3], + bitmap_rates[2], bitmap_rates[1], bitmap_rates[0], + pmpriv->is_data_rate_auto, pmpriv->data_rate); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + (t_void *)bitmap_rates); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get rate value + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +static mlan_status wlan_rate_ioctl_get_rate_value(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_rate *rate = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + rate = (mlan_ds_rate *)pioctl_req->pbuf; + rate->param.rate_cfg.is_rate_auto = pmpriv->is_data_rate_auto; + pioctl_req->data_read_written = + sizeof(mlan_rate_cfg_t) + MLAN_SUB_COMMAND_SIZE; + + /* If not connected, set rate to the lowest in each band */ + if (pmpriv->media_connected != MTRUE) { + if (pmpriv->config_bands & (BAND_B | BAND_G)) { + /* Return the lowest supported rate for BG band */ + rate->param.rate_cfg.rate = SupportedRates_BG[0] & 0x7f; + } else if (pmpriv->config_bands & (BAND_A | BAND_B)) { + /* Return the lowest supported rate for A band */ + rate->param.rate_cfg.rate = SupportedRates_BG[0] & 0x7f; + } else if (pmpriv->config_bands & BAND_A) { + /* Return the lowest supported rate for A band */ + rate->param.rate_cfg.rate = SupportedRates_A[0] & 0x7f; + } else if (pmpriv->config_bands & BAND_G) { + /* Return the lowest supported rate for G band */ + rate->param.rate_cfg.rate = SupportedRates_G[0] & 0x7f; + } else if (pmpriv->config_bands & BAND_B) { + /* Return the lowest supported rate for B band */ + rate->param.rate_cfg.rate = SupportedRates_B[0] & 0x7f; + } else if (pmpriv->config_bands & BAND_GN) { + /* Return the lowest supported rate for N band */ + rate->param.rate_cfg.rate = SupportedRates_N[0] & 0x7f; + } else { + PRINTM(MMSG, "Invalid Band 0x%x\n", + pmpriv->config_bands); + } + + } else { + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, + (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} + +/** + * @brief Set rate value + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_rate_ioctl_set_rate_value(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_rate *ds_rate = MNULL; + WLAN_802_11_RATES rates; + t_u8 *rate = MNULL; + int rate_index = 0; + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + t_u32 i = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + ds_rate = (mlan_ds_rate *)pioctl_req->pbuf; + + if (ds_rate->param.rate_cfg.is_rate_auto) { + memset(pmadapter, bitmap_rates, 0, sizeof(bitmap_rates)); + /* Support all HR/DSSS rates */ + bitmap_rates[0] = 0x000F; + /* Support all OFDM rates */ + bitmap_rates[1] = 0x00FF; + /* Rates talbe [0] HR/DSSS,[1] OFDM,[2..9] HT,[10..17] VHT */ + /* Support all HT-MCSs rate */ + for (i = 0; i < NELEMENTS(pmpriv->bitmap_rates) - 3 - 8; i++) + bitmap_rates[i + 2] = 0xFFFF; + bitmap_rates[9] = 0x3FFF; + /* Support all VHT-MCSs rate */ + for (i = 0; i < NELEMENTS(pmpriv->bitmap_rates) - 10; i++) + bitmap_rates[i + 10] = 0x03FF; /* 10 Bits valid */ + } else { + memset(pmadapter, rates, 0, sizeof(rates)); + wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, + (pmpriv->bss_mode == + MLAN_BSS_MODE_INFRA) ? + pmpriv->config_bands : + pmadapter->adhoc_start_band, + rates); + rate = rates; + for (i = 0; (rate[i] && i < WLAN_SUPPORTED_RATES); i++) { + PRINTM(MINFO, "Rate=0x%X Wanted=0x%X\n", rate[i], + ds_rate->param.rate_cfg.rate); + if ((rate[i] & 0x7f) == + (ds_rate->param.rate_cfg.rate & 0x7f)) + break; + } + if ((i < WLAN_SUPPORTED_RATES && !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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + t_u32 rate_index; + t_u32 rate_format; + t_u32 nss; + 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(pmpriv->usr_dev_mcs_support); + + ENTER(); + + ds_rate = (mlan_ds_rate *)pioctl_req->pbuf; + rate_format = ds_rate->param.rate_cfg.rate_format; + nss = ds_rate->param.rate_cfg.nss; + 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; + /* [10..17] VHT */ + /* Support all VHT-MCSs rate for NSS 1 and 2 */ + for (i = 10; i < 12; i++) + bitmap_rates[i] = 0x03FF; /* 10 Bits valid */ + /* Set to 0 as default value for all other NSSs */ + for (i = 12; i < 17; i++) + bitmap_rates[i] = 0x0; + /* [18..25] HE */ + /* Support all HE-MCSs rate for NSS1 and 2 */ + for (i = 18; i < 20; i++) + bitmap_rates[i] = 0x0FFF; + for (i = 20; i < NELEMENTS(bitmap_rates); i++) + bitmap_rates[i] = 0x0; + } else { + PRINTM(MINFO, "Rate index is %d\n", rate_index); + if ((rate_format == MLAN_RATE_FORMAT_HT) && + (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, pmpriv->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)); + if (rate_format == MLAN_RATE_FORMAT_LG) { + /* Bitmap of HR/DSSS rates */ + if (rate_index <= MLAN_RATE_INDEX_HRDSSS3) { + bitmap_rates[0] = 1 << rate_index; + ret = MLAN_STATUS_SUCCESS; + /* Bitmap of OFDM rates */ + } else if ((rate_index >= MLAN_RATE_INDEX_OFDM0) && + (rate_index <= MLAN_RATE_INDEX_OFDM7)) { + bitmap_rates[1] = 1 << (rate_index - + MLAN_RATE_INDEX_OFDM0); + ret = MLAN_STATUS_SUCCESS; + } + } else if (rate_format == MLAN_RATE_FORMAT_HT) { + if (rate_index <= MLAN_RATE_INDEX_MCS32) { + bitmap_rates[2 + (rate_index / 16)] = + 1 << (rate_index % 16); + ret = MLAN_STATUS_SUCCESS; + } + } + if (rate_format == MLAN_RATE_FORMAT_VHT) { + if ((rate_index <= MLAN_RATE_INDEX_MCS9) && + (MLAN_RATE_NSS1 <= nss) && + (nss <= MLAN_RATE_NSS2)) { + bitmap_rates[10 + nss - MLAN_RATE_NSS1] = + (1 << rate_index); + ret = MLAN_STATUS_SUCCESS; + } + } + if (rate_format == MLAN_RATE_FORMAT_HE) { + if (IS_FW_SUPPORT_11AX(pmadapter)) { + if ((rate_index <= MLAN_RATE_INDEX_MCS11) && + (MLAN_RATE_NSS1 <= nss) && + (nss <= MLAN_RATE_NSS2)) { + bitmap_rates[18 + nss - MLAN_RATE_NSS1] = + (1 << rate_index); + ret = MLAN_STATUS_SUCCESS; + } + } else { + PRINTM(MERROR, + "Error! Fw doesn't support 11AX\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "Invalid MCS index=%d. \n", rate_index); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + + PRINTM(MINFO, + "RateBitmap=%04x%04x%04x%04x%04x%04x%04x%04x" + "%04x%04x%04x%04x%04x%04x%04x%04x%04x%04x, " + "IsRateAuto=%d, DataRate=%d\n", + bitmap_rates[17], bitmap_rates[16], bitmap_rates[15], + bitmap_rates[14], bitmap_rates[13], bitmap_rates[12], + bitmap_rates[11], bitmap_rates[10], bitmap_rates[9], + bitmap_rates[8], bitmap_rates[7], bitmap_rates[6], + bitmap_rates[5], bitmap_rates[4], bitmap_rates[3], + bitmap_rates[2], bitmap_rates[1], bitmap_rates[0], + pmpriv->is_data_rate_auto, pmpriv->data_rate); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + (t_void *)bitmap_rates); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Rate configuration command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +mlan_status wlan_rate_ioctl_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_rate *rate = MNULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + rate = (mlan_ds_rate *)pioctl_req->pbuf; + if (rate->param.rate_cfg.rate_type == MLAN_RATE_BITMAP) { + if (pioctl_req->action == MLAN_ACT_GET) + status = wlan_rate_ioctl_get_rate_bitmap(pmadapter, + pioctl_req); + else + status = wlan_rate_ioctl_set_rate_bitmap(pmadapter, + pioctl_req); + } else if (rate->param.rate_cfg.rate_type == MLAN_RATE_VALUE) { + if (pioctl_req->action == MLAN_ACT_GET) + status = wlan_rate_ioctl_get_rate_value(pmadapter, + pioctl_req); + else + status = wlan_rate_ioctl_set_rate_value(pmadapter, + pioctl_req); + } else { + if (pioctl_req->action == MLAN_ACT_GET) + status = wlan_rate_ioctl_get_rate_index(pmadapter, + pioctl_req); + else + status = wlan_rate_ioctl_set_rate_index(pmadapter, + pioctl_req); + } + + LEAVE(); + return status; +} + +/** + * @brief Get data rates + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_rate_ioctl_get_data_rate(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req->action != MLAN_ACT_GET) { + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get remain on channel setting + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_radio_ioctl_remain_chan_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_radio_cfg *radio_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + radio_cfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_REMAIN_ON_CHANNEL, + cmd_action, 0, (t_void *)pioctl_req, + &radio_cfg->param.remain_chan); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief Set/Get wifi_direct_mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_bss_ioctl_wifi_direct_mode(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = MNULL; + + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_WIFI_DIRECT_MODE_CONFIG, + cmd_action, 0, (t_void *)pioctl_req, + &bss->param.wfd_mode); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get p2p config + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_misc_p2p_config(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_P2P_PARAMS_CONFIG, cmd_action, + 0, (t_void *)pioctl_req, + &misc_cfg->param.p2p_config); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set coalesce config + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_coalesce_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_COALESCE_CFG, cmd_action, 0, + (t_void *)pioctl_req, + &misc_cfg->param.coalesce_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set USB packet aggregation 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 + */ +mlan_status wlan_misc_ioctl_aggr_ctrl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_PACKET_AGGR_CTRL, cmd_action, + 0, (t_void *)pioctl_req, + &misc->param.aggr_params); + + if (ret == MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} + +#ifdef USB +/** + * @brief Get/Set USB packet aggregation 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 + */ +mlan_status wlan_misc_ioctl_usb_aggr_ctrl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + if (pmadapter->pcard_usb->fw_usb_aggr == MFALSE) { + PRINTM(MERROR, "USB aggregation not supported by FW\n"); + pioctl_req->status_code = MLAN_ERROR_CMD_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_PACKET_AGGR_OVER_HOST_INTERFACE, + cmd_action, 0, (t_void *)pioctl_req, + &misc->param.usb_aggr_params); + + if (ret == MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get/Set Tx control configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_misc_ioctl_txcontrol(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + pmpriv->pkt_tx_ctrl = misc->param.tx_control; + else + misc->param.tx_control = pmpriv->pkt_tx_ctrl; + + LEAVE(); + return ret; +} + +#ifdef RX_PACKET_COALESCE +/** + * @brief Get/Set RX packet coalescing configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_misc_ioctl_rx_pkt_coalesce_config(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_RX_PKT_COALESCE_CFG, + cmd_action, 0, (t_void *)pioctl_req, + &misc->param.rx_coalesce); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Is any uAP started or STA connected? + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MTRUE/MFALSE + */ +t_bool wlan_check_interface_active(mlan_adapter *pmadapter) +{ + t_bool ret = MFALSE; + pmlan_private pmpriv; + int i; + + if (pmadapter == MNULL) + return MFALSE; + + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv) { +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + ret = pmpriv->uap_bss_started; + else +#endif + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) + ret = pmpriv->media_connected; + } + if (ret) + return MTRUE; + } + + return MFALSE; +} + +/** + * @brief Get/Set DFS REPEATER mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_misc_ioctl_dfs_repeater_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) { + /* Make sure no interface is active + * before setting the dfs repeater mode + */ + if (wlan_check_interface_active(pmadapter)) { + PRINTM(MMSG, "DFS-Repeater active priv found," + " skip enabling the mode.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + cmd_action = HostCmd_ACT_GEN_SET; + } else { + cmd_action = HostCmd_ACT_GEN_GET; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_DFS_REPEATER_MODE, cmd_action, 0, + (t_void *)pioctl_req, &misc->param.dfs_repeater); + +done: + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Low Power Mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_misc_ioctl_low_pwr_mode(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCMD_CONFIG_LOW_POWER_MODE, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + &misc->param.low_pwr_mode); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Configure PMIC in Firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_misc_ioctl_pmic_configure(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_PMIC_CONFIGURE, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/* @brief Set/Get CW Mode Level control + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_misc_ioctl_cwmode_ctrl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 cmd_action = 0; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_CW_MODE_CTRL, cmd_action, 0, + (t_void *)pioctl_req, &misc->param.cwmode); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief push value to stack + * + * @param pmadapter A pointer to mlan_adapter structure + * @param s A pointer to mef_stack + * @param len Length of value + * @param val A pointer to value + * + * @return MLAN_STATUS_SUCCESS or FAIL + */ +inline mlan_status push_n(pmlan_adapter pmadapter, mef_stack *s, t_u8 len, + t_u8 *val) +{ + if ((s->sp + len) <= MAX_NUM_STACK_BYTES) { + memcpy_ext(pmadapter, s->byte + s->sp, val, len, + MAX_NUM_STACK_BYTES - s->sp); + s->sp += len; + return MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, "Stack is full\n"); + return MLAN_STATUS_FAILURE; + } +} + +/** + * @brief push value to stack accoring to operand type + * + * @param pmadapter A pointer to mlan_adapter structure + * @param s A pointer to mef_stack + * @param op A pointer to mef_op + * + * @return MLAN_STATUS_SUCCESS or FAIL + */ +inline mlan_status mef_push(pmlan_adapter pmadapter, mef_stack *s, mef_op *op) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 nbytes; + switch (op->operand_type) { + case OPERAND_DNUM: + ret = push_n(pmadapter, s, 4, op->val); + if (ret == MLAN_STATUS_SUCCESS) + ret = push_n(pmadapter, s, 1, &op->operand_type); + else + ret = MLAN_STATUS_FAILURE; + break; + case OPERAND_BYTE_SEQ: + nbytes = op->val[0]; + if (MLAN_STATUS_SUCCESS == + push_n(pmadapter, s, nbytes, op->val + 1) && + MLAN_STATUS_SUCCESS == push_n(pmadapter, s, 1, op->val) && + MLAN_STATUS_SUCCESS == + push_n(pmadapter, s, 1, &op->operand_type)) + ret = MLAN_STATUS_SUCCESS; + else + ret = MLAN_STATUS_FAILURE; + break; + default: + ret = push_n(pmadapter, s, 1, &op->operand_type); + break; + } + return ret; +} + +/** + * @brief push dnum filter to stack + * + * @param pmadapter A pointer to mlan_adapter structure + * @param s A pointer to mef_stack + * @param filter A pointer to filter item + * + * @return MLAN_STATUS_SUCCESS or FAIL + */ +mlan_status push_filter_dnum_eq(pmlan_adapter pmadapter, mef_stack *s, + mef_filter_t *filter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 dnum; + mef_op op; + + ENTER(); + + if (!filter) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (filter->fill_flag != (FILLING_TYPE | FILLING_PATTERN | + FILLING_OFFSET | FILLING_NUM_BYTES)) { + PRINTM(MERROR, "Filter item fill error\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Format of decimal num: + * | 5 bytes | 5 bytes | 5 bytes | 1 byte | | + * pattern | offset | num of bytes | type (TYPE_DNUM_EQ) | + */ + + /* push pattern */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = OPERAND_DNUM; + dnum = filter->pattern; + memcpy_ext(pmadapter, op.val, &dnum, sizeof(dnum), sizeof(op.val)); + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* push offset */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = OPERAND_DNUM; + dnum = filter->offset; + memcpy_ext(pmadapter, op.val, &dnum, sizeof(dnum), sizeof(op.val)); + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* push num of bytes */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = OPERAND_DNUM; + dnum = filter->num_bytes; + memcpy_ext(pmadapter, op.val, &dnum, sizeof(dnum), sizeof(op.val)); + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* push type */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = TYPE_DNUM_EQ; + ret = mef_push(pmadapter, s, &op); + +done: + LEAVE(); + return ret; +} + +/** + * @brief push byte_eq filter to stack + * + * @param pmadapter A pointer to mlan_adapter structure + * @param s A pointer to mef_stack + * @param filter A pointer to filter item + * + * @return MLAN_STATUS_SUCCESS or FAIL + */ +mlan_status push_filter_byte_eq(pmlan_adapter pmadapter, mef_stack *s, + mef_filter_t *filter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 dnum; + mef_op op; + + ENTER(); + + if (!filter) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (filter->fill_flag != (FILLING_TYPE | FILLING_REPEAT | + FILLING_BYTE_SEQ | FILLING_OFFSET)) { + PRINTM(MERROR, "Filter item fill error\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Format of decimal num: + * | 5 bytes | val | 5 bytes | 1 byte | | + * repeat | bytes seq | offset | type (TYPE_BYTE_EQ) | + */ + + /* push repeat */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = OPERAND_DNUM; + dnum = filter->repeat; + memcpy_ext(pmadapter, op.val, &dnum, sizeof(dnum), sizeof(op.val)); + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* push bytes seq */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = OPERAND_BYTE_SEQ; + op.val[0] = filter->num_byte_seq; + memcpy_ext(pmadapter, &op.val[1], filter->byte_seq, + filter->num_byte_seq, MAX_NUM_BYTE_SEQ); + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* push offset */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = OPERAND_DNUM; + dnum = filter->offset; + memcpy_ext(pmadapter, op.val, &dnum, sizeof(dnum), sizeof(op.val)); + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* push type */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = TYPE_BYTE_EQ; + ret = mef_push(pmadapter, s, &op); + +done: + LEAVE(); + return ret; +} + +/** + * @brief push bite_eq filter to stack + * + * @param pmadapter A pointer to mlan_adapter structure + * @param s A pointer to mef_stack + * @param filter A pointer to filter item + * + * @return MLAN_STATUS_SUCCESS or FAIL + */ +mlan_status push_filter_bit_eq(pmlan_adapter pmadapter, mef_stack *s, + mef_filter_t *filter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 dnum; + mef_op op; + + ENTER(); + + if (!filter) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (filter->fill_flag != (FILLING_TYPE | FILLING_REPEAT | + FILLING_BYTE_SEQ | FILLING_OFFSET)) { + PRINTM(MERROR, "Filter item fill error\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Format of decimal num: + * | val | 5 bytes | val | 1 byte | | + * bytes seq | offset | mask seq | type (TYPE_BIT_EQ) | + */ + + /* push bytes seq */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = OPERAND_BYTE_SEQ; + op.val[0] = filter->num_byte_seq; + memcpy_ext(pmadapter, &op.val[1], filter->byte_seq, + filter->num_byte_seq, MAX_NUM_BYTE_SEQ); + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* push offset */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = OPERAND_DNUM; + dnum = filter->offset; + memcpy_ext(pmadapter, op.val, &dnum, sizeof(dnum), sizeof(op.val)); + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* push mask seq */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = OPERAND_BYTE_SEQ; + op.val[0] = filter->num_mask_seq; + memcpy_ext(pmadapter, &op.val[1], filter->mask_seq, + filter->num_mask_seq, MAX_NUM_BYTE_SEQ); + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* push type */ + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = TYPE_BIT_EQ; + ret = mef_push(pmadapter, s, &op); + +done: + LEAVE(); + return ret; +} + +/** + * @brief push filter to stack + * + * @param pmadapter A pointer to mlan_adapter structure + * @param s A pointer to mef_stack + * @param filter A pointer to filter item + * + * @return MLAN_STATUS_SUCCESS or FAIL + */ +mlan_status wlan_push_filter(pmlan_adapter pmadapter, mef_stack *s, + mef_filter_t *filter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + switch (filter->type) { + case TYPE_DNUM_EQ: + ret = push_filter_dnum_eq(pmadapter, s, filter); + break; + case TYPE_BYTE_EQ: + ret = push_filter_byte_eq(pmadapter, s, filter); + break; + case TYPE_BIT_EQ: + ret = push_filter_bit_eq(pmadapter, s, filter); + break; + default: + PRINTM(MERROR, "Invalid filter type\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + return ret; +} + +/** + * @brief generate mef data + * + * @param pmadapter A pointer to mlan_adapter structure + * @param s A pointer to mef_stack + * @param entry A pointer to mef_entry_t + * + * @return MLAN_STATUS_SUCCESS or FAIL + */ +mlan_status wlan_generate_mef_filter_stack(pmlan_adapter pmadapter, + mef_stack *s, mef_entry_t *entry) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mef_op op; + int i; + + ENTER(); + + for (i = 0; i < entry->filter_num; i++) { + ret = wlan_push_filter(pmadapter, s, &entry->filter_item[i]); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "push filter to stack error\n"); + goto done; + } + if (i != 0) { + memset(pmadapter, &op, 0, sizeof(op)); + op.operand_type = entry->rpn[i]; + ret = mef_push(pmadapter, s, &op); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "push filter rpn error\n"); + goto done; + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set the mef entries to firmware + * + * @param pmpriv A pointer to mlan_private structure + * @param pmadapter A pointer to mlan_adapter structure + * @param pmef A pointer to mef_cfg structure + * + * @return MLAN_STATUS_SUCCESS or FAIL + */ +mlan_status wlan_set_mef_entry(mlan_private *pmpriv, pmlan_adapter pmadapter, + mef_cfg *pmef) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cmd *hostcmd; + HostCmd_DS_GEN *hostcmd_hdr; + HostCmd_DS_MEF_CFG *mef_hdr; + mef_entry_header *entry_hdr; + mef_stack *stack; + mef_entry_t *pentry; + t_u8 *buf; + t_u32 i, buf_len; + pmlan_callbacks pcb; + + ENTER(); + + if (pmef->entry_num > MAX_NUM_ENTRIES) { + PRINTM(MERROR, "Too many entries\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + pcb = &pmadapter->callbacks; + 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 cmd data buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto err_handle; + } + + /** Fill the cmd header data*/ + memset(pmadapter, hostcmd, 0, sizeof(mlan_ds_misc_cmd)); + buf = hostcmd->cmd; + hostcmd_hdr = (HostCmd_DS_GEN *)buf; + hostcmd_hdr->command = wlan_cpu_to_le16(HostCmd_CMD_MEF_CFG); + buf_len = S_DS_GEN; + + /** Fill HostCmd_DS_MEF_CFG*/ + mef_hdr = (HostCmd_DS_MEF_CFG *)(buf + buf_len); + mef_hdr->criteria = wlan_cpu_to_le32(pmef->criteria); + mef_hdr->nentries = wlan_cpu_to_le16(pmef->entry_num); + buf_len += sizeof(HostCmd_DS_MEF_CFG); + + /** generate mef entry data*/ + for (i = 0, pentry = pmef->pentry; i < pmef->entry_num; i++, pentry++) { + /** Fill entry header data*/ + entry_hdr = (mef_entry_header *)(buf + buf_len); + entry_hdr->mode = pentry->mode; + entry_hdr->action = pentry->action; + buf_len += sizeof(mef_entry_header); + + /** Fill Stack data*/ + stack = (mef_stack *)(buf + buf_len); + ret = wlan_generate_mef_filter_stack(pmadapter, stack, pentry); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Generate mef data error\n"); + goto err_handle; + } + buf_len += (stack->sp + sizeof(stack->sp)); + } + hostcmd_hdr->size = wlan_cpu_to_le16(buf_len); + hostcmd->len = wlan_cpu_to_le32(buf_len); + + DBG_HEXDUMP(MCMD_D, "MEF DATA", (t_u8 *)hostcmd, buf_len + 4); + + /** Send command to firmware*/ + ret = wlan_prepare_cmd(pmpriv, 0, 0, 0, (t_void *)MNULL, + (t_void *)hostcmd); + +err_handle: + if (hostcmd) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)hostcmd); +done: + LEAVE(); + return ret; +} + +/* + * @brief generate Host_CMD_MEF_CFG cmd data to firmware + * + * @param pmpriv A pointer to mlan_private structure + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or FAIL + */ +mlan_status wlan_process_mef_cfg_cmd(mlan_private *pmpriv, + pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb; + mef_cfg mef; + mef_entry_t *pentry; + mef_entry *pmef; + t_u16 entry_num = 0; + + ENTER(); + + pcb = &pmadapter->callbacks; + + /** check how many entries in adapter*/ + pmef = &pmadapter->entry_cfg; + entry_num += pmef->num_wowlan_entry; + entry_num += pmef->num_ipv6_ns_offload; + if (!entry_num) { + PRINTM(MIOCTL, "No filter entries\n"); + goto done; + } + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mef_entry_t) * entry_num, MLAN_MEM_DEF, + (t_u8 **)&mef.pentry); + if (ret != MLAN_STATUS_SUCCESS || mef.pentry == MNULL) { + PRINTM(MERROR, "Failed to allocate cmd data buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto err_handle; + } + /** Fill mef_cfg structure*/ + mef.criteria = pmef->criteria; + mef.entry_num = entry_num; + memset(pmadapter, mef.pentry, 0, sizeof(mef_entry_t) * entry_num); + pentry = mef.pentry; + /** Fill mef_entry_t structure*/ + /** Copy wowlan entry*/ + if (pmef->num_wowlan_entry) { + memcpy_ext(pmadapter, pentry, &pmef->entry[6], + sizeof(mef_entry_t), sizeof(mef_entry_t)); + pentry += pmef->num_wowlan_entry; + } + /** Copy IPv6 NS message offload entry */ + if (pmef->num_ipv6_ns_offload) + memcpy_ext(pmadapter, pentry, &pmef->entry[7], + sizeof(mef_entry_t), sizeof(mef_entry_t)); + + /** Set Entries to firmware*/ + ret = wlan_set_mef_entry(pmpriv, pmadapter, &mef); + if (ret != MLAN_STATUS_SUCCESS) + PRINTM(MERROR, "Set MEF entries error\n"); + +err_handle: + if (mef.pentry) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)mef.pentry); +done: + LEAVE(); + return ret; +} + +/* @brief Get/Set NV-FLT-CONFIG parameters + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_misc_ioctl_mef_flt_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = MNULL; + mlan_ds_misc_mef_flt_cfg *mef_cfg = MNULL; + mef_entry *pmef = MNULL; + + ENTER(); + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mef_cfg = &misc_cfg->param.mef_flt_cfg; + pmef = &pmadapter->entry_cfg; + switch (pioctl_req->action) { + case MLAN_ACT_SET: + if (mef_cfg->mef_act_type == MEF_ACT_WOWLAN) { + pmef->num_wowlan_entry = 1; + pmef->criteria |= mef_cfg->criteria; + memcpy_ext(pmadapter, &pmef->entry[6], + &mef_cfg->mef_entry, sizeof(mef_entry_t), + sizeof(mef_entry_t)); + } + if (mef_cfg->mef_act_type == MEF_ACT_IPV6_NS) { + pmef->num_ipv6_ns_offload = 1; + pmef->criteria |= mef_cfg->criteria; + memcpy_ext(pmadapter, &pmef->entry[7], + &mef_cfg->mef_entry, sizeof(mef_entry_t), + sizeof(mef_entry_t)); + } + break; + case MLAN_ACT_GET: + if (mef_cfg->mef_act_type == MEF_ACT_WOWLAN) + memcpy_ext(pmadapter, &mef_cfg->mef_entry, + &pmef->entry[6], sizeof(mef_entry_t), + sizeof(mef_entry_t)); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WPA passphrase for esupplicant + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_sec_ioctl_passphrase(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + t_u16 cmd_action = 0; +#ifdef STA_SUPPORT + BSSDescriptor_t *pbss_desc; + int i = 0; +#endif + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; +#ifdef DRV_EMBEDDED_SUPPLICANT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA && + !IS_FW_SUPPORT_SUPPLICANT(pmpriv->adapter)) { + if (sec->param.passphrase.psk_type == MLAN_PSK_QUERY) + SupplicantQueryPassphrase( + pmpriv->psapriv, + (void *)&sec->param.passphrase); + else if (sec->param.passphrase.psk_type == MLAN_PSK_CLEAR) + SupplicantClearPMK(pmpriv->psapriv, + (void *)&sec->param.passphrase); + else + SupplicantSetPassphrase(pmpriv->psapriv, + (void *)&sec->param.passphrase); + + LEAVE(); + return ret; + } +#endif + + if (!IS_FW_SUPPORT_SUPPLICANT(pmpriv->adapter)) { + LEAVE(); + return ret; + } + + if (pioctl_req->action == MLAN_ACT_SET) { + if (sec->param.passphrase.psk_type == MLAN_PSK_CLEAR) + cmd_action = HostCmd_ACT_GEN_REMOVE; + else + cmd_action = HostCmd_ACT_GEN_SET; + } else if (pioctl_req->action == MLAN_ACT_CLEAR) { + cmd_action = HostCmd_ACT_GEN_REMOVE; + } else { + if (sec->param.passphrase.psk_type == MLAN_PSK_QUERY) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA && + sec->param.passphrase.ssid.ssid_len == 0) { + i = wlan_find_bssid_in_list( + pmpriv, + (t_u8 *)&sec->param.passphrase.bssid, + MLAN_BSS_MODE_AUTO); + if (i >= 0) { + pbss_desc = &pmadapter->pscan_table[i]; + memcpy_ext(pmadapter, + &sec->param.passphrase.ssid, + &pbss_desc->ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + memset(pmadapter, + &sec->param.passphrase.bssid, 0, + MLAN_MAC_ADDR_LENGTH); + PRINTM(MINFO, + "PSK_QUERY: found ssid=%s\n", + sec->param.passphrase.ssid.ssid); + } + } else +#endif + memset(pmadapter, &sec->param.passphrase.bssid, + 0, MLAN_MAC_ADDR_LENGTH); + } + cmd_action = HostCmd_ACT_GEN_GET; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_SUPPLICANT_PMK, cmd_action, + 0, (t_void *)pioctl_req, (t_void *)sec); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set per packet Txctl and Rxinfo configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_misc_per_pkt_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + pmpriv->rx_pkt_info = MFALSE; + if (misc->param.txrx_pkt_ctrl & RX_PKT_INFO) + pmpriv->rx_pkt_info = MTRUE; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get region code + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_region(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + int i; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + misc->param.region_code = pmadapter->region_code; + } else { + if (pmadapter->otp_region && pmadapter->otp_region->force_reg) { + PRINTM(MERROR, + "ForceRegionRule is set in the on-chip OTP" + " memory\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (misc->param.region_code == region_code_index[i]) { + pmadapter->region_code = + (t_u16)misc->param.region_code; + break; + } + } + /* It's unidentified region code */ + if (i >= MRVDRV_MAX_REGION_CODE) { + PRINTM(MERROR, "Region Code not identified\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->cfp_code_bg = misc->param.region_code; + pmadapter->cfp_code_a = misc->param.region_code; + if (wlan_set_regiontable(pmpriv, (t_u8)pmadapter->region_code, + pmadapter->config_bands | + pmadapter->adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + ret = MLAN_STATUS_FAILURE; + } + } + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return ret; +} + +/** + * @brief Configure GPIO independent reset + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_ind_rst_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_INDEPENDENT_RESET_CFG, + cmd_action, 0, (t_void *)pioctl_req, + (t_void *)&misc->param.ind_rst_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get timestamp from firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_get_tsf(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, "No support set tsf!"); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_GET_TSF, cmd_action, 0, + (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Create custom regulatory cfg + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_chan_reg_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, "No support set channel region cfg!"); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_CHAN_REGION_CFG, cmd_action, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Check operating class validation + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_ioctl_operclass_validation(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u8 channel, oper_class; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + channel = misc->param.bw_chan_oper.channel; + oper_class = misc->param.bw_chan_oper.oper_class; + if (pioctl_req->action == MLAN_ACT_GET) { + ret = wlan_check_operclass_validation(pmpriv, channel, + oper_class); + } else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get Region channel power setting + * + * @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_rgchnpwr_cfg(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_CHAN_REGION_CFG, cmd_action, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get CHAN_TPRC setting + * + * @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_chan_trpc_cfg(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + cmd_action = HostCmd_ACT_GEN_GET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CHANNEL_TRPC_CONFIG, cmd_action, + 0, (t_void *)pioctl_req, + (t_void *)&misc->param.trpc_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get non-global operating class + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_ioctl_oper_class(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u8 channel, bandwidth, oper_class = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + channel = misc->param.bw_chan_oper.channel; + switch (misc->param.bw_chan_oper.bandwidth) { + case 20: + bandwidth = BW_20MHZ; + break; + case 40: + bandwidth = BW_40MHZ; + break; + case 80: + bandwidth = BW_80MHZ; + break; + default: + bandwidth = BW_20MHZ; + break; + } + + if (pioctl_req->action == MLAN_ACT_GET) { + ret = wlan_get_curr_oper_class(pmpriv, channel, bandwidth, + &oper_class); + misc->param.bw_chan_oper.oper_class = oper_class; + } else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief config dynamic bandwidth + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_ioctl_fw_dump_event(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_FW_DUMP_EVENT, cmd_action, 0, + (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief config boot sleep + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_bootsleep(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, "Unsupported cmd_action 0x%x\n", + pioctl_req->action); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_BOOT_SLEEP, cmd_action, 0, + (t_void *)pioctl_req, &misc->param.boot_sleep); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @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 + */ +mlan_status wlan_radio_ioctl_band_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + t_u32 i, global_band = 0; + t_u32 infra_band = 0; + t_u32 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 = radio_cfg->param.band_cfg.config_bands; + adhoc_band = 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 |= + (t_u32)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; + } +#ifdef STA_SUPPORT + if (wlan_11d_set_universaltable(pmpriv, + global_band | adhoc_band)) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#endif + 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; + +#ifdef STA_SUPPORT + /* + * 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)) { + 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; + } + +#endif + + } else { + /* Infra Bands */ + radio_cfg->param.band_cfg.config_bands = pmpriv->config_bands; + /* Adhoc Band */ + radio_cfg->param.band_cfg.adhoc_start_band = + pmadapter->adhoc_start_band; + /* Adhoc Channel */ + radio_cfg->param.band_cfg.adhoc_channel = pmpriv->adhoc_channel; + /* FW support Bands */ + radio_cfg->param.band_cfg.fw_bands = pmadapter->fw_bands; + PRINTM(MINFO, "Global config band = %d\n", + pmadapter->config_bands); +#ifdef STA_SUPPORT +#endif + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Rx Abort Cfg + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_rxabortcfg(pmlan_adapter pmadapter, + 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_RX_ABORT_CFG, cmd_action, 0, + (t_void *)pioctl_req, + &(pmisc->param.rx_abort_cfg)); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} +/** + * @brief Rx Abort Cfg ext + * + * @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_rxabortcfg_ext(pmlan_adapter pmadapter, + 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_RX_ABORT_CFG_EXT, cmd_action, + 0, (t_void *)pioctl_req, + &(pmisc->param.rx_abort_cfg_ext)); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Dot11mc unassociated FTM CFG + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_dot11mc_unassoc_ftm_cfg(pmlan_adapter pmadapter, + 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_DOT11MC_UNASSOC_FTM_CFG, + cmd_action, 0, (t_void *)pioctl_req, + &(pmisc->param.dot11mc_unassoc_ftm_cfg)); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Tx ampdu protection 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 + */ +mlan_status wlan_misc_ioctl_tx_ampdu_prot_mode(pmlan_adapter pmadapter, + 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_TX_AMPDU_PROT_MODE, + cmd_action, 0, (t_void *)pioctl_req, + &(pmisc->param.tx_ampdu_prot_mode)); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Rate adapt 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 + */ +mlan_status wlan_misc_ioctl_rate_adapt_cfg(pmlan_adapter pmadapter, + 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_RATE_ADAPT_CFG, cmd_action, + 0, (t_void *)pioctl_req, + &(pmisc->param.rate_adapt_cfg)); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief CCK Desense 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 + */ +mlan_status wlan_misc_ioctl_cck_desense_cfg(pmlan_adapter pmadapter, + 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_CCK_DESENSE_CFG, cmd_action, + 0, (t_void *)pioctl_req, + &(pmisc->param.cck_desense_cfg)); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief config dynamic bandwidth + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_ioctl_dyn_bw(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_DYN_BW, cmd_action, 0, + (t_void *)pioctl_req, &misc->param.dyn_bw); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get low power mode configuration parameter + * + * @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_power_ioctl_set_get_lpm(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_power_cfg *pm_cfg = MNULL; + t_u16 cmd_action = 0, lpm = 0; + + ENTER(); + + pm_cfg = (mlan_ds_power_cfg *)pioctl_req->pbuf; + cmd_action = HostCmd_ACT_GEN_GET; + if (pioctl_req->action == MLAN_ACT_SET) { + cmd_action = HostCmd_ACT_GEN_SET; + lpm = pm_cfg->param.lpm; + } + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_LOW_POWER_MODE_CFG, + cmd_action, 0, (t_void *)pioctl_req, &lpm); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief RF Test Mode 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 + */ +mlan_status wlan_misc_ioctl_rf_test_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = MNULL; + mlan_ds_misc_cfg *pmisc = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + t_u16 cmd_action = 0; + + ENTER(); + + if (!pioctl_req) + goto done; + + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + pmisc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + switch (pmisc->sub_command) { + case MLAN_OID_MISC_RF_TEST_GENERIC: + 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_MFG_COMMAND, + cmd_action, 0, (t_void *)pioctl_req, + &(pmisc->param.mfg_generic_cfg)); + break; + case MLAN_OID_MISC_RF_TEST_TX_CONT: + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MFG_COMMAND, + cmd_action, 0, (t_void *)pioctl_req, + &(pmisc->param.mfg_tx_cont)); + break; + case MLAN_OID_MISC_RF_TEST_TX_FRAME: + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else { + PRINTM(MERROR, "Unsupported cmd_action\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MFG_COMMAND, + cmd_action, 0, (t_void *)pioctl_req, + &(pmisc->param.mfg_tx_frame2)); + break; + } + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; +done: + LEAVE(); + return ret; +} + +/** + * @brief Range ext mode 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 + */ +mlan_status wlan_misc_ioctl_range_ext(pmlan_adapter pmadapter, + 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_RANGE_EXT, cmd_action, 0, + (t_void *)pioctl_req, + &(pmisc->param.range_ext_mode)); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_module.c b/mxm_wifiex/wlan_src/mlan/mlan_module.c new file mode 100644 index 0000000..4e26b90 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_module.c @@ -0,0 +1,65 @@ +/** @file mlan_module.c + * + * @brief This file declares the exported symbols from MLAN. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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); +#ifdef USB +EXPORT_SYMBOL(mlan_write_data_async_complete); +EXPORT_SYMBOL(mlan_recv); +#endif +EXPORT_SYMBOL(mlan_send_packet); +EXPORT_SYMBOL(mlan_ioctl); +EXPORT_SYMBOL(mlan_main_process); +EXPORT_SYMBOL(mlan_rx_process); +EXPORT_SYMBOL(mlan_select_wmm_queue); +#if defined(SDIO) || defined(PCIE) +EXPORT_SYMBOL(mlan_interrupt); +#if defined(SYSKT) +EXPORT_SYMBOL(mlan_hs_callback); +#endif /* SYSKT_MULTI || SYSKT */ +#endif /* SDIO || PCIE */ + +EXPORT_SYMBOL(mlan_pm_wakeup_card); +EXPORT_SYMBOL(mlan_is_main_process_running); +#ifdef PCIE +EXPORT_SYMBOL(mlan_set_int_mode); +#endif + +MODULE_DESCRIPTION("M-WLAN MLAN Driver"); +MODULE_AUTHOR("NXP"); +MODULE_VERSION(MLAN_RELEASE_VERSION); +MODULE_LICENSE("GPL"); +#endif /* LINUX */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_pcie.c b/mxm_wifiex/wlan_src/mlan/mlan_pcie.c new file mode 100644 index 0000000..51e55a9 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_pcie.c @@ -0,0 +1,4205 @@ +/** @file mlan_pcie.c + * + * @brief This file contains PCI-E specific code + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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/01/2012: 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_pcie.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +static mlan_status wlan_pcie_delete_evtbd_ring(pmlan_adapter pmadapter); +static mlan_status wlan_pcie_delete_rxbd_ring(pmlan_adapter pmadapter); + +#if defined(PCIE9098) || defined(PCIE9097) +/** + * @brief This function init the adma setting + * + * @param pmadapter A pointer to mlan_adapter structure + * @param adma_type TX/RX data, event, cmd/cmdresp + * @param pbase physical address + * @param size desc num/dma_size + * @param init init flag + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_init_adma(mlan_adapter *pmadapter, t_u8 type, + t_u64 pbase, t_u16 size, t_u8 init) +{ + t_u32 dma_cfg, dma_cfg2, int_mapping; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 q_addr = 0; + t_u8 direction = 0; + t_u8 dma_mode = 0; + t_u32 msix_data; + t_u32 msix_vector; + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + if (init) + PRINTM(MCMND, "Init ADMA: type=%d, size=%d init=%d\n", type, + size, init); + switch (type) { + case ADMA_TX_DATA: + q_addr = ADMA_CHAN0_Q0; + direction = ADMA_HOST_TO_DEVICE; + dma_mode = DMA_MODE_DUAL_DESC; + msix_vector = ADMA_VECTOR_CHAN0_Q0; + break; + case ADMA_RX_DATA: + q_addr = ADMA_CHAN1_Q0; + direction = ADMA_DEVICE_TO_HOST; + dma_mode = DMA_MODE_DUAL_DESC; + msix_vector = AMDA_VECTOR_CHAN1_Q0; + break; + case ADMA_EVENT: + q_addr = ADMA_CHAN1_Q1; + direction = ADMA_DEVICE_TO_HOST; + dma_mode = DMA_MODE_DUAL_DESC; + msix_vector = AMDA_VECTOR_CHAN1_Q1; + break; + case ADMA_CMD: + q_addr = ADMA_CHAN2_Q0; + direction = ADMA_HOST_TO_DEVICE; + dma_mode = DMA_MODE_DIRECT; + msix_vector = AMDA_VECTOR_CHAN2_Q0; + break; + case ADMA_CMDRESP: + q_addr = ADMA_CHAN2_Q1; + direction = ADMA_DEVICE_TO_HOST; + dma_mode = DMA_MODE_DIRECT; + msix_vector = AMDA_VECTOR_CHAN2_Q1; + break; + default: + PRINTM(MERROR, "unknow adma type\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + if (ret) + goto done; + if (init) { + if (dma_mode == DMA_MODE_DUAL_DESC) { + if (direction == ADMA_HOST_TO_DEVICE) + int_mapping = DEST_INT_TO_DEVICE; + else + int_mapping = DEST_INT_TO_HOST; + } else { + int_mapping = 0; + } + /* set INT_MAPPING register */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + ADMA_INT_MAPPING, + (t_u32)int_mapping)) { + PRINTM(MERROR, + "Failed to write ADMA_INT_MAPPING register.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Read the dma_cfg2 register */ + if (pcb->moal_read_reg(pmadapter->pmoal_handle, + q_addr + ADMA_DMA_CFG2, &dma_cfg2)) { + PRINTM(MERROR, "Fail to read DMA CFG2 register\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + dma_cfg2 &= ~(ADMA_SRC_DMA_DONE_INT_BYPASS_EN | + ADMA_DST_DMA_DONE_INT_BYPASS_EN | + ADMA_MSI_LEGACY_SRC_DMA_DONE_INT_BYPASS_EN | + ADMA_MSI_LEGACY_DST_DMA_DONE_INT_BYPASS_EN | + ADMA_MSI_LEGACY_ENABLE | ADMA_MSIX_ENABLE | + ADMA_MSIX_INT_SRC_DST_SEL); + + if (dma_mode == DMA_MODE_DUAL_DESC) { + if (direction == ADMA_HOST_TO_DEVICE) { + dma_cfg2 |= ADMA_SRC_DMA_DONE_INT_BYPASS_EN; + if (pmadapter->pcard_pcie->pcie_int_mode != + PCIE_INT_MODE_MSIX) + dma_cfg2 |= + ADMA_MSI_LEGACY_SRC_DMA_DONE_INT_BYPASS_EN; + } else { + dma_cfg2 |= ADMA_DST_DMA_DONE_INT_BYPASS_EN; + if (pmadapter->pcard_pcie->pcie_int_mode != + PCIE_INT_MODE_MSIX) + dma_cfg2 |= + ADMA_MSI_LEGACY_DST_DMA_DONE_INT_BYPASS_EN; + } + } else { + if (direction == ADMA_HOST_TO_DEVICE) + dma_cfg2 |= ADMA_SRC_ADDR_IS_HOST; + else + dma_cfg2 |= ADMA_DST_ADDR_IS_HOST; + } + + if (pmadapter->pcard_pcie->pcie_int_mode == + PCIE_INT_MODE_MSIX) { + if (pcb->moal_read_reg(pmadapter->pmoal_handle, + q_addr + ADMA_MSIX_DOORBELL_DATA, + &msix_data)) { + PRINTM(MERROR, + "Fail to read DMA MSIX data register\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + msix_data &= ~ADMA_MSIX_VECTOR_MASK; + msix_data &= ~ADMA_MSIX_PF_MASK; + msix_data |= msix_vector; + msix_data |= pmadapter->pcard_pcie->func_num + << ADMA_MSIX_PF_BIT; + PRINTM(MCMND, "msix_data=0x%x\n", msix_data); + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + + ADMA_MSIX_DOORBELL_DATA, + (t_u32)msix_data)) { + PRINTM(MERROR, + "Failed to write DMA DOORBELL_DATA.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + dma_cfg2 |= ADMA_MSIX_ENABLE; + } else + dma_cfg2 |= ADMA_MSI_LEGACY_ENABLE; + PRINTM(MCMND, "dma_cfg2=0x%x\n", dma_cfg2); + + /* enable INT_BYPASS_EN in the dma_cfg2 register */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + ADMA_DMA_CFG2, + (t_u32)dma_cfg2)) { + PRINTM(MERROR, "Failed to write DMA CFG2.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + /* Read the TX ring read pointer set by firmware */ + if (pcb->moal_read_reg(pmadapter->pmoal_handle, q_addr + ADMA_DMA_CFG, + &dma_cfg)) { + PRINTM(MERROR, "Fail to read DMA CFG register\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (direction == ADMA_HOST_TO_DEVICE) { + /* Write the lower 32bits of the physical address to + * ADMA_SRC_LOW */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + ADMA_SRC_LOW, (t_u32)pbase)) { + PRINTM(MERROR, "Failed to write ADMA_SRC_LOW.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Write the upper 32bits of the physical address to + * ADMA_SRC_HIGH */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + ADMA_SRC_HIGH, + (t_u32)((t_u64)pbase >> 32))) { + PRINTM(MERROR, "Failed to write ADMA_SRC_HIGH.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (init) { + /** Enable DMA done interrupt */ + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, + q_addr + ADMA_SRC_INT_STATUS_MASK, + DEF_ADMA_INT_MASK)) { + PRINTM(MERROR, + "Failed to write ADMA_SRC_INT_STATUS_MASK.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + ADMA_SRC_INT_MASK, + DEF_ADMA_INT_MASK)) { + PRINTM(MERROR, + "Failed to write ADMA_SRC_INT_MASK.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (dma_mode == DMA_MODE_DUAL_DESC) { + dma_cfg &= ~(DESC_MODE_MASK | DMA_MODE_MASK | + SRC_NUM_DESC_MASK | DMA_SIZE_MASK); + dma_cfg |= (t_u32)size << SRC_NUM_DESC_BIT; + dma_cfg |= DESC_MODE_RING << 2; + } else { + dma_cfg &= ~(DESC_MODE_MASK | DMA_MODE_MASK | + SRC_NUM_DESC_MASK | DST_NUM_DESC_MASK | + DMA_SIZE_MASK); + dma_cfg |= (t_u32)size << DMA_SIZE_BIT; + } + } else { + /* Write the lower 32bits of the physical address to + * ADMA_DST_LOW */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + ADMA_DST_LOW, (t_u32)pbase)) { + PRINTM(MERROR, "Failed to write ADMA_DST_LOW.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Write the upper 32bits of the physical address to + * ADMA_DST_HIGH */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + ADMA_DST_HIGH, + (t_u32)((t_u64)pbase >> 32))) { + PRINTM(MERROR, "Failed to write ADMA_DST_HIGH.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (init && (dma_mode == DMA_MODE_DUAL_DESC)) { + /** Enable DMA done interrupt */ + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, + q_addr + ADMA_DST_INT_STATUS_MASK, + DEF_ADMA_INT_MASK)) { + PRINTM(MERROR, + "Failed to write ADMA_SRC_INT_STATUS_MASK.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + ADMA_DST_INT_MASK, + DEF_ADMA_INT_MASK)) { + PRINTM(MERROR, + "Failed to write ADMA_SRC_INT_MASK.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (dma_mode == DMA_MODE_DUAL_DESC) { + dma_cfg &= ~(DESC_MODE_MASK | DMA_MODE_MASK | + DST_NUM_DESC_MASK | DMA_SIZE_MASK); + dma_cfg |= (t_u32)size << DST_NUM_DESC_BIT; + dma_cfg |= DESC_MODE_RING << 2; + } else { + dma_cfg &= ~(DESC_MODE_MASK | DMA_MODE_MASK | + SRC_NUM_DESC_MASK | DST_NUM_DESC_MASK); + } + } + dma_cfg |= (t_u32)dma_mode; + if (pcb->moal_write_reg(pmadapter->pmoal_handle, q_addr + ADMA_DMA_CFG, + dma_cfg)) { + PRINTM(MERROR, "Fail to set DMA CFG register\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (type == ADMA_CMD && !init) { + /* Write 1 to src_wr_ptr to trigger direct dma */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + q_addr + ADMA_SRC_RW_PTR, 1)) { + PRINTM(MERROR, "Failed to write ADMA_SRC_HIGH.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +done: + LEAVE(); + return ret; +} +#endif + +#if defined(PCIE9098) || defined(PCIE9097) +/** + * @brief This function set the host interrupt select mask + * + * @param pmadapter A pointer to mlan_adapter structure + * @param enable 0-disable 1-enable + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_set_host_int_select_mask(mlan_adapter *pmadapter, + t_u8 enable) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 int_sel_mask = 0; + t_u32 int_clr_mask = 0; + ENTER(); + + if (enable) { + int_sel_mask = PCIE9098_HOST_INTR_SEL_MASK; + int_clr_mask = pmadapter->pcard_pcie->reg->host_intr_mask; + } + + /* Simply write the mask to the register */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, PCIE9098_HOST_INT_SEL, + int_sel_mask)) { + PRINTM(MWARN, "Set host interrupt select register failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (pmadapter->pcard_pcie->pcie_int_mode == PCIE_INT_MODE_MSI) { + if (!pmadapter->pcard_pcie->reg->msi_int_wr_clr) { + /** enable read to clear interrupt */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_host_int_clr_sel, + int_clr_mask)) { + PRINTM(MWARN, + "enable read to clear interrupt failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif + +#if defined(PCIE9098) || defined(PCIE9097) +/** + * @brief This function handles command response completion + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_send_vdll_complete(mlan_adapter *pmadapter) +{ + mlan_buffer *pcmdbuf; + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + /*unmap the cmd pmbuf, so the cpu can not access the memory in the + * command node*/ + pcmdbuf = pmadapter->pcard_pcie->vdll_cmd_buf; + if (pcmdbuf) { + pcb->moal_unmap_memory(pmadapter->pmoal_handle, + pcmdbuf->pbuf + pcmdbuf->data_offset, + pcmdbuf->buf_pa, pcmdbuf->data_len, + PCI_DMA_TODEVICE); + pmadapter->pcard_pcie->vdll_cmd_buf = MNULL; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function downloads VDLL image to the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_send_vdll(mlan_adapter *pmadapter, + mlan_buffer *pmbuf) +{ + mlan_status ret = MLAN_STATUS_PENDING; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u16 *tmp; + t_u8 *payload; + + ENTER(); + pmadapter->cmd_sent = MTRUE; + payload = pmbuf->pbuf + pmbuf->data_offset; + + tmp = (t_u16 *)&payload[0]; + *tmp = wlan_cpu_to_le16((t_u16)pmbuf->data_len); + tmp = (t_u16 *)&payload[2]; + *tmp = wlan_cpu_to_le16(MLAN_TYPE_VDLL); + + if (MLAN_STATUS_FAILURE == + pcb->moal_map_memory( + pmadapter->pmoal_handle, pmbuf->pbuf + pmbuf->data_offset, + &pmbuf->buf_pa, pmbuf->data_len, PCI_DMA_TODEVICE)) { + PRINTM(MERROR, + "PCIE - Download VDLL block: moal_map_memory failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->pcard_pcie->vdll_cmd_buf = pmbuf; + /* issue the DMA */ + /* send the VDLL block down to the firmware */ + wlan_init_adma(pmadapter, ADMA_CMD, pmbuf->buf_pa, pmbuf->data_len, + MFALSE); + + PRINTM(MINFO, "PCIE - Download VDLL Block: successful.\n"); +done: + if (ret == MLAN_STATUS_FAILURE) + pmadapter->cmd_sent = MFALSE; + + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function disables the host interrupt + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_disable_host_int_mask(mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_host_int_mask, + 0x00000000)) { + PRINTM(MWARN, "Disable host interrupt failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function enables the host interrupt + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_enable_host_int_mask(mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + /* Simply write the mask to the register */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_host_int_mask, + pmadapter->pcard_pcie->reg->host_intr_mask)) { + PRINTM(MWARN, "Enable host interrupt failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function enables the host interrupts. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param enable 0-disable 1-enable + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status +wlan_pcie_enable_host_int_status_mask(mlan_adapter *pmadapter, t_u8 enable) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 host_int_status_mask = 0; + ENTER(); + if (enable) + host_int_status_mask = + pmadapter->pcard_pcie->reg->host_intr_mask; + /* Enable host int status mask */ + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_host_int_status_mask, + host_int_status_mask)) { + PRINTM(MWARN, "Enable host interrupt status mask failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function disables the host interrupts. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_disable_pcie_host_int(mlan_adapter *pmadapter) +{ + mlan_status ret; + + ENTER(); + ret = wlan_pcie_enable_host_int_status_mask(pmadapter, MFALSE); + if (ret) { + LEAVE(); + return ret; + } +#if defined(PCIE9098) || defined(PCIE9097) + if ((pmadapter->card_type == CARD_TYPE_PCIE9098) || + (pmadapter->card_type == CARD_TYPE_PCIE9097)) { + ret = wlan_pcie_set_host_int_select_mask(pmadapter, MFALSE); + if (ret) { + LEAVE(); + return ret; + } + } +#endif + ret = wlan_pcie_disable_host_int_mask(pmadapter); + 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_clear_pending_int_status(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 pcie_ireg = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (pmadapter->pcard_pcie->pcie_int_mode == PCIE_INT_MODE_MSIX) + goto done; + if (pcb->moal_read_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_host_int_status, + &pcie_ireg)) { + PRINTM(MERROR, "Read host int status register failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { + PRINTM(MMSG, "pcie_ireg=0x%x\n", pcie_ireg); + if (pmadapter->pcard_pcie->reg->msi_int_wr_clr) { + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_host_int_status, + ~pcie_ireg)) { + PRINTM(MERROR, + "Write host int status register failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @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_pcie_host_int(mlan_adapter *pmadapter) +{ + mlan_status ret; + + ENTER(); + wlan_clear_pending_int_status(pmadapter); + ret = wlan_pcie_enable_host_int_status_mask(pmadapter, MTRUE); + if (ret) { + LEAVE(); + return ret; + } +#if defined(PCIE9098) || defined(PCIE9097) + if ((pmadapter->card_type == CARD_TYPE_PCIE9098) || + (pmadapter->card_type == CARD_TYPE_PCIE9097)) { + ret = wlan_pcie_set_host_int_select_mask(pmadapter, MTRUE); + if (ret) { + LEAVE(); + return ret; + } + } +#endif + ret = wlan_pcie_enable_host_int_mask(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief This function creates buffer descriptor ring for TX + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_create_txbd_ring(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 i; +#if defined(PCIE8997) || defined(PCIE8897) + pmlan_pcie_data_buf ptx_bd_buf; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + padma_dual_desc_buf padma_bd_buf; +#endif + + ENTER(); + /* + * driver maintaines the write pointer and firmware maintaines the read + * pointer. + */ + pmadapter->pcard_pcie->txbd_wrptr = 0; + pmadapter->pcard_pcie->txbd_pending = 0; + pmadapter->pcard_pcie->txbd_rdptr = 0; + + /* allocate shared memory for the BD ring and divide the same in to + several descriptors */ +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) + pmadapter->pcard_pcie->txbd_ring_size = + sizeof(mlan_pcie_data_buf) * MLAN_MAX_TXRX_BD; +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) + pmadapter->pcard_pcie->txbd_ring_size = + sizeof(adma_dual_desc_buf) * MLAN_MAX_TXRX_BD; +#endif + PRINTM(MINFO, "TX ring: allocating %d bytes\n", + pmadapter->pcard_pcie->txbd_ring_size); + + ret = pcb->moal_malloc_consistent( + pmadapter->pmoal_handle, pmadapter->pcard_pcie->txbd_ring_size, + &pmadapter->pcard_pcie->txbd_ring_vbase, + &pmadapter->pcard_pcie->txbd_ring_pbase); + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "%s: No free moal_malloc_consistent\n", + __FUNCTION__); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MINFO, + "TX ring: - base: %p, pbase: %#x:%x," + "len: %x\n", + pmadapter->pcard_pcie->txbd_ring_vbase, + (t_u32)((t_u64)pmadapter->pcard_pcie->txbd_ring_pbase >> 32), + (t_u32)pmadapter->pcard_pcie->txbd_ring_pbase, + pmadapter->pcard_pcie->txbd_ring_size); + + for (i = 0; i < MLAN_MAX_TXRX_BD; i++) { + pmadapter->pcard_pcie->tx_buf_list[i] = MNULL; +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = + (adma_dual_desc_buf + *)(pmadapter->pcard_pcie + ->txbd_ring_vbase + + (sizeof(adma_dual_desc_buf) * i)); + pmadapter->pcard_pcie->txbd_ring[i] = + (t_void *)padma_bd_buf; + padma_bd_buf->paddr = 0; + padma_bd_buf->len = 0; + padma_bd_buf->flags = + ADMA_BD_FLAG_INT_EN | ADMA_BD_FLAG_SRC_HOST | + ADMA_BD_FLAG_SOP | ADMA_BD_FLAG_EOP; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + } +#endif + +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + ptx_bd_buf = + (mlan_pcie_data_buf + *)(pmadapter->pcard_pcie + ->txbd_ring_vbase + + (sizeof(mlan_pcie_data_buf) * i)); + pmadapter->pcard_pcie->txbd_ring[i] = + (t_void *)ptx_bd_buf; + ptx_bd_buf->paddr = 0; + ptx_bd_buf->len = 0; + ptx_bd_buf->flags = 0; + ptx_bd_buf->frag_len = 0; + ptx_bd_buf->offset = 0; + } +#endif + } + LEAVE(); + return ret; +} + +/** + * @brief This function frees TX buffer descriptor ring + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_delete_txbd_ring(mlan_adapter *pmadapter) +{ + t_u32 i; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer *pmbuf = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + mlan_pcie_data_buf *ptx_bd_buf; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf; +#endif + + ENTER(); + + for (i = 0; i < MLAN_MAX_TXRX_BD; i++) { + if (pmadapter->pcard_pcie->tx_buf_list[i]) { + pmbuf = pmadapter->pcard_pcie->tx_buf_list[i]; + pcb->moal_unmap_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->buf_pa, + MLAN_RX_DATA_BUF_SIZE, + PCI_DMA_TODEVICE); + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_FAILURE); + } + pmadapter->pcard_pcie->tx_buf_list[i] = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + ptx_bd_buf = + (mlan_pcie_data_buf *) + pmadapter->pcard_pcie->txbd_ring[i]; + + if (ptx_bd_buf) { + ptx_bd_buf->paddr = 0; + ptx_bd_buf->len = 0; + ptx_bd_buf->flags = 0; + ptx_bd_buf->frag_len = 0; + ptx_bd_buf->offset = 0; + } + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = + (adma_dual_desc_buf *) + pmadapter->pcard_pcie->txbd_ring[i]; + + if (padma_bd_buf) { + padma_bd_buf->paddr = 0; + padma_bd_buf->len = 0; + padma_bd_buf->flags = 0; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + } + } +#endif + pmadapter->pcard_pcie->txbd_ring[i] = MNULL; + } + + if (pmadapter->pcard_pcie->txbd_ring_vbase) { + pmadapter->callbacks.moal_mfree_consistent( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->txbd_ring_size, + pmadapter->pcard_pcie->txbd_ring_vbase, + pmadapter->pcard_pcie->txbd_ring_pbase); + } + pmadapter->pcard_pcie->txbd_pending = 0; + pmadapter->pcard_pcie->txbd_ring_size = 0; + pmadapter->pcard_pcie->txbd_wrptr = 0; + pmadapter->pcard_pcie->txbd_rdptr = 0; + pmadapter->pcard_pcie->txbd_ring_vbase = MNULL; + pmadapter->pcard_pcie->txbd_ring_pbase = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function creates buffer descriptor ring for RX + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_create_rxbd_ring(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer *pmbuf = MNULL; + t_u32 i; +#if defined(PCIE8997) || defined(PCIE8897) + mlan_pcie_data_buf *prxbd_buf; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf; +#endif + + ENTER(); + + pmadapter->pcard_pcie->rxbd_rdptr = 0; +#if defined(PCIE8997) || defined(PCIE8897) + /* + * driver maintaines the write pointer and firmware maintaines the read + * pointer. The read pointer starts at 0 (zero) while the write pointer + * starts at zero with rollover bit set + */ + if (!pmadapter->pcard_pcie->reg->use_adma) { + pmadapter->pcard_pcie->rxbd_wrptr = + pmadapter->pcard_pcie->reg->txrx_rw_ptr_rollover_ind; + /* allocate shared memory for the BD ring and divide the same in + to several descriptors */ + pmadapter->pcard_pcie->rxbd_ring_size = + sizeof(mlan_pcie_data_buf) * MLAN_MAX_TXRX_BD; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + /* + * driver maintaines the write pointer and firmware maintaines the read + * pointer. The read pointer starts at 0 (zero) while the write pointer + * starts at MLAN_MAX_TXRX_BD + */ + if (pmadapter->pcard_pcie->reg->use_adma) { + pmadapter->pcard_pcie->rxbd_wrptr = MLAN_MAX_TXRX_BD; + pmadapter->pcard_pcie->rxbd_ring_size = + sizeof(adma_dual_desc_buf) * MLAN_MAX_TXRX_BD; + } +#endif + + PRINTM(MINFO, "RX ring: allocating %d bytes\n", + pmadapter->pcard_pcie->rxbd_ring_size); + + ret = pcb->moal_malloc_consistent( + pmadapter->pmoal_handle, pmadapter->pcard_pcie->rxbd_ring_size, + &pmadapter->pcard_pcie->rxbd_ring_vbase, + &pmadapter->pcard_pcie->rxbd_ring_pbase); + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "%s: No free moal_malloc_consistent\n", + __FUNCTION__); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MINFO, + "RX ring: - base: %p, pbase: %#x:%x," + "len: %#x\n", + pmadapter->pcard_pcie->rxbd_ring_vbase, + (t_u32)((t_u64)pmadapter->pcard_pcie->rxbd_ring_pbase >> 32), + (t_u32)pmadapter->pcard_pcie->rxbd_ring_pbase, + pmadapter->pcard_pcie->rxbd_ring_size); + + for (i = 0; i < MLAN_MAX_TXRX_BD; i++) { + /* Allocate buffer here so that firmware can DMA data on it */ + pmbuf = wlan_alloc_mlan_buffer(pmadapter, MLAN_RX_DATA_BUF_SIZE, + MLAN_RX_HEADER_LEN, + MOAL_ALLOC_MLAN_BUFFER); + if (!pmbuf) { + PRINTM(MERROR, + "RX ring create : Unable to allocate mlan_buffer\n"); + wlan_pcie_delete_rxbd_ring(pmadapter); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pmadapter->pcard_pcie->rx_buf_list[i] = pmbuf; + + if (MLAN_STATUS_FAILURE == + pcb->moal_map_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + &pmbuf->buf_pa, MLAN_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE)) { + PRINTM(MERROR, + "Rx ring create : moal_map_memory failed\n"); + wlan_pcie_delete_rxbd_ring(pmadapter); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MINFO, + "RX ring: add new mlan_buffer base: %p, " + "buf_base: %p, buf_pbase: %#x:%x, " + "buf_len: %#x\n", + pmbuf, pmbuf->pbuf, (t_u32)((t_u64)pmbuf->buf_pa >> 32), + (t_u32)pmbuf->buf_pa, pmbuf->data_len); + +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + prxbd_buf = + (mlan_pcie_data_buf + *)(pmadapter->pcard_pcie + ->rxbd_ring_vbase + + (sizeof(mlan_pcie_data_buf) * i)); + pmadapter->pcard_pcie->rxbd_ring[i] = + (t_void *)prxbd_buf; + prxbd_buf->paddr = pmbuf->buf_pa; + prxbd_buf->len = (t_u16)pmbuf->data_len; + prxbd_buf->flags = MLAN_BD_FLAG_SOP | MLAN_BD_FLAG_EOP; + prxbd_buf->offset = 0; + prxbd_buf->frag_len = (t_u16)pmbuf->data_len; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = + (adma_dual_desc_buf + *)(pmadapter->pcard_pcie + ->rxbd_ring_vbase + + (sizeof(adma_dual_desc_buf) * i)); + pmadapter->pcard_pcie->rxbd_ring[i] = + (t_void *)padma_bd_buf; + padma_bd_buf->paddr = pmbuf->buf_pa; + padma_bd_buf->len = + ALIGN_SZ(pmbuf->data_len, ADMA_ALIGN_SIZE); + padma_bd_buf->flags = + ADMA_BD_FLAG_INT_EN | ADMA_BD_FLAG_DST_HOST; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + } +#endif + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function frees RX buffer descriptor ring + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_delete_rxbd_ring(mlan_adapter *pmadapter) +{ + t_u32 i; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer *pmbuf = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + mlan_pcie_data_buf *prxbd_buf; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf; +#endif + + ENTER(); + for (i = 0; i < MLAN_MAX_TXRX_BD; i++) { + if (pmadapter->pcard_pcie->rx_buf_list[i]) { + pmbuf = pmadapter->pcard_pcie->rx_buf_list[i]; + pcb->moal_unmap_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->buf_pa, + MLAN_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE); + wlan_free_mlan_buffer( + pmadapter, + pmadapter->pcard_pcie->rx_buf_list[i]); + } + pmadapter->pcard_pcie->rx_buf_list[i] = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + prxbd_buf = (mlan_pcie_data_buf *) + pmadapter->pcard_pcie->rxbd_ring[i]; + + if (prxbd_buf) { + prxbd_buf->paddr = 0; + prxbd_buf->offset = 0; + prxbd_buf->frag_len = 0; + } + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = + (adma_dual_desc_buf *) + pmadapter->pcard_pcie->rxbd_ring[i]; + + if (padma_bd_buf) { + padma_bd_buf->paddr = 0; + padma_bd_buf->flags = 0; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + padma_bd_buf->len = 0; + } + } +#endif + pmadapter->pcard_pcie->rxbd_ring[i] = MNULL; + } + + if (pmadapter->pcard_pcie->rxbd_ring_vbase) + pmadapter->callbacks.moal_mfree_consistent( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->rxbd_ring_size, + pmadapter->pcard_pcie->rxbd_ring_vbase, + pmadapter->pcard_pcie->rxbd_ring_pbase); + pmadapter->pcard_pcie->rxbd_ring_size = 0; + pmadapter->pcard_pcie->rxbd_rdptr = 0; + pmadapter->pcard_pcie->rxbd_wrptr = 0; + pmadapter->pcard_pcie->rxbd_ring_vbase = MNULL; + pmadapter->pcard_pcie->rxbd_ring_pbase = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function creates buffer descriptor ring for Events + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_create_evtbd_ring(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer *pmbuf = MNULL; + t_u32 i; +#if defined(PCIE8997) || defined(PCIE8897) + pmlan_pcie_evt_buf pevtbd_buf; +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf; +#endif + + ENTER(); + /* + * driver maintaines the write pointer and firmware maintaines the read + * pointer. The read pointer starts at 0 (zero) while the write pointer + * starts at zero with rollover bit set + */ + pmadapter->pcard_pcie->evtbd_rdptr = 0; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + pmadapter->pcard_pcie->evtbd_wrptr = EVT_RW_PTR_ROLLOVER_IND; + pmadapter->pcard_pcie->evtbd_ring_size = + sizeof(mlan_pcie_evt_buf) * MLAN_MAX_EVT_BD; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + pmadapter->pcard_pcie->evtbd_wrptr = MLAN_MAX_EVT_BD; + pmadapter->pcard_pcie->evtbd_ring_size = + sizeof(adma_dual_desc_buf) * MLAN_MAX_EVT_BD; + } +#endif + PRINTM(MINFO, "Evt ring: allocating %d bytes\n", + pmadapter->pcard_pcie->evtbd_ring_size); + + ret = pcb->moal_malloc_consistent( + pmadapter->pmoal_handle, pmadapter->pcard_pcie->evtbd_ring_size, + &pmadapter->pcard_pcie->evtbd_ring_vbase, + &pmadapter->pcard_pcie->evtbd_ring_pbase); + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "%s: No free moal_malloc_consistent\n", + __FUNCTION__); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + PRINTM(MINFO, + "Evt ring: - base: %p, pbase: %#x:%x," + "len: %#x\n", + pmadapter->pcard_pcie->evtbd_ring_vbase, + (t_u32)((t_u64)pmadapter->pcard_pcie->evtbd_ring_pbase >> 32), + (t_u32)pmadapter->pcard_pcie->evtbd_ring_pbase, + pmadapter->pcard_pcie->evtbd_ring_size); + + for (i = 0; i < MLAN_MAX_EVT_BD; i++) { + /* Allocate buffer here so that firmware can DMA data on it */ + pmbuf = wlan_alloc_mlan_buffer(pmadapter, MAX_EVENT_SIZE, + MLAN_RX_HEADER_LEN, + MOAL_ALLOC_MLAN_BUFFER); + if (!pmbuf) { + PRINTM(MERROR, + "Event ring create : Unable to allocate mlan_buffer\n"); + wlan_pcie_delete_evtbd_ring(pmadapter); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pmadapter->pcard_pcie->evt_buf_list[i] = pmbuf; + + if (MLAN_STATUS_FAILURE == + pcb->moal_map_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + &pmbuf->buf_pa, MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE)) { + PRINTM(MERROR, + "Event ring create : moal_map_memory failed\n"); + wlan_pcie_delete_evtbd_ring(pmadapter); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + pevtbd_buf = + (mlan_pcie_evt_buf + *)(pmadapter->pcard_pcie + ->evtbd_ring_vbase + + (sizeof(mlan_pcie_evt_buf) * i)); + pmadapter->pcard_pcie->evtbd_ring[i] = + (t_void *)pevtbd_buf; + pevtbd_buf->paddr = pmbuf->buf_pa; + pevtbd_buf->len = (t_u16)pmbuf->data_len; + pevtbd_buf->flags = 0; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = + (adma_dual_desc_buf + *)(pmadapter->pcard_pcie + ->evtbd_ring_vbase + + (sizeof(adma_dual_desc_buf) * i)); + pmadapter->pcard_pcie->evtbd_ring[i] = + (t_void *)padma_bd_buf; + padma_bd_buf->paddr = pmbuf->buf_pa; + padma_bd_buf->len = + ALIGN_SZ(pmbuf->data_len, ADMA_ALIGN_SIZE); + padma_bd_buf->flags = + ADMA_BD_FLAG_INT_EN | ADMA_BD_FLAG_DST_HOST; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + } +#endif + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function frees event buffer descriptor ring + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_delete_evtbd_ring(mlan_adapter *pmadapter) +{ + t_u32 i; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer *pmbuf = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + mlan_pcie_evt_buf *pevtbd_buf; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf; +#endif + + ENTER(); + for (i = 0; i < MLAN_MAX_EVT_BD; i++) { + if (pmadapter->pcard_pcie->evt_buf_list[i]) { + pmbuf = pmadapter->pcard_pcie->evt_buf_list[i]; + pcb->moal_unmap_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->buf_pa, MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE); + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + + pmadapter->pcard_pcie->evt_buf_list[i] = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + pevtbd_buf = + (mlan_pcie_evt_buf *) + pmadapter->pcard_pcie->evtbd_ring[i]; + + if (pevtbd_buf) { + pevtbd_buf->paddr = 0; + pevtbd_buf->len = 0; + pevtbd_buf->flags = 0; + } + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = + (adma_dual_desc_buf *) + pmadapter->pcard_pcie->evtbd_ring[i]; + + if (padma_bd_buf) { + padma_bd_buf->paddr = 0; + padma_bd_buf->len = 0; + padma_bd_buf->flags = 0; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + } + } +#endif + pmadapter->pcard_pcie->evtbd_ring[i] = MNULL; + } + + if (pmadapter->pcard_pcie->evtbd_ring_vbase) + pmadapter->callbacks.moal_mfree_consistent( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->evtbd_ring_size, + pmadapter->pcard_pcie->evtbd_ring_vbase, + pmadapter->pcard_pcie->evtbd_ring_pbase); + + pmadapter->pcard_pcie->evtbd_rdptr = 0; + pmadapter->pcard_pcie->evtbd_wrptr = 0; + pmadapter->pcard_pcie->evtbd_ring_size = 0; + pmadapter->pcard_pcie->evtbd_ring_vbase = MNULL; + pmadapter->pcard_pcie->evtbd_ring_pbase = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function allocates buffer for CMD and CMDRSP + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_alloc_cmdrsp_buf(mlan_adapter *pmadapter) +{ + mlan_buffer *pmbuf = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + /** Virtual base address of command response */ + t_u8 *cmdrsp_vbase; + /** Physical base address of command response */ + t_u64 cmdrsp_pbase = 0; + + ENTER(); + + /* Allocate memory for receiving command response data */ + pmbuf = wlan_alloc_mlan_buffer(pmadapter, 0, 0, MOAL_MALLOC_BUFFER); + if (!pmbuf) { + PRINTM(MERROR, + "Command resp buffer create : Unable to allocate mlan_buffer\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + ret = pcb->moal_malloc_consistent(pmadapter->pmoal_handle, + MRVDRV_SIZE_OF_CMD_BUFFER, + &cmdrsp_vbase, &cmdrsp_pbase); + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "%s: No free moal_malloc_consistent\n", + __FUNCTION__); + /* free pmbuf */ + wlan_free_mlan_buffer(pmadapter, pmbuf); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pmbuf->buf_pa = cmdrsp_pbase; + pmbuf->pbuf = cmdrsp_vbase; + pmbuf->data_offset = 0; + pmbuf->data_len = MRVDRV_SIZE_OF_CMD_BUFFER; + pmbuf->total_pcie_buf_len = MRVDRV_SIZE_OF_CMD_BUFFER; + pmadapter->pcard_pcie->cmdrsp_buf = pmbuf; + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function frees CMD and CMDRSP buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_delete_cmdrsp_buf(mlan_adapter *pmadapter) +{ + mlan_buffer *pmbuf = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *cmdrsp_vbase; + t_u64 cmdrsp_pbase; + ENTER(); + + if (!pmadapter) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (pmadapter->pcard_pcie->cmdrsp_buf) { + pmbuf = pmadapter->pcard_pcie->cmdrsp_buf; + cmdrsp_vbase = pmbuf->pbuf; + cmdrsp_pbase = pmbuf->buf_pa; + if (cmdrsp_vbase) + pmadapter->callbacks.moal_mfree_consistent( + pmadapter->pmoal_handle, + pmbuf->total_pcie_buf_len, cmdrsp_vbase, + cmdrsp_pbase); + wlan_free_mlan_buffer(pmadapter, pmbuf); + pmadapter->pcard_pcie->cmdrsp_buf = MNULL; + } + if (pmadapter->pcard_pcie->cmd_buf) { + pmbuf = pmadapter->pcard_pcie->cmd_buf; + pcb->moal_unmap_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->buf_pa, MRVDRV_SIZE_OF_CMD_BUFFER, + PCI_DMA_TODEVICE); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#if defined(PCIE8997) || defined(PCIE8897) +#define PCIE_TXBD_EMPTY(wrptr, rdptr, mask, rollover_ind) \ + (((wrptr & mask) == (rdptr & mask)) && \ + ((wrptr & rollover_ind) == (rdptr & rollover_ind))) + +/** + * @brief This function flushes the TX buffer descriptor ring + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_flush_txbd_ring(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 txrx_rw_ptr_mask = pmadapter->pcard_pcie->reg->txrx_rw_ptr_mask; + t_u32 txrx_rw_ptr_rollover_ind = + pmadapter->pcard_pcie->reg->txrx_rw_ptr_rollover_ind; + + ENTER(); + + if (!PCIE_TXBD_EMPTY(pmadapter->pcard_pcie->txbd_wrptr, + pmadapter->pcard_pcie->txbd_rdptr, + txrx_rw_ptr_mask, txrx_rw_ptr_rollover_ind)) { + pmadapter->pcard_pcie->txbd_flush = MTRUE; + /* write pointer already set at last send */ + /* send dnld-rdy intr again, wait for completion */ + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_cpu_int_event, + CPU_INTR_DNLD_RDY)) { + PRINTM(MERROR, + "SEND DATA (FLUSH): failed to assert dnld-rdy interrupt.\n"); + ret = MLAN_STATUS_FAILURE; + } + } + + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function check the tx pending buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * @param rdptr tx rdptr + * + * @return MTRUE/MFALSE; + */ +static t_u8 wlan_check_tx_pending_buffer(mlan_adapter *pmadapter, t_u32 rdptr) +{ +#if defined(PCIE8997) || defined(PCIE8897) + t_u32 txrx_rw_ptr_mask = pmadapter->pcard_pcie->reg->txrx_rw_ptr_mask; + t_u32 txrx_rw_ptr_rollover_ind = + pmadapter->pcard_pcie->reg->txrx_rw_ptr_rollover_ind; + if (!pmadapter->pcard_pcie->reg->use_adma) { + if (((pmadapter->pcard_pcie->txbd_rdptr & txrx_rw_ptr_mask) != + (rdptr & txrx_rw_ptr_mask)) || + ((pmadapter->pcard_pcie->txbd_rdptr & + txrx_rw_ptr_rollover_ind) != + (rdptr & txrx_rw_ptr_rollover_ind))) + return MTRUE; + else + return MFALSE; + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + if ((pmadapter->pcard_pcie->txbd_rdptr & + ADMA_RW_PTR_WRAP_MASK) != (rdptr & ADMA_RW_PTR_WRAP_MASK)) + return MTRUE; + else + return MFALSE; + } +#endif + return MFALSE; +} + +/** + * @brief This function unmaps and frees downloaded data buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_send_data_complete(mlan_adapter *pmadapter) +{ + const t_u32 num_tx_buffs = MLAN_MAX_TXRX_BD; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer *pmbuf; + t_u32 wrdoneidx; + t_u32 rdptr; + t_u32 unmap_count = 0; +#if defined(PCIE8997) || defined(PCIE8897) + t_u32 txrx_rw_ptr_mask = pmadapter->pcard_pcie->reg->txrx_rw_ptr_mask; + t_u32 txrx_rw_ptr_rollover_ind = + pmadapter->pcard_pcie->reg->txrx_rw_ptr_rollover_ind; + mlan_pcie_data_buf *ptx_bd_buf; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf; + t_u32 wrptr; +#endif + + ENTER(); + + /* Read the TX ring read pointer set by firmware */ + if (pcb->moal_read_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_txbd_rdptr, + &rdptr)) { + PRINTM(MERROR, + "SEND DATA COMP: failed to read REG_TXBD_RDPTR\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MINFO, "SEND DATA COMP: rdptr_prev=0x%x, rdptr=0x%x\n", + pmadapter->pcard_pcie->txbd_rdptr, rdptr); + +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) + rdptr = rdptr >> TXBD_RW_PTR_START; +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + wrptr = rdptr & 0xffff; + rdptr = rdptr >> ADMA_RPTR_START; + if (wrptr != pmadapter->pcard_pcie->txbd_wrptr) + PRINTM(MERROR, "wlan: Unexpected wrptr 0x%x 0x%x\n", + wrptr, pmadapter->pcard_pcie->txbd_wrptr); + } +#endif + + /* free from previous txbd_rdptr to current txbd_rdptr */ + while (wlan_check_tx_pending_buffer(pmadapter, rdptr)) { + wrdoneidx = + pmadapter->pcard_pcie->txbd_rdptr & (num_tx_buffs - 1); + pmbuf = pmadapter->pcard_pcie->tx_buf_list[wrdoneidx]; + if (pmbuf) { + PRINTM(MDAT_D, + "SEND DATA COMP: Detach pmbuf %p at tx_ring[%d], pmadapter->txbd_rdptr=0x%x\n", + pmbuf, wrdoneidx, + pmadapter->pcard_pcie->txbd_rdptr); + ret = pcb->moal_unmap_memory( + pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, pmbuf->buf_pa, + pmbuf->data_len, PCI_DMA_TODEVICE); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "%s: moal_unmap_memory failed\n", + __FUNCTION__); + break; + } + unmap_count++; + pmadapter->pcard_pcie->txbd_pending--; +#if defined(PCIE8997) || defined(PCIE8897) + if (pmadapter->pcard_pcie->txbd_flush) + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_FAILURE); + else +#endif + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_SUCCESS); + } + + pmadapter->pcard_pcie->tx_buf_list[wrdoneidx] = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + ptx_bd_buf = (mlan_pcie_data_buf *)pmadapter->pcard_pcie + ->txbd_ring[wrdoneidx]; + ptx_bd_buf->paddr = 0; + ptx_bd_buf->len = 0; + ptx_bd_buf->flags = 0; + ptx_bd_buf->frag_len = 0; + ptx_bd_buf->offset = 0; + pmadapter->pcard_pcie->txbd_rdptr++; + if ((pmadapter->pcard_pcie->txbd_rdptr & + txrx_rw_ptr_mask) == num_tx_buffs) + pmadapter->pcard_pcie->txbd_rdptr = + ((pmadapter->pcard_pcie->txbd_rdptr & + txrx_rw_ptr_rollover_ind) ^ + txrx_rw_ptr_rollover_ind); + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = + (adma_dual_desc_buf *)pmadapter->pcard_pcie + ->txbd_ring[wrdoneidx]; + padma_bd_buf->paddr = 0; + padma_bd_buf->len = 0; + padma_bd_buf->flags = 0; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + pmadapter->pcard_pcie->txbd_rdptr++; + pmadapter->pcard_pcie->txbd_rdptr &= + ADMA_RW_PTR_WRAP_MASK; + } +#endif + } + + if (unmap_count) + pmadapter->data_sent = MFALSE; +#if defined(PCIE8997) || defined(PCIE8897) + if (pmadapter->pcard_pcie->txbd_flush) { + if (PCIE_TXBD_EMPTY(pmadapter->pcard_pcie->txbd_wrptr, + pmadapter->pcard_pcie->txbd_rdptr, + txrx_rw_ptr_mask, txrx_rw_ptr_rollover_ind)) + pmadapter->pcard_pcie->txbd_flush = MFALSE; + else + wlan_pcie_flush_txbd_ring(pmadapter); + } +#endif +done: + LEAVE(); + return ret; +} + +#if defined(PCIE8997) || defined(PCIE8897) +#define PCIE_TXBD_NOT_FULL(wrptr, rdptr, mask, rollover_ind) \ + (((wrptr & mask) != (rdptr & mask)) || \ + ((wrptr & rollover_ind) == (rdptr & rollover_ind))) +#endif + +#if defined(PCIE9098) || defined(PCIE9097) +#define ADMA_TXBD_IS_FULL(wrptr, rdptr) \ + (((wrptr & TXRX_RW_PTR_MASK) == (rdptr & TXRX_RW_PTR_MASK)) && \ + ((wrptr & TXRX_RW_PTR_ROLLOVER_IND) != \ + (rdptr & TXRX_RW_PTR_ROLLOVER_IND))) +#endif + +static t_u8 wlan_check_txbd_not_full(mlan_adapter *pmadapter) +{ +#if defined(PCIE8997) || defined(PCIE8897) + t_u32 txrx_rw_ptr_mask = pmadapter->pcard_pcie->reg->txrx_rw_ptr_mask; + t_u32 txrx_rw_ptr_rollover_ind = + pmadapter->pcard_pcie->reg->txrx_rw_ptr_rollover_ind; + if (!pmadapter->pcard_pcie->reg->use_adma) { + if (PCIE_TXBD_NOT_FULL(pmadapter->pcard_pcie->txbd_wrptr, + pmadapter->pcard_pcie->txbd_rdptr, + txrx_rw_ptr_mask, + txrx_rw_ptr_rollover_ind)) + return MTRUE; + else + return MFALSE; + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + if (!ADMA_TXBD_IS_FULL(pmadapter->pcard_pcie->txbd_wrptr, + pmadapter->pcard_pcie->txbd_rdptr)) + return MTRUE; + else + return MFALSE; + } +#endif + return MFALSE; +} + +/** + * @brief This function downloads data to the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param type packet type + * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include + * PCIE header) + * @param tx_param A pointer to mlan_tx_param + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_send_data(mlan_adapter *pmadapter, t_u8 type, + mlan_buffer *pmbuf, + mlan_tx_param *tx_param) +{ + t_u32 reg_txbd_wrptr = pmadapter->pcard_pcie->reg->reg_txbd_wrptr; +#if defined(PCIE8997) || defined(PCIE8897) + t_u32 txrx_rw_ptr_mask = pmadapter->pcard_pcie->reg->txrx_rw_ptr_mask; + t_u32 txrx_rw_ptr_rollover_ind = + pmadapter->pcard_pcie->reg->txrx_rw_ptr_rollover_ind; + mlan_pcie_data_buf *ptx_bd_buf = MNULL; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf = MNULL; +#endif + const t_u32 num_tx_buffs = MLAN_MAX_TXRX_BD; + mlan_status ret = MLAN_STATUS_PENDING; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 rxbd_val = 0; + t_u32 wrindx; + t_u16 *tmp; + t_u8 *payload; + t_u32 wr_ptr_start = 0; + + ENTER(); + + if (!(pmadapter && pmbuf)) { + PRINTM(MERROR, "%s() has no buffer", __FUNCTION__); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (!(pmbuf->pbuf && pmbuf->data_len)) { + PRINTM(MERROR, "Invalid parameter <%p, %#x>\n", pmbuf->pbuf, + pmbuf->data_len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MINFO, "SEND DATA: \n", + pmadapter->pcard_pcie->txbd_rdptr, + pmadapter->pcard_pcie->txbd_wrptr); + + if (wlan_check_txbd_not_full(pmadapter)) { + pmadapter->data_sent = MTRUE; + + payload = pmbuf->pbuf + pmbuf->data_offset; + tmp = (t_u16 *)&payload[0]; + *tmp = wlan_cpu_to_le16((t_u16)pmbuf->data_len); + tmp = (t_u16 *)&payload[2]; + *tmp = wlan_cpu_to_le16(type); + + /* Map pmbuf, and attach to tx ring */ + if (MLAN_STATUS_FAILURE == + pcb->moal_map_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + &pmbuf->buf_pa, pmbuf->data_len, + PCI_DMA_TODEVICE)) { + PRINTM(MERROR, + "SEND DATA: failed to moal_map_memory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + wrindx = pmadapter->pcard_pcie->txbd_wrptr & (num_tx_buffs - 1); + PRINTM(MDAT_D, + "SEND DATA: Attach pmbuf %p at tx_ring[%d], txbd_wrptr=0x%x\n", + pmbuf, wrindx, pmadapter->pcard_pcie->txbd_wrptr); + pmadapter->pcard_pcie->tx_buf_list[wrindx] = pmbuf; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + wr_ptr_start = TXBD_RW_PTR_START; + ptx_bd_buf = (mlan_pcie_data_buf *)pmadapter->pcard_pcie + ->txbd_ring[wrindx]; + ptx_bd_buf->paddr = pmbuf->buf_pa; + ptx_bd_buf->len = (t_u16)pmbuf->data_len; + ptx_bd_buf->flags = MLAN_BD_FLAG_SOP | MLAN_BD_FLAG_EOP; + ptx_bd_buf->frag_len = (t_u16)pmbuf->data_len; + ptx_bd_buf->offset = 0; + pmadapter->pcard_pcie->last_tx_pkt_size[wrindx] = + pmbuf->data_len; + pmadapter->pcard_pcie->txbd_wrptr++; + if ((pmadapter->pcard_pcie->txbd_wrptr & + txrx_rw_ptr_mask) == num_tx_buffs) + pmadapter->pcard_pcie->txbd_wrptr = + ((pmadapter->pcard_pcie->txbd_wrptr & + txrx_rw_ptr_rollover_ind) ^ + txrx_rw_ptr_rollover_ind); + rxbd_val = pmadapter->pcard_pcie->rxbd_wrptr & + pmadapter->pcard_pcie->reg + ->txrx_rw_ptr_wrap_mask; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + wr_ptr_start = ADMA_WPTR_START; + padma_bd_buf = (adma_dual_desc_buf *)pmadapter + ->pcard_pcie->txbd_ring[wrindx]; + padma_bd_buf->paddr = pmbuf->buf_pa; + padma_bd_buf->len = + ALIGN_SZ(pmbuf->data_len, ADMA_ALIGN_SIZE); + padma_bd_buf->flags = + ADMA_BD_FLAG_SOP | ADMA_BD_FLAG_EOP | + ADMA_BD_FLAG_INT_EN | ADMA_BD_FLAG_SRC_HOST; + if (padma_bd_buf->len < ADMA_MIN_PKT_SIZE) + padma_bd_buf->len = ADMA_MIN_PKT_SIZE; + padma_bd_buf->pkt_size = pmbuf->data_len; + pmadapter->pcard_pcie->last_tx_pkt_size[wrindx] = + pmbuf->data_len; + pmadapter->pcard_pcie->txbd_wrptr++; + pmadapter->pcard_pcie->txbd_wrptr &= + ADMA_RW_PTR_WRAP_MASK; + } +#endif + pmadapter->pcard_pcie->txbd_pending++; + PRINTM(MINFO, "REG_TXBD_WRPT(0x%x) = 0x%x\n", reg_txbd_wrptr, + ((pmadapter->pcard_pcie->txbd_wrptr << wr_ptr_start) | + rxbd_val)); + /* Write the TX ring write pointer in to REG_TXBD_WRPTR */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, reg_txbd_wrptr, + (pmadapter->pcard_pcie->txbd_wrptr + << wr_ptr_start) | + rxbd_val)) { + PRINTM(MERROR, + "SEND DATA: failed to write REG_TXBD_WRPTR\n"); + ret = MLAN_STATUS_FAILURE; + goto done_unmap; + } + PRINTM(MINFO, "SEND DATA: Updated \n", + pmadapter->pcard_pcie->txbd_rdptr, + pmadapter->pcard_pcie->txbd_wrptr); + + if (wlan_check_txbd_not_full(pmadapter)) + pmadapter->data_sent = MFALSE; + + PRINTM(MINFO, "Sent packet to firmware successfully\n"); + } else { + PRINTM(MERROR, + "TX Ring full, can't send anymore packets to firmware\n"); + PRINTM(MINFO, "SEND DATA (FULL!): \n", + pmadapter->pcard_pcie->txbd_rdptr, + pmadapter->pcard_pcie->txbd_wrptr); + pmadapter->data_sent = MTRUE; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + /* Send the TX ready interrupt */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_cpu_int_event, + CPU_INTR_DNLD_RDY)) + PRINTM(MERROR, + "SEND DATA (FULL): failed to assert dnld-rdy interrupt\n"); + } +#endif + ret = MLAN_STATUS_FAILURE; + goto done; + } + + LEAVE(); + return ret; + +done_unmap: + if (MLAN_STATUS_FAILURE == + pcb->moal_unmap_memory( + pmadapter->pmoal_handle, pmbuf->pbuf + pmbuf->data_offset, + pmbuf->buf_pa, pmbuf->data_len, PCI_DMA_TODEVICE)) { + PRINTM(MERROR, "SEND DATA: failed to moal_unmap_memory\n"); + ret = MLAN_STATUS_FAILURE; + } + pmadapter->pcard_pcie->txbd_pending--; + pmadapter->pcard_pcie->tx_buf_list[wrindx] = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma && ptx_bd_buf) { + ptx_bd_buf->paddr = 0; + ptx_bd_buf->len = 0; + ptx_bd_buf->flags = 0; + ptx_bd_buf->frag_len = 0; + ptx_bd_buf->offset = 0; + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma && padma_bd_buf) { + padma_bd_buf->paddr = 0; + padma_bd_buf->len = 0; + padma_bd_buf->flags = 0; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + } +#endif +done: + LEAVE(); + return ret; +} + +/** + * @brief This function check the rx pending buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * @param rdptr rx rdptr + * + * @return MTRUE/MFALSE; + */ +static t_u8 wlan_check_rx_pending_buffer(mlan_adapter *pmadapter, t_u32 rdptr) +{ +#if defined(PCIE8997) || defined(PCIE8897) + t_u32 txrx_rw_ptr_mask = pmadapter->pcard_pcie->reg->txrx_rw_ptr_mask; + t_u32 txrx_rw_ptr_rollover_ind = + pmadapter->pcard_pcie->reg->txrx_rw_ptr_rollover_ind; + if (!pmadapter->pcard_pcie->reg->use_adma) { + if (((rdptr & txrx_rw_ptr_mask) != + (pmadapter->pcard_pcie->rxbd_rdptr & txrx_rw_ptr_mask)) || + ((rdptr & txrx_rw_ptr_rollover_ind) != + (pmadapter->pcard_pcie->rxbd_rdptr & + txrx_rw_ptr_rollover_ind))) + return MTRUE; + else + return MFALSE; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + if ((pmadapter->pcard_pcie->rxbd_rdptr & + ADMA_RW_PTR_WRAP_MASK) != (rdptr & ADMA_RW_PTR_WRAP_MASK)) + return MTRUE; + else + return MFALSE; + } +#endif + return MFALSE; +} + +/** + * @brief This function handles received buffer ring and + * dispatches packets to upper + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_process_recv_data(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 rdptr, rd_index; + mlan_buffer *pmbuf = MNULL; + t_u32 txbd_val = 0; + t_u16 rx_len, rx_type; + const t_u32 num_rx_buffs = MLAN_MAX_TXRX_BD; + t_u32 reg_rxbd_rdptr = pmadapter->pcard_pcie->reg->reg_rxbd_rdptr; +#if defined(PCIE8997) || defined(PCIE8897) + t_u32 txrx_rw_ptr_mask = pmadapter->pcard_pcie->reg->txrx_rw_ptr_mask; + t_u32 txrx_rw_ptr_rollover_ind = + pmadapter->pcard_pcie->reg->txrx_rw_ptr_rollover_ind; + mlan_pcie_data_buf *prxbd_buf; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf; +#endif + + ENTER(); + + /* Read the RX ring Read pointer set by firmware */ + if (pcb->moal_read_reg(pmadapter->pmoal_handle, reg_rxbd_rdptr, + &rdptr)) { + PRINTM(MERROR, "RECV DATA: failed to read REG_RXBD_RDPTR\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) + rdptr = rdptr >> ADMA_RPTR_START; +#endif + + while (wlan_check_rx_pending_buffer(pmadapter, rdptr)) { + /* detach pmbuf (with data) from Rx Ring */ + rd_index = + pmadapter->pcard_pcie->rxbd_rdptr & (num_rx_buffs - 1); + if (rd_index > MLAN_MAX_TXRX_BD - 1) { + PRINTM(MERROR, "RECV DATA: Invalid Rx buffer index.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmbuf = pmadapter->pcard_pcie->rx_buf_list[rd_index]; + + if (MLAN_STATUS_FAILURE == + pcb->moal_unmap_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->buf_pa, MLAN_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE)) { + PRINTM(MERROR, + "RECV DATA: moal_unmap_memory failed.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->pcard_pcie->rx_buf_list[rd_index] = MNULL; + PRINTM(MDAT_D, + "RECV DATA: Detach pmbuf %p at rx_ring[%d], pmadapter->rxbd_rdptr=0x%x\n", + pmbuf, rd_index, pmadapter->pcard_pcie->rxbd_rdptr); + + /* Get data length from interface header - + first 2 bytes are len, second 2 bytes are type */ + rx_len = *((t_u16 *)(pmbuf->pbuf + pmbuf->data_offset)); + rx_len = wlan_le16_to_cpu(rx_len); + rx_type = *((t_u16 *)(pmbuf->pbuf + pmbuf->data_offset + 2)); + rx_type = wlan_le16_to_cpu(rx_type); + + PRINTM(MINFO, + "RECV DATA: , Len=%d rx_type=%d\n", + pmadapter->pcard_pcie->rxbd_wrptr, rdptr, rx_len, + rx_type); + + if (rx_len <= MLAN_RX_DATA_BUF_SIZE) { + /* send buffer to host (which will free it) */ + pmbuf->data_len = rx_len - PCIE_INTF_HEADER_LEN; + pmbuf->data_offset += PCIE_INTF_HEADER_LEN; + // rx_trace 5 + if (pmadapter->tp_state_on) + pmadapter->callbacks.moal_tp_accounting( + pmadapter->pmoal_handle, pmbuf, + 5 /*RX_DROP_P1*/); + if (pmadapter->tp_state_drop_point == + 5 /*RX_DROP_P1*/) { + pmadapter->ops.data_complete(pmadapter, pmbuf, + ret); + } else { + PRINTM(MINFO, + "RECV DATA: Received packet from FW successfully\n"); + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + util_enqueue_list_tail( + pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + pmadapter->rx_pkts_queued++; + pmadapter->callbacks.moal_tp_accounting_rx_param( + pmadapter->pmoal_handle, 1, + pmadapter->rx_pkts_queued); + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + + pmadapter->data_received = MTRUE; + } + /* Create new buffer and attach it to Rx Ring */ + pmbuf = wlan_alloc_mlan_buffer(pmadapter, + MLAN_RX_DATA_BUF_SIZE, + MLAN_RX_HEADER_LEN, + MOAL_ALLOC_MLAN_BUFFER); + if (!pmbuf) { + PRINTM(MERROR, + "RECV DATA: Unable to allocate mlan_buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else { + /* Queue the mlan_buffer again */ + PRINTM(MERROR, "PCIE: Drop invalid packet, length=%d", + rx_len); + } + + if (MLAN_STATUS_FAILURE == + pcb->moal_map_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + &pmbuf->buf_pa, MLAN_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE)) { + PRINTM(MERROR, "RECV DATA: moal_map_memory failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MDAT_D, + "RECV DATA: Attach new pmbuf %p at rx_ring[%d]\n", pmbuf, + rd_index); + pmadapter->pcard_pcie->rx_buf_list[rd_index] = pmbuf; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + prxbd_buf = (mlan_pcie_data_buf *)pmadapter->pcard_pcie + ->rxbd_ring[rd_index]; + prxbd_buf->paddr = pmbuf->buf_pa; + prxbd_buf->len = (t_u16)pmbuf->data_len; + prxbd_buf->flags = MLAN_BD_FLAG_SOP | MLAN_BD_FLAG_EOP; + prxbd_buf->offset = 0; + prxbd_buf->frag_len = (t_u16)pmbuf->data_len; + + /* update rxbd's rdptrs */ + if ((++pmadapter->pcard_pcie->rxbd_rdptr & + txrx_rw_ptr_mask) == MLAN_MAX_TXRX_BD) { + pmadapter->pcard_pcie->rxbd_rdptr = + ((pmadapter->pcard_pcie->rxbd_rdptr & + txrx_rw_ptr_rollover_ind) ^ + txrx_rw_ptr_rollover_ind); + } + + /* update rxbd's wrptrs */ + if ((++pmadapter->pcard_pcie->rxbd_wrptr & + txrx_rw_ptr_mask) == MLAN_MAX_TXRX_BD) { + pmadapter->pcard_pcie->rxbd_wrptr = + ((pmadapter->pcard_pcie->rxbd_wrptr & + txrx_rw_ptr_rollover_ind) ^ + txrx_rw_ptr_rollover_ind); + } + txbd_val = pmadapter->pcard_pcie->txbd_wrptr & + pmadapter->pcard_pcie->reg + ->txrx_rw_ptr_wrap_mask; + txbd_val = txbd_val << TXBD_RW_PTR_START; + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = + (adma_dual_desc_buf *)pmadapter->pcard_pcie + ->rxbd_ring[rd_index]; + padma_bd_buf->paddr = pmbuf->buf_pa; + padma_bd_buf->len = + ALIGN_SZ(pmbuf->data_len, ADMA_ALIGN_SIZE); + padma_bd_buf->flags = + ADMA_BD_FLAG_INT_EN | ADMA_BD_FLAG_DST_HOST; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + pmadapter->pcard_pcie->rxbd_rdptr++; + pmadapter->pcard_pcie->rxbd_wrptr++; + pmadapter->pcard_pcie->rxbd_rdptr &= + ADMA_RW_PTR_WRAP_MASK; + pmadapter->pcard_pcie->rxbd_wrptr &= + ADMA_RW_PTR_WRAP_MASK; + } +#endif + PRINTM(MINFO, "RECV DATA: Updated \n", + pmadapter->pcard_pcie->rxbd_wrptr, rdptr); + + /* Write the RX ring write pointer in to REG_RXBD_WRPTR */ + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_rxbd_wrptr, + pmadapter->pcard_pcie->rxbd_wrptr | txbd_val)) { + PRINTM(MERROR, + "RECV DATA: failed to write REG_RXBD_WRPTR\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Read the RX ring read pointer set by firmware */ + if (pcb->moal_read_reg(pmadapter->pmoal_handle, reg_rxbd_rdptr, + &rdptr)) { + PRINTM(MERROR, + "RECV DATA: failed to read REG_RXBD_RDPTR\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) + rdptr = rdptr >> ADMA_RPTR_START; +#endif + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function downloads command to the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include + * PCIE header) + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_send_cmd(mlan_adapter *pmadapter, + mlan_buffer *pmbuf) +{ + mlan_status ret = MLAN_STATUS_PENDING; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *payload = MNULL; + + ENTER(); + if (!(pmadapter && pmbuf)) { + PRINTM(MERROR, "%s() has no buffer", __FUNCTION__); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (!(pmbuf->pbuf && pmbuf->data_len)) { + PRINTM(MERROR, "Invalid parameter <%p, %#x>\n", pmbuf->pbuf, + pmbuf->data_len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Make sure a command response buffer is available */ + if (!pmadapter->pcard_pcie->cmdrsp_buf) { + PRINTM(MERROR, + "No response buffer available, send command failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pmadapter->cmd_sent = MTRUE; + payload = pmbuf->pbuf + pmbuf->data_offset; + *(t_u16 *)&payload[0] = wlan_cpu_to_le16((t_u16)pmbuf->data_len); + *(t_u16 *)&payload[2] = wlan_cpu_to_le16(MLAN_TYPE_CMD); + + if (MLAN_STATUS_FAILURE == + pcb->moal_map_memory( + pmadapter->pmoal_handle, pmbuf->pbuf + pmbuf->data_offset, + &pmbuf->buf_pa, MLAN_RX_CMD_BUF_SIZE, PCI_DMA_TODEVICE)) { + PRINTM(MERROR, "Command buffer : moal_map_memory failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter->pcard_pcie->cmd_buf = pmbuf; + +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + /* To send a command, the driver will: + 1. Write the 64bit physical address of the data buffer to + SCRATCH1 + SCRATCH0 + 2. Ring the door bell (i.e. set the door bell interrupt) + + In response to door bell interrupt, the firmware will + perform the DMA of the command packet (first header to obtain + the total length and then rest of the command). + */ + + if (pmadapter->pcard_pcie->cmdrsp_buf) { + /* Write the lower 32bits of the cmdrsp buffer physical + address */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + REG_CMDRSP_ADDR_LO, + (t_u32)pmadapter->pcard_pcie + ->cmdrsp_buf->buf_pa)) { + PRINTM(MERROR, + "Failed to write download command to boot code.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Write the upper 32bits of the cmdrsp buffer physical + address */ + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, REG_CMDRSP_ADDR_HI, + (t_u32)((t_u64)pmadapter->pcard_pcie + ->cmdrsp_buf->buf_pa >> + 32))) { + PRINTM(MERROR, + "Failed to write download command to boot code.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* Write the lower 32bits of the physical address to + * REG_CMD_ADDR_LO */ + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, REG_CMD_ADDR_LO, + (t_u32)pmadapter->pcard_pcie->cmd_buf->buf_pa)) { + PRINTM(MERROR, + "Failed to write download command to boot code.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Write the upper 32bits of the physical address to + * REG_CMD_ADDR_HI */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + REG_CMD_ADDR_HI, + (t_u32)((t_u64)pmadapter->pcard_pcie + ->cmd_buf->buf_pa >> + 32))) { + PRINTM(MERROR, + "Failed to write download command to boot code.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Write the command length to REG_CMD_SIZE */ + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, REG_CMD_SIZE, + pmadapter->pcard_pcie->cmd_buf->data_len)) { + PRINTM(MERROR, + "Failed to write command length to REG_CMD_SIZE\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Ring the door bell */ + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_cpu_int_event, + CPU_INTR_DOOR_BELL)) { + PRINTM(MERROR, + "Failed to assert door-bell interrupt.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + /* To send a command, the driver will: + 1. driver prepare the cmdrep buffer for adma + 2. driver programs dma_mode field to direct programming mode + and programs dma_size field to define DMA data transfer size. + 3. driver programs src_base_addr register to define source + location of DMA data + 4. driver sets src_wptr to 1 to initiate DMA operation + */ + wlan_init_adma(pmadapter, ADMA_CMDRESP, + pmadapter->pcard_pcie->cmdrsp_buf->buf_pa, + MRVDRV_SIZE_OF_CMD_BUFFER, MFALSE); + wlan_init_adma(pmadapter, ADMA_CMD, + pmadapter->pcard_pcie->cmd_buf->buf_pa, + pmadapter->pcard_pcie->cmd_buf->data_len, + MFALSE); + } +#endif +done: + if ((ret == MLAN_STATUS_FAILURE) && pmadapter) + pmadapter->cmd_sent = MFALSE; + + LEAVE(); + return ret; +} + +#if defined(PCIE8997) || defined(PCIE8897) +#define MLAN_SLEEP_COOKIE_DEF 0xBEEFBEEF +#define MAX_DELAY_LOOP_COUNT 100 + +static void mlan_delay_for_sleep_cookie(mlan_adapter *pmadapter, + t_u32 max_delay_loop_cnt) +{ + t_u8 *buffer; + t_u32 sleep_cookie = 0; + t_u32 count = 0; + pmlan_buffer pmbuf = pmadapter->pcard_pcie->cmdrsp_buf; + + for (count = 0; count < max_delay_loop_cnt; count++) { + buffer = pmbuf->pbuf; + sleep_cookie = *(t_u32 *)buffer; + + if (sleep_cookie == MLAN_SLEEP_COOKIE_DEF) { + PRINTM(MINFO, "sleep cookie FOUND at count = %d!!\n", + count); + break; + } + wlan_udelay(pmadapter, 20); + } + + if (count >= max_delay_loop_cnt) + PRINTM(MERROR, "sleep cookie not found!!\n"); +} +#endif + +/** + * @brief This function handles command complete interrupt + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_process_cmd_resp(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + pmlan_buffer pmbuf = pmadapter->pcard_pcie->cmdrsp_buf; + pmlan_buffer cmd_buf = MNULL; + t_u16 resp_len = 0; + + ENTER(); + + PRINTM(MINFO, "Rx CMD Response\n"); + + if (pmbuf == MNULL) { + PRINTM(MMSG, "Rx CMD response pmbuf is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Get data length from interface header - + first 2 bytes are len, second 2 bytes are type */ + resp_len = *((t_u16 *)(pmbuf->pbuf + pmbuf->data_offset)); + + pmadapter->upld_len = wlan_le16_to_cpu(resp_len); + pmadapter->upld_len -= PCIE_INTF_HEADER_LEN; + + if (!pmadapter->curr_cmd) { + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) { + wlan_process_sleep_confirm_resp( + pmadapter, + pmbuf->pbuf + pmbuf->data_offset + + PCIE_INTF_HEADER_LEN, + pmadapter->upld_len); + /* We are sending sleep confirm done interrupt in the + * middle of sleep handshake. There is a corner case + * when Tx done interrupt is received from firmware + * during sleep handshake due to which host and firmware + * power states go out of sync causing Tx data timeout + * problem. Hence sleep confirm done interrupt is sent + * at the end of sleep handshake to fix the problem + * + * Host could be reading the interrupt during polling + * (while loop) or to address a FW interrupt. In either + * case, after clearing the interrupt driver needs to + * send a sleep confirm event at the end of processing + * command response right here. This marks the end of + * the sleep handshake with firmware. + */ + wlan_pcie_enable_host_int_mask(pmadapter); + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_cpu_int_event, + CPU_INTR_SLEEP_CFM_DONE)) { + PRINTM(MERROR, "Write register failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#if defined(PCIE8997) || defined(PCIE8897) + mlan_delay_for_sleep_cookie(pmadapter, + MAX_DELAY_LOOP_COUNT); +#endif + cmd_buf = pmadapter->pcard_pcie->cmd_buf; + if (cmd_buf) { + pcb->moal_unmap_memory( + pmadapter->pmoal_handle, + cmd_buf->pbuf + cmd_buf->data_offset, + cmd_buf->buf_pa, WLAN_UPLD_SIZE, + PCI_DMA_TODEVICE); + pmadapter->pcard_pcie->cmd_buf = MNULL; + } + } + memcpy_ext(pmadapter, pmadapter->upld_buf, + pmbuf->pbuf + pmbuf->data_offset + + PCIE_INTF_HEADER_LEN, + pmadapter->upld_len, MRVDRV_SIZE_OF_CMD_BUFFER); + + } else { + pmadapter->cmd_resp_received = MTRUE; + pmbuf->data_len = pmadapter->upld_len; + pmbuf->data_offset += PCIE_INTF_HEADER_LEN; + pmadapter->curr_cmd->respbuf = pmbuf; + + /* Take the pointer and set it to CMD node and will + return in the response complete callback */ + pmadapter->pcard_pcie->cmdrsp_buf = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + /* Clear the cmd-rsp buffer address in scratch + registers. This will prevent firmware from writing to + the same response buffer again. */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + REG_CMDRSP_ADDR_LO, 0)) { + PRINTM(MERROR, + "Rx CMD: failed to clear cmd_rsp address.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Write the upper 32bits of the cmdrsp buffer physical + address */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + REG_CMDRSP_ADDR_HI, 0)) { + PRINTM(MERROR, + "Rx CMD: failed to clear cmd_rsp address.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + /* Clear the cmd-rsp buffer address in adma registers. + This will prevent firmware from writing to the same + response buffer again. */ + if (wlan_init_adma(pmadapter, ADMA_CMDRESP, 0, 0, + MFALSE)) { + PRINTM(MERROR, + "Rx CMD: failed to clear cmd_rsp address.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +#endif + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles command response completion + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_pcie_cmdrsp_complete(mlan_adapter *pmadapter, + mlan_buffer *pmbuf, mlan_status status) +{ + mlan_buffer *pcmdmbuf; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + /*return the cmd response pmbuf*/ + if (pmbuf) { + pmbuf->data_len = MRVDRV_SIZE_OF_CMD_BUFFER; + pmbuf->data_offset -= PCIE_INTF_HEADER_LEN; + pmadapter->pcard_pcie->cmdrsp_buf = pmbuf; + } + + /*unmap the cmd pmbuf, so the cpu can not access the memory in the + * command node*/ + pcmdmbuf = pmadapter->pcard_pcie->cmd_buf; + + if (pcmdmbuf) { + pcb->moal_unmap_memory(pmadapter->pmoal_handle, + pcmdmbuf->pbuf + pcmdmbuf->data_offset, + pcmdmbuf->buf_pa, WLAN_UPLD_SIZE, + PCI_DMA_TODEVICE); + pmadapter->pcard_pcie->cmd_buf = MNULL; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function check pending evt buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * @param rdptr evt rdptr + * + * @return MTRUE/MFALSE; + */ +static t_u8 wlan_check_evt_buffer(mlan_adapter *pmadapter, t_u32 rdptr) +{ +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + if (((rdptr & EVT_RW_PTR_MASK) != + (pmadapter->pcard_pcie->evtbd_rdptr & EVT_RW_PTR_MASK)) || + ((rdptr & EVT_RW_PTR_ROLLOVER_IND) != + (pmadapter->pcard_pcie->evtbd_rdptr & + EVT_RW_PTR_ROLLOVER_IND))) + return MTRUE; + else + return MFALSE; + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + if ((pmadapter->pcard_pcie->evtbd_rdptr & + ADMA_RW_PTR_WRAP_MASK) != (rdptr & ADMA_RW_PTR_WRAP_MASK)) + return MTRUE; + else + return MFALSE; + } +#endif + return MFALSE; +} + +/** + * @brief This function handles FW event ready interrupt + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_process_event_ready(mlan_adapter *pmadapter) +{ + t_u32 rd_index = + pmadapter->pcard_pcie->evtbd_rdptr & (MLAN_MAX_EVT_BD - 1); + t_u32 rdptr, event; + pmlan_callbacks pcb = &pmadapter->callbacks; +#if defined(PCIE8997) || defined(PCIE8897) + mlan_pcie_evt_buf *pevtbd_buf; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf; +#endif + ENTER(); + + if (pmadapter->event_received) { + PRINTM(MINFO, "Event being processed, do not " + "process this interrupt just yet\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + if (rd_index >= MLAN_MAX_EVT_BD) { + PRINTM(MINFO, "Invalid rd_index...\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Read the event ring read pointer set by firmware */ + if (pcb->moal_read_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_evtbd_rdptr, + &rdptr)) { + PRINTM(MERROR, "EvtRdy: failed to read REG_EVTBD_RDPTR\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) + rdptr = rdptr >> ADMA_RPTR_START; +#endif + PRINTM(MINFO, "EvtRdy: Initial \n", + pmadapter->pcard_pcie->evtbd_wrptr, rdptr); + if (wlan_check_evt_buffer(pmadapter, rdptr)) { + mlan_buffer *pmbuf_evt; + t_u16 evt_len; + + PRINTM(MINFO, "EvtRdy: Read Index: %d\n", rd_index); + pmbuf_evt = pmadapter->pcard_pcie->evt_buf_list[rd_index]; + + /*unmap the pmbuf for CPU Access*/ + pcb->moal_unmap_memory(pmadapter->pmoal_handle, + pmbuf_evt->pbuf + pmbuf_evt->data_offset, + pmbuf_evt->buf_pa, MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE); + + /* Take the pointer and set it to event pointer in adapter + and will return back after event handling callback */ +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + pevtbd_buf = (mlan_pcie_evt_buf *)pmadapter->pcard_pcie + ->evtbd_ring[rd_index]; + pevtbd_buf->paddr = 0; + pevtbd_buf->len = 0; + pevtbd_buf->flags = 0; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = + (adma_dual_desc_buf *)pmadapter->pcard_pcie + ->evtbd_ring[rd_index]; + padma_bd_buf->paddr = 0; + padma_bd_buf->len = 0; + padma_bd_buf->flags = 0; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + } +#endif + pmadapter->pcard_pcie->evt_buf_list[rd_index] = MNULL; + + event = *((t_u32 *)&pmbuf_evt->pbuf[pmbuf_evt->data_offset + + PCIE_INTF_HEADER_LEN]); + pmadapter->event_cause = wlan_le32_to_cpu(event); + /* The first 4bytes will be the event transfer header + len is 2 bytes followed by type which is 2 bytes */ + evt_len = *((t_u16 *)&pmbuf_evt->pbuf[pmbuf_evt->data_offset]); + evt_len = wlan_le16_to_cpu(evt_len); + + if ((evt_len > 0) && (evt_len > MLAN_EVENT_HEADER_LEN) && + (evt_len - MLAN_EVENT_HEADER_LEN < MAX_EVENT_SIZE)) + memcpy_ext(pmadapter, pmadapter->event_body, + pmbuf_evt->pbuf + pmbuf_evt->data_offset + + MLAN_EVENT_HEADER_LEN, + evt_len - MLAN_EVENT_HEADER_LEN, + sizeof(pmadapter->event_body)); + + pmbuf_evt->data_offset += PCIE_INTF_HEADER_LEN; + pmbuf_evt->data_len = evt_len - PCIE_INTF_HEADER_LEN; + PRINTM(MINFO, "Event length: %d\n", pmbuf_evt->data_len); + + pmadapter->event_received = MTRUE; + pmadapter->pmlan_buffer_event = pmbuf_evt; + pmadapter->pcard_pcie->evtbd_rdptr++; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + if ((pmadapter->pcard_pcie->evtbd_rdptr & + EVT_RW_PTR_MASK) == MLAN_MAX_EVT_BD) { + pmadapter->pcard_pcie->evtbd_rdptr = + ((pmadapter->pcard_pcie->evtbd_rdptr & + EVT_RW_PTR_ROLLOVER_IND) ^ + EVT_RW_PTR_ROLLOVER_IND); + } + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) + pmadapter->pcard_pcie->evtbd_rdptr &= + ADMA_RW_PTR_WRAP_MASK; +#endif + + /* Do not update the event write pointer here, wait till the + buffer is released. This is just to make things simpler, + we need to find a better method of managing these buffers. + */ + } else { + PRINTM(MINTR, "------>EVENT DONE\n"); + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_cpu_int_event, + CPU_INTR_EVENT_DONE)) { + PRINTM(MERROR, + "Failed to asset event done interrupt\n"); + return MLAN_STATUS_FAILURE; + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles event completion + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_pcie_event_complete(mlan_adapter *pmadapter, + mlan_buffer *pmbuf, mlan_status status) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 wrptr = + pmadapter->pcard_pcie->evtbd_wrptr & (MLAN_MAX_EVT_BD - 1); + t_u32 rdptr; +#if defined(PCIE8997) || defined(PCIE8897) + mlan_pcie_evt_buf *pevtbd_buf; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + adma_dual_desc_buf *padma_bd_buf; +#endif + + ENTER(); + if (!pmbuf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (wrptr >= MLAN_MAX_EVT_BD) { + PRINTM(MERROR, "EvtCom: Invalid wrptr 0x%x\n", wrptr); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Read the event ring read pointer set by firmware */ + if (pcb->moal_read_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_evtbd_rdptr, + &rdptr)) { + PRINTM(MERROR, "EvtCom: failed to read REG_EVTBD_RDPTR\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) + rdptr = rdptr >> ADMA_RPTR_START; +#endif + + if (!pmadapter->pcard_pcie->evt_buf_list[wrptr]) { + pmbuf->data_len = MAX_EVENT_SIZE; + pmbuf->data_offset -= PCIE_INTF_HEADER_LEN; + + if (MLAN_STATUS_FAILURE == + pcb->moal_map_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + &pmbuf->buf_pa, MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE)) { + PRINTM(MERROR, "EvtCom: failed to moal_map_memory\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pmadapter->pcard_pcie->evt_buf_list[wrptr] = pmbuf; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + pevtbd_buf = (mlan_pcie_evt_buf *)pmadapter->pcard_pcie + ->evtbd_ring[wrptr]; + pevtbd_buf->paddr = pmbuf->buf_pa; + pevtbd_buf->len = (t_u16)pmbuf->data_len; + pevtbd_buf->flags = 0; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + padma_bd_buf = (adma_dual_desc_buf *)pmadapter + ->pcard_pcie->evtbd_ring[wrptr]; + padma_bd_buf->paddr = pmbuf->buf_pa; + padma_bd_buf->len = + ALIGN_SZ(pmbuf->data_len, ADMA_ALIGN_SIZE); + padma_bd_buf->flags = 0; + padma_bd_buf->flags = + ADMA_BD_FLAG_INT_EN | ADMA_BD_FLAG_DST_HOST; + padma_bd_buf->pkt_size = 0; + padma_bd_buf->reserved = 0; + } +#endif + pmbuf = MNULL; + } else { + PRINTM(MINFO, + "EvtCom: ERROR: Buffer is still valid at " + "index %d, <%p, %p>\n", + wrptr, pmadapter->pcard_pcie->evt_buf_list[wrptr], + pmbuf); + } + + pmadapter->pcard_pcie->evtbd_wrptr++; + +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + if ((pmadapter->pcard_pcie->evtbd_wrptr & EVT_RW_PTR_MASK) == + MLAN_MAX_EVT_BD) { + pmadapter->pcard_pcie->evtbd_wrptr = + ((pmadapter->pcard_pcie->evtbd_wrptr & + EVT_RW_PTR_ROLLOVER_IND) ^ + EVT_RW_PTR_ROLLOVER_IND); + } + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) + pmadapter->pcard_pcie->evtbd_wrptr &= ADMA_RW_PTR_WRAP_MASK; +#endif + PRINTM(MINFO, "EvtCom: Updated \n", + pmadapter->pcard_pcie->evtbd_wrptr, rdptr); + + /* Write the event ring write pointer in to REG_EVTBD_WRPTR */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_evtbd_wrptr, + pmadapter->pcard_pcie->evtbd_wrptr)) { + PRINTM(MERROR, "EvtCom: failed to write REG_EVTBD_WRPTR\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + +done: + /* Free the buffer for failure case */ + if (ret && pmbuf) + wlan_free_mlan_buffer(pmadapter, pmbuf); + + PRINTM(MINFO, "EvtCom: Check Events Again\n"); + ret = wlan_pcie_process_event_ready(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief This function downloads boot command to the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_send_boot_cmd(mlan_adapter *pmadapter, + mlan_buffer *pmbuf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (!pmadapter || !pmbuf) { + PRINTM(MERROR, "NULL Pointer\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (MLAN_STATUS_FAILURE == + pcb->moal_map_memory( + pmadapter->pmoal_handle, pmbuf->pbuf + pmbuf->data_offset, + &pmbuf->buf_pa, WLAN_UPLD_SIZE, PCI_DMA_TODEVICE)) { + PRINTM(MERROR, "BootCmd: failed to moal_map_memory\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (!(pmbuf->pbuf && pmbuf->data_len && pmbuf->buf_pa)) { + PRINTM(MERROR, "%s: Invalid buffer <%p, %#x:%x, len=%d>\n", + __func__, pmbuf->pbuf, + (t_u32)((t_u64)pmbuf->buf_pa >> 32), + (t_u32)pmbuf->buf_pa, pmbuf->data_len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Write the lower 32bits of the physical address to scratch + * register 0 */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_scratch_0, + (t_u32)pmbuf->buf_pa)) { + PRINTM(MERROR, + "Failed to write download command to boot code\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Write the upper 32bits of the physical address to scratch + * register 1 */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_scratch_1, + (t_u32)((t_u64)pmbuf->buf_pa >> 32))) { + PRINTM(MERROR, + "Failed to write download command to boot code\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Write the command length to scratch register 2 */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_scratch_2, + pmbuf->data_len)) { + PRINTM(MERROR, + "Failed to write command length to scratch register 2\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Ring the door bell */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_cpu_int_event, + CPU_INTR_DOOR_BELL)) { + PRINTM(MERROR, "Failed to assert door-bell interrupt\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + LEAVE(); + return ret; + +done: + if (MLAN_STATUS_FAILURE == + pcb->moal_unmap_memory( + pmadapter->pmoal_handle, pmbuf->pbuf + pmbuf->data_offset, + pmbuf->buf_pa, WLAN_UPLD_SIZE, PCI_DMA_TODEVICE)) + PRINTM(MERROR, "BootCmd: failed to moal_unmap_memory\n"); + LEAVE(); + return ret; +} + +/** + * @brief This function init rx port in firmware + * + * @param pmadapter A pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_pcie_init_fw(pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 txbd_val = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + txbd_val = pmadapter->pcard_pcie->txbd_wrptr & + pmadapter->pcard_pcie->reg->txrx_rw_ptr_wrap_mask; + txbd_val = txbd_val << TXBD_RW_PTR_START; + } +#endif + /* Write the RX ring write pointer in to REG_RXBD_WRPTR */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_rxbd_wrptr, + pmadapter->pcard_pcie->rxbd_wrptr | txbd_val)) { + PRINTM(MERROR, "Init FW: failed to write REG_RXBD_WRPTR\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Write the event ring write pointer in to REG_EVTBD_WRPTR */ + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_evtbd_wrptr, + pmadapter->pcard_pcie->evtbd_wrptr)) { + PRINTM(MERROR, "Init FW: failed to write REG_EVTBD_WRPTR\n"); + ret = MLAN_STATUS_FAILURE; + } +done: + return ret; +} + +/** + * @brief This function downloads FW blocks to device + * + * @param pmadapter A pointer to mlan_adapter + * @param fw A pointer to firmware image + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_pcie_prog_fw_w_helper(mlan_adapter *pmadapter, + mlan_fw_image *fw) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *firmware = fw->pfw_buf; + t_u32 firmware_len = fw->fw_len; + t_u32 offset = 0; + mlan_buffer *pmbuf = MNULL; + t_u32 txlen, tx_blocks = 0, tries, len; + t_u32 block_retry_cnt = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; +#if defined(PCIE9098) + t_u32 rev_id_reg = 0; + t_u32 revision_id = 0; +#endif + t_u8 check_fw_status = MFALSE; + t_u32 fw_dnld_status = 0; + t_u32 fw_dnld_offset = 0; + t_u8 mic_retry = 0; + + ENTER(); + if (!pmadapter) { + PRINTM(MERROR, "adapter structure is not valid\n"); + goto done; + } + + if (!firmware || !firmware_len) { + PRINTM(MERROR, + "No firmware image found! Terminating download\n"); + goto done; + } + + PRINTM(MINFO, "Downloading FW image (%d bytes)\n", firmware_len); + + if (wlan_disable_pcie_host_int(pmadapter)) { + PRINTM(MERROR, "prog_fw: Disabling interrupts failed\n"); + goto done; + } + + pmbuf = wlan_alloc_mlan_buffer(pmadapter, WLAN_UPLD_SIZE, 0, + MOAL_ALLOC_MLAN_BUFFER); + if (!pmbuf) { + PRINTM(MERROR, "prog_fw: Unable to allocate mlan_buffer\n"); + goto done; + } +#if defined(PCIE9098) + if (IS_PCIE9098(pmadapter->card_type)) { + rev_id_reg = pmadapter->pcard_pcie->reg->reg_rev_id; + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, rev_id_reg, + &revision_id); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Failed to read PCIe revision id register\n"); + goto done; + } + /* Skyhawk A0, need to check both CRC and MIC error */ + if (revision_id >= CHIP_9098_REV_A0) + check_fw_status = MTRUE; + } +#endif + /* For PCIE9097, need check both CRC and MIC error */ +#if defined(PCIE9097) + if (IS_PCIE9097(pmadapter->card_type)) + check_fw_status = MTRUE; +#endif + + /* Perform firmware data transfer */ + do { + t_u32 ireg_intr = 0; + t_u32 read_retry_cnt = 0; + + /* More data? */ + if (offset >= firmware_len) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = pcb->moal_read_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_scratch_2, + &len); + if (ret) { + PRINTM(MWARN, + "Failed reading length from boot code\n"); + goto done; + } + if (len || offset) + break; + wlan_udelay(pmadapter, 10); + } + + if (!len) { + break; + } else if (len > WLAN_UPLD_SIZE) { + PRINTM(MERROR, + "FW download failure @ %d, invalid length %d\n", + offset, len); + goto done; + } + + txlen = len; + + if (len & MBIT(0)) { + if (check_fw_status) { + /* Get offset from fw dnld offset Register */ + ret = pcb->moal_read_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_scratch_6, + &fw_dnld_offset); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "FW download failure @ %d, reading fw dnld offset failed\n", + offset); + goto done; + } + /* Get CRC MIC error from fw dnld status + * Register */ + ret = pcb->moal_read_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_scratch_7, + &fw_dnld_status); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "FW download failure @ %d, reading fw dnld status failed\n", + offset); + goto done; + } + PRINTM(MERROR, + "FW download error: status=0x%x offset = 0x%x fw offset = 0x%x\n", + fw_dnld_status, offset, fw_dnld_offset); + } + block_retry_cnt++; + if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) { + PRINTM(MERROR, + "FW download failure @ %d, over max " + "retry count\n", + offset); + goto done; + } + PRINTM(MERROR, + "FW CRC error indicated by the " + "helper: len = 0x%04X, txlen = %d\n", + len, txlen); + len &= ~MBIT(0); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + if (fw_dnld_status & (MBIT(6) | MBIT(7))) { + offset = 0; + mic_retry++; + if (mic_retry > MAX_FW_RETRY) { + PRINTM(MERROR, + "FW download failure @ %d, over max " + "mic retry count\n", + offset); + goto done; + } + } + } else { + block_retry_cnt = 0; + /* Set blocksize to transfer - checking for last block + */ + if (firmware_len - offset < txlen) + txlen = firmware_len - offset; + + PRINTM(MINFO, "."); + + tx_blocks = (txlen + MLAN_PCIE_BLOCK_SIZE_FW_DNLD - 1) / + MLAN_PCIE_BLOCK_SIZE_FW_DNLD; + + /* Copy payload to buffer */ + memmove(pmadapter, pmbuf->pbuf + pmbuf->data_offset, + &firmware[offset], txlen); + pmbuf->data_len = txlen; + } + + /* Send the boot command to device */ + if (wlan_pcie_send_boot_cmd(pmadapter, pmbuf)) { + PRINTM(MERROR, + "Failed to send firmware download command\n"); + goto done; + } + /* Wait for the command done interrupt */ + do { + if (read_retry_cnt > MAX_READ_REG_RETRY) { + PRINTM(MERROR, + "prog_fw: Failed to get command done interrupt " + "retry count = %d\n", + read_retry_cnt); + goto done; + } + if (pcb->moal_read_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_cpu_int_status, + &ireg_intr)) { + PRINTM(MERROR, + "prog_fw: Failed to read " + "interrupt status during fw dnld\n"); + /* buffer was mapped in send_boot_cmd, unmap + * first */ + pcb->moal_unmap_memory( + pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->buf_pa, WLAN_UPLD_SIZE, + PCI_DMA_TODEVICE); + goto done; + } + read_retry_cnt++; + pcb->moal_usleep_range(pmadapter->pmoal_handle, 10, 20); + } while ((ireg_intr & CPU_INTR_DOOR_BELL) == + CPU_INTR_DOOR_BELL); + /* got interrupt - can unmap buffer now */ + if (MLAN_STATUS_FAILURE == + pcb->moal_unmap_memory(pmadapter->pmoal_handle, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->buf_pa, WLAN_UPLD_SIZE, + PCI_DMA_TODEVICE)) { + PRINTM(MERROR, + "prog_fw: failed to moal_unmap_memory\n"); + goto done; + } + offset += txlen; + } while (MTRUE); + + PRINTM(MMSG, "FW download over, size %d bytes\n", offset); + + ret = MLAN_STATUS_SUCCESS; +done: + if (pmbuf) + wlan_free_mlan_buffer(pmadapter, pmbuf); + + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function get pcie device from card type + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_get_pcie_device(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 card_type = pmadapter->card_type; + + ENTER(); + + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_pcie_card), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->pcard_pcie); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pcard_pcie) { + PRINTM(MERROR, "Failed to allocate pcard_pcie\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + switch (card_type) { +#ifdef PCIE8897 + case CARD_TYPE_PCIE8897: + pmadapter->pcard_pcie->reg = &mlan_reg_pcie8897; + pmadapter->pcard_info = &mlan_card_info_pcie8897; + break; +#endif +#ifdef PCIE8997 + case CARD_TYPE_PCIE8997: + pmadapter->pcard_pcie->reg = &mlan_reg_pcie8997; + pmadapter->pcard_info = &mlan_card_info_pcie8997; + break; +#endif +#if defined(PCIE9098) || defined(PCIE9097) + case CARD_TYPE_PCIE9097: + case CARD_TYPE_PCIE9098: + pmadapter->pcard_pcie->reg = &mlan_reg_pcie9098; + pmadapter->pcard_info = &mlan_card_info_pcie9098; +#ifdef PCIE9097 + if (card_type == CARD_TYPE_PCIE9097 && + pmadapter->card_rev == CHIP_9097_REV_B0) + pmadapter->pcard_pcie->reg = &mlan_reg_pcie9097_b0; +#endif + break; +#endif + default: + PRINTM(MERROR, "can't get right pcie card type \n"); + ret = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief PCIE wakeup handler + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_pcie_wakeup(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 data = 0; + ENTER(); + /* Enable interrupts or any chip access will wakeup device */ + ret = pmadapter->callbacks.moal_read_reg( + pmadapter->pmoal_handle, pmadapter->pcard_pcie->reg->reg_ip_rev, + &data); + + if (ret == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, + "PCIE wakeup: Read PCI register to wakeup device ...\n"); + } else { + PRINTM(MINFO, "PCIE wakeup: Failed to wakeup device ...\n"); + ret = MLAN_STATUS_FAILURE; + } + LEAVE(); + return ret; +} + +/** + * @brief This function gets interrupt status. + * + */ +/** + * @param msg_id A message id + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_FAILURE -- if the intererupt is not for us + */ +mlan_status wlan_pcie_interrupt(t_u16 msg_id, pmlan_adapter pmadapter) +{ + t_u32 pcie_ireg; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_void *pmoal_handle = pmadapter->pmoal_handle; + t_void *pint_lock = pmadapter->pint_lock; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pmadapter->pcard_pcie->pcie_int_mode == PCIE_INT_MODE_MSI) { + pcb->moal_spin_lock(pmoal_handle, pint_lock); + pmadapter->ireg = 1; + pcb->moal_spin_unlock(pmoal_handle, pint_lock); + } else if (pmadapter->pcard_pcie->pcie_int_mode == PCIE_INT_MODE_MSIX) { + pcie_ireg = (1 << msg_id) & + pmadapter->pcard_pcie->reg->host_intr_mask; + if (pcie_ireg) { + if (!pmadapter->pps_uapsd_mode && + (pmadapter->ps_state == PS_STATE_SLEEP)) { + pmadapter->pm_wakeup_fw_try = MFALSE; + pmadapter->ps_state = PS_STATE_AWAKE; + pmadapter->pm_wakeup_card_req = MFALSE; + } + } + pcb->moal_spin_lock(pmoal_handle, pint_lock); + pmadapter->ireg |= pcie_ireg; + pcb->moal_spin_unlock(pmoal_handle, pint_lock); + + PRINTM(MINTR, "ireg: 0x%08x\n", pcie_ireg); + } else { + wlan_pcie_disable_host_int_mask(pmadapter); + if (pcb->moal_read_reg( + pmoal_handle, + pmadapter->pcard_pcie->reg->reg_host_int_status, + &pcie_ireg)) { + PRINTM(MWARN, "Read register failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { + PRINTM(MINTR, "pcie_ireg=0x%x\n", pcie_ireg); + if (!pmadapter->pps_uapsd_mode && + (pmadapter->ps_state == PS_STATE_SLEEP)) { + /* Potentially for PCIe we could get other + * interrupts like shared. */ + pmadapter->pm_wakeup_fw_try = MFALSE; + pmadapter->ps_state = PS_STATE_AWAKE; + pmadapter->pm_wakeup_card_req = MFALSE; + } + pcb->moal_spin_lock(pmoal_handle, pint_lock); + pmadapter->ireg |= pcie_ireg; + pcb->moal_spin_unlock(pmoal_handle, pint_lock); + + /* Clear the pending interrupts */ + if (pcb->moal_write_reg(pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_host_int_status, + ~pcie_ireg)) { + PRINTM(MWARN, "Write register failed\n"); + LEAVE(); + return ret; + } + } else { + wlan_pcie_enable_host_int_mask(pmadapter); + PRINTM(MINFO, "This is not our interrupt\n"); + ret = MLAN_STATUS_FAILURE; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function checks the msix 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_msix_int(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 pcie_ireg = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->pint_lock); + pcie_ireg = + pmadapter->ireg & pmadapter->pcard_pcie->reg->host_intr_mask; + pmadapter->ireg = 0; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, pmadapter->pint_lock); + + if (pcie_ireg & pmadapter->pcard_pcie->reg->host_intr_dnld_done) { + PRINTM(MINFO, "<--- DATA sent Interrupt --->\n"); + ret = wlan_pcie_send_data_complete(pmadapter); + if (ret) + goto done; + } + if (pcie_ireg & pmadapter->pcard_pcie->reg->host_intr_upld_rdy) { + PRINTM(MINFO, "Rx DATA\n"); + ret = wlan_pcie_process_recv_data(pmadapter); + if (ret) + goto done; + } + if (pcie_ireg & pmadapter->pcard_pcie->reg->host_intr_event_rdy) { + PRINTM(MINFO, "Rx EVENT\n"); + ret = wlan_pcie_process_event_ready(pmadapter); + if (ret) + goto done; + } + if (pcie_ireg & pmadapter->pcard_pcie->reg->host_intr_cmd_done) { + if (pmadapter->cmd_sent) { + PRINTM(MINFO, "<--- CMD sent Interrupt --->\n"); + pmadapter->cmd_sent = MFALSE; + } + ret = wlan_pcie_process_cmd_resp(pmadapter); + if (ret) + goto done; + } +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->host_intr_cmd_dnld && + (pcie_ireg & pmadapter->pcard_pcie->reg->host_intr_cmd_dnld)) { + if (pmadapter->cmd_sent) + pmadapter->cmd_sent = MFALSE; + if (pmadapter->pcard_pcie->vdll_cmd_buf) + wlan_pcie_send_vdll_complete(pmadapter); + PRINTM(MINFO, "<--- CMD DNLD DONE Interrupt --->\n"); + } +#endif + PRINTM(MINFO, "cmd_sent=%d data_sent=%d\n", pmadapter->cmd_sent, + pmadapter->data_sent); + +done: + 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_pcie_int_status(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 pcie_ireg = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (pmadapter->pcard_pcie->pcie_int_mode == PCIE_INT_MODE_MSIX) { + wlan_process_msix_int(pmadapter); + goto done; + } + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->pint_lock); + if (pmadapter->pcard_pcie->pcie_int_mode != PCIE_INT_MODE_MSI) + pcie_ireg = pmadapter->ireg; + pmadapter->ireg = 0; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, pmadapter->pint_lock); + if (pmadapter->pcard_pcie->pcie_int_mode == PCIE_INT_MODE_MSI) { + if (pcb->moal_read_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_host_int_status, + &pcie_ireg)) { + PRINTM(MWARN, "Read register failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { + PRINTM(MINTR, "pcie_ireg=0x%x\n", pcie_ireg); + if (pmadapter->pcard_pcie->reg->msi_int_wr_clr) { + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_host_int_status, + ~pcie_ireg)) { + PRINTM(MWARN, + "Write register failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (!pmadapter->pps_uapsd_mode && + (pmadapter->ps_state == PS_STATE_SLEEP)) { + /* Potentially for PCIe we could get other + * interrupts like shared. */ + pmadapter->pm_wakeup_fw_try = MFALSE; + pmadapter->ps_state = PS_STATE_AWAKE; + pmadapter->pm_wakeup_card_req = MFALSE; + } + } + } + while (pcie_ireg & pmadapter->pcard_pcie->reg->host_intr_mask) { + if (pcie_ireg & + pmadapter->pcard_pcie->reg->host_intr_dnld_done) { + pcie_ireg &= + ~pmadapter->pcard_pcie->reg->host_intr_dnld_done; + PRINTM(MINFO, "<--- DATA sent Interrupt --->\n"); + ret = wlan_pcie_send_data_complete(pmadapter); + if (ret) + goto done; + } + if (pcie_ireg & + pmadapter->pcard_pcie->reg->host_intr_upld_rdy) { + pcie_ireg &= + ~pmadapter->pcard_pcie->reg->host_intr_upld_rdy; + PRINTM(MINFO, "Rx DATA\n"); + pmadapter->callbacks.moal_tp_accounting_rx_param( + pmadapter->pmoal_handle, 0, 0); + ret = wlan_pcie_process_recv_data(pmadapter); + if (ret) + goto done; + } + if (pcie_ireg & + pmadapter->pcard_pcie->reg->host_intr_event_rdy) { + pcie_ireg &= + ~pmadapter->pcard_pcie->reg->host_intr_event_rdy; + PRINTM(MINFO, "Rx EVENT\n"); + ret = wlan_pcie_process_event_ready(pmadapter); + if (ret) + goto done; + } + if (pcie_ireg & + pmadapter->pcard_pcie->reg->host_intr_cmd_done) { + pcie_ireg &= + ~pmadapter->pcard_pcie->reg->host_intr_cmd_done; + if (pmadapter->cmd_sent) { + PRINTM(MINFO, "<--- CMD sent Interrupt --->\n"); + pmadapter->cmd_sent = MFALSE; + } + ret = wlan_pcie_process_cmd_resp(pmadapter); + if (ret) + goto done; + } +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->host_intr_cmd_dnld && + (pcie_ireg & + pmadapter->pcard_pcie->reg->host_intr_cmd_dnld)) { + pcie_ireg &= + ~pmadapter->pcard_pcie->reg->host_intr_cmd_dnld; + if (pmadapter->cmd_sent) + pmadapter->cmd_sent = MFALSE; + if (pmadapter->pcard_pcie->vdll_cmd_buf) + wlan_pcie_send_vdll_complete(pmadapter); + PRINTM(MINFO, "<--- CMD DNLD DONE Interrupt --->\n"); + } +#endif + if (pmadapter->pcard_pcie->pcie_int_mode == PCIE_INT_MODE_MSI) { + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pint_lock); + pmadapter->ireg = 0; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pint_lock); + } + if (pcb->moal_read_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_host_int_status, + &pcie_ireg)) { + PRINTM(MWARN, "Read register failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { + PRINTM(MINTR, "Poll: pcie_ireg=0x%x\n", pcie_ireg); + if ((pmadapter->pcard_pcie->pcie_int_mode == + PCIE_INT_MODE_LEGACY) || + pmadapter->pcard_pcie->reg->msi_int_wr_clr) { + if (pcb->moal_write_reg( + pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg + ->reg_host_int_status, + ~pcie_ireg)) { + PRINTM(MWARN, + "Write register failed\n"); + return MLAN_STATUS_FAILURE; + } + } + if (pmadapter->pcard_pcie->pcie_int_mode != + PCIE_INT_MODE_MSI) { + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pint_lock); + pcie_ireg |= pmadapter->ireg; + pmadapter->ireg = 0; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pint_lock); + } + /* Don't update the pmadapter->pcie_ireg, + * serving the status right now */ + } + } + PRINTM(MINFO, "cmd_sent=%d data_sent=%d\n", pmadapter->cmd_sent, + pmadapter->data_sent); + if (pmadapter->pcard_pcie->pcie_int_mode != PCIE_INT_MODE_MSI) { + if (pmadapter->ps_state != PS_STATE_SLEEP || + pmadapter->pcard_info->supp_ps_handshake) + wlan_pcie_enable_host_int_mask(pmadapter); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sets DRV_READY register + * + * @param pmadapter A pointer to mlan_adapter structure + * @param val Value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + * + */ +mlan_status wlan_set_drv_ready_reg(mlan_adapter *pmadapter, t_u32 val) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_drv_ready, + val)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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_pcie_check_winner_status(mlan_adapter *pmadapter, t_u32 *val) +{ + t_u32 winner = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + pcb->moal_read_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_scratch_3, + &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_pcie_check_fw_status(mlan_adapter *pmadapter, t_u32 pollnum) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 firmware_stat; + t_u32 tries; + + ENTER(); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + if (pcb->moal_read_reg(pmadapter->pmoal_handle, + pmadapter->pcard_pcie->reg->reg_scratch_3, + &firmware_stat)) + ret = MLAN_STATUS_FAILURE; + else + ret = MLAN_STATUS_SUCCESS; + if (ret) + continue; + if (firmware_stat == PCIE_FIRMWARE_READY) { + ret = MLAN_STATUS_SUCCESS; + break; + } else { + wlan_mdelay(pmadapter, 100); + ret = MLAN_STATUS_FAILURE; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function init the pcie interface + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_pcie_init(mlan_adapter *pmadapter) +{ + ENTER(); + + PRINTM(MINFO, "Setting driver ready signature\n"); + if (wlan_set_drv_ready_reg(pmadapter, PCIE_FIRMWARE_READY)) { + PRINTM(MERROR, "Failed to write driver ready signature\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @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_pcie_dnld_fw(pmlan_adapter pmadapter, pmlan_fw_image pmfw) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 poll_num = 1; + t_u32 winner = 0; + + ENTER(); + + ret = wlan_pcie_init(pmadapter); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "WLAN PCIE init failed\n", ret); + LEAVE(); + return ret; + } + /* Check if firmware is already running */ + ret = wlan_pcie_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_pcie_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; + } + + /* Download the firmware image via helper */ + ret = wlan_pcie_prog_fw_w_helper(pmadapter, pmfw); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "prog_fw failed ret=%#x\n", ret); + LEAVE(); + return ret; + } +poll_fw: + /* Check if the firmware is downloaded successfully or not */ + ret = wlan_pcie_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_pcie_host_int(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief This function downloads data from driver to card. + * + * Both commands and data packets are transferred to the card + * by this function. This function adds the PCIE specific header + * to the front of the buffer before transferring. The header + * contains the length of the packet and the type. The firmware + * handles the packets based upon this set type. + * + * @param pmpriv A pointer to mlan_private structure + * @param type data or command + * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include + * PCIE header) + * @param tx_param A pointer to mlan_tx_param (can be MNULL if type is + * command) + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_pcie_host_to_card(pmlan_private pmpriv, t_u8 type, + mlan_buffer *pmbuf, mlan_tx_param *tx_param) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = pmpriv->adapter; + + ENTER(); + + if (!pmbuf) { + PRINTM(MERROR, "Passed NULL pmbuf to %s\n", __FUNCTION__); + return MLAN_STATUS_FAILURE; + } + + if (type == MLAN_TYPE_DATA) + ret = wlan_pcie_send_data(pmadapter, type, pmbuf, tx_param); + else if (type == MLAN_TYPE_CMD) + ret = wlan_pcie_send_cmd(pmadapter, pmbuf); +#if defined(PCIE9098) || defined(PCIE9097) + else if (type == MLAN_TYPE_VDLL) + ret = wlan_pcie_send_vdll(pmadapter, pmbuf); +#endif + LEAVE(); + return ret; +} + +/** + * @brief This function allocates the PCIE buffer for SSU + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_alloc_ssu_pcie_buf(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer *pmbuf = MNULL; + /** Virtual base address of ssu buffer */ + t_u8 *ssu_vbase; + /** Physical base address of ssu buffer */ + t_u64 ssu_pbase = 0; + + ENTER(); + + if (pmadapter->ssu_buf) { + PRINTM(MCMND, "ssu buffer already allocated\n"); + LEAVE(); + return ret; + } + /* Allocate buffer here so that firmware can DMA data on it */ + pmbuf = wlan_alloc_mlan_buffer(pmadapter, 0, 0, MOAL_MALLOC_BUFFER); + if (!pmbuf) { + PRINTM(MERROR, + "SSU buffer create : Unable to allocate mlan_buffer\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + ret = pcb->moal_malloc_consistent(pmadapter->pmoal_handle, + MLAN_SSU_BUF_SIZE, &ssu_vbase, + &ssu_pbase); + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "%s: No free moal_malloc_consistent\n", + __FUNCTION__); + /* free pmbuf */ + wlan_free_mlan_buffer(pmadapter, pmbuf); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pmbuf->buf_pa = ssu_pbase; + pmbuf->pbuf = ssu_vbase; + pmbuf->data_offset = 0; + pmbuf->data_len = MLAN_SSU_BUF_SIZE; + pmbuf->total_pcie_buf_len = MLAN_SSU_BUF_SIZE; + + PRINTM(MCMND, + "SSU buffer: add new mlan_buffer base: %p, " + "buf_base: %p, data_offset: %x, buf_pbase: %#x:%x, " + "buf_len: %#x\n", + pmbuf, pmbuf->pbuf, pmbuf->data_offset, + (t_u32)((t_u64)pmbuf->buf_pa >> 32), (t_u32)pmbuf->buf_pa, + pmbuf->data_len); + + pmadapter->ssu_buf = pmbuf; + + LEAVE(); + return ret; +} + +/** + * @brief This function frees the allocated ssu buffer. + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_free_ssu_pcie_buf(pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer *pmbuf = MNULL; + t_u8 *ssu_vbase; + t_u64 ssu_pbase; + + ENTER(); + if (!pmadapter) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (pmadapter->ssu_buf) { + pmbuf = pmadapter->ssu_buf; + ssu_vbase = pmbuf->pbuf; + ssu_pbase = pmbuf->buf_pa; + if (ssu_vbase) + pcb->moal_mfree_consistent(pmadapter->pmoal_handle, + pmbuf->total_pcie_buf_len, + ssu_vbase, ssu_pbase); + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + pmadapter->ssu_buf = MNULL; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function allocates the PCIE ring buffers + * + * The following initializations steps are followed - + * - Allocate TXBD ring buffers + * - Allocate RXBD ring buffers + * - Allocate event BD ring buffers + * - Allocate command and command response buffer + * - Allocate sleep cookie buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_alloc_pcie_ring_buf(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + pmadapter->pcard_pcie->cmdrsp_buf = MNULL; + ret = wlan_pcie_create_txbd_ring(pmadapter); + if (ret) + goto err_cre_txbd; + ret = wlan_pcie_create_rxbd_ring(pmadapter); + if (ret) + goto err_cre_rxbd; + ret = wlan_pcie_create_evtbd_ring(pmadapter); + if (ret) + goto err_cre_evtbd; + ret = wlan_pcie_alloc_cmdrsp_buf(pmadapter); + if (ret) + goto err_alloc_cmdbuf; + return ret; +err_alloc_cmdbuf: + wlan_pcie_delete_evtbd_ring(pmadapter); +err_cre_evtbd: + wlan_pcie_delete_rxbd_ring(pmadapter); +err_cre_rxbd: + wlan_pcie_delete_txbd_ring(pmadapter); +err_cre_txbd: + + LEAVE(); + return ret; +} + +/** + * @brief This function frees the allocated ring buffers. + * + * The following are freed by this function - + * - TXBD ring buffers + * - RXBD ring buffers + * - Event BD ring buffers + * - Command and command response buffer + * - Sleep cookie buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_free_pcie_ring_buf(pmlan_adapter pmadapter) +{ + ENTER(); + + wlan_pcie_delete_cmdrsp_buf(pmadapter); + wlan_pcie_delete_evtbd_ring(pmadapter); + wlan_pcie_delete_rxbd_ring(pmadapter); + wlan_pcie_delete_txbd_ring(pmadapter); + pmadapter->pcard_pcie->cmdrsp_buf = MNULL; +#ifdef RPTR_MEM_COP + if ((pmadapter->card_type == CARD_TYPE_PCIE9098) || + (pmadapter->card_type == CARD_TYPE_PCIE9097)) + wlan_pcie_free_rdptrs(pmadapter); +#endif + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function cleans up packets in the ring buffers. + * + * The following are cleaned by this function - + * - TXBD ring buffers + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_clean_pcie_ring_buf(pmlan_adapter pmadapter) +{ + ENTER(); +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) + wlan_pcie_flush_txbd_ring(pmadapter); +#endif + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command to set PCI-Express + * host buffer configuration + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_set_pcie_buf_config(mlan_private *pmpriv) +{ + pmlan_adapter pmadapter = MNULL; +#if defined(PCIE8997) || defined(PCIE8897) + HostCmd_DS_PCIE_HOST_BUF_DETAILS host_spec; +#endif + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + if (!pmpriv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter = pmpriv->adapter; +#if defined(PCIE8997) || defined(PCIE8897) + if (!pmadapter->pcard_pcie->reg->use_adma) { + memset(pmadapter, &host_spec, 0, + sizeof(HostCmd_DS_PCIE_HOST_BUF_DETAILS)); + + /* Send the ring base addresses and count to firmware */ + host_spec.txbd_addr_lo = + (t_u32)(pmadapter->pcard_pcie->txbd_ring_pbase); + host_spec.txbd_addr_hi = (t_u32)( + ((t_u64)pmadapter->pcard_pcie->txbd_ring_pbase) >> 32); + host_spec.txbd_count = MLAN_MAX_TXRX_BD; + host_spec.rxbd_addr_lo = + (t_u32)(pmadapter->pcard_pcie->rxbd_ring_pbase); + host_spec.rxbd_addr_hi = (t_u32)( + ((t_u64)pmadapter->pcard_pcie->rxbd_ring_pbase) >> 32); + host_spec.rxbd_count = MLAN_MAX_TXRX_BD; + host_spec.evtbd_addr_lo = + (t_u32)(pmadapter->pcard_pcie->evtbd_ring_pbase); + host_spec.evtbd_addr_hi = (t_u32)( + ((t_u64)pmadapter->pcard_pcie->evtbd_ring_pbase) >> 32); + host_spec.evtbd_count = MLAN_MAX_EVT_BD; + + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_PCIE_HOST_BUF_DETAILS, + HostCmd_ACT_GEN_SET, 0, MNULL, + &host_spec); + if (ret) { + PRINTM(MERROR, + "PCIE_HOST_BUF_CFG: send command failed\n"); + ret = MLAN_STATUS_FAILURE; + } + } +#endif +#if defined(PCIE9098) || defined(PCIE9097) + if (pmadapter->pcard_pcie->reg->use_adma) { + /** config ADMA for Tx Data */ + wlan_init_adma(pmadapter, ADMA_TX_DATA, + pmadapter->pcard_pcie->txbd_ring_pbase, + TX_RX_NUM_DESC, MTRUE); + /** config ADMA for Rx Data */ + wlan_init_adma(pmadapter, ADMA_RX_DATA, + pmadapter->pcard_pcie->rxbd_ring_pbase, + TX_RX_NUM_DESC, MTRUE); + /** config ADMA for Rx Event */ + wlan_init_adma(pmadapter, ADMA_EVENT, + pmadapter->pcard_pcie->evtbd_ring_pbase, + EVT_NUM_DESC, MTRUE); + /** config ADMA for cmd */ + wlan_init_adma(pmadapter, ADMA_CMD, 0, 0, MTRUE); + /** config ADMA for cmdresp */ + wlan_init_adma(pmadapter, ADMA_CMDRESP, + pmadapter->pcard_pcie->cmdrsp_buf->buf_pa, 0, + MTRUE); + } +#endif + wlan_pcie_init_fw(pmadapter); + LEAVE(); + return ret; +} + +#if defined(PCIE8997) || defined(PCIE8897) +/** + * @brief This function prepares command PCIE host buffer config. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_pcie_host_buf_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_pvoid pdata_buf) +{ + HostCmd_DS_PCIE_HOST_BUF_DETAILS *ppcie_hoost_spec = + &cmd->params.pcie_host_spec; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_PCIE_HOST_BUF_DETAILS); + cmd->size = wlan_cpu_to_le16( + (sizeof(HostCmd_DS_PCIE_HOST_BUF_DETAILS)) + S_DS_GEN); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + memcpy_ext(pmpriv->adapter, ppcie_hoost_spec, pdata_buf, + sizeof(HostCmd_DS_PCIE_HOST_BUF_DETAILS), + sizeof(HostCmd_DS_PCIE_HOST_BUF_DETAILS)); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function wakes up the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param timeout set timeout flag + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_pm_pcie_wakeup_card(pmlan_adapter pmadapter, t_u8 timeout) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 age_ts_usec; + + ENTER(); + PRINTM(MEVENT, "Wakeup device...\n"); + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->pm_wakeup_in_secs, + &age_ts_usec); + + if (timeout) { + pmadapter->callbacks.moal_start_timer( + pmadapter->pmoal_handle, pmadapter->pwakeup_fw_timer, + MFALSE, MRVDRV_TIMER_3S); + pmadapter->wakeup_fw_timer_is_set = MTRUE; + } + + ret = wlan_pcie_wakeup(pmadapter); + + LEAVE(); + return ret; +} + +mlan_status wlan_pcie_debug_dump(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_buffer pmbuf = pmadapter->pcard_pcie->cmdrsp_buf; + ENTER(); + + if (pmbuf == MNULL) { + PRINTM(MMSG, "Rx CMD response pmbuf is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + PRINTM(MERROR, "Dump Rx CMD Response Buf:\n"); + DBG_HEXDUMP(MERROR, "CmdResp Buf", pmbuf->pbuf + pmbuf->data_offset, + 64); + + LEAVE(); + return ret; +} + +/** + * @brief This function handle data complete + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the mlan_buffer + * @return N/A + */ +mlan_status wlan_pcie_data_complete(pmlan_adapter pmadapter, mlan_buffer *pmbuf, + mlan_status status) +{ + ENTER(); + + wlan_free_mlan_buffer(pmadapter, pmbuf); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +mlan_adapter_operations mlan_pcie_ops = { + .dnld_fw = wlan_pcie_dnld_fw, + .interrupt = wlan_pcie_interrupt, + .process_int_status = wlan_process_pcie_int_status, + .host_to_card = wlan_pcie_host_to_card, + .wakeup_card = wlan_pm_pcie_wakeup_card, + .reset_card = wlan_pcie_wakeup, + .event_complete = wlan_pcie_event_complete, + .data_complete = wlan_pcie_data_complete, + .cmdrsp_complete = wlan_pcie_cmdrsp_complete, + .handle_rx_packet = wlan_handle_rx_packet, + .debug_dump = wlan_pcie_debug_dump, + .intf_header_len = PCIE_INTF_HEADER_LEN, +}; diff --git a/mxm_wifiex/wlan_src/mlan/mlan_pcie.h b/mxm_wifiex/wlan_src/mlan/mlan_pcie.h new file mode 100644 index 0000000..af40437 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_pcie.h @@ -0,0 +1,650 @@ +/** @file mlan_pcie.h + * + * @brief This file contains definitions for PCIE interface. + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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/01/2012: initial version +********************************************************/ + +#ifndef _MLAN_PCIE_H_ +#define _MLAN_PCIE_H_ +/** Tx DATA */ +#define ADMA_TX_DATA 0 +/** Rx DATA */ +#define ADMA_RX_DATA 1 +/** EVENT */ +#define ADMA_EVENT 2 +/** CMD */ +#define ADMA_CMD 3 +/** CMD RESP */ +#define ADMA_CMDRESP 4 +/** ADMA direction */ +#define ADMA_HOST_TO_DEVICE 0 +/** ADMA direction Rx */ +#define ADMA_DEVICE_TO_HOST 1 +/** Direct Program mode */ +#define DMA_MODE_DIRECT 0 +/** Single descriptor mode */ +#define DMA_MODE_SINGLE_DESC 1 +/** dual discriptor mode */ +#define DMA_MODE_DUAL_DESC 2 +/** descriptor mode: ring mode */ +#define DESC_MODE_RING 0 +/** descriptor mode: chain mode */ +#define DESC_MODE_CHAIN 1 +/** DMA size start bit */ +#define DMA_SIZE_BIT 16 +/** DMA size mask */ +#define DMA_SIZE_MASK 0xffff0000 +/** Descriptor mode */ +#define DESC_MODE_MASK 0x0004 +/** DMA MODE MASK */ +#define DMA_MODE_MASK 0x0003 +/** Dest Num Descriptor start bits */ +#define DST_NUM_DESC_BIT 12 +/** Destination Num of Descriptor mask */ +#define DST_NUM_DESC_MASK 0xf000 +/** Src Num Descriptor start bits */ +#define SRC_NUM_DESC_BIT 8 +/** Destination Num of Descriptor mask */ +#define SRC_NUM_DESC_MASK 0x0f00 +/** Virtual Q priority mask */ +#define Q_PRIO_WEIGHT_MASK 0x00f0 +/** DMA cfg register offset*/ +#define ADMA_DMA_CFG 0x0000 +/** source base low */ +#define ADMA_SRC_LOW 0x0004 +/** source base high */ +#define ADMA_SRC_HIGH 0x0008 +/** destination base low */ +#define ADMA_DST_LOW 0x000C +/** destination base high */ +#define ADMA_DST_HIGH 0x0010 +/** source rd/wr pointer */ +#define ADMA_SRC_RW_PTR 0x0014 +/** destination rd/wr pointer */ +#define ADMA_DST_RW_PTR 0x0018 +/** interrupt direction mapping reg, for each virtual Q, used for + * dual-descriptor only, only valid for Q0 */ +#define ADMA_INT_MAPPING 0x001C +/** destination interrupt to device */ +#define DEST_INT_TO_DEVICE MBIT(0) +/** destination interrupt to host */ +#define DEST_INT_TO_HOST MBIT(1) +/** interrupt pending status for each virtual Q, only valid for Q0 */ +#define ADMA_INT_PENDING 0x0020 +/** Default ADMA INT mask, We only enable dma done */ +#define DEF_ADMA_INT_MASK MBIT(0) +/** source interrupt status mask reg */ +#define ADMA_SRC_INT_STATUS_MASK 0x0024 +/** source interrupt mask reg */ +#define ADMA_SRC_INT_MASK 0x0028 +/** source interrupt status reg */ +#define ADMA_SRC_INT_STATUS 0x002C +/** destination interrupt status mask reg */ +#define ADMA_DST_INT_STATUS_MASK 0x0030 +/** destination interrupt mask reg */ +#define ADMA_DST_INT_MASK 0x0034 +/** destination interrupt status reg */ +#define ADMA_DST_INT_STATUS 0x0038 +/** DMA cfg2 register */ +#define ADMA_DMA_CFG2 0x003C +/** ADMA_MSI_LEGACY_DST_DMA_DONE_INT_BYPASS_EN */ +#define ADMA_MSI_LEGACY_DST_DMA_DONE_INT_BYPASS_EN MBIT(22) +/** ADMA_MSI_LEGACY_SRC_DMA_DONE_INT_BYPASS_EN */ +#define ADMA_MSI_LEGACY_SRC_DMA_DONE_INT_BYPASS_EN MBIT(21) +/* If this bit is set, MSIX trigger event will be from DST, other wise MSIX + * trigger event will be from SRC */ +#define ADMA_MSIX_INT_SRC_DST_SEL MBIT(20) +/** Enable MSI/Legacy for this Queue */ +#define ADMA_MSI_LEGACY_ENABLE MBIT(19) +/** Enable MSIX for this queue */ +#define ADMA_MSIX_ENABLE MBIT(18) +/** ADMA_DST_DMA_DONE_INT_BYPASS_EN */ +#define ADMA_DST_DMA_DONE_INT_BYPASS_EN MBIT(17) +/** SRC_DMA_DONE_INT_BYPASS_EN */ +#define ADMA_SRC_DMA_DONE_INT_BYPASS_EN MBIT(16) +/* Destination Read Pointer Memory Copy Enable */ +#define ADMA_DST_RPTR_MEM_COPY_EN MBIT(11) +/* Source Read Pointer Memory Copy Enable */ +#define ADMA_SRC_RPTR_MEM_COPY_EN MBIT(10) +/** Destination address is host */ +#define ADMA_DST_ADDR_IS_HOST MBIT(2) +/** Source address is host */ +#define ADMA_SRC_ADDR_IS_HOST MBIT(1) + +/** DMA cfg3 register */ +#define ADMA_DMA_CFG3 0x0040 +/** source rd ptr address low */ +#define ADMA_SRC_RD_PTR_LOW 0x0044 +/** source rd ptr address high */ +#define ADMA_SRC_RD_PTR_HIGH 0x0048 +/** destination rd ptr address low */ +#define ADMA_DST_RD_PTR_LOW 0x004C +/** destination rd ptr address high */ +#define ADMA_DST_RD_PTR_HIGH 0x0050 +/** source active interrupt mask */ +#define ADMA_SRC_ACTV_INT_MASK 0x0054 +/** destination active interrupt mask */ +#define ADMA_DST_ACTV_INT_MASK 0x0058 +/** Read pointer start from bit 16 */ +#define ADMA_RPTR_START 16 +/** write pointer start from bit 0 */ +#define ADMA_WPTR_START 0 +/** Tx/Rx Read/Write pointer's mask */ +#define TXRX_RW_PTR_MASK (MLAN_MAX_TXRX_BD - 1) +/** Tx/Rx Read/Write pointer's rollover indicate bit */ +#define TXRX_RW_PTR_ROLLOVER_IND MLAN_MAX_TXRX_BD +/** Start of packet flag */ +#define ADMA_BD_FLAG_SOP MBIT(0) +/** End of packet flag */ +#define ADMA_BD_FLAG_EOP MBIT(1) +/** interrupt enable flag */ +#define ADMA_BD_FLAG_INT_EN MBIT(2) +/** Source address is host side flag */ +#define ADMA_BD_FLAG_SRC_HOST MBIT(3) +/** Destination address is host side flag */ +#define ADMA_BD_FLAG_DST_HOST MBIT(4) +/** ADMA MIN PKT SIZE */ +#define ADMA_MIN_PKT_SIZE 128 +/** ADMA dual descriptor mode requir 8 bytes alignment in buf size */ +#define ADMA_ALIGN_SIZE 8 +/** ADMA RW_PTR wrap mask */ +#define ADMA_RW_PTR_WRAP_MASK 0x00001FFF +/** ADMA MSIX DOORBEEL DATA */ +#define ADMA_MSIX_DOORBELL_DATA 0x0064 +/** MSIX VECTOR MASK: BIT 0-10 */ +#define ADMA_MSIX_VECTOR_MASK 0x3f +/** PF mask: BIT 24-28 */ +#define ADMA_MSIX_PF_MASK 0x1f000000 +/** PF start bit */ +#define ADMA_MSIX_PF_BIT 24 + +#if defined(PCIE9098) || defined(PCIE9097) +/** PCIE9098 dev_id/vendor id reg */ +#define PCIE9098_DEV_ID_REG 0x0000 +/** PCIE revision ID register */ +#define PCIE9098_REV_ID_REG 0x0008 +/** PCIE IP revision register */ +#define PCIE9098_IP_REV_REG 0x1000 +/** PCIE CPU interrupt events */ +#define PCIE9098_CPU_INT_EVENT 0x1C20 +/** PCIE CPU interrupt status */ +#define PCIE9098_CPU_INT_STATUS 0x1C24 +/** PCIe CPU Interrupt Status Mask */ +#define PCIE9098_CPU_INT2ARM_ISM 0x1C28 +/** PCIE host interrupt status */ +#define PCIE9098_HOST_INT_STATUS 0x1C44 +/** PCIE host interrupt mask */ +#define PCIE9098_HOST_INT_MASK 0x1C48 +/** PCIE host interrupt clear select*/ +#define PCIE9098_HOST_INT_CLR_SEL 0x1C4C +/** PCIE host interrupt status mask */ +#define PCIE9098_HOST_INT_STATUS_MASK 0x1C50 +/** PCIE host interrupt status */ +#define PCIE9097_B0_HOST_INT_STATUS 0x3C44 +/** PCIE host interrupt mask */ +#define PCIE9097_B0_HOST_INT_MASK 0x3C48 +/** PCIE host interrupt clear select*/ +#define PCIE9097_B0_HOST_INT_CLR_SEL 0x3C4C +/** PCIE host interrupt status mask */ +#define PCIE9097_B0_HOST_INT_STATUS_MASK 0x3C50 +/** PCIE host interrupt select*/ +#define PCIE9098_HOST_INT_SEL 0x1C58 +/** PCIE data exchange register 0 */ +#define PCIE9098_SCRATCH_0_REG 0x1C60 +/** PCIE data exchange register 1 */ +#define PCIE9098_SCRATCH_1_REG 0x1C64 +/** PCIE data exchange register 2 */ +#define PCIE9098_SCRATCH_2_REG 0x1C68 +/** PCIE data exchange register 3 */ +#define PCIE9098_SCRATCH_3_REG 0x1C6C +/** PCIE data exchange register 4 */ +#define PCIE9098_SCRATCH_4_REG 0x1C70 +/** PCIE data exchange register 5 */ +#define PCIE9098_SCRATCH_5_REG 0x1C74 +/** PCIE data exchange register 6 */ +#define PCIE9098_SCRATCH_6_REG 0x1C78 +/** PCIE data exchange register 7 */ +#define PCIE9098_SCRATCH_7_REG 0x1C7C +/** PCIE data exchange register 8 */ +#define PCIE9098_SCRATCH_8_REG 0x1C80 +/** PCIE data exchange register 9 */ +#define PCIE9098_SCRATCH_9_REG 0x1C84 +/** PCIE data exchange register 10 */ +#define PCIE9098_SCRATCH_10_REG 0x1C88 +/** PCIE data exchange register 11 */ +#define PCIE9098_SCRATCH_11_REG 0x1C8C +/** PCIE data exchange register 12 */ +#define PCIE9098_SCRATCH_12_REG 0x1C90 +/** PCIE data exchange register 13 */ +#define PCIE9098_SCRATCH_13_REG 0x1C94 +/** PCIE data exchange register 14 */ +#define PCIE9098_SCRATCH_14_REG 0x1C98 +/** PCIE data exchange register 15 */ +#define PCIE9098_SCRATCH_15_REG 0x1C9C +/** ADMA CHAN0_Q0 start address, Tx Data */ +#define ADMA_CHAN0_Q0 0x10000 +/** ADMA CHAN1_Q0 start address, Rx Data */ +#define ADMA_CHAN1_Q0 0x10800 +/** ADMA CHAN1_Q1 start address, Rx Event */ +#define ADMA_CHAN1_Q1 0x10880 +/** ADMA CHAN2_Q0 start address, Tx Command */ +#define ADMA_CHAN2_Q0 0x11000 +/** ADMA CHAN2_Q1 start address, Command Resp */ +#define ADMA_CHAN2_Q1 0x11080 +/** CH0-Q0' src rd/wr ptr */ +#define ADMA_SRC_PTR_CH0_Q0 (ADMA_CHAN0_Q0 + ADMA_SRC_RW_PTR) +/** CH1-Q1' dest rd/wr ptr */ +#define ADMA_DST_PTR_CH1_Q0 (ADMA_CHAN1_Q0 + ADMA_DST_RW_PTR) +/** CH1-Q1' dest rd/wr ptr */ +#define ADMA_DST_PTR_CH1_Q1 (ADMA_CHAN1_Q1 + ADMA_DST_RW_PTR) +/* TX buffer description read pointer */ +#define PCIE9098_TXBD_RDPTR ADMA_SRC_PTR_CH0_Q0 +/* TX buffer description write pointer */ +#define PCIE9098_TXBD_WRPTR ADMA_SRC_PTR_CH0_Q0 +/* RX buffer description read pointer */ +#define PCIE9098_RXBD_RDPTR ADMA_DST_PTR_CH1_Q0 +/* RX buffer description write pointer */ +#define PCIE9098_RXBD_WRPTR ADMA_DST_PTR_CH1_Q0 +/* Event buffer description read pointer */ +#define PCIE9098_EVTBD_RDPTR ADMA_DST_PTR_CH1_Q1 +/* Event buffer description write pointer */ +#define PCIE9098_EVTBD_WRPTR ADMA_DST_PTR_CH1_Q1 +/* Driver ready signature write pointer */ +#define PCIE9098_DRV_READY PCIE9098_SCRATCH_12_REG + +/** interrupt bit define for ADMA CHAN0 Q0, For Tx DATA */ +#define ADMA_INT_CHAN0_Q0 MBIT(0) +/** interrupt bit define for ADMA CHAN1 Q0, For Rx Data */ +#define AMDA_INT_CHAN1_Q0 MBIT(16) +/** interrupt bit define for ADMA CHAN1 Q1, For Rx Event */ +#define AMDA_INT_CHAN1_Q1 MBIT(17) +/** interrupt bit define for ADMA CHAN2 Q0, For Tx Command */ +#define AMDA_INT_CHAN2_Q0 MBIT(24) +/** interrupt bit define for ADMA CHAN2 Q1, For Rx Command Resp */ +#define AMDA_INT_CHAN2_Q1 MBIT(25) + +/** interrupt vector number for ADMA CHAN0 Q0, For Tx DATA */ +#define ADMA_VECTOR_CHAN0_Q0 0 +/** interrupt vector number for ADMA CHAN1 Q0, For Rx Data */ +#define AMDA_VECTOR_CHAN1_Q0 16 +/** interrupt vector number for ADMA CHAN1 Q1, For Rx Event */ +#define AMDA_VECTOR_CHAN1_Q1 17 +/** interrupt vector number for ADMA CHAN2 Q0, For Tx Command */ +#define AMDA_VECTOR_CHAN2_Q0 24 +/** interrupt vector number for ADMA CHAN2 Q1, For Rx Command Resp */ +#define AMDA_VECTOR_CHAN2_Q1 25 + +/** Data sent interrupt for host */ +#define PCIE9098_HOST_INTR_DNLD_DONE ADMA_INT_CHAN0_Q0 +/** Data receive interrupt for host */ +#define PCIE9098_HOST_INTR_UPLD_RDY AMDA_INT_CHAN1_Q0 +/** Command sent interrupt for host */ +#define PCIE9098_HOST_INTR_CMD_DONE AMDA_INT_CHAN2_Q1 +/** Event ready interrupt for host */ +#define PCIE9098_HOST_INTR_EVENT_RDY AMDA_INT_CHAN1_Q1 +/** CMD sent interrupt for host */ +#define PCIE9098_HOST_INTR_CMD_DNLD MBIT(7) + +/** Interrupt mask for host */ +#define PCIE9098_HOST_INTR_MASK \ + (PCIE9098_HOST_INTR_DNLD_DONE | PCIE9098_HOST_INTR_UPLD_RDY | \ + PCIE9098_HOST_INTR_CMD_DONE | PCIE9098_HOST_INTR_CMD_DNLD | \ + PCIE9098_HOST_INTR_EVENT_RDY) + +/** Interrupt select mask for host */ +#define PCIE9098_HOST_INTR_SEL_MASK \ + (PCIE9098_HOST_INTR_DNLD_DONE | PCIE9098_HOST_INTR_UPLD_RDY | \ + PCIE9098_HOST_INTR_CMD_DONE | PCIE9098_HOST_INTR_EVENT_RDY) +#endif + +#if defined(PCIE8997) || defined(PCIE8897) +/* PCIE INTERNAL REGISTERS */ +/** PCIE data exchange register 0 */ +#define PCIE_SCRATCH_0_REG 0x0C10 +/** PCIE data exchange register 1 */ +#define PCIE_SCRATCH_1_REG 0x0C14 +/** PCIE CPU interrupt events */ +#define PCIE_CPU_INT_EVENT 0x0C18 +/** PCIE CPU interrupt status */ +#define PCIE_CPU_INT_STATUS 0x0C1C + +/** PCIe CPU Interrupt Status Mask */ +#define PCIE_CPU_INT2ARM_ISM 0x0C28 +/** PCIE host interrupt status */ +#define PCIE_HOST_INT_STATUS 0x0C30 +/** PCIE host interrupt mask */ +#define PCIE_HOST_INT_MASK 0x0C34 +/** PCIE host interrupt status mask */ +#define PCIE_HOST_INT_STATUS_MASK 0x0C3C +/** PCIE data exchange register 2 */ +#define PCIE_SCRATCH_2_REG 0x0C40 +/** PCIE data exchange register 3 */ +#define PCIE_SCRATCH_3_REG 0x0C44 + +#define PCIE_IP_REV_REG 0x0C48 + +/** PCIE data exchange register 4 */ +#define PCIE_SCRATCH_4_REG 0x0CD0 +/** PCIE data exchange register 5 */ +#define PCIE_SCRATCH_5_REG 0x0CD4 +/** PCIE data exchange register 6 */ +#define PCIE_SCRATCH_6_REG 0x0CD8 +/** PCIE data exchange register 7 */ +#define PCIE_SCRATCH_7_REG 0x0CDC +/** PCIE data exchange register 8 */ +#define PCIE_SCRATCH_8_REG 0x0CE0 +/** PCIE data exchange register 9 */ +#define PCIE_SCRATCH_9_REG 0x0CE4 +/** PCIE data exchange register 10 */ +#define PCIE_SCRATCH_10_REG 0x0CE8 +/** PCIE data exchange register 11 */ +#define PCIE_SCRATCH_11_REG 0x0CEC +/** PCIE data exchange register 12 */ +#define PCIE_SCRATCH_12_REG 0x0CF0 +#endif + +#ifdef PCIE8997 +/* PCIE read data pointer for queue 0 and 1 */ +#define PCIE8997_RD_DATA_PTR_Q0_Q1 0xC1A4 /* 0x8000C1A4 */ +/* PCIE read data pointer for queue 2 and 3 */ +#define PCIE8997_RD_DATA_PTR_Q2_Q3 0xC1A8 /* 0x8000C1A8 */ +/* PCIE write data pointer for queue 0 and 1 */ +#define PCIE8997_WR_DATA_PTR_Q0_Q1 0xC174 /* 0x8000C174 */ +/* PCIE write data pointer for queue 2 and 3 */ +#define PCIE8997_WR_DATA_PTR_Q2_Q3 0xC178 /* 0x8000C178 */ +#endif +#ifdef PCIE8897 +/* PCIE read data pointer for queue 0 and 1 */ +#define PCIE8897_RD_DATA_PTR_Q0_Q1 0xC08C /* 0x8000C08C */ +/* PCIE read data pointer for queue 2 and 3 */ +#define PCIE8897_RD_DATA_PTR_Q2_Q3 0xC090 /* 0x8000C090 */ +/* PCIE write data pointer for queue 0 and 1 */ +#define PCIE8897_WR_DATA_PTR_Q0_Q1 0xC05C /* 0x8000C05C */ +/* PCIE write data pointer for queue 2 and 3 */ +#define PCIE8897_WR_DATA_PTR_Q2_Q3 0xC060 /* 0x8000C060 */ +#endif + +/** Download ready interrupt for CPU */ +#define CPU_INTR_DNLD_RDY MBIT(0) +/** Command ready interrupt for CPU */ +#define CPU_INTR_DOOR_BELL MBIT(1) +/** Confirmation that sleep confirm message has been processed. + Device will enter sleep after receiving this interrupt */ +#define CPU_INTR_SLEEP_CFM_DONE MBIT(2) +/** Reset interrupt for CPU */ +#define CPU_INTR_RESET MBIT(3) +/** Set Event Done interupt to the FW*/ +#define CPU_INTR_EVENT_DONE MBIT(5) + +#if defined(PCIE8997) || defined(PCIE8897) +/** Data sent interrupt for host */ +#define HOST_INTR_DNLD_DONE MBIT(0) +/** Data receive interrupt for host */ +#define HOST_INTR_UPLD_RDY MBIT(1) +/** Command sent interrupt for host */ +#define HOST_INTR_CMD_DONE MBIT(2) +/** Event ready interrupt for host */ +#define HOST_INTR_EVENT_RDY MBIT(3) +/** Interrupt mask for host */ +#define HOST_INTR_MASK \ + (HOST_INTR_DNLD_DONE | HOST_INTR_UPLD_RDY | HOST_INTR_CMD_DONE | \ + HOST_INTR_EVENT_RDY) + +/** Lower 32bits command address holding register */ +#define REG_CMD_ADDR_LO PCIE_SCRATCH_0_REG +/** Upper 32bits command address holding register */ +#define REG_CMD_ADDR_HI PCIE_SCRATCH_1_REG +/** Command length holding register */ +#define REG_CMD_SIZE PCIE_SCRATCH_2_REG + +/** Lower 32bits command response address holding register */ +#define REG_CMDRSP_ADDR_LO PCIE_SCRATCH_4_REG +/** Upper 32bits command response address holding register */ +#define REG_CMDRSP_ADDR_HI PCIE_SCRATCH_5_REG + +/** TxBD's Read/Write pointer start from bit 16 */ +#define TXBD_RW_PTR_START 16 +/** RxBD's Read/Write pointer start from bit 0 */ +#define RXBD_RW_PTR_STRAT 0 + +#define MLAN_BD_FLAG_SOP MBIT(0) +#define MLAN_BD_FLAG_EOP MBIT(1) +#define MLAN_BD_FLAG_XS_SOP MBIT(2) +#define MLAN_BD_FLAG_XS_EOP MBIT(3) + +/* Event buffer description write pointer */ +#define REG_EVTBD_WRPTR PCIE_SCRATCH_10_REG +/* Event buffer description read pointer */ +#define REG_EVTBD_RDPTR PCIE_SCRATCH_11_REG +/* Driver ready signature write pointer */ +#define REG_DRV_READY PCIE_SCRATCH_12_REG + +/** Event Read/Write pointer mask */ +#define EVT_RW_PTR_MASK 0x0f +/** Event Read/Write pointer rollover bit */ +#define EVT_RW_PTR_ROLLOVER_IND MBIT(7) +#endif + +/* Define PCIE block size for firmware download */ +#define MLAN_PCIE_BLOCK_SIZE_FW_DNLD 256 + +/** Extra added macros **/ +#define MLAN_EVENT_HEADER_LEN 8 + +/** Max interrupt status register read limit */ +#define MAX_READ_REG_RETRY 10000 + +#ifdef PCIE8897 +static const struct _mlan_pcie_card_reg mlan_reg_pcie8897 = { + .reg_txbd_rdptr = PCIE8897_RD_DATA_PTR_Q0_Q1, + .reg_txbd_wrptr = PCIE8897_WR_DATA_PTR_Q0_Q1, + .reg_rxbd_rdptr = PCIE8897_RD_DATA_PTR_Q0_Q1, + .reg_rxbd_wrptr = PCIE8897_WR_DATA_PTR_Q0_Q1, + .reg_evtbd_rdptr = REG_EVTBD_RDPTR, + .reg_evtbd_wrptr = REG_EVTBD_WRPTR, + .reg_host_int_mask = PCIE_HOST_INT_MASK, + .reg_host_int_status_mask = PCIE_HOST_INT_STATUS_MASK, + .reg_host_int_status = PCIE_HOST_INT_STATUS, + .reg_cpu_int_event = PCIE_CPU_INT_EVENT, + .reg_ip_rev = PCIE_IP_REV_REG, + .reg_drv_ready = REG_DRV_READY, + .reg_cpu_int_status = PCIE_CPU_INT_STATUS, + .reg_scratch_0 = PCIE_SCRATCH_0_REG, + .reg_scratch_1 = PCIE_SCRATCH_1_REG, + .reg_scratch_2 = PCIE_SCRATCH_2_REG, + .reg_scratch_3 = PCIE_SCRATCH_3_REG, + .host_intr_mask = HOST_INTR_MASK, + .host_intr_dnld_done = HOST_INTR_DNLD_DONE, + .host_intr_upld_rdy = HOST_INTR_UPLD_RDY, + .host_intr_cmd_done = HOST_INTR_CMD_DONE, + .host_intr_event_rdy = HOST_INTR_EVENT_RDY, + .txrx_rw_ptr_mask = 0x000003FF, + .txrx_rw_ptr_wrap_mask = 0x000007FF, + .txrx_rw_ptr_rollover_ind = MBIT(10), + .use_adma = MFALSE, + .msi_int_wr_clr = MTRUE, +}; + +static const struct _mlan_card_info mlan_card_info_pcie8897 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 0, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#ifdef PCIE8997 +static const struct _mlan_pcie_card_reg mlan_reg_pcie8997 = { + .reg_txbd_rdptr = PCIE8997_RD_DATA_PTR_Q0_Q1, + .reg_txbd_wrptr = PCIE8997_WR_DATA_PTR_Q0_Q1, + .reg_rxbd_rdptr = PCIE8997_RD_DATA_PTR_Q0_Q1, + .reg_rxbd_wrptr = PCIE8997_WR_DATA_PTR_Q0_Q1, + .reg_evtbd_rdptr = REG_EVTBD_RDPTR, + .reg_evtbd_wrptr = REG_EVTBD_WRPTR, + .reg_host_int_mask = PCIE_HOST_INT_MASK, + .reg_host_int_status_mask = PCIE_HOST_INT_STATUS_MASK, + .reg_host_int_status = PCIE_HOST_INT_STATUS, + .reg_cpu_int_event = PCIE_CPU_INT_EVENT, + .reg_ip_rev = PCIE_IP_REV_REG, + .reg_drv_ready = REG_DRV_READY, + .reg_cpu_int_status = PCIE_CPU_INT_STATUS, + .reg_scratch_0 = PCIE_SCRATCH_0_REG, + .reg_scratch_1 = PCIE_SCRATCH_1_REG, + .reg_scratch_2 = PCIE_SCRATCH_2_REG, + .reg_scratch_3 = PCIE_SCRATCH_3_REG, + .host_intr_mask = HOST_INTR_MASK, + .host_intr_dnld_done = HOST_INTR_DNLD_DONE, + .host_intr_upld_rdy = HOST_INTR_UPLD_RDY, + .host_intr_cmd_done = HOST_INTR_CMD_DONE, + .host_intr_event_rdy = HOST_INTR_EVENT_RDY, + .txrx_rw_ptr_mask = 0x00000FFF, + .txrx_rw_ptr_wrap_mask = 0x00001FFF, + .txrx_rw_ptr_rollover_ind = MBIT(12), + .use_adma = MFALSE, + .msi_int_wr_clr = MTRUE, +}; + +static const struct _mlan_card_info mlan_card_info_pcie8997 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 1, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#if defined(PCIE9098) || defined(PCIE9097) +static const struct _mlan_pcie_card_reg mlan_reg_pcie9098 = { + .reg_txbd_rdptr = PCIE9098_TXBD_RDPTR, + .reg_txbd_wrptr = PCIE9098_TXBD_WRPTR, + .reg_rxbd_rdptr = PCIE9098_RXBD_RDPTR, + .reg_rxbd_wrptr = PCIE9098_RXBD_WRPTR, + .reg_evtbd_rdptr = PCIE9098_EVTBD_RDPTR, + .reg_evtbd_wrptr = PCIE9098_EVTBD_WRPTR, + .reg_host_int_mask = PCIE9098_HOST_INT_MASK, + .reg_host_int_status_mask = PCIE9098_HOST_INT_STATUS_MASK, + .reg_host_int_status = PCIE9098_HOST_INT_STATUS, + .reg_host_int_clr_sel = PCIE9098_HOST_INT_CLR_SEL, + .reg_cpu_int_event = PCIE9098_CPU_INT_EVENT, + .reg_ip_rev = PCIE9098_DEV_ID_REG, + .reg_drv_ready = PCIE9098_DRV_READY, + .reg_cpu_int_status = PCIE9098_CPU_INT_STATUS, + .reg_rev_id = PCIE9098_REV_ID_REG, + .reg_scratch_0 = PCIE9098_SCRATCH_0_REG, + .reg_scratch_1 = PCIE9098_SCRATCH_1_REG, + .reg_scratch_2 = PCIE9098_SCRATCH_2_REG, + .reg_scratch_3 = PCIE9098_SCRATCH_3_REG, + .reg_scratch_6 = PCIE9098_SCRATCH_6_REG, + .reg_scratch_7 = PCIE9098_SCRATCH_7_REG, + .host_intr_mask = PCIE9098_HOST_INTR_MASK, + .host_intr_dnld_done = PCIE9098_HOST_INTR_DNLD_DONE, + .host_intr_upld_rdy = PCIE9098_HOST_INTR_UPLD_RDY, + .host_intr_cmd_done = PCIE9098_HOST_INTR_CMD_DONE, + .host_intr_event_rdy = PCIE9098_HOST_INTR_EVENT_RDY, + .host_intr_cmd_dnld = PCIE9098_HOST_INTR_CMD_DNLD, + .use_adma = MTRUE, + .msi_int_wr_clr = MTRUE, +}; + +static const struct _mlan_card_info mlan_card_info_pcie9098 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 1, + .v17_fw_api = 1, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#ifdef PCIE9097 +static const struct _mlan_pcie_card_reg mlan_reg_pcie9097_b0 = { + .reg_txbd_rdptr = PCIE9098_TXBD_RDPTR, + .reg_txbd_wrptr = PCIE9098_TXBD_WRPTR, + .reg_rxbd_rdptr = PCIE9098_RXBD_RDPTR, + .reg_rxbd_wrptr = PCIE9098_RXBD_WRPTR, + .reg_evtbd_rdptr = PCIE9098_EVTBD_RDPTR, + .reg_evtbd_wrptr = PCIE9098_EVTBD_WRPTR, + .reg_host_int_mask = PCIE9097_B0_HOST_INT_MASK, + .reg_host_int_status_mask = PCIE9097_B0_HOST_INT_STATUS_MASK, + .reg_host_int_status = PCIE9097_B0_HOST_INT_STATUS, + .reg_host_int_clr_sel = PCIE9097_B0_HOST_INT_CLR_SEL, + .reg_cpu_int_event = PCIE9098_CPU_INT_EVENT, + .reg_ip_rev = PCIE9098_DEV_ID_REG, + .reg_drv_ready = PCIE9098_DRV_READY, + .reg_cpu_int_status = PCIE9098_CPU_INT_STATUS, + .reg_rev_id = PCIE9098_REV_ID_REG, + .reg_scratch_0 = PCIE9098_SCRATCH_0_REG, + .reg_scratch_1 = PCIE9098_SCRATCH_1_REG, + .reg_scratch_2 = PCIE9098_SCRATCH_2_REG, + .reg_scratch_3 = PCIE9098_SCRATCH_3_REG, + .reg_scratch_6 = PCIE9098_SCRATCH_6_REG, + .reg_scratch_7 = PCIE9098_SCRATCH_7_REG, + .host_intr_mask = PCIE9098_HOST_INTR_MASK, + .host_intr_dnld_done = PCIE9098_HOST_INTR_DNLD_DONE, + .host_intr_upld_rdy = PCIE9098_HOST_INTR_UPLD_RDY, + .host_intr_cmd_done = PCIE9098_HOST_INTR_CMD_DONE, + .host_intr_event_rdy = PCIE9098_HOST_INTR_EVENT_RDY, + .host_intr_cmd_dnld = PCIE9098_HOST_INTR_CMD_DNLD, + .use_adma = MTRUE, + .msi_int_wr_clr = MTRUE, +}; +#endif +/* Get pcie device from card type */ +mlan_status wlan_get_pcie_device(pmlan_adapter pmadapter); + +/** Set PCIE host buffer configurations */ +mlan_status wlan_set_pcie_buf_config(mlan_private *pmpriv); + +/** Init write pointer */ +mlan_status wlan_pcie_init_fw(pmlan_adapter pmadapter); + +#if defined(PCIE8997) || defined(PCIE8897) +/** Prepare command PCIE host buffer config */ +mlan_status wlan_cmd_pcie_host_buf_cfg(pmlan_private pmpriv, + pHostCmd_DS_COMMAND cmd, + t_u16 cmd_action, t_pvoid pdata_buf); +#endif + +/** Wakeup PCIE card */ +mlan_status wlan_pcie_wakeup(pmlan_adapter pmadapter); + +/** Set DRV_READY register */ +mlan_status wlan_set_drv_ready_reg(mlan_adapter *pmadapter, t_u32 val); +/** PCIE init */ +mlan_status wlan_pcie_init(mlan_adapter *pmadapter); + +/** Read interrupt status */ +mlan_status wlan_process_msix_int(mlan_adapter *pmadapter); +/** Transfer data to card */ +mlan_status wlan_pcie_host_to_card(pmlan_private pmpriv, t_u8 type, + mlan_buffer *mbuf, mlan_tx_param *tx_param); +/** Ring buffer allocation function */ +mlan_status wlan_alloc_pcie_ring_buf(pmlan_adapter pmadapter); +/** Ring buffer deallocation function */ +mlan_status wlan_free_pcie_ring_buf(pmlan_adapter pmadapter); +/** Ring buffer cleanup function, e.g. on deauth */ +mlan_status wlan_clean_pcie_ring_buf(pmlan_adapter pmadapter); +mlan_status wlan_alloc_ssu_pcie_buf(pmlan_adapter pmadapter); +mlan_status wlan_free_ssu_pcie_buf(pmlan_adapter pmadapter); + +#endif /* _MLAN_PCIE_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_scan.c b/mxm_wifiex/wlan_src/mlan/mlan_scan.c new file mode 100644 index 0000000..09484b1 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_scan.c @@ -0,0 +1,6468 @@ +/** @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 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11ac.h" +#include "mlan_11ax.h" +#include "mlan_11h.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif +/******************************************************** + Local Constants +********************************************************/ +/** minimum scan time for passive to active scan */ +#define MIN_PASSIVE_TO_ACTIVE_SCAN_TIME 150 + +#define MRVDRV_MAX_CHANNELS_PER_SCAN 40 +/** The maximum number of channels the firmware can scan per command */ +#define MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN 4 + +/** + * Number of channels to scan per firmware scan command issuance. + * + * Number restricted to prevent hitting the limit on the amount of scan data + * returned in a single firmware scan command. + */ +#define MRVDRV_CHANNELS_PER_SCAN_CMD 4 + +/** Memory needed to store a max sized Channel List TLV for a firmware scan */ +#define CHAN_TLV_MAX_SIZE \ + (sizeof(MrvlIEtypesHeader_t) + \ + (MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN * sizeof(ChanScanParamSet_t))) + +/** Memory needed to store supported rate */ +#define RATE_TLV_MAX_SIZE \ + (sizeof(MrvlIEtypes_RatesParamSet_t) + HOSTCMD_SUPPORTED_RATES) + +/** Memory needed to store a max number/size WildCard + * SSID TLV for a firmware scan */ +#define WILDCARD_SSID_TLV_MAX_SIZE \ + (MRVDRV_MAX_SSID_LIST_LENGTH * \ + (sizeof(MrvlIEtypes_WildCardSsIdParamSet_t) + \ + MRVDRV_MAX_SSID_LENGTH)) + +/** Memory needed to store a max number/size BSSID TLV for a firmware scan */ +#define BSSID_LIST_TLV_MAX_SIZE \ + (sizeof(MrvlIEtypesHeader_t) + \ + (MRVDRV_MAX_BSSID_LIST * MLAN_MAC_ADDR_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 + BSSID_LIST_TLV_MAX_SIZE + \ + WPS_TLV_MAX_SIZE) + +/******************************************************** + Local Variables +********************************************************/ + +/** + * Interally used to send a configured scan cmd between + * driver routines + */ +typedef union { + /** Scan configuration (variable length) */ + wlan_scan_cmd_config config; + /** Max allocated block */ + t_u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; +} wlan_scan_cmd_config_tlv; + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +/** Cipher suite definition */ +enum cipher_suite { + CIPHER_SUITE_WEP40, + CIPHER_SUITE_TKIP, + CIPHER_SUITE_CCMP, + CIPHER_SUITE_WEP104, + CIPHER_SUITE_GCMP, + CIPHER_SUITE_GCMP_256, + CIPHER_SUITE_CCMP_256, + CIPHER_SUITE_MAX +}; + +static t_u8 wpa_oui[CIPHER_SUITE_MAX][4] = { + {0x00, 0x50, 0xf2, 0x01}, /* WEP40 */ + {0x00, 0x50, 0xf2, 0x02}, /* TKIP */ + {0x00, 0x50, 0xf2, 0x04}, /* AES */ + {0x00, 0x50, 0xf2, 0x05}, /* WEP104 */ +}; + +static t_u8 rsn_oui[CIPHER_SUITE_MAX][4] = { + {0x00, 0x0f, 0xac, 0x01}, /* WEP40 */ + {0x00, 0x0f, 0xac, 0x02}, /* TKIP */ + {0x00, 0x0f, 0xac, 0x04}, /* AES */ + {0x00, 0x0f, 0xac, 0x05}, /* WEP104 */ + {0x00, 0x0f, 0xac, 0x08}, /* GCMP */ + {0x00, 0x0f, 0xac, 0x09}, /* GCMP-256 */ + {0x00, 0x0f, 0xac, 0x0a}, /* CCMP-256 */ +}; + +/** + * @brief Convert radio type scan parameter to a band config used in join cmd + * + * @param radio_type Scan parameter indicating the radio used for a channel + * in a scan command. + * + * @return Band type conversion of scanBand used in join/assoc cmds + * + */ +t_u8 radio_type_to_band(t_u8 radio_type) +{ + t_u8 ret_band; + + switch (radio_type) { + case BAND_5GHZ: + ret_band = BAND_A; + break; + case BAND_2GHZ: + default: + ret_band = BAND_G; + break; + } + + return ret_band; +} + +/** + * @brief This function will update the channel statistics from scan result + * + * @param pmpriv A pointer to mlan_private structure + * @param pchanstats_tlv A pointer to MrvlIEtypes_ChannelStats_t tlv + * + * @return NA + */ +void wlan_update_chan_statistics(mlan_private *pmpriv, + MrvlIEtypes_ChannelStats_t *pchanstats_tlv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u8 i; + chan_statistics_t *pchan_stats = + (chan_statistics_t *)((t_u8 *)pchanstats_tlv + + sizeof(MrvlIEtypesHeader_t)); + t_u8 num_chan = wlan_le16_to_cpu(pchanstats_tlv->header.len) / + sizeof(chan_statistics_t); + + ENTER(); + + for (i = 0; i < num_chan; i++) { + if (pmadapter->idx_chan_stats >= pmadapter->num_in_chan_stats) { + PRINTM(MERROR, + "Over flow: idx_chan_stats=%d, num_in_chan_stats=%d\n", + pmadapter->idx_chan_stats, + pmadapter->num_in_chan_stats); + break; + } + pchan_stats->total_networks = + wlan_le16_to_cpu(pchan_stats->total_networks); + pchan_stats->cca_scan_duration = + wlan_le16_to_cpu(pchan_stats->cca_scan_duration); + pchan_stats->cca_busy_duration = + wlan_le16_to_cpu(pchan_stats->cca_busy_duration); + PRINTM(MCMND, + "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n", + pchan_stats->chan_num, pchan_stats->noise, + pchan_stats->total_networks, + pchan_stats->cca_scan_duration, + pchan_stats->cca_busy_duration); + memcpy_ext(pmadapter, + (chan_statistics_t *)&pmadapter + ->pchan_stats[pmadapter->idx_chan_stats], + pchan_stats, sizeof(chan_statistics_t), + sizeof(chan_statistics_t)); + pmadapter->idx_chan_stats++; + pchan_stats++; + } + LEAVE(); + return; +} + +/** + * @brief This function will parse a given IE for a given OUI + * + * Parse a given WPA/RSN IE to find if it has a given oui in PTK, + * if no OUI found for PTK it returns 0. + * + * @param pbss_desc A pointer to current BSS descriptor + * @return 0 on failure to find OUI, 1 on success. + */ +static t_u8 search_oui_in_ie(mlan_adapter *pmadapter, IEBody *ie_body, + t_u8 *oui) +{ + t_u8 count; + + count = ie_body->PtkCnt[0]; + + ENTER(); + /* There could be multiple OUIs for PTK hence + * 1) Take the length. + * 2) Check all the OUIs for AES. + * 3) If one of them is AES then pass success. + */ + while (count) { + if (!memcmp(pmadapter, ie_body->PtkBody, oui, + sizeof(ie_body->PtkBody))) { + LEAVE(); + return MLAN_OUI_PRESENT; + } + + --count; + if (count) { + ie_body = (IEBody *)((t_u8 *)ie_body + + sizeof(ie_body->PtkBody)); + } + } + + PRINTM(MINFO, "The OUI %x:%x:%x:%x is not found in PTK\n", oui[0], + oui[1], oui[2], oui[3]); + LEAVE(); + return MLAN_OUI_NOT_PRESENT; +} + +/** + * @brief This function will pass the correct ie and oui to search_oui_in_ie + * + * Check the pbss_desc for appropriate IE and then check if RSN IE has AES + * OUI in it. If RSN IE does not have AES in PTK then return 0; + * + * @param pbss_desc A pointer to current BSS descriptor + * @return 0 on failure to find AES OUI, 1 on success. + */ +static t_u8 is_rsn_oui_present(mlan_adapter *pmadapter, + BSSDescriptor_t *pbss_desc, t_u32 cipher_suite) +{ + t_u8 *oui = MNULL; + IEBody *ie_body = MNULL; + t_u8 ret = MLAN_OUI_NOT_PRESENT; + + ENTER(); + if (pbss_desc->prsn_ie && + (pbss_desc->prsn_ie->ieee_hdr.element_id == RSN_IE) && + (pbss_desc->prsn_ie->ieee_hdr.len > RSN_GTK_OUI_OFFSET)) { + ie_body = (IEBody *)(((t_u8 *)pbss_desc->prsn_ie->data) + + RSN_GTK_OUI_OFFSET); + oui = &rsn_oui[cipher_suite][0]; + ret = search_oui_in_ie(pmadapter, ie_body, oui); + if (ret) { + LEAVE(); + return ret; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function will pass the correct ie and oui to search_oui_in_ie + * + * Check the wpa_ie 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 pmadapter A pointer to mlan adapter. + * @return 0 on failure to find AES OUI, 1 on success. + */ +static t_u8 is_rsn_oui_present_in_wpa_ie(mlan_private *pmpriv, + t_u32 cipher_suite) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u8 *oui = MNULL; + IEBody *ie_body = MNULL; + IEEEtypes_Generic_t *prsn_ie = MNULL; + t_u8 ret = MLAN_OUI_NOT_PRESENT; + + ENTER(); + prsn_ie = (IEEEtypes_Generic_t *)pmpriv->wpa_ie; + + if (prsn_ie && (prsn_ie->ieee_hdr.element_id == RSN_IE) && + (prsn_ie->ieee_hdr.len > RSN_GTK_OUI_OFFSET)) { + ie_body = (IEBody *)(prsn_ie->data + RSN_GTK_OUI_OFFSET); + oui = &rsn_oui[cipher_suite][0]; + ret = search_oui_in_ie(pmadapter, ie_body, oui); + if (ret) { + LEAVE(); + return ret; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function will pass the correct ie and oui to search_oui_in_ie + * + * Check the pbss_desc for appropriate IE and then check if WPA IE has AES + * OUI in it. If WPA IE does not have AES in PTK then return 0; + * + * @param pbss_desc A pointer to current BSS descriptor + * @return 0 on failure to find AES OUI, 1 on success. + */ +static t_u8 is_wpa_oui_present(mlan_adapter *pmadapter, + BSSDescriptor_t *pbss_desc, t_u32 cipher_suite) +{ + t_u8 *oui = MNULL; + IEBody *ie_body = MNULL; + t_u8 ret = MLAN_OUI_NOT_PRESENT; + + ENTER(); + if (((pbss_desc->pwpa_ie) && + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE))) { + ie_body = (IEBody *)pbss_desc->pwpa_ie->data; + oui = &wpa_oui[cipher_suite][0]; + ret = search_oui_in_ie(pmadapter, ie_body, oui); + if (ret) { + LEAVE(); + return ret; + } + } + LEAVE(); + return ret; +} + +/** + * @brief compare config band and a band from the scan result, + * which is defined by functiion radio_type_to_band(t_u8 radio_type) above + * + * @param cfg_band: band configured + * scan_band: band from scan result + * + * @return matched: non-zero. unmatched: 0 + * + */ +static t_u8 wlan_is_band_compatible(t_u8 cfg_band, t_u8 scan_band) +{ + t_u8 band; + switch (scan_band) { + case BAND_A: + band = BAND_A | BAND_AN | BAND_AAC; + break; + case BAND_G: + default: + band = BAND_B | BAND_G | BAND_GN | BAND_GAC; + } + 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(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( + mlan_private *pmpriv, const wlan_user_scan_cfg *puser_scan_in, + ChanScanParamSet_t *pscan_chan_list, 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; + t_u8 band; + t_u16 scan_dur = 0; + + 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 ((puser_scan_in && + (puser_scan_in->bss_mode == MLAN_SCAN_MODE_IBSS)) || + pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) + band = pmadapter->adhoc_start_band; + else + band = pmpriv->config_bands; + if (!wlan_is_band_compatible(band, pscan_region->band)) + continue; + for (next_chan = 0; next_chan < pscan_region->num_cfp; + next_chan++) { + /* 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 (cfp->dynamic.flags & NXP_CHANNEL_DISABLED) + continue; + + if (wlan_is_chan_passive(pmpriv, pscan_region->band, + (t_u8)cfp->channel)) { + /* do not send probe requests on this channel */ + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + switch (pscan_region->band) { + case BAND_A: + pscan_chan_list[chan_idx].bandcfg.chanBand = + BAND_5GHZ; + /* Passive scan on DFS channels */ + if (wlan_11h_radar_detect_required( + pmpriv, (t_u8)cfp->channel) && + scan_type != MLAN_SCAN_TYPE_PASSIVE) + scan_type = + MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE; + break; + case BAND_B: + case BAND_G: + if (wlan_bg_scan_type_is_passive( + pmpriv, (t_u8)cfp->channel)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + pscan_chan_list[chan_idx].bandcfg.chanBand = + BAND_2GHZ; + break; + default: + pscan_chan_list[chan_idx].bandcfg.chanBand = + BAND_2GHZ; + break; + } + + if (puser_scan_in && + puser_scan_in->chan_list[0].scan_time) { + scan_dur = (t_u16)puser_scan_in->chan_list[0] + .scan_time; + } else if (scan_type == MLAN_SCAN_TYPE_PASSIVE || + scan_type == + MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE) { + scan_dur = pmadapter->passive_scan_time; + } else if (filtered_scan) { + scan_dur = pmadapter->specific_scan_time; + } else { + scan_dur = pmadapter->active_scan_time; + } + if (scan_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE && + pmadapter->passive_to_active_scan == + MLAN_PASS_TO_ACT_SCAN_EN) { + scan_dur = MAX(scan_dur, + MIN_PASSIVE_TO_ACTIVE_SCAN_TIME); + pscan_chan_list[chan_idx] + .chan_scan_mode.passive_to_active_scan = + MTRUE; + } + pscan_chan_list[chan_idx].max_scan_time = + wlan_cpu_to_le16(scan_dur); + + if (scan_type == MLAN_SCAN_TYPE_PASSIVE || + scan_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE) { + pscan_chan_list[chan_idx] + .chan_scan_mode.passive_scan = MTRUE; + pscan_chan_list[chan_idx] + .chan_scan_mode.hidden_ssid_report = + MTRUE; + } else { + pscan_chan_list[chan_idx] + .chan_scan_mode.passive_scan = MFALSE; + } + + pscan_chan_list[chan_idx].chan_number = + (t_u8)cfp->channel; + PRINTM(MINFO, + "chan=%d, mode=%d, passive_to_active=%d\n", + pscan_chan_list[chan_idx].chan_number, + pscan_chan_list[chan_idx] + .chan_scan_mode.passive_scan, + pscan_chan_list[chan_idx] + .chan_scan_mode.passive_to_active_scan); + chan_idx++; + } + } + + 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(mlan_private *pmpriv, + 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_ext(pmpriv->adapter, *pptlv_out, + pmpriv->wps.wps_ie.vend_hdr.oui, + pmpriv->wps.wps_ie.vend_hdr.len, + 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(mlan_private *pmpriv, t_void *pioctl_buf, + t_u32 max_chan_per_scan, t_u8 filtered_scan, + wlan_scan_cmd_config *pscan_cfg_out, + MrvlIEtypes_ChanListParamSet_t *pchan_tlv_out, + ChanScanParamSet_t *pscan_chan_list) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + ChanScanParamSet_t *ptmp_chan_list; + ChanScanParamSet_t *pstart_chan; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + t_u8 *pchan_tlv_out_temp = MNULL; + t_u8 *ptlv_temp = MNULL; + t_bool foundJPch14 = MFALSE; + t_u16 tlv_buf_len = 0; + t_u32 tlv_idx; + t_u32 total_scan_time; + t_u32 done_early; + t_u32 cmd_no; + t_u32 first_chan = 1; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + + ENTER(); + + if (!pscan_cfg_out || !pchan_tlv_out || !pscan_chan_list) { + PRINTM(MINFO, "Scan: Null detect: %p, %p, %p\n", pscan_cfg_out, + pchan_tlv_out, pscan_chan_list); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (!pscan_chan_list->chan_number) { + PRINTM(MERROR, "Scan: No channel configured\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* check expiry before preparing scan list - may affect blacklist */ + wlan_11h_get_csa_closed_channel(pmpriv); + + pchan_tlv_out->header.type = wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + + /* Set the temp channel struct pointer to the start of the desired list + */ + ptmp_chan_list = pscan_chan_list; + + /* + * Loop through the desired channel list, sending a new firmware scan + * commands for each max_chan_per_scan channels (or for 1,6,11 + * individually if configured accordingly) + */ + while (ptmp_chan_list->chan_number) { + tlv_idx = 0; + total_scan_time = 0; + pchan_tlv_out->header.len = 0; + pstart_chan = ptmp_chan_list; + done_early = MFALSE; + + /* + * Construct the Channel TLV for the scan command. Continue to + * insert channel TLVs until: + * - the tlv_idx hits the maximum configured per scan command + * - the next channel to insert is 0 (end of desired + * channel list) + * - done_early is set (controlling individual + * scanning of 1,6,11) + */ + while (tlv_idx < max_chan_per_scan && + ptmp_chan_list->chan_number && !done_early) { + if (wlan_is_chan_blacklisted( + pmpriv, + radio_type_to_band( + ptmp_chan_list->bandcfg.chanBand), + ptmp_chan_list->chan_number) || + wlan_is_chan_disabled( + pmpriv, + radio_type_to_band( + ptmp_chan_list->bandcfg.chanBand), + ptmp_chan_list->chan_number)) { + ptmp_chan_list++; + continue; + } + + if (first_chan) { + ptmp_chan_list->chan_scan_mode.first_chan = + MTRUE; + first_chan = 0; + } + + PRINTM(MINFO, + "Scan: Chan(%3d), bandcfg(%x), Mode(%d,%d), Dur(%d)\n", + ptmp_chan_list->chan_number, + ptmp_chan_list->bandcfg, + ptmp_chan_list->chan_scan_mode.passive_scan, + ptmp_chan_list->chan_scan_mode.disable_chan_filt, + wlan_le16_to_cpu(ptmp_chan_list->max_scan_time)); + + if (foundJPch14 == MTRUE) { + foundJPch14 = MFALSE; + /* Restore the TLV buffer */ + pchan_tlv_out = + (MrvlIEtypes_ChanListParamSet_t *) + pchan_tlv_out_temp; + pchan_tlv_out->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + pchan_tlv_out->header.len = 0; + if (ptlv_temp) { + memcpy_ext(pmadapter, + pscan_cfg_out->tlv_buf, + ptlv_temp, tlv_buf_len, + tlv_buf_len); + pcb->moal_mfree(pmadapter->pmoal_handle, + ptlv_temp); + ptlv_temp = MNULL; + } + } + + /* Special Case: For Japan, Scan on CH14 for 11G rates + is not allowed + Hence Rates TLV needs to be updated to support only + 11B rates */ + if ((pmadapter->region_code == COUNTRY_CODE_JP_40 || + pmadapter->region_code == COUNTRY_CODE_JP_FF) && + (ptmp_chan_list->chan_number == 14) && + (pmadapter->ext_scan_type != EXT_SCAN_ENHANCE)) { + t_u8 *ptlv_pos = pscan_cfg_out->tlv_buf; + t_u16 old_ratetlv_len, new_ratetlv_len; + MrvlIEtypesHeader_t *header; + MrvlIEtypes_RatesParamSet_t *prates_tlv; + + /* Preserve the current TLV buffer */ + ret = pcb->moal_malloc( + pmadapter->pmoal_handle, + MAX_SCAN_CFG_ALLOC - CHAN_TLV_MAX_SIZE, + MLAN_MEM_DEF, (t_u8 **)&ptlv_temp); + if (ret != MLAN_STATUS_SUCCESS || !ptlv_temp) { + PRINTM(MERROR, + "Memory allocation for pscan_cfg_out failed!\n"); + if (pioctl_req) + pioctl_req->status_code = + MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pchan_tlv_out_temp = (t_u8 *)pchan_tlv_out; + tlv_buf_len = (t_u32)(pchan_tlv_out_temp - + pscan_cfg_out->tlv_buf); + memcpy_ext(pmadapter, ptlv_temp, ptlv_pos, + tlv_buf_len, + MAX_SCAN_CFG_ALLOC - + CHAN_TLV_MAX_SIZE); + + /* Search for Rates TLV */ + while ((!foundJPch14) && + (ptlv_pos < pchan_tlv_out_temp)) { + header = + (MrvlIEtypesHeader_t *)ptlv_pos; + if (header->type == + wlan_cpu_to_le16(TLV_TYPE_RATES)) + foundJPch14 = MTRUE; + else + ptlv_pos += + (sizeof(MrvlIEtypesHeader_t) + + wlan_le16_to_cpu( + header->len)); + } + + if (foundJPch14) { + /* Update the TLV buffer with *new* + * Rates TLV and rearrange remaining TLV + * buffer*/ + prates_tlv = + (MrvlIEtypes_RatesParamSet_t *) + ptlv_pos; + old_ratetlv_len = + sizeof(MrvlIEtypesHeader_t) + + wlan_le16_to_cpu( + prates_tlv->header.len); + + prates_tlv->header.len = wlan_copy_rates( + prates_tlv->rates, 0, + SupportedRates_B, + sizeof(SupportedRates_B)); + new_ratetlv_len = + sizeof(MrvlIEtypesHeader_t) + + prates_tlv->header.len; + prates_tlv->header.len = + wlan_cpu_to_le16( + prates_tlv->header.len); + + memmove(pmadapter, + ptlv_pos + new_ratetlv_len, + ptlv_pos + old_ratetlv_len, + (t_u32)(pchan_tlv_out_temp - + (ptlv_pos + + old_ratetlv_len))); + pchan_tlv_out = + (MrvlIEtypes_ChanListParamSet_t + *)(pchan_tlv_out_temp - + (old_ratetlv_len - + new_ratetlv_len)); + pchan_tlv_out->header.type = + wlan_cpu_to_le16( + TLV_TYPE_CHANLIST); + pchan_tlv_out->header.len = 0; + } + } + + /* Copy the current channel TLV to the command being + * prepared */ + memcpy_ext(pmadapter, + pchan_tlv_out->chan_scan_param + tlv_idx, + ptmp_chan_list, + sizeof(pchan_tlv_out->chan_scan_param), + sizeof(pchan_tlv_out->chan_scan_param)); + + /* Increment the TLV header length by the size appended + */ + pchan_tlv_out->header.len += + sizeof(pchan_tlv_out->chan_scan_param); + + /* + * The tlv buffer length is set to the number of + * bytes of the between the channel tlv pointer + * and the start of the tlv buffer. This + * compensates for any TLVs that were appended + * before the channel list. + */ + pscan_cfg_out->tlv_buf_len = (t_u32)( + (t_u8 *)pchan_tlv_out - pscan_cfg_out->tlv_buf); + + /* Add the size of the channel tlv header and the data + * length */ + pscan_cfg_out->tlv_buf_len += + (sizeof(pchan_tlv_out->header) + + pchan_tlv_out->header.len); + + /* Increment the index to the channel tlv we are + * constructing */ + tlv_idx++; + + /* Count the total scan time per command */ + total_scan_time += + wlan_le16_to_cpu(ptmp_chan_list->max_scan_time); + + done_early = MFALSE; + + /* + * Stop the loop if the *current* channel is in the + * 1,6,11 set and we are not filtering on a BSSID or + * SSID. + */ + if (!filtered_scan && + (ptmp_chan_list->chan_number == 1 || + ptmp_chan_list->chan_number == 6 || + ptmp_chan_list->chan_number == 11)) { + done_early = MTRUE; + } + + /* + * Stop the loop if the *current* channel is 14 + * and region code is Japan (0x40 or 0xFF) + */ + if ((pmadapter->region_code == COUNTRY_CODE_JP_40 || + pmadapter->region_code == COUNTRY_CODE_JP_FF) && + (ptmp_chan_list->chan_number == 14)) { + done_early = MTRUE; + } + + /* Increment the tmp pointer to the next channel to be + * scanned */ + ptmp_chan_list++; + + /* + * Stop the loop if the *next* channel is in the 1,6,11 + * set. This will cause it to be the only channel + * scanned on the next interation + */ + if (!filtered_scan && + (ptmp_chan_list->chan_number == 1 || + ptmp_chan_list->chan_number == 6 || + ptmp_chan_list->chan_number == 11)) { + done_early = MTRUE; + } + + /* + * Stop the loop if the *next* channel is 14 + * and region code is Japan (0x40 or 0xFF) + */ + if ((pmadapter->region_code == COUNTRY_CODE_JP_40 || + pmadapter->region_code == COUNTRY_CODE_JP_FF) && + (ptmp_chan_list->chan_number == 14)) { + done_early = MTRUE; + } + if (pmadapter->ext_scan && pmadapter->ext_scan_enh && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) + done_early = MFALSE; + } + + /* The total scan time should be less than scan command timeout + * value */ + if (total_scan_time > MRVDRV_MAX_TOTAL_SCAN_TIME) { + PRINTM(MMSG, + "Total scan time %d ms is over limit (%d ms), scan skipped\n", + total_scan_time, MRVDRV_MAX_TOTAL_SCAN_TIME); + if (pioctl_req) + pioctl_req->status_code = + MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + break; + } + + pchan_tlv_out->header.len = + wlan_cpu_to_le16(pchan_tlv_out->header.len); + + pmadapter->pscan_channels = pstart_chan; + + /* Send the scan command to the firmware with the specified cfg + */ + if (pmadapter->ext_scan) + cmd_no = HostCmd_CMD_802_11_SCAN_EXT; + else + cmd_no = HostCmd_CMD_802_11_SCAN; + ret = wlan_prepare_cmd(pmpriv, cmd_no, HostCmd_ACT_GEN_SET, 0, + MNULL, pscan_cfg_out); + if (ret) + break; + } + + LEAVE(); + + if (ptlv_temp) + pcb->moal_mfree(pmadapter->pmoal_handle, ptlv_temp); + + if (ret) + return MLAN_STATUS_FAILURE; + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Construct a wlan_scan_cmd_config structure to use in scan commands + * + * Application layer or other functions can invoke wlan_scan_networks + * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. + * This structure is used as the basis of one or many wlan_scan_cmd_config + * commands that are sent to the command processing module and sent to + * firmware. + * + * Create a wlan_scan_cmd_config based on the following user supplied + * parameters (if present): + * - SSID filter + * - BSSID filter + * - Number of Probes to be sent + * - Channel list + * + * If the SSID or BSSID filter is not present, disable/clear the filter. + * If the number of probes is not set, use the adapter default setting + * Qualify the channel + * + * @param pmpriv A pointer to mlan_private structure + * @param puser_scan_in MNULL or pointer to scan config parameters + * @param pscan_cfg_out Output parameter: Resulting scan configuration + * @param ppchan_list_out Output parameter: Pointer to the start of the + * channel TLV portion of the output scan config + * @param pscan_chan_list Output parameter: Pointer to the resulting + * channel list to scan + * @param pmax_chan_per_scan Output parameter: Number of channels to scan for + * each issuance of the firmware scan command + * @param pfiltered_scan Output parameter: Flag indicating whether or not + * a BSSID or SSID filter is being sent in the + * command to firmware. Used to increase the number + * of channels sent in a scan command and to + * disable the firmware channel scan filter. + * @param pscan_current_only Output parameter: Flag indicating whether or not + * we are only scanning our current active channel + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_scan_setup_scan_config( + mlan_private *pmpriv, wlan_user_scan_cfg *puser_scan_in, + wlan_scan_cmd_config *pscan_cfg_out, + MrvlIEtypes_ChanListParamSet_t **ppchan_list_out, + ChanScanParamSet_t *pscan_chan_list, t_u8 *pmax_chan_per_scan, + t_u8 *pfiltered_scan, t_u8 *pscan_current_only) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + MrvlIEtypes_NumProbes_t *pnum_probes_tlv; + MrvlIEtypes_WildCardSsIdParamSet_t *pwildcard_ssid_tlv; + MrvlIEtypes_RatesParamSet_t *prates_tlv; + MrvlIEtypes_Bssid_List_t *pbssid_tlv; + + const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0, 0, 0, 0, 0, 0}; + t_u8 *ptlv_pos; + t_u32 num_probes; + t_u32 ssid_len; + t_u32 chan_idx; + t_u32 chan_list_idx = 0; + t_u32 scan_type; + t_u16 scan_dur; + t_u8 channel; + t_u8 radio_type; + t_u32 ssid_idx; + t_u8 ssid_filter; + WLAN_802_11_RATES rates; + t_u32 rates_size; + MrvlIETypes_HTCap_t *pht_cap; + + MrvlIETypes_VHTCap_t *pvht_cap; + MrvlIEtypes_ScanChanGap_t *pscan_gap_tlv; + MrvlIEtypes_BssMode_t *pbss_mode; + MrvlIEtypes_Extension_t *phe_cap; + t_u16 len = 0; + + 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_ext(pmadapter, pscan_cfg_out->specific_bssid, + puser_scan_in->specific_bssid, + sizeof(pscan_cfg_out->specific_bssid), + sizeof(pscan_cfg_out->specific_bssid)); + + if (pmadapter->ext_scan) { + if (puser_scan_in->bssid_num) { + pbssid_tlv = + (MrvlIEtypes_Bssid_List_t *)ptlv_pos; + pbssid_tlv->header.type = TLV_TYPE_BSSID; + pbssid_tlv->header.len = wlan_cpu_to_le16( + MLAN_MAC_ADDR_LENGTH * + puser_scan_in->bssid_num); + memcpy_ext(pmadapter, pbssid_tlv->bssid, + puser_scan_in->bssid_list, + MLAN_MAC_ADDR_LENGTH * + puser_scan_in->bssid_num, + MLAN_MAC_ADDR_LENGTH * + puser_scan_in->bssid_num); + ptlv_pos += sizeof(MrvlIEtypesHeader_t) + + MLAN_MAC_ADDR_LENGTH * + puser_scan_in->bssid_num; + DBG_HEXDUMP( + MCMD_D, "scan bssid filter", pbssid_tlv, + sizeof(MrvlIEtypesHeader_t) + + MLAN_MAC_ADDR_LENGTH * + puser_scan_in + ->bssid_num); + } else if (memcmp(pmadapter, + pscan_cfg_out->specific_bssid, + &zero_mac, sizeof(zero_mac))) { + pbssid_tlv = + (MrvlIEtypes_Bssid_List_t *)ptlv_pos; + pbssid_tlv->header.type = TLV_TYPE_BSSID; + pbssid_tlv->header.len = + wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); + memcpy_ext(pmadapter, pbssid_tlv->bssid, + puser_scan_in->specific_bssid, + MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + ptlv_pos += sizeof(MrvlIEtypes_Bssid_List_t); + } + } + + for (ssid_idx = 0; + ((ssid_idx < NELEMENTS(puser_scan_in->ssid_list)) && + (*puser_scan_in->ssid_list[ssid_idx].ssid || + puser_scan_in->ssid_list[ssid_idx].max_len)); + ssid_idx++) { + ssid_len = wlan_strlen( + (char *)puser_scan_in->ssid_list[ssid_idx].ssid); + + pwildcard_ssid_tlv = + (MrvlIEtypes_WildCardSsIdParamSet_t *)ptlv_pos; + pwildcard_ssid_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WILDCARDSSID); + pwildcard_ssid_tlv->header.len = (t_u16)( + ssid_len + + sizeof(pwildcard_ssid_tlv->max_ssid_length)); + pwildcard_ssid_tlv->max_ssid_length = + puser_scan_in->ssid_list[ssid_idx].max_len; + + memcpy_ext(pmadapter, pwildcard_ssid_tlv->ssid, + puser_scan_in->ssid_list[ssid_idx].ssid, + ssid_len, MLAN_MAX_SSID_LENGTH); + + ptlv_pos += (sizeof(pwildcard_ssid_tlv->header) + + pwildcard_ssid_tlv->header.len); + + pwildcard_ssid_tlv->header.len = wlan_cpu_to_le16( + pwildcard_ssid_tlv->header.len); + + PRINTM(MINFO, "Scan: ssid_list[%d]: %s, %d\n", ssid_idx, + pwildcard_ssid_tlv->ssid, + pwildcard_ssid_tlv->max_ssid_length); + + if (ssid_len) { + ssid_filter = MTRUE; + if (!puser_scan_in->ssid_list[ssid_idx].max_len) { + PRINTM(MCMND, "user scan: %s\n", + pwildcard_ssid_tlv->ssid); + puser_scan_in->ssid_filter = MTRUE; + } + } + } + + /* + * The default number of channels sent in the command is low to + * ensure the response buffer from the firmware does not + * truncate scan results. That is not an issue with an SSID or + * BSSID filter applied to the scan results in the firmware. + */ + if ((ssid_idx && ssid_filter) || + memcmp(pmadapter, pscan_cfg_out->specific_bssid, &zero_mac, + sizeof(zero_mac))) { + *pfiltered_scan = MTRUE; + } + + } else { + pscan_cfg_out->bss_mode = (t_u8)pmadapter->scan_mode; + num_probes = pmadapter->scan_probes; + } + + /* + * If a specific BSSID or SSID is used, the number of channels in + * the scan command will be increased to the absolute maximum. + */ + if (*pfiltered_scan) + *pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; + else + *pmax_chan_per_scan = MRVDRV_CHANNELS_PER_SCAN_CMD; + + if (puser_scan_in) { + if (puser_scan_in->scan_chan_gap) { + *pmax_chan_per_scan = + MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; + PRINTM(MCMND, "Scan: channel gap = 0x%x\n", + puser_scan_in->scan_chan_gap); + pscan_gap_tlv = (MrvlIEtypes_ScanChanGap_t *)ptlv_pos; + pscan_gap_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); + pscan_gap_tlv->header.len = sizeof(pscan_gap_tlv->gap); + pscan_gap_tlv->gap = wlan_cpu_to_le16( + (t_u16)puser_scan_in->scan_chan_gap); + ptlv_pos += sizeof(pscan_gap_tlv->header) + + pscan_gap_tlv->header.len; + pscan_gap_tlv->header.len = + wlan_cpu_to_le16(pscan_gap_tlv->header.len); + } + } else if (pmadapter->scan_chan_gap) { + *pmax_chan_per_scan = MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; + PRINTM(MCMND, "Scan: channel gap = 0x%x\n", + pmadapter->scan_chan_gap); + pscan_gap_tlv = (MrvlIEtypes_ScanChanGap_t *)ptlv_pos; + pscan_gap_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); + pscan_gap_tlv->header.len = sizeof(pscan_gap_tlv->gap); + pscan_gap_tlv->gap = + wlan_cpu_to_le16((t_u16)pmadapter->scan_chan_gap); + ptlv_pos += sizeof(pscan_gap_tlv->header) + + pscan_gap_tlv->header.len; + } + if (pmadapter->ext_scan) { + pbss_mode = (MrvlIEtypes_BssMode_t *)ptlv_pos; + pbss_mode->header.type = wlan_cpu_to_le16(TLV_TYPE_BSS_MODE); + pbss_mode->header.len = sizeof(pbss_mode->bss_mode); + pbss_mode->bss_mode = pscan_cfg_out->bss_mode; + ptlv_pos += sizeof(pbss_mode->header) + pbss_mode->header.len; + pbss_mode->header.len = wlan_cpu_to_le16(pbss_mode->header.len); + if (pmadapter->ext_scan_enh) { + if (puser_scan_in) { + if (puser_scan_in->ext_scan_type == + EXT_SCAN_ENHANCE) + pmadapter->ext_scan_type = + EXT_SCAN_ENHANCE; + else + pmadapter->ext_scan_type = + EXT_SCAN_DEFAULT; + } else if (pmadapter->ext_scan == EXT_SCAN_TYPE_ENH) + pmadapter->ext_scan_type = EXT_SCAN_ENHANCE; + else + pmadapter->ext_scan_type = EXT_SCAN_DEFAULT; + if (pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) + *pmax_chan_per_scan = + MRVDRV_MAX_CHANNELS_PER_SCAN; + } + } + /* If the input config or adapter has the number of Probes set, add tlv + */ + if (num_probes) { + PRINTM(MINFO, "Scan: num_probes = %d\n", num_probes); + + pnum_probes_tlv = (MrvlIEtypes_NumProbes_t *)ptlv_pos; + pnum_probes_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_NUMPROBES); + pnum_probes_tlv->header.len = + sizeof(pnum_probes_tlv->num_probes); + pnum_probes_tlv->num_probes = + wlan_cpu_to_le16((t_u16)num_probes); + + ptlv_pos += sizeof(pnum_probes_tlv->header) + + pnum_probes_tlv->header.len; + + pnum_probes_tlv->header.len = + wlan_cpu_to_le16(pnum_probes_tlv->header.len); + } + + /* Append rates tlv */ + memset(pmadapter, rates, 0, sizeof(rates)); + + rates_size = wlan_get_supported_rates( + pmpriv, pmpriv->bss_mode, + (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) ? + pmpriv->config_bands : + pmadapter->adhoc_start_band, + rates); + + prates_tlv = (MrvlIEtypes_RatesParamSet_t *)ptlv_pos; + prates_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_RATES); + prates_tlv->header.len = wlan_cpu_to_le16((t_u16)rates_size); + memcpy_ext(pmadapter, prates_tlv->rates, rates, rates_size, rates_size); + ptlv_pos += sizeof(prates_tlv->header) + rates_size; + + PRINTM(MINFO, "SCAN_CMD: Rates size = %d\n", rates_size); + + if (ISSUPP_11NENABLED(pmpriv->adapter->fw_cap_info) && + (pmpriv->config_bands & BAND_GN || + pmpriv->config_bands & BAND_AN)) { + pht_cap = (MrvlIETypes_HTCap_t *)ptlv_pos; + memset(pmadapter, pht_cap, 0, sizeof(MrvlIETypes_HTCap_t)); + pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY); + pht_cap->header.len = sizeof(HTCap_t); + wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pmpriv->config_bands, + MTRUE); + HEXDUMP("SCAN: HT_CAPABILITIES IE", (t_u8 *)pht_cap, + sizeof(MrvlIETypes_HTCap_t)); + ptlv_pos += sizeof(MrvlIETypes_HTCap_t); + pht_cap->header.len = wlan_cpu_to_le16(pht_cap->header.len); + } + + if (ISSUPP_11ACENABLED(pmpriv->adapter->fw_cap_info) && + (pmpriv->config_bands & BAND_AAC)) { + pvht_cap = (MrvlIETypes_VHTCap_t *)ptlv_pos; + memset(pmadapter, pvht_cap, 0, sizeof(MrvlIETypes_VHTCap_t)); + pvht_cap->header.type = wlan_cpu_to_le16(VHT_CAPABILITY); + pvht_cap->header.len = sizeof(VHT_capa_t); + wlan_fill_vht_cap_tlv(pmpriv, pvht_cap, pmpriv->config_bands, + MFALSE, MFALSE); + HEXDUMP("SCAN: VHT_CAPABILITIES IE", (t_u8 *)pvht_cap, + sizeof(MrvlIETypes_VHTCap_t)); + ptlv_pos += sizeof(MrvlIETypes_VHTCap_t); + pvht_cap->header.len = wlan_cpu_to_le16(pvht_cap->header.len); + } + + if (IS_FW_SUPPORT_11AX(pmadapter) && + (pmpriv->config_bands & BAND_AAX)) { + phe_cap = (MrvlIEtypes_Extension_t *)ptlv_pos; + len = wlan_fill_he_cap_tlv(pmpriv, BAND_A, phe_cap, MFALSE); + HEXDUMP("SCAN: HE_CAPABILITIES IE", (t_u8 *)phe_cap, len); + ptlv_pos += len; + } + + if (wlan_is_ext_capa_support(pmpriv)) + wlan_add_ext_capa_info_ie(pmpriv, MNULL, &ptlv_pos); + if (pmpriv->adapter->ecsa_enable) { + t_u8 bandwidth = BW_20MHZ; + t_u8 oper_class = 1; + t_u32 usr_dot_11n_dev_cap; + if (pmpriv->media_connected) { + if (pmpriv->config_bands & BAND_A) + usr_dot_11n_dev_cap = + pmpriv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = + pmpriv->usr_dot_11n_dev_cap_bg; + if (usr_dot_11n_dev_cap & MBIT(17)) { + bandwidth = BW_40MHZ; + if (ISSUPP_11ACENABLED( + pmadapter->fw_cap_info) && + (pmpriv->config_bands & BAND_AAC)) + bandwidth = BW_80MHZ; + } + wlan_get_curr_oper_class( + pmpriv, + pmpriv->curr_bss_params.bss_descriptor.channel, + bandwidth, &oper_class); + } + wlan_add_supported_oper_class_ie(pmpriv, &ptlv_pos, oper_class); + } + wlan_add_wps_probe_request_ie(pmpriv, &ptlv_pos); + + if (puser_scan_in && puser_scan_in->proberesp_only) { + MrvlIEtypes_OnlyProberesp_t *proberesp_only = + (MrvlIEtypes_OnlyProberesp_t *)ptlv_pos; + memset(pmadapter, proberesp_only, 0, + sizeof(MrvlIEtypes_OnlyProberesp_t)); + proberesp_only->header.type = + wlan_cpu_to_le16(TLV_TYPE_ONLYPROBERESP); + proberesp_only->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + proberesp_only->proberesp_only = puser_scan_in->proberesp_only; + ptlv_pos += sizeof(MrvlIEtypes_OnlyProberesp_t); + } + + if (puser_scan_in && memcmp(pmadapter, puser_scan_in->random_mac, + zero_mac, MLAN_MAC_ADDR_LENGTH)) { + MrvlIEtypes_MacAddr_t *randomMacParam = + (MrvlIEtypes_MacAddr_t *)ptlv_pos; + memset(pmadapter, randomMacParam, 0, + sizeof(MrvlIEtypes_MacAddr_t)); + randomMacParam->header.type = + wlan_cpu_to_le16(TLV_TYPE_RANDOM_MAC); + randomMacParam->header.len = + wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); + memcpy_ext(pmadapter, randomMacParam->mac, + puser_scan_in->random_mac, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + ptlv_pos += sizeof(MrvlIEtypes_MacAddr_t); + } + /* + * Set the output for the channel TLV to the address in the tlv buffer + * past any TLVs that were added in this function (SSID, num_probes). + * Channel TLVs will be added past this for each scan command, + * preserving the TLVs that were previously added. + */ + *ppchan_list_out = (MrvlIEtypes_ChanListParamSet_t *)ptlv_pos; + + if (puser_scan_in && puser_scan_in->chan_list[0].chan_number) { + PRINTM(MINFO, "Scan: Using supplied channel list\n"); + + for (chan_idx = 0; + chan_idx < WLAN_USER_SCAN_CHAN_MAX && + puser_scan_in->chan_list[chan_idx].chan_number; + chan_idx++) { + radio_type = + puser_scan_in->chan_list[chan_idx].radio_type; + /*Ignore 5G/2G channels if radio_type do not match + * band*/ + if (!wlan_is_band_compatible( + pmpriv->config_bands, + radio_type_to_band(radio_type))) + continue; + (pscan_chan_list + chan_list_idx)->bandcfg.chanBand = + radio_type; + + channel = + puser_scan_in->chan_list[chan_idx].chan_number; + (pscan_chan_list + chan_list_idx)->chan_number = + channel; + + scan_type = + puser_scan_in->chan_list[chan_idx].scan_type; + if (scan_type == MLAN_SCAN_TYPE_UNCHANGED) + scan_type = pmadapter->scan_type; + + if (radio_type == BAND_5GHZ) { + if (pmadapter->fw_bands & BAND_A) + PRINTM(MINFO, + "UserScan request for A Band channel %d!!\n", + channel); + else { + PRINTM(MERROR, + "Scan in A band is not allowed!!\n"); + ret = MLAN_STATUS_FAILURE; + LEAVE(); + return ret; + } + } + + if (wlan_is_chan_passive(pmpriv, radio_type, channel)) { + /* do not send probe requests on this channel */ + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + /* Prevent active scanning on a radar controlled channel + */ + if (radio_type == BAND_5GHZ && + scan_type != MLAN_SCAN_TYPE_PASSIVE) { + if (pmadapter->active_scan_triggered == MFALSE) + if (wlan_11h_radar_detect_required( + pmpriv, channel)) { + scan_type = + MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE; + } + } + if (radio_type == BAND_2GHZ && + scan_type != MLAN_SCAN_TYPE_PASSIVE) { + if (pmadapter->active_scan_triggered == MFALSE) + if (wlan_bg_scan_type_is_passive( + pmpriv, channel)) { + scan_type = + MLAN_SCAN_TYPE_PASSIVE; + } + } + if (scan_type == MLAN_SCAN_TYPE_PASSIVE || + scan_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE) { + (pscan_chan_list + chan_list_idx) + ->chan_scan_mode.passive_scan = MTRUE; + (pscan_chan_list + chan_list_idx) + ->chan_scan_mode.hidden_ssid_report = + MTRUE; + } else { + (pscan_chan_list + chan_list_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_type == + MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE) { + scan_dur = pmadapter->passive_scan_time; + } else if (*pfiltered_scan) { + scan_dur = + pmadapter->specific_scan_time; + } else { + scan_dur = pmadapter->active_scan_time; + } + } + + if (pmadapter->coex_scan && + pmadapter->coex_min_scan_time && + (pmadapter->coex_min_scan_time > scan_dur)) + scan_dur = pmadapter->coex_min_scan_time; + if (scan_type == MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE && + pmadapter->passive_to_active_scan == + MLAN_PASS_TO_ACT_SCAN_EN) { + (pscan_chan_list + chan_list_idx) + ->chan_scan_mode.passive_to_active_scan = + MTRUE; + scan_dur = MAX(MIN_PASSIVE_TO_ACTIVE_SCAN_TIME, + scan_dur); + } + PRINTM(MINFO, + "chan=%d, mode=%d, passive_to_active=%d\n", + (pscan_chan_list + chan_list_idx)->chan_number, + (pscan_chan_list + chan_list_idx) + ->chan_scan_mode.passive_scan, + (pscan_chan_list + chan_list_idx) + ->chan_scan_mode.passive_to_active_scan); + + (pscan_chan_list + chan_list_idx)->min_scan_time = + wlan_cpu_to_le16(scan_dur); + (pscan_chan_list + chan_list_idx)->max_scan_time = + wlan_cpu_to_le16(scan_dur); + chan_list_idx++; + } + + /* 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(pmlan_adapter pmadapter, + MrvlIEtypes_Data_t *ptlv, + t_u32 tlv_buf_size, + t_u32 req_tlv_type, + MrvlIEtypes_Data_t **pptlv) +{ + MrvlIEtypes_Data_t *pcurrent_tlv; + t_u32 tlv_buf_left; + t_u32 tlv_type; + t_u32 tlv_len; + + ENTER(); + + pcurrent_tlv = ptlv; + tlv_buf_left = tlv_buf_size; + *pptlv = MNULL; + + PRINTM(MINFO, "SCAN_RESP: tlv_buf_size = %d\n", tlv_buf_size); + + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(pcurrent_tlv->header.type); + tlv_len = wlan_le16_to_cpu(pcurrent_tlv->header.len); + + if (sizeof(ptlv->header) + tlv_len > tlv_buf_left) { + PRINTM(MERROR, "SCAN_RESP: TLV buffer corrupt\n"); + break; + } + + if (req_tlv_type == tlv_type) { + switch (tlv_type) { + case TLV_TYPE_TSFTIMESTAMP: + PRINTM(MINFO, + "SCAN_RESP: TSF Timestamp TLV, len = %d\n", + tlv_len); + *pptlv = (MrvlIEtypes_Data_t *)pcurrent_tlv; + break; + case TLV_TYPE_CHANNELBANDLIST: + PRINTM(MINFO, + "SCAN_RESP: CHANNEL BAND LIST TLV, len = %d\n", + tlv_len); + *pptlv = (MrvlIEtypes_Data_t *)pcurrent_tlv; + break; + case TLV_TYPE_CHANNEL_STATS: + PRINTM(MINFO, + "SCAN_RESP: CHANNEL STATS TLV, len = %d\n", + tlv_len); + *pptlv = (MrvlIEtypes_Data_t *)pcurrent_tlv; + break; + default: + PRINTM(MERROR, + "SCAN_RESP: Unhandled TLV = %d\n", + tlv_type); + /* Give up, this seems corrupted */ + LEAVE(); + return; + } + } + + if (*pptlv) { + /* HEXDUMP("SCAN_RESP: TLV Buf", (t_u8 *)*pptlv+4, + * tlv_len); */ + break; + } + + tlv_buf_left -= (sizeof(ptlv->header) + tlv_len); + pcurrent_tlv = + (MrvlIEtypes_Data_t *)(pcurrent_tlv->data + tlv_len); + + } /* while */ + + LEAVE(); +} + +/** + * @brief Interpret a BSS scan response returned from the firmware + * + * Parse the various fixed fields and IEs passed back for a BSS probe + * response or beacon from the scan command. Record information as needed + * in the scan table BSSDescriptor_t for that entry. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pbss_entry Output parameter: Pointer to the BSS Entry + * @param pbeacon_info Pointer to the Beacon information + * @param bytes_left Number of bytes left to parse + * @param ext_scan extended scan + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_interpret_bss_desc_with_ie(pmlan_adapter pmadapter, + BSSDescriptor_t *pbss_entry, + t_u8 **pbeacon_info, + t_u32 *bytes_left, + t_u8 ext_scan) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + IEEEtypes_ElementId_e element_id; + IEEEtypes_FhParamSet_t *pfh_param_set; + IEEEtypes_DsParamSet_t *pds_param_set; + IEEEtypes_CfParamSet_t *pcf_param_set; + IEEEtypes_IbssParamSet_t *pibss_param_set; + IEEEtypes_CapInfo_t *pcap_info; + WLAN_802_11_FIXED_IEs fixed_ie; + t_u8 *pcurrent_ptr; + t_u8 *prate; + t_u8 element_len; + t_u16 total_ie_len; + t_u8 bytes_to_copy; + t_u8 rate_size; + t_u16 beacon_size; + t_u8 found_data_rate_ie; + t_u32 bytes_left_for_current_beacon; + IEEEtypes_ERPInfo_t *perp_info; + + IEEEtypes_VendorSpecific_t *pvendor_ie; + const t_u8 wpa_oui[4] = {0x00, 0x50, 0xf2, 0x01}; + const t_u8 wmm_oui[4] = {0x00, 0x50, 0xf2, 0x02}; + const t_u8 osen_oui[] = {0x50, 0x6f, 0x9a, 0x12}; + + IEEEtypes_CountryInfoSet_t *pcountry_info; + IEEEtypes_Extension_t *pext_tlv; + + 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_ext(pmadapter, &beacon_size, *pbeacon_info, + sizeof(beacon_size), 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_ext(pmadapter, pbss_entry->mac_address, pcurrent_ptr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + PRINTM(MINFO, "InterpretIE: AP MAC Addr-" MACSTR "\n", + MAC2STR(pbss_entry->mac_address)); + + pcurrent_ptr += MLAN_MAC_ADDR_LENGTH; + bytes_left_for_current_beacon -= MLAN_MAC_ADDR_LENGTH; + + /* + * Next 4 fields are RSSI (for legacy scan only), time stamp, + * beacon interval, and capability information + */ + if (!ext_scan) { + /* RSSI is 1 byte long */ + pbss_entry->rssi = (t_s32)(*pcurrent_ptr); + PRINTM(MINFO, "InterpretIE: RSSI=%02X\n", *pcurrent_ptr); + pcurrent_ptr += 1; + bytes_left_for_current_beacon -= 1; + } + + /* + * The RSSI is not part of the beacon/probe response. After we have + * advanced pcurrent_ptr past the RSSI field, save the remaining + * data for use at the application layer + */ + pbss_entry->pbeacon_buf = pcurrent_ptr; + pbss_entry->beacon_buf_size = bytes_left_for_current_beacon; + + /* Time stamp is 8 bytes long */ + memcpy_ext(pmadapter, fixed_ie.time_stamp, pcurrent_ptr, 8, + sizeof(fixed_ie.time_stamp)); + memcpy_ext(pmadapter, pbss_entry->time_stamp, pcurrent_ptr, 8, + sizeof(pbss_entry->time_stamp)); + pcurrent_ptr += 8; + bytes_left_for_current_beacon -= 8; + + /* Beacon interval is 2 bytes long */ + memcpy_ext(pmadapter, &fixed_ie.beacon_interval, pcurrent_ptr, 2, + sizeof(fixed_ie.beacon_interval)); + 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_ext(pmadapter, &fixed_ie.capabilities, pcurrent_ptr, 2, + sizeof(fixed_ie.capabilities)); + 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_ext(pmadapter, &pbss_entry->cap_info, pcap_info, + sizeof(IEEEtypes_CapInfo_t), 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; + ret = MLAN_STATUS_FAILURE; + continue; + } + + switch (element_id) { + case SSID: + if (element_len > MRVDRV_MAX_SSID_LENGTH) { + bytes_left_for_current_beacon = 0; + ret = MLAN_STATUS_FAILURE; + continue; + } + if (!pbss_entry->ssid.ssid_len) { + pbss_entry->ssid.ssid_len = element_len; + memcpy_ext(pmadapter, pbss_entry->ssid.ssid, + (pcurrent_ptr + 2), element_len, + sizeof(pbss_entry->ssid.ssid)); + } + 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; + ret = MLAN_STATUS_FAILURE; + continue; + } + memcpy_ext(pmadapter, pbss_entry->data_rates, + pcurrent_ptr + 2, element_len, + sizeof(pbss_entry->data_rates)); + memcpy_ext(pmadapter, pbss_entry->supported_rates, + pcurrent_ptr + 2, element_len, + sizeof(pbss_entry->supported_rates)); + 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_ext(pmadapter, + &pbss_entry->phy_param_set.fh_param_set, + pfh_param_set, 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_ext(pmadapter, + &pbss_entry->phy_param_set.ds_param_set, + pds_param_set, 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_ext(pmadapter, + &pbss_entry->ss_param_set.cf_param_set, + pcf_param_set, 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_ext(pmadapter, + &pbss_entry->ss_param_set.ibss_param_set, + pibss_param_set, 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_ext(pmadapter, &pbss_entry->country_info, + pcountry_info, pcountry_info->len + 2, + sizeof(pbss_entry->country_info)); + 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_ext(pmadapter, prate, pcurrent_ptr + 2, + bytes_to_copy, bytes_to_copy); + + prate = (t_u8 *)pbss_entry->supported_rates; + prate += rate_size; + memcpy_ext(pmadapter, prate, pcurrent_ptr + 2, + bytes_to_copy, 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_ext(pmadapter, + (t_u8 *)&pbss_entry->wmm_ie, + pcurrent_ptr, total_ie_len, + sizeof(pbss_entry->wmm_ie)); + HEXDUMP("InterpretIE: Resp WMM_IE", + (t_u8 *)&pbss_entry->wmm_ie, + total_ie_len); + } + } else if (!memcmp(pmadapter, pvendor_ie->vend_hdr.oui, + osen_oui, sizeof(osen_oui))) { + pbss_entry->posen_ie = + (IEEEtypes_Generic_t *)pcurrent_ptr; + pbss_entry->osen_offset = (t_u16)( + pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp OSEN_IE", + (t_u8 *)pbss_entry->posen_ie, + (*(pbss_entry->posen_ie)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + } + break; + case RSN_IE: + pbss_entry->prsn_ie = + (IEEEtypes_Generic_t *)pcurrent_ptr; + pbss_entry->rsn_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp RSN_IE", + (t_u8 *)pbss_entry->prsn_ie, + (*(pbss_entry->prsn_ie)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case WAPI_IE: + pbss_entry->pwapi_ie = + (IEEEtypes_Generic_t *)pcurrent_ptr; + pbss_entry->wapi_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp WAPI_IE", + (t_u8 *)pbss_entry->pwapi_ie, + (*(pbss_entry->pwapi_ie)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case HT_CAPABILITY: + pbss_entry->pht_cap = (IEEEtypes_HTCap_t *)pcurrent_ptr; + pbss_entry->ht_cap_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp HTCAP_IE", + (t_u8 *)pbss_entry->pht_cap, + (*(pbss_entry->pht_cap)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case HT_OPERATION: + pbss_entry->pht_info = + (IEEEtypes_HTInfo_t *)pcurrent_ptr; + pbss_entry->ht_info_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp HTINFO_IE", + (t_u8 *)pbss_entry->pht_info, + (*(pbss_entry->pht_info)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case BSSCO_2040: + pbss_entry->pbss_co_2040 = + (IEEEtypes_2040BSSCo_t *)pcurrent_ptr; + pbss_entry->bss_co_2040_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp 2040BSSCOEXISTANCE_IE", + (t_u8 *)pbss_entry->pbss_co_2040, + (*(pbss_entry->pbss_co_2040)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case EXT_CAPABILITY: + pbss_entry->pext_cap = + (IEEEtypes_ExtCap_t *)pcurrent_ptr; + pbss_entry->ext_cap_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp EXTCAP_IE", + (t_u8 *)pbss_entry->pext_cap, + (*(pbss_entry->pext_cap)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case OVERLAPBSSSCANPARAM: + pbss_entry->poverlap_bss_scan_param = + (IEEEtypes_OverlapBSSScanParam_t *)pcurrent_ptr; + pbss_entry->overlap_bss_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp OBSS_IE", + (t_u8 *)pbss_entry->poverlap_bss_scan_param, + (*(pbss_entry->poverlap_bss_scan_param)) + .ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case VHT_CAPABILITY: + pbss_entry->pvht_cap = + (IEEEtypes_VHTCap_t *)pcurrent_ptr; + pbss_entry->vht_cap_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp VHTCAP_IE", + (t_u8 *)pbss_entry->pvht_cap, + (*(pbss_entry->pvht_cap)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case VHT_OPERATION: + pbss_entry->pvht_oprat = + (IEEEtypes_VHTOprat_t *)pcurrent_ptr; + pbss_entry->vht_oprat_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp VHTOPER_IE", + (t_u8 *)pbss_entry->pvht_oprat, + (*(pbss_entry->pvht_oprat)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case EXT_BSS_LOAD: + pbss_entry->pext_bssload = + (IEEEtypes_ExtBSSload_t *)pcurrent_ptr; + pbss_entry->ext_bssload_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp EXTBSSLOAD_IE", + (t_u8 *)pbss_entry->pext_bssload, + (*(pbss_entry->pext_bssload)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case VHT_TX_POWER_ENV: + pbss_entry->pvht_txpower = + (IEEEtypes_VHTtxpower_t *)pcurrent_ptr; + pbss_entry->vht_txpower_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp TXPOW_IE", + (t_u8 *)pbss_entry->pvht_txpower, + (*(pbss_entry->pvht_txpower)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case EXT_POWER_CONSTR: + pbss_entry->pext_pwer = + (IEEEtypes_ExtPwerCons_t *)pcurrent_ptr; + pbss_entry->ext_pwer_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp EXTPOW_IE", + (t_u8 *)pbss_entry->pext_pwer, + (*(pbss_entry->pext_pwer)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case QUIET_CHAN: + pbss_entry->pquiet_chan = + (IEEEtypes_QuietChan_t *)pcurrent_ptr; + pbss_entry->quiet_chan_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp QUIETCHAN_IE", + (t_u8 *)pbss_entry->pquiet_chan, + (*(pbss_entry->pquiet_chan)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case BW_CHANNEL_SWITCH: + /* RANDYTODO */ + break; + case AID_INFO: + break; + case OPER_MODE_NTF: + pbss_entry->poper_mode = + (IEEEtypes_OperModeNtf_t *)pcurrent_ptr; + pbss_entry->oper_mode_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp OPERMODENTF_IE", + (t_u8 *)pbss_entry->poper_mode, + (*(pbss_entry->poper_mode)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + case EXTENSION: + pext_tlv = (IEEEtypes_Extension_t *)pcurrent_ptr; + switch (pext_tlv->ext_id) { + case HE_CAPABILITY: + pbss_entry->phe_cap = + (IEEEtypes_HECap_t *)pcurrent_ptr; + pbss_entry->he_cap_offset = (t_u16)( + pcurrent_ptr - pbss_entry->pbeacon_buf); + break; + case HE_OPERATION: + pbss_entry->phe_oprat = pext_tlv; + pbss_entry->he_oprat_offset = (t_u16)( + pcurrent_ptr - pbss_entry->pbeacon_buf); + break; + default: + break; + } + break; + case MOBILITY_DOMAIN: + PRINTM(MCMND, "Mobility Domain IE received in Scan\n"); + pbss_entry->pmd_ie = + (IEEEtypes_MobilityDomain_t *)pcurrent_ptr; + pbss_entry->md_offset = + (t_u16)(pcurrent_ptr - pbss_entry->pbeacon_buf); + HEXDUMP("InterpretIE: Resp Mobility Domain IE", + (t_u8 *)pbss_entry->pmd_ie, + (*(pbss_entry->pmd_ie)).ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + break; + default: + break; + } + + pcurrent_ptr += element_len + 2; + + /* Need to account for IE ID and IE Len */ + bytes_left_for_current_beacon -= (element_len + 2); + + } /* while (bytes_left_for_current_beacon > 2) */ + + LEAVE(); + return ret; +} + +/** + * @brief Adjust ie's position in BSSDescriptor_t + * + * @param pmpriv A pointer to mlan_private structure + * @param pbss_entry A pointer to BSSDescriptor_t structure + * + * @return N/A + */ +static t_void wlan_adjust_ie_in_bss_entry(mlan_private *pmpriv, + 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->posen_ie) { + pbss_entry->posen_ie = + (IEEEtypes_Generic_t *)(pbss_entry->pbeacon_buf + + pbss_entry->osen_offset); + } + if (pbss_entry->pmd_ie) { + pbss_entry->pmd_ie = + (IEEEtypes_MobilityDomain_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->md_offset); + } + if (pbss_entry->pht_cap) { + pbss_entry->pht_cap = + (IEEEtypes_HTCap_t *)(pbss_entry->pbeacon_buf + + pbss_entry->ht_cap_offset); + } + if (pbss_entry->pht_info) { + pbss_entry->pht_info = + (IEEEtypes_HTInfo_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->ht_info_offset); + } + if (pbss_entry->pbss_co_2040) { + pbss_entry->pbss_co_2040 = + (IEEEtypes_2040BSSCo_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->bss_co_2040_offset); + } + if (pbss_entry->pext_cap) { + pbss_entry->pext_cap = + (IEEEtypes_ExtCap_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->ext_cap_offset); + } + if (pbss_entry->poverlap_bss_scan_param) { + pbss_entry->poverlap_bss_scan_param = + (IEEEtypes_OverlapBSSScanParam_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->overlap_bss_offset); + } + if (pbss_entry->pvht_cap) { + pbss_entry->pvht_cap = + (IEEEtypes_VHTCap_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->vht_cap_offset); + } + if (pbss_entry->pvht_oprat) { + pbss_entry->pvht_oprat = + (IEEEtypes_VHTOprat_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->vht_oprat_offset); + } + if (pbss_entry->pvht_txpower) { + pbss_entry->pvht_txpower = + (IEEEtypes_VHTtxpower_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->vht_txpower_offset); + } + if (pbss_entry->pext_pwer) { + pbss_entry->pext_pwer = + (IEEEtypes_ExtPwerCons_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->ext_pwer_offset); + } + if (pbss_entry->pext_bssload) { + pbss_entry->pext_bssload = + (IEEEtypes_ExtBSSload_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->ext_bssload_offset); + } + if (pbss_entry->pquiet_chan) { + pbss_entry->pquiet_chan = + (IEEEtypes_QuietChan_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->quiet_chan_offset); + } + if (pbss_entry->poper_mode) { + pbss_entry->poper_mode = + (IEEEtypes_OperModeNtf_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->oper_mode_offset); + } + if (pbss_entry->phe_cap) { + pbss_entry->phe_cap = + (IEEEtypes_HECap_t *)(pbss_entry->pbeacon_buf + + pbss_entry->he_cap_offset); + } + + if (pbss_entry->phe_oprat) { + pbss_entry->phe_oprat = + (IEEEtypes_Extension_t + *)(pbss_entry->pbeacon_buf + + pbss_entry->he_oprat_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->posen_ie = MNULL; + pbss_entry->osen_offset = 0; + pbss_entry->pmd_ie = MNULL; + pbss_entry->md_offset = 0; + pbss_entry->pht_cap = MNULL; + pbss_entry->ht_cap_offset = 0; + pbss_entry->pht_info = MNULL; + pbss_entry->ht_info_offset = 0; + pbss_entry->pbss_co_2040 = MNULL; + pbss_entry->bss_co_2040_offset = 0; + pbss_entry->pext_cap = MNULL; + pbss_entry->ext_cap_offset = 0; + pbss_entry->poverlap_bss_scan_param = MNULL; + pbss_entry->overlap_bss_offset = 0; + } + LEAVE(); + return; +} + +/** + * @brief Store a beacon or probe response for a BSS returned in the scan + * + * Store a new scan response or an update for a previous scan response. New + * entries need to verify that they do not exceed the total amount of + * memory allocated for the table. + + * Replacement entries need to take into consideration the amount of space + * currently allocated for the beacon/probe response and adjust the entry + * as needed. + * + * A small amount of extra pad (SCAN_BEACON_ENTRY_PAD) is generally reserved + * for an entry in case it is a beacon since a probe response for the + * network will by larger per the standard. This helps to reduce the + * amount of memory copying to fit a new probe response into an entry + * already occupied by a network's previously stored beacon. + * + * @param pmpriv A pointer to mlan_private structure + * @param beacon_idx Index in the scan table to store this entry; may be + * replacing an older duplicate entry for this BSS + * @param num_of_ent Number of entries currently in the table + * @param pnew_beacon Pointer to the new beacon/probe response to save + * + * @return N/A + */ +static t_void wlan_ret_802_11_scan_store_beacon(mlan_private *pmpriv, + t_u32 beacon_idx, + t_u32 num_of_ent, + 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_ext(pmadapter, pbcn_store, + pnew_beacon->pbeacon_buf, + pnew_beacon->beacon_buf_size, + 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_ext(pmadapter, pbcn_store, + pnew_beacon->pbeacon_buf, new_bcn_size, + 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_ext(pmadapter, pbcn_store, + pnew_beacon->pbeacon_buf, new_bcn_size, + 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->posen_ie) + pnew_beacon->osen_offset = + pmadapter->pscan_table[beacon_idx] + .osen_offset; + if (pnew_beacon->pmd_ie) + pnew_beacon->md_offset = + pmadapter->pscan_table[beacon_idx] + .md_offset; + if (pnew_beacon->pht_cap) + pnew_beacon->ht_cap_offset = + pmadapter->pscan_table[beacon_idx] + .ht_cap_offset; + if (pnew_beacon->pht_info) + pnew_beacon->ht_info_offset = + pmadapter->pscan_table[beacon_idx] + .ht_info_offset; + if (pnew_beacon->pbss_co_2040) + pnew_beacon->bss_co_2040_offset = + pmadapter->pscan_table[beacon_idx] + .bss_co_2040_offset; + if (pnew_beacon->pext_cap) + pnew_beacon->ext_cap_offset = + pmadapter->pscan_table[beacon_idx] + .ext_cap_offset; + if (pnew_beacon->poverlap_bss_scan_param) + pnew_beacon->overlap_bss_offset = + pmadapter->pscan_table[beacon_idx] + .overlap_bss_offset; + if (pnew_beacon->pvht_cap) + pnew_beacon->vht_cap_offset = + pmadapter->pscan_table[beacon_idx] + .vht_cap_offset; + if (pnew_beacon->pvht_oprat) + pnew_beacon->vht_oprat_offset = + pmadapter->pscan_table[beacon_idx] + .vht_oprat_offset; + if (pnew_beacon->pvht_txpower) + pnew_beacon->vht_txpower_offset = + pmadapter->pscan_table[beacon_idx] + .vht_txpower_offset; + if (pnew_beacon->pext_pwer) + pnew_beacon->ext_pwer_offset = + pmadapter->pscan_table[beacon_idx] + .ext_pwer_offset; + if (pnew_beacon->pext_bssload) + pnew_beacon->ext_bssload_offset = + pmadapter->pscan_table[beacon_idx] + .ext_bssload_offset; + if (pnew_beacon->pquiet_chan) + pnew_beacon->quiet_chan_offset = + pmadapter->pscan_table[beacon_idx] + .quiet_chan_offset; + if (pnew_beacon->poper_mode) + pnew_beacon->oper_mode_offset = + pmadapter->pscan_table[beacon_idx] + .oper_mode_offset; + if (pnew_beacon->phe_cap) + pnew_beacon->he_cap_offset = + pmadapter->pscan_table[beacon_idx] + .he_cap_offset; + if (pnew_beacon->phe_oprat) + pnew_beacon->he_oprat_offset = + pmadapter->pscan_table[beacon_idx] + .he_oprat_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_ext(pmadapter, tmp_buf, + pmadapter->bcn_buf, bcn_size, + 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_ext(pmadapter, pmadapter->pbcn_buf_end, + pnew_beacon->pbeacon_buf, + pnew_beacon->beacon_buf_size, + pnew_beacon->beacon_buf_size); + + /* + * Update the beacon ptr to point to the table + * save area + */ + pnew_beacon->pbeacon_buf = pmadapter->pbcn_buf_end; + pnew_beacon->beacon_buf_size_max = + (pnew_beacon->beacon_buf_size + + SCAN_BEACON_ENTRY_PAD); + wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); + + /* Increment the end pointer by the size reserved */ + pmadapter->pbcn_buf_end += + pnew_beacon->beacon_buf_size_max; + + PRINTM(MINFO, + "AppControl: Beacon[%02d] sz=%03d," + " used = %04d, left = %04d\n", + beacon_idx, pnew_beacon->beacon_buf_size, + (pmadapter->pbcn_buf_end - pmadapter->bcn_buf), + (pmadapter->bcn_buf_size - + (pmadapter->pbcn_buf_end - pmadapter->bcn_buf))); + } else { + /* + * No space for new beacon + */ + PRINTM(MCMND, + "AppControl: No space beacon (%d): " MACSTR + "; sz=%03d, left=%03d\n", + beacon_idx, MAC2STR(pnew_beacon->mac_address), + pnew_beacon->beacon_buf_size, + (pmadapter->bcn_buf_size - + (pmadapter->pbcn_buf_end - pmadapter->bcn_buf))); + + /* + * Storage failure; clear storage records + * for this bcn + */ + pnew_beacon->pbeacon_buf = MNULL; + pnew_beacon->beacon_buf_size = 0; + pnew_beacon->beacon_buf_size_max = 0; + wlan_adjust_ie_in_bss_entry(pmpriv, pnew_beacon); + } + } + + LEAVE(); +} + +/** + * @brief update beacon buffer of the current bss descriptor + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS, otherwise failure + */ +static mlan_status wlan_update_curr_bcn(mlan_private *pmpriv) +{ + BSSDescriptor_t *pcurr_bss = &pmpriv->curr_bss_params.bss_descriptor; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pmpriv->pcurr_bcn_buf && pmpriv->curr_bcn_size) { + pcurr_bss->pbeacon_buf = pmpriv->pcurr_bcn_buf; + pcurr_bss->beacon_buf_size = pmpriv->curr_bcn_size; + pcurr_bss->beacon_buf_size_max = pmpriv->curr_bcn_size; + + /* adjust the pointers in the current bss descriptor */ + if (pcurr_bss->pwpa_ie) { + pcurr_bss->pwpa_ie = + (IEEEtypes_VendorSpecific_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->wpa_offset); + } + if (pcurr_bss->prsn_ie) { + pcurr_bss->prsn_ie = + (IEEEtypes_Generic_t *)(pcurr_bss->pbeacon_buf + + pcurr_bss->rsn_offset); + } + if (pcurr_bss->pmd_ie) { + pcurr_bss->pmd_ie = (IEEEtypes_MobilityDomain_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->md_offset); + } + if (pcurr_bss->pht_cap) { + pcurr_bss->pht_cap = + (IEEEtypes_HTCap_t *)(pcurr_bss->pbeacon_buf + + pcurr_bss->ht_cap_offset); + } + + if (pcurr_bss->pht_info) { + pcurr_bss->pht_info = + (IEEEtypes_HTInfo_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->ht_info_offset); + } + + if (pcurr_bss->pbss_co_2040) { + pcurr_bss->pbss_co_2040 = + (IEEEtypes_2040BSSCo_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->bss_co_2040_offset); + } + + if (pcurr_bss->pext_cap) { + pcurr_bss->pext_cap = + (IEEEtypes_ExtCap_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->ext_cap_offset); + } + + if (pcurr_bss->poverlap_bss_scan_param) { + pcurr_bss->poverlap_bss_scan_param = + (IEEEtypes_OverlapBSSScanParam_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->overlap_bss_offset); + } + + if (pcurr_bss->pvht_cap) { + pcurr_bss->pvht_cap = + (IEEEtypes_VHTCap_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->vht_cap_offset); + } + + if (pcurr_bss->pvht_oprat) { + pcurr_bss->pvht_oprat = + (IEEEtypes_VHTOprat_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->vht_oprat_offset); + } + + if (pcurr_bss->pvht_txpower) { + pcurr_bss->pvht_txpower = + (IEEEtypes_VHTtxpower_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->vht_txpower_offset); + } + + if (pcurr_bss->pext_pwer) { + pcurr_bss->pext_pwer = + (IEEEtypes_ExtPwerCons_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->ext_pwer_offset); + } + + if (pcurr_bss->pext_bssload) { + pcurr_bss->pext_bssload = + (IEEEtypes_ExtBSSload_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->ext_bssload_offset); + } + + if (pcurr_bss->pquiet_chan) { + pcurr_bss->pquiet_chan = + (IEEEtypes_QuietChan_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->quiet_chan_offset); + } + + if (pcurr_bss->poper_mode) { + pcurr_bss->poper_mode = + (IEEEtypes_OperModeNtf_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->oper_mode_offset); + } + if (pcurr_bss->phe_cap) { + pcurr_bss->phe_cap = + (IEEEtypes_HECap_t *)(pcurr_bss->pbeacon_buf + + pcurr_bss->he_cap_offset); + } + + if (pcurr_bss->phe_oprat) { + pcurr_bss->phe_oprat = + (IEEEtypes_Extension_t + *)(pcurr_bss->pbeacon_buf + + pcurr_bss->he_oprat_offset); + } + + PRINTM(MINFO, "current beacon restored %d\n", + pmpriv->curr_bcn_size); + } else { + PRINTM(MERROR, "curr_bcn_buf not saved\n"); + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief get the chan load from chan stats. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param channel channel * + * + * @return channel load + */ +static t_u16 wlan_get_chan_load(mlan_adapter *pmadapter, t_u8 channel) +{ + t_u16 chan_load = 0; + int i; + for (i = 0; i < pmadapter->num_in_chan_stats; i++) { + if ((pmadapter->pchan_stats[i].chan_num == channel) && + pmadapter->pchan_stats[i].cca_scan_duration) { + chan_load = + (pmadapter->pchan_stats[i].cca_busy_duration * + 100) / + pmadapter->pchan_stats[i].cca_scan_duration; + break; + } + } + return chan_load; +} + +/** + * @brief get the chan min/max rssi + * + * @param pmadapter A pointer to mlan_adapter structure + * @param channel channel * + * @param min_flag flag to get min rssi + * @return rssi + */ +static t_u8 wlan_get_chan_rssi(mlan_adapter *pmadapter, t_u8 channel, + t_u8 min_flag) +{ + t_u8 rssi = 0; + int i; + for (i = 0; i < pmadapter->num_in_scan_table; i++) { + if (pmadapter->pscan_table[i].channel == channel) { + if (rssi == 0) + rssi = (t_s32)pmadapter->pscan_table[i].rssi; + else { + if (min_flag) + rssi = MIN( + rssi, + pmadapter->pscan_table[i].rssi); + else + rssi = MAX( + rssi, + pmadapter->pscan_table[i].rssi); + } + } + } + return rssi; +} + +/** + * @brief update the min/max rssi for channel statistics. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return N/A + */ +static t_void wlan_update_chan_rssi(mlan_adapter *pmadapter) +{ + int i; + t_s8 min_rssi = 0; + t_s8 max_rssi = 0; + t_s8 rss = 0; + for (i = 0; i < pmadapter->num_in_chan_stats; i++) { + if (pmadapter->pchan_stats[i].chan_num && + pmadapter->pchan_stats[i].cca_scan_duration) { + min_rssi = -wlan_get_chan_rssi( + pmadapter, pmadapter->pchan_stats[i].chan_num, + MFALSE); + max_rssi = -wlan_get_chan_rssi( + pmadapter, pmadapter->pchan_stats[i].chan_num, + MTRUE); + rss = min_rssi - pmadapter->pchan_stats[i].noise; + // rss should always > 0, FW need fix the wrong + // rssi/noise in scantable + if (rss > 0) + pmadapter->pchan_stats[i].min_rss = rss; + else + pmadapter->pchan_stats[i].min_rss = 0; + + rss = max_rssi - pmadapter->pchan_stats[i].noise; + if (rss > 0) + pmadapter->pchan_stats[i].max_rss = rss; + else + pmadapter->pchan_stats[i].max_rss = 0; + PRINTM(MCMND, + "chan=%d, min_rssi=%d, max_rssi=%d noise=%d min_rss=%d, max_rss=%d\n", + pmadapter->pchan_stats[i].chan_num, min_rssi, + max_rssi, pmadapter->pchan_stats[i].noise, + pmadapter->pchan_stats[i].min_rss, + pmadapter->pchan_stats[i].max_rss); + } + } + return; +} + +/** + * @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(mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_s32 j; + t_u32 i; + mlan_status ret = MLAN_STATUS_SUCCESS; + BSSDescriptor_t *bss_new_entry = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (pmpriv->media_connected == MTRUE) { + j = wlan_find_bssid_in_list( + pmpriv, + pmpriv->curr_bss_params.bss_descriptor.mac_address, + pmpriv->bss_mode); + + if (j >= 0) { + memcpy_ext(pmadapter, &pmadapter->pscan_table[j].ssid, + &pmpriv->curr_bss_params.bss_descriptor.ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + 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.posen_ie = MNULL; + pmpriv->curr_bss_params.bss_descriptor.osen_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pmd_ie = MNULL; + pmpriv->curr_bss_params.bss_descriptor.md_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pht_cap = MNULL; + pmpriv->curr_bss_params.bss_descriptor.ht_cap_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor.pht_info = MNULL; + pmpriv->curr_bss_params.bss_descriptor.ht_info_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor.pbss_co_2040 = + MNULL; + pmpriv->curr_bss_params.bss_descriptor + .bss_co_2040_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pext_cap = MNULL; + pmpriv->curr_bss_params.bss_descriptor.ext_cap_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor + .poverlap_bss_scan_param = MNULL; + pmpriv->curr_bss_params.bss_descriptor + .overlap_bss_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pvht_cap = MNULL; + pmpriv->curr_bss_params.bss_descriptor.vht_cap_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor.pvht_oprat = + MNULL; + pmpriv->curr_bss_params.bss_descriptor.vht_oprat_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor.pvht_txpower = + MNULL; + pmpriv->curr_bss_params.bss_descriptor + .vht_txpower_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pext_pwer = + MNULL; + pmpriv->curr_bss_params.bss_descriptor.ext_pwer_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor.pext_bssload = + MNULL; + pmpriv->curr_bss_params.bss_descriptor + .ext_bssload_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.pquiet_chan = + MNULL; + pmpriv->curr_bss_params.bss_descriptor + .quiet_chan_offset = 0; + pmpriv->curr_bss_params.bss_descriptor.poper_mode = + MNULL; + pmpriv->curr_bss_params.bss_descriptor.oper_mode_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor.phe_cap = MNULL; + pmpriv->curr_bss_params.bss_descriptor.he_cap_offset = + 0; + pmpriv->curr_bss_params.bss_descriptor.phe_oprat = + MNULL; + pmpriv->curr_bss_params.bss_descriptor.he_oprat_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_ext( + pmadapter, + &pmpriv->curr_bss_params.bss_descriptor, + &pmadapter->pscan_table[j], + sizeof(pmpriv->curr_bss_params.bss_descriptor), + sizeof(pmpriv->curr_bss_params.bss_descriptor)); + + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + pmpriv->curr_bcn_buf_lock); + wlan_save_curr_bcn(pmpriv); + } else { + // Apend to the end of scan table + if (pmpriv->pcurr_bcn_buf && pmpriv->curr_bcn_size) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(BSSDescriptor_t), + MLAN_MEM_DEF, + (t_u8 **)&bss_new_entry); + if (ret == MLAN_STATUS_SUCCESS && + bss_new_entry) { + memcpy_ext( + pmadapter, bss_new_entry, + &pmpriv->curr_bss_params + .bss_descriptor, + sizeof(pmpriv->curr_bss_params + .bss_descriptor), + sizeof(BSSDescriptor_t)); + if (pmadapter->num_in_scan_table < + MRVDRV_MAX_BSSID_LIST) + pmadapter->num_in_scan_table++; + pmadapter + ->pscan_table + [pmadapter->num_in_scan_table - + 1] + .pbeacon_buf = MNULL; + wlan_ret_802_11_scan_store_beacon( + pmpriv, + pmadapter->num_in_scan_table - + 1, + pmadapter->num_in_scan_table, + bss_new_entry); + if (bss_new_entry->pbeacon_buf == MNULL) + pmadapter->num_in_scan_table--; + else + memcpy_ext( + pmadapter, + &pmadapter->pscan_table + [pmadapter->num_in_scan_table - + 1], + bss_new_entry, + sizeof(BSSDescriptor_t), + sizeof(BSSDescriptor_t)); + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)bss_new_entry); + } + } + } + } + + for (i = 0; i < pmadapter->num_in_scan_table; i++) { + PRINTM(MINFO, + "Scan:(%02d) " MACSTR ", " + "RSSI[%03d], SSID[%s]\n", + i, MAC2STR(pmadapter->pscan_table[i].mac_address), + (t_s32)pmadapter->pscan_table[i].rssi, + pmadapter->pscan_table[i].ssid.ssid); + pmadapter->pscan_table[i].chan_load = wlan_get_chan_load( + pmadapter, pmadapter->pscan_table[i].channel); + } + wlan_update_chan_rssi(pmadapter); + + /* + * Prepares domain info from scan table and downloads the + * domain info command to the FW. + */ + wlan_11d_prepare_dnld_domain_info_cmd(pmpriv); + PRINTM(MMSG, "wlan: SCAN COMPLETED: scanned AP count=%d\n", + pmadapter->num_in_scan_table); + LEAVE(); +} + +/** + * @brief Delete a specific indexed entry from the scan table. + * + * Delete the scan table entry indexed by table_idx. Compact the remaining + * entries and adjust any buffering of beacon/probe response data + * if needed. + * + * @param pmpriv A pointer to mlan_private structure + * @param table_idx Scan table entry index to delete from the table + * + * @return N/A + * + * @pre table_idx must be an index to a valid entry + */ +static t_void wlan_scan_delete_table_entry(mlan_private *pmpriv, + 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_ext(pmadapter, pmadapter->pscan_table + del_idx, + pmadapter->pscan_table + del_idx + 1, + sizeof(BSSDescriptor_t), 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].posen_ie) { + pmadapter->pscan_table[del_idx].posen_ie = + (IEEEtypes_Generic_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .osen_offset); + } + if (pmadapter->pscan_table[del_idx].pmd_ie) { + pmadapter->pscan_table[del_idx].pmd_ie = + (IEEEtypes_MobilityDomain_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .md_offset); + } + if (pmadapter->pscan_table[del_idx].pht_cap) { + pmadapter->pscan_table[del_idx].pht_cap = + (IEEEtypes_HTCap_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .ht_cap_offset); + } + + if (pmadapter->pscan_table[del_idx].pht_info) { + pmadapter->pscan_table[del_idx].pht_info = + (IEEEtypes_HTInfo_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .ht_info_offset); + } + if (pmadapter->pscan_table[del_idx].pbss_co_2040) { + pmadapter->pscan_table[del_idx].pbss_co_2040 = + (IEEEtypes_2040BSSCo_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .bss_co_2040_offset); + } + if (pmadapter->pscan_table[del_idx].pext_cap) { + pmadapter->pscan_table[del_idx].pext_cap = + (IEEEtypes_ExtCap_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .ext_cap_offset); + } + if (pmadapter->pscan_table[del_idx] + .poverlap_bss_scan_param) { + pmadapter->pscan_table[del_idx] + .poverlap_bss_scan_param = + (IEEEtypes_OverlapBSSScanParam_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .overlap_bss_offset); + } + + if (pmadapter->pscan_table[del_idx].pvht_cap) { + pmadapter->pscan_table[del_idx].pvht_cap = + (IEEEtypes_VHTCap_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .vht_cap_offset); + } + + if (pmadapter->pscan_table[del_idx].pvht_oprat) { + pmadapter->pscan_table[del_idx].pvht_oprat = + (IEEEtypes_VHTOprat_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .vht_oprat_offset); + } + if (pmadapter->pscan_table[del_idx].pvht_txpower) { + pmadapter->pscan_table[del_idx].pvht_txpower = + (IEEEtypes_VHTtxpower_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .vht_txpower_offset); + } + if (pmadapter->pscan_table[del_idx].pext_pwer) { + pmadapter->pscan_table[del_idx].pext_pwer = + (IEEEtypes_ExtPwerCons_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .ext_pwer_offset); + } + if (pmadapter->pscan_table[del_idx].pext_bssload) { + pmadapter->pscan_table[del_idx].pext_bssload = + (IEEEtypes_ExtBSSload_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .ext_bssload_offset); + } + if (pmadapter->pscan_table[del_idx].pquiet_chan) { + pmadapter->pscan_table[del_idx].pquiet_chan = + (IEEEtypes_QuietChan_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .quiet_chan_offset); + } + if (pmadapter->pscan_table[del_idx].poper_mode) { + pmadapter->pscan_table[del_idx].poper_mode = + (IEEEtypes_OperModeNtf_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .oper_mode_offset); + } + if (pmadapter->pscan_table[del_idx].phe_cap) { + pmadapter->pscan_table[del_idx].phe_cap = + (IEEEtypes_HECap_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .he_cap_offset); + } + if (pmadapter->pscan_table[del_idx].phe_oprat) { + pmadapter->pscan_table[del_idx].phe_oprat = + (IEEEtypes_Extension_t + *)(pmadapter + ->pscan_table[del_idx] + .pbeacon_buf + + pmadapter + ->pscan_table[del_idx] + .he_oprat_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(mlan_private *pmpriv, + 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(mlan_private *pmpriv, t_u32 index, t_u32 mode) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + BSSDescriptor_t *pbss_desc; + + ENTER(); + + pbss_desc = &pmadapter->pscan_table[index]; + /* Don't check for compatibility if roaming */ + if ((pmpriv->media_connected == MTRUE) && + (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) && + (pbss_desc->bss_mode == MLAN_BSS_MODE_INFRA)) { + LEAVE(); + return index; + } + + pbss_desc->disable_11n = MFALSE; + + /* if the HE CAP IE exists, HT CAP IE should exist too */ + /* 2.4G AX AP, don't have VHT CAP */ + if (pbss_desc->phe_cap && !pbss_desc->pht_cap) { + PRINTM(MINFO, + "Disable 11n if VHT CAP/HT CAP IE is not found from the 11AX AP\n"); + pbss_desc->disable_11n = MTRUE; + } + + /* if the VHT CAP IE exists, the HT CAP IE should exist too */ + if (pbss_desc->pvht_cap && !pbss_desc->pht_cap) { + PRINTM(MINFO, + "Disable 11n if HT CAP IE is not found from the 11AC AP\n"); + pbss_desc->disable_11n = MTRUE; + } + + 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 (pmpriv->sec_info.osen_enabled && pbss_desc->posen_ie && + ((*(pbss_desc->posen_ie)).ieee_hdr.element_id == + VENDOR_SPECIFIC_221)) { + /* Hotspot 2.0 OSEN AKM */ + PRINTM(MMSG, + "Return success directly in Hotspot OSEN: index=%d " + "encryption_mode=%#x\n", + index, pmpriv->sec_info.encryption_mode); + LEAVE(); + return index; + } + + if ((pbss_desc->bss_mode == mode) && + (pmpriv->sec_info.ewpa_enabled == MTRUE +#ifdef DRV_EMBEDDED_SUPPLICANT + || supplicantIsEnabled(pmpriv->psapriv) +#endif + )) { + if (((pbss_desc->pwpa_ie) && + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE)) || + ((pbss_desc->prsn_ie) && + ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE))) { + if (((pmpriv->adapter->config_bands & BAND_GN || + pmpriv->adapter->config_bands & BAND_AN) && + pbss_desc->pht_cap) && + (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) && + !is_wpa_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP) && + !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP) && + !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_GCMP) && + !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_GCMP_256) && + !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP_256)) { + 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->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 && + pbss_desc->privacy) { + /* Static WEP enabled */ + PRINTM(MINFO, "Disable 11n in WEP mode\n"); + pbss_desc->disable_11n = MTRUE; + /* Reject the following cases: */ + /* + * case 1: RSN IE w/o WEP OUI and WPA IE w/o WEP OUI + * case 2: RSN IE w/o WEP OUI and No WPA IE + * case 3: WPA IE w/o WEP OUI and No RSN IE + */ + if (((pbss_desc->prsn_ie) && + ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == + RSN_IE)) || + ((pbss_desc->pwpa_ie) && + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == + WPA_IE))) { + if (!is_rsn_oui_present(pmpriv->adapter, + pbss_desc, + CIPHER_SUITE_WEP40) && + !is_rsn_oui_present(pmpriv->adapter, + pbss_desc, + CIPHER_SUITE_WEP104) && + !is_wpa_oui_present(pmpriv->adapter, + pbss_desc, + CIPHER_SUITE_WEP40) && + !is_wpa_oui_present(pmpriv->adapter, + pbss_desc, + CIPHER_SUITE_WEP104)) + index = -1; + } + + LEAVE(); + return index; + } else if (pmpriv->sec_info.wep_status == + Wlan802_11WEPDisabled && + pmpriv->sec_info.wpa_enabled && + !pmpriv->sec_info.wpa2_enabled && + ((pbss_desc->pwpa_ie) && + ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == + WPA_IE)) + /* + * Privacy bit may NOT be set in some APs like + * LinkSys WRT54G && pbss_desc->privacy + */ + ) { + 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)) + /* + * 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) && + !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_GCMP) && + !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_GCMP_256) && + !is_rsn_oui_present(pmpriv->adapter, pbss_desc, + CIPHER_SUITE_CCMP_256)) { + 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 if (is_rsn_oui_present_in_wpa_ie( + pmpriv, CIPHER_SUITE_CCMP) || + is_rsn_oui_present_in_wpa_ie( + pmpriv, CIPHER_SUITE_GCMP) || + is_rsn_oui_present_in_wpa_ie( + pmpriv, + CIPHER_SUITE_GCMP_256) || + is_rsn_oui_present_in_wpa_ie( + pmpriv, + CIPHER_SUITE_CCMP_256)) { + LEAVE(); + return index; + } 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->sec_info.encryption_mode != + MLAN_ENCRYPTION_MODE_NONE && + pbss_desc->privacy) { + /* Dynamic WEP enabled */ + pbss_desc->disable_11n = MTRUE; + PRINTM(MINFO, + "wlan_is_network_compatible() dynamic WEP: index=%d " + "wpa_ie=%#x rsn_ie=%#x EncMode=%#x privacy=%#x\n", + index, + (pbss_desc->pwpa_ie) ? + (*(pbss_desc->pwpa_ie)) + .vend_hdr.element_id : + 0, + (pbss_desc->prsn_ie) ? + (*(pbss_desc->prsn_ie)) + .ieee_hdr.element_id : + 0, + pmpriv->sec_info.encryption_mode, + pbss_desc->privacy); + LEAVE(); + return index; + } + /* Security doesn't match */ + PRINTM(MINFO, + "wlan_is_network_compatible() FAILED: index=%d wpa_ie=%#x " + "rsn_ie=%#x WEP=%s WPA=%s WPA2=%s EncMode=%#x privacy=%#x\n", + index, + (pbss_desc->pwpa_ie) ? + (*(pbss_desc->pwpa_ie)).vend_hdr.element_id : + 0, + (pbss_desc->prsn_ie) ? + (*(pbss_desc->prsn_ie)).ieee_hdr.element_id : + 0, + (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled) ? + "e" : + "d", + (pmpriv->sec_info.wpa_enabled) ? "e" : "d", + (pmpriv->sec_info.wpa2_enabled) ? "e" : "d", + pmpriv->sec_info.encryption_mode, pbss_desc->privacy); + LEAVE(); + return -1; + } + /* Mode doesn't match */ + LEAVE(); + return -1; +} + +/** + * @brief Internal function used to flush the scan list + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_flush_scan_table(pmlan_adapter pmadapter) +{ + t_u8 i = 0; + ENTER(); + + PRINTM(MINFO, "Flushing scan table\n"); + + memset(pmadapter, pmadapter->pscan_table, 0, + (sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST)); + pmadapter->num_in_scan_table = 0; + + memset(pmadapter, pmadapter->bcn_buf, 0, pmadapter->bcn_buf_size); + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + + for (i = 0; i < pmadapter->num_in_chan_stats; i++) + pmadapter->pchan_stats[i].cca_scan_duration = 0; + pmadapter->idx_chan_stats = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Internal function used to start a scan based on an input config + * + * Use the input user scan configuration information when provided in + * order to send the appropriate scan commands to firmware to populate or + * update the internal driver scan table + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param puser_scan_in Pointer to the input configuration for the requested + * scan. + * + * @return MLAN_STATUS_SUCCESS or < 0 if error + */ +mlan_status wlan_scan_networks(mlan_private *pmpriv, t_void *pioctl_buf, + wlan_user_scan_cfg *puser_scan_in) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + cmd_ctrl_node *pcmd_node = MNULL; + pmlan_ioctl_req pioctl_req = (mlan_ioctl_req *)pioctl_buf; + + wlan_scan_cmd_config_tlv *pscan_cfg_out = MNULL; + MrvlIEtypes_ChanListParamSet_t *pchan_list_out; + t_u32 buf_size; + ChanScanParamSet_t *pscan_chan_list; + + t_u8 keep_previous_scan; + t_u8 filtered_scan; + t_u8 scan_current_chan_only; + t_u8 max_chan_per_scan; + t_u8 i; + + ENTER(); + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(wlan_scan_cmd_config_tlv), MLAN_MEM_DEF, + (t_u8 **)&pscan_cfg_out); + if (ret != MLAN_STATUS_SUCCESS || !pscan_cfg_out) { + PRINTM(MERROR, "Memory allocation for pscan_cfg_out failed!\n"); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + buf_size = sizeof(ChanScanParamSet_t) * WLAN_USER_SCAN_CHAN_MAX; + ret = pcb->moal_malloc(pmadapter->pmoal_handle, buf_size, MLAN_MEM_DEF, + (t_u8 **)&pscan_chan_list); + if (ret != MLAN_STATUS_SUCCESS || !pscan_chan_list) { + PRINTM(MERROR, "Failed to allocate scan_chan_list\n"); + if (pscan_cfg_out) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pscan_cfg_out); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(pmadapter, pscan_chan_list, 0x00, buf_size); + memset(pmadapter, pscan_cfg_out, 0x00, + sizeof(wlan_scan_cmd_config_tlv)); + + keep_previous_scan = MFALSE; + + ret = wlan_scan_setup_scan_config(pmpriv, puser_scan_in, + &pscan_cfg_out->config, + &pchan_list_out, pscan_chan_list, + &max_chan_per_scan, &filtered_scan, + &scan_current_chan_only); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to setup scan config\n"); + if (pscan_cfg_out) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pscan_cfg_out); + if (pscan_chan_list) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pscan_chan_list); + if (pioctl_req) + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (puser_scan_in) + keep_previous_scan = puser_scan_in->keep_previous_scan; + + if (keep_previous_scan == MFALSE) { + memset(pmadapter, pmadapter->pscan_table, 0x00, + sizeof(BSSDescriptor_t) * MRVDRV_MAX_BSSID_LIST); + pmadapter->num_in_scan_table = 0; + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + } + for (i = 0; i < pmadapter->num_in_chan_stats; i++) + pmadapter->pchan_stats[i].cca_scan_duration = 0; + pmadapter->idx_chan_stats = 0; + + ret = wlan_scan_channel_list(pmpriv, pioctl_buf, max_chan_per_scan, + filtered_scan, &pscan_cfg_out->config, + pchan_list_out, pscan_chan_list); + + /* Get scan command from scan_pending_q and put to cmd_pending_q */ + if (ret == MLAN_STATUS_SUCCESS) { + if (pmadapter->ext_scan && pmadapter->ext_scan_enh && + pmadapter->ext_scan_type == EXT_SCAN_ENHANCE) { + wlan_request_cmd_lock(pmadapter); + pmadapter->pscan_ioctl_req = pioctl_req; + pmadapter->scan_processing = MTRUE; + wlan_release_cmd_lock(pmadapter); + } else { + wlan_request_cmd_lock(pmadapter); + if (util_peek_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, MNULL, + MNULL)) { + pcmd_node = (cmd_ctrl_node *)util_dequeue_list( + pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, MNULL, + MNULL); + pmadapter->pscan_ioctl_req = pioctl_req; + pmadapter->scan_processing = MTRUE; + wlan_insert_cmd_to_pending_q(pmadapter, + pcmd_node, MTRUE); + } + wlan_release_cmd_lock(pmadapter); + } + } + if (pscan_cfg_out) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)pscan_cfg_out); + + if (pscan_chan_list) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pscan_chan_list); + + LEAVE(); + return ret; +} + +/** + * @brief Prepare a scan command to be sent to the firmware + * + * Use the wlan_scan_cmd_config sent to the command processing module in + * the wlan_prepare_cmd to configure a HostCmd_DS_802_11_SCAN command + * struct to send to firmware. + * + * The fixed fields specifying the BSS type and BSSID filters as well as a + * variable number/length of TLVs are sent in the command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure to be sent to + * firmware with the HostCmd_DS_801_11_SCAN structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_802_11_scan(mlan_private *pmpriv, HostCmd_DS_COMMAND *pcmd, + 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_ext(pmpriv->adapter, pscan_cmd->bssid, pscan_cfg->specific_bssid, + sizeof(pscan_cmd->bssid), sizeof(pscan_cmd->bssid)); + memcpy_ext(pmpriv->adapter, pscan_cmd->tlv_buffer, pscan_cfg->tlv_buf, + pscan_cfg->tlv_buf_len, pscan_cfg->tlv_buf_len); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + pcmd->size = (t_u16)wlan_cpu_to_le16( + (t_u16)(sizeof(pscan_cmd->bss_mode) + sizeof(pscan_cmd->bssid) + + pscan_cfg->tlv_buf_len + S_DS_GEN)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Check if any hidden SSID found in passive scan channels + * and do specific SSID active scan for those channels + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MTRUE/MFALSE + */ + +t_bool wlan_active_scan_req_for_passive_chan(mlan_private *pmpriv, + mlan_ioctl_req *pioctl_buf) +{ + t_bool ret = MFALSE; + mlan_adapter *pmadapter = pmpriv->adapter; + t_bool scan_reqd = MFALSE; + t_bool chan_listed = MFALSE; + t_u8 id = 0; + t_u32 bss_idx, i; + t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = {0}; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + wlan_user_scan_cfg *user_scan_cfg; + mlan_ds_scan *pscan = (mlan_ds_scan *)pioctl_buf->pbuf; + mlan_scan_req *pscan_req = MNULL; + wlan_user_scan_cfg *puser_scan_in = MNULL; + + ENTER(); + + if (pscan->sub_command == MLAN_OID_SCAN_USER_CONFIG) { + puser_scan_in = (wlan_user_scan_cfg *) + pscan->param.user_scan.scan_cfg_buf; + if (!puser_scan_in->ssid_filter) + goto done; + } + + if (pmadapter->active_scan_triggered) { + pmadapter->active_scan_triggered = MFALSE; + goto done; + } + + if ((pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(wlan_user_scan_cfg), MLAN_MEM_DEF, + (t_u8 **)&user_scan_cfg) != + MLAN_STATUS_SUCCESS) || + !user_scan_cfg) { + PRINTM(MERROR, "Memory allocation for user_scan_cfg failed\n"); + goto done; + } + memset(pmadapter, user_scan_cfg, 0, sizeof(wlan_user_scan_cfg)); + for (bss_idx = 0; bss_idx < pmadapter->num_in_scan_table; bss_idx++) { + scan_reqd = MFALSE; + if (!memcmp(pmadapter, + pmadapter->pscan_table[bss_idx].ssid.ssid, + null_ssid, + pmadapter->pscan_table[bss_idx].ssid.ssid_len)) { + if (puser_scan_in && + puser_scan_in->chan_list[0].chan_number) { + for (i = 0; + i < WLAN_USER_SCAN_CHAN_MAX && + puser_scan_in->chan_list[i].chan_number; + i++) { + if (puser_scan_in->chan_list[i] + .chan_number == + pmadapter->pscan_table[bss_idx] + .channel) { + if (puser_scan_in->chan_list[i] + .scan_type == + MLAN_SCAN_TYPE_PASSIVE) + scan_reqd = MTRUE; + break; + } + } + } else if (pmadapter->scan_type == + MLAN_SCAN_TYPE_PASSIVE) { + scan_reqd = MTRUE; + } else { + if ((pmadapter->pscan_table[bss_idx].bss_band & + BAND_A) && + wlan_11h_radar_detect_required( + pmpriv, + pmadapter->pscan_table[bss_idx] + .channel)) + scan_reqd = MTRUE; + if (pmadapter->pscan_table[bss_idx].bss_band & + (BAND_B | BAND_G) && + wlan_bg_scan_type_is_passive( + pmpriv, + pmadapter->pscan_table[bss_idx] + .channel)) + scan_reqd = MTRUE; + } + + if (scan_reqd) { + chan_listed = MFALSE; + for (i = 0; i < id; i++) { + if ((user_scan_cfg->chan_list[i] + .chan_number == + pmadapter->pscan_table[bss_idx] + .channel) && + (user_scan_cfg->chan_list[i] + .radio_type & + pmadapter->pscan_table[bss_idx] + .bss_band)) { + chan_listed = MTRUE; + break; + } + } + if (chan_listed == MTRUE) + continue; + user_scan_cfg->chan_list[id].chan_number = + pmadapter->pscan_table[bss_idx].channel; + if (pmadapter->pscan_table[bss_idx].bss_band & + (BAND_B | BAND_G)) + user_scan_cfg->chan_list[id].radio_type = + BAND_2GHZ; + if (pmadapter->pscan_table[bss_idx].bss_band & + BAND_A) + user_scan_cfg->chan_list[id].radio_type = + BAND_5GHZ; + user_scan_cfg->chan_list[id].scan_type = + MLAN_SCAN_TYPE_ACTIVE; + id++; + } + } + } + if (id) { + pmadapter->active_scan_triggered = MTRUE; + if (pscan->sub_command == MLAN_OID_SCAN_USER_CONFIG) { + memcpy_ext(pmpriv->adapter, user_scan_cfg->ssid_list, + puser_scan_in->ssid_list, + sizeof(user_scan_cfg->ssid_list), + sizeof(user_scan_cfg->ssid_list)); + } else { + pscan_req = &pscan->param.scan_req; + memcpy_ext(pmpriv->adapter, + user_scan_cfg->ssid_list[0].ssid, + pscan_req->scan_ssid.ssid, + pscan_req->scan_ssid.ssid_len, + MLAN_MAX_SSID_LENGTH); + } + user_scan_cfg->keep_previous_scan = MTRUE; + if (MLAN_STATUS_SUCCESS != + wlan_scan_networks(pmpriv, pioctl_buf, user_scan_cfg)) { + goto done; + } + ret = MTRUE; + } + if (user_scan_cfg) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)user_scan_cfg); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of scan + * + * The response buffer for the scan command has the following + * memory layout: + * + * .-------------------------------------------------------------. + * | Header (4 * sizeof(t_u16)): Standard command response hdr | + * .-------------------------------------------------------------. + * | BufSize (t_u16) : sizeof the BSS Description data | + * .-------------------------------------------------------------. + * | NumOfSet (t_u8) : Number of BSS Descs returned | + * .-------------------------------------------------------------. + * | BSSDescription data (variable, size given in BufSize) | + * .-------------------------------------------------------------. + * | TLV data (variable, size calculated using Header->Size, | + * | BufSize and sizeof the fixed fields above) | + * .-------------------------------------------------------------. + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ret_802_11_scan(mlan_private *pmpriv, HostCmd_DS_COMMAND *resp, + t_void *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = MNULL; + cmd_ctrl_node *pcmd_node = MNULL; + HostCmd_DS_802_11_SCAN_RSP *pscan_rsp = MNULL; + BSSDescriptor_t *bss_new_entry = MNULL; + MrvlIEtypes_Data_t *ptlv; + MrvlIEtypes_TsfTimestamp_t *ptsf_tlv = MNULL; + MrvlIEtypes_ChannelStats_t *pchanstats_tlv = MNULL; + t_u8 *pbss_info; + t_u32 scan_resp_size; + t_u32 bytes_left; + t_u32 num_in_table; + t_u32 bss_idx; + t_u32 idx; + t_u32 tlv_buf_size; + t_u64 tsf_val; + chan_freq_power_t *cfp; + MrvlIEtypes_ChanBandListParamSet_t *pchan_band_tlv = MNULL; + ChanBandParamSet_t *pchan_band; + t_u8 band; + t_u8 is_bgscan_resp; + t_u32 age_ts_usec; + t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = {0}; + t_u32 status_code = 0; + pmlan_ioctl_req pscan_ioctl_req = MNULL; + + ENTER(); + pcb = (pmlan_callbacks)&pmadapter->callbacks; + + is_bgscan_resp = (resp->command == HostCmd_CMD_802_11_BG_SCAN_QUERY); + if (is_bgscan_resp) + pscan_rsp = &resp->params.bg_scan_query_resp.scan_resp; + else + pscan_rsp = &resp->params.scan_resp; + + if (pscan_rsp->number_of_sets > MRVDRV_MAX_BSSID_LIST) { + PRINTM(MERROR, + "SCAN_RESP: Invalid number of AP returned (%d)!!\n", + pscan_rsp->number_of_sets); + status_code = MLAN_ERROR_CMD_SCAN_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bytes_left = wlan_le16_to_cpu(pscan_rsp->bss_descript_size); + PRINTM(MINFO, "SCAN_RESP: bss_descript_size %d\n", bytes_left); + + scan_resp_size = resp->size; + + PRINTM(MINFO, "SCAN_RESP: returned %d APs before parsing\n", + pscan_rsp->number_of_sets); + + num_in_table = pmadapter->num_in_scan_table; + pbss_info = pscan_rsp->bss_desc_and_tlv_buffer; + + /* + * The size of the TLV buffer is equal to the entire command response + * size (scan_resp_size) minus the fixed fields (sizeof()'s), the + * BSS Descriptions (bss_descript_size as bytesLef) and the command + * response header (S_DS_GEN) + */ + tlv_buf_size = scan_resp_size - + (bytes_left + sizeof(pscan_rsp->bss_descript_size) + + sizeof(pscan_rsp->number_of_sets) + S_DS_GEN); + if (is_bgscan_resp) + tlv_buf_size -= sizeof( + resp->params.bg_scan_query_resp.report_condition); + + ptlv = (MrvlIEtypes_Data_t *)(pscan_rsp->bss_desc_and_tlv_buffer + + bytes_left); + + /* + * Search the TLV buffer space in the scan response + * for any valid TLVs + */ + wlan_ret_802_11_scan_get_tlv_ptrs(pmadapter, ptlv, tlv_buf_size, + TLV_TYPE_TSFTIMESTAMP, + (MrvlIEtypes_Data_t **)&ptsf_tlv); + + /* + * Search the TLV buffer space in the scan response + * for any valid TLVs + */ + wlan_ret_802_11_scan_get_tlv_ptrs( + pmadapter, ptlv, tlv_buf_size, TLV_TYPE_CHANNELBANDLIST, + (MrvlIEtypes_Data_t **)&pchan_band_tlv); + wlan_ret_802_11_scan_get_tlv_ptrs( + pmadapter, ptlv, tlv_buf_size, TLV_TYPE_CHANNEL_STATS, + (MrvlIEtypes_Data_t **)&pchanstats_tlv); + + if (pchanstats_tlv) + wlan_update_chan_statistics(pmpriv, pchanstats_tlv); + + /* + * Process each scan response returned (pscan_rsp->number_of_sets). + * Save the information in the bss_new_entry and then insert into + * the driver scan table either as an update to an existing entry + * or as an addition at the end of the table + */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, sizeof(BSSDescriptor_t), + MLAN_MEM_DEF, (t_u8 **)&bss_new_entry); + + if (ret != MLAN_STATUS_SUCCESS || !bss_new_entry) { + PRINTM(MERROR, "Memory allocation for bss_new_entry failed!\n"); + status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + for (idx = 0; idx < pscan_rsp->number_of_sets && bytes_left; idx++) { + /* Zero out the bss_new_entry we are about to store info in */ + memset(pmadapter, bss_new_entry, 0x00, sizeof(BSSDescriptor_t)); + + /* Process the data fields and IEs returned for this BSS */ + if (wlan_interpret_bss_desc_with_ie( + pmadapter, bss_new_entry, &pbss_info, &bytes_left, + MFALSE) == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "SCAN_RESP: BSSID = " MACSTR "\n", + MAC2STR(bss_new_entry->mac_address)); + + band = BAND_G; + if (pchan_band_tlv) { + pchan_band = + &pchan_band_tlv->chan_band_param[idx]; + band = radio_type_to_band( + pchan_band->bandcfg.chanBand); + if (!bss_new_entry->channel) + bss_new_entry->channel = + pchan_band->chan_number; + } + /* + * Save the band designation for this entry + * for use in join + */ + bss_new_entry->bss_band = band; + + cfp = wlan_find_cfp_by_band_and_channel( + pmadapter, (t_u8)bss_new_entry->bss_band, + (t_u16)bss_new_entry->channel); + if (cfp) + bss_new_entry->freq = cfp->freq; + else + bss_new_entry->freq = 0; + + /* Skip entry if on blacklisted channel */ + if (cfp && cfp->dynamic.blacklist) { + PRINTM(MINFO, + "SCAN_RESP: dropping entry on blacklist channel.\n"); + continue; + } + + /* + * Search the scan table for the same bssid + */ + for (bss_idx = 0; bss_idx < num_in_table; bss_idx++) { + if (!memcmp(pmadapter, + bss_new_entry->mac_address, + pmadapter->pscan_table[bss_idx] + .mac_address, + sizeof(bss_new_entry->mac_address))) { + /* + * If the SSID matches as well, it is a + * duplicate of this entry. Keep the + * bss_idx set to this entry so we + * replace the old contents in the table + */ + if ((bss_new_entry->ssid.ssid_len == + pmadapter->pscan_table[bss_idx] + .ssid.ssid_len) && + (!memcmp( + pmadapter, + bss_new_entry->ssid.ssid, + pmadapter + ->pscan_table[bss_idx] + .ssid.ssid, + bss_new_entry->ssid + .ssid_len))) { + PRINTM(MINFO, + "SCAN_RESP: Duplicate of index: %d\n", + bss_idx); + + break; + } + /* + * If the SSID is NULL for same BSSID + * keep the bss_idx set to this entry + * so we replace the old contents in + * the table + */ + if (!memcmp(pmadapter, + pmadapter + ->pscan_table[bss_idx] + .ssid.ssid, + null_ssid, + pmadapter + ->pscan_table[bss_idx] + .ssid.ssid_len)) { + PRINTM(MINFO, + "SCAN_RESP: Duplicate of index: %d\n", + bss_idx); + break; + } + } + } + /* + * If the bss_idx is equal to the number of entries + * in the table, the new entry was not a duplicate; + * append it to the scan table + */ + if (bss_idx == num_in_table) { + /* + * Range check the bss_idx, keep it limited + * to the last entry + */ + if (bss_idx == MRVDRV_MAX_BSSID_LIST) + bss_idx--; + else + num_in_table++; + } else { + if ((bss_new_entry->channel != + pmadapter->pscan_table[bss_idx].channel) && + (bss_new_entry->rssi > + pmadapter->pscan_table[bss_idx].rssi)) { + PRINTM(MCMND, + "skip update the duplicate entry with low rssi\n"); + continue; + } + } + /* + * 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_ext( + pmpriv->adapter, &tsf_val, + &ptsf_tlv->tsf_data[idx * TSF_DATA_SIZE], + sizeof(tsf_val), sizeof(tsf_val)); + tsf_val = wlan_le64_to_cpu(tsf_val); + memcpy_ext(pmpriv->adapter, + &bss_new_entry->network_tsf, + &tsf_val, + sizeof(bss_new_entry->network_tsf), + sizeof(bss_new_entry->network_tsf)); + } + + /* Copy the locally created bss_new_entry to the scan + * table */ + memcpy_ext(pmadapter, &pmadapter->pscan_table[bss_idx], + bss_new_entry, + sizeof(pmadapter->pscan_table[bss_idx]), + sizeof(pmadapter->pscan_table[bss_idx])); + + } else { + /* Error parsing/interpreting the scan response, skipped + */ + PRINTM(MERROR, + "SCAN_RESP: wlan_interpret_bss_desc_with_ie returned error\n"); + } + } + + PRINTM(MINFO, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", + pscan_rsp->number_of_sets, + num_in_table - pmadapter->num_in_scan_table, num_in_table); + + /* Update the total number of BSSIDs in the scan table */ + pmadapter->num_in_scan_table = num_in_table; + /* Update the age_in_second */ + pmadapter->callbacks.moal_get_system_time( + pmadapter->pmoal_handle, &pmadapter->age_in_secs, &age_ts_usec); + if (is_bgscan_resp) + goto done; + wlan_request_cmd_lock(pmadapter); + if (!util_peek_list(pmadapter->pmoal_handle, &pmadapter->scan_pending_q, + MNULL, MNULL)) { + wlan_release_cmd_lock(pmadapter); + if (pmadapter->pscan_ioctl_req) { + if (((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf) + ->sub_command == + MLAN_OID_SCAN_SPECIFIC_SSID || + ((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf) + ->sub_command == + MLAN_OID_SCAN_USER_CONFIG) { + if (wlan_active_scan_req_for_passive_chan( + pmpriv, + pmadapter->pscan_ioctl_req)) { + goto done; + } + } + } + /* + * Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(pmpriv); + + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pscan_ioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Need to indicate IOCTL complete */ + if (pscan_ioctl_req) { + pscan_ioctl_req->status_code = MLAN_ERROR_NO_ERROR; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete( + pmadapter->pmoal_handle, + (pmlan_ioctl_req)pscan_ioctl_req, + MLAN_STATUS_SUCCESS); + } + wlan_release_cmd_lock(pmadapter); + pmadapter->bgscan_reported = MFALSE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); + } else { + /* If firmware not ready, do not issue any more scan commands */ + if (pmadapter->hw_status != WlanHardwareStatusReady) { + wlan_release_cmd_lock(pmadapter); + status_code = MLAN_ERROR_FW_NOT_READY; + ret = MLAN_STATUS_FAILURE; + goto done; + } else { + /* Get scan command from scan_pending_q and put to + * cmd_pending_q */ + pcmd_node = (cmd_ctrl_node *)util_dequeue_list( + pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, MNULL, MNULL); + wlan_insert_cmd_to_pending_q(pmadapter, pcmd_node, + MTRUE); + wlan_release_cmd_lock(pmadapter); + } + } + +done: + if (bss_new_entry) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)bss_new_entry); + if (ret) { + /* Flush all pending scan commands */ + wlan_flush_scan_queue(pmadapter); + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pscan_ioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + if (pscan_ioctl_req) { + pscan_ioctl_req->status_code = status_code; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pscan_ioctl_req, + MLAN_STATUS_FAILURE); + } + wlan_release_cmd_lock(pmadapter); + } + LEAVE(); + return ret; +} + +/** + * @brief Prepare an extended scan command to be sent to the firmware + * + * Use the wlan_scan_cmd_config sent to the command processing module in + * the wlan_prepare_cmd to configure a HostCmd_DS_802_11_SCAN_EXT command + * struct to send to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure to be sent to + * firmware with the HostCmd_DS_802_11_SCAN_EXT structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_802_11_scan_ext(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd, + t_void *pdata_buf) +{ + HostCmd_DS_802_11_SCAN_EXT *pext_scan_cmd = &pcmd->params.ext_scan; + wlan_scan_cmd_config *pscan_cfg = MNULL; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); + + if (pmpriv->adapter->ext_scan_enh == MTRUE) { + if (pdata_buf) { + if (pmpriv->adapter->ext_scan_type == EXT_SCAN_ENHANCE) + pext_scan_cmd->ext_scan_type = EXT_SCAN_ENHANCE; + else + pext_scan_cmd->ext_scan_type = EXT_SCAN_DEFAULT; + } else { + pcmd->size = wlan_cpu_to_le16((t_u16)( + sizeof(pext_scan_cmd->ext_scan_type) + + (t_u16)(sizeof(pext_scan_cmd->reserved)) + + S_DS_GEN)); + pext_scan_cmd->ext_scan_type = EXT_SCAN_CANCEL; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + pscan_cfg = (wlan_scan_cmd_config *)pdata_buf; + + memcpy_ext(pmpriv->adapter, pext_scan_cmd->tlv_buffer, + pscan_cfg->tlv_buf, pscan_cfg->tlv_buf_len, + pscan_cfg->tlv_buf_len); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + pcmd->size = wlan_cpu_to_le16( + (t_u16)(sizeof(pext_scan_cmd->ext_scan_type) + + (t_u16)sizeof(pext_scan_cmd->reserved) + + pscan_cfg->tlv_buf_len + S_DS_GEN)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of extended scan + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ret_802_11_scan_ext(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + t_void *pioctl_buf) +{ + HostCmd_DS_802_11_SCAN_EXT *pext_scan_cmd = &(resp->params.ext_scan); + MrvlIEtypesHeader_t *tlv = MNULL; + MrvlIEtypes_ChannelStats_t *tlv_chanstats = MNULL; + t_u16 tlv_buf_left = 0; + t_u16 tlv_type = 0; + t_u16 tlv_len = 0; + t_u32 ext_scan_type; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + pmlan_ioctl_req pioctl_req = (pmlan_ioctl_req)pioctl_buf; + mlan_adapter *pmadapter = pmpriv->adapter; + ENTER(); + + PRINTM(MINFO, "EXT scan returns successfully\n"); + ext_scan_type = pext_scan_cmd->ext_scan_type; + if (ext_scan_type == EXT_SCAN_CANCEL) { + PRINTM(MCMND, "Cancel scan command completed!\n"); + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pmadapter->ext_scan_type = EXT_SCAN_DEFAULT; + wlan_release_cmd_lock(pmadapter); + /* Need to indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = MLAN_STATUS_SUCCESS; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + (pmlan_ioctl_req)pioctl_req, + MLAN_STATUS_SUCCESS); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; + } else if (ext_scan_type == EXT_SCAN_ENHANCE) { + /* Setup the timer after scan command response */ + pcb->moal_start_timer(pmpriv->adapter->pmoal_handle, + pmpriv->adapter->pmlan_cmd_timer, MFALSE, + MRVDRV_TIMER_10S * 2); + pmpriv->adapter->cmd_timer_is_set = MTRUE; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + tlv = (MrvlIEtypesHeader_t *)pext_scan_cmd->tlv_buffer; + tlv_buf_left = resp->size - + (sizeof(HostCmd_DS_802_11_SCAN_EXT) - 1 + S_DS_GEN); + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, "Error processing scan gap TLV\n"); + break; + } + switch (tlv_type) { + case TLV_TYPE_CHANNEL_STATS: + tlv_chanstats = (MrvlIEtypes_ChannelStats_t *)tlv; + wlan_update_chan_statistics(pmpriv, tlv_chanstats); + break; + default: + break; + } + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function add a new entry to scan_table + * + * @param pmpriv A pointer to mlan_private structure + * @param bss_new_entry A pointer to the bss_new_entry + * @param num_in_tbl number of scan entry in scan_table + * + * @return N/A + */ +t_void wlan_add_new_entry_to_scan_table(mlan_private *pmpriv, + BSSDescriptor_t *bss_new_entry, + t_u32 *num_in_tbl) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u32 bss_idx; + t_u32 num_in_table = *num_in_tbl; + t_u8 null_ssid[MLAN_MAX_SSID_LENGTH] = {0}; + + /* + * Search the scan table for the same bssid + */ + for (bss_idx = 0; bss_idx < num_in_table; bss_idx++) { + if (!memcmp(pmadapter, bss_new_entry->mac_address, + pmadapter->pscan_table[bss_idx].mac_address, + sizeof(bss_new_entry->mac_address))) { + /* + * If the SSID matches as well, it is a + * duplicate of this entry. Keep the + * bss_idx set to this entry so we + * replace the old contents in the table + */ + if ((bss_new_entry->ssid.ssid_len == + pmadapter->pscan_table[bss_idx].ssid.ssid_len) && + (!memcmp(pmadapter, bss_new_entry->ssid.ssid, + pmadapter->pscan_table[bss_idx].ssid.ssid, + bss_new_entry->ssid.ssid_len))) { + PRINTM(MINFO, + "EXT_SCAN: Duplicate of index: %d\n", + bss_idx); + break; + } + /* + * If the SSID is NULL for same BSSID + * keep the bss_idx set to this entry + * so we replace the old contents in + * the table + */ + if (!memcmp(pmadapter, + pmadapter->pscan_table[bss_idx].ssid.ssid, + null_ssid, + pmadapter->pscan_table[bss_idx] + .ssid.ssid_len)) { + PRINTM(MINFO, + "EXT_SCAN: Duplicate of index: %d\n", + bss_idx); + break; + } + } + } + /* If the bss_idx is equal to the number of entries + * in the table, the new entry was not a duplicate; + * append it to the scan table + */ + if (bss_idx == num_in_table) { + /* Range check the bss_idx, keep it limited to the last entry */ + if (bss_idx == MRVDRV_MAX_BSSID_LIST) + bss_idx--; + else + num_in_table++; + } else { + if ((bss_new_entry->channel != + pmadapter->pscan_table[bss_idx].channel) && + (bss_new_entry->rssi > + pmadapter->pscan_table[bss_idx].rssi)) { + PRINTM(MCMND, + "skip update the duplicate entry with low rssi\n"); + return; + } + } + /* + * 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--; + goto done; + } else { + /* Copy the locally created bss_new_entry to the scan table */ + memcpy_ext(pmadapter, &pmadapter->pscan_table[bss_idx], + bss_new_entry, + sizeof(pmadapter->pscan_table[bss_idx]), + sizeof(pmadapter->pscan_table[bss_idx])); + } +done: + *num_in_tbl = num_in_table; + return; +} + +/** + * @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(mlan_private *pmpriv, + t_u8 number_of_sets, + t_u8 *pscan_resp, + 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 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, + MTRUE) == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "EXT_SCAN: BSSID = " MACSTR "\n", + MAC2STR(bss_new_entry->mac_address)); + + band = BAND_G; + /* + * If the BSS info TLV was appended to the scan results, + * save this entry's TSF value in the networkTSF field. + * The networkTSF is the firmware's TSF value at the + * time the beacon or probe response was received. + */ + if (pscan_info_tlv) { + /* RSSI is 2 byte long */ + bss_new_entry->rssi = -(t_s32)( + wlan_le16_to_cpu(pscan_info_tlv->rssi)); + PRINTM(MINFO, "EXT_SCAN: RSSI=%d\n", + bss_new_entry->rssi); + memcpy_ext(pmpriv->adapter, &tsf_val, + &pscan_info_tlv->tsf, + sizeof(tsf_val), sizeof(tsf_val)); + tsf_val = wlan_le64_to_cpu(tsf_val); + memcpy_ext(pmpriv->adapter, + &bss_new_entry->network_tsf, + &tsf_val, + sizeof(bss_new_entry->network_tsf), + sizeof(bss_new_entry->network_tsf)); + band = radio_type_to_band( + pscan_info_tlv->bandcfg.chanBand); + if (!bss_new_entry->channel) + bss_new_entry->channel = + pscan_info_tlv->channel; + } + /* Save the band designation for this entry for use in + * join */ + bss_new_entry->bss_band = band; + + cfp = wlan_find_cfp_by_band_and_channel( + pmadapter, (t_u8)bss_new_entry->bss_band, + (t_u16)bss_new_entry->channel); + if (cfp) + bss_new_entry->freq = cfp->freq; + else + bss_new_entry->freq = 0; + + /* Skip entry if on blacklisted channel */ + if (cfp && cfp->dynamic.blacklist) { + PRINTM(MINFO, + "EXT_SCAN: dropping entry on blacklist channel.\n"); + continue; + } + wlan_add_new_entry_to_scan_table(pmpriv, bss_new_entry, + &num_in_table); + + } else { + /* Error parsing/interpreting the scan response, skipped + */ + PRINTM(MERROR, + "EXT_SCAN: wlan_interpret_bss_desc_with_ie returned error\n"); + } + } + + PRINTM(MCMND, "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(mlan_private *pmpriv, + mlan_buffer *pmbuf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = &pmadapter->callbacks; + mlan_ioctl_req *pioctl_req = MNULL; + cmd_ctrl_node *pcmd_node = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + mlan_event_scan_result *pevent_scan = + (pmlan_event_scan_result)(pmbuf->pbuf + pmbuf->data_offset); + t_u8 *ptlv = (pmbuf->pbuf + pmbuf->data_offset + + sizeof(mlan_event_scan_result)); + t_u16 tlv_buf_left = wlan_le16_to_cpu(pevent_scan->buf_size); + + DBG_HEXDUMP(MCMD_D, "EVENT EXT_SCAN", pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + wlan_parse_ext_scan_result(pmpriv, pevent_scan->num_of_set, ptlv, + tlv_buf_left); + if (!pevent_scan->more_event && + (pmadapter->ext_scan_type != EXT_SCAN_ENHANCE)) { + wlan_request_cmd_lock(pmadapter); + if (!util_peek_list(pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, MNULL, MNULL)) { + wlan_release_cmd_lock(pmadapter); + if (pmadapter->pscan_ioctl_req) { + if (((mlan_ds_scan *) + pmadapter->pscan_ioctl_req->pbuf) + ->sub_command == + MLAN_OID_SCAN_SPECIFIC_SSID || + ((mlan_ds_scan *) + pmadapter->pscan_ioctl_req->pbuf) + ->sub_command == + MLAN_OID_SCAN_USER_CONFIG) { + if (wlan_active_scan_req_for_passive_chan( + pmpriv, + pmadapter->pscan_ioctl_req)) { + LEAVE(); + return ret; + } + } + } + /* + * Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(pmpriv); + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Need to indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = MLAN_ERROR_NO_ERROR; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete( + pmadapter->pmoal_handle, + (pmlan_ioctl_req)pioctl_req, + MLAN_STATUS_SUCCESS); + } + wlan_release_cmd_lock(pmadapter); + + pmadapter->bgscan_reported = MFALSE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, + MNULL); + } else { + /* If firmware not ready, do not issue any more scan + * commands */ + if (pmadapter->hw_status != WlanHardwareStatusReady) { + wlan_release_cmd_lock(pmadapter); + /* Flush all pending scan commands */ + wlan_flush_scan_queue(pmadapter); + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = + MLAN_ERROR_FW_NOT_READY; + + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete( + pmadapter->pmoal_handle, + (pmlan_ioctl_req)pioctl_req, + MLAN_STATUS_FAILURE); + } + wlan_release_cmd_lock(pmadapter); + } else { + /* Get scan command from scan_pending_q and put + * to cmd_pending_q */ + pcmd_node = (cmd_ctrl_node *)util_dequeue_list( + pmadapter->pmoal_handle, + &pmadapter->scan_pending_q, MNULL, + MNULL); + wlan_insert_cmd_to_pending_q(pmadapter, + pcmd_node, MTRUE); + wlan_release_cmd_lock(pmadapter); + } + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function handles the event extended scan status + * + * @param pmpriv A pointer to mlan_private structure + * @param pmbuf A pointer to mlan_buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_handle_event_ext_scan_status(mlan_private *pmpriv, + mlan_buffer *pmbuf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_event_scan_status *scan_event; + mlan_ioctl_req *pioctl_req; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + t_u16 tlv_buf_left, tlv_len, tlv_type; + MrvlIEtypesHeader_t *tlv; + MrvlIEtypes_ChannelStats_t *tlv_chan_stats; + t_u8 status; + + ENTER(); + + if (pmbuf->data_len < sizeof(mlan_event_scan_status)) { + PRINTM(MERROR, "Wrong ext scan status event data length\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + scan_event = + (pmlan_event_scan_status)(pmbuf->pbuf + pmbuf->data_offset); + DBG_HEXDUMP(MCMD_D, "EVENT: Ext_Scan_Status", scan_event, + pmbuf->data_len); + status = scan_event->scan_status; + PRINTM(MEVENT, "ext_scan_status: status %d (scan %s), buf_len %d\n", + status, status ? "cancelled" : "success", scan_event->buf_len); + + tlv = (MrvlIEtypesHeader_t *)scan_event->event_buf; + tlv_buf_left = pmbuf->data_len - sizeof(mlan_event_scan_status); + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if (tlv_buf_left < (tlv_len + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, + "Error process scan gap tlv: length %d type 0x%x\n", + tlv_len, tlv_type); + ret = MLAN_STATUS_FAILURE; + goto done; + } + switch (tlv_type) { + case TLV_TYPE_CHANNEL_STATS: + tlv_chan_stats = (MrvlIEtypes_ChannelStats_t *)tlv; + wlan_update_chan_statistics(pmpriv, tlv_chan_stats); + break; + default: + break; + } + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + +done: + /* Now we got response from FW, cancel the command timer */ + if (!pmadapter->curr_cmd && pmadapter->cmd_timer_is_set) { + /* Cancel command timeout timer */ + pcb->moal_stop_timer(pmadapter->pmoal_handle, + pmadapter->pmlan_cmd_timer); + /* Cancel command timeout timer */ + pmadapter->cmd_timer_is_set = MFALSE; + } + if (pmadapter->pscan_ioctl_req) { + if (((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf) + ->sub_command == + MLAN_OID_SCAN_SPECIFIC_SSID || + ((mlan_ds_scan *)pmadapter->pscan_ioctl_req->pbuf) + ->sub_command == + MLAN_OID_SCAN_USER_CONFIG) { + if (wlan_active_scan_req_for_passive_chan( + pmpriv, pmadapter->pscan_ioctl_req)) { + LEAVE(); + return ret; + } + } + } + /* + * Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(pmpriv); + /** Complete scan ioctl */ + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pmadapter->ext_scan_type = EXT_SCAN_DEFAULT; + pioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Need to indicate IOCTL complete */ + if (pioctl_req != MNULL) { + pioctl_req->status_code = MLAN_ERROR_NO_ERROR; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_req, + MLAN_STATUS_SUCCESS); + } + wlan_release_cmd_lock(pmadapter); + pmadapter->bgscan_reported = MFALSE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of bg_scan_query. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_802_11_bg_scan_query(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd, + 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(mlan_private *pmpriv, + 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; + t_u8 band; + + 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 ((pbg_scan_in && + (pbg_scan_in->bss_type == MLAN_SCAN_MODE_IBSS)) || + pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) + band = pmadapter->adhoc_start_band; + else + band = pmpriv->config_bands; + if (!wlan_is_band_compatible(band, pscan_region->band)) + continue; + for (next_chan = 0; next_chan < pscan_region->num_cfp; + next_chan++, chan_idx++) { + if (chan_idx >= WLAN_BG_SCAN_CHAN_MAX) + break; + /* + * Set the default scan type to ACTIVE SCAN type, will + * later be changed to passive on a per channel basis + * if restricted by regulatory requirements (11d or 11h) + */ + scan_type = MLAN_SCAN_TYPE_ACTIVE; + cfp = pscan_region->pcfp + next_chan; + + switch (pscan_region->band) { + case BAND_A: + tlv_chan_list->chan_scan_param[chan_idx] + .bandcfg.chanBand = BAND_5GHZ; + /* Passive scan on DFS channels */ + if (wlan_11h_radar_detect_required( + pmpriv, (t_u8)cfp->channel)) + scan_type = MLAN_SCAN_TYPE_PASSIVE; + break; + case BAND_B: + case BAND_G: + if (wlan_bg_scan_type_is_passive( + pmpriv, (t_u8)cfp->channel)) + scan_type = MLAN_SCAN_TYPE_PASSIVE; + tlv_chan_list->chan_scan_param[chan_idx] + .bandcfg.chanBand = BAND_2GHZ; + break; + default: + tlv_chan_list->chan_scan_param[chan_idx] + .bandcfg.chanBand = BAND_2GHZ; + break; + } + + if (pbg_scan_in && + pbg_scan_in->chan_list[0].scan_time) { + tlv_chan_list->chan_scan_param[chan_idx] + .max_scan_time = wlan_cpu_to_le16( + (t_u16)pbg_scan_in->chan_list[0] + .scan_time); + tlv_chan_list->chan_scan_param[chan_idx] + .min_scan_time = wlan_cpu_to_le16( + (t_u16)pbg_scan_in->chan_list[0] + .scan_time); + } else if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + tlv_chan_list->chan_scan_param[chan_idx] + .max_scan_time = wlan_cpu_to_le16( + pmadapter->passive_scan_time); + tlv_chan_list->chan_scan_param[chan_idx] + .min_scan_time = wlan_cpu_to_le16( + pmadapter->passive_scan_time); + } else { + tlv_chan_list->chan_scan_param[chan_idx] + .max_scan_time = wlan_cpu_to_le16( + pmadapter->specific_scan_time); + tlv_chan_list->chan_scan_param[chan_idx] + .min_scan_time = wlan_cpu_to_le16( + pmadapter->specific_scan_time); + } + + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + tlv_chan_list->chan_scan_param[chan_idx] + .chan_scan_mode.passive_scan = MTRUE; + } else { + tlv_chan_list->chan_scan_param[chan_idx] + .chan_scan_mode.passive_scan = MFALSE; + } + + tlv_chan_list->chan_scan_param[chan_idx].chan_number = + (t_u8)cfp->channel; + } + } + + 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(mlan_private *pmpriv, + HostCmd_DS_COMMAND *pcmd, t_void *pdata_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_BG_SCAN_CONFIG *bg_scan = + &pcmd->params.bg_scan_config; + wlan_bgscan_cfg *bg_scan_in = (wlan_bgscan_cfg *)pdata_buf; + t_u16 cmd_size = 0; + MrvlIEtypes_NumProbes_t *pnum_probes_tlv = MNULL; + MrvlIEtypes_BeaconLowRssiThreshold_t *rssi_tlv = MNULL; + MrvlIEtypes_BeaconLowSnrThreshold_t *snr_tlv = MNULL; + MrvlIEtypes_WildCardSsIdParamSet_t *pwildcard_ssid_tlv = MNULL; + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = MNULL; + MrvlIEtypes_StartLater_t *tlv_start_later = MNULL; + MrvlIEtypes_RepeatCount_t *tlv_repeat = MNULL; + MrvlIEtypes_EESParamSet_t *tlv_ees_cfg = MNULL; + MrvlIEtype_EESNetworkCfg_t *tlv_ees_net_cfg = MNULL; + MrvlIEtypes_Cipher_t *tlv_ees_cipher = MNULL; + MrvlIEtypes_SsIdParamSet_t *tlv_ssid = MNULL; + MrvlIETypes_HTCap_t *pht_cap = MNULL; + MrvlIETypes_VHTCap_t *pvht_cap = MNULL; + MrvlIEtypes_Extension_t *phe_cap = MNULL; + t_u16 len = 0; + + t_u8 index; + 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; + t_u8 band; + const t_u8 zero_mac[6] = {0, 0, 0, 0, 0, 0}; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_CONFIG); + bg_scan->action = wlan_cpu_to_le16(bg_scan_in->action); + bg_scan->enable = bg_scan_in->enable; + bg_scan->bss_type = bg_scan_in->bss_type; + cmd_size = sizeof(HostCmd_DS_802_11_BG_SCAN_CONFIG) + S_DS_GEN; + if (bg_scan_in->scan_interval) + bg_scan->scan_interval = + wlan_cpu_to_le32(bg_scan_in->scan_interval); + else + bg_scan->scan_interval = + wlan_cpu_to_le32(DEFAULT_BGSCAN_INTERVAL); + bg_scan->report_condition = + wlan_cpu_to_le32(bg_scan_in->report_condition); + + if ((bg_scan_in->action == BG_SCAN_ACT_GET) || + (bg_scan_in->action == BG_SCAN_ACT_GET_PPS_UAPSD) || + (!bg_scan->enable)) + goto done; + + tlv = (t_u8 *)bg_scan + sizeof(HostCmd_DS_802_11_BG_SCAN_CONFIG); + num_probes = (bg_scan_in->num_probes ? bg_scan_in->num_probes : + pmadapter->scan_probes); + if (num_probes) { + pnum_probes_tlv = (MrvlIEtypes_NumProbes_t *)tlv; + pnum_probes_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_NUMPROBES); + pnum_probes_tlv->header.len = + wlan_cpu_to_le16(sizeof(pnum_probes_tlv->num_probes)); + pnum_probes_tlv->num_probes = + wlan_cpu_to_le16((t_u16)num_probes); + tlv += sizeof(MrvlIEtypes_NumProbes_t); + cmd_size += sizeof(MrvlIEtypes_NumProbes_t); + } + if (bg_scan_in->rssi_threshold) { + rssi_tlv = (MrvlIEtypes_BeaconLowRssiThreshold_t *)tlv; + rssi_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + rssi_tlv->value = bg_scan_in->rssi_threshold; + rssi_tlv->frequency = 0; + tlv += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + } + if (bg_scan_in->snr_threshold) { + snr_tlv = (MrvlIEtypes_BeaconLowSnrThreshold_t *)tlv; + snr_tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_SNR_LOW); + snr_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_BeaconLowSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + snr_tlv->value = bg_scan_in->snr_threshold; + snr_tlv->frequency = 0; + tlv += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + } + if (bg_scan_in->repeat_count) { + tlv_repeat = (MrvlIEtypes_RepeatCount_t *)tlv; + tlv_repeat->header.type = + wlan_cpu_to_le16(TLV_TYPE_REPEAT_COUNT); + tlv_repeat->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_RepeatCount_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_repeat->repeat_count = + wlan_cpu_to_le16(bg_scan_in->repeat_count); + tlv += sizeof(MrvlIEtypes_RepeatCount_t); + cmd_size += sizeof(MrvlIEtypes_RepeatCount_t); + } + for (ssid_idx = 0; ((ssid_idx < NELEMENTS(bg_scan_in->ssid_list)) && + (*bg_scan_in->ssid_list[ssid_idx].ssid || + bg_scan_in->ssid_list[ssid_idx].max_len)); + ssid_idx++) { + ssid_len = wlan_strlen( + (char *)bg_scan_in->ssid_list[ssid_idx].ssid); + pwildcard_ssid_tlv = (MrvlIEtypes_WildCardSsIdParamSet_t *)tlv; + pwildcard_ssid_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WILDCARDSSID); + pwildcard_ssid_tlv->header.len = (t_u16)( + ssid_len + sizeof(pwildcard_ssid_tlv->max_ssid_length)); + pwildcard_ssid_tlv->max_ssid_length = + bg_scan_in->ssid_list[ssid_idx].max_len; + memcpy_ext(pmadapter, pwildcard_ssid_tlv->ssid, + bg_scan_in->ssid_list[ssid_idx].ssid, ssid_len, + MLAN_MAX_SSID_LENGTH); + 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 (bg_scan_in->bss_type == MLAN_SCAN_MODE_IBSS || + pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) + band = pmadapter->adhoc_start_band; + else + band = pmpriv->config_bands; + if (!wlan_is_band_compatible( + band, radio_type_to_band(radio_type))) + continue; + scan_type = bg_scan_in->chan_list[chan_idx].scan_type; + /* Prevent active scanning on a radar controlled channel + */ + if (radio_type == BAND_5GHZ) { + if (wlan_11h_radar_detect_required( + pmpriv, + bg_scan_in->chan_list[chan_idx] + .chan_number)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + } + if (radio_type == BAND_2GHZ) { + if (wlan_bg_scan_type_is_passive( + pmpriv, + bg_scan_in->chan_list[chan_idx] + .chan_number)) { + scan_type = MLAN_SCAN_TYPE_PASSIVE; + } + } + tlv_chan_list->chan_scan_param[chan_num].chan_number = + bg_scan_in->chan_list[chan_idx].chan_number; + tlv_chan_list->chan_scan_param[chan_num] + .bandcfg.chanBand = + bg_scan_in->chan_list[chan_idx].radio_type; + + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + tlv_chan_list->chan_scan_param[chan_num] + .chan_scan_mode.passive_scan = MTRUE; + } else { + tlv_chan_list->chan_scan_param[chan_num] + .chan_scan_mode.passive_scan = MFALSE; + } + if (bg_scan_in->chan_list[chan_idx].scan_time) { + scan_dur = + (t_u16)bg_scan_in->chan_list[chan_idx] + .scan_time; + } else { + if (scan_type == MLAN_SCAN_TYPE_PASSIVE) { + scan_dur = pmadapter->passive_scan_time; + } else { + scan_dur = + pmadapter->specific_scan_time; + } + } + tlv_chan_list->chan_scan_param[chan_num].min_scan_time = + wlan_cpu_to_le16(scan_dur); + tlv_chan_list->chan_scan_param[chan_num].max_scan_time = + wlan_cpu_to_le16(scan_dur); + chan_num++; + } + tlv_chan_list->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + tlv_chan_list->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t) * chan_num); + tlv += sizeof(MrvlIEtypesHeader_t) + + sizeof(ChanScanParamSet_t) * chan_num; + cmd_size += sizeof(MrvlIEtypesHeader_t) + + sizeof(ChanScanParamSet_t) * chan_num; + } else { + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *)tlv; + chan_num = wlan_bgscan_create_channel_list(pmpriv, bg_scan_in, + tlv_chan_list); + tlv_chan_list->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + tlv_chan_list->header.len = + wlan_cpu_to_le16(sizeof(ChanScanParamSet_t) * chan_num); + tlv += sizeof(MrvlIEtypesHeader_t) + + sizeof(ChanScanParamSet_t) * chan_num; + cmd_size += sizeof(MrvlIEtypesHeader_t) + + sizeof(ChanScanParamSet_t) * chan_num; + } + if (bg_scan_in->chan_per_scan) { + bg_scan->chan_per_scan = bg_scan_in->chan_per_scan; + } else { + if (bg_scan_in->report_condition & BG_SCAN_WAIT_ALL_CHAN_DONE) + bg_scan->chan_per_scan = chan_num; + else + bg_scan->chan_per_scan = + MRVDRV_MAX_CHANNELS_PER_SPECIFIC_SCAN; + } + if (ISSUPP_11NENABLED(pmpriv->adapter->fw_cap_info) && + (pmpriv->config_bands & BAND_GN || + pmpriv->config_bands & BAND_AN)) { + pht_cap = (MrvlIETypes_HTCap_t *)tlv; + memset(pmadapter, pht_cap, 0, sizeof(MrvlIETypes_HTCap_t)); + pht_cap->header.type = wlan_cpu_to_le16(HT_CAPABILITY); + pht_cap->header.len = sizeof(HTCap_t); + wlan_fill_ht_cap_tlv(pmpriv, pht_cap, pmpriv->config_bands, + MTRUE); + DBG_HEXDUMP(MCMD_D, "BGSCAN: HT_CAPABILITIES IE", + (t_u8 *)pht_cap, sizeof(MrvlIETypes_HTCap_t)); + tlv += sizeof(MrvlIETypes_HTCap_t); + cmd_size += sizeof(MrvlIETypes_HTCap_t); + pht_cap->header.len = wlan_cpu_to_le16(pht_cap->header.len); + } + if (ISSUPP_11ACENABLED(pmpriv->adapter->fw_cap_info) && + (pmpriv->config_bands & BAND_AAC)) { + pvht_cap = (MrvlIETypes_VHTCap_t *)tlv; + memset(pmadapter, pvht_cap, 0, sizeof(MrvlIETypes_VHTCap_t)); + pvht_cap->header.type = wlan_cpu_to_le16(VHT_CAPABILITY); + pvht_cap->header.len = sizeof(VHT_capa_t); + wlan_fill_vht_cap_tlv(pmpriv, pvht_cap, pmpriv->config_bands, + MFALSE, MFALSE); + DBG_HEXDUMP(MCMD_D, "BGSCAN: VHT_CAPABILITIES IE", + (t_u8 *)pvht_cap, sizeof(MrvlIETypes_VHTCap_t)); + tlv += sizeof(MrvlIETypes_VHTCap_t); + cmd_size += sizeof(MrvlIETypes_VHTCap_t); + pvht_cap->header.len = wlan_cpu_to_le16(pvht_cap->header.len); + } + + if (IS_FW_SUPPORT_11AX(pmadapter) && + (pmpriv->config_bands & BAND_AAX)) { + phe_cap = (MrvlIEtypes_Extension_t *)tlv; + len = wlan_fill_he_cap_tlv(pmpriv, BAND_A, phe_cap, MFALSE); + DBG_HEXDUMP(MCMD_D, "BGSCAN: HE_CAPABILITIES IE", + (t_u8 *)phe_cap, len); + tlv += len; + cmd_size += len; + } + if (wlan_is_ext_capa_support(pmpriv)) { + wlan_add_ext_capa_info_ie(pmpriv, MNULL, &tlv); + cmd_size += sizeof(MrvlIETypes_ExtCap_t); + } + if (pmpriv->adapter->ecsa_enable) { + t_u8 bandwidth = BW_20MHZ; + t_u8 oper_class = 1; + t_u32 usr_dot_11n_dev_cap; + if (pmpriv->media_connected) { + if (pmpriv->config_bands & BAND_A) + usr_dot_11n_dev_cap = + pmpriv->usr_dot_11n_dev_cap_a; + else + usr_dot_11n_dev_cap = + pmpriv->usr_dot_11n_dev_cap_bg; + if (usr_dot_11n_dev_cap & MBIT(17)) { + bandwidth = BW_40MHZ; + if (ISSUPP_11ACENABLED( + pmadapter->fw_cap_info) && + (pmpriv->config_bands & BAND_AAC)) + bandwidth = BW_80MHZ; + } + wlan_get_curr_oper_class( + pmpriv, + pmpriv->curr_bss_params.bss_descriptor.channel, + bandwidth, &oper_class); + } + len = wlan_add_supported_oper_class_ie(pmpriv, &tlv, + oper_class); + cmd_size += len; + } + + tlv_start_later = (MrvlIEtypes_StartLater_t *)tlv; + tlv_start_later->header.type = + wlan_cpu_to_le16(TLV_TYPE_STARTBGSCANLATER); + tlv_start_later->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_StartLater_t) - sizeof(MrvlIEtypesHeader_t)); + tlv_start_later->value = wlan_cpu_to_le16(bg_scan_in->start_later); + tlv += sizeof(MrvlIEtypes_StartLater_t); + cmd_size += sizeof(MrvlIEtypes_StartLater_t); + + if (bg_scan_in->config_ees) { + /* Fill EES configuration */ + tlv_ees_cfg = (MrvlIEtypes_EESParamSet_t *)tlv; + tlv_ees_cfg->header.type = wlan_cpu_to_le16(TLV_TYPE_EES_CFG); + tlv_ees_cfg->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_EESParamSet_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_ees_cfg->ees_mode = wlan_cpu_to_le16(bg_scan_in->ees_mode); + tlv_ees_cfg->report_cond = + wlan_cpu_to_le16(bg_scan_in->report_cond); + tlv_ees_cfg->high_period = + wlan_cpu_to_le16(bg_scan_in->high_period); + tlv_ees_cfg->high_period_count = + wlan_cpu_to_le16(bg_scan_in->high_period_count); + tlv_ees_cfg->mid_period = + wlan_cpu_to_le16(bg_scan_in->mid_period); + tlv_ees_cfg->mid_period_count = + wlan_cpu_to_le16(bg_scan_in->mid_period_count); + tlv_ees_cfg->low_period = + wlan_cpu_to_le16(bg_scan_in->low_period); + tlv_ees_cfg->low_period_count = + wlan_cpu_to_le16(bg_scan_in->low_period_count); + tlv += sizeof(MrvlIEtypes_EESParamSet_t); + cmd_size += sizeof(MrvlIEtypes_EESParamSet_t); + + if (bg_scan_in->network_count) { + /* Fill EES network configuration */ + tlv_ees_net_cfg = (MrvlIEtype_EESNetworkCfg_t *)tlv; + tlv_ees_net_cfg->header.type = + wlan_cpu_to_le16(TLV_TYPE_EES_NET_CFG); + tlv_ees_net_cfg->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtype_EESNetworkCfg_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_ees_net_cfg->network_count = + bg_scan_in->network_count; + tlv_ees_net_cfg->max_conn_count = + bg_scan_in->max_conn_count; + tlv_ees_net_cfg->black_list_exp = + bg_scan_in->black_list_exp; + tlv += sizeof(MrvlIEtype_EESNetworkCfg_t); + cmd_size += sizeof(MrvlIEtype_EESNetworkCfg_t); + for (index = 0; index < bg_scan_in->network_count; + index++) { + if (wlan_strlen((char *)bg_scan_in + ->ees_ssid_cfg[index] + .ssid)) { + /* Fill SSID settings */ + 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)bg_scan_in + ->ees_ssid_cfg[index] + .max_len); + memcpy_ext( + pmadapter, tlv_ssid->ssid, + bg_scan_in->ees_ssid_cfg[index] + .ssid, + bg_scan_in->ees_ssid_cfg[index] + .max_len, + MLAN_MAX_SSID_LENGTH); + tlv += sizeof(MrvlIEtypesHeader_t) + + tlv_ssid->header.len; + cmd_size += + sizeof(MrvlIEtypesHeader_t) + + tlv_ssid->header.len; + } else { + /* Fill Wildcard SSID settings */ + pwildcard_ssid_tlv = + (MrvlIEtypes_WildCardSsIdParamSet_t + *)tlv; + pwildcard_ssid_tlv->header.type = + wlan_cpu_to_le16( + TLV_TYPE_WILDCARDSSID); + pwildcard_ssid_tlv->header + .len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_WildCardSsIdParamSet_t) - + sizeof(MrvlIEtypesHeader_t)); + pwildcard_ssid_tlv->max_ssid_length = + MLAN_MAX_SSID_LENGTH; + tlv += sizeof(MrvlIEtypesHeader_t) + + sizeof(pwildcard_ssid_tlv + ->max_ssid_length); + cmd_size += + sizeof(MrvlIEtypesHeader_t) + + sizeof(pwildcard_ssid_tlv + ->max_ssid_length); + } + /* Fill Cipher settings */ + tlv_ees_cipher = (MrvlIEtypes_Cipher_t *)tlv; + tlv_ees_cipher->header.type = + wlan_cpu_to_le16(TLV_TYPE_CIPHER); + tlv_ees_cipher->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_Cipher_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_ees_cipher->pair_cipher = + bg_scan_in->ees_ssid_cfg[index] + .pair_cipher; + tlv_ees_cipher->group_cipher = + bg_scan_in->ees_ssid_cfg[index] + .group_cipher; + tlv += sizeof(MrvlIEtypes_Cipher_t); + cmd_size += sizeof(MrvlIEtypes_Cipher_t); + } + } + } + + if (memcmp(pmadapter, bg_scan_in->random_mac, zero_mac, + MLAN_MAC_ADDR_LENGTH)) { + MrvlIEtypes_MacAddr_t *randomMacParam = + (MrvlIEtypes_MacAddr_t *)tlv; + memset(pmadapter, randomMacParam, 0, + sizeof(MrvlIEtypes_MacAddr_t)); + randomMacParam->header.type = + wlan_cpu_to_le16(TLV_TYPE_RANDOM_MAC); + randomMacParam->header.len = + wlan_cpu_to_le16(MLAN_MAC_ADDR_LENGTH); + memcpy_ext(pmadapter, randomMacParam->mac, + bg_scan_in->random_mac, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + tlv += sizeof(MrvlIEtypes_MacAddr_t); + cmd_size += sizeof(MrvlIEtypes_MacAddr_t); + } +done: + pcmd->size = wlan_cpu_to_le16(cmd_size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of extended scan + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ret_bgscan_config(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_scan *pscan = MNULL; + HostCmd_DS_802_11_BG_SCAN_CONFIG *bg_scan = + &resp->params.bg_scan_config; + wlan_bgscan_cfg *bg_scan_out = MNULL; + + ENTER(); + if (pioctl_buf) { + pscan = (mlan_ds_scan *)pioctl_buf->pbuf; + bg_scan_out = + (wlan_bgscan_cfg *)pscan->param.user_scan.scan_cfg_buf; + bg_scan_out->action = wlan_le16_to_cpu(bg_scan->action); + if ((bg_scan_out->action == BG_SCAN_ACT_GET) || + (bg_scan_out->action == BG_SCAN_ACT_GET_PPS_UAPSD)) { + bg_scan_out->enable = bg_scan->enable; + bg_scan_out->bss_type = bg_scan->bss_type; + bg_scan_out->chan_per_scan = bg_scan->chan_per_scan; + bg_scan_out->scan_interval = + wlan_le32_to_cpu(bg_scan->scan_interval); + bg_scan_out->report_condition = + wlan_le32_to_cpu(bg_scan->report_condition); + pioctl_buf->data_read_written = + sizeof(mlan_ds_scan) + MLAN_SUB_COMMAND_SIZE; + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of bgscan_query + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ret_802_11_bgscan_query(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_scan *pscan = MNULL; + mlan_adapter *pmadapter = pmpriv->adapter; + ENTER(); + wlan_ret_802_11_scan(pmpriv, resp, MNULL); + if (pioctl_buf) { + pscan = (mlan_ds_scan *)pioctl_buf->pbuf; + pscan->param.scan_resp.pscan_table = + (t_u8 *)pmadapter->pscan_table; + pscan->param.scan_resp.num_in_scan_table = + pmadapter->num_in_scan_table; + pscan->param.scan_resp.age_in_secs = pmadapter->age_in_secs; + pscan->param.scan_resp.pchan_stats = + (t_u8 *)pmadapter->pchan_stats; + pscan->param.scan_resp.num_in_chan_stats = + pmadapter->num_in_chan_stats; + + pioctl_buf->data_read_written = + sizeof(mlan_scan_resp) + MLAN_SUB_COMMAND_SIZE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function finds ssid in ssid list. + * + * @param pmpriv A pointer to mlan_private structure + * @param ssid SSID to find in the list + * @param bssid BSSID to qualify the SSID selection (if provided) + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list or < 0 if error + */ +t_s32 wlan_find_ssid_in_list(mlan_private *pmpriv, mlan_802_11_ssid *ssid, + t_u8 *bssid, t_u32 mode) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_s32 net = -1, j; + t_u8 best_rssi = 0; + t_u32 i; + + ENTER(); + PRINTM(MINFO, "Num of entries in scan table = %d\n", + pmadapter->num_in_scan_table); + + /* + * Loop through the table until the maximum is reached or until a match + * is found based on the bssid field comparison + */ + for (i = 0; + i < pmadapter->num_in_scan_table && (!bssid || (bssid && net < 0)); + i++) { + if (!wlan_ssid_cmp(pmadapter, &pmadapter->pscan_table[i].ssid, + ssid) && + (!bssid || + !memcmp(pmadapter, pmadapter->pscan_table[i].mac_address, + bssid, MLAN_MAC_ADDR_LENGTH))) { + if ((mode == MLAN_BSS_MODE_INFRA) && + !wlan_is_band_compatible( + pmpriv->config_bands, + pmadapter->pscan_table[i].bss_band)) + continue; + + switch (mode) { + case MLAN_BSS_MODE_INFRA: + case MLAN_BSS_MODE_IBSS: + j = wlan_is_network_compatible(pmpriv, i, mode); + + if (j >= 0) { + if (SCAN_RSSI(pmadapter->pscan_table[i] + .rssi) > + best_rssi) { + best_rssi = SCAN_RSSI( + pmadapter + ->pscan_table[i] + .rssi); + net = i; + } + } else { + if (net == -1) + net = j; + } + break; + case MLAN_BSS_MODE_AUTO: + default: + /* + * Do not check compatibility if the mode + * requested is Auto/Unknown. Allows generic + * find to work without verifying against the + * Adapter security settings + */ + if (SCAN_RSSI(pmadapter->pscan_table[i].rssi) > + best_rssi) { + best_rssi = SCAN_RSSI( + pmadapter->pscan_table[i].rssi); + net = i; + } + break; + } + } + } + + LEAVE(); + return net; +} + +/** + * @brief This function finds a specific compatible BSSID in the scan list + * + * @param pmpriv A pointer to mlan_private structure + * @param bssid BSSID to find in the scan list + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list or < 0 if error + */ +t_s32 wlan_find_bssid_in_list(mlan_private *pmpriv, t_u8 *bssid, t_u32 mode) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_s32 net = -1; + t_u32 i; + + ENTER(); + + if (!bssid) { + LEAVE(); + return -1; + } + + PRINTM(MINFO, "FindBSSID: Num of BSSIDs = %d\n", + pmadapter->num_in_scan_table); + + /* + * Look through the scan table for a compatible match. The ret return + * variable will be equal to the index in the scan table (greater + * than zero) if the network is compatible. The loop will continue + * past a matched bssid that is not compatible in case there is an + * AP with multiple SSIDs assigned to the same BSSID + */ + for (i = 0; net < 0 && i < pmadapter->num_in_scan_table; i++) { + if (!memcmp(pmadapter, pmadapter->pscan_table[i].mac_address, + bssid, MLAN_MAC_ADDR_LENGTH)) { + if ((mode == MLAN_BSS_MODE_INFRA) && + !wlan_is_band_compatible( + pmpriv->config_bands, + pmadapter->pscan_table[i].bss_band)) + continue; + switch (mode) { + case MLAN_BSS_MODE_INFRA: + case MLAN_BSS_MODE_IBSS: + net = wlan_is_network_compatible(pmpriv, i, + mode); + break; + default: + net = i; + break; + } + } + } + + LEAVE(); + return net; +} + +/** + * @brief Compare two SSIDs + * + * @param pmadapter A pointer to mlan_adapter structure + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare + * + * @return 0--ssid is same, otherwise is different + */ +t_s32 wlan_ssid_cmp(pmlan_adapter pmadapter, 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(pmadapter, ssid1->ssid, ssid2->ssid, ssid1->ssid_len); +} + +/** + * @brief Find the AP with specific ssid in the scan list + * + * @param pmpriv A pointer to mlan_private structure + * @param preq_ssid_bssid A pointer to AP's ssid returned + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +mlan_status wlan_find_best_network(mlan_private *pmpriv, + 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_ext(pmadapter, &preq_ssid_bssid->ssid, &preq_bss->ssid, + sizeof(mlan_802_11_ssid), + sizeof(preq_ssid_bssid->ssid)); + memcpy_ext(pmadapter, (t_u8 *)&preq_ssid_bssid->bssid, + (t_u8 *)&preq_bss->mac_address, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + + /* Make sure we are in the right mode */ + if (pmpriv->bss_mode == MLAN_BSS_MODE_AUTO) + pmpriv->bss_mode = preq_bss->bss_mode; + preq_ssid_bssid->channel = (t_u16)preq_bss->channel; + if (preq_bss->pmd_ie && + wlan_ft_akm_is_used(pmpriv, (t_u8 *)preq_bss->prsn_ie)) { + preq_ssid_bssid->ft_md = preq_bss->pmd_ie->mdid; + preq_ssid_bssid->ft_cap = preq_bss->pmd_ie->ft_cap; + } + preq_ssid_bssid->bss_band = preq_bss->bss_band; + preq_ssid_bssid->idx = i + 1; + } + + if (!preq_ssid_bssid->ssid.ssid_len) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MINFO, + "Best network found = [%s], " + "[" MACSTR "]\n", + preq_ssid_bssid->ssid.ssid, MAC2STR(preq_ssid_bssid->bssid)); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Send a scan command for all available channels filtered on a spec + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param preq_ssid A pointer to AP's ssid returned + * + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +mlan_status wlan_scan_specific_ssid(mlan_private *pmpriv, t_void *pioctl_buf, + 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_ext(pmpriv->adapter, pscan_cfg->ssid_list[0].ssid, + preq_ssid->ssid, preq_ssid->ssid_len, MLAN_MAX_SSID_LENGTH); + pscan_cfg->keep_previous_scan = MFALSE; + + 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(mlan_private *pmpriv) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_callbacks *pcb = (pmlan_callbacks)&pmadapter->callbacks; + BSSDescriptor_t *pcurr_bss = &pmpriv->curr_bss_params.bss_descriptor; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmpriv->curr_bcn_buf_lock); + /* save the beacon buffer if it is not saved or updated */ + if ((pmpriv->pcurr_bcn_buf == MNULL) || + (pmpriv->curr_bcn_size != pcurr_bss->beacon_buf_size) || + (memcmp(pmpriv->adapter, pmpriv->pcurr_bcn_buf, + pcurr_bss->pbeacon_buf, pcurr_bss->beacon_buf_size))) { + if (pmpriv->pcurr_bcn_buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + pmpriv->pcurr_bcn_buf); + pmpriv->pcurr_bcn_buf = MNULL; + } + pmpriv->curr_bcn_size = pcurr_bss->beacon_buf_size; + + if (pmpriv->curr_bcn_size) { + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + pcurr_bss->beacon_buf_size, + MLAN_MEM_DEF, + &pmpriv->pcurr_bcn_buf); + + if ((ret == MLAN_STATUS_SUCCESS) && + pmpriv->pcurr_bcn_buf) { + memcpy_ext(pmpriv->adapter, + pmpriv->pcurr_bcn_buf, + pcurr_bss->pbeacon_buf, + pcurr_bss->beacon_buf_size, + pcurr_bss->beacon_buf_size); + PRINTM(MINFO, "current beacon saved %d\n", + pmpriv->curr_bcn_size); + } else { + PRINTM(MERROR, + "Fail to allocate curr_bcn_buf\n"); + } + } + } + wlan_update_curr_bcn(pmpriv); + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmpriv->curr_bcn_buf_lock); + + LEAVE(); +} + +/** + * @brief Free a beacon buffer of the current bss descriptor + * + * @param pmpriv A pointer to mlan_private structure + * + * @return N/A + */ +t_void wlan_free_curr_bcn(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/mxm_wifiex/wlan_src/mlan/mlan_sdio.c b/mxm_wifiex/wlan_src/mlan/mlan_sdio.c new file mode 100644 index 0000000..b88c4fd --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_sdio.c @@ -0,0 +1,3013 @@ +/** @file mlan_sdio.c + * + * @brief This file contains SDIO specific code + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/27/2008: initial version +********************************************************/ + +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_init.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_sdio.h" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function initialize the SDIO port + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_sdio_init_ioport(mlan_adapter *pmadapter) +{ + t_u32 reg; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 host_int_rsr_reg = pmadapter->pcard_sd->reg->host_int_rsr_reg; + t_u8 host_int_rsr_mask = pmadapter->pcard_sd->reg->sdio_int_mask; + t_u8 card_misc_cfg_reg = pmadapter->pcard_sd->reg->card_misc_cfg_reg; + t_u8 card_config_2_1_reg = + pmadapter->pcard_sd->reg->card_config_2_1_reg; + t_u8 cmd_config_0 = pmadapter->pcard_sd->reg->cmd_config_0; + t_u8 cmd_config_1 = pmadapter->pcard_sd->reg->cmd_config_1; + + ENTER(); + + pmadapter->pcard_sd->ioport = MEM_PORT; + PRINTM(MINFO, "SDIO FUNC1 IO port: 0x%x\n", + pmadapter->pcard_sd->ioport); + + /* enable sdio cmd53 new mode */ + if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, + card_config_2_1_reg, + ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, + card_config_2_1_reg, reg | CMD53_NEW_MODE); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* configure cmd port */ + /* enable reading rx length from the register */ + if (MLAN_STATUS_SUCCESS == + pcb->moal_read_reg(pmadapter->pmoal_handle, cmd_config_0, ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, cmd_config_0, + reg | CMD_PORT_RD_LEN_EN); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* enable Dnld/Upld ready auto reset for cmd port + * after cmd53 is completed */ + if (MLAN_STATUS_SUCCESS == + pcb->moal_read_reg(pmadapter->pmoal_handle, cmd_config_1, ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, cmd_config_1, + reg | CMD_PORT_AUTO_EN); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + +#if defined(SD8977) || defined(SD8978) + if (IS_SD8977(pmadapter->card_type) || + IS_SD8978(pmadapter->card_type)) { + if ((pmadapter->init_para.int_mode == INT_MODE_GPIO) && + (pmadapter->init_para.gpio_pin == GPIO_INT_NEW_MODE)) { + PRINTM(MMSG, "Enable GPIO-1 int mode\n"); + pcb->moal_write_reg(pmadapter->pmoal_handle, + SCRATCH_REG_32, + ENABLE_GPIO_1_INT_MODE); + } + } +#endif + /* Set Host interrupt reset to read to clear */ + if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, + host_int_rsr_reg, ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, host_int_rsr_reg, + reg | host_int_rsr_mask); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Dnld/Upld ready set to auto reset */ + if (MLAN_STATUS_SUCCESS == pcb->moal_read_reg(pmadapter->pmoal_handle, + card_misc_cfg_reg, + ®)) { + pcb->moal_write_reg(pmadapter->pmoal_handle, card_misc_cfg_reg, + reg | AUTO_RE_ENABLE_INT); + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function sends data to the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include + * SDIO header) + * @param port Port + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_write_data_sync(mlan_adapter *pmadapter, + mlan_buffer *pmbuf, t_u32 port) +{ + t_u32 i = 0; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + do { + ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, pmbuf, + port, 0); + if (ret != MLAN_STATUS_SUCCESS) { + i++; + PRINTM(MERROR, + "host_to_card, write iomem (%d) failed: %d\n", i, + ret); + if (MLAN_STATUS_SUCCESS != + pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + HOST_TERM_CMD53)) { + PRINTM(MERROR, "write CFG reg failed\n"); + } + ret = MLAN_STATUS_FAILURE; + if (i > MAX_WRITE_IOMEM_RETRY) { + pmbuf->status_code = MLAN_ERROR_DATA_TX_FAIL; + goto exit; + } + } + } while (ret == MLAN_STATUS_FAILURE); +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function gets available SDIO port for reading cmd/data + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pport A pointer to port number + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_get_rd_port(mlan_adapter *pmadapter, t_u8 *pport) +{ + t_u32 rd_bitmap = pmadapter->pcard_sd->mp_rd_bitmap; + const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; + t_u8 max_ports = MAX_PORT; + + ENTER(); + + PRINTM(MIF_D, "wlan_get_rd_port: mp_rd_bitmap=0x%08x\n", rd_bitmap); + + if (!(rd_bitmap & reg->data_port_mask)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (pmadapter->pcard_sd->mp_rd_bitmap & + (1 << pmadapter->pcard_sd->curr_rd_port)) { + pmadapter->pcard_sd->mp_rd_bitmap &= + (t_u32)(~(1 << pmadapter->pcard_sd->curr_rd_port)); + *pport = pmadapter->pcard_sd->curr_rd_port; + + /* hw rx wraps round only after port (MAX_PORT-1) */ + if (++pmadapter->pcard_sd->curr_rd_port == max_ports) + pmadapter->pcard_sd->curr_rd_port = reg->start_rd_port; + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MIF_D, "port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", *pport, + rd_bitmap, pmadapter->pcard_sd->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->pcard_sd->mp_wr_bitmap; + const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; + + ENTER(); + + PRINTM(MIF_D, "wlan_get_wr_port_data: mp_wr_bitmap=0x%08x\n", + wr_bitmap); + + if (!(wr_bitmap & pmadapter->pcard_sd->mp_data_port_mask)) { + pmadapter->data_sent = MTRUE; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + if (pmadapter->pcard_sd->mp_wr_bitmap & + (1 << pmadapter->pcard_sd->curr_wr_port)) { + pmadapter->pcard_sd->mp_wr_bitmap &= + (t_u32)(~(1 << pmadapter->pcard_sd->curr_wr_port)); + *pport = pmadapter->pcard_sd->curr_wr_port; + if (++pmadapter->pcard_sd->curr_wr_port == + pmadapter->pcard_sd->mp_end_port) + pmadapter->pcard_sd->curr_wr_port = reg->start_wr_port; + } else { + pmadapter->data_sent = MTRUE; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + PRINTM(MIF_D, "port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", *pport, + wr_bitmap, pmadapter->pcard_sd->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, + pmadapter->pcard_sd->reg->poll_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, + pmadapter->pcard_sd->reg->status_reg_0, &fws0)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (MLAN_STATUS_SUCCESS != + pcb->moal_read_reg(pmadapter->pmoal_handle, + pmadapter->pcard_sd->reg->status_reg_1, &fws1)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + *dat = (t_u16)((fws1 << 8) | fws0); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function reads firmware dnld offset 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_dnld_offset(mlan_adapter *pmadapter, + t_u32 *dat) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 fw_dnld_offset_0 = 0; + t_u32 fw_dnld_offset_1 = 0; + t_u32 fw_dnld_offset_2 = 0; + t_u32 fw_dnld_offset_3 = 0; + + ENTER(); + + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + reg->fw_dnld_offset_0_reg, &fw_dnld_offset_0); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Dev fw_dnld_offset_0 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", + reg->fw_dnld_offset_0_reg, fw_dnld_offset_0); + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + reg->fw_dnld_offset_1_reg, &fw_dnld_offset_1); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Dev fw_dnld_offset_1 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", + reg->fw_dnld_offset_1_reg, fw_dnld_offset_1); + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + reg->fw_dnld_offset_2_reg, &fw_dnld_offset_2); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Dev fw_dnld_offset_2 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", + reg->fw_dnld_offset_2_reg, fw_dnld_offset_2); + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + reg->fw_dnld_offset_3_reg, &fw_dnld_offset_3); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Dev fw_dnld_offset_3 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", + reg->fw_dnld_offset_3_reg, fw_dnld_offset_3); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + *dat = (t_u32)(((fw_dnld_offset_3 & 0xff) << 24) | + ((fw_dnld_offset_2 & 0xff) << 16) | + ((fw_dnld_offset_1 & 0xff) << 8) | + (fw_dnld_offset_0 & 0xff)); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function reads firmware dnld 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_dnld_status(mlan_adapter *pmadapter, + t_u16 *dat) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 fw_dnld_status_0 = 0; + t_u32 fw_dnld_status_1 = 0; + + ENTER(); + + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + reg->fw_dnld_status_0_reg, &fw_dnld_status_0); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Dev fw_dnld_status_0 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", + reg->fw_dnld_status_0_reg, fw_dnld_status_0); + ret = MLAN_STATUS_FAILURE; + goto done; + } + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + reg->fw_dnld_status_1_reg, &fw_dnld_status_1); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Dev fw_dnld_status_1 reg read failed: reg(0x%04X)=0x%x. Terminating download\n", + reg->fw_dnld_status_1_reg, fw_dnld_status_1); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + *dat = (t_u16)(((fw_dnld_status_1 & 0xff) << 8) | + (fw_dnld_status_0 & 0xff)); + +done: + LEAVE(); + return ret; +} + +/** @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, + pmadapter->pcard_sd->reg->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, + pmadapter->pcard_sd->reg->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, + pmadapter->pcard_sd->reg->host_int_mask_reg, + mask)) { + PRINTM(MWARN, "Enable host interrupt failed\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function reads data from the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param type A pointer to keep type as data or command + * @param nb A pointer to keep the data/cmd length returned in buffer + * @param pmbuf A pointer to the SDIO data/cmd buffer + * @param npayload the length of data/cmd buffer + * @param ioport the SDIO ioport + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_sdio_card_to_host(mlan_adapter *pmadapter, t_u32 *type, + t_u32 *nb, pmlan_buffer pmbuf, + t_u32 npayload, t_u32 ioport) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u32 i = 0; + + ENTER(); + + if (!pmbuf) { + PRINTM(MWARN, "pmbuf is NULL!\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + do { + ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, pmbuf, + ioport, 0); + + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "wlan: cmd53 read failed: %d ioport=0x%x retry=%d\n", + ret, ioport, i); + i++; + if (MLAN_STATUS_SUCCESS != + pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + HOST_TERM_CMD53)) { + PRINTM(MERROR, "Set Term cmd53 failed\n"); + } + if (i > MAX_WRITE_IOMEM_RETRY) { + pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } + } while (ret == MLAN_STATUS_FAILURE); + *nb = wlan_le16_to_cpu(*(t_u16 *)(pmbuf->pbuf + pmbuf->data_offset)); + if (*nb > npayload) { + PRINTM(MERROR, "invalid packet, *nb=%d, npayload=%d\n", *nb, + npayload); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + DBG_HEXDUMP(MIF_D, "SDIO Blk Rd", pmbuf->pbuf + pmbuf->data_offset, + MIN(*nb, MAX_DATA_DUMP_LEN)); + + *type = wlan_le16_to_cpu( + *(t_u16 *)(pmbuf->pbuf + pmbuf->data_offset + 2)); + +exit: + LEAVE(); + return ret; +} + +/** + * @brief This function downloads FW blocks to device + * + * @param pmadapter A pointer to mlan_adapter + * @param firmware A pointer to firmware image + * @param firmwarelen firmware len + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_sdio_prog_fw_w_helper(pmlan_adapter pmadapter, t_u8 *fw, + t_u32 fw_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *firmware = fw; + t_u32 firmwarelen = fw_len; + t_u32 offset = 0; + t_u32 base0, base1; + t_void *tmpfwbuf = MNULL; + t_u32 tmpfwbufsz; + t_u8 *fwbuf; + mlan_buffer mbuf; + t_u16 len = 0; + t_u32 txlen = 0, tx_blocks = 0, tries = 0; + t_u32 i = 0; + const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; + t_u32 read_base_0_reg = reg->base_0_reg; + t_u32 read_base_1_reg = reg->base_1_reg; +#if defined(SD9098) + t_u32 rev_id_reg = 0; + t_u32 revision_id = 0; +#endif + t_u8 check_fw_status = MFALSE; + t_u16 fw_dnld_status = 0; + t_u32 fw_dnld_offset = 0; + t_u8 mic_retry = 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); +#if defined(SD9098) + if (IS_SD9098(pmadapter->card_type)) { + rev_id_reg = pmadapter->pcard_sd->reg->card_revision_reg; + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, rev_id_reg, + &revision_id); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Card Revision register read failed:" + "card_revision_reg=0x%x\n", + rev_id_reg); + goto done; + } + /* Skyhawk A0, need to check both CRC and MIC error */ + if (revision_id >= CHIP_9098_REV_A0) + check_fw_status = MTRUE; + } +#endif +#if defined(SD9097) + if (IS_SD9097(pmadapter->card_type)) + check_fw_status = MTRUE; +#endif + /* Perform firmware data transfer */ + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY bits + */ + ret = wlan_sdio_poll_card_status( + pmadapter, CARD_IO_READY | DN_LD_CARD_RDY); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MFATAL, + "WLAN: FW download with helper poll status timeout @ %d\n", + offset); + goto done; + } + + /* More data */ + if (firmwarelen && offset >= firmwarelen) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + read_base_0_reg, &base0); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Dev BASE0 register read failed:" + " base0=0x%04X(%d). Terminating download\n", + base0, base0); + goto done; + } + ret = pcb->moal_read_reg(pmadapter->pmoal_handle, + read_base_1_reg, &base1); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Dev BASE1 register read failed:" + " base1=0x%04X(%d). Terminating download\n", + base1, base1); + goto done; + } + len = (t_u16)(((base1 & 0xff) << 8) | (base0 & 0xff)); + + if (len) + break; + wlan_udelay(pmadapter, 10); + } + + if (!len) + break; + else if (len > WLAN_UPLD_SIZE) { + PRINTM(MFATAL, + "WLAN: FW download failure @ %d, invalid length %d\n", + offset, len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Ignore CRC check before download the 1st packet */ + if (offset == 0 && (len & MBIT(0))) + len &= ~MBIT(0); + + txlen = len; + + if (len & MBIT(0)) { + /* New fw download process, check CRC and MIC error */ + if (check_fw_status) { + /* Get offset from fw dnld offset Register */ + ret = wlan_sdio_read_fw_dnld_offset( + pmadapter, &fw_dnld_offset); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MFATAL, + "WLAN: FW download with helper read fw dnld offset failed @ %d\n", + offset); + goto done; + } + /* Get CRC MIC error from fw dnld status + * Register */ + ret = wlan_sdio_read_fw_dnld_status( + pmadapter, &fw_dnld_status); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MFATAL, + "WLAN: FW download with helper read fw dnld status failed @ %d\n", + offset); + goto done; + } + PRINTM(MERROR, + "WLAN: FW download error: status=0x%x offset = 0x%x fw offset = 0x%x\n", + fw_dnld_status, offset, fw_dnld_offset); + } + 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); + if (fw_dnld_status & (MBIT(6) | MBIT(7))) { + offset = 0; + mic_retry++; + if (mic_retry > MAX_FW_RETRY) { + PRINTM(MFATAL, + "WLAN: FW download failure @ %d, over max mic retry count\n", + offset); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + PRINTM(MERROR, "WLAN: retry: %d, offset %d\n", i, + offset); + DBG_HEXDUMP(MERROR, "WLAN: FW block:", fwbuf, len); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking + * for last block */ + if (firmwarelen && firmwarelen - offset < txlen) + txlen = firmwarelen - offset; + PRINTM(MINFO, "."); + + tx_blocks = (txlen + MLAN_SDIO_BLOCK_SIZE_FW_DNLD - 1) / + MLAN_SDIO_BLOCK_SIZE_FW_DNLD; + + /* Copy payload to buffer */ + if (firmware) + memmove(pmadapter, fwbuf, &firmware[offset], + txlen); + else + pcb->moal_get_fw_data(pmadapter->pmoal_handle, + offset, txlen, fwbuf); + } + + /* Send data */ + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = (t_u8 *)fwbuf; + mbuf.data_len = tx_blocks * MLAN_SDIO_BLOCK_SIZE_FW_DNLD; + + ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, &mbuf, + pmadapter->pcard_sd->ioport, 0); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "WLAN: FW download, write iomem (%d) failed @ %d\n", + i, offset); + if (pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + HOST_TERM_CMD53) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "write CFG reg failed\n"); + } + ret = MLAN_STATUS_FAILURE; + goto done; + } + + offset += txlen; + } while (MTRUE); + + PRINTM(MMSG, "Wlan: FW download over, firmwarelen=%d downloaded %d\n", + firmwarelen, offset); + + ret = MLAN_STATUS_SUCCESS; +done: + if (tmpfwbuf) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)tmpfwbuf); + + LEAVE(); + return ret; +} + +/** + * @brief This function disables the host interrupts. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_disable_sdio_host_int(pmlan_adapter pmadapter) +{ + mlan_status ret; + + ENTER(); + ret = wlan_sdio_disable_host_int_mask(pmadapter, HIM_DISABLE); + LEAVE(); + return ret; +} + +/** + * @brief This function decodes the rx packet & + * calls corresponding handlers according to the packet type + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the SDIO data/cmd buffer + * @param upld_typ Type of rx packet + * @param lock_flag flag for spin_lock. + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_decode_rx_packet(mlan_adapter *pmadapter, + mlan_buffer *pmbuf, t_u32 upld_typ, + t_u8 lock_flag) +{ + t_u8 *cmd_buf; + t_u32 event; + + ENTER(); + + switch (upld_typ) { + case MLAN_TYPE_SPA_DATA: + PRINTM(MINFO, "--- Rx: SPA Data packet ---\n"); + pmbuf->data_len = pmadapter->upld_len; + if (pmadapter->rx_work_flag) { + pmbuf->buf_type = MLAN_BUF_TYPE_SPA_DATA; + if (lock_flag) + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, + (pmlan_linked_list)pmbuf, MNULL, + MNULL); + pmadapter->rx_pkts_queued++; + if (lock_flag) + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + } else { + wlan_decode_spa_buffer(pmadapter, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + wlan_free_mlan_buffer(pmadapter, pmbuf); + } + pmadapter->data_received = MTRUE; + break; + case MLAN_TYPE_DATA: + PRINTM(MINFO, "--- Rx: Data packet ---\n"); + if (pmadapter->upld_len > pmbuf->data_len) { + PRINTM(MERROR, + "SDIO: Drop packet upld_len=%d data_len=%d \n", + pmadapter->upld_len, pmbuf->data_len); + wlan_free_mlan_buffer(pmadapter, pmbuf); + break; + } + pmbuf->data_len = (pmadapter->upld_len - SDIO_INTF_HEADER_LEN); + pmbuf->data_offset += SDIO_INTF_HEADER_LEN; + if (pmadapter->rx_work_flag) { + if (lock_flag) + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, + (pmlan_linked_list)pmbuf, MNULL, + MNULL); + pmadapter->rx_pkts_queued++; + if (lock_flag) + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + } else { + wlan_handle_rx_packet(pmadapter, pmbuf); + } + pmadapter->data_received = MTRUE; + break; + + case MLAN_TYPE_CMD: + PRINTM(MINFO, "--- Rx: Cmd Response ---\n"); + /* take care of curr_cmd = NULL case */ + if (!pmadapter->curr_cmd) { + cmd_buf = pmadapter->upld_buf; + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) { + wlan_process_sleep_confirm_resp( + pmadapter, + pmbuf->pbuf + pmbuf->data_offset + + SDIO_INTF_HEADER_LEN, + pmadapter->upld_len - + SDIO_INTF_HEADER_LEN); + } + pmadapter->upld_len -= SDIO_INTF_HEADER_LEN; + memcpy_ext(pmadapter, cmd_buf, + pmbuf->pbuf + pmbuf->data_offset + + SDIO_INTF_HEADER_LEN, + pmadapter->upld_len - SDIO_INTF_HEADER_LEN, + MRVDRV_SIZE_OF_CMD_BUFFER); + wlan_free_mlan_buffer(pmadapter, pmbuf); + } else { + pmadapter->cmd_resp_received = MTRUE; + pmadapter->upld_len -= SDIO_INTF_HEADER_LEN; + pmbuf->data_len = pmadapter->upld_len; + pmbuf->data_offset += SDIO_INTF_HEADER_LEN; + pmadapter->curr_cmd->respbuf = pmbuf; + if (pmadapter->upld_len >= MRVDRV_SIZE_OF_CMD_BUFFER) { + PRINTM(MMSG, "Invalid CmdResp len=%d\n", + pmadapter->upld_len); + DBG_HEXDUMP(MERROR, "Invalid CmdResp", + pmbuf->pbuf + pmbuf->data_offset, + MAX_DATA_DUMP_LEN); + } + } + break; + + case MLAN_TYPE_EVENT: + PRINTM(MINFO, "--- Rx: Event ---\n"); + event = *(t_u32 *)&pmbuf->pbuf[pmbuf->data_offset + + SDIO_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_ext(pmadapter, pmadapter->event_body, + pmbuf->pbuf + pmbuf->data_offset + + MLAN_EVENT_HEADER_LEN, + pmadapter->upld_len - MLAN_EVENT_HEADER_LEN, + MAX_EVENT_SIZE); + } + + /* 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 += SDIO_INTF_HEADER_LEN; + pmbuf->data_len -= SDIO_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; +} + +/** + * @brief This function receives single packet + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_receive_single_packet(mlan_adapter *pmadapter) +{ + mlan_buffer *pmbuf; + t_u8 port; + t_u16 rx_len; + t_u32 pkt_type = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + pmbuf = pmadapter->pcard_sd->mpa_rx.mbuf_arr[0]; + port = pmadapter->pcard_sd->mpa_rx.start_port; + rx_len = pmadapter->pcard_sd->mpa_rx.len_arr[0]; + if (MLAN_STATUS_SUCCESS != + wlan_sdio_card_to_host(pmadapter, &pkt_type, + (t_u32 *)&pmadapter->upld_len, pmbuf, rx_len, + pmadapter->pcard_sd->ioport + port)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (pkt_type != MLAN_TYPE_DATA && pkt_type != MLAN_TYPE_SPA_DATA) { + PRINTM(MERROR, + "receive a wrong pkt from DATA PORT: type=%d, len=%dd\n", + pkt_type, pmbuf->data_len); + pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->pcard_sd->mpa_rx_count[0]++; + wlan_decode_rx_packet(pmadapter, pmbuf, pkt_type, MTRUE); +done: + if (ret != MLAN_STATUS_SUCCESS) + wlan_free_mlan_buffer(pmadapter, pmbuf); + MP_RX_AGGR_BUF_RESET(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief This function receives data from the card in aggregate mode. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_receive_mp_aggr_buf(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer mbuf_aggr; + mlan_buffer *mbuf_deaggr; + t_u32 pind = 0; + t_u32 pkt_len, pkt_type = 0; + t_u8 *curr_ptr; + t_u32 cmd53_port = 0; + t_u32 i = 0; + t_u32 port_count = 0; + + /* do aggr RX now */ + PRINTM(MINFO, "do_rx_aggr: num of packets: %d\n", + pmadapter->pcard_sd->mpa_rx.pkt_cnt); + + memset(pmadapter, &mbuf_aggr, 0, sizeof(mlan_buffer)); + + if (pmadapter->pcard_sd->mpa_rx.pkt_cnt == 1) + return wlan_receive_single_packet(pmadapter); + if (!pmadapter->pcard_sd->mpa_rx.buf) { + mbuf_aggr.data_len = pmadapter->pcard_sd->mpa_rx.buf_len; + mbuf_aggr.pnext = mbuf_aggr.pprev = &mbuf_aggr; + mbuf_aggr.use_count = 0; + for (pind = 0; pind < pmadapter->pcard_sd->mpa_rx.pkt_cnt; + pind++) { + pmadapter->pcard_sd->mpa_rx.mbuf_arr[pind]->data_len = + pmadapter->pcard_sd->mpa_rx.len_arr[pind]; + wlan_link_buf_to_aggr( + &mbuf_aggr, + pmadapter->pcard_sd->mpa_rx.mbuf_arr[pind]); + } + } else { + mbuf_aggr.pbuf = (t_u8 *)pmadapter->pcard_sd->mpa_rx.buf; + mbuf_aggr.data_len = pmadapter->pcard_sd->mpa_rx.buf_len; + } + + port_count = bitcount(pmadapter->pcard_sd->mpa_rx.ports) - 1; + /* port_count = pmadapter->mpa_rx.pkt_cnt - 1; */ + cmd53_port = (pmadapter->pcard_sd->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + + pmadapter->pcard_sd->mpa_rx.start_port; + do { + ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, + &mbuf_aggr, cmd53_port, 0); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "wlan: sdio mp cmd53 read failed: %d ioport=0x%x retry=%d\n", + ret, cmd53_port, i); + i++; + if (MLAN_STATUS_SUCCESS != + pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + HOST_TERM_CMD53)) { + PRINTM(MERROR, "Set Term cmd53 failed\n"); + } + if (i > MAX_WRITE_IOMEM_RETRY) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } while (ret == MLAN_STATUS_FAILURE); + if (pmadapter->rx_work_flag) + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + if (!pmadapter->pcard_sd->mpa_rx.buf && + pmadapter->pcard_sd->mpa_rx.pkt_cnt > 1) { + for (pind = 0; pind < pmadapter->pcard_sd->mpa_rx.pkt_cnt; + pind++) { + mbuf_deaggr = + pmadapter->pcard_sd->mpa_rx.mbuf_arr[pind]; + pkt_len = wlan_le16_to_cpu( + *(t_u16 *)(mbuf_deaggr->pbuf + + mbuf_deaggr->data_offset)); + pkt_type = wlan_le16_to_cpu( + *(t_u16 *)(mbuf_deaggr->pbuf + + mbuf_deaggr->data_offset + 2)); + pmadapter->upld_len = pkt_len; + wlan_decode_rx_packet(pmadapter, mbuf_deaggr, pkt_type, + MFALSE); + } + } else { + DBG_HEXDUMP(MIF_D, "SDIO MP-A Blk Rd", + pmadapter->pcard_sd->mpa_rx.buf, + MIN(pmadapter->pcard_sd->mpa_rx.buf_len, + MAX_DATA_DUMP_LEN)); + + curr_ptr = pmadapter->pcard_sd->mpa_rx.buf; + + for (pind = 0; pind < pmadapter->pcard_sd->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->pcard_sd->mpa_rx.mbuf_arr[pind]; + if ((pkt_type == MLAN_TYPE_DATA || + pkt_type == MLAN_TYPE_SPA_DATA) && + (pkt_len <= + pmadapter->pcard_sd->mpa_rx.len_arr[pind])) { + memcpy_ext(pmadapter, + mbuf_deaggr->pbuf + + mbuf_deaggr->data_offset, + curr_ptr, pkt_len, pkt_len); + pmadapter->upld_len = pkt_len; + /* Process de-aggr packet */ + wlan_decode_rx_packet(pmadapter, mbuf_deaggr, + pkt_type, MFALSE); + } else { + PRINTM(MERROR, + "Wrong aggr packet: type=%d, len=%d, max_len=%d\n", + pkt_type, pkt_len, + pmadapter->pcard_sd->mpa_rx + .len_arr[pind]); + wlan_free_mlan_buffer(pmadapter, mbuf_deaggr); + } + curr_ptr += pmadapter->pcard_sd->mpa_rx.len_arr[pind]; + } + } + if (pmadapter->rx_work_flag) + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + pmadapter->pcard_sd + ->mpa_rx_count[pmadapter->pcard_sd->mpa_rx.pkt_cnt - 1]++; + MP_RX_AGGR_BUF_RESET(pmadapter); +done: + return ret; +} + +/** + * @brief This function receives data from the card in aggregate mode. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the SDIO data/cmd buffer + * @param port Current port on which packet needs to be rxed + * @param rx_len Length of received packet + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_sdio_card_to_host_mp_aggr(mlan_adapter *pmadapter, + mlan_buffer *pmbuf, t_u8 port, + t_u16 rx_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_s32 f_do_rx_aggr = 0; + t_s32 f_do_rx_cur = 0; + t_s32 f_aggr_cur = 0; + t_s32 f_post_aggr_cur = 0; + t_u32 pind = 0; + t_u32 pkt_type = 0; + const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; + + ENTER(); + + if (!pmadapter->pcard_sd->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->pcard_sd->mp_rd_bitmap & reg->data_port_mask) { + /* Some more data RX pending */ + PRINTM(MINFO, "card_2_host_mp_aggr: Not last packet\n"); + + if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { + if (MP_RX_AGGR_BUF_HAS_ROOM(pmadapter, rx_len)) { + f_aggr_cur = 1; + } else { + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_aggr = 1; + f_post_aggr_cur = 1; + } + } else { + /* Rx aggr not in progress */ + f_aggr_cur = 1; + } + + } else { + /* No more data RX pending */ + PRINTM(MINFO, "card_2_host_mp_aggr: Last packet\n"); + + if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { + f_do_rx_aggr = 1; + if (MP_RX_AGGR_BUF_HAS_ROOM(pmadapter, rx_len)) { + f_aggr_cur = 1; + } else { + /* No room in Aggr buf, do rx aggr now */ + f_do_rx_cur = 1; + } + } else { + f_do_rx_cur = 1; + } + } + + if (f_aggr_cur) { + PRINTM(MINFO, "Current packet aggregation.\n"); + /* Curr pkt can be aggregated */ + MP_RX_AGGR_SETUP(pmadapter, pmbuf, port, rx_len); + + if (MP_RX_AGGR_PKT_LIMIT_REACHED(pmadapter) || + MP_RX_AGGR_PORT_LIMIT_REACHED(pmadapter)) { + PRINTM(MINFO, + "card_2_host_mp_aggr: Aggregation Packet limit reached\n"); + /* No more pkts allowed in Aggr buf, rx it */ + f_do_rx_aggr = 1; + } + } + + if (f_do_rx_aggr) { + /* do aggr RX now */ + if (MLAN_STATUS_SUCCESS != + wlan_receive_mp_aggr_buf(pmadapter)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +rx_curr_single: + if (f_do_rx_cur) { + PRINTM(MINFO, "RX: f_do_rx_cur: port: %d rx_len: %d\n", port, + rx_len); + + if (MLAN_STATUS_SUCCESS != + wlan_sdio_card_to_host( + pmadapter, &pkt_type, (t_u32 *)&pmadapter->upld_len, + pmbuf, rx_len, + pmadapter->pcard_sd->ioport + port)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (pkt_type != MLAN_TYPE_DATA && + pkt_type != MLAN_TYPE_SPA_DATA) { + PRINTM(MERROR, + "receive a wrong pkt from DATA PORT: type=%d, len=%dd\n", + pkt_type, pmbuf->data_len); + pmbuf->status_code = MLAN_ERROR_DATA_RX_FAIL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pmadapter->pcard_sd->mpa_rx_count[0]++; + + wlan_decode_rx_packet(pmadapter, pmbuf, pkt_type, MTRUE); + } + if (f_post_aggr_cur) { + PRINTM(MINFO, "Current packet aggregation.\n"); + /* Curr pkt can be aggregated */ + MP_RX_AGGR_SETUP(pmadapter, pmbuf, port, rx_len); + } +done: + if (ret == MLAN_STATUS_FAILURE) { + if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { + /* MP-A transfer failed - cleanup */ + for (pind = 0; + pind < pmadapter->pcard_sd->mpa_rx.pkt_cnt; + pind++) { + wlan_free_mlan_buffer( + pmadapter, pmadapter->pcard_sd->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; +} + +/** + * @brief This function sends aggr buf + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_send_mp_aggr_buf(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 cmd53_port = 0; + t_u32 port_count = 0; + mlan_buffer mbuf_aggr; + t_u8 i = 0; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + + ENTER(); + + if (!pmadapter->pcard_sd->mpa_tx.pkt_cnt) { + LEAVE(); + return ret; + } + PRINTM(MINFO, + "host_2_card_mp_aggr: Send aggregation buffer." + "%d %d\n", + pmadapter->pcard_sd->mpa_tx.start_port, + pmadapter->pcard_sd->mpa_tx.ports); + + memset(pmadapter, &mbuf_aggr, 0, sizeof(mlan_buffer)); + + if (!pmadapter->pcard_sd->mpa_tx.buf && + pmadapter->pcard_sd->mpa_tx.pkt_cnt > 1) { + mbuf_aggr.data_len = pmadapter->pcard_sd->mpa_tx.buf_len; + mbuf_aggr.pnext = mbuf_aggr.pprev = &mbuf_aggr; + mbuf_aggr.use_count = 0; + for (i = 0; i < pmadapter->pcard_sd->mpa_tx.pkt_cnt; i++) + wlan_link_buf_to_aggr( + &mbuf_aggr, + pmadapter->pcard_sd->mpa_tx.mbuf_arr[i]); + } else { + mbuf_aggr.pbuf = (t_u8 *)pmadapter->pcard_sd->mpa_tx.buf; + mbuf_aggr.data_len = pmadapter->pcard_sd->mpa_tx.buf_len; + } + + port_count = bitcount(pmadapter->pcard_sd->mpa_tx.ports) - 1; + cmd53_port = (pmadapter->pcard_sd->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + + pmadapter->pcard_sd->mpa_tx.start_port; + + if (pmadapter->pcard_sd->mpa_tx.pkt_cnt == 1) + cmd53_port = pmadapter->pcard_sd->ioport + + pmadapter->pcard_sd->mpa_tx.start_port; + /** only one packet */ + if (!pmadapter->pcard_sd->mpa_tx.buf && + pmadapter->pcard_sd->mpa_tx.pkt_cnt == 1) + ret = wlan_write_data_sync( + pmadapter, pmadapter->pcard_sd->mpa_tx.mbuf_arr[0], + cmd53_port); + else + ret = wlan_write_data_sync(pmadapter, &mbuf_aggr, cmd53_port); + if (!pmadapter->pcard_sd->mpa_tx.buf) { + /** free mlan buffer */ + for (i = 0; i < pmadapter->pcard_sd->mpa_tx.pkt_cnt; i++) { + wlan_write_data_complete( + pmadapter, + pmadapter->pcard_sd->mpa_tx.mbuf_arr[i], + MLAN_STATUS_SUCCESS); + } + } + if (!(pmadapter->pcard_sd->mp_wr_bitmap & + (1 << pmadapter->pcard_sd->curr_wr_port)) && + (pmadapter->pcard_sd->mpa_tx.pkt_cnt < mp_aggr_pkt_limit)) + pmadapter->pcard_sd->mpa_sent_no_ports++; + pmadapter->pcard_sd + ->mpa_tx_count[pmadapter->pcard_sd->mpa_tx.pkt_cnt - 1]++; + pmadapter->pcard_sd + ->last_mp_wr_bitmap[pmadapter->pcard_sd->last_mp_index] = + pmadapter->pcard_sd->mp_wr_bitmap; + pmadapter->pcard_sd + ->last_mp_wr_ports[pmadapter->pcard_sd->last_mp_index] = + cmd53_port; + pmadapter->pcard_sd->last_mp_wr_len[pmadapter->pcard_sd->last_mp_index] = + pmadapter->pcard_sd->mpa_tx.buf_len; + pmadapter->pcard_sd + ->last_curr_wr_port[pmadapter->pcard_sd->last_mp_index] = + pmadapter->pcard_sd->curr_wr_port; + memcpy_ext( + pmadapter, + (t_u8 *)&pmadapter->pcard_sd + ->last_mp_wr_info[pmadapter->pcard_sd->last_mp_index * + mp_aggr_pkt_limit], + (t_u8 *)pmadapter->pcard_sd->mpa_tx.mp_wr_info, + mp_aggr_pkt_limit * sizeof(t_u16), + mp_aggr_pkt_limit * sizeof(t_u16)); + pmadapter->pcard_sd->last_mp_index++; + if (pmadapter->pcard_sd->last_mp_index >= SDIO_MP_DBG_NUM) + pmadapter->pcard_sd->last_mp_index = 0; + MP_TX_AGGR_BUF_RESET(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card in SDIO aggregated mode. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param mbuf A pointer to the SDIO data/cmd buffer + * @param port current port for aggregation + * @param next_pkt_len Length of next packet used for multiport aggregation + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_host_to_card_mp_aggr(mlan_adapter *pmadapter, + mlan_buffer *mbuf, t_u8 port, + t_u32 next_pkt_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_s32 f_send_aggr_buf = 0; + t_s32 f_send_cur_buf = 0; + t_s32 f_precopy_cur_buf = 0; + t_s32 f_postcopy_cur_buf = 0; + t_u8 aggr_sg = 0; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + + ENTER(); + + PRINTM(MIF_D, "host_2_card_mp_aggr: next_pkt_len: %d curr_port:%d\n", + next_pkt_len, port); + + if (!pmadapter->pcard_sd->mpa_tx.enabled) { + PRINTM(MINFO, + "host_2_card_mp_aggr: tx aggregation disabled !\n"); + f_send_cur_buf = 1; + goto tx_curr_single; + } + + if (next_pkt_len) { + /* More pkt in TX queue */ + PRINTM(MINFO, "host_2_card_mp_aggr: More packets in Queue.\n"); + + if (MP_TX_AGGR_IN_PROGRESS(pmadapter)) { + if (MP_TX_AGGR_BUF_HAS_ROOM(pmadapter, mbuf, + mbuf->data_len)) { + f_precopy_cur_buf = 1; + + if (!(pmadapter->pcard_sd->mp_wr_bitmap & + (1 + << pmadapter->pcard_sd->curr_wr_port)) || + !MP_TX_AGGR_BUF_HAS_ROOM( + pmadapter, mbuf, + mbuf->data_len + next_pkt_len)) { + f_send_aggr_buf = 1; + } + } else { + /* No room in Aggr buf, send it */ + f_send_aggr_buf = 1; + + if (!(pmadapter->pcard_sd->mp_wr_bitmap & + (1 + << pmadapter->pcard_sd->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->pcard_sd->mp_wr_bitmap & + (1 << pmadapter->pcard_sd->curr_wr_port))) + f_precopy_cur_buf = 1; + else + f_send_cur_buf = 1; + } + } else { + /* Last pkt in TX queue */ + PRINTM(MINFO, + "host_2_card_mp_aggr: Last packet in Tx Queue.\n"); + + if (MP_TX_AGGR_IN_PROGRESS(pmadapter)) { + /* some packs in Aggr buf already */ + f_send_aggr_buf = 1; + + if (MP_TX_AGGR_BUF_HAS_ROOM(pmadapter, mbuf, + mbuf->data_len)) { + f_precopy_cur_buf = 1; + } else { + /* No room in Aggr buf, send it */ + f_send_cur_buf = 1; + } + } else { + f_send_cur_buf = 1; + } + pmadapter->pcard_sd->mpa_sent_last_pkt++; + } + + if (f_precopy_cur_buf) { + PRINTM(MINFO, "host_2_card_mp_aggr: Precopy current buffer\n"); + if (pmadapter->pcard_sd->mpa_buf) + memcpy_ext( + pmadapter, + pmadapter->pcard_sd->mpa_buf + + (pmadapter->pcard_sd->last_mp_index * + mp_aggr_pkt_limit + + pmadapter->pcard_sd->mpa_tx.pkt_cnt) * + MLAN_SDIO_BLOCK_SIZE, + mbuf->pbuf + mbuf->data_offset, + MLAN_SDIO_BLOCK_SIZE, MLAN_SDIO_BLOCK_SIZE); + if (!pmadapter->pcard_sd->mpa_tx.buf) { + MP_TX_AGGR_BUF_PUT_SG(pmadapter, mbuf, port); + aggr_sg = MTRUE; + } else { + MP_TX_AGGR_BUF_PUT(pmadapter, mbuf, port); + } + if (MP_TX_AGGR_PKT_LIMIT_REACHED(pmadapter)) { + PRINTM(MIF_D, + "host_2_card_mp_aggr: Aggregation Pkt limit reached\n"); + /* No more pkts allowed in Aggr buf, send it */ + f_send_aggr_buf = 1; + } + } + + if (f_send_aggr_buf) + ret = wlan_send_mp_aggr_buf(pmadapter); + +tx_curr_single: + if (f_send_cur_buf) { + PRINTM(MINFO, "host_2_card_mp_aggr: writing to port #%d\n", + port); + ret = wlan_write_data_sync(pmadapter, mbuf, + pmadapter->pcard_sd->ioport + port); + if (!(pmadapter->pcard_sd->mp_wr_bitmap & + (1 << pmadapter->pcard_sd->curr_wr_port))) + pmadapter->pcard_sd->mpa_sent_no_ports++; + pmadapter->pcard_sd + ->last_mp_wr_bitmap[pmadapter->pcard_sd->last_mp_index] = + pmadapter->pcard_sd->mp_wr_bitmap; + pmadapter->pcard_sd + ->last_mp_wr_ports[pmadapter->pcard_sd->last_mp_index] = + pmadapter->pcard_sd->ioport + port; + pmadapter->pcard_sd + ->last_mp_wr_len[pmadapter->pcard_sd->last_mp_index] = + mbuf->data_len; + memset(pmadapter, + (t_u8 *)&pmadapter->pcard_sd->last_mp_wr_info + [pmadapter->pcard_sd->last_mp_index * + mp_aggr_pkt_limit], + 0, sizeof(t_u16) * mp_aggr_pkt_limit); + pmadapter->pcard_sd + ->last_mp_wr_info[pmadapter->pcard_sd->last_mp_index * + mp_aggr_pkt_limit] = + *(t_u16 *)(mbuf->pbuf + mbuf->data_offset); + pmadapter->pcard_sd + ->last_curr_wr_port[pmadapter->pcard_sd->last_mp_index] = + pmadapter->pcard_sd->curr_wr_port; + if (pmadapter->pcard_sd->mpa_buf) + memcpy_ext(pmadapter, + pmadapter->pcard_sd->mpa_buf + + (pmadapter->pcard_sd->last_mp_index * + mp_aggr_pkt_limit * + MLAN_SDIO_BLOCK_SIZE), + mbuf->pbuf + mbuf->data_offset, + MLAN_SDIO_BLOCK_SIZE, MLAN_SDIO_BLOCK_SIZE); + pmadapter->pcard_sd->last_mp_index++; + if (pmadapter->pcard_sd->last_mp_index >= SDIO_MP_DBG_NUM) + pmadapter->pcard_sd->last_mp_index = 0; + pmadapter->pcard_sd->mpa_tx_count[0]++; + } + if (f_postcopy_cur_buf) { + PRINTM(MINFO, "host_2_card_mp_aggr: Postcopy current buffer\n"); + if (pmadapter->pcard_sd->mpa_buf) + memcpy_ext( + pmadapter, + pmadapter->pcard_sd->mpa_buf + + (pmadapter->pcard_sd->last_mp_index * + mp_aggr_pkt_limit + + pmadapter->pcard_sd->mpa_tx.pkt_cnt) * + MLAN_SDIO_BLOCK_SIZE, + mbuf->pbuf + mbuf->data_offset, + MLAN_SDIO_BLOCK_SIZE, MLAN_SDIO_BLOCK_SIZE); + if (!pmadapter->pcard_sd->mpa_tx.buf) { + MP_TX_AGGR_BUF_PUT_SG(pmadapter, mbuf, port); + aggr_sg = MTRUE; + } else { + MP_TX_AGGR_BUF_PUT(pmadapter, mbuf, port); + } + } + /* Always return PENDING in SG mode */ + if (aggr_sg) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/******************************************************** + 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_sdio_check_winner_status(mlan_adapter *pmadapter, t_u32 *val) +{ + t_u32 winner = 0; + pmlan_callbacks pcb; + t_u8 card_winner_check_reg = pmadapter->pcard_sd->reg->winner_check_reg; + + ENTER(); + + pcb = &pmadapter->callbacks; + + if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, + card_winner_check_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_sdio_check_fw_status(mlan_adapter *pmadapter, t_u32 pollnum) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 firmwarestat = 0; + t_u32 tries; + + ENTER(); + + /* Wait for firmware initialization event */ + for (tries = 0; tries < pollnum; tries++) { + ret = wlan_sdio_read_fw_status(pmadapter, &firmwarestat); + if (MLAN_STATUS_SUCCESS != ret) + continue; + if (firmwarestat == SDIO_FIRMWARE_READY) { + ret = MLAN_STATUS_SUCCESS; + break; + } else { + wlan_mdelay(pmadapter, 100); + ret = MLAN_STATUS_FAILURE; + } + } + + if (ret != MLAN_STATUS_SUCCESS) { + if (pollnum > 1) + PRINTM(MERROR, + "Fail to poll firmware status: firmwarestat=0x%x\n", + firmwarestat); + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function enables the host interrupts. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_enable_sdio_host_int(pmlan_adapter pmadapter) +{ + mlan_status ret; + t_u8 mask = pmadapter->pcard_sd->reg->host_int_enable; + + ENTER(); + ret = wlan_sdio_enable_host_int_mask(pmadapter, mask); + 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_sdio_dnld_fw(pmlan_adapter pmadapter, pmlan_fw_image pmfw) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 poll_num = 1; + t_u32 winner = 0; + + ENTER(); + + /*when using GPIO wakeup, don't run the below code. + *if using GPIO wakeup, host will do handshake with FW + *to check if FW wake up and pull up SDIO line, then reload driver. + *So when using GPIO wakeup, don't need driver to do check wakeup status + *again. when using SDIO interface wakeup, run the below code; if using + *SDIO interface wakeup, driver need to do check wakeup status with FW. + */ + + /* Card specific probing */ + ret = wlan_sdio_probe(pmadapter); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "WLAN SDIO probe failed\n", ret); + LEAVE(); + return ret; + } + + /* Check if firmware is already running */ + ret = wlan_sdio_check_fw_status(pmadapter, poll_num); + if (ret == MLAN_STATUS_SUCCESS) { +#if defined(SDIO) + if (pmfw->fw_reload == FW_RELOAD_SDIO_INBAND_RESET) { + PRINTM(MMSG, "Try reset fw in mlan\n"); + ret = wlan_reset_fw(pmadapter); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "FW reset failure!"); + LEAVE(); + return ret; + } + } else { +#endif + PRINTM(MMSG, + "WLAN FW already running! Skip FW download\n"); +#if defined(SDIO) + pmadapter->ops.wakeup_card(pmadapter, MFALSE); +#endif + goto done; +#if defined(SDIO) + } +#endif + } + poll_num = MAX_FIRMWARE_POLL_TRIES; + + /* Check if other interface is downloading */ + ret = wlan_sdio_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; + } + + /* Download the firmware image via helper */ + ret = wlan_sdio_prog_fw_w_helper(pmadapter, pmfw->pfw_buf, + pmfw->fw_len); + 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_sdio_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_sdio_host_int(pmadapter); + + 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, + pmadapter->pcard_sd->reg->host_int_status_reg, + &sdio_ireg); + + /* Disable host interrupt mask register for SDIO */ + ret = wlan_disable_sdio_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 get sdio device from card type + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_get_sdio_device(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 card_type = pmadapter->card_type; + + ENTER(); + + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_sdio_card), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->pcard_sd); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pcard_sd) { + PRINTM(MERROR, "Failed to allocate pcard_sd\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + switch (card_type) { +#ifdef SD8887 + case CARD_TYPE_SD8887: + pmadapter->pcard_sd->reg = &mlan_reg_sd8887; + pmadapter->pcard_info = &mlan_card_info_sd8887; + break; +#endif +#ifdef SD8897 + case CARD_TYPE_SD8897: + pmadapter->pcard_sd->reg = &mlan_reg_sd8897; + pmadapter->pcard_info = &mlan_card_info_sd8897; + break; +#endif +#if defined(SD8977) || defined(SD8978) + case CARD_TYPE_SD8977: + case CARD_TYPE_SD8978: + pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; + pmadapter->pcard_info = &mlan_card_info_sd8977; + break; +#endif +#ifdef SD8997 + case CARD_TYPE_SD8997: + pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; + pmadapter->pcard_info = &mlan_card_info_sd8997; + break; +#endif +#ifdef SD8987 + case CARD_TYPE_SD8987: + pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; + pmadapter->pcard_info = &mlan_card_info_sd8987; + break; +#endif +#ifdef SD9098 + case CARD_TYPE_SD9098: + pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; + pmadapter->pcard_info = &mlan_card_info_sd9098; + break; +#endif +#ifdef SD9097 + case CARD_TYPE_SD9097: + pmadapter->pcard_sd->reg = &mlan_reg_sd8977_sd8997; + pmadapter->pcard_info = &mlan_card_info_sd9097; + break; +#endif + default: + PRINTM(MERROR, "can't get right card type \n"); + ret = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function gets interrupt status. + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_sdio_interrupt(t_u16 msg_id, pmlan_adapter pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer mbuf; + t_u32 sdio_ireg = 0; + t_u8 offset = 0; + t_u8 max_mp_regs = pmadapter->pcard_sd->reg->max_mp_regs; + t_u8 host_int_status_reg = + pmadapter->pcard_sd->reg->host_int_status_reg; + + ENTER(); + + while (max_mp_regs) { + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = pmadapter->pcard_sd->mp_regs + offset; + mbuf.data_len = MIN(max_mp_regs, MLAN_SDIO_BLOCK_SIZE); + + if (MLAN_STATUS_SUCCESS != + pcb->moal_read_data_sync(pmadapter->pmoal_handle, &mbuf, + (REG_PORT + offset) | + MLAN_SDIO_BYTE_MODE_MASK, + 0)) { + PRINTM(MERROR, + "moal_read_data_sync: read registers failed\n"); + pmadapter->dbg.num_int_read_failure++; + goto done; + } + offset += mbuf.data_len; + max_mp_regs -= mbuf.data_len; + } + + DBG_HEXDUMP(MIF_D, "SDIO MP Registers", pmadapter->pcard_sd->mp_regs, + max_mp_regs); + sdio_ireg = pmadapter->pcard_sd->mp_regs[host_int_status_reg]; + pmadapter->dbg.last_int_status = pmadapter->ireg | sdio_ireg; + if (sdio_ireg) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * DN_LD_CMD_PORT_HOST_INT_STATUS and/or + * UP_LD_CMD_PORT_HOST_INT_STATUS + * Clear the interrupt status register + */ + PRINTM(MINTR, "wlan_interrupt: sdio_ireg = 0x%x\n", sdio_ireg); + pmadapter->pcard_sd->num_of_irq++; + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pint_lock); + pmadapter->ireg |= sdio_ireg; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pint_lock); + if (!pmadapter->pps_uapsd_mode && + pmadapter->ps_state == PS_STATE_SLEEP) { + pmadapter->pm_wakeup_fw_try = MFALSE; + pmadapter->ps_state = PS_STATE_AWAKE; + pmadapter->pm_wakeup_card_req = MFALSE; + } + } else { + PRINTM(MMSG, "wlan_interrupt: sdio_ireg = 0x%x\n", sdio_ireg); + } +done: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function try to read the packet when fail to alloc rx buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * @param port Current port on which packet needs to be rxed + * @param rx_len Length of received packet + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_sdio_card_to_host_recovery(mlan_adapter *pmadapter, + t_u8 port, t_u16 rx_len) +{ + mlan_buffer mbuf; + t_u32 pkt_type = 0; + mlan_status ret = MLAN_STATUS_FAILURE; + ENTER(); + + if (MP_RX_AGGR_IN_PROGRESS(pmadapter)) { + PRINTM(MDATA, "Recovery:do Rx Aggr\n"); + /* do aggr RX now */ + wlan_receive_mp_aggr_buf(pmadapter); + } + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = pmadapter->pcard_sd->rx_buf; + mbuf.data_len = rx_len; + + PRINTM(MDATA, "Recovery: Try read port=%d rx_len=%d\n", port, rx_len); + if (MLAN_STATUS_SUCCESS != + wlan_sdio_card_to_host(pmadapter, &pkt_type, + (t_u32 *)&pmadapter->upld_len, &mbuf, rx_len, + pmadapter->pcard_sd->ioport + port)) { + PRINTM(MERROR, "Recovery: Fail to do cmd53\n"); + } + if (pkt_type != MLAN_TYPE_DATA && pkt_type != MLAN_TYPE_SPA_DATA) { + PRINTM(MERROR, + "Recovery: Receive a wrong pkt: type=%d, len=%d\n", + pkt_type, pmadapter->upld_len); + goto done; + } + if (pkt_type == MLAN_TYPE_DATA) { + // TODO fill the hole in Rx reorder table + PRINTM(MDATA, "Recovery: Drop Data packet\n"); + pmadapter->dbg.num_pkt_dropped++; + } else if (pkt_type == MLAN_TYPE_SPA_DATA) { + PRINTM(MDATA, "Recovery: SPA Data packet len=%d\n", + pmadapter->upld_len); + wlan_decode_spa_buffer(pmadapter, pmadapter->pcard_sd->rx_buf, + pmadapter->upld_len); + pmadapter->data_received = MTRUE; + } + PRINTM(MMSG, "wlan: Success handle rx port=%d, rx_len=%d \n", port, + rx_len); + ret = MLAN_STATUS_SUCCESS; +done: + LEAVE(); + return ret; +} + +/** + * @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_sdio_int_status(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 sdio_ireg; + mlan_buffer *pmbuf = MNULL; + + t_u8 port = 0; + t_u32 len_reg_l, len_reg_u; + t_u32 rx_blocks; + t_u8 bit_count = 0; + t_u32 ps_state = pmadapter->ps_state; + t_u16 rx_len; + t_u32 upld_typ = 0; + t_u32 cr = 0; + const mlan_sdio_card_reg *reg = pmadapter->pcard_sd->reg; + t_u8 rd_len_p0_l = reg->rd_len_p0_l; + t_u8 rd_len_p0_u = reg->rd_len_p0_u; + t_u8 cmd_rd_len_0 = reg->cmd_rd_len_0; + t_u8 cmd_rd_len_1 = reg->cmd_rd_len_1; + + ENTER(); + + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->pint_lock); + sdio_ireg = (t_u8)pmadapter->ireg; + pmadapter->ireg = 0; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, pmadapter->pint_lock); + + if (!sdio_ireg) + goto done; + + /* check the command port */ + if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS) { + if (pmadapter->cmd_sent) + pmadapter->cmd_sent = MFALSE; + + PRINTM(MINFO, "cmd_sent=%d\n", pmadapter->cmd_sent); + } + + if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { + /* read the len of control packet */ + rx_len = ((t_u16)pmadapter->pcard_sd->mp_regs[cmd_rd_len_1]) + << 8; + rx_len |= (t_u16)pmadapter->pcard_sd->mp_regs[cmd_rd_len_0]; + PRINTM(MINFO, "RX: cmd port rx_len=%u\n", rx_len); + rx_blocks = (rx_len + MLAN_SDIO_BLOCK_SIZE - 1) / + MLAN_SDIO_BLOCK_SIZE; + if (rx_len <= SDIO_INTF_HEADER_LEN || + (rx_blocks * MLAN_SDIO_BLOCK_SIZE) > ALLOC_BUF_SIZE) { + PRINTM(MERROR, "invalid rx_len=%d\n", rx_len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + rx_len = (t_u16)(rx_blocks * MLAN_SDIO_BLOCK_SIZE); + pmbuf = wlan_alloc_mlan_buffer(pmadapter, rx_len, 0, + MOAL_MALLOC_BUFFER); + if (pmbuf == MNULL) { + PRINTM(MERROR, "Failed to allocate 'mlan_buffer'\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MINFO, "cmd rx buffer rx_len = %d\n", rx_len); + + /* Transfer data from card */ + if (MLAN_STATUS_SUCCESS != + wlan_sdio_card_to_host( + pmadapter, &upld_typ, (t_u32 *)&pmadapter->upld_len, + pmbuf, rx_len, + pmadapter->pcard_sd->ioport | CMD_PORT_SLCT)) { + pmadapter->dbg.num_cmdevt_card_to_host_failure++; + PRINTM(MERROR, + "Card-to-host cmd failed: int status=0x%x\n", + sdio_ireg); + wlan_free_mlan_buffer(pmadapter, pmbuf); + ret = MLAN_STATUS_FAILURE; + goto term_cmd53; + } + + if ((upld_typ != MLAN_TYPE_CMD) && + (upld_typ != MLAN_TYPE_EVENT)) + PRINTM(MERROR, + "receive a wrong packet from CMD PORT. type =0x%x\n", + upld_typ); + + wlan_decode_rx_packet(pmadapter, pmbuf, upld_typ, MFALSE); + + /* We might receive data/sleep_cfm at the same time */ + /* reset data_receive flag to avoid ps_state change */ + if ((ps_state == PS_STATE_SLEEP_CFM) && + (pmadapter->ps_state == PS_STATE_SLEEP)) + pmadapter->data_received = MFALSE; + } + + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { + if (pmadapter->pcard_sd->mp_wr_bitmap & + pmadapter->pcard_sd->mp_data_port_mask) + pmadapter->pcard_sd->mp_invalid_update++; + pmadapter->pcard_sd->mp_wr_bitmap = + (t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_l]; + pmadapter->pcard_sd->mp_wr_bitmap |= + ((t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_u]) + << 8; + pmadapter->pcard_sd->mp_wr_bitmap |= + ((t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_1l]) + << 16; + pmadapter->pcard_sd->mp_wr_bitmap |= + ((t_u32)pmadapter->pcard_sd->mp_regs[reg->wr_bitmap_1u]) + << 24; + bit_count = bitcount(pmadapter->pcard_sd->mp_wr_bitmap & + pmadapter->pcard_sd->mp_data_port_mask); + if (bit_count) { + pmadapter->pcard_sd->mp_update[bit_count - 1]++; + if (pmadapter->pcard_sd->mp_update[bit_count - 1] == + 0xffffffff) + memset(pmadapter, + pmadapter->pcard_sd->mp_update, 0, + sizeof(pmadapter->pcard_sd->mp_update)); + } + + pmadapter->pcard_sd->last_recv_wr_bitmap = + pmadapter->pcard_sd->mp_wr_bitmap; + PRINTM(MINTR, "DNLD: wr_bitmap=0x%08x\n", + pmadapter->pcard_sd->mp_wr_bitmap); + if (pmadapter->data_sent && + (pmadapter->pcard_sd->mp_wr_bitmap & + (1 << pmadapter->pcard_sd->curr_wr_port))) { + PRINTM(MINFO, " <--- Tx DONE Interrupt --->\n"); + pmadapter->data_sent = MFALSE; + } + } + + if (sdio_ireg & UP_LD_HOST_INT_STATUS) { + pmadapter->pcard_sd->mp_rd_bitmap = + (t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_l]; + pmadapter->pcard_sd->mp_rd_bitmap |= + ((t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_u]) + << 8; + pmadapter->pcard_sd->mp_rd_bitmap |= + ((t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_1l]) + << 16; + pmadapter->pcard_sd->mp_rd_bitmap |= + ((t_u32)pmadapter->pcard_sd->mp_regs[reg->rd_bitmap_1u]) + << 24; + PRINTM(MINTR, "UPLD: rd_bitmap=0x%08x\n", + pmadapter->pcard_sd->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->pcard_sd->mp_regs[len_reg_u]) + << 8; + rx_len |= + (t_u16)pmadapter->pcard_sd->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 <= SDIO_INTF_HEADER_LEN || + (rx_blocks * MLAN_SDIO_BLOCK_SIZE) > + pmadapter->pcard_sd->mpa_rx.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 (rx_len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) + pmbuf = wlan_alloc_mlan_buffer( + pmadapter, rx_len, 0, + MOAL_MALLOC_BUFFER); + else + pmbuf = wlan_alloc_mlan_buffer( + pmadapter, rx_len, MLAN_RX_HEADER_LEN, + MOAL_ALLOC_MLAN_BUFFER); + if (pmbuf == MNULL) { + PRINTM(MERROR, + "Failed to allocate 'mlan_buffer'\n"); + pmadapter->dbg.num_alloc_buffer_failure++; + if (MLAN_STATUS_SUCCESS == + wlan_sdio_card_to_host_recovery( + pmadapter, port, rx_len)) + continue; + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MINFO, "rx_len = %d\n", rx_len); + if (MLAN_STATUS_SUCCESS != + wlan_sdio_card_to_host_mp_aggr(pmadapter, pmbuf, + port, rx_len)) { + pmadapter->dbg.num_rx_card_to_host_failure++; + + PRINTM(MERROR, + "Card to host failed: int status=0x%x\n", + sdio_ireg); + ret = MLAN_STATUS_FAILURE; + goto term_cmd53; + } + } + /* We might receive data/sleep_cfm at the same time */ + /* reset data_receive flag to avoid ps_state change */ + if ((ps_state == PS_STATE_SLEEP_CFM) && + (pmadapter->ps_state == PS_STATE_SLEEP)) + pmadapter->data_received = MFALSE; + } + + ret = MLAN_STATUS_SUCCESS; + goto done; + +term_cmd53: + /* terminate cmd53 */ + if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + &cr)) + PRINTM(MERROR, "read CFG reg failed\n"); + PRINTM(MINFO, "Config Reg val = %d\n", cr); + if (MLAN_STATUS_SUCCESS != pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + (cr | HOST_TERM_CMD53))) + PRINTM(MERROR, "write CFG reg failed\n"); + PRINTM(MINFO, "write success\n"); + if (MLAN_STATUS_SUCCESS != pcb->moal_read_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + &cr)) + PRINTM(MERROR, "read CFG reg failed\n"); + PRINTM(MINFO, "Config reg val =%x\n", cr); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sends data to the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param type data or command + * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include + * SDIO header) + * @param tx_param A pointer to mlan_tx_param + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_sdio_host_to_card(mlan_adapter *pmadapter, t_u8 type, + mlan_buffer *pmbuf, mlan_tx_param *tx_param) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 buf_block_len; + t_u32 blksz; + t_u8 port = 0; + t_u32 cmd53_port = 0; + t_u8 *payload = pmbuf->pbuf + pmbuf->data_offset; + + ENTER(); + + /* Allocate buffer and copy payload */ + blksz = MLAN_SDIO_BLOCK_SIZE; + buf_block_len = (pmbuf->data_len + blksz - 1) / blksz; + *(t_u16 *)&payload[0] = wlan_cpu_to_le16((t_u16)pmbuf->data_len); + *(t_u16 *)&payload[2] = wlan_cpu_to_le16(type); + + /* + * This is SDIO specific header + * t_u16 length, + * t_u16 type (MLAN_TYPE_DATA = 0, + * MLAN_TYPE_CMD = 1, MLAN_TYPE_EVENT = 3) + */ + if (type == MLAN_TYPE_DATA) { + ret = wlan_get_wr_port_data(pmadapter, &port); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "no wr_port available: wr_bitmap=0x%08x curr_wr_port=%d\n", + pmadapter->pcard_sd->mp_wr_bitmap, + pmadapter->pcard_sd->curr_wr_port); + goto exit; + } + /* Transfer data to card */ + pmbuf->data_len = buf_block_len * blksz; + + 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 { + /*Type must be MLAN_TYPE_CMD*/ + pmadapter->cmd_sent = MTRUE; + if (pmbuf->data_len <= SDIO_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->pcard_sd->ioport) | CMD_PORT_SLCT; + ret = wlan_write_data_sync(pmadapter, pmbuf, cmd53_port); + } + + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "Error: host_to_card failed: 0x%X\n", ret); + 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->pcard_sd->mp_wr_bitmap & + (1 << pmadapter->pcard_sd->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(SD9098) || defined(SD9097)) +/** + * @brief This function sends vdll 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) + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_sdio_send_vdll(mlan_adapter *pmadapter, mlan_buffer *pmbuf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 buf_block_len; + t_u32 blksz; + t_u8 *payload = pmbuf->pbuf + pmbuf->data_offset; + t_u32 cmd53_port = 0; + ENTER(); + 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(MLAN_TYPE_VDLL); + + pmbuf->data_len = buf_block_len * blksz; + + if (pmbuf->data_len > MRVDRV_SIZE_OF_CMD_BUFFER) { + PRINTM(MERROR, "VDLL block is too big: %d\n", pmbuf->data_len); + return MLAN_STATUS_FAILURE; + } + cmd53_port = (pmadapter->pcard_sd->ioport) | CMD_PORT_SLCT; + pmadapter->cmd_sent = MTRUE; + ret = wlan_write_data_sync(pmadapter, pmbuf, cmd53_port); + if (ret == MLAN_STATUS_FAILURE) + PRINTM(MERROR, "Send Vdll: host_to_card failed: 0x%X\n", ret); + else + DBG_HEXDUMP(MIF_D, "SDIO Blk Wr", + pmbuf->pbuf + pmbuf->data_offset, + MIN(pmbuf->data_len, MAX_DATA_DUMP_LEN)); + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function sends data to the card. + * + * @param pmpriv A pointer to mlan_private 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_ext(pmlan_private pmpriv, t_u8 type, + mlan_buffer *pmbuf, + mlan_tx_param *tx_param) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = pmpriv->adapter; + +#if (defined(SD9098) || defined(SD9097)) + if (type == MLAN_TYPE_VDLL) + return wlan_sdio_send_vdll(pmadapter, pmbuf); +#endif + ret = wlan_sdio_host_to_card(pmadapter, type, pmbuf, tx_param); + + if (type == MLAN_TYPE_DATA && ret == MLAN_STATUS_FAILURE) + pmadapter->data_sent = MFALSE; + + LEAVE(); + return ret; +} + +/** + * @brief Deaggregate single port aggregation packet + * + * @param pmadapter A pointer to mlan_adapter structure + * @param buf A pointer to aggregated data packet + * @param len + * + * @return N/A + */ +void wlan_decode_spa_buffer(mlan_adapter *pmadapter, t_u8 *buf, t_u32 len) +{ + int total_pkt_len; + t_u8 block_num = 0; + t_u16 block_size = 0; + t_u8 *data; + t_u32 pkt_len, pkt_type = 0; + mlan_buffer *mbuf_deaggr = MNULL; + + ENTER(); + + data = (t_u8 *)buf; + total_pkt_len = len; + if (total_pkt_len < pmadapter->pcard_sd->sdio_rx_block_size) { + PRINTM(MERROR, "Invalid sp aggr packet size=%d\n", + total_pkt_len); + goto done; + } + while (total_pkt_len >= + (OFFSET_OF_SDIO_HEADER + SDIO_INTF_HEADER_LEN)) { + block_num = *(data + OFFSET_OF_BLOCK_NUMBER); + block_size = + pmadapter->pcard_sd->sdio_rx_block_size * block_num; + if (block_size > total_pkt_len) { + PRINTM(MERROR, + "Error in pkt, block_num=%d, pkt_len=%d\n", + block_num, total_pkt_len); + break; + } + pkt_len = wlan_le16_to_cpu( + *(t_u16 *)(data + OFFSET_OF_SDIO_HEADER)); + pkt_type = wlan_le16_to_cpu( + *(t_u16 *)(data + OFFSET_OF_SDIO_HEADER + 2)); + if ((pkt_len + OFFSET_OF_SDIO_HEADER) > block_size) { + PRINTM(MERROR, + "Error in pkt, pkt_len=%d, block_size=%d\n", + pkt_len, block_size); + break; + } + mbuf_deaggr = wlan_alloc_mlan_buffer( + pmadapter, pkt_len - SDIO_INTF_HEADER_LEN, + MLAN_RX_HEADER_LEN, MOAL_ALLOC_MLAN_BUFFER); + if (mbuf_deaggr == MNULL) { + PRINTM(MERROR, "Error allocating daggr mlan_buffer\n"); + break; + } + memcpy_ext(pmadapter, + mbuf_deaggr->pbuf + mbuf_deaggr->data_offset, + data + OFFSET_OF_SDIO_HEADER + SDIO_INTF_HEADER_LEN, + pkt_len - SDIO_INTF_HEADER_LEN, + pkt_len - SDIO_INTF_HEADER_LEN); + mbuf_deaggr->data_len = pkt_len - SDIO_INTF_HEADER_LEN; + wlan_handle_rx_packet(pmadapter, mbuf_deaggr); + data += block_size; + total_pkt_len -= block_size; + if (total_pkt_len < pmadapter->pcard_sd->sdio_rx_block_size) + break; + } +done: + LEAVE(); + return; +} + +/** + * @brief This function deaggr rx pkt + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the SDIO mpa data + * @return N/A + */ +t_void wlan_sdio_deaggr_rx_pkt(pmlan_adapter pmadapter, mlan_buffer *pmbuf) +{ + if (pmbuf->buf_type == MLAN_BUF_TYPE_SPA_DATA) { + wlan_decode_spa_buffer(pmadapter, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + wlan_free_mlan_buffer(pmadapter, pmbuf); + } else + wlan_handle_rx_packet(pmadapter, pmbuf); +} + +/** + * @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(mlan_adapter *pmadapter, + t_u32 mpa_tx_buf_size, + t_u32 mpa_rx_buf_size) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + + ENTER(); + + if ((pmadapter->pcard_sd->max_segs < mp_aggr_pkt_limit) || + (pmadapter->pcard_sd->max_seg_size < + pmadapter->pcard_sd->max_sp_tx_size)) { + ret = pcb->moal_malloc( + pmadapter->pmoal_handle, + mpa_tx_buf_size + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->pcard_sd->mpa_tx.head_ptr); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->pcard_sd->mpa_tx.head_ptr) { + PRINTM(MERROR, + "Could not allocate buffer for SDIO MP TX aggr\n"); + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->pcard_sd->mpa_tx.buf = (t_u8 *)ALIGN_ADDR( + pmadapter->pcard_sd->mpa_tx.head_ptr, DMA_ALIGNMENT); + } else { + PRINTM(MMSG, "wlan: Enable TX SG mode\n"); + pmadapter->pcard_sd->mpa_tx.head_ptr = MNULL; + pmadapter->pcard_sd->mpa_tx.buf = MNULL; + } + pmadapter->pcard_sd->mpa_tx.buf_size = mpa_tx_buf_size; + + if ((pmadapter->pcard_sd->max_segs < mp_aggr_pkt_limit) || + (pmadapter->pcard_sd->max_seg_size < + pmadapter->pcard_sd->max_sp_rx_size)) { + ret = pcb->moal_malloc( + pmadapter->pmoal_handle, + mpa_rx_buf_size + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->pcard_sd->mpa_rx.head_ptr); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->pcard_sd->mpa_rx.head_ptr) { + PRINTM(MERROR, + "Could not allocate buffer for SDIO MP RX aggr\n"); + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->pcard_sd->mpa_rx.buf = (t_u8 *)ALIGN_ADDR( + pmadapter->pcard_sd->mpa_rx.head_ptr, DMA_ALIGNMENT); + } else { + PRINTM(MMSG, "wlan: Enable RX SG mode\n"); + pmadapter->pcard_sd->mpa_rx.head_ptr = MNULL; + pmadapter->pcard_sd->mpa_rx.buf = MNULL; + } + pmadapter->pcard_sd->mpa_rx.buf_size = mpa_rx_buf_size; +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(mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + + if (pmadapter->pcard_sd->mpa_tx.buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_sd->mpa_tx.head_ptr); + pmadapter->pcard_sd->mpa_tx.head_ptr = MNULL; + pmadapter->pcard_sd->mpa_tx.buf = MNULL; + pmadapter->pcard_sd->mpa_tx.buf_size = 0; + } + + if (pmadapter->pcard_sd->mpa_rx.buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_sd->mpa_rx.head_ptr); + pmadapter->pcard_sd->mpa_rx.head_ptr = MNULL; + pmadapter->pcard_sd->mpa_rx.buf = MNULL; + pmadapter->pcard_sd->mpa_rx.buf_size = 0; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function re-allocate rx mpa buffer + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_re_alloc_sdio_rx_mpa_buffer(mlan_adapter *pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 mp_aggr_pkt_limit = SDIO_MP_AGGR_DEF_PKT_LIMIT; + t_u32 mpa_rx_buf_size = SDIO_MP_AGGR_BUF_SIZE_MAX; + + if (pmadapter->pcard_sd->mpa_rx.buf) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_sd->mpa_rx.head_ptr); + pmadapter->pcard_sd->mpa_rx.head_ptr = MNULL; + pmadapter->pcard_sd->mpa_rx.buf = MNULL; + pmadapter->pcard_sd->mpa_rx.buf_size = 0; + } + if (pmadapter->pcard_sd->sdio_rx_aggr_enable) { + mpa_rx_buf_size = MAX(mpa_rx_buf_size, SDIO_CMD53_MAX_SIZE); + /** reallocate rx buffer for recover when single port rx + * aggregation enabled */ + if (pmadapter->pcard_sd->rx_buffer) { + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->pcard_sd->rx_buffer); + pmadapter->pcard_sd->rx_buffer = MNULL; + pmadapter->pcard_sd->rx_buf = MNULL; + } + ret = pmadapter->callbacks.moal_malloc( + pmadapter->pmoal_handle, + SDIO_CMD53_MAX_SIZE + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->pcard_sd->rx_buffer); + + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->pcard_sd->rx_buffer) { + PRINTM(MERROR, "Failed to allocate receive buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->pcard_sd->rx_buf = (t_u8 *)ALIGN_ADDR( + pmadapter->pcard_sd->rx_buffer, DMA_ALIGNMENT); + } + if ((pmadapter->pcard_sd->max_segs < mp_aggr_pkt_limit) || + (pmadapter->pcard_sd->max_seg_size < + pmadapter->pcard_sd->max_sp_rx_size)) { + ret = pcb->moal_malloc( + pmadapter->pmoal_handle, + mpa_rx_buf_size + DMA_ALIGNMENT, + MLAN_MEM_DEF | MLAN_MEM_DMA, + (t_u8 **)&pmadapter->pcard_sd->mpa_rx.head_ptr); + if (ret != MLAN_STATUS_SUCCESS || + !pmadapter->pcard_sd->mpa_rx.head_ptr) { + PRINTM(MERROR, + "Could not allocate buffer for SDIO MP RX aggr\n"); + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->pcard_sd->mpa_rx.buf = (t_u8 *)ALIGN_ADDR( + pmadapter->pcard_sd->mpa_rx.head_ptr, DMA_ALIGNMENT); + } else { + PRINTM(MMSG, "wlan: Enable RX SG mode\n"); + pmadapter->pcard_sd->mpa_rx.head_ptr = MNULL; + pmadapter->pcard_sd->mpa_rx.buf = MNULL; + } + pmadapter->pcard_sd->mpa_rx.buf_size = mpa_rx_buf_size; + PRINTM(MMSG, "mpa_rx_buf_size=%d\n", mpa_rx_buf_size); +error: + return ret; +} + +/** + * @brief This function wakes up the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param timeout set timeout flag + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_pm_sdio_wakeup_card(pmlan_adapter pmadapter, t_u8 timeout) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 age_ts_usec; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + PRINTM(MEVENT, "Wakeup device...\n"); + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->pm_wakeup_in_secs, + &age_ts_usec); + + if (timeout) { + pmadapter->callbacks.moal_start_timer( + pmadapter->pmoal_handle, pmadapter->pwakeup_fw_timer, + MFALSE, MRVDRV_TIMER_3S); + pmadapter->wakeup_fw_timer_is_set = MTRUE; + } + + 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_sdio_reset_card(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 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(pmlan_private priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = MNULL; + HostCmd_DS_SDIO_GPIO_INT_CONFIG sdio_int_cfg; + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter = priv->adapter; + + ENTER(); + + if (pmadapter->pcard_sd->int_mode == INT_MODE_GPIO) { + if (pmadapter->pcard_sd->gpio_pin != GPIO_INT_NEW_MODE) { + PRINTM(MINFO, + "SDIO_GPIO_INT_CONFIG: interrupt mode is GPIO\n"); + sdio_int_cfg.action = HostCmd_ACT_GEN_SET; + sdio_int_cfg.gpio_pin = pmadapter->pcard_sd->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, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + 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_ext(pmpriv->adapter, psdio_gpio_int, pdata_buf, + sizeof(HostCmd_DS_SDIO_GPIO_INT_CONFIG), + 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; +} + +mlan_status wlan_reset_fw(pmlan_adapter pmadapter) +{ + t_u32 tries = 0; + t_u32 value = 1; + t_u32 reset_reg = pmadapter->pcard_sd->reg->fw_reset_reg; + t_u8 reset_val = pmadapter->pcard_sd->reg->fw_reset_val; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + wlan_pm_sdio_wakeup_card(pmadapter, MFALSE); + + /** wait SOC fully wake up */ + for (tries = 0; tries < MAX_POLL_TRIES; ++tries) { + if (MLAN_STATUS_SUCCESS == + pcb->moal_write_reg(pmadapter->pmoal_handle, reset_reg, + 0xba)) { + pcb->moal_read_reg(pmadapter->pmoal_handle, reset_reg, + &value); + if (value == 0xba) { + PRINTM(MMSG, "FW wake up\n"); + break; + } + } + pcb->moal_udelay(pmadapter->pmoal_handle, 1000); + } + /* Write register to notify FW */ + if (MLAN_STATUS_FAILURE == pcb->moal_write_reg(pmadapter->pmoal_handle, + reset_reg, reset_val)) { + PRINTM(MERROR, "Failed to write register.\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#if defined(SD8997) || defined(SD8977) || defined(SD8987) || \ + defined(SD9098) || defined(SD9097) || defined(SD8978) + if (MFALSE +#ifdef SD8997 + || IS_SD8997(pmadapter->card_type) +#endif +#ifdef SD8977 + || IS_SD8977(pmadapter->card_type) +#endif +#ifdef SD8978 + || IS_SD8978(pmadapter->card_type) +#endif +#ifdef SD8987 + || IS_SD8987(pmadapter->card_type) +#endif +#ifdef SD9098 + || IS_SD9098(pmadapter->card_type) +#endif +#ifdef SD9097 + || IS_SD9097(pmadapter->card_type) +#endif + ) { + pcb->moal_read_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, &value); + pcb->moal_write_reg(pmadapter->pmoal_handle, + HOST_TO_CARD_EVENT_REG, + value | HOST_POWER_UP); + } +#endif + /* Poll register around 100 ms */ + for (tries = 0; tries < MAX_POLL_TRIES; ++tries) { + pcb->moal_read_reg(pmadapter->pmoal_handle, reset_reg, &value); + if (value == 0) + /* FW is ready */ + break; + pcb->moal_udelay(pmadapter->pmoal_handle, 1000); + } + + if (value) { + PRINTM(MERROR, "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MMSG, "FW Reset success\n"); + ret = wlan_sdio_probe(pmadapter); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handle event/data/cmd complete + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the mlan_buffer + * @return N/A + */ +mlan_status wlan_sdio_data_evt_complete(pmlan_adapter pmadapter, + mlan_buffer *pmbuf, mlan_status status) +{ + ENTER(); + + wlan_free_mlan_buffer(pmadapter, pmbuf); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handle receive packet + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the mlan_buffer + * @return + */ +mlan_status wlan_sdio_handle_rx_packet(mlan_adapter *pmadapter, + pmlan_buffer pmbuf) +{ + ENTER(); + + wlan_sdio_deaggr_rx_pkt(pmadapter, pmbuf); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +mlan_adapter_operations mlan_sdio_ops = { + .dnld_fw = wlan_sdio_dnld_fw, + .interrupt = wlan_sdio_interrupt, + .process_int_status = wlan_process_sdio_int_status, + .host_to_card = wlan_sdio_host_to_card_ext, + .wakeup_card = wlan_pm_sdio_wakeup_card, + .reset_card = wlan_pm_sdio_reset_card, + .event_complete = wlan_sdio_data_evt_complete, + .data_complete = wlan_sdio_data_evt_complete, + .cmdrsp_complete = wlan_sdio_data_evt_complete, + .handle_rx_packet = wlan_sdio_handle_rx_packet, + + .intf_header_len = SDIO_INTF_HEADER_LEN, +}; diff --git a/mxm_wifiex/wlan_src/mlan/mlan_sdio.h b/mxm_wifiex/wlan_src/mlan/mlan_sdio.h new file mode 100644 index 0000000..6f65336 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_sdio.h @@ -0,0 +1,550 @@ +/** @file mlan_sdio.h + * + * @brief This file contains definitions for SDIO interface. + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ +/**************************************************** +Change log: +****************************************************/ + +#ifndef _MLAN_SDIO_H +#define _MLAN_SDIO_H + +/** Block mode */ +#ifndef BLOCK_MODE +#define BLOCK_MODE 1 +#endif + +/** Fixed address mode */ +#ifndef FIXED_ADDRESS +#define FIXED_ADDRESS 0 +#endif + +/* Host Control Registers */ +/** Host Control Registers : Host to Card Event */ +#define HOST_TO_CARD_EVENT_REG 0x00 +/** Host Control Registers : Host terminates Command 53 */ +#define HOST_TERM_CMD53 (0x1U << 2) +/** Host Control Registers : Host without Command 53 finish host */ +#define HOST_WO_CMD53_FINISH_HOST (0x1U << 2) +/** Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) +/** Host Control Registers : Host power down */ +#define HOST_POWER_DOWN (0x1U << 0) + +/** Host Control Registers : Upload host interrupt RSR */ +#define UP_LD_HOST_INT_RSR (0x1U) +#define HOST_INT_RSR_MASK 0xFF + +/** Host Control Registers : Upload command port host interrupt status */ +#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) +/** Host Control Registers : Download command port host interrupt status */ +#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) + +/** Host Control Registers : Upload host interrupt mask */ +#define UP_LD_HOST_INT_MASK (0x1U) +/** Host Control Registers : Download host interrupt mask */ +#define DN_LD_HOST_INT_MASK (0x2U) +/** Host Control Registers : Cmd port upload interrupt mask */ +#define CMD_PORT_UPLD_INT_MASK (0x1U << 6) +/** Host Control Registers : Cmd port download interrupt mask */ +#define CMD_PORT_DNLD_INT_MASK (0x1U << 7) +/** Enable Host interrupt mask */ +#define HIM_ENABLE \ + (UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | CMD_PORT_UPLD_INT_MASK | \ + CMD_PORT_DNLD_INT_MASK) +/** Disable Host interrupt mask */ +#define HIM_DISABLE 0xff + +/** Host Control Registers : Upload host interrupt status */ +#define UP_LD_HOST_INT_STATUS (0x1U) +/** Host Control Registers : Download host interrupt status */ +#define DN_LD_HOST_INT_STATUS (0x2U) + +/** Host Control Registers : 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 : Command port upload ready */ +#define UP_LD_CP_RDY (0x1U << 6) +/** Card Control Registers : Command port download ready */ +#define DN_LD_CP_RDY (0x1U << 7) +/** Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/** Card Control Registers : CIS card ready */ +#define CIS_CARD_RDY (0x1U << 2) +/** Card Control Registers : Upload card ready */ +#define UP_LD_CARD_RDY (0x1U << 1) +/** Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/** Card Control Registers : Host 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 : Power up interrupt */ +#define POWER_UP_INT (0x1U << 4) +/** Card Control Registers : Power down interrupt */ +#define POWER_DOWN_INT (0x1U << 3) + +/** 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 : SD test BUS 0 */ +#define SD_TESTBUS0 (0x1U) +/** Card Control Registers : SD test BUS 1 */ +#define SD_TESTBUS1 (0x1U) +/** Card Control Registers : SD test BUS 2 */ +#define SD_TESTBUS2 (0x1U) +/** Card Control Registers : SD test BUS 3 */ +#define SD_TESTBUS3 (0x1U) + +/** Port for registers */ +#define REG_PORT 0 +/** Port for memory */ +#define MEM_PORT 0x10000 + +/** Card Control Registers : cmd53 new mode */ +#define CMD53_NEW_MODE (0x1U << 0) +/** Card Control Registers : cmd53 tx len format 1 (0x10) */ +#define CMD53_TX_LEN_FORMAT_1 (0x1U << 4) +/** Card Control Registers : cmd53 tx len format 2 (0x20)*/ +#define CMD53_TX_LEN_FORMAT_2 (0x1U << 5) +/** Card Control Registers : cmd53 rx len format 1 (0x40) */ +#define CMD53_RX_LEN_FORMAT_1 (0x1U << 6) +/** Card Control Registers : cmd53 rx len format 2 (0x80)*/ +#define CMD53_RX_LEN_FORMAT_2 (0x1U << 7) + +#define CMD_PORT_RD_LEN_EN (0x1U << 2) +/* Card Control Registers : cmd port auto enable */ +#define CMD_PORT_AUTO_EN (0x1U << 0) + +/* Command port */ +#define CMD_PORT_SLCT 0x8000 + +/** Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT MBIT(4) + +/** Enable GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1*/ +#define ENABLE_GPIO_1_INT_MODE 0x88 +/** Scratch reg 3 2 : Configure GPIO-1 INT*/ +#define SCRATCH_REG_32 0xEE + +/** Event header Len*/ +#define MLAN_EVENT_HEADER_LEN 8 + +/** SDIO byte mode size */ +#define MAX_BYTE_MODE_SIZE 512 + +/** The base address for packet with multiple ports aggregation */ +#define SDIO_MPA_ADDR_BASE 0x1000 + +/** SDIO Tx aggregation in progress ? */ +#define MP_TX_AGGR_IN_PROGRESS(a) (a->pcard_sd->mpa_tx.pkt_cnt > 0) + +/** SDIO Tx aggregation buffer room for next packet ? */ +#define MP_TX_AGGR_BUF_HAS_ROOM(a, mbuf, len) \ + (((a->pcard_sd->mpa_tx.buf_len) + len) <= \ + (a->pcard_sd->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->pcard_sd->mpa_tx.buf[a->pcard_sd->mpa_tx.buf_len], \ + mbuf->pbuf + mbuf->data_offset, mbuf->data_len); \ + a->pcard_sd->mpa_tx.buf_len += mbuf->data_len; \ + a->pcard_sd->mpa_tx.mp_wr_info[a->pcard_sd->mpa_tx.pkt_cnt] = \ + *(t_u16 *)(mbuf->pbuf + mbuf->data_offset); \ + if (!a->pcard_sd->mpa_tx.pkt_cnt) { \ + a->pcard_sd->mpa_tx.start_port = port; \ + } \ + a->pcard_sd->mpa_tx.ports |= (1 << port); \ + a->pcard_sd->mpa_tx.pkt_cnt++; \ + } while (0) + +#define MP_TX_AGGR_BUF_PUT_SG(a, mbuf, port) \ + do { \ + a->pcard_sd->mpa_tx.buf_len += mbuf->data_len; \ + a->pcard_sd->mpa_tx.mp_wr_info[a->pcard_sd->mpa_tx.pkt_cnt] = \ + *(t_u16 *)(mbuf->pbuf + mbuf->data_offset); \ + a->pcard_sd->mpa_tx.mbuf_arr[a->pcard_sd->mpa_tx.pkt_cnt] = \ + mbuf; \ + if (!a->pcard_sd->mpa_tx.pkt_cnt) { \ + a->pcard_sd->mpa_tx.start_port = port; \ + } \ + a->pcard_sd->mpa_tx.ports |= (1 << port); \ + a->pcard_sd->mpa_tx.pkt_cnt++; \ + } while (0) +/** SDIO Tx aggregation limit ? */ +#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \ + ((a->pcard_sd->mpa_tx.pkt_cnt) == (a->pcard_sd->mpa_tx.pkt_aggr_limit)) + +/** Reset SDIO Tx aggregation buffer parameters */ +#define MP_TX_AGGR_BUF_RESET(a) \ + do { \ + memset(a, a->pcard_sd->mpa_tx.mp_wr_info, 0, \ + sizeof(a->pcard_sd->mpa_tx.mp_wr_info)); \ + a->pcard_sd->mpa_tx.pkt_cnt = 0; \ + a->pcard_sd->mpa_tx.buf_len = 0; \ + a->pcard_sd->mpa_tx.ports = 0; \ + a->pcard_sd->mpa_tx.start_port = 0; \ + } while (0) + +/** SDIO Rx aggregation limit ? */ +#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \ + (a->pcard_sd->mpa_rx.pkt_cnt == a->pcard_sd->mpa_rx.pkt_aggr_limit) + +/** SDIO Rx aggregation port limit ? */ +/** this is for test only, because port 0 is reserved for control port */ +/* #define MP_RX_AGGR_PORT_LIMIT_REACHED(a) (a->curr_rd_port == 1) */ + +/* receive packets aggregated up to a half of mp_end_port */ +/* note: hw rx wraps round only after port (MAX_PORT-1) */ +#define MP_RX_AGGR_PORT_LIMIT_REACHED(a) \ + (((a->pcard_sd->curr_rd_port < a->pcard_sd->mpa_rx.start_port) && \ + (((MAX_PORT - a->pcard_sd->mpa_rx.start_port) + \ + a->pcard_sd->curr_rd_port) >= (a->pcard_sd->mp_end_port >> 1))) || \ + ((a->pcard_sd->curr_rd_port - a->pcard_sd->mpa_rx.start_port) >= \ + (a->pcard_sd->mp_end_port >> 1))) + +/** SDIO Rx aggregation in progress ? */ +#define MP_RX_AGGR_IN_PROGRESS(a) (a->pcard_sd->mpa_rx.pkt_cnt > 0) + +/** SDIO Rx aggregation buffer room for next packet ? */ +#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ + ((a->pcard_sd->mpa_rx.buf_len + rx_len) <= a->pcard_sd->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->pcard_sd->mpa_rx.buf_len += rx_len; \ + if (!a->pcard_sd->mpa_rx.pkt_cnt) { \ + a->pcard_sd->mpa_rx.start_port = port; \ + } \ + a->pcard_sd->mpa_rx.ports |= (1 << port); \ + a->pcard_sd->mpa_rx.mbuf_arr[a->pcard_sd->mpa_rx.pkt_cnt] = \ + mbuf; \ + a->pcard_sd->mpa_rx.len_arr[a->pcard_sd->mpa_rx.pkt_cnt] = \ + rx_len; \ + a->pcard_sd->mpa_rx.pkt_cnt++; \ + } while (0) + +/** Reset SDIO Rx aggregation buffer parameters */ +#define MP_RX_AGGR_BUF_RESET(a) \ + do { \ + a->pcard_sd->mpa_rx.pkt_cnt = 0; \ + a->pcard_sd->mpa_rx.buf_len = 0; \ + a->pcard_sd->mpa_rx.ports = 0; \ + a->pcard_sd->mpa_rx.start_port = 0; \ + } while (0) + +/** aggr buf size 32k */ +#define SDIO_MP_AGGR_BUF_SIZE_32K (32768) +/** max aggr buf size 64k-256 */ +#define SDIO_MP_AGGR_BUF_SIZE_MAX (65280) + +#ifdef SD8887 +static const struct _mlan_sdio_card_reg mlan_reg_sd8887 = { + .start_rd_port = 0, + .start_wr_port = 0, + .base_0_reg = 0x6C, + .base_1_reg = 0x6D, + .poll_reg = 0x5C, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .host_int_status = DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS | + DN_LD_CMD_PORT_HOST_INT_STATUS | + UP_LD_CMD_PORT_HOST_INT_STATUS, + .status_reg_0 = 0x90, + .status_reg_1 = 0x91, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .max_mp_regs = 196, + .rd_bitmap_l = 0x10, + .rd_bitmap_u = 0x11, + .rd_bitmap_1l = 0x12, + .rd_bitmap_1u = 0x13, + .wr_bitmap_l = 0x14, + .wr_bitmap_u = 0x15, + .wr_bitmap_1l = 0x16, + .wr_bitmap_1u = 0x17, + .rd_len_p0_l = 0x18, + .rd_len_p0_u = 0x19, + .card_config_2_1_reg = 0xD9, + .cmd_config_0 = 0xC4, + .cmd_config_1 = 0xC5, + .cmd_config_2 = 0xC6, + .cmd_config_3 = 0xC7, + .cmd_rd_len_0 = 0xC0, + .cmd_rd_len_1 = 0xC1, + .cmd_rd_len_2 = 0xC2, + .cmd_rd_len_3 = 0xC3, + .io_port_0_reg = 0xE4, + .io_port_1_reg = 0xE5, + .io_port_2_reg = 0xE6, + .host_int_rsr_reg = 0x04, + .host_int_mask_reg = 0x08, + .host_int_status_reg = 0x0C, + .host_restart_reg = 0x58, + .card_to_host_event_reg = 0x5C, + .host_interrupt_mask_reg = 0x60, + .card_interrupt_status_reg = 0x64, + .card_interrupt_rsr_reg = 0x68, + .card_revision_reg = 0xC8, + .card_ocr_0_reg = 0xD4, + .card_ocr_1_reg = 0xD5, + .card_ocr_3_reg = 0xD6, + .card_config_reg = 0xD7, + .card_misc_cfg_reg = 0xD8, + .debug_0_reg = 0xDC, + .debug_1_reg = 0xDD, + .debug_2_reg = 0xDE, + .debug_3_reg = 0xDF, + .fw_reset_reg = 0x0B6, + .fw_reset_val = 1, + .winner_check_reg = 0x90, +}; + +static const struct _mlan_card_info mlan_card_info_sd8887 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K, + .v16_fw_api = 0, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_1X1, +}; +#endif + +#ifdef SD8897 +static const struct _mlan_sdio_card_reg mlan_reg_sd8897 = { + .start_rd_port = 0, + .start_wr_port = 0, + .base_0_reg = 0x60, + .base_1_reg = 0x61, + .poll_reg = 0x50, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .host_int_status = DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS | + DN_LD_CMD_PORT_HOST_INT_STATUS | + UP_LD_CMD_PORT_HOST_INT_STATUS, + .status_reg_0 = 0xC0, + .status_reg_1 = 0xC1, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .max_mp_regs = 184, + .rd_bitmap_l = 0x04, + .rd_bitmap_u = 0x05, + .rd_bitmap_1l = 0x06, + .rd_bitmap_1u = 0x07, + .wr_bitmap_l = 0x08, + .wr_bitmap_u = 0x09, + .wr_bitmap_1l = 0x0A, + .wr_bitmap_1u = 0x0B, + .rd_len_p0_l = 0x0C, + .rd_len_p0_u = 0x0D, + .card_config_2_1_reg = 0xCD, + .cmd_config_0 = 0xB8, + .cmd_config_1 = 0xB9, + .cmd_config_2 = 0xBA, + .cmd_config_3 = 0xBB, + .cmd_rd_len_0 = 0xB4, + .cmd_rd_len_1 = 0xB5, + .cmd_rd_len_2 = 0xB6, + .cmd_rd_len_3 = 0xB7, + .io_port_0_reg = 0xD8, + .io_port_1_reg = 0xD9, + .io_port_2_reg = 0xDA, + .host_int_rsr_reg = 0x01, + .host_int_mask_reg = 0x02, + .host_int_status_reg = 0x03, + .host_restart_reg = 0x4C, + .card_to_host_event_reg = 0x50, + .host_interrupt_mask_reg = 0x54, + .card_interrupt_status_reg = 0x58, + .card_interrupt_rsr_reg = 0x5C, + .card_revision_reg = 0xBC, + .card_ocr_0_reg = 0xC8, + .card_ocr_1_reg = 0xC9, + .card_ocr_3_reg = 0xCA, + .card_config_reg = 0xCB, + .card_misc_cfg_reg = 0xCC, + .debug_0_reg = 0xD0, + .debug_1_reg = 0xD1, + .debug_2_reg = 0xD2, + .debug_3_reg = 0xD3, + .fw_reset_reg = 0x0E8, + .fw_reset_val = 1, + .winner_check_reg = 0xC0, +}; + +static const struct _mlan_card_info mlan_card_info_sd8897 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 0, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#if defined(SD8977) || defined(SD8997) || defined(SD8987) || \ + defined(SD9098) || defined(SD9097) || defined(SD8978) +static const struct _mlan_sdio_card_reg mlan_reg_sd8977_sd8997 = { + .start_rd_port = 0, + .start_wr_port = 0, + .base_0_reg = 0xf8, + .base_1_reg = 0xf9, + .poll_reg = 0x5C, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK | + CMD_PORT_UPLD_INT_MASK | CMD_PORT_DNLD_INT_MASK, + .host_int_status = DN_LD_HOST_INT_STATUS | UP_LD_HOST_INT_STATUS | + DN_LD_CMD_PORT_HOST_INT_STATUS | + UP_LD_CMD_PORT_HOST_INT_STATUS, + .status_reg_0 = 0xe8, + .status_reg_1 = 0xe9, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .max_mp_regs = 196, + .rd_bitmap_l = 0x10, + .rd_bitmap_u = 0x11, + .rd_bitmap_1l = 0x12, + .rd_bitmap_1u = 0x13, + .wr_bitmap_l = 0x14, + .wr_bitmap_u = 0x15, + .wr_bitmap_1l = 0x16, + .wr_bitmap_1u = 0x17, + .rd_len_p0_l = 0x18, + .rd_len_p0_u = 0x19, + .card_config_2_1_reg = 0xD9, + .cmd_config_0 = 0xC4, + .cmd_config_1 = 0xC5, + .cmd_config_2 = 0xC6, + .cmd_config_3 = 0xC7, + .cmd_rd_len_0 = 0xC0, + .cmd_rd_len_1 = 0xC1, + .cmd_rd_len_2 = 0xC2, + .cmd_rd_len_3 = 0xC3, + .io_port_0_reg = 0xE4, + .io_port_1_reg = 0xE5, + .io_port_2_reg = 0xE6, + .host_int_rsr_reg = 0x04, + .host_int_mask_reg = 0x08, + .host_int_status_reg = 0x0C, + .host_restart_reg = 0x58, + .card_to_host_event_reg = 0x5C, + .host_interrupt_mask_reg = 0x60, + .card_interrupt_status_reg = 0x64, + .card_interrupt_rsr_reg = 0x68, + .card_revision_reg = 0xC8, + .card_ocr_0_reg = 0xD4, + .card_ocr_1_reg = 0xD5, + .card_ocr_3_reg = 0xD6, + .card_config_reg = 0xD7, + .card_misc_cfg_reg = 0xD8, + .debug_0_reg = 0xDC, + .debug_1_reg = 0xDD, + .debug_2_reg = 0xDE, + .debug_3_reg = 0xDF, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .fw_dnld_offset_0_reg = 0xEC, + .fw_dnld_offset_1_reg = 0xED, + .fw_dnld_offset_2_reg = 0xEE, + .fw_dnld_offset_3_reg = 0xEF, + .fw_dnld_status_0_reg = 0xE8, + .fw_dnld_status_1_reg = 0xE9, + .winner_check_reg = 0xFC, +}; + +#ifdef SD8997 +static const struct _mlan_card_info mlan_card_info_sd8997 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 1, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#ifdef SD9097 +static const struct _mlan_card_info mlan_card_info_sd9097 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 1, + .v17_fw_api = 1, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif +#ifdef SD9098 +static const struct _mlan_card_info mlan_card_info_sd9098 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 1, + .v17_fw_api = 1, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#if defined(SD8977) || defined(SD8978) +static const struct _mlan_card_info mlan_card_info_sd8977 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K, + .v16_fw_api = 1, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_1X1, +}; +#endif + +#ifdef SD8987 +static const struct _mlan_card_info mlan_card_info_sd8987 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_2K, + .v16_fw_api = 1, + .supp_ps_handshake = 0, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_1X1, +}; +#endif +#endif + +/** Probe and initialization function */ +mlan_status wlan_sdio_probe(pmlan_adapter pmadapter); +mlan_status wlan_get_sdio_device(pmlan_adapter pmadapter); + +mlan_status wlan_send_mp_aggr_buf(mlan_adapter *pmadapter); + +mlan_status wlan_re_alloc_sdio_rx_mpa_buffer(mlan_adapter *pmadapter); + +void wlan_decode_spa_buffer(mlan_adapter *pmadapter, t_u8 *buf, t_u32 len); +t_void wlan_sdio_deaggr_rx_pkt(pmlan_adapter pmadapter, mlan_buffer *pmbuf); +/** 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(pmlan_private priv); +mlan_status wlan_cmd_sdio_gpio_int(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf); +mlan_status wlan_reset_fw(pmlan_adapter pmadapter); + +#endif /* _MLAN_SDIO_H */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_shim.c b/mxm_wifiex/wlan_src/mlan/mlan_shim.c new file mode 100644 index 0000000..c5fc7f5 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_shim.c @@ -0,0 +1,1700 @@ +/** @file mlan_shim.c + * + * @brief This file contains APIs to MOAL module. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" +#ifdef SDIO +#include "mlan_sdio.h" +#endif /* SDIO */ +#ifdef PCIE +#include "mlan_pcie.h" +#endif /* PCIE */ +#ifdef UAP_SUPPORT +#include "mlan_uap.h" +#endif +#include "mlan_11h.h" +#include "mlan_11n_rxreorder.h" +#ifdef DRV_EMBEDDED_AUTHENTICATOR +#include "authenticator_api.h" +#endif + +/******************************************************** + 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)(t_pvoid pmoal_handle, 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)(t_pvoid pmoal_handle, t_u32 level, char *pformat, + IN...) = MNULL; + +/** Global moal_get_system_time callback */ +mlan_status (*get_sys_time_callback)(t_pvoid pmoal_handle, t_pu32 psec, + t_pu32 pusec) = MNULL; + +/** Global driver debug mit masks */ +t_u32 mlan_drvdbg = DEFAULT_DEBUG_MASK; +#endif + +#ifdef USB +extern mlan_status wlan_get_usb_device(pmlan_adapter pmadapter); +#endif +/******************************************************** + Local Functions +*******************************************************/ +/** + * @brief This function process pending ioctl + * + * @param pmadapter A pointer to mlan_adapter structure + * + */ +void wlan_process_pending_ioctl(mlan_adapter *pmadapter) +{ + pmlan_ioctl_req pioctl_buf; + mlan_status status = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + pmlan_ds_bss bss = MNULL; +#endif +#ifdef STA_SUPPORT + pmlan_ds_misc_cfg misc = MNULL; +#endif + ENTER(); + + pcb = &pmadapter->callbacks; + + while ((pioctl_buf = (pmlan_ioctl_req)util_dequeue_list( + pmadapter->pmoal_handle, &pmadapter->ioctl_pending_q, + pcb->moal_spin_lock, pcb->moal_spin_unlock))) { + switch (pioctl_buf->req_id) { +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + case MLAN_IOCTL_BSS: + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + if (bss->sub_command == MLAN_OID_BSS_ROLE) { + PRINTM(MCMND, "Role switch ioctl: %d\n", + bss->param.bss_role); + status = wlan_bss_ioctl_bss_role(pmadapter, + pioctl_buf); + } + break; +#endif +#ifdef STA_SUPPORT + case MLAN_IOCTL_MISC_CFG: + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + if (misc->sub_command == MLAN_OID_MISC_WARM_RESET) { + PRINTM(MCMND, "Warm Reset ioctl\n"); + status = wlan_misc_ioctl_warm_reset(pmadapter, + pioctl_buf); + } + break; +#endif + default: + break; + } + if (status != MLAN_STATUS_PENDING) + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + pioctl_buf, status); + } + LEAVE(); +} +/******************************************************** + Global Functions +********************************************************/ +#ifdef USB +extern mlan_adapter_operations mlan_usb_ops; +#endif +#ifdef PCIE +extern mlan_adapter_operations mlan_pcie_ops; +#endif +#ifdef SDIO +extern mlan_adapter_operations mlan_sdio_ops; +#endif + +/** + * @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( + * pmlan_device pmdevice, + * 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(pmlan_device pmdevice, t_void **ppmlan_adapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = MNULL; + pmlan_callbacks pcb = MNULL; + t_u8 i = 0; + t_u32 j = 0; + + if (!pmdevice || !ppmlan_adapter) { + return MLAN_STATUS_FAILURE; + } + MASSERT(ppmlan_adapter); + MASSERT(pmdevice->callbacks.moal_print); +#ifdef DEBUG_LEVEL1 + print_callback = pmdevice->callbacks.moal_print; + get_sys_time_callback = pmdevice->callbacks.moal_get_system_time; +#endif + assert_callback = pmdevice->callbacks.moal_assert; + + ENTER(); + + MASSERT(pmdevice->callbacks.moal_malloc); + MASSERT(pmdevice->callbacks.moal_mfree); + MASSERT(pmdevice->callbacks.moal_memset); + MASSERT(pmdevice->callbacks.moal_memmove); + MASSERT(pmdevice->callbacks.moal_udelay); + MASSERT(pmdevice->callbacks.moal_usleep_range); + + if (!pmdevice->callbacks.moal_malloc || + !pmdevice->callbacks.moal_mfree || + !pmdevice->callbacks.moal_memset || + !pmdevice->callbacks.moal_udelay || + !pmdevice->callbacks.moal_usleep_range || + !pmdevice->callbacks.moal_memmove) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate memory for adapter structure */ + if (pmdevice->callbacks.moal_vmalloc && pmdevice->callbacks.moal_vfree) + ret = pmdevice->callbacks.moal_vmalloc(pmdevice->pmoal_handle, + sizeof(mlan_adapter), + (t_u8 **)&pmadapter); + else + ret = pmdevice->callbacks.moal_malloc(pmdevice->pmoal_handle, + sizeof(mlan_adapter), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter); + if ((ret != MLAN_STATUS_SUCCESS) || !pmadapter) { + ret = MLAN_STATUS_FAILURE; + goto exit_register; + } + + pmdevice->callbacks.moal_memset(pmdevice->pmoal_handle, pmadapter, 0, + sizeof(mlan_adapter)); + + pcb = &pmadapter->callbacks; + + /* Save callback functions */ + pmdevice->callbacks.moal_memmove(pmadapter->pmoal_handle, pcb, + &pmdevice->callbacks, + sizeof(mlan_callbacks)); + + /* Assertion for all callback functions */ + MASSERT(pcb->moal_get_hw_spec_complete); + MASSERT(pcb->moal_init_fw_complete); + MASSERT(pcb->moal_shutdown_fw_complete); + MASSERT(pcb->moal_send_packet_complete); + MASSERT(pcb->moal_recv_packet); + MASSERT(pcb->moal_recv_event); + MASSERT(pcb->moal_ioctl_complete); + +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) { + MASSERT(pcb->moal_write_reg); + MASSERT(pcb->moal_read_reg); + MASSERT(pcb->moal_alloc_mlan_buffer); + MASSERT(pcb->moal_free_mlan_buffer); + } +#endif + MASSERT(pcb->moal_get_vdll_data); + MASSERT(pcb->moal_write_data_sync); + MASSERT(pcb->moal_read_data_sync); + MASSERT(pcb->moal_mfree); + MASSERT(pcb->moal_memcpy); + MASSERT(pcb->moal_memcpy_ext); + MASSERT(pcb->moal_memcmp); + MASSERT(pcb->moal_get_system_time); + MASSERT(pcb->moal_init_timer); + MASSERT(pcb->moal_free_timer); + MASSERT(pcb->moal_get_boot_ktime); + MASSERT(pcb->moal_start_timer); + MASSERT(pcb->moal_stop_timer); + MASSERT(pcb->moal_init_lock); + MASSERT(pcb->moal_free_lock); + MASSERT(pcb->moal_spin_lock); + MASSERT(pcb->moal_spin_unlock); + MASSERT(pcb->moal_hist_data_add); + /* Save pmoal_handle */ + pmadapter->pmoal_handle = pmdevice->pmoal_handle; + + pmadapter->feature_control = pmdevice->feature_control; + + pmadapter->card_type = pmdevice->card_type; + pmadapter->card_rev = pmdevice->card_rev; + pmadapter->init_para.uap_max_sta = pmdevice->uap_max_sta; + +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + PRINTM(MMSG, + "Attach mlan adapter operations.card_type is 0x%x.\n", + pmdevice->card_type); + memcpy_ext(pmadapter, &pmadapter->ops, &mlan_sdio_ops, + sizeof(mlan_adapter_operations), + sizeof(mlan_adapter_operations)); + ret = wlan_get_sdio_device(pmadapter); + if (MLAN_STATUS_SUCCESS != ret) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + if ((pmdevice->int_mode == INT_MODE_GPIO) && + (pmdevice->gpio_pin == 0)) { + PRINTM(MERROR, + "SDIO_GPIO_INT_CONFIG: Invalid GPIO Pin\n"); + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->init_para.int_mode = pmdevice->int_mode; + pmadapter->init_para.gpio_pin = pmdevice->gpio_pin; + /* card specific probing has been deferred until now .. */ + ret = wlan_sdio_probe(pmadapter); + if (MLAN_STATUS_SUCCESS != ret) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + pmadapter->pcard_sd->max_segs = pmdevice->max_segs; + pmadapter->pcard_sd->max_seg_size = pmdevice->max_seg_size; + + pmadapter->init_para.mpa_tx_cfg = pmdevice->mpa_tx_cfg; + pmadapter->init_para.mpa_rx_cfg = pmdevice->mpa_rx_cfg; + pmadapter->pcard_sd->sdio_rx_aggr_enable = + pmdevice->sdio_rx_aggr_enable; + } +#endif + +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type)) { + MASSERT(pcb->moal_malloc_consistent); + MASSERT(pcb->moal_mfree_consistent); + MASSERT(pcb->moal_map_memory); + MASSERT(pcb->moal_unmap_memory); + PRINTM(MMSG, + "Attach mlan adapter operations.card_type is 0x%x.\n", + pmdevice->card_type); + memcpy_ext(pmadapter, &pmadapter->ops, &mlan_pcie_ops, + sizeof(mlan_adapter_operations), + sizeof(mlan_adapter_operations)); + ret = wlan_get_pcie_device(pmadapter); + if (MLAN_STATUS_SUCCESS != ret) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + } +#endif + +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + MASSERT(pcb->moal_write_data_async); + MASSERT(pcb->moal_recv_complete); + PRINTM(MMSG, + "Attach mlan adapter operations.card_type is 0x%x.\n", + pmdevice->card_type); + memcpy_ext(pmadapter, &pmadapter->ops, &mlan_usb_ops, + sizeof(mlan_adapter_operations), + sizeof(mlan_adapter_operations)); + ret = wlan_get_usb_device(pmadapter); + if (MLAN_STATUS_SUCCESS != ret) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + } +#endif + +#ifdef DEBUG_LEVEL1 + mlan_drvdbg = pmdevice->drvdbg; +#endif + +#ifdef MFG_CMD_SUPPORT + pmadapter->init_para.mfg_mode = pmdevice->mfg_mode; +#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_12K || + 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 + if (IS_DFS_SUPPORT(pmadapter->feature_control)) + pmadapter->init_para.dfs_master_radar_det_en = + DFS_MASTER_RADAR_DETECT_EN; + pmadapter->init_para.dfs_slave_radar_det_en = DFS_SLAVE_RADAR_DETECT_EN; + pmadapter->init_para.dev_cap_mask = pmdevice->dev_cap_mask; + pmadapter->init_para.indrstcfg = pmdevice->indrstcfg; + pmadapter->rx_work_flag = pmdevice->rx_work; + pmadapter->init_para.passive_to_active_scan = + pmdevice->passive_to_active_scan; + pmadapter->fixed_beacon_buffer = pmdevice->fixed_beacon_buffer; + + pmadapter->multiple_dtim = pmdevice->multi_dtim; + pmadapter->inact_tmo = pmdevice->inact_tmo; + pmadapter->init_para.fw_region = pmdevice->fw_region; + pmadapter->hs_wake_interval = pmdevice->hs_wake_interval; + if (pmdevice->indication_gpio != 0xff) { + pmadapter->ind_gpio = pmdevice->indication_gpio & 0x0f; + pmadapter->level = (pmdevice->indication_gpio & 0xf0) >> 4; + if (pmadapter->level != 0 && pmadapter->level != 1) { + PRINTM(MERROR, + "Indication GPIO level is wrong and will use default value 0.\n"); + pmadapter->level = 0; + } + } + pmadapter->hs_mimo_switch = pmdevice->hs_mimo_switch; +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + pmadapter->tx_cmd_ep = pmdevice->tx_cmd_ep; + pmadapter->rx_cmd_ep = pmdevice->rx_cmd_ep; + pmadapter->tx_data_ep = pmdevice->tx_data_ep; + pmadapter->rx_data_ep = pmdevice->rx_data_ep; + } +#endif + pmadapter->init_para.dfs53cfg = pmdevice->dfs53cfg; + pmadapter->priv_num = 0; + pmadapter->priv[0] = MNULL; + + if (pcb->moal_vmalloc && pcb->moal_vfree) + ret = pcb->moal_vmalloc(pmadapter->pmoal_handle, + sizeof(mlan_private), + (t_u8 **)&pmadapter->priv[0]); + else + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_private), MLAN_MEM_DEF, + (t_u8 **)&pmadapter->priv[0]); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->priv[0]) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + pmadapter->priv_num++; + memset(pmadapter, pmadapter->priv[0], 0, sizeof(mlan_private)); + + pmadapter->priv[0]->adapter = pmadapter; + pmadapter->priv[0]->bss_type = (t_u8)pmdevice->bss_attr[0].bss_type; + pmadapter->priv[0]->frame_type = (t_u8)pmdevice->bss_attr[0].frame_type; + pmadapter->priv[0]->bss_priority = + (t_u8)pmdevice->bss_attr[0].bss_priority; + if (pmdevice->bss_attr[0].bss_type == MLAN_BSS_TYPE_STA) + pmadapter->priv[0]->bss_role = MLAN_BSS_ROLE_STA; + else if (pmdevice->bss_attr[0].bss_type == MLAN_BSS_TYPE_UAP) + pmadapter->priv[0]->bss_role = MLAN_BSS_ROLE_UAP; +#ifdef WIFI_DIRECT_SUPPORT + else if (pmdevice->bss_attr[0].bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + pmadapter->priv[0]->bss_role = MLAN_BSS_ROLE_STA; + if (pmdevice->bss_attr[0].bss_virtual) + pmadapter->priv[0]->bss_virtual = MTRUE; + } +#endif + /* Save bss_index and bss_num */ + pmadapter->priv[0]->bss_index = 0; + pmadapter->priv[0]->bss_num = (t_u8)pmdevice->bss_attr[0].bss_num; + + /* init function table */ + for (j = 0; mlan_ops[j]; j++) { + if (mlan_ops[j]->bss_role == GET_BSS_ROLE(pmadapter->priv[0])) { + memcpy_ext(pmadapter, &pmadapter->priv[0]->ops, + mlan_ops[j], sizeof(mlan_operations), + sizeof(mlan_operations)); + break; + } + } + /** back up bss_attr table */ + memcpy_ext(pmadapter, pmadapter->bss_attr, pmdevice->bss_attr, + sizeof(pmadapter->bss_attr), sizeof(pmadapter->bss_attr)); + + /* Initialize lock variables */ + if (wlan_init_lock_list(pmadapter) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + /** init lock varible for first priv */ + if (wlan_init_priv_lock_list(pmadapter, 0) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + /* Allocate memory for member of adapter structure */ + if (wlan_allocate_adapter(pmadapter)) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + + /* Initialize timers */ + if (wlan_init_timer(pmadapter) != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + /* Return pointer of mlan_adapter to MOAL */ + *ppmlan_adapter = pmadapter; + + goto exit_register; + +error: + PRINTM(MINFO, "Leave mlan_register with error\n"); + /* Free adapter structure */ + wlan_free_adapter(pmadapter); + + for (i = 0; i < MLAN_MAX_BSS_NUM; i++) { + if (pmadapter->priv[i]) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->priv[i]); + else if (pcb->moal_mfree) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->priv[i]); + } + } + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter); + else if (pcb->moal_mfree) + 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(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 private structures */ + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) { + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->priv[i]); + else if (pcb->moal_mfree) + pcb->moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)pmadapter->priv[i]); + } + } + + /* Free mlan_adapter */ + if (pcb->moal_vmalloc && pcb->moal_vfree) + pcb->moal_vfree(pmadapter->pmoal_handle, (t_u8 *)pmadapter); + else if (pcb->moal_mfree) + 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(t_void *pmlan_adapter, pmlan_fw_image pmfw) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + + ENTER(); + MASSERT(pmlan_adapter); + + /* Download helper/firmware */ + if (pmfw) { + ret = pmadapter->ops.dnld_fw(pmadapter, pmfw); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "wlan_dnld_fw fail ret=0x%x\n", ret); + LEAVE(); + return ret; + } + } + + 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(t_void *pmlan_adapter, pmlan_init_param pparam) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + + ENTER(); + MASSERT(pmlan_adapter); + + /** Save DPD data in MLAN */ + if ((pparam->pdpd_data_buf) || (pparam->dpd_data_len > 0)) { + pmadapter->pdpd_data = pparam->pdpd_data_buf; + pmadapter->dpd_data_len = pparam->dpd_data_len; + } + if (pparam->ptxpwr_data_buf && (pparam->txpwr_data_len > 0)) { + pmadapter->ptxpwr_data = pparam->ptxpwr_data_buf; + pmadapter->txpwr_data_len = pparam->txpwr_data_len; + } + /** Save cal data in MLAN */ + if ((pparam->pcal_data_buf) && (pparam->cal_data_len > 0)) { + pmadapter->pcal_data = pparam->pcal_data_buf; + pmadapter->cal_data_len = pparam->cal_data_len; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function initializes the firmware + * + * @param pmlan_adapter A pointer to a t_void pointer to store + * mlan_adapter structure pointer + * + * @return MLAN_STATUS_SUCCESS + * The firmware initialization succeeded. + * MLAN_STATUS_PENDING + * The firmware initialization is pending. + * MLAN_STATUS_FAILURE + * The firmware initialization failed. + */ +mlan_status mlan_init_fw(t_void *pmlan_adapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + + ENTER(); + MASSERT(pmlan_adapter); + + pmadapter->hw_status = WlanHardwareStatusGetHwSpec; + + /* Initialize firmware, may return PENDING */ + ret = wlan_init_fw(pmadapter); + PRINTM(MINFO, "wlan_init_fw returned ret=0x%x\n", ret); + + LEAVE(); + return ret; +} + +/** + * @brief Shutdown firmware + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS + * The firmware shutdown call succeeded. + * MLAN_STATUS_PENDING + * The firmware shutdown call is pending. + * MLAN_STATUS_FAILURE + * The firmware shutdown call failed. + */ +mlan_status mlan_shutdown_fw(t_void *pmlan_adapter) +{ + mlan_status ret = MLAN_STATUS_PENDING; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + pmlan_buffer pmbuf; + pmlan_ioctl_req pioctl_buf; + pmlan_callbacks pcb; + t_s32 i = 0; + + ENTER(); + + MASSERT(pmlan_adapter); + /* MLAN already shutdown */ + if (pmadapter->hw_status == WlanHardwareStatusNotReady) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + pmadapter->hw_status = WlanHardwareStatusClosing; + /* Wait for mlan_process to complete */ + if (pmadapter->mlan_processing) { + PRINTM(MWARN, "MLAN main processing is still running\n"); + LEAVE(); + return ret; + } + + /* Shut down MLAN */ + PRINTM(MINFO, "Shutdown MLAN...\n"); + + /* Cancel all pending commands and complete ioctls */ + wlan_cancel_all_pending_cmd(pmadapter, MTRUE); + + /* Clean up priv structures */ + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_free_priv(pmadapter->priv[i]); + } + + pcb = &pmadapter->callbacks; + /** cancel pending ioctl */ + while ((pioctl_buf = (pmlan_ioctl_req)util_dequeue_list( + pmadapter->pmoal_handle, &pmadapter->ioctl_pending_q, + pcb->moal_spin_lock, pcb->moal_spin_unlock))) { + pioctl_buf->status_code = MLAN_ERROR_CMD_CANCEL; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, pioctl_buf, + MLAN_STATUS_FAILURE); + } + + while ((pmbuf = (pmlan_buffer)util_dequeue_list( + pmadapter->pmoal_handle, &pmadapter->rx_data_queue, + pcb->moal_spin_lock, pcb->moal_spin_unlock))) { +#ifdef USB + if (IS_USB(pmadapter->card_type)) + pcb->moal_recv_complete(pmadapter->pmoal_handle, pmbuf, + pmadapter->rx_data_ep, + MLAN_STATUS_FAILURE); +#endif +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) + wlan_free_mlan_buffer(pmadapter, pmbuf); +#endif + } + pmadapter->rx_pkts_queued = 0; +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type) && + wlan_set_drv_ready_reg(pmadapter, 0)) { + PRINTM(MERROR, "Failed to write driver not-ready signature\n"); + } +#endif + + /* Notify completion */ + ret = wlan_shutdown_fw_complete(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief queue main work + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +static t_void mlan_queue_main_work(mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + + /* Check if already processing */ + if (pmadapter->mlan_processing) { + pmadapter->more_task_flag = MTRUE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + } else { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + wlan_recv_event(wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DEFER_HANDLING, MNULL); + } + LEAVE(); + return; +} + +/** + * @brief queue rx_work + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return N/A + */ +static t_void mlan_queue_rx_work(mlan_adapter *pmadapter) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + ENTER(); + + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->prx_proc_lock); + + /* Check if already processing */ + if (pmadapter->mlan_rx_processing) { + pmadapter->more_rx_task_flag = MTRUE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + } else { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + wlan_recv_event(wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_DEFER_RX_WORK, MNULL); + } + LEAVE(); + return; +} + +/** + * @brief block main process + * + * @param pmadapter A pointer to mlan_adapter structure + * @param block MTRUE/MFALSE + * + * @return N/A + */ +void mlan_block_main_process(mlan_adapter *pmadapter, t_u8 block) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + if (!block) { + pmadapter->main_lock_flag = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + } else { + pmadapter->main_lock_flag = MTRUE; + if (pmadapter->mlan_processing) { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + PRINTM(MEVENT, "wlan: wait main work done...\n"); + wlan_recv_event( + wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_FLUSH_MAIN_WORK, MNULL); + } else { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + } + } +} + +/** + * @brief block rx process + * + * @param pmadapter A pointer to mlan_adapter structure + * @param block MTRUE/MFALSE; + * + * @return N/A + */ +void mlan_block_rx_process(mlan_adapter *pmadapter, t_u8 block) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->prx_proc_lock); + if (!block) { + pmadapter->rx_lock_flag = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + } else { + pmadapter->rx_lock_flag = MTRUE; + if (pmadapter->mlan_rx_processing) { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + PRINTM(MEVENT, "wlan: wait rx work done...\n"); + wlan_recv_event(wlan_get_priv(pmadapter, + MLAN_BSS_ROLE_ANY), + MLAN_EVENT_ID_DRV_FLUSH_RX_WORK, MNULL); + } else { + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + } + } +} + +/** + * @brief The receive process + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * @param rx_pkts A pointer to save receive pkts number + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status mlan_rx_process(t_void *pmlan_adapter, t_u8 *rx_pkts) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + pmlan_callbacks pcb; + pmlan_buffer pmbuf; + t_u8 limit = 0; + t_u8 rx_num = 0; + + ENTER(); + + MASSERT(pmlan_adapter); + pcb = &pmadapter->callbacks; + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->prx_proc_lock); + if (pmadapter->mlan_rx_processing || pmadapter->rx_lock_flag) { + pmadapter->more_rx_task_flag = MTRUE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + goto exit_rx_proc; + } else { + pmadapter->mlan_rx_processing = MTRUE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + } + if (rx_pkts) + limit = *rx_pkts; + +rx_process_start: + /* Check for Rx data */ + while (MTRUE) { +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (pmadapter->authenticator_priv) { + if (IsAuthenticatorEnabled( + pmadapter->authenticator_priv->psapriv)) { + AuthenticatorKeyMgmtInit( + pmadapter->authenticator_priv->psapriv, + pmadapter->authenticator_priv + ->curr_addr); + pmadapter->authenticator_priv = MNULL; + } + } +#endif + if (pmadapter->flush_data) { + pmadapter->flush_data = MFALSE; + wlan_flush_rxreorder_tbl(pmadapter); + } + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + pmbuf = (pmlan_buffer)util_dequeue_list( + pmadapter->pmoal_handle, &pmadapter->rx_data_queue, + MNULL, MNULL); + if (!pmbuf) { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + break; + } + pmadapter->rx_pkts_queued--; + rx_num++; + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + + // rx_trace 6 + if (pmadapter->tp_state_on) + pmadapter->callbacks.moal_tp_accounting( + pmadapter->pmoal_handle, pmbuf, + 6 /*RX_DROP_P2*/); + if (pmadapter->tp_state_drop_point == 6 /*RX_DROP_P2*/) { + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + goto rx_process_start; + } + + if (pmadapter->delay_task_flag && + (pmadapter->rx_pkts_queued < LOW_RX_PENDING)) { + PRINTM(MEVENT, "Run\n"); + pmadapter->delay_task_flag = MFALSE; + mlan_queue_main_work(pmadapter); + } + pmadapter->ops.handle_rx_packet(pmadapter, pmbuf); + if (limit && rx_num >= limit) + break; + } + if (rx_pkts) + *rx_pkts = rx_num; + pcb->moal_spin_lock(pmadapter->pmoal_handle, pmadapter->prx_proc_lock); + if (pmadapter->more_rx_task_flag) { + pmadapter->more_rx_task_flag = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); + goto rx_process_start; + } + pmadapter->mlan_rx_processing = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->prx_proc_lock); +exit_rx_proc: + LEAVE(); + return ret; +} + +/** + * @brief The main process + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status mlan_main_process(t_void *pmlan_adapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + pmlan_callbacks pcb; + + ENTER(); + + MASSERT(pmlan_adapter); + + pcb = &pmadapter->callbacks; + + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + + /* Check if already processing */ + if (pmadapter->mlan_processing || pmadapter->main_lock_flag) { + pmadapter->more_task_flag = MTRUE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + goto exit_main_proc; + } else { + pmadapter->mlan_processing = MTRUE; + pmadapter->main_process_cnt++; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + } +process_start: + do { + /* Is MLAN shutting down or not ready? */ + if ((pmadapter->hw_status == WlanHardwareStatusClosing) || + (pmadapter->hw_status == WlanHardwareStatusNotReady)) + break; + if (pmadapter->pending_ioctl) { + wlan_process_pending_ioctl(pmadapter); + pmadapter->pending_ioctl = MFALSE; + } + if (pmadapter->pending_disconnect_priv) { + PRINTM(MEVENT, "Reset connect state\n"); + wlan_reset_connect_state( + pmadapter->pending_disconnect_priv, MTRUE); + pmadapter->pending_disconnect_priv = MNULL; + } +#if defined(SDIO) || defined(PCIE) + if (!IS_USB(pmadapter->card_type)) { + if (pmadapter->rx_pkts_queued > HIGH_RX_PENDING) { + pcb->moal_tp_accounting_rx_param( + pmadapter->pmoal_handle, 2, 0); + PRINTM(MEVENT, "Pause\n"); + pmadapter->delay_task_flag = MTRUE; + mlan_queue_rx_work(pmadapter); + break; + } + /* Handle pending interrupts if any */ + if (pmadapter->ireg) { + if (pmadapter->hs_activated == MTRUE) + wlan_process_hs_config(pmadapter); + pmadapter->ops.process_int_status(pmadapter); + if (pmadapter->data_received) + mlan_queue_rx_work(pmadapter); + } + } +#endif + + /* Need to wake up the card ? */ + if ((pmadapter->ps_state == PS_STATE_SLEEP) && + (pmadapter->pm_wakeup_card_req && + !pmadapter->pm_wakeup_fw_try) && + (wlan_is_cmd_pending(pmadapter) || + !wlan_bypass_tx_list_empty(pmadapter) || + !wlan_wmm_lists_empty(pmadapter))) { + pmadapter->ops.wakeup_card(pmadapter, MTRUE); + pmadapter->pm_wakeup_fw_try = MTRUE; + continue; + } + if (IS_CARD_RX_RCVD(pmadapter) || + (!pmadapter->cmd_sent && + pmadapter->vdll_ctrl.pending_block)) { + 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; + if (pmadapter->wakeup_fw_timer_is_set) { + pcb->moal_stop_timer( + pmadapter->pmoal_handle, + pmadapter->pwakeup_fw_timer); + pmadapter->wakeup_fw_timer_is_set = MFALSE; + } + } else { + /* We have tried to wakeup the card already */ + if (pmadapter->pm_wakeup_fw_try) + break; + /* Check if we need to confirm Sleep Request received + * previously */ + if (pmadapter->ps_state == PS_STATE_PRE_SLEEP) + if (!pmadapter->cmd_sent && + !pmadapter->curr_cmd && + !pmadapter->vdll_ctrl.pending_block) + wlan_check_ps_cond(pmadapter); + if (pmadapter->ps_state != PS_STATE_AWAKE || + (pmadapter->tx_lock_flag == MTRUE)) + break; + + if (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 || + !wlan_is_cmd_pending(pmadapter)) { + break; + } + } + } + + /* Check for Cmd Resp */ + if (pmadapter->cmd_resp_received) { + pmadapter->cmd_resp_received = MFALSE; + wlan_process_cmdresp(pmadapter); + + /* call moal back when init_fw is done */ + if (pmadapter->hw_status == + WlanHardwareStatusInitdone) { + pmadapter->hw_status = WlanHardwareStatusReady; + wlan_init_fw_complete(pmadapter); + } else if (pmadapter->hw_status == + WlanHardwareStatusGetHwSpecdone) { + pmadapter->hw_status = + WlanHardwareStatusInitializing; + wlan_get_hw_spec_complete(pmadapter); + } + } + + /* Check for event */ + if (pmadapter->event_received) { + pmadapter->event_received = MFALSE; + wlan_process_event(pmadapter); + } + + /* Check if we need to confirm Sleep Request received previously + */ + if (pmadapter->ps_state == PS_STATE_PRE_SLEEP) + if (!pmadapter->cmd_sent && !pmadapter->curr_cmd && + !pmadapter->vdll_ctrl.pending_block) + 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; + } + + /* in a case of race condition, download the VDLL block here */ + if (!pmadapter->cmd_sent && + pmadapter->vdll_ctrl.pending_block) { + wlan_download_vdll_block( + pmadapter, pmadapter->vdll_ctrl.pending_block, + pmadapter->vdll_ctrl.pending_block_len); + pmadapter->vdll_ctrl.pending_block = MNULL; + } + if (!pmadapter->cmd_sent && !pmadapter->curr_cmd) { + if (wlan_exec_next_cmd(pmadapter) == + MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + break; + } + } + + if (!pmadapter->data_sent && + !wlan_11h_radar_detected_tx_blocked(pmadapter) && + !wlan_bypass_tx_list_empty(pmadapter)) { + PRINTM(MINFO, "mlan_send_pkt(): deq(bybass_txq)\n"); + wlan_process_bypass_tx(pmadapter); + if (pmadapter->hs_activated == MTRUE) { + pmadapter->is_hs_configured = MFALSE; + wlan_host_sleep_activated_event( + wlan_get_priv(pmadapter, + MLAN_BSS_ROLE_ANY), + MFALSE); + } + } + + if (!pmadapter->data_sent && !wlan_wmm_lists_empty(pmadapter) && + !wlan_11h_radar_detected_tx_blocked(pmadapter)) { + wlan_wmm_process_tx(pmadapter); + if (pmadapter->hs_activated == MTRUE) { + pmadapter->is_hs_configured = MFALSE; + wlan_host_sleep_activated_event( + wlan_get_priv(pmadapter, + MLAN_BSS_ROLE_ANY), + MFALSE); + } + } + +#ifdef STA_SUPPORT + if (pmadapter->delay_null_pkt && !pmadapter->cmd_sent && + !pmadapter->curr_cmd && !wlan_is_cmd_pending(pmadapter) && + wlan_bypass_tx_list_empty(pmadapter) && + wlan_wmm_lists_empty(pmadapter)) { + if (wlan_send_null_packet( + wlan_get_priv(pmadapter, MLAN_BSS_ROLE_STA), + MRVDRV_TxPD_POWER_MGMT_NULL_PACKET | + MRVDRV_TxPD_POWER_MGMT_LAST_PACKET) == + MLAN_STATUS_SUCCESS) { + pmadapter->delay_null_pkt = MFALSE; + } + break; + } +#endif + + } while (MTRUE); + + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + if (pmadapter->more_task_flag == MTRUE) { + pmadapter->more_task_flag = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + goto process_start; + } + pmadapter->mlan_processing = MFALSE; + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + +exit_main_proc: + if (pmadapter->hw_status == WlanHardwareStatusClosing) + mlan_shutdown_fw(pmadapter); + LEAVE(); + return ret; +} + +/** + * @brief Function to send packet + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer structure + * + * @return MLAN_STATUS_PENDING + */ +mlan_status mlan_send_packet(t_void *pmlan_adapter, pmlan_buffer pmbuf) +{ + mlan_status ret = MLAN_STATUS_PENDING; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + mlan_private *pmpriv; + t_u16 eth_type = 0; + + ENTER(); + MASSERT(pmlan_adapter && pmbuf); + + if (!pmlan_adapter || !pmbuf) { + return MLAN_STATUS_FAILURE; + } + + MASSERT(pmbuf->bss_index < pmadapter->priv_num); + pmbuf->flags |= MLAN_BUF_FLAG_MOAL_TX_BUF; + pmpriv = pmadapter->priv[pmbuf->bss_index]; + + eth_type = + mlan_ntohs(*(t_u16 *)&pmbuf->pbuf[pmbuf->data_offset + + MLAN_ETHER_PKT_TYPE_OFFSET]); + if (((pmadapter->priv[pmbuf->bss_index]->port_ctrl_mode == MTRUE) && + ((eth_type == MLAN_ETHER_PKT_TYPE_EAPOL) || + (eth_type == MLAN_ETHER_PKT_TYPE_ARP) || + (eth_type == MLAN_ETHER_PKT_TYPE_WAPI))) || + (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) + + ) { + if (eth_type == MLAN_ETHER_PKT_TYPE_EAPOL) { + PRINTM_NETINTF(MMSG, pmpriv); + PRINTM(MMSG, "wlan: Send EAPOL pkt to " MACSTR "\n", + MAC2STR(pmbuf->pbuf + pmbuf->data_offset)); + } + if (pmadapter->tp_state_on) + pmadapter->callbacks.moal_tp_accounting( + pmadapter->pmoal_handle, pmbuf->pdesc, 2); + if (pmadapter->tp_state_drop_point == 2) + return 0; + else + wlan_add_buf_bypass_txqueue(pmadapter, pmbuf); + } else { + if (pmadapter->tp_state_on) + pmadapter->callbacks.moal_tp_accounting( + pmadapter->pmoal_handle, pmbuf->pdesc, 2); + if (pmadapter->tp_state_drop_point == 2) + return 0; + 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(t_void *adapter, pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + pmlan_private pmpriv = MNULL; + + ENTER(); + + if (pioctl_req == MNULL) { + PRINTM(MMSG, "Cancel all pending cmd!\n"); + wlan_cancel_all_pending_cmd(pmadapter, MFALSE); + 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; +} + +#ifdef USB +/** + * @brief Packet send completion callback + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer structure + * @param port Data port + * @param status Callback status + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status mlan_write_data_async_complete(t_void *pmlan_adapter, + pmlan_buffer pmbuf, t_u32 port, + mlan_status status) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + + ENTER(); + + if (port == pmadapter->tx_cmd_ep) { + pmadapter->cmd_sent = MFALSE; + PRINTM(MCMND, "mlan_write_data_async_complete: CMD\n"); + /* pmbuf was allocated by MLAN */ + wlan_free_mlan_buffer(pmadapter, pmbuf); + } else { + pmadapter->data_sent = MFALSE; + ret = wlan_write_data_complete(pmadapter, pmbuf, status); + } + + LEAVE(); + return ret; +} + +/** + * @brief Packet receive handler + * + * @param pmlan_adapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer structure + * @param port Data port + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or + * MLAN_STATUS_PENDING + */ +mlan_status mlan_recv(t_void *pmlan_adapter, pmlan_buffer pmbuf, t_u32 port) +{ + mlan_status ret = MLAN_STATUS_PENDING; + mlan_adapter *pmadapter = (mlan_adapter *)pmlan_adapter; + t_u8 *pbuf; + t_u32 len, recv_type; + t_u32 event_cause; +#ifdef DEBUG_LEVEL1 + t_u32 sec, usec; +#endif + t_u32 max_rx_data_size = MLAN_RX_DATA_BUF_SIZE; + + ENTER(); + + MASSERT(pmlan_adapter && pmbuf); + + if (pmadapter->hs_activated == MTRUE) + wlan_process_hs_config(pmadapter); + pbuf = pmbuf->pbuf + pmbuf->data_offset; + len = pmbuf->data_len; + + MASSERT(len >= MLAN_TYPE_LEN); + recv_type = *(t_u32 *)pbuf; + recv_type = wlan_le32_to_cpu(recv_type); + pbuf += MLAN_TYPE_LEN; + len -= MLAN_TYPE_LEN; + + if (port == pmadapter->rx_cmd_ep) { + switch (recv_type) { + case MLAN_USB_TYPE_CMD: + PRINTM_GET_SYS_TIME(MCMND, &sec, &usec); + PRINTM(MCMND, "mlan_recv: CMD (%lu.%06lu)\n", sec, + usec); + if (len > MRVDRV_SIZE_OF_CMD_BUFFER) { + pmbuf->status_code = MLAN_ERROR_CMD_RESP_FAIL; + ret = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "mlan_recv: CMD too large\n"); + } else if (!pmadapter->curr_cmd) { + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) { + pmbuf->data_offset += MLAN_TYPE_LEN; + pmbuf->data_len -= MLAN_TYPE_LEN; + wlan_process_sleep_confirm_resp( + pmadapter, + pmbuf->pbuf + + pmbuf->data_offset, + pmbuf->data_len); + pmbuf->flags |= + MLAN_BUF_FLAG_SLEEPCFM_RESP; + ret = MLAN_STATUS_SUCCESS; + + } else { + pmbuf->status_code = + MLAN_ERROR_CMD_RESP_FAIL; + ret = MLAN_STATUS_FAILURE; + } + PRINTM(MINFO, "mlan_recv: no curr_cmd\n"); + } else { + pmadapter->upld_len = len; + pmbuf->data_offset += MLAN_TYPE_LEN; + pmbuf->data_len -= MLAN_TYPE_LEN; + pmadapter->curr_cmd->respbuf = pmbuf; + pmadapter->cmd_resp_received = MTRUE; + } + break; + case MLAN_USB_TYPE_EVENT: + MASSERT(len >= sizeof(t_u32)); + memmove(pmadapter, &event_cause, pbuf, sizeof(t_u32)); + pmadapter->event_cause = wlan_le32_to_cpu(event_cause); + PRINTM_GET_SYS_TIME(MEVENT, &sec, &usec); + PRINTM(MEVENT, "mlan_recv: EVENT 0x%x (%lu.%06lu)\n", + pmadapter->event_cause, sec, usec); + pbuf += sizeof(t_u32); + len -= sizeof(t_u32); + if ((len > 0) && (len < MAX_EVENT_SIZE)) + memmove(pmadapter, pmadapter->event_body, pbuf, + len); + pmadapter->event_received = MTRUE; + pmadapter->pmlan_buffer_event = pmbuf; + /* remove 4 byte recv_type */ + pmbuf->data_offset += MLAN_TYPE_LEN; + pmbuf->data_len -= MLAN_TYPE_LEN; + /* MOAL to call mlan_main_process for processing */ + break; + default: + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + ret = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "mlan_recv: unknown recv_type 0x%x\n", + recv_type); + break; + } + } else if (port == pmadapter->rx_data_ep) { + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM(MDATA, "mlan_recv: DATA (%lu.%06lu)\n", sec, usec); +#if defined(USB) + if (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable) { + max_rx_data_size = pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_max; + if (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl + .aggr_mode == MLAN_USB_AGGR_MODE_NUM) { + max_rx_data_size *= + MAX(MLAN_USB_MAX_PKT_SIZE, + pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_align); + max_rx_data_size = MAX(max_rx_data_size, + MLAN_RX_DATA_BUF_SIZE); + } + } +#endif + if (len > max_rx_data_size) { + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + ret = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "mlan_recv: DATA too large\n"); + } else { + pmadapter->upld_len = len; + pmadapter->callbacks.moal_get_system_time( + pmadapter->pmoal_handle, &pmbuf->in_ts_sec, + &pmbuf->in_ts_usec); + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->rx_data_queue, + (pmlan_linked_list)pmbuf, MNULL, + MNULL); + pmadapter->rx_pkts_queued++; + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + pmadapter->rx_data_queue.plock); + pmadapter->data_received = MTRUE; + mlan_queue_rx_work(pmadapter); + } + } else { + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + ret = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "mlan_recv: unknown port number 0x%x\n", port); + } + + LEAVE(); + return ret; +} +#endif /* USB */ + +/** + * @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(t_void *pmlan_adapter, pmlan_buffer pmbuf, + 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(t_void *pmlan_adapter, t_u8 bss_num, 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; +} + +#if defined(SDIO) || defined(PCIE) +/** + * @brief This function gets interrupt status. + * @param msg_id only used for PCIE + */ +/** + * @param msg_id A message id + * @param adapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_FAILURE -- if the intererupt is not for us + */ +mlan_status mlan_interrupt(t_u16 msg_id, t_void *adapter) +{ + mlan_adapter *pmadapter = (mlan_adapter *)adapter; + mlan_status ret; + + ENTER(); + ret = pmadapter->ops.interrupt(msg_id, pmadapter); + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function wakeup firmware. + * + * @param adapter A pointer to mlan_adapter structure + * @param keep_wakeup keep wake up flag + * @return N/A + */ +t_void mlan_pm_wakeup_card(t_void *adapter, t_u8 keep_wakeup) +{ + mlan_adapter *pmadapter = (mlan_adapter *)adapter; + + ENTER(); + if (keep_wakeup) + pmadapter->ops.wakeup_card(pmadapter, MFALSE); + pmadapter->keep_wakeup = keep_wakeup; + + LEAVE(); +} + +/** + * @brief This function check main_process status. + * + * @param adapter A pointer to mlan_adapter structure + * @return MTRUE/MFALSE + */ +t_u8 mlan_is_main_process_running(t_void *adapter) +{ + mlan_adapter *pmadapter = (mlan_adapter *)adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 ret = MFALSE; + ENTER(); + pcb->moal_spin_lock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + + /* Check if already processing */ + if (pmadapter->mlan_processing) { + pmadapter->more_task_flag = MTRUE; + ret = MTRUE; + } + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pmadapter->pmain_proc_lock); + LEAVE(); + return ret; +} + +#ifdef PCIE +/** + * @brief This function sets the PCIE interrupt mode. + * + * @param adapter A pointer to mlan_adapter structure + * @param int_mode PCIE interrupt type active + * @param func_num PCIE function num + * @return N/A + */ +t_void mlan_set_int_mode(t_void *adapter, t_u32 int_mode, t_u8 func_num) +{ + mlan_adapter *pmadapter = (mlan_adapter *)adapter; + ENTER(); + pmadapter->pcard_pcie->pcie_int_mode = int_mode; + pmadapter->pcard_pcie->func_num = func_num; + LEAVE(); +} +#endif diff --git a/mxm_wifiex/wlan_src/mlan/mlan_sta_cmd.c b/mxm_wifiex/wlan_src/mlan/mlan_sta_cmd.c new file mode 100644 index 0000000..314e8fb --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_sta_cmd.c @@ -0,0 +1,2950 @@ +/** @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 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11ac.h" +#include "mlan_11ax.h" +#include "mlan_11h.h" +#ifdef SDIO +#include "mlan_sdio.h" +#endif /* SDIO */ +#include "mlan_meas.h" +#ifdef PCIE +#include "mlan_pcie.h" +#endif /* PCIE */ + +/******************************************************** + * 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action) +{ + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_RSSI_INFO); + pcmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_RSSI_INFO) + + S_DS_GEN); + pcmd->params.rssi_info.action = wlan_cpu_to_le16(cmd_action); + pcmd->params.rssi_info.ndata = + wlan_cpu_to_le16(pmpriv->data_avg_factor); + pcmd->params.rssi_info.nbcn = wlan_cpu_to_le16(pmpriv->bcn_avg_factor); + + /* Reset SNR/NF/RSSI values in private structure */ + pmpriv->data_rssi_last = 0; + pmpriv->data_nf_last = 0; + pmpriv->data_rssi_avg = 0; + pmpriv->data_nf_avg = 0; + pmpriv->bcn_rssi_last = 0; + pmpriv->bcn_nf_last = 0; + pmpriv->bcn_rssi_avg = 0; + pmpriv->bcn_nf_avg = 0; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of RSSI info. + * + * @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_ext(pmlan_private pmpriv, + HostCmd_DS_COMMAND *pcmd, + t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_802_11_RSSI_INFO_EXT *rssi_info_ext_cmd = + &pcmd->params.rssi_info_ext; + mlan_ds_get_info *info = (mlan_ds_get_info *)pdata_buf; + MrvlIEtypes_RSSI_EXT_t *signal_info_tlv = MNULL; + t_u8 *pos = MNULL; + + ENTER(); + + pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_RSSI_INFO_EXT); + pcmd->size = sizeof(HostCmd_DS_802_11_RSSI_INFO_EXT) + S_DS_GEN; + rssi_info_ext_cmd->action = wlan_cpu_to_le16(cmd_action); + rssi_info_ext_cmd->ndata = 0; + rssi_info_ext_cmd->nbcn = 0; + + if (info->param.path_id) { + pos = (t_u8 *)rssi_info_ext_cmd->tlv_buf; + signal_info_tlv = (MrvlIEtypes_RSSI_EXT_t *)pos; + signal_info_tlv->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_RSSI_EXT_t) - + sizeof(MrvlIEtypesHeader_t)); + signal_info_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_RSSI_INFO); + signal_info_tlv->path_id = + wlan_cpu_to_le16(info->param.path_id); + pcmd->size += sizeof(MrvlIEtypes_RSSI_EXT_t); + } + pcmd->size = wlan_cpu_to_le16(pcmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of snmp_mib. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param cmd_oid OID: ENABLE or DISABLE + * @param pdata_buf A pointer to command information buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_802_11_snmp_mib(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u32 cmd_oid, + t_void *pdata_buf) +{ + HostCmd_DS_802_11_SNMP_MIB *psnmp_mib = &cmd->params.smib; + t_u32 ul_temp; + + ENTER(); + PRINTM(MINFO, "SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); + cmd->size = sizeof(HostCmd_DS_802_11_SNMP_MIB) - 1 + S_DS_GEN; + + if (cmd_action == HostCmd_ACT_GEN_GET) { + psnmp_mib->query_type = wlan_cpu_to_le16(HostCmd_ACT_GEN_GET); + psnmp_mib->buf_size = wlan_cpu_to_le16(MAX_SNMP_BUF_SIZE); + cmd->size += MAX_SNMP_BUF_SIZE; + } + + switch (cmd_oid) { + case DtimPeriod_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)DtimPeriod_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + ul_temp = *((t_u32 *)pdata_buf); + psnmp_mib->value[0] = (t_u8)ul_temp; + cmd->size += sizeof(t_u8); + } + break; + case FragThresh_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)FragThresh_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *((t_u32 *)pdata_buf); + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case RtsThresh_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)RtsThresh_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *((t_u32 *)pdata_buf); + *(t_u16 *)(psnmp_mib->value) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + + case ShortRetryLim_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)ShortRetryLim_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = (*(t_u32 *)pdata_buf); + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case Dot11D_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)Dot11D_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *(t_u32 *)pdata_buf; + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case Dot11H_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)Dot11H_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *(t_u32 *)pdata_buf; + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case WwsMode_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)WwsMode_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *((t_u32 *)pdata_buf); + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + } + break; + case Thermal_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)Thermal_i); + break; + case NullPktPeriod_i: + /** keep alive null data pkt interval in full power mode */ + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)NullPktPeriod_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u32)); + ul_temp = *((t_u32 *)pdata_buf); + ul_temp = wlan_cpu_to_le32(ul_temp); + memcpy_ext(pmpriv->adapter, psnmp_mib->value, &ul_temp, + sizeof(t_u32), sizeof(t_u32)); + cmd->size += sizeof(t_u32); + } + break; + case ECSAEnable_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)ECSAEnable_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + psnmp_mib->value[0] = *((t_u8 *)pdata_buf); + cmd->size += sizeof(t_u8); + } + break; + case SignalextEnable_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)SignalextEnable_i); + if (cmd_action == HostCmd_ACT_GEN_SET) { + psnmp_mib->query_type = + wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + psnmp_mib->value[0] = *(t_u8 *)pdata_buf; + cmd->size += sizeof(t_u8); + } + break; + default: + break; + } + cmd->size = wlan_cpu_to_le16(cmd->size); + PRINTM(MINFO, + "SNMP_CMD: Action=0x%x, OID=0x%x, OIDSize=0x%x, Value=0x%x\n", + cmd_action, cmd_oid, wlan_le16_to_cpu(psnmp_mib->buf_size), + wlan_le16_to_cpu(*(t_u16 *)psnmp_mib->value)); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of get_log. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_802_11_get_log(pmlan_private pmpriv, + 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 MFG Continuous Tx cmd. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_mfg_tx_cont(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 action, + t_void *pdata_buf) +{ + struct mfg_cmd_tx_cont *mcmd = + (struct mfg_cmd_tx_cont *)&cmd->params.mfg_tx_cont; + struct mfg_cmd_tx_cont *cfg = (struct mfg_cmd_tx_cont *)pdata_buf; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_MFG_COMMAND); + cmd->size = wlan_cpu_to_le16(sizeof(struct mfg_cmd_tx_cont) + S_DS_GEN); + + mcmd->mfg_cmd = wlan_cpu_to_le32(cfg->mfg_cmd); + mcmd->action = wlan_cpu_to_le16(action); + if (action == HostCmd_ACT_GEN_SET) { + mcmd->enable_tx = wlan_cpu_to_le32(cfg->enable_tx); + mcmd->cw_mode = wlan_cpu_to_le32(cfg->cw_mode); + mcmd->payload_pattern = wlan_cpu_to_le32(cfg->payload_pattern); + mcmd->cs_mode = wlan_cpu_to_le32(cfg->cs_mode); + mcmd->act_sub_ch = wlan_cpu_to_le32(cfg->act_sub_ch); + mcmd->tx_rate = wlan_cpu_to_le32(cfg->tx_rate); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of MFG Tx frame. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_mfg_tx_frame(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 action, + t_void *pdata_buf) +{ + struct mfg_cmd_tx_frame2 *mcmd = + (struct mfg_cmd_tx_frame2 *)&cmd->params.mfg_tx_frame2; + struct mfg_cmd_tx_frame2 *cfg = (struct mfg_cmd_tx_frame2 *)pdata_buf; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_MFG_COMMAND); + cmd->size = + wlan_cpu_to_le16(sizeof(struct mfg_cmd_tx_frame2) + S_DS_GEN); + + mcmd->mfg_cmd = wlan_cpu_to_le32(cfg->mfg_cmd); + mcmd->action = wlan_cpu_to_le16(action); + if (action == HostCmd_ACT_GEN_SET) { + mcmd->enable = wlan_cpu_to_le32(cfg->enable); + mcmd->data_rate = wlan_cpu_to_le32(cfg->data_rate); + mcmd->frame_pattern = wlan_cpu_to_le32(cfg->frame_pattern); + mcmd->frame_length = wlan_cpu_to_le32(cfg->frame_length); + mcmd->adjust_burst_sifs = + wlan_cpu_to_le16(cfg->adjust_burst_sifs); + mcmd->burst_sifs_in_us = + wlan_cpu_to_le32(cfg->burst_sifs_in_us); + mcmd->short_preamble = wlan_cpu_to_le32(cfg->short_preamble); + mcmd->act_sub_ch = wlan_cpu_to_le32(cfg->act_sub_ch); + mcmd->short_gi = wlan_cpu_to_le32(cfg->short_gi); + mcmd->tx_bf = wlan_cpu_to_le32(cfg->tx_bf); + mcmd->gf_mode = wlan_cpu_to_le32(cfg->gf_mode); + mcmd->stbc = wlan_cpu_to_le32(cfg->stbc); + memcpy_ext(pmpriv->adapter, mcmd->bssid, cfg->bssid, + MLAN_MAC_ADDR_LENGTH, sizeof(mcmd->bssid)); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of MFG cmd. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_mfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 action, t_void *pdata_buf) +{ + struct mfg_cmd_generic_cfg *mcmd = + (struct mfg_cmd_generic_cfg *)&cmd->params.mfg_generic_cfg; + struct mfg_cmd_generic_cfg *cfg = + (struct mfg_cmd_generic_cfg *)pdata_buf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!mcmd || !cfg) { + ret = MLAN_STATUS_FAILURE; + goto cmd_mfg_done; + } + switch (cfg->mfg_cmd) { + case MFG_CMD_TX_CONT: + ret = wlan_cmd_mfg_tx_cont(pmpriv, cmd, action, pdata_buf); + goto cmd_mfg_done; + case MFG_CMD_TX_FRAME: + ret = wlan_cmd_mfg_tx_frame(pmpriv, cmd, action, pdata_buf); + goto cmd_mfg_done; + case MFG_CMD_SET_TEST_MODE: + case MFG_CMD_UNSET_TEST_MODE: + case MFG_CMD_TX_ANT: + case MFG_CMD_RX_ANT: + case MFG_CMD_RF_CHAN: + case MFG_CMD_CLR_RX_ERR: + case MFG_CMD_RF_BAND_AG: + case MFG_CMD_RF_CHANNELBW: + case MFG_CMD_RFPWR: + break; + default: + ret = MLAN_STATUS_FAILURE; + goto cmd_mfg_done; + } + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_MFG_COMMAND); + cmd->size = + wlan_cpu_to_le16(sizeof(struct mfg_cmd_generic_cfg) + S_DS_GEN); + + mcmd->mfg_cmd = wlan_cpu_to_le32(cfg->mfg_cmd); + mcmd->action = wlan_cpu_to_le16(action); + if (action == HostCmd_ACT_GEN_SET) { + mcmd->data1 = wlan_cpu_to_le32(cfg->data1); + mcmd->data2 = wlan_cpu_to_le32(cfg->data2); + mcmd->data3 = wlan_cpu_to_le32(cfg->data3); + } +cmd_mfg_done: + LEAVE(); + return ret; +} + +/** + * @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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_802_11_RF_TX_POWER *prtp = &cmd->params.txp; + + ENTER(); + + cmd->size = wlan_cpu_to_le16((sizeof(HostCmd_DS_802_11_RF_TX_POWER)) + + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_RF_TX_POWER); + prtp->action = cmd_action; + + PRINTM(MINFO, "RF_TX_POWER_CMD: Size:%d Cmd:0x%x Act:%d\n", cmd->size, + cmd->command, prtp->action); + + switch (cmd_action) { + case HostCmd_ACT_GEN_GET: + prtp->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_GET); + prtp->current_level = 0; + break; + + case HostCmd_ACT_GEN_SET: + prtp->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + prtp->current_level = wlan_cpu_to_le16(*((t_s16 *)pdata_buf)); + break; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief Check if any p2p interface is conencted + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return MTRUE/MFALSE; + */ +static t_u8 wlan_is_p2p_connected(pmlan_adapter pmadapter) +{ + int j; + pmlan_private priv; + + ENTER(); + for (j = 0; j < pmadapter->priv_num; ++j) { + priv = pmadapter->priv[j]; + if (priv) { + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if ((priv->bss_role == MLAN_BSS_ROLE_STA) && + (priv->media_connected == MTRUE)) { + LEAVE(); + return MTRUE; + } + if ((priv->bss_role == MLAN_BSS_ROLE_UAP) && + (priv->uap_bss_started == MTRUE)) { + LEAVE(); + return MTRUE; + } + } + } + } + LEAVE(); + return MFALSE; +} +#endif + +/** + * @brief This function prepares command of hs_cfg. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_802_11_hs_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + hs_config_param *pdata_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_HS_CFG_ENH *phs_cfg = &cmd->params.opt_hs_cfg; + t_u16 hs_activate = MFALSE; + t_u8 *tlv = (t_u8 *)phs_cfg + sizeof(HostCmd_DS_802_11_HS_CFG_ENH); + MrvlIEtypes_HsWakeHoldoff_t *holdoff_tlv = MNULL; + MrvlIEtypes_PsParamsInHs_t *psparam_tlv = MNULL; + MrvlIEtypes_WakeupSourceGPIO_t *gpio_tlv = MNULL; + MrvlIEtypes_MgmtFrameFilter_t *mgmt_filter_tlv = MNULL; + MrvlIEtypes_WakeupExtend_t *ext_tlv = MNULL; + MrvlIEtypes_HS_Antmode_t *antmode_tlv = MNULL; + + ENTER(); + + if (pdata_buf == MNULL) { + /* New Activate command */ + hs_activate = MTRUE; + } + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); + + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_802_11_HS_CFG_ENH); + + if (hs_activate) { + cmd->size = wlan_cpu_to_le16(cmd->size); + phs_cfg->action = wlan_cpu_to_le16(HS_ACTIVATE); + phs_cfg->params.hs_activate.resp_ctrl = + wlan_cpu_to_le16(RESP_NEEDED); + } else { + phs_cfg->action = wlan_cpu_to_le16(HS_CONFIGURE); +#ifdef WIFI_DIRECT_SUPPORT + if (wlan_is_p2p_connected(pmadapter)) + phs_cfg->params.hs_config.conditions = wlan_cpu_to_le32( + pdata_buf->conditions | + HOST_SLEEP_COND_MULTICAST_DATA); + else +#endif + phs_cfg->params.hs_config.conditions = + wlan_cpu_to_le32(pdata_buf->conditions); + phs_cfg->params.hs_config.gpio = pdata_buf->gpio; + phs_cfg->params.hs_config.gap = pdata_buf->gap; + if (pmadapter->min_wake_holdoff) { + cmd->size += sizeof(MrvlIEtypes_HsWakeHoldoff_t); + holdoff_tlv = (MrvlIEtypes_HsWakeHoldoff_t *)tlv; + holdoff_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_HS_WAKE_HOLDOFF); + holdoff_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_HsWakeHoldoff_t) - + sizeof(MrvlIEtypesHeader_t)); + holdoff_tlv->min_wake_holdoff = + wlan_cpu_to_le16(pmadapter->min_wake_holdoff); + tlv += sizeof(MrvlIEtypes_HsWakeHoldoff_t); + PRINTM(MCMND, "min_wake_holdoff=%d\n", + pmadapter->min_wake_holdoff); + } + if (pmadapter->hs_wake_interval && pmpriv->media_connected && + (pmpriv->bss_type == MLAN_BSS_TYPE_STA)) { + cmd->size += sizeof(MrvlIEtypes_PsParamsInHs_t); + psparam_tlv = (MrvlIEtypes_PsParamsInHs_t *)tlv; + psparam_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_PS_PARAMS_IN_HS); + psparam_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_PsParamsInHs_t) - + sizeof(MrvlIEtypesHeader_t)); + psparam_tlv->hs_wake_interval = + wlan_cpu_to_le32(pmadapter->hs_wake_interval); + psparam_tlv->hs_inactivity_timeout = wlan_cpu_to_le32( + pmadapter->hs_inactivity_timeout); + tlv += sizeof(MrvlIEtypes_PsParamsInHs_t); + PRINTM(MCMND, "hs_wake_interval=%d\n", + pmadapter->hs_wake_interval); + PRINTM(MCMND, "hs_inactivity_timeout=%d\n", + pmadapter->hs_inactivity_timeout); + } + if (pmadapter->param_type_ind == 1) { + cmd->size += sizeof(MrvlIEtypes_WakeupSourceGPIO_t); + gpio_tlv = (MrvlIEtypes_WakeupSourceGPIO_t *)tlv; + gpio_tlv->header.type = wlan_cpu_to_le16( + TLV_TYPE_HS_WAKEUP_SOURCE_GPIO); + gpio_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_WakeupSourceGPIO_t) - + sizeof(MrvlIEtypesHeader_t)); + gpio_tlv->ind_gpio = (t_u8)pmadapter->ind_gpio; + gpio_tlv->level = (t_u8)pmadapter->level; + tlv += sizeof(MrvlIEtypes_WakeupSourceGPIO_t); + } + if (pmadapter->param_type_ext == 2) { + cmd->size += sizeof(MrvlIEtypes_WakeupExtend_t); + ext_tlv = (MrvlIEtypes_WakeupExtend_t *)tlv; + ext_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WAKEUP_EXTEND); + ext_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_WakeupExtend_t) - + sizeof(MrvlIEtypesHeader_t)); + ext_tlv->event_force_ignore = + wlan_cpu_to_le32(pmadapter->event_force_ignore); + ext_tlv->event_use_ext_gap = + wlan_cpu_to_le32(pmadapter->event_use_ext_gap); + ext_tlv->ext_gap = pmadapter->ext_gap; + ext_tlv->gpio_wave = pmadapter->gpio_wave; + tlv += sizeof(MrvlIEtypes_WakeupExtend_t); + } + if (pmadapter->mgmt_filter[0].type) { + int i = 0; + mgmt_frame_filter mgmt_filter[MAX_MGMT_FRAME_FILTER]; + + memset(pmadapter, mgmt_filter, 0, + MAX_MGMT_FRAME_FILTER * + sizeof(mgmt_frame_filter)); + mgmt_filter_tlv = (MrvlIEtypes_MgmtFrameFilter_t *)tlv; + mgmt_filter_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_MGMT_FRAME_WAKEUP); + tlv += sizeof(MrvlIEtypesHeader_t); + while (i < MAX_MGMT_FRAME_FILTER && + pmadapter->mgmt_filter[i].type) { + mgmt_filter[i].action = + (t_u8)pmadapter->mgmt_filter[i].action; + mgmt_filter[i].type = + (t_u8)pmadapter->mgmt_filter[i].type; + mgmt_filter[i].frame_mask = wlan_cpu_to_le32( + pmadapter->mgmt_filter[i].frame_mask); + i++; + } + memcpy_ext(pmadapter, (t_u8 *)mgmt_filter_tlv->filter, + (t_u8 *)mgmt_filter, + i * sizeof(mgmt_frame_filter), + sizeof(mgmt_filter_tlv->filter)); + tlv += i * sizeof(mgmt_frame_filter); + mgmt_filter_tlv->header.len = + wlan_cpu_to_le16(i * sizeof(mgmt_frame_filter)); + cmd->size += i * sizeof(mgmt_frame_filter) + + sizeof(MrvlIEtypesHeader_t); + } + if (pmadapter->hs_mimo_switch) { + cmd->size += sizeof(MrvlIEtypes_HS_Antmode_t); + antmode_tlv = (MrvlIEtypes_HS_Antmode_t *)tlv; + antmode_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_HS_ANTMODE); + antmode_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_HS_Antmode_t) - + sizeof(MrvlIEtypesHeader_t)); + antmode_tlv->txpath_antmode = ANTMODE_FW_DECISION; + antmode_tlv->rxpath_antmode = ANTMODE_FW_DECISION; + tlv += sizeof(MrvlIEtypes_HS_Antmode_t); + PRINTM(MCMND, "hs_mimo_switch=%d\n", + pmadapter->hs_mimo_switch); + PRINTM(MCMND, "txpath_antmode=%d, rxpath_antmode=%d\n", + antmode_tlv->txpath_antmode, + antmode_tlv->rxpath_antmode); + } + cmd->size = wlan_cpu_to_le16(cmd->size); + PRINTM(MCMND, "HS_CFG_CMD: condition:0x%x gpio:0x%x\n", + phs_cfg->params.hs_config.conditions, + phs_cfg->params.hs_config.gpio); + PRINTM(MCMND, "HS_CFG_CMD: gap:0x%x holdoff=%d\n", + phs_cfg->params.hs_config.gap, + pmadapter->min_wake_holdoff); + PRINTM(MCMND, + "HS_CFG_CMD: wake_interval=%d inactivity_timeout=%d\n", + pmadapter->hs_wake_interval, + pmadapter->hs_inactivity_timeout); + } + + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + 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_ext(pmpriv->adapter, pmc_addr->mac_list, pmcast_list->mac_list, + pmcast_list->num_multicast_addr * MLAN_MAC_ADDR_LENGTH, + sizeof(pmc_addr->mac_list)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of deauthenticate/disassociate. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd_no Command number + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_802_11_deauthenticate(pmlan_private pmpriv, + t_u16 cmd_no, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf) +{ + HostCmd_DS_802_11_DEAUTHENTICATE *pdeauth = &cmd->params.deauth; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(cmd_no); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_802_11_DEAUTHENTICATE) + + S_DS_GEN); + + /* Set AP MAC address */ + memcpy_ext(pmpriv->adapter, pdeauth, (t_u8 *)pdata_buf, + sizeof(*pdeauth), sizeof(*pdeauth)); + if (cmd_no == HostCmd_CMD_802_11_DEAUTHENTICATE) + PRINTM(MCMND, "Deauth: " MACSTR "\n", + MAC2STR(pdeauth->mac_addr)); + else + PRINTM(MCMND, "Disassociate: " MACSTR "\n", + MAC2STR(pdeauth->mac_addr)); + + if (pmpriv->adapter->state_11h.recvd_chanswann_event) { +/** Reason code 36 = Requested from peer station as it is leaving the BSS */ +#define REASON_CODE_PEER_STA_LEAVING 36 + pdeauth->reason_code = + wlan_cpu_to_le16(REASON_CODE_PEER_STA_LEAVING); + } else { + pdeauth->reason_code = wlan_cpu_to_le16(pdeauth->reason_code); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of ad_hoc_stop. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_802_11_ad_hoc_stop(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd) +{ + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP); + cmd->size = wlan_cpu_to_le16(S_DS_GEN); + + if (wlan_11h_is_active(pmpriv)) + wlan_11h_activate(pmpriv, MNULL, MFALSE); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of key_material. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param cmd_oid OID: ENABLE or DISABLE + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_cmd_802_11_key_material(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u32 cmd_oid, + t_void *pdata_buf) +{ + HostCmd_DS_802_11_KEY_MATERIAL *pkey_material = + &cmd->params.key_material; + mlan_ds_encrypt_key *pkey = (mlan_ds_encrypt_key *)pdata_buf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + if (!pkey) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + pkey_material->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_GET) { + PRINTM(MCMND, "GET Key\n"); + pkey_material->key_param_set.key_idx = + pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pkey_material->key_param_set.key_info |= + KEY_INFO_MCAST_KEY; + else + pkey_material->key_param_set.key_info |= + KEY_INFO_UCAST_KEY; + if (pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK) + pkey_material->key_param_set.key_info = + KEY_INFO_CMAC_AES_KEY; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(pkey_material->action)); + goto done; + } + memset(pmpriv->adapter, &pkey_material->key_param_set, 0, + sizeof(MrvlIEtype_KeyParamSetV2_t)); + if (pkey->key_flags & KEY_FLAG_REMOVE_KEY) { + pkey_material->action = + wlan_cpu_to_le16(HostCmd_ACT_GEN_REMOVE); + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN); + pkey_material->key_param_set.key_idx = + pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.key_info = wlan_cpu_to_le16( + KEY_INFO_MCAST_KEY | KEY_INFO_UCAST_KEY); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Remove Key\n"); + goto done; + } + pkey_material->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + pkey_material->key_param_set.key_idx = pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.key_info = KEY_INFO_ENABLE_KEY; + memcpy_ext(pmpriv->adapter, pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + if (pkey->key_len <= MAX_WEP_KEY_SIZE) { + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(wep_param_t)); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_WEP; + if (pkey->is_current_wep_key) { + pkey_material->key_param_set.key_info |= + KEY_INFO_MCAST_KEY | KEY_INFO_UCAST_KEY; + if (pkey_material->key_param_set.key_idx == + (pmpriv->wep_key_curr_index & KEY_INDEX_MASK)) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + } else { + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pkey_material->key_param_set.key_info |= + KEY_INFO_MCAST_KEY; + else + pkey_material->key_param_set.key_info |= + KEY_INFO_UCAST_KEY; + } + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + pkey_material->key_param_set.key_params.wep.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.wep.key, + pkey->key_material, pkey->key_len, MAX_WEP_KEY_SIZE); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(wep_param_t) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set WEP Key\n"); + goto done; + } + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pkey_material->key_param_set.key_info |= KEY_INFO_MCAST_KEY; + else + pkey_material->key_param_set.key_info |= KEY_INFO_UCAST_KEY; + if (pkey->key_flags & KEY_FLAG_SET_TX_KEY) + pkey_material->key_param_set.key_info |= + KEY_INFO_TX_KEY | KEY_INFO_RX_KEY; + else + pkey_material->key_param_set.key_info |= KEY_INFO_RX_KEY; + if (pkey->is_wapi_key) { + pkey_material->key_param_set.key_type = KEY_TYPE_ID_WAPI; + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.wapi.pn, + pkey->pn, PN_SIZE, PN_SIZE); + pkey_material->key_param_set.key_params.wapi.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.wapi.key, + pkey->key_material, pkey->key_len, WAPI_KEY_SIZE); + if (!pmpriv->sec_info.wapi_key_on) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pmpriv->sec_info.wapi_key_on = MTRUE; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(wapi_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(wapi_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set WAPI Key\n"); + goto done; + } + if (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) { + /* Enable default key for WPA/WPA2 */ + if (!pmpriv->wpa_is_gtk_set) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + } else { + pkey_material->key_param_set.key_info |= KEY_INFO_DEFAULT_KEY; + /* Enable unicast bit for WPA-NONE/ADHOC_AES */ + if ((!pmpriv->sec_info.wpa2_enabled) && + (pkey->key_flags & KEY_FLAG_SET_TX_KEY)) + pkey_material->key_param_set.key_info |= + KEY_INFO_UCAST_KEY; + } + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + if (pkey->key_flags & KEY_FLAG_GCMP || + pkey->key_flags & KEY_FLAG_GCMP_256) { + if (pkey->key_flags & + (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) { + memcpy_ext( + pmpriv->adapter, + pkey_material->key_param_set.key_params.gcmp.pn, + pkey->pn, SEQ_MAX_SIZE, WPA_PN_SIZE); + } + if (pkey->key_flags & KEY_FLAG_GCMP) + pkey_material->key_param_set.key_type = + KEY_TYPE_ID_GCMP; + else + pkey_material->key_param_set.key_type = + KEY_TYPE_ID_GCMP_256; + pkey_material->key_param_set.key_params.gcmp.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.gcmp.key, + pkey->key_material, pkey->key_len, WPA_GCMP_KEY_LEN); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(gcmp_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(gcmp_param) + + sizeof(pkey_material->action)); + + goto done; + } + if (pkey->key_flags & KEY_FLAG_CCMP_256) { + if (pkey->key_flags & + (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) { + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params + .ccmp256.pn, + pkey->pn, SEQ_MAX_SIZE, WPA_PN_SIZE); + } + pkey_material->key_param_set.key_type = KEY_TYPE_ID_CCMP_256; + pkey_material->key_param_set.key_params.ccmp256.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.ccmp256.key, + pkey->key_material, pkey->key_len, + WPA_CCMP_256_KEY_LEN); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(ccmp_256_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(ccmp_256_param) + + sizeof(pkey_material->action)); + + goto done; + } + if (pkey->key_len == WPA_AES_KEY_LEN && + !(pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK)) { + if (pkey->key_flags & + (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy_ext( + pmpriv->adapter, + pkey_material->key_param_set.key_params.aes.pn, + pkey->pn, SEQ_MAX_SIZE, WPA_PN_SIZE); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_AES; + pkey_material->key_param_set.key_params.aes.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.aes.key, + pkey->key_material, pkey->key_len, WPA_AES_KEY_LEN); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(aes_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(aes_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set AES Key\n"); + goto done; + } + if (pkey->key_len == WPA_IGTK_KEY_LEN && + (pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK)) { + if (pkey->key_flags & + (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params + .cmac_aes.ipn, + pkey->pn, SEQ_MAX_SIZE, IGTK_PN_SIZE); + pkey_material->key_param_set.key_info &= + ~(wlan_cpu_to_le16(KEY_INFO_MCAST_KEY)); + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_AES_MCAST_IGTK); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; + pkey_material->key_param_set.key_params.cmac_aes.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.cmac_aes.key, + pkey->key_material, pkey->key_len, CMAC_AES_KEY_LEN); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(cmac_aes_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(cmac_aes_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set CMAC AES Key\n"); + goto done; + } + if (pkey->key_len == WPA_TKIP_KEY_LEN) { + if (pkey->key_flags & + (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy_ext( + pmpriv->adapter, + pkey_material->key_param_set.key_params.tkip.pn, + pkey->pn, SEQ_MAX_SIZE, WPA_PN_SIZE); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_TKIP; + pkey_material->key_param_set.key_params.tkip.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.tkip.key, + pkey->key_material, pkey->key_len, WPA_TKIP_KEY_LEN); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(tkip_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(tkip_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set TKIP Key\n"); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of gtk rekey offload + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param cmd_oid OID: ENABLE or DISABLE + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_cmd_gtk_rekey_offload(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u32 cmd_oid, + t_void *pdata_buf) +{ + HostCmd_DS_GTK_REKEY_PARAMS *rekey = &cmd->params.gtk_rekey; + mlan_ds_misc_gtk_rekey_data *data = + (mlan_ds_misc_gtk_rekey_data *)pdata_buf; + t_u64 rekey_ctr; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(*rekey) + S_DS_GEN); + + rekey->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + memcpy_ext(pmpriv->adapter, rekey->kek, data->kek, MLAN_KEK_LEN, + MLAN_KEK_LEN); + memcpy_ext(pmpriv->adapter, rekey->kck, data->kck, MLAN_KCK_LEN, + MLAN_KCK_LEN); + rekey_ctr = wlan_le64_to_cpu( + swap_byte_64(*(t_u64 *)data->replay_ctr)); + rekey->replay_ctr_low = wlan_cpu_to_le32((t_u32)rekey_ctr); + rekey->replay_ctr_high = + wlan_cpu_to_le32((t_u64)rekey_ctr >> 32); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function send eapol pkt to FW + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_eapol_pkt(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_EAPOL_PKT *eapol_pkt = &cmd->params.eapol_pkt; + mlan_buffer *pmbuf = (mlan_buffer *)pdata_buf; + + ENTER(); + eapol_pkt->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + cmd->size = sizeof(HostCmd_DS_EAPOL_PKT) + S_DS_GEN; + cmd->command = wlan_cpu_to_le16(cmd->command); + + eapol_pkt->tlv_eapol.header.type = wlan_cpu_to_le16(TLV_TYPE_EAPOL_PKT); + eapol_pkt->tlv_eapol.header.len = wlan_cpu_to_le16(pmbuf->data_len); + memcpy_ext(pmpriv->adapter, eapol_pkt->tlv_eapol.pkt_buf, + pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len, + pmbuf->data_len); + cmd->size += pmbuf->data_len; + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Handle the supplicant profile command + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_802_11_supplicant_profile(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_802_11_SUPPLICANT_PROFILE *sup_profile = + (HostCmd_DS_802_11_SUPPLICANT_PROFILE *)&( + cmd->params.esupplicant_profile); + MrvlIEtypes_EncrProto_t *encr_proto_tlv = MNULL; + MrvlIEtypes_Cipher_t *pcipher_tlv = MNULL; + t_u8 *ptlv_buffer = (t_u8 *)sup_profile->tlv_buf; + mlan_ds_esupp_mode *esupp = MNULL; + + ENTER(); + + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_802_11_SUPPLICANT_PROFILE) + S_DS_GEN - 1); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_SUPPLICANT_PROFILE); + sup_profile->action = wlan_cpu_to_le16(cmd_action); + if ((cmd_action == HostCmd_ACT_GEN_SET) && pdata_buf) { + esupp = (mlan_ds_esupp_mode *)pdata_buf; + if (esupp->rsn_mode) { + encr_proto_tlv = (MrvlIEtypes_EncrProto_t *)ptlv_buffer; + encr_proto_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_ENCRYPTION_PROTO); + encr_proto_tlv->header.len = + (t_u16)sizeof(encr_proto_tlv->rsn_mode); + encr_proto_tlv->rsn_mode = + wlan_cpu_to_le16(esupp->rsn_mode); + ptlv_buffer += (encr_proto_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += (encr_proto_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + encr_proto_tlv->header.len = + wlan_cpu_to_le16(encr_proto_tlv->header.len); + } + if (esupp->act_paircipher || esupp->act_groupcipher) { + pcipher_tlv = (MrvlIEtypes_Cipher_t *)ptlv_buffer; + pcipher_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_CIPHER); + pcipher_tlv->header.len = + (t_u16)(sizeof(pcipher_tlv->pair_cipher) + + sizeof(pcipher_tlv->group_cipher)); + if (esupp->act_paircipher) { + pcipher_tlv->pair_cipher = + esupp->act_paircipher & 0xff; + } + if (esupp->act_groupcipher) { + pcipher_tlv->group_cipher = + esupp->act_groupcipher & 0xff; + } + ptlv_buffer += (pcipher_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + cmd->size += (pcipher_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + pcipher_tlv->header.len = + wlan_cpu_to_le16(pcipher_tlv->header.len); + } + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of rf_channel. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_802_11_rf_channel(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + 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)) + prf_chan->rf_type.bandcfg.chanBand = BAND_5GHZ; + prf_chan->rf_type.bandcfg.chanWidth = + pmpriv->adapter->chan_bandwidth; + prf_chan->current_channel = + wlan_cpu_to_le16(*((t_u16 *)pdata_buf)); + } + prf_chan->action = wlan_cpu_to_le16(cmd_action); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of ibss_coalescing_status. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer or MNULL + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_ibss_coalescing_status(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, 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); + + 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_ext(pmpriv->adapter, + pmgmt_ie_list->ds_mgmt_ie.ie_data_list, + cust_ie->ie_data_list, cust_ie->len, + sizeof(pmgmt_ie_list->ds_mgmt_ie.ie_data_list)); + + cmd->size -= (MAX_MGMT_IE_INDEX_TO_FW * sizeof(custom_ie)) + + sizeof(tlvbuf_max_mgmt_ie); + cmd->size += cust_ie->len; + cmd->size = wlan_cpu_to_le16(cmd->size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG *cfg = &cmd->params.sys_clock_cfg; + mlan_ds_misc_sys_clock *clk_cfg = (mlan_ds_misc_sys_clock *)pdata_buf; + int i = 0; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG); + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG) + S_DS_GEN); + + cfg->action = wlan_cpu_to_le16(cmd_action); + cfg->cur_sys_clk = wlan_cpu_to_le16(clk_cfg->cur_sys_clk); + cfg->sys_clk_type = wlan_cpu_to_le16(clk_cfg->sys_clk_type); + cfg->sys_clk_len = + wlan_cpu_to_le16(clk_cfg->sys_clk_num) * sizeof(t_u16); + for (i = 0; i < clk_cfg->sys_clk_num; i++) + cfg->sys_clk[i] = wlan_cpu_to_le16(clk_cfg->sys_clk[i]); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of subscribe event. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_subscribe_event(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + mlan_ds_subscribe_evt *sub_evt = (mlan_ds_subscribe_evt *)pdata_buf; + HostCmd_DS_SUBSCRIBE_EVENT *evt = + (HostCmd_DS_SUBSCRIBE_EVENT *)&cmd->params.subscribe_event; + t_u16 cmd_size = 0; + t_u8 *tlv = MNULL; + MrvlIEtypes_BeaconLowRssiThreshold_t *rssi_low = MNULL; + MrvlIEtypes_BeaconLowSnrThreshold_t *snr_low = MNULL; + MrvlIEtypes_FailureCount_t *fail_count = MNULL; + MrvlIEtypes_BeaconsMissed_t *beacon_missed = MNULL; + MrvlIEtypes_BeaconHighRssiThreshold_t *rssi_high = MNULL; + MrvlIEtypes_BeaconHighSnrThreshold_t *snr_high = MNULL; + MrvlIEtypes_DataLowRssiThreshold_t *data_rssi_low = MNULL; + MrvlIEtypes_DataLowSnrThreshold_t *data_snr_low = MNULL; + MrvlIEtypes_DataHighRssiThreshold_t *data_rssi_high = MNULL; + MrvlIEtypes_DataHighSnrThreshold_t *data_snr_high = MNULL; + MrvlIEtypes_LinkQualityThreshold_t *link_quality = MNULL; + MrvlIETypes_PreBeaconMissed_t *pre_bcn_missed = MNULL; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT); + evt->action = wlan_cpu_to_le16(cmd_action); + cmd_size = sizeof(HostCmd_DS_SUBSCRIBE_EVENT) + S_DS_GEN; + if (cmd_action == HostCmd_ACT_GEN_GET) + goto done; + evt->action = wlan_cpu_to_le16(sub_evt->evt_action); + evt->event_bitmap = wlan_cpu_to_le16(sub_evt->evt_bitmap); + tlv = (t_u8 *)cmd + cmd_size; + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_RSSI_LOW) { + rssi_low = (MrvlIEtypes_BeaconLowRssiThreshold_t *)tlv; + rssi_low->header.type = wlan_cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_low->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + rssi_low->value = sub_evt->low_rssi; + rssi_low->frequency = sub_evt->low_rssi_freq; + tlv += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowRssiThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_SNR_LOW) { + snr_low = (MrvlIEtypes_BeaconLowSnrThreshold_t *)tlv; + snr_low->header.type = wlan_cpu_to_le16(TLV_TYPE_SNR_LOW); + snr_low->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_BeaconLowSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + snr_low->value = sub_evt->low_snr; + snr_low->frequency = sub_evt->low_snr_freq; + tlv += sizeof(MrvlIEtypes_BeaconLowSnrThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconLowSnrThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_MAX_FAIL) { + fail_count = (MrvlIEtypes_FailureCount_t *)tlv; + fail_count->header.type = wlan_cpu_to_le16(TLV_TYPE_FAILCOUNT); + fail_count->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_FailureCount_t) - + sizeof(MrvlIEtypesHeader_t)); + fail_count->value = sub_evt->failure_count; + fail_count->frequency = sub_evt->failure_count_freq; + tlv += sizeof(MrvlIEtypes_FailureCount_t); + cmd_size += sizeof(MrvlIEtypes_FailureCount_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_BEACON_MISSED) { + beacon_missed = (MrvlIEtypes_BeaconsMissed_t *)tlv; + beacon_missed->header.type = wlan_cpu_to_le16(TLV_TYPE_BCNMISS); + beacon_missed->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_BeaconsMissed_t) - + sizeof(MrvlIEtypesHeader_t)); + beacon_missed->value = sub_evt->beacon_miss; + beacon_missed->frequency = sub_evt->beacon_miss_freq; + tlv += sizeof(MrvlIEtypes_BeaconsMissed_t); + cmd_size += sizeof(MrvlIEtypes_BeaconsMissed_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_RSSI_HIGH) { + rssi_high = (MrvlIEtypes_BeaconHighRssiThreshold_t *)tlv; + rssi_high->header.type = wlan_cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_high->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_BeaconHighRssiThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + rssi_high->value = sub_evt->high_rssi; + rssi_high->frequency = sub_evt->high_rssi_freq; + tlv += sizeof(MrvlIEtypes_BeaconHighRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconHighRssiThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_SNR_HIGH) { + snr_high = (MrvlIEtypes_BeaconHighSnrThreshold_t *)tlv; + snr_high->header.type = wlan_cpu_to_le16(TLV_TYPE_SNR_HIGH); + snr_high->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_BeaconHighSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + snr_high->value = sub_evt->high_snr; + snr_high->frequency = sub_evt->high_snr_freq; + tlv += sizeof(MrvlIEtypes_BeaconHighSnrThreshold_t); + cmd_size += sizeof(MrvlIEtypes_BeaconHighSnrThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_DATA_RSSI_LOW) { + data_rssi_low = (MrvlIEtypes_DataLowRssiThreshold_t *)tlv; + data_rssi_low->header.type = + wlan_cpu_to_le16(TLV_TYPE_RSSI_LOW_DATA); + data_rssi_low->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_DataLowRssiThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + data_rssi_low->value = sub_evt->data_low_rssi; + data_rssi_low->frequency = sub_evt->data_low_rssi_freq; + tlv += sizeof(MrvlIEtypes_DataLowRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_DataLowRssiThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_DATA_SNR_LOW) { + data_snr_low = (MrvlIEtypes_DataLowSnrThreshold_t *)tlv; + data_snr_low->header.type = + wlan_cpu_to_le16(TLV_TYPE_SNR_LOW_DATA); + data_snr_low->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_DataLowSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + data_snr_low->value = sub_evt->data_low_snr; + data_snr_low->frequency = sub_evt->data_low_snr_freq; + tlv += sizeof(MrvlIEtypes_DataLowSnrThreshold_t); + cmd_size += sizeof(MrvlIEtypes_DataLowSnrThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_DATA_RSSI_HIGH) { + data_rssi_high = (MrvlIEtypes_DataHighRssiThreshold_t *)tlv; + data_rssi_high->header.type = + wlan_cpu_to_le16(TLV_TYPE_RSSI_HIGH_DATA); + data_rssi_high->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_DataHighRssiThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + data_rssi_high->value = sub_evt->data_high_rssi; + data_rssi_high->frequency = sub_evt->data_high_rssi_freq; + tlv += sizeof(MrvlIEtypes_DataHighRssiThreshold_t); + cmd_size += sizeof(MrvlIEtypes_DataHighRssiThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_DATA_SNR_HIGH) { + data_snr_high = (MrvlIEtypes_DataHighSnrThreshold_t *)tlv; + data_snr_high->header.type = + wlan_cpu_to_le16(TLV_TYPE_SNR_HIGH_DATA); + data_snr_high->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_DataHighSnrThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + data_snr_high->value = sub_evt->data_high_snr; + data_snr_high->frequency = sub_evt->data_high_snr_freq; + tlv += sizeof(MrvlIEtypes_DataHighSnrThreshold_t); + cmd_size += sizeof(MrvlIEtypes_DataHighSnrThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_LINK_QUALITY) { + link_quality = (MrvlIEtypes_LinkQualityThreshold_t *)tlv; + link_quality->header.type = + wlan_cpu_to_le16(TLV_TYPE_LINK_QUALITY); + link_quality->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_LinkQualityThreshold_t) - + sizeof(MrvlIEtypesHeader_t)); + link_quality->link_snr = wlan_cpu_to_le16(sub_evt->link_snr); + link_quality->link_snr_freq = + wlan_cpu_to_le16(sub_evt->link_snr_freq); + link_quality->link_rate = wlan_cpu_to_le16(sub_evt->link_rate); + link_quality->link_rate_freq = + wlan_cpu_to_le16(sub_evt->link_rate_freq); + link_quality->link_tx_latency = + wlan_cpu_to_le16(sub_evt->link_tx_latency); + link_quality->link_tx_lantency_freq = + wlan_cpu_to_le16(sub_evt->link_tx_lantency_freq); + tlv += sizeof(MrvlIEtypes_LinkQualityThreshold_t); + cmd_size += sizeof(MrvlIEtypes_LinkQualityThreshold_t); + } + if (sub_evt->evt_bitmap & SUBSCRIBE_EVT_PRE_BEACON_LOST) { + pre_bcn_missed = (MrvlIETypes_PreBeaconMissed_t *)tlv; + pre_bcn_missed->header.type = + wlan_cpu_to_le16(TLV_TYPE_PRE_BCNMISS); + pre_bcn_missed->header.len = + wlan_cpu_to_le16(sizeof(MrvlIETypes_PreBeaconMissed_t) - + sizeof(MrvlIEtypesHeader_t)); + pre_bcn_missed->value = sub_evt->pre_beacon_miss; + pre_bcn_missed->frequency = 0; + tlv += sizeof(MrvlIETypes_PreBeaconMissed_t); + cmd_size += sizeof(MrvlIETypes_PreBeaconMissed_t); + } +done: + cmd->size = wlan_cpu_to_le16(cmd_size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of OTP user data. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_otp_user_data(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + mlan_ds_misc_otp_user_data *user_data = + (mlan_ds_misc_otp_user_data *)pdata_buf; + HostCmd_DS_OTP_USER_DATA *cmd_user_data = + (HostCmd_DS_OTP_USER_DATA *)&cmd->params.otp_user_data; + t_u16 cmd_size = 0; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_OTP_READ_USER_DATA); + cmd_size = sizeof(HostCmd_DS_OTP_USER_DATA) + S_DS_GEN - 1; + + cmd_user_data->action = wlan_cpu_to_le16(cmd_action); + cmd_user_data->reserved = 0; + cmd_user_data->user_data_length = + wlan_cpu_to_le16(user_data->user_data_length); + cmd_size += user_data->user_data_length; + cmd->size = wlan_cpu_to_le16(cmd_size); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef USB +/** + * @brief This function prepares command of packet aggragation + * + * @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_packet_aggr_over_host_interface(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_PACKET_AGGR_OVER_HOST_INTERFACE *packet_aggr = + &cmd->params.packet_aggr; + MrvlIETypes_USBAggrParam_t *usb_aggr_param_tlv = MNULL; + mlan_ds_misc_usb_aggr_ctrl *usb_aggr_ctrl = + (mlan_ds_misc_usb_aggr_ctrl *)pdata_buf; + t_u8 *ptlv_buffer = (t_u8 *)packet_aggr->tlv_buf; + pmlan_adapter pmadapter = pmpriv->adapter; + + ENTER(); + + usb_aggr_param_tlv = (MrvlIETypes_USBAggrParam_t *)ptlv_buffer; + + cmd->command = + wlan_cpu_to_le16(HostCmd_CMD_PACKET_AGGR_OVER_HOST_INTERFACE); + packet_aggr->action = wlan_cpu_to_le16(cmd_action); + memset(pmadapter, usb_aggr_param_tlv, 0, + MRVL_USB_AGGR_PARAM_TLV_LEN + sizeof(MrvlIEtypesHeader_t)); + usb_aggr_param_tlv->header.type = + wlan_cpu_to_le16(MRVL_USB_AGGR_PARAM_TLV_ID); + usb_aggr_param_tlv->header.len = + wlan_cpu_to_le16(MRVL_USB_AGGR_PARAM_TLV_LEN); + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_PACKET_AGGR_OVER_HOST_INTERFACE) + S_DS_GEN + + MRVL_USB_AGGR_PARAM_TLV_LEN + sizeof(MrvlIEtypesHeader_t) - 1); + + if (pmadapter->data_sent || (!wlan_bypass_tx_list_empty(pmadapter)) || + (!wlan_wmm_lists_empty(pmadapter))) { + /* Make sure this is not issued during traffic */ + PRINTM(MERROR, + "USB aggregation parameters cannot be accessed during traffic.\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (cmd_action == HostCmd_ACT_GEN_SET) { + usb_aggr_param_tlv->enable = 0; + if (usb_aggr_ctrl->tx_aggr_ctrl.enable) + usb_aggr_param_tlv->enable |= MBIT(1); + usb_aggr_param_tlv->tx_aggr_align = wlan_cpu_to_le16( + usb_aggr_ctrl->tx_aggr_ctrl.aggr_align); + if (usb_aggr_ctrl->rx_deaggr_ctrl.enable) + usb_aggr_param_tlv->enable |= MBIT(0); + usb_aggr_param_tlv->rx_aggr_mode = wlan_cpu_to_le16( + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_mode); + usb_aggr_param_tlv->rx_aggr_align = wlan_cpu_to_le16( + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_align); + usb_aggr_param_tlv->rx_aggr_max = wlan_cpu_to_le16( + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_max); + usb_aggr_param_tlv->rx_aggr_tmo = wlan_cpu_to_le16( + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_tmo); + usb_aggr_param_tlv->enable = + wlan_cpu_to_le16(usb_aggr_param_tlv->enable); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif + +/** + * @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(HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + t_void *pdata_buf) +{ + pmlan_ds_inactivity_to inac_to; + HostCmd_DS_INACTIVITY_TIMEOUT_EXT *cmd_inac_to = + &cmd->params.inactivity_to; + + ENTER(); + + inac_to = (mlan_ds_inactivity_to *)pdata_buf; + + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_INACTIVITY_TIMEOUT_EXT) + + S_DS_GEN); + cmd->command = wlan_cpu_to_le16(cmd->command); + cmd_inac_to->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_SET) { + cmd_inac_to->timeout_unit = + wlan_cpu_to_le16((t_u16)inac_to->timeout_unit); + cmd_inac_to->unicast_timeout = + wlan_cpu_to_le16((t_u16)inac_to->unicast_timeout); + cmd_inac_to->mcast_timeout = + wlan_cpu_to_le16((t_u16)inac_to->mcast_timeout); + cmd_inac_to->ps_entry_timeout = + wlan_cpu_to_le16((t_u16)inac_to->ps_entry_timeout); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares Low 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 pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_low_pwr_mode(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf) +{ + HostCmd_CONFIG_LOW_PWR_MODE *cmd_lpm_cfg = + &cmd->params.low_pwr_mode_cfg; + t_u8 *enable; + + ENTER(); + + cmd->size = S_DS_GEN + sizeof(HostCmd_CONFIG_LOW_PWR_MODE); + + enable = (t_u8 *)pdata_buf; + cmd->size = wlan_cpu_to_le16(cmd->size); + cmd->command = wlan_cpu_to_le16(cmd->command); + cmd_lpm_cfg->enable = *enable; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares DFS repeater mode configuration + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_cmd_dfs_repeater_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + t_void *pdata_buf) +{ + mlan_ds_misc_dfs_repeater *dfs_repeater = MNULL; + HostCmd_DS_DFS_REPEATER_MODE *cmd_dfs_repeater = + &cmd->params.dfs_repeater; + + ENTER(); + + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_DFS_REPEATER_MODE); + + dfs_repeater = (mlan_ds_misc_dfs_repeater *)pdata_buf; + cmd->size = wlan_cpu_to_le16(cmd->size); + cmd->command = wlan_cpu_to_le16(cmd->command); + cmd_dfs_repeater->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) + cmd_dfs_repeater->mode = wlan_cpu_to_le16(dfs_repeater->mode); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of coalesce_config. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_coalesce_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_void *pdata_buf) +{ + HostCmd_DS_COALESCE_CONFIG *coalesce_config = + &cmd->params.coalesce_config; + mlan_ds_coalesce_cfg *cfg = (mlan_ds_coalesce_cfg *)pdata_buf; + t_u16 cnt, idx, length; + struct coalesce_filt_field_param *param; + struct coalesce_receive_filt_rule *rule; + + ENTER(); + + cmd->size = sizeof(HostCmd_DS_COALESCE_CONFIG) + S_DS_GEN; + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_COALESCE_CFG); + coalesce_config->action = wlan_cpu_to_le16(cmd_action); + coalesce_config->num_of_rules = wlan_cpu_to_le16(cfg->num_of_rules); + if (cmd_action == HostCmd_ACT_GEN_SET) { + rule = coalesce_config->rule; + for (cnt = 0; cnt < cfg->num_of_rules; cnt++) { + rule->header.type = + wlan_cpu_to_le16(TLV_TYPE_COALESCE_RULE); + rule->max_coalescing_delay = wlan_cpu_to_le16( + cfg->rule[cnt].max_coalescing_delay); + rule->pkt_type = cfg->rule[cnt].pkt_type; + rule->num_of_fields = cfg->rule[cnt].num_of_fields; + + length = 0; + + param = rule->params; + for (idx = 0; idx < cfg->rule[cnt].num_of_fields; + idx++) { + param->operation = + cfg->rule[cnt].params[idx].operation; + param->operand_len = + cfg->rule[cnt].params[idx].operand_len; + param->offset = wlan_cpu_to_le16( + cfg->rule[cnt].params[idx].offset); + memcpy_ext(pmpriv->adapter, + param->operand_byte_stream, + cfg->rule[cnt] + .params[idx] + .operand_byte_stream, + param->operand_len, + sizeof(param->operand_byte_stream)); + + length += sizeof( + struct coalesce_filt_field_param); + + param++; + } + + /* Total rule length is sizeof + * max_coalescing_delay(t_u16), num_of_fields(t_u8), + * pkt_type(t_u8) and total length of the all params + */ + rule->header.len = + wlan_cpu_to_le16(length + sizeof(t_u16) + + sizeof(t_u8) + sizeof(t_u8)); + + /* Add the rule length to the command size*/ + cmd->size += wlan_le16_to_cpu(rule->header.len) + + sizeof(MrvlIEtypesHeader_t); + + rule = (void *)((t_u8 *)rule->params + length); + } + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + * Global Functions + ********************************************************/ + +static mlan_status wlan_cmd_get_sensor_temp(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action) +{ + ENTER(); + + if (cmd_action != HostCmd_ACT_GEN_GET) { + PRINTM(MERROR, "wlan_cmd_get_sensor_temp: support GET only.\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + cmd->command = wlan_cpu_to_le16(HostCmd_DS_GET_SENSOR_TEMP); + cmd->size = wlan_cpu_to_le16(S_DS_GEN + 4); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of arb 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_arb_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_CMD_ARB_CONFIG *cfg_cmd = + (HostCmd_DS_CMD_ARB_CONFIG *)&cmd->params.arb_cfg; + mlan_ds_misc_arb_cfg *misc_cfg = (mlan_ds_misc_arb_cfg *)pdata_buf; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_ARB_CONFIG); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_CMD_ARB_CONFIG) + S_DS_GEN); + cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + cfg_cmd->arb_mode = wlan_cpu_to_le32(misc_cfg->arb_mode); + if (misc_cfg->arb_mode == 3) { +#define DEF_ARB_TX_WIN 4 +#define DEF_ARB_TIMEOUT 0 + pmpriv->add_ba_param.timeout = DEF_ARB_TIMEOUT; + pmpriv->add_ba_param.tx_win_size = DEF_ARB_TX_WIN; + } else { + pmpriv->add_ba_param.timeout = + MLAN_DEFAULT_BLOCK_ACK_TIMEOUT; + pmpriv->add_ba_param.tx_win_size = + MLAN_STA_AMPDU_DEF_TXWINSIZE; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function sends get sta band channel command to firmware. + * + * @param priv A pointer to mlan_private structure + * @param cmd Hostcmd ID + * @return N/A + */ +static mlan_status wlan_cmd_sta_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + mlan_ioctl_req *pioctl_buf, + t_void *pdata_buf) +{ + mlan_ds_bss *bss = MNULL; + HostCmd_DS_STA_CONFIGURE *sta_cfg_cmd = &cmd->params.sta_cfg; + MrvlIEtypes_channel_band_t *tlv_band_channel = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + if (!pioctl_buf) + return ret; + + if (pioctl_buf->req_id == MLAN_IOCTL_BSS) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + if ((bss->sub_command == MLAN_OID_BSS_CHAN_INFO) && + (cmd_action == HostCmd_ACT_GEN_GET)) { + cmd->command = + wlan_cpu_to_le16(HostCmd_CMD_STA_CONFIGURE); + cmd->size = wlan_cpu_to_le16( + S_DS_GEN + sizeof(HostCmd_DS_STA_CONFIGURE) + + sizeof(*tlv_band_channel)); + sta_cfg_cmd->action = wlan_cpu_to_le16(cmd_action); + tlv_band_channel = (MrvlIEtypes_channel_band_t *) + sta_cfg_cmd->tlv_buffer; + memset(pmpriv->adapter, tlv_band_channel, 0x00, + sizeof(*tlv_band_channel)); + tlv_band_channel->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + tlv_band_channel->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_channel_band_t) - + sizeof(MrvlIEtypesHeader_t)); + ret = MLAN_STATUS_SUCCESS; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function sends set and get auto tx command to firmware. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd Hostcmd ID + * @param cmd_action Command action + * @param cmd_oid Cmd oid: treated as sub command + * @param pdata_buf A void pointer to information buffer + * @return N/A + */ +static mlan_status wlan_cmd_auto_tx(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u16 cmd_action, + t_u32 cmd_oid, t_void *pdata_buf) +{ + HostCmd_DS_AUTO_TX *auto_tx_cmd = &cmd->params.auto_tx; + t_u8 *pos = (t_u8 *)auto_tx_cmd->tlv_buffer; + t_u16 len = 0; + MrvlIEtypes_Cloud_Keep_Alive_t *keep_alive_tlv = MNULL; + MrvlIEtypes_Keep_Alive_Ctrl_t *ctrl_tlv = MNULL; + MrvlIEtypes_Keep_Alive_Pkt_t *pkt_tlv = MNULL; + mlan_ds_misc_keep_alive *misc_keep_alive = MNULL; + t_u8 eth_ip[] = {0x08, 0x00}; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_AUTO_TX); + cmd->size = S_DS_GEN + sizeof(HostCmd_DS_AUTO_TX); + auto_tx_cmd->action = wlan_cpu_to_le16(cmd_action); + + switch (cmd_oid) { + case OID_CLOUD_KEEP_ALIVE: + misc_keep_alive = (mlan_ds_misc_keep_alive *)pdata_buf; + keep_alive_tlv = (MrvlIEtypes_Cloud_Keep_Alive_t *)pos; + + keep_alive_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_CLOUD_KEEP_ALIVE); + keep_alive_tlv->keep_alive_id = misc_keep_alive->mkeep_alive_id; + keep_alive_tlv->enable = misc_keep_alive->enable; + len = len + sizeof(keep_alive_tlv->keep_alive_id) + + sizeof(keep_alive_tlv->enable); + pos = pos + len + sizeof(MrvlIEtypesHeader_t); + if (cmd_action == HostCmd_ACT_GEN_SET) { + if (misc_keep_alive->enable) { + ctrl_tlv = (MrvlIEtypes_Keep_Alive_Ctrl_t *)pos; + ctrl_tlv->header.type = wlan_cpu_to_le16( + TLV_TYPE_KEEP_ALIVE_CTRL); + ctrl_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_Keep_Alive_Ctrl_t) - + sizeof(MrvlIEtypesHeader_t)); + ctrl_tlv->snd_interval = wlan_cpu_to_le32( + misc_keep_alive->send_interval); + ctrl_tlv->retry_interval = wlan_cpu_to_le16( + misc_keep_alive->retry_interval); + ctrl_tlv->retry_count = wlan_cpu_to_le16( + misc_keep_alive->retry_count); + len = len + + sizeof(MrvlIEtypes_Keep_Alive_Ctrl_t); + + pos = pos + + sizeof(MrvlIEtypes_Keep_Alive_Ctrl_t); + pkt_tlv = (MrvlIEtypes_Keep_Alive_Pkt_t *)pos; + pkt_tlv->header.type = wlan_cpu_to_le16( + TLV_TYPE_KEEP_ALIVE_PKT); + memcpy_ext(pmpriv->adapter, + pkt_tlv->eth_header.dest_addr, + misc_keep_alive->dst_mac, + MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + memcpy_ext(pmpriv->adapter, + pkt_tlv->eth_header.src_addr, + misc_keep_alive->src_mac, + MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + memcpy_ext( + pmpriv->adapter, + (t_u8 *)&pkt_tlv->eth_header.h803_len, + eth_ip, sizeof(t_u16), sizeof(t_u16)); + if (misc_keep_alive->ether_type) + pkt_tlv->eth_header + .h803_len = mlan_htons( + misc_keep_alive->ether_type); + else + memcpy_ext(pmpriv->adapter, + (t_u8 *)&pkt_tlv->eth_header + .h803_len, + eth_ip, sizeof(t_u16), + sizeof(t_u16)); + pkt_tlv->header.len = wlan_cpu_to_le16( + sizeof(Eth803Hdr_t) + + misc_keep_alive->pkt_len); + len = len + sizeof(MrvlIEtypesHeader_t) + + sizeof(Eth803Hdr_t) + + misc_keep_alive->pkt_len; + } else { + pkt_tlv = (MrvlIEtypes_Keep_Alive_Pkt_t *)pos; + pkt_tlv->header.type = wlan_cpu_to_le16( + TLV_TYPE_KEEP_ALIVE_PKT); + pkt_tlv->header.len = 0; + len = len + sizeof(MrvlIEtypesHeader_t); + } + } + if (cmd_action == HostCmd_ACT_GEN_RESET) { + pkt_tlv = (MrvlIEtypes_Keep_Alive_Pkt_t *)pos; + pkt_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_KEEP_ALIVE_PKT); + pkt_tlv->header.len = 0; + len = len + sizeof(MrvlIEtypesHeader_t); + } + keep_alive_tlv->header.len = wlan_cpu_to_le16(len); + + cmd->size = cmd->size + len + sizeof(MrvlIEtypesHeader_t); + cmd->size = wlan_cpu_to_le16(cmd->size); + break; + default: + break; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function check if the command is supported by firmware + * + * @param priv A pointer to mlan_private structure + * @param cmd_no Command number + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_is_cmd_allowed(mlan_private *priv, t_u16 cmd_no) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + if (priv->adapter->pcard_info->v16_fw_api) { + if (!IS_FW_SUPPORT_ADHOC(priv->adapter)) { + switch (cmd_no) { + case HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON: + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + case HostCmd_CMD_802_11_AD_HOC_START: + case HostCmd_CMD_802_11_AD_HOC_JOIN: + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = MLAN_STATUS_FAILURE; + break; + default: + break; + } + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function prepare the command before sending to firmware. + * + * @param priv A pointer to mlan_private structure + * @param cmd_no Command number + * @param cmd_action Command action: GET or SET + * @param cmd_oid Cmd oid: treated as sub command + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param pdata_buf A pointer to information buffer + * @param pcmd_buf A pointer to cmd buf + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ops_sta_prepare_cmd(t_void *priv, t_u16 cmd_no, + t_u16 cmd_action, t_u32 cmd_oid, + t_void *pioctl_buf, t_void *pdata_buf, + t_void *pcmd_buf) +{ + HostCmd_DS_COMMAND *cmd_ptr = (HostCmd_DS_COMMAND *)pcmd_buf; + mlan_private *pmpriv = (mlan_private *)priv; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wlan_is_cmd_allowed(pmpriv, cmd_no)) { + PRINTM(MERROR, "FW don't support the command 0x%x\n", cmd_no); + return MLAN_STATUS_FAILURE; + } + /* Prepare command */ + switch (cmd_no) { + case HostCmd_CMD_GET_HW_SPEC: + ret = wlan_cmd_get_hw_spec(pmpriv, cmd_ptr); + break; +#ifdef SDIO + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = wlan_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, pdata_buf); + break; +#endif + case HostCmd_CMD_CFG_DATA: + ret = wlan_cmd_cfg_data(pmpriv, cmd_ptr, cmd_action, cmd_oid, + pdata_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = wlan_cmd_mac_control(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_MAC_ADDRESS: + ret = wlan_cmd_802_11_mac_address(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_MAC_MULTICAST_ADR: + ret = wlan_cmd_mac_multicast_adr(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = wlan_cmd_tx_rate_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf, pioctl_buf); + break; + case HostCmd_CMD_802_11_RF_ANTENNA: + ret = wlan_cmd_802_11_rf_antenna(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_CW_MODE_CTRL: + ret = wlan_cmd_cw_mode_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TXPWR_CFG: + ret = wlan_cmd_tx_power_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_RF_TX_POWER: + ret = wlan_cmd_802_11_rf_tx_power(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = wlan_cmd_enh_power_mode(pmpriv, cmd_ptr, cmd_action, + (t_u16)cmd_oid, pdata_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = wlan_cmd_802_11_hs_cfg(pmpriv, cmd_ptr, cmd_action, + (hs_config_param *)pdata_buf); + break; + case HostCmd_CMD_802_11_ROBUSTCOEX: + ret = wlan_cmd_robustcoex(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_DMCS_CONFIG: + ret = wlan_cmd_dmcs_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#if defined(PCIE) + case HostCmd_CMD_SSU: + ret = wlan_cmd_ssu(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; +#endif + case HOST_CMD_PMIC_CONFIGURE: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_802_11_SLEEP_PERIOD: + ret = wlan_cmd_802_11_sleep_period(pmpriv, cmd_ptr, cmd_action, + (t_u16 *)pdata_buf); + break; + case HostCmd_CMD_802_11_SLEEP_PARAMS: + ret = wlan_cmd_802_11_sleep_params(pmpriv, cmd_ptr, cmd_action, + (t_u16 *)pdata_buf); + break; + case HostCmd_CMD_802_11_SCAN: + ret = wlan_cmd_802_11_scan(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_BG_SCAN_CONFIG: + ret = wlan_cmd_bgscan_config(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_BG_SCAN_QUERY: + ret = wlan_cmd_802_11_bg_scan_query(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_ASSOCIATE: + ret = wlan_cmd_802_11_associate(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_DEAUTHENTICATE: + case HostCmd_CMD_802_11_DISASSOCIATE: + ret = wlan_cmd_802_11_deauthenticate(pmpriv, cmd_no, cmd_ptr, + pdata_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_START: + ret = wlan_cmd_802_11_ad_hoc_start(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_JOIN: + ret = wlan_cmd_802_11_ad_hoc_join(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = wlan_cmd_802_11_ad_hoc_stop(pmpriv, cmd_ptr); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = wlan_cmd_802_11_get_log(pmpriv, cmd_ptr); + break; + case HostCmd_CMD_802_11_LINK_STATS: + ret = wlan_cmd_802_11_link_statistic(pmpriv, cmd_ptr, + cmd_action, pioctl_buf); + break; + case HostCmd_CMD_RSSI_INFO: + ret = wlan_cmd_802_11_rssi_info(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_RSSI_INFO_EXT: + ret = wlan_cmd_802_11_rssi_info_ext(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = wlan_cmd_802_11_snmp_mib(pmpriv, cmd_ptr, cmd_action, + cmd_oid, pdata_buf); + break; + case HostCmd_CMD_802_11_RADIO_CONTROL: + ret = wlan_cmd_802_11_radio_control(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + cmd_ptr->command = + wlan_cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); + cmd_ptr->size = wlan_cpu_to_le16(sizeof(HostCmd_TX_RATE_QUERY) + + S_DS_GEN); + pmpriv->tx_rate = 0; + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_VERSION_EXT: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->params.verext.version_str_sel = + (t_u8)(*((t_u32 *)pdata_buf)); + cmd_ptr->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_VERSION_EXT) + S_DS_GEN); + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_RX_MGMT_IND: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->params.rx_mgmt_ind.action = + wlan_cpu_to_le16(cmd_action); + cmd_ptr->params.rx_mgmt_ind.mgmt_subtype_mask = + wlan_cpu_to_le32((t_u32)(*((t_u32 *)pdata_buf))); + cmd_ptr->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_RX_MGMT_IND) + S_DS_GEN); + break; + case HostCmd_CMD_802_11_RF_CHANNEL: + ret = wlan_cmd_802_11_rf_channel(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_FUNC_INIT: + if (pmpriv->adapter->hw_status == WlanHardwareStatusReset) + pmpriv->adapter->hw_status = + WlanHardwareStatusInitializing; + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_FUNC_SHUTDOWN: + pmpriv->adapter->hw_status = WlanHardwareStatusReset; + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_SOFT_RESET: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = wlan_cmd_11n_addba_req(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_11N_DELBA: + ret = wlan_cmd_11n_delba(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = wlan_cmd_11n_addba_rspgen(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + ret = wlan_cmd_802_11_key_material(pmpriv, cmd_ptr, cmd_action, + cmd_oid, pdata_buf); + break; + case HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG: + ret = wlan_cmd_gtk_rekey_offload(pmpriv, cmd_ptr, cmd_action, + cmd_oid, pdata_buf); + break; + + case HostCmd_CMD_SUPPLICANT_PMK: + ret = wlan_cmd_802_11_supplicant_pmk(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_802_11_EAPOL_PKT: + ret = wlan_cmd_eapol_pkt(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_SUPPLICANT_PROFILE: + ret = wlan_cmd_802_11_supplicant_profile(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = wlan_cmd_802_11d_domain_info(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_802_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_11AC_CFG: + ret = wlan_cmd_11ac_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; +#if 0 + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + ret = wlan_cmd_recfg_tx_buf(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HostCmd_CMD_TX_BF_CFG: + ret = wlan_cmd_tx_bf_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_WMM_GET_STATUS: + PRINTM(MINFO, "WMM: WMM_GET_STATUS cmd sent\n"); + cmd_ptr->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS); + cmd_ptr->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_WMM_GET_STATUS) + S_DS_GEN); + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_WMM_ADDTS_REQ: + ret = wlan_cmd_wmm_addts_req(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_DELTS_REQ: + ret = wlan_cmd_wmm_delts_req(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_QUEUE_CONFIG: + ret = wlan_cmd_wmm_queue_config(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_QUEUE_STATS: + ret = wlan_cmd_wmm_queue_stats(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_TS_STATUS: + ret = wlan_cmd_wmm_ts_status(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_WMM_PARAM_CONFIG: + ret = wlan_cmd_wmm_param_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + ret = wlan_cmd_ibss_coalescing_status(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_MGMT_IE_LIST: + ret = wlan_cmd_mgmt_ie_list(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = wlan_cmd_802_11_scan_ext(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG: + ret = wlan_cmd_sysclock_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_TARGET_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + case HostCmd_CMD_BCA_REG_ACCESS: + ret = wlan_cmd_reg_access(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = wlan_cmd_mem_access(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_INACTIVITY_TIMEOUT_EXT: + ret = wlan_cmd_inactivity_timeout(cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_GET_TSF: + ret = wlan_cmd_get_tsf(pmpriv, cmd_ptr, cmd_action); + break; +#if defined(SDIO) + case HostCmd_CMD_SDIO_GPIO_INT_CONFIG: + ret = wlan_cmd_sdio_gpio_int(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + 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; +#if defined(PCIE) +#if defined(PCIE8997) || defined(PCIE8897) + case HostCmd_CMD_PCIE_HOST_BUF_DETAILS: + ret = wlan_cmd_pcie_host_buf_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif +#endif + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_cmd_remain_on_channel(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#ifdef WIFI_DIRECT_SUPPORT + case HOST_CMD_WIFI_DIRECT_MODE_CONFIG: + ret = wlan_cmd_wifi_direct_mode(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + ret = wlan_cmd_subscribe_event(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_OTP_READ_USER_DATA: + ret = wlan_cmd_otp_user_data(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = wlan_cmd_hs_wakeup_reason(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_REJECT_ADDBA_REQ: + ret = wlan_cmd_reject_addba_req(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_PACKET_AGGR_CTRL: + ret = wlan_cmd_packet_aggr_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#ifdef USB + case HostCmd_CMD_PACKET_AGGR_OVER_HOST_INTERFACE: + ret = wlan_cmd_packet_aggr_over_host_interface( + pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; +#endif +#ifdef RX_PACKET_COALESCE + case HostCmd_CMD_RX_PKT_COALESCE_CFG: + ret = wlan_cmd_rx_pkt_coalesce_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HostCMD_CONFIG_LOW_POWER_MODE: + ret = wlan_cmd_low_pwr_mode(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_DFS_REPEATER_MODE: + ret = wlan_cmd_dfs_repeater_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_COALESCE_CFG: + ret = wlan_cmd_coalesce_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_DS_GET_SENSOR_TEMP: + ret = wlan_cmd_get_sensor_temp(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_802_11_MIMO_SWITCH: + ret = wlan_cmd_802_11_mimo_switch(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_STA_CONFIGURE: + ret = wlan_cmd_sta_config(pmpriv, cmd_ptr, cmd_action, + pioctl_buf, pdata_buf); + break; + + case HostCmd_CMD_INDEPENDENT_RESET_CFG: + ret = wlan_cmd_ind_rst_cfg(cmd_ptr, cmd_action, pdata_buf); + break; + + case HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT: + ret = wlan_cmd_ps_inactivity_timeout(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_CHAN_REGION_CFG: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_CHAN_REGION_CFG) + S_DS_GEN); + cmd_ptr->params.reg_cfg.action = wlan_cpu_to_le16(cmd_action); + break; + case HostCmd_CMD_AUTO_TX: + ret = wlan_cmd_auto_tx(pmpriv, cmd_ptr, cmd_action, cmd_oid, + pdata_buf); + break; + case HOST_CMD_TX_RX_PKT_STATS: + ret = wlan_cmd_tx_rx_pkt_stats(pmpriv, cmd_ptr, + (pmlan_ioctl_req)pioctl_buf, + pdata_buf); + break; + case HostCmd_CMD_DYN_BW: + ret = wlan_cmd_config_dyn_bw(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_BOOT_SLEEP: + ret = wlan_cmd_boot_sleep(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_FW_DUMP_EVENT: + ret = wlan_cmd_fw_dump_event(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#if defined(DRV_EMBEDDED_SUPPLICANT) + case HostCmd_CMD_CRYPTO: + ret = wlan_cmd_crypto(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; +#endif + case HostCmd_CMD_11AX_CFG: + ret = wlan_cmd_11ax_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_11AX_CMD: + ret = wlan_cmd_11ax_cmd(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_RANGE_EXT: + ret = wlan_cmd_range_ext(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TWT_CFG: + ret = wlan_cmd_twt_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_RX_ABORT_CFG: + ret = wlan_cmd_rxabortcfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_RX_ABORT_CFG_EXT: + ret = wlan_cmd_rxabortcfg_ext(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_ARB_CONFIG: + ret = wlan_cmd_arb_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_TX_AMPDU_PROT_MODE: + ret = wlan_cmd_tx_ampdu_prot_mode(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_DOT11MC_UNASSOC_FTM_CFG: + ret = wlan_cmd_dot11mc_unassoc_ftm_cfg(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_RATE_ADAPT_CFG: + ret = wlan_cmd_rate_adapt_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_CCK_DESENSE_CFG: + ret = wlan_cmd_cck_desense_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CHANNEL_TRPC_CONFIG: + ret = wlan_cmd_get_chan_trpc_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_LOW_POWER_MODE_CFG: + ret = wlan_cmd_set_get_low_power_mode_cfg( + pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_MFG_COMMAND: + ret = wlan_cmd_mfg(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(t_void *priv, t_u8 first_bss) +{ + pmlan_private pmpriv = (pmlan_private)priv; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + + ENTER(); + + if (!pmpriv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (first_bss == MTRUE) { + ret = wlan_adapter_init_cmd(pmpriv->adapter); + if (ret == MLAN_STATUS_FAILURE) + goto done; + } + + /* get tx rate */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmpriv->data_rate = 0; + + /* get tx power */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_RF_TX_POWER, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + if (ret) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + 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/mxm_wifiex/wlan_src/mlan/mlan_sta_cmdresp.c b/mxm_wifiex/wlan_src/mlan/mlan_sta_cmdresp.c new file mode 100644 index 0000000..a7fee37 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_sta_cmdresp.c @@ -0,0 +1,2749 @@ +/** @file mlan_sta_cmdresp.c + * + * @brief This file contains the handling of command + * responses generated by firmware. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11ac.h" +#include "mlan_11ax.h" +#include "mlan_11h.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 mlan_status wlan_process_cmdresp_error(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + pmlan_ioctl_req pscan_ioctl_req = MNULL; + mlan_callbacks *pcb = MNULL; +#if defined(USB) + t_s32 i = 0; +#endif + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + if (resp->command != HostCmd_CMD_WMM_PARAM_CONFIG && + resp->command != HostCmd_CMD_CHAN_REGION_CFG) + PRINTM(MERROR, "CMD_RESP: cmd %#x error, result=%#x\n", + resp->command, resp->result); + if (pioctl_buf) + pioctl_buf->status_code = MLAN_ERROR_FW_CMDRESP; + + switch (resp->command) { + case HostCmd_CMD_802_11_PS_MODE_ENH: { + HostCmd_DS_802_11_PS_MODE_ENH *pm = &resp->params.psmode_enh; + PRINTM(MERROR, + "PS_MODE_ENH command failed: result=0x%x action=0x%X\n", + resp->result, wlan_le16_to_cpu(pm->action)); + /* + * We do not re-try enter-ps command in ad-hoc mode. + */ + if (wlan_le16_to_cpu(pm->action) == EN_AUTO_PS && + (wlan_le16_to_cpu(pm->params.auto_ps.ps_bitmap) & + BITMAP_STA_PS) && + pmpriv->bss_mode == MLAN_BSS_MODE_IBSS) + pmadapter->ps_mode = Wlan802_11PowerModeCAM; + } break; + case HostCmd_CMD_802_11_SCAN_EXT: + case HostCmd_CMD_802_11_SCAN: + /* Cancel all pending scan command */ + wlan_flush_scan_queue(pmadapter); + + pcb = (pmlan_callbacks)&pmadapter->callbacks; + + wlan_request_cmd_lock(pmadapter); + pmadapter->scan_processing = MFALSE; + pscan_ioctl_req = pmadapter->pscan_ioctl_req; + pmadapter->pscan_ioctl_req = MNULL; + /* Need to indicate IOCTL complete */ + if (pscan_ioctl_req) { + pscan_ioctl_req->status_code = MLAN_ERROR_CMD_SCAN_FAIL; + /* Indicate ioctl complete */ + pcb->moal_ioctl_complete( + pmadapter->pmoal_handle, + (pmlan_ioctl_req)pscan_ioctl_req, + MLAN_STATUS_FAILURE); + } + wlan_release_cmd_lock(pmadapter); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_SCAN_REPORT, MNULL); + break; + + case HostCmd_CMD_MAC_CONTROL: + break; + + case HostCmd_CMD_PACKET_AGGR_CTRL: +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) + pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_ctrl.enable = MFALSE; + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable = + MFALSE; + } +#endif + break; +#ifdef USB + case HostCmd_CMD_PACKET_AGGR_OVER_HOST_INTERFACE: + pmadapter->pcard_usb->fw_usb_aggr = MFALSE; + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) + pmadapter->pcard_usb->usb_tx_aggr[i].aggr_ctrl.enable = + MFALSE; + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable = MFALSE; + break; +#endif + case HostCmd_CMD_802_11_ASSOCIATE: + wlan_reset_connect_state(pmpriv, MTRUE); + break; +#ifdef SDIO + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + pmadapter->pcard_sd->sdio_rx_aggr_enable = MFALSE; + PRINTM(MMSG, "FW don't support SDIO single port rx aggr\n"); + break; +#endif + + case HostCmd_CMD_MGMT_IE_LIST: { + HostCmd_DS_MGMT_IE_LIST_CFG *pmgmt_ie_list = + &(resp->params.mgmt_ie_list); + t_u16 resp_len = 0, travel_len = 0, index; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + custom_ie *cptr; + + if (wlan_le16_to_cpu(pmgmt_ie_list->action) == + HostCmd_ACT_GEN_GET) + break; + + cust_ie = (mlan_ds_misc_custom_ie *)&pmgmt_ie_list->ds_mgmt_ie; + if (cust_ie) { + cust_ie->type = wlan_le16_to_cpu(cust_ie->type); + resp_len = cust_ie->len = + wlan_le16_to_cpu(cust_ie->len); + travel_len = 0; + /* conversion for index, mask, len */ + if (resp_len == sizeof(t_u16)) + cust_ie->ie_data_list[0] + .ie_index = wlan_cpu_to_le16( + cust_ie->ie_data_list[0].ie_index); + + while (resp_len > sizeof(t_u16)) { + cptr = (custom_ie *)(((t_u8 *)cust_ie + ->ie_data_list) + + travel_len); + index = cptr->ie_index = + wlan_le16_to_cpu(cptr->ie_index); + cptr->mgmt_subtype_mask = wlan_le16_to_cpu( + cptr->mgmt_subtype_mask); + cptr->ie_length = + wlan_le16_to_cpu(cptr->ie_length); + travel_len += cptr->ie_length + + sizeof(custom_ie) - MAX_IE_SIZE; + resp_len -= cptr->ie_length + + sizeof(custom_ie) - MAX_IE_SIZE; + if ((pmpriv->mgmt_ie[index].mgmt_subtype_mask == + cptr->mgmt_subtype_mask) && + (pmpriv->mgmt_ie[index].ie_length == + cptr->ie_length) && + !memcmp(pmpriv->adapter, + pmpriv->mgmt_ie[index].ie_buffer, + cptr->ie_buffer, cptr->ie_length)) { + PRINTM(MERROR, + "set custom ie fail, remove ie index :%d\n", + index); + memset(pmadapter, + &pmpriv->mgmt_ie[index], 0, + sizeof(custom_ie)); + } + } + } + } break; + case HostCmd_CMD_CHAN_REGION_CFG: + ret = MLAN_STATUS_SUCCESS; + PRINTM(MCMND, "FW don't support chan region cfg command!\n"); + break; +#if defined(DRV_EMBEDDED_SUPPLICANT) + case HostCmd_CMD_CRYPTO: + PRINTM(MCMND, "crypto cmd result=0x%x!\n", resp->result); + ret = wlan_ret_crypto(pmpriv, resp, pioctl_buf); + break; +#endif + default: + break; + } + /* + * Handling errors here + */ + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of RSSI info + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_802_11_rssi_info_ext(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_RSSI_INFO_EXT *prssi_info_rsp = + &resp->params.rssi_info_ext; + mlan_ds_get_signal *signal = MNULL; + mlan_ds_get_info *info = MNULL; + MrvlIEtypes_RSSI_EXT_t *signal_info_tlv = MNULL; + t_u16 tlv_left_len = 0, tlv_num = 0; + t_u16 tlv_id, tlv_len; + + ENTER(); + + /* Need to indicate IOCTL complete */ + if (pioctl_buf != MNULL) { + info = (mlan_ds_get_info *)pioctl_buf->pbuf; + signal_info_tlv = + (MrvlIEtypes_RSSI_EXT_t *)(prssi_info_rsp->tlv_buf); + tlv_left_len = + resp->size - + (sizeof(HostCmd_DS_802_11_RSSI_INFO_EXT) + S_DS_GEN); + + while (tlv_left_len >= sizeof(MrvlIEtypes_RSSI_EXT_t)) { + tlv_id = wlan_le16_to_cpu(signal_info_tlv->header.type); + tlv_len = wlan_le16_to_cpu(signal_info_tlv->header.len); + if ((tlv_id != TLV_TYPE_RSSI_INFO) || + (tlv_len != sizeof(MrvlIEtypes_RSSI_EXT_t) - + sizeof(MrvlIEtypesHeader_t))) { + PRINTM(MERROR, + "Invalid RSSI INFO TLV, type=%d, len=%d\n", + tlv_id, tlv_len); + break; + } + + signal = (mlan_ds_get_signal *)&info->param + .signal_ext[tlv_num]; + /* PATH ID */ + signal->selector = + wlan_le16_to_cpu(signal_info_tlv->path_id); + + /* RSSI */ + signal->bcn_rssi_last = wlan_le16_to_cpu( + signal_info_tlv->bcn_rssi_last); + signal->bcn_rssi_avg = + wlan_le16_to_cpu(signal_info_tlv->bcn_rssi_avg); + signal->data_rssi_last = wlan_le16_to_cpu( + signal_info_tlv->data_rssi_last); + signal->data_rssi_avg = wlan_le16_to_cpu( + signal_info_tlv->data_rssi_avg); + + /* SNR */ + signal->bcn_snr_last = CAL_SNR( + wlan_le16_to_cpu( + signal_info_tlv->bcn_rssi_last), + wlan_le16_to_cpu(signal_info_tlv->bcn_nf_last)); + signal->bcn_snr_avg = CAL_SNR( + wlan_le16_to_cpu(signal_info_tlv->bcn_rssi_avg), + wlan_le16_to_cpu(signal_info_tlv->bcn_nf_avg)); + signal->data_snr_last = CAL_SNR( + wlan_le16_to_cpu( + signal_info_tlv->data_rssi_last), + wlan_le16_to_cpu( + signal_info_tlv->data_nf_last)); + signal->data_snr_avg = CAL_SNR( + wlan_le16_to_cpu( + signal_info_tlv->data_rssi_avg), + wlan_le16_to_cpu(signal_info_tlv->data_nf_avg)); + + /* NF */ + signal->bcn_nf_last = + wlan_le16_to_cpu(signal_info_tlv->bcn_nf_last); + signal->bcn_nf_avg = + wlan_le16_to_cpu(signal_info_tlv->bcn_nf_avg); + signal->data_nf_last = + wlan_le16_to_cpu(signal_info_tlv->data_nf_last); + signal->data_nf_avg = + wlan_le16_to_cpu(signal_info_tlv->data_nf_avg); + + tlv_left_len -= sizeof(MrvlIEtypes_RSSI_EXT_t); + signal_info_tlv++; + tlv_num++; + if (tlv_num > MAX_PATH_NUM) + break; + } + + pioctl_buf->data_read_written = + tlv_num * sizeof(mlan_ds_get_signal) + sizeof(t_u32); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of RSSI info + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_802_11_rssi_info(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_RSSI_INFO_RSP *prssi_info_rsp = + &resp->params.rssi_info_rsp; + mlan_ds_get_info *pget_info = MNULL; + BSSDescriptor_t *pbss_desc; + t_s32 tbl_idx = 0; + + ENTER(); + + pmpriv->data_rssi_last = + wlan_le16_to_cpu(prssi_info_rsp->data_rssi_last); + pmpriv->data_nf_last = wlan_le16_to_cpu(prssi_info_rsp->data_nf_last); + + pmpriv->data_rssi_avg = wlan_le16_to_cpu(prssi_info_rsp->data_rssi_avg); + pmpriv->data_nf_avg = wlan_le16_to_cpu(prssi_info_rsp->data_nf_avg); + + pmpriv->bcn_rssi_last = wlan_le16_to_cpu(prssi_info_rsp->bcn_rssi_last); + pmpriv->bcn_nf_last = wlan_le16_to_cpu(prssi_info_rsp->bcn_nf_last); + + pmpriv->bcn_rssi_avg = wlan_le16_to_cpu(prssi_info_rsp->bcn_rssi_avg); + pmpriv->bcn_nf_avg = wlan_le16_to_cpu(prssi_info_rsp->bcn_nf_avg); + + /* Get current BSS info */ + pbss_desc = &pmpriv->curr_bss_params.bss_descriptor; + pbss_desc->rssi = -pmpriv->bcn_rssi_avg; + tbl_idx = wlan_find_ssid_in_list(pmpriv, &pbss_desc->ssid, + pbss_desc->mac_address, + pmpriv->bss_mode); + if (tbl_idx >= 0) { + pbss_desc = &pmpriv->adapter->pscan_table[tbl_idx]; + pbss_desc->rssi = -pmpriv->bcn_rssi_avg; + } + + /* Need to indicate IOCTL complete */ + if (pioctl_buf != MNULL) { + pget_info = (mlan_ds_get_info *)pioctl_buf->pbuf; + + memset(pmpriv->adapter, &pget_info->param.signal, 0, + sizeof(mlan_ds_get_signal)); + + pget_info->param.signal.selector = ALL_RSSI_INFO_MASK; + + /* RSSI */ + pget_info->param.signal.bcn_rssi_last = pmpriv->bcn_rssi_last; + pget_info->param.signal.bcn_rssi_avg = pmpriv->bcn_rssi_avg; + pget_info->param.signal.data_rssi_last = pmpriv->data_rssi_last; + pget_info->param.signal.data_rssi_avg = pmpriv->data_rssi_avg; + + /* SNR */ + pget_info->param.signal.bcn_snr_last = + CAL_SNR(pmpriv->bcn_rssi_last, pmpriv->bcn_nf_last); + pget_info->param.signal.bcn_snr_avg = + CAL_SNR(pmpriv->bcn_rssi_avg, pmpriv->bcn_nf_avg); + pget_info->param.signal.data_snr_last = + CAL_SNR(pmpriv->data_rssi_last, pmpriv->data_nf_last); + pget_info->param.signal.data_snr_avg = + CAL_SNR(pmpriv->data_rssi_avg, pmpriv->data_nf_avg); + + /* NF */ + pget_info->param.signal.bcn_nf_last = pmpriv->bcn_nf_last; + pget_info->param.signal.bcn_nf_avg = pmpriv->bcn_nf_avg; + pget_info->param.signal.data_nf_last = pmpriv->data_nf_last; + pget_info->param.signal.data_nf_avg = pmpriv->data_nf_avg; + + pioctl_buf->data_read_written = sizeof(mlan_ds_get_info); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of snmp_mib + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_802_11_snmp_mib(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_SNMP_MIB *psmib = &resp->params.smib; + t_u16 oid = wlan_le16_to_cpu(psmib->oid); + t_u16 query_type = wlan_le16_to_cpu(psmib->query_type); + t_u32 ul_temp; + mlan_ds_snmp_mib *mib = MNULL; + + ENTER(); + + if (pioctl_buf) + mib = (mlan_ds_snmp_mib *)pioctl_buf->pbuf; + + PRINTM(MINFO, "SNMP_RESP: value of the oid = 0x%x, query_type=0x%x\n", + oid, query_type); + PRINTM(MINFO, "SNMP_RESP: Buf size = 0x%x\n", + wlan_le16_to_cpu(psmib->buf_size)); + if (query_type == HostCmd_ACT_GEN_GET) { + switch (oid) { + case DtimPeriod_i: + ul_temp = psmib->value[0]; + PRINTM(MINFO, "SNMP_RESP: DTIM Period =%u\n", ul_temp); + if (mib) + mib->param.dtim_period = ul_temp; + break; + case FragThresh_i: + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: FragThsd =%u\n", ul_temp); + if (mib) + mib->param.frag_threshold = ul_temp; + break; + + case RtsThresh_i: + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: RTSThsd =%u\n", ul_temp); + if (mib) + mib->param.rts_threshold = ul_temp; + break; + + case ShortRetryLim_i: + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: TxRetryCount=%u\n", ul_temp); + if (mib) + mib->param.retry_count = ul_temp; + break; + case WwsMode_i: + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: WWSCfg =%u\n", ul_temp); + if (pioctl_buf) + ((mlan_ds_misc_cfg *)pioctl_buf->pbuf) + ->param.wws_cfg = ul_temp; + break; + case Thermal_i: + ul_temp = wlan_le32_to_cpu(*((t_u32 *)(psmib->value))); + PRINTM(MINFO, "SNMP_RESP: Thermal =%u\n", ul_temp); + if (pioctl_buf) + ((mlan_ds_misc_cfg *)pioctl_buf->pbuf) + ->param.thermal = ul_temp; + break; + case NullPktPeriod_i: + ul_temp = psmib->value[0]; + PRINTM(MINFO, "SNMP_RESP: Auto NULL Pkt Period =%u\n", + ul_temp); + break; + default: + break; + } + } else { /* (query_type == HostCmd_ACT_GEN_SET) */ + /* Update state for 11d */ + if (oid == Dot11D_i) { + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + /* Set 11d state to private */ + pmpriv->state_11d.enable_11d = ul_temp; + /* Set user enable flag if called from ioctl */ + if (pioctl_buf) + pmpriv->state_11d.user_enable_11d = ul_temp; + } + /* Update state for 11h */ + if (oid == Dot11H_i) { + ul_temp = wlan_le16_to_cpu(*((t_u16 *)(psmib->value))); + /* Set 11h state to priv */ + pmpriv->intf_state_11h.is_11h_active = + (ul_temp & ENABLE_11H_MASK); + /* Set radar_det state to adapter */ + pmpriv->adapter->state_11h.is_master_radar_det_active = + (ul_temp & MASTER_RADAR_DET_MASK) ? MTRUE : + MFALSE; + pmpriv->adapter->state_11h.is_slave_radar_det_active = + (ul_temp & SLAVE_RADAR_DET_MASK) ? MTRUE : + MFALSE; + } + } + + if (pioctl_buf) { + /* Indicate ioctl complete */ + pioctl_buf->data_read_written = sizeof(mlan_ds_snmp_mib); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of get_log + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_get_log(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_GET_LOG *pget_log = + (HostCmd_DS_802_11_GET_LOG *)&resp->params.get_log; + mlan_ds_get_info *pget_info = MNULL; + int i = 0; + + ENTER(); + + if (pioctl_buf) { + pget_info = (mlan_ds_get_info *)pioctl_buf->pbuf; + pget_info->param.stats.mcast_tx_frame = + wlan_le32_to_cpu(pget_log->mcast_tx_frame); + pget_info->param.stats.failed = + wlan_le32_to_cpu(pget_log->failed); + pget_info->param.stats.retry = + wlan_le32_to_cpu(pget_log->retry); + pget_info->param.stats.multi_retry = + wlan_le32_to_cpu(pget_log->multiretry); + pget_info->param.stats.frame_dup = + wlan_le32_to_cpu(pget_log->frame_dup); + pget_info->param.stats.rts_success = + wlan_le32_to_cpu(pget_log->rts_success); + pget_info->param.stats.rts_failure = + wlan_le32_to_cpu(pget_log->rts_failure); + pget_info->param.stats.ack_failure = + wlan_le32_to_cpu(pget_log->ack_failure); + pget_info->param.stats.rx_frag = + wlan_le32_to_cpu(pget_log->rx_frag); + pget_info->param.stats.mcast_rx_frame = + wlan_le32_to_cpu(pget_log->mcast_rx_frame); + pget_info->param.stats.fcs_error = + wlan_le32_to_cpu(pget_log->fcs_error); + pget_info->param.stats.tx_frame = + wlan_le32_to_cpu(pget_log->tx_frame); + pget_info->param.stats.wep_icv_error[0] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[0]); + pget_info->param.stats.wep_icv_error[1] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[1]); + pget_info->param.stats.wep_icv_error[2] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[2]); + pget_info->param.stats.wep_icv_error[3] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[3]); + pget_info->param.stats.bcn_rcv_cnt = + wlan_le32_to_cpu(pget_log->bcn_rcv_cnt); + pget_info->param.stats.bcn_miss_cnt = + wlan_le32_to_cpu(pget_log->bcn_miss_cnt); + pget_info->param.stats.amsdu_rx_cnt = pmpriv->amsdu_rx_cnt; + pget_info->param.stats.msdu_in_rx_amsdu_cnt = + pmpriv->msdu_in_rx_amsdu_cnt; + pget_info->param.stats.amsdu_tx_cnt = pmpriv->amsdu_tx_cnt; + pget_info->param.stats.msdu_in_tx_amsdu_cnt = + pmpriv->msdu_in_tx_amsdu_cnt; + pget_info->param.stats.rx_stuck_issue_cnt[0] = + wlan_le32_to_cpu(pget_log->rx_stuck_issue_cnt[0]); + pget_info->param.stats.rx_stuck_issue_cnt[1] = + wlan_le32_to_cpu(pget_log->rx_stuck_issue_cnt[1]); + pget_info->param.stats.rx_stuck_recovery_cnt = + wlan_le32_to_cpu(pget_log->rx_stuck_recovery_cnt); + pget_info->param.stats.rx_stuck_tsf[0] = + wlan_le64_to_cpu(pget_log->rx_stuck_tsf[0]); + pget_info->param.stats.rx_stuck_tsf[1] = + wlan_le64_to_cpu(pget_log->rx_stuck_tsf[1]); + pget_info->param.stats.tx_watchdog_recovery_cnt = + wlan_le32_to_cpu(pget_log->tx_watchdog_recovery_cnt); + pget_info->param.stats.tx_watchdog_tsf[0] = + wlan_le64_to_cpu(pget_log->tx_watchdog_tsf[0]); + pget_info->param.stats.tx_watchdog_tsf[1] = + wlan_le64_to_cpu(pget_log->tx_watchdog_tsf[1]); + pget_info->param.stats.channel_switch_ann_sent = + wlan_le32_to_cpu(pget_log->channel_switch_ann_sent); + pget_info->param.stats.channel_switch_state = + wlan_le32_to_cpu(pget_log->channel_switch_state); + pget_info->param.stats.reg_class = + wlan_le32_to_cpu(pget_log->reg_class); + pget_info->param.stats.channel_number = + wlan_le32_to_cpu(pget_log->channel_number); + pget_info->param.stats.channel_switch_mode = + wlan_le32_to_cpu(pget_log->channel_switch_mode); + if (pmpriv->adapter->getlog_enable) { + pget_info->param.stats.tx_frag_cnt = + wlan_le32_to_cpu(pget_log->tx_frag_cnt); + for (i = 0; i < 8; i++) { + pget_info->param.stats.qos_tx_frag_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_tx_frag_cnt[i]); + pget_info->param.stats.qos_failed_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_failed_cnt[i]); + pget_info->param.stats.qos_retry_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_retry_cnt[i]); + pget_info->param.stats.qos_multi_retry_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_multi_retry_cnt[i]); + pget_info->param.stats.qos_frm_dup_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_frm_dup_cnt[i]); + pget_info->param.stats.qos_rts_suc_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_rts_suc_cnt[i]); + pget_info->param.stats.qos_rts_failure_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_rts_failure_cnt[i]); + pget_info->param.stats.qos_ack_failure_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_ack_failure_cnt[i]); + pget_info->param.stats.qos_rx_frag_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_rx_frag_cnt[i]); + pget_info->param.stats.qos_tx_frm_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_tx_frm_cnt[i]); + pget_info->param.stats.qos_discarded_frm_cnt + [i] = wlan_le32_to_cpu( + pget_log->qos_discarded_frm_cnt[i]); + pget_info->param.stats.qos_mpdus_rx_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_mpdus_rx_cnt[i]); + pget_info->param.stats.qos_retries_rx_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_retries_rx_cnt[i]); + } + pget_info->param.stats.cmacicv_errors = + wlan_le32_to_cpu(pget_log->cmacicv_errors); + pget_info->param.stats.cmac_replays = + wlan_le32_to_cpu(pget_log->cmac_replays); + pget_info->param.stats.mgmt_ccmp_replays = + wlan_le32_to_cpu(pget_log->mgmt_ccmp_replays); + pget_info->param.stats.tkipicv_errors = + wlan_le32_to_cpu(pget_log->tkipicv_errors); + pget_info->param.stats.tkip_replays = + wlan_le32_to_cpu(pget_log->tkip_replays); + pget_info->param.stats.ccmp_decrypt_errors = + wlan_le32_to_cpu(pget_log->ccmp_decrypt_errors); + pget_info->param.stats.ccmp_replays = + wlan_le32_to_cpu(pget_log->ccmp_replays); + pget_info->param.stats.tx_amsdu_cnt = + wlan_le32_to_cpu(pget_log->tx_amsdu_cnt); + pget_info->param.stats.failed_amsdu_cnt = + wlan_le32_to_cpu(pget_log->failed_amsdu_cnt); + pget_info->param.stats.retry_amsdu_cnt = + wlan_le32_to_cpu(pget_log->retry_amsdu_cnt); + pget_info->param.stats.multi_retry_amsdu_cnt = + wlan_le32_to_cpu( + pget_log->multi_retry_amsdu_cnt); + pget_info->param.stats.tx_octets_in_amsdu_cnt = + wlan_le64_to_cpu( + pget_log->tx_octets_in_amsdu_cnt); + pget_info->param.stats.amsdu_ack_failure_cnt = + wlan_le32_to_cpu( + pget_log->amsdu_ack_failure_cnt); + pget_info->param.stats.rx_amsdu_cnt = + wlan_le32_to_cpu(pget_log->rx_amsdu_cnt); + pget_info->param.stats.rx_octets_in_amsdu_cnt = + wlan_le64_to_cpu( + pget_log->rx_octets_in_amsdu_cnt); + pget_info->param.stats.tx_ampdu_cnt = + wlan_le32_to_cpu(pget_log->tx_ampdu_cnt); + pget_info->param.stats.tx_mpdus_in_ampdu_cnt = + wlan_le32_to_cpu( + pget_log->tx_mpdus_in_ampdu_cnt); + pget_info->param.stats.tx_octets_in_ampdu_cnt = + wlan_le64_to_cpu( + pget_log->tx_octets_in_ampdu_cnt); + pget_info->param.stats.ampdu_rx_cnt = + wlan_le32_to_cpu(pget_log->ampdu_rx_cnt); + pget_info->param.stats.mpdu_in_rx_ampdu_cnt = + wlan_le32_to_cpu( + pget_log->mpdu_in_rx_ampdu_cnt); + pget_info->param.stats.rx_octets_in_ampdu_cnt = + wlan_le64_to_cpu( + pget_log->rx_octets_in_ampdu_cnt); + pget_info->param.stats.ampdu_delimiter_crc_error_cnt = + wlan_le32_to_cpu( + pget_log->ampdu_delimiter_crc_error_cnt); + + /* Indicate ioctl complete */ + pioctl_buf->data_read_written = + sizeof(mlan_ds_get_info); + } else + pioctl_buf->data_read_written = + sizeof(mlan_ds_get_stats_org) + + sizeof(pget_info->sub_command); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Get power level and rate index + * + * @param pmpriv A pointer to mlan_private structure + * @param pdata_buf Pointer to the data buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_get_power_level(pmlan_private pmpriv, void *pdata_buf) +{ + t_u16 length = 0; + t_s8 max_power = -1, min_power = -1; + MrvlTypes_Power_Group_t *ppg_tlv = MNULL; + Power_Group_t *pg = MNULL; + + ENTER(); + + if (pdata_buf) { + ppg_tlv = (MrvlTypes_Power_Group_t + *)((t_u8 *)pdata_buf + + sizeof(HostCmd_DS_TXPWR_CFG)); + pg = (Power_Group_t *)((t_u8 *)ppg_tlv + + sizeof(MrvlTypes_Power_Group_t)); + length = ppg_tlv->length; + if (length > 0) { + max_power = pg->power_max; + min_power = pg->power_min; + length -= sizeof(Power_Group_t); + } + while (length) { + pg++; + if (max_power < pg->power_max) + max_power = pg->power_max; + if (min_power > pg->power_min) + min_power = pg->power_min; + length -= sizeof(Power_Group_t); + } + if (ppg_tlv->length > 0) { + pmpriv->min_tx_power_level = min_power; + pmpriv->max_tx_power_level = max_power; + } + } else { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of tx_power_cfg + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_tx_power_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_TXPWR_CFG *ptxp_cfg = &resp->params.txp_cfg; + MrvlTypes_Power_Group_t *ppg_tlv = MNULL; + Power_Group_t *pg = MNULL; + t_u16 action = wlan_le16_to_cpu(ptxp_cfg->action); + mlan_ds_power_cfg *power = MNULL; + mlan_power_group *pwr_grp = MNULL; + t_u8 i = 0; + + ENTER(); + + ppg_tlv = (MrvlTypes_Power_Group_t *)(ptxp_cfg->tlv_buf); + pg = (Power_Group_t *)((t_u8 *)ppg_tlv + + sizeof(MrvlTypes_Power_Group_t)); + + switch (action) { + case HostCmd_ACT_GEN_GET: + ppg_tlv->length = wlan_le16_to_cpu(ppg_tlv->length); + if (pmpriv->adapter->hw_status == + WlanHardwareStatusInitializing) + wlan_get_power_level(pmpriv, ptxp_cfg); + pmpriv->tx_power_level = (t_s16)pg->power_min; + break; + + case HostCmd_ACT_GEN_SET: + if (wlan_le32_to_cpu(ptxp_cfg->mode)) { + if (pg->power_max == pg->power_min) + pmpriv->tx_power_level = (t_s16)pg->power_min; + } + break; + + default: + PRINTM(MERROR, "CMD_RESP: unknown command action %d\n", action); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + PRINTM(MINFO, "Current TxPower Level = %d,Max Power=%d, Min Power=%d\n", + pmpriv->tx_power_level, pmpriv->max_tx_power_level, + pmpriv->min_tx_power_level); + + if (pioctl_buf) { + power = (mlan_ds_power_cfg *)pioctl_buf->pbuf; + if (action == HostCmd_ACT_GEN_GET) { + if (power->sub_command == MLAN_OID_POWER_CFG) { + pioctl_buf->data_read_written = + sizeof(mlan_power_cfg_t) + + MLAN_SUB_COMMAND_SIZE; + power->param.power_cfg.power_level = + pmpriv->tx_power_level; + if (wlan_le32_to_cpu(ptxp_cfg->mode)) + power->param.power_cfg.is_power_auto = + 0; + else + power->param.power_cfg.is_power_auto = + 1; + } else { + power->param.power_ext.num_pwr_grp = 0; + i = 0; + while ((ppg_tlv->length) && + (i < MAX_POWER_GROUP)) { + pwr_grp = (mlan_power_group *)&power + ->param.power_ext + .power_group[i]; + pwr_grp->first_rate_ind = 0; + pwr_grp->last_rate_ind = 0; + if (pg->modulation_class == + MOD_CLASS_HR_DSSS) { + pwr_grp->rate_format = + MLAN_RATE_FORMAT_LG; + pwr_grp->first_rate_ind = + pg->first_rate_code; + pwr_grp->last_rate_ind = + pg->last_rate_code; + } else if (pg->modulation_class == + MOD_CLASS_OFDM) { + pwr_grp->rate_format = + MLAN_RATE_FORMAT_LG; + pwr_grp->first_rate_ind = + MLAN_RATE_INDEX_OFDM0 + + pg->first_rate_code; + pwr_grp->last_rate_ind = + MLAN_RATE_INDEX_OFDM0 + + pg->last_rate_code; + } else if (pg->modulation_class == + MOD_CLASS_HT) { + pwr_grp->rate_format = + MLAN_RATE_FORMAT_HT; + pwr_grp->first_rate_ind = + pg->first_rate_code; + pwr_grp->last_rate_ind = + pg->last_rate_code; + } else if (pg->modulation_class == + MOD_CLASS_VHT) { + pwr_grp->rate_format = + MLAN_RATE_FORMAT_VHT; + pwr_grp->first_rate_ind = + (pg->first_rate_code) & + 0xF; + pwr_grp->last_rate_ind = + (pg->last_rate_code) & + 0xF; + // pwr_grp->nss = 1 + + // (pg->first_rate_code >> 4); + pwr_grp->nss = + 1 + + (pg->last_rate_code >> + 4); + } + pwr_grp->bandwidth = pg->ht_bandwidth; + pwr_grp->power_min = pg->power_min; + pwr_grp->power_max = pg->power_max; + pwr_grp->power_step = pg->power_step; + ppg_tlv->length -= + sizeof(Power_Group_t); + pg++; + i++; + } + power->param.power_ext.num_pwr_grp = i; + pioctl_buf->data_read_written = + sizeof(mlan_power_cfg_ext) + + MLAN_SUB_COMMAND_SIZE; + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of rf_tx_power + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_802_11_rf_tx_power(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + t_u8 event_buf[32]; + mlan_event *pevent = (mlan_event *)event_buf; + 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); + memset(pmadapter, event_buf, 0, sizeof(event_buf)); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_DISCONNECT_LOGGER; + pevent->event_len = sizeof(resp->params.deauth.reason_code); + memcpy_ext(pmpriv->adapter, (t_u8 *)pevent->event_buf, + &resp->params.deauth.reason_code, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DISCONNECT_LOGGER, pevent); + + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_KEY_MATERIAL *pkey = &resp->params.key_material; + mlan_ds_sec_cfg *sec = MNULL; + t_u8 zero_kek[MLAN_KEK_LEN] = {0}; + + ENTER(); + + if (wlan_le16_to_cpu(pkey->action) == HostCmd_ACT_GEN_SET) { + if ((wlan_le16_to_cpu(pkey->key_param_set.key_info) & + KEY_INFO_TKIP_MCAST)) { + PRINTM(MINFO, "key: GTK is set\n"); + pmpriv->wpa_is_gtk_set = MTRUE; + if (pmpriv->port_ctrl_mode == MTRUE) { + /* GTK is set, open the port */ + PRINTM(MINFO, + "GTK_SET: Open port: WPA/WPA2 h-supp mode\n"); + pmpriv->port_open = MTRUE; + } + if (memcmp(pmpriv->adapter, pmpriv->gtk_rekey.kek, + zero_kek, sizeof(zero_kek)) != 0) { + wlan_prepare_cmd( + pmpriv, + HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmpriv->gtk_rekey); + memset(pmpriv->adapter, &pmpriv->gtk_rekey, 0, + sizeof(mlan_ds_misc_gtk_rekey_data)); + } + pmpriv->adapter->scan_block = MFALSE; + } + } else if (wlan_le16_to_cpu(pkey->action) == HostCmd_ACT_GEN_GET) { + if (pioctl_buf && (wlan_le16_to_cpu(pkey->key_param_set.type) == + TLV_TYPE_KEY_PARAM_V2)) { + sec = (mlan_ds_sec_cfg *)pioctl_buf->pbuf; + memcpy_ext(pmpriv->adapter, + sec->param.encrypt_key.mac_addr, + pkey->key_param_set.mac_addr, + MLAN_MAC_ADDR_LENGTH, + sizeof(sec->param.encrypt_key.mac_addr)); + sec->param.encrypt_key.key_index = + pkey->key_param_set.key_idx; + PRINTM(MIOCTL, + "key_type=%d, key_index=%d, key_info=0x%x " MACSTR + "\n", + pkey->key_param_set.key_type, + pkey->key_param_set.key_idx, + wlan_le16_to_cpu(pkey->key_param_set.key_info), + MAC2STR(sec->param.encrypt_key.mac_addr)); + switch (pkey->key_param_set.key_type) { + case KEY_TYPE_ID_WAPI: + sec->param.encrypt_key.is_wapi_key = MTRUE; + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu( + pkey->key_param_set.key_params + .wapi.key_len); + memcpy_ext( + pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params.wapi.key, + sec->param.encrypt_key.key_len, + sizeof(sec->param.encrypt_key + .key_material)); + memcpy_ext( + pmpriv->adapter, + sec->param.encrypt_key.pn, + pkey->key_param_set.key_params.wapi.pn, + PN_SIZE, + sizeof(sec->param.encrypt_key.pn)); + break; + case KEY_TYPE_ID_TKIP: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu( + pkey->key_param_set.key_params + .tkip.key_len); + memcpy_ext( + pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params.tkip.key, + sec->param.encrypt_key.key_len, + sizeof(sec->param.encrypt_key + .key_material)); + memcpy_ext( + pmpriv->adapter, + sec->param.encrypt_key.pn, + pkey->key_param_set.key_params.tkip.pn, + WPA_PN_SIZE, + sizeof(sec->param.encrypt_key.pn)); + break; + case KEY_TYPE_ID_AES: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu( + pkey->key_param_set.key_params + .aes.key_len); + memcpy_ext( + pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params.aes.key, + sec->param.encrypt_key.key_len, + sizeof(sec->param.encrypt_key + .key_material)); + memcpy_ext( + pmpriv->adapter, + sec->param.encrypt_key.pn, + pkey->key_param_set.key_params.aes.pn, + WPA_PN_SIZE, + sizeof(sec->param.encrypt_key.pn)); + break; + case KEY_TYPE_ID_AES_CMAC: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu( + pkey->key_param_set.key_params + .cmac_aes.key_len); + memcpy_ext(pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params + .cmac_aes.key, + sec->param.encrypt_key.key_len, + sizeof(sec->param.encrypt_key + .key_material)); + memcpy_ext(pmpriv->adapter, + sec->param.encrypt_key.pn, + pkey->key_param_set.key_params + .cmac_aes.ipn, + IGTK_PN_SIZE, + sizeof(sec->param.encrypt_key.pn)); + break; + case KEY_TYPE_ID_WEP: + sec->param.encrypt_key.key_len = + wlan_le16_to_cpu( + pkey->key_param_set.key_params + .wep.key_len); + memcpy_ext( + pmpriv->adapter, + sec->param.encrypt_key.key_material, + pkey->key_param_set.key_params.wep.key, + sec->param.encrypt_key.key_len, + sizeof(sec->param.encrypt_key + .key_material)); + break; + } + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Handle the supplicant profile response + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_ret_802_11_supplicant_profile(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp) +{ + HostCmd_DS_802_11_IBSS_STATUS *pibss_coal_resp = + &(resp->params.ibss_coalescing); + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0, 0, 0, 0, 0, 0}; + + ENTER(); + + if (wlan_le16_to_cpu(pibss_coal_resp->action) == HostCmd_ACT_GEN_SET) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + PRINTM(MINFO, "New BSSID " MACSTR "\n", + MAC2STR(pibss_coal_resp->bssid)); + + /* If rsp has MNULL BSSID, Just return..... No Action */ + if (!memcmp(pmpriv->adapter, pibss_coal_resp->bssid, zero_mac, + MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MMSG, "New BSSID is MNULL\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + /* If BSSID is diff, modify current BSS parameters */ + if (memcmp(pmpriv->adapter, + pmpriv->curr_bss_params.bss_descriptor.mac_address, + pibss_coal_resp->bssid, MLAN_MAC_ADDR_LENGTH)) { + /* BSSID */ + memcpy_ext(pmpriv->adapter, + pmpriv->curr_bss_params.bss_descriptor.mac_address, + pibss_coal_resp->bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(pmpriv->curr_bss_params.bss_descriptor + .mac_address)); + + /* 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + t_u16 resp_len = 0, travel_len = 0; + int i = 0; + mlan_ds_misc_cfg *misc = MNULL; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + custom_ie *cptr; + tlvbuf_max_mgmt_ie *max_mgmt_ie = MNULL; + HostCmd_DS_MGMT_IE_LIST_CFG *pmgmt_ie_list = + &(resp->params.mgmt_ie_list); + + ENTER(); + + if (wlan_le16_to_cpu(pmgmt_ie_list->action) == HostCmd_ACT_GEN_SET) { + if ((pmpriv->adapter->state_rdh.stage == RDH_SET_CUSTOM_IE) || + (pmpriv->adapter->state_rdh.stage == RDH_REM_CUSTOM_IE)) + if (!pmpriv->adapter->ecsa_enable) + wlan_11h_radar_detected_callback( + (t_void *)pmpriv); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + cust_ie = (mlan_ds_misc_custom_ie *)&pmgmt_ie_list->ds_mgmt_ie; + if (cust_ie) { + cust_ie->type = wlan_le16_to_cpu(cust_ie->type); + resp_len = cust_ie->len = wlan_le16_to_cpu(cust_ie->len); + travel_len = 0; + /* conversion for index, mask, len */ + if (resp_len == sizeof(t_u16)) + cust_ie->ie_data_list[0].ie_index = wlan_cpu_to_le16( + cust_ie->ie_data_list[0].ie_index); + + while (resp_len > sizeof(t_u16)) { + cptr = (custom_ie *)(((t_u8 *)cust_ie->ie_data_list) + + travel_len); + cptr->ie_index = wlan_le16_to_cpu(cptr->ie_index); + cptr->mgmt_subtype_mask = + wlan_le16_to_cpu(cptr->mgmt_subtype_mask); + cptr->ie_length = wlan_le16_to_cpu(cptr->ie_length); + travel_len += cptr->ie_length + sizeof(custom_ie) - + MAX_IE_SIZE; + resp_len -= cptr->ie_length + sizeof(custom_ie) - + MAX_IE_SIZE; + } + memcpy_ext(pmpriv->adapter, &misc->param.cust_ie, cust_ie, + (cust_ie->len + sizeof(MrvlIEtypesHeader_t)), + sizeof(misc->param.cust_ie)); + max_mgmt_ie = + (tlvbuf_max_mgmt_ie *)((t_u8 *)cust_ie + cust_ie->len + + sizeof(MrvlIEtypesHeader_t)); + if (max_mgmt_ie) { + max_mgmt_ie->type = wlan_le16_to_cpu(max_mgmt_ie->type); + if (max_mgmt_ie->type == TLV_TYPE_MAX_MGMT_IE) { + max_mgmt_ie->len = + wlan_le16_to_cpu(max_mgmt_ie->len); + max_mgmt_ie->count = + wlan_le16_to_cpu(max_mgmt_ie->count); + for (i = 0; i < max_mgmt_ie->count; i++) { + max_mgmt_ie->info[i] + .buf_size = wlan_le16_to_cpu( + max_mgmt_ie->info[i].buf_size); + max_mgmt_ie->info[i] + .buf_count = wlan_le16_to_cpu( + max_mgmt_ie->info[i].buf_count); + } + /* Append max_mgmt_ie TLV after custom_ie */ + memcpy_ext( + pmpriv->adapter, + (t_u8 *)&misc->param.cust_ie + + (cust_ie->len + + sizeof(MrvlIEtypesHeader_t)), + max_mgmt_ie, + max_mgmt_ie->len + + sizeof(MrvlIEtypesHeader_t), + sizeof(misc->param.cust_ie) - + (cust_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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *mis_ccfg = MNULL; + HostCmd_DS_ECL_SYSTEM_CLOCK_CONFIG *clk_cfg = + &resp->params.sys_clock_cfg; + int i = 0; + + ENTER(); + + if (pioctl_buf) { + mis_ccfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + mis_ccfg->param.sys_clock.cur_sys_clk = + wlan_le16_to_cpu(clk_cfg->cur_sys_clk); + mis_ccfg->param.sys_clock.sys_clk_type = + wlan_le16_to_cpu(clk_cfg->sys_clk_type); + mis_ccfg->param.sys_clock.sys_clk_num = + wlan_le16_to_cpu(clk_cfg->sys_clk_len) / sizeof(t_u16); + for (i = 0; i < mis_ccfg->param.sys_clock.sys_clk_num; i++) + mis_ccfg->param.sys_clock.sys_clk[i] = + wlan_le16_to_cpu(clk_cfg->sys_clk[i]); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of inactivity timeout + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_inactivity_timeout(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_SUBSCRIBE_EVENT *evt = + (HostCmd_DS_SUBSCRIBE_EVENT *)&resp->params.subscribe_event; + mlan_ds_subscribe_evt *sub_evt = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_GET)) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + sub_evt = &misc->param.subscribe_event; + sub_evt->evt_bitmap = wlan_le16_to_cpu(evt->event_bitmap); + pioctl_buf->data_read_written = sizeof(mlan_ds_misc_cfg); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of + * OTP user data + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_otp_user_data(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_OTP_USER_DATA *cmd_user_data = + (HostCmd_DS_OTP_USER_DATA *)&resp->params.otp_user_data; + mlan_ds_misc_otp_user_data *user_data = MNULL; + + ENTER(); + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_GET)) { + user_data = (mlan_ds_misc_otp_user_data *)pioctl_buf->pbuf; + user_data->user_data_length = + MIN(MAX_OTP_USER_DATA_LEN, + wlan_le16_to_cpu(cmd_user_data->user_data_length)); + memcpy_ext(pmpriv->adapter, user_data->user_data, + cmd_user_data->user_data, + user_data->user_data_length, + sizeof(user_data->user_data)); + pioctl_buf->data_read_written = + sizeof(mlan_ds_misc_otp_user_data) + + user_data->user_data_length; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef USB +/** + * @brief This function handles the command response of + * packet aggregation + * + * @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_packet_aggr_over_host_interface(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc = MNULL; + HostCmd_DS_PACKET_AGGR_OVER_HOST_INTERFACE *packet_aggr = + (HostCmd_DS_PACKET_AGGR_OVER_HOST_INTERFACE *)&resp->params + .packet_aggr; + MrvlIETypes_USBAggrParam_t *usb_aggr_param_tlv = MNULL; + mlan_ds_misc_usb_aggr_ctrl *usb_aggr_ctrl = MNULL; + t_u8 *ptlv_buffer = (t_u8 *)packet_aggr->tlv_buf; + mlan_adapter *pmadapter = pmpriv->adapter; + t_u16 tlv = 0; + int tlv_buf_len = 0; + t_u8 changed = 0; +#if defined(USB) + t_s32 i = 0; +#endif + + ENTER(); + + tlv_buf_len = + resp->size - + (sizeof(HostCmd_DS_PACKET_AGGR_OVER_HOST_INTERFACE) + S_DS_GEN); + + if (!pioctl_buf) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + while (tlv_buf_len > 0) { + changed = 0; + tlv = (*ptlv_buffer) | (*(ptlv_buffer + 1) << 8); + switch (tlv) { + case MRVL_USB_AGGR_PARAM_TLV_ID: + usb_aggr_param_tlv = + (MrvlIETypes_USBAggrParam_t *)ptlv_buffer; + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + usb_aggr_ctrl = (mlan_ds_misc_usb_aggr_ctrl *)&( + misc->param.usb_aggr_params); + usb_aggr_param_tlv->header.len = wlan_le16_to_cpu( + usb_aggr_param_tlv->header.len); + usb_aggr_param_tlv->enable = + wlan_le16_to_cpu(usb_aggr_param_tlv->enable); +#if defined(USB) + if (pioctl_buf->action == MLAN_ACT_SET) { + /* Update the Tx aggregation values in + * MLAN */ + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) { + pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_ctrl.enable = + usb_aggr_ctrl->tx_aggr_ctrl + .enable; + if (pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_ctrl.aggr_mode != + usb_aggr_ctrl->tx_aggr_ctrl + .aggr_mode) { + pmadapter->pcard_usb + ->usb_tx_aggr[i] + .aggr_ctrl.aggr_mode = + usb_aggr_ctrl + ->tx_aggr_ctrl + .aggr_mode; + changed = 1; + } + if (pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_ctrl.aggr_align != + usb_aggr_ctrl->tx_aggr_ctrl + .aggr_align) { + pmadapter->pcard_usb + ->usb_tx_aggr[i] + .aggr_ctrl.aggr_align = + usb_aggr_ctrl + ->tx_aggr_ctrl + .aggr_align; + changed = 1; + } + if (pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_ctrl.aggr_max != + usb_aggr_ctrl->tx_aggr_ctrl + .aggr_max) { + pmadapter->pcard_usb + ->usb_tx_aggr[i] + .aggr_ctrl.aggr_max = + usb_aggr_ctrl + ->tx_aggr_ctrl + .aggr_max; + changed = 1; + } + pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_ctrl.aggr_tmo = + usb_aggr_ctrl->tx_aggr_ctrl + .aggr_tmo; + } + } else { + if (usb_aggr_param_tlv->enable & MBIT(1)) + usb_aggr_ctrl->tx_aggr_ctrl.enable = + MTRUE; + else + usb_aggr_ctrl->tx_aggr_ctrl.enable = + MFALSE; + usb_aggr_ctrl->tx_aggr_ctrl + .aggr_align = wlan_le16_to_cpu( + usb_aggr_param_tlv->tx_aggr_align); + usb_aggr_ctrl->tx_aggr_ctrl.aggr_mode = + pmadapter->pcard_usb->usb_tx_aggr[0] + .aggr_ctrl.aggr_mode; + usb_aggr_ctrl->tx_aggr_ctrl.aggr_max = + pmadapter->pcard_usb->usb_tx_aggr[0] + .aggr_ctrl.aggr_max; + usb_aggr_ctrl->tx_aggr_ctrl.aggr_tmo = + pmadapter->pcard_usb->usb_tx_aggr[0] + .aggr_ctrl.aggr_tmo; + } + if (changed) + wlan_reset_usb_tx_aggr(pmadapter); +#endif + +#if defined(USB) + if (pioctl_buf->action == MLAN_ACT_SET) { + /* Update the Rx deaggregation values in + * MLAN */ + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl + .enable = + usb_aggr_ctrl->rx_deaggr_ctrl.enable; + if (pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_mode != + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_mode) + pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_mode = + usb_aggr_ctrl->rx_deaggr_ctrl + .aggr_mode; + if (pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_align != + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_align) + pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_align = + usb_aggr_ctrl->rx_deaggr_ctrl + .aggr_align; + if (pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_max != + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_max) + pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_max = + usb_aggr_ctrl->rx_deaggr_ctrl + .aggr_max; + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl + .aggr_tmo = + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_tmo; + } else { + if (usb_aggr_param_tlv->enable & MBIT(0)) + usb_aggr_ctrl->rx_deaggr_ctrl.enable = + MTRUE; + else + usb_aggr_ctrl->rx_deaggr_ctrl.enable = + MFALSE; + usb_aggr_ctrl->rx_deaggr_ctrl + .aggr_mode = wlan_le16_to_cpu( + usb_aggr_param_tlv->rx_aggr_mode); + usb_aggr_ctrl->rx_deaggr_ctrl + .aggr_align = wlan_le16_to_cpu( + usb_aggr_param_tlv->rx_aggr_align); + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_max = + wlan_le16_to_cpu( + usb_aggr_param_tlv->rx_aggr_max); + usb_aggr_ctrl->rx_deaggr_ctrl.aggr_tmo = + wlan_le16_to_cpu( + usb_aggr_param_tlv->rx_aggr_tmo); + } +#endif + ptlv_buffer += usb_aggr_param_tlv->header.len + + sizeof(MrvlIEtypesHeader_t); + tlv_buf_len -= (usb_aggr_param_tlv->header.len + + sizeof(MrvlIEtypesHeader_t)); + break; + default: + break; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif + +/** + * @brief This function handles the command response of + * DFS Repeater mode configuration + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_dfs_repeater_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_DFS_REPEATER_MODE *cmd_dfs_repeater = + &resp->params.dfs_repeater; + mlan_ds_misc_cfg *misc = MNULL; + mlan_ds_misc_dfs_repeater *dfs_cfg = MNULL; + + ENTER(); + + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_GET)) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + dfs_cfg = + (mlan_ds_misc_dfs_repeater *)&misc->param.dfs_repeater; + dfs_cfg->mode = wlan_le16_to_cpu(cmd_dfs_repeater->mode); + } + if (pioctl_buf && (pioctl_buf->action == MLAN_ACT_SET)) { + if (wlan_le16_to_cpu(cmd_dfs_repeater->mode) == 1) { + /* Set dfs_repeater mode to true/enabled + * for futher references. + */ + pmpriv->adapter->dfs_repeater = MTRUE; + } else { + pmpriv->adapter->dfs_repeater = MFALSE; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of coalesce config + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_coalesce_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + ENTER(); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +mlan_status wlan_ret_get_sensor_temp(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *pcfg = MNULL; + const HostCmd_DS_SENSOR_TEMP *pSensorT = &resp->params.temp_sensor; + + ENTER(); + + if (pioctl_buf) { + pcfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + pcfg->param.sensor_temp.temperature = + wlan_le32_to_cpu(pSensorT->temperature); + PRINTM(MCMND, "get SOC temperature %u C \n", + pSensorT->temperature); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of arb 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_arb_cfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CMD_ARB_CONFIG *cfg_cmd = + (HostCmd_DS_CMD_ARB_CONFIG *)&resp->params.arb_cfg; + mlan_ds_misc_cfg *misc_cfg = MNULL; + + ENTER(); + + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.arb_cfg.arb_mode = + wlan_le32_to_cpu(cfg_cmd->arb_mode); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sta get band and + * channel + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_ret_sta_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_STA_CONFIGURE *cmdrsp_sta_cfg = + (HostCmd_DS_STA_CONFIGURE *)&resp->params.sta_cfg; + mlan_ds_bss *bss = MNULL; + MrvlIEtypes_channel_band_t *tlv_band_channel = MNULL; + + ENTER(); + if (pioctl_buf) { + if (pioctl_buf->req_id == MLAN_IOCTL_BSS) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + if (bss->sub_command == MLAN_OID_BSS_CHAN_INFO) { + tlv_band_channel = + (MrvlIEtypes_channel_band_t *) + cmdrsp_sta_cfg->tlv_buffer; + bss->param.sta_channel.bandcfg = + tlv_band_channel->bandcfg; + bss->param.sta_channel.channel = + tlv_band_channel->channel; + bss->param.sta_channel.is_11n_enabled = + IS_11N_ENABLED(pmpriv); + if (bss->param.sta_channel.bandcfg.chanWidth == + CHAN_BW_80MHZ) + bss->param.sta_channel.center_chan = + wlan_get_center_freq_idx( + pmpriv, BAND_AAC, + bss->param.sta_channel + .channel, + CHANNEL_BW_80MHZ); + PRINTM(MCMND, + "Get STA channel, band=0x%x, channel=%d, is_11n_enabled=%d center_chan=%d\n", + bss->param.sta_channel.bandcfg, + bss->param.sta_channel.channel, + bss->param.sta_channel.is_11n_enabled, + bss->param.sta_channel.center_chan); + + pioctl_buf->data_read_written = + sizeof(mlan_ds_bss); + } + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of set/get auto tx + * + * @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_auto_tx(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_AUTO_TX *cmdrsp_auto_tx = + (HostCmd_DS_AUTO_TX *)&resp->params.auto_tx; + mlan_status status = MLAN_STATUS_SUCCESS; + MrvlIEtypesHeader_t *header = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 action; + t_u16 len = 0; + MrvlIEtypes_Cloud_Keep_Alive_t *keep_alive_tlv = MNULL; + MrvlIEtypes_Keep_Alive_Pkt_t *pkt_tlv = MNULL; + mlan_ds_misc_keep_alive *misc_keep_alive = MNULL; + + ENTER(); + + action = wlan_le16_to_cpu(cmdrsp_auto_tx->action); + if (!pioctl_buf) { + PRINTM(MERROR, "ioctl is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + + if (!misc) { + PRINTM(MERROR, "misc is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (resp->result == HostCmd_RESULT_OK) { + header = (MrvlIEtypesHeader_t *)cmdrsp_auto_tx->tlv_buffer; + header->type = wlan_le16_to_cpu(header->type); + len = wlan_le16_to_cpu(header->len); + if (header->type == TLV_TYPE_CLOUD_KEEP_ALIVE) { + keep_alive_tlv = (MrvlIEtypes_Cloud_Keep_Alive_t *) + cmdrsp_auto_tx->tlv_buffer; + misc_keep_alive = (mlan_ds_misc_keep_alive *)&misc + ->param.keep_alive; + misc_keep_alive->mkeep_alive_id = + keep_alive_tlv->keep_alive_id; + misc_keep_alive->enable = keep_alive_tlv->enable; + if (((action == HostCmd_ACT_GEN_SET) || + (action == HostCmd_ACT_GEN_RESET)) && + !keep_alive_tlv->enable) { + len = len - + sizeof(keep_alive_tlv->keep_alive_id) - + sizeof(keep_alive_tlv->enable); + if (len > sizeof(MrvlIEtypesHeader_t)) { + header = (MrvlIEtypesHeader_t *) + keep_alive_tlv->tlv; + header->type = + wlan_le16_to_cpu(header->type); + len = wlan_le16_to_cpu(header->len) - + sizeof(Eth803Hdr_t); + if (header->type == + TLV_TYPE_KEEP_ALIVE_PKT) { + pkt_tlv = + (MrvlIEtypes_Keep_Alive_Pkt_t + *)keep_alive_tlv + ->tlv; + memcpy_ext( + pmpriv->adapter, + misc_keep_alive->dst_mac, + pkt_tlv->eth_header + .dest_addr, + MLAN_MAC_ADDR_LENGTH, + sizeof(misc_keep_alive + ->dst_mac)); + memcpy_ext( + pmpriv->adapter, + misc_keep_alive->src_mac, + pkt_tlv->eth_header + .src_addr, + MLAN_MAC_ADDR_LENGTH, + sizeof(misc_keep_alive + ->src_mac)); + memcpy_ext( + pmpriv->adapter, + misc_keep_alive->packet, + pkt_tlv->ip_packet, len, + sizeof(misc_keep_alive + ->packet)); + misc_keep_alive->pkt_len = len; + } + } + } + } + } + + LEAVE(); + return status; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function prepares command resp of MFG Continuous Tx + * + * @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_mfg_tx_cont(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc = MNULL; + struct mfg_cmd_tx_cont *mcmd = + (struct mfg_cmd_tx_cont *)&resp->params.mfg_tx_cont; + struct mfg_cmd_tx_cont *cfg = MNULL; + + ENTER(); + if (!pioctl_buf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + cfg = (struct mfg_cmd_tx_cont *)&misc->param.mfg_tx_cont; + + cfg->error = wlan_le32_to_cpu(mcmd->error); + cfg->enable_tx = wlan_le32_to_cpu(mcmd->enable_tx); + cfg->cw_mode = wlan_le32_to_cpu(mcmd->cw_mode); + cfg->payload_pattern = wlan_le32_to_cpu(mcmd->payload_pattern); + cfg->cs_mode = wlan_le32_to_cpu(mcmd->cs_mode); + cfg->act_sub_ch = wlan_le32_to_cpu(mcmd->act_sub_ch); + cfg->tx_rate = wlan_le32_to_cpu(mcmd->tx_rate); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command resp of MFG Tx frame + * + * @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_mfg_tx_frame(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc = MNULL; + struct mfg_cmd_tx_frame2 *mcmd = + (struct mfg_cmd_tx_frame2 *)&resp->params.mfg_tx_frame2; + struct mfg_cmd_tx_frame2 *cfg = MNULL; + + ENTER(); + if (!pioctl_buf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + cfg = (struct mfg_cmd_tx_frame2 *)&misc->param.mfg_tx_frame2; + + cfg->error = wlan_le32_to_cpu(mcmd->error); + cfg->enable = wlan_le32_to_cpu(mcmd->enable); + cfg->data_rate = wlan_le32_to_cpu(mcmd->data_rate); + cfg->frame_pattern = wlan_le32_to_cpu(mcmd->frame_pattern); + cfg->frame_length = wlan_le32_to_cpu(mcmd->frame_length); + cfg->adjust_burst_sifs = wlan_le16_to_cpu(mcmd->adjust_burst_sifs); + cfg->burst_sifs_in_us = wlan_le32_to_cpu(mcmd->burst_sifs_in_us); + cfg->short_preamble = wlan_le32_to_cpu(mcmd->short_preamble); + cfg->act_sub_ch = wlan_le32_to_cpu(mcmd->act_sub_ch); + cfg->short_gi = wlan_le32_to_cpu(mcmd->short_gi); + cfg->tx_bf = wlan_le32_to_cpu(mcmd->tx_bf); + cfg->gf_mode = wlan_le32_to_cpu(mcmd->gf_mode); + cfg->stbc = wlan_le32_to_cpu(mcmd->stbc); + memcpy_ext(pmpriv->adapter, cfg->bssid, mcmd->bssid, + MLAN_MAC_ADDR_LENGTH, sizeof(cfg->bssid)); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command resp of MFG 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 + */ +mlan_status wlan_ret_mfg(pmlan_private pmpriv, HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_misc_cfg *misc = MNULL; + struct mfg_cmd_generic_cfg *mcmd = + (struct mfg_cmd_generic_cfg *)&resp->params.mfg_generic_cfg; + struct mfg_cmd_generic_cfg *cfg = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + if (!pioctl_buf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + switch (wlan_le32_to_cpu(mcmd->mfg_cmd)) { + case MFG_CMD_TX_CONT: + ret = wlan_ret_mfg_tx_cont(pmpriv, resp, pioctl_buf); + goto cmd_mfg_done; + case MFG_CMD_TX_FRAME: + ret = wlan_ret_mfg_tx_frame(pmpriv, resp, pioctl_buf); + goto cmd_mfg_done; + case MFG_CMD_SET_TEST_MODE: + case MFG_CMD_UNSET_TEST_MODE: + case MFG_CMD_TX_ANT: + case MFG_CMD_RX_ANT: + case MFG_CMD_RF_CHAN: + case MFG_CMD_CLR_RX_ERR: + case MFG_CMD_RF_BAND_AG: + case MFG_CMD_RF_CHANNELBW: + case MFG_CMD_RFPWR: + break; + default: + ret = MLAN_STATUS_FAILURE; + goto cmd_mfg_done; + } + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + cfg = (struct mfg_cmd_generic_cfg *)&misc->param.mfg_generic_cfg; + + cfg->error = wlan_le32_to_cpu(mcmd->error); + cfg->data1 = wlan_le32_to_cpu(mcmd->data1); + cfg->data2 = wlan_le32_to_cpu(mcmd->data2); + cfg->data3 = wlan_le32_to_cpu(mcmd->data3); +cmd_mfg_done: + LEAVE(); + return ret; +} + +/** + * @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(t_void *priv, t_u16 cmdresp_no, + t_void *pcmd_buf, 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; +#ifdef SDIO + int ctr; +#endif + + ENTER(); + + /* If the command is not successful, cleanup and return failure */ + if ((resp->result != HostCmd_RESULT_OK)) { + ret = wlan_process_cmdresp_error(pmpriv, resp, pioctl_buf); + LEAVE(); + return ret; + } + /* Command successful, handle response */ + switch (cmdresp_no) { + case HostCmd_CMD_GET_HW_SPEC: + ret = wlan_ret_get_hw_spec(pmpriv, resp, pioctl_buf); + break; +#ifdef SDIO + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = wlan_ret_sdio_rx_aggr_cfg(pmpriv, resp); + break; +#endif + case HostCmd_CMD_CFG_DATA: + ret = wlan_ret_cfg_data(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = wlan_ret_mac_control(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_MAC_ADDRESS: + ret = wlan_ret_802_11_mac_address(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MAC_MULTICAST_ADR: + ret = wlan_ret_mac_multicast_adr(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = wlan_ret_tx_rate_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_SCAN: + ret = wlan_ret_802_11_scan(pmpriv, resp, pioctl_buf); + pioctl_buf = MNULL; + pmadapter->curr_cmd->pioctl_buf = MNULL; + break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = wlan_ret_802_11_scan_ext(pmpriv, resp, pioctl_buf); + pioctl_buf = MNULL; + pmadapter->curr_cmd->pioctl_buf = MNULL; + break; + case HostCmd_CMD_802_11_BG_SCAN_CONFIG: + ret = wlan_ret_bgscan_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_BG_SCAN_QUERY: + ret = wlan_ret_802_11_bgscan_query(pmpriv, resp, pioctl_buf); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_BGSCAN_RESULT, MNULL); + PRINTM(MINFO, "CMD_RESP: BG_SCAN result is ready!\n"); + break; + case HostCmd_CMD_TXPWR_CFG: + ret = wlan_ret_tx_power_cfg(pmpriv, resp, pioctl_buf); + break; + + case HostCmd_CMD_802_11_RF_TX_POWER: + ret = wlan_ret_802_11_rf_tx_power(pmpriv, resp, pioctl_buf); + break; + + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = wlan_ret_enh_power_mode(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = wlan_ret_802_11_hs_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_SLEEP_PERIOD: + ret = wlan_ret_802_11_sleep_period(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_SLEEP_PARAMS: + ret = wlan_ret_802_11_sleep_params(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_ROBUSTCOEX: + break; + case HostCmd_CMD_DMCS_CONFIG: + ret = wlan_ret_dmcs_config(pmpriv, resp, pioctl_buf); + break; +#if defined(PCIE) + case HostCmd_CMD_SSU: + PRINTM(MCMND, + "SSU cmdresp:number_of_buffers %d, buffer_size %d rec_len %d\n", + resp->params.ssu_params.number_of_buffers, + resp->params.ssu_params.buffer_size, + resp->params.ssu_params.rec_len); + break; +#endif + case HostCmd_CMD_802_11_ASSOCIATE: + ret = wlan_ret_802_11_associate(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_DEAUTHENTICATE: + case HostCmd_CMD_802_11_DISASSOCIATE: + ret = wlan_ret_802_11_deauthenticate(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_START: + case HostCmd_CMD_802_11_AD_HOC_JOIN: + ret = wlan_ret_802_11_ad_hoc(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = wlan_ret_802_11_ad_hoc_stop(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = wlan_ret_get_log(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_LINK_STATS: + ret = wlan_ret_get_link_statistic(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RSSI_INFO_EXT: + ret = wlan_ret_802_11_rssi_info_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RSSI_INFO: + ret = wlan_ret_802_11_rssi_info(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = wlan_ret_802_11_snmp_mib(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_RADIO_CONTROL: + ret = wlan_ret_802_11_radio_control(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + ret = wlan_ret_802_11_tx_rate_query(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_RF_CHANNEL: + ret = wlan_ret_802_11_rf_channel(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_RF_ANTENNA: + ret = wlan_ret_802_11_rf_antenna(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_CW_MODE_CTRL: + ret = wlan_ret_cw_mode_ctrl(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_VERSION_EXT: + ret = wlan_ret_ver_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RX_MGMT_IND: + ret = wlan_ret_rx_mgmt_ind(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_FUNC_INIT: + case HostCmd_CMD_FUNC_SHUTDOWN: + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + ret = wlan_ret_802_11_key_material(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG: + break; + case HostCmd_CMD_SUPPLICANT_PMK: + ret = wlan_ret_802_11_supplicant_pmk(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_SUPPLICANT_PROFILE: + ret = wlan_ret_802_11_supplicant_profile(pmpriv, resp, + pioctl_buf); + break; + case HostCmd_CMD_802_11_EAPOL_PKT: + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = wlan_ret_802_11d_domain_info(pmpriv, resp); + break; + case HostCmd_CMD_802_11_TPC_ADAPT_REQ: + case HostCmd_CMD_802_11_TPC_INFO: + case HostCmd_CMD_802_11_CHAN_SW_ANN: + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = wlan_11h_cmdresp_process(pmpriv, resp); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = wlan_ret_11n_addba_req(pmpriv, resp); + break; + case HostCmd_CMD_11N_DELBA: + ret = wlan_ret_11n_delba(pmpriv, resp); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = wlan_ret_11n_addba_resp(pmpriv, resp); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + wlan_set_tx_pause_flag(pmpriv, MFALSE); + + pmadapter->tx_buf_size = + (t_u16)wlan_le16_to_cpu(resp->params.tx_buf.buff_size); +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + pmadapter->tx_buf_size = (pmadapter->tx_buf_size / + MLAN_SDIO_BLOCK_SIZE) * + MLAN_SDIO_BLOCK_SIZE; + pmadapter->pcard_sd->mp_end_port = wlan_le16_to_cpu( + resp->params.tx_buf.mp_end_port); + pmadapter->pcard_sd->mp_data_port_mask = + pmadapter->pcard_sd->reg->data_port_mask; + + for (ctr = 1; + ctr <= MAX_PORT - pmadapter->pcard_sd->mp_end_port; + ctr++) { + pmadapter->pcard_sd->mp_data_port_mask &= + ~(1 << (MAX_PORT - ctr)); + } + + pmadapter->pcard_sd->curr_wr_port = 0; + pmadapter->pcard_sd->mpa_tx.pkt_aggr_limit = + MIN(SDIO_MP_AGGR_DEF_PKT_LIMIT, + (pmadapter->pcard_sd->mp_end_port >> 1)); + PRINTM(MCMND, "end port %d, data port mask %x\n", + wlan_le16_to_cpu( + resp->params.tx_buf.mp_end_port), + pmadapter->pcard_sd->mp_data_port_mask); + } +#endif + pmadapter->curr_tx_buf_size = pmadapter->tx_buf_size; + PRINTM(MCMND, "max_tx_buf_size=%d, tx_buf_size=%d\n", + pmadapter->max_tx_buf_size, pmadapter->tx_buf_size); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + ret = wlan_ret_amsdu_aggr_ctrl(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_GET_STATUS: + ret = wlan_ret_wmm_get_status( + pmpriv, resp->params.get_wmm_status.queue_status_tlv, + resp->size - S_DS_GEN); + break; + case HostCmd_CMD_WMM_ADDTS_REQ: + ret = wlan_ret_wmm_addts_req(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_DELTS_REQ: + ret = wlan_ret_wmm_delts_req(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_QUEUE_CONFIG: + ret = wlan_ret_wmm_queue_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_QUEUE_STATS: + ret = wlan_ret_wmm_queue_stats(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_TS_STATUS: + ret = wlan_ret_wmm_ts_status(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_PARAM_CONFIG: + ret = wlan_ret_wmm_param_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + ret = wlan_ret_ibss_coalescing_status(pmpriv, resp); + break; + case HostCmd_CMD_MGMT_IE_LIST: + ret = wlan_ret_mgmt_ie_list(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_11N_CFG: + ret = wlan_ret_11n_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_11AC_CFG: + ret = wlan_ret_11ac_cfg(pmpriv, resp, pioctl_buf); + break; +#if 0 + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + pmadapter->tx_buf_size = (t_u16)wlan_le16_to_cpu(resp->params. + tx_buf.buff_size); + break; +#endif + case HostCmd_CMD_TX_BF_CFG: + ret = wlan_ret_tx_bf_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG: + ret = wlan_ret_sysclock_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_TARGET_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + case HostCmd_CMD_BCA_REG_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; +#if defined(SDIO) + case HostCmd_CMD_SDIO_GPIO_INT_CONFIG: + break; +#endif + 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; +#if defined(PCIE) +#if defined(PCIE8997) || defined(PCIE8897) + case HostCmd_CMD_PCIE_HOST_BUF_DETAILS: + PRINTM(MINFO, "PCIE host buffer configuration successful.\n"); + break; +#endif +#endif + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_ret_remain_on_channel(pmpriv, resp, pioctl_buf); + break; +#ifdef WIFI_DIRECT_SUPPORT + case HOST_CMD_WIFI_DIRECT_MODE_CONFIG: + ret = wlan_ret_wifi_direct_mode(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + ret = wlan_ret_subscribe_event(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_OTP_READ_USER_DATA: + ret = wlan_ret_otp_user_data(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = wlan_ret_hs_wakeup_reason(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_REJECT_ADDBA_REQ: + ret = wlan_ret_reject_addba_req(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_PACKET_AGGR_CTRL: + ret = wlan_ret_packet_aggr_ctrl(pmpriv, resp, pioctl_buf); + break; +#ifdef USB + case HostCmd_CMD_PACKET_AGGR_OVER_HOST_INTERFACE: + ret = wlan_ret_packet_aggr_over_host_interface(pmpriv, resp, + pioctl_buf); + break; +#endif +#ifdef RX_PACKET_COALESCE + case HostCmd_CMD_RX_PKT_COALESCE_CFG: + ret = wlan_ret_rx_pkt_coalesce_cfg(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCMD_CONFIG_LOW_POWER_MODE: + break; + case HostCmd_DFS_REPEATER_MODE: + ret = wlan_ret_dfs_repeater_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_COALESCE_CFG: + ret = wlan_ret_coalesce_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MEF_CFG: + break; + case HostCmd_DS_GET_SENSOR_TEMP: + ret = wlan_ret_get_sensor_temp(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_MIMO_SWITCH: + break; + case HostCmd_CMD_STA_CONFIGURE: + ret = wlan_ret_sta_config(pmpriv, resp, pioctl_buf); + break; + case HOST_CMD_PMIC_CONFIGURE: + break; + case HostCmd_CMD_INDEPENDENT_RESET_CFG: + ret = wlan_ret_ind_rst_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT: + break; + case HostCmd_CMD_GET_TSF: + ret = wlan_ret_get_tsf(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_CHAN_REGION_CFG: + ret = wlan_ret_chan_region_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_AUTO_TX: + ret = wlan_ret_auto_tx(pmpriv, resp, pioctl_buf); + break; + case HOST_CMD_TX_RX_PKT_STATS: + ret = wlan_ret_tx_rx_pkt_stats(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_DYN_BW: + ret = wlan_ret_dyn_bw(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_BOOT_SLEEP: + ret = wlan_ret_boot_sleep(pmpriv, resp, pioctl_buf); + break; +#if defined(DRV_EMBEDDED_SUPPLICANT) + case HostCmd_CMD_CRYPTO: + ret = wlan_ret_crypto(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCmd_CMD_11AX_CFG: + ret = wlan_ret_11ax_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_11AX_CMD: + ret = wlan_ret_11ax_cmd(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RANGE_EXT: + ret = wlan_ret_range_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TWT_CFG: + break; + case HostCmd_CMD_RX_ABORT_CFG: + ret = wlan_ret_rxabortcfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RX_ABORT_CFG_EXT: + ret = wlan_ret_rxabortcfg_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_ARB_CONFIG: + ret = wlan_ret_arb_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TX_AMPDU_PROT_MODE: + ret = wlan_ret_tx_ampdu_prot_mode(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_DOT11MC_UNASSOC_FTM_CFG: + ret = wlan_ret_dot11mc_unassoc_ftm_cfg(pmpriv, resp, + pioctl_buf); + break; + case HostCmd_CMD_RATE_ADAPT_CFG: + ret = wlan_ret_rate_adapt_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_CCK_DESENSE_CFG: + ret = wlan_ret_cck_desense_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CHANNEL_TRPC_CONFIG: + ret = wlan_ret_get_chan_trpc_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_LOW_POWER_MODE_CFG: + ret = wlan_ret_set_get_low_power_mode_cfg(pmpriv, resp, + pioctl_buf); + break; + case HostCmd_CMD_MFG_COMMAND: + ret = wlan_ret_mfg(pmpriv, resp, pioctl_buf); + break; + default: + PRINTM(MERROR, "CMD_RESP: Unknown command response %#x\n", + resp->command); + break; + } + + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_sta_event.c b/mxm_wifiex/wlan_src/mlan/mlan_sta_event.c new file mode 100644 index 0000000..cc96e9a --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_sta_event.c @@ -0,0 +1,1026 @@ +/** @file mlan_sta_event.c + * + * @brief This file contains MLAN event handling. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/13/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_wmm.h" +#include "mlan_11n.h" +#include "mlan_11h.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif +#ifdef PCIE +#include "mlan_pcie.h" +#endif /* PCIE */ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function handles link lost, deauth and + * disassoc events. + * + * @param pmpriv A pointer to mlan_private structure + * @return N/A + */ +static t_void wlan_handle_disconnect_event(pmlan_private pmpriv) +{ + ENTER(); + + if (pmpriv->media_connected == MTRUE) + wlan_reset_connect_state(pmpriv, MTRUE); + + LEAVE(); +} + +/******************************************************** + 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; + t_u8 event_buf[100]; + mlan_event *pevent = (mlan_event *)event_buf; + + ENTER(); + + PRINTM(MINFO, "Handles disconnect event.\n"); + +#ifdef UAP_SUPPORT + /* If DFS repeater mode is enabled and station interface disconnects + * then make sure that all uAPs are stopped. + */ + if (pmadapter->dfs_repeater) + wlan_dfs_rep_disconnect(pmadapter); +#endif + + if (drv_disconnect) { + priv->media_connected = MFALSE; + pmadapter->state_rdh.tx_block = MFALSE; + wlan_11h_check_update_radar_det_state(priv); + } + + if (priv->port_ctrl_mode == MTRUE) { + /* Close the port on Disconnect */ + PRINTM(MINFO, "DISC: port_status = CLOSED\n"); + priv->port_open = MFALSE; + } + memset(pmadapter, &priv->gtk_rekey, 0, + sizeof(mlan_ds_misc_gtk_rekey_data)); + priv->tx_pause = MFALSE; + pmadapter->scan_block = MFALSE; + + /* Reset SNR/NF/RSSI values */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->rxpd_rate = 0; + priv->rxpd_rate_info = 0; + priv->max_amsdu = 0; + priv->amsdu_disable = MFALSE; + wlan_coex_ampdu_rxwinsize(pmadapter); + + priv->sec_info.ewpa_enabled = MFALSE; + priv->sec_info.wpa_enabled = MFALSE; + priv->sec_info.wpa2_enabled = MFALSE; + priv->wpa_ie_len = 0; +#ifdef DRV_EMBEDDED_SUPPLICANT + supplicantStopSessionTimer(priv->psapriv); + supplicantClrEncryptKey(priv->psapriv); + supplicantDisable(priv->psapriv); +#endif + + priv->sec_info.wapi_enabled = MFALSE; + priv->wapi_ie_len = 0; + priv->sec_info.wapi_key_on = MFALSE; + + priv->wps.session_enable = MFALSE; + memset(priv->adapter, (t_u8 *)&priv->wps.wps_ie, 0x00, + sizeof(priv->wps.wps_ie)); + priv->sec_info.osen_enabled = MFALSE; + priv->osen_ie_len = 0; + + 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 (priv->bss_type == MLAN_BSS_TYPE_STA) + pmadapter->hs_wake_interval = 0; + + if ((wlan_fw_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; + } + + pevent->bss_index = priv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_DISCONNECTED; + pevent->event_len = sizeof(priv->disconnect_reason_code); + memcpy_ext(priv->adapter, pevent->event_buf, + &priv->disconnect_reason_code, pevent->event_len, + pevent->event_len); + wlan_recv_event(priv, MLAN_EVENT_ID_FW_DISCONNECTED, pevent); + priv->disconnect_reason_code = 0; + + 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; + pevent->event_len = ele_len; + /* Copy OBSS scan parameters */ + memcpy_ext(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); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM, + pevent); + } + + LEAVE(); +} + +/** + * @brief This function will process tx pause event + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * + * @return N/A + */ +static void wlan_process_sta_tx_pause_event(pmlan_private priv, + pmlan_buffer pevent) +{ + t_u16 tlv_type, tlv_len; + int tlv_buf_left = pevent->data_len - sizeof(t_u32); + MrvlIEtypesHeader_t *tlv = + (MrvlIEtypesHeader_t *)(pevent->pbuf + pevent->data_offset + + sizeof(t_u32)); + MrvlIEtypes_tx_pause_t *tx_pause_tlv; + t_u8 *bssid = MNULL; + ENTER(); + if (priv->media_connected) + bssid = priv->curr_bss_params.bss_descriptor.mac_address; + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_TX_PAUSE) { + tx_pause_tlv = (MrvlIEtypes_tx_pause_t *)tlv; + PRINTM(MCMND, "TxPause: " MACSTR " pause=%d, pkts=%d\n", + MAC2STR(tx_pause_tlv->peermac), + tx_pause_tlv->tx_pause, tx_pause_tlv->pkt_cnt); + + if (bssid && + !memcmp(priv->adapter, bssid, tx_pause_tlv->peermac, + MLAN_MAC_ADDR_LENGTH)) { + if (tx_pause_tlv->tx_pause) + priv->tx_pause = MTRUE; + else + priv->tx_pause = MFALSE; + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + + LEAVE(); + return; +} + +/** + * @brief This function will print diconnect reason code according + * to IEEE 802.11 spec + * + * @param reason_code reason code for the deauth/disaccoc + * received from firmware + * @return N/A + */ +static void wlan_print_disconnect_reason(t_u16 reason_code) +{ + ENTER(); + + switch (reason_code) { + case MLAN_REASON_UNSPECIFIED: + PRINTM(MMSG, "wlan: REASON: Unspecified reason\n"); + break; + case MLAN_REASON_PREV_AUTH_NOT_VALID: + PRINTM(MMSG, + "wlan: REASON: Previous authentication no longer valid\n"); + break; + case MLAN_REASON_DEAUTH_LEAVING: + PRINTM(MMSG, + "wlan: REASON: (Deauth) Sending STA is leaving (or has left) IBSS or ESS\n"); + break; + case MLAN_REASON_DISASSOC_DUE_TO_INACTIVITY: + PRINTM(MMSG, + "wlan: REASON: Disassociated due to inactivity \n"); + break; + case MLAN_REASON_DISASSOC_AP_BUSY: + PRINTM(MMSG, + "wlan: REASON: (Disassociated) AP unable to handle all connected STAs\n"); + break; + case MLAN_REASON_CLASS2_FRAME_FROM_NOAUTH_STA: + PRINTM(MMSG, + "wlan: REASON: Class 2 frame was received from nonauthenticated STA\n"); + break; + case MLAN_REASON_CLASS3_FRAME_FROM_NOASSOC_STA: + PRINTM(MMSG, + "wlan: REASON: Class 3 frame was received from nonassociated STA\n"); + break; + case MLAN_REASON_DISASSOC_STA_HAS_LEFT: + PRINTM(MMSG, + "wlan: REASON: (Disassocated) Sending STA is leaving (or has left) BSS\n"); + break; + case MLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH: + PRINTM(MMSG, + "wlan: REASON: STA requesting (re)assoc is not authenticated with responding STA\n"); + break; + default: + break; + } + + LEAVE(); + return; +} + +/** + * @brief This function handles events generated by firmware + * + * @param priv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ops_sta_process_event(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 = MNULL; + t_u8 *evt_buf = MNULL; + pmlan_buffer pmbuf = pmadapter->pmlan_buffer_event; + t_u16 reason_code; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_event *pevent = MNULL; + chan_band_info *pchan_band_info = MNULL; + t_u8 radar_chan; + t_u16 enable = 0; + + ENTER(); + + if (!pmbuf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Event length check */ + if ((pmbuf->data_len - sizeof(eventcause)) > MAX_EVENT_SIZE) { + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MAX_EVENT_SIZE + sizeof(mlan_event), + 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, event_buf, 0, MAX_EVENT_SIZE); + + if (eventcause != EVENT_PS_SLEEP && eventcause != EVENT_PS_AWAKE && + pmbuf->data_len > sizeof(eventcause)) + DBG_HEXDUMP(MEVT_D, "EVENT", pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + + switch (eventcause) { + case EVENT_DUMMY_HOST_WAKEUP_SIGNAL: + PRINTM(MERROR, + "Invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL, ignoring it\n"); + break; + case EVENT_LINK_SENSED: + PRINTM(MEVENT, "EVENT: LINK_SENSED\n"); + pmpriv->adhoc_is_link_sensed = MTRUE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED, + MNULL); + break; + + case EVENT_DEAUTHENTICATED: + if (pmpriv->wps.session_enable) { + PRINTM(MMSG, + "wlan: Receive deauth event in wps session\n"); + break; + } + reason_code = wlan_le16_to_cpu(*(t_u16 *)(pmbuf->pbuf + + pmbuf->data_offset + + sizeof(eventcause))); + PRINTM(MMSG, "wlan: EVENT: Deauthenticated (reason 0x%x)\n", + reason_code); + wlan_print_disconnect_reason(reason_code); + pmpriv->disconnect_reason_code = reason_code; + pmadapter->dbg.num_event_deauth++; + wlan_handle_disconnect_event(pmpriv); + + break; + + case EVENT_DISASSOCIATED: + if (pmpriv->wps.session_enable) { + PRINTM(MMSG, + "wlan: Receive disassociate event in wps session\n"); + break; + } + reason_code = wlan_le16_to_cpu(*(t_u16 *)(pmbuf->pbuf + + pmbuf->data_offset + + sizeof(eventcause))); + PRINTM(MMSG, "wlan: EVENT: Disassociated (reason 0x%x)\n", + reason_code); + wlan_print_disconnect_reason(reason_code); + pmpriv->disconnect_reason_code = reason_code; + pmadapter->dbg.num_event_disassoc++; + wlan_handle_disconnect_event(pmpriv); + break; + + case EVENT_LINK_LOST: + reason_code = wlan_le16_to_cpu(*(t_u16 *)(pmbuf->pbuf + + pmbuf->data_offset + + sizeof(eventcause))); + PRINTM(MMSG, "wlan: EVENT: Link lost (reason 0x%x)\n", + reason_code); + pmpriv->disconnect_reason_code = reason_code; + pmadapter->dbg.num_event_link_lost++; + wlan_handle_disconnect_event(pmpriv); + break; + + case EVENT_PS_SLEEP: + PRINTM(MINFO, "EVENT: SLEEP\n"); + PRINTM_NETINTF(MEVENT, pmpriv); + 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_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "|"); + if (!pmadapter->pps_uapsd_mode && pmpriv->media_connected && + (pmpriv->port_open || !pmpriv->port_ctrl_mode) && + pmadapter->sleep_period.period) { + pmadapter->pps_uapsd_mode = MTRUE; + PRINTM(MEVENT, "PPS/UAPSD mode activated\n"); + } + /* Handle unexpected PS AWAKE event */ + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) + break; + pmadapter->tx_lock_flag = MFALSE; + if (pmadapter->pps_uapsd_mode && pmadapter->gen_null_pkt) { + if (MTRUE == + wlan_check_last_packet_indication(pmpriv)) { + if (!pmadapter->data_sent) { + if (wlan_send_null_packet( + pmpriv, + MRVDRV_TxPD_POWER_MGMT_NULL_PACKET | + MRVDRV_TxPD_POWER_MGMT_LAST_PACKET) == + MLAN_STATUS_SUCCESS) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + } + } + pmadapter->ps_state = PS_STATE_AWAKE; + pmadapter->pm_wakeup_card_req = MFALSE; + pmadapter->pm_wakeup_fw_try = MFALSE; + break; + + case EVENT_HS_ACT_REQ: + PRINTM(MEVENT, "EVENT: HS_ACT_REQ\n"); + ret = wlan_prepare_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH, 0, + 0, MNULL, MNULL); + break; + case EVENT_MIC_ERR_UNICAST: + PRINTM(MEVENT, "EVENT: UNICAST MIC ERROR\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_MIC_ERR_UNI, MNULL); + break; + + case EVENT_MIC_ERR_MULTICAST: + PRINTM(MEVENT, "EVENT: MULTICAST MIC ERROR\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_MIC_ERR_MUL, MNULL); + break; + case EVENT_MIB_CHANGED: + case EVENT_INIT_DONE: + break; + + case EVENT_ADHOC_BCN_LOST: + PRINTM(MEVENT, "EVENT: ADHOC_BCN_LOST\n"); + pmpriv->adhoc_is_link_sensed = MFALSE; + wlan_clean_txrx(pmpriv); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_ADHOC_LINK_LOST, + MNULL); + break; + + case EVENT_FW_DEBUG_INFO: + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_DEBUG_INFO; + pevent->event_len = pmbuf->data_len - sizeof(eventcause); + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause), + pevent->event_len, pevent->event_len); + PRINTM(MEVENT, "EVENT: FW Debug Info %s\n", + (t_u8 *)pevent->event_buf); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + break; + + case EVENT_BG_SCAN_REPORT: + PRINTM(MEVENT, "EVENT: BGS_REPORT\n"); + pmadapter->bgscan_reported = MTRUE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BG_SCAN, MNULL); + break; + case EVENT_BG_SCAN_STOPPED: + PRINTM(MEVENT, "EVENT: BGS_STOPPED\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BG_SCAN_STOPPED, + MNULL); + break; + + case EVENT_PORT_RELEASE: + PRINTM(MEVENT, "EVENT: PORT RELEASE\n"); + /* Open the port for e-supp mode */ + if (pmpriv->port_ctrl_mode == MTRUE) { + PRINTM(MINFO, "PORT_REL: port_status = OPEN\n"); + pmpriv->port_open = MTRUE; + } + pmadapter->scan_block = MFALSE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_PORT_RELEASE, MNULL); + break; + + case EVENT_STOP_TX: + PRINTM(MEVENT, "EVENT: Stop Tx (%#x)\n", eventcause); + wlan_11h_tx_disable(pmpriv); /* this fn will send event up to + MOAL */ + break; + case EVENT_START_TX: + PRINTM(MEVENT, "EVENT: Start Tx (%#x)\n", eventcause); + wlan_11h_tx_enable(pmpriv); /* this fn will send event up to + MOAL */ + break; + case EVENT_CHANNEL_SWITCH: + PRINTM(MEVENT, "EVENT: Channel Switch (%#x)\n", eventcause); + if (pmadapter->ecsa_enable) { + MrvlIEtypes_channel_band_t *pchan_info = + (MrvlIEtypes_channel_band_t + *)(pmadapter->event_body); + t_u8 channel = pchan_info->channel; + chan_freq_power_t *cfp = MNULL; + DBG_HEXDUMP(MCMD_D, "chan band config", + (t_u8 *)pchan_info, + sizeof(MrvlIEtypes_channel_band_t)); + PRINTM(MEVENT, "Switch to channel %d success!\n", + channel); +#define MAX_CHANNEL_BAND_B 14 + if (channel <= MAX_CHANNEL_BAND_B) + cfp = wlan_find_cfp_by_band_and_channel( + pmadapter, BAND_B, channel); + else + cfp = wlan_find_cfp_by_band_and_channel( + pmadapter, BAND_A, channel); + pmpriv->curr_bss_params.bss_descriptor.channel = + channel; + if (cfp) + pmpriv->curr_bss_params.bss_descriptor.freq = + cfp->freq; + else + pmpriv->curr_bss_params.bss_descriptor.freq = 0; + if (pmpriv->adapter->state_rdh.stage == + RDH_SET_CUSTOM_IE) { + pmadapter->state_rdh.stage = + RDH_RESTART_TRAFFIC; + wlan_11h_radar_detected_handling(pmadapter, + pmpriv); + } + pmadapter->state_rdh.tx_block = MFALSE; + /* Setup event buffer */ + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE; + pevent->event_len = sizeof(chan_band_info); + pchan_band_info = (chan_band_info *)pevent->event_buf; + /* Copy event data */ + memcpy_ext(pmadapter, (t_u8 *)&pchan_band_info->bandcfg, + (t_u8 *)&pchan_info->bandcfg, + sizeof(pchan_info->bandcfg), + sizeof(pchan_band_info->bandcfg)); + pchan_band_info->channel = pchan_info->channel; + if (pchan_band_info->bandcfg.chanWidth == CHAN_BW_80MHZ) + pchan_band_info->center_chan = + wlan_get_center_freq_idx( + priv, BAND_AAC, + pchan_info->channel, + CHANNEL_BW_80MHZ); + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE, + pevent); + } + break; + case EVENT_CHANNEL_SWITCH_ANN: + PRINTM_NETINTF(MEVENT, pmpriv); + 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_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "EVENT: Radar Detected\n"); + + /* Send as passthru first, this event can cause other events */ + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + + if (pmadapter->state_rdh.stage == RDH_OFF) { + pmadapter->state_rdh.stage = RDH_CHK_INTFS; + wlan_11h_radar_detected_handling(pmadapter, pmpriv); + } else { + PRINTM(MEVENT, "Ignore Event Radar Detected - handling" + " already in progress.\n"); + } + + break; + + case EVENT_CHANNEL_REPORT_RDY: + PRINTM_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "EVENT: Channel Report Ready\n"); + 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_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause), + pevent->event_len, pevent->event_len); + /* Handle / pass event data */ + ret = wlan_11h_handle_event_chanrpt_ready(pmpriv, pevent, + &radar_chan); + /* Also send this event as passthru */ + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + /* Send up this Event to unblock MOAL waitqueue */ + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_MEAS_REPORT, MNULL); + break; + case EVENT_EXT_SCAN_REPORT: + PRINTM(MEVENT, "EVENT: EXT_SCAN Report (%d)\n", + pmbuf->data_len); + if (pmadapter->pscan_ioctl_req && pmadapter->ext_scan) + ret = wlan_handle_event_ext_scan_report(priv, pmbuf); + break; + case EVENT_EXT_SCAN_STATUS_REPORT: + PRINTM(MEVENT, "EVENT: EXT_SCAN status report (%d)\n", + pmbuf->data_len); + pmadapter->ext_scan_timeout = MFALSE; + ret = wlan_handle_event_ext_scan_status(priv, pmbuf); + break; + case EVENT_MEAS_REPORT_RDY: + PRINTM(MEVENT, "EVENT: Measurement Report Ready (%#x)\n", + eventcause); + ret = wlan_prepare_cmd(priv, HostCmd_CMD_MEASUREMENT_REPORT, + HostCmd_ACT_GEN_SET, 0, 0, MNULL); + break; + case EVENT_WMM_STATUS_CHANGE: + if (pmbuf && + pmbuf->data_len > + sizeof(eventcause) + sizeof(MrvlIEtypesHeader_t)) { + PRINTM(MEVENT, "EVENT: WMM status changed: %d\n", + pmbuf->data_len); + + evt_buf = (pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause)); + + wlan_ret_wmm_get_status(pmpriv, evt_buf, + pmbuf->data_len - + sizeof(eventcause)); + } else { + PRINTM(MEVENT, "EVENT: WMM status changed\n"); + ret = wlan_cmd_wmm_status_change(pmpriv); + } + break; + + case EVENT_RSSI_LOW: + PRINTM(MEVENT, "EVENT: Beacon RSSI_LOW\n"); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_BCN_RSSI_LOW; + pevent->event_len = sizeof(t_u16); + /** Fw send bcnRssi low value in event reason field*/ + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + (t_u8 *)&pmadapter->event_body, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + 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"); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_BCN_RSSI_HIGH; + pevent->event_len = sizeof(t_u16); + /** Fw send bcnRssi high value in event reason field*/ + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + (t_u8 *)&pmadapter->event_body, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + break; + case EVENT_SNR_HIGH: + PRINTM(MEVENT, "EVENT: Beacon SNR_HIGH\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BCN_SNR_HIGH, MNULL); + break; + case EVENT_DATA_RSSI_LOW: + PRINTM(MEVENT, "EVENT: Data RSSI_LOW\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_DATA_RSSI_LOW, MNULL); + break; + case EVENT_DATA_SNR_LOW: + PRINTM(MEVENT, "EVENT: Data SNR_LOW\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_DATA_SNR_LOW, MNULL); + break; + case EVENT_DATA_RSSI_HIGH: + PRINTM(MEVENT, "EVENT: Data RSSI_HIGH\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_DATA_RSSI_HIGH, MNULL); + break; + case EVENT_DATA_SNR_HIGH: + PRINTM(MEVENT, "EVENT: Data SNR_HIGH\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_DATA_SNR_HIGH, MNULL); + break; + case EVENT_LINK_QUALITY: + PRINTM(MEVENT, "EVENT: Link Quality\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_LINK_QUALITY, MNULL); + break; + case EVENT_PRE_BEACON_LOST: + PRINTM(MEVENT, "EVENT: Pre-Beacon Lost\n"); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_PRE_BCN_LOST, MNULL); + break; + case EVENT_IBSS_COALESCED: + PRINTM(MEVENT, "EVENT: IBSS_COALESCED\n"); + ret = wlan_prepare_cmd( + pmpriv, HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + HostCmd_ACT_GEN_GET, 0, MNULL, MNULL); + break; + case EVENT_ADDBA: + PRINTM(MEVENT, "EVENT: ADDBA Request\n"); + if (pmpriv->media_connected == MTRUE) + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, MNULL, + pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore ADDBA Request event in disconnected state\n"); + break; + case EVENT_DELBA: + PRINTM(MEVENT, "EVENT: DELBA Request\n"); + if (pmpriv->media_connected == MTRUE) + wlan_11n_delete_bastream(pmpriv, pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore DELBA Request event in disconnected state\n"); + break; + case EVENT_BA_STREAM_TIMEOUT: + PRINTM(MEVENT, "EVENT: BA Stream timeout\n"); + if (pmpriv->media_connected == MTRUE) + wlan_11n_ba_stream_timeout( + pmpriv, (HostCmd_DS_11N_BATIMEOUT *) + pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore BA Stream timeout event in disconnected state\n"); + break; + case EVENT_RXBA_SYNC: + PRINTM(MEVENT, "EVENT: RXBA_SYNC\n"); + wlan_11n_rxba_sync_event(pmpriv, pmadapter->event_body, + pmbuf->data_len - sizeof(eventcause)); + break; + case EVENT_AMSDU_AGGR_CTRL: + PRINTM(MEVENT, "EVENT: AMSDU_AGGR_CTRL %d\n", + *(t_u16 *)pmadapter->event_body); + pmadapter->tx_buf_size = + MIN(pmadapter->curr_tx_buf_size, + wlan_le16_to_cpu(*(t_u16 *)pmadapter->event_body)); + if (pmbuf->data_len == sizeof(eventcause) + sizeof(t_u32)) { + enable = wlan_le16_to_cpu( + *(t_u16 *)(pmadapter->event_body + + sizeof(t_u16))); + if (enable) + pmpriv->amsdu_disable = MFALSE; + else + pmpriv->amsdu_disable = MTRUE; + PRINTM(MEVENT, "amsdu_disable=%d\n", + pmpriv->amsdu_disable); + } + 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_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmadapter->event_body, pevent->event_len, + 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_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmadapter->event_body, pevent->event_len, + pevent->event_len); +#ifdef UAP_SUPPORT + if (pmadapter->dfs_repeater) + wlan_dfs_rep_bw_change(pmadapter); +#endif + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_BW_CHANGED, pevent); + break; + +#ifdef WIFI_DIRECT_SUPPORT + case EVENT_WIFIDIRECT_GENERIC_EVENT: + PRINTM(MEVENT, "EVENT: WIFIDIRECT event %d\n", eventcause); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + break; + case EVENT_WIFIDIRECT_SERVICE_DISCOVERY: + PRINTM(MEVENT, "EVENT: WIFIDIRECT service discovery event %d\n", + eventcause); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + pevent->event_len = pmbuf->data_len; + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + break; +#endif /* WIFI_DIRECT_SUPPORT */ + case EVENT_REMAIN_ON_CHANNEL_EXPIRED: + PRINTM_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "EVENT: REMAIN_ON_CHANNEL_EXPIRED reason=%d\n", + *(t_u16 *)pmadapter->event_body); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_FLUSH_RX_WORK, MNULL); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED, + MNULL); + break; + + case EVENT_TX_DATA_PAUSE: + PRINTM(MEVENT, "EVENT: TX_DATA_PAUSE\n"); + wlan_process_sta_tx_pause_event(priv, pmbuf); + break; + + case EVENT_IBSS_STATION_CONNECT: + break; + case EVENT_IBSS_STATION_DISCONNECT: + break; + case EVENT_SAD_REPORT: { +#ifdef DEBUG_LEVEL1 + t_u8 *pevt_dat = + pmbuf->pbuf + pmbuf->data_offset + sizeof(t_u32); +#endif + PRINTM(MEVENT, + "EVENT: Antenna Diversity %d (%d, %d, %d, %d)\n", + eventcause, pevt_dat[0] + 1, pevt_dat[1] + 1, + pevt_dat[2], pevt_dat[3]); + } break; + + case EVENT_FW_DUMP_INFO: + PRINTM(MINFO, "EVENT: Dump FW info\n"); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_DUMP_INFO; + pevent->event_len = pmbuf->data_len; + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + break; + case EVENT_TX_STATUS_REPORT: + PRINTM(MINFO, "EVENT: TX_STATUS\n"); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_TX_STATUS; + pevent->event_len = pmbuf->data_len; + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len, + pevent->event_len); + + wlan_recv_event(pmpriv, pevent->event_id, pevent); + break; + case EVENT_BT_COEX_WLAN_PARA_CHANGE: + PRINTM(MEVENT, "EVENT: BT coex wlan param update\n"); + wlan_bt_coex_wlan_param_update_event(pmpriv, pmbuf); + break; + +#if defined(PCIE) + case EVENT_SSU_DUMP_DMA: + PRINTM(MEVENT, "EVENT: EVENT_SSU_DUMP_DMA\n"); + if (!pmadapter->ssu_buf || !pmadapter->ssu_buf->pbuf) + break; + + /* If ADMA is supported, SSU header could not be received with + * SSU data. Instead, SSU header is received through this event. + * So, copy the header into the buffer before passing the buffer + * to upper layer for file writting + */ + memcpy_ext(pmadapter, + (t_u8 *)pmadapter->ssu_buf->pbuf + + pmadapter->ssu_buf->data_offset, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause), + (pmbuf->data_len - sizeof(eventcause)), + (pmbuf->data_len - sizeof(eventcause))); + + DBG_HEXDUMP(MEVT_D, "SSU data", + (t_u8 *)pmadapter->ssu_buf->pbuf + + pmadapter->ssu_buf->data_offset, + 512); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_SSU_DUMP_FILE; + pevent->event_len = MLAN_SSU_BUF_SIZE; + *(t_ptr *)pevent->event_buf = (t_ptr)pmadapter->ssu_buf->pbuf + + pmadapter->ssu_buf->data_offset; + wlan_recv_event(pmpriv, pevent->event_id, pevent); + wlan_free_ssu_pcie_buf(pmadapter); + break; +#endif + case EVENT_MEF_HOST_WAKEUP: + PRINTM(MEVENT, "EVENT: EVENT_MEF_HOST_WAKEUP len=%d\n", + pmbuf->data_len); + break; + case EVENT_MANAGEMENT_FRAME_WAKEUP: + PRINTM(MEVENT, "EVENT: EVENT_MANAGEMENT_FRAME_WAKEUP HOST\n"); + break; + case EVENT_CLOUD_KEEP_ALIVE_RETRY_FAIL: + break; + case EVENT_VDLL_IND: + wlan_process_vdll_event(pmpriv, pmbuf); + break; + case EVENT_FW_HANG_REPORT: + if (pmbuf->data_len < (sizeof(eventcause) + sizeof(t_u16))) { + PRINTM(MEVENT, + "EVENT: EVENT_FW_HANG_REPORT skip for len too short: %d\n", + pmbuf->data_len); + break; + } + PRINTM(MEVENT, "EVENT: EVENT_FW_HANG_REPORT reasoncode=%d\n", + wlan_le16_to_cpu(*(t_u16 *)(pmbuf->pbuf + + pmbuf->data_offset + + sizeof(eventcause)))); + pmadapter->fw_hang_report = MTRUE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DBG_DUMP, MNULL); + break; + default: + PRINTM(MEVENT, "EVENT: unknown event id: %#x\n", eventcause); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_FW_UNKNOWN, MNULL); + break; + } +done: + if (event_buf) + pcb->moal_mfree(pmadapter->pmoal_handle, event_buf); + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_sta_ioctl.c b/mxm_wifiex/wlan_src/mlan/mlan_sta_ioctl.c new file mode 100644 index 0000000..c565144 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_sta_ioctl.c @@ -0,0 +1,5615 @@ +/** @file mlan_sta_ioctl.c + * + * @brief This file contains the functions for station ioctl. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11ac.h" +#include "mlan_11ax.h" +#include "mlan_11h.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(mlan_ds_get_signal)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_get_signal); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Signal info can be obtained only if connected */ + if (pmpriv->media_connected == MFALSE) { + PRINTM(MINFO, "Can not get signal in disconnected state\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get signal information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_get_info_signal_ext(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_get_info *info = MNULL; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + info = (mlan_ds_get_info *)pioctl_req->pbuf; + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(mlan_ds_get_info)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_get_info); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_RSSI_INFO_EXT, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + (t_void *)info); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get statistics information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_get_info_stats(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(mlan_ds_get_stats)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_get_stats); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_GET_LOG, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get sta channel information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_bss_ioctl_get_chan_info(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(chan_band_info)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(chan_band_info); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_STA_CONFIGURE, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get BSS information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_get_info_bss_info(pmlan_adapter pmadapter, + 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_ext(pmadapter, &info->param.bss_info.ssid, &pbss_desc->ssid, + sizeof(mlan_802_11_ssid), sizeof(mlan_802_11_ssid)); + + /* BSSID */ + memcpy_ext(pmadapter, &info->param.bss_info.bssid, + &pbss_desc->mac_address, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + + /* Channel */ + info->param.bss_info.bss_chan = pbss_desc->channel; + + /* Beacon interval */ + info->param.bss_info.beacon_interval = pbss_desc->beacon_period; + + /* Band */ + info->param.bss_info.bss_band = (t_u8)pbss_desc->bss_band; + + /* Region code */ + info->param.bss_info.region_code = pmadapter->region_code; + + /* Scan table index if connected */ + info->param.bss_info.scan_table_idx = 0; + info->param.bss_info.scan_block = pmadapter->scan_block; + if (pmpriv->media_connected == MTRUE) { + tbl_idx = wlan_find_ssid_in_list(pmpriv, &pbss_desc->ssid, + pbss_desc->mac_address, + pmpriv->bss_mode); + if (tbl_idx >= 0) + info->param.bss_info.scan_table_idx = tbl_idx; + } + + /* Connection status */ + info->param.bss_info.media_connected = pmpriv->media_connected; + + /* Radio status */ + info->param.bss_info.radio_on = pmadapter->radio_on; + + /* Tx power information */ + info->param.bss_info.max_power_level = pmpriv->max_tx_power_level; + info->param.bss_info.min_power_level = pmpriv->min_tx_power_level; + + /* AdHoc state */ + info->param.bss_info.adhoc_state = pmpriv->adhoc_state; + + /* Last beacon NF */ + info->param.bss_info.bcn_nf_last = pmpriv->bcn_nf_last; + + /* wep status */ + if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled) + info->param.bss_info.wep_status = MTRUE; + else + info->param.bss_info.wep_status = MFALSE; + + info->param.bss_info.is_hs_configured = pmadapter->is_hs_configured; + info->param.bss_info.is_deep_sleep = pmadapter->is_deep_sleep; + + /* Capability Info */ + info->param.bss_info.capability_info = 0; + memcpy_ext(pmadapter, &info->param.bss_info.capability_info, + &pbss_desc->cap_info, + sizeof(info->param.bss_info.capability_info), + sizeof(info->param.bss_info.capability_info)); + + memset(pmadapter, &info->param.bss_info.ext_cap, 0, sizeof(ExtCap_t)); + if (pbss_desc->pext_cap) { + memcpy_ext(pmadapter, &info->param.bss_info.ext_cap, + (t_u8 *)pbss_desc->pext_cap + + sizeof(IEEEtypes_Header_t), + pbss_desc->pext_cap->ieee_hdr.len, + sizeof(info->param.bss_info.ext_cap)); + } + + /* 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_ext(pmadapter, info->param.bss_info.peer_supp_rates, + pbss_desc->supported_rates, + sizeof(pbss_desc->supported_rates), + sizeof(info->param.bss_info.peer_supp_rates)); + if (pbss_desc->pmd_ie) { + info->param.bss_info.mdid = pbss_desc->pmd_ie->mdid; + info->param.bss_info.ft_cap = pbss_desc->pmd_ie->ft_cap; + } + pioctl_req->data_read_written = + sizeof(mlan_bss_info) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return ret; +} + +/** + * @brief Get information handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_get_info_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_get_info *pget_info = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + pget_info = (mlan_ds_get_info *)pioctl_req->pbuf; + + switch (pget_info->sub_command) { + case MLAN_OID_GET_STATS: + status = wlan_get_info_stats(pmadapter, pioctl_req); + break; + case MLAN_OID_GET_SIGNAL: + status = wlan_get_info_signal(pmadapter, pioctl_req); + break; + case MLAN_OID_GET_SIGNAL_EXT: + status = wlan_get_info_signal_ext(pmadapter, pioctl_req); + break; + case MLAN_OID_GET_FW_INFO: + pioctl_req->data_read_written = + sizeof(mlan_fw_info) + MLAN_SUB_COMMAND_SIZE; + pget_info->param.fw_info.fw_ver = pmadapter->fw_release_number; + memcpy_ext(pmadapter, &pget_info->param.fw_info.mac_addr, + pmpriv->curr_addr, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + pget_info->param.fw_info.fw_bands = pmadapter->fw_bands; + pget_info->param.fw_info.region_code = pmadapter->region_code; + if (pmadapter->otp_region && pmadapter->otp_region->force_reg) + pget_info->param.fw_info.force_reg = MTRUE; + else + pget_info->param.fw_info.force_reg = MFALSE; + pget_info->param.fw_info.ecsa_enable = pmadapter->ecsa_enable; + pget_info->param.fw_info.getlog_enable = + pmadapter->getlog_enable; + pget_info->param.fw_info.hw_dev_mcs_support = + pmadapter->hw_dev_mcs_support; + pget_info->param.fw_info.hw_dot_11n_dev_cap = + pmadapter->hw_dot_11n_dev_cap; + pget_info->param.fw_info.usr_dev_mcs_support = + pmpriv->usr_dev_mcs_support; + if (IS_FW_SUPPORT_NO_80MHZ(pmadapter)) + pget_info->param.fw_info.prohibit_80mhz = MTRUE; + else + pget_info->param.fw_info.prohibit_80mhz = MFALSE; + pget_info->param.fw_info.hw_dot_11ac_mcs_support = + pmadapter->hw_dot_11ac_mcs_support; + pget_info->param.fw_info.hw_dot_11ac_dev_cap = + pmadapter->hw_dot_11ac_dev_cap; + pget_info->param.fw_info.usr_dot_11ac_dev_cap_bg = + pmpriv->usr_dot_11ac_dev_cap_bg; + pget_info->param.fw_info.usr_dot_11ac_mcs_support = + pmpriv->usr_dot_11ac_mcs_support; + pget_info->param.fw_info.usr_dot_11ac_dev_cap_a = + pmpriv->usr_dot_11ac_dev_cap_a; + pget_info->param.fw_info.hw_hecap_len = pmadapter->hw_hecap_len; + pget_info->param.fw_info.hw_2g_hecap_len = + pmadapter->hw_2g_hecap_len; + memcpy_ext(pmadapter, pget_info->param.fw_info.hw_he_cap, + pmadapter->hw_he_cap, pmadapter->hw_hecap_len, + sizeof(pget_info->param.fw_info.hw_he_cap)); + memcpy_ext(pmadapter, pget_info->param.fw_info.hw_2g_he_cap, + pmadapter->hw_2g_he_cap, pmadapter->hw_2g_hecap_len, + sizeof(pget_info->param.fw_info.hw_2g_he_cap)); + pget_info->param.fw_info.fw_supplicant_support = + IS_FW_SUPPORT_SUPPLICANT(pmadapter) ? 0x01 : 0x00; + pget_info->param.fw_info.antinfo = pmadapter->antinfo; + pget_info->param.fw_info.max_ap_assoc_sta = + pmadapter->max_sta_conn; + break; + case MLAN_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; + case MLAN_OID_LINK_STATS: + status = wlan_ioctl_link_statistic(pmpriv, 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + t_u16 cmd_oid = 0; + mlan_ds_snmp_mib *mib = MNULL; + t_u32 value = 0; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_snmp_mib)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_snmp_mib); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + mib = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + + switch (mib->sub_command) { + case MLAN_OID_SNMP_MIB_RTS_THRESHOLD: + value = mib->param.rts_threshold; + cmd_oid = RtsThresh_i; + break; + case MLAN_OID_SNMP_MIB_FRAG_THRESHOLD: + value = mib->param.frag_threshold; + cmd_oid = FragThresh_i; + break; + case MLAN_OID_SNMP_MIB_RETRY_COUNT: + value = mib->param.retry_count; + cmd_oid = ShortRetryLim_i; + break; + case MLAN_OID_SNMP_MIB_DTIM_PERIOD: + value = mib->param.dtim_period; + cmd_oid = DtimPeriod_i; + break; + case MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE: + value = mib->param.signalext_enable; + cmd_oid = SignalextEnable_i; + break; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SNMP_MIB, cmd_action, + cmd_oid, (t_void *)pioctl_req, &value); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_radio_cfg *radio_cfg = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_radio_cfg)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_radio_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + radio_cfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + switch (radio_cfg->sub_command) { + case MLAN_OID_RADIO_CTRL: + status = wlan_radio_ioctl_radio_ctl(pmadapter, pioctl_req); + break; + case MLAN_OID_BAND_CFG: + status = wlan_radio_ioctl_band_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_ANT_CFG: + status = wlan_radio_ioctl_ant_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_REMAIN_CHAN_CFG: + status = + wlan_radio_ioctl_remain_chan_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MIMO_SWITCH: + status = + wlan_radio_ioctl_mimo_switch_cfg(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief Set/Get MAC address + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_bss_ioctl_mac_address(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + t_u16 cmd_action; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + cmd_action = HostCmd_ACT_GEN_GET; + } else { + cmd_action = HostCmd_ACT_GEN_SET; + memcpy_ext(pmadapter, pmpriv->curr_addr, &bss->param.mac_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_MAC_ADDRESS, + cmd_action, 0, (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set multicast list + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +static mlan_status wlan_bss_ioctl_set_multicast_list(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 old_pkt_filter; + + ENTER(); + + old_pkt_filter = pmpriv->curr_pkt_filter; + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + pioctl_req->data_read_written = + sizeof(mlan_multicast_list) + MLAN_SUB_COMMAND_SIZE; + if (bss->param.multicast_list.mode == MLAN_PROMISC_MODE) { + PRINTM(MINFO, "Enable Promiscuous mode\n"); + pmpriv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; + pmpriv->curr_pkt_filter &= + ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + /* Multicast */ + pmpriv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; + if (bss->param.multicast_list.mode == MLAN_ALL_MULTI_MODE) { + PRINTM(MINFO, "Enabling All Multicast!\n"); + pmpriv->curr_pkt_filter |= + HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + pmpriv->curr_pkt_filter &= + ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + if (bss->param.multicast_list.num_multicast_addr) { + PRINTM(MINFO, "Set multicast list=%d\n", + bss->param.multicast_list + .num_multicast_addr); + /* Set multicast addresses to firmware */ + if (old_pkt_filter == pmpriv->curr_pkt_filter) { + /* Send request to firmware */ + ret = wlan_prepare_cmd( + pmpriv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, + &bss->param.multicast_list); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + /* Send request to firmware */ + ret = wlan_prepare_cmd( + pmpriv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, 0, MNULL, + &bss->param.multicast_list); + } + if (ret) + goto exit; + } + } + } + PRINTM(MINFO, "old_pkt_filter=0x%x, curr_pkt_filter=0x%x\n", + old_pkt_filter, pmpriv->curr_pkt_filter); + if (old_pkt_filter != pmpriv->curr_pkt_filter) { + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, + &pmpriv->curr_pkt_filter); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get channel list + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_bss_ioctl_get_channel_list(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = MNULL; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + chan_freq_power_t *cfp = MNULL; + ENTER(); + + if ((pioctl_req == MNULL) || (pioctl_req->pbuf == MNULL)) { + PRINTM(MERROR, "Request buffer not found!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + cfp = wlan_find_cfp_by_band_and_channel( + pmadapter, pmpriv->curr_bss_params.band, + (t_u16)pmpriv->curr_bss_params.bss_descriptor.channel); + if (cfp) { + bss->param.bss_chan.channel = cfp->channel; + bss->param.bss_chan.freq = cfp->freq; + } else { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } + pioctl_req->data_read_written = + sizeof(chan_freq) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; + } + if (!bss->param.bss_chan.channel && !bss->param.bss_chan.freq) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (pmadapter->adhoc_start_band & BAND_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) { + 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) { + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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->prior_port_status = pmpriv->port_open; + pmpriv->port_open = MFALSE; + } + pmadapter->scan_block = MFALSE; + + if (pmpriv->bss_mode == MLAN_BSS_MODE_INFRA) { + if (!bss->param.ssid_bssid.idx || + bss->param.ssid_bssid.idx > pmadapter->num_in_scan_table) { + /* Search for the requested SSID in the scan table */ + if (bss->param.ssid_bssid.ssid.ssid_len) { + if (memcmp(pmadapter, + &bss->param.ssid_bssid.bssid, + zero_mac, sizeof(zero_mac))) + i = wlan_find_ssid_in_list( + pmpriv, + &bss->param.ssid_bssid.ssid, + (t_u8 *)&bss->param.ssid_bssid + .bssid, + MLAN_BSS_MODE_INFRA); + else + i = wlan_find_ssid_in_list( + pmpriv, + &bss->param.ssid_bssid.ssid, + MNULL, MLAN_BSS_MODE_INFRA); + } else { + i = wlan_find_bssid_in_list( + pmpriv, + (t_u8 *)&bss->param.ssid_bssid.bssid, + MLAN_BSS_MODE_INFRA); + } + } else { + /* use bsslist index number to assoicate */ + i = wlan_is_network_compatible( + pmpriv, bss->param.ssid_bssid.idx - 1, + pmpriv->bss_mode); + } + if (i >= 0) { + /* block if upper-layer tries to reconnect before new + * scan */ + if (wlan_11h_get_csa_closed_channel(pmpriv) == + (t_u8)pmadapter->pscan_table[i].channel) { + PRINTM(MINFO, + "Attempt to reconnect on csa_closed_chan(%d)\n", + pmadapter->pscan_table[i].channel); + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto start_ssid_done; + } + PRINTM(MINFO, + "SSID found in scan list ... associating...\n"); + pmpriv->curr_bss_params.host_mlme = + bss->param.ssid_bssid.host_mlme; + /* Clear any past association response stored for + * application retrieval */ + pmpriv->assoc_rsp_size = 0; + pmpriv->curr_chan_flags = + bss->param.ssid_bssid.channel_flags; + if (IS_FW_SUPPORT_NO_80MHZ(pmadapter)) + pmpriv->curr_chan_flags |= CHAN_FLAGS_NO_80MHZ; + ret = wlan_associate(pmpriv, pioctl_req, + &pmadapter->pscan_table[i]); + if (ret) + goto start_ssid_done; + } else { /* i >= 0 */ + PRINTM(MERROR, + "SSID not found in scan list: ssid=%s, " MACSTR + ", idx=%d\n", + bss->param.ssid_bssid.ssid.ssid, + MAC2STR(bss->param.ssid_bssid.bssid), + (int)bss->param.ssid_bssid.idx); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto start_ssid_done; + } + } else { + /* Adhoc mode */ + /* If the requested SSID matches current SSID, return */ + if (bss->param.ssid_bssid.ssid.ssid_len && + (!wlan_ssid_cmp(pmadapter, + &pmpriv->curr_bss_params.bss_descriptor.ssid, + &bss->param.ssid_bssid.ssid))) { + ret = MLAN_STATUS_SUCCESS; + goto start_ssid_done; + } + + /* Exit Adhoc mode first */ + PRINTM(MINFO, "Sending Adhoc Stop\n"); + ret = wlan_disconnect(pmpriv, MNULL, MNULL); + if (ret) + goto start_ssid_done; + + pmpriv->adhoc_is_link_sensed = MFALSE; + + if (!bss->param.ssid_bssid.idx || + bss->param.ssid_bssid.idx > pmadapter->num_in_scan_table) { + /* Search for the requested network in the scan table */ + if (bss->param.ssid_bssid.ssid.ssid_len) { + i = wlan_find_ssid_in_list( + pmpriv, &bss->param.ssid_bssid.ssid, + MNULL, MLAN_BSS_MODE_IBSS); + } else { + i = wlan_find_bssid_in_list( + pmpriv, + (t_u8 *)&bss->param.ssid_bssid.bssid, + MLAN_BSS_MODE_IBSS); + } + } else { + /* use bsslist index number to assoicate */ + i = wlan_is_network_compatible( + pmpriv, bss->param.ssid_bssid.idx - 1, + pmpriv->bss_mode); + } + + if (i >= 0) { + PRINTM(MINFO, + "Network found in scan list ... joining ...\n"); + pmpriv->curr_chan_flags = + bss->param.ssid_bssid.channel_flags; + ret = wlan_adhoc_join(pmpriv, pioctl_req, + &pmadapter->pscan_table[i]); + if (ret) + goto start_ssid_done; + } else { /* i >= 0 */ + PRINTM(MINFO, + "Network not found in the list, " + "creating adhoc with ssid = %s\n", + bss->param.ssid_bssid.ssid.ssid); + pmpriv->curr_chan_flags = + bss->param.ssid_bssid.channel_flags; + ret = wlan_adhoc_start(pmpriv, pioctl_req, + &bss->param.ssid_bssid.ssid); + if (ret) + goto start_ssid_done; + } + } + + if (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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = (mlan_ds_bss *)pioctl_req->pbuf; + + ENTER(); + + ret = wlan_disconnect(pmpriv, pioctl_req, &bss->param.deauth_param); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get IBSS channel + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +static mlan_status wlan_bss_ioctl_ibss_channel(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + if (pmpriv->media_connected == MFALSE) { + bss->param.bss_chan.channel = pmpriv->adhoc_channel; + goto exit; + } + cmd_action = HostCmd_ACT_GEN_GET; + } else { + cmd_action = HostCmd_ACT_GEN_SET; + pmpriv->adhoc_channel = (t_u8)bss->param.bss_chan.channel; + pmpriv->intf_state_11h.adhoc_auto_sel_chan = MFALSE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_RF_CHANNEL, + cmd_action, 0, (t_void *)pioctl_req, + &bss->param.bss_chan.channel); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Listen Interval + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +static mlan_status wlan_bss_ioctl_listen_interval(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + bss->param.listen_interval = pmpriv->listen_interval; + else + pmpriv->listen_interval = bss->param.listen_interval; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/* + * @brief Set/Get beacon interval + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_bss_ioctl_beacon_interval(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_ds_bss *bss = MNULL; + mlan_ssid_bssid *ssid_bssid = MNULL; + mlan_ds_sec_cfg *sec = 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; + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, sizeof(mlan_ds_sec_cfg), + MLAN_MEM_DEF, (t_u8 **)&sec); + if (ret || !sec) { + PRINTM(MERROR, "Could not allocate sec!\n"); + LEAVE(); + return ret; + } + memset(pmadapter, sec, 0, sizeof(mlan_ds_sec_cfg)); + sec_pp = (mlan_ds_passphrase *)&sec->param.passphrase; + sec_pp->psk_type = MLAN_PSK_QUERY; + if (ssid_bssid->ssid.ssid_len == 0) { + i = wlan_find_bssid_in_list(priv, (t_u8 *)&ssid_bssid->bssid, + MLAN_BSS_MODE_AUTO); + if (i >= 0) { + pbss_desc = &pmadapter->pscan_table[i]; + memcpy_ext(pmadapter, (t_u8 *)&sec_pp->ssid, + &pbss_desc->ssid, sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + } else + memcpy_ext(pmadapter, (t_u8 *)&sec_pp->bssid, + &ssid_bssid->bssid, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + } else { + memcpy_ext(pmadapter, (t_u8 *)&sec_pp->ssid, &ssid_bssid->ssid, + sizeof(mlan_802_11_ssid), sizeof(mlan_802_11_ssid)); + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(priv, HostCmd_CMD_SUPPLICANT_PMK, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + (t_void *)sec); + if (sec) + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)sec); + LEAVE(); + return ret; +} + +/** + * @brief Search for a BSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +mlan_status wlan_bss_ioctl_find_bss(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DRV_EMBEDDED_SUPPLICANT + mlan_ds_bss *bss = MNULL; + mlan_ssid_bssid *ssid_bssid = MNULL; +#endif + + ENTER(); + + if (pmpriv->ewpa_query) { + if (wlan_query_passphrase(pmpriv, pioctl_req) == + MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "Find BSS ioctl: query passphrase\n"); + LEAVE(); + return MLAN_STATUS_PENDING; + } + } +#ifdef DRV_EMBEDDED_SUPPLICANT + if (!IS_FW_SUPPORT_SUPPLICANT(pmpriv->adapter)) { + bss = (mlan_ds_bss *)pioctl_req->pbuf; + ssid_bssid = &bss->param.ssid_bssid; + supplicantQueryPassphraseAndEnable(pmpriv->psapriv, + (t_u8 *)ssid_bssid); + } +#endif + + ret = wlan_find_bss(pmpriv, pioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief 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_bssid(pmlan_adapter pmadapter, + 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; + int i = 0; + + ENTER(); + bss = (mlan_ds_bss *)pioctl_req->pbuf; + i = wlan_find_bssid_in_list(pmpriv, (t_u8 *)&bss->param.bssid, + MLAN_BSS_MODE_AUTO); + if (i < 0) { + PRINTM(MCMND, "Can not find bssid " MACSTR "\n", + MAC2STR((t_u8 *)&bss->param.bssid)); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + PRINTM(MCMND, "Find bssid " MACSTR "\n", + MAC2STR((t_u8 *)&bss->param.bssid)); + LEAVE(); + return ret; +} + +/** + * @brief Check if BSS channel is valid for Station's region + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +mlan_status wlan_bss_ioctl_bss_11d_check_channel(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = MNULL; + mlan_ssid_bssid *ssid_bssid = MNULL; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + ssid_bssid = &bss->param.ssid_bssid; + + PRINTM(MINFO, "ssid: %s idx:%d\n", ssid_bssid->ssid.ssid, + ssid_bssid->idx); + PRINTM(MINFO, "band:%d channel:%d\n", (t_u8)ssid_bssid->bss_band, + (t_u32)ssid_bssid->channel); + + /* check if this channel is supported in the region */ + if (!wlan_find_cfp_by_band_and_channel(pmadapter, + (t_u8)ssid_bssid->bss_band, + (t_u32)ssid_bssid->channel)) { + PRINTM(MERROR, "Unsupported Channel for region 0x%x\n", + pmadapter->region_code); + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief BSS command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_bss_ioctl(pmlan_adapter pmadapter, + 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_BSS_FIND_BSSID: + status = wlan_bss_ioctl_find_bssid(pmadapter, pioctl_req); + break; + case MLAN_OID_IBSS_BCN_INTERVAL: + status = wlan_bss_ioctl_beacon_interval(pmadapter, pioctl_req); + break; + case MLAN_OID_IBSS_ATIM_WINDOW: + status = wlan_bss_ioctl_atim_window(pmadapter, pioctl_req); + break; + case MLAN_OID_IBSS_CHANNEL: + status = wlan_bss_ioctl_ibss_channel(pmadapter, pioctl_req); + break; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + case MLAN_OID_BSS_ROLE: + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + (pmlan_linked_list)pioctl_req, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + pmadapter->pending_ioctl = MTRUE; + status = MLAN_STATUS_PENDING; + break; +#endif +#ifdef WIFI_DIRECT_SUPPORT + case MLAN_OID_WIFI_DIRECT_MODE: + status = wlan_bss_ioctl_wifi_direct_mode(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_BSS_LISTEN_INTERVAL: + status = wlan_bss_ioctl_listen_interval(pmadapter, pioctl_req); + break; + case MLAN_OID_BSS_REMOVE: + status = wlan_bss_ioctl_bss_remove(pmadapter, pioctl_req); + break; + + case MLAN_OID_BSS_11D_CHECK_CHANNEL: + status = wlan_bss_ioctl_bss_11d_check_channel(pmadapter, + pioctl_req); + break; + case MLAN_OID_BSS_CHAN_INFO: + status = wlan_bss_ioctl_get_chan_info(pmadapter, pioctl_req); + break; + default: + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief Get supported rates + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status +wlan_rate_ioctl_get_supported_rate(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_rate *rate = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + if (pioctl_req->action != MLAN_ACT_GET) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + rate = (mlan_ds_rate *)pioctl_req->pbuf; + if (rate->param.rate_band_cfg.config_bands && + rate->param.rate_band_cfg.bss_mode) + wlan_get_active_data_rates( + pmpriv, rate->param.rate_band_cfg.bss_mode, + rate->param.rate_band_cfg.config_bands, + rate->param.rates); + else + wlan_get_active_data_rates(pmpriv, pmpriv->bss_mode, + (pmpriv->bss_mode == + MLAN_BSS_MODE_INFRA) ? + pmpriv->config_bands : + pmadapter->adhoc_start_band, + rate->param.rates); + pioctl_req->data_read_written = + MLAN_SUPPORTED_RATES + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Rate command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +static mlan_status wlan_rate_ioctl(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + t_u16 cmd_no, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_power_cfg *power = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + HostCmd_DS_TXPWR_CFG *txp_cfg = MNULL; + MrvlTypes_Power_Group_t *pg_tlv = MNULL; + Power_Group_t *pg = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *buf = MNULL; + t_s8 dbm = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + power = (mlan_ds_power_cfg *)pioctl_req->pbuf; + if (!power->param.power_cfg.is_power_auto) { + dbm = (t_s8)power->param.power_cfg.power_level; + if ((dbm < pmpriv->min_tx_power_level) || + (dbm > pmpriv->max_tx_power_level)) { + PRINTM(MERROR, + "The set txpower value %d dBm is out of range (%d dBm-%d dBm)!\n", + dbm, pmpriv->min_tx_power_level, + pmpriv->max_tx_power_level); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MRVDRV_SIZE_OF_CMD_BUFFER, MLAN_MEM_DEF, &buf); + if (ret != MLAN_STATUS_SUCCESS || buf == MNULL) { + PRINTM(MERROR, + "ALLOC_CMD_BUF: Failed to allocate command buffer\n"); + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + memset(pmadapter, buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + txp_cfg = (HostCmd_DS_TXPWR_CFG *)buf; + txp_cfg->action = HostCmd_ACT_GEN_SET; + if (!power->param.power_cfg.is_power_auto) { + txp_cfg->mode = 1; + pg_tlv = (MrvlTypes_Power_Group_t + *)(buf + sizeof(HostCmd_DS_TXPWR_CFG)); + pg_tlv->type = TLV_TYPE_POWER_GROUP; + pg_tlv->length = 4 * sizeof(Power_Group_t); + pg = (Power_Group_t *)(buf + sizeof(HostCmd_DS_TXPWR_CFG) + + sizeof(MrvlTypes_Power_Group_t)); + /* Power group for modulation class HR/DSSS */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x03; + pg->modulation_class = MOD_CLASS_HR_DSSS; + pg->power_step = 0; + pg->power_min = (t_s8)dbm; + pg->power_max = (t_s8)dbm; + pg++; + /* Power group for modulation class OFDM */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x07; + pg->modulation_class = MOD_CLASS_OFDM; + pg->power_step = 0; + pg->power_min = (t_s8)dbm; + pg->power_max = (t_s8)dbm; + pg++; + /* Power group for modulation class HTBW20 */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x20; + pg->modulation_class = MOD_CLASS_HT; + pg->power_step = 0; + pg->power_min = (t_s8)dbm; + pg->power_max = (t_s8)dbm; + pg->ht_bandwidth = HT_BW_20; + pg++; + /* Power group for modulation class HTBW40 */ + pg->first_rate_code = 0x00; + pg->last_rate_code = 0x20; + pg->modulation_class = MOD_CLASS_HT; + pg->power_step = 0; + pg->power_min = (t_s8)dbm; + pg->power_max = (t_s8)dbm; + pg->ht_bandwidth = HT_BW_40; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_TXPWR_CFG, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + buf); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + if (buf) + pcb->moal_mfree(pmadapter->pmoal_handle, buf); + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set extended power configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_power_ioctl_set_power_ext(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_power_cfg *power = MNULL; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *buf = MNULL; + HostCmd_DS_TXPWR_CFG *txp_cfg = MNULL; + MrvlTypes_Power_Group_t *pg_tlv = MNULL; + Power_Group_t *pg = MNULL; + mlan_power_group *pwr_grp = MNULL; + + ENTER(); + + power = (mlan_ds_power_cfg *)pioctl_req->pbuf; + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MRVDRV_SIZE_OF_CMD_BUFFER, MLAN_MEM_DEF, &buf); + if (ret != MLAN_STATUS_SUCCESS || buf == MNULL) { + PRINTM(MERROR, + "ALLOC_CMD_BUF: Failed to allocate command buffer\n"); + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + memset(pmadapter, buf, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + txp_cfg = (HostCmd_DS_TXPWR_CFG *)buf; + txp_cfg->action = HostCmd_ACT_GEN_SET; + pwr_grp = &power->param.power_ext.power_group[0]; + if (pwr_grp->rate_format == TX_PWR_CFG_AUTO_CTRL_OFF) + txp_cfg->mode = 0; + else { + txp_cfg->mode = 1; + + pg_tlv = (MrvlTypes_Power_Group_t + *)(buf + sizeof(HostCmd_DS_TXPWR_CFG)); + pg_tlv->type = TLV_TYPE_POWER_GROUP; + pg_tlv->length = sizeof(Power_Group_t); + pg = (Power_Group_t *)(buf + sizeof(HostCmd_DS_TXPWR_CFG) + + sizeof(MrvlTypes_Power_Group_t)); + pg->ht_bandwidth = pwr_grp->bandwidth; + pg->power_min = (t_s8)pwr_grp->power_min; + pg->power_max = (t_s8)pwr_grp->power_max; + pg->power_step = (t_s8)pwr_grp->power_step; + + if (pwr_grp->rate_format == MLAN_RATE_FORMAT_LG) { + if (pwr_grp->first_rate_ind <= + MLAN_RATE_INDEX_HRDSSS3) { + pg->modulation_class = MOD_CLASS_HR_DSSS; + } else { + pg->modulation_class = MOD_CLASS_OFDM; + pwr_grp->first_rate_ind -= + MLAN_RATE_INDEX_OFDM0; + pwr_grp->last_rate_ind -= MLAN_RATE_INDEX_OFDM0; + } + pg->first_rate_code = (t_u8)pwr_grp->first_rate_ind; + pg->last_rate_code = (t_u8)pwr_grp->last_rate_ind; + } else if (pwr_grp->rate_format == MLAN_RATE_FORMAT_HT) { + pg->modulation_class = MOD_CLASS_HT; + pg->first_rate_code = (t_u8)pwr_grp->first_rate_ind; + pg->last_rate_code = (t_u8)pwr_grp->last_rate_ind; + } else if (pwr_grp->rate_format == MLAN_RATE_FORMAT_VHT) { + pg->modulation_class = MOD_CLASS_VHT; + pg->first_rate_code = + (t_u8)((pwr_grp->first_rate_ind & 0xF) | + ((pwr_grp->nss - 1) << 4)); + pg->last_rate_code = + (t_u8)((pwr_grp->last_rate_ind & 0xF) | + ((pwr_grp->nss - 1) << 4)); + } else { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } + } + if (ret == MLAN_STATUS_FAILURE) { + if (buf) + pcb->moal_mfree(pmadapter->pmoal_handle, buf); + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_TXPWR_CFG, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + buf); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + if (buf) + pcb->moal_mfree(pmadapter->pmoal_handle, buf); + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Power configuration command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_power_ioctl(pmlan_adapter pmadapter, + 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; + case MLAN_OID_POWER_LOW_POWER_MODE: + status = wlan_power_ioctl_set_get_lpm(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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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.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.bcn_miss_timeout) + pmadapter->bcn_miss_time_out = + (t_u16)pm_cfg->param.ps_cfg.bcn_miss_timeout; + else + pm_cfg->param.ps_cfg.bcn_miss_timeout = + (t_u32)pmadapter->bcn_miss_time_out; + if (pm_cfg->param.ps_cfg.delay_to_ps != DELAY_TO_PS_UNCHANGED) + pmadapter->delay_to_ps = + (t_u16)pm_cfg->param.ps_cfg.delay_to_ps; + else + pm_cfg->param.ps_cfg.delay_to_ps = + (t_u32)pmadapter->delay_to_ps; + if (pm_cfg->param.ps_cfg.ps_mode) + pmadapter->enhanced_ps_mode = + (t_u16)pm_cfg->param.ps_cfg.ps_mode; + else + pm_cfg->param.ps_cfg.ps_mode = + (t_u32)pmadapter->enhanced_ps_mode; + } + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set/Get PS configuration parameter + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_set_get_bcn_timeout(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pm_cfg = MNULL; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_BCN_TMO, (t_void *)pioctl_req, + &pm_cfg->param.bcn_timeout); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set the sleep parameters + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_set_get_sleep_params(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_pm_cfg *pm_cfg = MNULL; + t_u16 cmd_action = 0; + + ENTER(); + + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + cmd_action = HostCmd_ACT_GEN_GET; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SLEEP_PARAMS, + cmd_action, 0, (t_void *)pioctl_req, + &pm_cfg->param.sleep_params); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief config management frame wakeup filter + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_config_mgmt_filter(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pm_cfg = MNULL; + int i = 0; + + ENTER(); + + memset(pmadapter, pmadapter->mgmt_filter, 0, + sizeof(mlan_mgmt_frame_wakeup) * MAX_MGMT_FRAME_FILTER); + pm_cfg = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + for (i = 0; i < MAX_MGMT_FRAME_FILTER; i++) + if (!pm_cfg->param.mgmt_filter[i].type) + break; + memcpy_ext(pmadapter, (t_u8 *)pmadapter->mgmt_filter, + (t_u8 *)pm_cfg->param.mgmt_filter, + (i + 1) * sizeof(mlan_mgmt_frame_wakeup), + sizeof(pmadapter->mgmt_filter)); + } else if (pioctl_req->action == MLAN_ACT_GET) + PRINTM(MERROR, "Get not support\n"); + LEAVE(); + return ret; +} + +/** + * @brief Power save command handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_pm_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pm = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_pm_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_pm_cfg); + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + pm = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + switch (pm->sub_command) { + case MLAN_OID_PM_CFG_IEEE_PS: + switch (pioctl_req->action) { + case MLAN_ACT_SET: + /**Block ieee power save disable command when bt coex + * enable*/ + if (pmadapter->coex_scan && !pm->param.ps_mode) + break; + if (pm->param.ps_mode) + pmadapter->ps_mode = Wlan802_11PowerModePSP; + else + pmadapter->ps_mode = Wlan802_11PowerModeCAM; + status = wlan_pm_ioctl_ps_mode(pmadapter, pioctl_req, + pmadapter->ps_mode); + break; + case MLAN_ACT_GET: + status = wlan_pm_ioctl_ps_mode(pmadapter, pioctl_req, + pmadapter->ps_mode); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + break; + case MLAN_OID_PM_CFG_HS_CFG: + status = wlan_pm_ioctl_hscfg(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_INACTIVITY_TO: + status = + wlan_pm_ioctl_inactivity_timeout(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_DEEP_SLEEP: + switch (pioctl_req->action) { + case MLAN_ACT_SET: + if (pmadapter->is_deep_sleep && + pm->param.auto_deep_sleep.auto_ds == + DEEP_SLEEP_ON) { + PRINTM(MMSG, + "Station already in enhanced deep sleep mode\n"); + status = MLAN_STATUS_FAILURE; + break; + } else if (!pmadapter->is_deep_sleep && + pm->param.auto_deep_sleep.auto_ds == + DEEP_SLEEP_OFF) { + PRINTM(MMSG, + "Station already not in enhanced deep sleep mode\n"); + status = MLAN_STATUS_FAILURE; + break; + } + status = + wlan_set_auto_deep_sleep(pmadapter, pioctl_req); + break; + case MLAN_ACT_GET: + if (pmadapter->is_deep_sleep) { + pm->param.auto_deep_sleep.auto_ds = + DEEP_SLEEP_ON; + pm->param.auto_deep_sleep.idletime = + pmadapter->idle_time; + } else + pm->param.auto_deep_sleep.auto_ds = + DEEP_SLEEP_OFF; + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + break; + case MLAN_OID_PM_CFG_PS_CFG: + status = wlan_set_get_ps_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_SLEEP_PD: + status = wlan_set_get_sleep_pd(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_SLEEP_PARAMS: + status = wlan_set_get_sleep_params(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_INFO: + status = wlan_get_pm_info(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_HS_WAKEUP_REASON: + status = wlan_get_hs_wakeup_reason(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_MGMT_FILTER: + status = wlan_config_mgmt_filter(pmadapter, pioctl_req); + break; + case MLAN_OID_PM_CFG_BCN_TIMEOUT: + status = wlan_set_get_bcn_timeout(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief Set/Get WPA IE + * + * @param priv A pointer to mlan_private structure + * @param ie_data_ptr A pointer to IE + * @param ie_len Length of the IE + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_set_wpa_ie_helper(mlan_private *priv, t_u8 *ie_data_ptr, + t_u16 ie_len) +{ + ENTER(); + + if (ie_len) { + if (ie_len > sizeof(priv->wpa_ie)) { + PRINTM(MERROR, "failed to copy, WPA IE is too big\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + memcpy_ext(priv->adapter, priv->wpa_ie, ie_data_ptr, ie_len, + sizeof(priv->wpa_ie)); + 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 OSEN 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_osen_ie(mlan_private *priv, t_u8 *ie_data_ptr, + t_u16 ie_len) +{ + ENTER(); + if (ie_len) { + if (ie_len > sizeof(priv->osen_ie)) { + PRINTM(MWARN, "failed to copy, WAPI IE is too big\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + memcpy_ext(priv->adapter, priv->osen_ie, ie_data_ptr, ie_len, + sizeof(priv->osen_ie)); + priv->osen_ie_len = (t_u8)ie_len; + PRINTM(MIOCTL, "Set osen_ie_len=%d IE=%#x\n", priv->osen_ie_len, + priv->osen_ie[0]); + DBG_HEXDUMP(MCMD_D, "osen_ie", priv->osen_ie, + priv->osen_ie_len); + priv->sec_info.osen_enabled = MTRUE; + } else { + memset(priv->adapter, priv->osen_ie, 0, sizeof(priv->osen_ie)); + priv->osen_ie_len = (t_u8)ie_len; + PRINTM(MINFO, "Reset osen_ie_len=%d IE=%#x\n", + priv->osen_ie_len, priv->osen_ie[0]); + priv->sec_info.osen_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_ext(priv->adapter, priv->wapi_ie, ie_data_ptr, ie_len, + sizeof(priv->wapi_ie)); + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_sec_cfg *sec = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) { + if (pmpriv->port_ctrl_mode) + sec->param.port_ctrl_enabled = MTRUE; + else + sec->param.port_ctrl_enabled = MFALSE; + } else { + if (sec->param.port_ctrl_enabled) { + pmpriv->port_ctrl_mode = MTRUE; + pmpriv->port_open = MFALSE; + } else { + if (pmpriv->port_ctrl_mode == MTRUE) { + pmpriv->port_ctrl_mode = MFALSE; + /* Cleanup the bypass TX queue */ + wlan_cleanup_bypass_txq(pmpriv); + } + } + } + PRINTM(MINFO, "port_ctrl: port_ctrl_mode=%d port_open=%d\n", + pmpriv->port_ctrl_mode, pmpriv->port_open); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get authentication mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_sec_ioctl_auth_mode(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + sec->param.auth_mode = pmpriv->sec_info.authentication_mode; + else + pmpriv->sec_info.authentication_mode = sec->param.auth_mode; + + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Set/Get encryption mode + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_sec_ioctl_encrypt_mode(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + sec->param.encrypt_mode = pmpriv->sec_info.encryption_mode; + else + pmpriv->sec_info.encryption_mode = sec->param.encrypt_mode; + + pioctl_req->data_read_written = sizeof(t_u32) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Get Random charactor + * + * @param pmadapter A pointer to mlan_adapter structure + * + * @return random charactor + */ +t_u8 wlan_get_random_charactor(pmlan_adapter pmadapter) +{ + t_u32 sec, usec; + t_u8 ch = 0; + + ENTER(); + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec, + &usec); + sec = (sec & 0xFFFF) + (sec >> 16); + usec = (usec & 0xFFFF) + (usec >> 16); + ch = (((sec << 16) + usec) % 26) + 'a'; + LEAVE(); + return ch; +} + +/** + * @brief Set/Get WPA status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_sec_ioctl_wpa_enable(pmlan_adapter pmadapter, + 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); + } + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + mrvl_wep_key_t *pwep_key = MNULL; + int index; + int i = 0; + + ENTER(); + + if (pmpriv->wep_key_curr_index >= MRVL_NUM_WEP_KEY) + pmpriv->wep_key_curr_index = 0; + pwep_key = &pmpriv->wep_key[pmpriv->wep_key_curr_index]; + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (sec->param.encrypt_key.key_index == MLAN_KEY_INDEX_DEFAULT) { + index = pmpriv->wep_key_curr_index; + sec->param.encrypt_key.key_index = index; + } else { + if (sec->param.encrypt_key.key_index >= MRVL_NUM_WEP_KEY) { + if ((sec->param.encrypt_key.key_remove == MTRUE) && + (sec->param.encrypt_key.key_index <= 5)) { + /* call firmware remove key */ + ret = wlan_prepare_cmd( + pmpriv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, + &sec->param.encrypt_key); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + goto exit; + } + PRINTM(MERROR, "Key_index is invalid\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + index = sec->param.encrypt_key.key_index; + } + + if ((sec->param.encrypt_key.key_disable == MTRUE) || + (sec->param.encrypt_key.key_remove == MTRUE)) { + pmpriv->sec_info.wep_status = Wlan802_11WEPDisabled; + /* remove key */ + if (sec->param.encrypt_key.key_remove == MTRUE) { + memset(pmadapter, &pmpriv->wep_key[index], 0, + sizeof(mrvl_wep_key_t)); + /* call firmware remove key */ + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, MNULL, + &sec->param.encrypt_key); + if (ret) + goto exit; + } + } else { + if (sec->param.encrypt_key.key_len) { + if ((sec->param.encrypt_key.key_len != + WEP_104_BIT_LEN) && + (sec->param.encrypt_key.key_len != + WEP_40_BIT_LEN)) { + PRINTM(MERROR, "Invalid wep key len=%d\n", + sec->param.encrypt_key.key_len); + /* We will use random key to clear the key + * buffer in FW */ + if (sec->param.encrypt_key.key_len < + WEP_40_BIT_LEN) + sec->param.encrypt_key.key_len = + WEP_40_BIT_LEN; + else + sec->param.encrypt_key.key_len = + WEP_104_BIT_LEN; + for (i = 0; i < sec->param.encrypt_key.key_len; + i++) + sec->param.encrypt_key.key_material[i] = + wlan_get_random_charactor( + pmadapter); + } + pwep_key = &pmpriv->wep_key[index]; + /* Cleanup */ + memset(pmadapter, pwep_key, 0, sizeof(mrvl_wep_key_t)); + /* Copy the key in the driver */ + + memcpy_ext(pmadapter, pwep_key->key_material, + sec->param.encrypt_key.key_material, + sec->param.encrypt_key.key_len, + MRVL_KEY_BUFFER_SIZE_IN_BYTE); + pwep_key->key_index = index; + pwep_key->key_length = sec->param.encrypt_key.key_len; + if (pmpriv->sec_info.wep_status != + Wlan802_11WEPEnabled) { + /* + * The status is set as Key Absent + * so as to make sure we display the + * keys when iwlist mlanX key is used + */ + pmpriv->sec_info.wep_status = + Wlan802_11WEPKeyAbsent; + } + } + if (sec->param.encrypt_key.is_current_wep_key == MTRUE) { + /* Copy the required key as the current key */ + pwep_key = &pmpriv->wep_key[index]; + if (!pwep_key->key_length) { + if (&pmpriv->sec_info.wpa_enabled || + &pmpriv->sec_info.wpa2_enabled || + &pmpriv->sec_info.wapi_enabled) { + ret = MLAN_STATUS_SUCCESS; + goto exit; + } + PRINTM(MERROR, + "Key %d not set,so cannot enable it\n", + index); + pioctl_req->status_code = + MLAN_ERROR_CMD_RESP_FAIL; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + pmpriv->wep_key_curr_index = (t_u16)index; + pmpriv->sec_info.wep_status = Wlan802_11WEPEnabled; + } + if (sec->param.encrypt_key.key_flags && pwep_key->key_length) { + pmpriv->wep_key_curr_index = (t_u16)index; + // Only do this if the key is an xmit key. If the key + // is a group key, we might be in wpa/wep mixed mode in + // which case we don't want to set wep_status = + // Wlan802_11WEPEnabled because that enables WEP at the + // MAC controller level and WPA stops working properly. + if (sec->param.encrypt_key.key_flags & + KEY_FLAG_SET_TX_KEY) { + pmpriv->sec_info.wep_status = + Wlan802_11WEPEnabled; + } + } + } + if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled) + pmpriv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; + else + pmpriv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; + + /* Send request to firmware */ + if (pmpriv->sec_info.wep_status == Wlan802_11WEPEnabled && + pwep_key->key_length) { + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, MNULL, + &pmpriv->curr_pkt_filter); + if (ret) + goto exit; + if (!sec->param.encrypt_key.key_len) { + sec->param.encrypt_key.key_index = pwep_key->key_index; + sec->param.encrypt_key.key_len = pwep_key->key_length; + memcpy_ext(pmadapter, + sec->param.encrypt_key.key_material, + pwep_key->key_material, + sec->param.encrypt_key.key_len, + MLAN_MAX_KEY_LENGTH); + } + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, + &sec->param.encrypt_key); + } else { + if (pwep_key->key_length) { + if (!sec->param.encrypt_key.key_len) { + sec->param.encrypt_key.key_index = + pwep_key->key_index; + sec->param.encrypt_key.key_len = + pwep_key->key_length; + memcpy_ext(pmadapter, + sec->param.encrypt_key.key_material, + pwep_key->key_material, + sec->param.encrypt_key.key_len, + MLAN_MAX_KEY_LENGTH); + } + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, MNULL, + &sec->param.encrypt_key); + if (ret) + goto exit; + } + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, + &pmpriv->curr_pkt_filter); + } + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set WPA key + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_sec_ioctl_set_wpa_key(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + /* Current driver only supports key length of up to 32 bytes */ + if (sec->param.encrypt_key.key_len > MLAN_MAX_KEY_LENGTH) { + PRINTM(MERROR, "Key length is incorrect\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + &sec->param.encrypt_key); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Get security keys + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static mlan_status wlan_sec_ioctl_get_key(pmlan_adapter pmadapter, + 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 (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) { + 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_ext(pmadapter, + sec->param.encrypt_key.key_material, + pmpriv->wep_key[index].key_material, + pmpriv->wep_key[index].key_length, + MLAN_MAX_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)) { + /* Return WPA enabled */ + sec->param.encrypt_key.key_disable = MFALSE; + memcpy_ext(pmadapter, + sec->param.encrypt_key.key_material, + pmpriv->aes_key.key_material, + pmpriv->aes_key.key_len, + MLAN_MAX_KEY_LENGTH); + sec->param.encrypt_key.key_len = MIN( + MLAN_MAX_KEY_LENGTH, pmpriv->aes_key.key_len); + } else { + sec->param.encrypt_key.key_disable = MTRUE; + } + } else { + index = sec->param.encrypt_key.key_index; + if (pmpriv->wep_key[index].key_length) { + sec->param.encrypt_key.key_index = + pmpriv->wep_key[index].key_index; + memcpy_ext(pmadapter, + sec->param.encrypt_key.key_material, + pmpriv->wep_key[index].key_material, + pmpriv->wep_key[index].key_length, + MLAN_MAX_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)) { + /* 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(pmlan_adapter pmadapter, + 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 Query Encrpyt 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_query_key(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + /* Current driver only supports get PTK/GTK */ + if (pmpriv->port_open && + (pmpriv->sec_info.wpa_enabled || pmpriv->sec_info.wpa2_enabled || + pmpriv->sec_info.wapi_enabled)) { + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_GET, 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 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u16 cmd_action = 0; + mlan_ds_sec_cfg *sec = MNULL; + + ENTER(); + + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + cmd_action = HostCmd_ACT_GEN_SET; + if (pmpriv->media_connected == MTRUE) { + PRINTM(MERROR, + "Cannot set esupplicant mode configuration while connected.\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (!sec->param.esupp_mode.rsn_mode || + (sec->param.esupp_mode.rsn_mode & RSN_TYPE_VALID_BITS) != + sec->param.esupp_mode.rsn_mode) { + PRINTM(MERROR, "Invalid RSN mode\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (!sec->param.esupp_mode.act_paircipher || + (sec->param.esupp_mode.act_paircipher & + EMBED_CIPHER_VALID_BITS) != + sec->param.esupp_mode.act_paircipher) { + PRINTM(MERROR, "Invalid pairwise cipher\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + if (!sec->param.esupp_mode.act_groupcipher || + (sec->param.esupp_mode.act_groupcipher & + EMBED_CIPHER_VALID_BITS) != + sec->param.esupp_mode.act_groupcipher) { + PRINTM(MERROR, "Invalid group cipher\n"); + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + ret = MLAN_STATUS_FAILURE; + goto exit; + } + } else { + cmd_action = HostCmd_ACT_GEN_GET_CURRENT; + } + + /* Send request to firmware */ + if (sec) { + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_SUPPLICANT_PROFILE, + cmd_action, 0, (t_void *)pioctl_req, + &sec->param.esupp_mode); + } else { + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_SUPPLICANT_PROFILE, + cmd_action, 0, (t_void *)pioctl_req, + MNULL); + } + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Security configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +static mlan_status wlan_sec_cfg_ioctl(pmlan_adapter pmadapter, + 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_QUERY_KEY: + status = wlan_sec_ioctl_query_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}; + const t_u8 osen_oui[] = {0x50, 0x6f, 0x9a, 0x12}; + t_u8 i = 0, temp[12] = {0}; + + ENTER(); + + /* If the passed length is zero, reset the buffer */ + if (!ie_len) { + priv->gen_ie_buf_len = 0; + priv->wps.session_enable = MFALSE; + wlan_set_wpa_ie_helper(priv, MNULL, 0); + wlan_set_wapi_ie(priv, MNULL, 0); + wlan_set_osen_ie(priv, MNULL, 0); + } else if (!ie_data_ptr) { + /* MNULL check */ + ret = MLAN_STATUS_FAILURE; + } else { + pvendor_ie = (IEEEtypes_VendorHeader_t *)ie_data_ptr; + if (pvendor_ie->element_id == EXT_CAPABILITY) { + memcpy_ext(priv->adapter, temp, &priv->ext_cap, + sizeof(priv->ext_cap), sizeof(temp)); + for (i = 0; + i < MIN(sizeof(priv->ext_cap), pvendor_ie->len); + i++) + temp[i] |= ie_data_ptr[2 + i]; + memcpy_ext(priv->adapter, &priv->ext_cap, temp, + sizeof(temp), sizeof(priv->ext_cap)); + } else + /* Test to see if it is a WPA IE, if not, then it is a + gen IE*/ + if (((pvendor_ie->element_id == WPA_IE) && + (!memcmp(priv->adapter, pvendor_ie->oui, wpa_oui, + sizeof(wpa_oui)))) || + (pvendor_ie->element_id == RSN_IE)) { + /* IE is a WPA/WPA2 IE so call set_wpa function */ + ret = wlan_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); + priv->wps.session_enable = MFALSE; + } else if (pvendor_ie->element_id == WAPI_IE) { + /* IE is a WAPI IE so call set_wapi function */ + ret = wlan_set_wapi_ie(priv, ie_data_ptr, ie_len); + } else if ((pvendor_ie->element_id == VENDOR_SPECIFIC_221) && + (!memcmp(priv->adapter, pvendor_ie->oui, osen_oui, + sizeof(osen_oui)))) { + /* IE is a OSEN IE so call set_osen function */ + ret = wlan_set_osen_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_ext(priv->adapter, + (t_u8 *)&priv->wps.wps_ie, + ie_data_ptr, ie_len, + sizeof(IEEEtypes_VendorSpecific_t)); + 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_ext(priv->adapter, + priv->gen_ie_buf + + priv->gen_ie_buf_len, + ie_data_ptr, ie_len, + MRVDRV_GENIE_BUF_SIZE - + priv->gen_ie_buf_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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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_EXT: + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wps_cfg *pwps = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_wps_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_wps_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + + pwps = (mlan_ds_wps_cfg *)pioctl_req->pbuf; + switch (pwps->sub_command) { + case MLAN_OID_WPS_CFG_SESSION: + if (pioctl_req->action == MLAN_ACT_SET) { + if (pwps->param.wps_session == + MLAN_WPS_CFG_SESSION_START) + pmpriv->wps.session_enable = MTRUE; + else + pmpriv->wps.session_enable = MFALSE; + } else { + pwps->param.wps_session = + (t_u32)pmpriv->wps.session_enable; + pioctl_req->data_read_written = sizeof(t_u32); + PRINTM(MINFO, "wpscfg GET=%d\n", + pwps->param.wps_session); + } + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief register memory access handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_reg_mem_ioctl(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 chan_width = CHAN_BW_20MHZ; + Band_Config_t bandcfg; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(pmadapter, &bandcfg, 0, sizeof(Band_Config_t)); + pmpriv->adhoc_state = ADHOC_STARTING; + + if ((pmadapter->adhoc_start_band & BAND_A)) { + if (pmpriv->intf_state_11h.adhoc_auto_sel_chan) + pmpriv->adhoc_channel = + wlan_11h_get_adhoc_start_channel(pmpriv); + + /* + * Check if the region and channel requires a channel + * availability check. + */ + if (wlan_11h_radar_detect_required(pmpriv, + pmpriv->adhoc_channel) && + !wlan_11h_is_channel_under_nop(pmadapter, + pmpriv->adhoc_channel)) { + /* + * Radar detection is required for this channel, make + * sure 11h is activated in the firmware + */ + ret = wlan_11h_activate(pmpriv, MNULL, MTRUE); + ret = wlan_11h_config_master_radar_det(pmpriv, MTRUE); + ret = wlan_11h_check_update_radar_det_state(pmpriv); + + /* Check for radar on the channel */ + if ((pmadapter->chan_bandwidth == + CHANNEL_BW_40MHZ_ABOVE) || + (pmadapter->chan_bandwidth == + CHANNEL_BW_40MHZ_BELOW)) { + chan_width = CHAN_BW_40MHZ; + if (pmadapter->chanrpt_param_bandcfg) { + bandcfg.chan2Offset = + pmadapter->chan_bandwidth; + } + } else if (pmadapter->chan_bandwidth == + CHANNEL_BW_80MHZ) + chan_width = CHAN_BW_80MHZ; + if (pmadapter->chanrpt_param_bandcfg) { + bandcfg.chanWidth = chan_width; + bandcfg.chanBand = BAND_5GHZ; + } else { + *((t_u8 *)&bandcfg) = chan_width; + } + + ret = wlan_11h_issue_radar_detect(pmpriv, pioctl_req, + pmpriv->adhoc_channel, + bandcfg); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief 802.11h set/get local power constraint + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status +wlan_11h_ioctl_local_power_constraint(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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; + case MLAN_OID_11H_DFS_TESTING: + status = wlan_11h_ioctl_dfs_testing(pmadapter, pioctl_req); + break; + case MLAN_OID_11H_DFS_W53_CFG: + status = wlan_11h_ioctl_dfs_w53_cfg(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + LEAVE(); + return status; +} + +/** + * @brief Set/Get 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(pmlan_adapter pmadapter, + 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_ext(pmadapter, misc->param.gen_ie.ie_data, + pmpriv->wpa_ie, misc->param.gen_ie.len, + MAX_IE_SIZE); + } else { + wlan_set_gen_ie_helper(pmpriv, + misc->param.gen_ie.ie_data, + (t_u16)misc->param.gen_ie.len); + } + break; + default: + PRINTM(MERROR, "Invalid IE type\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + } + pioctl_req->data_read_written = + sizeof(mlan_ds_misc_gen_ie) + MLAN_SUB_COMMAND_SIZE; + LEAVE(); + return ret; +} + +/** + * @brief Perform warm reset + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_ioctl_warm_reset(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + pmlan_buffer pmbuf; + t_s32 i = 0; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ENTER(); + mlan_block_rx_process(pmadapter, MTRUE); + + /* Cancel all pending commands and complete ioctls */ + if (misc->param.fw_reload) + wlan_cancel_all_pending_cmd(pmadapter, MTRUE); + + /** Init all the head nodes and free all the locks here */ + for (i = 0; i < pmadapter->priv_num; i++) + wlan_free_priv(pmadapter->priv[i]); + + while ((pmbuf = (pmlan_buffer)util_dequeue_list( + pmadapter->pmoal_handle, &pmadapter->rx_data_queue, + pcb->moal_spin_lock, pcb->moal_spin_unlock))) { + pmadapter->ops.data_complete(pmadapter, pmbuf, + MLAN_STATUS_FAILURE); + } + pmadapter->rx_pkts_queued = 0; + + /* Initialize adapter structure */ + wlan_init_adapter(pmadapter); + pmadapter->hw_status = WlanHardwareStatusInitializing; + + /* Initialize private structures */ + for (i = 0; i < pmadapter->priv_num; i++) { + if (pmadapter->priv[i]) + wlan_init_priv(pmadapter->priv[i]); + } + mlan_block_rx_process(pmadapter, MFALSE); + + if (misc->param.fw_reload != MTRUE) { + /* Restart the firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_FUNC_SHUTDOWN, + HostCmd_ACT_GEN_SET, 0, MNULL, MNULL); + if (ret) + goto done; + } + + /* Issue firmware initialize commands for first BSS, + * for other interfaces it will be called after getting + * the last init command response of previous interface + */ + pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!pmpriv) { + ret = MLAN_STATUS_FAILURE; + LEAVE(); + return ret; + } + ret = wlan_adapter_get_hw_spec(pmpriv->adapter); + if (ret == MLAN_STATUS_FAILURE) { + LEAVE(); + return ret; + } + ret = pmpriv->ops.init_cmd(pmpriv, MTRUE); + if (ret == MLAN_STATUS_FAILURE) { + LEAVE(); + return ret; + } + if (ret == MLAN_STATUS_PENDING) + pmadapter->pwarm_reset_ioctl_req = pioctl_req; +done: + LEAVE(); + return ret; +} + +#ifdef SDIO +/** + * @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(pmlan_adapter pmadapter, + 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->pcard_sd->mpa_tx.buf_size = + mpa_ctrl->tx_buf_size; + + if (mpa_ctrl->rx_buf_size > 0) + pmadapter->pcard_sd->mpa_rx.buf_size = + mpa_ctrl->rx_buf_size; + + if (wlan_alloc_sdio_mpa_buffers( + pmadapter, + pmadapter->pcard_sd->mpa_tx.buf_size, + pmadapter->pcard_sd->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->pcard_sd->mpa_tx.pkt_aggr_limit = + mpa_ctrl->tx_max_ports; + if (mpa_ctrl->rx_max_ports > 0) + pmadapter->pcard_sd->mpa_rx.pkt_aggr_limit = + mpa_ctrl->rx_max_ports; + + pmadapter->pcard_sd->mpa_tx.enabled = (t_u8)mpa_ctrl->tx_enable; + pmadapter->pcard_sd->mpa_rx.enabled = (t_u8)mpa_ctrl->rx_enable; + + } else { + mpa_ctrl->tx_enable = + (t_u16)pmadapter->pcard_sd->mpa_tx.enabled; + mpa_ctrl->rx_enable = + (t_u16)pmadapter->pcard_sd->mpa_rx.enabled; + mpa_ctrl->tx_buf_size = + (t_u16)pmadapter->pcard_sd->mpa_tx.buf_size; + mpa_ctrl->rx_buf_size = + (t_u16)pmadapter->pcard_sd->mpa_rx.buf_size; + mpa_ctrl->tx_max_ports = + (t_u16)pmadapter->pcard_sd->mpa_tx.pkt_aggr_limit; + mpa_ctrl->rx_max_ports = + (t_u16)pmadapter->pcard_sd->mpa_rx.pkt_aggr_limit; + } + +exit: + LEAVE(); + return ret; +} +#endif + +/** + * @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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else + cmd_action = HostCmd_ACT_GEN_SET; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_ECL_SYSTEM_CLOCK_CONFIG, + cmd_action, 0, (t_void *)pioctl_req, + (t_void *)&misc->param.sys_clock); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get the associate response + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_misc_ioctl_get_assoc_rsp(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_misc_cfg *misc = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if ((pioctl_req->action == MLAN_ACT_GET) && pmpriv->assoc_rsp_size) { + memcpy_ext(pmadapter, misc->param.assoc_resp.assoc_resp_buf, + pmpriv->assoc_rsp_buf, pmpriv->assoc_rsp_size, + ASSOC_RSP_BUF_SIZE); + misc->param.assoc_resp.assoc_resp_len = + MIN(ASSOC_RSP_BUF_SIZE, pmpriv->assoc_rsp_size); + } + + LEAVE(); + return ret; +} + +/** + * @brief Send function softreset command to firmware + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_misc_ioctl_soft_reset(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_SOFT_RESET, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +/** + * @brief Get the thermal reading + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING -- success, otherwise fail + */ +mlan_status wlan_misc_ioctl_thermal(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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; +} + +#define FLTR_BUF_IP_OFFSET 24 +#define FLTR_BUF_IP_OFFSET_2_IP_1 9 +#define FLTR_BUF_IP_OFFSET_2_IP_2 26 + +/** + * @brief Enable/Disable Auto ARP resonse + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * @param ipv4_addr ipv4 Address + * @param num_ipv4 Number of ipv4 Addresses + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_ipaddr_auto_arp_resp(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req, + t_u32 *ipv4_addr, t_u8 num_ipv4) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_GEN *hostcmd_hdr; + HostCmd_DS_MEF_CFG *mefcmd; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_ds_misc_cmd *hostcmd; + t_u32 buf_len = 0; + t_u8 *buf, *filter; + + t_u8 fltr_buf[] = {0x01, 0x10, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x08, 0x06, 0x02, 0x02, 0x14, 0x00, 0x00, + 0x00, 0x01, 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, + 0xc0, 0xa8, 0x01, 0x6d, 0x04, 0x02, 0x2e, 0x00, + 0x00, 0x00, 0x01, 0x41, 0x44}; + t_u8 fltr_buf_2_ip[] = {0x01, 0x10, 0x33, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0xc0, 0xa8, 0x01, 0x6d, 0x04, 0x02, 0x2e, + 0x00, 0x00, 0x00, 0x01, 0x41, 0x01, 0x00, 0x00, + 0x00, 0x01, 0xc0, 0xa8, 0x02, 0x6d, 0x04, 0x02, + 0x2e, 0x00, 0x00, 0x00, 0x01, 0x41, 0x45, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0x02, 0x02, + 0x14, 0x00, 0x00, 0x00, 0x01, 0x41, 0x44}; + + ENTER(); + + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_ds_misc_cmd), MLAN_MEM_DEF, + (t_u8 **)&hostcmd); + + if (ret != MLAN_STATUS_SUCCESS || hostcmd == MNULL) { + PRINTM(MERROR, "Failed to allocate hostcmd buffer\n"); + pioctl_req->status_code = MLAN_ERROR_NO_MEM; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(pmpriv->adapter, hostcmd, 0, sizeof(mlan_ds_misc_cmd)); + buf = hostcmd->cmd; + + /* Prepare hostcmd buffer */ + hostcmd_hdr = (HostCmd_DS_GEN *)(buf); + hostcmd_hdr->command = wlan_cpu_to_le16(HostCmd_CMD_MEF_CFG); + mefcmd = (HostCmd_DS_MEF_CFG *)(buf + S_DS_GEN); + buf_len = S_DS_GEN; + + if (!ipv4_addr) { + PRINTM(MINFO, "Disable Auto ARP Response\n"); + mefcmd->criteria = wlan_cpu_to_le32(0); + mefcmd->nentries = wlan_cpu_to_le16(0); + buf_len += sizeof(HostCmd_DS_MEF_CFG); + } else { + /* Event bit (bit2) of HS conditions should be masked out */ + mefcmd->criteria = wlan_cpu_to_le32( + pmpriv->adapter->hs_cfg.conditions & ~MBIT(2)); + mefcmd->nentries = wlan_cpu_to_le16(1); + buf_len += sizeof(HostCmd_DS_MEF_CFG); + filter = buf + buf_len; + if (num_ipv4 == 1) { + memcpy_ext(pmpriv->adapter, filter, fltr_buf, + sizeof(fltr_buf), sizeof(fltr_buf)); + memcpy_ext(pmpriv->adapter, &filter[FLTR_BUF_IP_OFFSET], + &ipv4_addr[0], sizeof(t_u32), sizeof(t_u32)); + buf_len += sizeof(fltr_buf); + } else if (num_ipv4 >= 2) { + memcpy_ext(pmpriv->adapter, filter, fltr_buf_2_ip, + sizeof(fltr_buf_2_ip), + sizeof(fltr_buf_2_ip)); + memcpy_ext(pmpriv->adapter, + &filter[FLTR_BUF_IP_OFFSET_2_IP_1], + &ipv4_addr[0], sizeof(t_u32), sizeof(t_u32)); + memcpy_ext(pmpriv->adapter, + &filter[FLTR_BUF_IP_OFFSET_2_IP_2], + &ipv4_addr[1], sizeof(t_u32), sizeof(t_u32)); + buf_len += sizeof(fltr_buf_2_ip); + } + } + hostcmd_hdr->size = wlan_cpu_to_le16(buf_len); + hostcmd->len = buf_len; + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, 0, 0, 0, (t_void *)pioctl_req, + (t_void *)hostcmd); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + pcb->moal_mfree(pmadapter->pmoal_handle, (t_u8 *)hostcmd); + + LEAVE(); + return ret; +} + +/** + * @brief MEF configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_mef_cfg(pmlan_adapter pmadapter, + 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_ext(pmpriv->adapter, filter, fltr_buf, sizeof(fltr_buf), + MRVDRV_SIZE_OF_CMD_BUFFER - buf_len); + 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_ext(pmpriv->adapter, filter, mef_cfg->param.cmd_buf.cmd, + mef_cfg->param.cmd_buf.len, + MRVDRV_SIZE_OF_CMD_BUFFER - 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 ipv4_addr[MAX_IPADDR] = {0}; + int i = 0; + + ENTER(); + + /* GET operation */ + if (pioctl_req->action == MLAN_ACT_GET) { + memcpy_ext(pmadapter, misc->param.ipaddr_cfg.ip_addr, + pmpriv->ip_addr, IPADDR_LEN, IPADDR_LEN); + misc->param.ipaddr_cfg.op_code = pmpriv->op_code; + goto done; + } + /* only one IP is supported in current firmware */ + for (i = 0; i < misc->param.ipaddr_cfg.ip_addr_num; i++) { + memcpy_ext(pmadapter, &ipv4_addr[i], + misc->param.ipaddr_cfg.ip_addr[i], sizeof(t_u32), + sizeof(t_u32)); + } + + if (misc->param.ipaddr_cfg.op_code != MLAN_IPADDR_OP_IP_REMOVE && + !misc->param.ipaddr_cfg.ip_addr_num) { + PRINTM(MERROR, "Invalid IPv4 address\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Save the values in MLAN */ + if (pioctl_req->action == MLAN_ACT_SET) { + pmpriv->op_code = misc->param.ipaddr_cfg.op_code; + memcpy_ext(pmadapter, pmpriv->ip_addr, + misc->param.ipaddr_cfg.ip_addr, IPADDR_LEN, + IPADDR_LEN); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief CFP code configuration + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_cfp_code_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_ds_misc_cfp_code *cfp_code = MNULL; + t_u32 region_bg = 0; + t_u32 region_a = 0; + int i; + + ENTER(); + + cfp_code = &misc->param.cfp_code; + if (pioctl_req->action == MLAN_ACT_SET) { + if (pmadapter->otp_region && pmadapter->otp_region->force_reg) { + PRINTM(MERROR, + "ForceRegionRule is set in the on-chip OTP" + "memory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Save the values in MLAN */ + if (!cfp_code->cfp_code_bg) + cfp_code->cfp_code_bg = pmadapter->cfp_code_bg; + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (cfp_code->cfp_code_bg == region_code_index[i]) { + region_bg = cfp_code->cfp_code_bg; + break; + } + } + if (!cfp_code->cfp_code_a) + cfp_code->cfp_code_a = pmadapter->cfp_code_a; + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* Use the region code to search for the index */ + if (cfp_code->cfp_code_a == region_code_index[i]) { + region_a = cfp_code->cfp_code_a; + break; + } + } + if (!region_a) { + for (i = 0; i < MRVDRV_MAX_CFP_CODE_A; i++) { + /* Use the CFP code to search for the index */ + if (cfp_code->cfp_code_a == cfp_code_index_a[i]) + break; + } + if (i >= MRVDRV_MAX_CFP_CODE_A) { + PRINTM(MERROR, + "CFP Code not identified for A\n"); + pioctl_req->status_code = + MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + pmadapter->cfp_code_bg = (t_u8)cfp_code->cfp_code_bg; + pmadapter->cfp_code_a = (t_u8)cfp_code->cfp_code_a; + if (region_bg && region_a && (region_bg == region_a)) + pmadapter->region_code = pmadapter->cfp_code_a; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, (t_u8)pmadapter->region_code, + pmadapter->config_bands | + pmadapter->adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else { + /* GET operation */ + cfp_code->cfp_code_bg = pmadapter->cfp_code_bg; + cfp_code->cfp_code_a = pmadapter->cfp_code_a; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sets up country code and downloads CMD to FW + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_ioctl_country_code(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_country_code *country_code = MNULL; + mlan_ds_misc_cfg *cfg_misc = MNULL; + t_u8 cfp_bg = 0, cfp_a = 0; + + ENTER(); + + cfg_misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + country_code = &cfg_misc->param.country_code; + + if (pioctl_req->action == MLAN_ACT_SET) { + if (pmadapter->otp_region && pmadapter->otp_region->force_reg) { + PRINTM(MERROR, + "ForceRegionRule is set in the on-chip OTP" + "memory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Update region code and table based on country code */ + if (wlan_misc_country_2_cfp_table_code( + pmadapter, country_code->country_code, &cfp_bg, + &cfp_a)) { + PRINTM(MERROR, "Country code not found!\n"); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmadapter->cfp_code_bg = cfp_bg; + pmadapter->cfp_code_a = cfp_a; + if (cfp_a) + pmadapter->region_code = cfp_a; + else if (cfp_bg) + pmadapter->region_code = cfp_bg; + else + pmadapter->region_code = 0; + if (wlan_set_regiontable(pmpriv, pmadapter->region_code, + pmadapter->config_bands | + pmadapter->adhoc_start_band)) { + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_FAILURE; + goto done; + } + memcpy_ext(pmadapter, pmadapter->country_code, + country_code->country_code, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + } else { + /* GET operation */ + memcpy_ext(pmadapter, country_code->country_code, + pmadapter->country_code, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Configure MFPC and MFPR for management frame protection + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_pmfcfg(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *cfg_misc = MNULL; + mlan_ds_misc_pmfcfg *pmfcfg; + + cfg_misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + pmfcfg = &cfg_misc->param.pmfcfg; + + if (pioctl_req->action == MLAN_ACT_SET) { + pmpriv->pmfcfg.mfpc = pmfcfg->mfpc; + pmpriv->pmfcfg.mfpr = pmfcfg->mfpr; + } else { + /* GET operation */ + pmfcfg->mfpc = pmpriv->pmfcfg.mfpc; + pmfcfg->mfpr = pmpriv->pmfcfg.mfpr; + } + + LEAVE(); + return ret; +} + +/** + * @brief HW ARB Cfg + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_arb_cfg(pmlan_adapter pmadapter, + 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_ARB_CONFIG, cmd_action, 0, + (t_void *)pioctl_req, &(pmisc->param.arb_cfg)); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Save tp accounting command 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 + */ +mlan_status wlan_misc_ioctl_tp_state(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_ds_misc_cfg *pmisc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + pmadapter->tp_state_on = pmisc->param.tp_state.on; + pmadapter->tp_state_drop_point = pmisc->param.tp_state.drop_point; + + LEAVE(); + return ret; +} + +mlan_status wlan_misc_ioctl_get_sensor_temp(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_GET) + cmd_action = HostCmd_ACT_GEN_GET; + else { + PRINTM(MERROR, " sensor temp only support get operation \n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_DS_GET_SENSOR_TEMP, cmd_action, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Gtk Rekey Offload + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_misc_ioctl_gtk_rekey_offload(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = MNULL; + t_u16 cmd_action = 0; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else if (pioctl_req->action == MLAN_ACT_CLEAR) + cmd_action = HostCmd_ACT_GEN_REMOVE; + else + cmd_action = HostCmd_ACT_GEN_GET; + + if (!pmpriv->wpa_is_gtk_set) { + /* Store the gtk rekey data if it has already set gtk */ + memcpy_ext(pmadapter, &pmpriv->gtk_rekey, + &misc_cfg->param.gtk_rekey, + sizeof(mlan_ds_misc_gtk_rekey_data), + sizeof(mlan_ds_misc_gtk_rekey_data)); + LEAVE(); + return ret; + } + /* Send request to firmware if it hasn't set gtk yet */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_GTK_REKEY_OFFLOAD_CFG, + cmd_action, 0, (t_void *)pioctl_req, + &misc_cfg->param.gtk_rekey); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief cloud keep alive + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req Pointer to the IOCTL request buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_misc_cloud_keep_alive(pmlan_adapter pmadapter, + mlan_ioctl_req *pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *misc = MNULL; + t_u16 cmd_action = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else if (pioctl_req->action == MLAN_ACT_GET) { + cmd_action = HostCmd_ACT_GEN_GET; + } else if (pioctl_req->action == MLAN_ACT_RESET) { + cmd_action = HostCmd_ACT_GEN_RESET; + } else { + cmd_action = HostCmd_ACT_GEN_REMOVE; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_AUTO_TX, cmd_action, + OID_CLOUD_KEEP_ALIVE, (t_void *)pioctl_req, + &misc->param.keep_alive); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Miscellaneous configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +static mlan_status wlan_misc_cfg_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = MNULL; + + ENTER(); + + if ((pioctl_req == MNULL) || (pioctl_req->pbuf == MNULL)) { + PRINTM(MERROR, "Request buffer not found!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (pioctl_req->buf_len < sizeof(mlan_ds_misc_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_misc_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + switch (misc->sub_command) { + case MLAN_OID_MISC_GEN_IE: + status = wlan_misc_ioctl_gen_ie(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_REGION: + status = wlan_misc_ioctl_region(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_WARM_RESET: + util_enqueue_list_tail(pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + (pmlan_linked_list)pioctl_req, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + pmadapter->pending_ioctl = MTRUE; + status = MLAN_STATUS_PENDING; + break; +#ifdef SDIO + case MLAN_OID_MISC_SDIO_MPA_CTRL: + status = wlan_misc_ioctl_sdio_mpa_ctrl(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_MISC_HOST_CMD: + status = wlan_misc_ioctl_host_cmd(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_SYS_CLOCK: + status = wlan_misc_ioctl_sysclock(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_WWS: + status = wlan_misc_ioctl_wws_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_ASSOC_RSP: + status = wlan_misc_ioctl_get_assoc_rsp(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_INIT_SHUTDOWN: + status = wlan_misc_ioctl_init_shutdown(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_SOFT_RESET: + status = wlan_misc_ioctl_soft_reset(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_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_CFP_CODE: + status = wlan_misc_ioctl_cfp_code_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_COUNTRY_CODE: + status = wlan_misc_ioctl_country_code(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_THERMAL: + status = wlan_misc_ioctl_thermal(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_SUBSCRIBE_EVENT: + status = wlan_misc_ioctl_subscribe_evt(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_HOTSPOT_CFG: + status = wlan_misc_hotspot_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_OTP_USER_DATA: + status = wlan_misc_otp_user_data(pmadapter, pioctl_req); + break; +#ifdef USB + case MLAN_OID_MISC_USB_AGGR_CTRL: + status = wlan_misc_ioctl_usb_aggr_ctrl(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_MISC_AGGR_CTRL: + status = wlan_misc_ioctl_aggr_ctrl(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_TXCONTROL: + status = wlan_misc_ioctl_txcontrol(pmadapter, pioctl_req); + break; +#ifdef STA_SUPPORT + case MLAN_OID_MISC_EXT_CAP_CFG: + status = wlan_misc_ext_capa_cfg(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_MISC_PMFCFG: + status = wlan_misc_pmfcfg(pmadapter, pioctl_req); + break; +#ifdef RX_PACKET_COALESCE + case MLAN_OID_MISC_RX_PACKET_COALESCE: + status = wlan_misc_ioctl_rx_pkt_coalesce_config(pmadapter, + pioctl_req); + break; +#endif + case MLAN_OID_MISC_LOW_PWR_MODE: + status = wlan_misc_ioctl_low_pwr_mode(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_PMIC_CFG: + status = wlan_misc_ioctl_pmic_configure(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_CWMODE_CTRL: + status = wlan_misc_ioctl_cwmode_ctrl(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_MEF_FLT_CFG: + status = wlan_misc_ioctl_mef_flt_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_DFS_REAPTER_MODE: + status = + wlan_misc_ioctl_dfs_repeater_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_COALESCE_CFG: + status = wlan_misc_ioctl_coalesce_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_SENSOR_TEMP: + status = wlan_misc_ioctl_get_sensor_temp(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_OPER_CLASS: + status = wlan_misc_ioctl_oper_class(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_OPER_CLASS_CHECK: + status = wlan_misc_ioctl_operclass_validation(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_GTK_REKEY_OFFLOAD: + status = wlan_misc_ioctl_gtk_rekey_offload(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_IND_RST_CFG: + status = wlan_misc_ioctl_ind_rst_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_TSF: + status = wlan_misc_ioctl_get_tsf(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_CHAN_REGION_CFG: + status = wlan_misc_chan_reg_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_CLOUD_KEEP_ALIVE: + status = wlan_misc_cloud_keep_alive(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_DYN_BW: + status = wlan_misc_ioctl_dyn_bw(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_FW_DUMP_EVENT: + status = wlan_misc_ioctl_fw_dump_event(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_PER_PKT_CFG: + status = wlan_misc_per_pkt_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_ROBUSTCOEX: + status = wlan_misc_robustcoex(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_DMCS_CONFIG: + status = wlan_misc_dmcs_config(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_TX_RX_HISTOGRAM: + status = wlan_get_tx_rx_histogram(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_BOOT_SLEEP: + status = wlan_misc_bootsleep(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_CFP_INFO: + status = wlan_get_cfpinfo(pmadapter, pioctl_req); + break; +#if defined(PCIE) + case MLAN_OID_MISC_SSU: + if (pmadapter->ssu_buf) + status = MLAN_STATUS_FAILURE; + else + status = wlan_misc_ssu(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_MISC_RX_ABORT_CFG: + status = wlan_misc_ioctl_rxabortcfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_RX_ABORT_CFG_EXT: + status = wlan_misc_ioctl_rxabortcfg_ext(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_TX_AMPDU_PROT_MODE: + status = wlan_misc_ioctl_tx_ampdu_prot_mode(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_DOT11MC_UNASSOC_FTM_CFG: + status = wlan_misc_ioctl_dot11mc_unassoc_ftm_cfg(pmadapter, + pioctl_req); + break; + case MLAN_OID_MISC_RATE_ADAPT_CFG: + status = wlan_misc_ioctl_rate_adapt_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_CCK_DESENSE_CFG: + status = wlan_misc_ioctl_cck_desense_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_REGIONPWR_CFG: + status = wlan_get_rgchnpwr_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_GET_CHAN_TRPC_CFG: + status = wlan_get_chan_trpc_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_RF_TEST_GENERIC: + case MLAN_OID_MISC_RF_TEST_TX_CONT: + case MLAN_OID_MISC_RF_TEST_TX_FRAME: + status = wlan_misc_ioctl_rf_test_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_ARB_CONFIG: + status = wlan_misc_ioctl_arb_cfg(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_RANGE_EXT: + status = wlan_misc_ioctl_range_ext(pmadapter, pioctl_req); + break; + case MLAN_OID_MISC_TP_STATE: + status = wlan_misc_ioctl_tp_state(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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req, + t_u32 action) +{ + mlan_ds_scan *scan = MNULL; + + ENTER(); + + scan = (mlan_ds_scan *)pioctl_req->pbuf; + if (action == MLAN_ACT_SET) { + 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; + if (scan->param.scan_cfg.passive_to_active_scan) + pmadapter->passive_to_active_scan = + scan->param.scan_cfg.passive_to_active_scan; + if (scan->param.scan_cfg.ext_scan) + pmadapter->ext_scan = scan->param.scan_cfg.ext_scan - 1; + pmadapter->scan_chan_gap = scan->param.scan_cfg.scan_chan_gap; + } + 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.passive_to_active_scan = + pmadapter->passive_to_active_scan; + scan->param.scan_cfg.ext_scan = pmadapter->ext_scan + 1; + scan->param.scan_cfg.scan_chan_gap = pmadapter->scan_chan_gap; + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_scan *pscan; + + ENTER(); + + pscan = (mlan_ds_scan *)pioctl_req->pbuf; + if (pscan->sub_command == MLAN_OID_SCAN_CONFIG || + pscan->sub_command == MLAN_OID_SCAN_BGSCAN_CONFIG) + goto start_config; + if (pmadapter->scan_processing && pioctl_req->action == MLAN_ACT_SET && + pscan->sub_command != MLAN_OID_SCAN_CANCEL) { + PRINTM(MINFO, "Scan already in process...\n"); + LEAVE(); + return status; + } + + if (pmadapter->scan_block && pioctl_req->action == MLAN_ACT_SET) { + PRINTM(MERROR, "Scan is blocked during association...\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +start_config: + /* Set scan */ + if (pioctl_req->action == MLAN_ACT_SET) { + switch (pscan->sub_command) { + case MLAN_OID_SCAN_NORMAL: + status = wlan_scan_networks(pmpriv, pioctl_req, MNULL); + break; + case MLAN_OID_SCAN_SPECIFIC_SSID: + status = wlan_scan_specific_ssid( + pmpriv, pioctl_req, + &pscan->param.scan_req.scan_ssid); + break; + case MLAN_OID_SCAN_USER_CONFIG: + status = wlan_scan_networks( + pmpriv, pioctl_req, + (wlan_user_scan_cfg *) + pscan->param.user_scan.scan_cfg_buf); + break; + case MLAN_OID_SCAN_CONFIG: + status = wlan_set_get_scan_cfg(pmadapter, pioctl_req, + MLAN_ACT_SET); + break; + case MLAN_OID_SCAN_CANCEL: + status = wlan_cancel_pending_scan_cmd(pmadapter, + pioctl_req); + break; + case MLAN_OID_SCAN_TABLE_FLUSH: + status = wlan_flush_scan_table(pmadapter); + break; + case MLAN_OID_SCAN_BGSCAN_CONFIG: + /* Send request to firmware */ + status = wlan_prepare_cmd( + pmpriv, HostCmd_CMD_802_11_BG_SCAN_CONFIG, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + pscan->param.user_scan.scan_cfg_buf); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + + if ((status == MLAN_STATUS_SUCCESS) && + (pscan->sub_command != MLAN_OID_SCAN_TABLE_FLUSH) && + (pscan->sub_command != MLAN_OID_SCAN_CANCEL) && + (pscan->sub_command != MLAN_OID_SCAN_CONFIG)) { + PRINTM(MINFO, + "wlan_scan_ioctl: return MLAN_STATUS_PENDING\n"); + status = MLAN_STATUS_PENDING; + } + } + /* Get scan */ + else { + if (pscan->sub_command == MLAN_OID_SCAN_CONFIG) { + status = wlan_set_get_scan_cfg(pmadapter, pioctl_req, + MLAN_ACT_GET); + } else if (pscan->sub_command == + MLAN_OID_SCAN_GET_CURRENT_BSS) { + pscan->param.scan_resp.num_in_scan_table = + pmadapter->num_in_scan_table; + pscan->param.scan_resp.pscan_table = + (t_u8 *)&pmpriv->curr_bss_params.bss_descriptor; + pioctl_req->data_read_written = + sizeof(mlan_scan_resp) + MLAN_SUB_COMMAND_SIZE; + } else { + if (pmadapter->bgscan_reported) { + pmadapter->bgscan_reported = MFALSE; + /* Clear the previous scan result */ + memset(pmadapter, pmadapter->pscan_table, 0x00, + sizeof(BSSDescriptor_t) * + MRVDRV_MAX_BSSID_LIST); + pmadapter->num_in_scan_table = 0; + pmadapter->pbcn_buf_end = pmadapter->bcn_buf; + status = wlan_prepare_cmd( + pmpriv, + HostCmd_CMD_802_11_BG_SCAN_QUERY, + HostCmd_ACT_GEN_GET, 0, + (t_void *)pioctl_req, MNULL); + if (status == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, + "wlan_scan_ioctl: return MLAN_STATUS_PENDING\n"); + status = MLAN_STATUS_PENDING; + } + } else { + pscan->param.scan_resp.pscan_table = + (t_u8 *)pmadapter->pscan_table; + pscan->param.scan_resp.num_in_scan_table = + pmadapter->num_in_scan_table; + pscan->param.scan_resp.age_in_secs = + pmadapter->age_in_secs; + pioctl_req->data_read_written = + sizeof(mlan_scan_resp) + + MLAN_SUB_COMMAND_SIZE; + pscan->param.scan_resp.pchan_stats = + (t_u8 *)pmadapter->pchan_stats; + pscan->param.scan_resp.num_in_chan_stats = + pmadapter->num_in_chan_stats; + } + } + } + + LEAVE(); + return status; +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Set ewpa mode + * + * @param priv A pointer to mlan_private structure + * @param psec_pp A pointer to mlan_ds_passphrase structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_set_ewpa_mode(mlan_private *priv, mlan_ds_passphrase *psec_pp) +{ + ENTER(); + + if ((psec_pp->psk_type == MLAN_PSK_PASSPHRASE && + psec_pp->psk.passphrase.passphrase_len > 0) || + (psec_pp->psk_type == MLAN_PSK_PMK)) + priv->sec_info.ewpa_enabled = MTRUE; + else + priv->sec_info.ewpa_enabled = MFALSE; + + PRINTM(MINFO, "Set ewpa mode = %d\n", priv->sec_info.ewpa_enabled); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Search for a BSS + * + * @param pmpriv A pointer to mlan_private structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise + * fail + */ +mlan_status wlan_find_bss(mlan_private *pmpriv, pmlan_ioctl_req pioctl_req) +{ + mlan_adapter *pmadapter = pmpriv->adapter; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + t_u8 mac[MLAN_MAC_ADDR_LENGTH]; + int i = 0; + BSSDescriptor_t *pbss_desc = MNULL; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + + if (memcmp(pmadapter, &bss->param.ssid_bssid.bssid, zero_mac, + sizeof(zero_mac))) { + if (bss->param.ssid_bssid.ssid.ssid_len) /* ssid & bssid */ + i = wlan_find_ssid_in_list( + pmpriv, &bss->param.ssid_bssid.ssid, + (t_u8 *)&bss->param.ssid_bssid.bssid, + pmpriv->bss_mode); + else + i = wlan_find_bssid_in_list( + pmpriv, (t_u8 *)&bss->param.ssid_bssid.bssid, + pmpriv->bss_mode); + if (i < 0) { + memcpy_ext(pmadapter, mac, &bss->param.ssid_bssid.bssid, + sizeof(mac), MLAN_MAC_ADDR_LENGTH); + PRINTM(MIOCTL, "Can not find bssid " MACSTR "\n", + MAC2STR(mac)); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pbss_desc = &pmadapter->pscan_table[i]; + memcpy_ext(pmadapter, &bss->param.ssid_bssid.ssid, + &pbss_desc->ssid, sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + bss->param.ssid_bssid.rssi = pbss_desc->rssi; + bss->param.ssid_bssid.channel = (t_u16)pbss_desc->channel; + + bss->param.ssid_bssid.bss_band = pbss_desc->bss_band; + /* index in bss list,start from 1 */ + bss->param.ssid_bssid.idx = i + 1; + } else if (bss->param.ssid_bssid.ssid.ssid_len) { + i = wlan_find_ssid_in_list(pmpriv, &bss->param.ssid_bssid.ssid, + MNULL, pmpriv->bss_mode); + if (i < 0) { + PRINTM(MIOCTL, "Can not find ssid %s\n", + bss->param.ssid_bssid.ssid.ssid); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pbss_desc = &pmadapter->pscan_table[i]; + memcpy_ext(pmadapter, (t_u8 *)&bss->param.ssid_bssid.bssid, + (t_u8 *)&pbss_desc->mac_address, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + bss->param.ssid_bssid.rssi = pbss_desc->rssi; + bss->param.ssid_bssid.channel = (t_u16)pbss_desc->channel; + + bss->param.ssid_bssid.bss_band = pbss_desc->bss_band; + /* index in bss list, start from 1 */ + bss->param.ssid_bssid.idx = i + 1; + } else { + ret = wlan_find_best_network(pmpriv, &bss->param.ssid_bssid); + } + + if (pbss_desc) { + /**if rsn do not have ft akm, don't set ft cap and ft md*/ + if (pbss_desc->pmd_ie && + wlan_ft_akm_is_used(pmpriv, (t_u8 *)pbss_desc->prsn_ie)) { + bss->param.ssid_bssid.ft_md = pbss_desc->pmd_ie->mdid; + bss->param.ssid_bssid.ft_cap = + pbss_desc->pmd_ie->ft_cap; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief MLAN station ioctl handler + * + * @param adapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, + * otherwise fail + */ +mlan_status wlan_ops_sta_ioctl(t_void *adapter, pmlan_ioctl_req pioctl_req) +{ + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + switch (pioctl_req->req_id) { + case MLAN_IOCTL_SCAN: + status = wlan_scan_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_BSS: + status = wlan_bss_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_RADIO_CFG: + status = wlan_radio_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_SNMP_MIB: + status = wlan_snmp_mib_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_GET_INFO: + status = wlan_get_info_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_SEC_CFG: + status = wlan_sec_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_RATE: + status = wlan_rate_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_POWER_CFG: + status = wlan_power_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_PM_CFG: + status = wlan_pm_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_WMM_CFG: + status = wlan_wmm_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_WPS_CFG: + status = wlan_wps_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11N_CFG: + status = wlan_11n_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11D_CFG: + status = wlan_11d_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_REG_MEM: + status = wlan_reg_mem_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_MISC_CFG: + status = wlan_misc_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11H_CFG: + status = wlan_11h_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11AC_CFG: + status = wlan_11ac_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11AX_CFG: + status = wlan_11ax_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/mxm_wifiex/wlan_src/mlan/mlan_sta_rx.c b/mxm_wifiex/wlan_src/mlan/mlan_sta_rx.c new file mode 100644 index 0000000..af9a143 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_sta_rx.c @@ -0,0 +1,488 @@ +/** @file mlan_sta_rx.c + * + * @brief This file contains the handling of RX in MLAN + * module. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/27/2008: initial version +********************************************************/ + +#include "mlan.h" +#include "mlan_join.h" +#include "mlan_util.h" +#include "mlan_fw.h" +#include "mlan_main.h" +#include "mlan_11n_aggr.h" +#include "mlan_11n_rxreorder.h" +#ifdef DRV_EMBEDDED_SUPPLICANT +#include "authenticator_api.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/** Ethernet II header */ +typedef struct { + /** Ethernet II header destination address */ + t_u8 dest_addr[MLAN_MAC_ADDR_LENGTH]; + /** Ethernet II header source address */ + t_u8 src_addr[MLAN_MAC_ADDR_LENGTH]; + /** Ethernet II header length */ + t_u16 ethertype; + +} EthII_Hdr_t; + +/** IPv4 ARP request header */ +typedef MLAN_PACK_START struct { + /** Hardware type */ + t_u16 Htype; + /** Protocol type */ + t_u16 Ptype; + /** Hardware address length */ + t_u8 addr_len; + /** Protocol address length */ + t_u8 proto_len; + /** Operation code */ + t_u16 op_code; + /** Source mac address */ + t_u8 src_mac[MLAN_MAC_ADDR_LENGTH]; + /** Sender IP address */ + t_u8 src_ip[4]; + /** Destination mac address */ + t_u8 dst_mac[MLAN_MAC_ADDR_LENGTH]; + /** Destination IP address */ + t_u8 dst_ip[4]; +} MLAN_PACK_END IPv4_ARP_t; + +/** IPv6 Nadv packet header */ +typedef MLAN_PACK_START struct { + /** IP protocol version */ + t_u8 version; + /** flow label */ + t_u8 flow_lab[3]; + /** Payload length */ + t_u16 payload_len; + /** Next header type */ + t_u8 next_hdr; + /** Hot limit */ + t_u8 hop_limit; + /** Source address */ + t_u8 src_addr[16]; + /** Destination address */ + t_u8 dst_addr[16]; + /** ICMP type */ + t_u8 icmp_type; + /** IPv6 Code */ + t_u8 ipv6_code; + /** IPv6 Checksum */ + t_u16 ipv6_checksum; + /** Flags */ + t_u32 flags; + /** Target address */ + t_u8 taget_addr[16]; + /** Reserved */ + t_u8 rev[8]; +} MLAN_PACK_END IPv6_Nadv_t; + +/******************************************************** + Global functions +********************************************************/ +/** + * @brief This function check and discard IPv4 and IPv6 gratuitous broadcast + * packets + * + * @param prx_pkt A pointer to RxPacketHdr_t structure of received packet + * @param pmadapter A pointer to pmlan_adapter structure + * @return TRUE if found such type of packets, FALSE not found + */ +static t_u8 discard_gratuitous_ARP_msg(RxPacketHdr_t *prx_pkt, + pmlan_adapter pmadapter) +{ + t_u8 proto_ARP_type[] = {0x08, 0x06}; + t_u8 proto_ARP_type_v6[] = {0x86, 0xDD}; + IPv4_ARP_t *parp_hdr; + IPv6_Nadv_t *pNadv_hdr; + t_u8 ret = MFALSE; + + /* IPV4 pkt check + * A gratuitous ARP is an ARP packet + * where the source and destination IP are both set to + * the IP of the machine issuing the packet. + */ + if (memcmp(pmadapter, proto_ARP_type, &prx_pkt->eth803_hdr.h803_len, + sizeof(proto_ARP_type)) == 0) { + parp_hdr = (IPv4_ARP_t *)(&prx_pkt->rfc1042_hdr); + /* Graguitous ARP can be ARP request or ARP reply*/ + if ((parp_hdr->op_code == mlan_htons(0x01)) || + (parp_hdr->op_code == mlan_htons(0x02))) + if (memcmp(pmadapter, parp_hdr->src_ip, + parp_hdr->dst_ip, 4) == 0) + ret = MTRUE; + } + + /* IPV6 pkt check + * An unsolicited Neighbor Advertisement pkt is + * marked by a cleared Solicited Flag + */ + if (memcmp(pmadapter, proto_ARP_type_v6, &prx_pkt->eth803_hdr.h803_len, + sizeof(proto_ARP_type_v6)) == 0) { + pNadv_hdr = (IPv6_Nadv_t *)(&prx_pkt->rfc1042_hdr); + /* Check Nadv type: next header is ICMPv6 and + * icmp type is Nadv */ + if (pNadv_hdr->next_hdr == 0x3A && pNadv_hdr->icmp_type == 0x88) + if ((pNadv_hdr->flags & mlan_htonl(0x40000000)) == 0) + ret = MTRUE; + } + + return ret; +} + +/** + * @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}; +#ifdef DRV_EMBEDDED_SUPPLICANT + t_u8 eapol_type[2] = {0x88, 0x8e}; +#endif + t_u8 ext_rate_info = 0; + + ENTER(); + + prx_pd = (RxPD *)(pmbuf->pbuf + pmbuf->data_offset); + prx_pkt = (RxPacketHdr_t *)((t_u8 *)prx_pd + prx_pd->rx_pkt_offset); + +/** Small debug type */ +#define DBG_TYPE_SMALL 2 +/** Size of debugging structure */ +#define SIZE_OF_DBG_STRUCT 4 + if (prx_pd->rx_pkt_type == PKT_TYPE_DEBUG) { + t_u8 dbg_type; + dbg_type = *(t_u8 *)&prx_pkt->eth803_hdr; + if (dbg_type == DBG_TYPE_SMALL) { + PRINTM(MFW_D, "\n"); + DBG_HEXDUMP(MFW_D, "FWDBG", + (char *)((t_u8 *)&prx_pkt->eth803_hdr + + SIZE_OF_DBG_STRUCT), + prx_pd->rx_pkt_length); + PRINTM(MFW_D, "FWDBG::\n"); + } + goto done; + } + + PRINTM(MINFO, + "RX Data: data_len - prx_pd->rx_pkt_offset = %d - %d = %d\n", + pmbuf->data_len, prx_pd->rx_pkt_offset, + pmbuf->data_len - prx_pd->rx_pkt_offset); + + HEXDUMP("RX Data: Dest", prx_pkt->eth803_hdr.dest_addr, + sizeof(prx_pkt->eth803_hdr.dest_addr)); + HEXDUMP("RX Data: Src", prx_pkt->eth803_hdr.src_addr, + sizeof(prx_pkt->eth803_hdr.src_addr)); + + if ((memcmp(pmadapter, &prx_pkt->rfc1042_hdr, snap_oui_802_h, + sizeof(snap_oui_802_h)) == 0) || + ((memcmp(pmadapter, &prx_pkt->rfc1042_hdr, rfc1042_eth_hdr, + sizeof(rfc1042_eth_hdr)) == 0) && + memcmp(pmadapter, &prx_pkt->rfc1042_hdr.snap_type, + appletalk_aarp_type, sizeof(appletalk_aarp_type)) && + memcmp(pmadapter, &prx_pkt->rfc1042_hdr.snap_type, ipx_snap_type, + sizeof(ipx_snap_type)))) { + /* + * Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type + * (ethertype). The firmware only passes up SNAP frames + * converting all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * To create the Ethernet II, just move the src, dst address + * right before the snap_type. + */ + peth_hdr = + (EthII_Hdr_t *)((t_u8 *)&prx_pkt->eth803_hdr + + sizeof(prx_pkt->eth803_hdr) + + sizeof(prx_pkt->rfc1042_hdr) - + sizeof(prx_pkt->eth803_hdr.dest_addr) - + sizeof(prx_pkt->eth803_hdr.src_addr) - + sizeof(prx_pkt->rfc1042_hdr.snap_type)); + + memcpy_ext(pmadapter, peth_hdr->src_addr, + prx_pkt->eth803_hdr.src_addr, + sizeof(peth_hdr->src_addr), + sizeof(peth_hdr->src_addr)); + memcpy_ext(pmadapter, peth_hdr->dest_addr, + prx_pkt->eth803_hdr.dest_addr, + sizeof(peth_hdr->dest_addr), + sizeof(peth_hdr->dest_addr)); + + /* Chop off the RxPD + the excess memory from the 802.2/llc/snap + * header that was removed. + */ + hdr_chop = (t_u32)((t_ptr)peth_hdr - (t_ptr)prx_pd); + } else { + HEXDUMP("RX Data: LLC/SNAP", (t_u8 *)&prx_pkt->rfc1042_hdr, + sizeof(prx_pkt->rfc1042_hdr)); + if ((priv->hotspot_cfg & HOTSPOT_ENABLED) && + discard_gratuitous_ARP_msg(prx_pkt, pmadapter)) { + ret = MLAN_STATUS_SUCCESS; + PRINTM(MDATA, + "Bypass sending Gratuitous ARP frame to Kernel.\n"); + goto done; + } + /* Chop off the RxPD */ + hdr_chop = (t_u32)((t_ptr)&prx_pkt->eth803_hdr - (t_ptr)prx_pd); + } + + /* Chop off the leading header bytes so the it points to the start of + * either the reconstructed EthII frame or the 802.2/llc/snap frame + */ + pmbuf->data_len -= hdr_chop; + pmbuf->data_offset += hdr_chop; + pmbuf->pparent = MNULL; + DBG_HEXDUMP(MDAT_D, "RxPD", (t_u8 *)prx_pd, + MIN(sizeof(RxPD), MAX_DATA_DUMP_LEN)); + DBG_HEXDUMP(MDAT_D, "Rx Payload", + ((t_u8 *)prx_pd + prx_pd->rx_pkt_offset), + MIN(prx_pd->rx_pkt_length, MAX_DATA_DUMP_LEN)); + + priv->rxpd_rate = prx_pd->rx_rate; + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmbuf->out_ts_sec, + &pmbuf->out_ts_usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => kernel seq_num=%d tid=%d\n", + pmbuf->out_ts_sec, pmbuf->out_ts_usec, prx_pd->seq_num, + prx_pd->priority); + +#ifdef DRV_EMBEDDED_SUPPLICANT + if (supplicantIsEnabled(priv->psapriv) && + (!memcmp(pmadapter, &prx_pkt->eth803_hdr.h803_len, eapol_type, + sizeof(eapol_type)))) { + // BML_SET_OFFSET(bufDesc, offset); + if (ProcessEAPoLPkt(priv->psapriv, pmbuf)) { + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + ret = MLAN_STATUS_SUCCESS; + PRINTM(MMSG, + "host supplicant eapol pkt process done.\n"); + + LEAVE(); + return ret; + } + } +#endif + + if (MFALSE || priv->rx_pkt_info) { + ext_rate_info = (t_u8)(prx_pd->rx_info >> 16); + pmbuf->u.rx_info.data_rate = + wlan_index_to_data_rate(priv->adapter, prx_pd->rx_rate, + prx_pd->rate_info, + ext_rate_info); + + pmbuf->u.rx_info.channel = + (prx_pd->rx_info & RXPD_CHAN_MASK) >> 5; + pmbuf->u.rx_info.antenna = prx_pd->antenna; + pmbuf->u.rx_info.rssi = prx_pd->snr - prx_pd->nf; + } + ret = pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, + pmbuf); + if (ret == MLAN_STATUS_FAILURE) { + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + PRINTM(MERROR, + "STA Rx Error: moal_recv_packet returned error\n"); + } +done: + if (ret != MLAN_STATUS_PENDING) + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); +#ifdef USB + else if (IS_USB(pmadapter->card_type)) + pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, + MNULL, MLAN_USB_EP_DATA, + MLAN_STATUS_SUCCESS); +#endif + 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(t_void *adapter, pmlan_buffer pmbuf) +{ + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + RxPD *prx_pd; + RxPacketHdr_t *prx_pkt; + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + t_u16 rx_pkt_type = 0; + wlan_mgmt_pkt *pmgmt_pkt_hdr = MNULL; + + sta_node *sta_ptr = MNULL; + t_u16 adj_rx_rate = 0; + t_u8 antenna = 0; + 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; + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + goto done; + } + pmbuf->data_len = prx_pd->rx_pkt_offset + prx_pd->rx_pkt_length; + + if (pmadapter->priv[pmbuf->bss_index]->mgmt_frame_passthru_mask && + prx_pd->rx_pkt_type == PKT_TYPE_MGMT_FRAME) { + /* Check if this is mgmt packet and needs to + * forwarded to app as an event + */ + pmgmt_pkt_hdr = (wlan_mgmt_pkt *)((t_u8 *)prx_pd + + prx_pd->rx_pkt_offset); + pmgmt_pkt_hdr->frm_len = + wlan_le16_to_cpu(pmgmt_pkt_hdr->frm_len); + + if ((pmgmt_pkt_hdr->wlan_header.frm_ctl & + IEEE80211_FC_MGMT_FRAME_TYPE_MASK) == 0) + wlan_process_802dot11_mgmt_pkt( + pmadapter->priv[pmbuf->bss_index], + (t_u8 *)&pmgmt_pkt_hdr->wlan_header, + pmgmt_pkt_hdr->frm_len + sizeof(wlan_mgmt_pkt) - + sizeof(pmgmt_pkt_hdr->frm_len), + prx_pd); + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + goto done; + } + if (rx_pkt_type != PKT_TYPE_BAR) { + priv->rxpd_rate_info = prx_pd->rate_info; + priv->rxpd_rate = prx_pd->rx_rate; + priv->rxpd_rx_info = (t_u8)(prx_pd->rx_info >> 16); + if (priv->bss_type == MLAN_BSS_TYPE_STA) { + antenna = wlan_adjust_antenna(priv, prx_pd); + adj_rx_rate = wlan_adjust_data_rate( + priv, priv->rxpd_rate, priv->rxpd_rate_info); + pmadapter->callbacks.moal_hist_data_add( + pmadapter->pmoal_handle, pmbuf->bss_index, + adj_rx_rate, prx_pd->snr, prx_pd->nf, antenna); + } + } + + /* + * If 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)) { + priv->snr = prx_pd->snr; + priv->nf = prx_pd->nf; + wlan_process_rx_packet(pmadapter, pmbuf); + goto done; + } + + if (queuing_ra_based(priv)) { + memcpy_ext(pmadapter, ta, prx_pkt->eth803_hdr.src_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + if (prx_pd->priority < MAX_NUM_TID) { + PRINTM(MDATA, "adhoc/tdls packet %p " MACSTR "\n", + pmbuf, MAC2STR(ta)); + sta_ptr = wlan_get_station_entry(priv, ta); + if (sta_ptr) { + sta_ptr->rx_seq[prx_pd->priority] = + prx_pd->seq_num; + sta_ptr->snr = prx_pd->snr; + sta_ptr->nf = prx_pd->nf; + } + if (!sta_ptr || !sta_ptr->is_11n_enabled) { + wlan_process_rx_packet(pmadapter, pmbuf); + goto done; + } + } + } else { + priv->snr = prx_pd->snr; + priv->nf = prx_pd->nf; + if ((rx_pkt_type != PKT_TYPE_BAR) && + (prx_pd->priority < MAX_NUM_TID)) + priv->rx_seq[prx_pd->priority] = prx_pd->seq_num; + memcpy_ext(pmadapter, ta, + priv->curr_bss_params.bss_descriptor.mac_address, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + } + if ((priv->port_ctrl_mode == MTRUE && priv->port_open == MFALSE) && + (rx_pkt_type != PKT_TYPE_BAR)) { + mlan_11n_rxreorder_pkt(priv, prx_pd->seq_num, prx_pd->priority, + ta, (t_u8)prx_pd->rx_pkt_type, + (t_void *)RX_PKT_DROPPED_IN_FW); + if (rx_pkt_type == PKT_TYPE_AMSDU) { + pmbuf->data_len = prx_pd->rx_pkt_length; + pmbuf->data_offset += prx_pd->rx_pkt_offset; + wlan_11n_deaggregate_pkt(priv, pmbuf); + } else { + wlan_process_rx_packet(pmadapter, pmbuf); + } + goto done; + } + /* Reorder and send to OS */ + ret = mlan_11n_rxreorder_pkt(priv, prx_pd->seq_num, prx_pd->priority, + ta, (t_u8)prx_pd->rx_pkt_type, + (void *)pmbuf); + if (ret || (rx_pkt_type == PKT_TYPE_BAR)) + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + +done: + + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_sta_tx.c b/mxm_wifiex/wlan_src/mlan/mlan_sta_tx.c new file mode 100644 index 0000000..737f2ba --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_sta_tx.c @@ -0,0 +1,326 @@ +/** @file mlan_sta_tx.c + * + * @brief This file contains the handling of data packet + * transmission in MLAN module. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" + +/******************************************************** + 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(t_void *priv, 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_ext(pmpriv->adapter, &pkt_type, + pmbuf->pbuf + pmbuf->data_offset, sizeof(pkt_type), + sizeof(pkt_type)); + memcpy_ext(pmpriv->adapter, &tx_control, + pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), + sizeof(tx_control), sizeof(tx_control)); + pmbuf->data_offset += sizeof(pkt_type) + sizeof(tx_control); + pmbuf->data_len -= sizeof(pkt_type) + sizeof(tx_control); + } + + if (pmbuf->data_offset < + (sizeof(TxPD) + pmpriv->intf_hr_len + DMA_ALIGNMENT)) { + PRINTM(MERROR, + "not enough space for TxPD: headroom=%d pkt_len=%d, required=%d\n", + pmbuf->data_offset, pmbuf->data_len, + sizeof(TxPD) + pmpriv->intf_hr_len + DMA_ALIGNMENT); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + goto done; + } + + /* head_ptr should be aligned */ + head_ptr = pmbuf->pbuf + pmbuf->data_offset - sizeof(TxPD) - + pmpriv->intf_hr_len; + head_ptr = (t_u8 *)((t_ptr)head_ptr & ~((t_ptr)(DMA_ALIGNMENT - 1))); + + plocal_tx_pd = (TxPD *)(head_ptr + pmpriv->intf_hr_len); + memset(pmadapter, plocal_tx_pd, 0, sizeof(TxPD)); + /* Set the BSS number to TxPD */ + plocal_tx_pd->bss_num = GET_BSS_NUM(pmpriv); + plocal_tx_pd->bss_type = pmpriv->bss_type; + plocal_tx_pd->tx_pkt_length = (t_u16)pmbuf->data_len; + + plocal_tx_pd->priority = (t_u8)pmbuf->priority; + plocal_tx_pd->pkt_delay_2ms = + wlan_wmm_compute_driver_packet_delay(pmpriv, pmbuf); + + if (plocal_tx_pd->priority < + NELEMENTS(pmpriv->wmm.user_pri_pkt_tx_ctrl)) + /* + * Set the priority specific tx_control field, setting of 0 will + * cause the default value to be used later in this function + */ + plocal_tx_pd->tx_control = + pmpriv->wmm.user_pri_pkt_tx_ctrl[plocal_tx_pd->priority]; + if (pmadapter->pps_uapsd_mode) { + if (MTRUE == wlan_check_last_packet_indication(pmpriv)) { + pmadapter->tx_lock_flag = MTRUE; + plocal_tx_pd->flags = + MRVDRV_TxPD_POWER_MGMT_LAST_PACKET; + } + } + /* Offset of actual data */ + plocal_tx_pd->tx_pkt_offset = (t_u16)( + (t_ptr)pmbuf->pbuf + pmbuf->data_offset - (t_ptr)plocal_tx_pd); + + if (!plocal_tx_pd->tx_control) { + /* TxCtrl set by user or default */ + plocal_tx_pd->tx_control = pmpriv->pkt_tx_ctrl; + } + + if (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { + plocal_tx_pd->tx_pkt_type = (t_u16)pkt_type; + plocal_tx_pd->tx_control = tx_control; + } + + if (pmbuf->flags & MLAN_BUF_FLAG_TX_STATUS) { + plocal_tx_pd->tx_control_1 |= pmbuf->tx_seq_num << 8; + plocal_tx_pd->flags |= MRVDRV_TxPD_FLAGS_TX_PACKET_STATUS; + } + if (pmbuf->flags & MLAN_BUF_FLAG_TX_CTRL) { + if (pmbuf->u.tx_info.data_rate) { + plocal_tx_pd->tx_control |= + (wlan_ieee_rateid_to_mrvl_rateid( + pmpriv, pmbuf->u.tx_info.data_rate, + MNULL) + << 16); + plocal_tx_pd->tx_control |= TXPD_TXRATE_ENABLE; + } + plocal_tx_pd->tx_control_1 |= pmbuf->u.tx_info.channel << 21; + if (pmbuf->u.tx_info.bw) { + plocal_tx_pd->tx_control_1 |= pmbuf->u.tx_info.bw << 16; + plocal_tx_pd->tx_control_1 |= TXPD_BW_ENABLE; + } + if (pmbuf->u.tx_info.tx_power.tp.hostctl) + plocal_tx_pd->tx_control |= + (t_u32)pmbuf->u.tx_info.tx_power.val; + if (pmbuf->u.tx_info.retry_limit) { + plocal_tx_pd->tx_control |= pmbuf->u.tx_info.retry_limit + << 8; + plocal_tx_pd->tx_control |= TXPD_RETRY_ENABLE; + } + } + endian_convert_TxPD(plocal_tx_pd); + + /* Adjust the data offset and length to include TxPD in pmbuf */ + pmbuf->data_len += pmbuf->data_offset; + pmbuf->data_offset = (t_u32)(head_ptr - pmbuf->pbuf); + pmbuf->data_len -= pmbuf->data_offset; + +done: + LEAVE(); + return head_ptr; +} + +/** + * @brief This function tells firmware to send a NULL data packet. + * + * @param priv A pointer to mlan_private structure + * @param flags Transmit Pkt Flags + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise + * failure + */ +mlan_status wlan_send_null_packet(pmlan_private priv, t_u8 flags) +{ + pmlan_adapter pmadapter = MNULL; + TxPD *ptx_pd; +/* sizeof(TxPD) + Interface specific header */ +#define NULL_PACKET_HDR 256 + t_u32 data_len = NULL_PACKET_HDR; + pmlan_buffer pmbuf = MNULL; + t_u8 *ptr; + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pmadapter = priv->adapter; + + if (pmadapter->surprise_removed == MTRUE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (priv->media_connected == MFALSE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (pmadapter->data_sent == MTRUE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + pmbuf = wlan_alloc_mlan_buffer(pmadapter, data_len, 0, + MOAL_MALLOC_BUFFER); + if (!pmbuf) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + memset(pmadapter, pmbuf->pbuf, 0, data_len); + pmbuf->bss_index = priv->bss_index; + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + pmbuf->flags |= MLAN_BUF_FLAG_NULL_PKT; + ptr = pmbuf->pbuf + pmbuf->data_offset; + pmbuf->data_len = sizeof(TxPD) + priv->intf_hr_len; + ptx_pd = (TxPD *)(ptr + priv->intf_hr_len); + ptx_pd->tx_control = priv->pkt_tx_ctrl; + ptx_pd->flags = flags; + ptx_pd->priority = WMM_HIGHEST_PRIORITY; + ptx_pd->tx_pkt_offset = sizeof(TxPD); + /* Set the BSS number to TxPD */ + ptx_pd->bss_num = GET_BSS_NUM(priv); + ptx_pd->bss_type = priv->bss_type; + + endian_convert_TxPD(ptx_pd); + + ret = pmadapter->ops.host_to_card(priv, MLAN_TYPE_DATA, pmbuf, MNULL); + + switch (ret) { +#ifdef USB + case MLAN_STATUS_PRESOURCE: + PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n"); + break; +#endif + case MLAN_STATUS_RESOURCE: + wlan_free_mlan_buffer(pmadapter, pmbuf); + PRINTM(MERROR, "STA Tx Error: Failed to send NULL packet!\n"); + pmadapter->dbg.num_tx_host_to_card_failure++; + goto done; + case MLAN_STATUS_FAILURE: + wlan_free_mlan_buffer(pmadapter, pmbuf); + PRINTM(MERROR, "STA Tx Error: Failed to send NULL packet!\n"); + pmadapter->dbg.num_tx_host_to_card_failure++; + goto done; + case MLAN_STATUS_SUCCESS: + wlan_free_mlan_buffer(pmadapter, pmbuf); + PRINTM(MINFO, "STA Tx: Successfully send the NULL packet\n"); + pmadapter->tx_lock_flag = MTRUE; + break; + case MLAN_STATUS_PENDING: + pmadapter->tx_lock_flag = MTRUE; + break; + default: + break; + } + + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Null data => FW\n", sec, usec); + DBG_HEXDUMP(MDAT_D, "Null data", ptr, sizeof(TxPD) + priv->intf_hr_len); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks if we need to send last packet indication. + * + * @param priv A pointer to mlan_private structure + * + * @return MTRUE or MFALSE + */ +t_u8 wlan_check_last_packet_indication(pmlan_private priv) +{ + pmlan_adapter pmadapter = priv->adapter; + t_u8 ret = MFALSE; + t_u8 prop_ps = MTRUE; + + ENTER(); + + if (!pmadapter->sleep_period.period) { + LEAVE(); + return ret; + } + if (wlan_bypass_tx_list_empty(pmadapter) && + wlan_wmm_lists_empty(pmadapter)) { + if (((priv->curr_bss_params.wmm_uapsd_enabled == MTRUE) && + priv->wmm_qosinfo) || + prop_ps) + + ret = MTRUE; + } + if (ret && !pmadapter->cmd_sent && !pmadapter->curr_cmd && + !wlan_is_cmd_pending(pmadapter)) { + pmadapter->delay_null_pkt = MFALSE; + ret = MTRUE; + } else { + ret = MFALSE; + pmadapter->delay_null_pkt = MTRUE; + } + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_txrx.c b/mxm_wifiex/wlan_src/mlan/mlan_txrx.c new file mode 100644 index 0000000..a8c93d5 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_txrx.c @@ -0,0 +1,424 @@ +/** + * @file mlan_txrx.c + * + * @brief This file contains the handling of TX/RX in MLAN + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" + +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function processes the received buffer + * + * @param pmadapter A pointer to mlan_adapter + * @param pmbuf A pointer to the received buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_handle_rx_packet(pmlan_adapter pmadapter, pmlan_buffer pmbuf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private priv = MNULL; + RxPD *prx_pd; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif + + ENTER(); + + prx_pd = (RxPD *)(pmbuf->pbuf + pmbuf->data_offset); + /* Get the BSS number from RxPD, get corresponding priv */ + priv = wlan_get_priv_by_id(pmadapter, prx_pd->bss_num & BSS_NUM_MASK, + prx_pd->bss_type); + if (!priv) + priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pmbuf->bss_index = priv->bss_index; + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data <= FW\n", sec, usec); + ret = priv->ops.process_rx_packet(pmadapter, pmbuf); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function checks the conditions and sends packet to device + * + * @param priv A pointer to mlan_private structure + * @param pmbuf A pointer to the mlan_buffer for process + * @param tx_param A pointer to mlan_tx_param structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise + * failure + */ +mlan_status wlan_process_tx(pmlan_private priv, pmlan_buffer pmbuf, + mlan_tx_param *tx_param) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_adapter pmadapter = priv->adapter; + t_u8 *head_ptr = MNULL; +#ifdef DEBUG_LEVEL1 + t_u32 sec = 0, usec = 0; +#endif +#ifdef STA_SUPPORT + PTxPD plocal_tx_pd = MNULL; +#endif + + ENTER(); + head_ptr = (t_u8 *)priv->ops.process_txpd(priv, pmbuf); + if (!head_ptr) { + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + ret = MLAN_STATUS_FAILURE; + goto done; + } +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + plocal_tx_pd = (TxPD *)(head_ptr + priv->intf_hr_len); +#endif + if (pmadapter->tp_state_on) + pmadapter->callbacks.moal_tp_accounting(pmadapter->pmoal_handle, + pmbuf, 4); + if (pmadapter->tp_state_drop_point == 4) + goto done; + else { + ret = pmadapter->ops.host_to_card(priv, MLAN_TYPE_DATA, pmbuf, + tx_param); + } +done: + switch (ret) { +#ifdef USB + case MLAN_STATUS_PRESOURCE: + PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n"); + DBG_HEXDUMP(MDAT_D, "Tx", head_ptr + priv->intf_hr_len, + MIN(pmbuf->data_len + sizeof(TxPD), + MAX_DATA_DUMP_LEN)); + break; +#endif + 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; + if (plocal_tx_pd != MNULL) + plocal_tx_pd->flags = 0; + } +#endif + PRINTM(MINFO, "MLAN_STATUS_RESOURCE is returned\n"); + break; + case MLAN_STATUS_FAILURE: + pmadapter->dbg.num_tx_host_to_card_failure++; + pmbuf->status_code = MLAN_ERROR_DATA_TX_FAIL; + wlan_write_data_complete(pmadapter, pmbuf, ret); + break; + case MLAN_STATUS_PENDING: + DBG_HEXDUMP(MDAT_D, "Tx", head_ptr + priv->intf_hr_len, + MIN(pmbuf->data_len + sizeof(TxPD), + MAX_DATA_DUMP_LEN)); + break; + case MLAN_STATUS_SUCCESS: + DBG_HEXDUMP(MDAT_D, "Tx", head_ptr + priv->intf_hr_len, + MIN(pmbuf->data_len + sizeof(TxPD), + MAX_DATA_DUMP_LEN)); + wlan_write_data_complete(pmadapter, pmbuf, ret); + break; + default: + break; + } + + if ((ret == MLAN_STATUS_SUCCESS) || (ret == MLAN_STATUS_PENDING)) { + PRINTM_GET_SYS_TIME(MDATA, &sec, &usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => FW\n", sec, usec); + } + LEAVE(); + return ret; +} + +/** + * @brief Packet send completion handling + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to mlan_buffer structure + * @param status Callback status + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_write_data_complete(pmlan_adapter pmadapter, + pmlan_buffer pmbuf, mlan_status status) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb; + + ENTER(); + + MASSERT(pmadapter && pmbuf); + if (!pmadapter || !pmbuf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pcb = &pmadapter->callbacks; + + if ((pmbuf->buf_type == MLAN_BUF_TYPE_DATA) || + (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA)) { + PRINTM(MINFO, "wlan_write_data_complete: DATA %p\n", pmbuf); +#if defined(USB) + if ((pmbuf->flags & MLAN_BUF_FLAG_USB_TX_AGGR) && + pmbuf->use_count) { + pmlan_buffer pmbuf_next; + t_u32 i, use_count = pmbuf->use_count; + for (i = 0; i <= use_count; i++) { + pmbuf_next = pmbuf->pnext; + if (pmbuf->flags & MLAN_BUF_FLAG_MOAL_TX_BUF) + pcb->moal_send_packet_complete( + pmadapter->pmoal_handle, pmbuf, + status); + else + wlan_free_mlan_buffer(pmadapter, pmbuf); + pmbuf = pmbuf_next; + } + } else { +#endif + if (pmbuf->flags & MLAN_BUF_FLAG_MOAL_TX_BUF) { + /* pmbuf was allocated by MOAL */ + pcb->moal_send_packet_complete( + pmadapter->pmoal_handle, pmbuf, status); + } else { + /* pmbuf was allocated by MLAN */ + wlan_free_mlan_buffer(pmadapter, pmbuf); + } +#if defined(USB) + } +#endif + } + + 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(pmlan_adapter pmadapter, + pmlan_buffer pmbuf, mlan_status status) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb; + + ENTER(); + + MASSERT(pmadapter && pmbuf); + if (!pmadapter || !pmbuf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + pcb = &pmadapter->callbacks; + MASSERT(pmbuf->bss_index < pmadapter->priv_num); + + if (pmbuf->pparent) { + /** we will free the pparaent at the end of deaggr */ + wlan_free_mlan_buffer(pmadapter, pmbuf); + } else { + pmadapter->ops.data_complete(pmadapter, pmbuf, status); + } + + LEAVE(); + return ret; +} + +/** + * @brief Add packet to Bypass TX queue + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * @param pmbuf Pointer to the mlan_buffer data struct + * + * @return N/A + */ +t_void wlan_add_buf_bypass_txqueue(mlan_adapter *pmadapter, pmlan_buffer pmbuf) +{ + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; + ENTER(); + + if (pmbuf->buf_type != MLAN_BUF_TYPE_RAW_DATA) + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->bypass_txq.plock); + pmadapter->bypass_pkt_count++; + util_enqueue_list_tail(pmadapter->pmoal_handle, &priv->bypass_txq, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->bypass_txq.plock); + LEAVE(); +} + +/** + * @brief Check if packets are available in Bypass TX queue + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * + * @return MFALSE if not empty; MTRUE if empty + */ +INLINE t_u8 wlan_bypass_tx_list_empty(mlan_adapter *pmadapter) +{ + return (pmadapter->bypass_pkt_count) ? MFALSE : MTRUE; +} + +/** + * @brief Clean up the By-pass TX queue + * + * @param priv Pointer to the mlan_private data struct + * + * @return N/A + */ +t_void wlan_cleanup_bypass_txq(mlan_private *priv) +{ + pmlan_buffer pmbuf; + mlan_adapter *pmadapter = priv->adapter; + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->bypass_txq.plock); + while ((pmbuf = (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &priv->bypass_txq, MNULL, + MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, &priv->bypass_txq, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + pmadapter->bypass_pkt_count--; + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->bypass_txq.plock); + LEAVE(); +} + +/** + * @brief Transmit the By-passed packet awaiting in by-pass queue + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * + * @return N/A + */ +t_void wlan_process_bypass_tx(pmlan_adapter pmadapter) +{ + pmlan_buffer pmbuf; + mlan_tx_param tx_param; + mlan_status status = MLAN_STATUS_SUCCESS; + pmlan_private priv; + int j = 0; + ENTER(); + do { + for (j = 0; j < pmadapter->priv_num; ++j) { + priv = pmadapter->priv[j]; + if (priv) { + pmbuf = (pmlan_buffer)util_dequeue_list( + pmadapter->pmoal_handle, + &priv->bypass_txq, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + if (pmbuf) { + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + priv->bypass_txq.plock); + pmadapter->bypass_pkt_count--; + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->bypass_txq.plock); + PRINTM(MINFO, + "Dequeuing bypassed packet %p\n", + pmbuf); + if (wlan_bypass_tx_list_empty( + pmadapter)) + tx_param.next_pkt_len = 0; + else + tx_param.next_pkt_len = + pmbuf->data_len; + 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 + */ + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + priv->bypass_txq.plock); + pmadapter->bypass_pkt_count++; + util_enqueue_list_head( + pmadapter->pmoal_handle, + &priv->bypass_txq, + (pmlan_linked_list)pmbuf, + pmadapter->callbacks + .moal_spin_lock, + pmadapter->callbacks + .moal_spin_unlock); + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->bypass_txq.plock); + } + break; + } else { + PRINTM(MINFO, "Nothing to send\n"); + } + } + } + } while (!pmadapter->data_sent && !pmadapter->tx_lock_flag && + !wlan_bypass_tx_list_empty(pmadapter)); + LEAVE(); +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_uap.h b/mxm_wifiex/wlan_src/mlan/mlan_uap.h new file mode 100644 index 0000000..f6f85b8 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_uap.h @@ -0,0 +1,57 @@ +/** @file mlan_uap.h + * + * @brief This file contains related macros, enum, and struct + * of uap functionalities + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_ + +mlan_status wlan_uap_get_channel(pmlan_private pmpriv); + +mlan_status wlan_uap_set_channel(pmlan_private pmpriv, + Band_Config_t uap_band_cfg, t_u8 channel); + +mlan_status wlan_uap_get_beacon_dtim(pmlan_private pmpriv); + +mlan_status wlan_ops_uap_ioctl(t_void *adapter, pmlan_ioctl_req pioctl_req); + +mlan_status wlan_ops_uap_prepare_cmd(t_void *priv, t_u16 cmd_no, + t_u16 cmd_action, t_u32 cmd_oid, + t_void *pioctl_buf, t_void *pdata_buf, + t_void *pcmd_buf); + +mlan_status wlan_ops_uap_process_cmdresp(t_void *priv, t_u16 cmdresp_no, + t_void *pcmd_buf, t_void *pioctl); + +mlan_status wlan_ops_uap_process_rx_packet(t_void *adapter, pmlan_buffer pmbuf); + +mlan_status wlan_ops_uap_process_event(t_void *priv); + +t_void *wlan_ops_uap_process_txpd(t_void *priv, pmlan_buffer pmbuf); + +mlan_status wlan_ops_uap_init_cmd(t_void *priv, t_u8 first_bss); + +#endif /* _MLAN_UAP_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_uap_cmdevent.c b/mxm_wifiex/wlan_src/mlan/mlan_uap_cmdevent.c new file mode 100644 index 0000000..1f905eb --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_uap_cmdevent.c @@ -0,0 +1,5587 @@ +/** @file mlan_uap_cmdevent.c + * + * @brief This file contains the handling of AP mode command and event + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" +#ifdef SDIO +#include "mlan_sdio.h" +#endif /* SDIO */ +#include "mlan_11n.h" +#include "mlan_11h.h" +#include "mlan_11ac.h" +#include "mlan_11ax.h" +#ifdef DRV_EMBEDDED_AUTHENTICATOR +#include "authenticator_api.h" +#endif +#ifdef PCIE +#include "mlan_pcie.h" +#endif /* PCIE */ +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function prepares command of BAND_STEERING_CFG + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_set_get_band_steering_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + t_void *pdata_buf) +{ + mlan_ds_band_steer_cfg *pband_steer_cfg = + (mlan_ds_band_steer_cfg *)pdata_buf; + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_BAND_STEERING); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_BAND_STEERING) + S_DS_GEN); + cmd->params.band_steer_info.state = pband_steer_cfg->state; + cmd->params.band_steer_info.block_2g_prb_req = + pband_steer_cfg->block_2g_prb_req; + cmd->params.band_steer_info.max_btm_req_allowed = + pband_steer_cfg->max_btm_req_allowed; + cmd->params.band_steer_info.action = cmd_action; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handle response of HostCmd_CMD_802_11_BAND_STEERING + * + * @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_set_get_band_steering_cfg(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_BAND_STEERING *pband_steer_info = + &resp->params.band_steer_info; + mlan_ds_misc_cfg *pband_steer; + + ENTER(); + + pband_steer = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + + pband_steer->param.band_steer_cfg.action = pband_steer_info->action; + pband_steer->param.band_steer_cfg.state = pband_steer_info->state; + pband_steer->param.band_steer_cfg.block_2g_prb_req = + pband_steer_info->block_2g_prb_req; + pband_steer->param.band_steer_cfg.max_btm_req_allowed = + pband_steer_info->max_btm_req_allowed; + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of BEACON_STUCK_CFG + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_set_get_beacon_stuck_cfg(IN pmlan_private pmpriv, + IN HostCmd_DS_COMMAND *cmd, + IN t_u16 cmd_action, + IN t_void *pdata_buf) +{ + HostCmd_DS_BEACON_STUCK_CFG *pbeacon_stuck_param_cfg = + (HostCmd_DS_BEACON_STUCK_CFG *)(pdata_buf + sizeof(t_u32)); + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_UAP_BEACON_STUCK_CFG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_BEACON_STUCK_CFG) + + S_DS_GEN); + cmd->params.beacon_stuck_cfg.beacon_stuck_detect_count = + pbeacon_stuck_param_cfg->beacon_stuck_detect_count; + cmd->params.beacon_stuck_cfg.recovery_confirm_count = + pbeacon_stuck_param_cfg->recovery_confirm_count; + cmd->params.beacon_stuck_cfg.action = cmd_action; + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handle response of HostCmd_CMD_UAP_BEACON_STUCK_CFG + * + * @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_set_get_beacon_stuck_cfg(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + HostCmd_DS_BEACON_STUCK_CFG *pbeacon_stuck_param_cfg = + &resp->params.beacon_stuck_cfg; + mlan_ds_misc_cfg *pbeacon_stuck; + + ENTER(); + + pbeacon_stuck = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + + pbeacon_stuck->param.beacon_stuck_cfg.action = + pbeacon_stuck_param_cfg->action; + pbeacon_stuck->param.beacon_stuck_cfg.beacon_stuck_detect_count = + pbeacon_stuck_param_cfg->beacon_stuck_detect_count; + pbeacon_stuck->param.beacon_stuck_cfg.recovery_confirm_count = + pbeacon_stuck_param_cfg->recovery_confirm_count; + + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response error + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to command buffer + * + * @return N/A + */ +static mlan_status uap_process_cmdresp_error(mlan_private *pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_adapter *pmadapter = pmpriv->adapter; +#if defined(USB) + t_u8 i; +#endif + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + if (resp->command != HostCmd_CMD_WMM_PARAM_CONFIG || + resp->command != HostCmd_CMD_CHAN_REGION_CFG) + PRINTM(MERROR, "CMD_RESP: cmd %#x error, result=%#x\n", + resp->command, resp->result); + if (pioctl_buf) + pioctl_buf->status_code = resp->result; + /* + * Handling errors here + */ + switch (resp->command) { +#ifdef SDIO + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + pmadapter->pcard_sd->sdio_rx_aggr_enable = MFALSE; + PRINTM(MMSG, "FW don't support SDIO single port rx aggr\n"); + break; +#endif + + case HOST_CMD_APCMD_SYS_CONFIGURE: { + HostCmd_DS_SYS_CONFIG *sys_config = + (HostCmd_DS_SYS_CONFIG *)&resp->params.sys_config; + t_u16 resp_len = 0, travel_len = 0, index; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + custom_ie *cptr; + + if (!pioctl_buf || (pioctl_buf->req_id != MLAN_IOCTL_MISC_CFG)) + break; + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + if ((pioctl_buf->action == MLAN_ACT_SET) && + (misc->sub_command == MLAN_OID_MISC_CUSTOM_IE)) { + cust_ie = (mlan_ds_misc_custom_ie *) + sys_config->tlv_buffer; + if (cust_ie) { + cust_ie->type = wlan_le16_to_cpu(cust_ie->type); + resp_len = cust_ie->len = + wlan_le16_to_cpu(cust_ie->len); + travel_len = 0; + /* conversion for index, mask, len */ + if (resp_len == sizeof(t_u16)) + cust_ie->ie_data_list[0].ie_index = + wlan_cpu_to_le16( + cust_ie->ie_data_list[0] + .ie_index); + + while (resp_len > sizeof(t_u16)) { + cptr = (custom_ie + *)(((t_u8 *)cust_ie + ->ie_data_list) + + travel_len); + index = cptr->ie_index = + wlan_le16_to_cpu( + cptr->ie_index); + cptr->mgmt_subtype_mask = + wlan_le16_to_cpu( + cptr->mgmt_subtype_mask); + cptr->ie_length = wlan_le16_to_cpu( + cptr->ie_length); + travel_len += cptr->ie_length + + sizeof(custom_ie) - + MAX_IE_SIZE; + resp_len -= cptr->ie_length + + sizeof(custom_ie) - + MAX_IE_SIZE; + if ((pmpriv->mgmt_ie[index] + .mgmt_subtype_mask == + cptr->mgmt_subtype_mask) && + (pmpriv->mgmt_ie[index].ie_length == + cptr->ie_length) && + !memcmp(pmpriv->adapter, + pmpriv->mgmt_ie[index] + .ie_buffer, + cptr->ie_buffer, + cptr->ie_length)) { + PRINTM(MERROR, + "set custom ie fail, remove ie index :%d\n", + index); + memset(pmadapter, + &pmpriv->mgmt_ie[index], + 0, sizeof(custom_ie)); + } + } + } + } + } break; + case HostCmd_CMD_PACKET_AGGR_CTRL: +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + for (i = 0; i < MAX_USB_TX_PORT_NUM; i++) + pmadapter->pcard_usb->usb_tx_aggr[i] + .aggr_ctrl.enable = MFALSE; + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable = + MFALSE; + } +#endif + break; + case HostCmd_CMD_CHAN_REGION_CFG: + ret = MLAN_STATUS_SUCCESS; + PRINTM(MCMND, "FW don't support chan region cfg command!\n"); + break; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) + case HostCmd_CMD_CRYPTO: + PRINTM(MCMND, "crypto cmd result=0x%x!\n", resp->result); + ret = wlan_ret_crypto(pmpriv, resp, pioctl_buf); + break; +#endif + default: + break; + } + + wlan_request_cmd_lock(pmadapter); + wlan_insert_cmd_to_free_q(pmadapter, pmadapter->curr_cmd); + pmadapter->curr_cmd = MNULL; + wlan_release_cmd_lock(pmadapter); + + LEAVE(); + return ret; +} + +/** + * @brief This function will return the pointer to station entry in station + * list table which matches the give mac address + * + * @param priv A pointer to mlan_private + * + * @return A pointer to structure sta_node + */ +void wlan_notify_station_deauth(mlan_private *priv) +{ + sta_node *sta_ptr; + t_u8 event_buf[100]; + mlan_event *pevent = (mlan_event *)event_buf; + t_u8 *pbuf; + + ENTER(); + sta_ptr = (sta_node *)util_peek_list( + priv->adapter->pmoal_handle, &priv->sta_list, + priv->adapter->callbacks.moal_spin_lock, + priv->adapter->callbacks.moal_spin_unlock); + if (!sta_ptr) { + LEAVE(); + return; + } + while (sta_ptr != (sta_node *)&priv->sta_list) { + memset(priv->adapter, event_buf, 0, sizeof(event_buf)); + pevent->bss_index = priv->bss_index; + pevent->event_id = MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT; + pevent->event_len = MLAN_MAC_ADDR_LENGTH + 2; + pbuf = (t_u8 *)pevent->event_buf; + /* reason field set to 0, Unspecified */ + memcpy_ext(priv->adapter, pbuf + 2, sta_ptr->mac_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + wlan_recv_event(priv, pevent->event_id, pevent); + sta_ptr = sta_ptr->pnext; + } + LEAVE(); + return; +} + +/** + * @brief This function prepares command of hs_cfg. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action The action: GET or SET + * @param pdata_buf A pointer to data buffer + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_cmd_802_11_hs_cfg(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + hs_config_param *pdata_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_HS_CFG_ENH *phs_cfg = + (HostCmd_DS_802_11_HS_CFG_ENH *)&(cmd->params.opt_hs_cfg); + t_u8 *tlv = (t_u8 *)phs_cfg + sizeof(HostCmd_DS_802_11_HS_CFG_ENH); + MrvlIEtypes_HsWakeHoldoff_t *holdoff_tlv = MNULL; + MrvlIEtypes_HS_Antmode_t *antmode_tlv = MNULL; + MrvlIEtypes_WakeupSourceGPIO_t *gpio_tlv = MNULL; + MrvlIEtypes_MgmtFrameFilter_t *mgmt_filter_tlv = MNULL; + MrvlIEtypes_WakeupExtend_t *ext_tlv = MNULL; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); + cmd->size = wlan_cpu_to_le16(S_DS_GEN + + sizeof(HostCmd_DS_802_11_HS_CFG_ENH)); + + if (pdata_buf == MNULL) { + phs_cfg->action = wlan_cpu_to_le16(HS_ACTIVATE); + phs_cfg->params.hs_activate.resp_ctrl = + wlan_cpu_to_le16(RESP_NEEDED); + } else { + phs_cfg->action = wlan_cpu_to_le16(HS_CONFIGURE); + phs_cfg->params.hs_config.conditions = + wlan_cpu_to_le32(pdata_buf->conditions); + phs_cfg->params.hs_config.gpio = pdata_buf->gpio; + phs_cfg->params.hs_config.gap = pdata_buf->gap; + if (pmpriv->adapter->min_wake_holdoff) { + cmd->size = wlan_cpu_to_le16( + S_DS_GEN + + sizeof(HostCmd_DS_802_11_HS_CFG_ENH) + + sizeof(MrvlIEtypes_HsWakeHoldoff_t)); + holdoff_tlv = (MrvlIEtypes_HsWakeHoldoff_t *)tlv; + holdoff_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_HS_WAKE_HOLDOFF); + holdoff_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_HsWakeHoldoff_t) - + sizeof(MrvlIEtypesHeader_t)); + holdoff_tlv->min_wake_holdoff = wlan_cpu_to_le16( + pmpriv->adapter->min_wake_holdoff); + tlv += sizeof(MrvlIEtypes_HsWakeHoldoff_t); + } + PRINTM(MCMND, + "HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x holdoff=%d\n", + phs_cfg->params.hs_config.conditions, + phs_cfg->params.hs_config.gpio, + phs_cfg->params.hs_config.gap, + pmpriv->adapter->min_wake_holdoff); + + if (pmadapter->param_type_ind == 1) { + cmd->size += sizeof(MrvlIEtypes_WakeupSourceGPIO_t); + gpio_tlv = (MrvlIEtypes_WakeupSourceGPIO_t *)tlv; + gpio_tlv->header.type = wlan_cpu_to_le16( + TLV_TYPE_HS_WAKEUP_SOURCE_GPIO); + gpio_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_WakeupSourceGPIO_t) - + sizeof(MrvlIEtypesHeader_t)); + gpio_tlv->ind_gpio = (t_u8)pmadapter->ind_gpio; + gpio_tlv->level = (t_u8)pmadapter->level; + tlv += sizeof(MrvlIEtypes_WakeupSourceGPIO_t); + } + if (pmadapter->param_type_ext == 2) { + cmd->size += sizeof(MrvlIEtypes_WakeupExtend_t); + ext_tlv = (MrvlIEtypes_WakeupExtend_t *)tlv; + ext_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_WAKEUP_EXTEND); + ext_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_WakeupExtend_t) - + sizeof(MrvlIEtypesHeader_t)); + ext_tlv->event_force_ignore = + wlan_cpu_to_le32(pmadapter->event_force_ignore); + ext_tlv->event_use_ext_gap = + wlan_cpu_to_le32(pmadapter->event_use_ext_gap); + ext_tlv->ext_gap = pmadapter->ext_gap; + ext_tlv->gpio_wave = pmadapter->gpio_wave; + tlv += sizeof(MrvlIEtypes_WakeupExtend_t); + } + if (pmadapter->mgmt_filter[0].type) { + int i = 0; + mgmt_frame_filter mgmt_filter[MAX_MGMT_FRAME_FILTER]; + memset(pmadapter, mgmt_filter, 0, + MAX_MGMT_FRAME_FILTER * + sizeof(mgmt_frame_filter)); + mgmt_filter_tlv = (MrvlIEtypes_MgmtFrameFilter_t *)tlv; + mgmt_filter_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_MGMT_FRAME_WAKEUP); + tlv += sizeof(MrvlIEtypesHeader_t); + while (i < MAX_MGMT_FRAME_FILTER && + pmadapter->mgmt_filter[i].type) { + mgmt_filter[i].action = + (t_u8)pmadapter->mgmt_filter[i].action; + mgmt_filter[i].type = + (t_u8)pmadapter->mgmt_filter[i].type; + mgmt_filter[i].frame_mask = wlan_cpu_to_le32( + pmadapter->mgmt_filter[i].frame_mask); + i++; + } + memcpy_ext(pmadapter, (t_u8 *)mgmt_filter_tlv->filter, + (t_u8 *)mgmt_filter, + i * sizeof(mgmt_frame_filter), + sizeof(mgmt_filter_tlv->filter)); + tlv += i * sizeof(mgmt_frame_filter); + mgmt_filter_tlv->header.len = + wlan_cpu_to_le16(i * sizeof(mgmt_frame_filter)); + cmd->size += i * sizeof(mgmt_frame_filter) + + sizeof(MrvlIEtypesHeader_t); + } + if (pmadapter->hs_mimo_switch) { + cmd->size += sizeof(MrvlIEtypes_HS_Antmode_t); + antmode_tlv = (MrvlIEtypes_HS_Antmode_t *)tlv; + antmode_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_HS_ANTMODE); + antmode_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_HS_Antmode_t) - + sizeof(MrvlIEtypesHeader_t)); + antmode_tlv->txpath_antmode = ANTMODE_FW_DECISION; + antmode_tlv->rxpath_antmode = ANTMODE_FW_DECISION; + tlv += sizeof(MrvlIEtypes_HS_Antmode_t); + PRINTM(MCMND, + "hs_mimo_switch=%d, txpath_antmode=%d, rxpath_antmode=%d\n", + pmadapter->hs_mimo_switch, + antmode_tlv->txpath_antmode, + antmode_tlv->rxpath_antmode); + } + } + 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, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_CMD_TX_DATA_PAUSE *pause_cmd = + (HostCmd_DS_CMD_TX_DATA_PAUSE *)&resp->params.tx_data_pause; + mlan_ds_misc_cfg *misc_cfg = MNULL; + + ENTER(); + + if (pioctl_buf) { + misc_cfg = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + misc_cfg->param.tx_datapause.tx_pause = + pause_cmd->enable_tx_pause; + misc_cfg->param.tx_datapause.tx_buf_cnt = + pause_cmd->pause_tx_count; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function will process tx pause event + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * + * @return N/A + */ +static void wlan_process_tx_pause_event(pmlan_private priv, pmlan_buffer pevent) +{ + t_u16 tlv_type, tlv_len; + int tlv_buf_left = pevent->data_len - sizeof(t_u32); + MrvlIEtypesHeader_t *tlv = + (MrvlIEtypesHeader_t *)(pevent->pbuf + pevent->data_offset + + sizeof(t_u32)); + MrvlIEtypes_tx_pause_t *tx_pause_tlv; + sta_node *sta_ptr = MNULL; + t_u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + t_u32 total_pkts_queued; + t_u16 tx_pkts_queued = 0; + ; + + ENTER(); + + total_pkts_queued = + util_scalar_read(priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + 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; + + if (!memcmp(priv->adapter, bc_mac, + tx_pause_tlv->peermac, + MLAN_MAC_ADDR_LENGTH)) + tx_pkts_queued = wlan_update_ralist_tx_pause( + priv, tx_pause_tlv->peermac, + tx_pause_tlv->tx_pause); + else if (!memcmp(priv->adapter, priv->curr_addr, + tx_pause_tlv->peermac, + MLAN_MAC_ADDR_LENGTH)) { + if (tx_pause_tlv->tx_pause) + priv->tx_pause = MTRUE; + else + priv->tx_pause = MFALSE; + } else { + sta_ptr = wlan_get_station_entry( + priv, tx_pause_tlv->peermac); + if (sta_ptr) { + if (sta_ptr->tx_pause != + tx_pause_tlv->tx_pause) { + sta_ptr->tx_pause = + tx_pause_tlv->tx_pause; + tx_pkts_queued = + wlan_update_ralist_tx_pause( + priv, + tx_pause_tlv + ->peermac, + tx_pause_tlv + ->tx_pause); + } + } + } + if (!tx_pause_tlv->tx_pause) + total_pkts_queued += tx_pkts_queued; + PRINTM(MCMND, + "TxPause: " MACSTR + " pause=%d, pkts=%d pending=%d total=%d\n", + MAC2STR(tx_pause_tlv->peermac), + tx_pause_tlv->tx_pause, tx_pause_tlv->pkt_cnt, + tx_pkts_queued, total_pkts_queued); + } + 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, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + 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; + MrvlIEtypes_preamble_t *tlv_preamble = MNULL; + + t_u32 cmd_size = 0; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + t_u16 i; + t_u16 ac; +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + int rx_mcs_supp = 0; +#endif + + 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_ext(pmpriv->adapter, tlv_mac->mac, + &bss->param.bss_config.mac_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + cmd_size += sizeof(MrvlIEtypes_MacAddr_t); + tlv += sizeof(MrvlIEtypes_MacAddr_t); + } + + if (bss->param.bss_config.bandcfg.scanMode == SCAN_MODE_ACS) { + /* ACS is not allowed when DFS repeater mode is on */ + if (pmpriv->adapter->dfs_repeater) { + PRINTM(MERROR, "ACS is not allowed when" + "DFS repeater mode is on.\n"); + return MLAN_STATUS_FAILURE; + } + } + + if (bss->param.bss_config.ssid.ssid_len) { + tlv_ssid = (MrvlIEtypes_SsIdParamSet_t *)tlv; + tlv_ssid->header.type = wlan_cpu_to_le16(TLV_TYPE_SSID); + tlv_ssid->header.len = wlan_cpu_to_le16( + (t_u16)bss->param.bss_config.ssid.ssid_len); + memcpy_ext(pmpriv->adapter, tlv_ssid->ssid, + bss->param.bss_config.ssid.ssid, + bss->param.bss_config.ssid.ssid_len, + MLAN_MAX_SSID_LENGTH); + cmd_size += sizeof(MrvlIEtypesHeader_t) + + bss->param.bss_config.ssid.ssid_len; + tlv += sizeof(MrvlIEtypesHeader_t) + + bss->param.bss_config.ssid.ssid_len; + } + + if ((bss->param.bss_config.beacon_period >= MIN_BEACON_PERIOD) && + (bss->param.bss_config.beacon_period <= MAX_BEACON_PERIOD)) { + tlv_beacon_period = (MrvlIEtypes_beacon_period_t *)tlv; + tlv_beacon_period->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); + tlv_beacon_period->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_beacon_period->beacon_period = + wlan_cpu_to_le16(bss->param.bss_config.beacon_period); + cmd_size += sizeof(MrvlIEtypes_beacon_period_t); + tlv += sizeof(MrvlIEtypes_beacon_period_t); + } + + if ((bss->param.bss_config.dtim_period >= MIN_DTIM_PERIOD) && + (bss->param.bss_config.dtim_period <= MAX_DTIM_PERIOD)) { + tlv_dtim_period = (MrvlIEtypes_dtim_period_t *)tlv; + tlv_dtim_period->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); + tlv_dtim_period->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_dtim_period->dtim_period = + bss->param.bss_config.dtim_period; + cmd_size += sizeof(MrvlIEtypes_dtim_period_t); + tlv += sizeof(MrvlIEtypes_dtim_period_t); + } + + if (bss->param.bss_config.rates[0]) { + tlv_rates = (MrvlIEtypes_RatesParamSet_t *)tlv; + tlv_rates->header.type = wlan_cpu_to_le16(TLV_TYPE_RATES); + for (i = 0; + i < MAX_DATA_RATES && bss->param.bss_config.rates[i]; + i++) { + tlv_rates->rates[i] = bss->param.bss_config.rates[i]; + } + tlv_rates->header.len = wlan_cpu_to_le16(i); + cmd_size += sizeof(MrvlIEtypesHeader_t) + i; + tlv += sizeof(MrvlIEtypesHeader_t) + i; + } + + if (bss->param.bss_config.tx_data_rate <= DATA_RATE_54M) { + tlv_txrate = (MrvlIEtypes_tx_rate_t *)tlv; + tlv_txrate->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_TX_DATA_RATE); + tlv_txrate->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_txrate->tx_data_rate = + wlan_cpu_to_le16(bss->param.bss_config.tx_data_rate); + cmd_size += sizeof(MrvlIEtypes_tx_rate_t); + tlv += sizeof(MrvlIEtypes_tx_rate_t); + } + + if (bss->param.bss_config.tx_beacon_rate <= DATA_RATE_54M) { + tlv_txrate = (MrvlIEtypes_tx_rate_t *)tlv; + tlv_txrate->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_TX_BEACON_RATE); + tlv_txrate->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_txrate->tx_data_rate = + wlan_cpu_to_le16(bss->param.bss_config.tx_beacon_rate); + cmd_size += sizeof(MrvlIEtypes_tx_rate_t); + tlv += sizeof(MrvlIEtypes_tx_rate_t); + } + + if (bss->param.bss_config.mcbc_data_rate <= DATA_RATE_54M) { + tlv_mcbc_rate = (MrvlIEtypes_mcbc_rate_t *)tlv; + tlv_mcbc_rate->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_MCBC_DATA_RATE); + tlv_mcbc_rate->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_mcbc_rate->mcbc_data_rate = + wlan_cpu_to_le16(bss->param.bss_config.mcbc_data_rate); + cmd_size += sizeof(MrvlIEtypes_mcbc_rate_t); + tlv += sizeof(MrvlIEtypes_mcbc_rate_t); + } + + if (bss->param.bss_config.tx_power_level <= MAX_TX_POWER) { + tlv_tx_power = (MrvlIEtypes_tx_power_t *)tlv; + tlv_tx_power->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_TX_POWER); + tlv_tx_power->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_tx_power->tx_power = bss->param.bss_config.tx_power_level; + cmd_size += sizeof(MrvlIEtypes_tx_power_t); + tlv += sizeof(MrvlIEtypes_tx_power_t); + } + + if (bss->param.bss_config.bcast_ssid_ctl <= MAX_BCAST_SSID_CTL) { + tlv_bcast_ssid = (MrvlIEtypes_bcast_ssid_t *)tlv; + tlv_bcast_ssid->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID_CTL); + tlv_bcast_ssid->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_bcast_ssid->bcast_ssid_ctl = + bss->param.bss_config.bcast_ssid_ctl; + cmd_size += sizeof(MrvlIEtypes_bcast_ssid_t); + tlv += sizeof(MrvlIEtypes_bcast_ssid_t); + } + + if ((bss->param.bss_config.tx_antenna == ANTENNA_MODE_A) || + (bss->param.bss_config.tx_antenna == ANTENNA_MODE_B)) { + tlv_antenna = (MrvlIEtypes_antenna_mode_t *)tlv; + tlv_antenna->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_ANTENNA_CTL); + tlv_antenna->header.len = + wlan_cpu_to_le16(sizeof(t_u8) + sizeof(t_u8)); + tlv_antenna->which_antenna = TX_ANTENNA; + tlv_antenna->antenna_mode = bss->param.bss_config.tx_antenna; + cmd_size += sizeof(MrvlIEtypes_antenna_mode_t); + tlv += sizeof(MrvlIEtypes_antenna_mode_t); + } + + if ((bss->param.bss_config.rx_antenna == ANTENNA_MODE_A) || + (bss->param.bss_config.rx_antenna == ANTENNA_MODE_B)) { + tlv_antenna = (MrvlIEtypes_antenna_mode_t *)tlv; + tlv_antenna->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_ANTENNA_CTL); + tlv_antenna->header.len = + wlan_cpu_to_le16(sizeof(t_u8) + sizeof(t_u8)); + tlv_antenna->which_antenna = RX_ANTENNA; + tlv_antenna->antenna_mode = bss->param.bss_config.rx_antenna; + cmd_size += sizeof(MrvlIEtypes_antenna_mode_t); + tlv += sizeof(MrvlIEtypes_antenna_mode_t); + } + + if (bss->param.bss_config.pkt_forward_ctl <= MAX_PKT_FWD_CTRL) { + tlv_pkt_forward = (MrvlIEtypes_pkt_forward_t *)tlv; + tlv_pkt_forward->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_PKT_FWD_CTL); + tlv_pkt_forward->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_pkt_forward->pkt_forward_ctl = + bss->param.bss_config.pkt_forward_ctl; + cmd_size += sizeof(MrvlIEtypes_pkt_forward_t); + tlv += sizeof(MrvlIEtypes_pkt_forward_t); + } + + if (bss->param.bss_config.max_sta_count <= MAX_STA_COUNT) { + tlv_sta_count = (MrvlIEtypes_max_sta_count_t *)tlv; + tlv_sta_count->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_MAX_STA_CNT); + tlv_sta_count->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_sta_count->max_sta_count = + wlan_cpu_to_le16(bss->param.bss_config.max_sta_count); + cmd_size += sizeof(MrvlIEtypes_max_sta_count_t); + tlv += sizeof(MrvlIEtypes_max_sta_count_t); + } + + if (((bss->param.bss_config.sta_ageout_timer >= MIN_STAGE_OUT_TIME) && + (bss->param.bss_config.sta_ageout_timer <= MAX_STAGE_OUT_TIME)) || + (bss->param.bss_config.sta_ageout_timer == 0)) { + tlv_sta_ageout = (MrvlIEtypes_sta_ageout_t *)tlv; + tlv_sta_ageout->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_STA_AGEOUT_TIMER); + tlv_sta_ageout->header.len = wlan_cpu_to_le16(sizeof(t_u32)); + tlv_sta_ageout->sta_ageout_timer = wlan_cpu_to_le32( + bss->param.bss_config.sta_ageout_timer); + cmd_size += sizeof(MrvlIEtypes_sta_ageout_t); + tlv += sizeof(MrvlIEtypes_sta_ageout_t); + } + + if (((bss->param.bss_config.ps_sta_ageout_timer >= MIN_STAGE_OUT_TIME) && + (bss->param.bss_config.ps_sta_ageout_timer <= + MAX_STAGE_OUT_TIME)) || + (bss->param.bss_config.ps_sta_ageout_timer == 0)) { + tlv_ps_sta_ageout = (MrvlIEtypes_ps_sta_ageout_t *)tlv; + tlv_ps_sta_ageout->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_PS_STA_AGEOUT_TIMER); + tlv_ps_sta_ageout->header.len = wlan_cpu_to_le16(sizeof(t_u32)); + tlv_ps_sta_ageout->ps_sta_ageout_timer = wlan_cpu_to_le32( + bss->param.bss_config.ps_sta_ageout_timer); + cmd_size += sizeof(MrvlIEtypes_ps_sta_ageout_t); + tlv += sizeof(MrvlIEtypes_ps_sta_ageout_t); + } + if (bss->param.bss_config.rts_threshold <= MAX_RTS_THRESHOLD) { + tlv_rts_threshold = (MrvlIEtypes_rts_threshold_t *)tlv; + tlv_rts_threshold->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); + tlv_rts_threshold->header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_rts_threshold->rts_threshold = + wlan_cpu_to_le16(bss->param.bss_config.rts_threshold); + cmd_size += sizeof(MrvlIEtypes_rts_threshold_t); + tlv += sizeof(MrvlIEtypes_rts_threshold_t); + } + + if ((bss->param.bss_config.frag_threshold >= MIN_FRAG_THRESHOLD) && + (bss->param.bss_config.frag_threshold <= MAX_FRAG_THRESHOLD)) { + tlv_frag_threshold = (MrvlIEtypes_frag_threshold_t *)tlv; + tlv_frag_threshold->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); + tlv_frag_threshold->header.len = + wlan_cpu_to_le16(sizeof(t_u16)); + tlv_frag_threshold->frag_threshold = + wlan_cpu_to_le16(bss->param.bss_config.frag_threshold); + cmd_size += sizeof(MrvlIEtypes_frag_threshold_t); + tlv += sizeof(MrvlIEtypes_frag_threshold_t); + } + + if (bss->param.bss_config.retry_limit <= MAX_RETRY_LIMIT) { + tlv_retry_limit = (MrvlIEtypes_retry_limit_t *)tlv; + tlv_retry_limit->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); + tlv_retry_limit->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_retry_limit->retry_limit = + (t_u8)bss->param.bss_config.retry_limit; + cmd_size += sizeof(MrvlIEtypes_retry_limit_t); + tlv += sizeof(MrvlIEtypes_retry_limit_t); + } + +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IS_FW_SUPPORT_AUTHENTICATOR(pmpriv->adapter)) { +#endif + if (bss->param.bss_config.pairwise_update_timeout < + (MAX_VALID_DWORD)) { + tlv_pairwise_timeout = + (MrvlIEtypes_eapol_pwk_hsk_timeout_t *)tlv; + tlv_pairwise_timeout->header.type = wlan_cpu_to_le16( + TLV_TYPE_UAP_EAPOL_PWK_HSK_TIMEOUT); + tlv_pairwise_timeout->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_pairwise_timeout + ->pairwise_update_timeout = wlan_cpu_to_le32( + bss->param.bss_config.pairwise_update_timeout); + cmd_size += sizeof(MrvlIEtypes_eapol_pwk_hsk_timeout_t); + tlv += sizeof(MrvlIEtypes_eapol_pwk_hsk_timeout_t); + } + + if (bss->param.bss_config.pwk_retries < (MAX_VALID_DWORD)) { + tlv_pairwise_retries = + (MrvlIEtypes_eapol_pwk_hsk_retries_t *)tlv; + tlv_pairwise_retries->header.type = wlan_cpu_to_le16( + TLV_TYPE_UAP_EAPOL_PWK_HSK_RETRIES); + tlv_pairwise_retries->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_pairwise_retries->pwk_retries = wlan_cpu_to_le32( + bss->param.bss_config.pwk_retries); + cmd_size += sizeof(MrvlIEtypes_eapol_pwk_hsk_retries_t); + tlv += sizeof(MrvlIEtypes_eapol_pwk_hsk_retries_t); + } + + if (bss->param.bss_config.groupwise_update_timeout < + (MAX_VALID_DWORD)) { + tlv_groupwise_timeout = + (MrvlIEtypes_eapol_gwk_hsk_timeout_t *)tlv; + tlv_groupwise_timeout->header.type = wlan_cpu_to_le16( + TLV_TYPE_UAP_EAPOL_GWK_HSK_TIMEOUT); + tlv_groupwise_timeout->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_groupwise_timeout + ->groupwise_update_timeout = wlan_cpu_to_le32( + bss->param.bss_config.groupwise_update_timeout); + cmd_size += sizeof(MrvlIEtypes_eapol_gwk_hsk_timeout_t); + tlv += sizeof(MrvlIEtypes_eapol_gwk_hsk_timeout_t); + } + + if (bss->param.bss_config.gwk_retries < (MAX_VALID_DWORD)) { + tlv_groupwise_retries = + (MrvlIEtypes_eapol_gwk_hsk_retries_t *)tlv; + tlv_groupwise_retries->header.type = wlan_cpu_to_le16( + TLV_TYPE_UAP_EAPOL_GWK_HSK_RETRIES); + tlv_groupwise_retries->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_groupwise_retries->gwk_retries = wlan_cpu_to_le32( + bss->param.bss_config.gwk_retries); + cmd_size += sizeof(MrvlIEtypes_eapol_gwk_hsk_retries_t); + tlv += sizeof(MrvlIEtypes_eapol_gwk_hsk_retries_t); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + } +#endif + if ((bss->param.bss_config.filter.filter_mode <= + MAC_FILTER_MODE_BLOCK_MAC) && + (bss->param.bss_config.filter.mac_count <= MAX_MAC_FILTER_NUM)) { + tlv_mac_filter = (MrvlIEtypes_mac_filter_t *)tlv; + tlv_mac_filter->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_STA_MAC_ADDR_FILTER); + tlv_mac_filter->header.len = wlan_cpu_to_le16( + 2 + MLAN_MAC_ADDR_LENGTH * + bss->param.bss_config.filter.mac_count); + tlv_mac_filter->count = + (t_u8)bss->param.bss_config.filter.mac_count; + tlv_mac_filter->filter_mode = + (t_u8)bss->param.bss_config.filter.filter_mode; + memcpy_ext(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, + MLAN_MAC_ADDR_LENGTH * MAX_MAC_FILTER_NUM); + cmd_size += sizeof(MrvlIEtypesHeader_t) + 2 + + MLAN_MAC_ADDR_LENGTH * + bss->param.bss_config.filter.mac_count; + tlv += sizeof(MrvlIEtypesHeader_t) + 2 + + MLAN_MAC_ADDR_LENGTH * + bss->param.bss_config.filter.mac_count; + } + + if (((bss->param.bss_config.bandcfg.scanMode == SCAN_MODE_MANUAL) && + (bss->param.bss_config.channel > 0) && + (bss->param.bss_config.channel <= MLAN_MAX_CHANNEL)) || + (bss->param.bss_config.bandcfg.scanMode == SCAN_MODE_ACS)) { + tlv_chan_band = (MrvlIEtypes_channel_band_t *)tlv; + tlv_chan_band->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_CHAN_BAND_CONFIG); + tlv_chan_band->header.len = + wlan_cpu_to_le16(sizeof(t_u8) + sizeof(t_u8)); + tlv_chan_band->bandcfg = bss->param.bss_config.bandcfg; + tlv_chan_band->channel = bss->param.bss_config.channel; + cmd_size += sizeof(MrvlIEtypes_channel_band_t); + tlv += sizeof(MrvlIEtypes_channel_band_t); + } + + if ((bss->param.bss_config.num_of_chan) && + (bss->param.bss_config.num_of_chan <= MLAN_MAX_CHANNEL)) { + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *)tlv; + tlv_chan_list->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + tlv_chan_list->header.len = wlan_cpu_to_le16( + (t_u16)(sizeof(ChanScanParamSet_t) * + bss->param.bss_config.num_of_chan)); + pscan_chan = tlv_chan_list->chan_scan_param; + for (i = 0; i < bss->param.bss_config.num_of_chan; i++) { + pscan_chan->chan_number = + bss->param.bss_config.chan_list[i].chan_number; + pscan_chan->bandcfg = + bss->param.bss_config.chan_list[i].bandcfg; + pscan_chan++; + } + cmd_size += sizeof(tlv_chan_list->header) + + (sizeof(ChanScanParamSet_t) * + bss->param.bss_config.num_of_chan); + tlv += sizeof(tlv_chan_list->header) + + (sizeof(ChanScanParamSet_t) * + bss->param.bss_config.num_of_chan); + } + + if ((bss->param.bss_config.auth_mode <= MLAN_AUTH_MODE_SHARED) || + (bss->param.bss_config.auth_mode == MLAN_AUTH_MODE_AUTO)) { + tlv_auth_type = (MrvlIEtypes_auth_type_t *)tlv; + tlv_auth_type->header.type = + wlan_cpu_to_le16(TLV_TYPE_AUTH_TYPE); + tlv_auth_type->header.len = wlan_cpu_to_le16(sizeof(t_u8)); + tlv_auth_type->auth_type = + (t_u8)bss->param.bss_config.auth_mode; + cmd_size += sizeof(MrvlIEtypes_auth_type_t); + tlv += sizeof(MrvlIEtypes_auth_type_t); + } + + if (bss->param.bss_config.protocol) { + tlv_encrypt_protocol = (MrvlIEtypes_encrypt_protocol_t *)tlv; + tlv_encrypt_protocol->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_ENCRYPT_PROTOCOL); + tlv_encrypt_protocol->header.len = + wlan_cpu_to_le16(sizeof(t_u16)); + tlv_encrypt_protocol->protocol = + wlan_cpu_to_le16(bss->param.bss_config.protocol); + cmd_size += sizeof(MrvlIEtypes_encrypt_protocol_t); + tlv += sizeof(MrvlIEtypes_encrypt_protocol_t); + } + + if ((bss->param.bss_config.protocol & PROTOCOL_WPA) || + (bss->param.bss_config.protocol & PROTOCOL_WPA2) || + (bss->param.bss_config.protocol & PROTOCOL_EAP)) { + tlv_akmp = (MrvlIEtypes_akmp_t *)tlv; + tlv_akmp->header.type = wlan_cpu_to_le16(TLV_TYPE_UAP_AKMP); + tlv_akmp->key_mgmt = + wlan_cpu_to_le16(bss->param.bss_config.key_mgmt); + tlv_akmp->header.len = sizeof(t_u16); + tlv_akmp->key_mgmt_operation = wlan_cpu_to_le16( + bss->param.bss_config.key_mgmt_operation); + tlv_akmp->header.len += sizeof(t_u16); + tlv_akmp->header.len = wlan_cpu_to_le16(tlv_akmp->header.len); + cmd_size += sizeof(MrvlIEtypes_akmp_t); + tlv += sizeof(MrvlIEtypes_akmp_t); + + if (bss->param.bss_config.wpa_cfg.pairwise_cipher_wpa & + VALID_CIPHER_BITMAP) { + tlv_pwk_cipher = (MrvlIEtypes_pwk_cipher_t *)tlv; + tlv_pwk_cipher->header.type = + wlan_cpu_to_le16(TLV_TYPE_PWK_CIPHER); + tlv_pwk_cipher->header.len = wlan_cpu_to_le16( + sizeof(t_u16) + sizeof(t_u8) + sizeof(t_u8)); + tlv_pwk_cipher->protocol = + wlan_cpu_to_le16(PROTOCOL_WPA); + tlv_pwk_cipher->pairwise_cipher = + bss->param.bss_config.wpa_cfg + .pairwise_cipher_wpa; + cmd_size += sizeof(MrvlIEtypes_pwk_cipher_t); + tlv += sizeof(MrvlIEtypes_pwk_cipher_t); + } + + if (bss->param.bss_config.wpa_cfg.pairwise_cipher_wpa2 & + VALID_CIPHER_BITMAP) { + tlv_pwk_cipher = (MrvlIEtypes_pwk_cipher_t *)tlv; + tlv_pwk_cipher->header.type = + wlan_cpu_to_le16(TLV_TYPE_PWK_CIPHER); + tlv_pwk_cipher->header.len = wlan_cpu_to_le16( + sizeof(t_u16) + sizeof(t_u8) + sizeof(t_u8)); + tlv_pwk_cipher->protocol = + wlan_cpu_to_le16(PROTOCOL_WPA2); + tlv_pwk_cipher->pairwise_cipher = + bss->param.bss_config.wpa_cfg + .pairwise_cipher_wpa2; + cmd_size += sizeof(MrvlIEtypes_pwk_cipher_t); + tlv += sizeof(MrvlIEtypes_pwk_cipher_t); + } + + if (bss->param.bss_config.wpa_cfg.group_cipher & + VALID_CIPHER_BITMAP) { + tlv_gwk_cipher = (MrvlIEtypes_gwk_cipher_t *)tlv; + tlv_gwk_cipher->header.type = + wlan_cpu_to_le16(TLV_TYPE_GWK_CIPHER); + tlv_gwk_cipher->header.len = + wlan_cpu_to_le16(sizeof(t_u8) + sizeof(t_u8)); + tlv_gwk_cipher->group_cipher = + bss->param.bss_config.wpa_cfg.group_cipher; + cmd_size += sizeof(MrvlIEtypes_gwk_cipher_t); + tlv += sizeof(MrvlIEtypes_gwk_cipher_t); + } + + if (bss->param.bss_config.wpa_cfg.rsn_protection <= MTRUE) { + tlv_rsn_prot = (MrvlIEtypes_rsn_replay_prot_t *)tlv; + tlv_rsn_prot->header.type = wlan_cpu_to_le16( + TLV_TYPE_UAP_RSN_REPLAY_PROTECT); + tlv_rsn_prot->header.len = + wlan_cpu_to_le16(sizeof(t_u8)); + tlv_rsn_prot->rsn_replay_prot = + bss->param.bss_config.wpa_cfg.rsn_protection; + cmd_size += sizeof(MrvlIEtypes_rsn_replay_prot_t); + tlv += sizeof(MrvlIEtypes_rsn_replay_prot_t); + } + +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IS_FW_SUPPORT_AUTHENTICATOR(pmpriv->adapter)) { +#endif + if (bss->param.bss_config.wpa_cfg.length) { + tlv_passphrase = + (MrvlIEtypes_passphrase_t *)tlv; + tlv_passphrase->header.type = wlan_cpu_to_le16( + TLV_TYPE_UAP_WPA_PASSPHRASE); + tlv_passphrase->header + .len = (t_u16)wlan_cpu_to_le16( + bss->param.bss_config.wpa_cfg.length); + memcpy_ext( + pmpriv->adapter, + tlv_passphrase->passphrase, + bss->param.bss_config.wpa_cfg.passphrase, + bss->param.bss_config.wpa_cfg.length, + bss->param.bss_config.wpa_cfg.length); + cmd_size += + sizeof(MrvlIEtypesHeader_t) + + bss->param.bss_config.wpa_cfg.length; + tlv += sizeof(MrvlIEtypesHeader_t) + + bss->param.bss_config.wpa_cfg.length; + } + + if (bss->param.bss_config.wpa_cfg.gk_rekey_time < + MAX_GRP_TIMER) { + tlv_rekey_time = + (MrvlIEtypes_group_rekey_time_t *)tlv; + tlv_rekey_time->header.type = wlan_cpu_to_le16( + TLV_TYPE_UAP_GRP_REKEY_TIME); + tlv_rekey_time->header.len = + wlan_cpu_to_le16(sizeof(t_u32)); + tlv_rekey_time->gk_rekey_time = + wlan_cpu_to_le32( + bss->param.bss_config.wpa_cfg + .gk_rekey_time); + cmd_size += + sizeof(MrvlIEtypes_group_rekey_time_t); + tlv += sizeof(MrvlIEtypes_group_rekey_time_t); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + } +#endif + } else { + if ((bss->param.bss_config.wep_cfg.key0.length) && + ((bss->param.bss_config.wep_cfg.key0.length == 5) || + (bss->param.bss_config.wep_cfg.key0.length == 10) || + (bss->param.bss_config.wep_cfg.key0.length == 13) || + (bss->param.bss_config.wep_cfg.key0.length == 26))) { + tlv_wep_key = (MrvlIEtypes_wep_key_t *)tlv; + tlv_wep_key->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + tlv_wep_key->header.len = wlan_cpu_to_le16( + 2 + bss->param.bss_config.wep_cfg.key0.length); + tlv_wep_key->key_index = + bss->param.bss_config.wep_cfg.key0.key_index; + tlv_wep_key->is_default = + bss->param.bss_config.wep_cfg.key0.is_default; + memcpy_ext(pmpriv->adapter, tlv_wep_key->key, + bss->param.bss_config.wep_cfg.key0.key, + bss->param.bss_config.wep_cfg.key0.length, + 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_ext(pmpriv->adapter, tlv_wep_key->key, + bss->param.bss_config.wep_cfg.key1.key, + bss->param.bss_config.wep_cfg.key1.length, + 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_ext(pmpriv->adapter, tlv_wep_key->key, + bss->param.bss_config.wep_cfg.key2.key, + bss->param.bss_config.wep_cfg.key2.length, + 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_ext(pmpriv->adapter, tlv_wep_key->key, + bss->param.bss_config.wep_cfg.key3.key, + bss->param.bss_config.wep_cfg.key3.length, + 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_ext(pmpriv->adapter, tlv_htcap->ht_cap.supported_mcs_set, + bss->param.bss_config.supported_mcs_set, 16, + sizeof(tlv_htcap->ht_cap.supported_mcs_set)); +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(SD9097) || defined(USB9097) + if (IS_CARD9098(pmpriv->adapter->card_type) || + IS_CARD9097(pmpriv->adapter->card_type)) { + if (bss->param.bss_config.supported_mcs_set[0]) { + if (bss->param.bss_config.bandcfg.chanBand == + BAND_5GHZ) + rx_mcs_supp = GET_RXMCSSUPP( + pmpriv->adapter->user_htstream >> + 8); + else + rx_mcs_supp = GET_RXMCSSUPP( + pmpriv->adapter->user_htstream); + + if (rx_mcs_supp == 0x1) { + tlv_htcap->ht_cap.supported_mcs_set[0] = + 0xFF; + tlv_htcap->ht_cap.supported_mcs_set[1] = + 0; + } else if (rx_mcs_supp == 0x2) { + tlv_htcap->ht_cap.supported_mcs_set[0] = + 0xFF; + tlv_htcap->ht_cap.supported_mcs_set[1] = + 0xFF; + } + } + } +#endif + 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.uap_host_based_config == MTRUE) || + (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_ext(pmpriv->adapter, tlv_wmm_parameter->wmm_para.ouitype, + bss->param.bss_config.wmm_para.ouitype, + sizeof(tlv_wmm_parameter->wmm_para.ouitype), + sizeof(tlv_wmm_parameter->wmm_para.ouitype)); + tlv_wmm_parameter->wmm_para.ouisubtype = + bss->param.bss_config.wmm_para.ouisubtype; + tlv_wmm_parameter->wmm_para.version = + bss->param.bss_config.wmm_para.version; + tlv_wmm_parameter->wmm_para.qos_info = + bss->param.bss_config.wmm_para.qos_info; + for (ac = 0; ac < 4; ac++) { + tlv_wmm_parameter->wmm_para.ac_params[ac] + .aci_aifsn.aifsn = + bss->param.bss_config.wmm_para.ac_params[ac] + .aci_aifsn.aifsn; + tlv_wmm_parameter->wmm_para.ac_params[ac].aci_aifsn.aci = + bss->param.bss_config.wmm_para.ac_params[ac] + .aci_aifsn.aci; + tlv_wmm_parameter->wmm_para.ac_params[ac].ecw.ecw_max = + bss->param.bss_config.wmm_para.ac_params[ac] + .ecw.ecw_max; + tlv_wmm_parameter->wmm_para.ac_params[ac].ecw.ecw_min = + bss->param.bss_config.wmm_para.ac_params[ac] + .ecw.ecw_min; + tlv_wmm_parameter->wmm_para.ac_params[ac].tx_op_limit = + wlan_cpu_to_le16(bss->param.bss_config.wmm_para + .ac_params[ac] + .tx_op_limit); + } + cmd_size += sizeof(MrvlIEtypes_wmm_parameter_t); + tlv += sizeof(MrvlIEtypes_wmm_parameter_t); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (!IS_FW_SUPPORT_AUTHENTICATOR(pmpriv->adapter)) + AuthenticatorBssConfig(pmpriv->psapriv, + (t_u8 *)&bss->param.bss_config, 0, 0, 0); +#endif + if (pmpriv->adapter->pcard_info->v17_fw_api && + bss->param.bss_config.preamble_type) { + tlv_preamble = (MrvlIEtypes_preamble_t *)tlv; + tlv_preamble->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_PREAMBLE_CTL); + tlv_preamble->header.len = + wlan_cpu_to_le16(sizeof(MrvlIEtypes_preamble_t) - + sizeof(MrvlIEtypesHeader_t)); + tlv_preamble->preamble_type = + wlan_cpu_to_le16(bss->param.bss_config.preamble_type); + + cmd_size += sizeof(MrvlIEtypes_preamble_t); + tlv += sizeof(MrvlIEtypes_preamble_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, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + pmlan_ioctl_req pioctl_buf, + t_void *pdata_buf) +{ + mlan_ds_bss *bss = MNULL; + HostCmd_DS_SYS_CONFIG *sys_config = + (HostCmd_DS_SYS_CONFIG *)&cmd->params.sys_config; + MrvlIEtypes_MacAddr_t *mac_tlv = MNULL; + MrvlIEtypes_channel_band_t *pdat_tlv_cb = MNULL; + MrvlIEtypes_beacon_period_t *bcn_pd_tlv = MNULL, + *pdat_tlv_bcnpd = MNULL; + MrvlIEtypes_dtim_period_t *dtim_pd_tlv = MNULL, + *pdat_tlv_dtimpd = MNULL; + MrvlIEtypes_wmm_parameter_t *tlv_wmm_parameter = MNULL; + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = MNULL; + ChanScanParamSet_t *pscan_chan = MNULL; + MrvlIEtypes_channel_band_t *chan_band_tlv = MNULL; + MrvlIEtypes_chan_bw_oper_t *poper_class_tlv = MNULL; + t_u8 length = 0; + t_u8 curr_oper_class = 1; + t_u8 *oper_class_ie = (t_u8 *)sys_config->tlv_buffer; + t_u16 i = 0; + t_u8 ac = 0; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + MrvlIEtypesHeader_t *ie_header = + (MrvlIEtypesHeader_t *)sys_config->tlv_buffer; + MrvlIEtypesHeader_t *pdata_header = (MrvlIEtypesHeader_t *)pdata_buf; + t_u8 *ie = (t_u8 *)sys_config->tlv_buffer + sizeof(MrvlIEtypesHeader_t); + t_u16 req_len = 0, travel_len = 0; + custom_ie *cptr = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HOST_CMD_APCMD_SYS_CONFIGURE); + sys_config->action = wlan_cpu_to_le16(cmd_action); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_SYS_CONFIG) - 1 + S_DS_GEN); + if (pioctl_buf == MNULL) { + if (pdata_buf) { + switch (pdata_header->type) { + case TLV_TYPE_UAP_CHAN_BAND_CONFIG: + pdat_tlv_cb = + (MrvlIEtypes_channel_band_t *)pdata_buf; + chan_band_tlv = (MrvlIEtypes_channel_band_t *) + sys_config->tlv_buffer; + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + + S_DS_GEN + + sizeof(MrvlIEtypes_channel_band_t)); + chan_band_tlv->header.type = wlan_cpu_to_le16( + TLV_TYPE_UAP_CHAN_BAND_CONFIG); + chan_band_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_channel_band_t) - + sizeof(MrvlIEtypesHeader_t)); + if (cmd_action) { + chan_band_tlv->bandcfg = + pdat_tlv_cb->bandcfg; + chan_band_tlv->channel = + pdat_tlv_cb->channel; + } + ret = MLAN_STATUS_SUCCESS; + break; + case TLV_TYPE_UAP_BEACON_PERIOD: + pdat_tlv_bcnpd = (MrvlIEtypes_beacon_period_t *) + pdata_buf; + bcn_pd_tlv = (MrvlIEtypes_beacon_period_t *) + sys_config->tlv_buffer; + cmd->size = sizeof(HostCmd_DS_SYS_CONFIG) - 1 + + S_DS_GEN + + sizeof(MrvlIEtypes_beacon_period_t); + bcn_pd_tlv->header.type = wlan_cpu_to_le16( + TLV_TYPE_UAP_BEACON_PERIOD); + bcn_pd_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_beacon_period_t) - + sizeof(MrvlIEtypesHeader_t)); + if (cmd_action) { + bcn_pd_tlv->beacon_period = + wlan_cpu_to_le16( + pdat_tlv_bcnpd + ->beacon_period); + } + /* Add TLV_UAP_DTIM_PERIOD if it follws in + * pdata_buf */ + pdat_tlv_dtimpd = + (MrvlIEtypes_dtim_period_t + *)(((t_u8 *)pdata_buf) + + sizeof(MrvlIEtypes_beacon_period_t)); + if (TLV_TYPE_UAP_DTIM_PERIOD == + pdat_tlv_dtimpd->header.type) { + dtim_pd_tlv = + (MrvlIEtypes_dtim_period_t + *)(sys_config + ->tlv_buffer + + sizeof(MrvlIEtypes_beacon_period_t)); + cmd->size += sizeof( + MrvlIEtypes_dtim_period_t); + dtim_pd_tlv->header + .type = wlan_cpu_to_le16( + TLV_TYPE_UAP_DTIM_PERIOD); + dtim_pd_tlv->header + .len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_dtim_period_t) - + sizeof(MrvlIEtypesHeader_t)); + if (cmd_action) { + dtim_pd_tlv->dtim_period = + pdat_tlv_dtimpd + ->dtim_period; + } + } + /* Finalize cmd size */ + cmd->size = wlan_cpu_to_le16(cmd->size); + ret = MLAN_STATUS_SUCCESS; + break; + case TLV_TYPE_MGMT_IE: + cust_ie = (mlan_ds_misc_custom_ie *)pdata_buf; + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + + S_DS_GEN + sizeof(MrvlIEtypesHeader_t) + + cust_ie->len); + ie_header->type = + wlan_cpu_to_le16(TLV_TYPE_MGMT_IE); + ie_header->len = wlan_cpu_to_le16(cust_ie->len); + + if (ie) { + 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_ext(pmpriv->adapter, ie, + cust_ie->ie_data_list, + cust_ie->len, cust_ie->len); + } + break; + case REGULATORY_CLASS: + poper_class_tlv = + (MrvlIEtypes_chan_bw_oper_t *)pdata_buf; + ret = wlan_get_curr_oper_class( + pmpriv, + poper_class_tlv->ds_chan_bw_oper.channel, + poper_class_tlv->ds_chan_bw_oper + .bandwidth, + &curr_oper_class); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Can not get current oper class! bandwidth = %d, channel = %d\n", + poper_class_tlv->ds_chan_bw_oper + .bandwidth, + poper_class_tlv->ds_chan_bw_oper + .channel); + } + + if (cmd_action == HostCmd_ACT_GEN_SET) + length = + wlan_add_supported_oper_class_ie( + pmpriv, &oper_class_ie, + curr_oper_class); + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + + S_DS_GEN + length); + break; + case TLV_TYPE_UAP_MAX_STA_CNT_PER_CHIP: + memcpy_ext( + pmpriv->adapter, sys_config->tlv_buffer, + pdata_buf, + sizeof(MrvlIEtypes_uap_max_sta_cnt_t), + sizeof(MrvlIEtypes_uap_max_sta_cnt_t)); + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + + S_DS_GEN + + sizeof(MrvlIEtypes_uap_max_sta_cnt_t)); + 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_ext(pmpriv->adapter, mac_tlv->mac, + &bss->param.mac_addr, + MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + } else if (bss->sub_command == MLAN_OID_UAP_CFG_WMM_PARAM) { + tlv_wmm_parameter = (MrvlIEtypes_wmm_parameter_t *) + sys_config->tlv_buffer; + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + S_DS_GEN + + sizeof(MrvlIEtypes_wmm_parameter_t)); + tlv_wmm_parameter->header.type = + wlan_cpu_to_le16(TLV_TYPE_AP_WMM_PARAM); + tlv_wmm_parameter->header.len = wlan_cpu_to_le16( + sizeof(bss->param.ap_wmm_para)); + if (cmd_action == HostCmd_ACT_GEN_SET) { + for (ac = 0; ac < 4; ac++) { + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .aci_aifsn.aifsn = + bss->param.ap_wmm_para + .ac_params[ac] + .aci_aifsn.aifsn; + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .aci_aifsn.aci = + bss->param.ap_wmm_para + .ac_params[ac] + .aci_aifsn.aci; + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .ecw.ecw_max = + bss->param.ap_wmm_para + .ac_params[ac] + .ecw.ecw_max; + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .ecw.ecw_min = + bss->param.ap_wmm_para + .ac_params[ac] + .ecw.ecw_min; + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .tx_op_limit = wlan_cpu_to_le16( + bss->param.ap_wmm_para + .ac_params[ac] + .tx_op_limit); + } + } + } else if (bss->sub_command == MLAN_OID_UAP_SCAN_CHANNELS) { + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *) + sys_config->tlv_buffer; + tlv_chan_list->header.type = + wlan_cpu_to_le16(TLV_TYPE_CHANLIST); + if (bss->param.ap_scan_channels.num_of_chan && + bss->param.ap_scan_channels.num_of_chan <= + MLAN_MAX_CHANNEL) { + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + + S_DS_GEN + + sizeof(tlv_chan_list->header) + + sizeof(ChanScanParamSet_t) * + bss->param.ap_scan_channels + .num_of_chan); + tlv_chan_list->header.len = wlan_cpu_to_le16( + (t_u16)(sizeof(ChanScanParamSet_t) * + bss->param.ap_scan_channels + .num_of_chan)); + pscan_chan = tlv_chan_list->chan_scan_param; + for (i = 0; + i < + bss->param.ap_scan_channels.num_of_chan; + i++) { + pscan_chan->chan_number = + bss->param.ap_scan_channels + .chan_list[i] + .chan_number; + pscan_chan->bandcfg = + bss->param.ap_scan_channels + .chan_list[i] + .bandcfg; + pscan_chan++; + } + PRINTM(MCMND, + "Set AP scan channel list = %d\n", + bss->param.ap_scan_channels.num_of_chan); + } else { + tlv_chan_list->header.len = wlan_cpu_to_le16( + (t_u16)(sizeof(ChanScanParamSet_t) * + MLAN_MAX_CHANNEL)); + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + + S_DS_GEN + + sizeof(MrvlIEtypes_ChanListParamSet_t) + + sizeof(ChanScanParamSet_t) * + MLAN_MAX_CHANNEL); + } + } else if (bss->sub_command == MLAN_OID_UAP_CHANNEL) { + chan_band_tlv = (MrvlIEtypes_channel_band_t *) + sys_config->tlv_buffer; + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + S_DS_GEN + + sizeof(MrvlIEtypes_channel_band_t)); + chan_band_tlv->header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_CHAN_BAND_CONFIG); + chan_band_tlv->header.len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_channel_band_t) - + sizeof(MrvlIEtypesHeader_t)); + if (cmd_action == HostCmd_ACT_GEN_SET) { + chan_band_tlv->bandcfg = + bss->param.ap_channel.bandcfg; + chan_band_tlv->channel = + bss->param.ap_channel.channel; + PRINTM(MCMND, + "Set AP channel, band=%d, channel=%d\n", + bss->param.ap_channel.bandcfg, + bss->param.ap_channel.channel); + } + } else if ((bss->sub_command == MLAN_OID_UAP_BSS_CONFIG) && + (cmd_action == HostCmd_ACT_GEN_SET)) { + ret = wlan_uap_cmd_ap_config(pmpriv, cmd, cmd_action, + pioctl_buf); + goto done; + } + } else if (pioctl_buf->req_id == MLAN_IOCTL_MISC_CFG) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + if ((misc->sub_command == MLAN_OID_MISC_GEN_IE) && + (misc->param.gen_ie.type == MLAN_IE_TYPE_GEN_IE)) { + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SYS_CONFIG) - 1 + S_DS_GEN + + sizeof(MrvlIEtypesHeader_t) + + misc->param.gen_ie.len); + ie_header->type = wlan_cpu_to_le16(TLV_TYPE_WAPI_IE); + ie_header->len = + wlan_cpu_to_le16(misc->param.gen_ie.len); + if (cmd_action == HostCmd_ACT_GEN_SET) + memcpy_ext(pmpriv->adapter, ie, + misc->param.gen_ie.ie_data, + misc->param.gen_ie.len, + 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) { + 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_ext( + pmpriv->adapter, ie, + misc->param.cust_ie.ie_data_list, + misc->param.cust_ie.len, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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_ext(pmpriv->adapter, + &bss->param.bss_config.mac_addr, + tlv_mac->mac, MLAN_MAC_ADDR_LENGTH, + 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_ext(pmpriv->adapter, + bss->param.bss_config.ssid.ssid, + tlv_ssid->ssid, tlv_len, + MLAN_MAX_SSID_LENGTH); + 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_ext(pmpriv->adapter, bss->param.bss_config.rates, + tlv_rates->rates, tlv_len, MAX_DATA_RATES); + break; + case TLV_TYPE_UAP_TX_DATA_RATE: + tlv_txrate = (MrvlIEtypes_tx_rate_t *)tlv; + bss->param.bss_config.tx_data_rate = + wlan_le16_to_cpu(tlv_txrate->tx_data_rate); + break; + case TLV_TYPE_UAP_TX_BEACON_RATE: + tlv_txrate = (MrvlIEtypes_tx_rate_t *)tlv; + bss->param.bss_config.tx_beacon_rate = + wlan_le16_to_cpu(tlv_txrate->tx_data_rate); + break; + case TLV_TYPE_UAP_MCBC_DATA_RATE: + tlv_mcbc_rate = (MrvlIEtypes_mcbc_rate_t *)tlv; + bss->param.bss_config.mcbc_data_rate = + wlan_le16_to_cpu(tlv_mcbc_rate->mcbc_data_rate); + break; + case TLV_TYPE_UAP_TX_POWER: + tlv_tx_power = (MrvlIEtypes_tx_power_t *)tlv; + bss->param.bss_config.tx_power_level = + tlv_tx_power->tx_power; + break; + case TLV_TYPE_UAP_BCAST_SSID_CTL: + tlv_bcast_ssid = (MrvlIEtypes_bcast_ssid_t *)tlv; + bss->param.bss_config.bcast_ssid_ctl = + tlv_bcast_ssid->bcast_ssid_ctl; + break; + case TLV_TYPE_UAP_ANTENNA_CTL: + tlv_antenna = (MrvlIEtypes_antenna_mode_t *)tlv; + if (tlv_antenna->which_antenna == TX_ANTENNA) + bss->param.bss_config.tx_antenna = + tlv_antenna->antenna_mode; + else if (tlv_antenna->which_antenna == RX_ANTENNA) + bss->param.bss_config.rx_antenna = + tlv_antenna->antenna_mode; + break; + case TLV_TYPE_UAP_PKT_FWD_CTL: + tlv_pkt_forward = (MrvlIEtypes_pkt_forward_t *)tlv; + bss->param.bss_config.pkt_forward_ctl = + tlv_pkt_forward->pkt_forward_ctl; + break; + case TLV_TYPE_UAP_MAX_STA_CNT: + tlv_sta_count = (MrvlIEtypes_max_sta_count_t *)tlv; + bss->param.bss_config.max_sta_count = + wlan_le16_to_cpu(tlv_sta_count->max_sta_count); + break; + case TLV_TYPE_UAP_STA_AGEOUT_TIMER: + tlv_sta_ageout = (MrvlIEtypes_sta_ageout_t *)tlv; + bss->param.bss_config.sta_ageout_timer = + wlan_le32_to_cpu( + tlv_sta_ageout->sta_ageout_timer); + break; + case TLV_TYPE_UAP_PS_STA_AGEOUT_TIMER: + tlv_ps_sta_ageout = (MrvlIEtypes_ps_sta_ageout_t *)tlv; + bss->param.bss_config.ps_sta_ageout_timer = + wlan_le32_to_cpu( + tlv_ps_sta_ageout->ps_sta_ageout_timer); + break; + case TLV_TYPE_UAP_RTS_THRESHOLD: + tlv_rts_threshold = (MrvlIEtypes_rts_threshold_t *)tlv; + bss->param.bss_config.rts_threshold = wlan_le16_to_cpu( + tlv_rts_threshold->rts_threshold); + break; + case TLV_TYPE_UAP_FRAG_THRESHOLD: + tlv_frag_threshold = + (MrvlIEtypes_frag_threshold_t *)tlv; + bss->param.bss_config.frag_threshold = wlan_le16_to_cpu( + tlv_frag_threshold->frag_threshold); + break; + case TLV_TYPE_UAP_RETRY_LIMIT: + tlv_retry_limit = (MrvlIEtypes_retry_limit_t *)tlv; + bss->param.bss_config.retry_limit = + tlv_retry_limit->retry_limit; + break; + case TLV_TYPE_UAP_EAPOL_PWK_HSK_TIMEOUT: + tlv_pairwise_timeout = + (MrvlIEtypes_eapol_pwk_hsk_timeout_t *)tlv; + bss->param.bss_config + .pairwise_update_timeout = wlan_le32_to_cpu( + tlv_pairwise_timeout->pairwise_update_timeout); + break; + case TLV_TYPE_UAP_EAPOL_PWK_HSK_RETRIES: + tlv_pairwise_retries = + (MrvlIEtypes_eapol_pwk_hsk_retries_t *)tlv; + bss->param.bss_config.pwk_retries = wlan_le32_to_cpu( + tlv_pairwise_retries->pwk_retries); + break; + case TLV_TYPE_UAP_EAPOL_GWK_HSK_TIMEOUT: + tlv_groupwise_timeout = + (MrvlIEtypes_eapol_gwk_hsk_timeout_t *)tlv; + bss->param.bss_config.groupwise_update_timeout = + wlan_le32_to_cpu( + tlv_groupwise_timeout + ->groupwise_update_timeout); + break; + case TLV_TYPE_UAP_EAPOL_GWK_HSK_RETRIES: + tlv_groupwise_retries = + (MrvlIEtypes_eapol_gwk_hsk_retries_t *)tlv; + bss->param.bss_config.gwk_retries = wlan_le32_to_cpu( + tlv_groupwise_retries->gwk_retries); + break; + case TLV_TYPE_UAP_MGMT_IE_PASSTHRU_MASK: + tlv_mgmt_ie_passthru = + (MrvlIEtypes_mgmt_ie_passthru_t *)tlv; + bss->param.bss_config.mgmt_ie_passthru_mask = + wlan_le32_to_cpu( + tlv_mgmt_ie_passthru->mgmt_ie_mask); + break; + case TLV_TYPE_2040_BSS_COEX_CONTROL: + tlv_2040_coex_enable = + (MrvlIEtypes_2040_coex_enable_t *)tlv; + bss->param.bss_config.enable_2040coex = + tlv_2040_coex_enable->enable_2040coex; + break; + case TLV_TYPE_UAP_STA_MAC_ADDR_FILTER: + tlv_mac_filter = (MrvlIEtypes_mac_filter_t *)tlv; + bss->param.bss_config.filter.mac_count = + MIN(MAX_MAC_FILTER_NUM, tlv_mac_filter->count); + bss->param.bss_config.filter.filter_mode = + tlv_mac_filter->filter_mode; + memcpy_ext( + 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, + sizeof(bss->param.bss_config.filter.mac_list)); + break; + case TLV_TYPE_UAP_CHAN_BAND_CONFIG: + tlv_chan_band = (MrvlIEtypes_channel_band_t *)tlv; + bss->param.bss_config.bandcfg = tlv_chan_band->bandcfg; + bss->param.bss_config.channel = tlv_chan_band->channel; + pmpriv->uap_state_chan_cb.bandcfg = + tlv_chan_band->bandcfg; + pmpriv->uap_state_chan_cb.channel = + tlv_chan_band->channel; + break; + case TLV_TYPE_CHANLIST: + tlv_chan_list = (MrvlIEtypes_ChanListParamSet_t *)tlv; + bss->param.bss_config.num_of_chan = + tlv_len / sizeof(ChanScanParamSet_t); + pscan_chan = tlv_chan_list->chan_scan_param; + for (i = 0; i < bss->param.bss_config.num_of_chan; + i++) { + bss->param.bss_config.chan_list[i].chan_number = + pscan_chan->chan_number; + bss->param.bss_config.chan_list[i].bandcfg = + pscan_chan->bandcfg; + pscan_chan++; + } + break; + case TLV_TYPE_AUTH_TYPE: + tlv_auth_type = (MrvlIEtypes_auth_type_t *)tlv; + bss->param.bss_config.auth_mode = + tlv_auth_type->auth_type; + break; + case TLV_TYPE_UAP_ENCRYPT_PROTOCOL: + tlv_encrypt_protocol = + (MrvlIEtypes_encrypt_protocol_t *)tlv; + bss->param.bss_config.protocol = wlan_le16_to_cpu( + tlv_encrypt_protocol->protocol); + break; + case TLV_TYPE_UAP_AKMP: + tlv_akmp = (MrvlIEtypes_akmp_t *)tlv; + bss->param.bss_config.key_mgmt = + wlan_le16_to_cpu(tlv_akmp->key_mgmt); + if (tlv_len > sizeof(t_u16)) + bss->param.bss_config.key_mgmt_operation = + wlan_le16_to_cpu( + tlv_akmp->key_mgmt_operation); + break; + case TLV_TYPE_PWK_CIPHER: + tlv_pwk_cipher = (MrvlIEtypes_pwk_cipher_t *)tlv; + if (wlan_le16_to_cpu(tlv_pwk_cipher->protocol) & + PROTOCOL_WPA) + bss->param.bss_config.wpa_cfg + .pairwise_cipher_wpa = + tlv_pwk_cipher->pairwise_cipher; + if (wlan_le16_to_cpu(tlv_pwk_cipher->protocol) & + PROTOCOL_WPA2) + bss->param.bss_config.wpa_cfg + .pairwise_cipher_wpa2 = + tlv_pwk_cipher->pairwise_cipher; + if (wlan_le16_to_cpu(tlv_pwk_cipher->protocol) & + PROTOCOL_WPA3_SAE) + 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_ext(pmpriv->adapter, + bss->param.bss_config.wpa_cfg.passphrase, + tlv_passphrase->passphrase, + bss->param.bss_config.wpa_cfg.length, + sizeof(bss->param.bss_config.wpa_cfg + .passphrase)); + break; +#ifdef WIFI_DIRECT_SUPPORT + case TLV_TYPE_UAP_PSK: + tlv_psk = (MrvlIEtypes_psk_t *)tlv; + memcpy_ext(pmpriv->adapter, bss->param.bss_config.psk, + tlv_psk->psk, tlv_len, MLAN_MAX_KEY_LENGTH); + 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_ext(pmpriv->adapter, pkey->key, + tlv_wep_key->key, pkey->length, + pkey->length); + } + break; + case TLV_TYPE_UAP_PREAMBLE_CTL: + tlv_preamble = (MrvlIEtypes_preamble_t *)tlv; + bss->param.bss_config.preamble_type = + tlv_preamble->preamble_type; + break; + case TLV_TYPE_BSS_STATUS: + tlv_bss_status = (MrvlIEtypes_bss_status_t *)tlv; + bss->param.bss_config.bss_status = + wlan_le16_to_cpu(tlv_bss_status->bss_status); + pmpriv->uap_bss_started = + (bss->param.bss_config.bss_status) ? MTRUE : + MFALSE; + break; + case TLV_TYPE_HT_CAPABILITY: + tlv_htcap = (MrvlIETypes_HTCap_t *)tlv; + bss->param.bss_config.ht_cap_info = + wlan_le16_to_cpu(tlv_htcap->ht_cap.ht_cap_info); + bss->param.bss_config.ampdu_param = + tlv_htcap->ht_cap.ampdu_param; + memcpy_ext( + pmpriv->adapter, + bss->param.bss_config.supported_mcs_set, + tlv_htcap->ht_cap.supported_mcs_set, 16, + sizeof(bss->param.bss_config.supported_mcs_set)); + bss->param.bss_config.ht_ext_cap = + wlan_le16_to_cpu(tlv_htcap->ht_cap.ht_ext_cap); + bss->param.bss_config.tx_bf_cap = + wlan_le32_to_cpu(tlv_htcap->ht_cap.tx_bf_cap); + bss->param.bss_config.asel = tlv_htcap->ht_cap.asel; + break; + case TLV_TYPE_VENDOR_SPECIFIC_IE: + tlv_wmm_parameter = (MrvlIEtypes_wmm_parameter_t *)tlv; + bss->param.bss_config.wmm_para.qos_info = + tlv_wmm_parameter->wmm_para.qos_info; + for (ac = 0; ac < 4; ac++) { + bss->param.bss_config.wmm_para.ac_params[ac] + .aci_aifsn.aifsn = + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .aci_aifsn.aifsn; + bss->param.bss_config.wmm_para.ac_params[ac] + .aci_aifsn.aci = + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .aci_aifsn.aci; + bss->param.bss_config.wmm_para.ac_params[ac] + .ecw.ecw_max = + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .ecw.ecw_max; + bss->param.bss_config.wmm_para.ac_params[ac] + .ecw.ecw_min = + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .ecw.ecw_min; + bss->param.bss_config.wmm_para.ac_params[ac] + .tx_op_limit = wlan_le16_to_cpu( + tlv_wmm_parameter->wmm_para + .ac_params[ac] + .tx_op_limit); + } + break; + } + + tlv_buf_left -= tlv_len + sizeof(MrvlIEtypesHeader_t); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (!IS_FW_SUPPORT_AUTHENTICATOR(pmpriv->adapter)) + AuthenticatorBssConfig(pmpriv->psapriv, + (t_u8 *)&bss->param.bss_config, 0, 0, 1); +#endif + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sys_reset + * Clear various private state variables used by DFS. + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_ret_sys_reset(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + ENTER(); + + memset(pmpriv->adapter, &(pmpriv->uap_state_chan_cb.bandcfg), 0, + sizeof(pmpriv->uap_state_chan_cb.bandcfg)); + pmpriv->uap_state_chan_cb.channel = 0; + pmpriv->uap_state_chan_cb.beacon_period = 0; + pmpriv->uap_state_chan_cb.dtim_period = 0; + + /* assume default 11d/11h states are off, should check with FW */ + /* currently don't clear domain_info... global, could be from STA */ + wlan_11d_priv_init(pmpriv); + wlan_11h_priv_init(pmpriv); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of sys_config + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_ret_sys_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + int resp_len = 0, travel_len = 0; + int i = 0; + custom_ie *cptr; + HostCmd_DS_SYS_CONFIG *sys_config = + (HostCmd_DS_SYS_CONFIG *)&resp->params.sys_config; + mlan_ds_bss *bss = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + MrvlIEtypes_MacAddr_t *tlv = + (MrvlIEtypes_MacAddr_t *)sys_config->tlv_buffer; + mlan_ds_misc_custom_ie *cust_ie = MNULL; + tlvbuf_max_mgmt_ie *max_mgmt_ie = MNULL; + MrvlIEtypes_wmm_parameter_t *tlv_wmm_parameter = + (MrvlIEtypes_wmm_parameter_t *)sys_config->tlv_buffer; + MrvlIEtypes_ChanListParamSet_t *tlv_chan_list = + (MrvlIEtypes_ChanListParamSet_t *)sys_config->tlv_buffer; + MrvlIEtypes_channel_band_t *chan_band_tlv = + (MrvlIEtypes_channel_band_t *)sys_config->tlv_buffer; + ChanScanParamSet_t *pscan_chan = MNULL; + t_u8 ac = 0; + MrvlIEtypes_channel_band_t *tlv_cb = MNULL; + MrvlIEtypes_beacon_period_t *tlv_bcnpd = MNULL; + MrvlIEtypes_dtim_period_t *tlv_dtimpd = MNULL; + MrvlIEtypes_uap_max_sta_cnt_t *tlv_uap_max_sta = 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_ext(pmpriv->adapter, + &bss->param.mac_addr, + tlv->mac, + MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + } + } else if (bss->sub_command == + MLAN_OID_UAP_CFG_WMM_PARAM) { + if (TLV_TYPE_AP_WMM_PARAM == + wlan_le16_to_cpu( + tlv_wmm_parameter->header.type)) { + if (wlan_le16_to_cpu( + tlv_wmm_parameter->header + .len) < + sizeof(bss->param.ap_wmm_para)) { + PRINTM(MCMND, + "FW don't support AP WMM PARAM\n"); + } else { + bss->param.ap_wmm_para.reserved = + MLAN_STATUS_COMPLETE; + for (ac = 0; ac < 4; ac++) { + bss->param.ap_wmm_para + .ac_params[ac] + .aci_aifsn + .aifsn = + tlv_wmm_parameter + ->wmm_para + .ac_params + [ac] + .aci_aifsn + .aifsn; + bss->param.ap_wmm_para + .ac_params[ac] + .aci_aifsn.aci = + tlv_wmm_parameter + ->wmm_para + .ac_params + [ac] + .aci_aifsn + .aci; + bss->param.ap_wmm_para + .ac_params[ac] + .ecw.ecw_max = + tlv_wmm_parameter + ->wmm_para + .ac_params + [ac] + .ecw + .ecw_max; + bss->param.ap_wmm_para + .ac_params[ac] + .ecw.ecw_min = + tlv_wmm_parameter + ->wmm_para + .ac_params + [ac] + .ecw + .ecw_min; + bss->param.ap_wmm_para + .ac_params[ac] + .tx_op_limit = wlan_le16_to_cpu( + tlv_wmm_parameter + ->wmm_para + .ac_params + [ac] + .tx_op_limit); + PRINTM(MCMND, + "ac=%d, aifsn=%d, aci=%d, ecw_max=%d, ecw_min=%d, tx_op=%d\n", + ac, + bss->param + .ap_wmm_para + .ac_params + [ac] + .aci_aifsn + .aifsn, + bss->param + .ap_wmm_para + .ac_params + [ac] + .aci_aifsn + .aci, + bss->param + .ap_wmm_para + .ac_params + [ac] + .ecw + .ecw_max, + bss->param + .ap_wmm_para + .ac_params + [ac] + .ecw + .ecw_min, + bss->param + .ap_wmm_para + .ac_params + [ac] + .tx_op_limit); + } + } + } + } else if (bss->sub_command == + MLAN_OID_UAP_SCAN_CHANNELS) { + if (TLV_TYPE_CHANLIST == + wlan_le16_to_cpu( + tlv_chan_list->header.type)) { + pscan_chan = + tlv_chan_list->chan_scan_param; + bss->param.ap_scan_channels.num_of_chan = + 0; + for (i = 0; + i < + wlan_le16_to_cpu( + tlv_chan_list->header.len) / + sizeof(ChanScanParamSet_t); + i++) { + if (bss->param.ap_scan_channels + .remove_nop_channel && + wlan_11h_is_channel_under_nop( + pmpriv->adapter, + pscan_chan + ->chan_number)) { + bss->param + .ap_scan_channels + .num_remvoed_channel++; + PRINTM(MCMND, + "Remove nop channel=%d\n", + pscan_chan + ->chan_number); + pscan_chan++; + continue; + } + bss->param.ap_scan_channels + .chan_list + [bss->param + .ap_scan_channels + .num_of_chan] + .chan_number = + pscan_chan->chan_number; + bss->param.ap_scan_channels + .chan_list + [bss->param + .ap_scan_channels + .num_of_chan] + .bandcfg = + pscan_chan->bandcfg; + bss->param.ap_scan_channels + .num_of_chan++; + pscan_chan++; + } + PRINTM(MCMND, + "AP scan channel list=%d\n", + bss->param.ap_scan_channels + .num_of_chan); + } + } else if (bss->sub_command == MLAN_OID_UAP_CHANNEL) { + if (TLV_TYPE_UAP_CHAN_BAND_CONFIG == + wlan_le16_to_cpu( + chan_band_tlv->header.type)) { + bss->param.ap_channel.bandcfg = + chan_band_tlv->bandcfg; + bss->param.ap_channel.channel = + chan_band_tlv->channel; + bss->param.ap_channel.is_11n_enabled = + pmpriv->is_11n_enabled; + bss->param.ap_channel.is_dfs_chan = + wlan_11h_radar_detect_required( + pmpriv, + bss->param.ap_channel + .channel); + if (chan_band_tlv->bandcfg.chanWidth == + CHAN_BW_80MHZ) + bss->param.ap_channel + .center_chan = + wlan_get_center_freq_idx( + pmpriv, + BAND_AAC, + chan_band_tlv + ->channel, + CHANNEL_BW_80MHZ); + PRINTM(MCMND, + "AP channel, band=0x%x, channel=%d, is_11n_enabled=%d center_chan=%d\n", + bss->param.ap_channel.bandcfg, + bss->param.ap_channel.channel, + bss->param.ap_channel + .is_11n_enabled, + bss->param.ap_channel + .center_chan); + } + } else if ((bss->sub_command == + MLAN_OID_UAP_BSS_CONFIG) && + (pioctl_buf->action == MLAN_ACT_GET)) { + wlan_uap_ret_cmd_ap_config(pmpriv, resp, + pioctl_buf); + } + } + if (pioctl_buf->req_id == MLAN_IOCTL_MISC_CFG) { + misc = (mlan_ds_misc_cfg *)pioctl_buf->pbuf; + cust_ie = (mlan_ds_misc_custom_ie *) + sys_config->tlv_buffer; + if ((pioctl_buf->action == MLAN_ACT_GET || + pioctl_buf->action == MLAN_ACT_SET) && + (misc->sub_command == MLAN_OID_MISC_CUSTOM_IE)) { + cust_ie->type = wlan_le16_to_cpu(cust_ie->type); + resp_len = cust_ie->len = + wlan_le16_to_cpu(cust_ie->len); + travel_len = 0; + /* conversion for index, mask, len */ + if (resp_len == sizeof(t_u16)) + cust_ie->ie_data_list[0].ie_index = + wlan_cpu_to_le16( + cust_ie->ie_data_list[0] + .ie_index); + + while (resp_len > sizeof(t_u16)) { + cptr = (custom_ie + *)(((t_u8 *)cust_ie + ->ie_data_list) + + travel_len); + cptr->ie_index = wlan_le16_to_cpu( + cptr->ie_index); + cptr->mgmt_subtype_mask = + wlan_le16_to_cpu( + cptr->mgmt_subtype_mask); + cptr->ie_length = wlan_le16_to_cpu( + cptr->ie_length); + travel_len += cptr->ie_length + + sizeof(custom_ie) - + MAX_IE_SIZE; + resp_len -= cptr->ie_length + + sizeof(custom_ie) - + MAX_IE_SIZE; + } + memcpy_ext(pmpriv->adapter, + &misc->param.cust_ie, cust_ie, + cust_ie->len + + sizeof(MrvlIEtypesHeader_t), + sizeof(mlan_ds_misc_custom_ie) - + sizeof(tlvbuf_max_mgmt_ie)); + max_mgmt_ie = + (tlvbuf_max_mgmt_ie + *)(sys_config->tlv_buffer + + cust_ie->len + + sizeof(MrvlIEtypesHeader_t)); + if (max_mgmt_ie) { + max_mgmt_ie->type = wlan_le16_to_cpu( + max_mgmt_ie->type); + if (max_mgmt_ie->type == + TLV_TYPE_MAX_MGMT_IE) { + max_mgmt_ie->len = + wlan_le16_to_cpu( + max_mgmt_ie + ->len); + max_mgmt_ie->count = + wlan_le16_to_cpu( + max_mgmt_ie + ->count); + for (i = 0; + i < max_mgmt_ie->count; + i++) { + max_mgmt_ie->info[i] + .buf_size = wlan_le16_to_cpu( + max_mgmt_ie + ->info[i] + .buf_size); + max_mgmt_ie->info[i] + .buf_count = wlan_le16_to_cpu( + max_mgmt_ie + ->info[i] + .buf_count); + } + /* Append max_mgmt_ie TLV after + * custom_ie */ + memcpy_ext( + pmpriv->adapter, + (t_u8 *)&misc->param + .cust_ie + + (cust_ie->len + + sizeof(MrvlIEtypesHeader_t)), + max_mgmt_ie, + max_mgmt_ie->len + + sizeof(MrvlIEtypesHeader_t), + sizeof(tlvbuf_max_mgmt_ie)); + } + } + } + } + } else { /* no ioctl: driver generated get/set */ + switch (wlan_le16_to_cpu(tlv->header.type)) { + case TLV_TYPE_UAP_MAC_ADDRESS: + memcpy_ext(pmpriv->adapter, pmpriv->curr_addr, tlv->mac, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + break; + case TLV_TYPE_UAP_MAX_STA_CNT_PER_CHIP: + tlv_uap_max_sta = (MrvlIEtypes_uap_max_sta_cnt_t *)tlv; + pmpriv->adapter->max_sta_conn = + wlan_le16_to_cpu(tlv_uap_max_sta->uap_max_sta); + PRINTM(MCMND, "Uap max_sta per chip=%d\n", + wlan_le16_to_cpu(tlv_uap_max_sta->uap_max_sta)); + break; + case TLV_TYPE_UAP_CHAN_BAND_CONFIG: + tlv_cb = (MrvlIEtypes_channel_band_t *)tlv; + pmpriv->uap_state_chan_cb.bandcfg = tlv_cb->bandcfg; + pmpriv->uap_state_chan_cb.channel = tlv_cb->channel; + /* call callback waiting for channel info */ + if (pmpriv->uap_state_chan_cb.get_chan_callback) + pmpriv->uap_state_chan_cb.get_chan_callback( + pmpriv); + break; + case TLV_TYPE_UAP_BEACON_PERIOD: + tlv_bcnpd = (MrvlIEtypes_beacon_period_t *)tlv; + pmpriv->uap_state_chan_cb.beacon_period = + wlan_le16_to_cpu(tlv_bcnpd->beacon_period); + /* copy dtim_period as well if it follows */ + tlv_dtimpd = + (MrvlIEtypes_dtim_period_t + *)(((t_u8 *)tlv) + + sizeof(MrvlIEtypes_beacon_period_t)); + if (TLV_TYPE_UAP_DTIM_PERIOD == + wlan_le16_to_cpu(tlv_dtimpd->header.type)) + pmpriv->uap_state_chan_cb.dtim_period = + tlv_dtimpd->dtim_period; + /* call callback waiting for beacon/dtim info */ + if (pmpriv->uap_state_chan_cb.get_chan_callback) + pmpriv->uap_state_chan_cb.get_chan_callback( + pmpriv); + break; + case TLV_TYPE_MGMT_IE: + if ((pmpriv->adapter->state_rdh.stage == + RDH_SET_CUSTOM_IE) || + (pmpriv->adapter->state_rdh.stage == + RDH_REM_CUSTOM_IE)) { + if (!pmpriv->adapter->ecsa_enable) + wlan_11h_radar_detected_callback( + (t_void *)pmpriv); + } + break; + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of snmp_mib + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param cmd_oid Cmd oid: treated as sub command + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @param pdata_buf A pointer to information buffer + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_uap_cmd_snmp_mib(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u32 cmd_oid, + pmlan_ioctl_req pioctl_buf, + t_void *pdata_buf) +{ + HostCmd_DS_802_11_SNMP_MIB *psnmp_mib = &cmd->params.smib; + HostCmd_DS_UAP_802_11_SNMP_MIB *puap_snmp_mib = &cmd->params.uap_smib; + + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *psnmp_oid = MNULL; + t_u32 ul_temp; + t_u8 i; + + t_u8 snmp_oids[] = { + tkip_mic_failures, + ccmp_decrypt_errors, + wep_undecryptable_count, + wep_icv_error_count, + decrypt_failure_count, + dot11_mcast_tx_count, + dot11_failed_count, + dot11_retry_count, + dot11_multi_retry_count, + dot11_frame_dup_count, + dot11_rts_success_count, + dot11_rts_failure_count, + dot11_ack_failure_count, + dot11_rx_fragment_count, + dot11_mcast_rx_frame_count, + dot11_fcs_error_count, + dot11_tx_frame_count, + dot11_rsna_tkip_cm_invoked, + dot11_rsna_4way_hshk_failures, + }; + + ENTER(); + + if (cmd_action == HostCmd_ACT_GEN_GET) { + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); + psnmp_mib->query_type = wlan_cpu_to_le16(HostCmd_ACT_GEN_GET); + if (cmd_oid == StopDeauth_i) { + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)StopDeauth_i); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + cmd->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_802_11_SNMP_MIB) + S_DS_GEN); + } else { + cmd->size = wlan_cpu_to_le16( + sizeof(t_u16) + S_DS_GEN + + sizeof(snmp_oids) * + sizeof(MrvlIEtypes_snmp_oid_t)); + psnmp_oid = (t_u8 *)&puap_snmp_mib->snmp_data; + for (i = 0; i < sizeof(snmp_oids); i++) { + /* SNMP OID header type */ + *(t_u16 *)psnmp_oid = + wlan_cpu_to_le16(snmp_oids[i]); + psnmp_oid += sizeof(t_u16); + /* SNMP OID header length */ + *(t_u16 *)psnmp_oid = + wlan_cpu_to_le16(sizeof(t_u32)); + psnmp_oid += sizeof(t_u16) + sizeof(t_u32); + } + } + } else { /* cmd_action == ACT_SET */ + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); + cmd->size = sizeof(HostCmd_DS_802_11_SNMP_MIB) - 1 + S_DS_GEN; + psnmp_mib->query_type = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + + switch (cmd_oid) { + case Dot11D_i: + case Dot11H_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)cmd_oid); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u16)); + ul_temp = *(t_u32 *)pdata_buf; + *((t_u16 *)(psnmp_mib->value)) = + wlan_cpu_to_le16((t_u16)ul_temp); + cmd->size += sizeof(t_u16); + break; + case ECSAEnable_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)cmd_oid); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + psnmp_mib->value[0] = *((t_u8 *)pdata_buf); + cmd->size += sizeof(t_u8); + break; + case StopDeauth_i: + psnmp_mib->oid = wlan_cpu_to_le16((t_u16)cmd_oid); + psnmp_mib->buf_size = wlan_cpu_to_le16(sizeof(t_u8)); + psnmp_mib->value[0] = *((t_u8 *)pdata_buf); + cmd->size += sizeof(t_u8); + break; + default: + PRINTM(MERROR, "Unsupported OID.\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + cmd->size = wlan_cpu_to_le16(cmd->size); + } + + LEAVE(); + return ret; +} + +/** + * @brief This function prepares command of get_log. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_cmd_802_11_get_log(pmlan_private pmpriv, + 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 bss_start. + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_cmd_bss_start(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd) +{ + MrvlIEtypes_HostMlme_t *tlv; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HOST_CMD_APCMD_BSS_START); + cmd->size = S_DS_GEN; + if (pmpriv->uap_host_based & UAP_FLAG_HOST_MLME) { + tlv = (MrvlIEtypes_HostMlme_t *)((t_u8 *)cmd + cmd->size); + tlv->header.type = wlan_cpu_to_le16(TLV_TYPE_HOST_MLME); + tlv->header.len = wlan_cpu_to_le16(sizeof(tlv->host_mlme)); + tlv->host_mlme = MTRUE; + cmd->size += sizeof(MrvlIEtypes_HostMlme_t); + } + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of snmp_mib + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_ret_snmp_mib(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + HostCmd_DS_802_11_SNMP_MIB *psnmp_mib = + (HostCmd_DS_802_11_SNMP_MIB *)&resp->params.smib; + mlan_ds_get_info *info; + mlan_ds_snmp_mib *mib = MNULL; + t_u16 oid = wlan_le16_to_cpu(psnmp_mib->oid); + t_u8 *psnmp_oid = MNULL; + t_u32 data; + t_u16 tlv_buf_left = 0; + t_u16 tlv_type = 0; + t_u16 query_type = wlan_le16_to_cpu(psnmp_mib->query_type); + + ENTER(); + if (query_type == HostCmd_ACT_GEN_GET) { + if (!pioctl_buf) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + if (oid == StopDeauth_i) { + mib = (mlan_ds_snmp_mib *)pioctl_buf->pbuf; + if (mib) + mib->param.deauthctrl = psnmp_mib->value[0]; + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + info = (mlan_ds_get_info *)pioctl_buf->pbuf; + tlv_buf_left = resp->size - (sizeof(t_u16) + S_DS_GEN); + psnmp_oid = (t_u8 *)&psnmp_mib->oid; + while (tlv_buf_left >= sizeof(MrvlIEtypes_snmp_oid_t)) { + tlv_type = wlan_le16_to_cpu(*(t_u16 *)psnmp_oid); + psnmp_oid += sizeof(t_u16) + sizeof(t_u16); + memcpy_ext(pmadapter, &data, psnmp_oid, sizeof(t_u32), + sizeof(t_u32)); + switch (tlv_type) { + case tkip_mic_failures: + info->param.ustats.tkip_mic_failures = + wlan_le32_to_cpu(data); + break; + case ccmp_decrypt_errors: + info->param.ustats.ccmp_decrypt_errors = + wlan_le32_to_cpu(data); + break; + case wep_undecryptable_count: + info->param.ustats.wep_undecryptable_count = + wlan_le32_to_cpu(data); + break; + case wep_icv_error_count: + info->param.ustats.wep_icv_error_count = + wlan_le32_to_cpu(data); + break; + case decrypt_failure_count: + info->param.ustats.decrypt_failure_count = + wlan_le32_to_cpu(data); + break; + case dot11_mcast_tx_count: + info->param.ustats.mcast_tx_count = + wlan_le32_to_cpu(data); + break; + case dot11_failed_count: + info->param.ustats.failed_count = + wlan_le32_to_cpu(data); + break; + case dot11_retry_count: + info->param.ustats.retry_count = + wlan_le32_to_cpu(data); + break; + case dot11_multi_retry_count: + info->param.ustats.multi_retry_count = + wlan_le32_to_cpu(data); + break; + case dot11_frame_dup_count: + info->param.ustats.frame_dup_count = + wlan_le32_to_cpu(data); + break; + case dot11_rts_success_count: + info->param.ustats.rts_success_count = + wlan_le32_to_cpu(data); + break; + case dot11_rts_failure_count: + info->param.ustats.rts_failure_count = + wlan_le32_to_cpu(data); + break; + case dot11_ack_failure_count: + info->param.ustats.ack_failure_count = + wlan_le32_to_cpu(data); + break; + case dot11_rx_fragment_count: + info->param.ustats.rx_fragment_count = + wlan_le32_to_cpu(data); + break; + case dot11_mcast_rx_frame_count: + info->param.ustats.mcast_rx_frame_count = + wlan_le32_to_cpu(data); + break; + case dot11_fcs_error_count: + info->param.ustats.fcs_error_count = + wlan_le32_to_cpu(data); + break; + case dot11_tx_frame_count: + info->param.ustats.tx_frame_count = + wlan_le32_to_cpu(data); + break; + case dot11_rsna_tkip_cm_invoked: + info->param.ustats.rsna_tkip_cm_invoked = + wlan_le32_to_cpu(data); + break; + case dot11_rsna_4way_hshk_failures: + info->param.ustats.rsna_4way_hshk_failures = + wlan_le32_to_cpu(data); + break; + } + tlv_buf_left -= sizeof(MrvlIEtypes_snmp_oid_t); + psnmp_oid += sizeof(t_u32); + } + } else { /* ACT_SET */ + switch (wlan_le16_to_cpu(psnmp_mib->oid)) { + case Dot11D_i: + data = wlan_le16_to_cpu(*((t_u16 *)(psnmp_mib->value))); + /* Set 11d state to private */ + pmpriv->state_11d.enable_11d = data; + /* Set user enable flag if called from ioctl */ + if (pioctl_buf) + pmpriv->state_11d.user_enable_11d = data; + break; + case Dot11H_i: + data = wlan_le16_to_cpu(*((t_u16 *)(psnmp_mib->value))); + /* Set 11h state to priv */ + pmpriv->intf_state_11h.is_11h_active = + (data & ENABLE_11H_MASK); + /* Set radar_det state to adapter */ + pmpriv->adapter->state_11h.is_master_radar_det_active = + (data & MASTER_RADAR_DET_MASK) ? MTRUE : MFALSE; + pmpriv->adapter->state_11h.is_slave_radar_det_active = + (data & SLAVE_RADAR_DET_MASK) ? MTRUE : MFALSE; + break; + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of get_log + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_ret_get_log(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_802_11_GET_LOG *pget_log = + (HostCmd_DS_802_11_GET_LOG *)&resp->params.get_log; + mlan_ds_get_info *pget_info = MNULL; + int i = 0; + + ENTER(); + + if (pioctl_buf) { + pget_info = (mlan_ds_get_info *)pioctl_buf->pbuf; + pget_info->param.stats.mcast_tx_frame = + wlan_le32_to_cpu(pget_log->mcast_tx_frame); + pget_info->param.stats.failed = + wlan_le32_to_cpu(pget_log->failed); + pget_info->param.stats.retry = + wlan_le32_to_cpu(pget_log->retry); + pget_info->param.stats.multi_retry = + wlan_le32_to_cpu(pget_log->multiretry); + pget_info->param.stats.frame_dup = + wlan_le32_to_cpu(pget_log->frame_dup); + pget_info->param.stats.rts_success = + wlan_le32_to_cpu(pget_log->rts_success); + pget_info->param.stats.rts_failure = + wlan_le32_to_cpu(pget_log->rts_failure); + pget_info->param.stats.ack_failure = + wlan_le32_to_cpu(pget_log->ack_failure); + pget_info->param.stats.rx_frag = + wlan_le32_to_cpu(pget_log->rx_frag); + pget_info->param.stats.mcast_rx_frame = + wlan_le32_to_cpu(pget_log->mcast_rx_frame); + pget_info->param.stats.fcs_error = + wlan_le32_to_cpu(pget_log->fcs_error); + pget_info->param.stats.tx_frame = + wlan_le32_to_cpu(pget_log->tx_frame); + pget_info->param.stats.wep_icv_error[0] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[0]); + pget_info->param.stats.wep_icv_error[1] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[1]); + pget_info->param.stats.wep_icv_error[2] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[2]); + pget_info->param.stats.wep_icv_error[3] = + wlan_le32_to_cpu(pget_log->wep_icv_err_cnt[3]); + pget_info->param.stats.bcn_rcv_cnt = + wlan_le32_to_cpu(pget_log->bcn_rcv_cnt); + pget_info->param.stats.bcn_miss_cnt = + wlan_le32_to_cpu(pget_log->bcn_miss_cnt); + pget_info->param.stats.amsdu_rx_cnt = pmpriv->amsdu_rx_cnt; + pget_info->param.stats.msdu_in_rx_amsdu_cnt = + pmpriv->msdu_in_rx_amsdu_cnt; + pget_info->param.stats.amsdu_tx_cnt = pmpriv->amsdu_tx_cnt; + pget_info->param.stats.msdu_in_tx_amsdu_cnt = + pmpriv->msdu_in_tx_amsdu_cnt; + pget_info->param.stats.rx_stuck_issue_cnt[0] = + wlan_le32_to_cpu(pget_log->rx_stuck_issue_cnt[0]); + pget_info->param.stats.rx_stuck_issue_cnt[1] = + wlan_le32_to_cpu(pget_log->rx_stuck_issue_cnt[1]); + pget_info->param.stats.rx_stuck_recovery_cnt = + wlan_le32_to_cpu(pget_log->rx_stuck_recovery_cnt); + pget_info->param.stats.rx_stuck_tsf[0] = + wlan_le64_to_cpu(pget_log->rx_stuck_tsf[0]); + pget_info->param.stats.rx_stuck_tsf[1] = + wlan_le64_to_cpu(pget_log->rx_stuck_tsf[1]); + pget_info->param.stats.tx_watchdog_recovery_cnt = + wlan_le32_to_cpu(pget_log->tx_watchdog_recovery_cnt); + pget_info->param.stats.tx_watchdog_tsf[0] = + wlan_le64_to_cpu(pget_log->tx_watchdog_tsf[0]); + pget_info->param.stats.tx_watchdog_tsf[1] = + wlan_le64_to_cpu(pget_log->tx_watchdog_tsf[1]); + pget_info->param.stats.channel_switch_ann_sent = + wlan_le32_to_cpu(pget_log->channel_switch_ann_sent); + pget_info->param.stats.channel_switch_state = + wlan_le32_to_cpu(pget_log->channel_switch_state); + pget_info->param.stats.reg_class = + wlan_le32_to_cpu(pget_log->reg_class); + pget_info->param.stats.channel_number = + wlan_le32_to_cpu(pget_log->channel_number); + pget_info->param.stats.channel_switch_mode = + wlan_le32_to_cpu(pget_log->channel_switch_mode); + if (pmpriv->adapter->getlog_enable) { + pget_info->param.stats.tx_frag_cnt = + wlan_le32_to_cpu(pget_log->tx_frag_cnt); + for (i = 0; i < 8; i++) { + pget_info->param.stats.qos_tx_frag_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_tx_frag_cnt[i]); + pget_info->param.stats.qos_failed_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_failed_cnt[i]); + pget_info->param.stats.qos_retry_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_retry_cnt[i]); + pget_info->param.stats.qos_multi_retry_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_multi_retry_cnt[i]); + pget_info->param.stats.qos_frm_dup_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_frm_dup_cnt[i]); + pget_info->param.stats.qos_rts_suc_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_rts_suc_cnt[i]); + pget_info->param.stats.qos_rts_failure_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_rts_failure_cnt[i]); + pget_info->param.stats.qos_ack_failure_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_ack_failure_cnt[i]); + pget_info->param.stats.qos_rx_frag_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_rx_frag_cnt[i]); + pget_info->param.stats.qos_tx_frm_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_tx_frm_cnt[i]); + pget_info->param.stats.qos_discarded_frm_cnt + [i] = wlan_le32_to_cpu( + pget_log->qos_discarded_frm_cnt[i]); + pget_info->param.stats.qos_mpdus_rx_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_mpdus_rx_cnt[i]); + pget_info->param.stats.qos_retries_rx_cnt[i] = + wlan_le32_to_cpu( + pget_log->qos_retries_rx_cnt[i]); + } + pget_info->param.stats.mgmt_ccmp_replays = + wlan_le32_to_cpu(pget_log->mgmt_ccmp_replays); + pget_info->param.stats.tx_amsdu_cnt = + wlan_le32_to_cpu(pget_log->tx_amsdu_cnt); + pget_info->param.stats.failed_amsdu_cnt = + wlan_le32_to_cpu(pget_log->failed_amsdu_cnt); + pget_info->param.stats.retry_amsdu_cnt = + wlan_le32_to_cpu(pget_log->retry_amsdu_cnt); + pget_info->param.stats.multi_retry_amsdu_cnt = + wlan_le32_to_cpu( + pget_log->multi_retry_amsdu_cnt); + pget_info->param.stats.tx_octets_in_amsdu_cnt = + wlan_le64_to_cpu( + pget_log->tx_octets_in_amsdu_cnt); + pget_info->param.stats.amsdu_ack_failure_cnt = + wlan_le32_to_cpu( + pget_log->amsdu_ack_failure_cnt); + pget_info->param.stats.rx_amsdu_cnt = + wlan_le32_to_cpu(pget_log->rx_amsdu_cnt); + pget_info->param.stats.rx_octets_in_amsdu_cnt = + wlan_le64_to_cpu( + pget_log->rx_octets_in_amsdu_cnt); + pget_info->param.stats.tx_ampdu_cnt = + wlan_le32_to_cpu(pget_log->tx_ampdu_cnt); + pget_info->param.stats.tx_mpdus_in_ampdu_cnt = + wlan_le32_to_cpu( + pget_log->tx_mpdus_in_ampdu_cnt); + pget_info->param.stats.tx_octets_in_ampdu_cnt = + wlan_le64_to_cpu( + pget_log->tx_octets_in_ampdu_cnt); + pget_info->param.stats.ampdu_rx_cnt = + wlan_le32_to_cpu(pget_log->ampdu_rx_cnt); + pget_info->param.stats.mpdu_in_rx_ampdu_cnt = + wlan_le32_to_cpu( + pget_log->mpdu_in_rx_ampdu_cnt); + pget_info->param.stats.rx_octets_in_ampdu_cnt = + wlan_le64_to_cpu( + pget_log->rx_octets_in_ampdu_cnt); + pget_info->param.stats.ampdu_delimiter_crc_error_cnt = + wlan_le32_to_cpu( + pget_log->ampdu_delimiter_crc_error_cnt); + + /* Indicate ioctl complete */ + pioctl_buf->data_read_written = + sizeof(mlan_ds_get_info); + } else + pioctl_buf->data_read_written = + sizeof(mlan_ds_get_stats_org) + + sizeof(pget_info->sub_command); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of deauth station + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_cmd_sta_deauth(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + 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_ext(pmpriv->adapter, pcmd_sta_deauth->mac, deauth->mac_addr, + MLAN_MAC_ADDR_LENGTH, 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 report mic_err + * + * @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_report_mic(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf) +{ + HostCmd_DS_REPORT_MIC *pcmd_report_mic = + (HostCmd_DS_REPORT_MIC *)&cmd->params.report_mic; + + ENTER(); + cmd->command = wlan_cpu_to_le16(HOST_CMD_APCMD_REPORT_MIC); + cmd->size = wlan_cpu_to_le16(S_DS_GEN + sizeof(HostCmd_DS_REPORT_MIC)); + memcpy_ext(pmpriv->adapter, pcmd_report_mic->mac, pdata_buf, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_u16 cmd_oid, + t_void *pdata_buf) +{ + HostCmd_DS_802_11_KEY_MATERIAL *pkey_material = + &cmd->params.key_material; + mlan_ds_encrypt_key *pkey = (mlan_ds_encrypt_key *)pdata_buf; + mlan_status ret = MLAN_STATUS_SUCCESS; + sta_node *sta_ptr = MNULL; + + ENTER(); + if (!pkey) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + pkey_material->action = wlan_cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_GET) { + cmd->size = wlan_cpu_to_le16(sizeof(pkey_material->action) + + S_DS_GEN); + goto done; + } + memset(pmpriv->adapter, &pkey_material->key_param_set, 0, + sizeof(MrvlIEtype_KeyParamSetV2_t)); + if (pkey->key_flags & KEY_FLAG_REMOVE_KEY) { + pkey_material->action = + wlan_cpu_to_le16(HostCmd_ACT_GEN_REMOVE); + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.length = + wlan_cpu_to_le16(KEY_PARAMS_FIXED_LEN); + pkey_material->key_param_set.key_idx = + pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.key_info = wlan_cpu_to_le16( + KEY_INFO_MCAST_KEY | KEY_INFO_UCAST_KEY); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Remove Key\n"); + goto done; + } + pkey_material->action = wlan_cpu_to_le16(HostCmd_ACT_GEN_SET); + pkey_material->key_param_set.key_idx = pkey->key_index & KEY_INDEX_MASK; + pkey_material->key_param_set.type = + wlan_cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + pkey_material->key_param_set.key_info = KEY_INFO_ENABLE_KEY; + memcpy_ext(pmpriv->adapter, pkey_material->key_param_set.mac_addr, + pkey->mac_addr, MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + if (pkey->key_len <= MAX_WEP_KEY_SIZE) { + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(wep_param_t)); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_WEP; + pkey_material->key_param_set.key_info |= + KEY_INFO_MCAST_KEY | KEY_INFO_UCAST_KEY; + if (pkey_material->key_param_set.key_idx == + (pmpriv->wep_key_curr_index & KEY_INDEX_MASK)) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + pkey_material->key_param_set.key_params.wep.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.wep.key, + pkey->key_material, pkey->key_len, MAX_WEP_KEY_SIZE); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(wep_param_t) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set WEP Key\n"); + goto done; + } + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) + pkey_material->key_param_set.key_info |= KEY_INFO_MCAST_KEY; + else + pkey_material->key_param_set.key_info |= KEY_INFO_UCAST_KEY; + if (pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK) + pkey_material->key_param_set.key_info = KEY_INFO_CMAC_AES_KEY; + if (pkey->key_flags & KEY_FLAG_SET_TX_KEY) + pkey_material->key_param_set.key_info |= + KEY_INFO_TX_KEY | KEY_INFO_RX_KEY; + else + pkey_material->key_param_set.key_info |= KEY_INFO_TX_KEY; + if (pkey->is_wapi_key) { + pkey_material->key_param_set.key_type = KEY_TYPE_ID_WAPI; + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.wapi.pn, + pkey->pn, PN_SIZE, PN_SIZE); + pkey_material->key_param_set.key_params.wapi.key_len = + wlan_cpu_to_le16(MIN(WAPI_KEY_SIZE, pkey->key_len)); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.wapi.key, + pkey->key_material, pkey->key_len, WAPI_KEY_SIZE); + if (!pmpriv->sec_info.wapi_key_on) + pkey_material->key_param_set.key_info |= + KEY_INFO_DEFAULT_KEY; + if (pkey->key_flags & KEY_FLAG_GROUP_KEY) { + pmpriv->sec_info.wapi_key_on = MTRUE; + } else { + /* WAPI pairwise key: unicast */ + sta_ptr = + wlan_add_station_entry(pmpriv, pkey->mac_addr); + if (sta_ptr) { + PRINTM(MCMND, "station: wapi_key_on\n"); + sta_ptr->wapi_key_on = MTRUE; + } + } + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(wapi_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(wapi_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set WAPI Key\n"); + goto done; + } + pkey_material->key_param_set.key_info |= KEY_INFO_DEFAULT_KEY; + pkey_material->key_param_set.key_info = + wlan_cpu_to_le16(pkey_material->key_param_set.key_info); + if (pkey->key_len == WPA_AES_KEY_LEN && + !(pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK)) { + if (pkey->key_flags & + (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy_ext( + pmpriv->adapter, + pkey_material->key_param_set.key_params.aes.pn, + pkey->pn, SEQ_MAX_SIZE, WPA_PN_SIZE); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_AES; + pkey_material->key_param_set.key_params.aes.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.aes.key, + pkey->key_material, pkey->key_len, WPA_AES_KEY_LEN); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(aes_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(aes_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set AES Key\n"); + goto done; + } + if (pkey->key_len == WPA_IGTK_KEY_LEN && + (pkey->key_flags & KEY_FLAG_AES_MCAST_IGTK)) { + if (pkey->key_flags & + (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params + .cmac_aes.ipn, + pkey->pn, SEQ_MAX_SIZE, IGTK_PN_SIZE); + pkey_material->key_param_set.key_info &= + ~(wlan_cpu_to_le16(KEY_INFO_MCAST_KEY)); + pkey_material->key_param_set.key_info |= + wlan_cpu_to_le16(KEY_INFO_AES_MCAST_IGTK); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; + pkey_material->key_param_set.key_params.cmac_aes.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.cmac_aes.key, + pkey->key_material, pkey->key_len, CMAC_AES_KEY_LEN); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(cmac_aes_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(cmac_aes_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set CMAC AES Key\n"); + goto done; + } + if (pkey->key_len == WPA_TKIP_KEY_LEN) { + if (pkey->key_flags & + (KEY_FLAG_RX_SEQ_VALID | KEY_FLAG_TX_SEQ_VALID)) + memcpy_ext( + pmpriv->adapter, + pkey_material->key_param_set.key_params.tkip.pn, + pkey->pn, SEQ_MAX_SIZE, WPA_PN_SIZE); + pkey_material->key_param_set.key_type = KEY_TYPE_ID_TKIP; + pkey_material->key_param_set.key_params.tkip.key_len = + wlan_cpu_to_le16(pkey->key_len); + memcpy_ext(pmpriv->adapter, + pkey_material->key_param_set.key_params.tkip.key, + pkey->key_material, pkey->key_len, WPA_TKIP_KEY_LEN); + pkey_material->key_param_set.length = wlan_cpu_to_le16( + KEY_PARAMS_FIXED_LEN + sizeof(tkip_param)); + cmd->size = wlan_cpu_to_le16(sizeof(MrvlIEtypesHeader_t) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(tkip_param) + + sizeof(pkey_material->action)); + PRINTM(MCMND, "Set TKIP Key\n"); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles the command response of sta_list + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_ret_sta_list(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_STA_LIST *sta_list = + (HostCmd_DS_STA_LIST *)&resp->params.sta_list; + mlan_ds_get_info *info; + MrvlIEtypes_sta_info_t *tlv = MNULL; + t_u8 i = 0; + sta_node *sta_ptr; + t_u8 tlv_len = 0; + t_u8 *buf = MNULL; + + 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); + buf = sta_list->tlv_buf; + tlv = (MrvlIEtypes_sta_info_t *)buf; + info->param.sta_list.sta_count = + MIN(info->param.sta_list.sta_count, MAX_NUM_CLIENTS); + pioctl_buf->data_read_written = + sizeof(info->param.sta_list.sta_count); + for (i = 0; i < info->param.sta_list.sta_count; i++) { + tlv_len = wlan_le16_to_cpu(tlv->header.len); + memcpy_ext(pmpriv->adapter, + info->param.sta_list.info[i].mac_address, + tlv->mac_address, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + info->param.sta_list.info[i].ie_len = + tlv_len + sizeof(MrvlIEtypesHeader_t) - + sizeof(MrvlIEtypes_sta_info_t); + if (info->param.sta_list.info[i].ie_len) + memcpy_ext(pmpriv->adapter, + info->param.sta_list.info[i].ie_buf, + tlv->ie_buf, + info->param.sta_list.info[i].ie_len, + info->param.sta_list.info[i].ie_len); + info->param.sta_list.info[i].power_mgmt_status = + tlv->power_mgmt_status; + info->param.sta_list.info[i].rssi = tlv->rssi; + sta_ptr = wlan_get_station_entry(pmpriv, + tlv->mac_address); + if (sta_ptr) { + info->param.sta_list.info[i].bandmode = + sta_ptr->bandmode; + memcpy_ext( + pmpriv->adapter, + &(info->param.sta_list.info[i].stats), + &(sta_ptr->stats), sizeof(sta_stats), + sizeof(sta_stats)); + } else + info->param.sta_list.info[i].bandmode = 0xFF; + pioctl_buf->data_read_written += + sizeof(sta_info) + + info->param.sta_list.info[i].ie_len; + buf += sizeof(MrvlIEtypes_sta_info_t) + + info->param.sta_list.info[i].ie_len; + tlv = (MrvlIEtypes_sta_info_t *)buf; + } + PRINTM(MCMND, "Total sta_list size=%d\n", + pioctl_buf->data_read_written); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** Fixed size of bss start event */ +#define BSS_START_EVENT_FIX_SIZE 12 + +/** + * @brief This function will search for the specific ie + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * + * @return N/A + */ +static void wlan_check_uap_capability(pmlan_private priv, pmlan_buffer pevent) +{ + t_u16 tlv_type, tlv_len; + int tlv_buf_left = pevent->data_len - BSS_START_EVENT_FIX_SIZE; + MrvlIEtypesHeader_t *tlv = + (MrvlIEtypesHeader_t *)(pevent->pbuf + pevent->data_offset + + BSS_START_EVENT_FIX_SIZE); + + const t_u8 wmm_oui[4] = {0x00, 0x50, 0xf2, 0x02}; + IEEEtypes_WmmParameter_t wmm_param_ie; + MrvlIEtypes_channel_band_t *pchan_info; + t_u8 event_buf[100]; + mlan_event *event = (mlan_event *)event_buf; + chan_band_info *pchan_band_info = (chan_band_info *)event->event_buf; + MrvlIEtypes_He_cap_t *pext_tlv = MNULL; + + ENTER(); + priv->wmm_enabled = MFALSE; + priv->pkt_fwd = MFALSE; + priv->is_11n_enabled = MFALSE; + priv->is_11ac_enabled = MFALSE; + priv->is_11ax_enabled = MFALSE; + + 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 == VHT_CAPABILITY) { + DBG_HEXDUMP(MCMD_D, "VHT_CAP tlv", tlv, + tlv_len + sizeof(MrvlIEtypesHeader_t)); + priv->is_11ac_enabled = MTRUE; + } + if (tlv_type == EXTENSION) { + pext_tlv = (MrvlIEtypes_He_cap_t *)tlv; + if (pext_tlv->ext_id == HE_CAPABILITY) { + DBG_HEXDUMP( + MCMD_D, "HE_CAP tlv", tlv, + tlv_len + sizeof(MrvlIEtypesHeader_t)); + priv->is_11ax_enabled = MTRUE; + } + } + + if (tlv_type == VENDOR_SPECIFIC_221) { + if (!memcmp(priv->adapter, + (t_u8 *)tlv + sizeof(MrvlIEtypesHeader_t), + wmm_oui, sizeof(wmm_oui))) { + DBG_HEXDUMP( + MCMD_D, "wmm ie tlv", tlv, + tlv_len + sizeof(MrvlIEtypesHeader_t)); + priv->wmm_enabled = MFALSE; + wlan_wmm_setup_ac_downgrade(priv); + priv->wmm_enabled = MTRUE; + memcpy_ext(priv->adapter, &wmm_param_ie, + ((t_u8 *)tlv + 2), + sizeof(IEEEtypes_WmmParameter_t), + sizeof(IEEEtypes_WmmParameter_t)); + wmm_param_ie.vend_hdr.len = (t_u8)tlv_len; + wmm_param_ie.vend_hdr.element_id = WMM_IE; + wlan_wmm_setup_queue_priorities(priv, + &wmm_param_ie); + } + } + if (tlv_type == TLV_TYPE_UAP_PKT_FWD_CTL) { + DBG_HEXDUMP(MCMD_D, "pkt_fwd tlv", tlv, + tlv_len + sizeof(MrvlIEtypesHeader_t)); + priv->pkt_fwd = + *((t_u8 *)tlv + sizeof(MrvlIEtypesHeader_t)); + PRINTM(MCMND, "pkt_fwd FW: 0x%x\n", priv->pkt_fwd); + if (priv->pkt_fwd & PKT_FWD_FW_BIT) + priv->pkt_fwd = MFALSE; + else + priv->pkt_fwd |= PKT_FWD_ENABLE_BIT; + PRINTM(MCMND, "pkt_fwd DRV: 0x%x\n", priv->pkt_fwd); + } + if (tlv_type == TLV_TYPE_UAP_CHAN_BAND_CONFIG) { + DBG_HEXDUMP(MCMD_D, "chan_band_config tlv", tlv, + tlv_len + sizeof(MrvlIEtypesHeader_t)); + pchan_info = (MrvlIEtypes_channel_band_t *)tlv; + priv->uap_channel = pchan_info->channel; + PRINTM(MCMND, "uap_channel FW: 0x%x\n", + priv->uap_channel); + event->bss_index = priv->bss_index; + event->event_id = MLAN_EVENT_ID_DRV_UAP_CHAN_INFO; + event->event_len = sizeof(chan_band_info); + memcpy_ext(priv->adapter, + (t_u8 *)&pchan_band_info->bandcfg, + (t_u8 *)&pchan_info->bandcfg, tlv_len, + tlv_len); + if (pchan_band_info->bandcfg.chanWidth == CHAN_BW_80MHZ) + pchan_band_info->center_chan = + wlan_get_center_freq_idx( + priv, BAND_AAC, + pchan_info->channel, + CHANNEL_BW_80MHZ); + if (priv->adapter->ecsa_enable) { + int ret; + t_u8 bandwidth = BW_20MHZ; + + MrvlIEtypes_chan_bw_oper_t chan_bw_oper; + chan_bw_oper.header.type = REGULATORY_CLASS; + chan_bw_oper.header.len = + sizeof(MrvlIEtypes_chan_bw_oper_t); + chan_bw_oper.ds_chan_bw_oper.channel = + pchan_info->channel; + + if (pchan_band_info->bandcfg.chanWidth == + CHAN_BW_40MHZ) + bandwidth = BW_40MHZ; + else if (pchan_band_info->bandcfg.chanWidth == + CHAN_BW_80MHZ) + bandwidth = BW_80MHZ; + chan_bw_oper.ds_chan_bw_oper.bandwidth = + bandwidth; + + ret = wlan_prepare_cmd( + priv, HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_SET, 0, MNULL, + &chan_bw_oper); + if (ret != MLAN_STATUS_SUCCESS && + ret != MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "%s(): Could not set supported operating class IE for priv=%p [priv_bss_idx=%d]!\n", + __func__, priv, priv->bss_index); + } + } + } + + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + if (priv->wmm_enabled == MFALSE) { + /* Since WMM is not enabled, setup the queues with the defaults + */ + wlan_wmm_setup_queues(priv); + } + if (event->event_id == MLAN_EVENT_ID_DRV_UAP_CHAN_INFO) { + pchan_band_info->is_11n_enabled = priv->is_11n_enabled; + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_UAP_CHAN_INFO, event); + } + + LEAVE(); +} + +/** + * @brief This function will update WAPI PN in statation assoc event + * + * @param priv A pointer to mlan_private + * @param pevent A pointer to event buf + * + * @return MFALSE + */ +static t_u32 wlan_update_wapi_info_tlv(pmlan_private priv, pmlan_buffer pevent) +{ + t_u32 ret = MFALSE; + t_u16 tlv_type, tlv_len; + t_u32 tx_pn[4]; + t_u32 i = 0; + int tlv_buf_left = pevent->data_len - ASSOC_EVENT_FIX_SIZE; + MrvlIEtypesHeader_t *tlv = + (MrvlIEtypesHeader_t *)(pevent->pbuf + pevent->data_offset + + ASSOC_EVENT_FIX_SIZE); + MrvlIEtypes_wapi_info_t *wapi_tlv = MNULL; + + ENTER(); + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_AP_WAPI_INFO) { + wapi_tlv = (MrvlIEtypes_wapi_info_t *)tlv; + DBG_HEXDUMP(MCMD_D, "Fw:multicast_PN", + wapi_tlv->multicast_PN, PN_SIZE); + memcpy_ext(priv->adapter, (t_u8 *)tx_pn, + wapi_tlv->multicast_PN, PN_SIZE, + sizeof(tx_pn)); + for (i = 0; i < 4; i++) + tx_pn[i] = mlan_ntohl(tx_pn[i]); + memcpy_ext(priv->adapter, wapi_tlv->multicast_PN, + (t_u8 *)tx_pn, PN_SIZE, + sizeof(wapi_tlv->multicast_PN)); + 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_ext(priv->adapter, pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + 6, pevent->event_len, + pevent->event_len); + while (tlv_buf_left >= (int)sizeof(MrvlIEtypesHeader_t)) { + tlv_type = wlan_le16_to_cpu(tlv->type); + tlv_len = wlan_le16_to_cpu(tlv->len); + if ((sizeof(MrvlIEtypesHeader_t) + tlv_len) > + (unsigned int)tlv_buf_left) { + PRINTM(MERROR, "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_MGMT_FRAME) { + mgmt_tlv = (MrvlIETypes_MgmtFrameSet_t *)tlv; + memcpy_ext(priv->adapter, &frame_control, + (t_u8 *)&(mgmt_tlv->frame_control), + sizeof(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_ext(priv->adapter, + pevent->event_buf + + pevent->event_len, + assoc_req_ie, ie_len, ie_len); + pevent->event_len += ie_len; + break; + } + } + tlv_buf_left -= (sizeof(MrvlIEtypesHeader_t) + tlv_len); + tlv = (MrvlIEtypesHeader_t *)((t_u8 *)tlv + tlv_len + + sizeof(MrvlIEtypesHeader_t)); + } + PRINTM(MEVENT, "STA assoc event len=%d\n", pevent->event_len); + DBG_HEXDUMP(MCMD_D, "STA assoc event", pevent->event_buf, + pevent->event_len); + wlan_recv_event(priv, pevent->event_id, pevent); + LEAVE(); + return ret; +} + +/** + * @brief This function handles repsonse of acs_scan. + * + * @param pmpriv A pointer to mlan_private structure + * @param pcmd A pointer to HostCmd_DS_COMMAND structure + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_cmd_uap_acs_scan(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCMD_DS_APCMD_ACS_SCAN *acs_scan = + (HostCMD_DS_APCMD_ACS_SCAN *)&resp->params.acs_scan; + mlan_ds_bss *bss = MNULL; + + ENTER(); + PRINTM(MCMND, "ACS scan done: bandcfg=%x, channel=%d\n", + acs_scan->bandcfg, acs_scan->chan); + if (pioctl_buf) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + bss->param.ap_acs_scan.chan = acs_scan->chan; + bss->param.ap_acs_scan.bandcfg = acs_scan->bandcfg; + pioctl_buf->data_read_written = sizeof(mlan_ds_bss); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares command of uap operation control + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action the action: GET or SET + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_cmd_oper_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, t_void *pdata_buf) +{ + HostCmd_DS_UAP_OPER_CTRL *poper_ctl = + (HostCmd_DS_UAP_OPER_CTRL *)&cmd->params.uap_oper_ctrl; + mlan_ds_bss *bss = (mlan_ds_bss *)pdata_buf; + mlan_uap_oper_ctrl *uap_oper_ctrl = &bss->param.ap_oper_ctrl; + Band_Config_t *bandcfg = MNULL; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HOST_CMD_APCMD_OPER_CTRL); + cmd->size = + wlan_cpu_to_le16(sizeof(HostCmd_DS_UAP_OPER_CTRL) + S_DS_GEN); + poper_ctl->action = wlan_cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) { + poper_ctl->ctrl = wlan_cpu_to_le16(uap_oper_ctrl->ctrl_value); + if (uap_oper_ctrl->ctrl_value == 2) { + poper_ctl->chan_opt = + wlan_cpu_to_le16(uap_oper_ctrl->chan_opt); + if (uap_oper_ctrl->chan_opt == 3) { + poper_ctl->channel_band.header.type = + wlan_cpu_to_le16( + TLV_TYPE_UAP_CHAN_BAND_CONFIG); + poper_ctl->channel_band.header + .len = wlan_cpu_to_le16( + sizeof(MrvlIEtypes_channel_band_t) - + sizeof(MrvlIEtypesHeader_t)); + bandcfg = &poper_ctl->channel_band.bandcfg; + if (uap_oper_ctrl->channel > 14) + bandcfg->chanBand = BAND_5GHZ; + bandcfg->chanWidth = uap_oper_ctrl->band_cfg; + if (bandcfg->chanWidth) + bandcfg->chan2Offset = + wlan_get_second_channel_offset( + uap_oper_ctrl->channel); + bandcfg->scanMode = SCAN_MODE_MANUAL; + poper_ctl->channel_band.channel = + uap_oper_ctrl->channel; + } + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of uap operation control + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +static mlan_status wlan_uap_ret_oper_ctrl(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + HostCmd_DS_UAP_OPER_CTRL *poper_ctl = + (HostCmd_DS_UAP_OPER_CTRL *)&resp->params.uap_oper_ctrl; + mlan_ds_bss *bss = MNULL; + mlan_uap_oper_ctrl *uap_oper_ctrl = MNULL; + Band_Config_t *bandcfg = MNULL; + + ENTER(); + + if (pioctl_buf && pioctl_buf->action == MLAN_ACT_GET) { + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + uap_oper_ctrl = (mlan_uap_oper_ctrl *)&bss->param.ap_oper_ctrl; + uap_oper_ctrl->ctrl_value = wlan_le16_to_cpu(poper_ctl->ctrl); + uap_oper_ctrl->chan_opt = wlan_le16_to_cpu(poper_ctl->chan_opt); + uap_oper_ctrl->channel = poper_ctl->channel_band.channel; + bandcfg = &poper_ctl->channel_band.bandcfg; + uap_oper_ctrl->band_cfg = bandcfg->chanWidth; + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Check 11B support Rates + * + * + * @param pmadapter Private mlan adapter structure + * + * @return MTRUE/MFALSE + * + */ +t_u8 wlan_check_11B_support_rates(MrvlIEtypes_RatesParamSet_t *prates_tlv) +{ + int i; + t_u8 rate; + t_u8 ret = MTRUE; + for (i = 0; i < prates_tlv->header.len; i++) { + rate = prates_tlv->rates[i] & 0x7f; + if ((rate != 0x02) && (rate != 0x04) && (rate != 0x0b) && + (rate != 0x16)) { + ret = MFALSE; + break; + } + } + return ret; +} + +/** + * @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 cmd action + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_uap_cmd_add_station(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_u16 cmd_action, + pmlan_ioctl_req pioctl_buf) +{ + mlan_ds_bss *bss = MNULL; + HostCmd_DS_ADD_STATION *new_sta = + (HostCmd_DS_ADD_STATION *)&cmd->params.sta_info; + sta_node *sta_ptr = MNULL; + t_u16 tlv_buf_left; + t_u8 *pos = MNULL; + t_u8 *tlv_buf = MNULL; + t_u16 travel_len = 0; + MrvlIEtypesHeader_t *tlv; + t_u16 tlv_len = 0; + t_u8 b_only = MFALSE; + mlan_adapter *pmadapter = pmpriv->adapter; + MrvlIETypes_HTCap_t *phtcap; + MrvlIETypes_VHTCap_t *pvhtcap; + MrvlIEtypes_Extension_t *pext_tlv; + MrvlIEtypes_StaFlag_t *pstaflag; + int i; + + ENTER(); + + if (!pioctl_buf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + bss = (mlan_ds_bss *)pioctl_buf->pbuf; + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_ADD_NEW_STATION); + new_sta->action = wlan_cpu_to_le16(cmd_action); + cmd->size = sizeof(HostCmd_DS_ADD_STATION) + S_DS_GEN; + if (cmd_action == HostCmd_ACT_ADD_STA) { + sta_ptr = wlan_get_station_entry(pmpriv, + bss->param.sta_info.peer_mac); + if (!sta_ptr) + sta_ptr = wlan_add_station_entry( + pmpriv, bss->param.sta_info.peer_mac); + } else { + sta_ptr = wlan_add_station_entry(pmpriv, + bss->param.sta_info.peer_mac); + } + if (!sta_ptr) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + memcpy_ext(pmadapter, new_sta->peer_mac, bss->param.sta_info.peer_mac, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + if (cmd_action != HostCmd_ACT_ADD_STA) + goto done; + new_sta->aid = wlan_cpu_to_le16(bss->param.sta_info.aid); + new_sta->listen_interval = + wlan_cpu_to_le32(bss->param.sta_info.listen_interval); + if (bss->param.sta_info.cap_info) + new_sta->cap_info = + wlan_cpu_to_le16(bss->param.sta_info.cap_info); + else + new_sta->cap_info = wlan_cpu_to_le16(sta_ptr->capability); + tlv_buf_left = bss->param.sta_info.tlv_len; + pos = new_sta->tlv; + tlv_buf = bss->param.sta_info.tlv; + tlv = (MrvlIEtypesHeader_t *)tlv_buf; + if (bss->param.sta_info.sta_flags & STA_FLAG_WME) { + PRINTM(MCMND, "STA flags supports wmm \n"); + sta_ptr->is_wmm_enabled = MTRUE; + } + // append sta_flag_flags. + pstaflag = (MrvlIEtypes_StaFlag_t *)pos; + pstaflag->header.type = wlan_cpu_to_le16(TLV_TYPE_UAP_STA_FLAGS); + pstaflag->header.len = wlan_cpu_to_le16(sizeof(t_u32)); + pstaflag->sta_flags = wlan_cpu_to_le32(bss->param.sta_info.sta_flags); + pos += sizeof(MrvlIEtypes_StaFlag_t); + cmd->size += sizeof(MrvlIEtypes_StaFlag_t); + + while (tlv_buf_left >= sizeof(MrvlIEtypesHeader_t)) { + if (tlv_buf_left < (sizeof(MrvlIEtypesHeader_t) + tlv->len)) + break; + switch (tlv->type) { + case EXT_CAPABILITY: + break; + case SUPPORTED_RATES: + b_only = wlan_check_11B_support_rates( + (MrvlIEtypes_RatesParamSet_t *)tlv); + break; + case QOS_INFO: + PRINTM(MCMND, "STA supports wmm\n"); + sta_ptr->is_wmm_enabled = MTRUE; + break; + case HT_CAPABILITY: + PRINTM(MCMND, "STA supports 11n\n"); + sta_ptr->is_11n_enabled = MTRUE; + phtcap = (MrvlIETypes_HTCap_t *)tlv; + if (sta_ptr->HTcap.ieee_hdr.element_id == + HT_CAPABILITY) { + if (GETHT_40MHZ_INTOLARANT( + sta_ptr->HTcap.ht_cap.ht_cap_info)) { + PRINTM(MCMND, + "SETHT_40MHZ_INTOLARANT\n"); + SETHT_40MHZ_INTOLARANT( + phtcap->ht_cap.ht_cap_info); + } + } + if (GETHT_MAXAMSDU(phtcap->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; + break; + case VHT_CAPABILITY: + PRINTM(MCMND, "STA supports 11ac\n"); + sta_ptr->is_11ac_enabled = MTRUE; + pvhtcap = (MrvlIETypes_VHTCap_t *)tlv; + if (GET_VHTCAP_MAXMPDULEN( + pvhtcap->vht_cap.vht_cap_info) == 2) + sta_ptr->max_amsdu = MLAN_TX_DATA_BUF_SIZE_12K; + else if (GET_VHTCAP_MAXMPDULEN( + pvhtcap->vht_cap.vht_cap_info) == 1) + sta_ptr->max_amsdu = MLAN_TX_DATA_BUF_SIZE_8K; + else + sta_ptr->max_amsdu = MLAN_TX_DATA_BUF_SIZE_4K; + break; + case OPER_MODE_NTF: + break; + case EXTENSION: + pext_tlv = (MrvlIEtypes_Extension_t *)tlv; + if (pext_tlv->ext_id == HE_CAPABILITY) { + sta_ptr->is_11ax_enabled = MTRUE; + PRINTM(MCMND, "STA supports 11ax\n"); + } + break; + default: + break; + } + tlv_len = tlv->len; + tlv->type = wlan_cpu_to_le16(tlv->type); + tlv->len = wlan_cpu_to_le16(tlv->len); + memcpy_ext(pmadapter, pos, (t_u8 *)tlv, + sizeof(MrvlIEtypesHeader_t) + tlv_len, + sizeof(MrvlIEtypesHeader_t) + tlv_len); + pos += sizeof(MrvlIEtypesHeader_t) + tlv_len; + tlv_buf += sizeof(MrvlIEtypesHeader_t) + tlv_len; + tlv = (MrvlIEtypesHeader_t *)tlv_buf; + travel_len += sizeof(MrvlIEtypesHeader_t) + tlv_len; + tlv_buf_left -= sizeof(MrvlIEtypesHeader_t) + tlv_len; + } + + if (sta_ptr->is_11n_enabled) { + if (pmpriv->uap_channel <= 14) + sta_ptr->bandmode = BAND_GN; + else + sta_ptr->bandmode = BAND_AN; + } else if (!b_only) { + if (pmpriv->uap_channel <= 14) + sta_ptr->bandmode = BAND_G; + else + sta_ptr->bandmode = BAND_A; + } else + sta_ptr->bandmode = BAND_B; + if (sta_ptr->is_11ac_enabled) { + if (pmpriv->uap_channel <= 14) + sta_ptr->bandmode = BAND_GAC; + else + sta_ptr->bandmode = BAND_AAC; + } + if (sta_ptr->is_11ax_enabled) { + if (pmpriv->uap_channel <= 14) + sta_ptr->bandmode = BAND_GAX; + else + sta_ptr->bandmode = BAND_AAX; + } + + 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)); +done: + cmd->size += travel_len; + cmd->size = wlan_cpu_to_le16(cmd->size); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function prepare the command before sending to firmware. + * + * @param priv A pointer to mlan_private structure + * @param cmd_no Command number + * @param cmd_action Command action: GET or SET + * @param cmd_oid Cmd oid: treated as sub command + * @param pioctl_buf A pointer to MLAN IOCTL Request buffer + * @param pdata_buf A pointer to information buffer + * @param pcmd_buf A pointer to cmd buf + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_ops_uap_prepare_cmd(t_void *priv, t_u16 cmd_no, + t_u16 cmd_action, t_u32 cmd_oid, + t_void *pioctl_buf, t_void *pdata_buf, + 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_APCMD_ACS_SCAN: + case HostCmd_CMD_SOFT_RESET: + case HOST_CMD_APCMD_BSS_STOP: + 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_BSS_START: + ret = wlan_uap_cmd_bss_start(pmpriv, cmd_ptr); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IsAuthenticatorEnabled(pmpriv->psapriv)) + AuthenticatorBssConfig(pmpriv->psapriv, MNULL, 1, 0, 0); +#endif + break; + case HOST_CMD_APCMD_SYS_CONFIGURE: + ret = wlan_uap_cmd_sys_configure(pmpriv, cmd_ptr, cmd_action, + (pmlan_ioctl_req)pioctl_buf, + pdata_buf); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = wlan_cmd_enh_power_mode(pmpriv, cmd_ptr, cmd_action, + (t_u16)cmd_oid, pdata_buf); + break; +#if defined(SDIO) + case HostCmd_CMD_SDIO_GPIO_INT_CONFIG: + ret = wlan_cmd_sdio_gpio_int(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HostCmd_CMD_FUNC_INIT: + if (pmpriv->adapter->hw_status == WlanHardwareStatusReset) + pmpriv->adapter->hw_status = + WlanHardwareStatusInitializing; + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_FUNC_SHUTDOWN: + pmpriv->adapter->hw_status = WlanHardwareStatusReset; + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_CFG_DATA: + ret = wlan_cmd_cfg_data(pmpriv, cmd_ptr, cmd_action, cmd_oid, + pdata_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = wlan_cmd_mac_control(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = wlan_uap_cmd_snmp_mib(pmpriv, cmd_ptr, cmd_action, + cmd_oid, + (pmlan_ioctl_req)pioctl_buf, + pdata_buf); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = wlan_uap_cmd_802_11_get_log(pmpriv, cmd_ptr); + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = wlan_cmd_802_11d_domain_info(pmpriv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = wlan_11h_cmd_process(pmpriv, cmd_ptr, pdata_buf); + break; + case HOST_CMD_APCMD_STA_DEAUTH: + ret = wlan_uap_cmd_sta_deauth(pmpriv, cmd_ptr, pdata_buf); + break; + case HOST_CMD_APCMD_REPORT_MIC: + ret = wlan_uap_cmd_report_mic(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; +#ifdef SDIO + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = wlan_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, pdata_buf); + break; +#endif + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = wlan_uap_cmd_802_11_hs_cfg(pmpriv, cmd_ptr, cmd_action, + (hs_config_param *)pdata_buf); + break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = wlan_cmd_hs_wakeup_reason(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_802_11_ROBUSTCOEX: + ret = wlan_cmd_robustcoex(pmpriv, cmd_ptr, cmd_action, + (t_u16 *)pdata_buf); + break; + case HostCmd_CMD_DMCS_CONFIG: + ret = wlan_cmd_dmcs_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + ret = wlan_cmd_recfg_tx_buf(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + ret = wlan_cmd_amsdu_aggr_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_11N_CFG: + ret = wlan_cmd_11n_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = wlan_cmd_11n_addba_req(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_11N_DELBA: + ret = wlan_cmd_11n_delba(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = wlan_cmd_11n_addba_rspgen(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_REJECT_ADDBA_REQ: + ret = wlan_cmd_reject_addba_req(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TX_BF_CFG: + ret = wlan_cmd_tx_bf_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#if defined(WIFI_DIRECT_SUPPORT) + case HostCmd_CMD_SET_BSS_MODE: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + if (pdata_buf) + cmd_ptr->params.bss_mode.con_type = *(t_u8 *)pdata_buf; + else + cmd_ptr->params.bss_mode.con_type = + BSS_MODE_WIFIDIRECT_GO; + cmd_ptr->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_SET_BSS_MODE) + S_DS_GEN); + ret = MLAN_STATUS_SUCCESS; + break; +#endif + case HostCmd_CMD_VERSION_EXT: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->params.verext.version_str_sel = + (t_u8)(*((t_u32 *)pdata_buf)); + cmd_ptr->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_VERSION_EXT) + S_DS_GEN); + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_RX_MGMT_IND: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->params.rx_mgmt_ind.action = + wlan_cpu_to_le16(cmd_action); + cmd_ptr->params.rx_mgmt_ind.mgmt_subtype_mask = + (t_u32)(*((t_u32 *)pdata_buf)); + cmd_ptr->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_RX_MGMT_IND) + S_DS_GEN); + break; + case HostCmd_CMD_CFG_TX_DATA_PAUSE: + ret = wlan_uap_cmd_txdatapause(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_RADIO_CONTROL: + ret = wlan_cmd_802_11_radio_control(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = wlan_cmd_tx_rate_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf, pioctl_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + cmd_ptr->command = + wlan_cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); + cmd_ptr->size = wlan_cpu_to_le16(sizeof(HostCmd_TX_RATE_QUERY) + + S_DS_GEN); + pmpriv->tx_rate = 0; + ret = MLAN_STATUS_SUCCESS; + break; + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_cmd_remain_on_channel(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#ifdef WIFI_DIRECT_SUPPORT + case HOST_CMD_WIFI_DIRECT_MODE_CONFIG: + ret = wlan_cmd_wifi_direct_mode(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HOST_CMD_P2P_PARAMS_CONFIG: + ret = wlan_cmd_p2p_params_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HostCmd_CMD_802_11_RF_ANTENNA: + ret = wlan_cmd_802_11_rf_antenna(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_MIMO_SWITCH: + ret = wlan_cmd_802_11_mimo_switch(pmpriv, cmd_ptr, pdata_buf); + break; + case HostCmd_CMD_11AC_CFG: + ret = wlan_cmd_11ac_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_DYN_BW: + ret = wlan_cmd_config_dyn_bw(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_TARGET_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + case HostCmd_CMD_BCA_REG_ACCESS: + ret = wlan_cmd_reg_access(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = wlan_cmd_mem_access(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_WMM_QUEUE_CONFIG: + ret = wlan_cmd_wmm_queue_config(pmpriv, cmd_ptr, pdata_buf); + break; +#ifdef RX_PACKET_COALESCE + case HostCmd_CMD_RX_PKT_COALESCE_CFG: + ret = wlan_cmd_rx_pkt_coalesce_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif + case HOST_CMD_APCMD_OPER_CTRL: + ret = wlan_uap_cmd_oper_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + + case HostCmd_CMD_INDEPENDENT_RESET_CFG: + ret = wlan_cmd_ind_rst_cfg(cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_GET_TSF: + ret = wlan_cmd_get_tsf(pmpriv, cmd_ptr, cmd_action); + break; + + case HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT: + ret = wlan_cmd_ps_inactivity_timeout(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + + case HostCmd_CMD_CHAN_REGION_CFG: + cmd_ptr->command = wlan_cpu_to_le16(cmd_no); + cmd_ptr->size = wlan_cpu_to_le16( + sizeof(HostCmd_DS_CHAN_REGION_CFG) + S_DS_GEN); + cmd_ptr->params.reg_cfg.action = wlan_cpu_to_le16(cmd_action); + break; + case HostCmd_CMD_PACKET_AGGR_CTRL: + ret = wlan_cmd_packet_aggr_ctrl(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#if defined(PCIE) +#if defined(PCIE8997) || defined(PCIE8897) + case HostCmd_CMD_PCIE_HOST_BUF_DETAILS: + ret = wlan_cmd_pcie_host_buf_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#endif +#endif + case HOST_CMD_TX_RX_PKT_STATS: + ret = wlan_cmd_tx_rx_pkt_stats(pmpriv, cmd_ptr, + (pmlan_ioctl_req)pioctl_buf, + pdata_buf); + break; + case HostCmd_CMD_FW_DUMP_EVENT: + ret = wlan_cmd_fw_dump_event(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_802_11_LINK_STATS: + ret = wlan_cmd_802_11_link_statistic(pmpriv, cmd_ptr, + cmd_action, pioctl_buf); + break; + case HostCmd_CMD_ADD_NEW_STATION: + ret = wlan_uap_cmd_add_station(pmpriv, cmd_ptr, cmd_action, + (pmlan_ioctl_req)pioctl_buf); + break; + case HostCmd_CMD_BOOT_SLEEP: + ret = wlan_cmd_boot_sleep(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) + case HostCmd_CMD_CRYPTO: + ret = wlan_cmd_crypto(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; +#endif + case HostCmd_CMD_11AX_CFG: + ret = wlan_cmd_11ax_cfg(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_11AX_CMD: + ret = wlan_cmd_11ax_cmd(pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_RANGE_EXT: + ret = wlan_cmd_range_ext(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_RX_ABORT_CFG: + ret = wlan_cmd_rxabortcfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_RX_ABORT_CFG_EXT: + ret = wlan_cmd_rxabortcfg_ext(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_TX_AMPDU_PROT_MODE: + ret = wlan_cmd_tx_ampdu_prot_mode(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_DOT11MC_UNASSOC_FTM_CFG: + ret = wlan_cmd_dot11mc_unassoc_ftm_cfg(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_RATE_ADAPT_CFG: + ret = wlan_cmd_rate_adapt_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_CCK_DESENSE_CFG: + ret = wlan_cmd_cck_desense_cfg(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CHANNEL_TRPC_CONFIG: + ret = wlan_cmd_get_chan_trpc_config(pmpriv, cmd_ptr, cmd_action, + pdata_buf); + break; + case HostCmd_CMD_LOW_POWER_MODE_CFG: + ret = wlan_cmd_set_get_low_power_mode_cfg( + pmpriv, cmd_ptr, cmd_action, pdata_buf); + break; + case HostCmd_CMD_802_11_BAND_STEERING: + ret = wlan_cmd_set_get_band_steering_cfg(pmpriv, cmd_ptr, + cmd_action, pdata_buf); + break; + case HostCmd_CMD_UAP_BEACON_STUCK_CFG: + ret = wlan_cmd_set_get_beacon_stuck_cfg(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(t_void *priv, t_u16 cmdresp_no, + t_void *pcmd_buf, 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; +#ifdef SDIO + int ctr; +#endif + wlan_dfs_device_state_t *pstate_dfs = + (wlan_dfs_device_state_t *)&pmpriv->adapter->state_dfs; + t_u32 sec, usec; + ENTER(); + + /* If the command is not successful, cleanup and return failure */ + if (resp->result != HostCmd_RESULT_OK) { + ret = uap_process_cmdresp_error(pmpriv, resp, pioctl_buf); + LEAVE(); + return ret; + } + + /* Command successful, handle response */ + switch (cmdresp_no) { + case HOST_CMD_APCMD_BSS_STOP: + pmpriv->uap_bss_started = MFALSE; + /* Timestamp update is required because bss_start after skip_cac + * enabled should not select non-current channel just because + * timestamp got expired + */ + if (!pmpriv->intf_state_11h.is_11h_host && + !pstate_dfs->dfs_check_pending && + pstate_dfs->dfs_check_channel) { + pmpriv->adapter->callbacks.moal_get_system_time( + pmpriv->adapter->pmoal_handle, &sec, &usec); + pstate_dfs->dfs_report_time_sec = sec; + } + if (pmpriv->intf_state_11h.is_11h_host) + pmpriv->intf_state_11h.tx_disabled = MFALSE; + else { + if (pmpriv->adapter->ecsa_enable) + wlan_11h_remove_custom_ie(pmpriv->adapter, + pmpriv); + wlan_11h_check_update_radar_det_state(pmpriv); + } + + if (pmpriv->adapter->state_rdh.stage == RDH_STOP_INTFS) + wlan_11h_radar_detected_callback((t_void *)pmpriv); + wlan_coex_ampdu_rxwinsize(pmadapter); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IsAuthenticatorEnabled(pmpriv->psapriv)) { + AuthenticatorBssConfig(pmpriv->psapriv, MNULL, 0, 1, 0); + AuthenticatorkeyClear(pmpriv->psapriv); + } +#endif + pmpriv->uap_host_based = 0; + break; + case HOST_CMD_APCMD_BSS_START: + if (!pmpriv->intf_state_11h.is_11h_host && + pmpriv->adapter->state_rdh.stage == RDH_RESTART_INTFS) + wlan_11h_radar_detected_callback((t_void *)pmpriv); + /* Stop pps_uapsd_mode once bss_start */ + pmpriv->adapter->tx_lock_flag = MFALSE; + pmpriv->adapter->pps_uapsd_mode = MFALSE; + pmpriv->adapter->delay_null_pkt = MFALSE; + /* Clear AMSDU statistics*/ + pmpriv->amsdu_rx_cnt = 0; + pmpriv->amsdu_tx_cnt = 0; + pmpriv->msdu_in_rx_amsdu_cnt = 0; + pmpriv->msdu_in_tx_amsdu_cnt = 0; + break; + case HOST_CMD_APCMD_SYS_RESET: + pmpriv->uap_bss_started = MFALSE; + pmpriv->uap_host_based = 0; +#ifdef DRV_EMBEDDED_AUTHENTICATOR + AuthenitcatorInitBssConfig(pmpriv->psapriv); +#endif + ret = wlan_uap_ret_sys_reset(pmpriv, resp, pioctl_buf); + wlan_11h_check_update_radar_det_state(pmpriv); + wlan_coex_ampdu_rxwinsize(pmadapter); + break; + case HOST_CMD_APCMD_SYS_INFO: + break; + case HOST_CMD_APCMD_SYS_CONFIGURE: + ret = wlan_uap_ret_sys_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = wlan_ret_enh_power_mode(pmpriv, resp, pioctl_buf); + break; +#if defined(SDIO) + case HostCmd_CMD_SDIO_GPIO_INT_CONFIG: + break; +#endif + case HostCmd_CMD_FUNC_INIT: + case HostCmd_CMD_FUNC_SHUTDOWN: + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = wlan_uap_ret_snmp_mib(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = wlan_uap_ret_get_log(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = wlan_ret_802_11d_domain_info(pmpriv, resp); + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = wlan_11h_cmdresp_process(pmpriv, resp); + break; + case HOST_CMD_APCMD_STA_DEAUTH: + break; + case HOST_CMD_APCMD_REPORT_MIC: + 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; +#ifdef SDIO + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = wlan_ret_sdio_rx_aggr_cfg(pmpriv, resp); + break; +#endif + case HostCmd_CMD_CFG_DATA: + ret = wlan_ret_cfg_data(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = wlan_ret_mac_control(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = wlan_ret_802_11_hs_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_HS_WAKEUP_REASON: + ret = wlan_ret_hs_wakeup_reason(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_ROBUSTCOEX: + break; + case HostCmd_CMD_DMCS_CONFIG: + ret = wlan_ret_dmcs_config(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: + wlan_set_tx_pause_flag(pmpriv, MFALSE); + + pmadapter->tx_buf_size = + (t_u16)wlan_le16_to_cpu(resp->params.tx_buf.buff_size); +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + pmadapter->tx_buf_size = (pmadapter->tx_buf_size / + MLAN_SDIO_BLOCK_SIZE) * + MLAN_SDIO_BLOCK_SIZE; + pmadapter->pcard_sd->mp_end_port = wlan_le16_to_cpu( + resp->params.tx_buf.mp_end_port); + pmadapter->pcard_sd->mp_data_port_mask = + pmadapter->pcard_sd->reg->data_port_mask; + + for (ctr = 1; + ctr <= MAX_PORT - pmadapter->pcard_sd->mp_end_port; + ctr++) { + pmadapter->pcard_sd->mp_data_port_mask &= + ~(1 << (MAX_PORT - ctr)); + } + + pmadapter->pcard_sd->curr_wr_port = 0; + pmadapter->pcard_sd->mpa_tx.pkt_aggr_limit = + MIN(SDIO_MP_AGGR_DEF_PKT_LIMIT, + (pmadapter->pcard_sd->mp_end_port >> 1)); + PRINTM(MCMND, "end port %d, data port mask %x\n", + wlan_le16_to_cpu( + resp->params.tx_buf.mp_end_port), + pmadapter->pcard_sd->mp_data_port_mask); + } +#endif + pmadapter->curr_tx_buf_size = pmadapter->tx_buf_size; + PRINTM(MCMND, "max_tx_buf_size=%d, tx_buf_size=%d\n", + pmadapter->max_tx_buf_size, pmadapter->tx_buf_size); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + ret = wlan_ret_amsdu_aggr_ctrl(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_11N_CFG: + ret = wlan_ret_11n_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_REJECT_ADDBA_REQ: + ret = wlan_ret_reject_addba_req(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TX_BF_CFG: + ret = wlan_ret_tx_bf_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_VERSION_EXT: + ret = wlan_ret_ver_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RX_MGMT_IND: + ret = wlan_ret_rx_mgmt_ind(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_CFG_TX_DATA_PAUSE: + ret = wlan_uap_ret_txdatapause(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_RADIO_CONTROL: + ret = wlan_ret_802_11_radio_control(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = wlan_ret_tx_rate_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + ret = wlan_ret_802_11_tx_rate_query(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_REMAIN_ON_CHANNEL: + ret = wlan_ret_remain_on_channel(pmpriv, resp, pioctl_buf); + break; +#ifdef WIFI_DIRECT_SUPPORT + case HOST_CMD_WIFI_DIRECT_MODE_CONFIG: + ret = wlan_ret_wifi_direct_mode(pmpriv, resp, pioctl_buf); + break; + case HOST_CMD_P2P_PARAMS_CONFIG: + ret = wlan_ret_p2p_params_config(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCmd_CMD_802_11_RF_ANTENNA: + ret = wlan_ret_802_11_rf_antenna(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_MIMO_SWITCH: + break; + case HostCmd_CMD_11AC_CFG: + ret = wlan_ret_11ac_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_DYN_BW: + ret = wlan_ret_dyn_bw(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_TARGET_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + case HostCmd_CMD_BCA_REG_ACCESS: + ret = wlan_ret_reg_access(pmpriv->adapter, cmdresp_no, resp, + pioctl_buf); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = wlan_ret_mem_access(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_WMM_QUEUE_CONFIG: + ret = wlan_ret_wmm_queue_config(pmpriv, resp, pioctl_buf); + break; +#ifdef RX_PACKET_COALESCE + case HostCmd_CMD_RX_PKT_COALESCE_CFG: + ret = wlan_ret_rx_pkt_coalesce_cfg(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCMD_APCMD_ACS_SCAN: + ret = wlan_ret_cmd_uap_acs_scan(pmpriv, resp, pioctl_buf); + break; + case HOST_CMD_APCMD_OPER_CTRL: + ret = wlan_uap_ret_oper_ctrl(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_INDEPENDENT_RESET_CFG: + ret = wlan_ret_ind_rst_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_PS_INACTIVITY_TIMEOUT: + break; + case HostCmd_CMD_GET_TSF: + ret = wlan_ret_get_tsf(pmpriv, resp, pioctl_buf); + break; + + case HostCmd_CMD_CHAN_REGION_CFG: + ret = wlan_ret_chan_region_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_PACKET_AGGR_CTRL: + ret = wlan_ret_packet_aggr_ctrl(pmpriv, resp, pioctl_buf); + break; +#if defined(PCIE) +#if defined(PCIE8997) || defined(PCIE8897) + case HostCmd_CMD_PCIE_HOST_BUF_DETAILS: + PRINTM(MINFO, "PCIE host buffer configuration successful.\n"); + break; +#endif +#endif + case HOST_CMD_TX_RX_PKT_STATS: + ret = wlan_ret_tx_rx_pkt_stats(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_802_11_LINK_STATS: + ret = wlan_ret_get_link_statistic(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_BOOT_SLEEP: + ret = wlan_ret_boot_sleep(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_ADD_NEW_STATION: + break; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) + case HostCmd_CMD_CRYPTO: + ret = wlan_ret_crypto(pmpriv, resp, pioctl_buf); + break; +#endif + case HostCmd_CMD_11AX_CFG: + ret = wlan_ret_11ax_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_11AX_CMD: + ret = wlan_ret_11ax_cmd(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RANGE_EXT: + ret = wlan_ret_range_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RX_ABORT_CFG: + ret = wlan_ret_rxabortcfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_RX_ABORT_CFG_EXT: + ret = wlan_ret_rxabortcfg_ext(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_TX_AMPDU_PROT_MODE: + ret = wlan_ret_tx_ampdu_prot_mode(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_DOT11MC_UNASSOC_FTM_CFG: + ret = wlan_ret_dot11mc_unassoc_ftm_cfg(pmpriv, resp, + pioctl_buf); + break; + case HostCmd_CMD_RATE_ADAPT_CFG: + ret = wlan_ret_rate_adapt_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_CCK_DESENSE_CFG: + ret = wlan_ret_cck_desense_cfg(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CHANNEL_TRPC_CONFIG: + ret = wlan_ret_get_chan_trpc_config(pmpriv, resp, pioctl_buf); + break; + case HostCmd_CMD_LOW_POWER_MODE_CFG: + ret = wlan_ret_set_get_low_power_mode_cfg(pmpriv, resp, + pioctl_buf); + break; + case HostCmd_CMD_802_11_BAND_STEERING: + ret = wlan_ret_set_get_band_steering_cfg(pmpriv, resp, + pioctl_buf); + break; + case HostCmd_CMD_UAP_BEACON_STUCK_CFG: + ret = wlan_ret_set_get_beacon_stuck_cfg(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(t_void *priv) +{ + pmlan_private pmpriv = (pmlan_private)priv; + pmlan_adapter pmadapter = pmpriv->adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 eventcause = pmadapter->event_cause; + pmlan_buffer pmbuf = pmadapter->pmlan_buffer_event; + t_u8 *event_buf = MNULL; + mlan_event *pevent = MNULL; + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + sta_node *sta_ptr = MNULL; + t_u8 i = 0; + t_u8 channel = 0; + MrvlIEtypes_channel_band_t *pchan_info = MNULL; + chan_band_info *pchan_band_info = MNULL; + event_exceed_max_p2p_conn *event_excd_p2p = MNULL; + t_u16 enable; + + ENTER(); + + if (!pmbuf) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* Event length check */ + if (pmbuf && (pmbuf->data_len - sizeof(eventcause)) > MAX_EVENT_SIZE) { + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate memory for event buffer */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, + MAX_EVENT_SIZE + sizeof(mlan_event), + 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; + pmpriv->is_data_rate_auto = MTRUE; + memcpy_ext(pmadapter, pmpriv->curr_addr, + pmadapter->event_body + 2, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + pevent->event_id = MLAN_EVENT_ID_UAP_FW_BSS_START; + wlan_check_uap_capability(pmpriv, pmbuf); + wlan_coex_ampdu_rxwinsize(pmadapter); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + if (IsAuthenticatorEnabled(pmpriv->psapriv)) { + pmadapter->authenticator_priv = pmpriv; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DEFER_RX_WORK, + MNULL); + } +#endif + break; + case EVENT_MICRO_AP_BSS_ACTIVE: + PRINTM(MEVENT, "EVENT: MICRO_AP_BSS_ACTIVE\n"); + pmpriv->media_connected = MTRUE; + pmpriv->port_open = MTRUE; + pevent->event_id = MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE; + break; + case EVENT_MICRO_AP_BSS_IDLE: + PRINTM(MEVENT, "EVENT: MICRO_AP_BSS_IDLE\n"); + pevent->event_id = MLAN_EVENT_ID_UAP_FW_BSS_IDLE; + pmpriv->media_connected = MFALSE; + wlan_clean_txrx(pmpriv); + wlan_notify_station_deauth(pmpriv); + wlan_delete_station_list(pmpriv); + pmpriv->port_open = MFALSE; + pmpriv->amsdu_disable = MFALSE; + pmpriv->tx_pause = MFALSE; + break; + case EVENT_MICRO_AP_MIC_COUNTERMEASURES: + PRINTM(MEVENT, "EVENT: MICRO_AP_MIC_COUNTERMEASURES\n"); + pevent->event_id = MLAN_EVENT_ID_UAP_FW_MIC_COUNTERMEASURES; + break; + case EVENT_PS_AWAKE: + PRINTM(MINFO, "EVENT: AWAKE\n"); + PRINTM_NETINTF(MEVENT, pmpriv); + 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_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "__"); + /* Handle unexpected PS SLEEP event */ + if (pmadapter->ps_state == PS_STATE_SLEEP_CFM) + break; + pmadapter->ps_state = PS_STATE_PRE_SLEEP; + wlan_check_ps_cond(pmadapter); + break; + case EVENT_MICRO_AP_STA_ASSOC: + wlan_process_sta_assoc_event(pmpriv, pevent, pmbuf); + memcpy_ext(pmadapter, sta_addr, pmadapter->event_body + 2, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + sta_ptr = wlan_add_station_entry(pmpriv, sta_addr); + PRINTM_NETINTF(MMSG, pmpriv); + PRINTM(MMSG, "wlan: EVENT: MICRO_AP_STA_ASSOC " MACSTR "\n", + MAC2STR(sta_addr)); + if (!sta_ptr) + break; + if (pmpriv->is_11n_enabled +#ifdef DRV_EMBEDDED_AUTHENTICATOR + || IsAuthenticatorEnabled(pmpriv->psapriv) +#endif + ) { + wlan_check_sta_capability(pmpriv, pmbuf, sta_ptr); + for (i = 0; i < MAX_NUM_TID; i++) { + if (sta_ptr->is_11n_enabled) + sta_ptr->ampdu_sta[i] = + pmpriv->aggr_prio_tbl[i] + .ampdu_user; + else + sta_ptr->ampdu_sta[i] = + BA_STREAM_NOT_ALLOWED; + } + memset(pmadapter, sta_ptr->rx_seq, 0xff, + sizeof(sta_ptr->rx_seq)); + } + if (pmpriv->sec_info.wapi_enabled) + wlan_update_wapi_info_tlv(pmpriv, pmbuf); +#ifdef DRV_EMBEDDED_AUTHENTICATOR + /**enter authenticator*/ + if (IsAuthenticatorEnabled(pmpriv->psapriv)) + AuthenticatorSendEapolPacket( + pmpriv->psapriv, sta_ptr->cm_connectioninfo); +#endif + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + case EVENT_MICRO_AP_STA_DEAUTH: + pevent->event_id = MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT; + pevent->bss_index = pmpriv->bss_index; + pevent->event_len = pmbuf->data_len - 4; + /* skip event length field */ + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + 4, + pevent->event_len, pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + memcpy_ext(pmadapter, sta_addr, pmadapter->event_body + 2, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + PRINTM_NETINTF(MMSG, pmpriv); + PRINTM(MMSG, "wlan: EVENT: MICRO_AP_STA_DEAUTH " MACSTR "\n", + MAC2STR(sta_addr)); + if (pmpriv->is_11n_enabled) { + wlan_cleanup_reorder_tbl(pmpriv, sta_addr); + wlan_11n_cleanup_txbastream_tbl(pmpriv, sta_addr); + } + wlan_wmm_delete_peer_ralist(pmpriv, sta_addr); + wlan_delete_station_entry(pmpriv, sta_addr); + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + case EVENT_HS_ACT_REQ: + PRINTM(MEVENT, "EVENT: HS_ACT_REQ\n"); + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_HS_CFG_ENH, 0, + 0, MNULL, MNULL); + break; + case EVENT_ADDBA: + PRINTM(MEVENT, "EVENT: ADDBA Request\n"); + if (pmpriv->media_connected == MTRUE) + ret = wlan_prepare_cmd(pmpriv, + HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, MNULL, + pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore ADDBA Request event in BSS idle state\n"); + break; + case EVENT_DELBA: + PRINTM(MEVENT, "EVENT: DELBA Request\n"); + if (pmpriv->media_connected == MTRUE) + wlan_11n_delete_bastream(pmpriv, pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore DELBA Request event in BSS idle state\n"); + break; + case EVENT_BA_STREAM_TIMEOUT: + PRINTM(MEVENT, "EVENT: BA Stream timeout\n"); + if (pmpriv->media_connected == MTRUE) + wlan_11n_ba_stream_timeout( + pmpriv, (HostCmd_DS_11N_BATIMEOUT *) + pmadapter->event_body); + else + PRINTM(MERROR, + "Ignore BA Stream timeout event in BSS idle state\n"); + break; + case EVENT_RXBA_SYNC: + PRINTM(MEVENT, "EVENT: RXBA_SYNC\n"); + wlan_11n_rxba_sync_event(pmpriv, pmadapter->event_body, + pmbuf->data_len - sizeof(eventcause)); + break; + case EVENT_AMSDU_AGGR_CTRL: + PRINTM(MEVENT, "EVENT: AMSDU_AGGR_CTRL %d\n", + *(t_u16 *)pmadapter->event_body); + pmadapter->tx_buf_size = + MIN(pmadapter->curr_tx_buf_size, + wlan_le16_to_cpu(*(t_u16 *)pmadapter->event_body)); + if (pmbuf->data_len == sizeof(eventcause) + sizeof(t_u32)) { + enable = wlan_le16_to_cpu( + *(t_u16 *)(pmadapter->event_body + + sizeof(t_u16))); + if (enable) + pmpriv->amsdu_disable = MFALSE; + else + pmpriv->amsdu_disable = MTRUE; + PRINTM(MEVENT, "amsdu_disable=%d\n", + pmpriv->amsdu_disable); + } + 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_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "EVENT: Radar Detected\n"); + if (pmpriv->adapter->dfs_test_params.cac_restart && + pmpriv->adapter->state_dfs.dfs_check_pending) { + wlan_11h_cancel_radar_detect(pmpriv); + wlan_11h_issue_radar_detect( + pmpriv, MNULL, + pmpriv->adapter->dfs_test_params.chan, + pmpriv->adapter->dfs_test_params.bandcfg); + pevent->event_id = 0; + break; + } + /* 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_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len, + pevent->event_len); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pevent->event_id = 0; /* clear to avoid resending at end of fcn + */ + + /* Print event data */ + pevent->event_id = MLAN_EVENT_ID_FW_RADAR_DETECTED; + pevent->event_len = pmbuf->data_len - sizeof(eventcause); + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause), + pevent->event_len, pevent->event_len); + wlan_11h_print_event_radar_detected(pmpriv, pevent, &channel); + *((t_u8 *)pevent->event_buf) = channel; + if (!pmpriv->intf_state_11h.is_11h_host) { + if (pmadapter->state_rdh.stage == RDH_OFF) { + pmadapter->state_rdh.stage = RDH_CHK_INTFS; + wlan_11h_radar_detected_handling(pmadapter, + pmpriv); + if (pmpriv->uap_host_based) + wlan_recv_event( + priv, + MLAN_EVENT_ID_FW_RADAR_DETECTED, + pevent); + } else { + PRINTM(MEVENT, + "Ignore Event Radar Detected - handling" + " already in progress.\n"); + } + } else { + if (pmpriv->adapter->dfs_test_params + .no_channel_change_on_radar || + pmpriv->adapter->dfs_test_params + .fixed_new_channel_on_radar) { + if (pmadapter->state_rdh.stage == RDH_OFF || + pmadapter->state_rdh.stage == + RDH_SET_CUSTOM_IE) { + pmadapter->state_rdh.stage = + RDH_CHK_INTFS; + wlan_11h_radar_detected_handling( + pmadapter, pmpriv); + } else + PRINTM(MEVENT, + "Ignore Event Radar Detected - handling already in progress.\n"); + } else { + pmpriv->intf_state_11h.tx_disabled = MTRUE; + wlan_recv_event(priv, + MLAN_EVENT_ID_FW_RADAR_DETECTED, + pevent); + } + } + + pevent->event_id = 0; /* clear to avoid resending at end of fcn + */ + break; + case EVENT_CHANNEL_REPORT_RDY: + PRINTM_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "EVENT: Channel Report Ready\n"); + pmpriv->adapter->dfs_test_params.cac_restart = MFALSE; + memset(pmadapter, event_buf, 0x00, MAX_EVENT_SIZE); + /* Setup event buffer */ + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY; + pevent->event_len = pmbuf->data_len - sizeof(eventcause); + /* Copy event data */ + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause), + pevent->event_len, pevent->event_len); + /* Handle / pass event data, and free buffer */ + ret = wlan_11h_handle_event_chanrpt_ready(pmpriv, pevent, + &channel); + if (pmpriv->intf_state_11h.is_11h_host) { + *((t_u8 *)pevent->event_buf) = + pmpriv->adapter->state_dfs.dfs_radar_found; + *((t_u8 *)pevent->event_buf + 1) = channel; + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY, + pevent); + } else { + /* Send up this Event to unblock MOAL waitqueue */ + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_MEAS_REPORT, + MNULL); + } + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + case EVENT_CHANNEL_SWITCH: + pchan_info = + (MrvlIEtypes_channel_band_t *)(pmadapter->event_body); + channel = pchan_info->channel; + PRINTM_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "EVENT: CHANNEL_SWITCH new channel %d\n", + channel); + pmpriv->uap_channel = channel; + pmpriv->uap_state_chan_cb.channel = pchan_info->channel; + pmpriv->uap_state_chan_cb.bandcfg = pchan_info->bandcfg; + if (wlan_11h_radar_detect_required(pmpriv, + pchan_info->channel)) { + if (!wlan_11h_is_active(pmpriv)) { + /* active 11h extention in Fw */ + ret = wlan_11h_activate(pmpriv, MNULL, MTRUE); + ret = wlan_11h_config_master_radar_det(pmpriv, + MTRUE); + ret = wlan_11h_check_update_radar_det_state( + pmpriv); + } + if (pmpriv->uap_host_based) + pmpriv->intf_state_11h.is_11h_host = MTRUE; + wlan_11h_set_dfs_check_chan(pmpriv, + pchan_info->channel); + } + if ((pmpriv->adapter->state_rdh.stage != RDH_OFF && + !pmpriv->intf_state_11h.is_11h_host) || + pmpriv->adapter->dfs_test_params.no_channel_change_on_radar || + pmpriv->adapter->dfs_test_params.fixed_new_channel_on_radar) { + /* Handle embedded DFS */ + if (pmpriv->adapter->state_rdh.stage == + RDH_SET_CUSTOM_IE) { + pmadapter->state_rdh.stage = + RDH_RESTART_TRAFFIC; + wlan_11h_radar_detected_handling(pmadapter, + pmpriv); + } + + } else { + /* Handle Host-based DFS and non-DFS(normal uap) case */ + pmpriv->intf_state_11h.tx_disabled = MFALSE; + memset(pmadapter, event_buf, 0x00, MAX_EVENT_SIZE); + /* Setup event buffer */ + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE; + pevent->event_len = sizeof(chan_band_info); + pchan_band_info = (chan_band_info *)pevent->event_buf; + /* Copy event data */ + memcpy_ext(pmadapter, (t_u8 *)&pchan_band_info->bandcfg, + (t_u8 *)&pchan_info->bandcfg, + sizeof(pchan_info->bandcfg), + sizeof(pchan_info->bandcfg)); + pchan_band_info->channel = pchan_info->channel; + if (pchan_band_info->bandcfg.chanWidth == CHAN_BW_80MHZ) + pchan_band_info->center_chan = + wlan_get_center_freq_idx( + priv, BAND_AAC, + pchan_info->channel, + CHANNEL_BW_80MHZ); + pchan_band_info->is_11n_enabled = + pmpriv->is_11n_enabled; + wlan_recv_event(pmpriv, + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE, + pevent); + pevent->event_id = 0; + } + break; + case EVENT_REMAIN_ON_CHANNEL_EXPIRED: + PRINTM_NETINTF(MEVENT, pmpriv); + PRINTM(MEVENT, "EVENT: REMAIN_ON_CHANNEL_EXPIRED reason=%d\n", + *(t_u16 *)pmadapter->event_body); + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_FLUSH_RX_WORK, MNULL); + pevent->event_id = MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED; + break; + + case EVENT_FW_DEBUG_INFO: + memset(pmadapter, event_buf, 0x00, MAX_EVENT_SIZE); + pevent->bss_index = pmpriv->bss_index; + pevent->event_id = MLAN_EVENT_ID_FW_DEBUG_INFO; + pevent->event_len = pmbuf->data_len - sizeof(eventcause); + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause), + pevent->event_len, pevent->event_len); + PRINTM(MEVENT, "EVENT: FW Debug Info %s\n", + (t_u8 *)pevent->event_buf); + wlan_recv_event(pmpriv, pevent->event_id, pevent); + pevent->event_id = 0; /* clear to avoid resending at end of fcn + */ + break; + case EVENT_TX_STATUS_REPORT: + PRINTM(MINFO, "EVENT: TX_STATUS\n"); + pevent->event_id = MLAN_EVENT_ID_FW_TX_STATUS; + break; + case EVENT_BT_COEX_WLAN_PARA_CHANGE: + PRINTM(MEVENT, "EVENT: BT coex wlan param update\n"); + wlan_bt_coex_wlan_param_update_event(pmpriv, pmbuf); + break; + case EVENT_EXCEED_MAX_P2P_CONN: + event_excd_p2p = + (event_exceed_max_p2p_conn *)(pmbuf->pbuf + + pmbuf->data_offset); + PRINTM(MEVENT, "EVENT: EXCEED MAX P2P CONNECTION\n"); + PRINTM(MEVENT, "REQUEST P2P MAC: " MACSTR "\n", + MAC2STR(event_excd_p2p->peer_mac_addr)); + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + case EVENT_VDLL_IND: + wlan_process_vdll_event(pmpriv, pmbuf); + break; + + case EVENT_FW_HANG_REPORT: + if (pmbuf->data_len < (sizeof(eventcause) + sizeof(t_u16))) { + PRINTM(MEVENT, + "EVENT: EVENT_FW_HANG_REPORT skip for len too short: %d\n", + pmbuf->data_len); + break; + } + PRINTM(MEVENT, "EVENT: EVENT_FW_HANG_REPORT reasoncode=%d\n", + wlan_le16_to_cpu(*(t_u16 *)(pmbuf->pbuf + + pmbuf->data_offset + + sizeof(eventcause)))); + pmadapter->fw_hang_report = MTRUE; + wlan_recv_event(pmpriv, MLAN_EVENT_ID_DRV_DBG_DUMP, MNULL); + break; + case EVENT_WATCHDOG_TMOUT: + PRINTM(MEVENT, "EVENT: EVENT_WATCHDOG_TMOUT reasoncode=%d\n", + wlan_le16_to_cpu(*(t_u16 *)(pmbuf->pbuf + + pmbuf->data_offset + + sizeof(eventcause)))); + pevent->event_id = MLAN_EVENT_ID_DRV_WIFI_STATUS; + pevent->event_len = sizeof(pevent->event_id) + sizeof(t_u16); + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset + + sizeof(eventcause), + sizeof(t_u16), sizeof(t_u16)); + break; + default: + pevent->event_id = MLAN_EVENT_ID_DRV_PASSTHRU; + break; + } + + if (pevent->event_id) { + pevent->bss_index = pmpriv->bss_index; + pevent->event_len = pmbuf->data_len; + memcpy_ext(pmadapter, (t_u8 *)pevent->event_buf, + pmbuf->pbuf + pmbuf->data_offset, pevent->event_len, + 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 set uap max sta number + * + * @param priv A pointer to mlan_private structure + * @param uap_max_sta Max station number uAP can supported (per chip) + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +mlan_status wlan_uap_set_uap_max_sta(pmlan_private pmpriv, t_u8 uap_max_sta) +{ + MrvlIEtypes_uap_max_sta_cnt_t tlv_uap_max_sta; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(pmpriv->adapter, &tlv_uap_max_sta, 0, sizeof(tlv_uap_max_sta)); + tlv_uap_max_sta.header.type = + wlan_cpu_to_le16(TLV_TYPE_UAP_MAX_STA_CNT_PER_CHIP); + tlv_uap_max_sta.header.len = wlan_cpu_to_le16(sizeof(t_u16)); + tlv_uap_max_sta.uap_max_sta = wlan_cpu_to_le16(uap_max_sta); + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_SET, 0, MNULL, &tlv_uap_max_sta); + 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(t_void *priv, t_u8 first_bss) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = (pmlan_private)priv; + t_u16 last_cmd = 0; + + ENTER(); + if (!pmpriv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (first_bss) { + if (wlan_adapter_init_cmd(pmpriv->adapter) == + MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (pmpriv->adapter->init_para.uap_max_sta && + (pmpriv->adapter->init_para.uap_max_sta <= MAX_STA_COUNT)) + wlan_uap_set_uap_max_sta( + pmpriv, pmpriv->adapter->init_para.uap_max_sta); + + 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/mxm_wifiex/wlan_src/mlan/mlan_uap_ioctl.c b/mxm_wifiex/wlan_src/mlan/mlan_uap_ioctl.c new file mode 100644 index 0000000..2610627 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_uap_ioctl.c @@ -0,0 +1,2240 @@ +/** @file mlan_uap_ioctl.c + * + * @brief This file contains the handling of AP mode ioctls + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_11n.h" +#include "mlan_fw.h" +#include "mlan_11h.h" +#include "mlan_11ac.h" +#include "mlan_11ax.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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +static t_bool wlan_can_radar_det_skip(mlan_private *priv) +{ + mlan_private *priv_list[MLAN_MAX_BSS_NUM]; + mlan_private *pmpriv; + mlan_adapter *pmadapter = priv->adapter; + t_u8 pcount, i; + + /* In MBSS environment, if one of the BSS is already beaconing and DRCS + * is off then 11n_radar detection is not required for subsequent BSSes + * since they will follow the primary bss. + */ + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP)) { + memset(pmadapter, priv_list, 0x00, sizeof(priv_list)); + pcount = wlan_get_privs_by_cond(pmadapter, wlan_is_intf_active, + priv_list); + for (i = 0; i < pcount; i++) { + pmpriv = priv_list[i]; + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) + return MTRUE; + } + } + return MFALSE; +} +/** + * @brief Callback to finish BSS IOCTL START + * Not to be called directly to initiate bss_start + * + * @param priv A pointer to mlan_private structure (cast from t_void*) + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_bss_ioctl_start + */ +static mlan_status wlan_uap_callback_bss_ioctl_start(t_void *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = (mlan_private *)priv; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + wlan_uap_get_info_cb_t *puap_state_chan_cb = &pmpriv->uap_state_chan_cb; + t_u8 old_channel; + t_bool under_nop = MFALSE; + ENTER(); + /* clear callback now that we're here */ + puap_state_chan_cb->get_chan_callback = MNULL; + + /* + * Check if the region and channel requires we check for radar. + */ + if ((puap_state_chan_cb->bandcfg.chanBand == BAND_5GHZ) && + !wlan_can_radar_det_skip(pmpriv) && + wlan_11h_radar_detect_required(pmpriv, + puap_state_chan_cb->channel)) { + /* If DFS repeater mode is on then before starting the uAP + * make sure that mlan0 is connected to some external AP + * for DFS channel operations. + */ + if (pmpriv->adapter->dfs_repeater) { + pmlan_private tmpriv = MNULL; + tmpriv = wlan_get_priv(pmpriv->adapter, + MLAN_BSS_ROLE_STA); + + if (tmpriv && !tmpriv->media_connected) { + PRINTM(MERROR, + "BSS start is blocked when DFS-repeater\n" + "mode is on and STA is not connected\n"); + pcb->moal_ioctl_complete( + pmpriv->adapter->pmoal_handle, + puap_state_chan_cb->pioctl_req_curr, + MLAN_STATUS_FAILURE); + goto done; + } else { + /* STA is connected. + * Skip DFS check for bss_start since its a + * repeater mode + */ + goto prep_bss_start; + } + } + + /* first check if channel is under NOP */ + if (wlan_11h_is_channel_under_nop( + pmpriv->adapter, puap_state_chan_cb->channel)) { + /* recently we've seen radar on this channel */ + ret = MLAN_STATUS_FAILURE; + under_nop = MTRUE; + } + + /* Check cached radar check on the channel */ + if (ret == MLAN_STATUS_SUCCESS) + ret = wlan_11h_check_chan_report( + pmpriv, puap_state_chan_cb->channel); + + /* Found radar: try to switch to a non-dfs channel */ + if (ret != MLAN_STATUS_SUCCESS) { + old_channel = puap_state_chan_cb->channel; + ret = wlan_11h_switch_non_dfs_chan( + pmpriv, &puap_state_chan_cb->channel); + + if (ret == MLAN_STATUS_SUCCESS) { + wlan_11h_update_bandcfg( + &pmpriv->uap_state_chan_cb.bandcfg, + puap_state_chan_cb->channel); + PRINTM(MCMD_D, + "NOP: uap band config:0x%x channel=%d\n", + pmpriv->uap_state_chan_cb.bandcfg, + puap_state_chan_cb->channel); + + ret = wlan_uap_set_channel( + pmpriv, + pmpriv->uap_state_chan_cb.bandcfg, + puap_state_chan_cb->channel); + if (ret == MLAN_STATUS_SUCCESS) { + if (under_nop) { + PRINTM(MMSG, + "Channel %d under NOP," + " switched to new channel %d successfully.\n", + old_channel, + puap_state_chan_cb + ->channel); + } else { + PRINTM(MMSG, + "Radar found on channel %d," + " switched to new channel %d successfully.\n", + old_channel, + puap_state_chan_cb + ->channel); + } + } else { + if (under_nop) { + PRINTM(MMSG, + "Channel %d under NOP," + " switch to new channel %d failed.\n", + old_channel, + puap_state_chan_cb + ->channel); + } else { + PRINTM(MMSG, + "Radar found on channel %d," + " switch to new channel %d failed.\n", + old_channel, + puap_state_chan_cb + ->channel); + } + pcb->moal_ioctl_complete( + pmpriv->adapter->pmoal_handle, + puap_state_chan_cb + ->pioctl_req_curr, + MLAN_STATUS_FAILURE); + goto done; + } + } else { + if (under_nop) { + PRINTM(MMSG, + "Channel %d under NOP, no switch channel available.\n", + old_channel); + } else { + PRINTM(MMSG, + "Radar found on channel %d, no switch channel available.\n", + old_channel); + } + /* No command sent with the ioctl, need manually + * signal completion */ + pcb->moal_ioctl_complete( + pmpriv->adapter->pmoal_handle, + puap_state_chan_cb->pioctl_req_curr, + MLAN_STATUS_FAILURE); + goto done; + } + } else { + PRINTM(MINFO, "No Radar found on channel %d\n", + puap_state_chan_cb->channel); + } + } + +prep_bss_start: + /* else okay to send command: not DFS channel or no radar */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_START, + HostCmd_ACT_GEN_SET, 0, + (t_void *)puap_state_chan_cb->pioctl_req_curr, + MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +done: + puap_state_chan_cb->pioctl_req_curr = MNULL; /* prevent re-use */ + LEAVE(); + return ret; +} + +/** + * @brief Start BSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +/** + * @sa wlan_uap_callback_bss_ioctl_start + */ +static mlan_status wlan_uap_bss_ioctl_start(pmlan_adapter pmadapter, + 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; + pmpriv->uap_host_based = bss->param.host_based; + if (!pmpriv->intf_state_11h.is_11h_host && + pmpriv->intf_state_11h.is_11h_active) { + /* if FW supports ACS+DFS then sequence is different */ + + /* First check channel report, defer BSS_START CMD to callback. + */ + /* store params, issue command to get UAP channel, whose + * CMD_RESP will callback remainder of bss_start handling */ + pmpriv->uap_state_chan_cb.pioctl_req_curr = pioctl_req; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_uap_callback_bss_ioctl_start; + pmpriv->intf_state_11h.is_11h_host = MFALSE; + ret = wlan_uap_get_channel(pmpriv); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_START, + HostCmd_ACT_GEN_SET, 0, + (t_void *)pioctl_req, MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + + LEAVE(); + return ret; +} + +/** + * @brief reset BSS + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_uap_bss_ioctl_reset(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + t_u8 i = 0; + + ENTER(); + + /* + * Reset any uap private parameters here + */ + for (i = 0; i < pmadapter->max_mgmt_ie_index; i++) + memset(pmadapter, &pmpriv->mgmt_ie[i], 0, sizeof(custom_ie)); + pmpriv->add_ba_param.timeout = MLAN_DEFAULT_BLOCK_ACK_TIMEOUT; + pmpriv->add_ba_param.tx_win_size = MLAN_UAP_AMPDU_DEF_TXWINSIZE; + pmpriv->add_ba_param.rx_win_size = MLAN_UAP_AMPDU_DEF_RXWINSIZE; + pmpriv->user_rxwinsize = pmpriv->add_ba_param.rx_win_size; + + for (i = 0; i < MAX_NUM_TID; i++) { + pmpriv->aggr_prio_tbl[i].ampdu_user = tos_to_tid_inv[i]; + pmpriv->aggr_prio_tbl[i].amsdu = tos_to_tid_inv[i]; + pmpriv->addba_reject[i] = ADDBA_RSP_STATUS_ACCEPT; + } + pmpriv->aggr_prio_tbl[6].ampdu_user = + pmpriv->aggr_prio_tbl[7].ampdu_user = BA_STREAM_NOT_ALLOWED; + pmpriv->addba_reject[6] = pmpriv->addba_reject[7] = + ADDBA_RSP_STATUS_REJECT; + + /* hs_configured, hs_activated are reset by main loop */ + pmadapter->hs_cfg.conditions = HOST_SLEEP_DEF_COND; + pmadapter->hs_cfg.gpio = HOST_SLEEP_DEF_GPIO; + pmadapter->hs_cfg.gap = HOST_SLEEP_DEF_GAP; + + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_RESET, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function to process add station. + * + * @param pmadapter A pointer to pmadapter. + * + * @param pioctl_req A pointer to pioctl_req + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE + */ +mlan_status wlan_uap_bss_ioctl_add_station(pmlan_adapter pmadapter, + 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_ADD_NEW_STATION, + HostCmd_ACT_ADD_STA, 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(pmlan_adapter pmadapter, + 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_ext(pmadapter, pmpriv->curr_addr, &bss->param.mac_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + cmd_action = HostCmd_ACT_GEN_SET; + } else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, cmd_action, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get wmm param + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_uap_bss_ioctl_uap_wmm_param(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, cmd_action, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get scan channels + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status +wlan_uap_bss_ioctl_uap_scan_channels(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, cmd_action, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get UAP channel + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_uap_bss_ioctl_uap_channel(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, cmd_action, + 0, (t_void *)pioctl_req, MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get UAP operation control vaule + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_uap_bss_ioctl_uap_oper_ctrl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_bss *bss = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 cmd_action = 0; + + ENTER(); + + if (pmadapter->fw_ver == HOST_API_VERSION_V15 && + pmadapter->fw_min_ver >= FW_MINOR_VERSION_1) { + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) + cmd_action = HostCmd_ACT_GEN_SET; + else + cmd_action = HostCmd_ACT_GEN_GET; + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_OPER_CTRL, + cmd_action, 0, (t_void *)pioctl_req, + (t_void *)pioctl_req->pbuf); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + PRINTM(MMSG, "FW don't support uap oper ctrl\n"); + } + LEAVE(); + return ret; +} + +/** + * @brief Get Uap statistics + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_uap_get_stats(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + + ENTER(); + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + MNULL); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief Get Uap MIB counters + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_uap_get_stats_log(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + pmlan_private pmpriv = MNULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (pioctl_req != MNULL) { + pmpriv = pmadapter->priv[pioctl_req->bss_index]; + } else { + PRINTM(MERROR, "MLAN IOCTL information is not present\n"); + ret = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Check information buffer length of MLAN IOCTL */ + if (pioctl_req->buf_len < sizeof(mlan_ds_get_stats)) { + PRINTM(MWARN, + "MLAN IOCTL information buffer length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_get_stats); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + ret = MLAN_STATUS_RESOURCE; + goto exit; + } + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_GET_LOG, + HostCmd_ACT_GEN_GET, 0, (t_void *)pioctl_req, + MNULL); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + +exit: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get AP config + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_uap_bss_ioctl_config(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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; + const t_u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + sta_node *sta_ptr = MNULL; + + ENTER(); + + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (pmpriv->uap_host_based & UAP_FLAG_HOST_MLME) { + if (memcmp(pmpriv->adapter, bss->param.deauth_param.mac_addr, + bc_mac, MLAN_MAC_ADDR_LENGTH)) { + sta_ptr = wlan_get_station_entry( + pmpriv, bss->param.deauth_param.mac_addr); + if (!sta_ptr) { + PRINTM(MCMND, + "Skip deauth to station " MACSTR "\n", + MAC2STR(bss->param.deauth_param + .mac_addr)); + LEAVE(); + return ret; + } + } + } + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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_ext(priv->adapter, priv->wapi_ie, + misc->param.gen_ie.ie_data, misc->param.gen_ie.len, + sizeof(priv->wapi_ie)); + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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 report mic error + * + * @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_sec_ioctl_report_mic_error(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_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, HOST_CMD_APCMD_REPORT_MIC, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + (t_void *)sec->param.sta_mac); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_sec_cfg *sec = MNULL; + + ENTER(); + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (pioctl_req->action != MLAN_ACT_SET) { + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (!sec->param.encrypt_key.key_remove && + !sec->param.encrypt_key.key_len) { + PRINTM(MCMND, "Skip set key with key_len = 0\n"); + LEAVE(); + return ret; + } + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, + (t_void *)pioctl_req, &sec->param.encrypt_key); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + LEAVE(); + return ret; +} + +/** + * @brief Get BSS information + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_uap_get_bss_info(pmlan_adapter pmadapter, + 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_ext(pmadapter, &info->param.bss_info.bssid, pmpriv->curr_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + info->param.bss_info.scan_block = pmadapter->scan_block; + + info->param.bss_info.is_hs_configured = pmadapter->is_hs_configured; + info->param.bss_info.is_11h_active = + pmpriv->intf_state_11h.is_11h_active; + info->param.bss_info.dfs_check_channel = + pmpriv->adapter->state_dfs.dfs_check_channel; + 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(pmlan_adapter pmadapter, + 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 Band Steering 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_misc_band_steering_cfg(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *pm = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_band_steer_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_band_steer_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + pm = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_BAND_STEERING, + (t_u16)pm->param.band_steer_cfg.action, 0, + (t_void *)pioctl_req, + (t_void *)&pm->param.band_steer_cfg); + LEAVE(); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + return ret; +} + +/** + * @brief Set Beacon Stuck Detect Mechanism 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_misc_beacon_stuck_cfg(IN pmlan_adapter pmadapter, + IN pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_misc_cfg *pm = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_beacon_stuck_param_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_beacon_stuck_param_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + pm = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_UAP_BEACON_STUCK_CFG, + (t_u16)pm->param.beacon_stuck_cfg.action, 0, + (t_void *)pioctl_req, + (t_void *)&pm->param.beacon_stuck_cfg); + + LEAVE(); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + 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(pmlan_adapter pmadapter, + 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(t_void *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = (mlan_private *)priv; + wlan_uap_get_info_cb_t *puap_state_chan_cb = &pmpriv->uap_state_chan_cb; + mlan_ds_11d_cfg *cfg11d; + t_u8 band; + pmlan_adapter pmadapter = pmpriv->adapter; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + /* clear callback now that we're here */ + puap_state_chan_cb->get_chan_callback = MNULL; + + if (!puap_state_chan_cb->pioctl_req_curr) { + PRINTM(MERROR, "pioctl_req_curr is null\n"); + LEAVE(); + return ret; + } + cfg11d = (mlan_ds_11d_cfg *)puap_state_chan_cb->pioctl_req_curr->pbuf; + band = (puap_state_chan_cb->bandcfg.chanBand == BAND_5GHZ) ? BAND_A : + BAND_B; + + ret = wlan_11d_handle_uap_domain_info( + pmpriv, band, cfg11d->param.domain_tlv, + puap_state_chan_cb->pioctl_req_curr); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + else { + puap_state_chan_cb->pioctl_req_curr->status_code = + MLAN_STATUS_FAILURE; + pcb->moal_ioctl_complete(pmadapter->pmoal_handle, + puap_state_chan_cb->pioctl_req_curr, + MLAN_STATUS_FAILURE); + } + + puap_state_chan_cb->pioctl_req_curr = MNULL; /* prevent re-use */ + LEAVE(); + return ret; +} + +/** + * @brief Set Domain Info for 11D + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_callback_domain_info + */ +static mlan_status wlan_uap_domain_info(pmlan_adapter pmadapter, + 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(t_void *priv) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_private *pmpriv = (mlan_private *)priv; + mlan_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + wlan_uap_get_info_cb_t *puap_state_chan_cb = &pmpriv->uap_state_chan_cb; + Band_Config_t *pband_cfg = &puap_state_chan_cb->bandcfg; + /* keep copy as local variable */ + pmlan_ioctl_req pioctl = puap_state_chan_cb->pioctl_req_curr; + ENTER(); + /* clear callback now that we're here */ + puap_state_chan_cb->get_chan_callback = MNULL; + /* clear early to avoid race condition */ + puap_state_chan_cb->pioctl_req_curr = MNULL; + + /* + * Check if the region and channel requires a channel availability + * check. + */ + if ((puap_state_chan_cb->bandcfg.chanBand == BAND_5GHZ) && + !wlan_can_radar_det_skip(pmpriv) && + wlan_11h_radar_detect_required(pmpriv, + puap_state_chan_cb->channel) && + !wlan_11h_is_channel_under_nop(pmpriv->adapter, + puap_state_chan_cb->channel)) { + /* + * Radar detection is required for this channel, make sure + * 11h is activated in the firmware + */ + ret = wlan_11h_activate(pmpriv, MNULL, MTRUE); + ret = wlan_11h_config_master_radar_det(pmpriv, MTRUE); + ret = wlan_11h_check_update_radar_det_state(pmpriv); + + /* Check for radar on the channel */ + ret = wlan_11h_issue_radar_detect(pmpriv, pioctl, + puap_state_chan_cb->channel, + *pband_cfg); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + /* No command sent with the ioctl, need manually signal + * completion */ + pcb->moal_ioctl_complete(pmpriv->adapter->pmoal_handle, pioctl, + MLAN_STATUS_COMPLETE); + } + + LEAVE(); + return ret; +} + +/** + * @brief 802.11h uap start channel check + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_callback_11h_channel_check_req + */ +static mlan_status wlan_uap_11h_channel_check_req(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_11h_cfg *p11h_cfg; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_11h_cfg)) { + PRINTM(MWARN, "MLAN 11h_cfg IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_11h_cfg); + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + p11h_cfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf; + pmpriv->intf_state_11h.is_11h_host = + p11h_cfg->param.chan_rpt_req.host_based; + + if (!pmpriv->intf_state_11h.is_11h_host) { + /* store params, issue command to get UAP channel, whose + * CMD_RESP will callback remainder of 11H channel check + * handling */ + pmpriv->uap_state_chan_cb.pioctl_req_curr = pioctl_req; + pmpriv->uap_state_chan_cb.get_chan_callback = + wlan_uap_callback_11h_channel_check_req; + + ret = wlan_uap_get_channel(pmpriv); + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } else { + if (!wlan_11h_is_active(pmpriv)) { + /* active 11h extention in Fw */ + ret = wlan_11h_activate(pmpriv, MNULL, MTRUE); + ret = wlan_11h_config_master_radar_det(pmpriv, MTRUE); + ret = wlan_11h_check_update_radar_det_state(pmpriv); + } + if (p11h_cfg->param.chan_rpt_req.millisec_dwell_time) { + if (pmpriv->adapter->dfs_test_params + .user_cac_period_msec) { + PRINTM(MCMD_D, + "cfg80211 dfs_testing - user CAC period=%d (msec)\n", + pmpriv->adapter->dfs_test_params + .user_cac_period_msec); + p11h_cfg->param.chan_rpt_req + .millisec_dwell_time = + pmpriv->adapter->dfs_test_params + .user_cac_period_msec; + } + ret = wlan_prepare_cmd( + pmpriv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + (t_void *)&p11h_cfg->param.chan_rpt_req); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Callback to finish 11H handling + * Not to be called directly to initiate 11H setting. + * + * @param pmpriv A pointer to mlan_private structure (cast from t_void*) + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + * @sa wlan_uap_snmp_mib_11h + */ +static mlan_status wlan_uap_callback_snmp_mib_11h(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_callbacks *pcb = (mlan_callbacks *)&pmpriv->adapter->callbacks; + mlan_ds_snmp_mib *snmp; + t_bool enable_11h; + + ENTER(); + /* clear callback now that we're here */ + puap_state_chan_cb->get_chan_callback = MNULL; + + snmp = (mlan_ds_snmp_mib *)puap_state_chan_cb->pioctl_req_curr->pbuf; + enable_11h = (snmp->param.oid_value) ? MTRUE : MFALSE; + + if (enable_11h) { + if ((puap_state_chan_cb->bandcfg.chanBand == BAND_5GHZ) && + !wlan_can_radar_det_skip(pmpriv) && + wlan_11h_radar_detect_required( + pmpriv, puap_state_chan_cb->channel)) { + if (!wlan_11h_is_master_radar_det_active(pmpriv)) + wlan_11h_config_master_radar_det(pmpriv, MTRUE); + } else { + puap_state_chan_cb->pioctl_req_curr->status_code = + MLAN_STATUS_SUCCESS; + pcb->moal_ioctl_complete( + pmpriv->adapter->pmoal_handle, + puap_state_chan_cb->pioctl_req_curr, + MLAN_STATUS_SUCCESS); + goto done; + } + } + + 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; +done: + 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(pmlan_adapter pmadapter, + 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_fw_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; +} + +/** + * @brief ACS scan + * + * @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_acs_scan(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + if (pmadapter->uap_fw_ver < UAP_FW_VERSION_2) { + PRINTM(MIOCTL, "FW don't support ACS SCAN API\n"); + return MLAN_STATUS_FAILURE; + } + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCMD_APCMD_ACS_SCAN, + HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req, + MNULL); + + 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(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(pmlan_private pmpriv, + Band_Config_t uap_band_cfg, t_u8 channel) +{ + MrvlIEtypes_channel_band_t tlv_chan_band; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(pmpriv->adapter, &tlv_chan_band, 0, sizeof(tlv_chan_band)); + tlv_chan_band.header.type = TLV_TYPE_UAP_CHAN_BAND_CONFIG; + tlv_chan_band.header.len = sizeof(MrvlIEtypes_channel_band_t) - + sizeof(MrvlIEtypesHeader_t); + tlv_chan_band.bandcfg = uap_band_cfg; + tlv_chan_band.channel = channel; + + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_SET, 0, MNULL, &tlv_chan_band); + LEAVE(); + return ret; +} + +/** + * @brief Issue CMD to UAP firmware to get current beacon and dtim periods + * + * @param pmpriv A pointer to mlan_private structure + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_uap_get_beacon_dtim(pmlan_private pmpriv) +{ + t_u8 tlv_buffer[sizeof(MrvlIEtypes_beacon_period_t) + + sizeof(MrvlIEtypes_dtim_period_t)]; + MrvlIEtypes_beacon_period_t *ptlv_beacon_pd; + MrvlIEtypes_dtim_period_t *ptlv_dtim_pd; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(pmpriv->adapter, &tlv_buffer, 0, sizeof(tlv_buffer)); + ptlv_beacon_pd = (MrvlIEtypes_beacon_period_t *)tlv_buffer; + ptlv_beacon_pd->header.type = TLV_TYPE_UAP_BEACON_PERIOD; + ptlv_beacon_pd->header.len = sizeof(MrvlIEtypes_beacon_period_t) - + sizeof(MrvlIEtypesHeader_t); + + ptlv_dtim_pd = + (MrvlIEtypes_dtim_period_t + *)(tlv_buffer + sizeof(MrvlIEtypes_beacon_period_t)); + ptlv_dtim_pd->header.type = TLV_TYPE_UAP_DTIM_PERIOD; + ptlv_dtim_pd->header.len = + sizeof(MrvlIEtypes_dtim_period_t) - sizeof(MrvlIEtypesHeader_t); + + ret = wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_SYS_CONFIGURE, + HostCmd_ACT_GEN_GET, 0, MNULL, tlv_buffer); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get deauth control. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +mlan_status wlan_uap_snmp_mib_ctrl_deauth(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_snmp_mib *mib = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + t_u16 cmd_action = 0; + + ENTER(); + + mib = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + if (pioctl_req->action == MLAN_ACT_SET) { + cmd_action = HostCmd_ACT_GEN_SET; + } else { + cmd_action = HostCmd_ACT_GEN_GET; + } + + /* Send command to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SNMP_MIB, cmd_action, + StopDeauth_i, (t_void *)pioctl_req, + &mib->param.deauthctrl); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief MLAN uap ioctl handler + * + * @param adapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_ops_uap_ioctl(t_void *adapter, pmlan_ioctl_req pioctl_req) +{ + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = MNULL; + mlan_ds_get_info *pget_info = MNULL; + mlan_ds_misc_cfg *misc = MNULL; + mlan_ds_sec_cfg *sec = MNULL; + mlan_ds_power_cfg *power = MNULL; + mlan_ds_pm_cfg *pm = MNULL; + mlan_ds_11d_cfg *cfg11d = MNULL; + mlan_ds_snmp_mib *snmp = MNULL; + mlan_ds_11h_cfg *cfg11h = MNULL; + mlan_ds_radio_cfg *radiocfg = MNULL; + mlan_ds_rate *rate = MNULL; + mlan_ds_reg_mem *reg_mem = MNULL; + mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index]; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + pmlan_ds_scan pscan; +#endif + + ENTER(); + switch (pioctl_req->req_id) { + case MLAN_IOCTL_BSS: + bss = (mlan_ds_bss *)pioctl_req->pbuf; + if (bss->sub_command == MLAN_OID_BSS_MAC_ADDR) + status = wlan_uap_bss_ioctl_mac_address(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_BSS_STOP) + status = wlan_uap_bss_ioctl_stop(pmadapter, pioctl_req); + else if (bss->sub_command == MLAN_OID_BSS_START) + status = + wlan_uap_bss_ioctl_start(pmadapter, pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_BSS_CONFIG) + status = wlan_uap_bss_ioctl_config(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_DEAUTH_STA) + status = wlan_uap_bss_ioctl_deauth_sta(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_BSS_RESET) + status = + wlan_uap_bss_ioctl_reset(pmadapter, pioctl_req); +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + else if (bss->sub_command == MLAN_OID_BSS_ROLE) { + util_enqueue_list_tail( + pmadapter->pmoal_handle, + &pmadapter->ioctl_pending_q, + (pmlan_linked_list)pioctl_req, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + pmadapter->pending_ioctl = MTRUE; + status = MLAN_STATUS_PENDING; + } +#endif +#ifdef WIFI_DIRECT_SUPPORT + else if (bss->sub_command == MLAN_OID_WIFI_DIRECT_MODE) + status = wlan_bss_ioctl_wifi_direct_mode(pmadapter, + pioctl_req); +#endif + else if (bss->sub_command == MLAN_OID_BSS_REMOVE) + status = wlan_bss_ioctl_bss_remove(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_CFG_WMM_PARAM) + status = wlan_uap_bss_ioctl_uap_wmm_param(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_SCAN_CHANNELS) + status = wlan_uap_bss_ioctl_uap_scan_channels( + pmadapter, pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_CHANNEL) + status = wlan_uap_bss_ioctl_uap_channel(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_ACS_SCAN) + status = wlan_uap_bss_ioctl_acs_scan(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_OPER_CTRL) + status = wlan_uap_bss_ioctl_uap_oper_ctrl(pmadapter, + pioctl_req); + else if (bss->sub_command == MLAN_OID_UAP_ADD_STATION) + status = wlan_uap_bss_ioctl_add_station(pmadapter, + pioctl_req); + break; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + case MLAN_IOCTL_SCAN: + pscan = (mlan_ds_scan *)pioctl_req->pbuf; + if ((pscan->sub_command == MLAN_OID_SCAN_NORMAL) && + (pioctl_req->action == MLAN_ACT_GET)) { + PRINTM(MIOCTL, "Get scan table in uap\n"); + pscan->param.scan_resp.pscan_table = + (t_u8 *)pmadapter->pscan_table; + pscan->param.scan_resp.num_in_scan_table = + pmadapter->num_in_scan_table; + pscan->param.scan_resp.age_in_secs = + pmadapter->age_in_secs; + pioctl_req->data_read_written = + sizeof(mlan_scan_resp) + MLAN_SUB_COMMAND_SIZE; + pscan->param.scan_resp.pchan_stats = + (t_u8 *)pmadapter->pchan_stats; + pscan->param.scan_resp.num_in_chan_stats = + pmadapter->num_in_chan_stats; + } + break; +#endif + case MLAN_IOCTL_GET_INFO: + pget_info = (mlan_ds_get_info *)pioctl_req->pbuf; + if (pget_info->sub_command == MLAN_OID_GET_VER_EXT) + status = wlan_get_info_ver_ext(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_DEBUG_INFO) + status = + wlan_get_info_debug_info(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_STATS) + status = wlan_uap_get_stats(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_UAP_STATS_LOG) + status = wlan_uap_get_stats_log(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_UAP_STA_LIST) + status = wlan_uap_get_sta_list(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_BSS_INFO) + status = wlan_uap_get_bss_info(pmadapter, pioctl_req); + else if (pget_info->sub_command == MLAN_OID_GET_FW_INFO) { + pioctl_req->data_read_written = + sizeof(mlan_fw_info) + MLAN_SUB_COMMAND_SIZE; + memcpy_ext(pmadapter, + &pget_info->param.fw_info.mac_addr, + pmpriv->curr_addr, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + pget_info->param.fw_info.fw_ver = + pmadapter->fw_release_number; + pget_info->param.fw_info.fw_bands = pmadapter->fw_bands; + pget_info->param.fw_info.ecsa_enable = + pmadapter->ecsa_enable; + pget_info->param.fw_info.getlog_enable = + pmadapter->getlog_enable; + pget_info->param.fw_info.hw_dev_mcs_support = + pmadapter->hw_dev_mcs_support; + pget_info->param.fw_info.hw_dot_11n_dev_cap = + pmadapter->hw_dot_11n_dev_cap; + pget_info->param.fw_info.usr_dev_mcs_support = + pmpriv->usr_dev_mcs_support; + if (IS_FW_SUPPORT_NO_80MHZ(pmadapter)) + pget_info->param.fw_info.prohibit_80mhz = MTRUE; + else + pget_info->param.fw_info.prohibit_80mhz = + MFALSE; + pget_info->param.fw_info.hw_dot_11ac_mcs_support = + pmadapter->hw_dot_11ac_mcs_support; + pget_info->param.fw_info.hw_dot_11ac_dev_cap = + pmadapter->hw_dot_11ac_dev_cap; + pget_info->param.fw_info.usr_dot_11ac_dev_cap_bg = + pmpriv->usr_dot_11ac_dev_cap_bg; + pget_info->param.fw_info.usr_dot_11ac_mcs_support = + pmpriv->usr_dot_11ac_mcs_support; + pget_info->param.fw_info.usr_dot_11ac_dev_cap_a = + pmpriv->usr_dot_11ac_dev_cap_a; + pget_info->param.fw_info.hw_hecap_len = + pmadapter->hw_hecap_len; + pget_info->param.fw_info.hw_2g_hecap_len = + pmadapter->hw_2g_hecap_len; + memcpy_ext(pmadapter, + pget_info->param.fw_info.hw_he_cap, + pmadapter->hw_he_cap, + pmadapter->hw_hecap_len, + sizeof(pget_info->param.fw_info.hw_he_cap)); + memcpy_ext( + pmadapter, + pget_info->param.fw_info.hw_2g_he_cap, + pmadapter->hw_2g_he_cap, + pmadapter->hw_2g_hecap_len, + sizeof(pget_info->param.fw_info.hw_2g_he_cap)); + pget_info->param.fw_info.region_code = + pmadapter->region_code; + if (pmadapter->otp_region && + pmadapter->otp_region->force_reg) + pget_info->param.fw_info.force_reg = MTRUE; + else + pget_info->param.fw_info.force_reg = MFALSE; + pget_info->param.fw_info.fw_supplicant_support = + IS_FW_SUPPORT_SUPPLICANT(pmadapter) ? 0x01 : + 0x00; + pget_info->param.fw_info.antinfo = pmadapter->antinfo; + pget_info->param.fw_info.max_ap_assoc_sta = + pmadapter->max_sta_conn; + } else if (pget_info->sub_command == MLAN_OID_LINK_STATS) + status = wlan_ioctl_link_statistic(pmpriv, pioctl_req); + break; + case MLAN_IOCTL_MISC_CFG: + misc = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + if (misc->sub_command == MLAN_OID_MISC_INIT_SHUTDOWN) + status = wlan_misc_ioctl_init_shutdown(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_SOFT_RESET) + status = wlan_uap_misc_ioctl_soft_reset(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_HOST_CMD) + status = + wlan_misc_ioctl_host_cmd(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_REGION) + status = wlan_misc_ioctl_region(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GEN_IE) + status = wlan_uap_misc_ioctl_gen_ie(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_CUSTOM_IE) + status = wlan_misc_ioctl_custom_ie_list( + pmadapter, pioctl_req, MTRUE); + if (misc->sub_command == MLAN_OID_MISC_TX_DATAPAUSE) + status = wlan_uap_misc_ioctl_txdatapause(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_RX_MGMT_IND) + status = wlan_reg_rx_mgmt_ind(pmadapter, pioctl_req); +#ifdef DEBUG_LEVEL1 + if (misc->sub_command == MLAN_OID_MISC_DRVDBG) + status = wlan_set_drvdbg(pmadapter, pioctl_req); +#endif + + if (misc->sub_command == MLAN_OID_MISC_TXCONTROL) + status = wlan_misc_ioctl_txcontrol(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_MAC_CONTROL) + status = wlan_misc_ioctl_mac_control(pmadapter, + pioctl_req); +#ifdef RX_PACKET_COALESCE + if (misc->sub_command == MLAN_OID_MISC_RX_PACKET_COALESCE) + status = wlan_misc_ioctl_rx_pkt_coalesce_config( + pmadapter, pioctl_req); +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (misc->sub_command == MLAN_OID_MISC_WIFI_DIRECT_CONFIG) + status = wlan_misc_p2p_config(pmadapter, pioctl_req); +#endif + + if (misc->sub_command == MLAN_OID_MISC_DFS_REAPTER_MODE) { + mlan_ds_misc_cfg *misc_cfg = MNULL; + + misc_cfg = (mlan_ds_misc_cfg *)pioctl_req->pbuf; + misc_cfg->param.dfs_repeater.mode = + pmadapter->dfs_repeater; + pioctl_req->data_read_written = + sizeof(mlan_ds_misc_dfs_repeater); + + status = MLAN_STATUS_SUCCESS; + } + if (misc->sub_command == MLAN_OID_MISC_IND_RST_CFG) + status = wlan_misc_ioctl_ind_rst_cfg(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GET_TSF) + status = wlan_misc_ioctl_get_tsf(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GET_CHAN_REGION_CFG) + status = wlan_misc_chan_reg_cfg(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_OPER_CLASS_CHECK) + status = wlan_misc_ioctl_operclass_validation( + pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_OPER_CLASS) + status = wlan_misc_ioctl_oper_class(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_AGGR_CTRL) + status = wlan_misc_ioctl_aggr_ctrl(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_PER_PKT_CFG) + status = wlan_misc_per_pkt_cfg(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_FW_DUMP_EVENT) + status = wlan_misc_ioctl_fw_dump_event(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_RX_ABORT_CFG) + status = wlan_misc_ioctl_rxabortcfg(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_RX_ABORT_CFG_EXT) + status = wlan_misc_ioctl_rxabortcfg_ext(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_TX_AMPDU_PROT_MODE) + status = wlan_misc_ioctl_tx_ampdu_prot_mode(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_DOT11MC_UNASSOC_FTM_CFG) + status = wlan_misc_ioctl_dot11mc_unassoc_ftm_cfg( + pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_RATE_ADAPT_CFG) + status = wlan_misc_ioctl_rate_adapt_cfg(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_CCK_DESENSE_CFG) + status = wlan_misc_ioctl_cck_desense_cfg(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_ROBUSTCOEX) + status = wlan_misc_robustcoex(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_DMCS_CONFIG) + status = wlan_misc_dmcs_config(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GET_TX_RX_HISTOGRAM) + status = + wlan_get_tx_rx_histogram(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_CFP_INFO) + status = wlan_get_cfpinfo(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_BOOT_SLEEP) + status = wlan_misc_bootsleep(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_DYN_BW) + status = wlan_misc_ioctl_dyn_bw(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GET_CHAN_TRPC_CFG) + status = wlan_get_chan_trpc_cfg(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_BAND_STEERING) + status = wlan_misc_band_steering_cfg(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_BEACON_STUCK) + status = wlan_misc_beacon_stuck_cfg(pmadapter, + pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_GET_REGIONPWR_CFG) + status = wlan_get_rgchnpwr_cfg(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_CFP_TABLE) + status = wlan_get_cfp_table(pmadapter, pioctl_req); + if (misc->sub_command == MLAN_OID_MISC_RANGE_EXT) + status = wlan_misc_ioctl_range_ext(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_POWER_CFG: + power = (mlan_ds_power_cfg *)pioctl_req->pbuf; + if (power->sub_command == MLAN_OID_POWER_LOW_POWER_MODE) + status = wlan_power_ioctl_set_get_lpm(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_PM_CFG: + pm = (mlan_ds_pm_cfg *)pioctl_req->pbuf; + if (pm->sub_command == MLAN_OID_PM_CFG_PS_MODE) + status = wlan_uap_pm_ioctl_mode(pmadapter, pioctl_req); + if (pm->sub_command == MLAN_OID_PM_CFG_DEEP_SLEEP) + status = wlan_uap_pm_ioctl_deepsleep(pmadapter, + pioctl_req); + if (pm->sub_command == MLAN_OID_PM_CFG_HS_CFG) + status = wlan_pm_ioctl_hscfg(pmadapter, pioctl_req); + if (pm->sub_command == MLAN_OID_PM_HS_WAKEUP_REASON) + status = wlan_get_hs_wakeup_reason(pmadapter, + pioctl_req); + if (pm->sub_command == MLAN_OID_PM_MGMT_FILTER) + status = wlan_config_mgmt_filter(pmadapter, pioctl_req); + if (pm->sub_command == MLAN_OID_PM_INFO) + status = wlan_get_pm_info(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_SNMP_MIB: + snmp = (mlan_ds_snmp_mib *)pioctl_req->pbuf; + if (snmp->sub_command == MLAN_OID_SNMP_MIB_CTRL_DEAUTH) + status = wlan_uap_snmp_mib_ctrl_deauth(pmadapter, + pioctl_req); + if (snmp->sub_command == MLAN_OID_SNMP_MIB_DOT11D) + status = wlan_uap_snmp_mib_11d(pmadapter, pioctl_req); + if (snmp->sub_command == MLAN_OID_SNMP_MIB_DOT11H) + status = wlan_uap_snmp_mib_11h(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_SEC_CFG: + sec = (mlan_ds_sec_cfg *)pioctl_req->pbuf; + if (sec->sub_command == MLAN_OID_SEC_CFG_ENCRYPT_KEY) + status = wlan_uap_sec_ioctl_set_encrypt_key(pmadapter, + pioctl_req); + if (sec->sub_command == MLAN_OID_SEC_CFG_WAPI_ENABLED) + status = wlan_uap_sec_ioctl_wapi_enable(pmadapter, + pioctl_req); + if (sec->sub_command == MLAN_OID_SEC_CFG_REPORT_MIC_ERR) + status = wlan_uap_sec_ioctl_report_mic_error( + 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); + else if (cfg11d->sub_command == MLAN_OID_11D_DOMAIN_INFO_EXT) + status = + wlan_11d_cfg_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 (cfg11h->sub_command == MLAN_OID_11H_DFS_TESTING) + status = wlan_11h_ioctl_dfs_testing(pmadapter, + pioctl_req); + if (cfg11h->sub_command == MLAN_OID_11H_CHAN_NOP_INFO) + status = wlan_11h_ioctl_get_channel_nop_info( + pmadapter, pioctl_req); + if (cfg11h->sub_command == MLAN_OID_11H_CHAN_REPORT_REQUEST) + status = wlan_11h_ioctl_dfs_chan_report(pmpriv, + pioctl_req); + if (cfg11h->sub_command == MLAN_OID_11H_CHAN_SWITCH_COUNT) + status = wlan_11h_ioctl_chan_switch_count(pmadapter, + pioctl_req); + if (cfg11h->sub_command == MLAN_OID_11H_DFS_W53_CFG) + status = wlan_11h_ioctl_dfs_w53_cfg(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_RADIO_CFG: + radiocfg = (mlan_ds_radio_cfg *)pioctl_req->pbuf; + if (radiocfg->sub_command == MLAN_OID_MIMO_SWITCH) + status = wlan_radio_ioctl_mimo_switch_cfg(pmadapter, + pioctl_req); + if (radiocfg->sub_command == MLAN_OID_RADIO_CTRL) + status = wlan_radio_ioctl_radio_ctl(pmadapter, + pioctl_req); + if (radiocfg->sub_command == MLAN_OID_REMAIN_CHAN_CFG) + status = wlan_radio_ioctl_remain_chan_cfg(pmadapter, + pioctl_req); + if (radiocfg->sub_command == MLAN_OID_ANT_CFG) + status = + wlan_radio_ioctl_ant_cfg(pmadapter, pioctl_req); + if (radiocfg->sub_command == MLAN_OID_BAND_CFG) + status = wlan_radio_ioctl_band_cfg(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_RATE: + rate = (mlan_ds_rate *)pioctl_req->pbuf; + if (rate->sub_command == MLAN_OID_RATE_CFG) + status = wlan_rate_ioctl_cfg(pmadapter, pioctl_req); + else if (rate->sub_command == MLAN_OID_GET_DATA_RATE) + status = wlan_rate_ioctl_get_data_rate(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_11AC_CFG: + status = wlan_11ac_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_11AX_CFG: + status = wlan_11ax_cfg_ioctl(pmadapter, pioctl_req); + break; + case MLAN_IOCTL_REG_MEM: + reg_mem = (mlan_ds_reg_mem *)pioctl_req->pbuf; + if (reg_mem->sub_command == MLAN_OID_REG_RW) + status = wlan_reg_mem_ioctl_reg_rw(pmadapter, + pioctl_req); + else if (reg_mem->sub_command == MLAN_OID_EEPROM_RD) + status = wlan_reg_mem_ioctl_read_eeprom(pmadapter, + pioctl_req); + else if (reg_mem->sub_command == MLAN_OID_MEM_RW) + status = wlan_reg_mem_ioctl_mem_rw(pmadapter, + pioctl_req); + break; + case MLAN_IOCTL_WMM_CFG: + status = wlan_wmm_cfg_ioctl(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + break; + } + LEAVE(); + return status; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_uap_txrx.c b/mxm_wifiex/wlan_src/mlan/mlan_uap_txrx.c new file mode 100644 index 0000000..5ea637f --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_uap_txrx.c @@ -0,0 +1,820 @@ +/** @file mlan_uap_txrx.c + * + * @brief This file contains AP mode transmit and receive functions + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_wmm.h" +#include "mlan_11n_aggr.h" +#include "mlan_11n_rxreorder.h" +#ifdef DRV_EMBEDDED_AUTHENTICATOR +#include "authenticator_api.h" +#endif + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer + * + * @param pmadapter A pointer to mlan_adapter + * @param pmbuf A pointer to mlan_buffer which includes the received packet + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status wlan_upload_uap_rx_packet(pmlan_adapter pmadapter, + pmlan_buffer pmbuf) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef DEBUG_LEVEL1 + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; +#endif + PRxPD prx_pd; + ENTER(); + prx_pd = (RxPD *)(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(RxPD), MAX_DATA_DUMP_LEN)); + DBG_HEXDUMP(MDAT_D, "uAP Rx Payload", + ((t_u8 *)prx_pd + prx_pd->rx_pkt_offset), + MIN(prx_pd->rx_pkt_length, MAX_DATA_DUMP_LEN)); + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmbuf->out_ts_sec, + &pmbuf->out_ts_usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => kernel seq_num=%d tid=%d\n", + pmbuf->out_ts_sec, pmbuf->out_ts_usec, prx_pd->seq_num, + prx_pd->priority); + ret = pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, + pmbuf); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, + "uAP Rx Error: moal_recv_packet returned error\n"); + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + } + + if (ret != MLAN_STATUS_PENDING) + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); +#ifdef USB + else if (IS_USB(pmadapter->card_type)) + pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, + MNULL, + pmadapter->rx_data_ep, + ret); +#endif + LEAVE(); + + return ret; +} + +/** + * @brief This function will check if unicast packet need be dropped + * + * @param priv A pointer to mlan_private + * @param mac mac address to find in station list table + * + * @return MLAN_STATUS_FAILURE -- drop packet, otherwise forward to + * network stack + */ +static mlan_status wlan_check_unicast_packet(mlan_private *priv, t_u8 *mac) +{ + int j; + sta_node *sta_ptr = MNULL; + pmlan_adapter pmadapter = priv->adapter; + pmlan_private pmpriv = MNULL; + t_u8 pkt_type = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + for (j = 0; j < MLAN_MAX_BSS_NUM; ++j) { + pmpriv = pmadapter->priv[j]; + if (pmpriv) { + if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) + continue; + sta_ptr = wlan_get_station_entry(pmpriv, mac); + if (sta_ptr) { + if (pmpriv == priv) + pkt_type = PKT_INTRA_UCAST; + else + pkt_type = PKT_INTER_UCAST; + break; + } + } + } + if ((pkt_type == PKT_INTRA_UCAST) && + (priv->pkt_fwd & PKT_FWD_INTRA_UCAST)) { + PRINTM(MDATA, "Drop INTRA_UCAST packet\n"); + ret = MLAN_STATUS_FAILURE; + } else if ((pkt_type == PKT_INTER_UCAST) && + (priv->pkt_fwd & PKT_FWD_INTER_UCAST)) { + PRINTM(MDATA, "Drop INTER_UCAST packet\n"); + ret = MLAN_STATUS_FAILURE; + } + LEAVE(); + return ret; +} +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function fill the txpd for tx packet + * + * @param priv A pointer to mlan_private structure + * @param pmbuf A pointer to the mlan_buffer for process + * + * @return headptr or MNULL + */ +t_void *wlan_ops_uap_process_txpd(t_void *priv, pmlan_buffer pmbuf) +{ + pmlan_private pmpriv = (pmlan_private)priv; + TxPD *plocal_tx_pd; + t_u8 *head_ptr = MNULL; + t_u32 pkt_type; + t_u32 tx_control; + t_u8 dst_mac[MLAN_MAC_ADDR_LENGTH]; + + ENTER(); + + if (!pmbuf->data_len) { + PRINTM(MERROR, "uAP Tx Error: Invalid packet length: %d\n", + pmbuf->data_len); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + goto done; + } + if (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { + memcpy_ext(pmpriv->adapter, &pkt_type, + pmbuf->pbuf + pmbuf->data_offset, sizeof(pkt_type), + sizeof(pkt_type)); + memcpy_ext(pmpriv->adapter, &tx_control, + pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), + sizeof(tx_control), sizeof(tx_control)); + pmbuf->data_offset += sizeof(pkt_type) + sizeof(tx_control); + pmbuf->data_len -= sizeof(pkt_type) + sizeof(tx_control); + } + if (pmbuf->data_offset < + (sizeof(TxPD) + pmpriv->intf_hr_len + DMA_ALIGNMENT)) { + PRINTM(MERROR, + "not enough space for TxPD: headroom=%d pkt_len=%d, required=%d\n", + pmbuf->data_offset, pmbuf->data_len, + sizeof(TxPD) + pmpriv->intf_hr_len + DMA_ALIGNMENT); + 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(TxPD) - + pmpriv->intf_hr_len; + head_ptr = (t_u8 *)((t_ptr)head_ptr & ~((t_ptr)(DMA_ALIGNMENT - 1))); + + plocal_tx_pd = (TxPD *)(head_ptr + pmpriv->intf_hr_len); + memset(pmpriv->adapter, 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 (pmbuf->flags & MLAN_BUF_FLAG_TX_STATUS) { + plocal_tx_pd->tx_control_1 |= pmbuf->tx_seq_num << 8; + plocal_tx_pd->flags |= MRVDRV_TxPD_FLAGS_TX_PACKET_STATUS; + } + + /* Offset of actual data */ + plocal_tx_pd->tx_pkt_offset = (t_u16)( + (t_ptr)pmbuf->pbuf + pmbuf->data_offset - (t_ptr)plocal_tx_pd); + + if (!plocal_tx_pd->tx_control) { + /* TxCtrl set by user or default */ + plocal_tx_pd->tx_control = pmpriv->pkt_tx_ctrl; + } + + if (pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { + plocal_tx_pd->tx_pkt_type = (t_u16)pkt_type; + plocal_tx_pd->tx_control = tx_control; + } + if (pmbuf->flags & MLAN_BUF_FLAG_TX_CTRL) { + if (pmbuf->u.tx_info.data_rate) { + memcpy_ext(pmpriv->adapter, dst_mac, + pmbuf->pbuf + pmbuf->data_offset, + sizeof(dst_mac), sizeof(dst_mac)); + plocal_tx_pd->tx_control |= + (wlan_ieee_rateid_to_mrvl_rateid( + pmpriv, pmbuf->u.tx_info.data_rate, + dst_mac) + << 16); + plocal_tx_pd->tx_control |= TXPD_TXRATE_ENABLE; + } + plocal_tx_pd->tx_control_1 |= pmbuf->u.tx_info.channel << 21; + if (pmbuf->u.tx_info.bw) { + plocal_tx_pd->tx_control_1 |= pmbuf->u.tx_info.bw << 16; + plocal_tx_pd->tx_control_1 |= TXPD_BW_ENABLE; + } + if (pmbuf->u.tx_info.tx_power.tp.hostctl) + plocal_tx_pd->tx_control |= + (t_u32)pmbuf->u.tx_info.tx_power.val; + if (pmbuf->u.tx_info.retry_limit) { + plocal_tx_pd->tx_control |= pmbuf->u.tx_info.retry_limit + << 8; + plocal_tx_pd->tx_control |= TXPD_RETRY_ENABLE; + } + } + + endian_convert_TxPD(plocal_tx_pd); + + /* Adjust the data offset and length to include TxPD in pmbuf */ + pmbuf->data_len += pmbuf->data_offset; + pmbuf->data_offset = (t_u32)((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(t_void *adapter, pmlan_buffer pmbuf) +{ + pmlan_adapter pmadapter = (pmlan_adapter)adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + RxPD *prx_pd; + wlan_mgmt_pkt *puap_pkt_hdr = MNULL; + + RxPacketHdr_t *prx_pkt; + pmlan_private priv = pmadapter->priv[pmbuf->bss_index]; + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + t_u16 rx_pkt_type = 0; + sta_node *sta_ptr = MNULL; +#ifdef DRV_EMBEDDED_AUTHENTICATOR + t_u8 eapol_type[2] = {0x88, 0x8e}; +#endif + t_u16 adj_rx_rate = 0; + t_u8 antenna = 0; + + t_u32 last_rx_sec = 0; + t_u32 last_rx_usec = 0; + t_u8 ext_rate_info = 0; + + ENTER(); + + prx_pd = (RxPD *)(pmbuf->pbuf + pmbuf->data_offset); + /* Endian conversion */ + endian_convert_RxPD(prx_pd); + + if (priv->rx_pkt_info) { + ext_rate_info = (t_u8)(prx_pd->rx_info >> 16); + pmbuf->u.rx_info.data_rate = + wlan_index_to_data_rate(priv->adapter, prx_pd->rx_rate, + prx_pd->rate_info, + ext_rate_info); + pmbuf->u.rx_info.channel = + (prx_pd->rx_info & RXPD_CHAN_MASK) >> 5; + pmbuf->u.rx_info.antenna = prx_pd->antenna; + pmbuf->u.rx_info.rssi = prx_pd->snr - prx_pd->nf; + } + + rx_pkt_type = prx_pd->rx_pkt_type; + prx_pkt = (RxPacketHdr_t *)((t_u8 *)prx_pd + prx_pd->rx_pkt_offset); + + PRINTM(MINFO, + "RX Data: data_len - prx_pd->rx_pkt_offset = %d - %d = %d\n", + pmbuf->data_len, prx_pd->rx_pkt_offset, + pmbuf->data_len - prx_pd->rx_pkt_offset); + + if ((prx_pd->rx_pkt_offset + prx_pd->rx_pkt_length) != + (t_u16)pmbuf->data_len) { + PRINTM(MERROR, + "Wrong rx packet: len=%d,rx_pkt_offset=%d," + " rx_pkt_length=%d\n", + pmbuf->data_len, prx_pd->rx_pkt_offset, + prx_pd->rx_pkt_length); + pmbuf->status_code = MLAN_ERROR_PKT_SIZE_INVALID; + ret = MLAN_STATUS_FAILURE; + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + goto done; + } + pmbuf->data_len = prx_pd->rx_pkt_offset + prx_pd->rx_pkt_length; + + if (pmadapter->priv[pmbuf->bss_index]->mgmt_frame_passthru_mask && + prx_pd->rx_pkt_type == PKT_TYPE_MGMT_FRAME) { + /* Check if this is mgmt packet and needs to + * forwarded to app as an event + */ + puap_pkt_hdr = (wlan_mgmt_pkt *)((t_u8 *)prx_pd + + prx_pd->rx_pkt_offset); + puap_pkt_hdr->frm_len = wlan_le16_to_cpu(puap_pkt_hdr->frm_len); + if ((puap_pkt_hdr->wlan_header.frm_ctl & + IEEE80211_FC_MGMT_FRAME_TYPE_MASK) == 0) + wlan_process_802dot11_mgmt_pkt( + pmadapter->priv[pmbuf->bss_index], + (t_u8 *)&puap_pkt_hdr->wlan_header, + puap_pkt_hdr->frm_len + sizeof(wlan_mgmt_pkt) - + sizeof(puap_pkt_hdr->frm_len), + (RxPD *)prx_pd); + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + goto done; + } + if (rx_pkt_type != PKT_TYPE_BAR) { + priv->rxpd_rate = prx_pd->rx_rate; + priv->rxpd_rate_info = prx_pd->rate_info; + priv->rxpd_rx_info = (t_u8)(prx_pd->rx_info >> 16); + + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + antenna = wlan_adjust_antenna(priv, (RxPD *)prx_pd); + adj_rx_rate = wlan_adjust_data_rate( + priv, priv->rxpd_rate, priv->rxpd_rate_info); + pmadapter->callbacks.moal_hist_data_add( + pmadapter->pmoal_handle, pmbuf->bss_index, + adj_rx_rate, prx_pd->snr, prx_pd->nf, antenna); + } + } + + sta_ptr = wlan_get_station_entry(priv, prx_pkt->eth803_hdr.src_addr); + if (sta_ptr) { + sta_ptr->snr = prx_pd->snr; + sta_ptr->nf = prx_pd->nf; + pmadapter->callbacks.moal_get_system_time( + pmadapter->pmoal_handle, &last_rx_sec, &last_rx_usec); + sta_ptr->stats.last_rx_in_msec = + (t_u64)last_rx_sec * 1000 + (t_u64)last_rx_usec / 1000; + } + +#ifdef DRV_EMBEDDED_AUTHENTICATOR + /**process eapol packet for uap*/ + if (IsAuthenticatorEnabled(priv->psapriv) && + (!memcmp(pmadapter, &prx_pkt->eth803_hdr.h803_len, eapol_type, + sizeof(eapol_type)))) { + ret = AuthenticatorProcessEapolPacket( + priv->psapriv, ((t_u8 *)prx_pd + prx_pd->rx_pkt_offset), + prx_pd->rx_pkt_length); + if (ret == MLAN_STATUS_SUCCESS) { + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + goto done; + } + } +#endif + + pmbuf->priority = prx_pd->priority; + memcpy_ext(pmadapter, ta, prx_pkt->eth803_hdr.src_addr, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + if ((rx_pkt_type != PKT_TYPE_BAR) && (prx_pd->priority < MAX_NUM_TID)) { + sta_ptr = wlan_get_station_entry(priv, ta); + if (sta_ptr) { + sta_ptr->rx_seq[prx_pd->priority] = prx_pd->seq_num; + sta_ptr->snr = prx_pd->snr; + sta_ptr->nf = prx_pd->nf; + } + } + /* check if UAP enable 11n */ + if (!priv->is_11n_enabled || + (!wlan_11n_get_rxreorder_tbl((mlan_private *)priv, prx_pd->priority, + ta) && + (prx_pd->rx_pkt_type != PKT_TYPE_AMSDU))) { + if (priv->pkt_fwd) + wlan_process_uap_rx_packet(priv, pmbuf); + else + wlan_upload_uap_rx_packet(pmadapter, pmbuf); + goto done; + } + /* Reorder and send to OS */ + ret = mlan_11n_rxreorder_pkt(priv, prx_pd->seq_num, prx_pd->priority, + ta, (t_u8)prx_pd->rx_pkt_type, + (void *)pmbuf); + if (ret || (rx_pkt_type == PKT_TYPE_BAR)) { + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer or send back to firmware + * + * @param priv A pointer to mlan_private + * @param pmbuf A pointer to mlan_buffer which includes the received packet + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_uap_recv_packet(mlan_private *priv, pmlan_buffer pmbuf) +{ + pmlan_adapter pmadapter = priv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + RxPacketHdr_t *prx_pkt; + pmlan_buffer newbuf = MNULL; + + ENTER(); + + prx_pkt = (RxPacketHdr_t *)((t_u8 *)pmbuf->pbuf + pmbuf->data_offset); + + DBG_HEXDUMP(MDAT_D, "uap_recv_packet", pmbuf->pbuf + pmbuf->data_offset, + MIN(pmbuf->data_len, MAX_DATA_DUMP_LEN)); + + PRINTM(MDATA, "AMSDU dest " MACSTR "\n", + MAC2STR(prx_pkt->eth803_hdr.dest_addr)); + + /* don't do packet forwarding in disconnected state */ + if ((priv->media_connected == MFALSE) || + (pmbuf->data_len > MV_ETH_FRAME_LEN)) + goto upload; + + if (prx_pkt->eth803_hdr.dest_addr[0] & 0x01) { + if (!(priv->pkt_fwd & PKT_FWD_INTRA_BCAST)) { + /* Multicast pkt */ + newbuf = + wlan_alloc_mlan_buffer(pmadapter, + MLAN_TX_DATA_BUF_SIZE_2K, + 0, MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = pmbuf->bss_index; + newbuf->buf_type = pmbuf->buf_type; + newbuf->priority = pmbuf->priority; + newbuf->in_ts_sec = pmbuf->in_ts_sec; + newbuf->in_ts_usec = pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(TxPD) + priv->intf_hr_len + + DMA_ALIGNMENT); + util_scalar_increment( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + + newbuf->flags |= MLAN_BUF_FLAG_BRIDGE_BUF; + + /* copy the data */ + memcpy_ext(pmadapter, + (t_u8 *)newbuf->pbuf + + newbuf->data_offset, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len, + MLAN_TX_DATA_BUF_SIZE_2K); + newbuf->data_len = pmbuf->data_len; + wlan_wmm_add_buf_txqueue(pmadapter, newbuf); + if (util_scalar_read( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks + .moal_spin_unlock) > + RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + wlan_recv_event( + priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + } else { + if ((!(priv->pkt_fwd & PKT_FWD_INTRA_UCAST)) && + (wlan_get_station_entry(priv, + prx_pkt->eth803_hdr.dest_addr))) { + /* Intra BSS packet */ + newbuf = + wlan_alloc_mlan_buffer(pmadapter, + MLAN_TX_DATA_BUF_SIZE_2K, + 0, MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = pmbuf->bss_index; + newbuf->buf_type = pmbuf->buf_type; + newbuf->priority = pmbuf->priority; + newbuf->in_ts_sec = pmbuf->in_ts_sec; + newbuf->in_ts_usec = pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(TxPD) + priv->intf_hr_len + + DMA_ALIGNMENT); + util_scalar_increment( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + newbuf->flags |= MLAN_BUF_FLAG_BRIDGE_BUF; + + /* copy the data */ + memcpy_ext(pmadapter, + (t_u8 *)newbuf->pbuf + + newbuf->data_offset, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len, + MLAN_TX_DATA_BUF_SIZE_2K); + newbuf->data_len = pmbuf->data_len; + wlan_wmm_add_buf_txqueue(pmadapter, newbuf); + if (util_scalar_read( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks + .moal_spin_unlock) > + RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + wlan_recv_event( + priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + goto done; + } else if (MLAN_STATUS_FAILURE == + wlan_check_unicast_packet( + priv, prx_pkt->eth803_hdr.dest_addr)) { + /* drop packet */ + PRINTM(MDATA, "Drop AMSDU dest " MACSTR "\n", + MAC2STR(prx_pkt->eth803_hdr.dest_addr)); + goto done; + } + } +upload: + /** send packet to moal */ + ret = pmadapter->callbacks.moal_recv_packet(pmadapter->pmoal_handle, + pmbuf); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer or send back to firmware + * + * @param priv A pointer to mlan_private + * @param pmbuf A pointer to mlan_buffer which includes the received packet + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_process_uap_rx_packet(mlan_private *priv, pmlan_buffer pmbuf) +{ + pmlan_adapter pmadapter = priv->adapter; + mlan_status ret = MLAN_STATUS_SUCCESS; + RxPD *prx_pd; + RxPacketHdr_t *prx_pkt; + pmlan_buffer newbuf = MNULL; + + ENTER(); + + prx_pd = (RxPD *)(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(RxPD), MAX_DATA_DUMP_LEN)); + DBG_HEXDUMP(MDAT_D, "uAP Rx Payload", + ((t_u8 *)prx_pd + prx_pd->rx_pkt_offset), + MIN(prx_pd->rx_pkt_length, MAX_DATA_DUMP_LEN)); + + PRINTM(MINFO, + "RX Data: data_len - prx_pd->rx_pkt_offset = %d - %d = %d\n", + pmbuf->data_len, prx_pd->rx_pkt_offset, + pmbuf->data_len - prx_pd->rx_pkt_offset); + PRINTM(MDATA, "Rx dest " MACSTR "\n", + MAC2STR(prx_pkt->eth803_hdr.dest_addr)); + + /* don't do packet forwarding in disconnected state */ + /* don't do packet forwarding when packet > 1514 */ + if ((priv->media_connected == MFALSE) || + ((pmbuf->data_len - prx_pd->rx_pkt_offset) > MV_ETH_FRAME_LEN)) + goto upload; + + if (prx_pkt->eth803_hdr.dest_addr[0] & 0x01) { + if (!(priv->pkt_fwd & PKT_FWD_INTRA_BCAST)) { + /* Multicast pkt */ + newbuf = + wlan_alloc_mlan_buffer(pmadapter, + MLAN_TX_DATA_BUF_SIZE_2K, + 0, MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = pmbuf->bss_index; + newbuf->buf_type = pmbuf->buf_type; + newbuf->priority = pmbuf->priority; + newbuf->in_ts_sec = pmbuf->in_ts_sec; + newbuf->in_ts_usec = pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(TxPD) + priv->intf_hr_len + + DMA_ALIGNMENT); + util_scalar_increment( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + newbuf->flags |= MLAN_BUF_FLAG_BRIDGE_BUF; + + /* copy the data, skip rxpd */ + memcpy_ext(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, + MLAN_TX_DATA_BUF_SIZE_2K); + newbuf->data_len = + pmbuf->data_len - prx_pd->rx_pkt_offset; + wlan_wmm_add_buf_txqueue(pmadapter, newbuf); + if (util_scalar_read( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks + .moal_spin_unlock) > + RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + wlan_recv_event( + priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + } + } else { + if ((!(priv->pkt_fwd & PKT_FWD_INTRA_UCAST)) && + (wlan_get_station_entry(priv, + prx_pkt->eth803_hdr.dest_addr))) { + /* Forwarding Intra-BSS packet */ +#ifdef USB + if (IS_USB(pmadapter->card_type)) { + if (pmbuf->flags & MLAN_BUF_FLAG_RX_DEAGGR) { + newbuf = wlan_alloc_mlan_buffer( + pmadapter, + MLAN_TX_DATA_BUF_SIZE_2K, 0, + MOAL_MALLOC_BUFFER); + if (newbuf) { + newbuf->bss_index = + pmbuf->bss_index; + newbuf->buf_type = + pmbuf->buf_type; + newbuf->priority = + pmbuf->priority; + newbuf->in_ts_sec = + pmbuf->in_ts_sec; + newbuf->in_ts_usec = + pmbuf->in_ts_usec; + newbuf->data_offset = + (sizeof(TxPD) + + priv->intf_hr_len + + DMA_ALIGNMENT); + util_scalar_increment( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks + .moal_spin_lock, + pmadapter->callbacks + .moal_spin_unlock); + newbuf->flags |= + MLAN_BUF_FLAG_BRIDGE_BUF; + + /* copy the data, skip rxpd */ + memcpy_ext( + 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, + pmbuf->data_len - + prx_pd->rx_pkt_offset); + newbuf->data_len = + pmbuf->data_len - + prx_pd->rx_pkt_offset; + wlan_wmm_add_buf_txqueue( + pmadapter, newbuf); + if (util_scalar_read( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks + .moal_spin_lock, + pmadapter->callbacks + .moal_spin_unlock) > + RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + wlan_recv_event( + priv, + MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + } + pmadapter->callbacks.moal_recv_complete( + pmadapter->pmoal_handle, pmbuf, + pmadapter->rx_data_ep, ret); + goto done; + } + } +#endif + pmbuf->data_len -= prx_pd->rx_pkt_offset; + pmbuf->data_offset += prx_pd->rx_pkt_offset; + pmbuf->flags |= MLAN_BUF_FLAG_BRIDGE_BUF; + util_scalar_increment( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock); + wlan_wmm_add_buf_txqueue(pmadapter, pmbuf); + if (util_scalar_read( + pmadapter->pmoal_handle, + &pmadapter->pending_bridge_pkts, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock) > + RX_HIGH_THRESHOLD) + wlan_drop_tx_pkts(priv); + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_DEFER_HANDLING, + MNULL); + goto done; + } else if (MLAN_STATUS_FAILURE == + wlan_check_unicast_packet( + priv, prx_pkt->eth803_hdr.dest_addr)) { + PRINTM(MDATA, "Drop Pkts: Rx dest " MACSTR "\n", + MAC2STR(prx_pkt->eth803_hdr.dest_addr)); + pmbuf->status_code = MLAN_ERROR_PKT_INVALID; + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); + goto done; + } + } + +upload: + /* Chop off RxPD */ + pmbuf->data_len -= prx_pd->rx_pkt_offset; + pmbuf->data_offset += prx_pd->rx_pkt_offset; + pmbuf->pparent = MNULL; + + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmbuf->out_ts_sec, + &pmbuf->out_ts_usec); + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "%lu.%06lu : Data => kernel seq_num=%d tid=%d\n", + pmbuf->out_ts_sec, pmbuf->out_ts_usec, prx_pd->seq_num, + prx_pd->priority); + + 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) + pmadapter->ops.data_complete(pmadapter, pmbuf, ret); +#ifdef USB + else if (IS_USB(pmadapter->card_type)) + pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, + MNULL, + pmadapter->rx_data_ep, + ret); +#endif +done: + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_usb.c b/mxm_wifiex/wlan_src/mlan/mlan_usb.c new file mode 100644 index 0000000..5a5f2d8 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_usb.c @@ -0,0 +1,1264 @@ +/** @file mlan_usb.c + * + * @brief This file contains USB specific code + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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/21/2009: initial version +********************************************************/ + +#include "mlan.h" +#ifdef STA_SUPPORT +#include "mlan_join.h" +#endif +#include "mlan_util.h" +#include "mlan_init.h" +#include "mlan_fw.h" +#include "mlan_main.h" + +/******************************************************** + Local Variables +********************************************************/ +#ifdef USB8897 +static const struct _mlan_card_info mlan_card_info_usb8897 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 0, + .supp_ps_handshake = 1, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#ifdef USB8997 +static const struct _mlan_card_info mlan_card_info_usb8997 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 1, + .supp_ps_handshake = 1, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#ifdef USB8978 +static const struct _mlan_card_info mlan_card_info_usb8978 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 1, + .supp_ps_handshake = 1, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#ifdef USB9098 +static const struct _mlan_card_info mlan_card_info_usb9098 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 1, + .v17_fw_api = 1, + .supp_ps_handshake = 1, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +#ifdef USB9097 +static const struct _mlan_card_info mlan_card_info_usb9097 = { + .max_tx_buf_size = MLAN_TX_DATA_BUF_SIZE_4K, + .v16_fw_api = 1, + .v17_fw_api = 1, + .supp_ps_handshake = 1, + .default_11n_tx_bf_cap = DEFAULT_11N_TX_BF_CAP_2X2, +}; +#endif + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +#if defined(USB9098) +/** + * @This function checks the chip revision id + * + * @param pmadapter A pointer to mlan_adapter structure + * @param rev_id A pointer to chip revision id + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_usb_check_revision(mlan_adapter *pmadapter, t_u32 *rev_id) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + mlan_buffer mbuf; + t_u8 *tx_buff = MNULL; + t_u8 *recv_buff = MNULL; + t_u8 tx_size = 16; + FWSyncPkt syncpkt; + + ENTER(); + /* Allocate memory for transmit */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, FW_DNLD_TX_BUF_SIZE, + MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&tx_buff); + if ((ret != MLAN_STATUS_SUCCESS) || !tx_buff) { + PRINTM(MERROR, "Could not allocate buffer for FW download\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* Allocate memory for receive */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, FW_DNLD_RX_BUF_SIZE, + MLAN_MEM_DEF | MLAN_MEM_DMA, &recv_buff); + if ((ret != MLAN_STATUS_SUCCESS) || !recv_buff) { + PRINTM(MERROR, + "Could not allocate buffer for FW download response\n"); + goto cleanup; + } + memset(pmadapter, &syncpkt, 0, sizeof(FWSyncPkt)); + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = (t_u8 *)tx_buff; + mbuf.data_len = tx_size; + ret = pcb->moal_write_data_sync(pmadapter->pmoal_handle, &mbuf, + pmadapter->tx_cmd_ep, + MLAN_USB_BULK_MSG_TIMEOUT); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "check revision: write_data failed, ret %d\n", + ret); + goto cleanup; + } + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = (t_u8 *)recv_buff; + mbuf.data_len = 2048; + ret = pcb->moal_read_data_sync(pmadapter->pmoal_handle, &mbuf, + pmadapter->rx_cmd_ep, + MLAN_USB_BULK_MSG_TIMEOUT); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "check revision: read_data failed, ret %d\n", + ret); + goto cleanup; + } + memcpy_ext(pmadapter, &syncpkt, recv_buff, sizeof(syncpkt), + sizeof(syncpkt)); + syncpkt.chip_rev = wlan_le32_to_cpu(syncpkt.chip_rev); + *rev_id = syncpkt.chip_rev & 0x000000ff; + PRINTM(MERROR, "chip_revision_id = %d\n", syncpkt.chip_rev); +cleanup: + if (recv_buff) + pcb->moal_mfree(pmadapter->pmoal_handle, recv_buff); + if (tx_buff) + pcb->moal_mfree(pmadapter->pmoal_handle, tx_buff); + + LEAVE(); + return ret; +} +#endif + +/** + * @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_usb_prog_fw_w_helper(pmlan_adapter pmadapter, + pmlan_fw_image pmfw) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_callbacks pcb = &pmadapter->callbacks; + t_u8 *firmware = pmfw->pfw_buf, *RecvBuff; + t_u32 retries = MAX_FW_RETRY, DataLength; + t_u32 FWSeqNum = 0, TotalBytes = 0, DnldCmd = 0; + t_u8 *TxBuff = MNULL; + FWData *fwdata = MNULL; + FWSyncHeader SyncFWHeader; + t_u8 check_winner = 1; + t_u8 check_fw_status = MFALSE; + t_u8 mic_retry = MAX_FW_RETRY; +#if defined(USB9098) + t_u32 revision_id = 0; +#endif + + ENTER(); + + if (!firmware && !pcb->moal_get_fw_data) { + PRINTM(MMSG, "No firmware image found! Terminating download\n"); + ret = MLAN_STATUS_FAILURE; + goto fw_exit; + } + + /* Allocate memory for transmit */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, FW_DNLD_TX_BUF_SIZE, + MLAN_MEM_DEF | MLAN_MEM_DMA, (t_u8 **)&TxBuff); + if ((ret != MLAN_STATUS_SUCCESS) || !TxBuff) { + PRINTM(MERROR, "Could not allocate buffer for FW download\n"); + goto fw_exit; + } + fwdata = (FWData *)TxBuff; + + /* Allocate memory for receive */ + ret = pcb->moal_malloc(pmadapter->pmoal_handle, FW_DNLD_RX_BUF_SIZE, + MLAN_MEM_DEF | MLAN_MEM_DMA, &RecvBuff); + if ((ret != MLAN_STATUS_SUCCESS) || !RecvBuff) { + PRINTM(MERROR, + "Could not allocate buffer for FW download response\n"); + goto cleanup; + } + + if (!IS_USB_NEW_INIT(pmadapter->feature_control)) + check_winner = 0; +#if defined(USB9098) + if (IS_USB9098(pmadapter->card_type)) { + ret = wlan_usb_check_revision(pmadapter, &revision_id); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to get USB chip revision ID\n"); + goto cleanup; + } + /* Skyhawk A0, need to check both CRC and MIC error */ + if (revision_id >= CHIP_9098_REV_A0) + check_fw_status = MTRUE; + } +#endif +#if defined(USB9097) + if (IS_USB9097(pmadapter->card_type)) + check_fw_status = MTRUE; +#endif + do { + /* Send pseudo data to check winner status first */ + if (check_winner) { + memset(pmadapter, &fwdata->fw_header, 0, + sizeof(FWHeader)); + DataLength = 0; + } else { + /* Copy the header of the firmware data to get the + * length */ + if (firmware) + memcpy_ext(pmadapter, &fwdata->fw_header, + &firmware[TotalBytes], + sizeof(FWHeader), + sizeof(fwdata->fw_header)); + else + pcb->moal_get_fw_data( + pmadapter->pmoal_handle, TotalBytes, + sizeof(FWHeader), + (t_u8 *)&fwdata->fw_header); + + DataLength = + wlan_le32_to_cpu(fwdata->fw_header.data_length); + DnldCmd = wlan_le32_to_cpu(fwdata->fw_header.dnld_cmd); + TotalBytes += sizeof(FWHeader); + + /** CMD 7 don't have data_length field */ + if (DnldCmd == FW_CMD_4 || DnldCmd == FW_CMD_6 || + DnldCmd == FW_CMD_7 || DnldCmd == FW_CMD_10) + DataLength = 0; + + if (DataLength > + (FW_DNLD_TX_BUF_SIZE - sizeof(FWHeader))) { + PRINTM(MERROR, + "Invalid Data Legth read from FW\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + + /* Copy the firmware data */ + if (firmware) + memcpy_ext(pmadapter, fwdata->data, + &firmware[TotalBytes], DataLength, + DataLength); + else + pcb->moal_get_fw_data(pmadapter->pmoal_handle, + TotalBytes, DataLength, + (t_u8 *)fwdata->data); + + fwdata->seq_num = wlan_cpu_to_le32(FWSeqNum); + TotalBytes += DataLength; + } + /* If the send/receive fails or CRC occurs then retry */ + while (retries) { + mlan_buffer mbuf; + int length = FW_DATA_XMIT_SIZE; + retries--; + + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = (t_u8 *)fwdata; + mbuf.data_len = length; + /* Send the firmware block */ + ret = pcb->moal_write_data_sync( + pmadapter->pmoal_handle, &mbuf, + pmadapter->tx_cmd_ep, + MLAN_USB_BULK_MSG_TIMEOUT); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "fw_dnld: write_data failed, ret %d\n", + ret); + continue; + } + + memset(pmadapter, &mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = RecvBuff; + mbuf.data_len = FW_DNLD_RX_BUF_SIZE; + + /* Receive the firmware block response */ + ret = pcb->moal_read_data_sync( + pmadapter->pmoal_handle, &mbuf, + pmadapter->rx_cmd_ep, + MLAN_USB_BULK_MSG_TIMEOUT); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "fw_dnld: read_data failed, ret %d\n", + ret); + continue; + } + memcpy_ext(pmadapter, &SyncFWHeader, RecvBuff, + sizeof(FWSyncHeader), sizeof(SyncFWHeader)); + endian_convert_syncfwheader(&SyncFWHeader); + /* Check the first firmware block response for highest + * bit set */ + if (check_winner) { + if (SyncFWHeader.cmd & 0x80000000) { + PRINTM(MMSG, + "USB is not the winner 0x%x, returning success\n", + SyncFWHeader.cmd); + ret = MLAN_STATUS_SUCCESS; + goto cleanup; + } + PRINTM(MINFO, + "USB is the winner, start to download FW\n"); + check_winner = 0; + break; + } + + /* Check the firmware block response for CRC errors */ + if (SyncFWHeader.cmd) { + /* Check firmware block response for CRC and MIC + * errors */ + if (check_fw_status) { + if (SyncFWHeader.status & MBIT(0)) { + PRINTM(MERROR, + "FW received Blk with CRC error 0x%x offset=%d\n", + SyncFWHeader.status, + SyncFWHeader.offset); + ret = MLAN_STATUS_FAILURE; + continue; + } + if (SyncFWHeader.status & + (MBIT(6) | MBIT(7))) { + PRINTM(MERROR, + "FW received Blk with MIC error 0x%x offset\n", + SyncFWHeader.status, + SyncFWHeader.offset); + mic_retry--; + FWSeqNum = 0; + TotalBytes = 0; + ret = MLAN_STATUS_FAILURE; + continue; + } + } else { + PRINTM(MERROR, + "FW received Blk with CRC error 0x%x\n", + SyncFWHeader.cmd); + ret = MLAN_STATUS_FAILURE; + continue; + } + } + + retries = MAX_FW_RETRY; + break; + } + + FWSeqNum++; + PRINTM(MINFO, ".\n"); + + } while ((DnldCmd != FW_HAS_LAST_BLOCK) && retries && mic_retry); + +cleanup: + PRINTM(MMSG, "fw_dnld: %d bytes downloaded\n", TotalBytes); + + if (RecvBuff) + pcb->moal_mfree(pmadapter->pmoal_handle, RecvBuff); + if (TxBuff) + pcb->moal_mfree(pmadapter->pmoal_handle, TxBuff); + if (retries && mic_retry) { + ret = MLAN_STATUS_SUCCESS; + } + +fw_exit: + LEAVE(); + return ret; +} + +/** + * @brief Get number of packets when deaggregated + * + * @param pmadapter A pointer to mlan_adapter + * @param pdata A pointer to packet data + * @param aggr_pkt_len Aggregate packet length + * + * @return Number of packets + */ +static int wlan_usb_deaggr_rx_num_pkts(pmlan_adapter pmadapter, t_u8 *pdata, + int aggr_pkt_len) +{ + int pkt_count = 0, pkt_len; + RxPD *prx_pd; + + ENTER(); + while (aggr_pkt_len >= sizeof(RxPD)) { + prx_pd = (RxPD *)pdata; + pkt_len = wlan_le16_to_cpu(prx_pd->rx_pkt_length) + + wlan_le16_to_cpu(prx_pd->rx_pkt_offset); + if (pkt_len == 0) /* blank RxPD can be at the end */ + break; + + ++pkt_count; + if (aggr_pkt_len == pkt_len) /* last packet has no padding */ + break; + + /* skip padding and goto next */ + if (pkt_len % + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_align) + pkt_len += + (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl + .aggr_align - + (pkt_len % pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_align)); + aggr_pkt_len -= pkt_len; + pdata += pkt_len; + } + LEAVE(); + return pkt_count; +} + +static inline t_u32 usb_tx_aggr_pad_len(t_u32 len, + usb_tx_aggr_params *pusb_tx_aggr) +{ + return (len % pusb_tx_aggr->aggr_ctrl.aggr_align) ? + (len + (pusb_tx_aggr->aggr_ctrl.aggr_align - + (len % pusb_tx_aggr->aggr_ctrl.aggr_align))) : + len; +} + +/** + * @brief Copy pmbuf to aggregation buffer + * + * @param pmadapter Pointer to mlan_adapter structure + * @param pmbuf_aggr Pointer to aggregation buffer + * @param pmbuf Pointer to buffer to copy + * @param pusb_tx_aggr Pointer to usb_tx_aggr_params + * + * @return N/A + */ +static inline t_void +wlan_usb_tx_copy_buf_to_aggr(pmlan_adapter pmadapter, pmlan_buffer pmbuf_aggr, + pmlan_buffer pmbuf, + usb_tx_aggr_params *pusb_tx_aggr) +{ + ENTER(); + pmbuf_aggr->data_len = + usb_tx_aggr_pad_len(pmbuf_aggr->data_len, pusb_tx_aggr); + memcpy_ext(pmadapter, + pmbuf_aggr->pbuf + pmbuf_aggr->data_offset + + pmbuf_aggr->data_len, + pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len, + pmbuf->data_len); + pmbuf_aggr->data_len += pmbuf->data_len; + LEAVE(); +} + +#define MLAN_TYPE_AGGR_DATA_V2 11 +/** + * @brief Copy pmbuf to aggregation buffer + * + * @param pmadapter Pointer to mlan_adapter structure + * @param pmbuf_aggr Pointer to aggregation buffer + * @param pmbuf Pointer to buffer to copy + * @param last last packet flag + * @param pusb_tx_aggr Pointer to usb_tx_aggr_params + * + * @return N/A + */ +static inline t_void +wlan_usb_tx_copy_buf_to_aggr_v2(pmlan_adapter pmadapter, + pmlan_buffer pmbuf_aggr, pmlan_buffer pmbuf, + t_u8 last, usb_tx_aggr_params *pusb_tx_aggr) +{ + t_u8 *payload; + t_u16 offset; + + ENTER(); + pmbuf_aggr->data_len = + usb_tx_aggr_pad_len(pmbuf_aggr->data_len, pusb_tx_aggr); + memcpy_ext(pmadapter, + pmbuf_aggr->pbuf + pmbuf_aggr->data_offset + + pmbuf_aggr->data_len, + pmbuf->pbuf + pmbuf->data_offset, pmbuf->data_len, + pmbuf->data_len); + payload = pmbuf_aggr->pbuf + pmbuf_aggr->data_offset + + pmbuf_aggr->data_len; + if (last) { + offset = pmbuf->data_len; + *(t_u16 *)&payload[2] = + wlan_cpu_to_le16(MLAN_TYPE_AGGR_DATA_V2 | 0x80); + } else { + offset = usb_tx_aggr_pad_len(pmbuf->data_len, pusb_tx_aggr); + *(t_u16 *)&payload[2] = + wlan_cpu_to_le16(MLAN_TYPE_AGGR_DATA_V2); + } + *(t_u16 *)&payload[0] = wlan_cpu_to_le16(offset); + pmbuf_aggr->data_len += pmbuf->data_len; + PRINTM(MIF_D, "offset=%d len=%d\n", offset, pmbuf->data_len); + LEAVE(); +} + +/** + * @brief Allocate Aggregation buffer and copy pending buffers to it. + * + * @param pmadapter Pointer to mlan_adapter structure + * @param pusb_tx_aggr Pointer to usb_tx_aggr_params + * + * @return Aggregation buffer + */ +static inline pmlan_buffer +wlan_usb_copy_buf_to_aggr(pmlan_adapter pmadapter, + usb_tx_aggr_params *pusb_tx_aggr) +{ + pmlan_buffer pmbuf_aggr = MNULL; + t_u8 i, use_count; + pmlan_buffer pmbuf_curr, pmbuf_next; + pmbuf_aggr = wlan_alloc_mlan_buffer(pmadapter, pusb_tx_aggr->aggr_len, + 0, MOAL_MALLOC_BUFFER); + if (pmbuf_aggr) { + pmbuf_curr = pusb_tx_aggr->pmbuf_aggr; + pmbuf_aggr->bss_index = pmbuf_curr->bss_index; + pmbuf_aggr->buf_type = pmbuf_curr->buf_type; + pmbuf_aggr->priority = pmbuf_curr->priority; + pmbuf_aggr->data_len = 0; + PRINTM(MIF_D, "use_count=%d,aggr_len=%d\n", + pmbuf_curr->use_count, pusb_tx_aggr->aggr_len); + use_count = pmbuf_curr->use_count; + for (i = 0; i <= use_count; i++) { + pmbuf_next = pmbuf_curr->pnext; + if (pusb_tx_aggr->aggr_ctrl.aggr_mode == + MLAN_USB_AGGR_MODE_LEN_V2) { + if (i == use_count) + wlan_usb_tx_copy_buf_to_aggr_v2( + pmadapter, pmbuf_aggr, + pmbuf_curr, MTRUE, + pusb_tx_aggr); + else + wlan_usb_tx_copy_buf_to_aggr_v2( + pmadapter, pmbuf_aggr, + pmbuf_curr, MFALSE, + pusb_tx_aggr); + } else + wlan_usb_tx_copy_buf_to_aggr(pmadapter, + pmbuf_aggr, + pmbuf_curr, + pusb_tx_aggr); + pmbuf_curr = pmbuf_next; + } + DBG_HEXDUMP(MIF_D, "USB AggrTx", + pmbuf_aggr->pbuf + pmbuf_aggr->data_offset, + pmbuf_aggr->data_len); + } + return pmbuf_aggr; +} + +/** + * @brief Link buffer into aggregate head buffer + * + * @param pmbuf_aggr Pointer to aggregation buffer + * @param pmbuf Pointer to buffer to add to the buffer list + * @param pusb_tx_aggr Pointer to usb_tx_aggr_params + */ +static inline t_void +wlan_usb_tx_link_buf_to_aggr(pmlan_buffer pmbuf_aggr, pmlan_buffer pmbuf, + usb_tx_aggr_params *pusb_tx_aggr) +{ + /* link new buf at end of list */ + pmbuf->pnext = pmbuf_aggr; + pmbuf->pprev = pmbuf_aggr->pprev; + pmbuf->pparent = pmbuf_aggr; + pmbuf_aggr->pprev->pnext = pmbuf; + pmbuf_aggr->pprev = pmbuf; + pmbuf_aggr->use_count++; + pusb_tx_aggr->aggr_len = + usb_tx_aggr_pad_len(pusb_tx_aggr->aggr_len, pusb_tx_aggr); + pusb_tx_aggr->aggr_len += pmbuf->data_len; +} + +/** + * @brief Send aggregated buffer + * + * @param pmadapter Pointer to mlan_adapter structure + * @param pusb_tx_aggr Pointer to usb_tx_aggr_params + */ +static inline t_void wlan_usb_tx_send_aggr(pmlan_adapter pmadapter, + usb_tx_aggr_params *pusb_tx_aggr) +{ + mlan_status ret; + pmlan_buffer pmbuf_aggr = pusb_tx_aggr->pmbuf_aggr; + ENTER(); + if (!pusb_tx_aggr->pmbuf_aggr) { + LEAVE(); + return; + } + + if (pusb_tx_aggr->pmbuf_aggr->use_count) { + pmbuf_aggr = wlan_usb_copy_buf_to_aggr(pmadapter, pusb_tx_aggr); + /* allocate new buffer for aggregation if not exist */ + if (!pmbuf_aggr) { + PRINTM(MERROR, + "Error allocating [usb_tx] aggr mlan_buffer.\n"); + pmadapter->dbg.num_tx_host_to_card_failure += + pusb_tx_aggr->pmbuf_aggr->use_count; + wlan_write_data_complete(pmadapter, + pusb_tx_aggr->pmbuf_aggr, + MLAN_STATUS_FAILURE); + pusb_tx_aggr->pmbuf_aggr = MNULL; + pusb_tx_aggr->aggr_len = 0; + LEAVE(); + return; + } else { + wlan_write_data_complete(pmadapter, + pusb_tx_aggr->pmbuf_aggr, + MLAN_STATUS_SUCCESS); + pusb_tx_aggr->pmbuf_aggr = MNULL; + pusb_tx_aggr->aggr_len = 0; + } + } else if (pusb_tx_aggr->aggr_ctrl.aggr_mode == + MLAN_USB_AGGR_MODE_LEN_V2) { + t_u8 *payload = pmbuf_aggr->pbuf + pmbuf_aggr->data_offset; + *(t_u16 *)&payload[0] = wlan_cpu_to_le16(pmbuf_aggr->data_len); + *(t_u16 *)&payload[2] = + wlan_cpu_to_le16(MLAN_TYPE_AGGR_DATA_V2 | 0x80); + PRINTM(MIF_D, "USB Send single packet len=%d\n", + pmbuf_aggr->data_len); + DBG_HEXDUMP(MIF_D, "USB Tx", + pmbuf_aggr->pbuf + pmbuf_aggr->data_offset, + pmbuf_aggr->data_len); + } + + if (pmbuf_aggr && pmbuf_aggr->data_len) { + pmadapter->data_sent = MTRUE; + ret = pmadapter->callbacks.moal_write_data_async( + pmadapter->pmoal_handle, pmbuf_aggr, + pusb_tx_aggr->port); + switch (ret) { + case MLAN_STATUS_PRESOURCE: + PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n"); + break; + case MLAN_STATUS_RESOURCE: + /* Shouldn't reach here due to next condition. */ + /* TODO: (maybe) How to requeue the aggregate? */ + /* It may occur when the pending tx urbs reach the high + * mark */ + /* Thus, block further pkts for a bit */ + PRINTM(MERROR, + "Error: moal_write_data_async failed: 0x%X\n", + ret); + pmadapter->dbg.num_tx_host_to_card_failure++; + pmbuf_aggr->status_code = MLAN_ERROR_DATA_TX_FAIL; + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + break; + case MLAN_STATUS_FAILURE: + pmadapter->data_sent = MFALSE; + PRINTM(MERROR, + "Error: moal_write_data_async failed: 0x%X\n", + ret); + pmadapter->dbg.num_tx_host_to_card_failure++; + pmbuf_aggr->status_code = MLAN_ERROR_DATA_TX_FAIL; + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + break; + case MLAN_STATUS_PENDING: + pmadapter->data_sent = MFALSE; + break; + case MLAN_STATUS_SUCCESS: + wlan_write_data_complete(pmadapter, pmbuf_aggr, ret); + break; + default: + break; + } + + /* aggr_buf now sent to bus, prevent others from using it */ + pusb_tx_aggr->pmbuf_aggr = MNULL; + pusb_tx_aggr->aggr_len = 0; + } + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function get pcie device from card type + * + * @param pmadapter A pointer to mlan_adapter structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_get_usb_device(pmlan_adapter pmadapter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u16 card_type = pmadapter->card_type; + + ENTER(); + + ret = pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(mlan_usb_card), + MLAN_MEM_DEF, + (t_u8 **)&pmadapter->pcard_usb); + if (ret != MLAN_STATUS_SUCCESS || !pmadapter->pcard_usb) { + PRINTM(MERROR, "Failed to allocate pcard_usb\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + switch (card_type) { +#ifdef USB8897 + case CARD_TYPE_USB8897: + pmadapter->pcard_info = &mlan_card_info_usb8897; + break; +#endif +#ifdef USB8997 + case CARD_TYPE_USB8997: + pmadapter->pcard_info = &mlan_card_info_usb8997; + break; +#endif +#ifdef USB8978 + case CARD_TYPE_USB8978: + pmadapter->pcard_info = &mlan_card_info_usb8978; + break; +#endif +#ifdef USB9098 + case CARD_TYPE_USB9098: + pmadapter->pcard_info = &mlan_card_info_usb9098; + break; +#endif +#ifdef USB9097 + case CARD_TYPE_USB9097: + pmadapter->pcard_info = &mlan_card_info_usb9097; + break; +#endif + default: + PRINTM(MERROR, "can't get right USB card type \n"); + ret = MLAN_STATUS_FAILURE; + break; + } + + 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_usb_dnld_fw(pmlan_adapter pmadapter, pmlan_fw_image pmfw) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = wlan_usb_prog_fw_w_helper(pmadapter, pmfw); + if (ret != MLAN_STATUS_SUCCESS) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function deaggregates USB RX Data Packet from device + * + * @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_usb_deaggr_rx_pkt(pmlan_adapter pmadapter, pmlan_buffer pmbuf) +{ + const t_u8 zero_rx_pd[sizeof(RxPD)] = {0}; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 curr_pkt_len; + RxPD *prx_pd; + t_u8 *pdata; + t_s32 aggr_len; + pmlan_buffer pdeaggr_buf; + + ENTER(); + + pdata = pmbuf->pbuf + pmbuf->data_offset; + prx_pd = (RxPD *)pdata; + curr_pkt_len = wlan_le16_to_cpu(prx_pd->rx_pkt_length) + + wlan_le16_to_cpu(prx_pd->rx_pkt_offset); + /* if non-aggregate, just send through, don’t process here */ + aggr_len = pmbuf->data_len; + if ((aggr_len == curr_pkt_len) || + (wlan_usb_deaggr_rx_num_pkts(pmadapter, pdata, aggr_len) == 1) || + (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable != MTRUE)) { + ret = wlan_handle_rx_packet(pmadapter, pmbuf); + LEAVE(); + return ret; + } + + while (aggr_len >= sizeof(RxPD)) { + /* check for (all-zeroes) termination RxPD */ + if (!memcmp(pmadapter, pdata, zero_rx_pd, sizeof(RxPD))) { + break; + } + + /* make new buffer and copy packet to it (including RxPD). + * Also, reserve headroom so that there must have space + * to change RxPD to TxPD for bridge packet in uAP mode */ + pdeaggr_buf = wlan_alloc_mlan_buffer(pmadapter, curr_pkt_len, + MLAN_RX_HEADER_LEN, + MOAL_ALLOC_MLAN_BUFFER); + if (pdeaggr_buf == MNULL) { + PRINTM(MERROR, + "Error allocating [usb_rx] deaggr mlan_buffer\n"); + ret = MLAN_STATUS_FAILURE; + break; + } + pdeaggr_buf->bss_index = pmbuf->bss_index; + pdeaggr_buf->buf_type = pmbuf->buf_type; + pdeaggr_buf->data_len = curr_pkt_len; + pdeaggr_buf->in_ts_sec = pmbuf->in_ts_sec; + pdeaggr_buf->in_ts_usec = pmbuf->in_ts_usec; + pdeaggr_buf->priority = pmbuf->priority; + memcpy_ext(pmadapter, + pdeaggr_buf->pbuf + pdeaggr_buf->data_offset, pdata, + curr_pkt_len, pdeaggr_buf->data_len); + + /* send new packet to processing */ + ret = wlan_handle_rx_packet(pmadapter, pdeaggr_buf); + if (ret == MLAN_STATUS_FAILURE) { + break; + } + /* last block has no padding bytes */ + if (aggr_len == curr_pkt_len) { + break; + } + + /* round up to next block boundary */ + if (curr_pkt_len % + pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.aggr_align) + curr_pkt_len += (pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_align - + (curr_pkt_len % + pmadapter->pcard_usb->usb_rx_deaggr + .aggr_ctrl.aggr_align)); + /* point to next packet */ + aggr_len -= curr_pkt_len; + pdata += curr_pkt_len; + prx_pd = (RxPD *)pdata; + curr_pkt_len = wlan_le16_to_cpu(prx_pd->rx_pkt_length) + + wlan_le16_to_cpu(prx_pd->rx_pkt_offset); + } + + /* free original pmbuf (since not sent for processing) */ + pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, pmbuf, + pmadapter->rx_data_ep, ret); + LEAVE(); + return ret; +} + +/** + * @brief This function restore tx_pause flag + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pusb_tx_aggr A pointer to usb_tx_aggr_params + * + * @return MTRUE/MFALSE + */ +t_u8 wlan_is_port_tx_paused(pmlan_adapter pmadapter, + usb_tx_aggr_params *pusb_tx_aggr) +{ + mlan_private *pmpriv = MNULL; + t_u8 i; + t_u8 ret = MFALSE; + for (i = 0; i < pmadapter->priv_num; i++) { + pmpriv = pmadapter->priv[i]; + if (pmpriv && pmpriv->tx_pause && + (pmpriv->port == pusb_tx_aggr->port)) { + ret = MTRUE; + break; + } + } + return ret; +} + +/** + * @brief This function handles the timeout of usb tx aggregation. + * It will send the aggregate buffer being held. + * + * @param function_context A pointer to function_context + * @return N/A + */ +t_void wlan_usb_tx_aggr_timeout_func(t_void *function_context) +{ + usb_tx_aggr_params *pusb_tx_aggr = + (usb_tx_aggr_params *)function_context; + pmlan_adapter pmadapter = (mlan_adapter *)pusb_tx_aggr->phandle; + pmlan_callbacks pcb = &pmadapter->callbacks; + + ENTER(); + pcb->moal_spin_lock(pmadapter->pmoal_handle, pusb_tx_aggr->paggr_lock); + pusb_tx_aggr->aggr_hold_timer_is_set = MFALSE; + if (pusb_tx_aggr->pmbuf_aggr && !pmadapter->data_sent && + !wlan_is_port_tx_paused(pmadapter, pusb_tx_aggr)) + wlan_usb_tx_send_aggr(pmadapter, pusb_tx_aggr); + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pusb_tx_aggr->paggr_lock); + LEAVE(); +} + +/** + * @brief This function aggregates USB TX Data Packet to send to device + * + * @param pmadapter A pointer to mlan_adapter + * @param pmbuf A pointer to the transmit buffer + * @param tx_param A pointer to mlan_tx_param + * @param pusb_tx_aggr A pointer to usb_tx_aggr_params + * + * @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE + */ +/* + * Non Scatter-Gather code creates a new large buffer where each incoming + * buffer's data contents are copied to (aligned to USB boundaries). + * The individual buffers are ALSO linked to the large buffer, + * in order to handle complete AFTER the aggregate is sent. + * pmbuf_aggr->data_len is used to keep track of bytes aggregated so far. + */ +mlan_status wlan_usb_host_to_card_aggr(pmlan_adapter pmadapter, + pmlan_buffer pmbuf, + mlan_tx_param *tx_param, + usb_tx_aggr_params *pusb_tx_aggr) +{ + pmlan_callbacks pcb = &pmadapter->callbacks; + pmlan_buffer pmbuf_aggr; + mlan_status ret = MLAN_STATUS_PENDING; + t_u32 next_pkt_len = (tx_param) ? tx_param->next_pkt_len : 0; + t_u32 aggr_len_counter = 0; + /* indicators */ + t_u8 f_precopy_cur_buf = 0; + t_u8 f_send_aggr_buf = 0; + t_u8 f_postcopy_cur_buf = 0; + t_u32 max_aggr_size = 0, max_aggr_num = 0; + + ENTER(); + + pcb->moal_spin_lock(pmadapter->pmoal_handle, pusb_tx_aggr->paggr_lock); + + /* stop timer while we process */ + if (pusb_tx_aggr->aggr_hold_timer_is_set) { + pcb->moal_stop_timer(pmadapter->pmoal_handle, + pusb_tx_aggr->paggr_hold_timer); + pusb_tx_aggr->aggr_hold_timer_is_set = MFALSE; + } + + pmbuf_aggr = pusb_tx_aggr->pmbuf_aggr; + + if (pusb_tx_aggr->aggr_ctrl.aggr_tmo == MLAN_USB_TX_AGGR_TIMEOUT_DYN) { + if (!pmbuf_aggr) { + /* Start aggr from min timeout value in micro sec */ + pusb_tx_aggr->hold_timeout_msec = + MLAN_USB_TX_MIN_AGGR_TIMEOUT; + } else { + /* Increase timeout in milisecond if pkts are + * consecutive */ + if (pusb_tx_aggr->hold_timeout_msec < + MLAN_USB_TX_MAX_AGGR_TIMEOUT) + pusb_tx_aggr->hold_timeout_msec++; + } + } else { + if (pusb_tx_aggr->aggr_ctrl.aggr_tmo) + pusb_tx_aggr->hold_timeout_msec = + pusb_tx_aggr->aggr_ctrl.aggr_tmo / 1000; + } + + max_aggr_size = max_aggr_num = pusb_tx_aggr->aggr_ctrl.aggr_max; + if (pusb_tx_aggr->aggr_ctrl.aggr_mode == MLAN_USB_AGGR_MODE_NUM) { + max_aggr_size *= MAX(MLAN_USB_MAX_PKT_SIZE, + pusb_tx_aggr->aggr_ctrl.aggr_align); + } + if (pusb_tx_aggr->aggr_ctrl.aggr_mode == MLAN_USB_AGGR_MODE_LEN) + max_aggr_num /= pusb_tx_aggr->aggr_ctrl.aggr_align; + else if (pusb_tx_aggr->aggr_ctrl.aggr_mode == MLAN_USB_AGGR_MODE_LEN_V2) + max_aggr_num = MLAN_USB_TX_AGGR_MAX_NUM; + if (!pmbuf_aggr) { + /* use this buf to start linked list, that's it */ + pmbuf->pnext = pmbuf->pprev = pmbuf; + pmbuf_aggr = pmbuf; + pusb_tx_aggr->pmbuf_aggr = pmbuf_aggr; + pusb_tx_aggr->aggr_len = pmbuf->data_len; + pmbuf->flags |= MLAN_BUF_FLAG_USB_TX_AGGR; + + } else { + /* DECIDE what to do */ + aggr_len_counter = usb_tx_aggr_pad_len(pusb_tx_aggr->aggr_len, + pusb_tx_aggr); + + if ((aggr_len_counter + pmbuf->data_len) < max_aggr_size) { + f_precopy_cur_buf = 1; /* can fit current packet in aggr + */ + if (next_pkt_len) { + aggr_len_counter += usb_tx_aggr_pad_len( + pmbuf->data_len, pusb_tx_aggr); + if ((aggr_len_counter + next_pkt_len) >= + max_aggr_size) + f_send_aggr_buf = 1; /* can't fit next + packet, send now + */ + } + } else { + /* can't fit current packet */ + if (pusb_tx_aggr->aggr_len) + f_send_aggr_buf = 1; /* send aggr first */ + f_postcopy_cur_buf = 1; /* then copy into new aggr_buf + */ + } + } + + /* For zero timeout and zero next packet length send pkt now */ + if (!pusb_tx_aggr->aggr_ctrl.aggr_tmo && !next_pkt_len) + f_send_aggr_buf = 1; + + /* PERFORM ACTIONS as decided */ + if (f_precopy_cur_buf) { + PRINTM(MIF_D, "%s: Precopy current buffer.\n", __FUNCTION__); + wlan_usb_tx_link_buf_to_aggr(pmbuf_aggr, pmbuf, pusb_tx_aggr); + } + if (pmbuf_aggr->use_count + 1 >= max_aggr_num) + f_send_aggr_buf = 1; + + if (pmbuf->flags & MLAN_BUF_FLAG_NULL_PKT || + pmbuf->flags & MLAN_BUF_FLAG_TCP_ACK) + f_send_aggr_buf = 1; + + if (f_send_aggr_buf) { + PRINTM(MIF_D, "%s: Send aggregate buffer.\n", __FUNCTION__); + wlan_usb_tx_send_aggr(pmadapter, pusb_tx_aggr); + pmbuf_aggr = pusb_tx_aggr->pmbuf_aggr; /* update ptr */ + } + + if (f_postcopy_cur_buf) { + PRINTM(MIF_D, "%s: Postcopy current buffer.\n", __FUNCTION__); + if (!pmbuf_aggr) { /* this is possible if just sent (above) */ + /* use this buf to start linked list */ + pmbuf->pnext = pmbuf->pprev = pmbuf; + pmbuf_aggr = pmbuf; + pusb_tx_aggr->pmbuf_aggr = pmbuf_aggr; + pusb_tx_aggr->aggr_len = pmbuf->data_len; + pmbuf->flags |= MLAN_BUF_FLAG_USB_TX_AGGR; + } + } + /* (re)start timer if there is something in the aggregation buffer */ + if (pmbuf_aggr && pmbuf_aggr->data_len) { + if (pusb_tx_aggr->aggr_ctrl.aggr_tmo) { + pcb->moal_start_timer(pmadapter->pmoal_handle, + pusb_tx_aggr->paggr_hold_timer, + MFALSE, + pusb_tx_aggr->hold_timeout_msec); + pusb_tx_aggr->aggr_hold_timer_is_set = MTRUE; + } + } + + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + pusb_tx_aggr->paggr_lock); + LEAVE(); + return ret; +} + +/** + * @brief This function wakes up the card. + * + * @param pmadapter A pointer to mlan_adapter structure + * @param timeout set timeout flag + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_pm_usb_wakeup_card(pmlan_adapter pmadapter, t_u8 timeout) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 age_ts_usec; + + ENTER(); + PRINTM(MEVENT, "Wakeup device...\n"); + pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, + &pmadapter->pm_wakeup_in_secs, + &age_ts_usec); + + /* Simulation of HS_AWAKE event */ + pmadapter->pm_wakeup_fw_try = MFALSE; + pmadapter->pm_wakeup_card_req = MFALSE; + /* TODO USB suspend/resume */ + pmadapter->ps_state = PS_STATE_AWAKE; + LEAVE(); + return ret; +} + +/** + * @brief This function downloads data from driver to card. + * + * Both commands and data packets are transferred to the card + * by this function. This function adds the PCIE specific header + * to the front of the buffer before transferring. The header + * contains the length of the packet and the type. The firmware + * handles the packets based upon this set type. + * + * @param pmpriv A pointer to pmlan_private structure + * @param type data or command + * @param pmbuf A pointer to mlan_buffer (pmbuf->data_len should include + * PCIE header) + * @param tx_param A pointer to mlan_tx_param (can be MNULL if type is + * command) + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status wlan_usb_host_to_card(pmlan_private pmpriv, t_u8 type, + mlan_buffer *pmbuf, mlan_tx_param *tx_param) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + usb_tx_aggr_params *pusb_tx_aggr = MNULL; + pmlan_adapter pmadapter = pmpriv->adapter; + + ENTER(); + + if (!pmbuf) { + PRINTM(MERROR, "Passed NULL pmbuf to %s\n", __FUNCTION__); + return MLAN_STATUS_FAILURE; + } + if (type == MLAN_TYPE_CMD +#if (defined(USB9098) || defined(USB9097)) + || type == MLAN_TYPE_VDLL +#endif + ) { + pmadapter->cmd_sent = MTRUE; + ret = pmadapter->callbacks.moal_write_data_async( + pmadapter->pmoal_handle, pmbuf, pmadapter->tx_cmd_ep); + if (ret == MLAN_STATUS_FAILURE) + pmadapter->cmd_sent = MFALSE; + LEAVE(); + return ret; + } + pusb_tx_aggr = wlan_get_usb_tx_aggr_params(pmadapter, pmpriv->port); + if (pusb_tx_aggr) { + ret = wlan_usb_host_to_card_aggr(pmadapter, pmbuf, tx_param, + pusb_tx_aggr); + } else { + pmadapter->data_sent = MTRUE; + ret = pmadapter->callbacks.moal_write_data_async( + pmadapter->pmoal_handle, pmbuf, pmpriv->port); + switch (ret) { + case MLAN_STATUS_PRESOURCE: + PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n"); + break; + case MLAN_STATUS_RESOURCE: + + break; + case MLAN_STATUS_FAILURE: + pmadapter->data_sent = MFALSE; + break; + case MLAN_STATUS_PENDING: + pmadapter->data_sent = MFALSE; + break; + case MLAN_STATUS_SUCCESS: + break; + default: + break; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief This function handle event/cmd complete + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the mlan_buffer + * @return N/A + */ +mlan_status wlan_usb_cmdevt_complete(pmlan_adapter pmadapter, + mlan_buffer *pmbuf, mlan_status status) +{ + ENTER(); + + pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, pmbuf, + pmadapter->rx_cmd_ep, status); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handle data complete + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the mlan_buffer + * @return N/A + */ +mlan_status wlan_usb_data_complete(pmlan_adapter pmadapter, mlan_buffer *pmbuf, + mlan_status status) +{ + ENTER(); + + pmadapter->callbacks.moal_recv_complete(pmadapter->pmoal_handle, pmbuf, + pmadapter->rx_data_ep, status); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handle receive packet + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pmbuf A pointer to the mlan_buffer + * @return + */ +mlan_status wlan_usb_handle_rx_packet(mlan_adapter *pmadapter, + pmlan_buffer pmbuf) +{ + ENTER(); + + if (pmadapter->pcard_usb->usb_rx_deaggr.aggr_ctrl.enable == MTRUE) + return wlan_usb_deaggr_rx_pkt(pmadapter, pmbuf); + else + return wlan_handle_rx_packet(pmadapter, pmbuf); + + LEAVE(); +} + +mlan_adapter_operations mlan_usb_ops = { + .dnld_fw = wlan_usb_dnld_fw, + .host_to_card = wlan_usb_host_to_card, + .wakeup_card = wlan_pm_usb_wakeup_card, + .event_complete = wlan_usb_cmdevt_complete, + .data_complete = wlan_usb_data_complete, + .cmdrsp_complete = wlan_usb_cmdevt_complete, + .handle_rx_packet = wlan_usb_handle_rx_packet, + + .intf_header_len = USB_INTF_HEADER_LEN, +}; diff --git a/mxm_wifiex/wlan_src/mlan/mlan_util.h b/mxm_wifiex/wlan_src/mlan/mlan_util.h new file mode 100644 index 0000000..c86269e --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_util.h @@ -0,0 +1,492 @@ +/** @file mlan_util.h + * + * @brief This file contains wrappers for linked-list, + * spinlock and timer defines. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/****************************************************** +Change log: + 10/28/2008: initial version +******************************************************/ + +#ifndef _MLAN_UTIL_H_ +#define _MLAN_UTIL_H_ + +/** Circular doubly linked list */ +typedef struct _mlan_linked_list { + /** Pointer to previous node */ + struct _mlan_linked_list *pprev; + /** Pointer to next node */ + struct _mlan_linked_list *pnext; +} mlan_linked_list, *pmlan_linked_list; + +/** List head */ +typedef struct _mlan_list_head { + /** Pointer to previous node */ + struct _mlan_linked_list *pprev; + /** Pointer to next node */ + struct _mlan_linked_list *pnext; + /** Pointer to lock */ + t_void *plock; +} mlan_list_head, *pmlan_list_head; + +/** + * @brief This function initializes a list without locking + * + * @param phead List head + * + * @return N/A + */ +static INLINE t_void util_init_list(pmlan_linked_list phead) +{ + /* Both next and prev point to self */ + phead->pprev = phead->pnext = (pmlan_linked_list)phead; +} + +/** + * @brief This function initializes a list + * + * @param phead List head + * @param lock_required A flag for spinlock requirement + * @param moal_init_lock A pointer to init lock handler + * + * @return N/A + */ +static INLINE t_void util_init_list_head( + t_void *pmoal_handle, pmlan_list_head phead, t_u8 lock_required, + mlan_status (*moal_init_lock)(t_void *handle, t_void **pplock)) +{ + /* Both next and prev point to self */ + util_init_list((pmlan_linked_list)phead); + if (lock_required) + moal_init_lock(pmoal_handle, &phead->plock); + else + phead->plock = 0; +} + +/** + * @brief This function frees a list + * + * @param phead List head + * @param moal_free_lock A pointer to free lock handler + * + * @return N/A + */ +static INLINE t_void util_free_list_head( + t_void *pmoal_handle, pmlan_list_head phead, + mlan_status (*moal_free_lock)(t_void *handle, t_void *plock)) +{ + phead->pprev = phead->pnext = 0; + if (phead->plock) + moal_free_lock(pmoal_handle, phead->plock); +} + +/** + * @brief This function peeks into a list + * + * @param phead List head + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return List node + */ +static INLINE pmlan_linked_list +util_peek_list(t_void *pmoal_handle, pmlan_list_head phead, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + pmlan_linked_list pnode = 0; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + if (phead->pnext != (pmlan_linked_list)phead) + pnode = phead->pnext; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); + return pnode; +} + +/** + * @brief This function queues a node at the list tail + * + * @param phead List head + * @param pnode List node to queue + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void util_enqueue_list_tail( + t_void *pmoal_handle, pmlan_list_head phead, pmlan_linked_list pnode, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + pmlan_linked_list pold_last; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + pold_last = phead->pprev; + pnode->pprev = pold_last; + pnode->pnext = (pmlan_linked_list)phead; + + phead->pprev = pold_last->pnext = pnode; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); +} + +/** + * @brief This function adds a node at the list head + * + * @param phead List head + * @param pnode List node to add + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void util_enqueue_list_head( + t_void *pmoal_handle, pmlan_list_head phead, pmlan_linked_list pnode, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + pmlan_linked_list pold_first; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + pold_first = phead->pnext; + pnode->pprev = (pmlan_linked_list)phead; + pnode->pnext = pold_first; + + phead->pnext = pold_first->pprev = pnode; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); +} + +/** + * @brief This function removes a node from the list + * + * @param phead List head + * @param pnode List node to remove + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void util_unlink_list( + t_void *pmoal_handle, pmlan_list_head phead, pmlan_linked_list pnode, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + pmlan_linked_list pmy_prev; + pmlan_linked_list pmy_next; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + pmy_prev = pnode->pprev; + pmy_next = pnode->pnext; + pmy_next->pprev = pmy_prev; + pmy_prev->pnext = pmy_next; + + pnode->pnext = pnode->pprev = 0; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); +} + +/** + * @brief This function dequeues a node from the list + * + * @param phead List head + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return List node + */ +static INLINE pmlan_linked_list util_dequeue_list( + t_void *pmoal_handle, pmlan_list_head phead, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + pmlan_linked_list pnode; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, phead->plock); + pnode = phead->pnext; + if (pnode && (pnode != (pmlan_linked_list)phead)) + util_unlink_list(pmoal_handle, phead, pnode, 0, 0); + else + pnode = 0; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, phead->plock); + return pnode; +} + +/** Access controlled scalar variable */ +typedef struct _mlan_scalar { + /** Value */ + t_s32 value; + /** Pointer to lock */ + t_void *plock; + /** Control flags */ + t_u32 flags; +} mlan_scalar, *pmlan_scalar; + +/** Flag to scalar lock acquired */ +#define MLAN_SCALAR_FLAG_UNIQUE_LOCK MBIT(16) + +/** scalar conditional value list */ +typedef enum _MLAN_SCALAR_CONDITIONAL { + MLAN_SCALAR_COND_EQUAL, + MLAN_SCALAR_COND_NOT_EQUAL, + MLAN_SCALAR_COND_GREATER_THAN, + MLAN_SCALAR_COND_GREATER_OR_EQUAL, + MLAN_SCALAR_COND_LESS_THAN, + MLAN_SCALAR_COND_LESS_OR_EQUAL +} MLAN_SCALAR_CONDITIONAL; + +/** + * @brief This function initializes a scalar + * + * @param pscalar Pointer to scalar + * @param val Initial scalar value + * @param plock_to_use A new lock is created if NULL, else lock to use + * @param moal_init_lock A pointer to init lock handler + * + * @return N/A + */ +static INLINE t_void +util_scalar_init(t_void *pmoal_handle, pmlan_scalar pscalar, t_s32 val, + t_void *plock_to_use, + mlan_status (*moal_init_lock)(t_void *handle, t_void **pplock)) +{ + pscalar->value = val; + pscalar->flags = 0; + if (plock_to_use) { + pscalar->flags &= ~MLAN_SCALAR_FLAG_UNIQUE_LOCK; + pscalar->plock = plock_to_use; + } else { + pscalar->flags |= MLAN_SCALAR_FLAG_UNIQUE_LOCK; + moal_init_lock(pmoal_handle, &pscalar->plock); + } +} + +/** + * @brief This function frees a scalar + * + * @param pscalar Pointer to scalar + * @param moal_free_lock A pointer to free lock handler + * + * @return N/A + */ +static INLINE t_void +util_scalar_free(t_void *pmoal_handle, pmlan_scalar pscalar, + mlan_status (*moal_free_lock)(t_void *handle, t_void *plock)) +{ + if (pscalar->flags & MLAN_SCALAR_FLAG_UNIQUE_LOCK) + moal_free_lock(pmoal_handle, pscalar->plock); +} + +/** + * @brief This function reads value from scalar + * + * @param pscalar Pointer to scalar + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return Stored value + */ +static INLINE t_s32 +util_scalar_read(t_void *pmoal_handle, pmlan_scalar pscalar, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + t_s32 val; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + val = pscalar->value; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); + + return val; +} + +/** + * @brief This function writes value to scalar + * + * @param pscalar Pointer to scalar + * @param val Value to write + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void util_scalar_write( + t_void *pmoal_handle, pmlan_scalar pscalar, t_s32 val, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + pscalar->value = val; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); +} + +/** + * @brief This function increments the value in scalar + * + * @param pscalar Pointer to scalar + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void util_scalar_increment( + t_void *pmoal_handle, pmlan_scalar pscalar, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + pscalar->value++; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); +} + +/** + * @brief This function decrements the value in scalar + * + * @param pscalar Pointer to scalar + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return N/A + */ +static INLINE t_void util_scalar_decrement( + t_void *pmoal_handle, pmlan_scalar pscalar, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + pscalar->value--; + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); +} + +/** + * @brief This function adds an offset to the value in scalar, + * and returns the new value + * + * @param pscalar Pointer to scalar + * @param offset Offset value (can be negative) + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return Value after offset + */ +static INLINE t_s32 util_scalar_offset( + t_void *pmoal_handle, pmlan_scalar pscalar, t_s32 offset, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + t_s32 newval; + + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + newval = (pscalar->value += offset); + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); + + return newval; +} + +/** + * @brief This function writes the value to the scalar + * if existing value compared with other value is true. + * + * @param pscalar Pointer to scalar + * @param condition Condition to check + * @param val_compare Value to compare against current value + * ((A X B), where B = val_compare) + * @param val_to_set Value to set if comparison is true + * @param moal_spin_lock A pointer to spin lock handler + * @param moal_spin_unlock A pointer to spin unlock handler + * + * @return Comparison result (MTRUE or MFALSE) + */ +static INLINE t_u8 util_scalar_conditional_write( + t_void *pmoal_handle, pmlan_scalar pscalar, + MLAN_SCALAR_CONDITIONAL condition, t_s32 val_compare, t_s32 val_to_set, + mlan_status (*moal_spin_lock)(t_void *handle, t_void *plock), + mlan_status (*moal_spin_unlock)(t_void *handle, t_void *plock)) +{ + t_u8 update; + if (moal_spin_lock) + moal_spin_lock(pmoal_handle, pscalar->plock); + + switch (condition) { + case MLAN_SCALAR_COND_EQUAL: + update = (pscalar->value == val_compare); + break; + case MLAN_SCALAR_COND_NOT_EQUAL: + update = (pscalar->value != val_compare); + break; + case MLAN_SCALAR_COND_GREATER_THAN: + update = (pscalar->value > val_compare); + break; + case MLAN_SCALAR_COND_GREATER_OR_EQUAL: + update = (pscalar->value >= val_compare); + break; + case MLAN_SCALAR_COND_LESS_THAN: + update = (pscalar->value < val_compare); + break; + case MLAN_SCALAR_COND_LESS_OR_EQUAL: + update = (pscalar->value <= val_compare); + break; + default: + update = MFALSE; + break; + } + if (update) + pscalar->value = val_to_set; + + if (moal_spin_unlock) + moal_spin_unlock(pmoal_handle, pscalar->plock); + return (update) ? MTRUE : MFALSE; +} + +/** + * @brief This function counts the bits of unsigned int number + * + * @param num number + * @return number of bits + */ +static INLINE t_u32 bitcount(t_u32 num) +{ + t_u32 count = 0; + static t_u32 nibblebits[] = {0, 1, 1, 2, 1, 2, 2, 3, + 1, 2, 2, 3, 2, 3, 3, 4}; + for (; num != 0; num >>= 4) + count += nibblebits[num & 0x0f]; + return count; +} + +#endif /* !_MLAN_UTIL_H_ */ diff --git a/mxm_wifiex/wlan_src/mlan/mlan_wmm.c b/mxm_wifiex/wlan_src/mlan/mlan_wmm.c new file mode 100644 index 0000000..f32a166 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_wmm.c @@ -0,0 +1,3282 @@ +/** @file mlan_wmm.c + * + * @brief This file contains functions for WMM. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" +#ifdef SDIO +#include "mlan_sdio.h" +#endif /* SDIO */ +#ifdef PCIE +#include "mlan_pcie.h" +#endif /* PCIE */ + +/******************************************************** + Local Variables +********************************************************/ + +/** Maximum value FW can accept for driver delay in packet transmission */ +#define DRV_PKT_DELAY_TO_FW_MAX 512 + +/* + * Upper and Lower threshold for packet queuing in the driver + + * - When the number of packets queued reaches the upper limit, + * the driver will stop the net queue in the app/kernel space. + + * - When the number of packets drops beneath the lower limit after + * having reached the upper limit, the driver will restart the net + * queue. + */ + +/** Lower threshold for packet queuing in the driver. + * When the number of packets drops beneath the lower limit after having + * reached the upper limit, the driver will restart the net queue. + */ +#define WMM_QUEUED_PACKET_LOWER_LIMIT 180 + +/** Upper threshold for packet queuing in the driver. + * When the number of packets queued reaches the upper limit, the driver + * will stop the net queue in the app/kernel space. + */ +#define WMM_QUEUED_PACKET_UPPER_LIMIT 200 + +/** Offset for TOS field in the IP header */ +#define IPTOS_OFFSET 5 + +/** WMM information IE */ +static const t_u8 wmm_info_ie[] = {WMM_IE, 0x07, 0x00, 0x50, 0xf2, + 0x02, 0x00, 0x01, 0x00}; + +/** Type enumeration of WMM AC_QUEUES */ +typedef MLAN_PACK_START enum _wmm_ac_e { + AC_BE, + AC_BK, + AC_VI, + AC_VO +} MLAN_PACK_END wmm_ac_e; + +/** + * AC Priorities go from AC_BK to AC_VO. The ACI enumeration for AC_BK (1) + * is higher than the enumeration for AC_BE (0); hence the needed + * mapping conversion for wmm AC to priority Queue Index + */ +static const t_u8 wmm_aci_to_qidx_map[] = {WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, + WMM_AC_VO}; +/** + * This table will be used to store the tid values based on ACs. + * It is initialized to default values per TID. + */ +t_u8 tos_to_tid[] = { + /* TID DSCP_P2 DSCP_P1 DSCP_P0 WMM_AC */ + 0x01, /* 0 1 0 AC_BK */ + 0x02, /* 0 0 0 AC_BK */ + 0x00, /* 0 0 1 AC_BE */ + 0x03, /* 0 1 1 AC_BE */ + 0x04, /* 1 0 0 AC_VI */ + 0x05, /* 1 0 1 AC_VI */ + 0x06, /* 1 1 0 AC_VO */ + 0x07 /* 1 1 1 AC_VO */ +}; + +/** + * This table inverses the tos_to_tid operation to get a priority + * which is in sequential order, and can be compared. + * Use this to compare the priority of two different TIDs. + */ +t_u8 tos_to_tid_inv[] = {0x02, /* from tos_to_tid[2] = 0 */ + 0x00, /* from tos_to_tid[0] = 1 */ + 0x01, /* from tos_to_tid[1] = 2 */ + 0x03, 0x04, 0x05, 0x06, 0x07}; + +/** + * This table will provide the tid value for given ac. This table does not + * change and will be used to copy back the default values to tos_to_tid in + * case of disconnect. + */ +const t_u8 ac_to_tid[4][2] = {{1, 2}, {0, 3}, {4, 5}, {6, 7}}; + +/* Map of TOS UP values to WMM AC */ +static const mlan_wmm_ac_e tos_to_ac[] = {WMM_AC_BE, WMM_AC_BK, WMM_AC_BK, + WMM_AC_BE, WMM_AC_VI, WMM_AC_VI, + WMM_AC_VO, WMM_AC_VO}; + +raListTbl *wlan_wmm_get_ralist_node(pmlan_private priv, t_u8 tid, + t_u8 *ra_addr); + +/******************************************************** + Local Functions +********************************************************/ +#ifdef DEBUG_LEVEL2 +/** + * @brief Debug print function to display the priority parameters for a WMM AC + * + * @param pac_param Pointer to the AC parameters to display + * + * @return N/A + */ +static void +wlan_wmm_ac_debug_print(const IEEEtypes_WmmAcParameters_t *pac_param) +{ + const char *ac_str[] = {"BK", "BE", "VI", "VO"}; + + ENTER(); + + PRINTM(MINFO, + "WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, " + "EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", + ac_str[wmm_aci_to_qidx_map[pac_param->aci_aifsn.aci]], + pac_param->aci_aifsn.aci, pac_param->aci_aifsn.acm, + pac_param->aci_aifsn.aifsn, pac_param->ecw.ecw_min, + pac_param->ecw.ecw_max, + wlan_le16_to_cpu(pac_param->tx_op_limit)); + + LEAVE(); +} +/** Print the WMM AC for debug purpose */ +#define PRINTM_AC(pac_param) wlan_wmm_ac_debug_print(pac_param) +#else +/** Print the WMM AC for debug purpose */ +#define PRINTM_AC(pac_param) +#endif + +/** + * @brief Allocate route address + * + * @param pmadapter Pointer to the mlan_adapter structure + * @param ra Pointer to the route address + * + * @return ra_list + */ +static raListTbl *wlan_wmm_allocate_ralist_node(pmlan_adapter pmadapter, + t_u8 *ra) +{ + raListTbl *ra_list = MNULL; + + ENTER(); + + if (pmadapter->callbacks.moal_malloc(pmadapter->pmoal_handle, + sizeof(raListTbl), MLAN_MEM_DEF, + (t_u8 **)&ra_list)) { + PRINTM(MERROR, "Fail to allocate ra_list\n"); + goto done; + } + util_init_list((pmlan_linked_list)ra_list); + util_init_list_head((t_void *)pmadapter->pmoal_handle, + &ra_list->buf_head, MFALSE, + pmadapter->callbacks.moal_init_lock); + + memcpy_ext(pmadapter, ra_list->ra, ra, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + + ra_list->del_ba_count = 0; + ra_list->total_pkts = 0; + ra_list->tx_pause = 0; + PRINTM(MINFO, "RAList: Allocating buffers for TID %p\n", ra_list); +done: + LEAVE(); + return ra_list; +} + +/** + * @brief 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 INLINE mlan_wmm_ac_e 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 INLINE t_u8 wlan_wmm_downgrade_tid(pmlan_private priv, t_u32 tid) +{ + mlan_wmm_ac_e ac_down; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + ac_down = priv->wmm.ac_down_graded_vals[wlan_wmm_convert_tos_to_ac( + pmadapter, tid)]; + LEAVE(); + /* + * Send the index to tid array, picking from the array will be + * taken care by dequeuing function + */ + if (tid == 1 || tid == 2) + return ac_to_tid[ac_down][(tid + 1) % 2]; + else if (tid >= MAX_NUM_TID) + return ac_to_tid[ac_down][0]; + else + return ac_to_tid[ac_down][tid % 2]; +} + +/** + * @brief Delete packets in RA node + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra_list Pointer to raListTbl + * + * @return N/A + */ +static INLINE void wlan_wmm_del_pkts_in_ralist_node(pmlan_private priv, + raListTbl *ra_list) +{ + pmlan_buffer pmbuf; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + while ((pmbuf = (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &ra_list->buf_head, MNULL, + MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, &ra_list->buf_head, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + } + util_free_list_head((t_void *)pmadapter->pmoal_handle, + &ra_list->buf_head, + pmadapter->callbacks.moal_free_lock); + + LEAVE(); +} + +/** + * @brief Delete packets in RA list + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra_list_head ra list header + * + * @return N/A + */ +static INLINE void wlan_wmm_del_pkts_in_ralist(pmlan_private priv, + mlan_list_head *ra_list_head) +{ + raListTbl *ra_list; + + ENTER(); + + ra_list = (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + ra_list_head, MNULL, MNULL); + + while (ra_list && ra_list != (raListTbl *)ra_list_head) { + wlan_wmm_del_pkts_in_ralist_node(priv, ra_list); + + ra_list = ra_list->pnext; + } + + LEAVE(); +} + +/** + * @brief Clean up the wmm queue + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +static void wlan_wmm_cleanup_queues(pmlan_private priv) +{ + int i; + + ENTER(); + + for (i = 0; i < MAX_NUM_TID; i++) { + wlan_wmm_del_pkts_in_ralist(priv, + &priv->wmm.tid_tbl_ptr[i].ra_list); + priv->wmm.pkts_queued[i] = 0; + priv->wmm.pkts_paused[i] = 0; + } + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, 0, MNULL, MNULL); + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, HIGH_PRIO_TID, MNULL, + MNULL); + + LEAVE(); +} + +/** + * @brief Delete all route address from RA list + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +static void wlan_wmm_delete_all_ralist(pmlan_private priv) +{ + raListTbl *ra_list; + int i; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + for (i = 0; i < MAX_NUM_TID; ++i) { + PRINTM(MINFO, "RAList: Freeing buffers for TID %d\n", i); + while ((ra_list = (raListTbl *)util_peek_list( + pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i].ra_list, MNULL, + MNULL))) { + util_unlink_list(pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i].ra_list, + (pmlan_linked_list)ra_list, MNULL, + MNULL); + + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)ra_list); + } + + util_init_list( + (pmlan_linked_list)&priv->wmm.tid_tbl_ptr[i].ra_list); + priv->wmm.tid_tbl_ptr[i].ra_list_curr = MNULL; + } + + LEAVE(); +} + +/** + * @brief Get queue RA pointer + * + * @param priv Pointer to the mlan_private driver data struct + * @param tid TID + * @param ra_addr Pointer to the route address + * + * @return ra_list + */ +static raListTbl *wlan_wmm_get_queue_raptr(pmlan_private priv, t_u8 tid, + t_u8 *ra_addr) +{ + raListTbl *ra_list; +#ifdef 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; + } +#ifdef 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_NETINTF(MERROR, priv); + PRINTM(MERROR, + "Drop packets to unknow station " MACSTR "\n", + MAC2STR(ra_addr)); + LEAVE(); + return MNULL; + } + } +#endif + wlan_ralist_add(priv, ra_addr); + + ra_list = wlan_wmm_get_ralist_node(priv, tid, ra_addr); + LEAVE(); + return ra_list; +} + +#ifdef STA_SUPPORT +/** + * @brief Sends wmmac host event + * + * @param priv Pointer to the mlan_private driver data struct + * @param type_str Type of host event + * @param src_addr Pointer to the source Address + * @param tid TID + * @param up User priority + * @param status Status code or Reason code + * + * @return N/A + */ +static void wlan_send_wmmac_host_event(pmlan_private priv, char *type_str, + t_u8 *src_addr, t_u8 tid, t_u8 up, + t_u8 status) +{ + t_u8 event_buf[100]; + mlan_event *pevent; + t_u8 *pout_buf; + + ENTER(); + + /* Format one of the following two output strings: + ** - TSPEC:ADDTS_RSP:[]:TID=X:UP=Y + ** - TSPEC:DELTS_RX:[]:TID=X:UP=Y + */ + pevent = (mlan_event *)event_buf; + pout_buf = pevent->event_buf; + + memcpy_ext(priv->adapter, pout_buf, (t_u8 *)"TSPEC:", 6, 6); + pout_buf += 6; + + memcpy_ext(priv->adapter, pout_buf, (t_u8 *)type_str, + wlan_strlen(type_str), wlan_strlen(type_str)); + pout_buf += wlan_strlen(type_str); + + *pout_buf++ = ':'; + *pout_buf++ = '['; + + if (status >= 100) { + *pout_buf++ = (status / 100) + '0'; + status = (status % 100); + } + + if (status >= 10) { + *pout_buf++ = (status / 10) + '0'; + status = (status % 10); + } + + *pout_buf++ = status + '0'; + + memcpy_ext(priv->adapter, pout_buf, (t_u8 *)"]:TID", 5, 5); + pout_buf += 5; + *pout_buf++ = tid + '0'; + + memcpy_ext(priv->adapter, pout_buf, (t_u8 *)":UP", 3, 3); + pout_buf += 3; + *pout_buf++ = up + '0'; + + *pout_buf = '\0'; + + pevent->bss_index = priv->bss_index; + pevent->event_id = MLAN_EVENT_ID_DRV_REPORT_STRING; + pevent->event_len = wlan_strlen((const char *)(pevent->event_buf)); + + wlan_recv_event(priv, MLAN_EVENT_ID_DRV_REPORT_STRING, pevent); + LEAVE(); +} +#endif /* STA_SUPPORT */ + +/** + * @brief This function gets the highest priority list pointer + * + * @param pmadapter A pointer to mlan_adapter + * @param priv A pointer to mlan_private + * @param tid A pointer to return tid + * + * @return raListTbl + */ +static raListTbl *wlan_wmm_get_highest_priolist_ptr(pmlan_adapter pmadapter, + pmlan_private *priv, + int *tid) +{ + pmlan_private priv_tmp; + raListTbl *ptr, *head; + mlan_bssprio_node *bssprio_node, *bssprio_head; + tid_tbl_t *tid_ptr; + int i, j; + int next_prio = 0; + int next_tid = 0; + ENTER(); + + PRINTM(MDAT_D, "POP\n"); + for (j = pmadapter->priv_num - 1; j >= 0; --j) { + if (!(util_peek_list(pmadapter->pmoal_handle, + &pmadapter->bssprio_tbl[j].bssprio_head, + MNULL, MNULL))) + continue; + + if (pmadapter->bssprio_tbl[j].bssprio_cur == + (mlan_bssprio_node *)&pmadapter->bssprio_tbl[j] + .bssprio_head) { + pmadapter->bssprio_tbl[j].bssprio_cur = + pmadapter->bssprio_tbl[j].bssprio_cur->pnext; + } + + bssprio_head = bssprio_node = + pmadapter->bssprio_tbl[j].bssprio_cur; + + do { + priv_tmp = bssprio_node->priv; + if ((priv_tmp->port_ctrl_mode == MTRUE) && + (priv_tmp->port_open == MFALSE)) { + PRINTM(MINFO, + "get_highest_prio_ptr(): " + "PORT_CLOSED Ignore pkts from BSS%d\n", + priv_tmp->bss_index); + /* Ignore data pkts from a BSS if port is closed + */ + goto next_intf; + } + if (priv_tmp->tx_pause == MTRUE) { + PRINTM(MINFO, + "get_highest_prio_ptr(): " + "TX PASUE Ignore pkts from BSS%d\n", + priv_tmp->bss_index); + /* Ignore data pkts from a BSS if tx pause */ + goto next_intf; + } + + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + priv_tmp->wmm.ra_list_spinlock); + + for (i = util_scalar_read( + pmadapter->pmoal_handle, + &priv_tmp->wmm.highest_queued_prio, MNULL, + MNULL); + i >= LOW_PRIO_TID; --i) { + tid_ptr = &(priv_tmp) + ->wmm + .tid_tbl_ptr[tos_to_tid[i]]; + if (!util_peek_list(pmadapter->pmoal_handle, + &tid_ptr->ra_list, MNULL, + MNULL)) + continue; + + /* + * Always choose the next ra we transmitted + * last time, this way we pick the ra's in + * round robin fashion. + */ + head = ptr = tid_ptr->ra_list_curr->pnext; + if (ptr == (raListTbl *)&tid_ptr->ra_list) + head = ptr = ptr->pnext; + + do { + if (!ptr->tx_pause && + util_peek_list( + pmadapter->pmoal_handle, + &ptr->buf_head, MNULL, + MNULL)) { + /* Because WMM only support + * BK/BE/VI/VO, we have 8 tid + * We should balance the traffic + * of the same AC */ + if (i % 2) + next_prio = i - 1; + else + next_prio = i + 1; + next_tid = + tos_to_tid[next_prio]; + if (priv_tmp->wmm.pkts_queued + [next_tid] && + (priv_tmp->wmm.pkts_queued + [next_tid] > + priv_tmp->wmm.pkts_paused + [next_tid])) + util_scalar_write( + pmadapter->pmoal_handle, + &priv_tmp->wmm + .highest_queued_prio, + next_prio, + MNULL, MNULL); + else + /* if + * highest_queued_prio > + * i, set it to i */ + util_scalar_conditional_write( + pmadapter->pmoal_handle, + &priv_tmp->wmm + .highest_queued_prio, + MLAN_SCALAR_COND_GREATER_THAN, + i, i, MNULL, + MNULL); + *priv = priv_tmp; + *tid = tos_to_tid[i]; + /* hold priv->ra_list_spinlock + * to maintain ptr */ + PRINTM(MDAT_D, + "get highest prio ptr %p, tid %d\n", + ptr, *tid); + LEAVE(); + return ptr; + } + + ptr = ptr->pnext; + if (ptr == + (raListTbl *)&tid_ptr->ra_list) + ptr = ptr->pnext; + } while (ptr != head); + } + + /* If priv still has packets queued, reset to + * HIGH_PRIO_TID */ + if (util_scalar_read(pmadapter->pmoal_handle, + &priv_tmp->wmm.tx_pkts_queued, + MNULL, MNULL)) + util_scalar_write( + pmadapter->pmoal_handle, + &priv_tmp->wmm.highest_queued_prio, + HIGH_PRIO_TID, MNULL, MNULL); + else + /* No packet at any TID for this priv. Mark as + * such to skip checking TIDs for this priv + * (until pkt is added). */ + util_scalar_write( + pmadapter->pmoal_handle, + &priv_tmp->wmm.highest_queued_prio, + NO_PKT_PRIO_TID, MNULL, MNULL); + + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv_tmp->wmm.ra_list_spinlock); + + next_intf: + bssprio_node = bssprio_node->pnext; + if (bssprio_node == + (mlan_bssprio_node *)&pmadapter->bssprio_tbl[j] + .bssprio_head) + bssprio_node = bssprio_node->pnext; + pmadapter->bssprio_tbl[j].bssprio_cur = bssprio_node; + } while (bssprio_node != bssprio_head); + } + + LEAVE(); + return MNULL; +} + +/** + * @brief This function gets the number of packets in the Tx queue + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param max_buf_size Maximum buffer size + * + * @return Packet count + */ +static int wlan_num_pkts_in_txq(mlan_private *priv, raListTbl *ptr, + int max_buf_size) +{ + int count = 0, total_size = 0; + pmlan_buffer pmbuf; + + ENTER(); + + for (pmbuf = (pmlan_buffer)ptr->buf_head.pnext; + pmbuf != (pmlan_buffer)(&ptr->buf_head); pmbuf = pmbuf->pnext) { + total_size += pmbuf->data_len; + if (total_size < max_buf_size) + ++count; + else + break; + } + + LEAVE(); + return count; +} + +/** + * @brief This function sends a single packet + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param ptrindex ptr's TID index + * + * @return N/A + */ +static INLINE void wlan_send_single_packet(pmlan_private priv, raListTbl *ptr, + int ptrindex) +{ + pmlan_buffer pmbuf; + pmlan_buffer pmbuf_next; + mlan_tx_param tx_param; + pmlan_adapter pmadapter = priv->adapter; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle, + &ptr->buf_head, MNULL, MNULL); + if (pmbuf) { + PRINTM(MINFO, "Dequeuing the packet %p %p\n", ptr, pmbuf); + priv->wmm.pkts_queued[ptrindex]--; + util_scalar_decrement(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + ptr->total_pkts--; + pmbuf_next = (pmlan_buffer)util_peek_list( + pmadapter->pmoal_handle, &ptr->buf_head, MNULL, MNULL); + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + + tx_param.next_pkt_len = + ((pmbuf_next) ? pmbuf_next->data_len + sizeof(TxPD) : + 0); + status = wlan_process_tx(priv, pmbuf, &tx_param); + + if (status == MLAN_STATUS_RESOURCE) { + /** Queue the packet back at the head */ + PRINTM(MDAT_D, "Queuing pkt back to raList %p %p\n", + ptr, pmbuf); + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + if (!wlan_is_ralist_valid(priv, ptr, ptrindex)) { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_FAILURE); + LEAVE(); + return; + } + priv->wmm.pkts_queued[ptrindex]++; + util_scalar_increment(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, + MNULL); + util_enqueue_list_head(pmadapter->pmoal_handle, + &ptr->buf_head, + (pmlan_linked_list)pmbuf, MNULL, + MNULL); + + ptr->total_pkts++; + pmbuf->flags |= MLAN_BUF_FLAG_REQUEUED_PKT; + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + } else { + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + if (wlan_is_ralist_valid(priv, ptr, ptrindex)) { + priv->wmm.packets_out[ptrindex]++; + priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = + ptr; + } + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur = + pmadapter->bssprio_tbl[priv->bss_priority] + .bssprio_cur->pnext; + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + } + } else { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + PRINTM(MINFO, "Nothing to send\n"); + } + + LEAVE(); +} + +/** + * @brief This function checks if this mlan_buffer is already processed. + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * + * @return MTRUE or MFALSE + */ +static INLINE int wlan_is_ptr_processed(mlan_private *priv, raListTbl *ptr) +{ + pmlan_buffer pmbuf; + + pmbuf = (pmlan_buffer)util_peek_list(priv->adapter->pmoal_handle, + &ptr->buf_head, MNULL, MNULL); + if (pmbuf && (pmbuf->flags & MLAN_BUF_FLAG_REQUEUED_PKT)) + return MTRUE; + + return MFALSE; +} + +/** + * @brief This function sends a single packet that has been processed + * + * @param priv A pointer to mlan_private + * @param ptr A pointer to RA list table + * @param ptrindex ptr's TID index + * + * @return N/A + */ +static INLINE void wlan_send_processed_packet(pmlan_private priv, + raListTbl *ptr, int ptrindex) +{ + pmlan_buffer pmbuf_next = MNULL; + mlan_tx_param tx_param; + pmlan_buffer pmbuf; + pmlan_adapter pmadapter = priv->adapter; + mlan_status ret = MLAN_STATUS_FAILURE; + + pmbuf = (pmlan_buffer)util_dequeue_list(pmadapter->pmoal_handle, + &ptr->buf_head, MNULL, MNULL); + if (pmbuf) { + pmbuf_next = (pmlan_buffer)util_peek_list( + pmadapter->pmoal_handle, &ptr->buf_head, MNULL, MNULL); + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + tx_param.next_pkt_len = + ((pmbuf_next) ? pmbuf_next->data_len + sizeof(TxPD) : + 0); + + ret = pmadapter->ops.host_to_card(priv, MLAN_TYPE_DATA, pmbuf, + &tx_param); + switch (ret) { +#ifdef USB + case MLAN_STATUS_PRESOURCE: + PRINTM(MINFO, "MLAN_STATUS_PRESOURCE is returned\n"); + break; +#endif + 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: + PRINTM(MERROR, "Error: Failed to write data\n"); + pmadapter->dbg.num_tx_host_to_card_failure++; + pmbuf->status_code = MLAN_ERROR_DATA_TX_FAIL; + wlan_write_data_complete(pmadapter, pmbuf, ret); + break; + case MLAN_STATUS_PENDING: + break; + case MLAN_STATUS_SUCCESS: + DBG_HEXDUMP(MDAT_D, "Tx", + pmbuf->pbuf + pmbuf->data_offset, + MIN(pmbuf->data_len + sizeof(TxPD), + MAX_DATA_DUMP_LEN)); + wlan_write_data_complete(pmadapter, pmbuf, ret); + break; + default: + break; + } + if (ret != MLAN_STATUS_RESOURCE) { + pmadapter->callbacks.moal_spin_lock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + if (wlan_is_ralist_valid(priv, ptr, ptrindex)) { + priv->wmm.packets_out[ptrindex]++; + priv->wmm.tid_tbl_ptr[ptrindex].ra_list_curr = + ptr; + ptr->total_pkts--; + } + pmadapter->bssprio_tbl[priv->bss_priority].bssprio_cur = + pmadapter->bssprio_tbl[priv->bss_priority] + .bssprio_cur->pnext; + priv->wmm.pkts_queued[ptrindex]--; + util_scalar_decrement(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, + MNULL); + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + } + } else { + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + } +} + +/** + * @brief This function dequeues a packet + * + * @param pmadapter A pointer to mlan_adapter + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static int wlan_dequeue_tx_packet(pmlan_adapter pmadapter) +{ + raListTbl *ptr; + pmlan_private priv = MNULL; + int ptrindex = 0; + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + int tid_del = 0; + int tid = 0; + mlan_buffer *pmbuf = MNULL; + + ENTER(); + + ptr = wlan_wmm_get_highest_priolist_ptr(pmadapter, &priv, &ptrindex); + if (!ptr) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Note:- Spinlock is locked in wlan_wmm_get_highest_priolist_ptr + * when it returns a pointer (for the priv it returns), + * and is unlocked in wlan_send_processed_packet, + * wlan_send_single_packet or wlan_11n_aggregate_pkt. + * The spinlock would be required for some parts of both of function. + * But, the the bulk of these function will execute w/o spinlock. + * Unlocking the spinlock inside these function will help us avoid + * taking the spinlock again, check to see if the ptr is still + * valid and then proceed. This is done purely to increase + * execution time. */ + + /* Note:- Also, anybody adding code which does not get into + * wlan_send_processed_packet, wlan_send_single_packet, or + * wlan_11n_aggregate_pkt should make sure ra_list_spinlock + * is freed. Otherwise there would be a lock up. */ + + tid = wlan_get_tid(priv->adapter, ptr); + if (tid >= MAX_NUM_TID) + tid = wlan_wmm_downgrade_tid(priv, tid); + + if (wlan_is_ptr_processed(priv, ptr)) { + wlan_send_processed_packet(priv, ptr, ptrindex); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + if (ptr->del_ba_count >= DEL_BA_THRESHOLD) + wlan_update_del_ba_count(priv, ptr); + if (pmadapter->tp_state_on) { + pmbuf = (pmlan_buffer)util_peek_list( + pmadapter->pmoal_handle, &ptr->buf_head, MNULL, MNULL); + if (pmbuf) { + pmadapter->callbacks.moal_tp_accounting( + pmadapter->pmoal_handle, pmbuf->pdesc, 3); + if (pmadapter->tp_state_drop_point == 3) { + pmbuf = (pmlan_buffer)util_dequeue_list( + pmadapter->pmoal_handle, &ptr->buf_head, + MNULL, MNULL); + PRINTM(MERROR, "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--; + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_SUCCESS); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + } + if (!ptr->is_11n_enabled || + (ptr->ba_status || ptr->del_ba_count >= DEL_BA_THRESHOLD) +#ifdef STA_SUPPORT + || priv->wps.session_enable +#endif /* STA_SUPPORT */ + ) { + if (ptr->is_11n_enabled && ptr->ba_status && + ptr->amsdu_in_ampdu && + wlan_is_amsdu_allowed(priv, ptr, tid) && + (wlan_num_pkts_in_txq(priv, ptr, pmadapter->tx_buf_size) >= + MIN_NUM_AMSDU)) { + wlan_11n_aggregate_pkt(priv, ptr, priv->intf_hr_len, + ptrindex); + } else + wlan_send_single_packet(priv, ptr, ptrindex); + } else { + if (wlan_is_ampdu_allowed(priv, ptr, tid) && + (ptr->packet_count > ptr->ba_packet_threshold)) { + if (wlan_is_bastream_avail(priv)) { + PRINTM(MINFO, + "BA setup threshold %d reached. tid=%d\n", + ptr->packet_count, tid); + if (!wlan_11n_get_txbastream_tbl( + priv, tid, ptr->ra, MFALSE)) { + wlan_11n_create_txbastream_tbl( + priv, ptr->ra, tid, + BA_STREAM_SETUP_INPROGRESS); + wlan_send_addba(priv, tid, ptr->ra); + } + } else if (wlan_find_stream_to_delete(priv, ptr, tid, + &tid_del, ra)) { + PRINTM(MDAT_D, "tid_del=%d tid=%d\n", tid_del, + tid); + if (!wlan_11n_get_txbastream_tbl( + priv, tid, ptr->ra, MFALSE)) { + wlan_11n_create_txbastream_tbl( + priv, ptr->ra, tid, + BA_STREAM_SETUP_INPROGRESS); + wlan_send_delba(priv, MNULL, tid_del, + ra, 1); + } + } + } + if (wlan_is_amsdu_allowed(priv, ptr, tid) && + (wlan_num_pkts_in_txq(priv, ptr, pmadapter->tx_buf_size) >= + MIN_NUM_AMSDU)) { + wlan_11n_aggregate_pkt(priv, ptr, priv->intf_hr_len, + ptrindex); + } else { + wlan_send_single_packet(priv, ptr, ptrindex); + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief update tx_pause flag in ra_list + * + * @param priv A pointer to mlan_private + * @param mac peer mac address + * @param tx_pause tx_pause flag (0/1) + * + * + * @return packets queued for this mac + */ +t_u16 wlan_update_ralist_tx_pause(pmlan_private priv, t_u8 *mac, t_u8 tx_pause) +{ + raListTbl *ra_list; + int i; + pmlan_adapter pmadapter = priv->adapter; + t_u32 pkt_cnt = 0; + t_u32 tx_pkts_queued = 0; + ENTER(); + + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = wlan_wmm_get_ralist_node(priv, i, mac); + if (ra_list && ra_list->tx_pause != tx_pause) { + pkt_cnt += ra_list->total_pkts; + ra_list->tx_pause = tx_pause; + if (tx_pause) + priv->wmm.pkts_paused[i] += ra_list->total_pkts; + else + priv->wmm.pkts_paused[i] -= ra_list->total_pkts; + } + } + if (pkt_cnt) { + tx_pkts_queued = util_scalar_read(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + MNULL, MNULL); + if (tx_pause) + tx_pkts_queued -= pkt_cnt; + else + tx_pkts_queued += pkt_cnt; + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, tx_pkts_queued, + MNULL, MNULL); + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, HIGH_PRIO_TID, + MNULL, MNULL); + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); + return pkt_cnt; +} + +#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 + + 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) + + pmadapter->min_ba_threshold; + PRINTM(MINFO, "pmadapter->min_ba_threshold = %d\n", + pmadapter->min_ba_threshold); + PRINTM(MINFO, "setup BA after %d packets\n", ba_threshold); + + LEAVE(); + return ba_threshold; +} + +/** + * @brief This function cleans Tx/Rx queues + * + * @param priv A pointer to mlan_private + * + * @return N/A + */ +t_void wlan_clean_txrx(pmlan_private priv) +{ + mlan_adapter *pmadapter = priv->adapter; + t_u8 i = 0; + + ENTER(); + wlan_cleanup_bypass_txq(priv); + wlan_11n_cleanup_reorder_tbl(priv); + wlan_11n_deleteall_txbastream_tbl(priv); +#if defined(USB) + if (IS_USB(pmadapter->card_type)) + wlan_reset_usb_tx_aggr(priv->adapter); +#endif +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type)) + wlan_clean_pcie_ring_buf(priv->adapter); +#endif + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + wlan_wmm_cleanup_queues(priv); + wlan_wmm_delete_all_ralist(priv); + memcpy_ext(pmadapter, tos_to_tid, ac_to_tid, sizeof(tos_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; +#ifdef UAP_SUPPORT + priv->num_drop_pkts = 0; +#endif +#ifdef SDIO + if (IS_SD(pmadapter->card_type)) { + memset(pmadapter, pmadapter->pcard_sd->mpa_tx_count, 0, + sizeof(pmadapter->pcard_sd->mpa_tx_count)); + pmadapter->pcard_sd->mpa_sent_no_ports = 0; + pmadapter->pcard_sd->mpa_sent_last_pkt = 0; + memset(pmadapter, pmadapter->pcard_sd->mpa_rx_count, 0, + sizeof(pmadapter->pcard_sd->mpa_rx_count)); + } +#endif + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + LEAVE(); +} + +/** + * @brief Set the WMM queue priorities to their default values + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +void wlan_wmm_default_queue_priorities(pmlan_private priv) +{ + ENTER(); + + /* Default queue priorities: VO->VI->BE->BK */ + priv->wmm.queue_priority[0] = WMM_AC_VO; + priv->wmm.queue_priority[1] = WMM_AC_VI; + priv->wmm.queue_priority[2] = WMM_AC_BE; + priv->wmm.queue_priority[3] = WMM_AC_BK; + + LEAVE(); +} + +/** + * @brief Initialize WMM priority queues + * + * @param priv Pointer to the mlan_private driver data struct + * @param pwmm_ie Pointer to the IEEEtypes_WmmParameter_t data struct + * + * @return N/A + */ +void wlan_wmm_setup_queue_priorities(pmlan_private priv, + IEEEtypes_WmmParameter_t *pwmm_ie) +{ + t_u16 cw_min, avg_back_off, tmp[4]; + t_u32 i, j, num_ac; + t_u8 ac_idx; + + ENTER(); + + if (!pwmm_ie || priv->wmm_enabled == MFALSE) { + /* WMM is not enabled, just set the defaults and return */ + wlan_wmm_default_queue_priorities(priv); + LEAVE(); + return; + } + memset(priv->adapter, tmp, 0, sizeof(tmp)); + + HEXDUMP("WMM: setup_queue_priorities: param IE", (t_u8 *)pwmm_ie, + sizeof(IEEEtypes_WmmParameter_t)); + + PRINTM(MINFO, + "WMM Parameter IE: version=%d, " + "qos_info Parameter Set Count=%d, Reserved=%#x\n", + pwmm_ie->vend_hdr.version, pwmm_ie->qos_info.para_set_count, + pwmm_ie->reserved); + + for (num_ac = 0; num_ac < NELEMENTS(pwmm_ie->ac_params); num_ac++) { + cw_min = (1 << pwmm_ie->ac_params[num_ac].ecw.ecw_min) - 1; + avg_back_off = (cw_min >> 1) + + pwmm_ie->ac_params[num_ac].aci_aifsn.aifsn; + + ac_idx = wmm_aci_to_qidx_map[pwmm_ie->ac_params[num_ac] + .aci_aifsn.aci]; + priv->wmm.queue_priority[ac_idx] = ac_idx; + tmp[ac_idx] = avg_back_off; + + PRINTM(MCMND, "WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", + (1 << pwmm_ie->ac_params[num_ac].ecw.ecw_max) - 1, + cw_min, avg_back_off); + PRINTM_AC(&pwmm_ie->ac_params[num_ac]); + } + + HEXDUMP("WMM: avg_back_off", (t_u8 *)tmp, sizeof(tmp)); + HEXDUMP("WMM: queue_priority", priv->wmm.queue_priority, + sizeof(priv->wmm.queue_priority)); + + /* Bubble sort */ + for (i = 0; i < num_ac; i++) { + for (j = 1; j < num_ac - i; j++) { + if (tmp[j - 1] > tmp[j]) { + SWAP_U16(tmp[j - 1], tmp[j]); + SWAP_U8(priv->wmm.queue_priority[j - 1], + priv->wmm.queue_priority[j]); + } else if (tmp[j - 1] == tmp[j]) { + if (priv->wmm.queue_priority[j - 1] < + priv->wmm.queue_priority[j]) { + SWAP_U8(priv->wmm.queue_priority[j - 1], + priv->wmm.queue_priority[j]); + } + } + } + } + + wlan_wmm_queue_priorities_tid(priv, priv->wmm.queue_priority); + + HEXDUMP("WMM: avg_back_off, sort", (t_u8 *)tmp, sizeof(tmp)); + DBG_HEXDUMP(MCMD_D, "WMM: queue_priority, sort", + priv->wmm.queue_priority, sizeof(priv->wmm.queue_priority)); + LEAVE(); +} + +/** + * @brief Downgrade WMM priority queue + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +void wlan_wmm_setup_ac_downgrade(pmlan_private priv) +{ + int ac_val; + + ENTER(); + + PRINTM(MINFO, "WMM: AC Priorities: BK(0), BE(1), VI(2), VO(3)\n"); + + if (priv->wmm_enabled == MFALSE) { + /* WMM is not enabled, default priorities */ + for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { + priv->wmm.ac_down_graded_vals[ac_val] = + (mlan_wmm_ac_e)ac_val; + } + } else { + for (ac_val = WMM_AC_BK; ac_val <= WMM_AC_VO; ac_val++) { + priv->wmm.ac_down_graded_vals[ac_val] = + wlan_wmm_eval_downgrade_ac( + priv, (mlan_wmm_ac_e)ac_val); + PRINTM(MINFO, "WMM: AC PRIO %d maps to %d\n", ac_val, + priv->wmm.ac_down_graded_vals[ac_val]); + } + } + + LEAVE(); +} + +/** + * @brief Allocate and add a RA list for all TIDs with the given RA + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra Address of the receiver STA (AP in case of infra) + * + * @return N/A + */ +void wlan_ralist_add(mlan_private *priv, t_u8 *ra) +{ + int i; + raListTbl *ra_list; + pmlan_adapter pmadapter = priv->adapter; + + ENTER(); + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = wlan_wmm_allocate_ralist_node(pmadapter, ra); + PRINTM(MINFO, "Creating RA List %p for tid %d\n", ra_list, i); + if (!ra_list) + break; + ra_list->max_amsdu = 0; + ra_list->ba_status = BA_STREAM_NOT_SETUP; + ra_list->amsdu_in_ampdu = MFALSE; + if (queuing_ra_based(priv)) { + ra_list->is_11n_enabled = wlan_is_11n_enabled(priv, ra); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = + get_station_max_amsdu_size(priv, ra); + ra_list->tx_pause = wlan_is_tx_pause(priv, ra); + } else { + ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = priv->max_amsdu; + } + + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, "ralist %p: is_11n_enabled=%d max_amsdu=%d\n", + ra_list, ra_list->is_11n_enabled, ra_list->max_amsdu); + + if (ra_list->is_11n_enabled) { + ra_list->packet_count = 0; + ra_list->ba_packet_threshold = + wlan_get_random_ba_threshold(pmadapter); + } + + util_enqueue_list_tail(pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i].ra_list, + (pmlan_linked_list)ra_list, MNULL, + MNULL); + + if (!priv->wmm.tid_tbl_ptr[i].ra_list_curr) + priv->wmm.tid_tbl_ptr[i].ra_list_curr = ra_list; + } + + LEAVE(); +} + +/** + * @brief Initialize the WMM parameter. + * + * @param pmadapter Pointer to the mlan_adapter data structure + * + * @return N/A + */ +t_void wlan_init_wmm_param(pmlan_adapter pmadapter) +{ + /* Reuse the same structure of WmmAcParameters_t for configuration + * purpose here. the definition of acm bit is changed to ucm (user + * configuration mode) FW will take the setting of + * aifsn,ecw_max,ecw_min, tx_op_limit only when ucm is set to 1. + * othewise the default setting/behavoir in firmware will be used. + */ + pmadapter->ac_params[AC_BE].aci_aifsn.acm = 0; + pmadapter->ac_params[AC_BE].aci_aifsn.aci = AC_BE; + pmadapter->ac_params[AC_BE].aci_aifsn.aifsn = 3; + pmadapter->ac_params[AC_BE].ecw.ecw_max = 10; + pmadapter->ac_params[AC_BE].ecw.ecw_min = 4; + pmadapter->ac_params[AC_BE].tx_op_limit = 0; + + pmadapter->ac_params[AC_BK].aci_aifsn.acm = 0; + pmadapter->ac_params[AC_BK].aci_aifsn.aci = AC_BK; + pmadapter->ac_params[AC_BK].aci_aifsn.aifsn = 7; + pmadapter->ac_params[AC_BK].ecw.ecw_max = 10; + pmadapter->ac_params[AC_BK].ecw.ecw_min = 4; + pmadapter->ac_params[AC_BK].tx_op_limit = 0; + + pmadapter->ac_params[AC_VI].aci_aifsn.acm = 0; + pmadapter->ac_params[AC_VI].aci_aifsn.aci = AC_VI; + pmadapter->ac_params[AC_VI].aci_aifsn.aifsn = 2; + pmadapter->ac_params[AC_VI].ecw.ecw_max = 4; + pmadapter->ac_params[AC_VI].ecw.ecw_min = 3; + pmadapter->ac_params[AC_VI].tx_op_limit = 188; + + pmadapter->ac_params[AC_VO].aci_aifsn.acm = 0; + pmadapter->ac_params[AC_VO].aci_aifsn.aci = AC_VO; + pmadapter->ac_params[AC_VO].aci_aifsn.aifsn = 2; + pmadapter->ac_params[AC_VO].ecw.ecw_max = 3; + pmadapter->ac_params[AC_VO].ecw.ecw_min = 2; + pmadapter->ac_params[AC_VO].tx_op_limit = 102; +} + +/** + * @brief Initialize the WMM state information and the WMM data path queues. + * + * @param pmadapter Pointer to the mlan_adapter data structure + * + * @return N/A + */ +t_void wlan_wmm_init(pmlan_adapter pmadapter) +{ + int i, j; + pmlan_private priv; + + ENTER(); + + for (j = 0; j < pmadapter->priv_num; ++j) { + priv = pmadapter->priv[j]; + if (priv) { + for (i = 0; i < MAX_NUM_TID; ++i) { + priv->aggr_prio_tbl[i].amsdu = + tos_to_tid_inv[i]; + priv->aggr_prio_tbl[i].ampdu_ap = + priv->aggr_prio_tbl[i].ampdu_user = + tos_to_tid_inv[i]; + priv->ibss_ampdu[i] = + priv->aggr_prio_tbl[i].ampdu_user; + priv->wmm.pkts_queued[i] = 0; + priv->wmm.pkts_paused[i] = 0; + priv->wmm.tid_tbl_ptr[i].ra_list_curr = MNULL; + } + priv->wmm.drv_pkt_delay_max = WMM_DRV_DELAY_MAX; + + priv->aggr_prio_tbl[6].amsdu = BA_STREAM_NOT_ALLOWED; + priv->aggr_prio_tbl[7].amsdu = BA_STREAM_NOT_ALLOWED; + priv->aggr_prio_tbl[6].ampdu_ap = + priv->aggr_prio_tbl[6].ampdu_user = + BA_STREAM_NOT_ALLOWED; + priv->ibss_ampdu[6] = BA_STREAM_NOT_ALLOWED; + + priv->aggr_prio_tbl[7].ampdu_ap = + priv->aggr_prio_tbl[7].ampdu_user = + BA_STREAM_NOT_ALLOWED; + priv->ibss_ampdu[7] = BA_STREAM_NOT_ALLOWED; + + priv->add_ba_param.timeout = + MLAN_DEFAULT_BLOCK_ACK_TIMEOUT; +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) { + priv->add_ba_param.tx_win_size = + MLAN_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MLAN_STA_AMPDU_DEF_RXWINSIZE; + } +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + priv->add_ba_param.tx_win_size = + MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; + priv->add_ba_param.rx_win_size = + MLAN_WFD_AMPDU_DEF_TXRXWINSIZE; + } +#endif +#ifdef UAP_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + priv->add_ba_param.tx_win_size = + MLAN_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MLAN_UAP_AMPDU_DEF_RXWINSIZE; + } +#endif + priv->user_rxwinsize = priv->add_ba_param.rx_win_size; + priv->add_ba_param.tx_amsdu = MTRUE; + priv->add_ba_param.rx_amsdu = MTRUE; + memset(priv->adapter, priv->rx_seq, 0xff, + sizeof(priv->rx_seq)); + wlan_wmm_default_queue_priorities(priv); + } + } + + LEAVE(); +} + +/** + * @brief Setup the queue priorities and downgrade any queues as required + * by the WMM info. Setups default values if WMM is not active + * for this association. + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +void wlan_wmm_setup_queues(pmlan_private priv) +{ + ENTER(); + wlan_wmm_setup_queue_priorities(priv, MNULL); + wlan_wmm_setup_ac_downgrade(priv); + LEAVE(); +} + +#ifdef STA_SUPPORT +/** + * @brief Send a command to firmware to retrieve the current WMM status + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return MLAN_STATUS_SUCCESS; MLAN_STATUS_FAILURE + */ +mlan_status wlan_cmd_wmm_status_change(pmlan_private priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + ret = wlan_prepare_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, 0, 0, 0, + MNULL); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Check if wmm TX queue is empty + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * + * @return MFALSE if not empty; MTRUE if empty + */ +int wlan_wmm_lists_empty(pmlan_adapter pmadapter) +{ + int j; + pmlan_private priv; + + ENTER(); + + for (j = 0; j < pmadapter->priv_num; ++j) { + priv = pmadapter->priv[j]; + if (priv) { + if ((priv->port_ctrl_mode == MTRUE) && + (priv->port_open == MFALSE)) { + PRINTM(MINFO, + "wmm_lists_empty: PORT_CLOSED Ignore pkts from BSS%d\n", + j); + continue; + } + if (priv->tx_pause) + continue; + + if (util_scalar_read( + pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + pmadapter->callbacks.moal_spin_lock, + pmadapter->callbacks.moal_spin_unlock)) { + LEAVE(); + return MFALSE; + } + } + } + + LEAVE(); + return MTRUE; +} + +/** + * @brief Get ralist node + * + * @param priv Pointer to the mlan_private driver data struct + * @param tid TID + * @param ra_addr Pointer to the route address + * + * @return ra_list or MNULL + */ +raListTbl *wlan_wmm_get_ralist_node(pmlan_private priv, t_u8 tid, t_u8 *ra_addr) +{ + raListTbl *ra_list; + ENTER(); + ra_list = + (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[tid].ra_list, + MNULL, MNULL); + while (ra_list && + (ra_list != (raListTbl *)&priv->wmm.tid_tbl_ptr[tid].ra_list)) { + if (!memcmp(priv->adapter, ra_list->ra, ra_addr, + MLAN_MAC_ADDR_LENGTH)) { + LEAVE(); + return ra_list; + } + ra_list = ra_list->pnext; + } + LEAVE(); + return MNULL; +} + +/** + * @brief Check if RA list is valid or not + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra_list Pointer to raListTbl + * @param ptrindex TID pointer index + * + * @return MTRUE- valid. MFALSE- invalid. + */ +int wlan_is_ralist_valid(mlan_private *priv, raListTbl *ra_list, int ptrindex) +{ + raListTbl *rlist; + + ENTER(); + + rlist = (raListTbl *)util_peek_list( + priv->adapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[ptrindex].ra_list, MNULL, MNULL); + + while (rlist && + (rlist != + (raListTbl *)&priv->wmm.tid_tbl_ptr[ptrindex].ra_list)) { + if (rlist == ra_list) { + LEAVE(); + return MTRUE; + } + + rlist = rlist->pnext; + } + LEAVE(); + return MFALSE; +} + +/** + * @brief Update an existing raList with a new RA and 11n capability + * + * @param priv Pointer to the mlan_private driver data struct + * @param old_ra Old receiver address + * @param new_ra New receiver address + * + * @return integer count of updated nodes + */ +int wlan_ralist_update(mlan_private *priv, t_u8 *old_ra, t_u8 *new_ra) +{ + t_u8 tid; + int update_count; + raListTbl *ra_list; + + ENTER(); + + update_count = 0; + + for (tid = 0; tid < MAX_NUM_TID; ++tid) { + ra_list = wlan_wmm_get_ralist_node(priv, tid, old_ra); + + if (ra_list) { + update_count++; + + if (queuing_ra_based(priv)) { + ra_list->is_11n_enabled = + wlan_is_11n_enabled(priv, new_ra); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = + get_station_max_amsdu_size( + priv, new_ra); + } else { + ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = priv->max_amsdu; + } + + ra_list->tx_pause = MFALSE; + ra_list->packet_count = 0; + ra_list->ba_packet_threshold = + wlan_get_random_ba_threshold(priv->adapter); + ra_list->amsdu_in_ampdu = MFALSE; + ra_list->ba_status = BA_STREAM_NOT_SETUP; + PRINTM(MINFO, + "ralist_update: %p, %d, " MACSTR "-->" MACSTR + "\n", + ra_list, ra_list->is_11n_enabled, + MAC2STR(ra_list->ra), MAC2STR(new_ra)); + + memcpy_ext(priv->adapter, ra_list->ra, new_ra, + MLAN_MAC_ADDR_LENGTH, 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; +#ifdef UAP_SUPPORT + psta_node sta_ptr = MNULL; +#endif + + ENTER(); + + pmbuf->buf_type = MLAN_BUF_TYPE_DATA; + if (!priv->media_connected) { + PRINTM_NETINTF(MWARN, priv); + PRINTM(MWARN, "Drop packet %p in disconnect state\n", pmbuf); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + LEAVE(); + return; + } + tid = pmbuf->priority; + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + tid_down = wlan_wmm_downgrade_tid(priv, tid); + + /* In case of infra as we have already created the list during + association we just don't have to call get_queue_raptr, we will have + only 1 raptr for a tid in case of infra */ + if (!queuing_ra_based(priv)) { + ra_list = (raListTbl *)util_peek_list( + pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[tid_down].ra_list, MNULL, MNULL); + } else { + memcpy_ext(pmadapter, ra, pmbuf->pbuf + pmbuf->data_offset, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + /** put multicast/broadcast packet in the same ralist */ + if (ra[0] & 0x01) + memset(pmadapter, ra, 0xff, sizeof(ra)); +#ifdef UAP_SUPPORT + else if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + sta_ptr = wlan_get_station_entry(priv, ra); + if (sta_ptr) { + if (!sta_ptr->is_wmm_enabled && + !priv->is_11ac_enabled) { + tid_down = wlan_wmm_downgrade_tid(priv, + 0xff); + } + } + } +#endif + ra_list = wlan_wmm_get_queue_raptr(priv, tid_down, ra); + } + + if (!ra_list) { + PRINTM_NETINTF(MWARN, priv); + PRINTM(MWARN, + "Drop packet %p, ra_list=%p, media_connected=%d\n", + pmbuf, ra_list, priv->media_connected); + pmadapter->callbacks.moal_spin_unlock( + pmadapter->pmoal_handle, priv->wmm.ra_list_spinlock); + wlan_write_data_complete(pmadapter, pmbuf, MLAN_STATUS_FAILURE); + LEAVE(); + return; + } + + PRINTM_NETINTF(MDATA, priv); + PRINTM(MDATA, + "Adding pkt %p (priority=%d, tid_down=%d) to ra_list %p\n", + pmbuf, pmbuf->priority, tid_down, ra_list); + util_enqueue_list_tail(pmadapter->pmoal_handle, &ra_list->buf_head, + (pmlan_linked_list)pmbuf, MNULL, MNULL); + + ra_list->total_pkts++; + ra_list->packet_count++; + + priv->wmm.pkts_queued[tid_down]++; + if (ra_list->tx_pause) { + priv->wmm.pkts_paused[tid_down]++; + } else { + util_scalar_increment(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + /* if highest_queued_prio < prio(tid_down), set it to + * prio(tid_down) */ + util_scalar_conditional_write( + pmadapter->pmoal_handle, &priv->wmm.highest_queued_prio, + MLAN_SCALAR_COND_LESS_THAN, tos_to_tid_inv[tid_down], + tos_to_tid_inv[tid_down], MNULL, MNULL); + } + /* Record the current time the packet was queued; used to determine + * the amount of time the packet was queued in the driver before it + * was sent to the firmware. The delay is then sent along with the + * packet to the firmware for aggregate delay calculation for stats + * and MSDU lifetime expiry. + */ + pmadapter->callbacks.moal_get_system_time( + pmadapter->pmoal_handle, &pmbuf->in_ts_sec, &pmbuf->in_ts_usec); + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + LEAVE(); +} + +#ifdef STA_SUPPORT +/** + * @brief Process the GET_WMM_STATUS command response from firmware + * + * The GET_WMM_STATUS response may contain multiple TLVs for: + * - AC Queue status TLVs + * - Current WMM Parameter IE TLV + * - Admission Control action frame TLVs + * + * This function parses the TLVs and then calls further functions + * to process any changes in the queue prioritize or state. + * + * @param priv Pointer to the mlan_private driver data struct + * @param ptlv Pointer to the tlv block returned in the response. + * @param resp_len Length of TLV block + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_wmm_get_status(pmlan_private priv, t_u8 *ptlv, + int resp_len) +{ + t_u8 *pcurrent = ptlv; + t_u32 tlv_len; + t_u8 send_wmm_event; + MrvlIEtypes_Data_t *ptlv_hdr; + MrvlIEtypes_WmmQueueStatus_t *ptlv_wmm_q_status; + IEEEtypes_WmmParameter_t *pwmm_param_ie = MNULL; + WmmAcStatus_t *pac_status; + + MrvlIETypes_ActionFrame_t *ptlv_action; + IEEEtypes_Action_WMM_AddTsRsp_t *padd_ts_rsp; + IEEEtypes_Action_WMM_DelTs_t *pdel_ts; + + ENTER(); + + send_wmm_event = MFALSE; + + PRINTM(MINFO, "WMM: WMM_GET_STATUS cmdresp received: %d\n", resp_len); + HEXDUMP("CMD_RESP: WMM_GET_STATUS", pcurrent, resp_len); + + while (resp_len >= sizeof(ptlv_hdr->header)) { + ptlv_hdr = (MrvlIEtypes_Data_t *)pcurrent; + tlv_len = wlan_le16_to_cpu(ptlv_hdr->header.len); + if ((tlv_len + sizeof(ptlv_hdr->header)) > resp_len) { + PRINTM(MERROR, + "WMM get status: Error in processing TLV buffer\n"); + resp_len = 0; + continue; + } + + switch (wlan_le16_to_cpu(ptlv_hdr->header.type)) { + case TLV_TYPE_WMMQSTATUS: + ptlv_wmm_q_status = + (MrvlIEtypes_WmmQueueStatus_t *)ptlv_hdr; + PRINTM(MEVENT, "WMM_STATUS: QSTATUS TLV: %d\n", + ptlv_wmm_q_status->queue_index); + + PRINTM(MINFO, + "CMD_RESP: WMM_GET_STATUS: QSTATUS TLV: %d, %d, %d\n", + ptlv_wmm_q_status->queue_index, + ptlv_wmm_q_status->flow_required, + ptlv_wmm_q_status->disabled); + + pac_status = + &priv->wmm.ac_status[ptlv_wmm_q_status + ->queue_index]; + pac_status->disabled = ptlv_wmm_q_status->disabled; + pac_status->flow_required = + ptlv_wmm_q_status->flow_required; + pac_status->flow_created = + ptlv_wmm_q_status->flow_created; + break; + + case TLV_TYPE_VENDOR_SPECIFIC_IE: /* WMM_IE */ + /* + * Point the regular IEEE IE 2 bytes into the NXP IE + * and setup the IEEE IE type and length byte fields + */ + + PRINTM(MEVENT, "WMM STATUS: WMM IE\n"); + + HEXDUMP("WMM: WMM TLV:", (t_u8 *)ptlv_hdr, tlv_len + 4); + + pwmm_param_ie = + (IEEEtypes_WmmParameter_t *)(pcurrent + 2); + pwmm_param_ie->vend_hdr.len = (t_u8)tlv_len; + pwmm_param_ie->vend_hdr.element_id = WMM_IE; + + PRINTM(MINFO, + "CMD_RESP: WMM_GET_STATUS: WMM Parameter Set: %d\n", + pwmm_param_ie->qos_info.para_set_count); + + memcpy_ext(priv->adapter, + (t_u8 *)&priv->curr_bss_params.bss_descriptor + .wmm_ie, + pwmm_param_ie, + (pwmm_param_ie->vend_hdr.len + 2), + sizeof(IEEEtypes_WmmParameter_t)); + send_wmm_event = MTRUE; + break; + + case TLV_TYPE_IEEE_ACTION_FRAME: + PRINTM(MEVENT, "WMM_STATUS: IEEE Action Frame\n"); + ptlv_action = (MrvlIETypes_ActionFrame_t *)pcurrent; + + ptlv_action->actionFrame.wmmAc.tspecAct.category = + wlan_le32_to_cpu(ptlv_action->actionFrame.wmmAc + .tspecAct.category); + if (ptlv_action->actionFrame.wmmAc.tspecAct.category == + IEEE_MGMT_ACTION_CATEGORY_WMM_TSPEC) { + ptlv_action->actionFrame.wmmAc.tspecAct.action = + wlan_le32_to_cpu( + ptlv_action->actionFrame.wmmAc + .tspecAct.action); + switch (ptlv_action->actionFrame.wmmAc.tspecAct + .action) { + case TSPEC_ACTION_CODE_ADDTS_RSP: + padd_ts_rsp = &ptlv_action->actionFrame + .wmmAc.addTsRsp; + wlan_send_wmmac_host_event( + priv, "ADDTS_RSP", + ptlv_action->srcAddr, + padd_ts_rsp->tspecIE.TspecBody + .TSInfo.TID, + padd_ts_rsp->tspecIE.TspecBody + .TSInfo.UserPri, + padd_ts_rsp->statusCode); + break; + + case TSPEC_ACTION_CODE_DELTS: + pdel_ts = &ptlv_action->actionFrame + .wmmAc.delTs; + wlan_send_wmmac_host_event( + priv, "DELTS_RX", + ptlv_action->srcAddr, + pdel_ts->tspecIE.TspecBody + .TSInfo.TID, + pdel_ts->tspecIE.TspecBody + .TSInfo.UserPri, + pdel_ts->reasonCode); + break; + + case TSPEC_ACTION_CODE_ADDTS_REQ: + default: + break; + } + } + break; + + default: + break; + } + + pcurrent += (tlv_len + sizeof(ptlv_hdr->header)); + resp_len -= (tlv_len + sizeof(ptlv_hdr->header)); + } + + wlan_wmm_setup_queue_priorities(priv, pwmm_param_ie); + wlan_wmm_setup_ac_downgrade(priv); + + if (send_wmm_event) { + wlan_recv_event(priv, MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE, + MNULL); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Call back from the command module to allow insertion of a WMM TLV + * + * If the BSS we are associating to supports WMM, add the required WMM + * Information IE to the association request command buffer in the form + * of a NXP extended IEEE IE. + * + * @param priv Pointer to the mlan_private driver data struct + * @param ppassoc_buf Output parameter: Pointer to the TLV output buffer, + * modified on return to point after the appended WMM TLV + * @param pwmm_ie Pointer to the WMM IE for the BSS we are joining + * @param pht_cap Pointer to the HT IE for the BSS we are joining + * + * @return Length of data appended to the association tlv buffer + */ +t_u32 wlan_wmm_process_association_req(pmlan_private priv, t_u8 **ppassoc_buf, + IEEEtypes_WmmParameter_t *pwmm_ie, + IEEEtypes_HTCap_t *pht_cap) +{ + MrvlIEtypes_WmmParamSet_t *pwmm_tlv; + t_u32 ret_len = 0; + + ENTER(); + + /* Null checks */ + if (!ppassoc_buf) { + LEAVE(); + return 0; + } + if (!(*ppassoc_buf)) { + LEAVE(); + return 0; + } + + if (!pwmm_ie) { + LEAVE(); + return 0; + } + + PRINTM(MINFO, "WMM: process assoc req: bss->wmmIe=0x%x\n", + pwmm_ie->vend_hdr.element_id); + + if ((priv->wmm_required || + (pht_cap && (pht_cap->ieee_hdr.element_id == HT_CAPABILITY) && + (priv->config_bands & BAND_GN || priv->config_bands & BAND_AN))) && + pwmm_ie->vend_hdr.element_id == WMM_IE) { + pwmm_tlv = (MrvlIEtypes_WmmParamSet_t *)*ppassoc_buf; + pwmm_tlv->header.type = (t_u16)wmm_info_ie[0]; + pwmm_tlv->header.type = wlan_cpu_to_le16(pwmm_tlv->header.type); + pwmm_tlv->header.len = (t_u16)wmm_info_ie[1]; + memcpy_ext(priv->adapter, pwmm_tlv->wmm_ie, &wmm_info_ie[2], + pwmm_tlv->header.len, pwmm_tlv->header.len); + if (pwmm_ie->qos_info.qos_uapsd) + memcpy_ext(priv->adapter, + (t_u8 *)(pwmm_tlv->wmm_ie + + pwmm_tlv->header.len - + sizeof(priv->wmm_qosinfo)), + &priv->wmm_qosinfo, + sizeof(priv->wmm_qosinfo), + sizeof(priv->wmm_qosinfo)); + + ret_len = sizeof(pwmm_tlv->header) + pwmm_tlv->header.len; + pwmm_tlv->header.len = wlan_cpu_to_le16(pwmm_tlv->header.len); + + HEXDUMP("ASSOC_CMD: WMM IE", (t_u8 *)pwmm_tlv, ret_len); + *ppassoc_buf += ret_len; + } + + LEAVE(); + return ret_len; +} +#endif /* STA_SUPPORT */ + +/** + * @brief Compute the time delay in the driver queues for a given packet. + * + * When the packet is received at the OS/Driver interface, the current + * time is set in the packet structure. The difference between the present + * time and that received time is computed in this function and limited + * based on pre-compiled limits in the driver. + * + * @param priv Ptr to the mlan_private driver data struct + * @param pmbuf Ptr to the mlan_buffer which has been previously timestamped + * + * @return Time delay of the packet in 2ms units after having limit applied + */ +t_u8 wlan_wmm_compute_driver_packet_delay(pmlan_private priv, + const pmlan_buffer pmbuf) +{ + t_u8 ret_val = 0; + t_u32 out_ts_sec, out_ts_usec; + t_s32 queue_delay; + + ENTER(); + + priv->adapter->callbacks.moal_get_system_time( + priv->adapter->pmoal_handle, &out_ts_sec, &out_ts_usec); + + queue_delay = (t_s32)(out_ts_sec - pmbuf->in_ts_sec) * 1000; + queue_delay += (t_s32)(out_ts_usec - pmbuf->in_ts_usec) / 1000; + + /* + * Queue delay is passed as a uint8 in units of 2ms (ms shifted + * by 1). Min value (other than 0) is therefore 2ms, max is 510ms. + * + * Pass max value if queue_delay is beyond the uint8 range + */ + ret_val = (t_u8)(MIN(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); + + PRINTM(MINFO, "WMM: Pkt Delay: %d ms, %d ms sent to FW\n", queue_delay, + ret_val); + + LEAVE(); + return ret_val; +} + +/** + * @brief Transmit the highest priority packet awaiting in the WMM Queues + * + * @param pmadapter Pointer to the mlan_adapter driver data struct + * + * @return N/A + */ +void wlan_wmm_process_tx(pmlan_adapter pmadapter) +{ + ENTER(); + + do { + if (wlan_dequeue_tx_packet(pmadapter)) + break; +#ifdef SDIO + if (IS_SD(pmadapter->card_type) && + (pmadapter->ireg & UP_LD_CMD_PORT_HOST_INT_STATUS)) { + wlan_send_mp_aggr_buf(pmadapter); + break; + } +#endif + +#ifdef PCIE + if (IS_PCIE(pmadapter->card_type) && + (pmadapter->ireg & + pmadapter->pcard_pcie->reg->host_intr_event_rdy)) + break; +#endif +#ifdef USB + if (IS_USB(pmadapter->card_type) && pmadapter->event_received) + break; +#endif + /* Check if busy */ + } while (!pmadapter->data_sent && !pmadapter->tx_lock_flag && + !wlan_wmm_lists_empty(pmadapter)); + + LEAVE(); + return; +} + +/** + * @brief select wmm queue + * + * @param pmpriv A pointer to mlan_private structure + * @param tid TID 0-7 + * + * @return wmm_queue priority (0-3) + */ +t_u8 wlan_wmm_select_queue(mlan_private *pmpriv, t_u8 tid) +{ + pmlan_adapter pmadapter = pmpriv->adapter; + t_u8 i; + mlan_wmm_ac_e ac_down = + pmpriv->wmm.ac_down_graded_vals[wlan_wmm_convert_tos_to_ac( + pmadapter, tid)]; + + ENTER(); + + for (i = 0; i < 4; i++) { + if (pmpriv->wmm.queue_priority[i] == ac_down) { + LEAVE(); + return i; + } + } + LEAVE(); + return 0; +} + +/** + * @brief Delete tx packets in RA list + * + * @param priv Pointer to the mlan_private driver data struct + * @param ra_list_head ra list header + * @param tid tid + * + * @return N/A + */ +static INLINE t_u8 wlan_del_tx_pkts_in_ralist(pmlan_private priv, + mlan_list_head *ra_list_head, + int tid) +{ + raListTbl *ra_list = MNULL; + pmlan_adapter pmadapter = priv->adapter; + pmlan_buffer pmbuf = MNULL; + t_u8 ret = MFALSE; + ENTER(); + ra_list = (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + ra_list_head, MNULL, MNULL); + while (ra_list && ra_list != (raListTbl *)ra_list_head) { + if (ra_list->total_pkts && + (ra_list->tx_pause || + (ra_list->total_pkts > RX_LOW_THRESHOLD))) { + pmbuf = (pmlan_buffer)util_dequeue_list( + pmadapter->pmoal_handle, &ra_list->buf_head, + MNULL, MNULL); + if (pmbuf) { + PRINTM(MDATA, + "Drop pkts: tid=%d tx_pause=%d pkts=%d " MACSTR + "\n", + tid, ra_list->tx_pause, + ra_list->total_pkts, + MAC2STR(ra_list->ra)); + wlan_write_data_complete(pmadapter, pmbuf, + MLAN_STATUS_FAILURE); + priv->wmm.pkts_queued[tid]--; + priv->num_drop_pkts++; + ra_list->total_pkts--; + if (ra_list->tx_pause) + priv->wmm.pkts_paused[tid]--; + else + util_scalar_decrement( + pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + MNULL, MNULL); + ret = MTRUE; + break; + } + } + ra_list = ra_list->pnext; + } + + LEAVE(); + return ret; +} + +/** + * @brief Drop tx pkts + * + * @param priv Pointer to the mlan_private driver data struct + * + * @return N/A + */ +t_void wlan_drop_tx_pkts(pmlan_private priv) +{ + int j; + static int i; + pmlan_adapter pmadapter = priv->adapter; + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + for (j = 0; j < MAX_NUM_TID; j++, i++) { + if (i == MAX_NUM_TID) + i = 0; + if (wlan_del_tx_pkts_in_ralist( + priv, &priv->wmm.tid_tbl_ptr[i].ra_list, i)) { + i++; + break; + } + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + return; +} + +/** + * @brief Remove peer ralist + * + * @param priv A pointer to mlan_private + * @param mac peer mac address + * + * @return N/A + */ +t_void wlan_wmm_delete_peer_ralist(pmlan_private priv, t_u8 *mac) +{ + raListTbl *ra_list; + int i; + pmlan_adapter pmadapter = priv->adapter; + t_u32 pkt_cnt = 0; + t_u32 tx_pkts_queued = 0; + + ENTER(); + pmadapter->callbacks.moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = wlan_wmm_get_ralist_node(priv, i, mac); + if (ra_list) { + PRINTM(MINFO, "delete sta ralist %p\n", ra_list); + priv->wmm.pkts_queued[i] -= ra_list->total_pkts; + if (ra_list->tx_pause) + priv->wmm.pkts_paused[i] -= ra_list->total_pkts; + else + pkt_cnt += ra_list->total_pkts; + wlan_wmm_del_pkts_in_ralist_node(priv, ra_list); + + util_unlink_list(pmadapter->pmoal_handle, + &priv->wmm.tid_tbl_ptr[i].ra_list, + (pmlan_linked_list)ra_list, MNULL, + MNULL); + pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle, + (t_u8 *)ra_list); + if (priv->wmm.tid_tbl_ptr[i].ra_list_curr == ra_list) + priv->wmm.tid_tbl_ptr[i].ra_list_curr = + (raListTbl *)&priv->wmm.tid_tbl_ptr[i] + .ra_list; + } + } + if (pkt_cnt) { + tx_pkts_queued = util_scalar_read(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, + MNULL, MNULL); + tx_pkts_queued -= pkt_cnt; + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, tx_pkts_queued, + MNULL, MNULL); + util_scalar_write(priv->adapter->pmoal_handle, + &priv->wmm.highest_queued_prio, HIGH_PRIO_TID, + MNULL, MNULL); + } + pmadapter->callbacks.moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + LEAVE(); +} + +#ifdef STA_SUPPORT + +/** + * @brief 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, 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_ext(pmpriv->adapter, pcmd_addts->tspec_data, paddts->ie_data, + paddts->ie_data_len, WMM_TSPEC_SIZE); + + 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(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm = MNULL; + mlan_ds_wmm_addts *paddts = MNULL; + const HostCmd_DS_WMM_ADDTS_REQ *presp_addts = &resp->params.add_ts; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + paddts = (mlan_ds_wmm_addts *)&pwmm->param.addts; + paddts->result = wlan_le32_to_cpu(presp_addts->command_result); + paddts->dialog_tok = presp_addts->dialog_token; + paddts->status_code = (t_u32)presp_addts->ieee_status_code; + + if (paddts->result == MLAN_CMD_RESULT_SUCCESS) { + /* The tspecData field is potentially variable in size + * due to extra IEs that may have been in the ADDTS + * response action frame. Calculate the data length from + * the firmware command response. + */ + paddts->ie_data_len = + (t_u8)(resp->size - + sizeof(presp_addts->command_result) - + sizeof(presp_addts->timeout_ms) - + sizeof(presp_addts->dialog_token) - + sizeof(presp_addts->ieee_status_code) - + S_DS_GEN); + + /* Copy the TSPEC data include any extra IEs after the + * TSPEC */ + memcpy_ext(pmpriv->adapter, paddts->ie_data, + presp_addts->tspec_data, paddts->ie_data_len, + sizeof(paddts->ie_data)); + } 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, 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_ext(pmpriv->adapter, pcmd_delts->tspec_data, pdelts->ie_data, + pdelts->ie_data_len, WMM_TSPEC_SIZE); + + 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(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm; + IEEEtypes_WMM_TSPEC_t *ptspec_ie; + const HostCmd_DS_WMM_DELTS_REQ *presp_delts = &resp->params.del_ts; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + pwmm->param.delts.result = + wlan_le32_to_cpu(presp_delts->command_result); + + PRINTM(MINFO, "TSPEC: DELTS result = %d\n", + presp_delts->command_result); + + if (pwmm->param.delts.result == 0) { + ptspec_ie = (IEEEtypes_WMM_TSPEC_t *) + presp_delts->tspec_data; + wlan_send_wmmac_host_event( + pmpriv, "DELTS_TX", MNULL, + ptspec_ie->TspecBody.TSInfo.TID, + ptspec_ie->TspecBody.TSInfo.UserPri, + presp_delts->ieee_reason_code); + } + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares the command of WMM_QUEUE_STATS + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_wmm_queue_stats(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, 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(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + 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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, 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_ext(pmpriv->adapter, (t_void *)pcmd_ts_status, + (t_void *)pts_status, sizeof(HostCmd_DS_WMM_TS_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(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + 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_ext(pmpriv->adapter, (t_void *)&pwmm->param.ts_status, + (t_void *)presp_ts_status, + sizeof(mlan_ds_wmm_ts_status), + sizeof(mlan_ds_wmm_ts_status)); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set/Get WMM status + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success + */ +static mlan_status wlan_wmm_ioctl_enable(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *cfg = MNULL; + + ENTER(); + cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_DELTS_REQ, 0, 0, + (t_void *)pioctl_req, + (t_void *)&cfg->param.delts); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief To get and start/stop queue stats on a WMM AC + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_wmm_ioctl_queue_stats(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + 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(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *cfg = MNULL; + + ENTER(); + + cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_TS_STATUS, 0, 0, + (t_void *)pioctl_req, + (t_void *)&cfg->param.ts_status); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} +#endif /* STA_SUPPORT */ + +/** + * @brief This function prepares the command of WMM_PARAM_CONFIG + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param cmd_action cmd action. + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_wmm_param_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u8 cmd_action, + t_void *pdata_buf) +{ + wmm_ac_parameters_t *ac_params = (wmm_ac_parameters_t *)pdata_buf; + HostCmd_DS_WMM_PARAM_CONFIG *pcmd_cfg = &cmd->params.param_config; + t_u8 i = 0; + + ENTER(); + + cmd->command = wlan_cpu_to_le16(HostCmd_CMD_WMM_PARAM_CONFIG); + cmd->size = wlan_cpu_to_le16(sizeof(HostCmd_DS_WMM_PARAM_CONFIG) + + S_DS_GEN); + cmd->result = 0; + + pcmd_cfg->action = cmd_action; + if (cmd_action == HostCmd_ACT_GEN_SET) { + memcpy_ext(pmpriv->adapter, pcmd_cfg->ac_params, ac_params, + sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES, + sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES); + for (i = 0; i < MAX_AC_QUEUES; i++) { + pcmd_cfg->ac_params[i].tx_op_limit = wlan_cpu_to_le16( + pcmd_cfg->ac_params[i].tx_op_limit); + } + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the command response of WMM_PARAM_CONFIG + * + * @param pmpriv A pointer to mlan_private structure + * @param resp A pointer to HostCmd_DS_COMMAND + * @param pioctl_buf A pointer to mlan_ioctl_req structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_ret_wmm_param_config(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm = MNULL; + HostCmd_DS_WMM_PARAM_CONFIG *pcfg = + (HostCmd_DS_WMM_PARAM_CONFIG *)&resp->params.param_config; + t_u8 i; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + for (i = 0; i < MAX_AC_QUEUES; i++) { + pcfg->ac_params[i].tx_op_limit = wlan_le16_to_cpu( + pcfg->ac_params[i].tx_op_limit); + } + memcpy_ext(pmpriv->adapter, pwmm->param.ac_params, + pcfg->ac_params, + sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES, + sizeof(wmm_ac_parameters_t) * MAX_AC_QUEUES); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function prepares the command of WMM_QUEUE_CONFIG + * + * @param pmpriv A pointer to mlan_private structure + * @param cmd A pointer to HostCmd_DS_COMMAND structure + * @param pdata_buf A pointer to data buffer + * @return MLAN_STATUS_SUCCESS + */ +mlan_status wlan_cmd_wmm_queue_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + 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(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf) +{ + mlan_ds_wmm_cfg *pwmm = MNULL; + const HostCmd_DS_WMM_QUEUE_CONFIG *presp_qcfg = + &resp->params.queue_config; + + ENTER(); + + if (pioctl_buf) { + pwmm = (mlan_ds_wmm_cfg *)pioctl_buf->pbuf; + pwmm->param.q_cfg.action = wlan_le32_to_cpu(presp_qcfg->action); + pwmm->param.q_cfg.access_category = + wlan_le32_to_cpu(presp_qcfg->access_category); + pwmm->param.q_cfg.msdu_lifetime_expiry = + wlan_le16_to_cpu(presp_qcfg->msdu_lifetime_expiry); + } + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Set/Get a specified AC Queue's parameters + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_PENDING --success, otherwise fail + */ +static mlan_status wlan_wmm_ioctl_queue_config(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pmlan_private pmpriv = pmadapter->priv[pioctl_req->bss_index]; + mlan_ds_wmm_cfg *cfg = MNULL; + + ENTER(); + cfg = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + + /* Send request to firmware */ + ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_WMM_QUEUE_CONFIG, 0, 0, + (t_void *)pioctl_req, + (t_void *)&cfg->param.q_cfg); + + if (ret == MLAN_STATUS_SUCCESS) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief WMM configuration handler + * + * @param pmadapter A pointer to mlan_adapter structure + * @param pioctl_req A pointer to ioctl request buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +mlan_status wlan_wmm_cfg_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_wmm_cfg *wmm = MNULL; + + ENTER(); + + if (pioctl_req->buf_len < sizeof(mlan_ds_wmm_cfg)) { + PRINTM(MWARN, "MLAN bss IOCTL length is too short.\n"); + pioctl_req->data_read_written = 0; + pioctl_req->buf_len_needed = sizeof(mlan_ds_wmm_cfg); + pioctl_req->status_code = MLAN_ERROR_INVALID_PARAMETER; + LEAVE(); + return MLAN_STATUS_RESOURCE; + } + wmm = (mlan_ds_wmm_cfg *)pioctl_req->pbuf; + switch (wmm->sub_command) { +#ifdef STA_SUPPORT + case MLAN_OID_WMM_CFG_ENABLE: + status = wlan_wmm_ioctl_enable(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_QOS: + status = wlan_wmm_ioctl_qos(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_ADDTS: + status = wlan_wmm_ioctl_addts_req(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_DELTS: + status = wlan_wmm_ioctl_delts_req(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_QUEUE_STATS: + status = wlan_wmm_ioctl_queue_stats(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_QUEUE_STATUS: + status = wlan_wmm_ioctl_queue_status(pmadapter, pioctl_req); + break; + case MLAN_OID_WMM_CFG_TS_STATUS: + status = wlan_wmm_ioctl_ts_status(pmadapter, pioctl_req); + break; +#endif + case MLAN_OID_WMM_CFG_QUEUE_CONFIG: + status = wlan_wmm_ioctl_queue_config(pmadapter, pioctl_req); + break; + default: + pioctl_req->status_code = MLAN_ERROR_IOCTL_INVALID; + status = MLAN_STATUS_FAILURE; + break; + } + LEAVE(); + return status; +} + +/** + * @brief Get ralist info + * + * @param priv A pointer to mlan_private structure + * @param buf A pointer to ralist_info structure + * @return number of ralist entry + * + */ +int wlan_get_ralist_info(mlan_private *priv, ralist_info *buf) +{ + ralist_info *plist = buf; + mlan_list_head *ra_list_head = MNULL; + raListTbl *ra_list; + int i; + int count = 0; + for (i = 0; i < MAX_NUM_TID; i++) { + ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; + ra_list = + (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + ra_list_head, MNULL, MNULL); + while (ra_list && ra_list != (raListTbl *)ra_list_head) { + if (ra_list->total_pkts) { + plist->total_pkts = ra_list->total_pkts; + plist->tid = i; + plist->tx_pause = ra_list->tx_pause; + memcpy_ext(priv->adapter, plist->ra, + ra_list->ra, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + plist++; + count++; + if (count >= MLAN_MAX_RALIST_NUM) + break; + } + ra_list = ra_list->pnext; + } + } + LEAVE(); + return count; +} + +/** + * @brief dump ralist info + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + * + */ +void wlan_dump_ralist(mlan_private *priv) +{ + mlan_list_head *ra_list_head = MNULL; + raListTbl *ra_list; + mlan_adapter *pmadapter = priv->adapter; + int i; + t_u32 tx_pkts_queued; + + tx_pkts_queued = + util_scalar_read(pmadapter->pmoal_handle, + &priv->wmm.tx_pkts_queued, MNULL, MNULL); + PRINTM(MERROR, "bss_index = %d, tx_pkts_queued = %d\n", priv->bss_index, + tx_pkts_queued); + if (!tx_pkts_queued) + return; + for (i = 0; i < MAX_NUM_TID; i++) { + ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; + ra_list = + (raListTbl *)util_peek_list(priv->adapter->pmoal_handle, + ra_list_head, MNULL, MNULL); + while (ra_list && ra_list != (raListTbl *)ra_list_head) { + if (ra_list->total_pkts) { + PRINTM(MERROR, + "ralist ra: %02x:%02x:%02x:%02x:%02x:%02x tid=%d pkts=%d pause=%d\n", + ra_list->ra[0], ra_list->ra[1], + ra_list->ra[2], ra_list->ra[3], + ra_list->ra[4], ra_list->ra[5], i, + ra_list->total_pkts, ra_list->tx_pause); + } + ra_list = ra_list->pnext; + } + } + return; +} + +/** + * @brief get tid down + * + * @param priv A pointer to mlan_private structure + * @param tid tid + * + * @return tid_down + * + */ +int wlan_get_wmm_tid_down(mlan_private *priv, int tid) +{ + return wlan_wmm_downgrade_tid(priv, tid); +} diff --git a/mxm_wifiex/wlan_src/mlan/mlan_wmm.h b/mxm_wifiex/wlan_src/mlan/mlan_wmm.h new file mode 100644 index 0000000..33856f0 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlan/mlan_wmm.h @@ -0,0 +1,242 @@ +/** @file mlan_wmm.h + * + * @brief This file contains related macros, enum, and struct + * of wmm functionalities + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 t_u32 wlan_get_tid(pmlan_adapter pmadapter, praListTbl ptr) +{ + pmlan_buffer mbuf; + + ENTER(); + mbuf = (pmlan_buffer)util_peek_list(pmadapter->pmoal_handle, + &ptr->buf_head, MNULL, MNULL); + LEAVE(); + + if (!mbuf) { + return 0; // The default TID,BE + } else + return mbuf->priority; +} + +/** + * @brief This function gets the length of a list + * + * @param head A pointer to mlan_list_head + * + * @return Length of list + */ +static INLINE t_u32 wlan_wmm_list_len(pmlan_list_head head) +{ + pmlan_linked_list pos; + t_u32 count = 0; + + ENTER(); + + pos = head->pnext; + + while (pos != (pmlan_linked_list)head) { + ++count; + pos = pos->pnext; + } + + LEAVE(); + return count; +} + +/** + * @brief This function requests a ralist lock + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + */ +static INLINE t_void wlan_request_ralist_lock(pmlan_private priv) +{ + mlan_adapter *pmadapter = priv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + + ENTER(); + + /* Call MOAL spin lock callback function */ + pcb->moal_spin_lock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + LEAVE(); + return; +} + +/** + * @brief This function releases a lock on ralist + * + * @param priv A pointer to mlan_private structure + * + * @return N/A + */ +static INLINE t_void wlan_release_ralist_lock(pmlan_private priv) +{ + mlan_adapter *pmadapter = priv->adapter; + mlan_callbacks *pcb = (mlan_callbacks *)&pmadapter->callbacks; + + ENTER(); + + /* Call MOAL spin unlock callback function */ + pcb->moal_spin_unlock(pmadapter->pmoal_handle, + priv->wmm.ra_list_spinlock); + + LEAVE(); + return; +} + +/** Add buffer to WMM Tx queue */ +void wlan_wmm_add_buf_txqueue(pmlan_adapter pmadapter, pmlan_buffer pmbuf); +/** Add to RA list */ +void wlan_ralist_add(mlan_private *priv, t_u8 *ra); +/** Update the RA list */ +int wlan_ralist_update(mlan_private *priv, t_u8 *old_ra, t_u8 *new_ra); + +/** WMM status change command handler */ +mlan_status wlan_cmd_wmm_status_change(pmlan_private priv); +/** Check if WMM lists are empty */ +int wlan_wmm_lists_empty(pmlan_adapter pmadapter); +/** Process WMM transmission */ +t_void wlan_wmm_process_tx(pmlan_adapter pmadapter); +/** Test to see if the ralist ptr is valid */ +int wlan_is_ralist_valid(mlan_private *priv, raListTbl *ra_list, int tid); + +raListTbl *wlan_wmm_get_ralist_node(pmlan_private priv, t_u8 tid, + t_u8 *ra_addr); +t_u8 wlan_get_random_ba_threshold(pmlan_adapter pmadapter); + +/** Compute driver packet delay */ +t_u8 wlan_wmm_compute_driver_packet_delay(pmlan_private priv, + const pmlan_buffer pmbuf); +/** Initialize WMM */ +t_void wlan_wmm_init(pmlan_adapter pmadapter); +/** Initialize WMM paramter */ +t_void wlan_init_wmm_param(pmlan_adapter pmadapter); +/** Setup WMM queues */ +extern void wlan_wmm_setup_queues(pmlan_private priv); +/* Setup default queues */ +void wlan_wmm_default_queue_priorities(pmlan_private priv); +/* process wmm_param_config command */ +mlan_status wlan_cmd_wmm_param_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, t_u8 cmd_action, + t_void *pdata_buf); + +/* process wmm_param_config command response */ +mlan_status wlan_ret_wmm_param_config(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +#ifdef STA_SUPPORT +/** Process WMM association request */ +extern t_u32 wlan_wmm_process_association_req(pmlan_private priv, + t_u8 **ppAssocBuf, + IEEEtypes_WmmParameter_t *pWmmIE, + IEEEtypes_HTCap_t *pHTCap); +#endif /* STA_SUPPORT */ + +/** setup wmm queue priorities */ +void wlan_wmm_setup_queue_priorities(pmlan_private priv, + IEEEtypes_WmmParameter_t *wmm_ie); + +/* Get tid_down from tid */ +int wlan_get_wmm_tid_down(mlan_private *priv, int tid); +/** Downgrade WMM priority queue */ +void wlan_wmm_setup_ac_downgrade(pmlan_private priv); +/** select WMM queue */ +t_u8 wlan_wmm_select_queue(mlan_private *pmpriv, t_u8 tid); +t_void wlan_wmm_delete_peer_ralist(pmlan_private priv, t_u8 *mac); + +#ifdef STA_SUPPORT +/* + * Functions used in the cmd handling routine + */ +/** WMM ADDTS request command handler */ +extern mlan_status wlan_cmd_wmm_addts_req(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf); +/** WMM DELTS request command handler */ +extern mlan_status wlan_cmd_wmm_delts_req(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf); +/** WMM QUEUE_STATS command handler */ +extern mlan_status wlan_cmd_wmm_queue_stats(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf); +/** WMM TS_STATUS command handler */ +extern mlan_status wlan_cmd_wmm_ts_status(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + 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(pmlan_private priv, t_u8 *ptlv, + int resp_len); +/** WMM ADDTS request command response handler */ +extern mlan_status wlan_ret_wmm_addts_req(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +/** WMM DELTS request command response handler */ +extern mlan_status wlan_ret_wmm_delts_req(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +/** WMM QUEUE_STATS command response handler */ +extern mlan_status wlan_ret_wmm_queue_stats(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +/** WMM TS_STATUS command response handler */ +extern mlan_status wlan_ret_wmm_ts_status(pmlan_private pmpriv, + HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); +#endif /* STA_SUPPORT */ + +/** WMM QUEUE_CONFIG command handler */ +extern mlan_status wlan_cmd_wmm_queue_config(pmlan_private pmpriv, + HostCmd_DS_COMMAND *cmd, + t_void *pdata_buf); + +/** WMM QUEUE_CONFIG command response handler */ +extern mlan_status wlan_ret_wmm_queue_config(pmlan_private pmpriv, + const HostCmd_DS_COMMAND *resp, + mlan_ioctl_req *pioctl_buf); + +mlan_status wlan_wmm_cfg_ioctl(pmlan_adapter pmadapter, + pmlan_ioctl_req pioctl_req); +#endif /* !_MLAN_WMM_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/mlan.h b/mxm_wifiex/wlan_src/mlinux/mlan.h new file mode 100644 index 0000000..d2cd022 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/mlan.h @@ -0,0 +1,37 @@ +/** @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 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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/mxm_wifiex/wlan_src/mlinux/mlan_decl.h b/mxm_wifiex/wlan_src/mlinux/mlan_decl.h new file mode 100644 index 0000000..48169d7 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/mlan_decl.h @@ -0,0 +1,1988 @@ +/** @file mlan_decl.h + * + * @brief This file declares the generic data structures and APIs. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 _MLAN_DECL_H_ +#define _MLAN_DECL_H_ + +/** MLAN release version */ +#define MLAN_RELEASE_VERSION "214" + +/** Re-define generic data types for MLAN/MOAL */ +/** Signed char (1-byte) */ +typedef signed char t_s8, *t_ps8; +/** Unsigned char (1-byte) */ +typedef unsigned char t_u8, *t_pu8; +/** Signed short (2-bytes) */ +typedef short t_s16, *t_ps16; +/** Unsigned short (2-bytes) */ +typedef unsigned short t_u16, *t_pu16; +/** Signed long (4-bytes) */ +typedef int t_s32, *t_ps32; +/** Unsigned long (4-bytes) */ +typedef unsigned int t_u32, *t_pu32; +/** Signed long long 8-bytes) */ +typedef long long t_s64, *t_ps64; +/** Unsigned long long 8-bytes) */ +typedef unsigned long long t_u64, *t_pu64; +/** Void pointer (4-bytes) */ +typedef void t_void, *t_pvoid; +/** 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) + +#ifndef MACSTR +/** MAC address security format */ +#define MACSTR "%02x:XX:XX:XX:%02x:%02x" +#endif + +#ifndef MAC2STR +/** MAC address security print arguments */ +#define MAC2STR(a) (a)[0], (a)[4], (a)[5] +#endif + +#ifndef FULL_MACSTR +#define FULL_MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif +#ifndef FULL_MAC2STR +#define FULL_MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#endif + +/** Macros for Data Alignment : size */ +#define ALIGN_SZ(p, a) (((p) + ((a)-1)) & ~((a)-1)) + +/** Macros for Data Alignment : address */ +#define ALIGN_ADDR(p, a) \ + ((((t_ptr)(p)) + (((t_ptr)(a)) - 1)) & ~(((t_ptr)(a)) - 1)) + +/** Return the byte offset of a field in the given structure */ +#define MLAN_FIELD_OFFSET(type, field) ((t_u32)(t_ptr) & (((type *)0)->field)) +/** Return aligned offset */ +#define OFFSET_ALIGN_ADDR(p, a) (t_u32)(ALIGN_ADDR(p, a) - (t_ptr)p) + +#if defined(WIFI_DIRECT_SUPPORT) +/** Maximum BSS numbers */ +#define MLAN_MAX_BSS_NUM (16) +#else +/** Maximum BSS numbers */ +#define MLAN_MAX_BSS_NUM (2) +#endif + +/** NET IP alignment */ +#define MLAN_NET_IP_ALIGN 2 + +/** DMA alignment */ +/* SDIO3.0 Inrevium Adapter require 32 bit DMA alignment */ +#define DMA_ALIGNMENT 32 + +/** max size of TxPD */ +#define MAX_TXPD_SIZE 32 + +/** Minimum data header length */ +#define MLAN_MIN_DATA_HEADER_LEN (DMA_ALIGNMENT + MAX_TXPD_SIZE) + +/** rx data header length */ +#define MLAN_RX_HEADER_LEN MLAN_MIN_DATA_HEADER_LEN + +/** This is current limit on Maximum Tx AMPDU allowed */ +#define MLAN_MAX_TX_BASTREAM_SUPPORTED 16 +#define MLAN_MAX_TX_BASTREAM_DEFAULT 2 +/** This is current limit on Maximum Rx AMPDU allowed */ +#define MLAN_MAX_RX_BASTREAM_SUPPORTED 16 + +#ifdef STA_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_STA_AMPDU_DEF_TXWINSIZE 64 +/** Default Win size attached during ADDBA response */ +#define MLAN_STA_AMPDU_DEF_RXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_STA_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +/** Default Win size attached during ADDBA request */ +#define MLAN_UAP_AMPDU_DEF_TXWINSIZE 64 +/** Default Win size attached during ADDBA response */ +#define MLAN_UAP_AMPDU_DEF_RXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_UAP_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif /* UAP_SUPPORT */ + +#ifdef WIFI_DIRECT_SUPPORT +/** WFD use the same window size for tx/rx */ +#define MLAN_WFD_AMPDU_DEF_TXRXWINSIZE 64 +/** RX winsize for COEX */ +#define MLAN_WFD_COEX_AMPDU_DEF_RXWINSIZE 16 +#endif + +/** Block ack timeout value */ +#define MLAN_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff +/** Maximum Tx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_TXWINSIZE 0x3ff +/** Maximum Rx Win size configured for ADDBA request [10 bits] */ +#define MLAN_AMPDU_MAX_RXWINSIZE 0x3ff + +/** Rate index for HR/DSSS 0 */ +#define MLAN_RATE_INDEX_HRDSSS0 0 +/** Rate index for HR/DSSS 3 */ +#define MLAN_RATE_INDEX_HRDSSS3 3 +/** Rate index for OFDM 0 */ +#define MLAN_RATE_INDEX_OFDM0 4 +/** Rate index for OFDM 7 */ +#define MLAN_RATE_INDEX_OFDM7 11 +/** Rate index for MCS 0 */ +#define MLAN_RATE_INDEX_MCS0 0 +/** Rate index for MCS 2 */ +#define MLAN_RATE_INDEX_MCS2 2 +/** Rate index for MCS 4 */ +#define MLAN_RATE_INDEX_MCS4 4 +/** Rate index for MCS 7 */ +#define MLAN_RATE_INDEX_MCS7 7 +/** Rate index for MCS 9 */ +#define MLAN_RATE_INDEX_MCS9 9 +/** Rate index for MCS11 */ +#define MLAN_RATE_INDEX_MCS11 11 +/** Rate index for MCS15 */ +#define MLAN_RATE_INDEX_MCS15 15 +/** Rate index for MCS 32 */ +#define MLAN_RATE_INDEX_MCS32 32 +/** Rate index for MCS 127 */ +#define MLAN_RATE_INDEX_MCS127 127 +#define MLAN_RATE_NSS1 1 +#define MLAN_RATE_NSS2 2 + +/** 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 +#define MLAN_RATE_BITMAP_NSS1_MCS0 160 +#define MLAN_RATE_BITMAP_NSS1_MCS9 169 +#define MLAN_RATE_BITMAP_NSS2_MCS0 176 +#define MLAN_RATE_BITMAP_NSS2_MCS9 185 + +/** MU beamformer */ +#define DEFALUT_11AC_CAP_BEAMFORMING_RESET_MASK (MBIT(19)) + +/** Size of rx data buffer 4096+256 */ +#define MLAN_RX_DATA_BUF_SIZE 4352 + +/** Size of command buffer */ +/** because cal_data_size 2.4 k */ +#define MRVDRV_SIZE_OF_CMD_BUFFER (4 * 1024) +/** Size of rx command buffer */ +#define MLAN_RX_CMD_BUF_SIZE MRVDRV_SIZE_OF_CMD_BUFFER +/** Upload size */ +#define WLAN_UPLD_SIZE MRVDRV_SIZE_OF_CMD_BUFFER + +#if defined(PCIE) +#define MLAN_SSU_MAX_PKT_SIZE (283 * 4) +#define MLAN_SSU_HEADER_SIZE 256 +/** + * Size of DMA buffer to collect 10ms SSU data: + * 2500 spectral packets, plus header + */ +#define MLAN_SSU_BUF_SIZE_1MS (MLAN_SSU_MAX_PKT_SIZE * 250) +#define MLAN_SSU_BUF_SIZE (MLAN_SSU_HEADER_SIZE + MLAN_SSU_BUF_SIZE_1MS * 10) +#define MLAN_SSU_BUF_SIZE_HOST (MLAN_SSU_BUF_SIZE) +#endif + +/** driver initial the fw reset */ +#define FW_RELOAD_SDIO_INBAND_RESET 1 +/** out band reset trigger reset, no interface re-emulation */ +#define FW_RELOAD_NO_EMULATION 2 +/** out band reset with interface re-emulation */ +#define FW_RELOAD_WITH_EMULATION 3 +#ifdef PCIE +/** pcie card reset */ +#define FW_RELOAD_PCIE_RESET 4 +#endif + +#ifdef USB +#define MLAN_USB_BLOCK_SIZE (512) +#define MLAN_USB_AGGR_MODE_NUM (0) +#define MLAN_USB_AGGR_MODE_LEN (1) +#define MLAN_USB_AGGR_MODE_LEN_V2 (2) +#define MLAN_USB_TX_AGGR_MAX_LEN (16000) +#define MLAN_USB_TX_AGGR_MAX_NUM 10 +#define MLAN_USB_TX_AGGR_V2_ALIGN 4 +#define MLAN_USB_TX_AGGR_HEADER 4 +#define MLAN_USB_MAX_PKT_SIZE (MLAN_USB_BLOCK_SIZE * 4) + +#define MLAN_USB_RX_ALIGN_SIZE MLAN_USB_BLOCK_SIZE +#define MLAN_USB_RX_MAX_AGGR_NUM (8) +#define MLAN_USB_RX_DEAGGR_TIMEOUT_USEC (200) + +#define MLAN_USB_TX_AGGR_ALIGN (MLAN_USB_BLOCK_SIZE * 4) +#define MLAN_USB_TX_MAX_AGGR_NUM (8) +#define MLAN_USB_TX_MAX_AGGR_SIZE \ + (MLAN_USB_BLOCK_SIZE * 4 * MLAN_USB_TX_MAX_AGGR_NUM) +#define MLAN_USB_TX_MIN_AGGR_TIMEOUT (1) +#define MLAN_USB_TX_MAX_AGGR_TIMEOUT (4) +#define MLAN_USB_TX_AGGR_TIMEOUT_MSEC MLAN_USB_TX_MIN_AGGR_TIMEOUT +#define MLAN_USB_TX_AGGR_TIMEOUT_DYN (0xFFFF) +#endif /*USB*/ + +/** MLAN MAC Address Length */ +#define MLAN_MAC_ADDR_LENGTH (6) +/** MLAN 802.11 MAC Address */ +typedef t_u8 mlan_802_11_mac_addr[MLAN_MAC_ADDR_LENGTH]; + +/** MLAN Maximum SSID Length */ +#define MLAN_MAX_SSID_LENGTH (32) + +/** RTS/FRAG related defines */ +/** Minimum RTS value */ +#define MLAN_RTS_MIN_VALUE (0) +/** Maximum RTS value */ +#define MLAN_RTS_MAX_VALUE (2347) +/** Minimum FRAG value */ +#define MLAN_FRAG_MIN_VALUE (256) +/** Maximum FRAG value */ +#define MLAN_FRAG_MAX_VALUE (2346) + +/** Minimum tx retry count */ +#define MLAN_TX_RETRY_MIN (0) +/** Maximum tx retry count */ +#define MLAN_TX_RETRY_MAX (14) + +/** max Wmm AC queues */ +#define MAX_AC_QUEUES 4 + +#ifdef SDIO +/** 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 MLAN_RX_DATA_BUF_SIZE +/** SDIO MP aggr pkt limit */ +#define SDIO_MP_AGGR_DEF_PKT_LIMIT (16) +/** max SDIO MP aggr pkt limit */ +#define SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX (16) + +/** SDIO IO Port mask */ +#define MLAN_SDIO_IO_PORT_MASK 0xfffff +/** SDIO Block/Byte mode mask */ +#define MLAN_SDIO_BYTE_MODE_MASK 0x80000000 +#endif /* SDIO */ + +/** SD Interface */ +#define INTF_SD MBIT(0) +#define IS_SD(ct) (ct & (INTF_SD << 8)) +/** PCIE Interface */ +#define INTF_PCIE MBIT(1) +#define IS_PCIE(ct) (ct & (INTF_PCIE << 8)) +/** USB Interface */ +#define INTF_USB MBIT(2) +#define IS_USB(ct) (ct & (INTF_USB << 8)) + +/** 8887 card type */ +#define CARD_TYPE_8887 0x01 +/** 8897 card type */ +#define CARD_TYPE_8897 0x02 +/** 8977 card type */ +#define CARD_TYPE_8977 0x03 +/** 8997 card type */ +#define CARD_TYPE_8997 0x04 +/** 8987 card type */ +#define CARD_TYPE_8987 0x05 +/** 9098 card type */ +#define CARD_TYPE_9098 0x06 +/** 9097 card type */ +#define CARD_TYPE_9097 0x07 +/** 8978 card type */ +#define CARD_TYPE_8978 0x08 + +/** 9098 A0 reverion num */ +#define CHIP_9098_REV_A0 1 +#define CHIP_9098_REV_A1 2 +/** 9097 CHIP REV */ +#define CHIP_9097_REV_B0 1 + +#define INTF_MASK 0xff +#define CARD_TYPE_MASK 0xff + +#ifdef SDIO +/** SD8887 card type */ +#define CARD_TYPE_SD8887 (CARD_TYPE_8887 | (INTF_SD << 8)) +/** SD8897 card type */ +#define CARD_TYPE_SD8897 (CARD_TYPE_8897 | (INTF_SD << 8)) +/** SD8977 card type */ +#define CARD_TYPE_SD8977 (CARD_TYPE_8977 | (INTF_SD << 8)) +/** SD8978 card type */ +#define CARD_TYPE_SD8978 (CARD_TYPE_8978 | (INTF_SD << 8)) +/** SD8997 card type */ +#define CARD_TYPE_SD8997 (CARD_TYPE_8997 | (INTF_SD << 8)) +/** SD8987 card type */ +#define CARD_TYPE_SD8987 (CARD_TYPE_8987 | (INTF_SD << 8)) +/** SD9097 card type */ +#define CARD_TYPE_SD9097 (CARD_TYPE_9097 | (INTF_SD << 8)) +/** SD9098 card type */ +#define CARD_TYPE_SD9098 (CARD_TYPE_9098 | (INTF_SD << 8)) + +#define IS_SD8887(ct) (CARD_TYPE_SD8887 == (ct)) +#define IS_SD8897(ct) (CARD_TYPE_SD8897 == (ct)) +#define IS_SD8977(ct) (CARD_TYPE_SD8977 == (ct)) +#define IS_SD8978(ct) (CARD_TYPE_SD8978 == (ct)) +#define IS_SD8997(ct) (CARD_TYPE_SD8997 == (ct)) +#define IS_SD8987(ct) (CARD_TYPE_SD8987 == (ct)) +#define IS_SD9097(ct) (CARD_TYPE_SD9097 == (ct)) +#define IS_SD9098(ct) (CARD_TYPE_SD9098 == (ct)) + +/** SD8887 Card */ +#define CARD_SD8887 "SD8887" +/** SD8897 Card */ +#define CARD_SD8897 "SD8897" +/** SD8977 Card */ +#define CARD_SD8977 "SD8977" +/** SD8978 Card */ +#define CARD_SD8978 "SD8978" +/** SD8997 Card */ +#define CARD_SD8997 "SD8997" +/** SD8987 Card */ +#define CARD_SD8987 "SD8987" +/** SD9097 Card */ +#define CARD_SD9097 "SD9097" +/** SD9098 Card */ +#define CARD_SD9098 "SD9098" +#endif + +#ifdef PCIE +/** PCIE8897 card type */ +#define CARD_TYPE_PCIE8897 (CARD_TYPE_8897 | (INTF_PCIE << 8)) +/** PCIE8997 card type */ +#define CARD_TYPE_PCIE8997 (CARD_TYPE_8997 | (INTF_PCIE << 8)) +/** PCIE9097 card type */ +#define CARD_TYPE_PCIE9097 (CARD_TYPE_9097 | (INTF_PCIE << 8)) +/** PCIE9098 card type */ +#define CARD_TYPE_PCIE9098 (CARD_TYPE_9098 | (INTF_PCIE << 8)) + +#define IS_PCIE8897(ct) (CARD_TYPE_PCIE8897 == (ct)) +#define IS_PCIE8997(ct) (CARD_TYPE_PCIE8997 == (ct)) +#define IS_PCIE9097(ct) (CARD_TYPE_PCIE9097 == (ct)) +#define IS_PCIE9098(ct) (CARD_TYPE_PCIE9098 == (ct)) + +/** PCIE8897 Card */ +#define CARD_PCIE8897 "PCIE8897" +/** PCIE8997 Card */ +#define CARD_PCIE8997 "PCIE8997" +/** PCIE9097 Card */ +#define CARD_PCIE9097 "PCIE9097" +/** PCIE9000S Card */ +#define CARD_PCIE9000S "PCIE9000S" +/** PCIE9098 Card */ +#define CARD_PCIE9098 "PCIE9098" +#endif + +#ifdef USB +/** USB8897 card type */ +#define CARD_TYPE_USB8897 (CARD_TYPE_8897 | (INTF_USB << 8)) +/** USB8997 card type */ +#define CARD_TYPE_USB8997 (CARD_TYPE_8997 | (INTF_USB << 8)) +/** USB8978 card type */ +#define CARD_TYPE_USB8978 (CARD_TYPE_8978 | (INTF_USB << 8)) +/** USB9098 card type */ +#define CARD_TYPE_USB9098 (CARD_TYPE_9098 | (INTF_USB << 8)) +/** USB9097 card type */ +#define CARD_TYPE_USB9097 (CARD_TYPE_9097 | (INTF_USB << 8)) + +#define IS_USB8897(ct) (CARD_TYPE_USB8897 == (ct)) +#define IS_USB8997(ct) (CARD_TYPE_USB8997 == (ct)) +#define IS_USB8978(ct) (CARD_TYPE_USB8978 == (ct)) +#define IS_USB9098(ct) (CARD_TYPE_USB9098 == (ct)) +#define IS_USB9097(ct) (CARD_TYPE_USB9097 == (ct)) + +/** USB8897 Card */ +#define CARD_USB8897 "USB8897" +/** USB8997 Card */ +#define CARD_USB8997 "USB8997" +/** USB8978 Card */ +#define CARD_USB8978 "USB8978" +/** USB9098 Card */ +#define CARD_USB9098 "USB9098" +/** USB9097 Card */ +#define CARD_USB9097 "USB9097" +#endif + +#define IS_CARD8887(ct) (CARD_TYPE_8887 == ((ct)&0xf)) +#define IS_CARD8897(ct) (CARD_TYPE_8897 == ((ct)&0xf)) +#define IS_CARD8977(ct) (CARD_TYPE_8977 == ((ct)&0xf)) +#define IS_CARD8997(ct) (CARD_TYPE_8997 == ((ct)&0xf)) +#define IS_CARD8987(ct) (CARD_TYPE_8987 == ((ct)&0xf)) +#define IS_CARD9098(ct) (CARD_TYPE_9098 == ((ct)&0xf)) +#define IS_CARD9097(ct) (CARD_TYPE_9097 == ((ct)&0xf)) + +typedef struct _card_type_entry { + t_u16 card_type; + t_u16 func_id; + char *name; +} card_type_entry; + +#if defined(SDIO) || defined(PCIE) +/** Max retry number of IO write */ +#define MAX_WRITE_IOMEM_RETRY 2 +#endif /* SDIO || PCIE */ + +#ifdef PCIE +typedef enum { + PCIE_INT_MODE_LEGACY = 0, + PCIE_INT_MODE_MSI, + PCIE_INT_MODE_MSIX, + PCIE_INT_MODE_MAX, +} PCIE_INT_MODE; +#endif /* PCIE */ + +/** 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 USB +/** Buffer flag for deaggregated rx packet */ +#define MLAN_BUF_FLAG_RX_DEAGGR MBIT(5) + +/** Buffer flag for sleep confirm resp packet */ +#define MLAN_BUF_FLAG_SLEEPCFM_RESP MBIT(6) + +/** Buffer flag for USB TX AGGR */ +#define MLAN_BUF_FLAG_USB_TX_AGGR MBIT(7) +#endif + +/** Buffer flag for TCP_ACK */ +#define MLAN_BUF_FLAG_TCP_ACK MBIT(9) + +/** Buffer flag for TX_STATUS */ +#define MLAN_BUF_FLAG_TX_STATUS MBIT(10) + +/** Buffer flag for NULL data packet */ +#define MLAN_BUF_FLAG_NULL_PKT MBIT(12) +/** Buffer flag for Diag pkt */ +#define MLAN_BUF_FLAG_DIAG_BUF MBIT(13) + +#define MLAN_BUF_FLAG_TX_CTRL MBIT(14) + +#ifdef DEBUG_LEVEL1 +/** Debug level bit definition */ +#define MMSG MBIT(0) +#define MFATAL MBIT(1) +#define MERROR MBIT(2) +#define MDATA MBIT(3) +#define MCMND MBIT(4) +#define MEVENT MBIT(5) +#define MINTR MBIT(6) +#define MIOCTL MBIT(7) + +#define MREG_D MBIT(9) + +#define MMPA_D MBIT(15) +#define MDAT_D MBIT(16) +#define MCMD_D MBIT(17) +#define MEVT_D MBIT(18) +#define MFW_D MBIT(19) +#define MIF_D MBIT(20) + +#define MENTRY MBIT(28) +#define MWARN MBIT(29) +#define MINFO MBIT(30) +#define MHEX_DUMP MBIT(31) +#endif /* DEBUG_LEVEL1 */ + +/** Memory allocation type: DMA */ +#define MLAN_MEM_DMA MBIT(0) + +/** Default memory allocation flag */ +#define MLAN_MEM_DEF 0 + +/** mlan_status */ +typedef enum _mlan_status { + MLAN_STATUS_FAILURE = 0xffffffff, + MLAN_STATUS_SUCCESS = 0, + MLAN_STATUS_PENDING, + MLAN_STATUS_RESOURCE, +#ifdef USB + /* Status pending and no resource */ + MLAN_STATUS_PRESOURCE, +#endif + MLAN_STATUS_COMPLETE, + MLAN_STATUS_FILE_ERR, +} mlan_status; + +/** mlan_error_code */ +typedef enum _mlan_error_code { + /** No error */ + MLAN_ERROR_NO_ERROR = 0, + /** Firmware/device errors below (MSB=0) */ + MLAN_ERROR_FW_NOT_READY = 0x00000001, + MLAN_ERROR_FW_BUSY = 0x00000002, + MLAN_ERROR_FW_CMDRESP = 0x00000003, + MLAN_ERROR_DATA_TX_FAIL = 0x00000004, + MLAN_ERROR_DATA_RX_FAIL = 0x00000005, + /** Driver errors below (MSB=1) */ + MLAN_ERROR_PKT_SIZE_INVALID = 0x80000001, + MLAN_ERROR_PKT_TIMEOUT = 0x80000002, + MLAN_ERROR_PKT_INVALID = 0x80000003, + MLAN_ERROR_CMD_INVALID = 0x80000004, + MLAN_ERROR_CMD_TIMEOUT = 0x80000005, + MLAN_ERROR_CMD_DNLD_FAIL = 0x80000006, + MLAN_ERROR_CMD_CANCEL = 0x80000007, + MLAN_ERROR_CMD_RESP_FAIL = 0x80000008, + MLAN_ERROR_CMD_ASSOC_FAIL = 0x80000009, + MLAN_ERROR_CMD_SCAN_FAIL = 0x8000000A, + MLAN_ERROR_IOCTL_INVALID = 0x8000000B, + MLAN_ERROR_IOCTL_FAIL = 0x8000000C, + MLAN_ERROR_EVENT_UNKNOWN = 0x8000000D, + MLAN_ERROR_INVALID_PARAMETER = 0x8000000E, + MLAN_ERROR_NO_MEM = 0x8000000F, + /** More to add */ +} mlan_error_code; + +/** mlan_buf_type */ +typedef enum _mlan_buf_type { + MLAN_BUF_TYPE_CMD = 1, + MLAN_BUF_TYPE_DATA, + MLAN_BUF_TYPE_EVENT, + MLAN_BUF_TYPE_RAW_DATA, +#ifdef SDIO + MLAN_BUF_TYPE_SPA_DATA, +#endif +} mlan_buf_type; + +#ifdef USB +/** mlan_usb_ep */ +typedef enum _mlan_usb_ep { + MLAN_USB_EP_CTRL = 0, + MLAN_USB_EP_CMD_EVENT = 1, + MLAN_USB_EP_DATA = 2, + MLAN_USB_EP_DATA_CH2 = 3, + MLAN_USB_EP_CMD_EVENT_IF2 = 4, + MLAN_USB_EP_DATA_IF2 = 5, + MLAN_USB_EP_DATA_CH2_IF2 = 6, +} mlan_usb_ep; + +/** Timeout in milliseconds for usb_bulk_msg function */ +#define MLAN_USB_BULK_MSG_TIMEOUT 100 +#endif /* USB */ + +/** 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 mask */ +#define BSS_ROLE_MASK (MBIT(0) | MBIT(1)) + +/** Get BSS role */ +#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_MASK) + +/** mlan_data_frame_type */ +typedef enum _mlan_data_frame_type { + MLAN_DATA_FRAME_TYPE_ETH_II = 0, + MLAN_DATA_FRAME_TYPE_802_11, +} mlan_data_frame_type; + +/** mlan_event_id */ +typedef enum _mlan_event_id { + /* Event generated by firmware (MSB=0) */ + MLAN_EVENT_ID_FW_UNKNOWN = 0x00000001, + MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED = 0x00000002, + MLAN_EVENT_ID_FW_ADHOC_LINK_LOST = 0x00000003, + MLAN_EVENT_ID_FW_DISCONNECTED = 0x00000004, + MLAN_EVENT_ID_FW_MIC_ERR_UNI = 0x00000005, + MLAN_EVENT_ID_FW_MIC_ERR_MUL = 0x00000006, + MLAN_EVENT_ID_FW_BCN_RSSI_LOW = 0x00000007, + MLAN_EVENT_ID_FW_BCN_RSSI_HIGH = 0x00000008, + MLAN_EVENT_ID_FW_BCN_SNR_LOW = 0x00000009, + MLAN_EVENT_ID_FW_BCN_SNR_HIGH = 0x0000000A, + MLAN_EVENT_ID_FW_MAX_FAIL = 0x0000000B, + MLAN_EVENT_ID_FW_DATA_RSSI_LOW = 0x0000000C, + MLAN_EVENT_ID_FW_DATA_RSSI_HIGH = 0x0000000D, + MLAN_EVENT_ID_FW_DATA_SNR_LOW = 0x0000000E, + MLAN_EVENT_ID_FW_DATA_SNR_HIGH = 0x0000000F, + MLAN_EVENT_ID_FW_LINK_QUALITY = 0x00000010, + MLAN_EVENT_ID_FW_PORT_RELEASE = 0x00000011, + MLAN_EVENT_ID_FW_PRE_BCN_LOST = 0x00000012, + MLAN_EVENT_ID_FW_DEBUG_INFO = 0x00000013, + MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE = 0x0000001A, + MLAN_EVENT_ID_FW_HS_WAKEUP = 0x0000001B, + MLAN_EVENT_ID_FW_BG_SCAN = 0x0000001D, + MLAN_EVENT_ID_FW_BG_SCAN_STOPPED = 0x0000001E, + MLAN_EVENT_ID_FW_WEP_ICV_ERR = 0x00000020, + MLAN_EVENT_ID_FW_STOP_TX = 0x00000021, + MLAN_EVENT_ID_FW_START_TX = 0x00000022, + MLAN_EVENT_ID_FW_CHANNEL_SWITCH_ANN = 0x00000023, + MLAN_EVENT_ID_FW_RADAR_DETECTED = 0x00000024, + MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY = 0x00000025, + MLAN_EVENT_ID_FW_BW_CHANGED = 0x00000026, + MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED = 0x0000002B, + +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_UAP_FW_BSS_START = 0x0000002C, + MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE = 0x0000002D, + MLAN_EVENT_ID_UAP_FW_BSS_IDLE = 0x0000002E, + MLAN_EVENT_ID_UAP_FW_MIC_COUNTERMEASURES = 0x0000002F, + MLAN_EVENT_ID_UAP_FW_STA_CONNECT = 0x00000030, + MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT = 0x00000031, +#endif + + MLAN_EVENT_ID_FW_DUMP_INFO = 0x00000033, + + MLAN_EVENT_ID_FW_TX_STATUS = 0x00000034, + MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE = 0x00000036, +#if defined(PCIE) + MLAN_EVENT_ID_SSU_DUMP_FILE = 0x00000039, +#endif /* SSU_SUPPORT */ + /* Event generated by MLAN driver (MSB=1) */ + MLAN_EVENT_ID_DRV_CONNECTED = 0x80000001, + MLAN_EVENT_ID_DRV_DEFER_HANDLING = 0x80000002, + MLAN_EVENT_ID_DRV_HS_ACTIVATED = 0x80000003, + MLAN_EVENT_ID_DRV_HS_DEACTIVATED = 0x80000004, + MLAN_EVENT_ID_DRV_MGMT_FRAME = 0x80000005, + MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM = 0x80000006, + MLAN_EVENT_ID_DRV_PASSTHRU = 0x80000007, + MLAN_EVENT_ID_DRV_SCAN_REPORT = 0x80000009, + MLAN_EVENT_ID_DRV_MEAS_REPORT = 0x8000000A, + MLAN_EVENT_ID_DRV_ASSOC_FAILURE_REPORT = 0x8000000B, + MLAN_EVENT_ID_DRV_REPORT_STRING = 0x8000000F, + MLAN_EVENT_ID_DRV_DBG_DUMP = 0x80000012, + MLAN_EVENT_ID_DRV_BGSCAN_RESULT = 0x80000013, + MLAN_EVENT_ID_DRV_FLUSH_RX_WORK = 0x80000015, + MLAN_EVENT_ID_DRV_DEFER_RX_WORK = 0x80000016, + MLAN_EVENT_ID_DRV_FT_RESPONSE = 0x80000018, + MLAN_EVENT_ID_DRV_FLUSH_MAIN_WORK = 0x80000019, +#ifdef UAP_SUPPORT + MLAN_EVENT_ID_DRV_UAP_CHAN_INFO = 0x80000020, +#endif + MLAN_EVENT_ID_DRV_ASSOC_FAILURE_LOGGER = 0x80000026, + MLAN_EVENT_ID_DRV_ASSOC_SUCC_LOGGER = 0x80000027, + MLAN_EVENT_ID_DRV_DISCONNECT_LOGGER = 0x80000028, + MLAN_EVENT_ID_DRV_WIFI_STATUS = 0x80000029, + MLAN_EVENT_ID_STORE_HOST_CMD_RESP = 0x80000030, +} mlan_event_id; + +/** Data Structures */ +/** mlan_image data structure */ +typedef struct _mlan_fw_image { + /** Firmware image buffer pointer */ + t_u8 *pfw_buf; + /** Firmware image length */ + t_u32 fw_len; + /** Firmware reload flag */ + t_u8 fw_reload; +} mlan_fw_image, *pmlan_fw_image; + +/** MrvlIEtypesHeader_t */ +typedef MLAN_PACK_START struct _MrvlIEtypesHeader { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; +} MLAN_PACK_END MrvlIEtypesHeader_t; + +/** MrvlExtIEtypesHeader_t */ +typedef MLAN_PACK_START struct _MrvlExtIEtypesHeader { + /** Header type */ + t_u16 type; + /** Header length */ + t_u16 len; + /** ext id */ + t_u8 ext_id; +} MLAN_PACK_END MrvlExtIEtypesHeader_t; + +/** MrvlIEtypes_Data_t */ +typedef MLAN_PACK_START struct _MrvlExtIEtypes_Data_t { + /** Header */ + MrvlExtIEtypesHeader_t header; + /** Data */ + t_u8 data[]; +} MLAN_PACK_END MrvlExtIEtypes_Data_t; + +/** MrvlIEtypes_Data_t */ +typedef MLAN_PACK_START struct _MrvlIEtypes_Data_t { + /** Header */ + MrvlIEtypesHeader_t header; + /** Data */ + t_u8 data[]; +} MLAN_PACK_END MrvlIEtypes_Data_t; + +#define OID_TYPE_CAL 0x2 +#define OID_TYPE_DPD 0xa +#define UNKNOW_DPD_LENGTH 0xffffffff + +/** Custom data structure */ +typedef struct _mlan_init_param { + /** DPD data buffer pointer */ + t_u8 *pdpd_data_buf; + /** DPD data length */ + t_u32 dpd_data_len; + /** region txpowerlimit cfg data buffer pointer */ + t_u8 *ptxpwr_data_buf; + /** region txpowerlimit cfg data length */ + t_u32 txpwr_data_len; + /** Cal data buffer pointer */ + t_u8 *pcal_data_buf; + /** Cal data length */ + t_u32 cal_data_len; + /** Other custom data */ +} mlan_init_param, *pmlan_init_param; + +/** channel type */ +enum mlan_channel_type { + CHAN_NO_HT, + CHAN_HT20, + CHAN_HT40MINUS, + CHAN_HT40PLUS, + CHAN_VHT80 +}; + +/** channel band */ +enum { BAND_2GHZ = 0, + BAND_5GHZ = 1, + BAND_4GHZ = 2, +}; + +/** channel offset */ +enum { SEC_CHAN_NONE = 0, + SEC_CHAN_ABOVE = 1, + SEC_CHAN_5MHZ = 2, + SEC_CHAN_BELOW = 3 }; + +/** channel bandwidth */ +enum { CHAN_BW_20MHZ = 0, + CHAN_BW_10MHZ, + CHAN_BW_40MHZ, + CHAN_BW_80MHZ, +}; + +/** scan mode */ +enum { SCAN_MODE_MANUAL = 0, + SCAN_MODE_ACS, + SCAN_MODE_USER, +}; + +/** max cac time 10 minutes */ +#define MAX_CAC_DWELL_TIME 600000 +/** default cac time 60 seconds */ +#define DEF_CAC_DWELL_TIME 60000 +/** start freq for 5G */ +#define START_FREQ_11A_BAND 5000 +/** DFS state */ +typedef enum _dfs_state_t { + /** Channel can be used, CAC (Channel Availability Check) must be done + before using it */ + DFS_USABLE = 0, + /** Channel is not available, radar was detected */ + DFS_UNAVAILABLE = 1, + /** Channel is Available, CAC is done and is free of radar */ + DFS_AVAILABLE = 2, +} dfs_state_t; + +typedef enum _dfs_w53_cfg_t { + /** DFS W53 Default Fw Value */ + DFS_W53_DEFAULT_FW = 0, + /** DFS W53 New W53 Rules/Standard */ + DFS_W53_NEW = 1, + /** DFS W53 Old W53 Rules/Standard */ + DFS_W53_OLD = 2 +} dfs_w53_cfg_t; + +/** Band_Config_t */ +typedef MLAN_PACK_START struct _Band_Config_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=user*/ + t_u8 scanMode : 2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset : 2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth : 2; + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand : 2; +#else + /** Band Info - (00)=2.4GHz, (01)=5GHz */ + t_u8 chanBand : 2; + /** Channel Width - (00)=20MHz, (10)=40MHz, (11)=80MHz */ + t_u8 chanWidth : 2; + /** Secondary Channel Offset - (00)=None, (01)=Above, (11)=Below */ + t_u8 chan2Offset : 2; + /** Channel Selection Mode - (00)=manual, (01)=ACS, (02)=Adoption mode*/ + t_u8 scanMode : 2; +#endif +} MLAN_PACK_END Band_Config_t; + +/** channel_band_t */ +typedef MLAN_PACK_START struct _chan_band_info { + /** Band Configuration */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** 11n flag */ + t_u8 is_11n_enabled; + /** center channel */ + t_u8 center_chan; + /** dfs channel flag */ + t_u8 is_dfs_chan; +} MLAN_PACK_END chan_band_info, *pchan_band_info; + +/** Channel usability flags */ +#define NXP_CHANNEL_NO_OFDM MBIT(9) +#define NXP_CHANNEL_NO_CCK MBIT(8) +#define NXP_CHANNEL_DISABLED MBIT(7) +/* BIT 5/6 resevered for FW */ +#define NXP_CHANNEL_NOHT160 MBIT(4) +#define NXP_CHANNEL_NOHT80 MBIT(3) +#define NXP_CHANNEL_NOHT40 MBIT(2) +#define NXP_CHANNEL_DFS MBIT(1) +#define NXP_CHANNEL_PASSIVE MBIT(0) + +/** CFP dynamic (non-const) elements */ +typedef struct _cfp_dyn_t { + /** extra flags to specify channel usability + * bit 9 : if set, channel is non-OFDM + * bit 8 : if set, channel is non-CCK + * bit 7 : if set, channel is disabled + * bit 5/6 resevered for FW + * bit 4 : if set, 160MHz on channel is disabled + * bit 3 : if set, 80MHz on channel is disabled + * bit 2 : if set, 40MHz on channel is disabled + * bit 1 : if set, channel is DFS channel + * bit 0 : if set, channel is passive + */ + t_u16 flags; + /** TRUE: Channel is blacklisted (do not use) */ + t_bool blacklist; +} cfp_dyn_t; + +/** Chan-Freq-TxPower mapping table*/ +typedef struct _chan_freq_power_t { + /** Channel Number */ + t_u16 channel; + /** Frequency of this Channel */ + t_u32 freq; + /** Max allowed Tx power level */ + t_u16 max_tx_power; + /** TRUE:radar detect required for BAND A or passive scan for BAND B/G; + * FALSE:radar detect not required for BAND A or active scan for BAND + * B/G*/ + t_bool passive_scan_or_radar_detect; + /** Elements associated to cfp that change at run-time */ + cfp_dyn_t dynamic; +} chan_freq_power_t; + +/** 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[]; +} mlan_event, *pmlan_event; + +/** mlan_cmdresp_event data structure */ +typedef struct _mlan_cmdresp_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; + /** resp buffer pointer */ + t_u8 *resp; +} mlan_cmdresp_event, *pmlan_cmdresp_event; + +/** csi event data structure */ + +/** mlan_ioctl_req data structure */ +typedef struct _mlan_ioctl_req { + /** Pointer to previous mlan_ioctl_req */ + struct _mlan_ioctl_req *pprev; + /** Pointer to next mlan_ioctl_req */ + struct _mlan_ioctl_req *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Request id */ + t_u32 req_id; + /** Action: set or get */ + t_u32 action; + /** Pointer to buffer */ + t_u8 *pbuf; + /** Length of buffer */ + t_u32 buf_len; + /** Length of the data read/written in buffer */ + t_u32 data_read_written; + /** Length of buffer needed */ + t_u32 buf_len_needed; + /** Reserved for MOAL module */ + t_ptr reserved_1; +} mlan_ioctl_req, *pmlan_ioctl_req; + +/** txpower structure */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl : 1; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign : 1; + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val : 6; +#else + /** Power to be used for transmission(in dBm) */ + t_u8 abs_val : 6; + /** Sign of the power specified in bit[5:0] */ + t_u8 sign : 1; + /** Host tx power ctrl: + 0x0: use fw setting for TX power + 0x1: value specified in bit[6] and bit[5:0] are valid */ + t_u8 hostctl : 1; +#endif +} MLAN_PACK_END tx_power_t; +/* pkt_txctrl */ +typedef MLAN_PACK_START struct _pkt_txctrl { + /**Data rate in unit of 0.5Mbps */ + t_u16 data_rate; + /*Channel number to transmit the frame */ + t_u8 channel; + /** Bandwidth to transmit the frame*/ + t_u8 bw; + /** Power to be used for transmission*/ + union { + tx_power_t tp; + t_u8 val; + } tx_power; + /** Retry time of tx transmission*/ + t_u8 retry_limit; +} MLAN_PACK_END pkt_txctrl, *ppkt_txctrl; + +/** pkt_rxinfo */ +typedef MLAN_PACK_START struct _pkt_rxinfo { + /** Data rate of received paccket*/ + t_u16 data_rate; + /** Channel on which packet was received*/ + t_u8 channel; + /** Rx antenna*/ + t_u8 antenna; + /** Rx Rssi*/ + t_u8 rssi; +} MLAN_PACK_END pkt_rxinfo, *ppkt_rxinfo; + +/** mlan_buffer data structure */ +typedef struct _mlan_buffer { + /** Pointer to previous mlan_buffer */ + struct _mlan_buffer *pprev; + /** Pointer to next mlan_buffer */ + struct _mlan_buffer *pnext; + /** Status code from firmware/driver */ + t_u32 status_code; + /** Flags for this buffer */ + t_u32 flags; + /** BSS index number for multiple BSS support */ + t_u32 bss_index; + /** Buffer descriptor, e.g. skb in Linux */ + t_void *pdesc; + /** Pointer to buffer */ + t_u8 *pbuf; +#ifdef PCIE + /** Physical address of the pbuf pointer */ + t_u64 buf_pa; + t_u32 total_pcie_buf_len; +#endif + /** Offset to data */ + t_u32 data_offset; + /** Data length */ + t_u32 data_len; + /** Buffer type: data, cmd, event etc. */ + mlan_buf_type buf_type; + + /** Fields below are valid for data packet only */ + /** QoS priority */ + t_u32 priority; + /** Time stamp when packet is received (seconds) */ + t_u32 in_ts_sec; + /** Time stamp when packet is received (micro seconds) */ + t_u32 in_ts_usec; + /** Time stamp when packet is processed (seconds) */ + t_u32 out_ts_sec; + /** Time stamp when packet is processed (micro seconds) */ + t_u32 out_ts_usec; + /** tx_seq_num */ + t_u32 tx_seq_num; + + /** Fields below are valid for MLAN module only */ + /** Pointer to parent mlan_buffer */ + struct _mlan_buffer *pparent; + /** Use count for this buffer */ + t_u32 use_count; + union { + pkt_txctrl tx_info; + pkt_rxinfo rx_info; + } u; +} mlan_buffer, *pmlan_buffer, **ppmlan_buffer; + +/** mlan_fw_info data structure */ +typedef struct _mlan_hw_info { + t_u32 fw_cap; +} mlan_hw_info, *pmlan_hw_info; + +/** mlan_bss_attr data structure */ +typedef struct _mlan_bss_attr { + /** BSS type */ + t_u32 bss_type; + /** Data frame type: Ethernet II, 802.11, etc. */ + t_u32 frame_type; + /** The BSS is active (non-0) or not (0). */ + t_u32 active; + /** BSS Priority */ + t_u32 bss_priority; + /** BSS number */ + t_u32 bss_num; + /** The BSS is virtual */ + t_u32 bss_virtual; +} mlan_bss_attr, *pmlan_bss_attr; + +/** bss tbl data structure */ +typedef struct _mlan_bss_tbl { + /** BSS Attributes */ + mlan_bss_attr bss_attr[MLAN_MAX_BSS_NUM]; +} mlan_bss_tbl, *pmlan_bss_tbl; + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/** Type enumeration for the command result */ +typedef MLAN_PACK_START enum _mlan_cmd_result_e { + MLAN_CMD_RESULT_SUCCESS = 0, + MLAN_CMD_RESULT_FAILURE = 1, + MLAN_CMD_RESULT_TIMEOUT = 2, + MLAN_CMD_RESULT_INVALID_DATA = 3 +} MLAN_PACK_END mlan_cmd_result_e; + +/** Type enumeration of WMM AC_QUEUES */ +typedef MLAN_PACK_START enum _mlan_wmm_ac_e { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VO +} MLAN_PACK_END mlan_wmm_ac_e; + +/** Type enumeration for the action field in the Queue Config command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_config_action_e { + MLAN_WMM_QUEUE_CONFIG_ACTION_GET = 0, + MLAN_WMM_QUEUE_CONFIG_ACTION_SET = 1, + MLAN_WMM_QUEUE_CONFIG_ACTION_DEFAULT = 2, + MLAN_WMM_QUEUE_CONFIG_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_config_action_e; + +/** Type enumeration for the action field in the queue stats command */ +typedef MLAN_PACK_START enum _mlan_wmm_queue_stats_action_e { + MLAN_WMM_STATS_ACTION_START = 0, + MLAN_WMM_STATS_ACTION_STOP = 1, + MLAN_WMM_STATS_ACTION_GET_CLR = 2, + MLAN_WMM_STATS_ACTION_SET_CFG = 3, /* Not currently used */ + MLAN_WMM_STATS_ACTION_GET_CFG = 4, /* Not currently used */ + MLAN_WMM_STATS_ACTION_MAX +} MLAN_PACK_END mlan_wmm_queue_stats_action_e; + +/** + * @brief IOCTL structure for a Traffic stream status. + * + */ +typedef MLAN_PACK_START struct { + /** TSID: Range: 0->7 */ + t_u8 tid; + /** TSID specified is valid */ + t_u8 valid; + /** AC TSID is active on */ + t_u8 access_category; + /** UP specified for the TSID */ + t_u8 user_priority; + /** Power save mode for TSID: 0 (legacy), 1 (UAPSD) */ + t_u8 psb; + /** Upstream(0), Downlink(1), Bidirectional(3) */ + t_u8 flow_dir; + /** Medium time granted for the TSID */ + t_u16 medium_time; +} MLAN_PACK_END wlan_ioctl_wmm_ts_status_t, + /** Type definition of mlan_ds_wmm_ts_status for + MLAN_OID_WMM_CFG_TS_STATUS */ + mlan_ds_wmm_ts_status, *pmlan_ds_wmm_ts_status; + +/** Max Ie length */ +#define MAX_IE_SIZE 256 + +/** custom IE */ +typedef MLAN_PACK_START struct _custom_ie { + /** IE Index */ + t_u16 ie_index; + /** Mgmt Subtype Mask */ + t_u16 mgmt_subtype_mask; + /** IE Length */ + t_u16 ie_length; + /** IE buffer */ + t_u8 ie_buffer[MAX_IE_SIZE]; +} MLAN_PACK_END custom_ie; + +/** Max IE index to FW */ +#define MAX_MGMT_IE_INDEX_TO_FW 4 +/** Max IE index per BSS */ +#define MAX_MGMT_IE_INDEX 26 + +/** custom IE info */ +typedef MLAN_PACK_START struct _custom_ie_info { + /** size of buffer */ + t_u16 buf_size; + /** no of buffers of buf_size */ + t_u16 buf_count; +} MLAN_PACK_END custom_ie_info; + +/** TLV buffer : Max Mgmt IE */ +typedef MLAN_PACK_START struct _tlvbuf_max_mgmt_ie { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** No of tuples */ + t_u16 count; + /** custom IE info tuples */ + custom_ie_info info[MAX_MGMT_IE_INDEX]; +} MLAN_PACK_END tlvbuf_max_mgmt_ie; + +/** TLV buffer : custom IE */ +typedef MLAN_PACK_START struct _tlvbuf_custom_ie { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; + /** IE data */ + custom_ie ie_data_list[MAX_MGMT_IE_INDEX_TO_FW]; + /** Max mgmt IE TLV */ + tlvbuf_max_mgmt_ie max_mgmt_ie; +} MLAN_PACK_END mlan_ds_misc_custom_ie; + +/** channel width */ +typedef enum wifi_channel_width { + WIFI_CHAN_WIDTH_20 = 0, + WIFI_CHAN_WIDTH_40 = 1, + WIFI_CHAN_WIDTH_80 = 2, + WIFI_CHAN_WIDTH_160 = 3, + WIFI_CHAN_WIDTH_80P80 = 4, + WIFI_CHAN_WIDTH_5 = 5, + WIFI_CHAN_WIDTH_10 = 6, + WIFI_CHAN_WIDTH_INVALID = -1 +} wifi_channel_width_t; + +/** channel information */ +typedef struct { + /** channel width (20, 40, 80, 80+80, 160) */ + wifi_channel_width_t width; + /** primary 20 MHz channel */ + int center_freq; + /** center frequency (MHz) first segment */ + int center_freq0; + /** center frequency (MHz) second segment */ + int center_freq1; +} wifi_channel_info; + +/** wifi rate */ +typedef struct { + /** 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */ + t_u32 preamble : 3; + /** 0:1x1, 1:2x2, 3:3x3, 4:4x4 */ + t_u32 nss : 2; + /** 0:20MHz, 1:40Mhz, 2:80Mhz, 3:160Mhz */ + t_u32 bw : 3; + /** OFDM/CCK rate code would be as per ieee std in the units of 0.5mbps + */ + /** HT/VHT it would be mcs index */ + t_u32 rateMcsIdx : 8; + /** reserved */ + t_u32 reserved : 16; + /** units of 100 Kbps */ + t_u32 bitrate; +} wifi_rate; + +/** wifi Preamble type */ +typedef enum { + WIFI_PREAMBLE_LEGACY = 0x1, + WIFI_PREAMBLE_HT = 0x2, + WIFI_PREAMBLE_VHT = 0x4 +} wifi_preamble; + +/** timeval */ +typedef struct { + /** Time (seconds) */ + t_u32 time_sec; + /** Time (micro seconds) */ + t_u32 time_usec; +} wifi_timeval; + +#define MAX_NUM_RATE 32 +#define MAX_RADIO 2 +#define MAX_NUM_CHAN 1 +#define VHT_NUM_SUPPORT_MCS 10 +#define MCS_NUM_SUPP 16 + +#define BUF_MAXLEN 4096 +/** connection state */ +typedef enum { + MLAN_DISCONNECTED = 0, + MLAN_AUTHENTICATING = 1, + MLAN_ASSOCIATING = 2, + MLAN_ASSOCIATED = 3, + /** if done by firmware/driver */ + MLAN_EAPOL_STARTED = 4, + /** if done by firmware/driver */ + MLAN_EAPOL_COMPLETED = 5, +} mlan_connection_state; +/** roam state */ +typedef enum { + MLAN_ROAMING_IDLE = 0, + MLAN_ROAMING_ACTIVE = 1, +} mlan_roam_state; +/** interface mode */ +typedef enum { + MLAN_INTERFACE_STA = 0, + MLAN_INTERFACE_SOFTAP = 1, + MLAN_INTERFACE_IBSS = 2, + MLAN_INTERFACE_P2P_CLIENT = 3, + MLAN_INTERFACE_P2P_GO = 4, + MLAN_INTERFACE_NAN = 5, + MLAN_INTERFACE_MESH = 6, +} mlan_interface_mode; + +/** set for QOS association */ +#define MLAN_CAPABILITY_QOS 0x00000001 +/** set for protected association (802.11 beacon frame control protected bit + * set) */ +#define MLAN_CAPABILITY_PROTECTED 0x00000002 +/** set if 802.11 Extended Capabilities element interworking bit is set */ +#define MLAN_CAPABILITY_INTERWORKING 0x00000004 +/** set for HS20 association */ +#define MLAN_CAPABILITY_HS20 0x00000008 +/** set is 802.11 Extended Capabilities element UTF-8 SSID bit is set */ +#define MLAN_CAPABILITY_SSID_UTF8 0x00000010 +/** set is 802.11 Country Element is present */ +#define MLAN_CAPABILITY_COUNTRY 0x00000020 + +/** link layer status */ +typedef struct { + /** interface mode */ + mlan_interface_mode mode; + /** interface mac address (self) */ + t_u8 mac_addr[6]; + /** connection state (valid for STA, CLI only) */ + mlan_connection_state state; + /** roaming state */ + mlan_roam_state roaming; + /** WIFI_CAPABILITY_XXX (self) */ + t_u32 capabilities; + /** null terminated SSID */ + t_u8 ssid[33]; + /** bssid */ + t_u8 bssid[6]; + /** country string advertised by AP */ + t_u8 ap_country_str[3]; + /** country string for this association */ + t_u8 country_str[3]; +} mlan_interface_link_layer_info, *mlan_interface_handle; + +/** channel statistics */ +typedef struct { + /** channel */ + wifi_channel_info channel; + /** msecs the radio is awake (32 bits number accruing over time) */ + t_u32 on_time; + /** msecs the CCA register is busy (32 bits number accruing over time) + */ + t_u32 cca_busy_time; +} wifi_channel_stat; + +#define timeval_to_msec(timeval) \ + (t_u64)((t_u64)(timeval.time_sec) * 1000 + \ + (t_u64)(timeval.time_usec) / 1000) +#define timeval_to_usec(timeval) \ + (t_u64)((t_u64)(timeval.time_sec) * 1000 * 1000 + \ + (t_u64)(timeval.time_usec)) +#define is_zero_timeval(timeval) \ + ((timeval.time_sec == 0) && (timeval.time_usec == 0)) + +/** radio statistics */ +typedef struct { + /** wifi radio (if multiple radio supported) */ + int radio; + /** msecs the radio is awake (32 bits number accruing over time) */ + t_u32 on_time; + /** msecs the radio is transmitting (32 bits number accruing over time) + */ + t_u32 tx_time; + /** TBD: num_tx_levels: number of radio transmit power levels */ + t_u32 reserved0; + /** TBD: tx_time_per_levels: pointer to an array of radio transmit per + * power levels in msecs accured over time */ + /* t_u32 *reserved1;*/ + /** msecs the radio is in active receive (32 bits number accruing over + * time) */ + t_u32 rx_time; + /** msecs the radio is awake due to all scan (32 bits number accruing + * over time) */ + t_u32 on_time_scan; + /** msecs the radio is awake due to NAN (32 bits number accruing over + * time) */ + t_u32 on_time_nbd; + /** msecs the radio is awake due to G?scan (32 bits number accruing over + * time) */ + t_u32 on_time_gscan; + /** msecs the radio is awake due to roam?scan (32 bits number accruing + * over time) */ + t_u32 on_time_roam_scan; + /** msecs the radio is awake due to PNO scan (32 bits number accruing + * over time) */ + t_u32 on_time_pno_scan; + /** msecs the radio is awake due to HS2.0 scans and GAS exchange (32 + * bits number accruing over time) */ + t_u32 on_time_hs20; + /** number of channels */ + t_u32 num_channels; + /** channel statistics */ + wifi_channel_stat channels[MAX_NUM_CHAN]; +} wifi_radio_stat; + +/** per rate statistics */ +typedef struct { + /** rate information */ + wifi_rate rate; + /** number of successfully transmitted data pkts (ACK rcvd) */ + t_u32 tx_mpdu; + /** number of received data pkts */ + t_u32 rx_mpdu; + /** number of data packet losses (no ACK) */ + t_u32 mpdu_lost; + /** total number of data pkt retries */ + t_u32 retries; + /** number of short data pkt retries */ + t_u32 retries_short; + /** number of long data pkt retries */ + t_u32 retries_long; +} wifi_rate_stat; + +/** wifi peer type */ +typedef enum { + WIFI_PEER_STA, + WIFI_PEER_AP, + WIFI_PEER_P2P_GO, + WIFI_PEER_P2P_CLIENT, + WIFI_PEER_NAN, + WIFI_PEER_TDLS, + WIFI_PEER_INVALID, +} wifi_peer_type; + +/** per peer statistics */ +typedef struct { + /** peer type (AP, TDLS, GO etc.) */ + wifi_peer_type type; + /** mac address */ + t_u8 peer_mac_address[6]; + /** peer WIFI_CAPABILITY_XXX */ + t_u32 capabilities; + /** number of rates */ + t_u32 num_rate; + /** per rate statistics, number of entries = num_rate */ + wifi_rate_stat rate_stats[]; +} wifi_peer_info; + +/** per access category statistics */ +typedef struct { + /** access category (VI, VO, BE, BK) */ + mlan_wmm_ac_e ac; + /** number of successfully transmitted unicast data pkts (ACK rcvd) */ + t_u32 tx_mpdu; + /** number of received unicast mpdus */ + t_u32 rx_mpdu; + /** number of succesfully transmitted multicast data packets */ + /** STA case: implies ACK received from AP for the unicast packet in + * which mcast pkt was sent */ + t_u32 tx_mcast; + /** number of received multicast data packets */ + t_u32 rx_mcast; + /** number of received unicast a-mpdus */ + t_u32 rx_ampdu; + /** number of transmitted unicast a-mpdus */ + t_u32 tx_ampdu; + /** number of data pkt losses (no ACK) */ + t_u32 mpdu_lost; + /** total number of data pkt retries */ + t_u32 retries; + /** number of short data pkt retries */ + t_u32 retries_short; + /** number of long data pkt retries */ + t_u32 retries_long; + /** data pkt min contention time (usecs) */ + t_u32 contention_time_min; + /** data pkt max contention time (usecs) */ + t_u32 contention_time_max; + /** data pkt avg contention time (usecs) */ + t_u32 contention_time_avg; + /** num of data pkts used for contention statistics */ + t_u32 contention_num_samples; +} wifi_wmm_ac_stat; + +/** interface statistics */ +typedef struct { + /** wifi interface */ + /* wifi_interface_handle iface;*/ + /** current state of the interface */ + mlan_interface_link_layer_info info; + /** access point beacon received count from connected AP */ + t_u32 beacon_rx; + /** Average beacon offset encountered (beacon_TSF - TBTT) + * the average_tsf_offset field is used so as to calculate the + * typical beacon contention time on the channel as well may be + * used to debug beacon synchronization and related power consumption + * issue + */ + t_u64 average_tsf_offset; + /** indicate that this AP typically leaks packets beyond the driver + * guard time */ + t_u32 leaky_ap_detected; + /** average number of frame leaked by AP after frame with PM bit set was + * ACK'ed by AP */ + t_u32 leaky_ap_avg_num_frames_leaked; + /** Guard time currently in force (when implementing IEEE power + * management based on frame control PM bit), How long driver waits + * before shutting down the radio and after receiving an ACK for a data + * frame with PM bit set) + */ + t_u32 leaky_ap_guard_time; + /** access point mgmt frames received count from connected AP (including + * Beacon) */ + t_u32 mgmt_rx; + /** action frames received count */ + t_u32 mgmt_action_rx; + /** action frames transmit count */ + t_u32 mgmt_action_tx; + /** access Point Beacon and Management frames RSSI (averaged) */ + t_s32 rssi_mgmt; + /** access Point Data Frames RSSI (averaged) from connected AP */ + t_s32 rssi_data; + /** access Point ACK RSSI (averaged) from connected AP */ + t_s32 rssi_ack; + /** per ac data packet statistics */ + wifi_wmm_ac_stat ac[MAX_AC_QUEUES]; + /** number of peers */ + t_u32 num_peers; + /** per peer statistics */ + wifi_peer_info peer_info[]; +} wifi_iface_stat; + +/** link layer stat configuration params */ +typedef struct { + /** threshold to classify the pkts as short or long */ + t_u32 mpdu_size_threshold; + /** wifi statistics bitmap */ + t_u32 aggressive_statistics_gathering; +} wifi_link_layer_params; + +/** wifi statistics bitmap */ +#define WIFI_STATS_RADIO 0x00000001 /** all radio statistics */ +#define WIFI_STATS_RADIO_CCA \ + 0x00000002 /** cca_busy_time (within radio statistics) */ +#define WIFI_STATS_RADIO_CHANNELS \ + 0x00000004 /** all channel statistics (within radio statistics) */ +#define WIFI_STATS_RADIO_SCAN \ + 0x00000008 /** all scan statistics (within radio statistics) */ +#define WIFI_STATS_IFACE 0x00000010 /** all interface statistics */ +#define WIFI_STATS_IFACE_TXRATE \ + 0x00000020 /** all tx rate statistics (within interface statistics) */ +#define WIFI_STATS_IFACE_AC \ + 0x00000040 /** all ac statistics (within interface statistics) */ +#define WIFI_STATS_IFACE_CONTENTION \ + 0x00000080 /** all contention (min, max, avg) statistics (within ac \ + statisctics) */ + +/** station stats */ +typedef struct _sta_stats { + t_u64 last_rx_in_msec; +} sta_stats; + +#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)(t_void *pmoal_handle, t_u32 offset, + t_u32 len, t_u8 *pbuf); + mlan_status (*moal_get_vdll_data)(t_void *pmoal_handle, t_u32 len, + t_u8 *pbuf); + /** moal_get_hw_spec_complete */ + mlan_status (*moal_get_hw_spec_complete)(t_void *pmoal_handle, + mlan_status status, + pmlan_hw_info phw, + pmlan_bss_tbl ptbl); + /** moal_init_fw_complete */ + mlan_status (*moal_init_fw_complete)(t_void *pmoal_handle, + mlan_status status); + /** moal_shutdown_fw_complete */ + mlan_status (*moal_shutdown_fw_complete)(t_void *pmoal_handle, + mlan_status status); + /** moal_send_packet_complete */ + mlan_status (*moal_send_packet_complete)(t_void *pmoal_handle, + pmlan_buffer pmbuf, + mlan_status status); + /** moal_recv_complete */ + mlan_status (*moal_recv_complete)(t_void *pmoal_handle, + pmlan_buffer pmbuf, t_u32 port, + mlan_status status); + /** moal_recv_packet */ + mlan_status (*moal_recv_packet)(t_void *pmoal_handle, + pmlan_buffer pmbuf); + /** moal_recv_event */ + mlan_status (*moal_recv_event)(t_void *pmoal_handle, + pmlan_event pmevent); + /** moal_ioctl_complete */ + mlan_status (*moal_ioctl_complete)(t_void *pmoal_handle, + pmlan_ioctl_req pioctl_req, + mlan_status status); + + /** moal_alloc_mlan_buffer */ + mlan_status (*moal_alloc_mlan_buffer)(t_void *pmoal_handle, t_u32 size, + ppmlan_buffer pmbuf); + /** moal_free_mlan_buffer */ + mlan_status (*moal_free_mlan_buffer)(t_void *pmoal_handle, + pmlan_buffer pmbuf); + +#ifdef USB + /** moal_write_data_async */ + mlan_status (*moal_write_data_async)(t_void *pmoal_handle, + pmlan_buffer pmbuf, t_u32 port); +#endif /* USB */ +#if defined(SDIO) || defined(PCIE) + /** moal_write_reg */ + mlan_status (*moal_write_reg)(t_void *pmoal_handle, t_u32 reg, + t_u32 data); + /** moal_read_reg */ + mlan_status (*moal_read_reg)(t_void *pmoal_handle, t_u32 reg, + t_u32 *data); +#endif /* SDIO || PCIE */ + /** moal_write_data_sync */ + mlan_status (*moal_write_data_sync)(t_void *pmoal_handle, + pmlan_buffer pmbuf, t_u32 port, + t_u32 timeout); + /** moal_read_data_sync */ + mlan_status (*moal_read_data_sync)(t_void *pmoal_handle, + pmlan_buffer pmbuf, t_u32 port, + t_u32 timeout); + /** moal_malloc */ + mlan_status (*moal_malloc)(t_void *pmoal_handle, t_u32 size, t_u32 flag, + t_u8 **ppbuf); + /** moal_mfree */ + mlan_status (*moal_mfree)(t_void *pmoal_handle, t_u8 *pbuf); + /** moal_vmalloc */ + mlan_status (*moal_vmalloc)(t_void *pmoal_handle, t_u32 size, + t_u8 **ppbuf); + /** moal_vfree */ + mlan_status (*moal_vfree)(t_void *pmoal_handle, t_u8 *pbuf); +#ifdef PCIE + /** moal_malloc_consistent */ + mlan_status (*moal_malloc_consistent)(t_void *pmoal_handle, t_u32 size, + t_u8 **ppbuf, t_u64 *pbuf_pa); + /** moal_mfree_consistent */ + mlan_status (*moal_mfree_consistent)(t_void *pmoal_handle, t_u32 size, + t_u8 *pbuf, t_u64 buf_pa); + /** moal_map_memory */ + mlan_status (*moal_map_memory)(t_void *pmoal_handle, t_u8 *pbuf, + t_u64 *pbuf_pa, t_u32 size, t_u32 flag); + /** moal_unmap_memory */ + mlan_status (*moal_unmap_memory)(t_void *pmoal_handle, t_u8 *pbuf, + t_u64 buf_pa, t_u32 size, t_u32 flag); +#endif /* PCIE */ + /** moal_memset */ + t_void *(*moal_memset)(t_void *pmoal_handle, t_void *pmem, t_u8 byte, + t_u32 num); + /** moal_memcpy */ + t_void *(*moal_memcpy)(t_void *pmoal_handle, t_void *pdest, + const t_void *psrc, t_u32 num); + /** moal_memcpy_ext */ + t_void *(*moal_memcpy_ext)(t_void *pmoal_handle, t_void *pdest, + const t_void *psrc, t_u32 num, + t_u32 dest_size); + /** moal_memmove */ + t_void *(*moal_memmove)(t_void *pmoal_handle, t_void *pdest, + const t_void *psrc, t_u32 num); + /** moal_memcmp */ + t_s32 (*moal_memcmp)(t_void *pmoal_handle, const t_void *pmem1, + const t_void *pmem2, t_u32 num); + /** moal_udelay */ + t_void (*moal_udelay)(t_void *pmoal_handle, t_u32 udelay); + /** moal_usleep_range */ + t_void (*moal_usleep_range)(t_void *pmoal_handle, t_u32 min_delay, + t_u32 max_delay); + /** moal_get_boot_ktime */ + mlan_status (*moal_get_boot_ktime)(t_void *pmoal_handle, t_u64 *pnsec); + /** moal_get_system_time */ + mlan_status (*moal_get_system_time)(t_void *pmoal_handle, t_u32 *psec, + t_u32 *pusec); + /** moal_init_timer*/ + mlan_status (*moal_init_timer)(t_void *pmoal_handle, t_void **pptimer, + IN t_void (*callback)(t_void *pcontext), + t_void *pcontext); + /** moal_free_timer */ + mlan_status (*moal_free_timer)(t_void *pmoal_handle, t_void *ptimer); + /** moal_start_timer*/ + mlan_status (*moal_start_timer)(t_void *pmoal_handle, t_void *ptimer, + t_u8 periodic, t_u32 msec); + /** moal_stop_timer*/ + mlan_status (*moal_stop_timer)(t_void *pmoal_handle, t_void *ptimer); + /** moal_init_lock */ + mlan_status (*moal_init_lock)(t_void *pmoal_handle, t_void **pplock); + /** moal_free_lock */ + mlan_status (*moal_free_lock)(t_void *pmoal_handle, t_void *plock); + /** moal_spin_lock */ + mlan_status (*moal_spin_lock)(t_void *pmoal_handle, t_void *plock); + /** moal_spin_unlock */ + mlan_status (*moal_spin_unlock)(t_void *pmoal_handle, t_void *plock); + /** moal_print */ + t_void (*moal_print)(t_void *pmoal_handle, t_u32 level, char *pformat, + IN...); + /** moal_print_netintf */ + t_void (*moal_print_netintf)(t_void *pmoal_handle, t_u32 bss_index, + t_u32 level); + /** moal_assert */ + t_void (*moal_assert)(t_void *pmoal_handle, t_u32 cond); + + /** moal_hist_data_add */ + t_void (*moal_hist_data_add)(t_void *pmoal_handle, t_u32 bss_index, + t_u16 rx_rate, t_s8 snr, t_s8 nflr, + t_u8 antenna); +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + mlan_status (*moal_wait_hostcmd_complete)(t_void *pmoal_handle, + t_u32 bss_index); + mlan_status (*moal_notify_hostcmd_complete)(t_void *pmoal_handle, + t_u32 bss_index); +#endif + void (*moal_tp_accounting)(t_void *pmoal_handle, t_void *buf, + t_u32 drop_point); + void (*moal_tp_accounting_rx_param)(t_void *pmoal_handle, + unsigned int type, + unsigned int rsvd1); + +} mlan_callbacks, *pmlan_callbacks; + +/** Parameter unchanged, use MLAN default setting */ +#define ROBUSTCOEX_GPIO_UNCHANGED 0 +/** Parameter enabled, override MLAN default setting */ +#define ROBUSTCOEX_GPIO_CFG 1 + +#if defined(SDIO) +/** Interrupt Mode SDIO */ +#define INT_MODE_SDIO 0 +/** Interrupt Mode GPIO */ +#define INT_MODE_GPIO 1 +/** New mode: GPIO-1 as a duplicated signal of interrupt as appear of SDIO_DAT1 + */ +#define GPIO_INT_NEW_MODE 255 +#endif + +/** 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 + +/** Control bit for stream 2X2 */ +#define FEATURE_CTRL_STREAM_2X2 MBIT(0) +/** Control bit for DFS support */ +#define FEATURE_CTRL_DFS_SUPPORT MBIT(1) +#ifdef USB +/** Control bit for winner check & not wait for FW ready event */ +#define FEATURE_CTRL_USB_NEW_INIT MBIT(2) +#endif +/** Default feature control */ +#define FEATURE_CTRL_DEFAULT 0xffffffff +/** Check if stream 2X2 enabled */ +#define IS_STREAM_2X2(x) ((x)&FEATURE_CTRL_STREAM_2X2) +/** Check if DFS support enabled */ +#define IS_DFS_SUPPORT(x) ((x)&FEATURE_CTRL_DFS_SUPPORT) +#ifdef USB +/** Check if winner check & not wait for FW ready event */ +#define IS_USB_NEW_INIT(x) ((x)&FEATURE_CTRL_USB_NEW_INIT) +#endif + +/* +#define DRV_MODE_NAN MBIT(4) +#define DRV_MODE_11P MBIT(5) +#define DRV_MODE_MAC80211 MBIT(6) +#define DRV_MODE_DFS MBIT(7)*/ +#define DRV_MODE_MASK (MBIT(4) | MBIT(5) | MBIT(6) | MBIT(7)) + +/** 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 +#if defined(SDIO) + /** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ + t_u32 int_mode; + /** GPIO interrupt pin number */ + t_u32 gpio_pin; +#endif +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif + /** allocate fixed buffer size for scan beacon buffer*/ + t_u32 fixed_beacon_buffer; + /** SDIO MPA Tx */ + t_u32 mpa_tx_cfg; + /** SDIO MPA Rx */ + t_u32 mpa_rx_cfg; +#ifdef SDIO + /** SDIO Single port rx aggr */ + t_u8 sdio_rx_aggr_enable; + /* see blk_queue_max_segment_size */ + t_u32 max_seg_size; + /* see blk_queue_max_segments */ + t_u16 max_segs; +#endif + /** Auto deep sleep */ + t_u32 auto_ds; + /** IEEE PS mode */ + t_u32 ps_mode; + /** Max Tx buffer size */ + t_u32 max_tx_buf; +#if defined(STA_SUPPORT) + /** 802.11d configuration */ + t_u32 cfg_11d; +#endif + /** Feature control bitmask */ + t_u32 feature_control; + /** enable/disable rx work */ + t_u8 rx_work; + /** dev cap mask */ + t_u32 dev_cap_mask; + /** oob independent reset */ + t_u32 indrstcfg; + /** dtim interval */ + t_u16 multi_dtim; + /** IEEE ps inactivity timeout value */ + t_u16 inact_tmo; + /** card type */ + t_u16 card_type; + /** card rev */ + t_u8 card_rev; + /** Host sleep wakeup interval */ + t_u32 hs_wake_interval; + /** GPIO to indicate wakeup source */ + t_u8 indication_gpio; + /** Dynamic MIMO-SISO switch for hscfg*/ + t_u8 hs_mimo_switch; +#ifdef USB + /** Tx CMD endpoint address */ + t_u8 tx_cmd_ep; + /** Rx CMD/EVT endpoint address */ + t_u8 rx_cmd_ep; + + /** Rx data endpoint address */ + t_u8 rx_data_ep; + /** Tx data endpoint address */ + t_u8 tx_data_ep; +#endif + /** fw region */ + t_bool fw_region; + /** passive to active scan */ + t_u8 passive_to_active_scan; + /** uap max supported station per chip */ + t_u8 uap_max_sta; + /** drv mode */ + t_u32 drv_mode; + /** dfs w53 cfg */ + t_u8 dfs53cfg; +} mlan_device, *pmlan_device; + +/** MLAN API function prototype */ +#define MLAN_API + +/** Registration */ +MLAN_API mlan_status mlan_register(pmlan_device pmdevice, + t_void **ppmlan_adapter); + +/** Un-registration */ +MLAN_API mlan_status mlan_unregister(t_void *pmlan_adapter); + +/** Firmware Downloading */ +MLAN_API mlan_status mlan_dnld_fw(t_void *pmlan_adapter, pmlan_fw_image pmfw); + +/** Custom data pass API */ +MLAN_API mlan_status mlan_set_init_param(t_void *pmlan_adapter, + pmlan_init_param pparam); + +/** Firmware Initialization */ +MLAN_API mlan_status mlan_init_fw(t_void *pmlan_adapter); + +/** Firmware Shutdown */ +MLAN_API mlan_status mlan_shutdown_fw(t_void *pmlan_adapter); + +/** Main Process */ +MLAN_API mlan_status mlan_main_process(t_void *pmlan_adapter); + +/** Rx process */ +mlan_status mlan_rx_process(t_void *pmlan_adapter, t_u8 *rx_pkts); + +/** Packet Transmission */ +MLAN_API mlan_status mlan_send_packet(t_void *pmlan_adapter, + pmlan_buffer pmbuf); + +#ifdef USB +/** mlan_write_data_async_complete */ +MLAN_API mlan_status mlan_write_data_async_complete(t_void *pmlan_adapter, + pmlan_buffer pmbuf, + t_u32 port, + mlan_status status); + +/** Packet Reception */ +MLAN_API mlan_status mlan_recv(t_void *pmlan_adapter, pmlan_buffer pmbuf, + t_u32 port); +#endif /* USB */ + +/** Packet Reception complete callback */ +MLAN_API mlan_status mlan_recv_packet_complete(t_void *pmlan_adapter, + pmlan_buffer pmbuf, + mlan_status status); + +#if defined(SDIO) || defined(PCIE) +/** interrupt handler */ +MLAN_API mlan_status mlan_interrupt(t_u16 msg_id, t_void *pmlan_adapter); + +#if defined(SYSKT) +/** GPIO IRQ callback function */ +MLAN_API t_void mlan_hs_callback(t_void *pctx); +#endif /* SYSKT_MULTI || SYSKT */ +#endif /* SDIO || PCIE */ + +MLAN_API t_void mlan_pm_wakeup_card(t_void *pmlan_adapter, t_u8 keep_wakeup); + +MLAN_API t_u8 mlan_is_main_process_running(t_void *adapter); +#ifdef PCIE +MLAN_API t_void mlan_set_int_mode(t_void *adapter, t_u32 int_mode, + t_u8 func_num); +#endif +/** mlan ioctl */ +MLAN_API mlan_status mlan_ioctl(t_void *pmlan_adapter, + pmlan_ioctl_req pioctl_req); +/** mlan select wmm queue */ +MLAN_API t_u8 mlan_select_wmm_queue(t_void *pmlan_adapter, t_u8 bss_num, + t_u8 tid); + +#endif /* !_MLAN_DECL_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/mlan_ieee.h b/mxm_wifiex/wlan_src/mlinux/mlan_ieee.h new file mode 100644 index 0000000..03b5fd4 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/mlan_ieee.h @@ -0,0 +1,1888 @@ +/** @file mlan_ieee.h + * + * @brief This file contains IEEE information element related + * definitions used in MLAN and MOAL module. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/****************************************************** +Change log: + 11/03/2008: initial version +******************************************************/ + +#ifndef _MLAN_IEEE_H_ +#define _MLAN_IEEE_H_ + +/** FIX IES size in beacon buffer */ +#define WLAN_802_11_FIXED_IE_SIZE 12 +/** WLAN supported rates */ +#define WLAN_SUPPORTED_RATES 14 + +/** WLAN supported rates extension */ +#define WLAN_SUPPORTED_RATES_EXT 60 + +/** Enumeration definition*/ +/** WLAN_802_11_NETWORK_TYPE */ +typedef enum _WLAN_802_11_NETWORK_TYPE { + Wlan802_11FH, + Wlan802_11DS, + /* Defined as upper bound*/ + Wlan802_11NetworkTypeMax +} WLAN_802_11_NETWORK_TYPE; + +#ifdef BIG_ENDIAN_SUPPORT +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x3000 +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc)&0xF000) >> 12) +#else +/** Frame control: Type Mgmt frame */ +#define IEEE80211_FC_MGMT_FRAME_TYPE_MASK 0x000C +/** Frame control: SubType Mgmt frame */ +#define IEEE80211_GET_FC_MGMT_FRAME_SUBTYPE(fc) (((fc)&0x00F0) >> 4) +#endif + +#ifdef PRAGMA_PACK +#pragma pack(push, 1) +#endif + +/* Reason codes */ +#define IEEE_80211_REASONCODE_UNSPECIFIED 1 + +typedef enum _IEEEtypes_Ext_ElementId_e { + HE_CAPABILITY = 35, + HE_OPERATION = 36 +} IEEEtypes_Ext_ElementId_e; + +/** IEEE Type definitions */ +typedef MLAN_PACK_START enum _IEEEtypes_ElementId_e { + SSID = 0, + SUPPORTED_RATES = 1, + + FH_PARAM_SET = 2, + DS_PARAM_SET = 3, + CF_PARAM_SET = 4, + + IBSS_PARAM_SET = 6, + COUNTRY_INFO = 7, + POWER_CONSTRAINT = 32, + POWER_CAPABILITY = 33, + TPC_REQUEST = 34, + TPC_REPORT = 35, + CHANNEL_SWITCH_ANN = 37, + EXTEND_CHANNEL_SWITCH_ANN = 60, + QUIET = 40, + IBSS_DFS = 41, + SUPPORTED_CHANNELS = 36, + REGULATORY_CLASS = 59, + HT_CAPABILITY = 45, + QOS_INFO = 46, + HT_OPERATION = 61, + MULTI_BSSID = 71, + BSSCO_2040 = 72, + OVERLAPBSSSCANPARAM = 74, + NONTX_BSSID_CAP = 83, + MBSSID_INDEX = 85, + EXT_CAPABILITY = 127, + /*IEEE802.11r*/ + MOBILITY_DOMAIN = 54, + FAST_BSS_TRANSITION = 55, + TIMEOUT_INTERVAL = 56, + RIC = 57, + QOS_MAPPING = 110, + VHT_CAPABILITY = 191, + VHT_OPERATION = 192, + EXT_BSS_LOAD = 193, + BW_CHANNEL_SWITCH = 194, + VHT_TX_POWER_ENV = 195, + EXT_POWER_CONSTR = 196, + AID_INFO = 197, + QUIET_CHAN = 198, + OPER_MODE_NTF = 199, + + ERP_INFO = 42, + + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + WMM_IE = VENDOR_SPECIFIC_221, + + WPS_IE = VENDOR_SPECIFIC_221, + /* WPA */ + WPA_IE = VENDOR_SPECIFIC_221, + /* WPA2 */ + RSN_IE = 48, + VS_IE = VENDOR_SPECIFIC_221, + WAPI_IE = 68, + FRAGMENT = 242, + EXTENSION = 255 +} MLAN_PACK_END IEEEtypes_ElementId_e; + +/** IEEE IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_Header_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; +} MLAN_PACK_END IEEEtypes_Header_t, *pIEEEtypes_Header_t; + +/** Vendor specific IE header */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorHeader_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** OUI */ + t_u8 oui[3]; + /** OUI type */ + t_u8 oui_type; + /** OUI subtype */ + t_u8 oui_subtype; + /** Version */ + t_u8 version; +} MLAN_PACK_END IEEEtypes_VendorHeader_t, *pIEEEtypes_VendorHeader_t; + +/** Vendor specific IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VendorSpecific_t { + /** Vendor specific IE header */ + IEEEtypes_VendorHeader_t vend_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_VendorHeader_t)]; +} MLAN_PACK_END IEEEtypes_VendorSpecific_t, *pIEEEtypes_VendorSpecific_t; + +/** IEEE IE */ +typedef MLAN_PACK_START struct _IEEEtypes_Generic_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** IE Max - size of previous fields */ + t_u8 data[IEEE_MAX_IE_SIZE - sizeof(IEEEtypes_Header_t)]; +} MLAN_PACK_END IEEEtypes_Generic_t, *pIEEEtypes_Generic_t; + +/**ft capability policy*/ +typedef MLAN_PACK_START struct _IEEEtypes_FtCapPolicy_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved : 6; + /** RIC support */ + t_u8 ric : 1; + /** FT over the DS capable */ + t_u8 ft_over_ds : 1; +#else + /** FT over the DS capable */ + t_u8 ft_over_ds : 1; + /** RIC support */ + t_u8 ric : 1; + /** Reserved */ + t_u8 reserved : 6; +#endif +} MLAN_PACK_END IEEEtypes_FtCapPolicy_t; + +/** Mobility domain IE */ +typedef MLAN_PACK_START struct _IEEEtypes_MobilityDomain_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Mobility Domain ID */ + t_u16 mdid; + /** FT Capability policy */ + t_u8 ft_cap; +} MLAN_PACK_END IEEEtypes_MobilityDomain_t; + +/**FT MIC Control*/ +typedef MLAN_PACK_START struct _IEEEtypes_FT_MICControl_t { + /** reserved */ + t_u8 reserved; + /** element count */ + t_u8 element_count; +} MLAN_PACK_END IEEEtypes_FT_MICControl_t; + +/** FTIE MIC LEN */ +#define FTIE_MIC_LEN 16 + +/**FT IE*/ +typedef MLAN_PACK_START struct _IEEEtypes_FastBssTransElement_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** mic control */ + IEEEtypes_FT_MICControl_t mic_control; + /** mic */ + t_u8 mic[FTIE_MIC_LEN]; + /** ANonce */ + t_u8 a_nonce[32]; + /** SNonce */ + t_u8 s_nonce[32]; + /** sub element */ + t_u8 sub_element[1]; +} MLAN_PACK_END IEEEtypes_FastBssTransElement_t; + +/*Category for FT*/ +#define FT_CATEGORY 6 +/** FT ACTION request */ +#define FT_ACTION_REQUEST 1 +/** FT ACTION response */ +#define FT_ACTION_RESPONSE 2 + +/*FT response and FT ack*/ +typedef MLAN_PACK_START struct { + /** category */ + t_u8 category; + /** action */ + t_u8 action; + /** sta address */ + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + /** target ap address */ + t_u8 target_ap_addr[MLAN_MAC_ADDR_LENGTH]; + /** status code */ + t_u16 status_code; + /** varible */ + t_u8 variable[]; +} MLAN_PACK_END IEEEtypes_Ft_action_response; + +/**FT request */ +typedef MLAN_PACK_START struct { + /** category */ + t_u8 category; + /** action */ + t_u8 action; + /** sta address */ + t_u8 sta_addr[MLAN_MAC_ADDR_LENGTH]; + /** target ap address */ + t_u8 target_ap_addr[MLAN_MAC_ADDR_LENGTH]; + /** varible */ + t_u8 variable[]; +} MLAN_PACK_END IEEEtypes_Ft_action_request; + +/** auth frame body*/ +typedef MLAN_PACK_START struct { + /** auth alg */ + t_u16 auth_alg; + /** auth transaction */ + t_u16 auth_transaction; + /** status code */ + t_u16 status_code; + /** variable */ + t_u8 variable[]; +} MLAN_PACK_END IEEEtypes_Auth_framebody; + +/** associate request frame */ +typedef MLAN_PACK_START struct { + t_u16 capab_info; + t_u16 listen_interval; + /** followed by SSID and Supported rates */ + t_u8 variablep[]; +} MLAN_PACK_END IEEEtypes_assoc_req; + +/*Mgmt frame*/ +typedef MLAN_PACK_START struct { + /** frame control */ + t_u16 frame_control; + /** duration */ + t_u16 duration; + /** dest address */ + t_u8 da[MLAN_MAC_ADDR_LENGTH]; + /** source address */ + t_u8 sa[MLAN_MAC_ADDR_LENGTH]; + /** bssid */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** seq control */ + t_u16 seq_ctrl; + /** address 4 */ + t_u8 addr4[MLAN_MAC_ADDR_LENGTH]; + union { + IEEEtypes_Auth_framebody auth; + IEEEtypes_assoc_req assoc_req; + IEEEtypes_Ft_action_response ft_resp; + IEEEtypes_Ft_action_request ft_req; + } u; +} MLAN_PACK_END IEEE80211_MGMT; + +/** TLV header */ +typedef MLAN_PACK_START struct _TLV_Generic_t { + /** Type */ + t_u16 type; + /** Length */ + t_u16 len; +} MLAN_PACK_END TLV_Generic_t, *pTLV_Generic_t; + +/** Capability information mask */ +#define CAPINFO_MASK (~(MBIT(15) | MBIT(14) | MBIT(11) | MBIT(9))) + +/** Capability Bit Map*/ +#ifdef BIG_ENDIAN_SUPPORT +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t { + t_u8 rsrvd1 : 2; + t_u8 dsss_ofdm : 1; + t_u8 radio_measurement : 1; + t_u8 rsvrd2 : 1; + t_u8 short_slot_time : 1; + t_u8 rsrvd3 : 1; + t_u8 spectrum_mgmt : 1; + t_u8 chan_agility : 1; + t_u8 pbcc : 1; + t_u8 short_preamble : 1; + t_u8 privacy : 1; + t_u8 cf_poll_rqst : 1; + t_u8 cf_pollable : 1; + t_u8 ibss : 1; + t_u8 ess : 1; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#else +typedef MLAN_PACK_START struct _IEEEtypes_CapInfo_t { + /** Capability Bit Map : ESS */ + t_u8 ess : 1; + /** Capability Bit Map : IBSS */ + t_u8 ibss : 1; + /** Capability Bit Map : CF pollable */ + t_u8 cf_pollable : 1; + /** Capability Bit Map : CF poll request */ + t_u8 cf_poll_rqst : 1; + /** Capability Bit Map : privacy */ + t_u8 privacy : 1; + /** Capability Bit Map : Short preamble */ + t_u8 short_preamble : 1; + /** Capability Bit Map : PBCC */ + t_u8 pbcc : 1; + /** Capability Bit Map : Channel agility */ + t_u8 chan_agility : 1; + /** Capability Bit Map : Spectrum management */ + t_u8 spectrum_mgmt : 1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd3 : 1; + /** Capability Bit Map : Short slot time */ + t_u8 short_slot_time : 1; + /** Capability Bit Map : APSD */ + t_u8 Apsd : 1; + /** Capability Bit Map : Reserved */ + t_u8 rsvrd2 : 1; + /** Capability Bit Map : DSS OFDM */ + t_u8 dsss_ofdm : 1; + /** Capability Bit Map : Reserved */ + t_u8 rsrvd1 : 2; +} MLAN_PACK_END IEEEtypes_CapInfo_t, *pIEEEtypes_CapInfo_t; +#endif /* BIG_ENDIAN_SUPPORT */ + +/** IEEEtypes_Ssid_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Ssid_t { + /** SSID: Element ID */ + t_u8 element_id; + /** SSID : Length */ + t_u8 len; + /** ssid */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; +} MLAN_PACK_END IEEEtypes_Ssid_t, *pIEEEtypes_Ssid_t; + +/** IEEEtypes_CfParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_CfParamSet_t { + /** CF peremeter : Element ID */ + t_u8 element_id; + /** CF peremeter : Length */ + t_u8 len; + /** CF peremeter : Count */ + t_u8 cfp_cnt; + /** CF peremeter : Period */ + t_u8 cfp_period; + /** CF peremeter : Maximum duration */ + t_u16 cfp_max_duration; + /** CF peremeter : Remaining duration */ + t_u16 cfp_duration_remaining; +} MLAN_PACK_END IEEEtypes_CfParamSet_t, *pIEEEtypes_CfParamSet_t; + +/** IEEEtypes_IbssParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_IbssParamSet_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ATIM window value in milliseconds */ + t_u16 atim_window; +} MLAN_PACK_END IEEEtypes_IbssParamSet_t, *pIEEEtypes_IbssParamSet_t; + +/** IEEEtypes_SsParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_SsParamSet_t { + /** SS parameter : CF parameter set */ + IEEEtypes_CfParamSet_t cf_param_set; + /** SS parameter : IBSS parameter set */ + IEEEtypes_IbssParamSet_t ibss_param_set; +} MLAN_PACK_END IEEEtypes_SsParamSet_t, *pIEEEtypes_SsParamSet_t; + +/** IEEEtypes_FhParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_FhParamSet_t { + /** FH parameter : Element ID */ + t_u8 element_id; + /** FH parameter : Length */ + t_u8 len; + /** FH parameter : Dwell time in milliseconds */ + t_u16 dwell_time; + /** FH parameter : Hop set */ + t_u8 hop_set; + /** FH parameter : Hop pattern */ + t_u8 hop_pattern; + /** FH parameter : Hop index */ + t_u8 hop_index; +} MLAN_PACK_END IEEEtypes_FhParamSet_t, *pIEEEtypes_FhParamSet_t; + +/** IEEEtypes_DsParamSet_t */ +typedef MLAN_PACK_START struct _IEEEtypes_DsParamSet_t { + /** DS parameter : Element ID */ + t_u8 element_id; + /** DS parameter : Length */ + t_u8 len; + /** DS parameter : Current channel */ + t_u8 current_chan; +} MLAN_PACK_END IEEEtypes_DsParamSet_t, *pIEEEtypes_DsParamSet_t; + +/** IEEEtypes_PhyParamSet_t */ +typedef MLAN_PACK_START union _IEEEtypes_PhyParamSet_t { + /** FH parameter set */ + IEEEtypes_FhParamSet_t fh_param_set; + /** DS parameter set */ + IEEEtypes_DsParamSet_t ds_param_set; +} MLAN_PACK_END IEEEtypes_PhyParamSet_t, *pIEEEtypes_PhyParamSet_t; + +/** IEEEtypes_ERPInfo_t */ +typedef MLAN_PACK_START struct _IEEEtypes_ERPInfo_t { + /** Element ID */ + t_u8 element_id; + /** Length */ + t_u8 len; + /** ERP flags */ + t_u8 erp_flags; +} MLAN_PACK_END IEEEtypes_ERPInfo_t, *pIEEEtypes_ERPInfo_t; + +/** IEEEtypes_AId_t */ +typedef t_u16 IEEEtypes_AId_t; + +/** IEEEtypes_StatusCode_t */ +typedef t_u16 IEEEtypes_StatusCode_t; + +/** Fixed size in assoc_resp */ +#define ASSOC_RESP_FIXED_SIZE 6 + +/** IEEEtypes_SeqCtl_t */ +typedef MLAN_PACK_START struct _IEEEtypes_SeqCtl_t { + /** Fragment Number */ + t_u16 FragNum : 4; + /** Sequence Number */ + t_u16 SeqNum : 12; +} MLAN_PACK_END IEEEtypes_SeqCtl_t; + +/** IEEEtypes_MgmtHdr_t */ +typedef MLAN_PACK_START struct _IEEEtypes_MgmtHdr_t { + /** FrmCtl*/ + t_u16 FrmCtl; + /** Duration*/ + t_u16 Duration; + /** Destination Addr*/ + t_u8 DestAddr[6]; + /** Source Addr*/ + t_u8 SrcAddr[6]; + /** BSSID */ + t_u8 BssId[6]; + /** IEEEtypes_SeqCtl_t */ + IEEEtypes_SeqCtl_t SeqCtl; +} MLAN_PACK_END IEEEtypes_MgmtHdr_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 +/** AKM: PSK SHA256 */ +#define RSN_AKM_PSK_SHA256 6 + +/** AKM: PSK SHA256 */ +#define RSN_AKM_SAE 8 +/** AKM: PSK SHA256 */ +#define RSN_AKM_OWE 18 + +#if defined(STA_SUPPORT) +/** Pairwise Cipher Suite length */ +#define PAIRWISE_CIPHER_SUITE_LEN 4 +/** AKM Suite length */ +#define AKM_SUITE_LEN 4 +/** MFPC bit in RSN capability */ +#define MFPC_BIT 7 +/** MFPR bit in RSN capability */ +#define MFPR_BIT 6 +/** PMF ORing mask */ +#define PMF_MASK 0x00c0 +#endif + +/** wpa_suite_t */ +typedef MLAN_PACK_START struct _wpa_suite_t { + /** OUI */ + t_u8 oui[3]; + /** tyep */ + t_u8 type; +} MLAN_PACK_END wpa_suite, wpa_suite_mcast_t; + +/** wpa_suite_ucast_t */ +typedef MLAN_PACK_START struct { + /* count */ + t_u16 count; + /** wpa_suite list */ + wpa_suite list[1]; +} MLAN_PACK_END wpa_suite_ucast_t, wpa_suite_auth_key_mgmt_t; + +/** IEEEtypes_Rsn_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Rsn_t { + /** Rsn : Element ID */ + t_u8 element_id; + /** Rsn : Length */ + t_u8 len; + /** Rsn : version */ + t_u16 version; + /** Rsn : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Rsn : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Rsn_t, *pIEEEtypes_Rsn_t; + +/** IEEEtypes_Wpa_t */ +typedef MLAN_PACK_START struct _IEEEtypes_Wpa_t { + /** Wpa : Element ID */ + t_u8 element_id; + /** Wpa : Length */ + t_u8 len; + /** Wpa : oui */ + t_u8 oui[4]; + /** version */ + t_u16 version; + /** Wpa : group cipher */ + wpa_suite_mcast_t group_cipher; + /** Wpa : pairwise cipher */ + wpa_suite_ucast_t pairwise_cipher; +} MLAN_PACK_END IEEEtypes_Wpa_t, *pIEEEtypes_Wpa_t; + +/** Data structure of WMM QoS information */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmQosInfo_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; + /** Reserved */ + t_u8 reserved : 3; + /** Parameter set count */ + t_u8 para_set_count : 4; +#else + /** Parameter set count */ + t_u8 para_set_count : 4; + /** Reserved */ + t_u8 reserved : 3; + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmQosInfo_t, *pIEEEtypes_WmmQosInfo_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAciAifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved : 1; + /** Aci */ + t_u8 aci : 2; + /** Acm */ + t_u8 acm : 1; + /** Aifsn */ + t_u8 aifsn : 4; +#else + /** Aifsn */ + t_u8 aifsn : 4; + /** Acm */ + t_u8 acm : 1; + /** Aci */ + t_u8 aci : 2; + /** Reserved */ + t_u8 reserved : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmAciAifsn_t, *pIEEEtypes_WmmAciAifsn_t; + +/** Data structure of WMM ECW */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmEcw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max : 4; + /** Minimum Ecw */ + t_u8 ecw_min : 4; +#else + /** Minimum Ecw */ + t_u8 ecw_min : 4; + /** Maximum Ecw */ + t_u8 ecw_max : 4; +#endif /* BIG_ENDIAN_SUPPORT */ +} MLAN_PACK_END IEEEtypes_WmmEcw_t, *pIEEEtypes_WmmEcw_t; + +/** Data structure of WMM AC parameters */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmAcParameters_t { + IEEEtypes_WmmAciAifsn_t aci_aifsn; /**< AciAifSn */ + IEEEtypes_WmmEcw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} MLAN_PACK_END IEEEtypes_WmmAcParameters_t, *pIEEEtypes_WmmAcParameters_t; + +/** Data structure of WMM Info IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmInfo_t { + /** + * WMM Info IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [7] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [0] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + +} MLAN_PACK_END IEEEtypes_WmmInfo_t, *pIEEEtypes_WmmInfo_t; + +/** Data structure of WMM parameter IE */ +typedef MLAN_PACK_START struct _IEEEtypes_WmmParameter_t { + /** + * WMM Parameter IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [24] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [1] + * Version [1] + */ + IEEEtypes_VendorHeader_t vend_hdr; + + /** QoS information */ + IEEEtypes_WmmQosInfo_t qos_info; + /** Reserved */ + t_u8 reserved; + + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + IEEEtypes_WmmAcParameters_t ac_params[MAX_AC_QUEUES]; +} MLAN_PACK_END IEEEtypes_WmmParameter_t, *pIEEEtypes_WmmParameter_t; + +/** Enumerator for TSPEC direction */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_Direction_e { + + TSPEC_DIR_UPLINK = 0, + TSPEC_DIR_DOWNLINK = 1, + /* 2 is a reserved value */ + TSPEC_DIR_BIDIRECT = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_Direction_e; + +/** Enumerator for TSPEC PSB */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_PSB_e { + + TSPEC_PSB_LEGACY = 0, + TSPEC_PSB_TRIG = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_PSB_e; + +/** Enumerator for TSPEC Ack Policy */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e { + + TSPEC_ACKPOLICY_NORMAL = 0, + TSPEC_ACKPOLICY_NOACK = 1, + /* 2 is reserved */ + TSPEC_ACKPOLICY_BLOCKACK = 3, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e; + +/** Enumerator for TSPEC Trafffice type */ +typedef MLAN_PACK_START enum _IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e { + + TSPEC_TRAFFIC_APERIODIC = 0, + TSPEC_TRAFFIC_PERIODIC = 1, + +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e; + +/** Data structure of WMM TSPEC information */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u8 Reserved17_23 : 7; /* ! Reserved */ + t_u8 Schedule : 1; + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy : 2; + t_u8 UserPri : 3; /* ! 802.1d User Priority */ + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior : 1; /* ! + Legacy/Trigg + */ + t_u8 Aggregation : 1; /* ! Reserved */ + t_u8 AccessPolicy2 : 1; /* ! */ + t_u8 AccessPolicy1 : 1; /* ! */ + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction : 2; + t_u8 TID : 4; /* ! Unique identifier */ + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType : 1; +#else + IEEEtypes_WMM_TSPEC_TS_TRAFFIC_TYPE_e TrafficType : 1; + t_u8 TID : 4; /* ! Unique identifier */ + IEEEtypes_WMM_TSPEC_TS_Info_Direction_e Direction : 2; + t_u8 AccessPolicy1 : 1; /* ! */ + t_u8 AccessPolicy2 : 1; /* ! */ + t_u8 Aggregation : 1; /* ! Reserved */ + IEEEtypes_WMM_TSPEC_TS_Info_PSB_e PowerSaveBehavior : 1; /* ! + Legacy/Trigg + */ + t_u8 UserPri : 3; /* ! 802.1d User Priority */ + IEEEtypes_WMM_TSPEC_TS_Info_AckPolicy_e AckPolicy : 2; + t_u8 Schedule : 1; + t_u8 Reserved17_23 : 7; /* ! Reserved */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_TS_Info_t; + +/** Data structure of WMM TSPEC Nominal Size */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Fixed : 1; /* ! 1: Fixed size given in Size, 0: Var, size is + nominal */ + t_u16 Size : 15; /* ! Nominal size in octets */ +#else + t_u16 Size : 15; /* ! Nominal size in octets */ + t_u16 Fixed : 1; /* ! 1: Fixed size given in Size, 0: Var, size is + nominal */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_NomMSDUSize_t; + +/** Data structure of WMM TSPEC SBWA */ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + t_u16 Whole : 3; /* ! Whole portion */ + t_u16 Fractional : 13; /* ! Fractional portion */ +#else + t_u16 Fractional : 13; /* ! Fractional portion */ + t_u16 Whole : 3; /* ! Whole portion */ +#endif +} MLAN_PACK_END IEEEtypes_WMM_TSPEC_SBWA; + +/** Data structure of WMM TSPEC Body */ +typedef MLAN_PACK_START struct { + 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; + +#define MAX_DSCP_EXCEPTION_NUM 21 +/** DSCP Range */ +typedef struct MLAN_PACK_START _DSCP_Exception_t { + /* DSCP value 0 to 63 or ff */ + t_u8 dscp_value; + /* user priority 0-7*/ + t_u8 user_priority; +} MLAN_PACK_END DSCP_Exception_t, *pDSCP_Exception_t; + +/** DSCP Range */ +typedef struct MLAN_PACK_START _DSCP_Range_t { + /* DSCP low value */ + t_u8 dscp_low_value; + /* DSCP high value */ + t_u8 dscp_high_value; +} MLAN_PACK_END DSCP_Range_t, *pDSCP_Range_t; + +/** Overlapping BSS Scan Parameters Data */ +typedef struct MLAN_PACK_START _OverlapBSSScanParam_t { + /** OBSS Scan Passive Dwell in milliseconds */ + t_u16 obss_scan_passive_dwell; + /** OBSS Scan Active Dwell in milliseconds */ + t_u16 obss_scan_active_dwell; + /** BSS Channel Width Trigger Scan Interval in seconds */ + t_u16 bss_chan_width_trigger_scan_int; + /** OBSS Scan Passive Total Per Channel */ + t_u16 obss_scan_passive_total; + /** OBSS Scan Active Total Per Channel */ + t_u16 obss_scan_active_total; + /** BSS Width Channel Transition Delay Factor */ + t_u16 bss_width_chan_trans_delay; + /** OBSS Scan Activity Threshold */ + t_u16 obss_scan_active_threshold; +} MLAN_PACK_END OBSSScanParam_t, *pOBSSScanParam_t; + +/** HT Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTCap struct */ + HTCap_t ht_cap; +} MLAN_PACK_END IEEEtypes_HTCap_t, *pIEEEtypes_HTCap_t; + +/** HT Information IE */ +typedef MLAN_PACK_START struct _IEEEtypes_HTInfo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** HTInfo struct */ + HTInfo_t ht_info; +} MLAN_PACK_END IEEEtypes_HTInfo_t, *pIEEEtypes_HTInfo_t; + +/** 20/40 BSS Coexistence IE */ +typedef MLAN_PACK_START struct _IEEEtypes_2040BSSCo_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** BSSCo2040_t struct */ + BSSCo2040_t bss_co_2040; +} MLAN_PACK_END IEEEtypes_2040BSSCo_t, *pIEEEtypes_2040BSSCo_t; + +/** Extended Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_ExtCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** ExtCap_t struct */ + ExtCap_t ext_cap; +} MLAN_PACK_END IEEEtypes_ExtCap_t, *pIEEEtypes_ExtCap_t; + +/** Overlapping BSS Scan Parameters IE */ +typedef MLAN_PACK_START struct _IEEEtypes_OverlapBSSScanParam_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** OBSSScanParam_t struct */ + OBSSScanParam_t obss_scan_param; +} MLAN_PACK_END IEEEtypes_OverlapBSSScanParam_t, + *pIEEEtypes_OverlapBSSScanParam_t; + +/** VHT MCS rate set field, refer to 802.11ac */ +typedef MLAN_PACK_START struct _VHT_MCS_set { + t_u16 rx_mcs_map; + t_u16 rx_max_rate; /* bit 29-31 reserved */ + t_u16 tx_mcs_map; + t_u16 tx_max_rate; /* bit 61-63 reserved */ +} MLAN_PACK_END VHT_MCS_set_t, *pVHT_MCS_set_t; + +/** VHT Capabilities info field, reference 802.11ac D1.4 p89 */ +typedef MLAN_PACK_START struct _VHT_capa { +#if 0 +#ifdef BIG_ENDIAN_SUPPORT + t_u8 mpdu_max_len:2; + t_u8 chan_width:2; + t_u8 rx_LDPC:1; + t_u8 sgi_80:1; + t_u8 sgi_160:1; + t_u8 tx_STBC:1; + t_u8 rx_STBC:3; + t_u8 SU_beamformer_capa:1; + t_u8 SU_beamformee_capa:1; + t_u8 beamformer_ante_num:3; + t_u8 sounding_dim_num:3; + t_u8 MU_beamformer_capa:1; + t_u8 MU_beamformee_capa:1; + t_u8 VHT_TXOP_ps:1; + t_u8 HTC_VHT_capa:1; + t_u8 max_ampdu_len:3; + t_u8 link_apapt_capa:2; + t_u8 reserved_1:4; +#else + t_u8 reserved_1:4; + t_u8 link_apapt_capa:2; + t_u8 max_ampdu_len:3; + t_u8 HTC_VHT_capa:1; + t_u8 VHT_TXOP_ps:1; + t_u8 MU_beamformee_capa:1; + t_u8 MU_beamformer_capa:1; + t_u8 sounding_dim_num:3; + t_u8 beamformer_ante_num:3; + t_u8 SU_beamformee_capa:1; + t_u8 SU_beamformer_capa:1; + t_u8 rx_STBC:3; + t_u8 tx_STBC:1; + t_u8 sgi_160:1; + t_u8 sgi_80:1; + t_u8 rx_LDPC:1; + t_u8 chan_width:2; + t_u8 mpdu_max_len:2; +#endif /* BIG_ENDIAN_SUPPORT */ +#endif + t_u32 vht_cap_info; + VHT_MCS_set_t mcs_sets; +} MLAN_PACK_END VHT_capa_t, *pVHT_capa_t; + +/** VHT Capabilities IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VHTCap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + VHT_capa_t vht_cap; +} MLAN_PACK_END IEEEtypes_VHTCap_t, *pIEEEtypes_VHTCap_t; + +#define VHT_CAP_CHWD_80MHZ 0 +#define VHT_CAP_CHWD_160MHZ 1 +#define VHT_CAP_CHWD_80_80MHZ 2 + +/** VHT Operations IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VHTOprat_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 chan_width; + t_u8 chan_center_freq_1; + t_u8 chan_center_freq_2; + /** Basic MCS set map, each 2 bits stands for a Nss */ + t_u16 basic_MCS_map; +} MLAN_PACK_END IEEEtypes_VHTOprat_t, *pIEEEtypes_VHTOprat_t; + +#define VHT_OPER_CHWD_20_40MHZ 0 +#define VHT_OPER_CHWD_80MHZ 1 +#define VHT_OPER_CHWD_160MHZ 2 +#define VHT_OPER_CHWD_80_80MHZ 3 + +/** VHT Transmit Power Envelope IE */ +typedef MLAN_PACK_START struct _IEEEtypes_VHTtxpower_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 max_tx_power; + t_u8 chan_center_freq; + t_u8 chan_width; +} MLAN_PACK_END IEEEtypes_VHTtxpower_t, *pIEEEtypes_VHTtxpower_t; + +/** Extended Power Constraint IE */ +typedef MLAN_PACK_START struct _IEEEtypes_ExtPwerCons_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** channel width */ + t_u8 chan_width; + /** local power constraint */ + t_u8 local_power_cons; +} MLAN_PACK_END IEEEtypes_ExtPwerCons_t, *pIEEEtypes_ExtPwerCons_t; + +/** Extended BSS Load IE */ +typedef MLAN_PACK_START struct _IEEEtypes_ExtBSSload_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 MU_MIMO_capa_count; + t_u8 stream_underutilization; + t_u8 VHT40_util; + t_u8 VHT80_util; + t_u8 VHT160_util; +} MLAN_PACK_END IEEEtypes_ExtBSSload_t, *pIEEEtypes_ExtBSSload_t; + +/** Quiet Channel IE */ +typedef MLAN_PACK_START struct _IEEEtypes_QuietChan_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 AP_quiet_mode; + t_u8 quiet_count; + t_u8 quiet_period; + t_u16 quiet_dur; + t_u16 quiet_offset; +} MLAN_PACK_END IEEEtypes_QuietChan_t, *pIEEEtypes_QuietChan_t; + +/** Wide Bandwidth Channel Switch IE */ +typedef MLAN_PACK_START struct _IEEEtypes_BWSwitch_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + t_u8 new_chan_width; + t_u8 new_chan_center_freq_1; + t_u8 new_chan_center_freq_2; +} MLAN_PACK_END IEEEtypes_BWSwitch_t, *pIEEEtypes_BWSwitch_t; + +/** AID IE */ +typedef MLAN_PACK_START struct _IEEEtypes_AID_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** AID number */ + t_u16 AID; +} MLAN_PACK_END IEEEtypes_AID_t, *pIEEEtypes_AID_t; + +/** Operating Mode Notificaton IE */ +typedef MLAN_PACK_START struct _IEEEtypes_OperModeNtf_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Operating Mode */ + t_u8 oper_mode; +} MLAN_PACK_END IEEEtypes_OperModeNtf_t, *pIEEEtypes_OperModeNtf_t; + +typedef MLAN_PACK_START struct _IEEEtypes_Extension_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Element id extension */ + t_u8 ext_id; + /** payload */ + t_u8 data[]; +} MLAN_PACK_END IEEEtypes_Extension_t, *pIEEEtypes_Extension_t; + +typedef MLAN_PACK_START struct _IEEEtypes_HECap_t { + /** Generic IE header */ + IEEEtypes_Header_t ieee_hdr; + /** Element id extension */ + t_u8 ext_id; + /** he mac capability info */ + t_u8 he_mac_cap[6]; + /** he phy capability info */ + t_u8 he_phy_cap[11]; + /** he txrx mcs support , size would be 4 or 8 or 12 */ + t_u8 he_txrx_mcs_support[4]; + /** PPE Thresholds (optional) */ +} MLAN_PACK_END IEEEtypes_HECap_t, *pIEEEtypes_HECap_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; + +/** default channel switch count */ +#define DEF_CHAN_SWITCH_COUNT 5 + +/* 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; + +/** data structure for extended channel switch */ +typedef MLAN_PACK_START struct { + /** IEEE element ID = 60 */ + t_u8 element_id; + /** Element length after id and len, set to 4 */ + t_u8 len; + /** STA should not transmit any frames if 1 */ + t_u8 chan_switch_mode; + /** Operate class # that AP/IBSS is moving to */ + t_u8 new_oper_class; + /** Channel # that AP/IBSS is moving to */ + t_u8 new_channel_num; + /** of TBTTs before channel switch */ + t_u8 chan_switch_count; +} MLAN_PACK_END IEEEtypes_ExtChanSwitchAnn_t; + +/* IEEE Wide Bandwidth Channel Switch Element */ +/** + * 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 wide bandwidth chan + * switch element. + */ +typedef MLAN_PACK_START struct { + /** Generic IE header IEEE Element ID = 194*/ + IEEEtypes_Header_t ieee_hdr; + t_u8 new_channel_width; + t_u8 new_channel_center_freq0; + t_u8 new_channel_center_freq1; +} MLAN_PACK_END IEEEtypes_WideBWChanSwitch_t; + +/* IEEE VHT Transmit Power Envelope Element */ +/** + * Provided in beacons and probe responses. Used to advertise the max + * TX power in sepeate bandwidth and as a sub element of Channel Switch + * Wrapper IE. + */ +typedef MLAN_PACK_START struct { + /** Generic IE header IEEE Element ID = 195*/ + IEEEtypes_Header_t ieee_hdr; + t_u8 tpc_info; /**< Transmit Power Information>*/ + t_u8 local_max_tp_20mhz; /**< Local Maximum Transmit Power for 20 MHZ>*/ + t_u8 local_max_tp_40mhz; /**< Local Maximum Transmit Power for 40 MHZ>*/ + t_u8 local_max_tp_80mhz; /**< Local Maximum Transmit Power for 80 MHZ>*/ +} MLAN_PACK_END IEEEtypes_VhtTpcEnvelope_t; + +/* IEEE Quiet Period Element (7.3.2.23) */ +/** + * Provided in beacons and probe responses. Indicates times during + * which the STA should not be transmitting data. Only starting STAs in + * an IBSS and APs are allowed to originate a quiet element. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 40 */ + t_u8 len; /**< Element length after id and len */ + t_u8 quiet_count; /**< Number of TBTTs until beacon with the quiet + period */ + t_u8 quiet_period; /**< Regular quiet period, # of TBTTS between periods + */ + t_u16 quiet_duration; /**< Duration of the quiet period in TUs */ + t_u16 quiet_offset; /**< Offset in TUs from the TBTT for the quiet + period */ + +} MLAN_PACK_END IEEEtypes_Quiet_t; + +/** +*** @brief Map octet of the basic measurement report (7.3.2.22.1) +**/ +typedef MLAN_PACK_START struct { +#ifdef BIG_ENDIAN_SUPPORT + /**< Reserved */ + t_u8 rsvd5_7 : 3; + /**< Channel is unmeasured */ + t_u8 unmeasured : 1; + /**< Radar detected on channel */ + t_u8 radar : 1; + /**< Unidentified signal found on channel */ + t_u8 unidentified_sig : 1; + /**< OFDM preamble detected on channel */ + t_u8 ofdm_preamble : 1; + /**< At least one valid MPDU received on channel */ + t_u8 bss : 1; +#else + /**< At least one valid MPDU received on channel */ + t_u8 bss : 1; + /**< OFDM preamble detected on channel */ + t_u8 ofdm_preamble : 1; + /**< Unidentified signal found on channel */ + t_u8 unidentified_sig : 1; + /**< Radar detected on channel */ + t_u8 radar : 1; + /**< Channel is unmeasured */ + t_u8 unmeasured : 1; + /**< Reserved */ + t_u8 rsvd5_7 : 3; +#endif /* BIG_ENDIAN_SUPPORT */ + +} MLAN_PACK_END MeasRptBasicMap_t; + +/* IEEE DFS Channel Map field (7.3.2.24) */ +/** + * Used to list supported channels and provide a octet "map" field which + * contains a basic measurement report for that channel in the + * IEEEtypes_IBSS_DFS_t element + */ +typedef MLAN_PACK_START struct { + t_u8 channel_number; /**< Channel number */ + MeasRptBasicMap_t rpt_map; /**< Basic measurement report for the channel + */ + +} MLAN_PACK_END IEEEtypes_ChannelMap_t; + +/* IEEE IBSS DFS Element (7.3.2.24) */ +/** + * IBSS DFS element included in ad hoc beacons and probe responses. + * Provides information regarding the IBSS DFS Owner as well as the + * originating STAs supported channels and basic measurement results. + */ +typedef MLAN_PACK_START struct { + t_u8 element_id; /**< IEEE Element ID = 41 */ + t_u8 len; /**< Element length after id and len */ + t_u8 dfs_owner[MLAN_MAC_ADDR_LENGTH]; /**< DFS Owner STA Address */ + t_u8 dfs_recovery_interval; /**< DFS Recovery time in TBTTs */ + + /** Variable length map field, one Map entry for each supported channel + */ + IEEEtypes_ChannelMap_t channel_map[WLAN_11H_MAX_IBSS_DFS_CHANNELS]; + +} MLAN_PACK_END IEEEtypes_IBSS_DFS_t; + +/* 802.11h BSS information kept for each BSSID received in scan results */ +/** + * IEEE BSS information needed from scan results for later processing in + * join commands + */ +typedef struct { + t_u8 sensed_11h; /**< Capability bit set or 11h IE found in this BSS */ + + IEEEtypes_PowerConstraint_t power_constraint; /**< Power Constraint IE + */ + IEEEtypes_PowerCapability_t power_capability; /**< Power Capability IE + */ + IEEEtypes_TPCReport_t tpc_report; /**< TPC Report IE */ + IEEEtypes_ChanSwitchAnn_t chan_switch_ann; /**< Channel Switch + Announcement IE */ + IEEEtypes_Quiet_t quiet; /**< Quiet IE */ + IEEEtypes_IBSS_DFS_t ibss_dfs; /**< IBSS DFS Element IE */ + +} wlan_11h_bss_info_t; + +/** action code for 20/40 BSS Coexsitence Management frame */ +#define BSS_20_40_COEX 0 + +#ifdef STA_SUPPORT +/** Macro for maximum size of scan response buffer */ +#define MAX_SCAN_RSP_BUF (16 * 1024) + +/** Maximum number of channels that can be sent in user scan config */ +#define WLAN_USER_SCAN_CHAN_MAX 50 +/** Maximum length of SSID list */ +#define MRVDRV_MAX_SSID_LIST_LENGTH 10 + +/** Maximum length of BSSID list */ +#define MAX_BSSID_FILTER_LIST 5 + +/** Scan all the channels in specified band */ +#define BAND_SPECIFIED 0x80 + +/** + * IOCTL SSID List sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Used to specify SSID specific filters as well as SSID pattern matching + * filters for scan result processing in firmware. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_ssid { + /** SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH + 1]; + /** Maximum length of SSID */ + t_u8 max_len; +} MLAN_PACK_END wlan_user_scan_ssid; + +/** + * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Multiple instances of this structure are included in the IOCTL command + * to configure a instance of a scan on the specific channel. + */ +typedef MLAN_PACK_START struct _wlan_user_scan_chan { + /** Channel Number to scan */ + t_u8 chan_number; + /** Radio type: 'B/G' Band = 0, 'A' Band = 1 */ + t_u8 radio_type; + /** Scan type: Active = 1, Passive = 2 */ + t_u8 scan_type; + /** Reserved */ + t_u8 reserved; + /** Scan duration in milliseconds; if 0 default used */ + t_u32 scan_time; +} MLAN_PACK_END wlan_user_scan_chan; + +/** channel statictics */ +typedef MLAN_PACK_START struct _ChanStatistics_t { + /** channle number */ + t_u8 chan_num; + /** band info */ + Band_Config_t bandcfg; + /** flags */ + t_u8 flags; + /** noise */ + t_s8 noise; + /** total network */ + t_u16 total_networks; + /** scan duration */ + t_u16 cca_scan_duration; + /** busy duration */ + t_u16 cca_busy_duration; + /** min rss */ + t_u8 min_rss; + /** max rssi */ + t_u8 max_rss; +} MLAN_PACK_END ChanStatistics_t; + +/** Enhance ext scan type defination */ +typedef enum _MLAN_EXT_SCAN_TYPE { + EXT_SCAN_DEFAULT, + EXT_SCAN_ENHANCE, + EXT_SCAN_CANCEL, +} MLAN_EXT_SCAN_TYPE; + +/** + * Input structure to configure an immediate scan cmd to firmware + * + * Specifies a number of parameters to be used in general for the scan + * as well as a channel list (wlan_user_scan_chan) for each scan period + * desired. + */ +typedef MLAN_PACK_START struct { + /** + * Flag set to keep the previous scan table intact + * + * If set, the scan results will accumulate, replacing any previous + * matched entries for a BSS with the new scan data + */ + t_u8 keep_previous_scan; + /** + * BSS mode to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. Valid settings are: + * + * - MLAN_SCAN_MODE_BSS (infrastructure) + * - MLAN_SCAN_MODE_IBSS (adhoc) + * - MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_mode; + /** + * Configure the number of probe requests for active chan scans + */ + t_u8 num_probes; + /** + * @brief ssid filter flag + */ + t_u8 ssid_filter; + /** + * @brief BSSID filter sent in the firmware command to limit the + * results + */ + t_u8 specific_bssid[MLAN_MAC_ADDR_LENGTH]; + /** + * SSID filter list used in the to limit the scan results + */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** + * Variable number (fixed maximum) of channels to scan up + */ + wlan_user_scan_chan chan_list[WLAN_USER_SCAN_CHAN_MAX]; + /** scan channel gap */ + t_u16 scan_chan_gap; + /** scan type: 0 legacy, 1: enhance scan*/ + t_u8 ext_scan_type; + /** flag to filer only probe response */ + t_u8 proberesp_only; + t_u8 random_mac[MLAN_MAC_ADDR_LENGTH]; + /** Number of BSSIDs to be filtered */ + t_u8 bssid_num; + /** BSSID filter list used in the to limit the scan results */ + mlan_802_11_mac_addr bssid_list[MAX_BSSID_FILTER_LIST]; +} MLAN_PACK_END wlan_user_scan_cfg; + +/** Default scan interval in millisecond*/ +#define DEFAULT_BGSCAN_INTERVAL 30000 + +/** action get all, except pps/uapsd config */ +#define BG_SCAN_ACT_GET 0x0000 +/** action set all, except pps/uapsd config */ +#define BG_SCAN_ACT_SET 0x0001 +/** action get pps/uapsd config */ +#define BG_SCAN_ACT_GET_PPS_UAPSD 0x0100 +/** action set pps/uapsd config */ +#define BG_SCAN_ACT_SET_PPS_UAPSD 0x0101 +/** action set all */ +#define BG_SCAN_ACT_SET_ALL 0xff01 +/** ssid match */ +#define BG_SCAN_SSID_MATCH 0x0001 +/** ssid match and RSSI exceeded */ +#define BG_SCAN_SSID_RSSI_MATCH 0x0004 +/**wait for all channel scan to complete to report scan result*/ +#define BG_SCAN_WAIT_ALL_CHAN_DONE 0x80000000 +/** Maximum number of channels that can be sent in bg scan config */ +#define WLAN_BG_SCAN_CHAN_MAX 38 + +/** Enumeration definition */ +/** EES MODE */ +typedef enum { + /** EES MODE: LOW */ + EES_MODE_LOW = 0, + /** EES MODE: MID */ + EES_MODE_MID, + /** EES MODE: HIGH */ + EES_MODE_HIGH, + /** EES MODE: OFF */ + EES_MODE_OFF, + /** EES MODE: LOOP */ + EES_MODE_LOOP = 15, +} ees_modes; + +/** EES Maximum SSID */ +#define EES_MAX_SSIDS 2 + +/** ees_ssid_config */ +typedef MLAN_PACK_START struct { + /** SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH + 1]; + /** Maximum length of SSID */ + t_u8 max_len; + /** PairCipher */ + t_u8 pair_cipher; + /** GroupCipher */ + t_u8 group_cipher; +} MLAN_PACK_END ees_ssid_config; + +/** + * Input structure to configure bs scan cmd to firmware + */ +typedef MLAN_PACK_START struct { + /** action */ + t_u16 action; + /** enable/disable */ + t_u8 enable; + /** BSS type: + * MLAN_SCAN_MODE_BSS (infrastructure) + * MLAN_SCAN_MODE_IBSS (adhoc) + * MLAN_SCAN_MODE_ANY (unrestricted, adhoc and infrastructure) + */ + t_u8 bss_type; + /** number of channel scanned during each scan */ + t_u8 chan_per_scan; + /** interval between consecutive scan */ + t_u32 scan_interval; + /** bit 0: ssid match bit 1: ssid match and SNR exceeded + * bit 2: ssid match and RSSI exceeded + * bit 31: wait for all channel scan to complete to report scan result + */ + t_u32 report_condition; + /* Configure the number of probe requests for active chan scans */ + t_u8 num_probes; + /** RSSI threshold */ + t_u8 rssi_threshold; + /** SNR threshold */ + t_u8 snr_threshold; + /** repeat count */ + t_u16 repeat_count; + /** start later flag */ + t_u16 start_later; + /** SSID filter list used in the to limit the scan results */ + wlan_user_scan_ssid ssid_list[MRVDRV_MAX_SSID_LIST_LENGTH]; + /** Variable number (fixed maximum) of channels to scan up */ + wlan_user_scan_chan chan_list[WLAN_BG_SCAN_CHAN_MAX]; + /** scan channel gap */ + t_u16 scan_chan_gap; + /** Enable EES configuration */ + t_u8 config_ees; + /** EES scan mode */ + t_u16 ees_mode; + /** EES report condition */ + t_u16 report_cond; + /** EES High Period scan interval */ + t_u16 high_period; + /** EES High Period scan count */ + t_u16 high_period_count; + /** EES Medium Period scan interval */ + t_u16 mid_period; + /** EES Medium Period scan count */ + t_u16 mid_period_count; + /** EES Low Period scan interval */ + t_u16 low_period; + /** EES Low Period scan count */ + t_u16 low_period_count; + /** Number of networks in the list */ + t_u8 network_count; + /** Maximum number of connection count */ + t_u8 max_conn_count; + /** Black List Exp */ + t_u8 black_list_exp; + /** Array of ees config struct */ + ees_ssid_config ees_ssid_cfg[EES_MAX_SSIDS]; + t_u8 random_mac[MLAN_MAC_ADDR_LENGTH]; +} 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 load */ + t_u8 chan_load; + /** Channel */ + t_u32 channel; + + /** Freq */ + t_u32 freq; + + /** Beacon period */ + t_u16 beacon_period; + + /** ATIM window */ + t_u32 atim_window; + + /** ERP flags */ + t_u8 erp_flags; + + /** Type of network in use */ + WLAN_802_11_NETWORK_TYPE network_type_use; + + /** Network infrastructure mode */ + t_u32 bss_mode; + + /** Network supported rates */ + WLAN_802_11_RATES supported_rates; + + /** Supported data rates */ + t_u8 data_rates[WLAN_SUPPORTED_RATES]; + + /** Current channel bandwidth + * 0 : 20MHZ + * 1 : 40MHZ + * 2 : 80MHZ + * 3 : 160MHZ + */ + t_u8 curr_bandwidth; + + /** Network band. + * BAND_B(0x01): 'b' band + * BAND_G(0x02): 'g' band + * BAND_A(0X04): 'a' band + */ + t_u16 bss_band; + + /** TSF timestamp from the current firmware TSF */ + t_u64 network_tsf; + + /** TSF value included in the beacon/probe response */ + t_u8 time_stamp[8]; + + /** PHY parameter set */ + IEEEtypes_PhyParamSet_t phy_param_set; + + /** SS parameter set */ + IEEEtypes_SsParamSet_t ss_param_set; + + /** Capability information */ + IEEEtypes_CapInfo_t cap_info; + + /** WMM IE */ + IEEEtypes_WmmParameter_t wmm_ie; + + /** 802.11h BSS information */ + wlan_11h_bss_info_t wlan_11h_bss_info; + + /** Indicate disabling 11n when associate with AP */ + t_u8 disable_11n; + /** 802.11n BSS information */ + /** HT Capabilities IE */ + IEEEtypes_HTCap_t *pht_cap; + /** HT Capabilities Offset */ + t_u16 ht_cap_offset; + /** HT Information IE */ + IEEEtypes_HTInfo_t *pht_info; + /** HT Information Offset */ + t_u16 ht_info_offset; + /** 20/40 BSS Coexistence IE */ + IEEEtypes_2040BSSCo_t *pbss_co_2040; + /** 20/40 BSS Coexistence Offset */ + t_u16 bss_co_2040_offset; + /** Extended Capabilities IE */ + IEEEtypes_ExtCap_t *pext_cap; + /** Extended Capabilities Offset */ + t_u16 ext_cap_offset; + /** Overlapping BSS Scan Parameters IE */ + IEEEtypes_OverlapBSSScanParam_t *poverlap_bss_scan_param; + /** Overlapping BSS Scan Parameters Offset */ + t_u16 overlap_bss_offset; + + /** VHT Capabilities IE */ + IEEEtypes_VHTCap_t *pvht_cap; + /** VHT Capabilities IE offset */ + t_u16 vht_cap_offset; + /** VHT Operations IE */ + IEEEtypes_VHTOprat_t *pvht_oprat; + /** VHT Operations IE offset */ + t_u16 vht_oprat_offset; + /** VHT Transmit Power Envelope IE */ + IEEEtypes_VHTtxpower_t *pvht_txpower; + /** VHT Transmit Power Envelope IE offset */ + t_u16 vht_txpower_offset; + /** Extended Power Constraint IE */ + IEEEtypes_ExtPwerCons_t *pext_pwer; + /** Extended Power Constraint IE offset */ + t_u16 ext_pwer_offset; + /** Extended BSS Load IE */ + IEEEtypes_ExtBSSload_t *pext_bssload; + /** Extended BSS Load IE offset */ + t_u16 ext_bssload_offset; + /** Quiet Channel IE */ + IEEEtypes_QuietChan_t *pquiet_chan; + /** Quiet Channel IE offset */ + t_u16 quiet_chan_offset; + /** Operating Mode Notification IE */ + IEEEtypes_OperModeNtf_t *poper_mode; + /** Operating Mode Notification IE offset */ + t_u16 oper_mode_offset; + /** HE Capability IE */ + IEEEtypes_HECap_t *phe_cap; + /** HE Capability IE offset */ + t_u16 he_cap_offset; + /** HE operation IE */ + IEEEtypes_Extension_t *phe_oprat; + /** HE operation IE offset */ + t_u16 he_oprat_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 + /* Hotspot 2.0 OSEN AKM IE*/ + IEEEtypes_Generic_t *posen_ie; + /** osen IE offset in the beacon buffer */ + t_u16 osen_offset; + /* Mobility domain IE */ + IEEEtypes_MobilityDomain_t *pmd_ie; + /** Mobility domain IE offset in the beacon buffer */ + t_u16 md_offset; + + /** Pointer to the returned scan response */ + t_u8 *pbeacon_buf; + /** Length of the stored scan response */ + t_u32 beacon_buf_size; + /** Max allocated size for updated scan response */ + t_u32 beacon_buf_size_max; + +} BSSDescriptor_t, *pBSSDescriptor_t; + +#endif /* !_MLAN_IEEE_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/mlan_ioctl.h b/mxm_wifiex/wlan_src/mlinux/mlan_ioctl.h new file mode 100644 index 0000000..2315b2a --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/mlan_ioctl.h @@ -0,0 +1,5159 @@ +/** @file mlan_ioctl.h + * + * @brief This file declares the IOCTL data structures and APIs. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/****************************************************** +Change log: + 11/07/2008: initial version +******************************************************/ + +#ifndef _MLAN_IOCTL_H_ +#define _MLAN_IOCTL_H_ + +/** Enumeration for IOCTL request ID */ +enum _mlan_ioctl_req_id { + /* Scan Group */ + MLAN_IOCTL_SCAN = 0x00010000, + MLAN_OID_SCAN_NORMAL = 0x00010001, + MLAN_OID_SCAN_SPECIFIC_SSID = 0x00010002, + MLAN_OID_SCAN_USER_CONFIG = 0x00010003, + MLAN_OID_SCAN_CONFIG = 0x00010004, + MLAN_OID_SCAN_GET_CURRENT_BSS = 0x00010005, + MLAN_OID_SCAN_CANCEL = 0x00010006, + MLAN_OID_SCAN_TABLE_FLUSH = 0x0001000A, + MLAN_OID_SCAN_BGSCAN_CONFIG = 0x0001000B, + /* BSS Configuration Group */ + MLAN_IOCTL_BSS = 0x00020000, + MLAN_OID_BSS_START = 0x00020001, + MLAN_OID_BSS_STOP = 0x00020002, + MLAN_OID_BSS_MODE = 0x00020003, + MLAN_OID_BSS_CHANNEL = 0x00020004, + MLAN_OID_BSS_CHANNEL_LIST = 0x00020005, + MLAN_OID_BSS_MAC_ADDR = 0x00020006, + MLAN_OID_BSS_MULTICAST_LIST = 0x00020007, + MLAN_OID_BSS_FIND_BSS = 0x00020008, + MLAN_OID_IBSS_BCN_INTERVAL = 0x00020009, + MLAN_OID_IBSS_ATIM_WINDOW = 0x0002000A, + MLAN_OID_IBSS_CHANNEL = 0x0002000B, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_BSS_CONFIG = 0x0002000C, + MLAN_OID_UAP_DEAUTH_STA = 0x0002000D, + MLAN_OID_UAP_BSS_RESET = 0x0002000E, +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + MLAN_OID_BSS_ROLE = 0x0002000F, +#endif +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_WIFI_DIRECT_MODE = 0x00020010, +#endif +#ifdef STA_SUPPORT + MLAN_OID_BSS_LISTEN_INTERVAL = 0x00020011, +#endif + MLAN_OID_BSS_REMOVE = 0x00020014, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_CFG_WMM_PARAM = 0x00020015, +#endif + MLAN_OID_BSS_11D_CHECK_CHANNEL = 0x00020016, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_ACS_SCAN = 0x00020017, + MLAN_OID_UAP_SCAN_CHANNELS = 0x00020018, + MLAN_OID_UAP_CHANNEL = 0x00020019, + MLAN_OID_UAP_OPER_CTRL = 0x0002001A, +#endif +#ifdef STA_SUPPORT + MLAN_OID_BSS_CHAN_INFO = 0x0002001B, +#endif +#ifdef UAP_SUPPORT + MLAN_OID_UAP_ADD_STATION = 0x0002001C, +#endif + + MLAN_OID_BSS_FIND_BSSID = 0x0002001D, + + /* Radio Configuration Group */ + MLAN_IOCTL_RADIO_CFG = 0x00030000, + MLAN_OID_RADIO_CTRL = 0x00030001, + MLAN_OID_BAND_CFG = 0x00030002, + MLAN_OID_ANT_CFG = 0x00030003, + MLAN_OID_REMAIN_CHAN_CFG = 0x00030004, + MLAN_OID_MIMO_SWITCH = 0x00030005, + + /* SNMP MIB Group */ + MLAN_IOCTL_SNMP_MIB = 0x00040000, + MLAN_OID_SNMP_MIB_RTS_THRESHOLD = 0x00040001, + MLAN_OID_SNMP_MIB_FRAG_THRESHOLD = 0x00040002, + MLAN_OID_SNMP_MIB_RETRY_COUNT = 0x00040003, + MLAN_OID_SNMP_MIB_DOT11D = 0x00040004, +#if defined(UAP_SUPPORT) + MLAN_OID_SNMP_MIB_DOT11H = 0x00040005, +#endif + MLAN_OID_SNMP_MIB_DTIM_PERIOD = 0x00040006, + MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE = 0x00040007, + MLAN_OID_SNMP_MIB_CTRL_DEAUTH = 0x00040008, + + /* Status Information Group */ + MLAN_IOCTL_GET_INFO = 0x00050000, + MLAN_OID_GET_STATS = 0x00050001, + MLAN_OID_GET_SIGNAL = 0x00050002, + MLAN_OID_GET_FW_INFO = 0x00050003, + MLAN_OID_GET_VER_EXT = 0x00050004, + MLAN_OID_GET_BSS_INFO = 0x00050005, + MLAN_OID_GET_DEBUG_INFO = 0x00050006, +#ifdef UAP_SUPPORT + MLAN_OID_UAP_STA_LIST = 0x00050007, +#endif + MLAN_OID_GET_SIGNAL_EXT = 0x00050008, + MLAN_OID_LINK_STATS = 0x00050009, + MLAN_OID_GET_UAP_STATS_LOG = 0x0005000A, + /* Security Configuration Group */ + MLAN_IOCTL_SEC_CFG = 0x00060000, + MLAN_OID_SEC_CFG_AUTH_MODE = 0x00060001, + MLAN_OID_SEC_CFG_ENCRYPT_MODE = 0x00060002, + MLAN_OID_SEC_CFG_WPA_ENABLED = 0x00060003, + MLAN_OID_SEC_CFG_ENCRYPT_KEY = 0x00060004, + MLAN_OID_SEC_CFG_PASSPHRASE = 0x00060005, + MLAN_OID_SEC_CFG_EWPA_ENABLED = 0x00060006, + MLAN_OID_SEC_CFG_ESUPP_MODE = 0x00060007, + MLAN_OID_SEC_CFG_WAPI_ENABLED = 0x00060009, + MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED = 0x0006000A, +#ifdef UAP_SUPPORT + MLAN_OID_SEC_CFG_REPORT_MIC_ERR = 0x0006000B, +#endif + MLAN_OID_SEC_QUERY_KEY = 0x0006000C, + + /* Rate Group */ + MLAN_IOCTL_RATE = 0x00070000, + MLAN_OID_RATE_CFG = 0x00070001, + MLAN_OID_GET_DATA_RATE = 0x00070002, + MLAN_OID_SUPPORTED_RATES = 0x00070003, + + /* Power Configuration Group */ + MLAN_IOCTL_POWER_CFG = 0x00080000, + MLAN_OID_POWER_CFG = 0x00080001, + MLAN_OID_POWER_CFG_EXT = 0x00080002, + MLAN_OID_POWER_LOW_POWER_MODE = 0x00080003, + + /* Power Management Configuration Group */ + MLAN_IOCTL_PM_CFG = 0x00090000, + MLAN_OID_PM_CFG_IEEE_PS = 0x00090001, + MLAN_OID_PM_CFG_HS_CFG = 0x00090002, + MLAN_OID_PM_CFG_INACTIVITY_TO = 0x00090003, + MLAN_OID_PM_CFG_DEEP_SLEEP = 0x00090004, + MLAN_OID_PM_CFG_SLEEP_PD = 0x00090005, + MLAN_OID_PM_CFG_PS_CFG = 0x00090006, + MLAN_OID_PM_CFG_SLEEP_PARAMS = 0x00090008, +#ifdef UAP_SUPPORT + MLAN_OID_PM_CFG_PS_MODE = 0x00090009, +#endif /* UAP_SUPPORT */ + MLAN_OID_PM_INFO = 0x0009000A, + MLAN_OID_PM_HS_WAKEUP_REASON = 0x0009000B, + MLAN_OID_PM_MGMT_FILTER = 0x0009000C, + MLAN_OID_PM_CFG_BCN_TIMEOUT = 0x0009000D, + + /* WMM Configuration Group */ + MLAN_IOCTL_WMM_CFG = 0x000A0000, + MLAN_OID_WMM_CFG_ENABLE = 0x000A0001, + MLAN_OID_WMM_CFG_QOS = 0x000A0002, + MLAN_OID_WMM_CFG_ADDTS = 0x000A0003, + MLAN_OID_WMM_CFG_DELTS = 0x000A0004, + MLAN_OID_WMM_CFG_QUEUE_CONFIG = 0x000A0005, + MLAN_OID_WMM_CFG_QUEUE_STATS = 0x000A0006, + MLAN_OID_WMM_CFG_QUEUE_STATUS = 0x000A0007, + MLAN_OID_WMM_CFG_TS_STATUS = 0x000A0008, + + /* WPS Configuration Group */ + MLAN_IOCTL_WPS_CFG = 0x000B0000, + MLAN_OID_WPS_CFG_SESSION = 0x000B0001, + + /* 802.11n Configuration Group */ + MLAN_IOCTL_11N_CFG = 0x000C0000, + MLAN_OID_11N_CFG_TX = 0x000C0001, + MLAN_OID_11N_HTCAP_CFG = 0x000C0002, + MLAN_OID_11N_CFG_ADDBA_REJECT = 0x000C0003, + MLAN_OID_11N_CFG_AGGR_PRIO_TBL = 0x000C0004, + MLAN_OID_11N_CFG_ADDBA_PARAM = 0x000C0005, + MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE = 0x000C0006, + MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL = 0x000C0007, + MLAN_OID_11N_CFG_SUPPORTED_MCS_SET = 0x000C0008, + MLAN_OID_11N_CFG_TX_BF_CAP = 0x000C0009, + MLAN_OID_11N_CFG_TX_BF_CFG = 0x000C000A, + MLAN_OID_11N_CFG_STREAM_CFG = 0x000C000B, + MLAN_OID_11N_CFG_DELBA = 0x000C000C, + MLAN_OID_11N_CFG_REJECT_ADDBA_REQ = 0x000C000D, + MLAN_OID_11N_CFG_COEX_RX_WINSIZE = 0x000C000E, + MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM = 0x000C0010, + MLAN_OID_11N_CFG_MIN_BA_THRESHOLD = 0x000C0011, + + /* 802.11d Configuration Group */ + MLAN_IOCTL_11D_CFG = 0x000D0000, +#ifdef STA_SUPPORT + MLAN_OID_11D_CFG_ENABLE = 0x000D0001, + MLAN_OID_11D_CLR_CHAN_TABLE = 0x000D0002, +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + MLAN_OID_11D_DOMAIN_INFO = 0x000D0003, +#endif + MLAN_OID_11D_DOMAIN_INFO_EXT = 0x000D0004, + + /* Register Memory Access Group */ + MLAN_IOCTL_REG_MEM = 0x000E0000, + MLAN_OID_REG_RW = 0x000E0001, + MLAN_OID_EEPROM_RD = 0x000E0002, + MLAN_OID_MEM_RW = 0x000E0003, + + /* Multi-Radio Configuration Group */ + MLAN_IOCTL_MFR_CFG = 0x00100000, + /* 802.11h Configuration Group */ + MLAN_IOCTL_11H_CFG = 0x00110000, + MLAN_OID_11H_CHANNEL_CHECK = 0x00110001, + MLAN_OID_11H_LOCAL_POWER_CONSTRAINT = 0x00110002, + MLAN_OID_11H_DFS_TESTING = 0x00110003, + MLAN_OID_11H_CHAN_REPORT_REQUEST = 0x00110004, + MLAN_OID_11H_CHAN_SWITCH_COUNT = 0x00110005, + MLAN_OID_11H_CHAN_NOP_INFO = 0x00110006, + MLAN_OID_11H_DFS_W53_CFG = 0x00110008, + + /* 802.11n Configuration Group RANDYTODO for value assign */ + MLAN_IOCTL_11AC_CFG = 0x00120000, + MLAN_OID_11AC_VHT_CFG = 0x00120001, + MLAN_OID_11AC_CFG_SUPPORTED_MCS_SET = 0x00120002, + MLAN_OID_11AC_OPERMODE_CFG = 0x00120003, + + /* 802.11ax Configuration Group */ + MLAN_IOCTL_11AX_CFG = 0x00170000, + MLAN_OID_11AX_HE_CFG = 0x00170001, + MLAN_OID_11AX_CMD_CFG = 0x00170002, + MLAN_OID_11AX_TWT_CFG = 0x00170003, + + /* Miscellaneous Configuration Group */ + MLAN_IOCTL_MISC_CFG = 0x00200000, + MLAN_OID_MISC_GEN_IE = 0x00200001, + MLAN_OID_MISC_REGION = 0x00200002, + MLAN_OID_MISC_WARM_RESET = 0x00200003, +#ifdef SDIO + MLAN_OID_MISC_SDIO_MPA_CTRL = 0x00200006, +#endif + MLAN_OID_MISC_HOST_CMD = 0x00200007, + MLAN_OID_MISC_SYS_CLOCK = 0x00200009, + MLAN_OID_MISC_SOFT_RESET = 0x0020000A, + MLAN_OID_MISC_WWS = 0x0020000B, + MLAN_OID_MISC_ASSOC_RSP = 0x0020000C, + MLAN_OID_MISC_INIT_SHUTDOWN = 0x0020000D, + MLAN_OID_MISC_CUSTOM_IE = 0x0020000F, + MLAN_OID_MISC_TX_DATAPAUSE = 0x00200012, + MLAN_OID_MISC_IP_ADDR = 0x00200013, + MLAN_OID_MISC_MAC_CONTROL = 0x00200014, + MLAN_OID_MISC_MEF_CFG = 0x00200015, + MLAN_OID_MISC_CFP_CODE = 0x00200016, + MLAN_OID_MISC_COUNTRY_CODE = 0x00200017, + MLAN_OID_MISC_THERMAL = 0x00200018, + MLAN_OID_MISC_RX_MGMT_IND = 0x00200019, + MLAN_OID_MISC_SUBSCRIBE_EVENT = 0x0020001A, +#ifdef DEBUG_LEVEL1 + MLAN_OID_MISC_DRVDBG = 0x0020001B, +#endif + MLAN_OID_MISC_HOTSPOT_CFG = 0x0020001C, + MLAN_OID_MISC_OTP_USER_DATA = 0x0020001D, +#ifdef USB + MLAN_OID_MISC_USB_AGGR_CTRL = 0x0020001F, +#endif + MLAN_OID_MISC_TXCONTROL = 0x00200020, +#ifdef STA_SUPPORT + MLAN_OID_MISC_EXT_CAP_CFG = 0x00200021, +#endif +#if defined(STA_SUPPORT) + MLAN_OID_MISC_PMFCFG = 0x00200022, +#endif +#ifdef WIFI_DIRECT_SUPPORT + MLAN_OID_MISC_WIFI_DIRECT_CONFIG = 0x00200025, +#endif + MLAN_OID_MISC_LOW_PWR_MODE = 0x00200029, + MLAN_OID_MISC_MEF_FLT_CFG = 0x0020002A, + MLAN_OID_MISC_DFS_REAPTER_MODE = 0x0020002B, +#ifdef RX_PACKET_COALESCE + MLAN_OID_MISC_RX_PACKET_COALESCE = 0x0020002C, +#endif + MLAN_OID_MISC_COALESCE_CFG = 0x0020002E, + MLAN_OID_MISC_GET_SENSOR_TEMP = 0x00200030, + MLAN_OID_MISC_GTK_REKEY_OFFLOAD = 0x00200037, + MLAN_OID_MISC_OPER_CLASS = 0x00200038, + MLAN_OID_MISC_PMIC_CFG = 0x00200039, + MLAN_OID_MISC_IND_RST_CFG = 0x00200040, + MLAN_OID_MISC_GET_TSF = 0x00200045, + MLAN_OID_MISC_GET_CHAN_REGION_CFG = 0x00200046, + MLAN_OID_MISC_CLOUD_KEEP_ALIVE = 0x00200048, + MLAN_OID_MISC_OPER_CLASS_CHECK = 0x00200049, + + MLAN_OID_MISC_CWMODE_CTRL = 0x00200051, + MLAN_OID_MISC_AGGR_CTRL = 0x00200052, + MLAN_OID_MISC_DYN_BW = 0x00200053, + MLAN_OID_MISC_FW_DUMP_EVENT = 0x00200054, + MLAN_OID_MISC_PER_PKT_CFG = 0x00200055, + + MLAN_OID_MISC_ROBUSTCOEX = 0x00200056, + MLAN_OID_MISC_GET_TX_RX_HISTOGRAM = 0x00200057, + MLAN_OID_MISC_CFP_INFO = 0x00200060, + MLAN_OID_MISC_BOOT_SLEEP = 0x00200061, +#if defined(PCIE) + MLAN_OID_MISC_SSU = 0x00200062, +#endif + MLAN_OID_MISC_DMCS_CONFIG = 0x00200065, + MLAN_OID_MISC_RX_ABORT_CFG = 0x00200066, + MLAN_OID_MISC_RX_ABORT_CFG_EXT = 0x00200067, + MLAN_OID_MISC_TX_AMPDU_PROT_MODE = 0x00200068, + MLAN_OID_MISC_RATE_ADAPT_CFG = 0x00200069, + MLAN_OID_MISC_CCK_DESENSE_CFG = 0x00200070, + MLAN_OID_MISC_GET_CHAN_TRPC_CFG = 0x00200072, + MLAN_OID_MISC_BAND_STEERING = 0x00200073, + MLAN_OID_MISC_GET_REGIONPWR_CFG = 0x00200074, + MLAN_OID_MISC_RF_TEST_GENERIC = 0x00200075, + MLAN_OID_MISC_RF_TEST_TX_CONT = 0x00200076, + MLAN_OID_MISC_RF_TEST_TX_FRAME = 0x00200077, + MLAN_OID_MISC_ARB_CONFIG = 0x00200078, + MLAN_OID_MISC_BEACON_STUCK = 0x00200079, + MLAN_OID_MISC_CFP_TABLE = 0x0020007A, + MLAN_OID_MISC_RANGE_EXT = 0x0020007B, + MLAN_OID_MISC_DOT11MC_UNASSOC_FTM_CFG = 0x0020007C, + MLAN_OID_MISC_TP_STATE = 0x0020007D, +}; + +/** Sub command size */ +#define MLAN_SUB_COMMAND_SIZE 4 + +/** Enumeration for the action of IOCTL request */ +enum _mlan_act_ioctl { + MLAN_ACT_SET = 1, + MLAN_ACT_GET, + MLAN_ACT_CANCEL, + MLAN_ACT_CLEAR, + MLAN_ACT_RESET, + MLAN_ACT_DEFAULT +}; + +/** 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, + MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE +}; + +/** Enumeration for passive to active scan */ +enum _mlan_pass_to_act_scan { + MLAN_PASS_TO_ACT_SCAN_UNCHANGED = 0, + MLAN_PASS_TO_ACT_SCAN_EN, + MLAN_PASS_TO_ACT_SCAN_DIS +}; + +/** Max number of supported rates */ +#define MLAN_SUPPORTED_RATES 32 + +/** Mrvl Proprietary Tlv base */ +#define PROPRIETARY_TLV_BASE_ID 0x100 + +/** 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 +/** Max gap time between 2 scan in milliseconds */ +#define MRVDRV_MAX_SCAN_CHAN_GAP_TIME 500 + +/** Maximum number of probes to send on each channel */ +#define MAX_PROBES 5 + +/** 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; + /** channel load */ + t_u8 chan_load; + /** 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; + +typedef MLAN_PACK_START struct _tx_status_event { + /** packet type */ + t_u8 packet_type; + /** tx_token_id */ + t_u8 tx_token_id; + /** 0--success, 1--fail, 2--watchdogtimeout */ + t_u8 status; +} MLAN_PACK_END tx_status_event; + +/** + * Sructure to retrieve the scan table + */ +typedef struct { + /** + * - Zero based scan entry to start retrieval in command request + * - Number of scans entries returned in command response + */ + t_u32 scan_number; + /** + * Buffer marker for multiple wlan_ioctl_get_scan_table_entry + * structures. Each struct is padded to the nearest 32 bit boundary. + */ + t_u8 scan_table_entry_buf[1]; +} wlan_ioctl_get_scan_table_info; + +/** + * Structure passed in the wlan_ioctl_get_scan_table_info for each + * BSS returned in the WLAN_GET_SCAN_RESP IOCTL + */ +typedef struct _wlan_ioctl_get_scan_table_entry { + /** + * Fixed field length included in the response. + * + * Length value is included so future fixed fields can be added to the + * response without breaking backwards compatibility. Use the length + * to find the offset for the bssInfoLength field, not a sizeof() + * calc. + */ + t_u32 fixed_field_length; + + /** + * Length of the BSS Information (probe resp or beacon) that + * follows after the fixed_field_length + */ + t_u32 bss_info_length; + + /** + * Always present, fixed length data fields for the BSS + */ + wlan_get_scan_table_fixed fixed_fields; + + /* + * Probe response or beacon scanned for the BSS. + * + * Field layout: + * - TSF 8 octets + * - Beacon Interval 2 octets + * - Capability Info 2 octets + * + * - IEEE Infomation Elements; variable number & length per 802.11 spec + */ + /* t_u8 bss_info_buffer[]; */ +} wlan_ioctl_get_scan_table_entry; + +/** Type definition of mlan_scan_time_params */ +typedef struct _mlan_scan_time_params { + /** Scan channel time for specific scan in milliseconds */ + t_u32 specific_scan_time; + /** Scan channel time for active scan in milliseconds */ + t_u32 active_scan_time; + /** Scan channel time for passive scan in milliseconds */ + t_u32 passive_scan_time; +} mlan_scan_time_params, *pmlan_scan_time_params; + +/** Type definition of mlan_user_scan */ +typedef struct _mlan_user_scan { + /** Length of scan_cfg_buf */ + t_u32 scan_cfg_len; + /** Buffer of scan config */ + t_u8 scan_cfg_buf[1]; +} mlan_user_scan, *pmlan_user_scan; + +/** Type definition of mlan_scan_req */ +typedef struct _mlan_scan_req { + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan type */ + t_u32 scan_type; + /** SSID */ + mlan_802_11_ssid scan_ssid; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; +} mlan_scan_req, *pmlan_scan_req; + +/** Type defnition of mlan_scan_resp */ +typedef struct _mlan_scan_resp { + /** Number of scan result */ + t_u32 num_in_scan_table; + /** Scan table */ + t_u8 *pscan_table; + /* Age in seconds */ + t_u32 age_in_secs; + /** channel statstics */ + t_u8 *pchan_stats; + /** Number of records in the chan_stats */ + t_u32 num_in_chan_stats; +} mlan_scan_resp, *pmlan_scan_resp; + +#define EXT_SCAN_TYPE_ENH 2 +/** Type definition of mlan_scan_cfg */ +typedef struct _mlan_scan_cfg { + /** Scan type */ + t_u32 scan_type; + /** BSS mode for scanning */ + t_u32 scan_mode; + /** Scan probe */ + t_u32 scan_probe; + /** Scan time parameters */ + mlan_scan_time_params scan_time; + /** First passive scan then active scan */ + t_u8 passive_to_active_scan; + /** Ext_scan: 0 disable, 1: enable, 2: enhance scan*/ + t_u32 ext_scan; + /** scan channel gap */ + t_u32 scan_chan_gap; +} mlan_scan_cfg, *pmlan_scan_cfg; + +/** Type defnition of mlan_ds_scan for MLAN_IOCTL_SCAN */ +typedef struct _mlan_ds_scan { + /** Sub-command */ + t_u32 sub_command; + /** Scan request/response */ + union { + /** Scan request */ + mlan_scan_req scan_req; + /** Scan response */ + mlan_scan_resp scan_resp; + /** Scan config parameters in user scan */ + mlan_user_scan user_scan; + /** Scan config parameters */ + mlan_scan_cfg scan_cfg; + } param; +} mlan_ds_scan, *pmlan_ds_scan; + +/*-----------------------------------------------------------------*/ +/** BSS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for BSS mode */ +enum _mlan_bss_mode { + MLAN_BSS_MODE_INFRA = 1, + MLAN_BSS_MODE_IBSS, + MLAN_BSS_MODE_AUTO +}; + +/** Maximum key length */ +#define MLAN_MAX_KEY_LENGTH 32 + +/** Maximum atim window in milliseconds */ +#define MLAN_MAX_ATIM_WINDOW 50 + +/** Minimum beacon interval */ +#define MLAN_MIN_BEACON_INTERVAL 20 +/** Maximum beacon interval */ +#define MLAN_MAX_BEACON_INTERVAL 1000 +/** Default beacon interval */ +#define MLAN_BEACON_INTERVAL 100 + +/** Receive all packets */ +#define MLAN_PROMISC_MODE 1 +/** Receive multicast packets in multicast list */ +#define MLAN_MULTICAST_MODE 2 +/** Receive all multicast packets */ +#define MLAN_ALL_MULTI_MODE 4 + +/** Maximum size of multicast list */ +#define MLAN_MAX_MULTICAST_LIST_SIZE 32 + +/** mlan_multicast_list data structure for MLAN_OID_BSS_MULTICAST_LIST */ +typedef struct _mlan_multicast_list { + /** Multicast mode */ + t_u32 mode; + /** Number of multicast addresses in the list */ + t_u32 num_multicast_addr; + /** Multicast address list */ + mlan_802_11_mac_addr mac_list[MLAN_MAX_MULTICAST_LIST_SIZE]; +} mlan_multicast_list, *pmlan_multicast_list; + +/** Max channel */ +#define MLAN_MAX_CHANNEL 165 +/** Maximum number of channels in table */ +#define MLAN_MAX_CHANNEL_NUM 128 + +/** Channel/frequence for MLAN_OID_BSS_CHANNEL */ +typedef struct _chan_freq { + /** Channel Number */ + t_u32 channel; + /** Frequency of this Channel */ + t_u32 freq; +} chan_freq; + +/** mlan_chan_list data structure for MLAN_OID_BSS_CHANNEL_LIST */ +typedef struct _mlan_chan_list { + /** Number of channel */ + t_u32 num_of_chan; + /** Channel-Frequency table */ + chan_freq cf[MLAN_MAX_CHANNEL_NUM]; +} mlan_chan_list; + +/* This channel is disabled.*/ +#define CHAN_FLAGS_DISABLED MBIT(0) +/* do not initiate radiation, this includes sending probe requests or beaconing + */ +#define CHAN_FLAGS_NO_IR MBIT(1) +/* Radar detection is required on this channel */ +#define CHAN_FLAGS_RADAR MBIT(3) +/* extension channel above this channel is not permitted */ +#define CHAN_FLAGS_NO_HT40PLUS MBIT(4) +/* extension channel below this channel is not permitted */ +#define CHAN_FLAGS_NO_HT40MINUS MBIT(5) +/* OFDM is not allowed on this channel */ +#define CHAN_FLAGS_NO_OFDM MBIT(6) +/** 80Mhz can not used on this channel */ +#define CHAN_FLAGS_NO_80MHZ MBIT(7) +/** 180Mhz can not used on this channel */ +#define CHAN_FLAGS_NO_160MHZ MBIT(8) +/* Only indoor use is permitted on this channel */ +#define CHAN_FLAGS_INDOOR_ONLY MBIT(9) +/* IR operation is allowed on this channel if it's + * connected concurrently to a BSS on the same channel on + * the 2 GHz band or to a channel in the same UNII band (on the 5 GHz + * band), and IEEE80211_CHAN_RADAR is not set */ +#define CHAN_FLAGS_IR_CONCURRENT MBIT(10) +/* 20 MHz operation is not allowed on this channel */ +#define CHAN_FLAGS_20MHZ MBIT(11) +/* 10 MHz operation is not allowed on this channel */ +#define CHAN_FLAGS_NO_10MHZ MBIT(12) +/** This channel's flag is valid */ +#define CHAN_FLAGS_MAX MBIT(31) + +/** Maximum response buffer length */ +#define ASSOC_RSP_BUF_SIZE 500 + +/** Type definition of mlan_ds_misc_assoc_rsp for MLAN_OID_MISC_ASSOC_RSP */ +typedef struct _mlan_ds_misc_assoc_rsp { + /** Associate response buffer */ + t_u8 assoc_resp_buf[ASSOC_RSP_BUF_SIZE]; + /** Response buffer length */ + t_u32 assoc_resp_len; +} mlan_ds_misc_assoc_rsp, *pmlan_ds_misc_assoc_rsp; + +/** mlan_ssid_bssid data structure for + * MLAN_OID_BSS_START and MLAN_OID_BSS_FIND_BSS + */ +typedef struct _mlan_ssid_bssid { + /** SSID */ + mlan_802_11_ssid ssid; + /** BSSID */ + mlan_802_11_mac_addr bssid; + /** index in BSSID list, start from 1 */ + t_u32 idx; + /** Receive signal strength in dBm */ + t_s32 rssi; + /**channel*/ + t_u16 channel; + /**mobility domain value*/ + t_u16 ft_md; + /**ft capability*/ + t_u8 ft_cap; + /**band*/ + t_u16 bss_band; + /** channel flag */ + t_u32 channel_flags; + /** host mlme flag*/ + t_u8 host_mlme; + /** assoicate resp frame/ie from firmware */ + mlan_ds_misc_assoc_rsp assoc_rsp; +} mlan_ssid_bssid, *pmlan_ssid_bssid; + +/** Data structure of WMM ECW */ +typedef struct _wmm_ecw_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Maximum Ecw */ + t_u8 ecw_max : 4; + /** Minimum Ecw */ + t_u8 ecw_min : 4; +#else + /** Minimum Ecw */ + t_u8 ecw_min : 4; + /** Maximum Ecw */ + t_u8 ecw_max : 4; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_ecw_t, *pwmm_ecw_t; + +/** Data structure of WMM Aci/Aifsn */ +typedef struct _wmm_aci_aifsn_t { +#ifdef BIG_ENDIAN_SUPPORT + /** Reserved */ + t_u8 reserved : 1; + /** Aci */ + t_u8 aci : 2; + /** Acm */ + t_u8 acm : 1; + /** Aifsn */ + t_u8 aifsn : 4; +#else + /** Aifsn */ + t_u8 aifsn : 4; + /** Acm */ + t_u8 acm : 1; + /** Aci */ + t_u8 aci : 2; + /** Reserved */ + t_u8 reserved : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_aci_aifsn_t, *pwmm_aci_aifsn_t; + +/** Data structure of WMM AC parameters */ +typedef struct _wmm_ac_parameters_t { + wmm_aci_aifsn_t aci_aifsn; /**< AciAifSn */ + wmm_ecw_t ecw; /**< Ecw */ + t_u16 tx_op_limit; /**< Tx op limit */ +} wmm_ac_parameters_t, *pwmm_ac_parameters_t; + +/** mlan_deauth_param */ +typedef struct _mlan_deauth_param { + /** STA mac addr */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** deauth reason */ + t_u16 reason_code; +} mlan_deauth_param; + +#ifdef UAP_SUPPORT +/** UAP FLAG: Host based */ +#define UAP_FLAG_HOST_BASED MBIT(0) +/** UAP FLAG: Host mlme */ +#define UAP_FLAG_HOST_MLME MBIT(1) + +/** 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 64 +/** Maximum RTS threshold */ +#define MAX_RTS_THRESHOLD 2347 +/** Maximum fragmentation threshold */ +#define MAX_FRAG_THRESHOLD 2346 +/** Minimum fragmentation threshold */ +#define MIN_FRAG_THRESHOLD 256 +/** data rate 54 M */ +#define DATA_RATE_54M 108 +/** Maximum value of bcast_ssid_ctl */ +#define MAX_BCAST_SSID_CTL 2 +/** antenna A */ +#define ANTENNA_MODE_A 0 +/** antenna B */ +#define ANTENNA_MODE_B 1 +/** transmit antenna */ +#define TX_ANTENNA 1 +/** receive antenna */ +#define RX_ANTENNA 0 +/** Maximum stage out time */ +#define MAX_STAGE_OUT_TIME 864000 +/** Minimum stage out time */ +#define MIN_STAGE_OUT_TIME 50 +/** Maximum Retry Limit */ +#define MAX_RETRY_LIMIT 14 + +/** Maximum group key timer in seconds */ +#define MAX_GRP_TIMER 86400 + +/** Maximum value of 4 byte configuration */ +#define MAX_VALID_DWORD 0x7FFFFFFF /* (1 << 31) - 1 */ + +/** default UAP BAND 2.4G */ +#define DEFAULT_UAP_BAND 0 +/** default UAP channel 6 */ +#define DEFAULT_UAP_CHANNEL 6 + +/** Maximum data rates */ +#define MAX_DATA_RATES 14 + +/** auto data rate */ +#define DATA_RATE_AUTO 0 + +/**filter mode: disable */ +#define MAC_FILTER_MODE_DISABLE 0 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_ALLOW_MAC 1 +/**filter mode: block mac address */ +#define MAC_FILTER_MODE_BLOCK_MAC 2 +/** Maximum mac filter num */ +#define MAX_MAC_FILTER_NUM 64 + +/* 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 +/** WPA3 SAE */ +#define PROTOCOL_WPA3_SAE 0x100 + +/** Key_mgmt_psk */ +#define KEY_MGMT_NONE 0x04 +/** Key_mgmt_none */ +#define KEY_MGMT_PSK 0x02 +/** Key_mgmt_eap */ +#define KEY_MGMT_EAP 0x01 +/** Key_mgmt_psk_sha256 */ +#define KEY_MGMT_PSK_SHA256 0x100 +/** Key_mgmt_sae */ +#define KEY_MGMT_SAE 0x400 +/** Key_mgmt_owe */ +#define KEY_MGMT_OWE 0x200 + +/** TKIP */ +#define CIPHER_TKIP 0x04 +/** AES CCMP */ +#define CIPHER_AES_CCMP 0x08 + +/** Valid cipher bitmap */ +#define VALID_CIPHER_BITMAP 0x0c + +/** Packet forwarding to be done by FW or host */ +#define PKT_FWD_FW_BIT 0x01 +/** Intra-BSS broadcast packet forwarding allow bit */ +#define PKT_FWD_INTRA_BCAST 0x02 +/** Intra-BSS unicast packet forwarding allow bit */ +#define PKT_FWD_INTRA_UCAST 0x04 +/** Inter-BSS unicast packet forwarding allow bit */ +#define PKT_FWD_INTER_UCAST 0x08 +/** Intra-BSS unicast packet */ +#define PKT_INTRA_UCAST 0x01 +/** Inter-BSS unicast packet */ +#define PKT_INTER_UCAST 0x02 +/** Enable Host PKT forwarding */ +#define PKT_FWD_ENABLE_BIT 0x01 + +/** Channel List Entry */ +typedef struct _channel_list { + /** Channel Number */ + t_u8 chan_number; + /** Band Config */ + Band_Config_t bandcfg; +} scan_chan_list; + +/** mac_filter data structure */ +typedef struct _mac_filter { + /** mac filter mode */ + t_u16 filter_mode; + /** mac adress count */ + t_u16 mac_count; + /** mac address list */ + mlan_802_11_mac_addr mac_list[MAX_MAC_FILTER_NUM]; +} mac_filter; + +/** wpa parameter */ +typedef struct _wpa_param { + /** Pairwise cipher WPA */ + t_u8 pairwise_cipher_wpa; + /** Pairwise cipher WPA2 */ + t_u8 pairwise_cipher_wpa2; + /** group cipher */ + t_u8 group_cipher; + /** RSN replay protection */ + t_u8 rsn_protection; + /** passphrase length */ + t_u32 length; + /** passphrase */ + t_u8 passphrase[64]; + /**group key rekey time in seconds */ + t_u32 gk_rekey_time; +} wpa_param; + +/** wep key */ +typedef struct _wep_key { + /** key index 0-3 */ + t_u8 key_index; + /** is default */ + t_u8 is_default; + /** length */ + t_u16 length; + /** key data */ + t_u8 key[26]; +} wep_key; + +/** wep param */ +typedef struct _wep_param { + /** key 0 */ + wep_key key0; + /** key 1 */ + wep_key key1; + /** key 2 */ + wep_key key2; + /** key 3 */ + wep_key key3; +} wep_param; + +/** Data structure of WMM QoS information */ +typedef struct _wmm_qos_info_t { +#ifdef BIG_ENDIAN_SUPPORT + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; + /** Reserved */ + t_u8 reserved : 3; + /** Parameter set count */ + t_u8 para_set_count : 4; +#else + /** Parameter set count */ + t_u8 para_set_count : 4; + /** Reserved */ + t_u8 reserved : 3; + /** QoS UAPSD */ + t_u8 qos_uapsd : 1; +#endif /* BIG_ENDIAN_SUPPORT */ +} wmm_qos_info_t, *pwmm_qos_info_t; + +/** Data structure of WMM parameter IE */ +typedef struct _wmm_parameter_t { + /** OuiType: 00:50:f2:02 */ + t_u8 ouitype[4]; + /** Oui subtype: 01 */ + t_u8 ouisubtype; + /** version: 01 */ + t_u8 version; + /** QoS information */ + t_u8 qos_info; + /** Reserved */ + t_u8 reserved; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; +} wmm_parameter_t, *pwmm_parameter_t; + +/** MAX BG channel */ +#define MAX_BG_CHANNEL 14 +/** mlan_bss_param + * Note: For each entry you must enter an invalid value + * in the MOAL function woal_set_sys_config_invalid_data(). + * Otherwise for a valid data an unwanted TLV will be + * added to that command. + */ +typedef struct _mlan_uap_bss_param { + /** AP mac addr */ + mlan_802_11_mac_addr mac_addr; + /** SSID */ + mlan_802_11_ssid ssid; + /** Broadcast ssid control */ + t_u8 bcast_ssid_ctl; + /** Radio control: on/off */ + t_u8 radio_ctl; + /** dtim period */ + t_u8 dtim_period; + /** beacon period */ + t_u16 beacon_period; + /** rates */ + t_u8 rates[MAX_DATA_RATES]; + /** Tx data rate */ + t_u16 tx_data_rate; + /** Tx beacon rate */ + t_u16 tx_beacon_rate; + /** multicast/broadcast data rate */ + t_u16 mcbc_data_rate; + /** Tx power level in dBm */ + t_u8 tx_power_level; + /** Tx antenna */ + t_u8 tx_antenna; + /** Rx antenna */ + t_u8 rx_antenna; + /** packet forward control */ + t_u8 pkt_forward_ctl; + /** max station count */ + t_u16 max_sta_count; + /** mac filter */ + mac_filter filter; + /** station ageout timer in unit of 100ms */ + t_u32 sta_ageout_timer; + /** PS station ageout timer in unit of 100ms */ + t_u32 ps_sta_ageout_timer; + /** RTS threshold */ + t_u16 rts_threshold; + /** fragmentation threshold */ + t_u16 frag_threshold; + /** retry_limit */ + t_u16 retry_limit; + /** pairwise update timeout in milliseconds */ + t_u32 pairwise_update_timeout; + /** pairwise handshake retries */ + t_u32 pwk_retries; + /** groupwise update timeout in milliseconds */ + t_u32 groupwise_update_timeout; + /** groupwise handshake retries */ + t_u32 gwk_retries; + /** preamble type */ + t_u8 preamble_type; + /** band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** auth mode */ + t_u16 auth_mode; + /** encryption protocol */ + t_u16 protocol; + /** key managment type */ + t_u16 key_mgmt; + /** wep param */ + wep_param wep_cfg; + /** wpa param */ + wpa_param wpa_cfg; + /** Mgmt IE passthru mask */ + t_u32 mgmt_ie_passthru_mask; + /* + * 11n HT Cap HTCap_t ht_cap + */ + /** HT Capabilities Info field */ + t_u16 ht_cap_info; + /** A-MPDU Parameters field */ + t_u8 ampdu_param; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[16]; + /** HT Extended Capabilities field */ + t_u16 ht_ext_cap; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Antenna Selection Capability field */ + t_u8 asel; + /** Enable 2040 Coex */ + t_u8 enable_2040coex; + /** key management operation */ + t_u16 key_mgmt_operation; + /** BSS status */ + t_u16 bss_status; +#ifdef WIFI_DIRECT_SUPPORT + /* pre shared key */ + t_u8 psk[MLAN_MAX_KEY_LENGTH]; +#endif /* WIFI_DIRECT_SUPPORT */ + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; + /** Wmm parameters */ + wmm_parameter_t wmm_para; + + /** uap host based config */ + t_u32 uap_host_based_config; +} mlan_uap_bss_param, *pmlan_uap_bss_param; + +/** mlan_uap_scan_channels */ +typedef struct _mlan_uap_scan_channels { + /** flag for remove nop channel*/ + t_u8 remove_nop_channel; + /** num of removed channel */ + t_u8 num_remvoed_channel; + /** Number of channels in scan_channel_list */ + t_u32 num_of_chan; + /** scan channel list in ACS mode */ + scan_chan_list chan_list[MLAN_MAX_CHANNEL]; +} mlan_uap_scan_channels; + +/** mlan_uap_oper_ctrl */ +typedef struct _mlan_uap_oper_ctrl { + /** control value + * 0: do nothing, + * 2: uap stops and restarts automaticaly + */ + t_u16 ctrl_value; + /** channel opt + * 1: uap restart on default 2.4G/channel 6 + * 2: uap restart on the band/channel configured by driver previously + * 3: uap restart on the band/channel specified by band_cfg and channel + */ + t_u16 chan_opt; + /** band cfg 0 + * 0: 20Mhz 2: 40 Mhz 3: 80Mhz + */ + t_u8 band_cfg; + /** channel */ + t_u8 channel; +} mlan_uap_oper_ctrl; + +/** mlan_uap_acs_scan */ +typedef struct _mlan_uap_acs_scan { + /** band */ + Band_Config_t bandcfg; + /** channel */ + t_u8 chan; +} mlan_uap_acs_scan; + +/** station is authorized (802.1X) */ +#define STA_FLAG_AUTHORIZED MBIT(1) +/** Station is capable of receiving frames with short barker preamble */ +#define STA_FLAG_SHORT_PREAMBLE MBIT(2) +/** station is WME/QoS capable */ +#define STA_FLAG_WME MBIT(3) +/** station uses management frame protection */ +#define STA_FLAG_MFP MBIT(4) +/** station is authenticated */ +#define STA_FLAG_AUTHENTICATED MBIT(5) +/** station is a TDLS peer */ +#define STA_FLAG_TDLS_PEER MBIT(6) +/** station is associated */ +#define STA_FLAG_ASSOCIATED MBIT(7) +/** mlan_ds_sta_info */ +typedef struct _mlan_ds_sta_info { + /** aid */ + t_u16 aid; + /** peer_mac */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Listen Interval */ + int listen_interval; + /** Capability Info */ + t_u16 cap_info; + /** station flag */ + t_u32 sta_flags; + /** tlv len */ + t_u16 tlv_len; + /** tlv start */ + t_u8 tlv[]; +} mlan_ds_sta_info; +#endif + +#ifdef WIFI_DIRECT_SUPPORT +/** mode: disable wifi direct */ +#define WIFI_DIRECT_MODE_DISABLE 0 +/** mode: listen */ +#define WIFI_DIRECT_MODE_LISTEN 1 +/** mode: GO */ +#define WIFI_DIRECT_MODE_GO 2 +/** mode: client */ +#define WIFI_DIRECT_MODE_CLIENT 3 +/** mode: find */ +#define WIFI_DIRECT_MODE_FIND 4 +/** mode: stop find */ +#define WIFI_DIRECT_MODE_STOP_FIND 5 +#endif + +/** Type definition of mlan_ds_bss for MLAN_IOCTL_BSS */ +typedef struct _mlan_ds_bss { + /** Sub-command */ + t_u32 sub_command; + /** BSS parameter */ + union { + /** SSID-BSSID for MLAN_OID_BSS_START */ + mlan_ssid_bssid ssid_bssid; + /** BSSID for MLAN_OID_BSS_STOP */ + mlan_802_11_mac_addr bssid; + /** BSS mode for MLAN_OID_BSS_MODE */ + t_u32 bss_mode; + /** BSS channel/frequency for MLAN_OID_BSS_CHANNEL */ + chan_freq bss_chan; + /** BSS channel list for MLAN_OID_BSS_CHANNEL_LIST */ + mlan_chan_list chanlist; + /** MAC address for MLAN_OID_BSS_MAC_ADDR */ + mlan_802_11_mac_addr mac_addr; + /** Multicast list for MLAN_OID_BSS_MULTICAST_LIST */ + mlan_multicast_list multicast_list; + /** Beacon interval for MLAN_OID_IBSS_BCN_INTERVAL */ + t_u32 bcn_interval; + /** ATIM window for MLAN_OID_IBSS_ATIM_WINDOW */ + t_u32 atim_window; + /** deauth param for MLAN_OID_BSS_STOP & MLAN_OID_UAP_DEAUTH_STA + */ + mlan_deauth_param deauth_param; +#ifdef UAP_SUPPORT + /** host based flag for MLAN_OID_BSS_START */ + t_u8 host_based; + /** BSS param for AP mode for MLAN_OID_UAP_BSS_CONFIG */ + mlan_uap_bss_param bss_config; + /** AP Wmm parameters for MLAN_OID_UAP_CFG_WMM_PARAM */ + wmm_parameter_t ap_wmm_para; + /** ap scan channels for MLAN_OID_UAP_SCAN_CHANNELS*/ + mlan_uap_scan_channels ap_scan_channels; + /** ap channel for MLAN_OID_UAP_CHANNEL*/ + chan_band_info ap_channel; + /** ap operation control for MLAN_OID_UAP_OPER_CTRL*/ + mlan_uap_oper_ctrl ap_oper_ctrl; + /** AP acs scan MLAN_OID_UAP_ACS_SCAN */ + mlan_uap_acs_scan ap_acs_scan; +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + /** BSS role for MLAN_OID_BSS_ROLE */ + t_u8 bss_role; +#endif +#ifdef WIFI_DIRECT_SUPPORT + /** wifi direct mode for MLAN_OID_WIFI_DIRECT_MODE */ + t_u16 wfd_mode; +#endif +#ifdef STA_SUPPORT + /** Listen interval for MLAN_OID_BSS_LISTEN_INTERVAL */ + t_u16 listen_interval; + /** STA channel info for MLAN_OID_BSS_CHAN_INFO */ + chan_band_info sta_channel; +#endif +#ifdef UAP_SUPPORT + /** STA info for MLAN_OID_UAP_ADD_STATION */ + mlan_ds_sta_info sta_info; +#endif + } param; +} mlan_ds_bss, *pmlan_ds_bss; + +/* OTP Region info */ +typedef MLAN_PACK_START struct _otp_region_info { + t_u8 country_code[2]; + t_u8 region_code; + t_u8 environment; + t_u16 force_reg : 1; + t_u16 reserved : 15; +} MLAN_PACK_END otp_region_info_t; + +/** Type definition of mlan_ds_custom_reg_domain */ +typedef struct _mlan_ds_custom_reg_domain { + otp_region_info_t region; + /** num of 2g channels in custom_reg_domain */ + t_u8 num_bg_chan; + /** num of 5g channels in custom_reg_domain */ + t_u8 num_a_chan; + /** cfp table */ + chan_freq_power_t cfp_tbl[]; +} mlan_ds_custom_reg_domain; +/*-----------------------------------------------------------------*/ +/** Radio Control Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for band */ +enum _mlan_band_def { + BAND_B = 1, + BAND_G = 2, + BAND_A = 4, + BAND_GN = 8, + BAND_AN = 16, + BAND_GAC = 32, + BAND_AAC = 64, + BAND_GAX = 256, + BAND_AAX = 512, + +}; + +/** Channel bandwidth */ +#define CHANNEL_BW_20MHZ 0 +#define CHANNEL_BW_40MHZ_ABOVE 1 +#define CHANNEL_BW_40MHZ_BELOW 3 +/** secondary channel is 80Mhz bandwidth for 11ac */ +#define CHANNEL_BW_80MHZ 4 +#define CHANNEL_BW_160MHZ 5 + +/** RF antenna selection */ +#define RF_ANTENNA_MASK(n) ((1 << (n)) - 1) +/** RF antenna auto select */ +#define RF_ANTENNA_AUTO 0xFFFF + +/** Type definition of mlan_ds_band_cfg for MLAN_OID_BAND_CFG */ +typedef struct _mlan_ds_band_cfg { + /** Infra band */ + t_u32 config_bands; + /** Ad-hoc start band */ + t_u32 adhoc_start_band; + /** Ad-hoc start channel */ + t_u32 adhoc_channel; + /** 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; +/** Type definition of mlan_ds_mimo_switch for MLAN_OID_MIMO_SWITCH */ +typedef struct _mlan_ds_mimo_switch { + /** Tx antenna mode */ + t_u8 txpath_antmode; + /** Rx antenna mode */ + t_u8 rxpath_antmode; +} mlan_ds_mimo_switch, *pmlan_ds_mimo_switch; +/** Type definition of mlan_ds_ant_cfg_1x1 for MLAN_OID_ANT_CFG */ +typedef struct _mlan_ds_ant_cfg_1x1 { + /** Antenna mode */ + t_u32 antenna; + /** Evaluate time */ + t_u16 evaluate_time; + /** Current antenna */ + t_u16 current_antenna; +} mlan_ds_ant_cfg_1x1, *pmlan_ds_ant_cfg_1x1; + +/** Type definition of mlan_ds_remain_chan for MLAN_OID_REMAIN_CHAN_CFG */ +typedef struct _mlan_ds_remain_chan { + /** remove flag */ + t_u16 remove; + /** status */ + t_u8 status; + /** Band cfg */ + Band_Config_t bandcfg; + /** channel */ + t_u8 channel; + /** remain time: Unit ms*/ + t_u32 remain_period; +} mlan_ds_remain_chan, *pmlan_ds_remain_chan; + +/** Type definition of mlan_ds_radio_cfg for MLAN_IOCTL_RADIO_CFG */ +typedef struct _mlan_ds_radio_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Radio control parameter */ + union { + /** Radio on/off for MLAN_OID_RADIO_CTRL */ + t_u32 radio_on_off; + /** Band info for MLAN_OID_BAND_CFG */ + mlan_ds_band_cfg band_cfg; + /** Antenna info for MLAN_OID_ANT_CFG */ + mlan_ds_ant_cfg ant_cfg; + /** Antenna mode for MLAN_OID_MIMO_SWITCH */ + mlan_ds_mimo_switch mimo_switch_cfg; + /** Antenna info for MLAN_OID_ANT_CFG */ + mlan_ds_ant_cfg_1x1 ant_cfg_1x1; + /** remain on channel for MLAN_OID_REMAIN_CHAN_CFG */ + mlan_ds_remain_chan remain_chan; + } param; +} mlan_ds_radio_cfg, *pmlan_ds_radio_cfg; + +enum COALESCE_OPERATION { + RECV_FILTER_MATCH_TYPE_EQ = 0x80, + RECV_FILTER_MATCH_TYPE_NE, +}; + +enum COALESCE_PACKET_TYPE { + PACKET_TYPE_UNICAST = 1, + PACKET_TYPE_MULTICAST = 2, + PACKET_TYPE_BROADCAST = 3 +}; + +#define COALESCE_MAX_RULES 8 +#define COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ +#define COALESCE_MAX_FILTERS 4 +#define MAX_COALESCING_DELAY 100 /* in msecs */ +#define MAX_PATTERN_LEN 20 +#define MAX_OFFSET_LEN 100 + +struct filt_field_param { + t_u8 operation; + t_u8 operand_len; + t_u16 offset; + t_u8 operand_byte_stream[COALESCE_MAX_BYTESEQ]; +}; + +struct coalesce_rule { + t_u16 max_coalescing_delay; + t_u8 num_of_fields; + t_u8 pkt_type; + struct filt_field_param params[COALESCE_MAX_FILTERS]; +}; + +typedef struct _mlan_ds_coalesce_cfg { + t_u16 num_of_rules; + struct coalesce_rule rule[COALESCE_MAX_RULES]; +} mlan_ds_coalesce_cfg; + +/*-----------------------------------------------------------------*/ +/** SNMP MIB Group */ +/*-----------------------------------------------------------------*/ +/** Type definition of mlan_ds_snmp_mib for MLAN_IOCTL_SNMP_MIB */ +typedef struct _mlan_ds_snmp_mib { + /** Sub-command */ + t_u32 sub_command; + /** SNMP MIB parameter */ + union { + /** RTS threshold for MLAN_OID_SNMP_MIB_RTS_THRESHOLD */ + t_u32 rts_threshold; + /** Fragment threshold for MLAN_OID_SNMP_MIB_FRAG_THRESHOLD */ + t_u32 frag_threshold; + /** Retry count for MLAN_OID_SNMP_MIB_RETRY_COUNT */ + t_u32 retry_count; + /** OID value for MLAN_OID_SNMP_MIB_DOT11D/H */ + t_u32 oid_value; + /** DTIM period for MLAN_OID_SNMP_MIB_DTIM_PERIOD */ + t_u32 dtim_period; + /** Singal_ext Enable for MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE */ + t_u8 signalext_enable; + /** Control deauth when uap switch channel */ + t_u8 deauthctrl; + } param; +} mlan_ds_snmp_mib, *pmlan_ds_snmp_mib; + +/*-----------------------------------------------------------------*/ +/** Status Information Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for ad-hoc status */ +enum _mlan_adhoc_status { + ADHOC_IDLE, + ADHOC_STARTED, + ADHOC_JOINED, + ADHOC_COALESCED, + ADHOC_STARTING +}; + +typedef struct _mlan_ds_get_stats_org { + /** Statistics counter */ + /** Multicast transmitted frame count */ + t_u32 mcast_tx_frame; + /** Failure count */ + t_u32 failed; + /** Retry count */ + t_u32 retry; + /** Multi entry count */ + t_u32 multi_retry; + /** Duplicate frame count */ + t_u32 frame_dup; + /** RTS success count */ + t_u32 rts_success; + /** RTS failure count */ + t_u32 rts_failure; + /** Ack failure count */ + t_u32 ack_failure; + /** Rx fragmentation count */ + t_u32 rx_frag; + /** Multicast Tx frame count */ + t_u32 mcast_rx_frame; + /** FCS error count */ + t_u32 fcs_error; + /** Tx frame count */ + t_u32 tx_frame; + /** WEP ICV error count */ + t_u32 wep_icv_error[4]; + /** beacon recv count */ + t_u32 bcn_rcv_cnt; + /** beacon miss count */ + t_u32 bcn_miss_cnt; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; +} mlan_ds_get_stats_org; + +/** Type definition of mlan_ds_get_stats for MLAN_OID_GET_STATS */ +typedef struct _mlan_ds_get_stats { + /** Statistics counter */ + /** Multicast transmitted frame count */ + t_u32 mcast_tx_frame; + /** Failure count */ + t_u32 failed; + /** Retry count */ + t_u32 retry; + /** Multi entry count */ + t_u32 multi_retry; + /** Duplicate frame count */ + t_u32 frame_dup; + /** RTS success count */ + t_u32 rts_success; + /** RTS failure count */ + t_u32 rts_failure; + /** Ack failure count */ + t_u32 ack_failure; + /** Rx fragmentation count */ + t_u32 rx_frag; + /** Multicast Tx frame count */ + t_u32 mcast_rx_frame; + /** FCS error count */ + t_u32 fcs_error; + /** Tx frame count */ + t_u32 tx_frame; + /** WEP ICV error count */ + t_u32 wep_icv_error[4]; + /** beacon recv count */ + t_u32 bcn_rcv_cnt; + /** beacon miss count */ + t_u32 bcn_miss_cnt; + /** received amsdu count*/ + t_u32 amsdu_rx_cnt; + /** received msdu count in amsdu*/ + t_u32 msdu_in_rx_amsdu_cnt; + /** tx amsdu count*/ + t_u32 amsdu_tx_cnt; + /** tx msdu count in amsdu*/ + t_u32 msdu_in_tx_amsdu_cnt; + + /** Tx frag count */ + t_u32 tx_frag_cnt; + /** Qos Tx frag count */ + t_u32 qos_tx_frag_cnt[8]; + /** Qos failed count */ + t_u32 qos_failed_cnt[8]; + /** Qos retry count */ + t_u32 qos_retry_cnt[8]; + /** Qos multi retry count */ + t_u32 qos_multi_retry_cnt[8]; + /** Qos frame dup count */ + t_u32 qos_frm_dup_cnt[8]; + /** Qos rts success count */ + t_u32 qos_rts_suc_cnt[8]; + /** Qos rts failure count */ + t_u32 qos_rts_failure_cnt[8]; + /** Qos ack failure count */ + t_u32 qos_ack_failure_cnt[8]; + /** Qos Rx frag count */ + t_u32 qos_rx_frag_cnt[8]; + /** Qos Tx frame count */ + t_u32 qos_tx_frm_cnt[8]; + /** Qos discarded frame count */ + t_u32 qos_discarded_frm_cnt[8]; + /** Qos mpdus Rx count */ + t_u32 qos_mpdus_rx_cnt[8]; + /** Qos retry rx count */ + t_u32 qos_retries_rx_cnt[8]; + /** CMAC ICV errors count */ + t_u32 cmacicv_errors; + /** CMAC replays count */ + t_u32 cmac_replays; + /** mgmt CCMP replays count */ + t_u32 mgmt_ccmp_replays; + /** TKIP ICV errors count */ + t_u32 tkipicv_errors; + /** TKIP replays count */ + t_u32 tkip_replays; + /** CCMP decrypt errors count */ + t_u32 ccmp_decrypt_errors; + /** CCMP replays count */ + t_u32 ccmp_replays; + /** Tx amsdu count */ + t_u32 tx_amsdu_cnt; + /** failed amsdu count */ + t_u32 failed_amsdu_cnt; + /** retry amsdu count */ + t_u32 retry_amsdu_cnt; + /** multi-retry amsdu count */ + t_u32 multi_retry_amsdu_cnt; + /** Tx octets in amsdu count */ + t_u64 tx_octets_in_amsdu_cnt; + /** amsdu ack failure count */ + t_u32 amsdu_ack_failure_cnt; + /** Rx amsdu count */ + t_u32 rx_amsdu_cnt; + /** Rx octets in amsdu count */ + t_u64 rx_octets_in_amsdu_cnt; + /** Tx ampdu count */ + t_u32 tx_ampdu_cnt; + /** tx mpdus in ampdu count */ + t_u32 tx_mpdus_in_ampdu_cnt; + /** tx octets in ampdu count */ + t_u64 tx_octets_in_ampdu_cnt; + /** ampdu Rx count */ + t_u32 ampdu_rx_cnt; + /** mpdu in Rx ampdu count */ + t_u32 mpdu_in_rx_ampdu_cnt; + /** Rx octets ampdu count */ + t_u64 rx_octets_in_ampdu_cnt; + /** ampdu delimiter CRC error count */ + t_u32 ampdu_delimiter_crc_error_cnt; + /** Rx Stuck Related Info*/ + /** Rx Stuck Issue count */ + t_u32 rx_stuck_issue_cnt[2]; + /** Rx Stuck Recovery count */ + t_u32 rx_stuck_recovery_cnt; + /** Rx Stuck TSF */ + t_u64 rx_stuck_tsf[2]; + /** Tx Watchdog Recovery Related Info */ + /** Tx Watchdog Recovery count */ + t_u32 tx_watchdog_recovery_cnt; + /** Tx Watchdog TSF */ + t_u64 tx_watchdog_tsf[2]; + /** Channel Switch Related Info */ + /** Channel Switch Announcement Sent */ + t_u32 channel_switch_ann_sent; + /** Channel Switch State */ + t_u32 channel_switch_state; + /** Register Class */ + t_u32 reg_class; + /** Channel Number */ + t_u32 channel_number; + /** Channel Switch Mode */ + t_u32 channel_switch_mode; +} 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 +#define MAX_PATH_NUM 3 +/** path A */ +#define PATH_A 0x01 +/** path B */ +#define PATH_B 0x02 +/** path AB */ +#define PATH_AB 0x03 +/** ALL the path */ +#define PATH_ALL 0 +/** Type definition of mlan_ds_get_signal for MLAN_OID_GET_SIGNAL */ +typedef struct _mlan_ds_get_signal { + /** Selector of get operation */ + /* + * Bit0: Last Beacon RSSI, Bit1: Average Beacon RSSI, + * Bit2: Last Data RSSI, Bit3: Average Data RSSI, + * Bit4: Last Beacon SNR, Bit5: Average Beacon SNR, + * Bit6: Last Data SNR, Bit7: Average Data SNR, + * Bit8: Last Beacon NF, Bit9: Average Beacon NF, + * Bit10: Last Data NF, Bit11: Average Data NF + * + * Bit0: PATH A + * Bit1: PATH B + */ + t_u16 selector; + + /** RSSI */ + /** RSSI of last beacon */ + t_s16 bcn_rssi_last; + /** RSSI of beacon average */ + t_s16 bcn_rssi_avg; + /** RSSI of last data packet */ + t_s16 data_rssi_last; + /** RSSI of data packet average */ + t_s16 data_rssi_avg; + + /** SNR */ + /** SNR of last beacon */ + t_s16 bcn_snr_last; + /** SNR of beacon average */ + t_s16 bcn_snr_avg; + /** SNR of last data packet */ + t_s16 data_snr_last; + /** SNR of data packet average */ + t_s16 data_snr_avg; + + /** NF */ + /** NF of last beacon */ + t_s16 bcn_nf_last; + /** NF of beacon average */ + t_s16 bcn_nf_avg; + /** NF of last data packet */ + t_s16 data_nf_last; + /** NF of data packet average */ + t_s16 data_nf_avg; +} mlan_ds_get_signal, *pmlan_ds_get_signal; + +/** bit for 2.4 G antenna diversity */ +#define ANT_DIVERSITY_2G MBIT(3) +/** bit for 5 G antenna diversity */ +#define ANT_DIVERSITY_5G MBIT(7) + +/** mlan_fw_info data structure for MLAN_OID_GET_FW_INFO */ +typedef struct _mlan_fw_info { + /** Firmware version */ + t_u32 fw_ver; + /** MAC address */ + mlan_802_11_mac_addr mac_addr; + /** 802.11n device capabilities */ + t_u32 hw_dot_11n_dev_cap; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; + /** user's MCS setting */ + t_u8 usr_dev_mcs_support; + /** 802.11ac device capabilities */ + t_u32 hw_dot_11ac_dev_cap; + /** 802.11ac device Capabilities for 2.4GHz */ + t_u32 usr_dot_11ac_dev_cap_bg; + /** 802.11ac device Capabilities for 5GHz */ + t_u32 usr_dot_11ac_dev_cap_a; + /** length of hw he capability */ + t_u8 hw_hecap_len; + /** 802.11ax HE capability */ + t_u8 hw_he_cap[54]; + /** length of hw 2.4G he capability */ + t_u8 hw_2g_hecap_len; + /** 802.11ax 2.4G HE capability */ + t_u8 hw_2g_he_cap[54]; + /** 802.11ac device support for MIMO abstraction of MCSs */ + t_u32 hw_dot_11ac_mcs_support; + /** User conf 802.11ac device support for MIMO abstraction of MCSs */ + t_u32 usr_dot_11ac_mcs_support; + /** fw supported band */ + t_u16 fw_bands; + /** region code */ + t_u16 region_code; + /** force_reg */ + t_u8 force_reg; + /** ECSA support */ + t_u8 ecsa_enable; + /** Get log support */ + t_u8 getlog_enable; + /** FW support for embedded supplicant */ + t_u8 fw_supplicant_support; + /** ant info */ + t_u8 antinfo; + /** max AP associated sta count supported by fw */ + t_u8 max_ap_assoc_sta; + /** Bandwidth not support 80Mhz */ + t_u8 prohibit_80mhz; +} mlan_fw_info, *pmlan_fw_info; + +/** Version string buffer length */ +#define MLAN_MAX_VER_STR_LEN 128 + +/** mlan_ver_ext data structure for MLAN_OID_GET_VER_EXT */ +typedef struct _mlan_ver_ext { + /** Selected version string */ + t_u32 version_str_sel; + /** Version string */ + char version_str[MLAN_MAX_VER_STR_LEN]; +} mlan_ver_ext, *pmlan_ver_ext; + +#ifdef BIG_ENDIAN_SUPPORT +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t { + /** Extended Capabilities value */ + t_u8 rsvdBit79 : 1; /* bit 79 */ + t_u8 TWTResp : 1; /* bit 78 */ + t_u8 TWTReq : 1; /* bit 77 */ + t_u8 rsvdBit76 : 1; /* bit 76 */ + t_u8 rsvdBit75 : 1; /* bit 75 */ + t_u8 rsvdBit74 : 1; /* bit 74 */ + t_u8 rsvdBit73 : 1; /* bit 73 */ + t_u8 FILS : 1; /* bit 72 */ + t_u8 FTMI : 1; /* bit 71 */ + t_u8 FTMR : 1; /* bit 70 */ + t_u8 CAQ : 1; /* bit 69 */ + t_u8 rsvdBit68 : 1; /* bit 68 */ + t_u8 NCC : 1; /* bit 67 */ + t_u8 rsvdBit66 : 1; /* bit 66 */ + t_u8 chanSchedMgnt : 1; /* bit 65 */ + t_u8 MaxAMSDU1 : 1; /* bit 64 */ + t_u8 MaxAMSDU0 : 1; /* bit 63 */ + t_u8 OperModeNtf : 1; /* bit 62 */ + t_u8 TDLSWildBandwidth : 1; /* bit 61 */ + t_u8 rsvdBit60 : 1; /* bit 60 */ + t_u8 rsvdBit59 : 1; /* bit 59 */ + t_u8 rsvdBit58 : 1; /* bit 58 */ + t_u8 rsvdBit57 : 1; /* bit 57 */ + t_u8 rsvdBit56 : 1; /* bit 56 */ + t_u8 rsvdBit55 : 1; /* bit 55 */ + t_u8 rsvdBit54 : 1; /* bit 54 */ + t_u8 rsvdBit53 : 1; /* bit 53 */ + t_u8 rsvdBit52 : 1; /* bit 52 */ + t_u8 rsvdBit51 : 1; /* bit 51 */ + t_u8 rsvdBit50 : 1; /* bit 50 */ + t_u8 rsvdBit49 : 1; /* bit 49 */ + t_u8 rsvdBit48 : 1; /* bit 48 */ + t_u8 rsvdBit47 : 1; /* bit 47 */ + t_u8 rsvdBit46 : 1; /* bit 46 */ + t_u8 rsvdBit45 : 1; /* bit 45 */ + t_u8 rsvdBit44 : 1; /* bit 44 */ + t_u8 rsvdBit43 : 1; /* bit 43 */ + t_u8 rsvdBit42 : 1; /* bit 42 */ + t_u8 rsvdBit41 : 1; /* bit 41 */ + t_u8 rsvdBit40 : 1; /* bit 40 */ + t_u8 TDLSChlSwitchProhib : 1; /* bit 39 */ + t_u8 TDLSProhibited : 1; /* bit 38 */ + t_u8 TDLSSupport : 1; /* bit 37 */ + t_u8 MSGCF_Capa : 1; /* bit 36 */ + t_u8 Reserved35 : 1; /* bit 35 */ + t_u8 SSPN_Interface : 1; /* bit 34 */ + t_u8 EBR : 1; /* bit 33 */ + t_u8 Qos_Map : 1; /* bit 32 */ + t_u8 Interworking : 1; /* bit 31 */ + t_u8 TDLSChannelSwitching : 1; /* bit 30 */ + t_u8 TDLSPeerPSMSupport : 1; /* bit 29 */ + t_u8 TDLSPeerUAPSDSupport : 1; /* bit 28 */ + t_u8 UTC : 1; /* bit 27 */ + t_u8 DMS : 1; /* bit 26 */ + t_u8 SSID_List : 1; /* bit 25 */ + t_u8 ChannelUsage : 1; /* bit 24 */ + t_u8 TimingMeasurement : 1; /* bit 23 */ + t_u8 MultipleBSSID : 1; /* bit 22 */ + t_u8 AC_StationCount : 1; /* bit 21 */ + t_u8 QoSTrafficCap : 1; /* bit 20 */ + t_u8 BSS_Transition : 1; /* bit 19 */ + t_u8 TIM_Broadcast : 1; /* bit 18 */ + t_u8 WNM_Sleep : 1; /* bit 17 */ + t_u8 TFS : 1; /* bit 16 */ + t_u8 GeospatialLocation : 1; /* bit 15 */ + t_u8 CivicLocation : 1; /* bit 14 */ + t_u8 CollocatedIntf : 1; /* bit 13 */ + t_u8 ProxyARPService : 1; /* bit 12 */ + t_u8 FMS : 1; /* bit 11 */ + t_u8 LocationTracking : 1; /* bit 10 */ + t_u8 MulticastDiagnostics : 1; /* bit 9 */ + t_u8 Diagnostics : 1; /* bit 8 */ + t_u8 Event : 1; /* bit 7 */ + t_u8 SPSMP_Support : 1; /* bit 6 */ + t_u8 Reserved5 : 1; /* bit 5 */ + t_u8 PSMP_Capable : 1; /* bit 4 */ + t_u8 RejectUnadmFrame : 1; /* bit 3 */ + t_u8 ExtChanSwitching : 1; /* bit 2 */ + t_u8 Reserved1 : 1; /* bit 1 */ + t_u8 BSS_CoexistSupport : 1; /* bit 0 */ +} MLAN_PACK_END ExtCap_t, *pExtCap_t; +#else +/** Extended Capabilities Data */ +typedef struct MLAN_PACK_START _ExtCap_t { + /** Extended Capabilities value */ + t_u8 BSS_CoexistSupport : 1; /* bit 0 */ + t_u8 Reserved1 : 1; /* bit 1 */ + t_u8 ExtChanSwitching : 1; /* bit 2 */ + t_u8 RejectUnadmFrame : 1; /* bit 3 */ + t_u8 PSMP_Capable : 1; /* bit 4 */ + t_u8 Reserved5 : 1; /* bit 5 */ + t_u8 SPSMP_Support : 1; /* bit 6 */ + t_u8 Event : 1; /* bit 7 */ + t_u8 Diagnostics : 1; /* bit 8 */ + t_u8 MulticastDiagnostics : 1; /* bit 9 */ + t_u8 LocationTracking : 1; /* bit 10 */ + t_u8 FMS : 1; /* bit 11 */ + t_u8 ProxyARPService : 1; /* bit 12 */ + t_u8 CollocatedIntf : 1; /* bit 13 */ + t_u8 CivicLocation : 1; /* bit 14 */ + t_u8 GeospatialLocation : 1; /* bit 15 */ + t_u8 TFS : 1; /* bit 16 */ + t_u8 WNM_Sleep : 1; /* bit 17 */ + t_u8 TIM_Broadcast : 1; /* bit 18 */ + t_u8 BSS_Transition : 1; /* bit 19 */ + t_u8 QoSTrafficCap : 1; /* bit 20 */ + t_u8 AC_StationCount : 1; /* bit 21 */ + t_u8 MultipleBSSID : 1; /* bit 22 */ + t_u8 TimingMeasurement : 1; /* bit 23 */ + t_u8 ChannelUsage : 1; /* bit 24 */ + t_u8 SSID_List : 1; /* bit 25 */ + t_u8 DMS : 1; /* bit 26 */ + t_u8 UTC : 1; /* bit 27 */ + t_u8 TDLSPeerUAPSDSupport : 1; /* bit 28 */ + t_u8 TDLSPeerPSMSupport : 1; /* bit 29 */ + t_u8 TDLSChannelSwitching : 1; /* bit 30 */ + t_u8 Interworking : 1; /* bit 31 */ + t_u8 Qos_Map : 1; /* bit 32 */ + t_u8 EBR : 1; /* bit 33 */ + t_u8 SSPN_Interface : 1; /* bit 34 */ + t_u8 Reserved35 : 1; /* bit 35 */ + t_u8 MSGCF_Capa : 1; /* bit 36 */ + t_u8 TDLSSupport : 1; /* bit 37 */ + t_u8 TDLSProhibited : 1; /* bit 38 */ + t_u8 TDLSChlSwitchProhib : 1; /* bit 39 */ + t_u8 rsvdBit40 : 1; /* bit 40 */ + t_u8 rsvdBit41 : 1; /* bit 41 */ + t_u8 rsvdBit42 : 1; /* bit 42 */ + t_u8 rsvdBit43 : 1; /* bit 43 */ + t_u8 rsvdBit44 : 1; /* bit 44 */ + t_u8 rsvdBit45 : 1; /* bit 45 */ + t_u8 rsvdBit46 : 1; /* bit 46 */ + t_u8 rsvdBit47 : 1; /* bit 47 */ + t_u8 rsvdBit48 : 1; /* bit 48 */ + t_u8 rsvdBit49 : 1; /* bit 49 */ + t_u8 rsvdBit50 : 1; /* bit 50 */ + t_u8 rsvdBit51 : 1; /* bit 51 */ + t_u8 rsvdBit52 : 1; /* bit 52 */ + t_u8 rsvdBit53 : 1; /* bit 53 */ + t_u8 rsvdBit54 : 1; /* bit 54 */ + t_u8 rsvdBit55 : 1; /* bit 55 */ + t_u8 rsvdBit56 : 1; /* bit 56 */ + t_u8 rsvdBit57 : 1; /* bit 57 */ + t_u8 rsvdBit58 : 1; /* bit 58 */ + t_u8 rsvdBit59 : 1; /* bit 59 */ + t_u8 rsvdBit60 : 1; /* bit 60 */ + t_u8 TDLSWildBandwidth : 1; /* bit 61 */ + t_u8 OperModeNtf : 1; /* bit 62 */ + t_u8 MaxAMSDU0 : 1; /* bit 63 */ + t_u8 MaxAMSDU1 : 1; /* bit 64 */ + t_u8 chanSchedMgnt : 1; /* bit 65 */ + t_u8 rsvdBit66 : 1; /* bit 66 */ + t_u8 NCC : 1; /* bit 67 */ + t_u8 rsvdBit68 : 1; /* bit 68 */ + t_u8 CAQ : 1; /* bit 69 */ + t_u8 FTMR : 1; /* bit 70 */ + t_u8 FTMI : 1; /* bit 71 */ + t_u8 FILS : 1; /* bit 72 */ + t_u8 rsvdBit73 : 1; /* bit 73 */ + t_u8 rsvdBit74 : 1; /* bit 74 */ + t_u8 rsvdBit75 : 1; /* bit 75 */ + t_u8 rsvdBit76 : 1; /* bit 76 */ + t_u8 TWTReq : 1; /* bit 77 */ + t_u8 TWTResp : 1; /* bit 78 */ + t_u8 rsvdBit79 : 1; /* bit 79 */ +} MLAN_PACK_END ExtCap_t, *pExtCap_t; +#endif + +/** ExtCap : TDLS prohibited */ +#define IS_EXTCAP_TDLS_PROHIBITED(ext_cap) (ext_cap.TDLSProhibited) +/** ExtCap : TDLS channel switch prohibited */ +#define IS_EXTCAP_TDLS_CHLSWITCHPROHIB(ext_cap) (ext_cap.TDLSChlSwitchProhib) + +/** mlan_bss_info data structure for MLAN_OID_GET_BSS_INFO */ +typedef struct _mlan_bss_info { + /** BSS mode */ + t_u32 bss_mode; + /** SSID */ + mlan_802_11_ssid ssid; + /** Table index */ + t_u32 scan_table_idx; + /** Channel */ + t_u32 bss_chan; + /** Band */ + t_u8 bss_band; + /** Region code */ + t_u32 region_code; + /** Connection status */ + t_u32 media_connected; + /** Radio on */ + t_u32 radio_on; + /** Max power level in dBm */ + t_s32 max_power_level; + /** Min power level in dBm */ + t_s32 min_power_level; + /** Adhoc state */ + t_u32 adhoc_state; + /** NF of last beacon */ + t_s32 bcn_nf_last; + /** wep status */ + t_u32 wep_status; + /** scan block status */ + t_u8 scan_block; + /** Host Sleep configured flag */ + t_u32 is_hs_configured; + /** Deep Sleep flag */ + t_u32 is_deep_sleep; + /** BSSID */ + mlan_802_11_mac_addr bssid; +#ifdef STA_SUPPORT + /** Capability Info */ + t_u16 capability_info; + /** Beacon Interval */ + t_u16 beacon_interval; + /** Listen Interval */ + t_u16 listen_interval; + /** Association Id */ + t_u16 assoc_id; + /** AP/Peer supported rates */ + t_u8 peer_supp_rates[MLAN_SUPPORTED_RATES]; + /** extend capability for AP */ + ExtCap_t ext_cap; +#endif /* STA_SUPPORT */ + /** Mobility Domain ID */ + t_u16 mdid; + /** FT Capability policy */ + t_u8 ft_cap; + /** 11h active */ + t_bool is_11h_active; + /** dfs check channel */ + t_u8 dfs_check_channel; +} mlan_bss_info, *pmlan_bss_info; + +/** MAXIMUM number of TID */ +#define MAX_NUM_TID 8 + +/** Max RX Win size */ +#define MAX_RX_WINSIZE 64 + +/** rx_reorder_tbl */ +typedef struct { + /** TID */ + t_u16 tid; + /** TA */ + t_u8 ta[MLAN_MAC_ADDR_LENGTH]; + /** Start window */ + t_u32 start_win; + /** Window size */ + t_u32 win_size; + /** amsdu flag */ + t_u8 amsdu; + /** buffer status */ + t_u32 buffer[MAX_RX_WINSIZE]; +} rx_reorder_tbl; + +/** tx_ba_stream_tbl */ +typedef struct { + /** TID */ + t_u16 tid; + /** RA */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** amsdu flag */ + t_u8 amsdu; +} tx_ba_stream_tbl; + +/** Debug command number */ +#define DBG_CMD_NUM 10 + +#ifdef SDIO +/** sdio mp debug number */ +#define SDIO_MP_DBG_NUM 10 +#endif + +#ifdef PCIE +#define MLAN_MAX_TXRX_BD 0x20 +#endif + +/** Maximum size of IEEE Information Elements */ +#define IEEE_MAX_IE_SIZE 256 + +/** max ralist num */ +#define MLAN_MAX_RALIST_NUM 8 +/** ralist info */ +typedef struct _ralist_info { + /** RA list buffer */ + t_u8 ra[MLAN_MAC_ADDR_LENGTH]; + /** total packets in RA list */ + t_u16 total_pkts; + /** tid num */ + t_u8 tid; + /** tx_pause flag */ + t_u8 tx_pause; +} ralist_info, *pralist_info; + +/** mlan_debug_info data structure for MLAN_OID_GET_DEBUG_INFO */ +typedef struct _mlan_debug_info { + /* WMM AC_BK count */ + t_u32 wmm_ac_bk; + /* WMM AC_BE count */ + t_u32 wmm_ac_be; + /* WMM AC_VI count */ + t_u32 wmm_ac_vi; + /* WMM AC_VO count */ + t_u32 wmm_ac_vo; + /** Corresponds to max_tx_buf_size member of mlan_adapter*/ + t_u32 max_tx_buf_size; + /** Corresponds to tx_buf_size member of mlan_adapter*/ + t_u32 tx_buf_size; + /** Corresponds to curr_tx_buf_size member of mlan_adapter*/ + t_u32 curr_tx_buf_size; + /** Tx table num */ + t_u32 tx_tbl_num; + /** Tx ba stream table */ + tx_ba_stream_tbl tx_tbl[MLAN_MAX_TX_BASTREAM_SUPPORTED]; + /** Rx table num */ + t_u32 rx_tbl_num; + /** Rx reorder table*/ + rx_reorder_tbl rx_tbl[MLAN_MAX_RX_BASTREAM_SUPPORTED]; + /** ralist num */ + t_u32 ralist_num; + /** ralist info */ + ralist_info ralist[MLAN_MAX_RALIST_NUM]; + /** Corresponds to ps_mode member of mlan_adapter */ + t_u16 ps_mode; + /** Corresponds to ps_state member of mlan_adapter */ + t_u32 ps_state; +#ifdef STA_SUPPORT + /** Corresponds to is_deep_sleep member of mlan_adapter */ + t_u8 is_deep_sleep; +#endif /** STA_SUPPORT */ + /** Corresponds to pm_wakeup_card_req member of mlan_adapter */ + t_u8 pm_wakeup_card_req; + /** Corresponds to pm_wakeup_fw_try member of mlan_adapter */ + t_u32 pm_wakeup_fw_try; + /** time stamp when host try to wake up firmware */ + t_u32 pm_wakeup_in_secs; + /** wake up timeout happened */ + t_u32 pm_wakeup_timeout; + /** Corresponds to is_hs_configured member of mlan_adapter */ + t_u8 is_hs_configured; + /** Corresponds to hs_activated member of mlan_adapter */ + t_u8 hs_activated; + /** Corresponds to pps_uapsd_mode member of mlan_adapter */ + t_u16 pps_uapsd_mode; + /** Corresponds to sleep_period.period member of mlan_adapter */ + t_u16 sleep_pd; + /** Corresponds to wmm_qosinfo member of mlan_private */ + t_u8 qos_cfg; + /** Corresponds to tx_lock_flag member of mlan_adapter */ + t_u8 tx_lock_flag; + /** Corresponds to port_open member of mlan_private */ + t_u8 port_open; + /** bypass pkt count */ + t_u16 bypass_pkt_count; + /** Corresponds to scan_processing member of mlan_adapter */ + t_u32 scan_processing; + /** Corresponds to mlan_processing member of mlan_adapter */ + t_u32 mlan_processing; + /** Corresponds to main_lock_flag member of mlan_adapter */ + t_u32 main_lock_flag; + /** Corresponds to main_process_cnt member of mlan_adapter */ + t_u32 main_process_cnt; + /** Corresponds to delay_task_flag member of mlan_adapter */ + t_u32 delay_task_flag; + /** mlan_rx_processing */ + t_u32 mlan_rx_processing; + /** rx pkts queued */ + t_u32 rx_pkts_queued; + /** Number of host to card command failures */ + t_u32 num_cmd_host_to_card_failure; + /** Number of host to card sleep confirm failures */ + t_u32 num_cmd_sleep_cfm_host_to_card_failure; + /** Number of host to card Tx failures */ + t_u32 num_tx_host_to_card_failure; + /** Number of allocate buffer failure */ + t_u32 num_alloc_buffer_failure; + /** Number of pkt dropped */ + t_u32 num_pkt_dropped; +#ifdef SDIO + /** Number of card to host command/event failures */ + t_u32 num_cmdevt_card_to_host_failure; + /** Number of card to host Rx failures */ + t_u32 num_rx_card_to_host_failure; + /** Number of interrupt read failures */ + t_u32 num_int_read_failure; + /** Last interrupt status */ + t_u32 last_int_status; + /** number of interrupt receive */ + t_u32 num_of_irq; + /** flag for sdio rx aggr */ + t_u8 sdio_rx_aggr; + /** FW update port number */ + t_u32 mp_update[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX * 2]; + /** Invalid port update count */ + t_u32 mp_invalid_update; + /** Number of packets tx aggr */ + t_u32 mpa_tx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** no more packets count*/ + t_u32 mpa_sent_last_pkt; + /** no write_ports count */ + t_u32 mpa_sent_no_ports; + /** last recv wr_bitmap */ + t_u32 last_recv_wr_bitmap; + /** last mp_wr_bitmap */ + t_u32 last_mp_wr_bitmap[SDIO_MP_DBG_NUM]; + /** last ports for cmd53 write data */ + t_u32 last_mp_wr_ports[SDIO_MP_DBG_NUM]; + /** last len for cmd53 write data */ + t_u32 last_mp_wr_len[SDIO_MP_DBG_NUM]; + /** last curr_wr_port */ + t_u8 last_curr_wr_port[SDIO_MP_DBG_NUM]; + /** length info for cmd53 write data */ + t_u16 last_mp_wr_info[SDIO_MP_DBG_NUM * SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** last mp_index */ + t_u8 last_mp_index; + /** buffer for mp debug */ + t_u8 *mpa_buf; + /** length info for mp buf size */ + t_u32 mpa_buf_size; + /** Number of packets rx aggr */ + t_u32 mpa_rx_count[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + /** mp aggr_pkt limit */ + t_u8 mp_aggr_pkt_limit; +#endif + /** Number of deauthentication events */ + t_u32 num_event_deauth; + /** Number of disassosiation events */ + t_u32 num_event_disassoc; + /** Number of link lost events */ + t_u32 num_event_link_lost; + /** Number of deauthentication commands */ + t_u32 num_cmd_deauth; + /** Number of association comamnd successes */ + t_u32 num_cmd_assoc_success; + /** Number of association command failures */ + t_u32 num_cmd_assoc_failure; + /** Number of consecutive association failures */ + t_u32 num_cons_assoc_failure; + + /** Number of command timeouts */ + t_u32 num_cmd_timeout; + /** Timeout command ID */ + t_u16 timeout_cmd_id; + /** Timeout command action */ + t_u16 timeout_cmd_act; + /** List of last command IDs */ + t_u16 last_cmd_id[DBG_CMD_NUM]; + /** List of last command actions */ + t_u16 last_cmd_act[DBG_CMD_NUM]; + /** Last command index */ + t_u16 last_cmd_index; + /** List of last command response IDs */ + t_u16 last_cmd_resp_id[DBG_CMD_NUM]; + /** Last command response index */ + t_u16 last_cmd_resp_index; + /** List of last events */ + t_u16 last_event[DBG_CMD_NUM]; + /** Last event index */ + t_u16 last_event_index; + /** Number of no free command node */ + t_u16 num_no_cmd_node; + /** pending command id */ + t_u16 pending_cmd; + /** time stamp for dnld last cmd */ + t_u32 dnld_cmd_in_secs; + /** Corresponds to data_sent member of mlan_adapter */ + t_u8 data_sent; + /** Corresponds to cmd_sent member of mlan_adapter */ + t_u8 cmd_sent; + /** SDIO multiple port read bitmap */ + t_u32 mp_rd_bitmap; + /** SDIO multiple port write bitmap */ + t_u32 mp_wr_bitmap; + /** Current available port for read */ + t_u8 curr_rd_port; + /** Current available port for write */ + t_u8 curr_wr_port; +#ifdef PCIE + /** PCIE txbd read pointer */ + t_u32 txbd_rdptr; + /** PCIE txbd write pointer */ + t_u32 txbd_wrptr; + /** PCIE rxbd read pointer */ + t_u32 rxbd_rdptr; + /** PCIE rxbd write pointer */ + t_u32 rxbd_wrptr; + /** PCIE eventbd read pointer */ + t_u32 eventbd_rdptr; + /** PCIE eventbd write pointer */ + t_u32 eventbd_wrptr; + /** Last pkt size in transmit */ + t_u32 last_tx_pkt_size[MLAN_MAX_TXRX_BD]; + /** txbd ring vbase */ + t_u8 *txbd_ring_vbase; + /** txbd ring size */ + t_u32 txbd_ring_size; + /** rxbd ring vbase */ + t_u8 *rxbd_ring_vbase; + /** rxbd ring size */ + t_u32 rxbd_ring_size; + /** evtbd ring vbase */ + t_u8 *evtbd_ring_vbase; + /** evtbd ring size */ + t_u32 evtbd_ring_size; +#endif + /** 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 + /** FW hang report */ + t_u8 fw_hang_report; + /** mlan_adapter pointer */ + t_void *mlan_adapter; + /** mlan_adapter_size */ + t_u32 mlan_adapter_size; + /** mlan_priv vector */ + t_void *mlan_priv[MLAN_MAX_BSS_NUM]; + /** mlan_priv_size */ + t_u32 mlan_priv_size[MLAN_MAX_BSS_NUM]; + /** mlan_priv_num */ + t_u8 mlan_priv_num; +} mlan_debug_info, *pmlan_debug_info; + +#ifdef UAP_SUPPORT +/** Maximum number of clients supported by AP */ +#define MAX_NUM_CLIENTS MAX_STA_COUNT + +/** station info */ +typedef struct _sta_info { + /** STA MAC address */ + t_u8 mac_address[MLAN_MAC_ADDR_LENGTH]; + /** Power mgmt status */ + t_u8 power_mgmt_status; + /** RSSI */ + t_s8 rssi; + /** station bandmode */ + t_u16 bandmode; + /** station stats */ + sta_stats stats; + /** ie length */ + t_u16 ie_len; + /** ie buffer */ + t_u8 ie_buf[]; +} sta_info; + +/** mlan_ds_sta_list structure for MLAN_OID_UAP_STA_LIST */ +typedef struct _mlan_ds_sta_list { + /** station count */ + t_u16 sta_count; + /** station list */ + sta_info info[MAX_NUM_CLIENTS]; +} mlan_ds_sta_list, *pmlan_ds_sta_list; +#endif + +/** Type definition of mlan_ds_get_info for MLAN_IOCTL_GET_INFO */ +typedef struct _mlan_ds_get_info { + /** Sub-command */ + t_u32 sub_command; + + /** Status information parameter */ + union { + /** Signal information for MLAN_OID_GET_SIGNAL */ + mlan_ds_get_signal signal; + /** Signal path id for MLAN_OID_GET_SIGNAL_EXT */ + t_u16 path_id; + /** Signal information for MLAN_OID_GET_SIGNAL_EXT */ + mlan_ds_get_signal signal_ext[MAX_PATH_NUM]; + /** Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_get_stats stats; + /** Statistics information for MLAN_OID_LINK_STATS*/ + t_u8 link_statistic[1]; + /** Firmware information for MLAN_OID_GET_FW_INFO */ + mlan_fw_info fw_info; + /** Extended version information for MLAN_OID_GET_VER_EXT */ + mlan_ver_ext ver_ext; + /** BSS information for MLAN_OID_GET_BSS_INFO */ + mlan_bss_info bss_info; + /** Debug information for MLAN_OID_GET_DEBUG_INFO */ + t_u8 debug_info[1]; +#ifdef UAP_SUPPORT + /** UAP Statistics information for MLAN_OID_GET_STATS */ + mlan_ds_uap_stats ustats; + /** UAP station list for MLAN_OID_UAP_STA_LIST */ + mlan_ds_sta_list sta_list; +#endif + } param; +} mlan_ds_get_info, *pmlan_ds_get_info; + +/*-----------------------------------------------------------------*/ +/** Security Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for authentication mode */ +enum _mlan_auth_mode { + MLAN_AUTH_MODE_OPEN = 0x00, + MLAN_AUTH_MODE_SHARED = 0x01, + MLAN_AUTH_MODE_FT = 0x02, + MLAN_AUTH_MODE_SAE = 0x03, + MLAN_AUTH_MODE_NETWORKEAP = 0x80, + MLAN_AUTH_MODE_AUTO = 0xFF, +}; + +/**Enumeration for AssocAgent authentication mode, sync from FW.*/ +typedef enum { + AssocAgentAuth_Open, + AssocAgentAuth_Shared, + AssocAgentAuth_FastBss, + AssocAgentAuth_FastBss_Skip, + AssocAgentAuth_Network_EAP, + AssocAgentAuth_Wpa3Sae, + AssocAgentAuth_Auto, +} AssocAgentAuthType_e; + +/** Enumeration for encryption mode */ +enum _mlan_encryption_mode { + MLAN_ENCRYPTION_MODE_NONE = 0, + MLAN_ENCRYPTION_MODE_WEP40 = 1, + MLAN_ENCRYPTION_MODE_TKIP = 2, + MLAN_ENCRYPTION_MODE_CCMP = 3, + MLAN_ENCRYPTION_MODE_WEP104 = 4, + MLAN_ENCRYPTION_MODE_GCMP = 5, + MLAN_ENCRYPTION_MODE_GCMP_256 = 6, + MLAN_ENCRYPTION_MODE_CCMP_256 = 7, +}; + +/** Enumeration for PSK */ +enum _mlan_psk_type { + MLAN_PSK_PASSPHRASE = 1, + MLAN_PSK_PMK, + MLAN_PSK_CLEAR, + MLAN_PSK_QUERY, + MLAN_PSK_SAE_PASSWORD, +}; + +/** 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 +/** Minimum sae_password length */ +#define MLAN_MIN_SAE_PASSWORD_LENGTH 8 +/** Maximum sae_password length */ +#define MLAN_MAX_SAE_PASSWORD_LENGTH 255 +/** PMK length */ +#define MLAN_PMK_HEXSTR_LENGTH 64 +/* A few details needed for WEP (Wireless Equivalent Privacy) */ +/** 104 bits */ +#define MAX_WEP_KEY_SIZE 13 +/** 40 bits RC4 - WEP */ +#define MIN_WEP_KEY_SIZE 5 +/** packet number size */ +#define PN_SIZE 16 +/** max seq size of wpa/wpa2 key */ +#define SEQ_MAX_SIZE 8 + +/** key flag for tx_seq */ +#define KEY_FLAG_TX_SEQ_VALID 0x00000001 +/** key flag for rx_seq */ +#define KEY_FLAG_RX_SEQ_VALID 0x00000002 +/** key flag for group key */ +#define KEY_FLAG_GROUP_KEY 0x00000004 +/** key flag for tx */ +#define KEY_FLAG_SET_TX_KEY 0x00000008 +/** key flag for mcast IGTK */ +#define KEY_FLAG_AES_MCAST_IGTK 0x00000010 +/** key flag for remove key */ +#define KEY_FLAG_REMOVE_KEY 0x80000000 +/** key flag for GCMP */ +#define KEY_FLAG_GCMP 0x00000020 +/** key flag for GCMP_256 */ +#define KEY_FLAG_GCMP_256 0x00000040 +/** key flag for ccmp 256 */ +#define KEY_FLAG_CCMP_256 0x00000080 + +/** 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 definition of mlan_sae_password_t */ +typedef struct _mlan_sae_password_t { + /** Length of SAE Password */ + t_u32 sae_password_len; + /** SAE Password */ + t_u8 sae_password[MLAN_MAX_SAE_PASSWORD_LENGTH]; +} mlan_sae_password_t; + +/** Type defnition of mlan_pmk_t */ +typedef struct _mlan_pmk_t { + /** PMK */ + t_u8 pmk[MLAN_MAX_KEY_LENGTH]; +} mlan_pmk_t; + +/** Embedded supplicant RSN type: No RSN */ +#define RSN_TYPE_NO_RSN MBIT(0) +/** Embedded supplicant RSN type: WPA */ +#define RSN_TYPE_WPA MBIT(3) +/** Embedded supplicant RSN type: WPA-NONE */ +#define RSN_TYPE_WPANONE MBIT(4) +/** Embedded supplicant RSN type: WPA2 */ +#define RSN_TYPE_WPA2 MBIT(5) +/** Embedded supplicant RSN type: RFU */ +#define RSN_TYPE_VALID_BITS \ + (RSN_TYPE_NO_RSN | RSN_TYPE_WPA | RSN_TYPE_WPANONE | RSN_TYPE_WPA2) + +/** Embedded supplicant cipher type: TKIP */ +#define EMBED_CIPHER_TKIP MBIT(2) +/** Embedded supplicant cipher type: AES */ +#define EMBED_CIPHER_AES MBIT(3) +/** Embedded supplicant cipher type: RFU */ +#define EMBED_CIPHER_VALID_BITS (EMBED_CIPHER_TKIP | EMBED_CIPHER_AES) + +/** Type definition of mlan_ds_passphrase for MLAN_OID_SEC_CFG_PASSPHRASE */ +typedef struct _mlan_ds_passphrase { + /** SSID may be used */ + mlan_802_11_ssid ssid; + /** BSSID may be used */ + mlan_802_11_mac_addr bssid; + /** Flag for passphrase or pmk used */ + t_u16 psk_type; + /** Passphrase or PMK */ + union { + /** Passphrase */ + mlan_passphrase_t passphrase; + /** SAE Password */ + mlan_sae_password_t sae_password; + /** 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; +#ifdef UAP_SUPPORT + t_u8 sta_mac[MLAN_MAC_ADDR_LENGTH]; +#endif + } param; +} mlan_ds_sec_cfg, *pmlan_ds_sec_cfg; + +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) +#define BIT_TLV_TYPE_CRYPTO_KEY (1 << 0) +#define BIT_TLV_TYPE_CRYPTO_KEY_IV (1 << 1) +#define BIT_TLV_TYPE_CRYPTO_KEY_PREFIX (1 << 2) +#define BIT_TLV_TYPE_CRYPTO_KEY_DATA_BLK (1 << 3) + +/** Type definition of mlan_ds_sup_cfg */ +typedef struct _mlan_ds_sup_cfg { + /** Sub-command */ + t_u8 sub_command; + /** output length */ + t_u16 output_len; + /** number of data blks */ + t_u16 data_blks_nr; + /** sub action code */ + t_u8 sub_action_code; + /** skip bytes */ + t_u16 skip_bytes; + /** iteration */ + t_u32 iteration; + /** count */ + t_u32 count; + /** pointer to output */ + t_u8 *output; + /** key length */ + t_u16 key_len; + /** pointer to key */ + t_u8 *key; + /** key iv length */ + t_u16 key_iv_len; + /** pointer to key iv */ + t_u8 *key_iv; + /** key prefix length */ + t_u16 key_prefix_len; + /** pointer to key prefix */ + t_u8 *key_prefix; + /** pointer to data blk length array */ + t_u32 *key_data_blk_len; + /** pointer to key data blk pointer array */ + t_u8 **key_data_blk; + /** callback */ + t_u8 call_back; +} mlan_ds_sup_cfg, *pmlan_ds_sup_cfg; +#endif + +/*-----------------------------------------------------------------*/ +/** Rate Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for rate type */ +enum _mlan_rate_type { MLAN_RATE_INDEX, MLAN_RATE_VALUE, MLAN_RATE_BITMAP }; + +/** Enumeration for rate format */ +enum _mlan_rate_format { + MLAN_RATE_FORMAT_LG = 0, + MLAN_RATE_FORMAT_HT, + MLAN_RATE_FORMAT_VHT, + MLAN_RATE_FORMAT_HE, + MLAN_RATE_FORMAT_AUTO = 0xFF, +}; + +/** Max bitmap rates size */ +#define MAX_BITMAP_RATES_SIZE 26 + +/** Type definition of mlan_rate_cfg_t for MLAN_OID_RATE_CFG */ +typedef struct _mlan_rate_cfg_t { + /** Fixed rate: 0, auto rate: 1 */ + t_u32 is_rate_auto; + /** Rate type. 0: index; 1: value; 2: bitmap */ + t_u32 rate_type; + /** Rate/MCS index or rate value if fixed rate */ + t_u32 rate; + /** Rate Bitmap */ + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + /** NSS */ + t_u32 nss; + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 rate_format; + /** Rate Setting */ + t_u16 rate_setting; +} mlan_rate_cfg_t; + +/** HT channel bandwidth */ +typedef enum _mlan_ht_bw { + MLAN_HT_BW20, + MLAN_HT_BW40, + /** VHT channel bandwidth */ + MLAN_VHT_BW80, + MLAN_VHT_BW160, +} mlan_ht_bw; + +/** HT guard interval */ +typedef enum _mlan_ht_gi { + MLAN_HT_LGI, + MLAN_HT_SGI, +} mlan_ht_gi; + +typedef enum _mlan_vht_stbc { + MLAN_VHT_STBC, + MLAN_VHT_NO_STBC, +} mlan_vht_stbc; + +typedef enum _mlan_vht_ldpc { + MLAN_VHT_LDPC, + MLAN_VHT_NO_LDPC, +} mlan_vht_ldpc; + +/** Band and BSS mode */ +typedef struct _mlan_band_data_rate { + /** Band configuration */ + t_u8 config_bands; + /** BSS mode (Infra or IBSS) */ + t_u8 bss_mode; +} mlan_band_data_rate; + +/** Type definition of mlan_data_rate for MLAN_OID_GET_DATA_RATE */ +typedef struct _mlan_data_rate { + /** Tx data rate */ + t_u32 tx_data_rate; + /** Rx data rate */ + t_u32 rx_data_rate; + + /** Tx channel bandwidth */ + t_u32 tx_ht_bw; + /** Tx guard interval */ + t_u32 tx_ht_gi; + /** Rx channel bandwidth */ + t_u32 rx_ht_bw; + /** Rx guard interval */ + t_u32 rx_ht_gi; + /** MCS index */ + t_u32 tx_mcs_index; + t_u32 rx_mcs_index; + /** NSS */ + t_u32 tx_nss; + t_u32 rx_nss; + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 tx_rate_format; + t_u32 rx_rate_format; +} mlan_data_rate; + +/** Type definition of mlan_ds_rate for MLAN_IOCTL_RATE */ +typedef struct _mlan_ds_rate { + /** Sub-command */ + t_u32 sub_command; + /** Rate configuration parameter */ + union { + /** Rate configuration for MLAN_OID_RATE_CFG */ + mlan_rate_cfg_t rate_cfg; + /** Data rate for MLAN_OID_GET_DATA_RATE */ + mlan_data_rate data_rate; + /** Supported rates for MLAN_OID_SUPPORTED_RATES */ + t_u8 rates[MLAN_SUPPORTED_RATES]; + /** Band/BSS mode for getting supported rates */ + mlan_band_data_rate rate_band_cfg; + } param; +} mlan_ds_rate, *pmlan_ds_rate; + +/*-----------------------------------------------------------------*/ +/** Power Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** Type definition of mlan_power_cfg_t for MLAN_OID_POWER_CFG */ +typedef struct _mlan_power_cfg_t { + /** Is power auto */ + t_u32 is_power_auto; + /** Power level in dBm */ + t_s32 power_level; +} mlan_power_cfg_t; + +/** max power table size */ +#define MAX_POWER_TABLE_SIZE 128 +#define TX_PWR_CFG_AUTO_CTRL_OFF 0xFF +#define MAX_POWER_GROUP 64 +/** Type definition of mlan_power group info */ +typedef struct mlan_power_group { + /** rate format (LG: 0, HT: 1, VHT: 2, no auto ctrl: 0xFF) */ + t_u32 rate_format; + /** bandwidth (LG: 20 MHz, HT: 20/40 MHz, VHT: 80/160/80+80 MHz) */ + t_u8 bandwidth; + /** NSS */ + t_u32 nss; + /** LG: first rate index, HT/VHT: first MCS */ + t_u8 first_rate_ind; + /** LG: last rate index, HT/VHT: last MCS */ + t_u8 last_rate_ind; + /** minmum tx power (dBm) */ + t_s8 power_min; + /** maximum tx power (dBm) */ + t_s8 power_max; + /** tx power step (dB) */ + t_s8 power_step; +} mlan_power_group; + +/** Type definition of mlan_power_cfg_ext for MLAN_OID_POWER_CFG_EXT */ +typedef struct _mlan_power_cfg_ext { + /** number of power_groups */ + t_u32 num_pwr_grp; + /** array of power groups */ + mlan_power_group power_group[MAX_POWER_GROUP]; +} mlan_power_cfg_ext; + +/** Type definition of mlan_ds_power_cfg for MLAN_IOCTL_POWER_CFG */ +typedef struct _mlan_ds_power_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Power configuration parameter */ + union { + /** Power configuration for MLAN_OID_POWER_CFG */ + mlan_power_cfg_t power_cfg; + /** Extended power configuration for MLAN_OID_POWER_CFG_EXT */ + mlan_power_cfg_ext power_ext; + /** Low power mode for MLAN_OID_POWER_LOW_POWER_MODE */ + t_u16 lpm; + } param; +} mlan_ds_power_cfg, *pmlan_ds_power_cfg; + +/** Type definition of mlan_ds_band_steer_cfg for MLAN_IOCTL_POWER_CFG */ +typedef struct _mlan_ds_band_steer_cfg { + /** Set/Get */ + t_u8 action; + /** enable/disable band steering*/ + t_u8 state; + /** Probe Response will be blocked to 2G channel for first + * block_2g_prb_req probe requests*/ + t_u8 block_2g_prb_req; + /** When band steering is enabled, limit the btm request sent to STA at + * */ + t_u8 max_btm_req_allowed; +} mlan_ds_band_steer_cfg, *pmlan_ds_band_steer_cfg; + +/** Type definition of mlan_ds_beacon_stuck_param_cfg for MLAN_IOCTL_POWER_CFG + */ +typedef struct _mlan_ds_beacon_stuck_param_cfg { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u8 action; + /** No of beacon interval after which firmware will check if beacon Tx + * is going fine */ + t_u8 beacon_stuck_detect_count; + /** Upon performing MAC reset, no of beacon interval after which + * firmware will check if recovery was successful */ + t_u8 recovery_confirm_count; +} mlan_ds_beacon_stuck_param_cfg, *pmlan_ds_beacon_stuck_param_cfg; + +/*-----------------------------------------------------------------*/ +/** Power Management Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Host sleep config conditions : Cancel */ +#define HOST_SLEEP_CFG_CANCEL 0xffffffff + +/** Host sleep config condition: broadcast data */ +#define HOST_SLEEP_COND_BROADCAST_DATA MBIT(0) +/** Host sleep config condition: unicast data */ +#define HOST_SLEEP_COND_UNICAST_DATA MBIT(1) +/** Host sleep config condition: mac event */ +#define HOST_SLEEP_COND_MAC_EVENT MBIT(2) +/** Host sleep config condition: multicast data */ +#define HOST_SLEEP_COND_MULTICAST_DATA MBIT(3) +/** Host sleep config condition: IPV6 packet */ +#define HOST_SLEEP_COND_IPV6_PACKET MBIT(31) + +/** Host sleep config conditions: Default */ +#define HOST_SLEEP_DEF_COND 0 + +/** Host sleep config GPIO : Default */ +#define HOST_SLEEP_DEF_GPIO 0xff +/** Host sleep config gap : Default */ +#define HOST_SLEEP_DEF_GAP 200 +/** Host sleep config min wake holdoff */ +#define HOST_SLEEP_DEF_WAKE_HOLDOFF 0; +/** Host sleep config inactivity timeout */ +#define HOST_SLEEP_DEF_INACTIVITY_TIMEOUT 10; + +/** Type definition of mlan_ds_hs_cfg for MLAN_OID_PM_CFG_HS_CFG */ +typedef struct _mlan_ds_hs_cfg { + /** MTRUE to invoke the HostCmd, MFALSE otherwise */ + t_u32 is_invoke_hostcmd; + /** Host sleep config condition */ + /** Bit0: broadcast data + * Bit1: unicast data + * Bit2: mac event + * Bit3: multicast data + */ + t_u32 conditions; + /** GPIO pin or 0xff for interface */ + t_u32 gpio; + /** Gap in milliseconds or or 0xff for special + * setting when GPIO is used to wakeup host + */ + t_u32 gap; + /** Host sleep wake interval */ + t_u32 hs_wake_interval; + /** Parameter type for indication gpio*/ + t_u8 param_type_ind; + /** GPIO pin for indication wakeup source */ + t_u32 ind_gpio; + /** Level on ind_gpio pin for indication normal wakeup source */ + t_u32 level; + /** Parameter type for extend hscfg*/ + t_u8 param_type_ext; + /** Events that will be forced ignore*/ + t_u32 event_force_ignore; + /** Events that will use extend gap to inform host*/ + t_u32 event_use_ext_gap; + /** Ext gap*/ + t_u8 ext_gap; + /** GPIO wave level for extend hscfg*/ + t_u8 gpio_wave; +} mlan_ds_hs_cfg, *pmlan_ds_hs_cfg; + +#define MAX_MGMT_FRAME_FILTER 2 +typedef struct _mlan_mgmt_frame_wakeup { + /** action - bitmap + ** On matching rx'd pkt and filter during NON_HOSTSLEEP mode: + ** Action[1]=0 Discard + ** Action[1]=1 Allow + ** Note that default action on non-match is "Allow". + ** + ** On matching rx'd pkt and filter during HOSTSLEEP mode: + ** Action[1:0]=00 Discard and Not Wake host + ** Action[1:0]=01 Discard and Wake host + ** Action[1:0]=10 Invalid + ** Note that default action on non-match is "Discard and Not Wake + *host". + **/ + t_u32 action; + /** Frame type(p2p, tdls...) + ** type=0: invalid + ** type=1: p2p + ** type=others: reserved + **/ + t_u32 type; + /** Frame mask according to each type + ** When type=1 for p2p, frame-mask have following define: + ** Bit Frame + ** 0 GO Negotiation Request + ** 1 GO Negotiation Response + ** 2 GO Negotiation Confirmation + ** 3 P2P Invitation Request + ** 4 P2P Invitation Response + ** 5 Device Discoverability Request + ** 6 Device Discoverability Response + ** 7 Provision Discovery Request + ** 8 Provision Discovery Response + ** 9 Notice of Absence + ** 10 P2P Presence Request + ** 11 P2P Presence Response + ** 12 GO Discoverability Request + ** 13-31 Reserved + ** + ** When type=others, frame-mask is reserved. + **/ + t_u32 frame_mask; +} mlan_mgmt_frame_wakeup, *pmlan_mgmt_frame_wakeup; + +/** Enable deep sleep mode */ +#define DEEP_SLEEP_ON 1 +/** Disable deep sleep mode */ +#define DEEP_SLEEP_OFF 0 + +/** Default idle time in milliseconds for auto deep sleep */ +#define DEEP_SLEEP_IDLE_TIME 100 + +typedef struct _mlan_ds_auto_ds { + /** auto ds mode, 0 - disable, 1 - enable */ + t_u16 auto_ds; + /** auto ds idle time in milliseconds */ + t_u16 idletime; +} mlan_ds_auto_ds; + +/** Type definition of mlan_ds_inactivity_to + * for MLAN_OID_PM_CFG_INACTIVITY_TO + */ +typedef struct _mlan_ds_inactivity_to { + /** Timeout unit in microsecond, 0 means 1000us (1ms) */ + t_u32 timeout_unit; + /** Inactivity timeout for unicast data */ + t_u32 unicast_timeout; + /** Inactivity timeout for multicast data */ + t_u32 mcast_timeout; + /** Timeout for additional Rx traffic after Null PM1 packet exchange */ + t_u32 ps_entry_timeout; +} mlan_ds_inactivity_to, *pmlan_ds_inactivity_to; + +/** Minimum sleep period in milliseconds */ +#define MIN_SLEEP_PERIOD 10 +/** Maximum sleep period in milliseconds */ +#define MAX_SLEEP_PERIOD 60 +/** Special setting for UPSD certification tests */ +#define SLEEP_PERIOD_RESERVED_FF 0xFF + +/** PS null interval disable */ +#define PS_NULL_DISABLE (-1) + +/** Local listen interval disable */ +#define MRVDRV_LISTEN_INTERVAL_DISABLE (-1) +/** Minimum listen interval */ +#define MRVDRV_MIN_LISTEN_INTERVAL 0 + +/** Minimum multiple DTIM */ +#define MRVDRV_MIN_MULTIPLE_DTIM 0 +/** Maximum multiple DTIM */ +#define MRVDRV_MAX_MULTIPLE_DTIM 5 +/** Ignore multiple DTIM */ +#define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe +/** Match listen interval to closest DTIM */ +#define MRVDRV_MATCH_CLOSEST_DTIM 0xfffd + +/** Minimum 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; + /** Beacon miss timeout in milliseconds */ + t_u32 bcn_miss_timeout; + /** Delay to PS in milliseconds */ + t_s32 delay_to_ps; + /** PS mode */ + t_u32 ps_mode; +} mlan_ds_ps_cfg, *pmlan_ds_ps_cfg; + +/** Type definition of mlan_ds_sleep_params for MLAN_OID_PM_CFG_SLEEP_PARAMS */ +typedef struct _mlan_ds_sleep_params { + /** Error */ + t_u32 error; + /** Offset in microseconds */ + t_u32 offset; + /** Stable time in microseconds */ + t_u32 stable_time; + /** Calibration control */ + t_u32 cal_control; + /** External sleep clock */ + t_u32 ext_sleep_clk; + /** Reserved */ + t_u32 reserved; +} mlan_ds_sleep_params, *pmlan_ds_sleep_params; + +/** sleep_param */ +typedef struct _ps_sleep_param { + /** control bitmap */ + t_u32 ctrl_bitmap; + /** minimum sleep period (micro second) */ + t_u32 min_sleep; + /** maximum sleep period (micro second) */ + t_u32 max_sleep; +} ps_sleep_param; + +/** inactivity sleep_param */ +typedef struct _inact_sleep_param { + /** inactivity timeout (micro second) */ + t_u32 inactivity_to; + /** miniumu awake period (micro second) */ + t_u32 min_awake; + /** maximum awake period (micro second) */ + t_u32 max_awake; +} inact_sleep_param; + +/** flag for ps mode */ +#define PS_FLAG_PS_MODE 1 +/** flag for sleep param */ +#define PS_FLAG_SLEEP_PARAM 2 +/** flag for inactivity sleep param */ +#define PS_FLAG_INACT_SLEEP_PARAM 4 + +/** Enable Robust Coex mode */ +#define ROBUSTCOEX_GPIOCFG_ENABLE 1 +/** Disable Robust Coex mode */ +#define ROBUSTCOEX_GPIOCFG_DISABLE 0 + +/** Disable power mode */ +#define PS_MODE_DISABLE 0 +/** Enable periodic dtim ps */ +#define PS_MODE_PERIODIC_DTIM 1 +/** Enable inactivity ps */ +#define PS_MODE_INACTIVITY 2 +/** FW wake up method interface */ +#define FW_WAKEUP_METHOD_INTERFACE 1 +/** FW wake up method gpio */ +#define FW_WAKEUP_METHOD_GPIO 2 +/** mlan_ds_ps_mgmt */ +typedef struct _mlan_ds_ps_mgmt { + /** flags for valid field */ + t_u16 flags; + /** power mode */ + t_u16 ps_mode; + /** sleep param */ + ps_sleep_param sleep_param; + /** inactivity sleep param */ + inact_sleep_param inact_param; +} mlan_ds_ps_mgmt; + +/** mlan_ds_ps_info */ +typedef struct _mlan_ds_ps_info { + /** suspend allowed flag */ + t_u32 is_suspend_allowed; +} mlan_ds_ps_info; + +/** Type definition of mlan_ds_wakeup_reason for MLAN_OID_PM_HS_WAKEUP_REASON */ +typedef struct _mlan_ds_hs_wakeup_reason { + t_u16 hs_wakeup_reason; +} mlan_ds_hs_wakeup_reason; + +/** Type definition of mlan_ds_ps_cfg for MLAN_OID_PM_CFG_PS_CFG */ +typedef struct _mlan_ds_bcn_timeout { + /** Beacon miss timeout period window */ + t_u16 bcn_miss_tmo_window; + /** Beacon miss timeout period */ + t_u16 bcn_miss_tmo_period; + /** Beacon reacquire timeout period window */ + t_u16 bcn_rq_tmo_window; + /** Beacon reacquire timeout period */ + t_u16 bcn_rq_tmo_period; +} mlan_ds_bcn_timeout, *pmlan_ds_bcn_timeout; + +/** Type definition of mlan_ds_pm_cfg for MLAN_IOCTL_PM_CFG */ +typedef struct _mlan_ds_pm_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Power management parameter */ + union { + /** Power saving mode for MLAN_OID_PM_CFG_IEEE_PS */ + t_u32 ps_mode; + /** Host Sleep configuration for MLAN_OID_PM_CFG_HS_CFG */ + mlan_ds_hs_cfg hs_cfg; + /** Deep sleep mode for MLAN_OID_PM_CFG_DEEP_SLEEP */ + mlan_ds_auto_ds auto_deep_sleep; + /** Inactivity timeout for MLAN_OID_PM_CFG_INACTIVITY_TO */ + mlan_ds_inactivity_to inactivity_to; + /** Sleep period for MLAN_OID_PM_CFG_SLEEP_PD */ + t_u32 sleep_period; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_CFG */ + mlan_ds_ps_cfg ps_cfg; + /** PS configuration parameters for MLAN_OID_PM_CFG_SLEEP_PARAMS + */ + mlan_ds_sleep_params sleep_params; + /** PS configuration parameters for MLAN_OID_PM_CFG_PS_MODE */ + mlan_ds_ps_mgmt ps_mgmt; + /** power info for MLAN_OID_PM_INFO */ + mlan_ds_ps_info ps_info; + /** hs wakeup reason for MLAN_OID_PM_HS_WAKEUP_REASON */ + mlan_ds_hs_wakeup_reason wakeup_reason; + /** config manamgement frame for hs wakeup */ + mlan_mgmt_frame_wakeup mgmt_filter[MAX_MGMT_FRAME_FILTER]; + /** Beacon timout parameters for MLAN_OID_PM_CFG_BCN_TIMEOUT */ + mlan_ds_bcn_timeout bcn_timeout; + } param; +} mlan_ds_pm_cfg, *pmlan_ds_pm_cfg; + +#ifdef RX_PACKET_COALESCE +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + + t_u32 pkt_threshold; /** Packet threshold */ + t_u16 delay; /** Timeout value in milliseconds */ +} wlan_ioctl_rx_pkt_coalesce_config_t; +#endif + +/*-----------------------------------------------------------------*/ +/** WMM Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** WMM TSpec size */ +#define MLAN_WMM_TSPEC_SIZE 63 +/** WMM Add TS extra IE bytes */ +#define MLAN_WMM_ADDTS_EXTRA_IE_BYTES 256 +/** WMM statistics for packets hist bins */ +#define MLAN_WMM_STATS_PKTS_HIST_BINS 7 +/** Maximum number of AC QOS queues available */ +#define MLAN_WMM_MAX_AC_QUEUES 4 + +/** + * @brief IOCTL structure to send an ADDTS request and retrieve the response. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an ADDTS management frame with an appropriate TSPEC IE as well + * as any additional IEs appended in the ADDTS Action frame. + * + * @sa woal_wmm_addts_req_ioctl + */ +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + + t_u32 timeout_ms; /**< Timeout value in milliseconds */ + t_u8 ieee_status_code; /**< IEEE status code */ + + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE /**< TSPEC to send in the ADDTS */ + + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; /**< Extra IE buf*/ +} wlan_ioctl_wmm_addts_req_t; + +/** + * @brief IOCTL structure to send a DELTS request. + * + * IOCTL structure from the application layer relayed to firmware to + * instigate an DELTS management frame with an appropriate TSPEC IE. + * + * @sa woal_wmm_delts_req_ioctl + */ +typedef struct { + mlan_cmd_result_e cmd_result; /**< Firmware execution result */ + t_u8 ieee_reason_code; /**< IEEE reason code sent, unused for WMM */ + t_u32 ie_data_len; /**< Length of ie block in ie_data */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; /**< TSPEC to send in the DELTS */ +} wlan_ioctl_wmm_delts_req_t; + +/** + * @brief IOCTL structure to configure a specific AC Queue's parameters + * + * IOCTL structure from the application layer relayed to firmware to + * get, set, or default the WMM AC queue parameters. + * + * - msdu_lifetime_expiry is ignored if set to 0 on a set command + * + * @sa woal_wmm_queue_config_ioctl + */ +typedef struct { + mlan_wmm_queue_config_action_e action; /**< Set, Get, or Default */ + mlan_wmm_ac_e access_category; /**< WMM_AC_BK(0) to WMM_AC_VO(3) */ + t_u16 msdu_lifetime_expiry; /**< lifetime expiry in TUs */ + t_u8 supported_rates[10]; /**< Not supported yet */ +} wlan_ioctl_wmm_queue_config_t; + +/** + * @brief IOCTL structure to start, stop, and get statistics for a WMM AC + * + * IOCTL structure from the application layer relayed to firmware to + * start or stop statistical collection for a given AC. Also used to + * retrieve and clear the collected stats on a given AC. + * + * @sa woal_wmm_queue_stats_ioctl + */ +typedef struct { + /** Action of Queue Config : Start, Stop, or Get */ + mlan_wmm_queue_stats_action_e action; + /** User Priority */ + t_u8 user_priority; + /** Number of successful packets transmitted */ + t_u16 pkt_count; + /** Packets lost; not included in pkt_count */ + t_u16 pkt_loss; + /** Average Queue delay in microseconds */ + t_u32 avg_queue_delay; + /** Average Transmission delay in microseconds */ + t_u32 avg_tx_delay; + /** Calculated used time in units of 32 microseconds */ + t_u16 used_time; + /** Calculated policed time in units of 32 microseconds */ + t_u16 policed_time; + /** Queue Delay Histogram; number of packets per queue delay range + * + * [0] - 0ms <= delay < 5ms + * [1] - 5ms <= delay < 10ms + * [2] - 10ms <= delay < 20ms + * [3] - 20ms <= delay < 30ms + * [4] - 30ms <= delay < 40ms + * [5] - 40ms <= delay < 50ms + * [6] - 50ms <= delay < msduLifetime (TUs) + */ + t_u16 delay_histogram[MLAN_WMM_STATS_PKTS_HIST_BINS]; +} wlan_ioctl_wmm_queue_stats_t, + /** Type definition of mlan_ds_wmm_queue_stats + * for MLAN_OID_WMM_CFG_QUEUE_STATS + */ + mlan_ds_wmm_queue_stats, *pmlan_ds_wmm_queue_stats; + +/** + * @brief IOCTL sub structure for a specific WMM AC Status + */ +typedef struct { + /** WMM Acm */ + t_u8 wmm_acm; + /** Flow required flag */ + t_u8 flow_required; + /** Flow created flag */ + t_u8 flow_created; + /** Disabled flag */ + t_u8 disabled; +} wlan_ioctl_wmm_queue_status_ac_t; + +/** + * @brief IOCTL structure to retrieve the WMM AC Queue status + * + * IOCTL structure from the application layer to retrieve: + * - ACM bit setting for the AC + * - Firmware status (flow required, flow created, flow disabled) + * + * @sa woal_wmm_queue_status_ioctl + */ +typedef struct { + /** WMM AC queue status */ + wlan_ioctl_wmm_queue_status_ac_t ac_status[MLAN_WMM_MAX_AC_QUEUES]; +} wlan_ioctl_wmm_queue_status_t, + /** Type definition of mlan_ds_wmm_queue_status + * for MLAN_OID_WMM_CFG_QUEUE_STATUS + */ + mlan_ds_wmm_queue_status, *pmlan_ds_wmm_queue_status; + +/** Type definition of mlan_ds_wmm_addts for MLAN_OID_WMM_CFG_ADDTS */ +typedef struct _mlan_ds_wmm_addts { + /** Result of ADDTS request */ + mlan_cmd_result_e result; + /** Timeout value in milliseconds */ + t_u32 timeout; + /** IEEE status code */ + t_u32 status_code; + /** Dialog token */ + t_u8 dialog_tok; + /** TSPEC data length */ + t_u32 ie_data_len; + /** TSPEC to send in the ADDTS + buffering for any extra IEs */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE + MLAN_WMM_ADDTS_EXTRA_IE_BYTES]; +} mlan_ds_wmm_addts, *pmlan_ds_wmm_addts; + +/** Type definition of mlan_ds_wmm_delts for MLAN_OID_WMM_CFG_DELTS */ +typedef struct _mlan_ds_wmm_delts { + /** Result of DELTS request */ + mlan_cmd_result_e result; + /** IEEE status code */ + t_u32 status_code; + /** TSPEC data length */ + t_u8 ie_data_len; + /** TSPEC to send in the DELTS */ + t_u8 ie_data[MLAN_WMM_TSPEC_SIZE]; +} mlan_ds_wmm_delts, *pmlan_ds_wmm_delts; + +/** Type definition of mlan_ds_wmm_queue_config + * for MLAN_OID_WMM_CFG_QUEUE_CONFIG + */ +typedef struct _mlan_ds_wmm_queue_config { + /** Action of Queue Config : Set, Get, or Default */ + mlan_wmm_queue_config_action_e action; + /** WMM Access Category: WMM_AC_BK(0) to WMM_AC_VO(3) */ + mlan_wmm_ac_e access_category; + /** Lifetime expiry in TUs */ + t_u16 msdu_lifetime_expiry; + /** Reserve for future use */ + t_u8 reserved[10]; +} mlan_ds_wmm_queue_config, *pmlan_ds_wmm_queue_config; + +/** Type definition of mlan_ds_wmm_cfg for MLAN_IOCTL_WMM_CFG */ +typedef struct _mlan_ds_wmm_cfg { + /** Sub-command */ + t_u32 sub_command; + /** WMM configuration parameter */ + union { + /** WMM enable for MLAN_OID_WMM_CFG_ENABLE */ + t_u32 wmm_enable; + /** QoS configuration for MLAN_OID_WMM_CFG_QOS */ + t_u8 qos_cfg; + /** WMM add TS for MLAN_OID_WMM_CFG_ADDTS */ + mlan_ds_wmm_addts addts; + /** WMM delete TS for MLAN_OID_WMM_CFG_DELTS */ + mlan_ds_wmm_delts delts; + /** WMM queue configuration for MLAN_OID_WMM_CFG_QUEUE_CONFIG */ + mlan_ds_wmm_queue_config q_cfg; + /** AC Parameters Record WMM_AC_BE, WMM_AC_BK, WMM_AC_VI, + * WMM_AC_VO */ + wmm_ac_parameters_t ac_params[MAX_AC_QUEUES]; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATS */ + mlan_ds_wmm_queue_stats q_stats; + /** WMM queue status for MLAN_OID_WMM_CFG_QUEUE_STATUS */ + mlan_ds_wmm_queue_status q_status; + /** WMM TS status for MLAN_OID_WMM_CFG_TS_STATUS */ + mlan_ds_wmm_ts_status ts_status; + } param; +} mlan_ds_wmm_cfg, *pmlan_ds_wmm_cfg; + +/*-----------------------------------------------------------------*/ +/** WPS Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Enumeration for WPS session */ +enum _mlan_wps_status { + MLAN_WPS_CFG_SESSION_START = 1, + MLAN_WPS_CFG_SESSION_END = 0 +}; + +/** Type definition of mlan_ds_wps_cfg for MLAN_IOCTL_WPS_CFG */ +typedef struct _mlan_ds_wps_cfg { + /** Sub-command */ + t_u32 sub_command; + /** WPS configuration parameter */ + union { + /** WPS session for MLAN_OID_WPS_CFG_SESSION */ + t_u32 wps_session; + } param; +} mlan_ds_wps_cfg, *pmlan_ds_wps_cfg; + +/*-----------------------------------------------------------------*/ +/** 802.11n Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum MCS */ +#define NUM_MCS_FIELD 16 + +/** 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 +/** TX Sounding*/ +#define TX_SOUNDING_CFG 0x05 + +/* Maximum number of peer MAC and status/SNR tuples */ +#define MAX_PEER_MAC_TUPLES 10 + +/** Any new subcommand structure should be declare here */ + +/** bf global cfg args */ +typedef struct _mlan_bf_global_cfg_args { + /** Global enable/disable bf */ + t_u8 bf_enbl; + /** Global enable/disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; + /** SNR Threshold */ + t_u8 snr_threshold; + /** Sounding interval in milliseconds */ + t_u16 sounding_interval; + /** BF mode */ + t_u8 bf_mode; + /** Reserved */ + t_u8 reserved; +} mlan_bf_global_cfg_args; + +/** trigger sounding args */ +typedef struct _mlan_trigger_sound_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Status */ + t_u8 status; +} mlan_trigger_sound_args; + +/** bf periodicity args */ +typedef struct _mlan_bf_periodicity_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Current Tx BF Interval in milliseconds */ + t_u16 interval; + /** Status */ + t_u8 status; +} mlan_bf_periodicity_args; + +/** tx bf peer args */ +typedef struct _mlan_tx_bf_peer_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** Reserved */ + t_u16 reserved; + /** Enable/Disable Beamforming */ + t_u8 bf_enbl; + /** Enable/Disable sounding */ + t_u8 sounding_enbl; + /** FB Type */ + t_u8 fb_type; +} mlan_tx_bf_peer_args; + +/** SNR threshold args */ +typedef struct _mlan_snr_thr_args { + /** Peer MAC address */ + t_u8 peer_mac[MLAN_MAC_ADDR_LENGTH]; + /** SNR for peer */ + t_u8 snr; +} mlan_snr_thr_args; + +/** Type definition of mlan_ds_11n_tx_bf_cfg for MLAN_OID_11N_CFG_TX_BF_CFG */ +typedef struct _mlan_ds_11n_tx_bf_cfg { + /** BF Action */ + t_u16 bf_action; + /** Action */ + t_u16 action; + /** Number of peers */ + t_u32 no_of_peers; + union { + mlan_bf_global_cfg_args bf_global_cfg; + mlan_trigger_sound_args bf_sound[MAX_PEER_MAC_TUPLES]; + mlan_bf_periodicity_args bf_periodicity[MAX_PEER_MAC_TUPLES]; + mlan_tx_bf_peer_args tx_bf_peer[MAX_PEER_MAC_TUPLES]; + mlan_snr_thr_args bf_snr[MAX_PEER_MAC_TUPLES]; + } body; +} mlan_ds_11n_tx_bf_cfg, *pmlan_ds_11n_tx_bf_cfg; + +/** Type definition of mlan_ds_11n_amsdu_aggr_ctrl for + * MLAN_OID_11N_AMSDU_AGGR_CTRL*/ +typedef struct _mlan_ds_11n_amsdu_aggr_ctrl { + /** Enable/Disable */ + t_u16 enable; + /** Current AMSDU size valid */ + t_u16 curr_buf_size; +} mlan_ds_11n_amsdu_aggr_ctrl, *pmlan_ds_11n_amsdu_aggr_ctrl; + +/** Type definition of mlan_ds_11n_aggr_prio_tbl + * for MLAN_OID_11N_CFG_AGGR_PRIO_TBL + */ +typedef struct _mlan_ds_11n_aggr_prio_tbl { + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** amsdu priority table */ + t_u8 amsdu[MAX_NUM_TID]; +} mlan_ds_11n_aggr_prio_tbl, *pmlan_ds_11n_aggr_prio_tbl; + +/** DelBA All TIDs */ +#define DELBA_ALL_TIDS 0xff +/** DelBA Tx */ +#define DELBA_TX MBIT(0) +/** DelBA Rx */ +#define DELBA_RX MBIT(1) + +/** Type definition of mlan_ds_11n_delba for MLAN_OID_11N_CFG_DELBA */ +typedef struct _mlan_ds_11n_delba { + /** TID */ + t_u8 tid; + /** Peer MAC address */ + t_u8 peer_mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** Direction (Tx: bit 0, Rx: bit 1) */ + t_u8 direction; +} mlan_ds_11n_delba, *pmlan_ds_11n_delba; + +/** Type definition of mlan_ds_delba for MLAN_OID_11N_CFG_REJECT_ADDBA_REQ */ +typedef struct _mlan_ds_reject_addba_req { + /** Bit0 : host sleep activated + * Bit1 : auto reconnect enabled + * Others : reserved + */ + t_u32 conditions; +} mlan_ds_reject_addba_req, *pmlan_ds_reject_addba_req; + +/** Type definition of mlan_ds_ibss_ampdu_param */ +typedef struct _mlan_ds_ibss_ampdu_param { + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** rx amdpdu setting */ + t_u8 addba_reject[MAX_NUM_TID]; +} mlan_ds_ibss_ampdu_param, *pmlan_ds_ibss_ampdu_param; + +/** Type definition of mlan_ds_11n_cfg for MLAN_IOCTL_11N_CFG */ +typedef struct _mlan_ds_11n_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11n configuration parameter */ + union { + /** Tx param for 11n for MLAN_OID_11N_CFG_TX */ + mlan_ds_11n_tx_cfg tx_cfg; + /** Aggr priority table for MLAN_OID_11N_CFG_AGGR_PRIO_TBL */ + mlan_ds_11n_aggr_prio_tbl aggr_prio_tbl; + /** Add BA param for MLAN_OID_11N_CFG_ADDBA_PARAM */ + mlan_ds_11n_addba_param addba_param; + /** Add BA Reject paramters for MLAN_OID_11N_CFG_ADDBA_REJECT */ + t_u8 addba_reject[MAX_NUM_TID]; + /** Tx buf size for MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE */ + t_u32 tx_buf_size; + /** HT cap info configuration for MLAN_OID_11N_HTCAP_CFG */ + mlan_ds_11n_htcap_cfg htcap_cfg; + /** Tx param for 11n for MLAN_OID_11N_AMSDU_AGGR_CTRL */ + mlan_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[NUM_MCS_FIELD]; + /** Transmit Beamforming Capabilities field */ + t_u32 tx_bf_cap; + /** Transmit Beamforming configuration */ + mlan_ds_11n_tx_bf_cfg tx_bf; + /** HT stream configuration */ + t_u32 stream_cfg; + /** DelBA for MLAN_OID_11N_CFG_DELBA */ + mlan_ds_11n_delba del_ba; + /** Reject Addba Req for MLAN_OID_11N_CFG_REJECT_ADDBA_REQ */ + mlan_ds_reject_addba_req reject_addba_req; + /** Control coex RX window size configuration */ + t_u32 coex_rx_winsize; + /** aggrprirotity table for MLAN_OID_11N_CFG_IBSS_AMPDU_PARAM */ + mlan_ds_ibss_ampdu_param ibss_ampdu; + /** Minimum BA Threshold for MLAN_OID_11N_CFG_MIN_BA_THRESHOLD + */ + t_u8 min_ba_threshold; + } param; +} mlan_ds_11n_cfg, *pmlan_ds_11n_cfg; + +#define NUM_MCS_SUPP 20 +#define VHT_MCS_SET_LEN 8 + +/** Type definition of mlan_ds_11ac_vhtcap_cfg for MLAN_OID_11AC_VHTCAP_CFG */ +typedef struct _mlan_ds_11ac_vhtcap_cfg { + /** HT Capability information */ + t_u32 vhtcap; + /** Band selection */ + t_u32 misc_cfg; + /** Hardware HT cap information required */ + t_u32 hw_cap_req; +} mlan_ds_11ac_vhtcap_cfg, *pmlan_ds_11ac_vhtcap_cfg; + +/** Type definition of mlan_ds_11ac_tx_cfg for MLAN_OID_11AC_CFG_TX */ +typedef struct _mlan_ds_11ac_tx_cfg { + /** Band selection */ + t_u8 band_cfg; + /** misc configuration */ + t_u8 misc_cfg; + /** HTTxCap */ + t_u16 vhttxcap; + /** HTTxInfo */ + t_u16 vhttxinfo; +} mlan_ds_11ac_tx_cfg, *pmlan_ds_11ac_tx_cfg; + +/** Tx */ +#define MLAN_RADIO_TX MBIT(0) +/** Rx */ +#define MLAN_RADIO_RX MBIT(1) +/** Tx & Rx */ +#define MLAN_RADIO_TXRX (MLAN_RADIO_TX | MLAN_RADIO_RX) + +/** Type definition of mlan_ds_11ac_tx_cfg for MLAN_OID_11AC_CFG */ +typedef struct _mlan_ds_11ac_vht_cfg { + /** Band selection (1: 2.4G, 2: 5 G, 3: both 2.4G and 5G) */ + t_u32 band; + /** TxRx (1: Tx, 2: Rx, 3: both Tx and Rx) */ + t_u32 txrx; + /** BW CFG (0: 11N CFG, 1: vhtcap) */ + t_u32 bwcfg; + /** VHT capabilities. */ + t_u32 vht_cap_info; + /** VHT Tx mcs */ + t_u32 vht_tx_mcs; + /** VHT Rx mcs */ + t_u32 vht_rx_mcs; + /** VHT rx max rate */ + t_u16 vht_rx_max_rate; + /** VHT max tx rate */ + t_u16 vht_tx_max_rate; + /** Skip usr 11ac mcs cfg */ + t_bool skip_usr_11ac_mcs_cfg; +} mlan_ds_11ac_vht_cfg, *pmlan_ds_11ac_vht_cfg; + +/** Type definition of mlan_ds_11ac_tx_cfg for MLAN_OID_11AC_CFG */ +typedef struct _mlan_ds_11ac_opermode_cfg { + /** channel width: 1-20MHz, 2-40MHz, 3-80MHz, 4-160MHz or 80+80MHz */ + t_u8 bw; + /** Rx NSS */ + t_u8 nss; +} mlan_ds_11ac_opermode_cfg, *pmlan_ds_11ac_opermode_cfg; + +/** Type definition of mlan_ds_11ac_cfg for MLAN_IOCTL_11AC_CFG */ +typedef struct _mlan_ds_11ac_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11n configuration parameter */ + union { + /** VHT configuration for MLAN_OID_11AC_VHT_CFG */ + mlan_ds_11ac_vht_cfg vht_cfg; + /** Supported MCS Set field */ + t_u8 supported_mcs_set[NUM_MCS_SUPP]; + /** Oper mode configuration for MLAN_OID_11AC_OPERMODE_CFG */ + mlan_ds_11ac_opermode_cfg opermode_cfg; + } param; +} mlan_ds_11ac_cfg, *pmlan_ds_11ac_cfg; + +/** Type definition of mlan_ds_11ax_he_capa for MLAN_OID_11AX_HE_CFG */ +typedef MLAN_PACK_START struct _mlan_ds_11ax_he_capa { + /** tlv id of he capability */ + t_u16 id; + /** length of the payload */ + t_u16 len; + /** extension id */ + t_u8 ext_id; + /** he mac capability info */ + t_u8 he_mac_cap[6]; + /** he phy capability info */ + t_u8 he_phy_cap[11]; + /** he txrx mcs support for 80MHz */ + t_u8 he_txrx_mcs_support[4]; + /** val for txrx mcs 160Mhz or 80+80, and PPE thresholds */ + t_u8 val[28]; +} MLAN_PACK_END mlan_ds_11ax_he_capa, *pmlan_ds_11ax_he_capa; + +/** Type definition of mlan_ds_11ax_he_cfg for MLAN_OID_11AX_HE_CFG */ +typedef struct _mlan_ds_11ax_he_cfg { + /** band, BIT0:2.4G, BIT1:5G*/ + t_u8 band; + /** mlan_ds_11ax_he_capa */ + mlan_ds_11ax_he_capa he_cap; +} mlan_ds_11ax_he_cfg, *pmlan_ds_11ax_he_cfg; +/** Type definition of mlan_ds_11as_cfg for MLAN_IOCTL_11AX_CFG */ +typedef struct _mlan_ds_11ax_cfg { + /** Sub-command */ + t_u32 sub_command; + /** 802.11n configuration parameter */ + union { + /** HE configuration for MLAN_OID_11AX_HE_CFG */ + mlan_ds_11ax_he_cfg he_cfg; + } param; +} mlan_ds_11ax_cfg, *pmlan_ds_11ax_cfg; + +#define MLAN_11AXCMD_CFG_ID_SR_OBSS_PD_OFFSET 1 +#define MLAN_11AXCMD_CFG_ID_SR_ENABLE 2 +#define MLAN_11AXCMD_CFG_ID_BEAM_CHANGE 3 +#define MLAN_11AXCMD_CFG_ID_HTC_ENABLE 4 +#define MLAN_11AXCMD_CFG_ID_TXOP_RTS 5 +#define MLAN_11AXCMD_CFG_ID_TX_OMI 6 +#define MLAN_11AXCMD_CFG_ID_OBSSNBRU_TOLTIME 7 + +#define MLAN_11AXCMD_SR_SUBID 0x102 +#define MLAN_11AXCMD_BEAM_SUBID 0x103 +#define MLAN_11AXCMD_HTC_SUBID 0x104 +#define MLAN_11AXCMD_TXOMI_SUBID 0x105 +#define MLAN_11AXCMD_OBSS_TOLTIME_SUBID 0x106 +#define MLAN_11AXCMD_TXOPRTS_SUBID 0x108 + +#define MLAN_11AX_TWT_SETUP_SUBID 0x114 +#define MLAN_11AX_TWT_TEARDOWN_SUBID 0x115 + +#define MRVL_DOT11AX_ENABLE_SR_TLV_ID (PROPRIETARY_TLV_BASE_ID + 322) +#define MRVL_DOT11AX_OBSS_PD_OFFSET_TLV_ID (PROPRIETARY_TLV_BASE_ID + 323) + +/** Type definition of mlan_11axcmdcfg_obss_pd_offset for MLAN_OID_11AX_CMD_CFG + */ +typedef struct MLAN_PACK_START _mlan_11axcmdcfg_obss_pd_offset { + /** */ + t_u8 offset[2]; +} MLAN_PACK_END mlan_11axcmdcfg_obss_pd_offset; + +/** Type definition of mlan_11axcmdcfg_sr_control for MLAN_OID_11AX_CMD_CFG */ +typedef struct MLAN_PACK_START _mlan_11axcmdcfg_sr_control { + /** 1 enable, 0 disable */ + t_u8 control; +} MLAN_PACK_END mlan_11axcmdcfg_sr_control; + +/** Type definition of mlan_ds_11ax_sr_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct MLAN_PACK_START _mlan_ds_11ax_sr_cmd { + /** type*/ + t_u16 type; + /** length of TLV */ + t_u16 len; + /** value */ + union { + mlan_11axcmdcfg_obss_pd_offset obss_pd_offset; + mlan_11axcmdcfg_sr_control sr_control; + } param; +} MLAN_PACK_END mlan_ds_11ax_sr_cmd, *pmlan_ds_11ax_sr_cmd; + +/** Type definition of mlan_ds_11ax_beam_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_beam_cmd { + /** command value: 1 is disable, 0 is enable*/ + t_u8 value; +} mlan_ds_11ax_beam_cmd, *pmlan_ds_11ax_beam_cmd; + +/** Type definition of mlan_ds_11ax_htc_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_htc_cmd { + /** command value: 1 is enable, 0 is disable*/ + t_u8 value; +} mlan_ds_11ax_htc_cmd, *pmlan_ds_11ax_htc_cmd; + +/** Type definition of mlan_ds_11ax_htc_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_txop_cmd { + /** Two byte rts threshold value of which only 10 bits, bit 0 to bit 9 + * are valid */ + t_u16 rts_thres; +} mlan_ds_11ax_txop_cmd, *pmlan_ds_11ax_txop_cmd; + +/** Type definition of mlan_ds_11ax_htc_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_txomi_cmd { + /* 11ax spec 9.2.4.6a.2 OM Control 12 bits. Bit 0 to bit 11 */ + t_u16 omi; +} mlan_ds_11ax_txomi_cmd, *pmlan_ds_11ax_txomi_cmd; + +/** Type definition of mlan_ds_11ax_toltime_cmd for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_toltime_cmd { + /* OBSS Narrow Bandwidth RU Tolerance Time */ + t_u32 tol_time; +} mlan_ds_11ax_toltime_cmd, *pmlan_ds_11ax_toltime_cmd; + +/** Type definition of mlan_ds_11ax_cmd_cfg for MLAN_OID_11AX_CMD_CFG */ +typedef struct _mlan_ds_11ax_cmd_cfg { + /** Sub-command */ + t_u32 sub_command; + /** Sub-id */ + t_u32 sub_id; + /** 802.11n configuration parameter */ + union { + /** SR configuration for MLAN_11AXCMD_SR_SUBID */ + mlan_ds_11ax_sr_cmd sr_cfg; + /** Beam configuration for MLAN_11AXCMD_BEAM_SUBID */ + mlan_ds_11ax_beam_cmd beam_cfg; + /** HTC configuration for MLAN_11AXCMD_HTC_SUBID */ + mlan_ds_11ax_htc_cmd htc_cfg; + /** txop RTS configuration for MLAN_11AXCMD_TXOPRTS_SUBID */ + mlan_ds_11ax_txop_cmd txop_cfg; + /** tx omi configuration for MLAN_11AXCMD_TXOMI_SUBID */ + mlan_ds_11ax_txomi_cmd txomi_cfg; + /** OBSS tolerance time configuration for + * MLAN_11AXCMD_TOLTIME_SUBID */ + mlan_ds_11ax_toltime_cmd toltime_cfg; + } param; +} mlan_ds_11ax_cmd_cfg, *pmlan_ds_11ax_cmd_cfg; + +/** Type definition of mlan_ds_twt_setup for MLAN_OID_11AX_TWT_CFG */ +typedef struct MLAN_PACK_START _mlan_ds_twt_setup { + /** Implicit, 0: TWT session is explicit, 1: Session is implicit */ + t_u8 implicit; + /** Announced, 0: Unannounced, 1: Announced TWT */ + t_u8 announced; + /** Trigger Enabled, 0: Non-Trigger enabled, 1: Trigger enabled TWT */ + t_u8 trigger_enabled; + /** TWT Information Disabled, 0: TWT info enabled, 1: TWT info disabled + */ + t_u8 twt_info_disabled; + /** Negotiation Type, 0: Future Individual TWT SP start time, 1: Next + * Wake TBTT time */ + t_u8 negotiation_type; + /** TWT Wakeup Duration, time after which the TWT requesting STA can + * transition to doze state */ + t_u8 twt_wakeup_duration; + /** Flow Identifier. Range: [0-7]*/ + t_u8 flow_identifier; + /** Hard Constraint, 0: FW can tweak the TWT setup parameters if it is + *rejected by AP. + ** 1: Firmware should not tweak any parameters. */ + t_u8 hard_constraint; + /** TWT Exponent, Range: [0-63] */ + t_u8 twt_exponent; + /** TWT Mantissa Range: [0-sizeof(UINT16)] */ + t_u16 twt_mantissa; + /** TWT Request Type, 0: REQUEST_TWT, 1: SUGGEST_TWT*/ + t_u8 twt_request; +} MLAN_PACK_END mlan_ds_twt_setup, *pmlan_ds_twt_setup; + +/** Type definition of mlan_ds_twt_teardown for MLAN_OID_11AX_TWT_CFG */ +typedef struct MLAN_PACK_START _mlan_ds_twt_teardown { + /** TWT Flow Identifier. Range: [0-7] */ + t_u8 flow_identifier; + /** Negotiation Type. 0: Future Individual TWT SP start time, 1: Next + * Wake TBTT time */ + t_u8 negotiation_type; + /** Tear down all TWT. 1: To teardown all TWT, 0 otherwise */ + t_u8 teardown_all_twt; +} MLAN_PACK_END mlan_ds_twt_teardown, *pmlan_ds_twt_teardown; + +/** Type definition of mlan_ds_twtcfg for MLAN_OID_11AX_TWT_CFG */ +typedef struct MLAN_PACK_START _mlan_ds_twtcfg { + /** Sub-command */ + t_u32 sub_command; + /** Sub-id */ + t_u32 sub_id; + /** TWT Setup/Teardown configuration parameter */ + union { + /** TWT Setup config for Sub ID: MLAN_11AX_TWT_SETUP_SUBID */ + mlan_ds_twt_setup twt_setup; + /** TWT Teardown config for Sub ID: MLAN_11AX_TWT_TEARDOWN_SUBID + */ + mlan_ds_twt_teardown twt_teardown; + } param; +} MLAN_PACK_END mlan_ds_twtcfg, *pmlan_ds_twtcfg; + +/** Country code length */ +#define COUNTRY_CODE_LEN 3 + +/*-----------------------------------------------------------------*/ +/** 802.11d Configuration Group */ +/*-----------------------------------------------------------------*/ +/** Maximum subbands for 11d */ +#define MRVDRV_MAX_SUBBAND_802_11D 83 + +/** 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; + +/** 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; +#endif /* STA_SUPPORT */ + /** Domain info for MLAN_OID_11D_DOMAIN_INFO_EXT */ + mlan_ds_11d_domain_info domain_info; +#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 CSU target device type */ +enum _mlan_csu_target_type { + MLAN_CSU_TARGET_CAU = 1, + MLAN_CSU_TARGET_PSU, +}; + +/** Enumeration for register type */ +enum _mlan_reg_type { + MLAN_REG_MAC = 1, + MLAN_REG_BBP, + MLAN_REG_RF, + MLAN_REG_CAU = 5, + MLAN_REG_PSU = 6, + MLAN_REG_BCA = 7, +#if defined(PCIE9098) || defined(SD9098) || defined(USB9098) || \ + defined(PCIE9097) || defined(USB9097) || defined(SD9097) + MLAN_REG_MAC2 = 0x81, + MLAN_REG_BBP2 = 0x82, + MLAN_REG_RF2 = 0x83, + MLAN_REG_BCA2 = 0x87 +#endif +}; + +/** 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 */ +/*-----------------------------------------------------------------*/ +/** Type definition of mlan_ds_11h_dfs_testing for MLAN_OID_11H_DFS_TESTING */ +typedef struct _mlan_ds_11h_dfs_testing { + /** User-configured CAC period in milliseconds, 0 to use default */ + t_u32 usr_cac_period_msec; + /** User-configured NOP period in seconds, 0 to use default */ + t_u16 usr_nop_period_sec; + /** User-configured skip channel change, 0 to disable */ + t_u8 usr_no_chan_change; + /** User-configured fixed channel to change to, 0 to use random channel + */ + t_u8 usr_fixed_new_chan; + /** User-configured cac restart */ + t_u8 usr_cac_restart; +} mlan_ds_11h_dfs_testing, *pmlan_ds_11h_dfs_testing; + +/** Type definition of mlan_ds_11h_dfs_testing for MLAN_OID_11H_CHAN_NOP_INFO */ +typedef struct _mlan_ds_11h_chan_nop_info { + /** current channel */ + t_u8 curr_chan; + /** channel_width */ + t_u8 chan_width; + /** flag for chan under nop */ + t_bool chan_under_nop; + /** chan_ban_info for new channel */ + chan_band_info new_chan; +} mlan_ds_11h_chan_nop_info, *pmlan_ds_11h_chan_nop_info; + +typedef struct _mlan_ds_11h_chan_rep_req { + t_u16 startFreq; + Band_Config_t bandcfg; + t_u8 chanNum; + t_u32 millisec_dwell_time; /**< Channel dwell time in milliseconds */ + t_u8 host_based; +} mlan_ds_11h_chan_rep_req; + +typedef struct _mlan_ds_11h_dfs_w53_cfg { + /** dfs w53 cfg */ + t_u8 dfs53cfg; +} mlan_ds_11h_dfs_w53_cfg; + +/** 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; + /** User-configuation for MLAN_OID_11H_DFS_TESTING */ + mlan_ds_11h_dfs_testing dfs_testing; + /** channel NOP information for MLAN_OID_11H_CHAN_NOP_INFO */ + mlan_ds_11h_chan_nop_info ch_nop_info; + /** channel report req for MLAN_OID_11H_CHAN_REPORT_REQUEST */ + mlan_ds_11h_chan_rep_req chan_rpt_req; + /** channel switch count for MLAN_OID_11H_CHAN_SWITCH_COUNT*/ + t_s8 cs_count; + mlan_ds_11h_dfs_w53_cfg dfs_w53_cfg; + } param; +} mlan_ds_11h_cfg, *pmlan_ds_11h_cfg; + +/*-----------------------------------------------------------------*/ +/** Miscellaneous Configuration Group */ +/*-----------------------------------------------------------------*/ + +/** CMD buffer size */ +#define MLAN_SIZE_OF_CMD_BUFFER (3 * 1024) + +/** 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, +}; + +/** 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; + +#ifdef SDIO +/** 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[MRVDRV_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; + +/** Type definition of mlan_ds_misc_rx_abort_cfg + * for MLAN_OID_MISC_RX_ABORT_CFG + */ +typedef struct _mlan_ds_misc_rx_abort_cfg { + /** enable/disable rx abort */ + t_u8 enable; + /** Rx weak RSSI pkt threshold */ + t_s8 rssi_threshold; +} mlan_ds_misc_rx_abort_cfg; + +/** Type definition of mlan_ds_misc_rx_abort_cfg_ext + * for MLAN_OID_MISC_RX_ABORT_CFG_EXT + */ +typedef struct _mlan_ds_misc_rx_abort_cfg_ext { + /** enable/disable dynamic rx abort */ + t_u8 enable; + /** rssi margin */ + t_s8 rssi_margin; + /** specify ceil rssi threshold */ + t_s8 ceil_rssi_threshold; +} mlan_ds_misc_rx_abort_cfg_ext; + +/** Type definition of mlan_ds_misc_rx_abort_cfg_ext + * for MLAN_OID_MISC_TX_AMDPU_PROT_MODE + */ +typedef struct _mlan_ds_misc_tx_ampdu_prot_mode { + /** set prot mode */ + t_u16 mode; +} mlan_ds_misc_tx_ampdu_prot_mode; + +/** Type definition of mlan_ds_misc_dot11mc_unassoc_ftm_cfg + * for MLAN_OID_MISC_DOT11MC_UNASSOC_FTM_CFG + */ +typedef struct _mlan_ds_misc_dot11mc_unassoc_ftm_cfg { + /** set the state */ + t_u16 state; +} mlan_ds_misc_dot11mc_unassoc_ftm_cfg; + +#define RATEADAPT_ALGO_LEGACY 0 +#define RATEADAPT_ALGO_SR 1 + +/** Type definition of mlan_ds_misc_rate_adapt_cfg + * for MLAN_OID_MISC_RATE_ADAPT_CFG + */ +typedef struct _mlan_ds_misc_rate_adapt_cfg { + /** SR Rateadapt */ + t_u8 sr_rateadapt; + /** set low threshold */ + t_u8 ra_low_thresh; + /** set high threshold */ + t_u8 ra_high_thresh; + /** set interval */ + t_u16 ra_interval; +} mlan_ds_misc_rate_adapt_cfg; + +/** Type definition of mlan_ds_misc_cck_desense_cfg + * for MLAN_OID_MISC_CCK_DESENSE_CFG + */ +typedef struct _mlan_ds_misc_cck_desense_cfg { + /** cck desense mode: 0:disable 1:normal 2:dynamic */ + t_u16 mode; + /** specify rssi margin */ + t_s8 margin; + /** specify ceil rssi threshold */ + t_s8 ceil_thresh; + /** cck desense "on" interval count */ + t_u8 num_on_intervals; + /** cck desense "off" interval count */ + t_u8 num_off_intervals; +} mlan_ds_misc_cck_desense_cfg; + +/** IP address length */ +#define IPADDR_LEN (16) +/** Max number of ip */ +#define MAX_IPADDR (4) +/** IP address type - NONE*/ +#define IPADDR_TYPE_NONE (0) +/** IP address type - IPv4*/ +#define IPADDR_TYPE_IPV4 (1) +/** IP operation remove */ +#define MLAN_IPADDR_OP_IP_REMOVE (0) +/** IP operation ARP response */ +#define MLAN_IPADDR_OP_AUTO_ARP_RESP MBIT(1) + +/** Type definition of mlan_ds_misc_ipaddr_cfg for MLAN_OID_MISC_IP_ADDR */ +typedef struct _mlan_ds_misc_ipaddr_cfg { + /** Operation code */ + t_u32 op_code; + /** IP address type */ + t_u32 ip_addr_type; + /** Number of IP */ + t_u32 ip_addr_num; + /** IP address */ + t_u8 ip_addr[MAX_IPADDR][IPADDR_LEN]; +} mlan_ds_misc_ipaddr_cfg; + +/* MEF configuration disable */ +#define MEF_CFG_DISABLE 0 +/* MEF configuration Rx filter enable */ +#define MEF_CFG_RX_FILTER_ENABLE 1 +/* MEF configuration auto ARP response */ +#define MEF_CFG_AUTO_ARP_RESP 2 +/* MEF configuration host command */ +#define MEF_CFG_HOSTCMD 0xFFFF + +/** Type definition of mlan_ds_misc_mef_cfg for MLAN_OID_MISC_MEF_CFG */ +typedef struct _mlan_ds_misc_mef_cfg { + /** Sub-ID for operation */ + t_u32 sub_id; + /** Parameter according to sub-ID */ + union { + /** MEF command buffer for MEF_CFG_HOSTCMD */ + mlan_ds_misc_cmd cmd_buf; + } param; +} mlan_ds_misc_mef_cfg; + +/** Type definition of mlan_ds_misc_cfp_code for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_misc_cfp_code { + /** CFP table code for 2.4GHz */ + t_u32 cfp_code_bg; + /** CFP table code for 5GHz */ + t_u32 cfp_code_a; +} mlan_ds_misc_cfp_code; + +/** Type definition of mlan_ds_misc_arb_cfg + * for MLAN_OID_MISC_ARB_CFG + */ +typedef struct _mlan_ds_misc_arb_cfg { + /** arb mode 0-4 */ + t_u32 arb_mode; +} mlan_ds_misc_arb_cfg; + +/** Type definition of mlan_ds_misc_tp_state + * for MLAN_OID_MISC_TP_STATE + */ +typedef struct _mlan_ds_misc_tp_state { + /** TP account mode 0-disable 1-enable */ + t_u32 on; + /** Packet drop point */ + t_u32 drop_point; +} mlan_ds_misc_tp_state; + +/** Type definition of mlan_ds_misc_country_code + * for MLAN_OID_MISC_COUNTRY_CODE + */ +typedef struct _mlan_ds_misc_country_code { + /** Country Code */ + t_u8 country_code[COUNTRY_CODE_LEN]; +} mlan_ds_misc_country_code; + +/** action for set */ +#define SUBSCRIBE_EVT_ACT_BITWISE_SET 0x0002 +/** action for clear */ +#define SUBSCRIBE_EVT_ACT_BITWISE_CLR 0x0003 +/** BITMAP for subscribe event rssi low */ +#define SUBSCRIBE_EVT_RSSI_LOW MBIT(0) +/** BITMAP for subscribe event snr low */ +#define SUBSCRIBE_EVT_SNR_LOW MBIT(1) +/** BITMAP for subscribe event max fail */ +#define SUBSCRIBE_EVT_MAX_FAIL MBIT(2) +/** BITMAP for subscribe event beacon missed */ +#define SUBSCRIBE_EVT_BEACON_MISSED MBIT(3) +/** BITMAP for subscribe event rssi high */ +#define SUBSCRIBE_EVT_RSSI_HIGH MBIT(4) +/** BITMAP for subscribe event snr high */ +#define SUBSCRIBE_EVT_SNR_HIGH MBIT(5) +/** BITMAP for subscribe event data rssi low */ +#define SUBSCRIBE_EVT_DATA_RSSI_LOW MBIT(6) +/** BITMAP for subscribe event data snr low */ +#define SUBSCRIBE_EVT_DATA_SNR_LOW MBIT(7) +/** BITMAP for subscribe event data rssi high */ +#define SUBSCRIBE_EVT_DATA_RSSI_HIGH MBIT(8) +/** BITMAP for subscribe event data snr high */ +#define SUBSCRIBE_EVT_DATA_SNR_HIGH MBIT(9) +/** BITMAP for subscribe event link quality */ +#define SUBSCRIBE_EVT_LINK_QUALITY MBIT(10) +/** BITMAP for subscribe event pre_beacon_lost */ +#define SUBSCRIBE_EVT_PRE_BEACON_LOST MBIT(11) +/** default PRE_BEACON_MISS_COUNT */ +#define DEFAULT_PRE_BEACON_MISS 30 + +/** Type definition of mlan_ds_subscribe_evt for MLAN_OID_MISC_CFP_CODE */ +typedef struct _mlan_ds_subscribe_evt { + /** evt action */ + t_u16 evt_action; + /** bitmap for subscribe event */ + t_u16 evt_bitmap; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 low_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 low_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 low_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 low_snr_freq; + /** Failure count threshold */ + t_u8 failure_count; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 failure_count_freq; + /** num of missed beacons */ + t_u8 beacon_miss; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 beacon_miss_freq; + /** Absolute value of RSSI threshold value (dBm) */ + t_u8 high_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 high_rssi_freq; + /** SNR threshold value (dB) */ + t_u8 high_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 high_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_low_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_low_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_low_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_low_snr_freq; + /** Absolute value of data RSSI threshold value (dBm) */ + t_u8 data_high_rssi; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_high_rssi_freq; + /** Absolute value of data SNR threshold value (dBm) */ + t_u8 data_high_snr; + /** 0--report once, 1--report everytime happen, + * N -- report only happend > N consecutive times + */ + t_u8 data_high_snr_freq; + /* Link SNR threshold (dB)*/ + t_u16 link_snr; + /* Link SNR frequency */ + t_u16 link_snr_freq; + /* Second minimum rate value as per the rate table below */ + t_u16 link_rate; + /* Second minimum rate frequency */ + t_u16 link_rate_freq; + /* Tx latency value (us) */ + t_u16 link_tx_latency; + /* Tx latency frequency */ + t_u16 link_tx_lantency_freq; + /* Number of pre missed beacons */ + t_u8 pre_beacon_miss; +} mlan_ds_subscribe_evt; + +/** Max OTP user data length */ +#define MAX_OTP_USER_DATA_LEN 252 + +/** Type definition of mlan_ds_misc_otp_user_data + * for MLAN_OID_MISC_OTP_USER_DATA + */ +typedef struct _mlan_ds_misc_otp_user_data { + /** Reserved */ + t_u16 reserved; + /** OTP user data length */ + t_u16 user_data_length; + /** User data buffer */ + t_u8 user_data[MAX_OTP_USER_DATA_LEN]; +} mlan_ds_misc_otp_user_data; + +typedef struct _aggr_ctrl { + /** Enable */ + t_u16 enable; + /** Aggregation alignment */ + t_u16 aggr_align; + /** Aggregation max size */ + t_u16 aggr_max_size; + /** Aggregation max packet number */ + t_u16 aggr_max_num; + /** Aggrgation timeout, in microseconds */ + t_u16 aggr_tmo; +} aggr_ctrl; + +/** Type definition of mlan_ds_misc_aggr_ctrl + * for MLAN_OID_MISC_AGGR_CTRL + */ +typedef struct _mlan_ds_misc_aggr_ctrl { + /** Tx aggregation control */ + aggr_ctrl tx; +} mlan_ds_misc_aggr_ctrl; + +#ifdef USB +typedef struct _usb_aggr_ctrl { + /** Enable */ + t_u16 enable; + /** Aggregation mode */ + t_u16 aggr_mode; + /** Aggregation alignment */ + t_u16 aggr_align; + /** Aggregation max packet/size */ + t_u16 aggr_max; + /** Aggrgation timeout, in microseconds */ + t_u16 aggr_tmo; +} usb_aggr_ctrl; + +/** Type definition of mlan_ds_misc_usb_aggr_ctrl + * for MLAN_OID_MISC_USB_AGGR_CTRL + */ +typedef struct _mlan_ds_misc_usb_aggr_ctrl { + /** Tx aggregation control */ + usb_aggr_ctrl tx_aggr_ctrl; + /** Rx deaggregation control */ + usb_aggr_ctrl rx_deaggr_ctrl; +} mlan_ds_misc_usb_aggr_ctrl; +#endif + +#ifdef WIFI_DIRECT_SUPPORT +/** flag for NOA */ +#define WIFI_DIRECT_NOA 1 +/** flag for OPP_PS */ +#define WIFI_DIRECT_OPP_PS 2 +/** Type definition of mlan_ds_wifi_direct_config + * for MLAN_OID_MISC_WIFI_DIRECT_CONFIG + */ +typedef struct _mlan_ds_wifi_direct_config { + /** flags for NOA/OPP_PS */ + t_u8 flags; + /** NoA enable/disable */ + t_u8 noa_enable; + /** index */ + t_u16 index; + /** NoA count */ + t_u8 noa_count; + /** NoA duration */ + t_u32 noa_duration; + /** NoA interval */ + t_u32 noa_interval; + /** opp ps enable/disable */ + t_u8 opp_ps_enable; + /** CT window value */ + t_u8 ct_window; +} mlan_ds_wifi_direct_config; +#endif + +#if defined(STA_SUPPORT) +typedef struct _mlan_ds_misc_pmfcfg { + /** Management Frame Protection Capable */ + t_u8 mfpc; + /** Management Frame Protection Required */ + t_u8 mfpr; +} mlan_ds_misc_pmfcfg; +#endif + +#define MAX_SSID_NUM 16 +#define MAX_AP_LIST 8 + +#ifdef RX_PACKET_COALESCE +typedef struct _mlan_ds_misc_rx_packet_coalesce { + /** packet threshold */ + t_u32 packet_threshold; + /** timeout value */ + t_u16 delay; +} mlan_ds_misc_rx_packet_coalesce; +#endif + +typedef struct _mlan_ds_misc_dfs_repeater { + /** Set or Get */ + t_u16 action; + /** 1 on or 0 off */ + t_u16 mode; +} mlan_ds_misc_dfs_repeater; + +#define WOWLAN_MAX_PATTERN_LEN 20 +#define WOWLAN_MAX_OFFSET_LEN 50 +#define MAX_NUM_FILTERS 10 +#define MEF_MODE_HOST_SLEEP (1 << 0) +#define MEF_MODE_NON_HOST_SLEEP (1 << 1) +#define MEF_ACTION_WAKE (1 << 0) +#define MEF_ACTION_ALLOW (1 << 1) +#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3 +#define MEF_AUTO_ARP 0x10 +#define MEF_AUTO_PING 0x20 +#define MEF_NS_RESP 0x40 +#define MEF_MAGIC_PKT 0x80 +#define CRITERIA_BROADCAST BIT(0) +#define CRITERIA_UNICAST BIT(1) +#define CRITERIA_MULTICAST BIT(3) + +#define MAX_NUM_ENTRIES 8 +#define MAX_NUM_BYTE_SEQ 6 +#define MAX_NUM_MASK_SEQ 6 + +#define OPERAND_DNUM 1 +#define OPERAND_BYTE_SEQ 2 + +#define MAX_OPERAND 0x40 +#define TYPE_BYTE_EQ (MAX_OPERAND + 1) +#define TYPE_DNUM_EQ (MAX_OPERAND + 2) +#define TYPE_BIT_EQ (MAX_OPERAND + 3) + +#define RPN_TYPE_AND (MAX_OPERAND + 4) +#define RPN_TYPE_OR (MAX_OPERAND + 5) + +#define ICMP_OF_IP_PROTOCOL 0x01 +#define TCP_OF_IP_PROTOCOL 0x06 +#define UDP_OF_IP_PROTOCOL 0x11 + +#define IPV4_PKT_OFFSET 20 +#define IP_PROTOCOL_OFFSET 31 +#define PORT_PROTOCOL_OFFSET 44 + +#define FILLING_TYPE MBIT(0) +#define FILLING_PATTERN MBIT(1) +#define FILLING_OFFSET MBIT(2) +#define FILLING_NUM_BYTES MBIT(3) +#define FILLING_REPEAT MBIT(4) +#define FILLING_BYTE_SEQ MBIT(5) +#define FILLING_MASK_SEQ MBIT(6) + +/** Type definition of filter_item + * Support three match methods: + * <1>Byte comparison type=0x41 + * <2>Decimal comparison type=0x42 + * <3>Bit comparison type=0x43 + */ +typedef struct _mef_filter_t { + /** flag*/ + t_u32 fill_flag; + /** BYTE 0X41; Decimal 0X42; Bit 0x43*/ + t_u16 type; + /** value*/ + t_u32 pattern; + /** offset*/ + t_u16 offset; + /** number of bytes*/ + t_u16 num_bytes; + /** repeat*/ + t_u16 repeat; + /** byte number*/ + t_u8 num_byte_seq; + /** array*/ + t_u8 byte_seq[MAX_NUM_BYTE_SEQ]; + /** mask numbers*/ + t_u8 num_mask_seq; + /** array*/ + t_u8 mask_seq[MAX_NUM_MASK_SEQ]; +} mef_filter_t; + +typedef struct _mef_entry_t { + /** mode: bit0--hostsleep mode; bit1--non hostsleep mode */ + t_u8 mode; + /** action: 0--discard and not wake host; + 1--discard and wake host; + 3--allow and wake host;*/ + t_u8 action; + /** filter number */ + t_u8 filter_num; + /** filter array*/ + mef_filter_t filter_item[MAX_NUM_FILTERS]; + /** rpn array*/ + t_u8 rpn[MAX_NUM_FILTERS]; +} mef_entry_t; + +/** Type definition of mlan_ds_nvflt_mef_entry + *for MLAN_OID_MISC_MEF_FLT_CFG + */ +typedef struct _mlan_ds_misc_mef_flt_cfg { + /** Type of action*/ + int mef_act_type; + /** NV Filter Criteria*/ + t_u32 criteria; + /** NV MEF entry*/ + mef_entry_t mef_entry; +} mlan_ds_misc_mef_flt_cfg; + +/** Enumeration for action type*/ +enum _mlan_act_mef_act_type { + MEF_ACT_ADD = 1, + MEF_ACT_ENABLE, + MEF_ACT_DISABLE, + MEF_ACT_CANCEL, + MEF_ACT_AUTOARP, + MEF_ACT_WOWLAN, + MEF_ACT_IPV6_NS, +}; + +typedef struct _mlan_ds_sensor_temp { + t_u32 temperature; +} mlan_ds_sensor_temp; + +#define MLAN_KCK_LEN 16 +#define MLAN_KEK_LEN 16 +#define MLAN_REPLAY_CTR_LEN 8 +/** mlan_ds_misc_gtk_rekey_data */ +typedef struct _mlan_ds_misc_gtk_rekey_data { + /** key encryption key */ + t_u8 kek[MLAN_KEK_LEN]; + /** key confirmation key */ + t_u8 kck[MLAN_KCK_LEN]; + /** replay counter */ + t_u8 replay_ctr[MLAN_REPLAY_CTR_LEN]; +} mlan_ds_misc_gtk_rekey_data; +typedef struct _mlan_ds_bw_chan_oper { + /* bandwidth 20:20M 40:40M 80:80M*/ + t_u8 bandwidth; + /* channel number */ + t_u8 channel; + /* Non-global operating class */ + t_u8 oper_class; +} mlan_ds_bw_chan_oper; + +typedef struct _mlan_ds_ind_rst_cfg { + /** Set or Get */ + t_u16 action; + /** oob mode enable/ disable */ + t_u8 ir_mode; + /** gpio pin */ + t_u8 gpio_pin; +} mlan_ds_ind_rst_cfg; + +#define MKEEP_ALIVE_IP_PKT_MAX 256 +typedef struct _mlan_ds_misc_keep_alive { + t_u8 mkeep_alive_id; + t_u8 enable; + /** enable/disable tcp reset*/ + t_u8 reset; + /**True means saved in driver, false means not saved or download*/ + t_u8 cached; + t_u32 send_interval; + t_u16 retry_interval; + t_u16 retry_count; + t_u8 dst_mac[MLAN_MAC_ADDR_LENGTH]; + t_u8 src_mac[MLAN_MAC_ADDR_LENGTH]; + t_u16 pkt_len; + t_u8 packet[MKEEP_ALIVE_IP_PKT_MAX]; + /** Ethernet type */ + t_u16 ether_type; +} mlan_ds_misc_keep_alive, *pmlan_ds_misc_keep_alive; + +/** TX and RX histogram statistic parameters*/ +typedef MLAN_PACK_START struct _mlan_ds_misc_tx_rx_histogram { + /** Enable or disable get tx/rx histogram statistic */ + t_u8 enable; + /** Choose to get TX, RX or both histogram statistic */ + t_u16 action; + /** Size of Tx/Rx info */ + t_u16 size; + /** Store Tx/Rx info */ + t_u8 value[1]; +} MLAN_PACK_END mlan_ds_misc_tx_rx_histogram; + +typedef MLAN_PACK_START struct _mlan_ds_cw_mode_ctrl { + /** Mode of Operation 0: Disable 1: Tx Continuous Packet 2: Tx + * Continuous Wave */ + t_u8 mode; + /*channel*/ + t_u8 channel; + /* channel info*/ + t_u8 chanInfo; + /** Tx Power level in dBm */ + t_u16 txPower; + /** Packet Length */ + t_u16 pktLength; + /** bit rate Info */ + t_u32 rateInfo; +} MLAN_PACK_END mlan_ds_cw_mode_ctrl; + +#define RX_PKT_INFO MBIT(1) +/** Struct for per-packet configuration */ +typedef struct _mlan_per_pkt_cfg { + /** Type ID*/ + t_u16 type; + /** Length of payload*/ + t_u16 len; + /** Tx/Rx per-packet control */ + t_u8 tx_rx_control; + /** Number of ethernet types in ether_type array */ + t_u8 proto_type_num; + /** Array of ether_type for per-packet control */ + t_u16 ether_type[]; +} mlan_per_pkt_cfg; + +/** Type definition of mlan_ds_misc_robustcoex_params for MLAN_IOCTL_MISC_CFG */ +typedef struct _mlan_ds_misc_robustcoex_params { + t_u16 method; + /** enable/disable robustcoex gpio cfg */ + t_u8 enable; + /** Number of GPIO */ + t_u8 gpio_num; + /** Polarity of GPIO */ + t_u8 gpio_polarity; +} mlan_ds_misc_robustcoex_params; + +#if defined(PCIE) +typedef struct _mlan_ds_ssu_params { + t_u32 nskip; + t_u32 nsel; + t_u32 adcdownsample; + t_u32 mask_adc_pkt; + t_u32 out_16bits; + t_u32 spec_pwr_enable; + t_u32 rate_deduction; + t_u32 n_pkt_avg; +} mlan_ds_ssu_params; +#endif + +#define MAX_NUM_MAC 2 +/** Type definition of mlan_ds_misc_mapping_policy */ +typedef struct _mlan_ds_misc_mapping_policy { + /** Enable/disable dynamic mapping */ + t_u16 subcmd; + /** Mapping policy */ + t_u8 mapping_policy; +} mlan_ds_misc_mapping_policy, *pmlan_ds_misc_mapping_policy; + +typedef struct _dmcsChanStatus_t { + /** Channel number */ + t_u8 channel; + /** Number of ap on this channel */ + t_u8 ap_count; + /** Number of sta on this channel */ + t_u8 sta_count; +} dmcsChanStatus_t, *pdmcsChanStatus_t; + +typedef struct _dmcsStatus_t { + /** Radio ID */ + t_u8 radio_id; + /** Running mode + ** 0 - Idle + ** 1 - DBC + ** 2 - DRCS + */ + t_u8 running_mode; + /** Current channel status */ + dmcsChanStatus_t chan_status[2]; +} dmcsStatus_t, *pdmcsStatus_t; + +/** Type definition of mlan_ds_misc_dmcs_status */ +typedef struct _mlan_ds_misc_dmcs_status { + t_u8 mapping_policy; + dmcsStatus_t radio_status[MAX_NUM_MAC]; +} mlan_ds_misc_dmcs_status, *pmlan_ds_misc_dmcs_status; + +/** Type definition of mlan_ds_misc_chan_trpc_cfg for + * MLAN_OID_MISC_GET_CHAN_TRPC_CFG */ +typedef struct _mlan_ds_misc_chan_trpc_cfg { + /** sub_band */ + t_u16 sub_band; + /** length */ + t_u16 length; + /** buf */ + t_u8 trpc_buf[2048]; +} mlan_ds_misc_chan_trpc_cfg; + +#define MFG_CMD_SET_TEST_MODE 1 +#define MFG_CMD_UNSET_TEST_MODE 0 +#define MFG_CMD_TX_ANT 0x1004 +#define MFG_CMD_RX_ANT 0x1005 +#define MFG_CMD_TX_CONT 0x1009 +#define MFG_CMD_RF_CHAN 0x100A +#define MFG_CMD_CLR_RX_ERR 0x1010 +#define MFG_CMD_TX_FRAME 0x1021 +#define MFG_CMD_RFPWR 0x1033 +#define MFG_CMD_RF_BAND_AG 0x1034 +#define MFG_CMD_RF_CHANNELBW 0x1044 +/** MFG CMD generic cfg */ +struct MLAN_PACK_START mfg_cmd_generic_cfg { + /** MFG command code */ + t_u32 mfg_cmd; + /** Action */ + t_u16 action; + /** Device ID */ + t_u16 device_id; + /** MFG Error code */ + t_u32 error; + /** value 1 */ + t_u32 data1; + /** value 2 */ + t_u32 data2; + /** value 3 */ + t_u32 data3; +} MLAN_PACK_END; + +/** MFG CMD Tx Frame 2 */ +struct MLAN_PACK_START mfg_cmd_tx_frame2 { + /** MFG command code */ + t_u32 mfg_cmd; + /** Action */ + t_u16 action; + /** Device ID */ + t_u16 device_id; + /** MFG Error code */ + t_u32 error; + /** enable */ + t_u32 enable; + /** data_rate */ + t_u32 data_rate; + /** frame pattern */ + t_u32 frame_pattern; + /** frame length */ + t_u32 frame_length; + /** BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** Adjust burst sifs */ + t_u16 adjust_burst_sifs; + /** Burst sifs in us*/ + t_u32 burst_sifs_in_us; + /** short preamble */ + t_u32 short_preamble; + /** active sub channel */ + t_u32 act_sub_ch; + /** short GI */ + t_u32 short_gi; + /** Adv coding */ + t_u32 adv_coding; + /** Tx beamforming */ + t_u32 tx_bf; + /** HT Greenfield Mode*/ + t_u32 gf_mode; + /** STBC */ + t_u32 stbc; + /** power id */ + t_u32 rsvd[2]; +} MLAN_PACK_END; + +/* MFG CMD Tx Continuous */ +struct MLAN_PACK_START mfg_cmd_tx_cont { + /** MFG command code */ + t_u32 mfg_cmd; + /** Action */ + t_u16 action; + /** Device ID */ + t_u16 device_id; + /** MFG Error code */ + t_u32 error; + /** enable Tx*/ + t_u32 enable_tx; + /** Continuous Wave mode */ + t_u32 cw_mode; + /** payload pattern */ + t_u32 payload_pattern; + /** CS Mode */ + t_u32 cs_mode; + /** active sub channel */ + t_u32 act_sub_ch; + /** Tx rate */ + t_u32 tx_rate; + /** power id */ + t_u32 rsvd; +} MLAN_PACK_END; + +typedef struct _mlan_ds_misc_chnrgpwr_cfg { + /** length */ + t_u16 length; + /** chnrgpwr buf */ + t_u8 chnrgpwr_buf[2048]; +} mlan_ds_misc_chnrgpwr_cfg; + +/** dfs chan list for MLAN_OID_MISC_CFP_TABLE */ +typedef struct _mlan_ds_misc_cfp_tbl { + /** band */ + t_u8 band; + /** num chan */ + t_u8 num_chan; + /** cfp table */ + chan_freq_power_t cfp_tbl[]; +} mlan_ds_misc_cfp_tbl; + +/** 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; +#ifdef SDIO + /** SDIO MP-A Ctrl command for MLAN_OID_MISC_SDIO_MPA_CTRL */ + mlan_ds_misc_sdio_mpa_ctrl mpa_ctrl; +#endif + /** Hostcmd for MLAN_OID_MISC_HOST_CMD */ + mlan_ds_misc_cmd hostcmd; + /** System clock for MLAN_OID_MISC_SYS_CLOCK */ + mlan_ds_misc_sys_clock sys_clock; + /** WWS set/get for MLAN_OID_MISC_WWS */ + t_u32 wws_cfg; + /** Get associate response for MLAN_OID_MISC_ASSOC_RSP */ + mlan_ds_misc_assoc_rsp assoc_resp; + /** Function init/shutdown for MLAN_OID_MISC_INIT_SHUTDOWN */ + t_u32 func_init_shutdown; + /** Custom IE for MLAN_OID_MISC_CUSTOM_IE */ + mlan_ds_misc_custom_ie cust_ie; + /** Config dynamic bandwidth*/ + t_u16 dyn_bw; + /** Tx data pause for MLAN_OID_MISC_TX_DATAPAUSE */ + mlan_ds_misc_tx_datapause tx_datapause; + /** IP address configuration */ + mlan_ds_misc_ipaddr_cfg ipaddr_cfg; + /** MAC control for MLAN_OID_MISC_MAC_CONTROL */ + t_u32 mac_ctrl; + /** MEF configuration for MLAN_OID_MISC_MEF_CFG */ + mlan_ds_misc_mef_cfg mef_cfg; + /** CFP code for MLAN_OID_MISC_CFP_CODE */ + mlan_ds_misc_cfp_code cfp_code; + /** Country code for MLAN_OID_MISC_COUNTRY_CODE */ + mlan_ds_misc_country_code country_code; + /** Thermal reading for MLAN_OID_MISC_THERMAL */ + t_u32 thermal; + /** Mgmt subtype mask for MLAN_OID_MISC_RX_MGMT_IND */ + t_u32 mgmt_subtype_mask; + /** subscribe event for MLAN_OID_MISC_SUBSCRIBE_EVENT */ + mlan_ds_subscribe_evt subscribe_event; +#ifdef DEBUG_LEVEL1 + /** Driver debug bit masks */ + t_u32 drvdbg; +#endif + /** Hotspot config param set */ + t_u32 hotspot_cfg; +#ifdef STA_SUPPORT + ExtCap_t ext_cap; +#endif + mlan_ds_misc_otp_user_data otp_user_data; +#ifdef USB + /** USB aggregation parameters for MLAN_OID_MISC_USB_AGGR_CTRL + */ + mlan_ds_misc_usb_aggr_ctrl usb_aggr_params; +#endif + mlan_ds_misc_aggr_ctrl aggr_params; + /** Tx control */ + t_u32 tx_control; +#if defined(STA_SUPPORT) + mlan_ds_misc_pmfcfg pmfcfg; +#endif +#ifdef WIFI_DIRECT_SUPPORT + mlan_ds_wifi_direct_config p2p_config; +#endif + mlan_ds_coalesce_cfg coalesce_cfg; + t_u8 low_pwr_mode; + /** MEF-FLT-CONFIG for MLAN_OID_MISC_NV_FLT_CFG */ + mlan_ds_misc_mef_flt_cfg mef_flt_cfg; + mlan_ds_misc_dfs_repeater dfs_repeater; +#ifdef RX_PACKET_COALESCE + mlan_ds_misc_rx_packet_coalesce rx_coalesce; +#endif + /** FW reload flag */ + t_u8 fw_reload; + mlan_ds_sensor_temp sensor_temp; + /** GTK rekey data */ + mlan_ds_misc_gtk_rekey_data gtk_rekey; + mlan_ds_bw_chan_oper bw_chan_oper; + mlan_ds_ind_rst_cfg ind_rst_cfg; + t_u64 misc_tsf; + mlan_ds_custom_reg_domain custom_reg_domain; + mlan_ds_misc_keep_alive keep_alive; + mlan_ds_misc_tx_rx_histogram tx_rx_histogram; + mlan_ds_cw_mode_ctrl cwmode; + /** Tx/Rx per-packet control */ + t_u8 txrx_pkt_ctrl; + mlan_ds_misc_robustcoex_params robustcoexparams; +#if defined(PCIE) + mlan_ds_ssu_params ssu_params; +#endif + /** boot sleep enable or disable */ + t_u16 boot_sleep; + /** Mapping Policy */ + mlan_ds_misc_mapping_policy dmcs_policy; + mlan_ds_misc_dmcs_status dmcs_status; + mlan_ds_misc_rx_abort_cfg rx_abort_cfg; + mlan_ds_misc_rx_abort_cfg_ext rx_abort_cfg_ext; + mlan_ds_misc_tx_ampdu_prot_mode tx_ampdu_prot_mode; + mlan_ds_misc_rate_adapt_cfg rate_adapt_cfg; + mlan_ds_misc_cck_desense_cfg cck_desense_cfg; + mlan_ds_misc_chan_trpc_cfg trpc_cfg; + mlan_ds_misc_chnrgpwr_cfg rgchnpwr_cfg; + + mlan_ds_band_steer_cfg band_steer_cfg; + mlan_ds_beacon_stuck_param_cfg beacon_stuck_cfg; + struct mfg_cmd_generic_cfg mfg_generic_cfg; + struct mfg_cmd_tx_cont mfg_tx_cont; + struct mfg_cmd_tx_frame2 mfg_tx_frame2; + mlan_ds_misc_arb_cfg arb_cfg; + mlan_ds_misc_cfp_tbl cfp; + t_u8 range_ext_mode; + mlan_ds_misc_dot11mc_unassoc_ftm_cfg dot11mc_unassoc_ftm_cfg; + mlan_ds_misc_tp_state tp_state; + } param; +} mlan_ds_misc_cfg, *pmlan_ds_misc_cfg; + +/** Hotspot status enable */ +#define HOTSPOT_ENABLED MBIT(0) +/** Hotspot status disable */ +#define HOTSPOT_DISABLED MFALSE +/** Keep Hotspot2.0 compatible in mwu and wpa_supplicant */ +#define HOTSPOT_BY_SUPPLICANT MBIT(1) + +/** Reason codes */ +#define MLAN_REASON_UNSPECIFIED 1 +#define MLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define MLAN_REASON_DEAUTH_LEAVING 3 +#define MLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define MLAN_REASON_DISASSOC_AP_BUSY 5 +#define MLAN_REASON_CLASS2_FRAME_FROM_NOAUTH_STA 6 +#define MLAN_REASON_CLASS3_FRAME_FROM_NOASSOC_STA 7 +#define MLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define MLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 +#endif /* !_MLAN_IOCTL_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.c b/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.c new file mode 100644 index 0000000..abcf614 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.c @@ -0,0 +1,4679 @@ +/** @file moal_cfg80211.c + * + * @brief This file contains the functions for CFG80211. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 = 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, + }, +}; + +/** Channel definitions for 2 GHz to be advertised to cfg80211 */ +static struct ieee80211_channel cfg80211_channels_2ghz[] = { + {.center_freq = 2412, .hw_value = 1, .max_power = 20}, + {.center_freq = 2417, .hw_value = 2, .max_power = 20}, + {.center_freq = 2422, .hw_value = 3, .max_power = 20}, + {.center_freq = 2427, .hw_value = 4, .max_power = 20}, + {.center_freq = 2432, .hw_value = 5, .max_power = 20}, + {.center_freq = 2437, .hw_value = 6, .max_power = 20}, + {.center_freq = 2442, .hw_value = 7, .max_power = 20}, + {.center_freq = 2447, .hw_value = 8, .max_power = 20}, + {.center_freq = 2452, .hw_value = 9, .max_power = 20}, + {.center_freq = 2457, .hw_value = 10, .max_power = 20}, + {.center_freq = 2462, .hw_value = 11, .max_power = 20}, + {.center_freq = 2467, .hw_value = 12, .max_power = 20}, + {.center_freq = 2472, .hw_value = 13, .max_power = 20}, + {.center_freq = 2484, .hw_value = 14, .max_power = 20}, +}; + +/** Channel definitions for 5 GHz to be advertised to cfg80211 */ +static struct ieee80211_channel cfg80211_channels_5ghz[] = { + {.center_freq = 5180, .hw_value = 36, .max_power = 20}, + {.center_freq = 5200, .hw_value = 40, .max_power = 20}, + {.center_freq = 5220, .hw_value = 44, .max_power = 20}, + {.center_freq = 5240, .hw_value = 48, .max_power = 20}, + {.center_freq = 5260, .hw_value = 52, .max_power = 20}, + {.center_freq = 5280, .hw_value = 56, .max_power = 20}, + {.center_freq = 5300, .hw_value = 60, .max_power = 20}, + {.center_freq = 5320, .hw_value = 64, .max_power = 20}, + {.center_freq = 5500, .hw_value = 100, .max_power = 20}, + {.center_freq = 5520, .hw_value = 104, .max_power = 20}, + {.center_freq = 5540, .hw_value = 108, .max_power = 20}, + {.center_freq = 5560, .hw_value = 112, .max_power = 20}, + {.center_freq = 5580, .hw_value = 116, .max_power = 20}, + {.center_freq = 5600, .hw_value = 120, .max_power = 20}, + {.center_freq = 5620, .hw_value = 124, .max_power = 20}, + {.center_freq = 5640, .hw_value = 128, .max_power = 20}, + {.center_freq = 5660, .hw_value = 132, .max_power = 20}, + {.center_freq = 5680, .hw_value = 136, .max_power = 20}, + {.center_freq = 5700, .hw_value = 140, .max_power = 20}, + {.center_freq = 5720, .hw_value = 144, .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}, +}; + +struct ieee80211_supported_band cfg80211_band_2ghz = { + .channels = cfg80211_channels_2ghz, + .band = IEEE80211_BAND_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, + .band = IEEE80211_BAND_5GHZ, + .n_channels = ARRAY_SIZE(cfg80211_channels_5ghz), + .bitrates = cfg80211_rates + 4, + .n_bitrates = ARRAY_SIZE(cfg80211_rates) - 4, +}; + +/** Channel definitions for 5 GHz to be advertised to cfg80211 */ +static struct ieee80211_channel mac1_cfg80211_channels_5ghz[] = { + {.center_freq = 5180, .hw_value = 36, .max_power = 20}, + {.center_freq = 5200, .hw_value = 40, .max_power = 20}, + {.center_freq = 5220, .hw_value = 44, .max_power = 20}, + {.center_freq = 5240, .hw_value = 48, .max_power = 20}, + {.center_freq = 5260, .hw_value = 52, .max_power = 20}, + {.center_freq = 5280, .hw_value = 56, .max_power = 20}, + {.center_freq = 5300, .hw_value = 60, .max_power = 20}, + {.center_freq = 5320, .hw_value = 64, .max_power = 20}, + {.center_freq = 5500, .hw_value = 100, .max_power = 20}, + {.center_freq = 5520, .hw_value = 104, .max_power = 20}, + {.center_freq = 5540, .hw_value = 108, .max_power = 20}, + {.center_freq = 5560, .hw_value = 112, .max_power = 20}, + {.center_freq = 5580, .hw_value = 116, .max_power = 20}, + {.center_freq = 5600, .hw_value = 120, .max_power = 20}, + {.center_freq = 5620, .hw_value = 124, .max_power = 20}, + {.center_freq = 5640, .hw_value = 128, .max_power = 20}, + {.center_freq = 5660, .hw_value = 132, .max_power = 20}, + {.center_freq = 5680, .hw_value = 136, .max_power = 20}, + {.center_freq = 5700, .hw_value = 140, .max_power = 20}, + {.center_freq = 5720, .hw_value = 144, .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}, +}; + +struct ieee80211_supported_band mac1_cfg80211_band_2ghz = { + .channels = cfg80211_channels_2ghz, + .band = IEEE80211_BAND_2GHZ, + .n_channels = ARRAY_SIZE(cfg80211_channels_2ghz), + .bitrates = cfg80211_rates, + .n_bitrates = ARRAY_SIZE(cfg80211_rates), +}; + +struct ieee80211_supported_band mac1_cfg80211_band_5ghz = { + .channels = mac1_cfg80211_channels_5ghz, + .band = IEEE80211_BAND_5GHZ, + .n_channels = ARRAY_SIZE(mac1_cfg80211_channels_5ghz), + .bitrates = cfg80211_rates + 4, + .n_bitrates = ARRAY_SIZE(cfg80211_rates) - 4, +}; + +#if KERNEL_VERSION(2, 6, 29) < LINUX_VERSION_CODE +#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 Get the private structure from net device + * + * @param dev A pointer to net_device structure + * + * @return Pointer to moal_private + */ +void *woal_get_netdev_priv(struct net_device *dev) +{ + return (void *)netdev_priv(dev); +} + +/** + * @brief Get current frequency of active interface + * + * @param priv A pointer to moal_private + * + * @return channel frequency + */ +int woal_get_active_intf_freq(moal_private *priv) +{ + moal_handle *handle = priv->phandle; + int i; + + if (priv->media_connected == MTRUE +#ifdef UAP_SUPPORT + || priv->bss_started == MTRUE +#endif + ) + return ieee80211_channel_to_frequency( + priv->channel +#if KERNEL_VERSION(2, 6, 39) <= CFG80211_VERSION_CODE + , + (priv->channel <= 14 ? IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ) +#endif + ); + + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) + return ieee80211_channel_to_frequency( + handle->priv[i]->channel +#if KERNEL_VERSION(2, 6, 39) <= CFG80211_VERSION_CODE + , + (handle->priv[i]->channel <= 14 ? + IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ) +#endif + ); + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) { + if (handle->priv[i]->bss_started == MTRUE) + return ieee80211_channel_to_frequency( + handle->priv[i]->channel +#if KERNEL_VERSION(2, 6, 39) <= CFG80211_VERSION_CODE + , + (handle->priv[i]->channel <= 14 ? + IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ) +#endif + ); + } +#endif + } + return 0; +} + +/** + * @brief Convert driver band configuration to IEEE band type + * + * @param band Driver band configuration + * + * @return IEEE band type + */ +t_u8 woal_band_cfg_to_ieee_band(t_u32 band) +{ + t_u8 ret_radio_type; + + ENTER(); + + switch (band) { + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + ret_radio_type = IEEE80211_BAND_5GHZ; + break; + case BAND_B: + case BAND_G: + case BAND_B | BAND_G: + case BAND_GN: + case BAND_B | BAND_GN: + /* Fall Through */ + default: + ret_radio_type = IEEE80211_BAND_2GHZ; + break; + } + + LEAVE(); + return ret_radio_type; +} + +/** + * @brief Set/Enable encryption key + * + * @param priv A pointer to moal_private structure + * @param is_enable_wep Enable WEP default key + * @param cipher Cipher suite selector + * @param key A pointer to key + * @param key_len Key length + * @param seq A pointer to sequence + * @param seq_len Sequence length + * @param key_index Key index + * @param addr Mac for which key is to be set + * @param disable Key disabled or not + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_cfg80211_set_key(moal_private *priv, t_u8 is_enable_wep, + t_u32 cipher, const t_u8 *key, int key_len, + const t_u8 *seq, int seq_len, t_u8 key_index, + const t_u8 *addr, int disable, + t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 bcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + + ENTER(); + +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (is_enable_wep) { + PRINTM(MIOCTL, "Enable UAP default key=%d\n", + key_index); + priv->uap_wep_key[key_index].is_default = MTRUE; + goto done; + } + if (key && key_len && + ((cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104))) { + priv->uap_wep_key[key_index].length = key_len; + moal_memcpy_ext( + priv->phandle, priv->uap_wep_key[key_index].key, + key, key_len, + sizeof(priv->uap_wep_key[key_index].key)); + priv->cipher = cipher; + priv->uap_wep_key[key_index].key_index = key_index; + priv->uap_wep_key[key_index].is_default = MFALSE; + PRINTM(MIOCTL, "Set UAP WEP key: key_index=%d len=%d\n", + key_index, key_len); + goto done; + } + } +#endif +#endif + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + + if (is_enable_wep) { + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.is_current_wep_key = MTRUE; + } else if (!disable) { + if (cipher != WLAN_CIPHER_SUITE_WEP40 && + cipher != WLAN_CIPHER_SUITE_WEP104 && + cipher != WLAN_CIPHER_SUITE_TKIP && + cipher != WLAN_CIPHER_SUITE_SMS4 && + cipher != WLAN_CIPHER_SUITE_AES_CMAC && +#if KERNEL_VERSION(4, 0, 0) <= CFG80211_VERSION_CODE + cipher != WLAN_CIPHER_SUITE_CCMP_256 && +#endif +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE + cipher != WLAN_CIPHER_SUITE_GCMP && +#endif +#if KERNEL_VERSION(4, 0, 0) <= CFG80211_VERSION_CODE + cipher != WLAN_CIPHER_SUITE_BIP_GMAC_256 && + cipher != WLAN_CIPHER_SUITE_GCMP_256 && +#endif + 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) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.key_material, + key, key_len, MLAN_MAX_KEY_LENGTH); + 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) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.pn, seq, + PN_SIZE, PN_SIZE); + DBG_HEXDUMP(MCMD_D, "WAPI PN", + sec->param.encrypt_key.pn, seq_len); + } + } + if (addr) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.mac_addr, addr, + ETH_ALEN, MLAN_MAC_ADDR_LENGTH); + if (memcmp(sec->param.encrypt_key.mac_addr, bcast_addr, + ETH_ALEN) == 0) + sec->param.encrypt_key.key_flags = + KEY_FLAG_GROUP_KEY; + else + sec->param.encrypt_key.key_flags = + KEY_FLAG_SET_TX_KEY; + } else { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.mac_addr, + bcast_addr, ETH_ALEN, + MLAN_MAC_ADDR_LENGTH); + sec->param.encrypt_key.key_flags = + KEY_FLAG_GROUP_KEY | KEY_FLAG_SET_TX_KEY; + } + if (seq && seq_len) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.pn, seq, seq_len, + PN_SIZE); + sec->param.encrypt_key.key_flags |= + KEY_FLAG_RX_SEQ_VALID; + } + +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE + if (cipher == WLAN_CIPHER_SUITE_GCMP) + sec->param.encrypt_key.key_flags |= KEY_FLAG_GCMP; +#endif +#if KERNEL_VERSION(4, 0, 0) <= CFG80211_VERSION_CODE + else if (cipher == WLAN_CIPHER_SUITE_GCMP_256) + sec->param.encrypt_key.key_flags |= KEY_FLAG_GCMP_256; +#endif +#if KERNEL_VERSION(4, 0, 0) <= CFG80211_VERSION_CODE + if (cipher == WLAN_CIPHER_SUITE_CCMP_256) + sec->param.encrypt_key.key_flags |= KEY_FLAG_CCMP_256; +#endif + + if (cipher == WLAN_CIPHER_SUITE_AES_CMAC +#if KERNEL_VERSION(4, 0, 0) <= CFG80211_VERSION_CODE + || cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 +#endif + ) { + sec->param.encrypt_key.key_flags |= + KEY_FLAG_AES_MCAST_IGTK; + } + } else { + if (key_index == KEY_INDEX_CLEAR_ALL) + sec->param.encrypt_key.key_disable = MTRUE; + else { + sec->param.encrypt_key.key_remove = MTRUE; + sec->param.encrypt_key.key_index = key_index; + } + sec->param.encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY; + if (addr) + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.mac_addr, addr, + ETH_ALEN, MLAN_MAC_ADDR_LENGTH); + } + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Enable the WEP key to driver + * + * @param priv A pointer to moal_private structure + * @param key A pointer to key data + * @param key_len Length of the key data + * @param index Key index + * @param wait_option wait_option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_cfg80211_set_wep_keys(moal_private *priv, const t_u8 *key, + int key_len, t_u8 index, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 cipher = 0; + + ENTER(); + + if (key_len) { + if (key_len == 5) + cipher = WLAN_CIPHER_SUITE_WEP40; + else + cipher = WLAN_CIPHER_SUITE_WEP104; + ret = woal_cfg80211_set_key(priv, 0, cipher, key, key_len, NULL, + 0, index, NULL, 0, wait_option); + } else { + /* No key provided so it is enable key. We + * want to just set the transmit key index + */ + woal_cfg80211_set_key(priv, 1, cipher, key, key_len, NULL, 0, + index, NULL, 0, wait_option); + } + + LEAVE(); + return ret; +} + +/** + * @brief clear all mgmt ies + * + * @param priv A pointer to moal private structure + * @param wait_option wait_option + * @return N/A + */ +void woal_clear_all_mgmt_ies(moal_private *priv, t_u8 wait_option) +{ + t_u16 mask = 0; + /* clear BEACON WPS/P2P IE */ + if (priv->beacon_wps_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + PRINTM(MCMND, "Clear BEACON WPS ie\n"); + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, MGMT_MASK_BEACON_WPS_P2P, + wait_option); + priv->beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if (priv->assocresp_qos_map_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + PRINTM(MCMND, "Clear associate response QOS map ie\n"); + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, + MGMT_MASK_ASSOC_RESP_QOS_MAP, + wait_option); + priv->assocresp_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + /* clear mgmt frame ies */ + if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_REQ; + if (priv->beacon_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_BEACON; + if (priv->proberesp_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_RESP; + if (priv->assocresp_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_ASSOC_RESP; + if (priv->proberesp_p2p_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_RESP; + if (priv->beacon_vendor_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_BEACON; + if (mask) { + PRINTM(MCMND, "Clear IES: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + priv->beacon_index, priv->probereq_index, + priv->proberesp_index, priv->assocresp_index, + priv->proberesp_p2p_index, priv->beacon_vendor_index); + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, mask, wait_option); + } + priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; +} + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief set bss role + * + * @param priv A pointer to moal private structure + * @param action Action: set or get + * @param role A pointer to bss role + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_bss_role_cfg(moal_private *priv, t_u16 action, t_u8 *bss_role) +{ + int ret = 0; + + ENTER(); + + if (action == MLAN_ACT_SET) { + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(priv, action, MOAL_IOCTL_WAIT, bss_role)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_SET) { + /* set back the mac address */ + woal_request_set_mac_address(priv, MOAL_IOCTL_WAIT); + /* clear the mgmt ies */ + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + + /* Enable interfaces */ + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); + } + +done: + LEAVE(); + return ret; +} +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** + * @brief This function display P2P public action frame type + * + * @param buf A buffer to a management frame + * @param len buffer len + * @param chan the channel + * @param flag Tx/Rx flag. Tx:flag = 1;Rx:flag = 0; + * + * @return N/A + */ +void woal_cfg80211_display_p2p_actframe(const t_u8 *buf, int len, + struct ieee80211_channel *chan, + const t_u8 flag) +{ + const t_u8 p2p_oui[] = {0x50, 0x6f, 0x9a, 0x09}; + t_u8 subtype; + + ENTER(); + + if (!buf || len < (P2P_ACT_FRAME_OUI_SUBTYPE_OFFSET + 1)) { + LEAVE(); + return; + } + + if (((struct ieee80211_mgmt *)buf)->u.action.category == + P2P_ACT_FRAME_CATEGORY && + !memcmp(buf + P2P_ACT_FRAME_OUI_OFFSET, p2p_oui, sizeof(p2p_oui))) { + subtype = *(buf + P2P_ACT_FRAME_OUI_SUBTYPE_OFFSET); + switch (subtype) { + case P2P_GO_NEG_REQ: + PRINTM(MMSG, + "wlan: %s P2P Group Owner Negotiation Req Frame, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_GO_NEG_RSP: + PRINTM(MMSG, + "wlan: %s P2P Group Owner Negotiation Rsp Frame, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_GO_NEG_CONF: + PRINTM(MMSG, + "wlan: %s P2P Group Owner Negotiation Confirm Frame, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_INVITE_REQ: + PRINTM(MMSG, + "wlan: %s P2P Invitation Request, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_INVITE_RSP: + PRINTM(MMSG, + "wlan: %s P2P Invitation Response, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_DEVDIS_REQ: + PRINTM(MMSG, + "wlan: %s P2P Device Discoverability Request, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_DEVDIS_RSP: + PRINTM(MIOCTL, + "wlan: %s P2P Device Discoverability Response, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_PROVDIS_REQ: + PRINTM(MMSG, + "wlan: %s P2P Provision Discovery Request, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_PROVDIS_RSP: + PRINTM(MMSG, + "wlan: %s P2P Provision Discovery Response, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + default: + PRINTM(MMSG, + "wlan: %s Unknown P2P Action Frame, channel=%d, subtype=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0, subtype); + break; + } + } + + LEAVE(); +} + +/** + * @brief initialize p2p client for wpa_supplicant + * + * @param priv A pointer to moal private structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_init_p2p_client(moal_private *priv) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u16 wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + t_u8 bss_role; + + ENTER(); + + /* bss type check */ + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MERROR, "Unexpected bss type when init p2p client\n"); + ret = -EFAULT; + goto done; + } + + /* get the bss role */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_GET, &bss_role)) { + ret = -EFAULT; + goto done; + } + + if (bss_role != MLAN_BSS_ROLE_STA) { + bss_role = MLAN_BSS_ROLE_STA; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, &bss_role)) { + ret = -EFAULT; + goto done; + } + } + + wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* first, init wifi direct to listen mode */ + wifi_direct_mode = WIFI_DIRECT_MODE_LISTEN; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* second, init wifi direct client */ + wifi_direct_mode = WIFI_DIRECT_MODE_CLIENT; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief initialize p2p GO for wpa_supplicant + * + * @param priv A pointer to moal private structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_init_p2p_go(moal_private *priv) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u16 wifi_direct_mode; + t_u8 bss_role; + mlan_ds_wifi_direct_config p2p_config; + mlan_ds_ps_mgmt ps_mgmt; + + ENTER(); + + /* bss type check */ + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MERROR, "Unexpected bss type when init p2p GO\n"); + ret = -EFAULT; + goto done; + } + + wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* first, init wifi direct to listen mode */ + wifi_direct_mode = WIFI_DIRECT_MODE_LISTEN; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* second, init wifi direct to GO mode */ + wifi_direct_mode = WIFI_DIRECT_MODE_GO; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* get the bss role, and set it to uAP */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_GET, &bss_role)) { + ret = -EFAULT; + goto done; + } + + if (bss_role != MLAN_BSS_ROLE_UAP) { + bss_role = MLAN_BSS_ROLE_UAP; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, &bss_role)) { + ret = -EFAULT; + goto done; + } + } +/* NoA:-- Interval = 100TUs and Duration= 50TUs, count=255*/ +#define DEF_NOA_COUNT 255 + if (priv->phandle->noa_duration && priv->phandle->card_info->go_noa) { + memset(&p2p_config, 0, sizeof(p2p_config)); + p2p_config.noa_enable = MTRUE; + p2p_config.index = 0; + p2p_config.noa_count = DEF_NOA_COUNT; + p2p_config.noa_duration = priv->phandle->noa_duration; + p2p_config.noa_interval = priv->phandle->noa_interval; + p2p_config.flags = WIFI_DIRECT_NOA; + woal_p2p_config(priv, MLAN_ACT_SET, &p2p_config); + memset(&ps_mgmt, 0, sizeof(ps_mgmt)); + ps_mgmt.flags = PS_FLAG_PS_MODE; + ps_mgmt.ps_mode = PS_MODE_INACTIVITY; + woal_set_get_uap_power_mode(priv, MLAN_ACT_SET, &ps_mgmt); + PRINTM(MMSG, "Enable NOA: duration=%d, interval=%d\n", + priv->phandle->noa_duration, + priv->phandle->noa_interval); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief reset bss role and wifi direct mode for wpa_supplicant + * + * @param priv A pointer to moal private structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_deinit_p2p(moal_private *priv) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u16 wifi_direct_mode; + t_u8 bss_role; + t_u8 channel_status; + moal_private *remain_priv = NULL; + mlan_ds_ps_mgmt ps_mgmt; + + ENTER(); + + /* bss type check */ + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MERROR, "Unexpected bss type when deinit p2p\n"); + ret = -EFAULT; + goto done; + } + /* unregister mgmt frame from FW */ + if (priv->mgmt_subtype_mask) { + priv->mgmt_subtype_mask = 0; + if (woal_reg_rx_mgmt_ind(priv, MLAN_ACT_SET, + &priv->mgmt_subtype_mask, + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, + "deinit_p2p: fail to unregister mgmt frame\n"); + ret = -EFAULT; + goto done; + } + } + + /* cancel previous remain on channel */ + if (priv->phandle->remain_on_channel) { + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "deinit_p2p: wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg( + remain_priv, MOAL_IOCTL_WAIT, MTRUE, + &channel_status, NULL, 0, 0)) { + PRINTM(MERROR, + "deinit_p2p: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if KERNEL_VERSION(3, 6, 0) > CFG80211_VERSION_CODE + remain_priv->netdev, +#else + remain_priv->wdev, +#endif + priv->phandle->cookie, &priv->phandle->chan, +#if KERNEL_VERSION(3, 8, 0) > CFG80211_VERSION_CODE + priv->phandle->channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + + /* get the bss role */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_GET, &bss_role)) { + ret = -EFAULT; + goto done; + } + + /* reset bss role */ + if (bss_role != MLAN_BSS_ROLE_STA) { + memset(&ps_mgmt, 0, sizeof(ps_mgmt)); + ps_mgmt.flags = PS_FLAG_PS_MODE; + ps_mgmt.ps_mode = PS_MODE_DISABLE; + woal_set_get_uap_power_mode(priv, MLAN_ACT_SET, &ps_mgmt); + bss_role = MLAN_BSS_ROLE_STA; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, &bss_role)) { + ret = -EFAULT; + goto done; + } + } + + wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + +/** + * @brief Request the driver to change the interface type + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param type Virtual interface types + * @param flags Flags + * @param params A pointer to vif_params structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, +#if KERNEL_VERSION(4, 12, 0) > CFG80211_VERSION_CODE + u32 *flags, +#endif + struct vif_params *params) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + t_u8 bss_role; +#endif + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (priv->wdev->iftype == type) { + PRINTM(MINFO, "Already set to required type\n"); + goto done; + } +#ifdef UAP_SUPPORT + if ((priv->bss_type == MLAN_BSS_TYPE_UAP) && (priv->bss_index > 0)) { + priv->wdev->iftype = type; + PRINTM(MMSG, "%s: Skip change virtual intf on uap: type=%d\n", + dev->name, type); + goto done; + } +#endif + + PRINTM(MIOCTL, "%s: change virturl intf=%d\n", dev->name, type); +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_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; + moal_private *remain_priv = NULL; + + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "change_virtual_intf:wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg( + remain_priv, MOAL_IOCTL_WAIT, MTRUE, + &channel_status, NULL, 0, 0)) { + PRINTM(MERROR, + "change_virtual_intf: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if KERNEL_VERSION(3, 6, 0) > CFG80211_VERSION_CODE + remain_priv->netdev, +#else + remain_priv->wdev, +#endif + priv->phandle->cookie, &priv->phandle->chan, +#if KERNEL_VERSION(3, 8, 0) > CFG80211_VERSION_CODE + priv->phandle->channel_type, +#endif + 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_STATION: +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + (priv->wdev->iftype == NL80211_IFTYPE_AP || + priv->wdev->iftype == NL80211_IFTYPE_P2P_GO || + priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + if (priv->phandle->is_go_timer_set) { + woal_cancel_timer(&priv->phandle->go_timer); + priv->phandle->is_go_timer_set = MFALSE; + } + /* if we support wifi direct && priv->bss_type == + * wifi_direct, and currently the interface type is AP + * or GO or client, that means wpa_supplicant deinit() + * wifi direct interface, so we should deinit bss_role + * and wifi direct mode, for other bss_type, we should + * not update bss_role and wifi direct mode + */ + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_deinit_p2p(priv)) { + ret = -EFAULT; + goto done; + } + } +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + woal_cfg80211_del_beacon(wiphy, dev); + bss_role = MLAN_BSS_ROLE_STA; + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, + &bss_role); + PRINTM(MIOCTL, "set bss role for STA\n"); + } +#endif + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + priv->wdev->iftype = NL80211_IFTYPE_STATION; + PRINTM(MINFO, "Setting interface type to managed\n"); + break; +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + case NL80211_IFTYPE_P2P_CLIENT: + if (priv->phandle->is_go_timer_set) { + woal_cancel_timer(&priv->phandle->go_timer); + priv->phandle->is_go_timer_set = MFALSE; + } + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_init_p2p_client(priv)) { + ret = -EFAULT; + goto done; + } + + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + priv->wdev->iftype = NL80211_IFTYPE_P2P_CLIENT; + PRINTM(MINFO, "Setting interface type to P2P client\n"); + + break; +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + case NL80211_IFTYPE_AP: +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + /* Fall Through */ + case NL80211_IFTYPE_P2P_GO: + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_init_p2p_go(priv)) { + ret = -EFAULT; + goto done; + } + priv->phandle->is_go_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->go_timer, + MOAL_TIMER_10S); + } + if (type == NL80211_IFTYPE_P2P_GO) + priv->wdev->iftype = NL80211_IFTYPE_P2P_GO; +#endif +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + if (priv->bss_type == MLAN_BSS_TYPE_STA) { +#ifdef STA_CFG80211 + /** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#endif + if (priv->probereq_index != + MLAN_CUSTOM_IE_AUTO_IDX_MASK) + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, + 0, NULL, 0, NULL, 0, + MGMT_MASK_PROBE_REQ, + MOAL_IOCTL_WAIT); + bss_role = MLAN_BSS_ROLE_UAP; + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, + &bss_role); + PRINTM(MIOCTL, "set bss role for AP\n"); + } +#endif + if (type == NL80211_IFTYPE_AP) + priv->wdev->iftype = NL80211_IFTYPE_AP; + PRINTM(MINFO, "Setting interface type to P2P GO\n"); + + /* there is no need for P2P GO to set bss_mode */ + goto done; + + break; + + case NL80211_IFTYPE_UNSPECIFIED: + bss->param.bss_mode = MLAN_BSS_MODE_AUTO; + priv->wdev->iftype = NL80211_IFTYPE_STATION; + PRINTM(MINFO, "Setting interface type to auto\n"); + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to change the value of fragment + * threshold or rts threshold or retry limit + * + * @param wiphy A pointer to wiphy structure + * @param changed Change flags + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + pmlan_uap_bss_param sys_cfg = NULL; +#endif +#endif + int frag_thr = wiphy->frag_threshold; + int rts_thr = wiphy->rts_threshold; + int retry = wiphy->retry_long; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return -EFAULT; + } + if (rts_thr == MLAN_FRAG_RTS_DISABLED) + rts_thr = MLAN_RTS_MAX_VALUE; + if (frag_thr == MLAN_FRAG_RTS_DISABLED) + frag_thr = MLAN_FRAG_MAX_VALUE; + +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, + "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + /* Initialize the invalid values so that the correct + * values below are downloaded to firmware + */ + woal_set_sys_config_invalid_data(sys_cfg); + sys_cfg->frag_threshold = frag_thr; + sys_cfg->rts_threshold = rts_thr; + sys_cfg->retry_limit = retry; + + if ((changed & WIPHY_PARAM_RTS_THRESHOLD) || + (changed & WIPHY_PARAM_FRAG_THRESHOLD) || + (changed & + (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT))) { + if (woal_set_get_sys_config(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, sys_cfg)) { + kfree(sys_cfg); + goto fail; + } + } + kfree(sys_cfg); + } +#endif +#endif + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + if (woal_set_get_rts(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, &rts_thr)) + goto fail; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + if (woal_set_get_frag(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, &frag_thr)) + goto fail; + } + if (changed & + (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT)) + if (woal_set_get_retry(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, &retry)) + goto fail; + } +#endif +#endif + LEAVE(); + return 0; + +fail: + PRINTM(MERROR, "Failed to change wiphy params %x\n", changed); + LEAVE(); + return -EFAULT; +} + +#if KERNEL_VERSION(2, 6, 36) < CFG80211_VERSION_CODE +/** + * @brief Request the driver to add a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param pairwise Flag to indicate pairwise or group (for kernel > 2.6.36) + * @param mac_addr MAC address (NULL for group key) + * @param params A pointer to key_params structure + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request the driver to add a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param mac_addr MAC address (NULL for group key) + * @param params A pointer to key_params structure + * + * @return 0 -- success, otherwise fail + */ +#endif +int woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, + t_u8 key_index, +#if KERNEL_VERSION(2, 6, 36) < CFG80211_VERSION_CODE + bool pairwise, +#endif + const t_u8 *mac_addr, struct key_params *params) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(netdev); + + ENTER(); + if (priv->ft_pre_connect) { + PRINTM(MINFO, "Skip set keys during ft connecting\n"); + return -EFAULT; + } + if (woal_cfg80211_set_key(priv, 0, params->cipher, params->key, + params->key_len, params->seq, params->seq_len, + key_index, mac_addr, 0, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Error adding the crypto keys\n"); + LEAVE(); + return -EFAULT; + } + + PRINTM(MINFO, "Crypto keys added\n"); + + LEAVE(); + return 0; +} + +#if KERNEL_VERSION(2, 6, 36) < CFG80211_VERSION_CODE +/** + * @brief Request the driver to delete a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param pairwise Flag to indicate pairwise or group (for kernel > 2.6.36) + * @param mac_addr MAC address (NULL for group key) + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request the driver to delete a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param mac_addr MAC address (NULL for group key) + * + * @return 0 -- success, otherwise fail + */ +#endif +int woal_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, + t_u8 key_index, +#if KERNEL_VERSION(2, 6, 36) < CFG80211_VERSION_CODE + bool pairwise, +#endif + const t_u8 *mac_addr) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(netdev); + + ENTER(); + if (priv->phandle->driver_status) { + PRINTM(MERROR, "Block %s in abnormal driver state\n", __func__); + LEAVE(); + return -EFAULT; + } + /* del_key will be trigger from cfg80211_rx_mlme_mgmt funtion + * where we receive deauth/disassoicate packet in rx_work + * use MOAL_NO_WAIT to avoid dead lock + */ + if (MLAN_STATUS_FAILURE == + woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, key_index, + mac_addr, 1, MOAL_NO_WAIT)) { + PRINTM(MERROR, "Error deleting the crypto keys\n"); + LEAVE(); + return -EFAULT; + } + + PRINTM(MINFO, "Crypto keys deleted\n"); + LEAVE(); + return 0; +} + +#if KERNEL_VERSION(2, 6, 37) < CFG80211_VERSION_CODE +/** + * @brief Request to enable WEP key to driver + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param ucast Unicast flag (for kernel > 2.6.37) + * @param mcast Multicast flag (for kernel > 2.6.37) + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request to enable WEP key to driver + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * + * @return 0 -- success, otherwise fail + */ +#endif +int woal_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *netdev, t_u8 key_index +#if KERNEL_VERSION(2, 6, 37) < CFG80211_VERSION_CODE + , + bool ucast, bool mcast +#endif +) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(netdev); + mlan_bss_info bss_info; + + ENTER(); + memset(&bss_info, 0, sizeof(mlan_bss_info)); + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (!bss_info.wep_status) { + LEAVE(); + return ret; + } + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_wep_keys(priv, NULL, 0, key_index, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + LEAVE(); + return ret; +} + +#if KERNEL_VERSION(2, 6, 30) <= CFG80211_VERSION_CODE +int woal_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *netdev, + t_u8 key_index) +{ + PRINTM(MINFO, "set default mgmt key, key index=%d\n", key_index); + + return 0; +} +#endif + +#if KERNEL_VERSION(3, 1, 0) <= CFG80211_VERSION_CODE +/** + * @brief Set GTK rekey data to driver + * + * @param priv A pointer to moal_private structure + * @param gtk_rekey A pointer to mlan_ds_misc_gtk_rekey_data structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * + * @return 0 --success, otherwise fail + */ +mlan_status woal_set_rekey_data(moal_private *priv, + mlan_ds_misc_gtk_rekey_data *gtk_rekey, + t_u8 action, t_u8 wait_option) +{ + mlan_ioctl_req *req; + mlan_ds_misc_cfg *misc_cfg; + int ret = 0; + mlan_status status; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + + if (req == NULL) { + ret = -ENOMEM; + } else { + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_GTK_REKEY_OFFLOAD; + req->req_id = MLAN_IOCTL_MISC_CFG; + + req->action = action; + if (action == MLAN_ACT_SET) + moal_memcpy_ext(priv->phandle, + &misc_cfg->param.gtk_rekey, gtk_rekey, + sizeof(mlan_ds_misc_gtk_rekey_data), + sizeof(mlan_ds_misc_gtk_rekey_data)); + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(req); + } + + LEAVE(); + return ret; +} + +/** + * @brief Give the data necessary for GTK rekeying to the driver + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param data A pointer to cfg80211_gtk_rekey_data structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_gtk_rekey_data *data) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_ds_misc_gtk_rekey_data rekey; + mlan_fw_info fw_info; + + ENTER(); + + if (priv->phandle->params.gtk_rekey_offload == + GTK_REKEY_OFFLOAD_DISABLE) { + PRINTM(MMSG, "%s return: gtk_rekey_offload is DISABLE\n", + __func__); + LEAVE(); + return ret; + } + + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if (!fw_info.fw_supplicant_support) { + LEAVE(); + return -1; + } + + moal_memcpy_ext(priv->phandle, rekey.kek, data->kek, MLAN_KEK_LEN, + MLAN_KEK_LEN); + moal_memcpy_ext(priv->phandle, rekey.kck, data->kck, MLAN_KCK_LEN, + MLAN_KCK_LEN); + moal_memcpy_ext(priv->phandle, rekey.replay_ctr, data->replay_ctr, + MLAN_REPLAY_CTR_LEN, MLAN_REPLAY_CTR_LEN); + + moal_memcpy_ext(priv->phandle, &priv->gtk_rekey_data, &rekey, + sizeof(mlan_ds_misc_gtk_rekey_data), + sizeof(mlan_ds_misc_gtk_rekey_data)); + if (priv->phandle->params.gtk_rekey_offload == + GTK_REKEY_OFFLOAD_SUSPEND) { + priv->gtk_data_ready = MTRUE; + LEAVE(); + return ret; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_rekey_data(priv, &rekey, MLAN_ACT_SET, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} +#endif + +#ifdef STA_SUPPORT +/* Opportunistic Key Caching APIs functions support + * + * this function get pmksa entry list in private structure + * @param priv A pointer to moal_private structure + * @param bssid A pointer to bssid + * @return pointer to target entry or NULL + */ +struct pmksa_entry *woal_get_pmksa_entry(moal_private *priv, const u8 *bssid) +{ + struct pmksa_entry *entry = NULL; + unsigned long flags; + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + return NULL; + } + + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_for_each_entry (entry, &priv->pmksa_cache_list, link) { + if (!memcmp(entry->bssid, bssid, ETH_ALEN)) { + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + return entry; + } + } + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + + return NULL; +} + +/** + * This function flush pmksa entry list in private structure + * @param priv A pointer to moal_private structure + * @return success of failure + */ +int woal_flush_pmksa_list(moal_private *priv) +{ + struct pmksa_entry *entry, *tmp; + unsigned long flags; + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + return -1; + } + + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_for_each_entry_safe (entry, tmp, &priv->pmksa_cache_list, link) { + list_del(&entry->link); + kfree(entry); + } + INIT_LIST_HEAD(&priv->pmksa_cache_list); + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + + return 0; +} + +/** + * This function add new pmksa entry to list + * @param wiphy A pointer to struct wiphy + * @param dev A pointer to net_device structure + * @param pmksa A pointer to cfg80211_pmksa structure + * @return success of failure + */ +int woal_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct pmksa_entry *entry = NULL; + unsigned long flags; + int ret = 0; + + ENTER(); + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + ret = -1; + goto done; + } + + PRINTM(MIOCTL, "Set pmksa entry: bssid=" MACSTR "\n", + MAC2STR(pmksa->bssid)); + entry = woal_get_pmksa_entry(priv, pmksa->bssid); + if (!entry) { + entry = kzalloc(sizeof(struct pmksa_entry), GFP_ATOMIC); + if (!entry) { + PRINTM(MERROR, "Fail to allocate pmksa entry\n"); + goto done; + } + INIT_LIST_HEAD(&entry->link); + moal_memcpy_ext(priv->phandle, entry->bssid, pmksa->bssid, + ETH_ALEN, ETH_ALEN); + moal_memcpy_ext(priv->phandle, entry->pmkid, pmksa->pmkid, + PMKID_LEN, PMKID_LEN); + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_add_tail(&entry->link, &priv->pmksa_cache_list); + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + } else { + /** pmkid is differnt from previous value? */ + memset(entry->pmkid, 0, PMKID_LEN); + moal_memcpy_ext(priv->phandle, entry->pmkid, pmksa->pmkid, + PMKID_LEN, PMKID_LEN); + } + + /** Check if current roaming is going and received target pmkid */ + if (priv->wait_target_ap_pmkid) { + struct cfg80211_connect_params *param = &priv->sme_current; + + if (param && !memcmp(pmksa->bssid, param->bssid, ETH_ALEN)) { + PRINTM(MIOCTL, + "Current roaming target bssid=" MACSTR "\n", + MAC2STR(param->bssid)); + priv->target_ap_pmksa = entry; + priv->wait_target_ap_pmkid = MFALSE; + wake_up_interruptible(&priv->okc_wait_q); + } + } + +done: + LEAVE(); + return ret; +} + +/** + * This function delete pmksa entry + * @param wiphy A pointer to struct wiphy + * @param dev A pointer to net_device structure + * @param pmksa A pointer to cfg80211_pmksa structure + * @return success of failure + */ +int woal_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct pmksa_entry *entry, *tmp; + unsigned long flags; + + ENTER(); + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + LEAVE(); + return -1; + } + + PRINTM(MIOCTL, "Delete pmksa: bssid=" MACSTR "\n", + MAC2STR(pmksa->bssid)); + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_for_each_entry_safe (entry, tmp, &priv->pmksa_cache_list, link) { + if (!memcmp(entry->bssid, pmksa->bssid, ETH_ALEN)) { + list_del(&entry->link); + kfree(entry); + } + } + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + + LEAVE(); + return 0; +} + +/** + * This function flush pmksa entry list + * @param wiphy A pointer to struct wiphy + * @param dev A pointer to net_device structure + * @return success of failure + */ +int woal_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) + return -1; + + PRINTM(MIOCTL, "Flush pmksa list.\n"); + return woal_flush_pmksa_list(priv); +} +#endif + +#if KERNEL_VERSION(3, 5, 0) > CFG80211_VERSION_CODE +#if KERNEL_VERSION(2, 6, 34) < CFG80211_VERSION_CODE +/** + * @brief Request the driver to change the channel + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type of nl80211_channel_type + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request the driver to change the channel + * + * @param wiphy A pointer to wiphy structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type of nl80211_channel_type + * + * @return 0 -- success, otherwise fail + */ +#endif +int woal_cfg80211_set_channel(struct wiphy *wiphy, +#if KERNEL_VERSION(2, 6, 34) < CFG80211_VERSION_CODE + struct net_device *dev, +#endif + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + int ret = 0; + moal_private *priv = NULL; + + ENTER(); + +#if KERNEL_VERSION(3, 5, 0) > CFG80211_VERSION_CODE +#if KERNEL_VERSION(2, 6, 34) < CFG80211_VERSION_CODE + if (dev) + priv = woal_get_netdev_priv(dev); +#endif +#endif + if (!priv) { + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + + if (handle) + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + } + if (priv) { +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (priv->media_connected == MTRUE) { + PRINTM(MERROR, + "This configuration is valid only when station is not connected\n"); + LEAVE(); + return -EINVAL; + } + ret = woal_set_rf_channel(priv, chan, channel_type, + MOAL_IOCTL_WAIT); + } +#endif +#endif + priv->channel = + ieee80211_frequency_to_channel(chan->center_freq); + } + /* set monitor channel support */ + + LEAVE(); + return ret; +} +#endif + +#if KERNEL_VERSION(3, 12, 0) <= CFG80211_VERSION_CODE +static bool woal_is_pattern_supported(struct cfg80211_pkt_pattern *pat, + t_u8 *byte_seq, t_u8 max_byte_seq) +{ + int j, k, valid_byte_cnt = 0; + bool dont_care_byte = false; + + for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { + for (k = 0; k < 8; k++) { + if (pat->mask[j] & 1 << k) { + moal_memcpy_ext(NULL, byte_seq + valid_byte_cnt, + &pat->pattern[j * 8 + k], 1, + (t_u32)max_byte_seq - + (t_u32)valid_byte_cnt); + valid_byte_cnt++; + if (dont_care_byte) + return false; + } else { + if (valid_byte_cnt) + dont_care_byte = true; + } + + if (valid_byte_cnt > max_byte_seq) + return false; + } + } + + byte_seq[max_byte_seq] = valid_byte_cnt; + + return true; +} + +static int woal_get_coalesce_pkt_type(t_u8 *byte_seq) +{ + const t_u8 ipv4_mc_mac[] = {0x33, 0x33}; + const t_u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + const t_u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff}; + + if ((byte_seq[0] & 0x01) && (byte_seq[COALESCE_MAX_BYTESEQ] == 1)) + return PACKET_TYPE_UNICAST; + else if (!memcmp(byte_seq, bc_mac, 4)) + return PACKET_TYPE_BROADCAST; + else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + byte_seq[COALESCE_MAX_BYTESEQ] == 2) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + byte_seq[COALESCE_MAX_BYTESEQ] == 3)) + return PACKET_TYPE_MULTICAST; + + return 0; +} + +static int woal_fill_coalesce_rule_info(struct cfg80211_coalesce_rules *crule, + struct coalesce_rule *mrule) +{ + t_u8 byte_seq[COALESCE_MAX_BYTESEQ + 1]; + struct filt_field_param *param; + int i; + + mrule->max_coalescing_delay = crule->delay; + + param = mrule->params; + + for (i = 0; i < crule->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!woal_is_pattern_supported(&crule->patterns[i], byte_seq, + COALESCE_MAX_BYTESEQ)) { + PRINTM(MERROR, "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!crule->patterns[i].pkt_offset) { + u8 pkt_type; + + pkt_type = woal_get_coalesce_pkt_type(byte_seq); + if (pkt_type && mrule->pkt_type) { + PRINTM(MERROR, + "Multiple packet types not allowed\n"); + return -EOPNOTSUPP; + } else if (pkt_type) { + mrule->pkt_type = pkt_type; + continue; + } + } + + if (crule->condition == NL80211_COALESCE_CONDITION_MATCH) + param->operation = RECV_FILTER_MATCH_TYPE_EQ; + else + param->operation = RECV_FILTER_MATCH_TYPE_NE; + + param->operand_len = byte_seq[COALESCE_MAX_BYTESEQ]; + moal_memcpy_ext(NULL, param->operand_byte_stream, byte_seq, + param->operand_len, COALESCE_MAX_BYTESEQ); + param->offset = crule->patterns[i].pkt_offset; + param++; + + mrule->num_of_fields++; + } + + if (!mrule->pkt_type) { + PRINTM(MERROR, "Packet type can not be determined\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +/** + * @brief Set coalesce parameter + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param coalesce_cfg A pointer to coalesce structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_set_coalesce(moal_private *priv, t_u16 action, + mlan_ds_coalesce_cfg *coalesce_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_COALESCE_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + + moal_memcpy_ext(priv->phandle, &misc_cfg->param.coalesce_cfg, + coalesce_cfg, sizeof(mlan_ds_coalesce_cfg), + sizeof(mlan_ds_coalesce_cfg)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to set the coalesce + * + * @param wiphy A pointer to wiphy structure + * @param coalesce A pointer to cfg80211_coalesce structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce) +{ + int ret = 0; + int i; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + mlan_ds_coalesce_cfg coalesce_cfg; + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + ret = -EINVAL; + goto done; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = -EINVAL; + goto done; + } + + memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); + if (!coalesce) { + PRINTM(MMSG, "Disable coalesce and reset all previous rules\n"); + } else { + coalesce_cfg.num_of_rules = coalesce->n_rules; + for (i = 0; i < coalesce->n_rules; i++) { + ret = woal_fill_coalesce_rule_info( + &coalesce->rules[i], &coalesce_cfg.rule[i]); + if (ret) { + PRINTM(MERROR, + "Recheck the patterns provided for rule %d\n", + i + 1); + return ret; + } + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_coalesce(priv, MLAN_ACT_SET, &coalesce_cfg)) { + PRINTM(MERROR, "wlan: Fail to set coalesce\n"); + ret = -EFAULT; + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Request the driver to set the bitrate + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param peer A pointer to peer address + * @param mask A pointer to cfg80211_bitrate_mask structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask) +{ + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_bss_info bss_info; + enum ieee80211_band band; + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + mlan_rate_cfg_t *rate_cfg = NULL; + + ENTER(); + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can not set data rate in disconnected state\n"); + ret = -EINVAL; + goto done; + } + + status = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (status) + goto done; + band = woal_band_cfg_to_ieee_band(bss_info.bss_band); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + rate = (mlan_ds_rate *)req->pbuf; + rate_cfg = &rate->param.rate_cfg; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_SET; + rate_cfg->rate_type = MLAN_RATE_BITMAP; + + /* Fill HR/DSSS rates. */ + if (band == IEEE80211_BAND_2GHZ) + rate_cfg->bitmap_rates[0] = mask->control[band].legacy & 0x000f; + + /* Fill OFDM rates */ + if (band == IEEE80211_BAND_2GHZ) + rate_cfg->bitmap_rates[1] = + (mask->control[band].legacy & 0x0ff0) >> 4; + else + rate_cfg->bitmap_rates[1] = mask->control[band].legacy; + +#if KERNEL_VERSION(3, 4, 0) <= CFG80211_VERSION_CODE + /* Fill MCS rates */ +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE + rate_cfg->bitmap_rates[2] = mask->control[band].ht_mcs[0]; +#else + rate_cfg->bitmap_rates[2] = mask->control[band].mcs[0]; +#endif +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE + rate_cfg->bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; +#else + rate_cfg->bitmap_rates[2] |= mask->control[band].mcs[1] << 8; +#endif +#endif + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if KERNEL_VERSION(2, 6, 38) <= CFG80211_VERSION_CODE +/** + * @brief Request the driver to get antenna configuration + * + * @param wiphy A pointer to wiphy structure + * @param tx_ant Bitmaps of allowed antennas to use for TX + * @param rx_ant Bitmaps of allowed antennas to use for RX + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + ret = -EINVAL; + goto done; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (handle->feature_control & FEATURE_CTRL_STREAM_2X2) { + *tx_ant = radio->param.ant_cfg.tx_antenna; + *rx_ant = radio->param.ant_cfg.rx_antenna; + } else { + *tx_ant = radio->param.ant_cfg_1x1.antenna; + *rx_ant = radio->param.ant_cfg_1x1.antenna; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + /* Driver must return -EINVAL to cfg80211 */ + if (ret) + ret = -EINVAL; + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to set antenna configuration + * + * @param wiphy A pointer to wiphy structure + * @param tx_ant Bitmaps of allowed antennas to use for TX + * @param rx_ant Bitmaps of allowed antennas to use for RX + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + ret = -EINVAL; + goto done; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_SET; + if (handle->feature_control & FEATURE_CTRL_STREAM_2X2) { + radio->param.ant_cfg.tx_antenna = tx_ant; + radio->param.ant_cfg.rx_antenna = rx_ant; + } else { + radio->param.ant_cfg_1x1.antenna = tx_ant; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + /* Driver must return -EINVAL to cfg80211 */ + if (ret) + ret = -EINVAL; + LEAVE(); + return ret; +} +#endif +/** + * @brief register/unregister mgmt frame forwarding + * + * @param priv A pointer to moal_private structure + * @param frame_type Bit mask for mgmt frame type + * @param reg Register or unregister + * + * @return 0 -- success, otherwise fail + */ +void woal_mgmt_frame_register(moal_private *priv, u16 frame_type, bool reg) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 mgmt_subtype_mask = 0x0; + t_u32 last_mgmt_subtype_mask = priv->mgmt_subtype_mask; + + ENTER(); + +#ifdef SDIO_SUSPEND_RESUME + if (priv->phandle->shutdown_hs_in_process) { + LEAVE(); + return; + } +#endif + + if (reg == MTRUE) { + /* set mgmt_subtype_mask based on origin value */ + mgmt_subtype_mask = + last_mgmt_subtype_mask | BIT(frame_type >> 4); + } else { + /* clear mgmt_subtype_mask */ + mgmt_subtype_mask = + last_mgmt_subtype_mask & ~BIT(frame_type >> 4); + } + PRINTM(MIOCTL, + "%s: frame_type=0x%x mgmt_subtype_mask=0x%x last_mgmt_subtype_mask=0x%x\n", + priv->netdev->name, frame_type, mgmt_subtype_mask, + last_mgmt_subtype_mask); + if (mgmt_subtype_mask != last_mgmt_subtype_mask) { + last_mgmt_subtype_mask = mgmt_subtype_mask; + /* Notify driver that a mgmt frame type was registered. + * Note that this callback may not sleep, and cannot run + * concurrently with itself. + */ + status = woal_reg_rx_mgmt_ind(priv, MLAN_ACT_SET, + &mgmt_subtype_mask, MOAL_NO_WAIT); + priv->mgmt_subtype_mask = last_mgmt_subtype_mask; + } + + LEAVE(); +} +#if KERNEL_VERSION(3, 6, 0) > CFG80211_VERSION_CODE +/** + * @brief register/unregister mgmt frame forwarding + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param frame_type Bit mask for mgmt frame type + * @param reg Register or unregister + * + * @return 0 -- success, otherwise fail + */ +void woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct net_device *dev, u16 frame_type, + bool reg) +#else +#if KERNEL_VERSION(5, 8, 0) <= CFG80211_VERSION_CODE +void woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct mgmt_frame_regs *upd) +#else +/** + * @brief register/unregister mgmt frame forwarding + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param frame_type Bit mask for mgmt frame type + * @param reg Register or unregister + * + * @return 0 -- success, otherwise fail + */ +void woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + u16 frame_type, bool reg) +#endif +#endif +{ +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE + struct net_device *dev = wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + +#if KERNEL_VERSION(5, 8, 0) <= CFG80211_VERSION_CODE + if ((upd->interface_stypes & BIT(IEEE80211_STYPE_AUTH >> 4)) + /** Supplicant 2.8 always register auth, FW will handle auth when + * host_mlme=0 + */ + && !moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) + upd->interface_stypes &= ~BIT(IEEE80211_STYPE_AUTH >> 4); + woal_reg_rx_mgmt_ind(priv, MLAN_ACT_SET, &upd->interface_stypes, + MOAL_NO_WAIT); +#else + if (frame_type == IEEE80211_STYPE_AUTH +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE + /** Supplicant 2.8 always register auth, FW will handle auth when + * host_mlme=0 + */ + && !moal_extflg_isset(priv->phandle, EXT_HOST_MLME) +#endif + ) { + LEAVE(); + return; + } + woal_mgmt_frame_register(priv, frame_type, reg); +#endif + LEAVE(); +} + +#if KERNEL_VERSION(3, 2, 0) <= CFG80211_VERSION_CODE +#if KERNEL_VERSION(3, 3, 0) <= CFG80211_VERSION_CODE +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param params A pointer to cfg80211_mgmt_tx_params structure + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param dont_wait_for_ack Do not wait for ACK + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param dont_wait_for_ack Do not wait for ACK + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param dont_wait_for_ack Do not wait for ACK + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +int woal_cfg80211_mgmt_tx(struct wiphy *wiphy, +#if KERNEL_VERSION(3, 6, 0) > CFG80211_VERSION_CODE + struct net_device *dev, +#else + struct wireless_dev *wdev, +#endif +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE + struct cfg80211_mgmt_tx_params *params, +#else + struct ieee80211_channel *chan, bool offchan, +#if KERNEL_VERSION(3, 8, 0) > CFG80211_VERSION_CODE + enum nl80211_channel_type channel_type, + bool channel_type_valid, +#endif + unsigned int wait, const u8 *buf, size_t len, +#if KERNEL_VERSION(3, 2, 0) <= CFG80211_VERSION_CODE + bool no_cck, +#endif +#if KERNEL_VERSION(3, 3, 0) <= CFG80211_VERSION_CODE + bool dont_wait_for_ack, +#endif +#endif + u64 *cookie) +{ +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE + struct net_device *dev = wdev->netdev; +#endif +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE + struct ieee80211_channel *chan = params->chan; + unsigned int wait = params->wait; + const u8 *buf = params->buf; + size_t len = params->len; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + pmlan_buffer pmbuf = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u16 packet_len = 0; + t_u8 addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + t_u32 pkt_type; + t_u32 tx_control; +#if KERNEL_VERSION(2, 6, 39) <= CFG80211_VERSION_CODE + t_u8 channel_status; + t_u32 duration; + moal_private *remain_priv = NULL; +#endif + + unsigned long flags; + struct sk_buff *skb = NULL; + struct tx_status_info *tx_info = NULL; + t_u32 remain_len = 0; + t_u16 fc, type, stype; + + ENTER(); + + if (buf == NULL || len == 0) { + PRINTM(MERROR, "%s: corrupt data\n", __func__); + LEAVE(); + return -EFAULT; + } + + /* If the packet is probe response, that means we are in listen phase, + * so we should not call remain_on_channel_cfg because + * remain_on_channl already handled it. If the packet if action, that + * means we are in PD/GO negotiation, so we should call + * remain_on_channel_cfg in order to receive action frame from peer + * device + */ + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (ieee80211_is_probe_resp( + ((struct ieee80211_mgmt *)buf)->frame_control)) { + PRINTM(MIOCTL, "Skip send probe_resp in GO/UAP mode\n"); + goto done; + } + fc = le16_to_cpu(((struct ieee80211_mgmt *)buf)->frame_control); + type = fc & IEEE80211_FCTL_FTYPE; + stype = fc & IEEE80211_FCTL_STYPE; + if (type == IEEE80211_FTYPE_MGMT) { + switch (stype) { + case IEEE80211_STYPE_AUTH: + PRINTM(MMSG, "wlan: HostMlme %s send Auth\n", + priv->netdev->name); + break; + case IEEE80211_STYPE_DEAUTH: + case IEEE80211_STYPE_DISASSOC: + PRINTM(MMSG, + "wlan: HostMlme %s send deauth/disassoc\n", + priv->netdev->name); + break; + case IEEE80211_STYPE_ASSOC_RESP: + case IEEE80211_STYPE_REASSOC_RESP: + PRINTM(MMSG, + "wlan: HostMlme %s send assoc/reassoc resp\n", + priv->netdev->name); + break; + default: + break; + } + } + } +#if KERNEL_VERSION(2, 6, 39) <= CFG80211_VERSION_CODE + if ((ieee80211_is_action(((struct ieee80211_mgmt *)buf)->frame_control)) +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE + || moal_extflg_isset(priv->phandle, EXT_HOST_MLME) +#endif + ) { +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + woal_cfg80211_display_p2p_actframe(buf, len, chan, + MTRUE); + if (priv->phandle->is_go_timer_set) { + woal_cancel_timer(&priv->phandle->go_timer); + priv->phandle->is_go_timer_set = MFALSE; + } +#endif + if (priv->phandle->is_remain_timer_set) { + woal_cancel_timer(&priv->phandle->remain_timer); + woal_remain_timer_func(priv->phandle); + } + /* With sd8777 We have difficulty to receive response packet in + * 500ms + */ +#define MGMT_TX_DEFAULT_WAIT_TIME 1500 + if (priv->phandle->remain_on_channel) + remain_priv = + priv->phandle + ->priv[priv->phandle->remain_bss_index]; + /** cancel previous remain on channel */ + if (priv->phandle->remain_on_channel && remain_priv) { + if (woal_cfg80211_remain_on_channel_cfg( + remain_priv, MOAL_IOCTL_WAIT, MTRUE, + &channel_status, NULL, 0, 0)) + PRINTM(MERROR, + "mgmt_tx:Fail to cancel remain on channel\n"); + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if KERNEL_VERSION(3, 6, 0) > CFG80211_VERSION_CODE + remain_priv->netdev, +#else + remain_priv->wdev, +#endif + priv->phandle->cookie, + &priv->phandle->chan, +#if KERNEL_VERSION(3, 8, 0) > CFG80211_VERSION_CODE + priv->phandle->channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } +#ifdef STA_CFG80211 + /** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#endif + + if (chan) { + duration = (wait > MGMT_TX_DEFAULT_WAIT_TIME) ? + wait : + MGMT_TX_DEFAULT_WAIT_TIME; +#if KERNEL_VERSION(3, 8, 0) > CFG80211_VERSION_CODE + if (channel_type_valid) + ret = woal_cfg80211_remain_on_channel_cfg( + priv, MOAL_IOCTL_WAIT, MFALSE, + &channel_status, chan, channel_type, + duration); + else +#endif + ret = woal_cfg80211_remain_on_channel_cfg( + priv, MOAL_IOCTL_WAIT, MFALSE, + &channel_status, chan, 0, duration); + if (ret) { + /* Return fail will cause p2p connection fail + */ + woal_sched_timeout(2); +#if KERNEL_VERSION(3, 8, 0) > CFG80211_VERSION_CODE + if (channel_type_valid) + ret = woal_cfg80211_remain_on_channel_cfg( + priv, MOAL_IOCTL_WAIT, MFALSE, + &channel_status, chan, + channel_type, duration); + else +#endif + ret = woal_cfg80211_remain_on_channel_cfg( + priv, MOAL_IOCTL_WAIT, MFALSE, + &channel_status, chan, 0, + duration); + PRINTM(MERROR, + "Try configure remain on channel again, ret=%d\n", + ret); + ret = 0; + } else { + priv->phandle->remain_on_channel = MTRUE; + priv->phandle->remain_bss_index = + priv->bss_index; +#if KERNEL_VERSION(3, 8, 0) > CFG80211_VERSION_CODE + priv->phandle->channel_type = channel_type; +#endif + moal_memcpy_ext( + priv->phandle, &priv->phandle->chan, + chan, sizeof(struct ieee80211_channel), + sizeof(struct ieee80211_channel)); + PRINTM(MIOCTL, + "%s: Mgmt Tx: Set remain channel=%d duration=%d\n", + dev->name, + ieee80211_frequency_to_channel( + chan->center_freq), + duration); + } + } + } +#endif + + /* pkt_type + tx_control */ +#define HEADER_SIZE 8 + packet_len = (t_u16)len + MLAN_MAC_ADDR_LENGTH; + pmbuf = woal_alloc_mlan_buffer(priv->phandle, + MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + + packet_len + sizeof(packet_len)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } +#if KERNEL_VERSION(3, 8, 0) > LINUX_VERSION_CODE + *cookie = random32() | 1; +#else + *cookie = prandom_u32() | 1; +#endif + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + pkt_type = MRVL_PKT_TYPE_MGMT_FRAME; + tx_control = 0; + remain_len = HEADER_SIZE + packet_len + sizeof(packet_len); + /* Add pkt_type and tx_control */ + moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset, + &pkt_type, sizeof(pkt_type), remain_len); + remain_len -= sizeof(pkt_type); + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), + &tx_control, sizeof(tx_control), remain_len); + remain_len -= sizeof(tx_control); + /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl */ +#define PACKET_ADDR4_POS (2 + 2 + 6 + 6 + 6 + 2) + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE, + &packet_len, sizeof(packet_len), remain_len); + remain_len -= sizeof(packet_len); + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE + + sizeof(packet_len), + buf, PACKET_ADDR4_POS, remain_len); + remain_len -= PACKET_ADDR4_POS; + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE + + sizeof(packet_len) + PACKET_ADDR4_POS, + addr, MLAN_MAC_ADDR_LENGTH, remain_len); + remain_len -= MLAN_MAC_ADDR_LENGTH; + moal_memcpy_ext(priv->phandle, + 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, + remain_len); + + pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len); + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + if ((ieee80211_is_action(((struct ieee80211_mgmt *)buf)->frame_control)) +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE + || moal_extflg_isset(priv->phandle, EXT_HOST_MLME) +#endif + ) { + pmbuf->flags = MLAN_BUF_FLAG_TX_STATUS; + if (!priv->tx_seq_num) + priv->tx_seq_num++; + pmbuf->tx_seq_num = priv->tx_seq_num++; + tx_info = kzalloc(sizeof(struct tx_status_info), GFP_ATOMIC); + if (tx_info) { + skb = alloc_skb(len, GFP_ATOMIC); + if (skb) { + moal_memcpy_ext(priv->phandle, skb->data, buf, + len, len); + skb_put(skb, len); + spin_lock_irqsave(&priv->tx_stat_lock, flags); + tx_info->tx_cookie = *cookie; + tx_info->tx_skb = skb; + tx_info->tx_seq_num = pmbuf->tx_seq_num; + if ((priv->bss_role == MLAN_BSS_ROLE_UAP) && + (priv->phandle->remain_on_channel && !wait)) + tx_info->cancel_remain_on_channel = + MTRUE; + INIT_LIST_HEAD(&tx_info->link); + list_add_tail(&tx_info->link, + &priv->tx_stat_queue); + spin_unlock_irqrestore(&priv->tx_stat_lock, + flags); + } else { + kfree(tx_info); + tx_info = NULL; + } + } + } + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + + /* Delay 30ms to guarantee the packet has been already tx'ed, + * because if we call cfg80211_mgmt_tx_status() immediately, + * then wpa_supplicant will call cancel_remain_on_channel(), + * which may affect the mgmt frame tx. Meanwhile it is only + * necessary for P2P action handshake to wait 30ms. + */ + if ((ieee80211_is_action( + ((struct ieee80211_mgmt *)buf)->frame_control)) +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE + || moal_extflg_isset(priv->phandle, EXT_HOST_MLME) +#endif + ) { + if (tx_info) + break; + else + woal_sched_timeout(30); + } + /* Notify the mgmt tx status */ +#if KERNEL_VERSION(2, 6, 37) <= CFG80211_VERSION_CODE +#if KERNEL_VERSION(3, 6, 0) > CFG80211_VERSION_CODE + cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, + GFP_ATOMIC); +#else + cfg80211_mgmt_tx_status(priv->wdev, *cookie, buf, len, true, + GFP_ATOMIC); +#endif +#endif + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } + +done: + + if (status != MLAN_STATUS_PENDING) { + if (tx_info) + woal_remove_tx_info(priv, tx_info->tx_seq_num); + } + + LEAVE(); + return ret; +} + +/** + * @brief Add custom ie to mgmt frames. + * + * @param priv A pointer to moal private structure + * @param beacon_ies_data Beacon ie + * @param beacon_index The index for beacon when auto index + * @param proberesp_ies_data Probe resp ie + * @param proberesp_index The index for probe resp when auto index + * @param assocresp_ies_data Assoc resp ie + * @param assocresp_index The index for assoc resp when auto index + * @param probereq_ies_data Probe req ie + * @param probereq_index The index for probe req when auto index + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +static mlan_status +woal_cfg80211_custom_ie(moal_private *priv, custom_ie *beacon_ies_data, + t_u16 *beacon_index, custom_ie *proberesp_ies_data, + t_u16 *proberesp_index, custom_ie *assocresp_ies_data, + t_u16 *assocresp_index, custom_ie *probereq_ies_data, + t_u16 *probereq_index, t_u8 wait_option) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *custom_ie = NULL; + t_u8 *pos = NULL; + t_u16 len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 remain_len = 0; + + ENTER(); + + custom_ie = kzalloc(sizeof(mlan_ds_misc_custom_ie), GFP_KERNEL); + if (!custom_ie) { + PRINTM(MERROR, "Fail to allocate custome_ie\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + + custom_ie->type = TLV_TYPE_MGMT_IE; + + pos = (t_u8 *)custom_ie->ie_data_list; + remain_len = sizeof(custom_ie->ie_data_list); + if (beacon_ies_data) { + len = sizeof(*beacon_ies_data) - MAX_IE_SIZE + + beacon_ies_data->ie_length; + moal_memcpy_ext(priv->phandle, pos, beacon_ies_data, len, + remain_len); + pos += len; + remain_len -= len; + custom_ie->len += len; + } + + if (proberesp_ies_data) { + len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE + + proberesp_ies_data->ie_length; + moal_memcpy_ext(priv->phandle, pos, proberesp_ies_data, len, + remain_len); + pos += len; + remain_len -= len; + custom_ie->len += len; + } + + if (assocresp_ies_data) { + len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE + + assocresp_ies_data->ie_length; + moal_memcpy_ext(priv->phandle, pos, assocresp_ies_data, len, + remain_len); + pos += len; + remain_len -= len; + custom_ie->len += len; + } + + if (probereq_ies_data) { + len = sizeof(*probereq_ies_data) - MAX_IE_SIZE + + probereq_ies_data->ie_length; + moal_memcpy_ext(priv->phandle, pos, probereq_ies_data, len, + remain_len); + pos += len; + remain_len -= len; + custom_ie->len += len; + } + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + PRINTM(MERROR, "Fail to allocate ioctl_req\n"); + status = MLAN_STATUS_FAILURE; + 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; + + moal_memcpy_ext(priv->phandle, &misc->param.cust_ie, custom_ie, + sizeof(mlan_ds_misc_custom_ie), + sizeof(mlan_ds_misc_custom_ie)); + + status = woal_request_ioctl(priv, ioctl_req, wait_option); + if (status != MLAN_STATUS_SUCCESS) + 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; + } + // TODO why we check status_code at end + if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) + status = MLAN_STATUS_FAILURE; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + kfree(custom_ie); + LEAVE(); + return status; +} + +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE +/** + * @brief set Qos map + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param qos_map A pointer to cfg80211_qos_map structure + * + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_qos_map(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_qos_map *qos_map) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int i, j, ret = 0; + + ENTER(); + /**clear dscp map*/ + if (!qos_map) { + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + goto done; + } + + /**update dscp map*/ + for (i = 0; i < MAX_NUM_TID; i++) { + PRINTM(MINFO, "TID %d: dscp_low=%d, dscp_high=%d\n", i, + qos_map->up[i].low, qos_map->up[i].high); + if (qos_map->up[i].low != 0xff && qos_map->up[i].high != 0xff && + qos_map->up[i].high <= 63) { + for (j = qos_map->up[i].low; j <= qos_map->up[i].high; + j++) + priv->dscp_map[j] = i; + } + } + + for (i = 0; i < qos_map->num_des; i++) { + if ((qos_map->dscp_exception[i].dscp <= 63) && + (qos_map->dscp_exception[i].up <= 7)) { + PRINTM(MINFO, "dscp excpt: value=%d priority=%d\n", + qos_map->dscp_exception[i].dscp, + qos_map->dscp_exception[i].up); + priv->dscp_map[qos_map->dscp_exception[i].dscp] = + qos_map->dscp_exception[i].up; + } + } + + /**UAP update (re)associate response*/ + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + IEEEtypes_Generic_t qos_map_ie; + t_u16 qos_map_ies_len; + + qos_map_ie.ieee_hdr.element_id = QOS_MAPPING; + qos_map_ie.ieee_hdr.len = + 2 * qos_map->num_des + sizeof(qos_map->up); + qos_map_ies_len = + qos_map_ie.ieee_hdr.len + sizeof(qos_map_ie.ieee_hdr); + + if (qos_map_ies_len > sizeof(qos_map_ie.data)) { + PRINTM(MERROR, + "QoS MAP IE size exceeds the buffer len\n"); + goto done; + } + moal_memcpy_ext(priv->phandle, qos_map_ie.data, + (t_u8 *)qos_map->dscp_exception, + 2 * qos_map->num_des, sizeof(qos_map_ie.data)); + moal_memcpy_ext(priv->phandle, + &qos_map_ie.data[2 * qos_map->num_des], + (t_u8 *)qos_map->up, sizeof(qos_map->up), + sizeof(qos_map_ie.data) - 2 * qos_map->num_des); + + /* set the assoc response ies */ + ret = woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, + (t_u8 *)&qos_map_ie, + qos_map_ies_len, NULL, 0, + MGMT_MASK_ASSOC_RESP_QOS_MAP, + MOAL_IOCTL_WAIT); + if (ret) { + PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); + goto done; + } + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief get specific ie + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ie_out Pointer to out IE buf + * @param ie_out_len Total length of ie_out + * @param mask IE mask + * + * @return out IE length + */ +static t_u16 woal_get_specific_ie(const t_u8 *ie, int len, t_u8 *ie_out, + t_u32 ie_out_len, t_u16 mask) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + t_u16 out_len = 0; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = {0x00, 0x50, 0xf2, 0x04}; + const u8 p2p_oui[4] = {0x50, 0x6f, 0x9a, 0x09}; + const u8 wfd_oui[4] = {0x50, 0x6f, 0x9a, 0x0a}; + const t_u8 wmm_oui[4] = {0x00, 0x50, 0xf2, 0x02}; + + ENTER(); + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + if (id == VENDOR_SPECIFIC_221) { + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp(pvendor_ie->vend_hdr.oui, wmm_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wmm_oui[3]) { + PRINTM(MIOCTL, "find WMM IE\n"); + } else if (!memcmp(pvendor_ie->vend_hdr.oui, p2p_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == + p2p_oui[3]) { + if (mask & IE_MASK_P2P) { + /** only get first p2p ie here */ + moal_memcpy_ext(NULL, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + break; + } + } else if (!memcmp(pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == + wps_oui[3]) { + if (mask & IE_MASK_WPS) { + if ((out_len + length + 2) < + ie_out_len) { + moal_memcpy_ext( + NULL, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "get_specific_ie: IE too big, fail copy WPS IE\n"); + break; + } + } + } else if (!memcmp(pvendor_ie->vend_hdr.oui, wfd_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == + wfd_oui[3]) { + if (mask & IE_MASK_WFD) { + if ((out_len + length + 2) < + ie_out_len) { + moal_memcpy_ext( + NULL, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "get_specific_ie: IE too big, fail copy WFD IE\n"); + break; + } + } + } else if (mask & IE_MASK_VENDOR) { + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(NULL, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "get_specific_ie:IE too big, fail copy VENDOR IE\n"); + break; + } + } + } + pos += (length + 2); + left_len -= (length + 2); + } + LEAVE(); + return out_len; +} + +/** + * @brief Find specific IE from IE buffer + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param spec_ie Pointer to specific IE buffer + * @param spec_len Total length of specific IE + * + * @return out IE length + */ +static t_u8 woal_find_ie(const t_u8 *ie, int len, const t_u8 *spec_ie, + int spec_len) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + if ((length + 2) == spec_len) { + if (!memcmp(pos, spec_ie, spec_len)) + return MTRUE; + } + pos += (length + 2); + left_len -= (length + 2); + } + return MFALSE; +} + +/** + * @brief Filter specific IE in ie buf + * + * @param priv pointer to moal private structure + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ie_out Pointer to out IE buf + * @param ie_out_len Total length of ie_out + * @param wps_flag flag for wps/p2p + * @param dup_ie Pointer to duplicate ie + * @param dup_ie_len duplicate IE len + * + * @return out IE length + */ +static t_u16 woal_filter_beacon_ies(moal_private *priv, const t_u8 *ie, int len, + t_u8 *ie_out, t_u32 ie_out_len, + t_u16 wps_flag, const t_u8 *dup_ie, + int dup_ie_len) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + t_u16 out_len = 0; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = {0x00, 0x50, 0xf2, 0x04}; + const u8 p2p_oui[4] = {0x50, 0x6f, 0x9a, 0x09}; + const u8 wfd_oui[4] = {0x50, 0x6f, 0x9a, 0x0a}; + const t_u8 wmm_oui[4] = {0x00, 0x50, 0xf2, 0x02}; + t_u8 find_p2p_ie = MFALSE; + t_u8 enable_11d = MFALSE; + t_u8 ext_id = 0; + int ie_len; + + /* ERP_INFO/EXTENDED_SUPPORT_RATES/HT_CAPABILITY/HT_OPERATION/WMM + * and WPS/P2P/WFD IE will be fileter out + */ + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + if (dup_ie && dup_ie_len && + woal_find_ie(dup_ie, dup_ie_len, pos, length + 2)) { + PRINTM(MIOCTL, "skip duplicate IE\n"); + pos += (length + 2); + left_len -= (length + 2); + continue; + } + switch (id) { + case COUNTRY_INFO: + enable_11d = MTRUE; + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(priv->phandle, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "IE too big, fail copy COUNTRY INFO IE\n"); + } + break; + case HT_CAPABILITY: + case HT_OPERATION: + case VHT_CAPABILITY: + case VHT_OPERATION: + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) { + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(priv->phandle, + ie_out + out_len, pos, + length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "IE too big, fail copy COUNTRY INFO IE\n"); + } + } + break; + case EXTENDED_SUPPORTED_RATES: + case WLAN_EID_ERP_INFO: + /* Fall Through */ + case REGULATORY_CLASS: + /* Fall Through */ + case OVERLAPBSSSCANPARAM: + /* Fall Through */ + case WAPI_IE: + break; + case EXTENSION: + ext_id = *(pos + 2); + if ((ext_id == HE_CAPABILITY || + ext_id == HE_OPERATION) && + !moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) + break; + else { + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(priv->phandle, + ie_out + out_len, pos, + length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "IE too big, fail copy EXTENSION IE\n"); + } + break; + } + case EXT_CAPABILITY: + /* filter out EXTCAP */ + if (wps_flag & IE_MASK_EXTCAP) { + ie_len = length + 2; + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + (t_u8 *)pos, &ie_len, + MOAL_IOCTL_WAIT)) + PRINTM(MERROR, + "Fail to set EXTCAP IE\n"); + break; + } + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(priv->phandle, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "IE too big, fail copy EXTCAP IE\n"); + } + break; + case VENDOR_SPECIFIC_221: + /* filter out wmm ie */ + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp(pvendor_ie->vend_hdr.oui, wmm_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wmm_oui[3]) { + break; + } + /* filter out wps ie */ + else if (!memcmp(pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wps_oui[3]) { + if (wps_flag & IE_MASK_WPS) + break; + } + /* filter out first p2p ie */ + else if (!memcmp(pvendor_ie->vend_hdr.oui, p2p_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == p2p_oui[3]) { + if (!find_p2p_ie && (wps_flag & IE_MASK_P2P)) { + find_p2p_ie = MTRUE; + break; + } + } + /* filter out wfd ie */ + else if (!memcmp(pvendor_ie->vend_hdr.oui, wfd_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wfd_oui[3]) { + if (wps_flag & IE_MASK_WFD) + break; + } else if (wps_flag & IE_MASK_VENDOR) { + // filter out vendor IE + break; + } + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(priv->phandle, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "IE too big, fail copy VENDOR_SPECIFIC_221 IE\n"); + } + break; + default: + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(priv->phandle, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, "IE too big, fail copy %d IE\n", + id); + } + break; + } + pos += (length + 2); + left_len -= (length + 2); + } + + if (enable_11d) + woal_set_11d(priv, MOAL_IOCTL_WAIT, MTRUE); + return out_len; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief Check if selected_registrar_on in wps_ie + * + * @param ie Pointer to IEs + * @param len Total length of ie + * + * @return MTRUE/MFALSE + */ +t_u8 is_selected_registrar_on(const t_u8 *ie, int len) +{ +#define WPS_IE_FIX_LEN 6 +#define TLV_ID_SELECTED_REGISTRAR 0x1041 + int left_len = len - WPS_IE_FIX_LEN; + + TLV_Generic_t *tlv = (TLV_Generic_t *)(ie + WPS_IE_FIX_LEN); + u16 tlv_type, tlv_len; + u8 *pos = NULL; + + while (left_len > sizeof(TLV_Generic_t)) { + tlv_type = ntohs(tlv->type); + tlv_len = ntohs(tlv->len); + if (tlv_type == TLV_ID_SELECTED_REGISTRAR) { + PRINTM(MIOCTL, "Selected Registrar found !"); + pos = (u8 *)tlv + sizeof(TLV_Generic_t); + if (*pos == 1) + return MTRUE; + else + return MFALSE; + } + tlv = (TLV_Generic_t *)((u8 *)tlv + tlv_len + + sizeof(TLV_Generic_t)); + left_len -= tlv_len + sizeof(TLV_Generic_t); + } + return MFALSE; +} + +/** + * @brief Check if selected_registrar_on in ies + * + * @param ie Pointer to IEs + * @param len Total length of ie + * + * + * @return MTRUE/MFALSE + */ +static t_u16 woal_is_selected_registrar_on(const t_u8 *ie, int len) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = {0x00, 0x50, 0xf2, 0x04}; + + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + switch (id) { + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp(pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wps_oui[3]) { + PRINTM(MIOCTL, "Find WPS ie\n"); + return is_selected_registrar_on(pos, + length + 2); + } + break; + default: + break; + } + pos += (length + 2); + left_len -= (length + 2); + } + return MFALSE; +} +#endif + +/** + * @brief config AP or GO for mgmt frame ies. + * + * @param priv A pointer to moal private structure + * @param beacon_ies A pointer to beacon ies + * @param beacon_ies_len Beacon ies length + * @param proberesp_ies A pointer to probe resp ies + * @param proberesp_ies_len Probe resp ies length + * @param assocresp_ies A pointer to probe resp ies + * @param assocresp_ies_len Assoc resp ies length + * @param probereq_ies A pointer to probe req ies + * @param probereq_ies_len Probe req ies length * + * @param mask Mgmt frame mask + * @param wait_option wait_option + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_mgmt_frame_ie( + moal_private *priv, const t_u8 *beacon_ies, size_t beacon_ies_len, + const t_u8 *proberesp_ies, size_t proberesp_ies_len, + const t_u8 *assocresp_ies, size_t assocresp_ies_len, + const t_u8 *probereq_ies, size_t probereq_ies_len, t_u16 mask, + t_u8 wait_option) +{ + int ret = 0; + t_u8 *pos = NULL; + custom_ie *beacon_ies_data = NULL; + custom_ie *proberesp_ies_data = NULL; + custom_ie *assocresp_ies_data = NULL; + custom_ie *probereq_ies_data = NULL; + + /* static variables for mgmt frame ie auto-indexing */ + t_u16 beacon_index = priv->beacon_index; + t_u16 proberesp_index = priv->proberesp_index; + t_u16 assocresp_index = priv->assocresp_index; + t_u16 probereq_index = priv->probereq_index; + t_u16 beacon_wps_index = priv->beacon_wps_index; + t_u16 proberesp_p2p_index = priv->proberesp_p2p_index; + t_u16 assocrep_qos_map_index = priv->assocresp_qos_map_index; + t_u16 beacon_vendor_index = priv->beacon_vendor_index; + + ENTER(); + + /* we need remove vendor IE from beacon extra IE, vendor IE will be + * configure through proberesp_vendor_index + */ + if (mask & MGMT_MASK_BEACON_WPS_P2P) { + beacon_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!beacon_ies_data) { + ret = -ENOMEM; + goto done; + } + if (beacon_ies && beacon_ies_len) { +#ifdef WIFI_DIRECT_SUPPORT + if (woal_is_selected_registrar_on(beacon_ies, + beacon_ies_len)) { + PRINTM(MIOCTL, "selected_registrar is on\n"); + priv->phandle->is_go_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->go_timer, + MOAL_TIMER_10S); + } else + PRINTM(MIOCTL, "selected_registrar is off\n"); +#endif + beacon_ies_data->ie_index = beacon_wps_index; + beacon_ies_data->mgmt_subtype_mask = MGMT_MASK_BEACON; + beacon_ies_data->ie_length = woal_filter_beacon_ies( + priv, beacon_ies, beacon_ies_len, + beacon_ies_data->ie_buffer, MAX_IE_SIZE, + IE_MASK_VENDOR, NULL, 0); + DBG_HEXDUMP(MCMD_D, "beacon extra ie", + beacon_ies_data->ie_buffer, + beacon_ies_data->ie_length); + } else { + /* clear the beacon wps ies */ + if (beacon_wps_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid beacon wps index for mgmt frame ie.\n"); + goto done; + } + + beacon_ies_data->ie_index = beacon_wps_index; + beacon_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if ((beacon_ies && beacon_ies_len && + beacon_ies_data->ie_length) || + (beacon_ies_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK)) { + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie( + priv, beacon_ies_data, &beacon_wps_index, + proberesp_ies_data, &proberesp_index, + assocresp_ies_data, &assocresp_index, + probereq_ies_data, &probereq_index, + wait_option)) { + PRINTM(MERROR, "Fail to set beacon wps IE\n"); + ret = -EFAULT; + } + priv->beacon_wps_index = beacon_wps_index; + PRINTM(MCMND, "beacon_wps_index=0x%x len=%d\n", + beacon_wps_index, beacon_ies_data->ie_length); + goto done; + } + kfree(beacon_ies_data); // Further allocation of beacon_ies_data + // is happening, so need to free here. + beacon_ies_data = NULL; + } + + if (mask & MGMT_MASK_ASSOC_RESP_QOS_MAP) { + assocresp_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!assocresp_ies_data) { + ret = -ENOMEM; + goto done; + } + if (assocresp_ies && assocresp_ies_len) { + /* set the assoc response qos map ies */ + assocresp_ies_data->ie_index = assocrep_qos_map_index; + assocresp_ies_data->mgmt_subtype_mask = + MGMT_MASK_ASSOC_RESP; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == + assocrep_qos_map_index) + assocresp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + if (assocresp_ies_len > MAX_IE_SIZE) { + PRINTM(MERROR, + "IE too big: assocresp_ies_len=%d\n", + (int)assocresp_ies_len); + goto done; + } + assocresp_ies_data->ie_length = assocresp_ies_len; + pos = assocresp_ies_data->ie_buffer; + moal_memcpy_ext(priv->phandle, pos, assocresp_ies, + assocresp_ies_len, MAX_IE_SIZE); + DBG_HEXDUMP(MCMD_D, "Qos Map", + assocresp_ies_data->ie_buffer, + assocresp_ies_data->ie_length); + } else { + /* clear the assoc response qos map ie */ + if (assocrep_qos_map_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid Qos map index for mgmt frame ie.\n"); + goto done; + } + + assocresp_ies_data->ie_index = assocrep_qos_map_index; + assocresp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + assocresp_ies_data->ie_length = 0; + assocrep_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie(priv, NULL, &beacon_wps_index, NULL, + &proberesp_index, + assocresp_ies_data, + &assocrep_qos_map_index, NULL, + &probereq_index, wait_option)) { + PRINTM(MERROR, "Fail to set Qos map IE\n"); + ret = -EFAULT; + } + priv->assocresp_qos_map_index = assocrep_qos_map_index; + PRINTM(MCMND, "qos map ie index=0x%x len=%d\n", + assocrep_qos_map_index, assocresp_ies_data->ie_length); + goto done; + } + + if (mask & MGMT_MASK_BEACON) { + beacon_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!beacon_ies_data) { + ret = -ENOMEM; + goto done; + } + } + + if (mask & MGMT_MASK_PROBE_RESP) { + /** set or clear proberesp ie */ + if (proberesp_ies_len || + (!proberesp_ies_len && !beacon_ies_len)) { + proberesp_ies_data = + kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!proberesp_ies_data) { + ret = -ENOMEM; + goto done; + } + } + } + + if (mask & MGMT_MASK_ASSOC_RESP) { + /** set or clear assocresp ie */ + if (assocresp_ies_len || + (!assocresp_ies_len && !beacon_ies_len)) { + assocresp_ies_data = + kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!assocresp_ies_data) { + ret = -ENOMEM; + goto done; + } + } + } + if (mask & MGMT_MASK_PROBE_REQ) { + probereq_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!probereq_ies_data) { + ret = -ENOMEM; + goto done; + } + } + + if (beacon_ies_data) { + if (beacon_ies && beacon_ies_len) { + /* set the probe response/beacon vendor ies which + * includes wpa IE + */ + beacon_ies_data->ie_index = beacon_vendor_index; + beacon_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_RESP | MGMT_MASK_BEACON; + if (beacon_vendor_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) + beacon_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + beacon_ies_data->ie_length = + woal_get_specific_ie(beacon_ies, beacon_ies_len, + beacon_ies_data->ie_buffer, + MAX_IE_SIZE, + IE_MASK_VENDOR); + DBG_HEXDUMP(MCMD_D, "beacon vendor IE", + beacon_ies_data->ie_buffer, + beacon_ies_data->ie_length); + } + if (beacon_vendor_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK && + !beacon_ies_data->ie_length) { + /* clear the beacon vendor ies */ + if (beacon_vendor_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid beacon_vendor_index for mgmt frame ie.\n"); + goto done; + } + beacon_ies_data->ie_index = beacon_vendor_index; + beacon_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + beacon_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if ((beacon_ies && beacon_ies_len && + beacon_ies_data->ie_length) || + (beacon_ies_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK)) { + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie( + priv, beacon_ies_data, &beacon_vendor_index, + NULL, &proberesp_index, NULL, + &assocresp_index, NULL, &probereq_index, + wait_option)) { + PRINTM(MERROR, + "Fail to set beacon vendor IE\n"); + ret = -EFAULT; + goto done; + } + priv->beacon_vendor_index = beacon_vendor_index; + PRINTM(MCMND, "beacon_vendor=0x%x len=%d\n", + beacon_vendor_index, beacon_ies_data->ie_length); + } + memset(beacon_ies_data, 0x00, sizeof(custom_ie)); + if (beacon_ies && beacon_ies_len) { + /* set the beacon ies */ + /* we need remove vendor IE from beacon tail, vendor/wpa + * IE will be configure through beacon_vendor_index + */ + beacon_ies_data->ie_index = beacon_index; + beacon_ies_data->mgmt_subtype_mask = + MGMT_MASK_BEACON | MGMT_MASK_ASSOC_RESP | + MGMT_MASK_PROBE_RESP; + beacon_ies_data->ie_length = woal_filter_beacon_ies( + priv, beacon_ies, beacon_ies_len, + beacon_ies_data->ie_buffer, MAX_IE_SIZE, + IE_MASK_WPS | IE_MASK_WFD | IE_MASK_P2P | + IE_MASK_VENDOR, + proberesp_ies, proberesp_ies_len); + if (beacon_ies_data->ie_length) + DBG_HEXDUMP(MCMD_D, "beacon ie", + beacon_ies_data->ie_buffer, + beacon_ies_data->ie_length); + else { + kfree(beacon_ies_data); + beacon_ies_data = NULL; + } + } else { + /* clear the beacon ies */ + if (beacon_index > MAX_MGMT_IE_INDEX) { + PRINTM(MINFO, + "Invalid beacon index for mgmt frame ie.\n"); + goto done; + } + + beacon_ies_data->ie_index = beacon_index; + beacon_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (proberesp_ies_data) { + if (proberesp_ies && proberesp_ies_len) { + /* set the probe response p2p ies */ + proberesp_ies_data->ie_index = proberesp_p2p_index; + proberesp_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_RESP; + proberesp_ies_data->ie_length = woal_get_specific_ie( + proberesp_ies, proberesp_ies_len, + proberesp_ies_data->ie_buffer, MAX_IE_SIZE, + IE_MASK_P2P); + DBG_HEXDUMP(MCMD_D, "proberesp p2p ie", + proberesp_ies_data->ie_buffer, + proberesp_ies_data->ie_length); + } else if (proberesp_p2p_index != + MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* clear the probe response p2p ies */ + if (proberesp_p2p_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid proberesp_p2p_index for mgmt frame ie.\n"); + goto done; + } + proberesp_ies_data->ie_index = proberesp_p2p_index; + proberesp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + proberesp_ies_data->ie_length = 0; + proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if ((proberesp_ies && proberesp_ies_len && + proberesp_ies_data->ie_length) || + (proberesp_ies_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK)) { + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie( + priv, NULL, &beacon_index, + proberesp_ies_data, &proberesp_p2p_index, + NULL, &assocresp_index, NULL, + &probereq_index, wait_option)) { + PRINTM(MERROR, + "Fail to set proberesp p2p IE\n"); + ret = -EFAULT; + goto done; + } + priv->proberesp_p2p_index = proberesp_p2p_index; + PRINTM(MCMND, "proberesp_p2p=0x%x len=%d\n", + proberesp_p2p_index, + proberesp_ies_data->ie_length); + } + memset(proberesp_ies_data, 0x00, sizeof(custom_ie)); + if (proberesp_ies && proberesp_ies_len) { + /* set the probe response ies */ + proberesp_ies_data->ie_index = proberesp_index; + proberesp_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_RESP; + if (proberesp_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) + proberesp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + proberesp_ies_data->ie_length = woal_filter_beacon_ies( + priv, proberesp_ies, proberesp_ies_len, + proberesp_ies_data->ie_buffer, MAX_IE_SIZE, + IE_MASK_P2P | IE_MASK_VENDOR, NULL, 0); + if (proberesp_ies_data->ie_length) { + DBG_HEXDUMP(MCMD_D, "proberesp ie", + proberesp_ies_data->ie_buffer, + proberesp_ies_data->ie_length); + } else { + kfree(proberesp_ies_data); + proberesp_ies_data = NULL; + } + } else { + /* clear the probe response ies */ + if (proberesp_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid probe resp index for mgmt frame ie.\n"); + goto done; + } + proberesp_ies_data->ie_index = proberesp_index; + proberesp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + proberesp_ies_data->ie_length = 0; + proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + if (assocresp_ies_data) { + if (assocresp_ies && assocresp_ies_len) { + /* set the assoc response ies */ + assocresp_ies_data->ie_index = assocresp_index; + assocresp_ies_data->mgmt_subtype_mask = + MGMT_MASK_ASSOC_RESP; + if (assocresp_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) + assocresp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + if (assocresp_ies_len > MAX_IE_SIZE) { + PRINTM(MERROR, + "IE too big, assocresp_ies_len=%d\n", + (int)assocresp_ies_len); + goto done; + } + assocresp_ies_data->ie_length = assocresp_ies_len; + pos = assocresp_ies_data->ie_buffer; + moal_memcpy_ext(priv->phandle, pos, assocresp_ies, + assocresp_ies_len, MAX_IE_SIZE); + DBG_HEXDUMP(MCMD_D, "assocresp ie", + assocresp_ies_data->ie_buffer, + assocresp_ies_data->ie_length); + } else { + /* clear the assoc response ies */ + if (assocresp_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid assoc resp index for mgmt frame ie.\n"); + goto done; + } + + assocresp_ies_data->ie_index = assocresp_index; + assocresp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + assocresp_ies_data->ie_length = 0; + assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (probereq_ies_data) { + if (probereq_ies && probereq_ies_len) { + /* set the probe req ies */ + probereq_ies_data->ie_index = probereq_index; + probereq_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_REQ; +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + /* filter out P2P/WFD ie/EXT_CAP ie */ + probereq_ies_data->ie_length = + woal_filter_beacon_ies( + priv, probereq_ies, + probereq_ies_len, + probereq_ies_data->ie_buffer, + MAX_IE_SIZE, + IE_MASK_P2P | IE_MASK_WFD | + IE_MASK_EXTCAP, + NULL, 0); + } else { +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + if (probereq_ies_len > MAX_IE_SIZE) { + PRINTM(MERROR, + "IE too big, probereq_ies_len=%d\n", + (int)probereq_ies_len); + goto done; + } + probereq_ies_data->ie_length = probereq_ies_len; + pos = probereq_ies_data->ie_buffer; + moal_memcpy_ext(priv->phandle, pos, + probereq_ies, probereq_ies_len, + MAX_IE_SIZE); +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + } +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + if (probereq_ies_data->ie_length) + DBG_HEXDUMP(MCMD_D, "probereq ie", + probereq_ies_data->ie_buffer, + probereq_ies_data->ie_length); + else { + kfree(probereq_ies_data); + probereq_ies_data = NULL; + } + } else { + /* clear the probe req ies */ + if (probereq_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid probe req index for mgmt frame ie.\n"); + goto done; + } + probereq_ies_data->ie_index = probereq_index; + probereq_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + probereq_ies_data->ie_length = 0; + probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (beacon_ies_data || proberesp_ies_data || assocresp_ies_data || + probereq_ies_data) { + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie( + priv, beacon_ies_data, &beacon_index, + proberesp_ies_data, &proberesp_index, + assocresp_ies_data, &assocresp_index, + probereq_ies_data, &probereq_index, wait_option)) { + PRINTM(MERROR, + "Fail to set beacon proberesp assoc probereq IES\n"); + ret = -EFAULT; + goto done; + } + } + if (beacon_ies_data) { + priv->beacon_index = beacon_index; + PRINTM(MCMND, "beacon ie length = %d\n", + beacon_ies_data->ie_length); + } + if (assocresp_ies_data) { + priv->assocresp_index = assocresp_index; + PRINTM(MCMND, "assocresp ie length = %d\n", + assocresp_ies_data->ie_length); + } + if (proberesp_ies_data) { + priv->proberesp_index = proberesp_index; + PRINTM(MCMND, "proberesp ie length = %d\n", + proberesp_ies_data->ie_length); + } + if (probereq_ies_data) { + priv->probereq_index = probereq_index; + PRINTM(MCMND, "probereq ie length = %d\n", + probereq_ies_data->ie_length); + } + PRINTM(MCMND, "beacon=%x assocresp=%x proberesp=%x probereq=%x\n", + beacon_index, assocresp_index, proberesp_index, probereq_index); +done: + kfree(beacon_ies_data); + kfree(proberesp_ies_data); + kfree(assocresp_ies_data); + kfree(probereq_ies_data); + + LEAVE(); + + return ret; +} + +/** + * @brief Sets up the CFG802.11 specific HT capability fields + * with default values + * + * @param ht_info A pointer to ieee80211_sta_ht_cap structure + * @param dev_cap Device capability information + * @param mcs_set Device MCS sets + * + * @return N/A + */ +void woal_cfg80211_setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info, + t_u32 dev_cap, t_u8 *mcs_set) +{ + ENTER(); + + ht_info->ht_supported = true; + ht_info->ampdu_factor = 0x3; + ht_info->ampdu_density = 0; + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + ht_info->cap = 0; + if (mcs_set) + moal_memcpy_ext(NULL, ht_info->mcs.rx_mask, mcs_set, + sizeof(ht_info->mcs.rx_mask), + sizeof(ht_info->mcs.rx_mask)); + if (dev_cap & MBIT(8)) /* 40Mhz intolarance enabled */ + ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; + if (dev_cap & MBIT(17)) /* Channel width 20/40Mhz support */ + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + if ((dev_cap >> 20) & 0x03) /* Delayed ACK supported */ + ht_info->cap |= IEEE80211_HT_CAP_DELAY_BA; + if (dev_cap & MBIT(22)) /* Rx LDPC supported */ + ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; + if (dev_cap & MBIT(23)) /* Short GI @ 20Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + if (dev_cap & MBIT(24)) /* Short GI @ 40Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + if (dev_cap & MBIT(25)) /* Tx STBC supported */ + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + if (dev_cap & MBIT(26)) /* Rx STBC supported */ + ht_info->cap |= IEEE80211_HT_CAP_RX_STBC; + if (dev_cap & MBIT(27)) /* MIMO PS supported */ + ht_info->cap |= 0; /* WLAN_HT_CAP_SM_PS_STATIC */ + else /* Disable HT SM PS */ + ht_info->cap |= IEEE80211_HT_CAP_SM_PS; + if (dev_cap & MBIT(29)) /* Green field supported */ + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + if (dev_cap & MBIT(31)) /* MAX AMSDU supported */ + ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; + /* DSSS/CCK in 40Mhz supported*/ + ht_info->cap |= IEEE80211_HT_CAP_DSSSCCK40; + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + LEAVE(); +} + +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE +/** + * @brief Sets up the CFG802.11 specific VHT capability fields + * with default values + * + * @param priv A pointer to moal private structure + * @param vht_cap A pointer to ieee80211_sta_vht_cap structure + * + * @return N/A + */ +void woal_cfg80211_setup_vht_cap(moal_private *priv, + struct ieee80211_sta_vht_cap *vht_cap) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11ac_cfg *cfg_11ac = NULL; + mlan_status status; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ac_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "Fail to allocate buf for setup vht_cap\n"); + goto done; + } + cfg_11ac = (mlan_ds_11ac_cfg *)req->pbuf; + cfg_11ac->sub_command = MLAN_OID_11AC_VHT_CFG; + req->req_id = MLAN_IOCTL_11AC_CFG; + req->action = MLAN_ACT_GET; + cfg_11ac->param.vht_cfg.band = BAND_SELECT_A; + cfg_11ac->param.vht_cfg.txrx = MLAN_RADIO_RX; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Fail to get vht_cfg\n"); + goto done; + } + vht_cap->vht_supported = true; + vht_cap->cap = cfg_11ac->param.vht_cfg.vht_cap_info; + vht_cap->vht_mcs.rx_mcs_map = (t_u16)cfg_11ac->param.vht_cfg.vht_rx_mcs; + vht_cap->vht_mcs.rx_highest = + (t_u16)cfg_11ac->param.vht_cfg.vht_rx_max_rate; + vht_cap->vht_mcs.tx_mcs_map = (t_u16)cfg_11ac->param.vht_cfg.vht_tx_mcs; + vht_cap->vht_mcs.tx_highest = + (t_u16)cfg_11ac->param.vht_cfg.vht_tx_max_rate; + PRINTM(MCMND, + "vht_cap=0x%x rx_mcs_map=0x%x rx_max=0x%x tx_mcs_map=0x%x tx_max=0x%x\n", + vht_cap->cap, vht_cap->vht_mcs.rx_mcs_map, + vht_cap->vht_mcs.rx_highest, vht_cap->vht_mcs.tx_mcs_map, + vht_cap->vht_mcs.tx_highest); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); +} +#endif + +#if KERNEL_VERSION(4, 20, 0) <= CFG80211_VERSION_CODE +/* +=============== +11AX CAP for uAP +=============== +Note: bits not mentioned below are set to 0. + +5G +=== +HE MAC Cap: +Bit0: 1 (+HTC HE Support) +Bit25: 1 (OM Control Support. But uAP does not support + Tx OM received from the STA, as it does not support UL OFDMA) + +HE PHY Cap: +Bit1-7: 0x2 (Supported Channel Width Set. + Note it would be changed after 80+80 MHz is supported) +Bit8-11: 0x3 (Punctured Preamble Rx. + Note: it would be changed after 80+80 MHz is supported) +Bit12: 0x0 (Device Class) +Bit13: 0x1 (LDPC coding in Payload) +Bit17: 0x1 (NDP with 4xHE-LTF+3.2usGI) +Bit18: 0x1 (STBC Tx <= 80 MHz) +Bit19: 0x1 (STBC Rx <= 80 MHz) +Bit20: 0x1 (Doppler Tx) +Bit21: 0x1 (Doppler Rx) +Bit27-28: 0x1 (DCM Max Constellation Rx) +Bit31: 0x1 (SU Beamformer) +Bit32: 0x1 (SU BeamFormee) +Bit34-36: 0x7 (Beamformee STS <= 80 MHz) +Bit40-42: 0x1 (Number of Sounding Dimentions <= 80 MHz) +Bit53: 0x1 (Partial Bandwidth Extended Range) +Bit55: 0x1 (PPE Threshold Present. + Note: PPE threshold may have some changes later) +Bit58: 0x1 (HE SU PPDU and HE MU PPDU with 4xHE-LTF+0.8usGI) +Bit59-61: 0x1 (Max Nc) +Bit75: 0x1 (Rx 1024-QAM Support < 242-tone RU) +*/ + +#define UAP_HE_MAC_CAP0_MASK 0x00 +#define UAP_HE_MAC_CAP1_MASK 0x00 +#define UAP_HE_MAC_CAP2_MASK 0x00 +#define UAP_HE_MAC_CAP3_MASK 0x00 +#define UAP_HE_MAC_CAP4_MASK 0x02 +#define UAP_HE_MAC_CAP5_MASK 0x00 +#define UAP_HE_PHY_CAP0_MASK 0x04 +#define UAP_HE_PHY_CAP1_MASK 0x23 +#define UAP_HE_PHY_CAP2_MASK 0x3E +#define UAP_HE_PHY_CAP3_MASK 0x88 +#define UAP_HE_PHY_CAP4_MASK 0x1D +#define UAP_HE_PHY_CAP5_MASK 0x01 +#define UAP_HE_PHY_CAP6_MASK 0xA0 +#define UAP_HE_PHY_CAP7_MASK 0x0C +#define UAP_HE_PHY_CAP8_MASK 0x00 +#define UAP_HE_PHY_CAP9_MASK 0x08 +#define UAP_HE_PHY_CAP10_MASK 0x00 + +/* +2G +=== +HE MAC Cap: +Bit0: 1 (+HTC HE Support) +Bit25: 1 (OM Control Support. Note: uAP does not support + Tx OM received from the STA, as it does not support UL OFDMA) + +HE PHY Cap: +Bit1-7: 0x1 (Supported Channel Width Set) +Bit8-11: 0x0 (Punctured Preamble Rx) +Bit12: 0x0 (Device Class) +Bit13: 0x1 (LDPC coding in Payload) +Bit17: 0x1 (NDP with 4xLTF+3.2usGI) +Bit18: 0x1 (STBC Tx <= 80 MHz) +Bit19: 0x1 (STBC Rx <= 80 MHz) +Bit20: 0x1 (Doppler Tx) +Bit21: 0x1 (Doppler Rx) +Bit27-28: 0x1 (DCM Max Constellation Rx) +Bit31: 0x1 (SU Beamformer) +Bit32: 0x1 (SU BeamFormee) +Bit34-36: 0x7 (Beamformee STS <= 80 MHz) +Bit40-42: 0x1 (Number of Sounding Dimentions <= 80 MHz) +Bit53: 0x1 (Partial Bandwidth Extended Range) +Bit55: 0x1 (PPE Threshold Present. + Note: PPE threshold may have some changes later) +Bit58: 0x1 (HE SU PPDU and HE MU PPDU with 4xHE-LTF+0.8usGI) +Bit59-61: 0x1 (Max Nc) +Bit75: 0x1 (Rx 1024-QAM Support < 242-tone RU) +*/ +#define UAP_HE_2G_MAC_CAP0_MASK 0x00 +#define UAP_HE_2G_MAC_CAP1_MASK 0x00 +#define UAP_HE_2G_MAC_CAP2_MASK 0x00 +#define UAP_HE_2G_MAC_CAP3_MASK 0x00 +#define UAP_HE_2G_MAC_CAP4_MASK 0x02 +#define UAP_HE_2G_MAC_CAP5_MASK 0x00 +#define UAP_HE_2G_PHY_CAP0_MASK 0x04 +#define UAP_HE_2G_PHY_CAP1_MASK 0x20 +#define UAP_HE_2G_PHY_CAP2_MASK 0x3E +#define UAP_HE_2G_PHY_CAP3_MASK 0x88 +#define UAP_HE_2G_PHY_CAP4_MASK 0x1D +#define UAP_HE_2G_PHY_CAP5_MASK 0x01 +#define UAP_HE_2G_PHY_CAP6_MASK 0xA0 +#define UAP_HE_2G_PHY_CAP7_MASK 0x0C +#define UAP_HE_2G_PHY_CAP8_MASK 0x00 +#define UAP_HE_2G_PHY_CAP9_MASK 0x08 +#define UAP_HE_2G_PHY_CAP10_MASK 0x00 + +/** + * @brief update 11ax ie for AP mode * + * @param band band config + * @hecap_ie a pointer to mlan_ds_11ax_he_capa + * + * @return 0--success, otherwise failure + */ +void woal_uap_update_11ax_ie(t_u8 band, mlan_ds_11ax_he_capa *hecap_ie) +{ + if (band == BAND_5GHZ) { + hecap_ie->he_mac_cap[0] &= UAP_HE_MAC_CAP0_MASK; + hecap_ie->he_mac_cap[1] &= UAP_HE_MAC_CAP1_MASK; + hecap_ie->he_mac_cap[2] &= UAP_HE_MAC_CAP2_MASK; + hecap_ie->he_mac_cap[3] &= UAP_HE_MAC_CAP3_MASK; + hecap_ie->he_mac_cap[4] &= UAP_HE_MAC_CAP4_MASK; + hecap_ie->he_mac_cap[5] &= UAP_HE_MAC_CAP5_MASK; + hecap_ie->he_phy_cap[0] &= UAP_HE_PHY_CAP0_MASK; + hecap_ie->he_phy_cap[1] &= UAP_HE_PHY_CAP1_MASK; + hecap_ie->he_phy_cap[2] &= UAP_HE_PHY_CAP2_MASK; + hecap_ie->he_phy_cap[3] &= UAP_HE_PHY_CAP3_MASK; + hecap_ie->he_phy_cap[4] &= UAP_HE_PHY_CAP4_MASK; + hecap_ie->he_phy_cap[5] &= UAP_HE_PHY_CAP5_MASK; + hecap_ie->he_phy_cap[6] &= UAP_HE_PHY_CAP6_MASK; + hecap_ie->he_phy_cap[7] &= UAP_HE_PHY_CAP7_MASK; + hecap_ie->he_phy_cap[8] &= UAP_HE_PHY_CAP8_MASK; + hecap_ie->he_phy_cap[9] &= UAP_HE_PHY_CAP9_MASK; + hecap_ie->he_phy_cap[10] &= UAP_HE_PHY_CAP10_MASK; + } else { + hecap_ie->he_mac_cap[0] &= UAP_HE_2G_MAC_CAP0_MASK; + hecap_ie->he_mac_cap[1] &= UAP_HE_2G_MAC_CAP1_MASK; + hecap_ie->he_mac_cap[2] &= UAP_HE_2G_MAC_CAP2_MASK; + hecap_ie->he_mac_cap[3] &= UAP_HE_2G_MAC_CAP3_MASK; + hecap_ie->he_mac_cap[4] &= UAP_HE_2G_MAC_CAP4_MASK; + hecap_ie->he_mac_cap[5] &= UAP_HE_2G_MAC_CAP5_MASK; + hecap_ie->he_phy_cap[0] &= UAP_HE_2G_PHY_CAP0_MASK; + hecap_ie->he_phy_cap[1] &= UAP_HE_2G_PHY_CAP1_MASK; + hecap_ie->he_phy_cap[2] &= UAP_HE_2G_PHY_CAP2_MASK; + hecap_ie->he_phy_cap[3] &= UAP_HE_2G_PHY_CAP3_MASK; + hecap_ie->he_phy_cap[4] &= UAP_HE_2G_PHY_CAP4_MASK; + hecap_ie->he_phy_cap[5] &= UAP_HE_2G_PHY_CAP5_MASK; + hecap_ie->he_phy_cap[6] &= UAP_HE_2G_PHY_CAP6_MASK; + hecap_ie->he_phy_cap[7] &= UAP_HE_2G_PHY_CAP7_MASK; + hecap_ie->he_phy_cap[8] &= UAP_HE_2G_PHY_CAP8_MASK; + hecap_ie->he_phy_cap[9] &= UAP_HE_2G_PHY_CAP9_MASK; + hecap_ie->he_phy_cap[10] &= UAP_HE_2G_PHY_CAP10_MASK; + } + return; +} + +/** + * @brief Sets up the CFG802.11 specific HE capability fields * with default + * values + * + * @param priv A pointer to moal private structure + * @param iftype_data A pointer to ieee80211_sband_iftype_data structure + * + * @return N/A + */ +void woal_cfg80211_setup_he_cap(moal_private *priv, + struct ieee80211_supported_band *band) +{ + mlan_fw_info fw_info; + struct ieee80211_sband_iftype_data *iftype_data = NULL; + t_u8 extra_mcs_size = 0; + int ppe_threshold_len = 0; + mlan_ds_11ax_he_capa *phe_cap = NULL; + t_u8 hw_hecap_len; + + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if (band->band == NL80211_BAND_5GHZ) { + phe_cap = (mlan_ds_11ax_he_capa *)fw_info.hw_he_cap; + hw_hecap_len = fw_info.hw_hecap_len; + woal_uap_update_11ax_ie(BAND_5GHZ, phe_cap); + } else { + phe_cap = (mlan_ds_11ax_he_capa *)fw_info.hw_2g_he_cap; + hw_hecap_len = fw_info.hw_2g_hecap_len; + woal_uap_update_11ax_ie(BAND_2GHZ, phe_cap); + } + + if (!hw_hecap_len) + return; + DBG_HEXDUMP(MCMD_D, "Setup HECAP", (u8 *)phe_cap, hw_hecap_len); + iftype_data = + kmalloc(sizeof(struct ieee80211_sband_iftype_data), GFP_KERNEL); + if (!iftype_data) { + PRINTM(MERROR, "Fail to allocate iftype data\n"); + goto done; + } + iftype_data->types_mask = + MBIT(NL80211_IFTYPE_STATION) | MBIT(NL80211_IFTYPE_AP) | + MBIT(NL80211_IFTYPE_P2P_CLIENT) | MBIT(NL80211_IFTYPE_P2P_GO); + iftype_data->he_cap.has_he = true; + moal_memcpy_ext(priv->phandle, + iftype_data->he_cap.he_cap_elem.mac_cap_info, + phe_cap->he_mac_cap, sizeof(phe_cap->he_mac_cap), + sizeof(iftype_data->he_cap.he_cap_elem.mac_cap_info)); + moal_memcpy_ext(priv->phandle, + iftype_data->he_cap.he_cap_elem.phy_cap_info, + phe_cap->he_phy_cap, sizeof(phe_cap->he_phy_cap), + sizeof(iftype_data->he_cap.he_cap_elem.phy_cap_info)); + memset(&iftype_data->he_cap.he_mcs_nss_supp, 0xff, + sizeof(struct ieee80211_he_mcs_nss_supp)); + moal_memcpy_ext(priv->phandle, &iftype_data->he_cap.he_mcs_nss_supp, + phe_cap->he_txrx_mcs_support, + sizeof(phe_cap->he_txrx_mcs_support), + sizeof(struct ieee80211_he_mcs_nss_supp)); + // Support 160Mhz + if (phe_cap->he_phy_cap[0] & MBIT(3)) + extra_mcs_size += 4; + + // Support 80+80 + if (phe_cap->he_phy_cap[0] & MBIT(4)) + extra_mcs_size += 4; + if (extra_mcs_size) + moal_memcpy_ext( + priv->phandle, + (t_u8 *)&iftype_data->he_cap.he_mcs_nss_supp.rx_mcs_160, + phe_cap->val, extra_mcs_size, + sizeof(struct ieee80211_he_mcs_nss_supp) - 4); + +#define HE_CAP_FIX_SIZE 22 + // Support PPE threshold + ppe_threshold_len = phe_cap->len - HE_CAP_FIX_SIZE - extra_mcs_size; + if (phe_cap->he_phy_cap[6] & MBIT(7) && ppe_threshold_len) { + moal_memcpy_ext(priv->phandle, iftype_data->he_cap.ppe_thres, + &phe_cap->val[extra_mcs_size], + ppe_threshold_len, + sizeof(iftype_data->he_cap.ppe_thres)); + } else { + iftype_data->he_cap.he_cap_elem.phy_cap_info[6] &= ~MBIT(7); + PRINTM(MCMND, "Clear PPE threshold 0x%x\n", + iftype_data->he_cap.he_cap_elem.phy_cap_info[7]); + } + band->n_iftype_data = 1; + band->iftype_data = iftype_data; +done: + LEAVE(); +} + +/** + * @brief free iftype_data + * + * @param wiphy A pointer to struct wiphy + * + * + * @return N/A + */ +void woal_cfg80211_free_iftype_data(struct wiphy *wiphy) +{ + enum nl80211_band band; + + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; ++band) { + if (!wiphy->bands[band]) + continue; + if (!wiphy->bands[band]->iftype_data) + continue; + kfree(wiphy->bands[band]->iftype_data); + wiphy->bands[band]->n_iftype_data = 0; + } +} +#endif + +/* + * @brief prepare and send fake deauth packet to cfg80211 to + * notify wpa_supplicant about disconnection + * + * + * @param priv A pointer moal_private structure + * @param reason_code disconnect reason code + * + * @return N/A + */ +void woal_deauth_event(moal_private *priv, int reason_code) +{ + struct woal_event *evt; + unsigned long flags; + moal_handle *handle = priv->phandle; + + evt = kzalloc(sizeof(struct woal_event), GFP_ATOMIC); + if (!evt) { + PRINTM(MERROR, "Fail to alloc memory for deauth event\n"); + LEAVE(); + return; + } + evt->priv = priv; + evt->type = WOAL_EVENT_DEAUTH; + evt->reason_code = reason_code; + INIT_LIST_HEAD(&evt->link); + spin_lock_irqsave(&handle->evt_lock, flags); + list_add_tail(&evt->link, &handle->evt_queue); + spin_unlock_irqrestore(&handle->evt_lock, flags); + queue_work(handle->evt_workqueue, &handle->evt_work); +} + +#ifdef STA_CFG80211 +#if KERNEL_VERSION(3, 2, 0) <= CFG80211_VERSION_CODE +/** + * @brief prepare woal_bgscan_stop event + * + * @param priv A pointer moal_private structure + * @param pchan_info A pointer to chan_band structure + * + * @return N/A + */ +void woal_bgscan_stop_event(moal_private *priv) +{ + struct woal_event *evt; + unsigned long flags; + moal_handle *handle = priv->phandle; + + evt = kzalloc(sizeof(struct woal_event), GFP_ATOMIC); + if (evt) { + evt->priv = priv; + evt->type = WOAL_EVENT_BGSCAN_STOP; + INIT_LIST_HEAD(&evt->link); + spin_lock_irqsave(&handle->evt_lock, flags); + list_add_tail(&evt->link, &handle->evt_queue); + spin_unlock_irqrestore(&handle->evt_lock, flags); + queue_work(handle->evt_workqueue, &handle->evt_work); + } +} + +/** + * @brief Notify cfg80211 schedule scan stopped + * + * @param priv A pointer moal_private structure + * + * @return N/A + */ +void woal_cfg80211_notify_sched_scan_stop(moal_private *priv) +{ + cfg80211_sched_scan_stopped(priv->wdev->wiphy +#if KERNEL_VERSION(4, 12, 0) <= CFG80211_VERSION_CODE + , + 0 +#endif + ); + priv->sched_scanning = MFALSE; + PRINTM(MEVENT, "Sched_Scan stopped\n"); +} +#endif +#endif + +#if KERNEL_VERSION(3, 5, 0) <= CFG80211_VERSION_CODE +/** + * @brief Handle woal_channel_switch event + * + * @param priv A pointer moal_private structure + * @param pchan_info A pointer to chan_band structure + * + * @return N/A + */ +void woal_channel_switch_event(moal_private *priv, chan_band_info *pchan_info) +{ + struct woal_event *evt; + unsigned long flags; + moal_handle *handle = priv->phandle; + + evt = kzalloc(sizeof(struct woal_event), GFP_ATOMIC); + if (evt) { + evt->priv = priv; + evt->type = WOAL_EVENT_CHAN_SWITCH; + moal_memcpy_ext(priv->phandle, &evt->chan_info, pchan_info, + sizeof(chan_band_info), sizeof(chan_band_info)); + INIT_LIST_HEAD(&evt->link); + spin_lock_irqsave(&handle->evt_lock, flags); + list_add_tail(&evt->link, &handle->evt_queue); + spin_unlock_irqrestore(&handle->evt_lock, flags); + queue_work(handle->evt_workqueue, &handle->evt_work); + } +} + +/** + * @brief Notify cfg80211 supplicant channel changed + * + * @param priv A pointer moal_private structure + * @param pchan_info A pointer to chan_band structure + * + * @return N/A + */ +void woal_cfg80211_notify_channel(moal_private *priv, + chan_band_info *pchan_info) +{ +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE + struct cfg80211_chan_def chandef; +#else +#if KERNEL_VERSION(3, 5, 0) <= CFG80211_VERSION_CODE + enum nl80211_channel_type type; + enum ieee80211_band band; + int freq = 0; +#endif +#endif + ENTER(); + +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE + if (MLAN_STATUS_SUCCESS == + woal_chandef_create(priv, &chandef, pchan_info)) { + cfg80211_ch_switch_notify(priv->netdev, &chandef); + priv->channel = pchan_info->channel; +#ifdef UAP_CFG80211 + moal_memcpy_ext(priv->phandle, &priv->chan, &chandef, + sizeof(struct cfg80211_chan_def), + sizeof(struct cfg80211_chan_def)); +#endif + } +#else +#if KERNEL_VERSION(3, 5, 0) <= CFG80211_VERSION_CODE + if (pchan_info->bandcfg.chanBand == BAND_2GHZ) + band = IEEE80211_BAND_2GHZ; + else if (pchan_info->bandcfg.chanBand == BAND_5GHZ) + band = IEEE80211_BAND_5GHZ; + else { + LEAVE(); + return; + } + priv->channel = pchan_info->channel; + freq = ieee80211_channel_to_frequency(pchan_info->channel, band); + switch (pchan_info->bandcfg.chanWidth) { + case CHAN_BW_20MHZ: + if (pchan_info->is_11n_enabled) + type = NL80211_CHAN_HT20; + else + type = NL80211_CHAN_NO_HT; + break; + default: + if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_ABOVE) + type = NL80211_CHAN_HT40PLUS; + else if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_BELOW) + type = NL80211_CHAN_HT40MINUS; + else + type = NL80211_CHAN_HT20; + break; + } + cfg80211_ch_switch_notify(priv->netdev, freq, type); +#endif +#endif + LEAVE(); +} +#endif + +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE +/** + * @brief create cfg80211_chan_def structure based on chan_band info + * + * @param priv A pointer moal_private structure + * @param chandef A pointer to cfg80211_chan_def structure + * @param pchan_info A pointer to chan_band_info structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE + */ +mlan_status woal_chandef_create(moal_private *priv, + struct cfg80211_chan_def *chandef, + chan_band_info *pchan_info) +{ + enum ieee80211_band band = IEEE80211_BAND_2GHZ; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(chandef, 0, sizeof(struct cfg80211_chan_def)); + chandef->center_freq2 = 0; + if (pchan_info->bandcfg.chanBand == BAND_2GHZ) + band = IEEE80211_BAND_2GHZ; + else if (pchan_info->bandcfg.chanBand == BAND_5GHZ) + band = IEEE80211_BAND_5GHZ; + chandef->chan = ieee80211_get_channel( + priv->wdev->wiphy, + ieee80211_channel_to_frequency(pchan_info->channel, band)); + if (chandef->chan == NULL) { + PRINTM(MERROR, + "Fail on ieee80211_get_channel, channel=%d, band=%d\n", + pchan_info->channel, band); + status = MLAN_STATUS_FAILURE; + goto done; + } + switch (pchan_info->bandcfg.chanWidth) { + case CHAN_BW_20MHZ: + if (pchan_info->is_11n_enabled) + chandef->width = NL80211_CHAN_WIDTH_20; + else + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = chandef->chan->center_freq; + break; + case CHAN_BW_40MHZ: + chandef->width = NL80211_CHAN_WIDTH_40; + if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_ABOVE) + chandef->center_freq1 = chandef->chan->center_freq + 10; + else if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_BELOW) + chandef->center_freq1 = chandef->chan->center_freq - 10; + break; + case CHAN_BW_80MHZ: + chandef->width = NL80211_CHAN_WIDTH_80; + chandef->center_freq1 = ieee80211_channel_to_frequency( + pchan_info->center_chan, band); + break; + default: + break; + } +done: + LEAVE(); + return status; +} +#endif diff --git a/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.h b/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.h new file mode 100644 index 0000000..f68e50c --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.h @@ -0,0 +1,478 @@ +/** @file moal_cfg80211.h + * + * @brief This file contains the CFG80211 specific defines. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _MOAL_CFG80211_H_ +#define _MOAL_CFG80211_H_ + +#include "moal_main.h" + +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE +#define IEEE80211_CHAN_PASSIVE_SCAN IEEE80211_CHAN_NO_IR +#define IEEE80211_CHAN_NO_IBSS IEEE80211_CHAN_NO_IR +#endif + +#if KERNEL_VERSION(3, 16, 0) <= CFG80211_VERSION_CODE +#define MAX_CSA_COUNTERS_NUM 2 +#endif + +/* Clear all key indexes */ +#define KEY_INDEX_CLEAR_ALL (0x0000000F) + +/** RTS/FRAG disabled value */ +#define MLAN_FRAG_RTS_DISABLED (0xFFFFFFFF) + +#ifndef WLAN_CIPHER_SUITE_SMS4 +#define WLAN_CIPHER_SUITE_SMS4 0x00000020 +#endif + +#ifndef WLAN_CIPHER_SUITE_AES_CMAC +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 +#endif +#if KERNEL_VERSION(4, 0, 0) <= CFG80211_VERSION_CODE +#ifndef WLAN_CIPHER_SUITE_BIP_GMAC_256 +#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C +#endif +#endif + +/* define for custom ie operation */ +#define MLAN_CUSTOM_IE_AUTO_IDX_MASK 0xffff +#define MLAN_CUSTOM_IE_NEW_MASK 0x8000 +#define IE_MASK_WPS 0x0001 +#define IE_MASK_P2P 0x0002 +#define IE_MASK_WFD 0x0004 +#define IE_MASK_VENDOR 0x0008 +#define IE_MASK_EXTCAP 0x0010 + +#define MRVL_PKT_TYPE_MGMT_FRAME 0xE5 + +/** + * If multiple wiphys are registered e.g. a regular netdev with + * assigned ieee80211_ptr and you won't know whether it points + * to a wiphy your driver has registered or not. Assign this to + * something global to your driver to help determine whether + * you own this wiphy or not. + */ +static const void *const mrvl_wiphy_privid = &mrvl_wiphy_privid; + +/* Get the private structure from wiphy */ +void *woal_get_wiphy_priv(struct wiphy *wiphy); + +/* Get the private structure from net device */ +void *woal_get_netdev_priv(struct net_device *dev); +#ifdef STA_SUPPORT +/** get scan interface */ +pmoal_private woal_get_scan_interface(pmoal_handle handle); +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE +/** AUTH pending flag */ +#define HOST_MLME_AUTH_PENDING MBIT(0) +/** AUTH complete flag */ +#define HOST_MLME_AUTH_DONE MBIT(1) +#define HOST_MLME_ASSOC_PENDING MBIT(2) +#define HOST_MLME_ASSOC_DONE MBIT(3) +void woal_host_mlme_disconnect(pmoal_private priv, u16 reason_code, u8 *sa); +void woal_host_mlme_work_queue(struct work_struct *work); +void woal_host_mlme_process_assoc_resp(moal_private *priv, + mlan_ds_misc_assoc_rsp *assoc_rsp); +#endif +#endif + +t_u8 woal_band_cfg_to_ieee_band(t_u32 band); + +int woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, +#if KERNEL_VERSION(4, 12, 0) > CFG80211_VERSION_CODE + u32 *flags, +#endif + struct vif_params *params); + +int woal_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed); + +int woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev, + t_u8 key_index, +#if KERNEL_VERSION(2, 6, 36) < CFG80211_VERSION_CODE + 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 KERNEL_VERSION(2, 6, 36) < CFG80211_VERSION_CODE + bool pairwise, +#endif + const t_u8 *mac_addr); +#ifdef STA_SUPPORT +/** Opportunistic Key Caching APIs support */ +struct pmksa_entry *woal_get_pmksa_entry(pmoal_private priv, const u8 *bssid); + +int woal_flush_pmksa_list(moal_private *priv); + +int woal_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); + +int woal_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa); + +int woal_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev); +#endif + +int woal_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask); +#if KERNEL_VERSION(2, 6, 38) <= CFG80211_VERSION_CODE +int woal_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant); +int woal_cfg80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant); +#endif + +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE +int woal_cfg80211_set_qos_map(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_qos_map *qos_map); +#endif + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT +int woal_set_rf_channel(moal_private *priv, struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + t_u8 wait_option); + +static inline int woal_cfg80211_scan_done(struct cfg80211_scan_request *request, + bool aborted) +{ +#if KERNEL_VERSION(4, 8, 0) <= CFG80211_VERSION_CODE + struct cfg80211_scan_info info; + + info.aborted = aborted; + cfg80211_scan_done(request, &info); +#else + cfg80211_scan_done(request, aborted); +#endif + return 0; +} +mlan_status woal_inform_bss_from_scan_result(moal_private *priv, + pmlan_ssid_bssid ssid_bssid, + t_u8 wait_option); +#endif +#endif + +#if KERNEL_VERSION(3, 5, 0) > CFG80211_VERSION_CODE +int woal_cfg80211_set_channel(struct wiphy *wiphy, +#if KERNEL_VERSION(2, 6, 34) < CFG80211_VERSION_CODE + struct net_device *dev, +#endif + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type); +#endif + +#if KERNEL_VERSION(2, 6, 37) < CFG80211_VERSION_CODE +int woal_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, + t_u8 key_index, bool ucast, bool mcast); +#else +int woal_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *dev, + t_u8 key_index); +#endif + +#if KERNEL_VERSION(2, 6, 30) <= CFG80211_VERSION_CODE +int woal_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *netdev, + t_u8 key_index); +#endif + +#if KERNEL_VERSION(3, 1, 0) <= CFG80211_VERSION_CODE +int woal_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_gtk_rekey_data *data); +#endif +void woal_mgmt_frame_register(moal_private *priv, u16 frame_type, bool reg); +void woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif +#if KERNEL_VERSION(5, 8, 0) <= CFG80211_VERSION_CODE + struct mgmt_frame_regs *upd +#else + t_u16 frame_type, bool reg +#endif +); + +int woal_cfg80211_mgmt_tx(struct wiphy *wiphy, +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE + struct cfg80211_mgmt_tx_params *params, +#else + struct ieee80211_channel *chan, bool offchan, +#if KERNEL_VERSION(3, 8, 0) > CFG80211_VERSION_CODE + enum nl80211_channel_type channel_type, + bool channel_type_valid, +#endif + unsigned int wait, const u8 *buf, size_t len, +#if KERNEL_VERSION(3, 2, 0) <= CFG80211_VERSION_CODE + bool no_cck, +#endif +#if KERNEL_VERSION(3, 3, 0) <= CFG80211_VERSION_CODE + bool dont_wait_for_ack, +#endif +#endif + u64 *cookie); + +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE +void woal_update_radar_chans_dfs_state(struct wiphy *wiphy); +#endif + +mlan_status woal_register_cfg80211(moal_private *priv); + +extern struct ieee80211_supported_band cfg80211_band_2ghz; +extern struct ieee80211_supported_band cfg80211_band_5ghz; +extern struct ieee80211_supported_band mac1_cfg80211_band_2ghz; +extern struct ieee80211_supported_band mac1_cfg80211_band_5ghz; + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +int woal_cfg80211_bss_role_cfg(moal_private *priv, t_u16 action, + t_u8 *bss_role); +#endif +#if KERNEL_VERSION(4, 1, 0) <= CFG80211_VERSION_CODE +struct wireless_dev * +woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, +#if KERNEL_VERSION(4, 12, 0) > CFG80211_VERSION_CODE + u32 *flags, +#endif + struct vif_params *params); +#else +#if KERNEL_VERSION(3, 7, 0) <= CFG80211_VERSION_CODE +struct wireless_dev *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + const char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); +#else +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE +struct wireless_dev *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); +#else +#if KERNEL_VERSION(2, 6, 37) < CFG80211_VERSION_CODE +struct net_device *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); +#else +int woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, char *name, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params); +#endif +#endif +#endif +#endif +int woal_cfg80211_del_virt_if(struct wiphy *wiphy, struct net_device *dev); +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE +int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, + struct wireless_dev *wdev); +#else +int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev); +#endif + +#ifdef WIFI_DIRECT_SUPPORT +/* Group Owner Negotiation Req */ +#define P2P_GO_NEG_REQ 0 +/* Group Owner Negotiation Rsp */ +#define P2P_GO_NEG_RSP 1 +/* Group Owner Negotiation Confirm */ +#define P2P_GO_NEG_CONF 2 +/* P2P Invitation Request */ +#define P2P_INVITE_REQ 3 +/* P2P Invitation Response */ +#define P2P_INVITE_RSP 4 +/* Device Discoverability Request */ +#define P2P_DEVDIS_REQ 5 +/* Device Discoverability Response */ +#define P2P_DEVDIS_RSP 6 +/* Provision Discovery Request */ +#define P2P_PROVDIS_REQ 7 +/* Provision Discovery Response */ +#define P2P_PROVDIS_RSP 8 +/** P2P category */ +#define P2P_ACT_FRAME_CATEGORY 0x04 +/** P2P oui offset */ +#define P2P_ACT_FRAME_OUI_OFFSET 26 +/** P2P subtype offset */ +#define P2P_ACT_FRAME_OUI_SUBTYPE_OFFSET 30 +void woal_cfg80211_display_p2p_actframe(const t_u8 *buf, int len, + struct ieee80211_channel *chan, + const t_u8 flag); + +/** Define kernel version for wifi direct */ +#define WIFI_DIRECT_KERNEL_VERSION KERNEL_VERSION(2, 6, 39) + +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + +int woal_cfg80211_init_p2p_client(moal_private *priv); + +int woal_cfg80211_init_p2p_go(moal_private *priv); + +int woal_cfg80211_deinit_p2p(moal_private *priv); + +void woal_remove_virtual_interface(moal_handle *handle); + +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + +/** Define for remain on channel duration timer */ +#define MAX_REMAIN_ON_CHANNEL_DURATION (1000) + +int woal_cfg80211_remain_on_channel_cfg(moal_private *priv, t_u8 wait_option, + t_u8 remove, t_u8 *status, + struct ieee80211_channel *chan, + enum mlan_channel_type channel_type, + t_u32 duration); + +#ifdef UAP_CFG80211 +int woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, +#if KERNEL_VERSION(3, 16, 0) <= CFG80211_VERSION_CODE + const u8 *mac, +#else + u8 *mac, +#endif + struct station_info *stainfo); + +int woal_uap_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, t_u8 *mac, + struct station_info *sinfo); +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE +int woal_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev, + struct bss_parameters *params); +#endif +#if KERNEL_VERSION(3, 9, 0) <= CFG80211_VERSION_CODE +int woal_cfg80211_set_mac_acl(struct wiphy *wiphy, struct net_device *dev, + const struct cfg80211_acl_data *params); +#endif +#if KERNEL_VERSION(3, 1, 0) <= CFG80211_VERSION_CODE +int woal_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_txq_params *params); +#endif + +#if KERNEL_VERSION(3, 12, 0) <= CFG80211_VERSION_CODE +int woal_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce); +#endif + +#if KERNEL_VERSION(3, 4, 0) <= CFG80211_VERSION_CODE +int woal_cfg80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *params); + +int woal_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *params); +#else +int woal_cfg80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *params); + +int woal_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *params); +#endif + +int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev); +int woal_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, +#if KERNEL_VERSION(3, 19, 0) <= CFG80211_VERSION_CODE + struct station_del_parameters *param); +#else +#if KERNEL_VERSION(3, 16, 0) <= CFG80211_VERSION_CODE + const u8 *mac_addr); +#else + u8 *mac_addr); +#endif +#endif +#if KERNEL_VERSION(3, 12, 0) <= CFG80211_VERSION_CODE +#if KERNEL_VERSION(3, 15, 0) <= CFG80211_VERSION_CODE +int woal_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms); +#else +int woal_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef); +#endif + +int woal_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params); + +void woal_cac_timer_func(void *context); +void woal_csa_work_queue(struct work_struct *work); +#endif +#endif /* UAP_CFG80211 */ +#if defined(UAP_CFG80211) || defined(STA_CFG80211) +#if KERNEL_VERSION(3, 5, 0) <= CFG80211_VERSION_CODE +void woal_cfg80211_notify_channel(moal_private *priv, + pchan_band_info pchan_info); +void woal_channel_switch_event(moal_private *priv, chan_band_info *pchan_info); +#endif +#endif + +#ifdef STA_CFG80211 +#if KERNEL_VERSION(3, 2, 0) <= CFG80211_VERSION_CODE +void woal_bgscan_stop_event(moal_private *priv); +void woal_cfg80211_notify_sched_scan_stop(moal_private *priv); +#endif +#endif + +void woal_deauth_event(moal_private *priv, int reason_code); + +#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE +mlan_status woal_chandef_create(moal_private *priv, + struct cfg80211_chan_def *chandef, + chan_band_info *pchan_info); +#endif + +#if KERNEL_VERSION(4, 20, 0) <= CFG80211_VERSION_CODE +void woal_cfg80211_setup_he_cap(moal_private *priv, + struct ieee80211_supported_band *band); +void woal_cfg80211_free_iftype_data(struct wiphy *wiphy); +#endif + +void woal_clear_all_mgmt_ies(moal_private *priv, t_u8 wait_option); +int woal_cfg80211_mgmt_frame_ie( + moal_private *priv, const t_u8 *beacon_ies, size_t beacon_ies_len, + const t_u8 *proberesp_ies, size_t proberesp_ies_len, + const t_u8 *assocresp_ies, size_t assocresp_ies_len, + const t_u8 *probereq_ies, size_t probereq_ies_len, t_u16 mask, + t_u8 wait_option); + +int woal_get_active_intf_freq(moal_private *priv); + +void woal_cfg80211_setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info, + t_u32 dev_cap, t_u8 *mcs_set); +#if KERNEL_VERSION(3, 6, 0) <= CFG80211_VERSION_CODE +void woal_cfg80211_setup_vht_cap(moal_private *priv, + struct ieee80211_sta_vht_cap *vht_cap); +#endif +int woal_cfg80211_assoc(moal_private *priv, void *sme, t_u8 wait_option, + pmlan_ds_misc_assoc_rsp assoc_rsp); + +#endif /* _MOAL_CFG80211_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_cfg80211_util.c b/mxm_wifiex/wlan_src/mlinux/moal_cfg80211_util.c new file mode 100644 index 0000000..0e124c5 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_cfg80211_util.c @@ -0,0 +1,4209 @@ +/** @file moal_cfg80211_util.c + * + * @brief This file contains the functions for CFG80211 vendor. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_util.h" +#include "moal_cfg80211.h" + +/******************************************************** + * Local Variables + ********************************************************/ + +/******************************************************** + * Global Variables + ********************************************************/ + +/******************************************************** + * Local Functions + ********************************************************/ + +/******************************************************** + * Global Functions + ********************************************************/ + +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE +/**nxp vendor command and event*/ +#define MRVL_VENDOR_ID 0x005043 +/** vendor events */ +const struct nl80211_vendor_cmd_info vendor_events[] = { + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_hang, + }, /*event_id 0*/ + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_rssi_monitor, + }, /*event_id 0x1501*/ + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_cloud_keep_alive, + }, /*event_id 0x10003*/ + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_dfs_radar_detected, + }, /*event_id 0x10004*/ + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_dfs_cac_started, + }, /*event_id 0x10005*/ + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_dfs_cac_finished, + }, /*event_id 0x10006*/ + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_dfs_cac_aborted, + }, /*event_id 0x10007*/ + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_dfs_nop_finished, + }, /*event_id 0x10008*/ + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_wifi_logger_ring_buffer_data, + }, + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_wifi_logger_alert, + }, + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_packet_fate_monitor, + }, + { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = event_wake_reason_report, + }, + /**add vendor event here*/ +}; + +/**nxp vendor policies*/ +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + +static const struct nla_policy woal_ll_stat_policy[ATTR_LL_STATS_MAX + 1] = { + [ATTR_LL_STATS_MPDU_SIZE_THRESHOLD] = {.type = NLA_U32}, + [ATTR_LL_STATS_AGGRESSIVE_STATS_GATHERING] = {.type = NLA_U32}, + [ATTR_LL_STATS_CLEAR_REQ_MASK] = {.type = NLA_U32}, + [ATTR_LL_STATS_STOP_REQ] = {.type = NLA_U8}}; + +static const struct nla_policy woal_logger_policy[ATTR_WIFI_LOGGER_MAX + 1] = { + [ATTR_WIFI_LOGGER_RING_ID] = {.type = NLA_STRING}, + [ATTR_WIFI_LOGGER_VERBOSE_LEVEL] = {.type = NLA_U32}, + [ATTR_WIFI_LOGGER_FLAGS] = {.type = NLA_U32}, + [ATTR_WIFI_LOGGER_MIN_DATA_SIZE] = {.type = NLA_U32}, + [ATTR_WIFI_LOGGER_MAX_INTERVAL_SEC] = {.type = NLA_U32}}; + +static const struct nla_policy woal_attr_policy[ATTR_WIFI_MAX + 1] = { + [ATTR_CHANNELS_BAND] = {.type = NLA_U32}, + [ATTR_SCAN_MAC_OUI_SET] = {.type = NLA_STRING, .len = 3}, + [ATTR_NODFS_VALUE] = {.type = NLA_U32}, + [ATTR_GET_CONCURRENCY_MATRIX_SET_SIZE_MAX] = {.type = NLA_U32}, +}; + +// clang-format off +static const struct nla_policy + woal_nd_offload_policy[ATTR_ND_OFFLOAD_MAX + 1] = { + [ATTR_ND_OFFLOAD_CONTROL] = {.type = NLA_U8}, +}; +// clang-format on + +static const struct nla_policy + woal_rssi_monitor_policy[ATTR_RSSI_MONITOR_MAX + 1] = { + [ATTR_RSSI_MONITOR_CONTROL] = {.type = NLA_U32}, + [ATTR_RSSI_MONITOR_MIN_RSSI] = {.type = NLA_S8}, + [ATTR_RSSI_MONITOR_MAX_RSSI] = {.type = NLA_S8}, +}; + +static const struct nla_policy + woal_packet_filter_policy[ATTR_PACKET_FILTER_MAX + 1] = { + [ATTR_PACKET_FILTER_TOTAL_LENGTH] = {.type = NLA_U32}, + [ATTR_PACKET_FILTER_PROGRAM] = {.type = NLA_STRING}, +}; + +// clang-format off +static const struct nla_policy + woal_fw_roaming_policy[MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_MAX + 1] = { + [MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONTROL] = {.type = NLA_U32}, + [MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_BSSID] = { + .type = NLA_BINARY, + .len = sizeof(int)}, + [MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_SSID] = { + .type = NLA_BINARY, + .len = sizeof(int)}, +}; +// clang-format on + +static const struct nla_policy + woal_keep_alive_policy[MKEEP_ALIVE_ATTRIBUTE_MAX + 1] = { + [MKEEP_ALIVE_ATTRIBUTE_ID] = {.type = NLA_U8}, + [MKEEP_ALIVE_ATTRIBUTE_ETHER_TYPE] = {.type = NLA_U16}, + [MKEEP_ALIVE_ATTRIBUTE_IP_PKT] = {.type = NLA_BINARY, .len = 1}, + [MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN] = {.type = NLA_U16}, + [MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR] = {.type = NLA_STRING, + .len = ETH_ALEN}, + [MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR] = {.type = NLA_STRING, + .len = ETH_ALEN}, + [MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC] = {.type = NLA_U32}, + [MKEEP_ALIVE_ATTRIBUTE_RETRY_INTERVAL] = {.type = NLA_U32}, + [MKEEP_ALIVE_ATTRIBUTE_RETRY_CNT] = {.type = NLA_U8}, +}; +#endif + +/** + * @brief get the event id of the events array + * + * @param event vendor event + * + * @return index of events array + */ +int woal_get_event_id(int event) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(vendor_events); i++) { + if (vendor_events[i].subcmd == event) + return i; + } + + return event_max; +} + +/** + * @brief send vendor event to kernel + * + * @param priv A pointer to moal_private + * @param event vendor event + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +int woal_cfg80211_vendor_event(moal_private *priv, int event, t_u8 *data, + int len) +{ + struct wiphy *wiphy = NULL; + struct sk_buff *skb = NULL; + int event_id = 0; + t_u8 *pos = NULL; + int ret = 0; + + ENTER(); + + if (!priv || !priv->wdev || !priv->wdev->wiphy) { + LEAVE(); + return ret; + } + wiphy = priv->wdev->wiphy; + PRINTM(MEVENT, "vendor event :0x%x\n", event); + event_id = woal_get_event_id(event); + if (event_max == event_id) { + PRINTM(MERROR, "Not find this event %d\n", event_id); + ret = 1; + LEAVE(); + return ret; + } + + /**allocate skb*/ +#if KERNEL_VERSION(4, 1, 0) <= CFG80211_VERSION_CODE + skb = cfg80211_vendor_event_alloc(wiphy, priv->wdev, len, event_id, + GFP_ATOMIC); +#else + skb = cfg80211_vendor_event_alloc(wiphy, len, event_id, GFP_ATOMIC); +#endif + + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor event\n"); + ret = 1; + LEAVE(); + return ret; + } + pos = skb_put(skb, len); + moal_memcpy_ext(priv->phandle, pos, data, len, len); + /**send event*/ + cfg80211_vendor_event(skb, GFP_ATOMIC); + + LEAVE(); + return ret; +} + +/** + * @brief send vendor event to kernel + * + * @param priv A pointer to moal_private + * @param event vendor event + * @param len data length + * + * @return 0: success 1: fail + */ +struct sk_buff *woal_cfg80211_alloc_vendor_event(moal_private *priv, int event, + int len) +{ + struct wiphy *wiphy = NULL; + struct sk_buff *skb = NULL; + int event_id = 0; + + ENTER(); + + if (!priv || !priv->wdev || !priv->wdev->wiphy) { + PRINTM(MERROR, "Not find this event %d\n", event_id); + goto done; + } + wiphy = priv->wdev->wiphy; + PRINTM(MEVENT, "vendor event :0x%x\n", event); + event_id = woal_get_event_id(event); + if (event_max == event_id) { + PRINTM(MERROR, "Not find this event %d\n", event_id); + goto done; + } + + /**allocate skb*/ +#if KERNEL_VERSION(4, 1, 0) <= CFG80211_VERSION_CODE + skb = cfg80211_vendor_event_alloc(wiphy, priv->wdev, len, event_id, + GFP_ATOMIC); +#else + skb = cfg80211_vendor_event_alloc(wiphy, len, event_id, GFP_ATOMIC); +#endif + + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor event\n"); + goto done; + } + +done: + LEAVE(); + return skb; +} + +/** + * @brief send dfs vendor event to kernel + * + * @param priv A pointer to moal_private + * @param event dfs vendor event + * @param chandef a pointer to struct cfg80211_chan_def + * + * @return N/A + */ +void woal_cfg80211_dfs_vendor_event(moal_private *priv, int event, + struct cfg80211_chan_def *chandef) +{ + dfs_event evt; + + ENTER(); + if (!chandef) { + LEAVE(); + return; + } + memset(&evt, 0, sizeof(dfs_event)); + evt.freq = chandef->chan->center_freq; + evt.chan_width = chandef->width; + evt.cf1 = chandef->center_freq1; + evt.cf2 = chandef->center_freq2; + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + evt.ht_enabled = 0; + break; + case NL80211_CHAN_WIDTH_20: + evt.ht_enabled = 1; + break; + case NL80211_CHAN_WIDTH_40: + evt.ht_enabled = 1; + if (chandef->center_freq1 < chandef->chan->center_freq) + evt.chan_offset = -1; + else + evt.chan_offset = 1; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + evt.ht_enabled = 1; + break; + default: + break; + } + woal_cfg80211_vendor_event(priv, event, (t_u8 *)&evt, + sizeof(dfs_event)); + LEAVE(); +} + +/** + * @brief vendor command to set drvdbg + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_set_drvdbg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ +#ifdef DEBUG_LEVEL1 + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + t_u8 *pos = NULL; +#endif + int ret = 1; + + ENTER(); +#ifdef DEBUG_LEVEL1 + /**handle this sub command*/ + DBG_HEXDUMP(MCMD_D, "Vendor drvdbg", (t_u8 *)data, data_len); + + if (data_len) { + /* Get the driver debug bit masks from user */ + drvdbg = *((t_u32 *)data); + PRINTM(MIOCTL, "new drvdbg %x\n", drvdbg); + /* Set the driver debug bit masks into mlan */ + if (woal_set_drvdbg(priv, drvdbg)) { + PRINTM(MERROR, "Set drvdbg failed!\n"); + ret = 1; + } + } + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(drvdbg)); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = 1; + LEAVE(); + return ret; + } + pos = skb_put(skb, sizeof(drvdbg)); + moal_memcpy_ext(priv->phandle, pos, &drvdbg, sizeof(drvdbg), + sizeof(drvdbg)); + ret = cfg80211_vendor_cmd_reply(skb); +#endif + LEAVE(); + return ret; +} + +/** + * @brief process one channel in bucket + * + * @param priv A pointer to moal_private struct + * + * @param channel a pointer to channel + * + * @return 0: success other: fail + */ +static mlan_status woal_band_to_valid_channels(moal_private *priv, + wifi_band w_band, int channel[], + t_u32 *nchannel) +{ + int band = 0; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int i = 0; + t_u8 cnt = 0; + int *ch_ptr = channel; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!priv->wdev->wiphy->bands[band]) + continue; + if ((band == IEEE80211_BAND_2GHZ) && !(w_band & WIFI_BAND_BG)) + continue; + if ((band == IEEE80211_BAND_5GHZ) && + !((w_band & WIFI_BAND_A) || (w_band & WIFI_BAND_A_DFS))) + continue; + sband = priv->wdev->wiphy->bands[band]; + for (i = 0; (i < sband->n_channels); i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) { + PRINTM(MERROR, "Skip DISABLED channel %d\n", + ch->center_freq); + continue; + } + if (band == IEEE80211_BAND_5GHZ) { + if (((ch->flags & IEEE80211_CHAN_RADAR) && + !(w_band & WIFI_BAND_A_DFS)) || + (!(ch->flags & IEEE80211_CHAN_RADAR) && + !(w_band & WIFI_BAND_A))) + continue; + } + if (cnt >= *nchannel) { + PRINTM(MERROR, + "cnt=%d is exceed %d, cur ch=%d %dMHz\n", + cnt, *nchannel, ch->hw_value, + ch->center_freq); + break; + } + *ch_ptr = ch->center_freq; + ch_ptr++; + cnt++; + } + } + + PRINTM(MCMND, "w_band=%d cnt=%d\n", w_band, cnt); + *nchannel = cnt; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief GSCAN subcmd - enable full scan results + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * + * @param data a pointer to data + * @param data_len data length + * + * @return 0: success other: fail + */ +static int woal_cfg80211_subcmd_get_valid_channels(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct nlattr *tb[ATTR_WIFI_MAX + 1]; + t_u32 band = 0; + int ch_out[MAX_CHANNEL_NUM]; + t_u32 nchannel = 0; + t_u32 mem_needed = 0; + struct sk_buff *skb = NULL; + int err = 0; + + ENTER(); + PRINTM(MCMND, "Enter %s()\n", __func__); + + err = nla_parse(tb, ATTR_WIFI_MAX, data, len, NULL +#if KERNEL_VERSION(4, 12, 0) <= CFG80211_VERSION_CODE + , + NULL +#endif + ); + if (err) { + PRINTM(MERROR, "%s: nla_parse fail\n", __func__); + err = -EFAULT; + goto done; + } + + if (!tb[ATTR_CHANNELS_BAND]) { + PRINTM(MERROR, "%s: null attr: tb[ATTR_GET_CH]=%p\n", __func__, + tb[ATTR_CHANNELS_BAND]); + err = -EINVAL; + goto done; + } + band = nla_get_u32(tb[ATTR_CHANNELS_BAND]); + if (band > WIFI_BAND_MAX) { + PRINTM(MERROR, "%s: invalid band=%d\n", __func__, band); + err = -EINVAL; + goto done; + } + + memset(ch_out, 0x00, sizeof(ch_out)); + nchannel = MAX_CHANNEL_NUM; + if (woal_band_to_valid_channels(priv, band, ch_out, &nchannel) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "get_channel_list: woal_band_to_valid_channels fail\n"); + return -EFAULT; + } + + mem_needed = nla_total_size(nchannel * sizeof(ch_out[0])) + + nla_total_size(sizeof(nchannel)) + VENDOR_REPLY_OVERHEAD; + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, mem_needed); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed"); + err = -ENOMEM; + goto done; + } + + if (nla_put_u32(skb, ATTR_NUM_CHANNELS, nchannel) || + nla_put(skb, ATTR_CHANNEL_LIST, nchannel * sizeof(ch_out[0]), + ch_out)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree_skb(skb); + err = -ENOMEM; + goto done; + } + err = cfg80211_vendor_cmd_reply(skb); + if (err) { + PRINTM(MERROR, "Vendor Command reply failed ret:%d\n", err); + goto done; + } + +done: + LEAVE(); + return err; +} + +/** + * @brief vendor command to get driver version + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_get_drv_version(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + int ret = 0; + t_u32 drv_len = 0; + char drv_version[MLAN_MAX_VER_STR_LEN] = {0}; + char *pos; + + ENTER(); + moal_memcpy_ext(priv->phandle, drv_version, + &priv->phandle->driver_version, MLAN_MAX_VER_STR_LEN, + MLAN_MAX_VER_STR_LEN); + drv_len = strlen(drv_version); + pos = strstr(drv_version, "%s"); + /* remove 3 char "-%s" in driver_version string */ + if (pos != NULL) + moal_memcpy_ext(priv->phandle, pos, pos + 3, strlen(pos) - 3, + strlen(pos)); + + reply_len = strlen(drv_version) + 1; + drv_len -= 3; + drv_version[drv_len] = '\0'; + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_NAME, reply_len, + (t_u8 *)drv_version); + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get firmware version + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_get_fw_version(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + char end_c = '\0'; + int ret = 0; + char fw_ver[32] = {0}; + union { + t_u32 l; + t_u8 c[4]; + } ver; + + ENTER(); + + ver.l = priv->phandle->fw_release_number; + snprintf(fw_ver, sizeof(fw_ver), "%u.%u.%u.p%u%c", ver.c[2], ver.c[1], + ver.c[0], ver.c[3], end_c); + reply_len = strlen(fw_ver) + 1; + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_NAME, reply_len, (t_u8 *)fw_ver); + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get firmware memory dump + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_get_fw_dump(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + moal_handle *handle = NULL; + int ret = MLAN_STATUS_SUCCESS; + int length = 0; + struct sk_buff *skb = NULL; + + ENTER(); + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + handle = priv->phandle; + if (handle) { + memset(handle->firmware_dump_file, 0, + sizeof(handle->firmware_dump_file)); + } + length = sizeof(handle->firmware_dump_file); + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length); + if (!skb) { + PRINTM(MERROR, "Failed to allocate memory for skb\n"); + ret = -ENOMEM; + goto done; + } + nla_put(skb, ATTR_FW_DUMP_PATH, sizeof(handle->firmware_dump_file), + handle->firmware_dump_file); + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get driver memory dump + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_get_drv_dump(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + moal_handle *handle = NULL; + int ret = MLAN_STATUS_SUCCESS; + int length = 0; + char driver_dump_file[128]; + char path_name[64]; + struct sk_buff *skb = NULL; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + handle = priv->phandle; + memset(path_name, 0, sizeof(path_name)); + woal_create_dump_dir(handle, path_name, sizeof(path_name)); + PRINTM(MMSG, "driver dump path name is %s\n", path_name); + woal_dump_drv_info(handle, path_name); + memset(driver_dump_file, 0, sizeof(driver_dump_file)); + sprintf(driver_dump_file, "%s/%s", path_name, "file_drv_info"); + PRINTM(MMSG, "driver dump file is %s\n", driver_dump_file); + length = sizeof(driver_dump_file); + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length); + if (!skb) { + PRINTM(MERROR, "Failed to allocate memory for skb\n"); + ret = -ENOMEM; + goto done; + } + if (nla_put_string(skb, ATTR_DRV_DUMP_PATH, driver_dump_file)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree_skb(skb); + ret = -ENOMEM; + goto done; + } + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get supported feature set + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_get_supp_feature_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct sk_buff *skb = NULL; + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_fw_info fw_info; + t_u32 reply_len = 0; + int ret = 0; + t_u32 supp_feature_set = 0; + + ENTER(); + + supp_feature_set = WLAN_FEATURE_INFRA +#if defined(UAP_SUPPORT) && defined(STA_SUPPORT) + | WLAN_FEATURE_AP_STA +#endif + | WLAN_FEATURE_LINK_LAYER_STATS | + WLAN_FEATURE_LOGGER | WLAN_FEATURE_RSSI_MONITOR | + WLAN_FEATURE_CONFIG_NDO | + WLAN_FEATURE_CONTROL_ROAMING | + WLAN_FEATURE_SCAN_RAND | WLAN_FEATURE_MKEEP_ALIVE; + + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if (fw_info.fw_bands & BAND_A) + supp_feature_set |= WLAN_FEATURE_INFRA_5G; + + reply_len = sizeof(supp_feature_set); + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + if (nla_put_u32(skb, ATTR_FEATURE_SET, supp_feature_set)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree_skb(skb); + ret = -ENOMEM; + goto done; + } + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to set country code + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_set_country_code(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + int ret = 0, rem, type; + const struct nlattr *iter; + char country[COUNTRY_CODE_LEN] = {0}; + + ENTER(); + + nla_for_each_attr (iter, data, data_len, rem) { + type = nla_type(iter); + switch (type) { + case ATTR_COUNTRY_CODE: + strncpy(country, nla_data(iter), + MIN(sizeof(country) - 1, nla_len(iter))); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + return ret; + } + } + + if (!moal_extflg_isset((moal_handle *)woal_get_wiphy_priv(wiphy), + EXT_DISABLE_REGD_BY_DRIVER)) + regulatory_hint(wiphy, country); + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get supported feature set + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_get_wifi_logger_supp_feature_set( + struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, + int data_len) +{ + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + int ret = 0; + t_u32 supp_feature_set = 0; + + ENTER(); + + supp_feature_set = WIFI_LOGGER_CONNECT_EVENT_SUPPORTED; + reply_len = sizeof(supp_feature_set); + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + if (nla_put_u32(skb, ATTR_WIFI_LOGGER_FEATURE_SET, supp_feature_set)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree_skb(skb); + ret = -ENOMEM; + goto done; + } + ret = cfg80211_vendor_cmd_reply(skb); + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief get ring status + * + * @param priv A pointer to moal_private struct + * @param ring_id ring buffer id + * @param status a pointer to wifi_ring_buffer_status + * + * @return void + */ +static void woal_get_ring_status(moal_private *priv, int ring_id, + wifi_ring_buffer_status *status) +{ + int id = 0; + wifi_ring_buffer *ring; + + ENTER(); + for (id = 0; id < RING_ID_MAX; id++) { + ring = (wifi_ring_buffer *)priv->rings[id]; + if (ring && VALID_RING(ring->ring_id) && + ring_id == ring->ring_id) { + strncpy(status->name, ring->name, + sizeof(status->name) - 1); + status->ring_id = ring->ring_id; + status->ring_buffer_byte_size = ring->ring_size; + status->written_bytes = ring->ctrl.written_bytes; + status->written_records = ring->ctrl.written_records; + status->read_bytes = ring->ctrl.read_bytes; + status->verbose_level = ring->log_level; + PRINTM(MINFO, + "%s, name: %s, ring_id: %d, ring_size : %d, written_bytes: %d, written_records: %d, read_bytes: %d\n", + __func__, status->name, status->ring_id, + status->ring_buffer_byte_size, + status->written_bytes, status->written_records, + status->read_bytes); + break; + } + } + LEAVE(); +} + +/** + * @brief vendor command to get ring buffer status + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_get_ring_buff_status(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + t_u8 ringIdx, ring_cnt = 0; + int ret = 0; + wifi_ring_buffer_status status[RING_ID_MAX]; + wifi_ring_buffer_status ring_status; + + ENTER(); + reply_len = + RING_ID_MAX * sizeof(wifi_ring_buffer_status) + sizeof(t_u32); + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + + /* should sync with HAL side to decide payload layout. + * just ring buffer status or ring buffer num + ring buffer status + * currently only have ring buffer status + */ + for (ringIdx = 0; ringIdx < RING_ID_MAX; ringIdx++) { + memset(&ring_status, 0, sizeof(wifi_ring_buffer_status)); + woal_get_ring_status(priv, ringIdx, &ring_status); + moal_memcpy_ext(priv->phandle, &status[ring_cnt++], + &ring_status, sizeof(wifi_ring_buffer_status), + sizeof(wifi_ring_buffer_status)); + } + + if (nla_put_u32(skb, ATTR_NUM_RINGS, ring_cnt) || + nla_put(skb, ATTR_RING_BUFFER_STATUS, + sizeof(wifi_ring_buffer_status) * ring_cnt, status)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree_skb(skb); + ret = -ENOMEM; + goto done; + } + + ret = cfg80211_vendor_cmd_reply(skb); + + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief return ring_id based on ring_name + * + * @param priv A pointer to moal_private struct + * @param ring_name A pointer to ring_name + * + * @return An invalid ring id for failure or valid ring id on success. + */ +int woal_get_ring_id_by_name(moal_private *priv, char *ring_name) +{ + int id; + wifi_ring_buffer *ring; + + ENTER(); + + for (id = 0; id < RING_ID_MAX; id++) { + ring = (wifi_ring_buffer *)priv->rings[id]; + if (ring && + !strncmp(ring->name, ring_name, sizeof(ring->name) - 1)) + break; + } + + LEAVE(); + return id; +} + +/** + * @brief start logger + * + * @param priv A pointer to moal_private struct + * @param ring_name string ring_name + * @param log_level log level to record + * @param flags reserved + * @param time_intval interval to report log to HAL + * @param threshold buffer threshold to report log to HAL + * + * @return 0: success 1: fail + */ +int woal_start_logging(moal_private *priv, char *ring_name, int log_level, + int flags, int time_intval, int threshold) +{ + int ret = 0; + int ring_id; + wifi_ring_buffer *ring_buffer; + unsigned long lock_flags; + + ENTER(); + + ring_id = woal_get_ring_id_by_name(priv, ring_name); + if (!VALID_RING(ring_id)) { + ret = -EINVAL; + goto done; + } + + PRINTM(MCMND, + "%s , log_level : %d, time_intval : %d, threshod %d Bytes\n", + __func__, log_level, time_intval, threshold); + + ring_buffer = (wifi_ring_buffer *)priv->rings[ring_id]; + if (!ring_buffer || ring_buffer->state == RING_STOP) { + PRINTM(MERROR, "Ring is stopped!\n"); + ret = -EAGAIN; + goto done; + } + + spin_lock_irqsave(&ring_buffer->lock, lock_flags); + ring_buffer->log_level = log_level; + ring_buffer->threshold = threshold; + if (log_level == 0) + ring_buffer->state = RING_SUSPEND; + else + ring_buffer->state = RING_ACTIVE; + ring_buffer->interval = msecs_to_jiffies(time_intval * MSEC_PER_SEC); + spin_unlock_irqrestore(&ring_buffer->lock, lock_flags); + + if (log_level == 0) { + cancel_delayed_work_sync(&ring_buffer->work); + } else { + if (ring_buffer->interval) + schedule_delayed_work(&ring_buffer->work, + ring_buffer->interval); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to start logging + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_start_logging(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + int ret = 0, rem, type; + char ring_name[RING_NAME_MAX] = {0}; + int log_level = 0, flags = 0, time_intval = 0, threshold = 0; + const struct nlattr *iter; + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + + nla_for_each_attr (iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case ATTR_WIFI_LOGGER_RING_ID: + strncpy(ring_name, nla_data(iter), + MIN(sizeof(ring_name) - 1, nla_len(iter))); + break; + case ATTR_WIFI_LOGGER_VERBOSE_LEVEL: + log_level = nla_get_u32(iter); + break; + case ATTR_WIFI_LOGGER_FLAGS: + flags = nla_get_u32(iter); + break; + case ATTR_WIFI_LOGGER_MAX_INTERVAL_SEC: + time_intval = nla_get_u32(iter); + break; + case ATTR_WIFI_LOGGER_MIN_DATA_SIZE: + threshold = nla_get_u32(iter); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + ret = -EINVAL; + goto done; + } + } + + ret = woal_start_logging(priv, ring_name, log_level, flags, time_intval, + threshold); + if (ret < 0) + PRINTM(MERROR, "Start_logging is failed ret: %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief get log data in ring buffer + * + * @param priv A pointer to moal_private struct + * @param ring_name ring name string + * + * @return 0: success 1: fail + */ +static int woal_trigger_get_ring_data(moal_private *priv, char *ring_name) +{ + int ret = 0; + int ring_id; + wifi_ring_buffer *ring_buffer; + + ENTER(); + + ring_id = woal_get_ring_id_by_name(priv, ring_name); + if (!VALID_RING(ring_id)) { + PRINTM(MERROR, "invalid ring_id\n"); + ret = -EINVAL; + goto done; + } + + ring_buffer = (wifi_ring_buffer *)priv->rings[ring_id]; + if (!ring_buffer) { + PRINTM(MERROR, "invalid ring_buffer\n"); + ret = -EINVAL; + goto done; + } + if (ring_buffer->interval) + cancel_delayed_work_sync(&ring_buffer->work); + schedule_delayed_work(&ring_buffer->work, 0); + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get ring buffer data + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_get_ring_data(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + int ret = 0, rem, type; + char ring_name[RING_NAME_MAX] = {0}; + const struct nlattr *iter; + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + + nla_for_each_attr (iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case ATTR_WIFI_LOGGER_RING_ID: + strncpy(ring_name, nla_data(iter), + MIN(sizeof(ring_name) - 1, nla_len(iter))); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + ret = -EINVAL; + goto done; + } + } + + ret = woal_trigger_get_ring_data(priv, ring_name); + if (ret < 0) + PRINTM(MERROR, "trigger_get_data failed ret:%d\n", ret); + +done: + LEAVE(); + return ret; +} + +/** + * @brief ring reset + * + * @param priv A pointer to moal_private struct + * @param ring A pointer to wifi_ring_buffer + * + * @return void + */ +static void woal_ring_reset(wifi_ring_buffer *ring) +{ + ENTER(); + ring->wp = 0; + ring->rp = 0; + memset(&(ring->ctrl), 0x00, sizeof(ring->ctrl)); + memset(ring->ring_buf, 0, ring->ring_size); + + LEAVE(); +} + +/** + * @brief get ring buffer entry + * + * @param ring A pointer to wifi_ring_buffer struct + * @param offset entry offset + * + * @return A pointer to wifi_ring_buffer_entry struct + */ +static wifi_ring_buffer_entry *woal_get_ring_entry(wifi_ring_buffer *ring, + t_u32 offset) +{ + wifi_ring_buffer_entry *entry = + (wifi_ring_buffer_entry *)(ring->ring_buf + offset); + + ENTER(); + + if (!entry->entry_size) + return (wifi_ring_buffer_entry *)ring->ring_buf; + + LEAVE(); + return entry; +} + +/** + * @brief get next ring buffer entry + * + * @param ring A pointer to wifi_ring_buffer struct + * @param offset next entry offset + * + * @return offset of next entry + */ +static t_u32 woal_get_ring_next_entry(wifi_ring_buffer *ring, t_u32 offset) +{ + wifi_ring_buffer_entry *entry = + (wifi_ring_buffer_entry *)(ring->ring_buf + offset); + wifi_ring_buffer_entry *next_entry = NULL; + + ENTER(); + + if (!entry->entry_size) { + entry = (wifi_ring_buffer_entry *)ring->ring_buf; + LEAVE(); + return ENTRY_LENGTH(entry); + } + if ((offset + ENTRY_LENGTH(entry)) >= ring->ring_size) { + /* move to head */ + LEAVE(); + return 0; + } + next_entry = (wifi_ring_buffer_entry *)(ring->ring_buf + offset + + ENTRY_LENGTH(entry)); + if (!next_entry->entry_size) { + /* move to head */ + LEAVE(); + return 0; + } + LEAVE(); + return offset + ENTRY_LENGTH(entry); +} + +/** + * @brief prepare log data to send to HAL + * + * @param priv A pointer to moal_private struct + * @param ring_id ring ID + * @param data A pointer to data buffer + * @param buf_len log data length + * + * @return data length + */ +int woal_ring_pull_data(moal_private *priv, int ring_id, void *data, + t_s32 buf_len) +{ + t_s32 r_len = 0; + wifi_ring_buffer *ring; + wifi_ring_buffer_entry *hdr; + t_s32 buf_left = buf_len; + + ENTER(); + + ring = (wifi_ring_buffer *)priv->rings[ring_id]; + + /* get a fresh pending length */ + while (buf_left > 0) { + hdr = woal_get_ring_entry(ring, ring->rp); + if (buf_left < ENTRY_LENGTH(hdr)) + break; + moal_memcpy_ext(priv->phandle, data, hdr, ENTRY_LENGTH(hdr), + buf_left); + r_len += ENTRY_LENGTH(hdr); + data += ENTRY_LENGTH(hdr); + buf_left -= ENTRY_LENGTH(hdr); + ring->ctrl.read_bytes += ENTRY_LENGTH(hdr); + if (!buf_left) { + ring->rp += ENTRY_LENGTH(hdr); + break; + } + ring->rp = woal_get_ring_next_entry(ring, ring->rp); + } + PRINTM(MDATA, "Ring pull data: [wp=%d rp=%d] r_len=%d buf_len=%d\n", + ring->wp, ring->rp, r_len, buf_len); + + LEAVE(); + return r_len; +} + +/** + * @brief send vendor event to kernel + * + * @param priv A pointer to moal_private + * @param event vendor event + * @param data a pointer to data + * @param len data length + * @param ring_status A pointer to wifi_ring_buffer status struct + * + * @return 0: success 1: fail + */ +int woal_ring_buffer_data_vendor_event(moal_private *priv, int ring_id, + t_u8 *data, int len, + wifi_ring_buffer_status *ring_status) +{ + struct wiphy *wiphy = NULL; + struct sk_buff *skb = NULL; + int event_id = 0; + int ret = 0; + + ENTER(); + + if (!priv || !priv->wdev || !priv->wdev->wiphy) { + PRINTM(MERROR, "priv is null\n"); + ret = -EINVAL; + goto done; + } + wiphy = priv->wdev->wiphy; + PRINTM(MEVENT, "%s ring_id:%d\n", __func__, ring_id); + event_id = woal_get_event_id(event_wifi_logger_ring_buffer_data); + if (event_max == event_id) { + PRINTM(MERROR, "Not find this event %d\n", event_id); + ret = -EINVAL; + goto done; + } + +/**allocate skb*/ +#if KERNEL_VERSION(4, 1, 0) <= CFG80211_VERSION_CODE + skb = cfg80211_vendor_event_alloc(wiphy, priv->wdev, + len + sizeof(wifi_ring_buffer_status), + event_id, GFP_ATOMIC); +#else + skb = cfg80211_vendor_event_alloc(wiphy, + len + sizeof(wifi_ring_buffer_status), + event_id, GFP_ATOMIC); +#endif + + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor event\n"); + ret = -ENOMEM; + goto done; + } + nla_put(skb, ATTR_RING_BUFFER_STATUS, sizeof(wifi_ring_buffer_status), + ring_status); + DBG_HEXDUMP(MEVT_D, "ring_buffer_data", data, len); + nla_put(skb, ATTR_RING_BUFFER, len, data); + + /**send event*/ + cfg80211_vendor_event(skb, GFP_ATOMIC); + +done: + LEAVE(); + return ret; +} + +/** + * @brief send log data to HAL + * + * @param priv A pointer to moal_private struct + * @param ring_id ring ID + * @param data A pointer to data buffer + * @param len log data length + * @param ring_status A pointer to wifi_ring_buffer status struct + * + * @return void + */ +static void woal_ring_data_send(moal_private *priv, int ring_id, + const void *data, const t_u32 len, + wifi_ring_buffer_status *ring_status) +{ + struct net_device *ndev = priv->netdev; + + ENTER(); + + if (!ndev) + goto done; + woal_ring_buffer_data_vendor_event(priv, ring_id, (t_u8 *)data, len, + ring_status); + +done: + LEAVE(); +} + +/** + * @brief main thread to send log data + * + * @param work A pointer to work_struct struct + * + * @return void + */ +void woal_ring_poll_worker(struct work_struct *work) +{ + struct delayed_work *d_work = to_delayed_work(work); + wifi_ring_buffer *ring_info = + container_of(d_work, wifi_ring_buffer, work); + moal_private *priv = ring_info->priv; + int ringid = ring_info->ring_id; + wifi_ring_buffer_status ring_status; + void *buf = NULL; + wifi_ring_buffer_entry *hdr; + t_s32 buflen, rlen; + unsigned long flags; + + ENTER(); + if (ring_info->state != RING_ACTIVE) { + PRINTM(MERROR, "Ring is not active!\n"); + goto exit; + } + buf = kmalloc(ring_info->ring_size, GFP_KERNEL); + if (!buf) { + PRINTM(MERROR, "Fail to allocate buffer for poll work\n"); + goto exit; + } + spin_lock_irqsave(&ring_info->lock, flags); + memset(&ring_status, 0, sizeof(ring_status)); + woal_get_ring_status(priv, ringid, &ring_status); + PRINTM(MDATA, "Ring work: ringid %d write %d, read %d, size %d\n", + ringid, ring_status.written_bytes, ring_status.read_bytes, + ring_status.ring_buffer_byte_size); + if (ring_status.written_bytes > ring_status.read_bytes) { + buflen = ring_status.written_bytes - ring_status.read_bytes; + if (buflen > ring_info->ring_size) { + PRINTM(MERROR, "Pending data bigger than ring size\n"); + spin_unlock_irqrestore(&ring_info->lock, flags); + goto exit; + } + } else { + spin_unlock_irqrestore(&ring_info->lock, flags); + PRINTM(MINFO, "No new records\n"); + goto exit; + } + + rlen = woal_ring_pull_data(priv, ringid, buf, buflen); + spin_unlock_irqrestore(&ring_info->lock, flags); + hdr = (wifi_ring_buffer_entry *)buf; + while (rlen > 0) { + ring_status.read_bytes += ENTRY_LENGTH(hdr); + woal_ring_data_send(priv, ringid, hdr, ENTRY_LENGTH(hdr), + &ring_status); + rlen -= ENTRY_LENGTH(hdr); + hdr = (wifi_ring_buffer_entry *)((void *)hdr + + ENTRY_LENGTH(hdr)); + } +exit: + kfree(buf); + if (ring_info->interval) + schedule_delayed_work(&ring_info->work, ring_info->interval); + + LEAVE(); +} + +/** + * @brief add log data to ring buffer + * + * @param priv A pointer to moal_private struct + * @param ring_id ring ID + * @param hdr A pointer to wifi_ring_buffer_entry struct + * @param data A pointer to data buffer + * + * @return 0: success -1: fail + */ +int woal_ring_push_data(moal_private *priv, int ring_id, + wifi_ring_buffer_entry *hdr, void *data) +{ + unsigned long flags; + t_u32 w_len; + wifi_ring_buffer *ring; + wifi_ring_buffer_entry *w_entry; + int ret = 0; + + ENTER(); + + ring = (wifi_ring_buffer *)priv->rings[ring_id]; + + if (!ring || ring->state != RING_ACTIVE) { + PRINTM(MERROR, "Ring is not active\n"); + ret = -EINVAL; + goto done; + } + + w_len = ENTRY_LENGTH(hdr); + if (w_len > ring->ring_size) { + PRINTM(MERROR, "event size too big =%d\n", w_len); + ret = -EINVAL; + goto done; + } + spin_lock_irqsave(&ring->lock, flags); + PRINTM(MDATA, "Ring push data: rp=%d wp=%d, w_len=%d\n", ring->rp, + ring->wp, w_len); + /* prep the space */ + do { + if (ring->rp == ring->wp) { + if (ring->ctrl.read_bytes == ring->ctrl.written_bytes) { + if (ring->wp) + woal_ring_reset(ring); + break; + } else { + /* full, we should drop one entry */ + w_entry = + (wifi_ring_buffer_entry + *)(ring->ring_buf + ring->rp); + ring->rp = woal_get_ring_next_entry(ring, + ring->rp); + ring->ctrl.written_bytes -= + ENTRY_LENGTH(w_entry); + memset((u8 *)w_entry, 0, ENTRY_LENGTH(w_entry)); + ring->ctrl.written_records--; + continue; + } + } + if (ring->rp < ring->wp) { + if (ring->ring_size - ring->wp >= w_len) { + break; + } else if (ring->ring_size - ring->wp < w_len) { + if (ring->rp == 0) { + /** drop one entry */ + w_entry = (wifi_ring_buffer_entry + *)(ring->ring_buf + + ring->rp); + ring->rp = woal_get_ring_next_entry( + ring, ring->rp); + ring->ctrl.written_bytes -= + ENTRY_LENGTH(w_entry); + memset((u8 *)w_entry, 0, + ENTRY_LENGTH(w_entry)); + ring->ctrl.written_records--; + } + ring->wp = 0; + continue; + } + } + if (ring->rp > ring->wp) { + if (ring->rp - ring->wp < w_len) { + /** drop one entry */ + w_entry = + (wifi_ring_buffer_entry + *)(ring->ring_buf + ring->rp); + ring->rp = woal_get_ring_next_entry(ring, + ring->rp); + ring->ctrl.written_bytes -= + ENTRY_LENGTH(w_entry); + memset((u8 *)w_entry, 0, ENTRY_LENGTH(w_entry)); + ring->ctrl.written_records--; + continue; + } else { + break; + } + } + } while (1); + + if ((ring->wp + w_len) > ring->ring_size || + (ring->ctrl.written_bytes + w_len) > ring->ring_size) { + PRINTM(MERROR, + "Ring push buffer overflow: rp=%d wp=%d, write_bytes=%d\n", + ring->rp, ring->wp, ring->ctrl.written_bytes); + goto done; + } + + w_entry = (wifi_ring_buffer_entry *)(ring->ring_buf + ring->wp); + /* header */ + moal_memcpy_ext(priv->phandle, w_entry, hdr, RING_ENTRY_SIZE, + ENTRY_LENGTH(w_entry)); + /* payload */ + moal_memcpy_ext(priv->phandle, (char *)w_entry + RING_ENTRY_SIZE, data, + w_entry->entry_size, w_entry->entry_size); + /* update write pointer */ + ring->wp += w_len; + /* update statistics */ + ring->ctrl.written_records++; + ring->ctrl.written_bytes += w_len; + spin_unlock_irqrestore(&ring->lock, flags); + + PRINTM(MDATA, + "Ring push data: wp=%d rp=%d write_bytes=%d, read_bytes=%d\n", + ring->wp, ring->rp, ring->ctrl.written_bytes, + ring->ctrl.read_bytes); + + /* if the current pending size is bigger than threshold */ + if (ring->threshold && (READ_AVAIL_SPACE(ring) >= ring->threshold)) { + PRINTM(MDATA, "Ring threshold reached\n"); + if (ring->interval) + cancel_delayed_work_sync(&ring->work); + schedule_delayed_work(&ring->work, 0); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function will init wifi logger ring buffer + * + * @param priv A pointer to moal_private structure + * @param ring_buff A pointer to wifi_ring_buffer structure + * @param ringIdx Ring buffer ID + * @param name Ring buffer name + * @param ring_sz Ring buffer size + * + * @return 0 - success + */ +static int woal_init_ring_buffer_internal(moal_private *priv, void **ring, + t_u16 ringIdx, t_u8 *name, + t_u32 ring_sz) +{ + unsigned long flags; + wifi_ring_buffer *ring_buff; + + ENTER(); + + *ring = vmalloc(sizeof(wifi_ring_buffer)); + if (!unlikely(*ring)) { + PRINTM(MERROR, "WiFi Logger: ring alloc failed\n"); + goto done; + } + memset(*ring, 0, sizeof(wifi_ring_buffer)); + ring_buff = (wifi_ring_buffer *)*ring; + + ring_buff->ring_buf = vmalloc(ring_sz); + if (!unlikely(ring_buff->ring_buf)) { + PRINTM(MERROR, "WiFi Logger: ring buffer data alloc failed\n"); + vfree(ring_buff); + *ring = NULL; + goto done; + } + memset(ring_buff->ring_buf, 0, ring_sz); + spin_lock_init(&ring_buff->lock); + spin_lock_irqsave(&ring_buff->lock, flags); + ring_buff->rp = ring_buff->wp = 0; + INIT_DELAYED_WORK(&ring_buff->work, woal_ring_poll_worker); + moal_memcpy_ext(priv->phandle, ring_buff->name, name, strlen(name), + RING_NAME_MAX - 1); + ring_buff->name[RING_NAME_MAX - 1] = 0; + ring_buff->ring_id = ringIdx; + ring_buff->ring_size = ring_sz; + ring_buff->state = RING_SUSPEND; + ring_buff->threshold = ring_buff->ring_size / 2; + ring_buff->priv = priv; + spin_unlock_irqrestore(&ring_buff->lock, flags); +done: + LEAVE(); + return 0; +} + +/** + * @brief init each ring in moal_private + * + * @param priv A pointer to moal_private struct + * + * @return data length + */ +static int woal_init_ring_buffer(moal_private *priv) +{ + ENTER(); + woal_init_ring_buffer_internal(priv, &priv->rings[VERBOSE_RING_ID], + VERBOSE_RING_ID, VERBOSE_RING_NAME, + DEFAULT_RING_BUFFER_SIZE); + woal_init_ring_buffer_internal(priv, &priv->rings[EVENT_RING_ID], + EVENT_RING_ID, EVENT_RING_NAME, + DEFAULT_RING_BUFFER_SIZE); + LEAVE(); + return 0; +} + +/** + * @brief deinit each ring in moal_private + * + * @param priv A pointer to moal_private struct + * + * @return data length + */ +static int woal_deinit_ring_buffer(moal_private *priv) +{ + int i; + enum ring_state ring_state = RING_STOP; + unsigned long lock_flags = 0; + wifi_ring_buffer *ring_buff; + + ENTER(); + + for (i = 0; i < RING_ID_MAX - 1; i++) { + ring_buff = (wifi_ring_buffer *)priv->rings[i]; + if (!ring_buff) + continue; + spin_lock_irqsave(&ring_buff->lock, lock_flags); + ring_state = ring_buff->state; + if (ring_state == RING_ACTIVE) + ring_buff->state = RING_STOP; + spin_unlock_irqrestore(&ring_buff->lock, lock_flags); + if (ring_state == RING_ACTIVE) + cancel_delayed_work_sync(&ring_buff->work); + vfree(ring_buff->ring_buf); + ring_buff->ring_buf = NULL; + vfree(ring_buff); + priv->rings[i] = NULL; + } + + LEAVE(); + return 0; +} + +/** + * @brief add log data to ring buffer + * + * @param priv A pointer to moal_private struct + * @param ring_id ring ID + * @param hdr A pointer to wifi_ring_buffer_entry struct + * @param data A pointer to data buffer + * + * @return 0: success -1: fail + */ +int woal_ring_event_logger(moal_private *priv, int ring_id, pmlan_event pmevent) +{ + t_u8 event_buf[100] = {0}; + wifi_ring_buffer_driver_connectivity_event *connectivity_event; + tlv_log *tlv; + t_u8 *pos; + wifi_ring_buffer_entry msg_hdr; + wifi_ring_buffer *ring; + + ENTER(); + ring = (wifi_ring_buffer *)priv->rings[ring_id]; + + if (!ring || ring->state != RING_ACTIVE) { + PRINTM(MINFO, "Ring is not active\n"); + goto done; + } + + switch (pmevent->event_id) { + case MLAN_EVENT_ID_DRV_ASSOC_SUCC_LOGGER: + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + assoc_logger_data *pbss_desc = + (assoc_logger_data *)pmevent->event_buf; + memset(&msg_hdr, 0, sizeof(msg_hdr)); + msg_hdr.flags |= RING_BUFFER_ENTRY_FLAGS_HAS_TIMESTAMP; + msg_hdr.type = ENTRY_TYPE_CONNECT_EVENT; + connectivity_event = + (wifi_ring_buffer_driver_connectivity_event *) + event_buf; + connectivity_event->event = WIFI_EVENT_ASSOC_COMPLETE; + pos = (t_u8 *)connectivity_event->tlvs; + + tlv = (tlv_log *)pos; + tlv->tag = WIFI_TAG_VENDOR_SPECIFIC; + tlv->length = MLAN_MAC_ADDR_LENGTH / 2; + moal_memcpy_ext(priv->phandle, tlv->value, + pbss_desc->oui, tlv->length, + sizeof(event_buf) - + (tlv->value - event_buf)); + msg_hdr.entry_size += tlv->length + TLV_LOG_HEADER_LEN; + pos = pos + tlv->length + TLV_LOG_HEADER_LEN; + + tlv = (tlv_log *)pos; + tlv->tag = WIFI_TAG_BSSID; + tlv->length = sizeof(pbss_desc->bssid); + moal_memcpy_ext( + priv->phandle, tlv->value, pbss_desc->bssid, + sizeof(pbss_desc->bssid), + sizeof(event_buf) - (tlv->value - event_buf)); + msg_hdr.entry_size += tlv->length + TLV_LOG_HEADER_LEN; + pos = pos + tlv->length + TLV_LOG_HEADER_LEN; + + tlv = (tlv_log *)pos; + tlv->tag = WIFI_TAG_SSID; + tlv->length = strlen(pbss_desc->ssid); + moal_memcpy_ext(priv->phandle, tlv->value, + pbss_desc->ssid, tlv->length, + sizeof(event_buf) - + (tlv->value - event_buf)); + msg_hdr.entry_size += tlv->length + TLV_LOG_HEADER_LEN; + pos = pos + tlv->length + TLV_LOG_HEADER_LEN; + + if (pbss_desc->rssi) { + tlv = (tlv_log *)pos; + tlv->tag = WIFI_TAG_RSSI; + tlv->length = sizeof(pbss_desc->rssi); + moal_memcpy_ext( + priv->phandle, tlv->value, + &pbss_desc->rssi, tlv->length, + sizeof(event_buf) - + (tlv->value - event_buf)); + msg_hdr.entry_size += + tlv->length + TLV_LOG_HEADER_LEN; + pos = pos + tlv->length + TLV_LOG_HEADER_LEN; + } + if (pbss_desc->channel) { + tlv = (tlv_log *)pos; + tlv->tag = WIFI_TAG_CHANNEL; + tlv->length = sizeof(pbss_desc->channel); + moal_memcpy_ext( + priv->phandle, tlv->value, + &pbss_desc->channel, + sizeof(pbss_desc->channel), + sizeof(event_buf) - + (tlv->value - event_buf)); + msg_hdr.entry_size += + tlv->length + TLV_LOG_HEADER_LEN; + } + msg_hdr.entry_size += sizeof(connectivity_event->event); + DBG_HEXDUMP(MCMD_D, "connectivity_event", + (t_u8 *)connectivity_event, + msg_hdr.entry_size); + woal_ring_push_data(priv, ring_id, &msg_hdr, + connectivity_event); + } + break; + case MLAN_EVENT_ID_DRV_ASSOC_FAILURE_LOGGER: + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + int status_code = *(int *)pmevent->event_buf; + + memset(&msg_hdr, 0, sizeof(msg_hdr)); + msg_hdr.flags |= RING_BUFFER_ENTRY_FLAGS_HAS_TIMESTAMP; + msg_hdr.type = ENTRY_TYPE_CONNECT_EVENT; + connectivity_event = + (wifi_ring_buffer_driver_connectivity_event *) + event_buf; + connectivity_event->event = WIFI_EVENT_ASSOC_COMPLETE; + pos = (t_u8 *)connectivity_event->tlvs; + tlv = (tlv_log *)pos; + tlv->tag = WIFI_TAG_STATUS; + tlv->length = sizeof(status_code); + moal_memcpy_ext(priv->phandle, tlv->value, &status_code, + sizeof(status_code), + sizeof(event_buf) - + (tlv->value - event_buf)); + msg_hdr.entry_size += tlv->length + 4; + msg_hdr.entry_size += sizeof(connectivity_event->event); + woal_ring_push_data(priv, ring_id, &msg_hdr, + connectivity_event); + } + break; + case MLAN_EVENT_ID_DRV_DISCONNECT_LOGGER: + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + t_u16 reason_code = *(t_u16 *)pmevent->event_buf; + + memset(&msg_hdr, 0, sizeof(msg_hdr)); + msg_hdr.flags |= RING_BUFFER_ENTRY_FLAGS_HAS_TIMESTAMP; + msg_hdr.type = ENTRY_TYPE_CONNECT_EVENT; + connectivity_event = + (wifi_ring_buffer_driver_connectivity_event *) + event_buf; + connectivity_event->event = WIFI_EVENT_ASSOC_COMPLETE; + pos = (t_u8 *)connectivity_event->tlvs; + tlv = (tlv_log *)pos; + tlv->tag = WIFI_TAG_REASON_CODE; + tlv->length = sizeof(reason_code); + moal_memcpy_ext(priv->phandle, tlv->value, &reason_code, + sizeof(reason_code), + sizeof(event_buf) - + (tlv->value - event_buf)); + msg_hdr.entry_size += tlv->length + 4; + msg_hdr.entry_size += sizeof(connectivity_event->event); + woal_ring_push_data(priv, ring_id, &msg_hdr, + connectivity_event); + } + break; + default: + break; + } +done: + LEAVE(); + return 0; +} + +/** + * @brief send vendor event to kernel + * + * @param priv A pointer to moal_private struct + * @param wake_reason wake_reason + * + * @return 0: success 1: fail + */ +int woal_wake_reason_vendor_event(moal_private *priv, + mlan_ds_hs_wakeup_reason wake_reason) +{ + struct wiphy *wiphy = NULL; + struct sk_buff *skb = NULL; + int event_id = 0; + int ret = 0; + + ENTER(); + + if (!priv || !priv->wdev || !priv->wdev->wiphy) { + PRINTM(MERROR, "priv is null\n"); + ret = -EINVAL; + goto done; + } + wiphy = priv->wdev->wiphy; + + event_id = woal_get_event_id(event_wake_reason_report); + if (event_max == event_id) { + PRINTM(MERROR, "Not find this event %d\n", event_id); + ret = MLAN_STATUS_FAILURE; + goto done; + } + +/**allocate skb*/ +#if KERNEL_VERSION(4, 1, 0) <= CFG80211_VERSION_CODE + skb = cfg80211_vendor_event_alloc(wiphy, priv->wdev, + sizeof(wake_reason.hs_wakeup_reason), + event_id, GFP_ATOMIC); +#else + skb = cfg80211_vendor_event_alloc(wiphy, + sizeof(wake_reason.hs_wakeup_reason), + event_id, GFP_ATOMIC); +#endif + + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor event\n"); + ret = -ENOMEM; + goto done; + } + + PRINTM(MINFO, "wake_reason.hs_wakeup_reason = %d\n", + wake_reason.hs_wakeup_reason); + nla_put_u16(skb, ATTR_WAKE_REASON_STAT, wake_reason.hs_wakeup_reason); + /**send event*/ + cfg80211_vendor_event(skb, GFP_ATOMIC); + +done: + PRINTM(MINFO, "wake reason vendor event ret %d\n", ret); + LEAVE(); + return ret; +} + +/** + * @brief log wake reason + * + * @param priv A pointer to moal_private struct + * @param wake_reason wake_reason + * + * @return 0: success -1: fail + */ +int woal_wake_reason_logger(moal_private *priv, + mlan_ds_hs_wakeup_reason wake_reason) +{ + int ret = 0; + + ENTER(); + + ret = woal_wake_reason_vendor_event(priv, wake_reason); + + LEAVE(); + return ret; +} + +/** + * @brief vendor command to start packet fate monitor + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int +woal_cfg80211_subcmd_start_packet_fate_monitor(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + int ret = 0; + + /* TODO: Tune pkt fate monitor for TP, Disabling it for now */ + // struct net_device *dev = wdev->netdev; + // moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + + /* TODO: Tune pkt fate monitor for TP, Disabling it for now */ + // priv->pkt_fate_monitor_enable = MTRUE; + + ret = cfg80211_vendor_cmd_reply(skb); + + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief send vendor event to kernel + * + * @param priv A pointer to moal_private struct + * @param pkt_type tx or rx + * @param fate packet fate + * @param payload_type type of payload + * @param drv_ts_usec driver time in usec + * @param fw_ts_usec firmware time in usec + * @param data frame data + * @param len frame data len + * + * @return 0: success 1: fail + */ +int woal_packet_fate_vendor_event(moal_private *priv, + packet_fate_packet_type pkt_type, t_u8 fate, + frame_type payload_type, t_u32 drv_ts_usec, + t_u32 fw_ts_usec, t_u8 *data, t_u32 len) +{ + struct wiphy *wiphy = NULL; + struct sk_buff *skb = NULL; + int event_id = 0; + int ret = 0; + PACKET_FATE_REPORT fate_report; + + ENTER(); + + if (!priv || !priv->wdev || !priv->wdev->wiphy) { + PRINTM(MERROR, "priv is null\n"); + ret = -EINVAL; + goto done; + } + wiphy = priv->wdev->wiphy; + + event_id = woal_get_event_id(event_packet_fate_monitor); + if (event_max == event_id) { + PRINTM(MERROR, "Not find this event %d\n", event_id); + ret = MLAN_STATUS_FAILURE; + goto done; + } + +/**allocate skb*/ +#if KERNEL_VERSION(4, 1, 0) <= CFG80211_VERSION_CODE + skb = cfg80211_vendor_event_alloc(wiphy, priv->wdev, + len + sizeof(PACKET_FATE_REPORT), + event_id, GFP_ATOMIC); +#else + skb = cfg80211_vendor_event_alloc( + wiphy, len + sizeof(PACKET_FATE_REPORT), event_id, GFP_ATOMIC); +#endif + + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor event\n"); + ret = -ENOMEM; + goto done; + } + + memset(&fate_report, 0, sizeof(PACKET_FATE_REPORT)); + if (pkt_type == PACKET_TYPE_TX) { + fate_report.u.tx_report_i.fate = fate; + fate_report.u.tx_report_i.frame_inf.payload_type = payload_type; + fate_report.u.tx_report_i.frame_inf.driver_timestamp_usec = + drv_ts_usec; + fate_report.u.tx_report_i.frame_inf.firmware_timestamp_usec = + fw_ts_usec; + fate_report.u.tx_report_i.frame_inf.frame_len = len; + nla_put(skb, ATTR_PACKET_FATE_TX, sizeof(PACKET_FATE_REPORT), + &fate_report); + } else { + fate_report.u.rx_report_i.fate = fate; + fate_report.u.rx_report_i.frame_inf.payload_type = payload_type; + fate_report.u.rx_report_i.frame_inf.driver_timestamp_usec = + drv_ts_usec; + fate_report.u.rx_report_i.frame_inf.firmware_timestamp_usec = + fw_ts_usec; + fate_report.u.rx_report_i.frame_inf.frame_len = len; + nla_put(skb, ATTR_PACKET_FATE_RX, sizeof(PACKET_FATE_REPORT), + &fate_report); + } + DBG_HEXDUMP(MCMD_D, "fate_report", (t_u8 *)&fate_report, + sizeof(PACKET_FATE_REPORT)); + + nla_put(skb, ATTR_PACKET_FATE_DATA, len, data); + DBG_HEXDUMP(MCMD_D, "packet_fate_data", data, len); + + /**send event*/ + cfg80211_vendor_event(skb, GFP_ATOMIC); + +done: + PRINTM(MINFO, "packet fate vendor event ret %d\n", ret); + LEAVE(); + return ret; +} + +/** + * @brief log packet fate + * + * @param priv A pointer to moal_private struct + * @param pkt_type tx or rx + * @param fate packet fate + * @param payload_type type of payload + * @param drv_ts_usec driver time in usec + * @param fw_ts_usec firmware time in usec + * @param data frame data + * @param len frame data len + * + * @return 0: success -1: fail + */ +int woal_packet_fate_monitor(moal_private *priv, + packet_fate_packet_type pkt_type, t_u8 fate, + frame_type payload_type, t_u32 drv_ts_usec, + t_u32 fw_ts_usec, t_u8 *data, t_u32 len) +{ + int ret = 0; + + ENTER(); + + if (priv->pkt_fate_monitor_enable) + ret = woal_packet_fate_vendor_event(priv, pkt_type, fate, + payload_type, drv_ts_usec, + fw_ts_usec, data, len); + + PRINTM(MINFO, "packet fate monitor ret %d\n", ret); + LEAVE(); + return ret; +} + +/** + * @brief init packet_filter in moal_private + * + * @param priv A pointer to moal_private struct + * + * @return 0: success -1: fail + */ +static int woal_init_packet_filter(moal_private *priv) +{ + int ret = 0; + packet_filter *pkt_filter = NULL; + + ENTER(); + + pkt_filter = vmalloc(sizeof(packet_filter)); + if (!unlikely(pkt_filter)) { + PRINTM(MERROR, "WiFi Logger: packet_filter alloc failed\n"); + ret = -ENOMEM; + goto done; + } + + memset(pkt_filter, 0, sizeof(packet_filter)); + + spin_lock_init(&pkt_filter->lock); + pkt_filter->packet_filter_max_len = PACKET_FILTER_MAX_LEN; + pkt_filter->packet_filter_len = 0; + pkt_filter->packet_filter_version = APF_VERSION; + pkt_filter->state = PACKET_FILTER_STATE_STOP; + + priv->packet_filter = pkt_filter; + +done: + LEAVE(); + return ret; +} + +/** + * @brief deinit packet_filter in moal_private + * + * @param priv A pointer to moal_private struct + * + * @return 0: success -1: fail + */ +static int woal_deinit_packet_filter(moal_private *priv) +{ + int ret = 0; + packet_filter *pkt_filter = NULL; + unsigned long flags; + + ENTER(); + + pkt_filter = priv->packet_filter; + + if (!unlikely(pkt_filter)) + goto done; + + spin_lock_irqsave(&pkt_filter->lock, flags); + pkt_filter->state = PACKET_FILTER_STATE_INIT; + spin_unlock_irqrestore(&pkt_filter->lock, flags); + + vfree(pkt_filter); + priv->packet_filter = NULL; + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to set packet filter + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_set_packet_filter(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0, rem, type; + const struct nlattr *iter; + packet_filter *pkt_filter = NULL; + t_u32 packet_filter_len = 0; + unsigned long flags; + + ENTER(); + pkt_filter = priv->packet_filter; + if (!unlikely(pkt_filter) || + pkt_filter->state == PACKET_FILTER_STATE_INIT) { + PRINTM(MERROR, "packet_filter not init\n"); + ret = -EINVAL; + goto done; + } + + nla_for_each_attr (iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case ATTR_PACKET_FILTER_TOTAL_LENGTH: + packet_filter_len = nla_get_u32(iter); + if (packet_filter_len > + pkt_filter->packet_filter_max_len) { + PRINTM(MERROR, + "packet_filter_len exceed max\n"); + ret = -EINVAL; + goto done; + } + break; + case ATTR_PACKET_FILTER_PROGRAM: + spin_lock_irqsave(&pkt_filter->lock, flags); + strncpy(pkt_filter->packet_filter_program, + nla_data(iter), + MIN(packet_filter_len, nla_len(iter))); + pkt_filter->packet_filter_len = + MIN(packet_filter_len, nla_len(iter)); + pkt_filter->state = PACKET_FILTER_STATE_START; + spin_unlock_irqrestore(&pkt_filter->lock, flags); + DBG_HEXDUMP(MCMD_D, "packet_filter_program", + pkt_filter->packet_filter_program, + pkt_filter->packet_filter_len); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + ret = -EINVAL; + goto done; + } + } + + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to get packet filter capability + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_get_packet_filter_capability( + struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, + int data_len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + t_u32 reply_len = 0; + int ret = 0; + packet_filter *pkt_filter; + + ENTER(); + pkt_filter = priv->packet_filter; + if (!unlikely(pkt_filter)) { + PRINTM(MERROR, "packet_filter not init\n"); + ret = -EINVAL; + goto done; + } + + reply_len = sizeof(pkt_filter->packet_filter_version) + + sizeof(pkt_filter->packet_filter_max_len); + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, reply_len); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = -ENOMEM; + goto done; + } + + if (nla_put_u32(skb, ATTR_PACKET_FILTER_VERSION, + pkt_filter->packet_filter_version) || + nla_put_u32(skb, ATTR_PACKET_FILTER_MAX_LEN, + pkt_filter->packet_filter_max_len)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree_skb(skb); + ret = -ENOMEM; + goto done; + } + + ret = cfg80211_vendor_cmd_reply(skb); + + if (ret) + PRINTM(MERROR, "Vendor command reply failed ret = %d\n", ret); +done: + LEAVE(); + return ret; +} + +/** + * Runs a packet filtering program over a packet. + * + * @param program the program bytecode. + * @param program_len the length of {@code apf_program} in bytes. + * @param packet the packet bytes, starting from the 802.3 header and not + * including any CRC bytes at the end. + * @param packet_len the length of {@code packet} in bytes. + * @param filter_age the number of seconds since the filter was programmed. + * + * @return non-zero if packet should be passed to AP, zero if + * packet should be dropped. + */ +int process_packet(const t_u8 *program, t_u32 program_len, const t_u8 *packet, + t_u32 packet_len, t_u32 filter_age) +{ + /* Program counter */ + t_u32 pc = 0; + /* Memory slot values. */ + t_u32 mem[MEM_ITEMS] = {}; + /* Register values. */ + t_u32 reg[2] = {}; + /* Number of instructions remaining to execute. This is done to make + * sure there is upper bound on execution time. It should never be hit + * and is only for safety. Initialize to the number of bytes in the + * program which is an + * upper bound on the number of instructions in the program. + */ + t_u32 instructions_left = program_len; + + t_u8 bytecode; + t_u32 opcode; + t_u32 reg_num; + t_u32 len_field; + t_u32 imm_len; + t_u32 end_offs; + t_u32 val = 0; + t_u32 last_pkt_offs; + t_u32 imm = 0; + int32_t sign_imm = 0; + t_u32 offs = 0; + t_u32 i; + t_u32 load_size; + +/* Is offset within program bounds? */ +#define WITHIN_PROGRAM_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < program_len) +/* Is offset within packet bounds? */ +#define WITHIN_PACKET_BOUNDS(p) (ENFORCE_UNSIGNED(p) && (p) < packet_len) +/* Verify an internal condition and accept packet if it fails. */ +#define ASSERT_RETURN(c) \ + do { \ + if (!(c)) \ + return PASS_PKT; \ + } while (0) +/* If not within program bounds, Accept the packet */ +#define ASSERT_WITHIN_PROGRAM_BOUNDS(p) ASSERT_RETURN(WITHIN_PROGRAM_BOUNDS(p)) +/* If not within packet bounds, Accept the packet */ +#define ASSERT_WITHIN_PKT_BOUNDS(p) ASSERT_RETURN(WITHIN_PACKET_BOUNDS(p)) +/* If not within program or not ahead of program counter, Accept the packet */ +#define ASSERT_FORWARD_WITHIN_PROGRAM(p) \ + ASSERT_RETURN(WITHIN_PROGRAM_BOUNDS(p) && (p) >= pc) + + /* Fill in pre-filled memory slot values. */ + mem[MEM_OFFSET_PKT_SIZE] = packet_len; + mem[MEM_OFFSET_FILTER_AGE] = filter_age; + ASSERT_WITHIN_PKT_BOUNDS(APF_FRAME_HEADER_SIZE); + + /* Only populate if IP version is IPv4. */ + if ((packet[APF_FRAME_HEADER_SIZE] & 0xf0) == 0x40) { + mem[MEM_OFFSET_IPV4_HEADER_SIZE] = + (packet[APF_FRAME_HEADER_SIZE] & 15) * 4; + } + + do { + if (pc == program_len) + return PASS_PKT; + else if (pc == (program_len + 1)) + return DROP_PKT; + ASSERT_WITHIN_PROGRAM_BOUNDS(pc); + bytecode = program[pc++]; + opcode = GET_OPCODE(bytecode); + reg_num = GET_REGISTER(bytecode); + + /* All instructions have immediate fields, so load them now. */ + len_field = GET_IMM_LENGTH(bytecode); + imm = 0; + sign_imm = 0; + +#define REG (reg[reg_num]) +#define OTHER_REG (reg[reg_num ^ 1]) + + if (len_field) { + imm_len = 1 << (len_field - 1); + ASSERT_FORWARD_WITHIN_PROGRAM(pc + imm_len - 1); + for (i = 0; i < imm_len; i++) + imm = (imm << 8) | program[pc++]; + /* Sign extend imm into signed_imm. */ + sign_imm = imm << ((4 - imm_len) * 8); + sign_imm >>= (4 - imm_len) * 8; + } + switch (opcode) { + case NXP_LDB_OPCODE: + case NXP_LDH_OPCODE: + case NXP_LDW_OPCODE: + case NXP_LDBX_OPCODE: + case NXP_LDHX_OPCODE: + case NXP_LDWX_OPCODE: { + offs = imm; + if (opcode >= NXP_LDBX_OPCODE) { + /* Note: this can overflow and actually decrease + * offs. + */ + offs += reg[1]; + } + ASSERT_WITHIN_PKT_BOUNDS(offs); + switch (opcode) { + case NXP_LDB_OPCODE: + case NXP_LDBX_OPCODE: + load_size = 1; + break; + case NXP_LDH_OPCODE: + case NXP_LDHX_OPCODE: + load_size = 2; + break; + case NXP_LDW_OPCODE: + case NXP_LDWX_OPCODE: + load_size = 4; + break; + /* Immediately enclosing switch statement + * guarantees + * opcode cannot be any other value. + */ + } + end_offs = offs + (load_size - 1); + /* Catch overflow/wrap-around. */ + ASSERT_RETURN(end_offs >= offs); + ASSERT_WITHIN_PKT_BOUNDS(end_offs); + val = 0; + while (load_size--) + val = (val << 8) | packet[offs++]; + REG = val; + break; + } + case NXP_JMP_OPCODE: + /* This can jump backwards. Infinite looping prevented + * by instructions_remaining. + */ + pc += imm; + break; + case NXP_JEQ_OPCODE: + case NXP_JNE_OPCODE: + case NXP_JGT_OPCODE: + case NXP_JLT_OPCODE: + case NXP_JSET_OPCODE: + case NXP_JNEBS_OPCODE: { + /* Load second immediate field. */ + t_u32 cmp_imm = 0; + + if (reg_num == 1) { + cmp_imm = reg[1]; + } else if (len_field != 0) { + t_u32 cmp_imm_len = 1 << (len_field - 1); + + ASSERT_FORWARD_WITHIN_PROGRAM(pc + cmp_imm_len - + 1); + for (i = 0; i < cmp_imm_len; i++) + cmp_imm = + (cmp_imm << 8) | program[pc++]; + } + switch (opcode) { + case NXP_JEQ_OPCODE: + if (reg[0] == cmp_imm) + pc += imm; + break; + case NXP_JNE_OPCODE: + if (reg[0] != cmp_imm) + pc += imm; + break; + case NXP_JGT_OPCODE: + if (reg[0] > cmp_imm) + pc += imm; + break; + case NXP_JLT_OPCODE: + if (reg[0] < cmp_imm) + pc += imm; + break; + case NXP_JSET_OPCODE: + if (reg[0] & cmp_imm) + pc += imm; + break; + case NXP_JNEBS_OPCODE: { + /* cmp_imm is size in bytes of data to compare. + * pc is offset of program bytes to compare. + * imm is jump target offset. + * REG is offset of packet bytes to compare. + */ + ASSERT_FORWARD_WITHIN_PROGRAM(pc + cmp_imm - 1); + ASSERT_WITHIN_PKT_BOUNDS(REG); + last_pkt_offs = REG + cmp_imm - 1; + ASSERT_RETURN(last_pkt_offs >= REG); + ASSERT_WITHIN_PKT_BOUNDS(last_pkt_offs); + if (memcmp(program + pc, packet + REG, cmp_imm)) + pc += imm; + /* skip past comparison bytes */ + pc += cmp_imm; + break; + } + } + break; + } + case NXP_ADD_OPCODE: + reg[0] += reg_num ? reg[1] : imm; + break; + case NXP_MUL_OPCODE: + reg[0] *= reg_num ? reg[1] : imm; + break; + case NXP_DIV_OPCODE: { + const t_u32 div_operand = reg_num ? reg[1] : imm; + + ASSERT_RETURN(div_operand); + reg[0] /= div_operand; + break; + } + case NXP_AND_OPCODE: + reg[0] &= reg_num ? reg[1] : imm; + break; + case NXP_OR_OPCODE: + reg[0] |= reg_num ? reg[1] : imm; + break; + case NXP_SH_OPCODE: { + const int32_t shift_val = + reg_num ? (int32_t)reg[1] : sign_imm; + if (shift_val > 0) + reg[0] <<= shift_val; + else + reg[0] >>= -shift_val; + break; + } + case NXP_LI_OPCODE: + REG = sign_imm; + break; + case NXP_EXT_OPCODE: + if ( +/* If LDM_EXT_OPCODE is 0 and imm is compared with it, a compiler error will + * result, + * instead just enforce that imm is unsigned (so it's always greater or equal to + * 0). + */ +#if NXP_LDM_EXT_OPCODE == 0 + ENFORCE_UNSIGNED(imm) && +#else + imm >= NXP_LDM_EXT_OPCODE && +#endif + imm < (NXP_LDM_EXT_OPCODE + MEM_ITEMS)) { + REG = mem[imm - NXP_LDM_EXT_OPCODE]; + } else if (imm >= NXP_STM_EXT_OPCODE && + imm < (NXP_STM_EXT_OPCODE + MEM_ITEMS)) { + mem[imm - NXP_STM_EXT_OPCODE] = REG; + } else + switch (imm) { + case NXP_NOT_EXT_OPCODE: + REG = ~REG; + break; + case NXP_NEG_EXT_OPCODE: + REG = -REG; + break; + case NXP_SWAP_EXT_OPCODE: { + t_u32 tmp = REG; + + REG = OTHER_REG; + OTHER_REG = tmp; + break; + } + case NXP_MOV_EXT_OPCODE: + REG = OTHER_REG; + break; + /* Unknown extended opcode */ + default: + /* Bail out */ + return PASS_PKT; + } + break; + /* Unknown opcode */ + default: + /* Bail out */ + return PASS_PKT; + } + } while (instructions_left--); + return PASS_PKT; +} + +/** + * @brief filter packet + * + * @param priv A pointer to moal_private struct + * @param data packet data + * @param len packet len + * @param filter_age filter age + * @return non-zero if packet should be passed to AP, zero if + * packet should be dropped. + */ +int woal_filter_packet(moal_private *priv, t_u8 *data, t_u32 len, + t_u32 filter_age) +{ + packet_filter *pkt_filter = NULL; + int ret = PASS_PKT; + unsigned long flags; + + ENTER(); + pkt_filter = priv->packet_filter; + if (!unlikely(pkt_filter)) { + PRINTM(MINFO, "packet_filter not init\n"); + goto done; + } + + if (pkt_filter->state != PACKET_FILTER_STATE_START) + goto done; + + DBG_HEXDUMP(MCMD_D, "packet_filter_program", + pkt_filter->packet_filter_program, + pkt_filter->packet_filter_len); + DBG_HEXDUMP(MCMD_D, "packet_filter_data", data, len); + spin_lock_irqsave(&pkt_filter->lock, flags); + ret = process_packet(pkt_filter->packet_filter_program, + pkt_filter->packet_filter_len, data, len, + filter_age); + spin_unlock_irqrestore(&pkt_filter->lock, flags); + +done: + PRINTM(MINFO, "packet filter ret %d\n", ret); + LEAVE(); + return ret; +} + +/** + * @brief init wifi hal + * + * @param priv A pointer to moal_private struct + * + * @return 0: success 1: fail + */ +int woal_init_wifi_hal(moal_private *priv) +{ + ENTER(); + woal_init_ring_buffer(priv); + priv->pkt_fate_monitor_enable = MFALSE; + woal_init_packet_filter(priv); + + LEAVE(); + return 0; +} + +/** + * @brief deinit wifi hal + * + * @param priv A pointer to moal_private struct + * + * @return 0: success 1: fail + */ +int woal_deinit_wifi_hal(moal_private *priv) +{ + ENTER(); + woal_deinit_ring_buffer(priv); + priv->pkt_fate_monitor_enable = MFALSE; + woal_deinit_packet_filter(priv); + + LEAVE(); + return 0; +} + +/** + * @brief vendor command to get link layer statistic + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success -1: fail + */ +static int woal_cfg80211_subcmd_link_statistic_get(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + struct net_device *dev = wdev->netdev; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct sk_buff *skb = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + wifi_radio_stat *radio_stat = NULL; + wifi_radio_stat *radio_stat_tmp = NULL; + wifi_iface_stat *iface_stat = NULL; + t_u32 num_radio = 0, iface_stat_len = 0, radio_stat_len = 0; + int err = -1, length = 0, i; + char *ioctl_link_stats_buf = NULL; + mlan_ds_get_stats stats; + t_u64 cur_time = 0; + t_u64 inter_msec = 0; + t_u64 max_msec = (t_u64)24 * (t_u64)24 * (t_u64)3600 * (t_u64)1000; + moal_handle *handle = priv->phandle; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(t_u32) + BUF_MAXLEN); + if (req == NULL) { + PRINTM(MERROR, "Could not allocate mlan ioctl request!\n"); + return -ENOMEM; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_LINK_STATS; + req->req_id = MLAN_IOCTL_GET_INFO; + 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 link layer statistic fail\n"); + goto done; + } + + /* Get Log from the firmware */ + memset(&stats, 0, sizeof(mlan_ds_get_stats)); + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { + PRINTM(MERROR, "Error getting stats information\n"); + goto done; + } + + ioctl_link_stats_buf = info->param.link_statistic; + num_radio = *((t_u32 *)info->param.link_statistic); + + radio_stat = (wifi_radio_stat *)(info->param.link_statistic + + sizeof(num_radio)); + radio_stat_len = num_radio * sizeof(wifi_radio_stat); + + /* Re-write on_time/tx_time/rx_time/on_time_scan from moal handle */ + PRINTM(MINFO, "handle->on_time=%llu\n", handle->on_time); + if (handle->on_time) { + moal_get_boot_ktime(handle, &cur_time); + inter_msec = moal_do_div(cur_time - handle->on_time, 1000000); + PRINTM(MINFO, "cur_time=%llu inter_msec=%llu max_msec=%llu\n", + cur_time, inter_msec, max_msec); + /* When we report the time up, u32 is not big enough(represent + * max 49days) and might out of range, make the max value to + * 24days. + */ + if (inter_msec > max_msec) { + PRINTM(MMSG, + "Out of range, set inter_msec=%llu to max_msec=%llu\n", + inter_msec, max_msec); + inter_msec = max_msec; + } + } + PRINTM(MINFO, "handle->tx_time=%llu\n", handle->tx_time); + PRINTM(MINFO, "handle->rx_time=%llu\n", handle->rx_time); + PRINTM(MINFO, "handle->scan_time=%llu\n", handle->scan_time); + radio_stat_tmp = radio_stat; + for (i = 0; i < num_radio; i++) { + radio_stat_tmp->on_time = (t_u32)inter_msec; + radio_stat_tmp->tx_time = + (t_u32)moal_do_div(handle->tx_time, 1000); + radio_stat_tmp->rx_time = + (t_u32)moal_do_div(handle->rx_time, 1000); + radio_stat_tmp->on_time_scan = + (t_u32)moal_do_div(handle->scan_time, 1000); + radio_stat_tmp++; + } + + iface_stat = (wifi_iface_stat *)(info->param.link_statistic + + sizeof(num_radio) + radio_stat_len); + iface_stat_len = sizeof(wifi_iface_stat); + /* Fill some fileds */ + iface_stat->beacon_rx = stats.bcn_rcv_cnt; + + /* could get peer info with separate cmd */ + for (i = 0; i < iface_stat->num_peers; i++) { + /* no need copy, just increase iface_stat length*/ + iface_stat_len += sizeof(wifi_peer_info) + + sizeof(wifi_rate_stat) * + iface_stat->peer_info[i].num_rate; + } + + /* Here the length doesn't contain addition 2 attribute header length */ + length = NLA_HDRLEN * 2 + sizeof(num_radio) + radio_stat_len + + iface_stat_len; + + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + goto done; + } + + if (nla_put_u32(skb, ATTR_LL_STATS_NUM_RADIO, num_radio) || + nla_put(skb, ATTR_LL_STATS_RADIO, radio_stat_len, radio_stat) || + nla_put(skb, ATTR_LL_STATS_IFACE, iface_stat_len, iface_stat)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree_skb(skb); + goto done; + } + + PRINTM(MCMD_D, "%s: <<< Start DUMP\n", __func__); + PRINTM(MCMD_D, "sizeof(wifi_radio_stat)=%zu\n", + sizeof(wifi_radio_stat)); + DBG_HEXDUMP(MCMD_D, "radio_stat", (t_u8 *)radio_stat, radio_stat_len); + PRINTM(MCMD_D, "sizeof(wifi_channel_stat)=%zu\n", + sizeof(wifi_channel_stat)); + DBG_HEXDUMP(MCMD_D, "iface_stat", (t_u8 *)iface_stat, iface_stat_len); + PRINTM(MCMD_D, "num_radio=%d\n", num_radio); + radio_stat_tmp = radio_stat; + for (i = 0; i < num_radio; i++) { + PRINTM(MCMD_D, "--radio_stat[%d]--\n", i); + PRINTM(MCMD_D, "radio=%d\n", radio_stat_tmp->radio); + PRINTM(MCMD_D, "on_time=%d\n", radio_stat_tmp->on_time); + PRINTM(MCMD_D, "tx_time=%d\n", radio_stat_tmp->tx_time); + PRINTM(MCMD_D, "reserved0=%d\n", radio_stat_tmp->reserved0); + PRINTM(MCMD_D, "rx_time=%d\n", radio_stat_tmp->rx_time); + PRINTM(MCMD_D, "on_time_scan=%d\n", + radio_stat_tmp->on_time_scan); + PRINTM(MCMD_D, "on_time_nbd=%d\n", radio_stat_tmp->on_time_nbd); + PRINTM(MCMD_D, "on_time_gscan=%d\n", + radio_stat_tmp->on_time_gscan); + PRINTM(MCMD_D, "on_time_roam_scan=%d\n", + radio_stat_tmp->on_time_roam_scan); + PRINTM(MCMD_D, "on_time_pno_scan=%d\n", + radio_stat_tmp->on_time_pno_scan); + PRINTM(MCMD_D, "on_time_hs20=%d\n", + radio_stat_tmp->on_time_hs20); + PRINTM(MCMD_D, "num_channels=%d\n", + radio_stat_tmp->num_channels); + radio_stat_tmp++; + } + PRINTM(MCMD_D, "%s: >>> End DUMP\n", __func__); + + err = cfg80211_vendor_cmd_reply(skb); + if (unlikely(err)) + PRINTM(MERROR, "Vendor Command reply failed ret:%d\n", err); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + return err; +} + +/** + * @brief API to trigger the link layer statistics collection. + * Unless his API is invoked - link layer statistics will not be collected. + * Radio statistics (once started) do not stop or get reset unless + * wifi_clear_link_stats is invoked, Interface statistics (once started) + * reset and start afresh after each connection. + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success -1: fail + */ +static int woal_cfg80211_subcmd_link_statistic_set(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev); + struct nlattr *tb[ATTR_LL_STATS_MAX + 1]; + wifi_link_layer_params ll_params; + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int err = 0; + + err = nla_parse(tb, ATTR_LL_STATS_MAX, data, len, NULL +#if KERNEL_VERSION(4, 12, 0) <= CFG80211_VERSION_CODE + , + NULL +#endif + ); + if (err) + return err; + + if (!tb[ATTR_LL_STATS_MPDU_SIZE_THRESHOLD] || + !tb[ATTR_LL_STATS_AGGRESSIVE_STATS_GATHERING]) + return -EINVAL; + + ll_params.mpdu_size_threshold = + nla_get_u32(tb[ATTR_LL_STATS_MPDU_SIZE_THRESHOLD]); + ll_params.aggressive_statistics_gathering = + nla_get_u32(tb[ATTR_LL_STATS_AGGRESSIVE_STATS_GATHERING]); + + PRINTM(MEVENT, + "link layer params mpdu_size_threshold = 0x%x, aggressive_statistics_gathering = 0x%x\n", + ll_params.mpdu_size_threshold, + ll_params.aggressive_statistics_gathering); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(t_u32) + + sizeof(mlan_ds_get_info)); + if (req == NULL) { + PRINTM(MERROR, "Could not allocate mlan ioctl request!\n"); + return -ENOMEM; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_LINK_STATS; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_SET; + + /* Configure parameter to firmware */ + moal_memcpy_ext(priv->phandle, info->param.link_statistic, &ll_params, + sizeof(ll_params), + sizeof(mlan_ds_get_info) - sizeof(t_u32)); + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) + PRINTM(MMSG, "enable link layer statistic successfully\n"); + + if (status != MLAN_STATUS_PENDING) + kfree(req); + return 0; +} + +/** + * @brief clear function should download command to fimrware, + * so that firmware could cleanup per peer statistic number + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success -1: fail + */ +static int woal_cfg80211_subcmd_link_statistic_clr(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev); + struct nlattr *tb[ATTR_LL_STATS_MAX + 1]; + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + u32 stats_clear_req_mask = 0x0, stats_clear_rsp_mask = 0x0; + u8 stop_req = 0x0, stop_rsp = 0x0; + struct sk_buff *skb = NULL; + int err = 0, length = 0; + + err = nla_parse(tb, ATTR_LL_STATS_MAX, data, len, NULL +#if KERNEL_VERSION(4, 12, 0) <= CFG80211_VERSION_CODE + , + NULL +#endif + ); + if (err) + return err; + + if (!tb[ATTR_LL_STATS_CLEAR_REQ_MASK]) + return -EINVAL; + else + stats_clear_req_mask = + nla_get_u32(tb[ATTR_LL_STATS_CLEAR_REQ_MASK]); + + if (!tb[ATTR_LL_STATS_STOP_REQ]) + return -EINVAL; + else + stop_req = nla_get_u8(tb[ATTR_LL_STATS_STOP_REQ]); + + PRINTM(MEVENT, + "link layer clear stats_clear_req_mask = 0x%x, stop_req = 0x%x\n", + stats_clear_req_mask, stop_req); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(t_u32) + + sizeof(mlan_ds_get_info)); + if (req == NULL) { + PRINTM(MERROR, "Could not allocate mlan ioctl request!\n"); + return -ENOMEM; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_LINK_STATS; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_CLEAR; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) + PRINTM(MMSG, "enable link layer statistic successfully\n"); + + length = NLA_HDRLEN + sizeof(stats_clear_rsp_mask) + sizeof(stop_rsp); + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, length); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + err = -EINVAL; + goto exit; + } + + /* clear api to reset statistics, stats_clear_rsp_mask identifies what + * stats have been cleared stop_req = 1 will imply whether to stop the + * statistics collection. stop_rsp = 1 will imply that stop_req was + * honored and statistics collection was stopped. + */ + stats_clear_rsp_mask = WIFI_STATS_RADIO | WIFI_STATS_IFACE; + stop_rsp = 1; + if (nla_put_u32(skb, ATTR_LL_STATS_CLEAR_RSP_MASK, + stats_clear_rsp_mask) || + nla_put_u8(skb, ATTR_LL_STATS_STOP_RSP, stop_rsp)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree_skb(skb); + err = -EINVAL; + goto exit; + } + + err = cfg80211_vendor_cmd_reply(skb); + if (unlikely(err)) { + PRINTM(MERROR, "Vendor Command reply failed ret:%d\n", err); + goto exit; + ; + } + +exit: + if (status != MLAN_STATUS_PENDING) + kfree(req); + return err; +} + +#ifdef STA_CFG80211 +#define RSSI_MONOTOR_START 1 +#define RSSI_MONOTOR_STOP 0 + +/** + * @brief vendor command to control rssi monitor + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success -1: fail + */ +static int woal_cfg80211_subcmd_rssi_monitor(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + struct nlattr *tb[ATTR_RSSI_MONITOR_MAX + 1]; + moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev); + u32 rssi_monitor_control = 0x0; + s8 rssi_min = 0, rssi_max = 0; + int err = 0; + t_u8 *pos = NULL; + struct sk_buff *skb = NULL; + int ret = 0; + + ENTER(); + + if (!priv->media_connected) { + ret = -EINVAL; + goto done; + } + + ret = nla_parse(tb, ATTR_RSSI_MONITOR_MAX, data, len, NULL +#if KERNEL_VERSION(4, 12, 0) <= CFG80211_VERSION_CODE + , + NULL +#endif + ); + if (ret) + goto done; + + if (!tb[ATTR_RSSI_MONITOR_CONTROL]) { + ret = -EINVAL; + goto done; + } + rssi_monitor_control = nla_get_u32(tb[ATTR_RSSI_MONITOR_CONTROL]); + + if (rssi_monitor_control == RSSI_MONOTOR_START) { + if ((!tb[ATTR_RSSI_MONITOR_MIN_RSSI]) || + (!tb[ATTR_RSSI_MONITOR_MAX_RSSI])) { + ret = -EINVAL; + goto done; + } + + rssi_min = nla_get_s8(tb[ATTR_RSSI_MONITOR_MIN_RSSI]); + rssi_max = nla_get_s8(tb[ATTR_RSSI_MONITOR_MAX_RSSI]); + + PRINTM(MEVENT, + "start rssi monitor rssi_min = %d, rssi_max= %d\n", + rssi_min, rssi_max); + + /* set rssi low/high threshold */ + priv->cqm_rssi_high_thold = rssi_max; + priv->cqm_rssi_thold = rssi_min; + priv->cqm_rssi_hyst = 4; + woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT); + } else if (rssi_monitor_control == RSSI_MONOTOR_STOP) { + /* stop rssi monitor */ + PRINTM(MEVENT, "stop rssi monitor\n"); + /* set both rssi_thold/hyst to 0, will trigger subscribe event + * clear + */ + priv->cqm_rssi_high_thold = 0; + priv->cqm_rssi_thold = 0; + priv->cqm_rssi_hyst = 0; + woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT); + } else { + PRINTM(MERROR, "invalid rssi_monitor control request\n"); + ret = -EINVAL; + goto done; + } + + /* Alloc the SKB for cmd reply */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, len); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + ret = -EINVAL; + goto done; + } + pos = skb_put(skb, len); + moal_memcpy_ext(priv->phandle, pos, data, len, len); + err = cfg80211_vendor_cmd_reply(skb); + if (unlikely(err)) { + PRINTM(MERROR, "Vendor Command reply failed ret:%d\n", err); + ret = err; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief send rssi event to kernel + * + * @param priv A pointer to moal_private + * @param rssi current rssi value + * + * @return N/A + */ +void woal_cfg80211_rssi_monitor_event(moal_private *priv, t_s16 rssi) +{ + struct sk_buff *skb = NULL; + t_s8 rssi_value = 0; + + ENTER(); + + skb = dev_alloc_skb(NLA_HDRLEN * 2 + ETH_ALEN + sizeof(t_s8)); + if (!skb) + goto done; + /* convert t_s16 to t_s8*/ + rssi_value = -abs(rssi); + if (nla_put(skb, ATTR_RSSI_MONITOR_CUR_BSSID, ETH_ALEN, + priv->conn_bssid) || + nla_put_s8(skb, ATTR_RSSI_MONITOR_CUR_RSSI, rssi_value)) { + PRINTM(MERROR, "nla_put failed!\n"); + kfree(skb); + goto done; + } + woal_cfg80211_vendor_event(priv, event_rssi_monitor, (t_u8 *)skb->data, + skb->len); + kfree(skb); +done: + LEAVE(); +} +#endif + +/** + * @brief vendor command to get fw roaming capability + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int +woal_cfg80211_subcmd_get_roaming_capability(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + int ret = MLAN_STATUS_SUCCESS; + wifi_roaming_capabilities capa; + struct sk_buff *skb = NULL; + int err = 0; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + capa.max_blacklist_size = MAX_AP_LIST; + capa.max_whitelist_size = MAX_SSID_NUM; + + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb( + wiphy, sizeof(wifi_roaming_capabilities) + 50); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + goto done; + } + + /* Push the data to the skb */ + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CAPA, + sizeof(wifi_roaming_capabilities), (t_u8 *)&capa); + + err = cfg80211_vendor_cmd_reply(skb); + if (unlikely(err)) + PRINTM(MERROR, "Vendor Command reply failed ret:%d\n", err); + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to enable/disable fw roaming + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int woal_cfg80211_subcmd_fw_roaming_enable(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + moal_private *priv; + struct net_device *dev; + int ret = MLAN_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + const struct nlattr *iter; + int type, rem, err; + t_u32 fw_roaming_enable = 0; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + if (!priv || !priv->phandle) { + LEAVE(); + return -EFAULT; + } + + nla_for_each_attr (iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONTROL: + fw_roaming_enable = nla_get_u32(iter); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + ret = -EINVAL; + goto done; + } + } + + PRINTM(MMSG, "FW roaming set enable=%d from wifi hal.\n", + fw_roaming_enable); +#if defined(STA_CFG80211) + if (fw_roaming_enable) + priv->roaming_enabled = MTRUE; + else + priv->roaming_enabled = MFALSE; +#endif + /* Alloc the SKB for vendor_event */ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(t_u32) + 50); + if (unlikely(!skb)) { + PRINTM(MERROR, "skb alloc failed\n"); + goto done; + } + + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONTROL, sizeof(t_u32), + &fw_roaming_enable); + err = cfg80211_vendor_cmd_reply(skb); + if (unlikely(err)) + PRINTM(MERROR, "Vendor Command reply failed ret:%d\n", err); + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to config blacklist and whitelist for fw roaming + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int woal_cfg80211_subcmd_fw_roaming_config(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + moal_private *priv; + struct net_device *dev; + int ret = MLAN_STATUS_SUCCESS; + const struct nlattr *iter; + int type, rem; + wifi_bssid_params blacklist; + wifi_ssid_params whitelist; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + if (!priv || !priv->phandle) { + LEAVE(); + return -EFAULT; + } + + memset((char *)&blacklist, 0, sizeof(wifi_bssid_params)); + memset((char *)&whitelist, 0, sizeof(wifi_ssid_params)); + nla_for_each_attr (iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_BSSID: + moal_memcpy_ext(priv->phandle, (t_u8 *)&blacklist, + nla_data(iter), nla_len(iter), + sizeof(wifi_bssid_params)); + break; + case MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_SSID: + moal_memcpy_ext(priv->phandle, (t_u8 *)&whitelist, + nla_data(iter), nla_len(iter), + sizeof(wifi_ssid_params)); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + ret = -EINVAL; + goto done; + } + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to enable/disable 11k + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param data_len data length + * + * @return 0: success <0: fail + */ +static int woal_cfg80211_subcmd_11k_cfg(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + struct nlattr *tb_vendor[ATTR_ND_OFFLOAD_MAX + 1]; + int ret = 0; + int status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + + nla_parse(tb_vendor, ATTR_ND_OFFLOAD_MAX, (struct nlattr *)data, + data_len, NULL +#if KERNEL_VERSION(4, 12, 0) <= CFG80211_VERSION_CODE + , + NULL +#endif + ); + if (!tb_vendor[ATTR_ND_OFFLOAD_CONTROL]) { + PRINTM(MINFO, "%s: ATTR_ND_OFFLOAD not found\n", __func__); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief vendor command to set scan mac oui + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param data_len data length + * + * @return 0: success <0: fail + */ +static int woal_cfg80211_subcmd_set_scan_mac_oui(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + struct nlattr *tb_vendor[ATTR_WIFI_MAX + 1]; + t_u8 mac_oui[3]; + int ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + + nla_parse(tb_vendor, ATTR_WIFI_MAX, (struct nlattr *)data, data_len, + NULL +#if KERNEL_VERSION(4, 12, 0) <= CFG80211_VERSION_CODE + , + NULL +#endif + ); + if (!tb_vendor[ATTR_SCAN_MAC_OUI_SET]) { + PRINTM(MINFO, "%s: ATTR_SCAN_MAC_OUI_SET not found\n", + __func__); + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, mac_oui, + nla_data(tb_vendor[ATTR_SCAN_MAC_OUI_SET]), 3, 3); + moal_memcpy_ext(priv->phandle, priv->random_mac, priv->current_addr, + ETH_ALEN, MLAN_MAC_ADDR_LENGTH); + moal_memcpy_ext(priv->phandle, priv->random_mac, mac_oui, 3, + MLAN_MAC_ADDR_LENGTH); + PRINTM(MCMND, "random_mac is " FULL_MACSTR "\n", + FULL_MAC2STR(priv->random_mac)); +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to start keep alive + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int woal_cfg80211_subcmd_start_keep_alive(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + moal_private *priv; + struct net_device *dev; + int ret = MLAN_STATUS_SUCCESS; + int type, rem; + t_u8 mkeep_alive_id = 0; + t_u16 ether_type = 0; + t_u8 *ip_pkt = NULL; + t_u16 ip_pkt_len = 0; + t_u8 src_mac[ETH_ALEN]; + t_u8 dst_mac[ETH_ALEN]; + t_u32 period_msec = 0; + t_u32 retry_interval = 0; + t_u8 retry_cnt = 0; + const struct nlattr *iter; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + if (!priv) { + LEAVE(); + return -EFAULT; + } + + nla_for_each_attr (iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case MKEEP_ALIVE_ATTRIBUTE_ID: + mkeep_alive_id = nla_get_u8(iter); + break; + case MKEEP_ALIVE_ATTRIBUTE_ETHER_TYPE: + ether_type = nla_get_u16(iter); + break; + case MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN: + ip_pkt_len = nla_get_u16(iter); + if (ip_pkt_len > MKEEP_ALIVE_IP_PKT_MAX) { + ret = -EINVAL; + goto exit; + } + break; + case MKEEP_ALIVE_ATTRIBUTE_IP_PKT: + if (ip_pkt_len) { + ip_pkt = (u8 *)kzalloc(ip_pkt_len, GFP_ATOMIC); + if (ip_pkt == NULL) { + ret = -ENOMEM; + PRINTM(MERROR, + "Failed to allocate mem for ip packet\n"); + goto exit; + } + moal_memcpy_ext(priv->phandle, ip_pkt, + (u8 *)nla_data(iter), + nla_len(iter), ip_pkt_len); + } + break; + case MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR: + moal_memcpy_ext(priv->phandle, src_mac, nla_data(iter), + nla_len(iter), ETH_ALEN); + break; + case MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR: + moal_memcpy_ext(priv->phandle, dst_mac, nla_data(iter), + nla_len(iter), ETH_ALEN); + break; + case MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC: + period_msec = nla_get_u32(iter); + break; + case MKEEP_ALIVE_ATTRIBUTE_RETRY_INTERVAL: + retry_interval = nla_get_u32(iter); + break; + case MKEEP_ALIVE_ATTRIBUTE_RETRY_CNT: + retry_cnt = nla_get_u8(iter); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + ret = -EINVAL; + goto exit; + } + } + + ret = woal_priv_save_cloud_keep_alive_params( + priv, mkeep_alive_id, true, ether_type, ip_pkt, ip_pkt_len, + src_mac, dst_mac, period_msec, retry_interval, retry_cnt); + if (ret < 0) + PRINTM(MERROR, "start_mkeep_alive is failed ret: %d\n", ret); + +exit: + if (ip_pkt) + kfree(ip_pkt); + + LEAVE(); + return ret; +} + +/** + * @brief vendor command to stop keep alive + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success fail otherwise + */ +static int woal_cfg80211_subcmd_stop_keep_alive(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int len) +{ + moal_private *priv; + struct net_device *dev; + int ret = MLAN_STATUS_SUCCESS; + int type, rem; + t_u8 mkeep_alive_id = 0; + const struct nlattr *iter; + + ENTER(); + + if (!wdev || !wdev->netdev) { + LEAVE(); + return -EFAULT; + } + + dev = wdev->netdev; + priv = (moal_private *)woal_get_netdev_priv(dev); + if (!priv) { + LEAVE(); + return -EFAULT; + } + + nla_for_each_attr (iter, data, len, rem) { + type = nla_type(iter); + switch (type) { + case MKEEP_ALIVE_ATTRIBUTE_ID: + mkeep_alive_id = nla_get_u8(iter); + break; + default: + PRINTM(MERROR, "Unknown type: %d\n", type); + ret = -EINVAL; + break; + } + } + + ret = woal_stop_mkeep_alive(priv, mkeep_alive_id, 0, NULL, NULL); + if (ret < 0) + PRINTM(MERROR, "stop_mkeep_alive is failed ret: %d\n", ret); + + LEAVE(); + return ret; +} + +/** + * @brief Upload last keep alive packet to Host through vendor + event + * + * @param priv Pointer to moal_private structure + * @param mkeep_alive Pointer to mlan_ds_misc_keep_alive structure + + * @return 0: success fail otherwise + */ +int woal_mkeep_alive_vendor_event(moal_private *priv, + mlan_ds_misc_keep_alive *mkeep_alive) +{ + struct wiphy *wiphy = priv->wdev->wiphy; + struct sk_buff *skb = NULL; + int ret = MLAN_STATUS_SUCCESS; + int event_id = 0; + t_u16 len = 0; + + ENTER(); + + event_id = woal_get_event_id(event_cloud_keep_alive); + if (event_max == event_id) { + PRINTM(MERROR, "Not find this event %d\n", event_id); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (!mkeep_alive) { + PRINTM(MERROR, "Parameter is NULL\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + len = mkeep_alive->pkt_len; + + /**allocate skb*/ +#if KERNEL_VERSION(4, 1, 0) <= CFG80211_VERSION_CODE + skb = cfg80211_vendor_event_alloc(wiphy, priv->wdev, len + 50, +#else + skb = cfg80211_vendor_event_alloc(wiphy, len + 50, +#endif + event_id, GFP_ATOMIC); + + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor event\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + nla_put(skb, MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN, sizeof(t_u16), &len); + nla_put(skb, MKEEP_ALIVE_ATTRIBUTE_IP_PKT, len, mkeep_alive->packet); + + /**send event*/ + cfg80211_vendor_event(skb, GFP_ATOMIC); + +done: + LEAVE(); + return ret; +} + +/** + * @brief vendor command to set enable/disable dfs offload + * + * @param wiphy A pointer to wiphy struct + * @param wdev A pointer to wireless_dev struct + * @param data a pointer to data + * @param len data length + * + * @return 0: success 1: fail + */ +static int woal_cfg80211_subcmd_set_dfs_offload(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct sk_buff *skb = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + int dfs_offload; + int ret = 1; + + ENTER(); + dfs_offload = moal_extflg_isset(handle, EXT_DFS_OFFLOAD); + + /** Allocate skb for cmd reply*/ + skb = cfg80211_vendor_cmd_alloc_reply_skb(wiphy, sizeof(dfs_offload)); + if (!skb) { + PRINTM(MERROR, "allocate memory fail for vendor cmd\n"); + ret = 1; + LEAVE(); + return ret; + } + nla_put(skb, MRVL_WLAN_VENDOR_ATTR_DFS, sizeof(t_u32), &dfs_offload); + ret = cfg80211_vendor_cmd_reply(skb); + + LEAVE(); + return ret; +} + +// clang-format off +const struct wiphy_vendor_command vendor_commands[] = { + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_set_drvdbg, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_set_drvdbg, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_valid_channels, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_valid_channels, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_attr_policy, + .maxattr = ATTR_WIFI_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_set_scan_mac_oui, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_set_scan_mac_oui, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_attr_policy, + .maxattr = ATTR_WIFI_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_link_statistic_set, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_link_statistic_set, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_ll_stat_policy, + .maxattr = ATTR_LL_STATS_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_link_statistic_get, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_link_statistic_get, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_link_statistic_clr, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_link_statistic_clr, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_ll_stat_policy, + .maxattr = ATTR_LL_STATS_MAX, +#endif + }, +#ifdef STA_CFG80211 + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_rssi_monitor, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_rssi_monitor, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_rssi_monitor_policy, + .maxattr = ATTR_RSSI_MONITOR_MAX, +#endif + }, +#endif + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_roaming_capability, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_roaming_capability, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_fw_roaming_enable, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_fw_roaming_enable, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_fw_roaming_policy, + .maxattr = MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_fw_roaming_config, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_fw_roaming_config, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_fw_roaming_policy, + .maxattr = MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_start_keep_alive, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_start_keep_alive, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_keep_alive_policy, + .maxattr = MKEEP_ALIVE_ATTRIBUTE_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_stop_keep_alive, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_stop_keep_alive, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_dfs_capability, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_set_dfs_offload, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + + + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_nd_offload + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_11k_cfg, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_nd_offload_policy, + .maxattr = ATTR_ND_OFFLOAD_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_drv_version, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_drv_version, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_fw_version, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_fw_version, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_wifi_supp_feature_set, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_supp_feature_set, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_set_country_code, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_set_country_code, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = + sub_cmd_get_wifi_logger_supp_feature_set, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV, + .doit = woal_cfg80211_subcmd_get_wifi_logger_supp_feature_set, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_logger_policy, + .maxattr = ATTR_WIFI_LOGGER_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_ring_buff_status, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = woal_cfg80211_subcmd_get_ring_buff_status, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_logger_policy, + .maxattr = ATTR_WIFI_LOGGER_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_start_logging, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = woal_cfg80211_subcmd_start_logging, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_logger_policy, + .maxattr = ATTR_WIFI_LOGGER_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_ring_buff_data, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = woal_cfg80211_subcmd_get_ring_data, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_logger_policy, + .maxattr = ATTR_WIFI_LOGGER_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_start_packet_fate_monitor, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = woal_cfg80211_subcmd_start_packet_fate_monitor, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_fw_mem_dump, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = woal_cfg80211_subcmd_get_fw_dump, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_drv_mem_dump, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = woal_cfg80211_subcmd_get_drv_dump, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_set_packet_filter, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = woal_cfg80211_subcmd_set_packet_filter, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = woal_packet_filter_policy, + .maxattr = ATTR_PACKET_FILTER_MAX, +#endif + }, + { + .info = { + .vendor_id = MRVL_VENDOR_ID, + .subcmd = sub_cmd_get_packet_filter_capability, + }, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = woal_cfg80211_subcmd_get_packet_filter_capability, +#if KERNEL_VERSION(5, 3, 0) <= CFG80211_VERSION_CODE + .policy = VENDOR_CMD_RAW_DATA, +#endif + }, +}; +// clang-format on + +/** + * @brief register vendor commands and events + * + * @param wiphy A pointer to wiphy struct + * + * @return + */ +void woal_register_cfg80211_vendor_command(struct wiphy *wiphy) +{ + ENTER(); + wiphy->vendor_commands = vendor_commands; + wiphy->n_vendor_commands = ARRAY_SIZE(vendor_commands); + wiphy->vendor_events = vendor_events; + wiphy->n_vendor_events = ARRAY_SIZE(vendor_events); + LEAVE(); +} +#endif diff --git a/mxm_wifiex/wlan_src/mlinux/moal_cfg80211_util.h b/mxm_wifiex/wlan_src/mlinux/moal_cfg80211_util.h new file mode 100644 index 0000000..d98eca3 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_cfg80211_util.h @@ -0,0 +1,781 @@ +/** @file moal_cfg80211_util.h + * + * @brief This file contains the CFG80211 vendor specific defines. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _MOAL_CFGVENDOR_H_ +#define _MOAL_CFGVENDOR_H_ + +#include "moal_main.h" + +#if KERNEL_VERSION(3, 14, 0) <= CFG80211_VERSION_CODE +#define RING_NAME_MAX 32 +typedef int wifi_ring_buffer_id; + +#define VALID_RING(id) (id >= 0 && id < RING_ID_MAX) + +/** WiFi ring control structure */ +typedef struct _wifi_ring_ctrl { + /** Written bytes */ + t_u32 written_bytes; + /** Read Bytes */ + t_u32 read_bytes; + /** Written records */ + t_u32 written_records; +} ring_buffer_ctrl; + +enum ring_state { + /** ring is not initialized*/ + RING_STOP = 0, + /** ring is live and logging*/ + RING_ACTIVE, + /** ring is initialized but not logging*/ + RING_SUSPEND, +}; + +/** WiFi ring buffer sttructure */ +typedef struct _wifi_ring_buffer { + /** Ring ID */ + wifi_ring_buffer_id ring_id; + /** Ring name */ + t_u8 name[RING_NAME_MAX]; + /** Ring size */ + t_u32 ring_size; + /** Write pointer */ + t_u32 wp; + /** Read pointer */ + t_u32 rp; + /** Log level */ + t_u32 log_level; + /** Threshold */ + t_u32 threshold; + /** Ring buffer */ + void *ring_buf; + /** Lock */ + spinlock_t lock; + /** Buffer control */ + ring_buffer_ctrl ctrl; + /** Ring state */ + enum ring_state state; + /** Delayed work */ + struct delayed_work work; + /** Interval */ + unsigned long interval; + /** Moal priv */ + moal_private *priv; +} wifi_ring_buffer; + +#define VERBOSE_RING_NAME "verbose" +#define EVENT_RING_NAME "event" + +#define DEFAULT_RING_BUFFER_SIZE 1024 + +#define TLV_LOG_HEADER_LEN 4 + +#define WIFI_LOGGER_MEMORY_DUMP_SUPPORTED MBIT(0) /* Memory dump of Fw*/ +#define WIFI_LOGGER_PER_PACKET_TX_RX_STATUS_SUPPORT MBIT(1) /*PKT status*/ +#define WIFI_LOGGER_CONNECT_EVENT_SUPPORTED MBIT(2) /* connectivity event*/ +#define WIFI_LOGGER_POWER_EVENT_SUPPORTED MBIT(3) /* Power of driver*/ +#define WIFI_LOGGER_WAKE_LOCK_SUPPORTED MBIT(4) /* Wake lock of driver*/ +#define WIFI_LOGGER_VERBOSE_SUPPORTED MBIT(5) /*verbose log of Fw*/ +#define WIFI_LOGGER_WATCHDOG_TIMER_SUPPORTED \ + MBIT(6) /*monitor the health of Fw*/ + +/** + * Parameters of wifi logger events are TLVs + * Event parameters tags are defined as: + */ +#define WIFI_TAG_VENDOR_SPECIFIC 0 // take a byte stream as parameter +#define WIFI_TAG_BSSID 1 // takes a 6 bytes MAC address as parameter +#define WIFI_TAG_ADDR 2 // takes a 6 bytes MAC address as parameter +#define WIFI_TAG_SSID 3 // takes a 32 bytes SSID address as parameter +#define WIFI_TAG_STATUS 4 // takes an integer as parameter +#define WIFI_TAG_REASON_CODE 14 // take a reason code as per 802.11 as parameter +#define WIFI_TAG_RSSI 21 // take an integer as parameter +#define WIFI_TAG_CHANNEL 22 // take an integer as parameter + +#define RING_ENTRY_SIZE (sizeof(wifi_ring_buffer_entry)) +#define ENTRY_LENGTH(hdr) (hdr->entry_size + RING_ENTRY_SIZE) +#define READ_AVAIL_SPACE(ring) \ + (ring->ctrl.written_bytes - ring->ctrl.read_bytes) + +enum logger_attributes { + ATTR_WIFI_LOGGER_INVALID = 0, + ATTR_WIFI_LOGGER_RING_ID, + ATTR_WIFI_LOGGER_FLAGS, + ATTR_WIFI_LOGGER_VERBOSE_LEVEL, + ATTR_WIFI_LOGGER_MIN_DATA_SIZE, + ATTR_RING_BUFFER_STATUS, + ATTR_NUM_RINGS, + ATTR_WIFI_LOGGER_FEATURE_SET, + ATTR_WIFI_LOGGER_MAX_INTERVAL_SEC, + ATTR_RING_BUFFER, + ATTR_NAME, + ATTR_MEM_DUMP, + ATTR_ERR_CODE, + ATTR_RING_DATA, + ATTR_WAKE_REASON_STAT, + ATTR_PACKET_FATE_TX, + ATTR_PACKET_FATE_RX, + ATTR_PACKET_FATE_DATA, + ATTR_FW_DUMP_PATH = 20, + ATTR_DRV_DUMP_PATH = 21, + ATTR_WIFI_LOGGER_AFTER_LAST, + ATTR_WIFI_LOGGER_MAX = ATTR_WIFI_LOGGER_AFTER_LAST - 1 +}; + +/* Below events refer to the wifi_connectivity_event ring and shall be supported + */ +enum { WIFI_EVENT_ASSOCIATION_REQUESTED = 0, + WIFI_EVENT_AUTH_COMPLETE, + WIFI_EVENT_ASSOC_COMPLETE, +}; + +enum { + /* set for binary entries */ + RING_BUFFER_ENTRY_FLAGS_HAS_BINARY = (1 << (0)), + /* set if 64 bits timestamp is present */ + RING_BUFFER_ENTRY_FLAGS_HAS_TIMESTAMP = (1 << (1)) +}; + +enum { ENTRY_TYPE_CONNECT_EVENT = 1, + ENTRY_TYPE_PKT, + ENTRY_TYPE_WAKE_LOCK, + ENTRY_TYPE_POWER_EVENT, + ENTRY_TYPE_DATA }; + +/** WiFi ring buffer entry structure */ +typedef struct { + /** size of payload excluding the header */ + t_u16 entry_size; + /** Flags */ + t_u8 flags; + /** entry type */ + t_u8 type; + /** present if has_timestamp bit is set. */ + t_u64 timestamp; +} __attribute__((packed)) wifi_ring_buffer_entry; + +/** WiFi ring buffer status structure*/ +typedef struct _wifi_ring_buffer_status { + /** Ring name */ + t_u8 name[RING_NAME_MAX]; + /** Flag */ + t_u32 flag; + /** Ring ID */ + wifi_ring_buffer_id ring_id; + /** Buffer size */ + t_u32 ring_buffer_byte_size; + /** Verbose Level */ + t_u32 verbose_level; + /** Written bytes */ + t_u32 written_bytes; + /** Read bytes */ + t_u32 read_bytes; + /** Written records */ + t_u32 written_records; +} wifi_ring_buffer_status; + +/** TLV log structure */ +typedef struct { + /** Tag */ + u16 tag; + /** Length of value*/ + u16 length; + /** Value */ + u8 value[]; +} __attribute__((packed)) tlv_log; + +/** WiFi ring buffer driver structure */ +typedef struct { + /** event */ + u16 event; + /** TLV log structure array */ + tlv_log tlvs[]; + /** separate parameter structure per event to be provided and optional + * data the event_data is expected to include an official android part, + * with some parameter as transmit rate, num retries, num scan result + * found etc... as well, event_data can include a vendor proprietary + * part which is understood by the developer only + */ +} __attribute__((packed)) wifi_ring_buffer_driver_connectivity_event; + +/** Assoc logger data structure */ +typedef struct _assoc_logger { + /** vendor specific */ + t_u8 oui[3]; + /** BSSID */ + t_u8 bssid[MLAN_MAC_ADDR_LENGTH]; + /** SSID */ + t_u8 ssid[MLAN_MAX_SSID_LENGTH]; + /** RSSI */ + t_s32 rssi; + /** Channel */ + t_u32 channel; +} assoc_logger_data; + +int woal_ring_event_logger(moal_private *priv, int ring_id, + pmlan_event pmevent); + +int woal_wake_reason_logger(moal_private *priv, + mlan_ds_hs_wakeup_reason wake_reason); + +#define MD5_PREFIX_LEN 4 +#define MAX_FATE_LOG_LEN 32 +#define MAX_FRAME_LEN_ETHERNET 1518 +#define MAX_FRAME_LEN_80211_MGMT 2352 + +/** packet_fate_packet_type */ +typedef enum { + PACKET_TYPE_TX, + PACKET_TYPE_RX, +} packet_fate_packet_type; + +/** packet fate frame_type */ +typedef enum { + FRAME_TYPE_UNKNOWN, + FRAME_TYPE_ETHERNET_II, + FRAME_TYPE_80211_MGMT, +} frame_type; + +/** wifi_tx_packet_fate */ +typedef enum { + /** Sent over air and ACKed. */ + TX_PKT_FATE_ACKED, + + /** Sent over air but not ACKed. (Normal for broadcast/multicast.) */ + TX_PKT_FATE_SENT, + + /** Queued within firmware, but not yet sent over air. */ + TX_PKT_FATE_FW_QUEUED, + + /** Dropped by firmware as invalid. E.g. bad source address, bad + * checksum, or invalid for current state. + */ + TX_PKT_FATE_FW_DROP_INVALID, + + /** Dropped by firmware due to lack of buffer space. */ + TX_PKT_FATE_FW_DROP_NOBUFS, + + /** Dropped by firmware for any other reason. Includes frames that were + * sent by driver to firmware, but unaccounted for by firmware. + */ + TX_PKT_FATE_FW_DROP_OTHER, + + /** Queued within driver, not yet sent to firmware. */ + TX_PKT_FATE_DRV_QUEUED, + + /** Dropped by driver as invalid. E.g. bad source address, or invalid + * for current state. + */ + TX_PKT_FATE_DRV_DROP_INVALID, + + /** Dropped by driver due to lack of buffer space. */ + TX_PKT_FATE_DRV_DROP_NOBUFS, + + /** Dropped by driver for any other reason. */ + TX_PKT_FATE_DRV_DROP_OTHER, +} wifi_tx_packet_fate; + +/** wifi_rx_packet_fate */ +typedef enum { + /** Valid and delivered to network stack (e.g., netif_rx()). */ + RX_PKT_FATE_SUCCESS, + + /** Queued within firmware, but not yet sent to driver. */ + RX_PKT_FATE_FW_QUEUED, + + /** Dropped by firmware due to host-programmable filters. */ + RX_PKT_FATE_FW_DROP_FILTER, + + /** Dropped by firmware as invalid. E.g. bad checksum, decrypt failed, + * or invalid for current state. + */ + RX_PKT_FATE_FW_DROP_INVALID, + + /** Dropped by firmware due to lack of buffer space. */ + RX_PKT_FATE_FW_DROP_NOBUFS, + + /** Dropped by firmware for any other reason. */ + RX_PKT_FATE_FW_DROP_OTHER, + + /** Queued within driver, not yet delivered to network stack. */ + RX_PKT_FATE_DRV_QUEUED, + + /** Dropped by driver due to filter rules. */ + RX_PKT_FATE_DRV_DROP_FILTER, + + /** Dropped by driver as invalid. E.g. not permitted in current state. + */ + RX_PKT_FATE_DRV_DROP_INVALID, + + /** Dropped by driver due to lack of buffer space. */ + RX_PKT_FATE_DRV_DROP_NOBUFS, + + /** Dropped by driver for any other reason. */ + RX_PKT_FATE_DRV_DROP_OTHER, +} wifi_rx_packet_fate; + +/** frame_info_i */ +typedef struct { + /** Payload Type */ + frame_type payload_type; + /** Driver timestamp in uS */ + u32 driver_timestamp_usec; + /** FW timestamp in uS */ + u32 firmware_timestamp_usec; + /** Frame Length */ + u32 frame_len; +} frame_info_i; + +/** wifi_tx_report_i */ +typedef struct { + /** MD5 prefix */ + char md5_prefix[MD5_PREFIX_LEN]; + /** TX packet fate */ + wifi_tx_packet_fate fate; + /** frame information */ + frame_info_i frame_inf; +} wifi_tx_report_i; + +/** wifi_rx_report_i */ +typedef struct { + /** MD5 prefix */ + char md5_prefix[MD5_PREFIX_LEN]; + /** TX packet fate */ + wifi_rx_packet_fate fate; + /** frame information */ + frame_info_i frame_inf; +} wifi_rx_report_i; + +/** packet_fate_report_t */ +typedef struct packet_fate_report_t { + union { + wifi_tx_report_i tx_report_i; + wifi_rx_report_i rx_report_i; + } u; +} PACKET_FATE_REPORT; + +int woal_packet_fate_monitor(moal_private *priv, + packet_fate_packet_type pkt_type, t_u8 fate, + frame_type payload_type, + t_u32 driver_timestamp_usec, + t_u32 firmware_timestamp_usec, t_u8 *data, + t_u32 len); + +/** =========== Define Copied from apf.h START =========== */ +/* Number of memory slots, see ldm/stm instructions. */ +#define MEM_ITEMS 16 +/* Upon program execution starting some memory slots are prefilled: */ +/* 4*([APF_FRAME_HEADER_SIZE]&15) */ +#define MEM_OFFSET_IPV4_HEADER_SIZE 13 +/* Size of packet in bytes. */ +#define MEM_OFFSET_PKT_SIZE 14 +/* Age since filter installed in seconds. */ +#define MEM_OFFSET_FILTER_AGE 15 + +/* Leave 0 opcode unused as it's a good indicator of accidental incorrect + * execution (e.g. data). + */ +/* Load 1 byte from immediate offset, e.g. "ldb R0, [5]" */ +#define NXP_LDB_OPCODE 1 +/* Load 2 bytes from immediate offset, e.g. "ldh R0, [5]" */ +#define NXP_LDH_OPCODE 2 +/* Load 4 bytes from immediate offset, e.g. "ldw R0, [5]" */ +#define NXP_LDW_OPCODE 3 +/* Load 1 byte from immediate offset plus register, e.g. "ldbx R0, [5]R0" */ +#define NXP_LDBX_OPCODE 4 +/* Load 2 byte from immediate offset plus register, e.g. "ldhx R0, [5]R0" */ +#define NXP_LDHX_OPCODE 5 +/* Load 4 byte from immediate offset plus register, e.g. "ldwx R0, [5]R0" */ +#define NXP_LDWX_OPCODE 6 +/* Add, e.g. "add R0,5" */ +#define NXP_ADD_OPCODE 7 +/* Multiply, e.g. "mul R0,5" */ +#define NXP_MUL_OPCODE 8 +/* Divide, e.g. "div R0,5" */ +#define NXP_DIV_OPCODE 9 +/* And, e.g. "and R0,5" */ +#define NXP_AND_OPCODE 10 +/* Or, e.g. "or R0,5" */ +#define NXP_OR_OPCODE 11 +/* Left shift, e.g, "sh R0, 5" or "sh R0, -5" (shifts right) */ +#define NXP_SH_OPCODE 12 +/* Load immediate, e.g. "li R0,5" (immediate encoded as signed value) */ +#define NXP_LI_OPCODE 13 +/* Unconditional jump, e.g. "jmp label" */ +#define NXP_JMP_OPCODE 14 +/* Compare equal and branch, e.g. "jeq R0,5,label" */ +#define NXP_JEQ_OPCODE 15 +/* Compare not equal and branch, e.g. "jne R0,5,label" */ +#define NXP_JNE_OPCODE 16 +/* Compare greater than and branch, e.g. "jgt R0,5,label" */ +#define NXP_JGT_OPCODE 17 +/* Compare less than and branch, e.g. "jlt R0,5,label" */ +#define NXP_JLT_OPCODE 18 +/* Compare any bits set and branch, e.g. "jset R0,5,label" */ +#define NXP_JSET_OPCODE 19 +/* Compare not equal byte sequence, e.g. "jnebs R0,5,label,0x1122334455" */ +#define NXP_JNEBS_OPCODE 20 +/* Immediate value is one of *_EXT_OPCODE + * Extended opcodes. These all have an opcode of EXT_OPCODE + * and specify the actual opcode in the immediate field. + */ +#define NXP_EXT_OPCODE 21 +/* Load from memory, e.g. "ldm R0,5" + * Values 0-15 represent loading the different memory slots. + */ +#define NXP_LDM_EXT_OPCODE 0 +/* Store to memory, e.g. "stm R0,5" * + * Values 16-31 represent storing to the different memory slots. + */ +#define NXP_STM_EXT_OPCODE 16 +/* Not, e.g. "not R0" */ +#define NXP_NOT_EXT_OPCODE 32 +/* Negate, e.g. "neg R0" */ +#define NXP_NEG_EXT_OPCODE 33 +/* Swap, e.g. "swap R0,R1" */ +#define NXP_SWAP_EXT_OPCODE 34 +/* Move, e.g. "move R0,R1" */ +#define NXP_MOV_EXT_OPCODE 35 + +#define GET_OPCODE(i) (((i) >> 3) & 31) +#define GET_REGISTER(i) ((i)&1) +#define GET_IMM_LENGTH(i) (((i) >> 1) & 3) +/** =========== Define Copied from apf.h END =========== */ + +/** =========== Define Copied from apf_interpreter.h START =========== */ +/** + * Version of APF instruction set processed by accept_packet(). + * Should be returned by wifi_get_packet_filter_info. + */ +#define APF_VERSION 2 +/** =========== Define Copied from apf_interpreter.h END =========== */ + +/** =========== Define Copied from apf_interpreter.c START =========== */ +/* Return code indicating "packet" should accepted. */ +#define PASS_PKT 1 +/* Return code indicating "packet" should be dropped. */ +#define DROP_PKT 0 +/* If "c" is of an unsigned type, generate a compile warning that gets promoted + * to an error. This makes bounds checking simpler because ">= 0" can be + * avoided. Otherwise adding superfluous ">= 0" with unsigned expressions + * generates compile warnings. + */ +#define ENFORCE_UNSIGNED(c) ((c) == (uint32_t)(c)) +/** =========== Define Copied from apf_interpreter.c END =========== */ + +/** depend on the format of skb->data */ +#define APF_FRAME_HEADER_SIZE 14 +#define PACKET_FILTER_MAX_LEN 1024 + +enum { PACKET_FILTER_STATE_INIT = 0, + PACKET_FILTER_STATE_STOP, + PACKET_FILTER_STATE_START, +}; + +enum wifi_attr_packet_filter { + ATTR_PACKET_FILTER_INVALID = 0, + ATTR_PACKET_FILTER_TOTAL_LENGTH, + ATTR_PACKET_FILTER_PROGRAM, + ATTR_PACKET_FILTER_VERSION, + ATTR_PACKET_FILTER_MAX_LEN, + ATTR_PACKET_FILTER_AFTER_LAST, + ATTR_PACKET_FILTER_MAX = ATTR_PACKET_FILTER_AFTER_LAST - 1 +}; + +/** Packet filter structure */ +typedef struct _packet_filter { + spinlock_t lock; + t_u8 state; + t_u8 packet_filter_program[PACKET_FILTER_MAX_LEN]; + t_u8 packet_filter_len; + t_u32 packet_filter_version; + t_u32 packet_filter_max_len; +} packet_filter; + +int woal_filter_packet(moal_private *priv, t_u8 *data, t_u32 len, + t_u32 filter_age); + +int woal_init_wifi_hal(moal_private *priv); +int woal_deinit_wifi_hal(moal_private *priv); + +#define ATTRIBUTE_U32_LEN (nla_total_size(NLA_HDRLEN + 4)) +#define VENDOR_ID_OVERHEAD ATTRIBUTE_U32_LEN +#define VENDOR_SUBCMD_OVERHEAD ATTRIBUTE_U32_LEN +#define VENDOR_DATA_OVERHEAD (nla_total_size(NLA_HDRLEN)) + +#define VENDOR_REPLY_OVERHEAD \ + (VENDOR_ID_OVERHEAD + VENDOR_SUBCMD_OVERHEAD + VENDOR_DATA_OVERHEAD) + +/* Features Enums*/ +#define WLAN_FEATURE_INFRA 0x0001 // Basic infrastructure mode support +#define WLAN_FEATURE_INFRA_5G 0x0002 // 5 GHz Band support +#define WLAN_FEATURE_HOTSPOT 0x0004 // GAS/ANQP support +#define WLAN_FEATURE_P2P 0x0008 // Wifi-Direct/P2P +#define WLAN_FEATURE_SOFT_AP 0x0010 // Soft AP support +#define WLAN_FEATURE_GSCAN 0x0020 // Google-Scan APIsi support +#define WLAN_FEATURE_NAN 0x0040 // Neighbor Awareness Networking (NAN) +#define WLAN_FEATURE_D2D_RTT 0x0080 // Device-to-device RTT support +#define WLAN_FEATURE_D2AP_RTT 0x0100 // Device-to-AP RTT support +#define WLAN_FEATURE_BATCH_SCAN 0x0200 // Batched Scan (legacy) support +#define WLAN_FEATURE_PNO 0x0400 // Preferred network offload support +#define WLAN_FEATURE_ADDITIONAL_STA 0x0800 // Two STAs support +#define WLAN_FEATURE_TDLS 0x1000 // Tunnel directed link setup (TDLS) +#define WLAN_FEATURE_TDLS_OFFCHANNEL 0x2000 // TDLS off channel support +#define WLAN_FEATURE_EPR 0x4000 // Enhanced power reporting support +#define WLAN_FEATURE_AP_STA 0x8000 // AP STA Concurrency support +#define WLAN_FEATURE_LINK_LAYER_STATS \ + 0x10000 // Link layer stats collection support +#define WLAN_FEATURE_LOGGER 0x20000 // WiFi Logger support +#define WLAN_FEATURE_HAL_EPNO 0x40000 // WiFi enhanced PNO support +#define WLAN_FEATURE_RSSI_MONITOR 0x80000 // RSSI Monitor support +#define WLAN_FEATURE_MKEEP_ALIVE 0x100000 // WiFi mkeep_alive support +#define WLAN_FEATURE_CONFIG_NDO 0x200000 // ND offload configure support +#define WLAN_FEATURE_TX_TRANSMIT_POWER \ + 0x400000 // Capture Tx transmit power levels +#define WLAN_FEATURE_CONTROL_ROAMING 0x800000 // Enable/Disable firmware roaming +#define WLAN_FEATURE_IE_WHITELIST 0x1000000 // Probe IE white listing support +#define WLAN_FEATURE_SCAN_RAND \ + 0x2000000 // MAC & Probe Sequence Number randomization Support +// Add more features here + +#define MAX_CHANNEL_NUM 200 + +/** Wifi Band */ +typedef enum { + WIFI_BAND_UNSPECIFIED, + /** 2.4 GHz */ + WIFI_BAND_BG = 1, + /** 5 GHz without DFS */ + WIFI_BAND_A = 2, + /** 5 GHz DFS only */ + WIFI_BAND_A_DFS = 4, + /** 5 GHz with DFS */ + WIFI_BAND_A_WITH_DFS = 6, + /** 2.4 GHz + 5 GHz; no DFS */ + WIFI_BAND_ABG = 3, + /** 2.4 GHz + 5 GHz with DFS */ + WIFI_BAND_ABG_WITH_DFS = 7, + + /** Keep it last */ + WIFI_BAND_LAST, + WIFI_BAND_MAX = WIFI_BAND_LAST - 1, +} wifi_band; + +typedef enum wifi_attr { + ATTR_FEATURE_SET_INVALID = 0, + ATTR_SCAN_MAC_OUI_SET = 1, + ATTR_FEATURE_SET = 2, + ATTR_NODFS_VALUE = 3, + ATTR_COUNTRY_CODE = 4, + ATTR_CHANNELS_BAND = 5, + ATTR_NUM_CHANNELS = 6, + ATTR_CHANNEL_LIST = 7, + ATTR_GET_CONCURRENCY_MATRIX_SET_SIZE_MAX = 8, + ATTR_GET_CONCURRENCY_MATRIX_SET_SIZE = 9, + ATTR_GET_CONCURRENCY_MATRIX_SET = 10, + ATTR_WIFI_AFTER_LAST, + ATTR_WIFI_MAX = ATTR_WIFI_AFTER_LAST - 1 +} wifi_attr_t; + +enum mrvl_wlan_vendor_attr_wifi_logger { + MRVL_WLAN_VENDOR_ATTR_NAME = 10, +}; + +/**vendor event*/ +enum vendor_event { + event_hang = 0, + event_rssi_monitor = 0x1501, + event_cloud_keep_alive = 0x10003, + event_dfs_radar_detected = 0x10004, + event_dfs_cac_started = 0x10005, + event_dfs_cac_finished = 0x10006, + event_dfs_cac_aborted = 0x10007, + event_dfs_nop_finished = 0x10008, + event_wifi_logger_ring_buffer_data = 0x1000b, + event_wifi_logger_alert, + event_packet_fate_monitor, + event_wake_reason_report, + event_max, +}; + +/** struct dfs_event */ +typedef struct _dfs_event { + /** Frequency */ + int freq; + /** HT enable */ + int ht_enabled; + /** Channel Offset */ + int chan_offset; + /** Channel width */ + enum nl80211_chan_width chan_width; + /** Center Frequency 1 */ + int cf1; + /** Center Frequency 2 */ + int cf2; +} dfs_event; + +void woal_cfg80211_dfs_vendor_event(moal_private *priv, int event, + struct cfg80211_chan_def *chandef); + +enum ATTR_LINK_LAYER_STAT { + ATTR_LL_STATS_INVALID, + ATTR_LL_STATS_MPDU_SIZE_THRESHOLD, + ATTR_LL_STATS_AGGRESSIVE_STATS_GATHERING, + ATTR_LL_STATS_IFACE, + ATTR_LL_STATS_NUM_RADIO, + ATTR_LL_STATS_RADIO, + ATTR_LL_STATS_CLEAR_REQ_MASK, + ATTR_LL_STATS_STOP_REQ, + ATTR_LL_STATS_CLEAR_RSP_MASK, + ATTR_LL_STATS_STOP_RSP, + ATTR_LL_STATS_AFTER_LAST, + ATTR_LL_STATS_MAX = ATTR_LL_STATS_AFTER_LAST - 1, +}; + +enum ATTR_RSSI_MONITOR { + ATTR_RSSI_MONITOR_INVALID, + ATTR_RSSI_MONITOR_CONTROL, + ATTR_RSSI_MONITOR_MIN_RSSI, + ATTR_RSSI_MONITOR_MAX_RSSI, + ATTR_RSSI_MONITOR_CUR_BSSID, + ATTR_RSSI_MONITOR_CUR_RSSI, + ATTR_RSSI_MONITOR_AFTER_LAST, + ATTR_RSSI_MONITOR_MAX = ATTR_RSSI_MONITOR_AFTER_LAST - 1, +}; +void woal_cfg80211_rssi_monitor_event(moal_private *priv, t_s16 rssi); + +/**vendor sub command*/ +enum vendor_sub_command { + sub_cmd_set_drvdbg = 0, + sub_cmd_start_keep_alive = 0x0003, + sub_cmd_stop_keep_alive = 0x0004, + sub_cmd_dfs_capability = 0x0005, + sub_cmd_set_scan_mac_oui = 0x0007, + sub_cmd_set_packet_filter = 0x0011, + sub_cmd_get_packet_filter_capability, + sub_cmd_nd_offload = 0x0100, + sub_cmd_link_statistic_set = 0x1200, + sub_cmd_link_statistic_get = 0x1201, + sub_cmd_link_statistic_clr = 0x1202, + sub_cmd_get_valid_channels = 0x1009, + sub_cmd_get_wifi_supp_feature_set = 0x100a, + sub_cmd_set_country_code = 0x100d, + sub_cmd_get_fw_version = 0x1404, + sub_cmd_get_drv_version = 0x1406, + sub_cmd_start_logging = 0x1400, + sub_cmd_get_wifi_logger_supp_feature_set, + sub_cmd_get_ring_buff_data, + sub_cmd_get_ring_buff_status, + sub_cmd_get_fw_mem_dump = 0x1405, + sub_cmd_get_drv_mem_dump = 0x1407, + sub_cmd_start_packet_fate_monitor = 0x1408, + sub_cmd_rssi_monitor = 0x1500, + /*Sub-command for wifi hal*/ + sub_cmd_get_roaming_capability = 0x1700, + sub_cmd_fw_roaming_enable = 0x1701, + sub_cmd_fw_roaming_config = 0x1702, + sub_cmd_max, +}; + +void woal_register_cfg80211_vendor_command(struct wiphy *wiphy); +int woal_cfg80211_vendor_event(moal_private *priv, int event, t_u8 *data, + int len); + +enum mrvl_wlan_vendor_attr { + MRVL_WLAN_VENDOR_ATTR_INVALID = 0, + /* Used by MRVL_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */ + MRVL_WLAN_VENDOR_ATTR_DFS = 1, + MRVL_WLAN_VENDOR_ATTR_AFTER_LAST, + + MRVL_WLAN_VENDOR_ATTR_MAX = MRVL_WLAN_VENDOR_ATTR_AFTER_LAST - 1, +}; + +typedef enum { + ATTR_ND_OFFLOAD_INVALID = 0, + ATTR_ND_OFFLOAD_CONTROL, + ATTR_ND_OFFLOAD_AFTER_LAST, + ATTR_ND_OFFLOAD_MAX = ATTR_ND_OFFLOAD_AFTER_LAST - 1, +} ND_OFFLOAD_ATTR; + +#define MKEEP_ALIVE_IP_PKT_MAX 256 +enum mkeep_alive_attributes { + MKEEP_ALIVE_ATTRIBUTE_INVALID = 0, + MKEEP_ALIVE_ATTRIBUTE_ID, + MKEEP_ALIVE_ATTRIBUTE_ETHER_TYPE, + MKEEP_ALIVE_ATTRIBUTE_IP_PKT, + MKEEP_ALIVE_ATTRIBUTE_IP_PKT_LEN, + MKEEP_ALIVE_ATTRIBUTE_SRC_MAC_ADDR, + MKEEP_ALIVE_ATTRIBUTE_DST_MAC_ADDR, + MKEEP_ALIVE_ATTRIBUTE_PERIOD_MSEC, + MKEEP_ALIVE_ATTRIBUTE_RETRY_INTERVAL, + MKEEP_ALIVE_ATTRIBUTE_RETRY_CNT, + MKEEP_ALIVE_ATTRIBUTE_AFTER_LAST, + MKEEP_ALIVE_ATTRIBUTE_MAX = MKEEP_ALIVE_ATTRIBUTE_AFTER_LAST - 1 +}; + +/** WiFi roaming capabilities structure */ +typedef struct { + /** max blacklist size */ + u32 max_blacklist_size; + /** max whitelist size */ + u32 max_whitelist_size; +} wifi_roaming_capabilities; + +/** WiFi BSSID params structure */ +typedef struct { + /** Num of BSSID */ + u32 num_bssid; + /** List of AP mac address */ + t_u8 mac_addr[MAX_AP_LIST][MLAN_MAC_ADDR_LENGTH]; +} wifi_bssid_params; + +/** SSID structure */ +typedef struct { + /** Length */ + u32 length; + /** SSID */ + char ssid[MLAN_MAX_SSID_LENGTH]; +} ssid_t; + +/** WiFi SSID params structure */ +typedef struct { + /** No of SSID */ + u32 num_ssid; + /** Whitelist SSID */ + ssid_t whitelist_ssid[MAX_SSID_NUM]; +} wifi_ssid_params; + +/*Attribute for wifi hal*/ +enum mrvl_wlan_vendor_attr_fw_roaming { + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_INVALID = 0, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CAPA, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONTROL, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_BSSID, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_CONFIG_SSID, + /* keep last */ + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_AFTER_LAST, + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_MAX = + MRVL_WLAN_VENDOR_ATTR_FW_ROAMING_AFTER_LAST - 1 +}; + +#endif +#endif /* _MOAL_CFGVENDOR_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_debug.c b/mxm_wifiex/wlan_src/mlinux/moal_debug.c new file mode 100644 index 0000000..10da4dc --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_debug.c @@ -0,0 +1,1468 @@ +/** @file moal_debug.c + * + * @brief This file contains functions for debug proc file. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" +#ifdef USB +#include "moal_usb.h" +#endif + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Variables +********************************************************/ +#ifdef CONFIG_PROC_FS + +/** Get info item size */ +#define item_size(n) (sizeof(((mlan_debug_info *)0)->n)) +/** Get info item address */ +#define item_addr(n) ((t_ptr) & (((mlan_debug_info *)0)->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 USB +/** Get moal card member size */ +#define item_card_size(n) (sizeof(((struct usb_card_rec *)0)->n)) +/** Get moal card member address */ +#define item_card_addr(n) ((t_ptr) & (((struct usb_card_rec *)0)->n)) +#endif + +#ifdef STA_SUPPORT +static struct debug_data items[] = { +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(drvdbg), (t_ptr)&drvdbg, 0}, +#endif + {"mlan_processing", item_size(mlan_processing), + item_addr(mlan_processing), INFO_ADDR}, + {"main_process_cnt", item_size(main_process_cnt), + item_addr(main_process_cnt), INFO_ADDR}, + {"main_lock_flag", item_size(main_lock_flag), item_addr(main_lock_flag), + INFO_ADDR}, + {"delay_task_flag", item_size(delay_task_flag), + item_addr(delay_task_flag), INFO_ADDR}, + {"mlan_rx_processing", item_size(mlan_rx_processing), + item_addr(mlan_rx_processing), INFO_ADDR}, + {"rx_pkts_queued", item_size(rx_pkts_queued), item_addr(rx_pkts_queued), + INFO_ADDR}, + {"wmm_ac_vo", item_size(wmm_ac_vo), item_addr(wmm_ac_vo), INFO_ADDR}, + {"wmm_ac_vi", item_size(wmm_ac_vi), item_addr(wmm_ac_vi), INFO_ADDR}, + {"wmm_ac_be", item_size(wmm_ac_be), item_addr(wmm_ac_be), INFO_ADDR}, + {"wmm_ac_bk", item_size(wmm_ac_bk), item_addr(wmm_ac_bk), INFO_ADDR}, + {"max_tx_buf_size", item_size(max_tx_buf_size), + item_addr(max_tx_buf_size), INFO_ADDR}, + {"tx_buf_size", item_size(tx_buf_size), item_addr(tx_buf_size), + INFO_ADDR}, + {"curr_tx_buf_size", item_size(curr_tx_buf_size), + item_addr(curr_tx_buf_size), INFO_ADDR}, + {"ps_mode", item_size(ps_mode), item_addr(ps_mode), INFO_ADDR}, + {"ps_state", item_size(ps_state), item_addr(ps_state), INFO_ADDR}, + {"is_deep_sleep", item_size(is_deep_sleep), item_addr(is_deep_sleep), + INFO_ADDR}, + {"wakeup_dev_req", item_size(pm_wakeup_card_req), + item_addr(pm_wakeup_card_req), INFO_ADDR}, + {"wakeup_tries", item_size(pm_wakeup_fw_try), + item_addr(pm_wakeup_fw_try), INFO_ADDR}, + {"wakeup_timeout", item_size(pm_wakeup_timeout), + item_addr(pm_wakeup_timeout), INFO_ADDR}, + {"hs_configured", item_size(is_hs_configured), + item_addr(is_hs_configured), INFO_ADDR}, + {"hs_activated", item_size(hs_activated), item_addr(hs_activated), + INFO_ADDR}, + {"rx_pkts_queued", item_size(rx_pkts_queued), item_addr(rx_pkts_queued), + INFO_ADDR}, + {"tx_pkts_queued", item_size(tx_pkts_queued), item_addr(tx_pkts_queued), + INFO_ADDR}, + {"pps_uapsd_mode", item_size(pps_uapsd_mode), item_addr(pps_uapsd_mode), + INFO_ADDR}, + {"sleep_pd", item_size(sleep_pd), item_addr(sleep_pd), INFO_ADDR}, + {"qos_cfg", item_size(qos_cfg), item_addr(qos_cfg), INFO_ADDR}, + {"tx_lock_flag", item_size(tx_lock_flag), item_addr(tx_lock_flag), + INFO_ADDR}, + {"port_open", item_size(port_open), item_addr(port_open), INFO_ADDR}, + {"bypass_pkt_count", item_size(bypass_pkt_count), + item_addr(bypass_pkt_count), INFO_ADDR}, + {"scan_processing", item_size(scan_processing), + item_addr(scan_processing), INFO_ADDR}, + {"num_cmd_timeout", item_size(num_cmd_timeout), + item_addr(num_cmd_timeout), INFO_ADDR}, + {"timeout_cmd_id", item_size(timeout_cmd_id), item_addr(timeout_cmd_id), + INFO_ADDR}, + {"timeout_cmd_act", item_size(timeout_cmd_act), + item_addr(timeout_cmd_act), INFO_ADDR}, + {"last_cmd_id", item_size(last_cmd_id), item_addr(last_cmd_id), + INFO_ADDR}, + {"last_cmd_act", item_size(last_cmd_act), item_addr(last_cmd_act), + INFO_ADDR}, + {"last_cmd_index", item_size(last_cmd_index), item_addr(last_cmd_index), + INFO_ADDR}, + {"last_cmd_resp_id", item_size(last_cmd_resp_id), + item_addr(last_cmd_resp_id), INFO_ADDR}, + {"last_cmd_resp_index", item_size(last_cmd_resp_index), + item_addr(last_cmd_resp_index), INFO_ADDR}, + {"last_event", item_size(last_event), item_addr(last_event), INFO_ADDR}, + {"last_event_index", item_size(last_event_index), + item_addr(last_event_index), INFO_ADDR}, + {"num_no_cmd_node", item_size(num_no_cmd_node), + item_addr(num_no_cmd_node), INFO_ADDR}, + {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), + item_addr(num_cmd_host_to_card_failure), INFO_ADDR}, + {"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), INFO_ADDR}, + {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), + item_addr(num_tx_host_to_card_failure), INFO_ADDR}, + {"num_alloc_buffer_failure", item_size(num_alloc_buffer_failure), + item_addr(num_alloc_buffer_failure), INFO_ADDR}, +#ifdef SDIO + {"num_cmdevt_c2h_fail", item_size(num_cmdevt_card_to_host_failure), + item_addr(num_cmdevt_card_to_host_failure), + INFO_ADDR | (INTF_SD << 8)}, + {"num_rx_c2h_fail", item_size(num_rx_card_to_host_failure), + item_addr(num_rx_card_to_host_failure), INFO_ADDR | (INTF_SD << 8)}, + {"num_int_read_fail", item_size(num_int_read_failure), + item_addr(num_int_read_failure), INFO_ADDR | (INTF_SD << 8)}, + {"last_int_status", item_size(last_int_status), + item_addr(last_int_status), INFO_ADDR | (INTF_SD << 8)}, + {"num_of_irq", item_size(num_of_irq), item_addr(num_of_irq), + INFO_ADDR | (INTF_SD << 8)}, + {"mp_invalid_update", item_size(mp_invalid_update), + item_addr(mp_invalid_update), INFO_ADDR | (INTF_SD << 8)}, + {"sdio_rx_aggr", item_size(sdio_rx_aggr), item_addr(sdio_rx_aggr), + INFO_ADDR | (INTF_SD << 8)}, + {"mpa_sent_last_pkt", item_size(mpa_sent_last_pkt), + item_addr(mpa_sent_last_pkt), INFO_ADDR | (INTF_SD << 8)}, + {"mpa_sent_no_ports", item_size(mpa_sent_no_ports), + item_addr(mpa_sent_no_ports), INFO_ADDR | (INTF_SD << 8)}, +#endif + {"num_evt_deauth", item_size(num_event_deauth), + item_addr(num_event_deauth), INFO_ADDR}, + {"num_evt_disassoc", item_size(num_event_disassoc), + item_addr(num_event_disassoc), INFO_ADDR}, + {"num_evt_link_lost", item_size(num_event_link_lost), + item_addr(num_event_link_lost), INFO_ADDR}, + {"num_cmd_deauth", item_size(num_cmd_deauth), item_addr(num_cmd_deauth), + INFO_ADDR}, + {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), + item_addr(num_cmd_assoc_success), INFO_ADDR}, + {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), + item_addr(num_cmd_assoc_failure), INFO_ADDR}, + {"num_cons_assoc_failure", item_size(num_cons_assoc_failure), + item_addr(num_cons_assoc_failure), INFO_ADDR}, + {"cmd_sent", item_size(cmd_sent), item_addr(cmd_sent), INFO_ADDR}, + {"data_sent", item_size(data_sent), item_addr(data_sent), INFO_ADDR}, + {"mp_rd_bitmap", item_size(mp_rd_bitmap), item_addr(mp_rd_bitmap), + INFO_ADDR}, + {"curr_rd_port", item_size(curr_rd_port), item_addr(curr_rd_port), + INFO_ADDR}, + {"mp_wr_bitmap", item_size(mp_wr_bitmap), item_addr(mp_wr_bitmap), + INFO_ADDR}, + {"curr_wr_port", item_size(curr_wr_port), item_addr(curr_wr_port), + INFO_ADDR}, +#ifdef PCIE + {"txbd_rdptr", item_size(txbd_rdptr), item_addr(txbd_rdptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"txbd_wrptr", item_size(txbd_wrptr), item_addr(txbd_wrptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"rxbd_rdptr", item_size(rxbd_rdptr), item_addr(rxbd_rdptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"rxbd_wrptr", item_size(rxbd_wrptr), item_addr(rxbd_wrptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"eventbd_rdptr", item_size(eventbd_rdptr), item_addr(eventbd_rdptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"eventbd_wrptr", item_size(eventbd_wrptr), item_addr(eventbd_wrptr), + INFO_ADDR | (INTF_PCIE << 8)}, +#endif + {"cmd_resp_received", item_size(cmd_resp_received), + item_addr(cmd_resp_received), INFO_ADDR}, + {"event_received", item_size(event_received), item_addr(event_received), + INFO_ADDR}, + +#ifdef USB + {"tx_cmd_urb_pending", item_card_size(tx_cmd_urb_pending), + item_card_addr(tx_cmd_urb_pending), CARD_ADDR | (INTF_USB << 8)}, + {"tx_data_urb_pending", item_card_size(tx_data_urb_pending), + item_card_addr(tx_data_urb_pending), CARD_ADDR | (INTF_USB << 8)}, +#ifdef USB_CMD_DATA_EP + {"rx_cmd_urb_pending", item_card_size(rx_cmd_urb_pending), + item_card_addr(rx_cmd_urb_pending), CARD_ADDR | (INTF_USB << 8)}, +#endif + {"rx_data_urb_pending", item_card_size(rx_data_urb_pending), + item_card_addr(rx_data_urb_pending), CARD_ADDR | (INTF_USB << 8)}, +#endif /* USB */ + {"num_tx_timeout", item_priv_size(num_tx_timeout), + item_priv_addr(num_tx_timeout), PRIV_ADDR}, + {"ioctl_pending", item_handle_size(ioctl_pending), + item_handle_addr(ioctl_pending), HANDLE_ADDR}, + {"tx_pending", item_handle_size(tx_pending), + item_handle_addr(tx_pending), HANDLE_ADDR}, + {"rx_pending", item_handle_size(rx_pending), + item_handle_addr(rx_pending), HANDLE_ADDR}, + {"lock_count", item_handle_size(lock_count), + item_handle_addr(lock_count), HANDLE_ADDR}, + {"malloc_count", item_handle_size(malloc_count), + item_handle_addr(malloc_count), HANDLE_ADDR}, + {"vmalloc_count", item_handle_size(vmalloc_count), + item_handle_addr(vmalloc_count), HANDLE_ADDR}, + {"mbufalloc_count", item_handle_size(mbufalloc_count), + item_handle_addr(mbufalloc_count), HANDLE_ADDR}, +#ifdef PCIE + {"malloc_cons_count", item_handle_size(malloc_cons_count), + item_handle_addr(malloc_cons_count), HANDLE_ADDR}, +#endif + {"main_state", item_handle_size(main_state), + item_handle_addr(main_state), HANDLE_ADDR}, + {"driver_state", item_handle_size(driver_state), + item_handle_addr(driver_state), HANDLE_ADDR}, +#ifdef SDIO_MMC_DEBUG + {"sdiocmd53w", item_handle_size(cmd53w), item_handle_addr(cmd53w), + HANDLE_ADDR}, + {"sdiocmd53r", item_handle_size(cmd53r), item_handle_addr(cmd53r), + HANDLE_ADDR}, +#endif + {"hs_skip_count", item_handle_size(hs_skip_count), + item_handle_addr(hs_skip_count), HANDLE_ADDR}, + {"hs_force_count", item_handle_size(hs_force_count), + item_handle_addr(hs_force_count), HANDLE_ADDR}, +}; + +#endif + +#ifdef UAP_SUPPORT +static struct debug_data uap_items[] = { +#ifdef DEBUG_LEVEL1 + {"drvdbg", sizeof(drvdbg), (t_ptr)&drvdbg, 0}, +#endif + {"mlan_processing", item_size(mlan_processing), + item_addr(mlan_processing), INFO_ADDR}, + {"main_process_cnt", item_size(main_process_cnt), + item_addr(main_process_cnt), INFO_ADDR}, + {"main_lock_flag", item_size(main_lock_flag), item_addr(main_lock_flag), + INFO_ADDR}, + {"delay_task_flag", item_size(delay_task_flag), + item_addr(delay_task_flag), INFO_ADDR}, + {"mlan_rx_processing", item_size(mlan_rx_processing), + item_addr(mlan_rx_processing), INFO_ADDR}, + {"rx_pkts_queued", item_size(rx_pkts_queued), item_addr(rx_pkts_queued), + INFO_ADDR}, + {"wmm_ac_vo", item_size(wmm_ac_vo), item_addr(wmm_ac_vo), INFO_ADDR}, + {"wmm_ac_vi", item_size(wmm_ac_vi), item_addr(wmm_ac_vi), INFO_ADDR}, + {"wmm_ac_be", item_size(wmm_ac_be), item_addr(wmm_ac_be), INFO_ADDR}, + {"wmm_ac_bk", item_size(wmm_ac_bk), item_addr(wmm_ac_bk), INFO_ADDR}, + {"max_tx_buf_size", item_size(max_tx_buf_size), + item_addr(max_tx_buf_size), INFO_ADDR}, + {"tx_buf_size", item_size(tx_buf_size), item_addr(tx_buf_size), + INFO_ADDR}, + {"curr_tx_buf_size", item_size(curr_tx_buf_size), + item_addr(curr_tx_buf_size), INFO_ADDR}, + {"ps_mode", item_size(ps_mode), item_addr(ps_mode), INFO_ADDR}, + {"ps_state", item_size(ps_state), item_addr(ps_state), INFO_ADDR}, + {"wakeup_dev_req", item_size(pm_wakeup_card_req), + item_addr(pm_wakeup_card_req), INFO_ADDR}, + {"wakeup_tries", item_size(pm_wakeup_fw_try), + item_addr(pm_wakeup_fw_try), INFO_ADDR}, + {"wakeup_timeout", item_size(pm_wakeup_timeout), + item_addr(pm_wakeup_timeout), INFO_ADDR}, + {"hs_configured", item_size(is_hs_configured), + item_addr(is_hs_configured), INFO_ADDR}, + {"hs_activated", item_size(hs_activated), item_addr(hs_activated), + INFO_ADDR}, + {"rx_pkts_queued", item_size(rx_pkts_queued), item_addr(rx_pkts_queued), + INFO_ADDR}, + {"tx_pkts_queued", item_size(tx_pkts_queued), item_addr(tx_pkts_queued), + INFO_ADDR}, + {"bypass_pkt_count", item_size(bypass_pkt_count), + item_addr(bypass_pkt_count), INFO_ADDR}, + {"num_bridge_pkts", item_size(num_bridge_pkts), + item_addr(num_bridge_pkts), INFO_ADDR}, + {"num_drop_pkts", item_size(num_drop_pkts), item_addr(num_drop_pkts), + INFO_ADDR}, + {"num_cmd_timeout", item_size(num_cmd_timeout), + item_addr(num_cmd_timeout), INFO_ADDR}, + {"timeout_cmd_id", item_size(timeout_cmd_id), item_addr(timeout_cmd_id), + INFO_ADDR}, + {"timeout_cmd_act", item_size(timeout_cmd_act), + item_addr(timeout_cmd_act), INFO_ADDR}, + {"last_cmd_id", item_size(last_cmd_id), item_addr(last_cmd_id), + INFO_ADDR}, + {"last_cmd_act", item_size(last_cmd_act), item_addr(last_cmd_act), + INFO_ADDR}, + {"last_cmd_index", item_size(last_cmd_index), item_addr(last_cmd_index), + INFO_ADDR}, + {"last_cmd_resp_id", item_size(last_cmd_resp_id), + item_addr(last_cmd_resp_id), INFO_ADDR}, + {"last_cmd_resp_index", item_size(last_cmd_resp_index), + item_addr(last_cmd_resp_index), INFO_ADDR}, + {"last_event", item_size(last_event), item_addr(last_event), INFO_ADDR}, + {"last_event_index", item_size(last_event_index), + item_addr(last_event_index), INFO_ADDR}, + {"num_no_cmd_node", item_size(num_no_cmd_node), + item_addr(num_no_cmd_node), INFO_ADDR}, + {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), + item_addr(num_cmd_host_to_card_failure), INFO_ADDR}, + {"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), INFO_ADDR}, + {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), + item_addr(num_tx_host_to_card_failure), INFO_ADDR}, + {"num_alloc_buffer_failure", item_size(num_alloc_buffer_failure), + item_addr(num_alloc_buffer_failure), INFO_ADDR}, +#ifdef SDIO + {"num_cmdevt_c2h_fail", item_size(num_cmdevt_card_to_host_failure), + item_addr(num_cmdevt_card_to_host_failure), + INFO_ADDR | (INTF_SD << 8)}, + {"num_rx_c2h_fail", item_size(num_rx_card_to_host_failure), + item_addr(num_rx_card_to_host_failure), INFO_ADDR | (INTF_SD << 8)}, + {"num_int_read_fail", item_size(num_int_read_failure), + item_addr(num_int_read_failure), INFO_ADDR | (INTF_SD << 8)}, + {"last_int_status", item_size(last_int_status), + item_addr(last_int_status), INFO_ADDR | (INTF_SD << 8)}, + {"num_of_irq", item_size(num_of_irq), item_addr(num_of_irq), + INFO_ADDR | (INTF_SD << 8)}, + {"mp_invalid_update", item_size(mp_invalid_update), + item_addr(mp_invalid_update), INFO_ADDR | (INTF_SD << 8)}, + {"sdio_rx_aggr", item_size(sdio_rx_aggr), item_addr(sdio_rx_aggr), + INFO_ADDR | (INTF_SD << 8)}, + {"mpa_sent_last_pkt", item_size(mpa_sent_last_pkt), + item_addr(mpa_sent_last_pkt), INFO_ADDR | (INTF_SD << 8)}, + {"mpa_sent_no_ports", item_size(mpa_sent_no_ports), + item_addr(mpa_sent_no_ports), INFO_ADDR | (INTF_SD << 8)}, +#endif + {"cmd_sent", item_size(cmd_sent), item_addr(cmd_sent), INFO_ADDR}, + {"data_sent", item_size(data_sent), item_addr(data_sent), INFO_ADDR}, + {"mp_rd_bitmap", item_size(mp_rd_bitmap), item_addr(mp_rd_bitmap), + INFO_ADDR}, + {"curr_rd_port", item_size(curr_rd_port), item_addr(curr_rd_port), + INFO_ADDR}, + {"mp_wr_bitmap", item_size(mp_wr_bitmap), item_addr(mp_wr_bitmap), + INFO_ADDR}, + {"curr_wr_port", item_size(curr_wr_port), item_addr(curr_wr_port), + INFO_ADDR}, +#ifdef PCIE + {"txbd_rdptr", item_size(txbd_rdptr), item_addr(txbd_rdptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"txbd_wrptr", item_size(txbd_wrptr), item_addr(txbd_wrptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"rxbd_rdptr", item_size(rxbd_rdptr), item_addr(rxbd_rdptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"rxbd_wrptr", item_size(rxbd_wrptr), item_addr(rxbd_wrptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"eventbd_rdptr", item_size(eventbd_rdptr), item_addr(eventbd_rdptr), + INFO_ADDR | (INTF_PCIE << 8)}, + {"eventbd_wrptr", item_size(eventbd_wrptr), item_addr(eventbd_wrptr), + INFO_ADDR | (INTF_PCIE << 8)}, +#endif + {"cmd_resp_received", item_size(cmd_resp_received), + item_addr(cmd_resp_received), INFO_ADDR}, + {"event_received", item_size(event_received), item_addr(event_received), + INFO_ADDR}, + +#ifdef USB + {"tx_cmd_urb_pending", item_card_size(tx_cmd_urb_pending), + item_card_addr(tx_cmd_urb_pending), CARD_ADDR | (INTF_USB << 8)}, + {"tx_data_urb_pending", item_card_size(tx_data_urb_pending), + item_card_addr(tx_data_urb_pending), CARD_ADDR | (INTF_USB << 8)}, +#ifdef USB_CMD_DATA_EP + {"rx_cmd_urb_pending", item_card_size(rx_cmd_urb_pending), + item_card_addr(rx_cmd_urb_pending), CARD_ADDR | (INTF_USB << 8)}, +#endif + {"rx_data_urb_pending", item_card_size(rx_data_urb_pending), + item_card_addr(rx_data_urb_pending), CARD_ADDR | (INTF_USB << 8)}, +#endif /* USB */ + {"num_tx_timeout", item_priv_size(num_tx_timeout), + item_priv_addr(num_tx_timeout), PRIV_ADDR}, + {"ioctl_pending", item_handle_size(ioctl_pending), + item_handle_addr(ioctl_pending), HANDLE_ADDR}, + {"tx_pending", item_handle_size(tx_pending), + item_handle_addr(tx_pending), HANDLE_ADDR}, + {"rx_pending", item_handle_size(rx_pending), + item_handle_addr(rx_pending), HANDLE_ADDR}, + {"lock_count", item_handle_size(lock_count), + item_handle_addr(lock_count), HANDLE_ADDR}, + {"malloc_count", item_handle_size(malloc_count), + item_handle_addr(malloc_count), HANDLE_ADDR}, + {"vmalloc_count", item_handle_size(vmalloc_count), + item_handle_addr(vmalloc_count), HANDLE_ADDR}, + {"mbufalloc_count", item_handle_size(mbufalloc_count), + item_handle_addr(mbufalloc_count), HANDLE_ADDR}, +#ifdef PCIE + {"malloc_cons_count", item_handle_size(malloc_cons_count), + item_handle_addr(malloc_cons_count), HANDLE_ADDR | (INTF_PCIE << 8)}, +#endif + {"main_state", item_handle_size(main_state), + item_handle_addr(main_state), HANDLE_ADDR}, + {"driver_state", item_handle_size(driver_state), + item_handle_addr(driver_state), HANDLE_ADDR}, +#ifdef SDIO_MMC_DEBUG + {"sdiocmd53w", item_handle_size(cmd53w), item_handle_addr(cmd53w), + HANDLE_ADDR | (INTF_SD << 8)}, + {"sdiocmd53r", item_handle_size(cmd53r), item_handle_addr(cmd53r), + HANDLE_ADDR | (INTF_SD << 8)}, +#endif + {"hs_skip_count", item_handle_size(hs_skip_count), + item_handle_addr(hs_skip_count), HANDLE_ADDR}, + {"hs_force_count", item_handle_size(hs_force_count), + item_handle_addr(hs_force_count), HANDLE_ADDR}, +}; +#endif /* UAP_SUPPORT */ + +/** + * @brief This function reset histogram data + * + * @param priv A pointer to moal_private + * + * @return N/A + */ +void woal_hist_do_reset(moal_private *priv, void *data) +{ + hgm_data *phist_data = (hgm_data *)data; + int ix; + t_u16 rx_rate_max_size = priv->phandle->card_info->rx_rate_max; + + if (!phist_data) + return; + atomic_set(&(phist_data->num_samples), 0); + for (ix = 0; ix < rx_rate_max_size; ix++) + atomic_set(&(phist_data->rx_rate[ix]), 0); + for (ix = 0; ix < SNR_MAX; ix++) + atomic_set(&(phist_data->snr[ix]), 0); + for (ix = 0; ix < NOISE_FLR_MAX; ix++) + atomic_set(&(phist_data->noise_flr[ix]), 0); + for (ix = 0; ix < SIG_STRENGTH_MAX; ix++) + atomic_set(&(phist_data->sig_str[ix]), 0); +} + +/** + * @brief This function reset all histogram data + * + * @param priv A pointer to moal_private + * + * @return N/A + */ +void woal_hist_data_reset(moal_private *priv) +{ + int i = 0; + for (i = 0; i < priv->phandle->card_info->histogram_table_num; i++) + woal_hist_do_reset(priv, priv->hist_data[i]); +} +/** + * @brief This function reset histogram data according to antenna + * + * @param priv A pointer to moal_private + * + * @return N/A + */ +void woal_hist_reset_table(moal_private *priv, t_u8 antenna) +{ + hgm_data *phist_data = priv->hist_data[antenna]; + + woal_hist_do_reset(priv, phist_data); +} + +/** NF calculation */ +#define CAL_NF(NF) ((t_s8)(-(t_s8)(NF))) +/** RSSI calculation */ +#define CAL_RSSI(SNR, NF) ((t_s8)((t_s8)(SNR) + CAL_NF(NF))) + +/** + * @brief This function set histogram data + * + * @param priv A pointer to moal_private + * @param rx_rate rx rate + * @param snr snr + * @param nflr NF + * + * @return N/A + */ +static void woal_hist_data_set(moal_private *priv, t_u16 rx_rate, t_s8 snr, + t_s8 nflr, t_u8 antenna) +{ + hgm_data *phist_data = priv->hist_data[antenna]; + t_s8 nf = CAL_NF(nflr); + t_s8 rssi = CAL_RSSI(snr, nflr); + + atomic_inc(&(phist_data->num_samples)); + if (rx_rate < priv->phandle->card_info->rx_rate_max) + atomic_inc(&(phist_data->rx_rate[rx_rate])); + atomic_inc(&(phist_data->snr[snr + 128])); + atomic_inc(&(phist_data->noise_flr[nf + 128])); + atomic_inc(&(phist_data->sig_str[rssi + 128])); +} + +/** + * @brief This function add histogram data + * + * @param priv A pointer to moal_private + * @param rx_rate rx rate + * @param snr snr + * @param nflr NF + * + * @return N/A + */ +void woal_hist_data_add(moal_private *priv, t_u16 rx_rate, t_s8 snr, t_s8 nflr, + t_u8 antenna) +{ + hgm_data *phist_data = NULL; + unsigned long curr_size; + + if ((antenna + 1) > priv->phandle->card_info->histogram_table_num) + antenna = 0; + phist_data = priv->hist_data[antenna]; + curr_size = atomic_read(&(phist_data->num_samples)); + if (curr_size > HIST_MAX_SAMPLES) + woal_hist_reset_table(priv, antenna); + woal_hist_data_set(priv, rx_rate, snr, nflr, antenna); +} +#define MAX_MCS_NUM_SUPP 16 +#define MAX_MCS_NUM_AC 10 +#define MAX_MCS_NUM_AX 12 +#define RATE_INDEX_MCS0 12 +/** + * @brief histogram info in proc + * + * @param sfp pointer to seq_file structure + * @param data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int woal_histogram_info(struct seq_file *sfp, void *data) +{ + hgm_data *phist_data = (hgm_data *)data; + int i = 0; + int value = 0; + t_bool sgi_enable = 0; + t_u8 bw = 0; + t_u8 mcs_index = 0; + t_u8 nss = 0; + t_u8 gi = 0; + wlan_hist_proc_data *hist_data = (wlan_hist_proc_data *)sfp->private; + moal_private *priv = (moal_private *)hist_data->priv; + t_u16 rx_rate_max_size = priv->phandle->card_info->rx_rate_max; + + ENTER(); + if (MODULE_GET == 0) { + LEAVE(); + return -EFAULT; + } + + seq_printf(sfp, "total samples = %d \n", + atomic_read(&(phist_data->num_samples))); + seq_printf(sfp, "rx rates (in Mbps):\n"); + seq_printf(sfp, "\t0-3: B-MCS 0-3\n"); + seq_printf(sfp, "\t4-11: G-MCS 0-7\n"); + seq_printf( + sfp, + "\t12-27: N-MCS 0-15(BW20) 28-43: N-MCS 0-15(BW40)\n"); + seq_printf( + sfp, + "\t44-59: N-MCS 0-15(BW20:SGI) 60-75: N-MCS 0-15(BW40:SGI)\n"); + seq_printf( + sfp, + "\t76-85: AC-MCS 0-9(VHT:BW20:NSS1) 86-95: AC-MCS 0-9(VHT:BW20:NSS2)\n"); + seq_printf( + sfp, + "\t96-105: AC-MCS 0-9(VHT:BW40:NSS1) 106-115: AC-MCS 0-9(VHT:BW40:NSS2)\n"); + seq_printf( + sfp, + "\t116-125: AC-MCS 0-9(VHT:BW80:NSS1) 126-135: AC-MCS 0-9(VHT:BW80:NSS2)\n"); + seq_printf( + sfp, + "\t136-145: AC-MCS 0-9(VHT:BW20:NSS1:SGI) 146-155: AC-MCS 0-9(VHT:BW20:NSS2:SGI)\n"); + seq_printf( + sfp, + "\t156-165: AC-MCS 0-9(VHT:BW40:NSS1:SGI) 166-175: AC-MCS 0-9(VHT:BW40:NSS2:SGI)\n"); + seq_printf( + sfp, + "\t176-185: AC-MCS 0-9(VHT:BW80:NSS1:SGI) 186-195: AC-MCS 0-9(VHT:BW80:NSS2:SGI)\n\n"); + seq_printf( + sfp, + "\t196-207: AX-MCS 0-11(BW20:NSS1) 208-219: AX-MCS 0-11(BW20:NSS2)\n"); + seq_printf( + sfp, + "\t220-231: AX-MCS 0-11(BW40:NSS1) 232-243: AX-MCS 0-11(BW40:NSS2)\n"); + seq_printf( + sfp, + "\t244-255: AX-MCS 0-11(BW80:NSS1) 256-267: AX-MCS 0-11(BW80:NSS2)\n"); + seq_printf( + sfp, + "\t268-279: AX-MCS 0-11(BW20:NSS1:GI1) 280-291: AX-MCS 0-11(BW20:NSS2:GI1)\n"); + seq_printf( + sfp, + "\t292-303: AX-MCS 0-11(BW40:NSS1:GI1) 304-315: AX-MCS 0-11(BW40:NSS2:GI1)\n"); + seq_printf( + sfp, + "\t316-327: AX-MCS 0-11(BW80:NSS1:GI1) 328-339: AX-MCS 0-11(BW80:NSS2:GI1)\n"); + seq_printf( + sfp, + "\t340-351: AX-MCS 0-11(BW20:NSS1:GI2) 352-363: AX-MCS 0-11(BW20:NSS2:GI2)\n"); + seq_printf( + sfp, + "\t364-375: AX-MCS 0-11(BW40:NSS1:GI2) 376-387: AX-MCS 0-11(BW40:NSS2:GI2)\n"); + seq_printf( + sfp, + "\t388-399: AX-MCS 0-11(BW80:NSS1:GI2) 400-411: AX-MCS 0-11(BW80:NSS2:GI2)\n"); + + for (i = 0; i < rx_rate_max_size; i++) { + value = atomic_read(&(phist_data->rx_rate[i])); + if (value) { + if (i <= 11) + seq_printf(sfp, "rx_rate[%03d] = %d\n", i, + value); + else if (i <= 75) { + sgi_enable = (i - 12) / + (MAX_MCS_NUM_SUPP * 2); // 0:LGI, + // 1:SGI + bw = ((i - 12) % (MAX_MCS_NUM_SUPP * 2)) / + MAX_MCS_NUM_SUPP; // 0:20MHz, 1:40MHz + mcs_index = (i - 12) % MAX_MCS_NUM_SUPP; + seq_printf( + sfp, + "rx_rate[%03d] = %d (MCS:%d HT BW:%dMHz%s)\n", + i, value, mcs_index, (1 << bw) * 20, + sgi_enable ? " SGI" : ""); + } else if (i <= 195) { + sgi_enable = (i - 76) / + (MAX_MCS_NUM_AC * 6); // 0:LGI, + // 1:SGI + bw = ((i - 76) % (MAX_MCS_NUM_AC * 6)) / + (MAX_MCS_NUM_AC * 2); // 0:20MHz, 1:40MHz, + // 2:80MHz + nss = (((i - 76) % (MAX_MCS_NUM_AC * 6)) % + (MAX_MCS_NUM_AC * 2)) / + MAX_MCS_NUM_AC; // 0:NSS1, 1:NSS2 + mcs_index = (i - 76) % MAX_MCS_NUM_AC; + + seq_printf( + sfp, + "rx_rate[%03d] = %d (MCS:%d VHT BW:%dMHz NSS:%d%s)\n", + i, value, mcs_index, (1 << bw) * 20, + nss + 1, sgi_enable ? " SGI" : ""); + } else if (i <= 411) { + gi = (i - 196) / (MAX_MCS_NUM_AX * 6); // 0,1,2 + bw = ((i - 196) % (MAX_MCS_NUM_AX * 6)) / + (MAX_MCS_NUM_AX * 2); // 0:20MHz, 1:40MHz, + // 2:80MHz + nss = (((i - 196) % (MAX_MCS_NUM_AX * 6)) % + (MAX_MCS_NUM_AX * 2)) / + MAX_MCS_NUM_AX; // 0:NSS1, 1:NSS2 + mcs_index = (i - 196) % MAX_MCS_NUM_AX; + + seq_printf( + sfp, + "rx_rate[%03d] = %d (MCS:%d AX BW:%dMHz NSS:%d GI:%d)\n", + i, value, mcs_index, (1 << bw) * 20, + nss + 1, gi); + } + } + } + for (i = 0; i < SNR_MAX; i++) { + value = atomic_read(&(phist_data->snr[i])); + if (value) + seq_printf(sfp, "snr[%02ddB] = %d\n", (int)(i - 128), + value); + } + for (i = 0; i < NOISE_FLR_MAX; i++) { + value = atomic_read(&(phist_data->noise_flr[i])); + if (value) + seq_printf(sfp, "noise_flr[%02ddBm] = %d\n", + (int)(i - 128), value); + } + for (i = 0; i < SIG_STRENGTH_MAX; i++) { + value = atomic_read(&(phist_data->sig_str[i])); + if (value) + seq_printf(sfp, "sig_strength[%02ddBm] = %d\n", + (int)(i - 128), value); + } + + MODULE_PUT; + LEAVE(); + return 0; +} + +/** + * @brief Proc read function for histogram + * + * @param sfp pointer to seq_file structure + * @param data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int woal_histogram_read(struct seq_file *sfp, void *data) +{ + wlan_hist_proc_data *hist_data = (wlan_hist_proc_data *)sfp->private; + moal_private *priv = (moal_private *)hist_data->priv; + + ENTER(); + if (!priv) { + LEAVE(); + return -EFAULT; + } + + if (hist_data->ant_idx < priv->phandle->card_info->histogram_table_num) + woal_histogram_info(sfp, priv->hist_data[hist_data->ant_idx]); + + LEAVE(); + return 0; +} + +static int woal_histogram_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_histogram_read, PDE_DATA(inode)); +#else + return single_open(file, woal_histogram_read, PDE(inode)->data); +#endif +} + +/** + * @brief Proc write function for histogram + * + * @param f file pointer + * @param buf pointer to data buffer + * @param count data number to write + * @param off Offset + * + * @return number of data + */ +static ssize_t woal_histogram_write(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + struct seq_file *sfp = f->private_data; + wlan_hist_proc_data *hist_data = (wlan_hist_proc_data *)sfp->private; + moal_private *priv = (moal_private *)hist_data->priv; + woal_hist_reset_table(priv, hist_data->ant_idx); + return count; +} + +/** + * @brief Proc read function for log + * + * @param sfp pointer to seq_file structure + * @param data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int woal_log_read(struct seq_file *sfp, void *data) +{ + moal_private *priv = (moal_private *)sfp->private; + mlan_ds_get_stats stats; + int i = 0; + ENTER(); + if (!priv) { + LEAVE(); + return -EFAULT; + } + if (MODULE_GET == 0) { + LEAVE(); + return -EFAULT; + } + + memset(&stats, 0x00, sizeof(stats)); + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { + PRINTM(MERROR, + "woal_log_read: Get log: Failed to get stats info!"); + MODULE_PUT; + LEAVE(); + return -EFAULT; + } + + seq_printf(sfp, "dot11GroupTransmittedFrameCount = %d\n", + stats.mcast_tx_frame); + seq_printf(sfp, "dot11FailedCount = %d\n", stats.failed); + seq_printf(sfp, "dot11RetryCount = %d\n", stats.retry); + seq_printf(sfp, "dot11MultipleRetryCount = %d\n", stats.multi_retry); + seq_printf(sfp, "dot11FrameDuplicateCount = %d\n", stats.frame_dup); + seq_printf(sfp, "dot11RTSSuccessCount = %d\n", stats.rts_success); + seq_printf(sfp, "dot11RTSFailureCount = %d\n", stats.rts_failure); + seq_printf(sfp, "dot11ACKFailureCount = %d\n", stats.ack_failure); + seq_printf(sfp, "dot11ReceivedFragmentCount = %d\n", stats.rx_frag); + seq_printf(sfp, "dot11GroupReceivedFrameCount = %d\n", + stats.mcast_rx_frame); + seq_printf(sfp, "dot11FCSErrorCount = %d\n", stats.fcs_error); + seq_printf(sfp, "dot11TransmittedFrameCount = %d\n", stats.tx_frame); + seq_printf(sfp, "wepicverrcnt-1 = %d\n", stats.wep_icv_error[0]); + seq_printf(sfp, "wepicverrcnt-2 = %d\n", stats.wep_icv_error[1]); + seq_printf(sfp, "wepicverrcnt-3 = %d\n", stats.wep_icv_error[2]); + seq_printf(sfp, "wepicverrcnt-4 = %d\n", stats.wep_icv_error[3]); + seq_printf(sfp, "beaconReceivedCount = %d\n", stats.bcn_rcv_cnt); + seq_printf(sfp, "beaconMissedCount = %d\n", stats.bcn_miss_cnt); + if (stats.amsdu_rx_cnt) + seq_printf(sfp, "ReceivedMSDUinPerAMSDU = %d\n", + stats.msdu_in_rx_amsdu_cnt / stats.amsdu_rx_cnt); + seq_printf(sfp, "ReceivedMSDUinAMSDUCount = %d\n", + stats.msdu_in_rx_amsdu_cnt); + if (stats.amsdu_tx_cnt) + seq_printf(sfp, "TransmitMSDUinPerAMSDU = %d\n", + stats.msdu_in_tx_amsdu_cnt / stats.amsdu_tx_cnt); + seq_printf(sfp, "TransmitMSDUinAMSDUCount = %d\n", + stats.msdu_in_tx_amsdu_cnt); + if (priv->phandle->fw_getlog_enable) { + seq_printf(sfp, "dot11TransmittedFragmentCount = %u\n", + stats.tx_frag_cnt); + seq_printf(sfp, "dot11QosTransmittedFragmentCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_tx_frag_cnt[i]); + } + seq_printf(sfp, "\ndot11QosFailedCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_failed_cnt[i]); + } + seq_printf(sfp, "\ndot11QosRetryCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_retry_cnt[i]); + } + seq_printf(sfp, "\ndot11QosMultipleRetryCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_multi_retry_cnt[i]); + } + seq_printf(sfp, "\ndot11QosFrameDuplicateCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_frm_dup_cnt[i]); + } + seq_printf(sfp, "\ndot11QosRTSSuccessCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_rts_suc_cnt[i]); + } + seq_printf(sfp, "\ndot11QosRTSFailureCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_rts_failure_cnt[i]); + } + seq_printf(sfp, "\ndot11QosACKFailureCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_ack_failure_cnt[i]); + } + seq_printf(sfp, "\ndot11QosReceivedFragmentCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_rx_frag_cnt[i]); + } + seq_printf(sfp, "\ndot11QosTransmittedFrameCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_tx_frm_cnt[i]); + } + seq_printf(sfp, "\ndot11QosDiscardedFrameCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_discarded_frm_cnt[i]); + } + seq_printf(sfp, "\ndot11QosMPDUsReceivedCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_mpdus_rx_cnt[i]); + } + seq_printf(sfp, "\ndot11QosRetriesReceivedCount = "); + for (i = 0; i < 8; i++) { + seq_printf(sfp, "%u ", stats.qos_retries_rx_cnt[i]); + } + seq_printf(sfp, + "\ndot11RSNAStatsCMACICVErrors = %u\n" + "dot11RSNAStatsCMACReplays = %u\n" + "dot11RSNAStatsRobustMgmtCCMPReplays = %u\n" + "dot11RSNAStatsTKIPICVErrors = %u\n" + "dot11RSNAStatsTKIPReplays = %u\n" + "dot11RSNAStatsCCMPDecryptErrors = %u\n" + "dot11RSNAstatsCCMPReplays = %u\n" + "dot11TransmittedAMSDUCount = %u\n" + "dot11FailedAMSDUCount = %u\n" + "dot11RetryAMSDUCount = %u\n" + "dot11MultipleRetryAMSDUCount = %u\n" + "dot11TransmittedOctetsInAMSDUCount = %llu\n" + "dot11AMSDUAckFailureCount = %u\n" + "dot11ReceivedAMSDUCount = %u\n" + "dot11ReceivedOctetsInAMSDUCount = %llu\n" + "dot11TransmittedAMPDUCount = %u\n" + "dot11TransmittedMPDUsInAMPDUCount = %u\n" + "dot11TransmittedOctetsInAMPDUCount = %llu\n" + "dot11AMPDUReceivedCount = %u\n" + "dot11MPDUInReceivedAMPDUCount = %u\n" + "dot11ReceivedOctetsInAMPDUCount = %llu\n" + "dot11AMPDUDelimiterCRCErrorCount = %u\n", + stats.cmacicv_errors, stats.cmac_replays, + stats.mgmt_ccmp_replays, stats.tkipicv_errors, + stats.tkip_replays, stats.ccmp_decrypt_errors, + stats.ccmp_replays, stats.tx_amsdu_cnt, + stats.failed_amsdu_cnt, stats.retry_amsdu_cnt, + stats.multi_retry_amsdu_cnt, + stats.tx_octets_in_amsdu_cnt, + stats.amsdu_ack_failure_cnt, stats.rx_amsdu_cnt, + stats.rx_octets_in_amsdu_cnt, stats.tx_ampdu_cnt, + stats.tx_mpdus_in_ampdu_cnt, + stats.tx_octets_in_ampdu_cnt, stats.ampdu_rx_cnt, + stats.mpdu_in_rx_ampdu_cnt, + stats.rx_octets_in_ampdu_cnt, + stats.ampdu_delimiter_crc_error_cnt); + } + + MODULE_PUT; + LEAVE(); + return 0; +} + +/** + * @brief Proc read function for log + * + * @param inode pointer to inode + * @param file file pointer + * + * @return number of data + */ +static int woal_log_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_log_read, PDE_DATA(inode)); +#else + return single_open(file, woal_log_read, PDE(inode)->data); +#endif +} + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Proc read function + * + * @param sfp pointer to seq_file structure + * @param data + * + * @return Number of output data or MLAN_STATUS_FAILURE + */ +static int woal_debug_read(struct seq_file *sfp, void *data) +{ + int val = 0; + unsigned int i; + + struct debug_data_priv *items_priv = + (struct debug_data_priv *)sfp->private; + struct debug_data *d = items_priv->items; + moal_private *priv = items_priv->priv; + mlan_debug_info *info = NULL; + t_u32 intf_mask = INTF_MASK << 8; +#ifdef SDIO + unsigned int j; + t_u8 mp_aggr_pkt_limit = 0; +#endif + + ENTER(); + + if (priv == NULL) { + LEAVE(); + return -EFAULT; + } + + info = &(priv->phandle->debug_info); + + if (MODULE_GET == 0) { + LEAVE(); + return -EFAULT; + } + + priv->phandle->driver_state = woal_check_driver_status(priv->phandle); + /* Get debug information */ + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) + goto exit; + + for (i = 0; i < (unsigned int)items_priv->num_of_items; i++) { + /* If this item is interface specific but card interface is NOT + * correspond type, we will not count it. */ + if ((d[i].attr & intf_mask) && + !((d[i].attr & intf_mask) & + (priv->phandle->card_type & intf_mask))) + continue; + + if (d[i].size == 1) + val = *((t_u8 *)d[i].addr); + else if (d[i].size == 2) + val = *((t_u16 *)d[i].addr); + else if (d[i].size == 4) + val = *((t_u32 *)d[i].addr); + else { + unsigned int j; + seq_printf(sfp, "%s=", d[i].name); + for (j = 0; j < d[i].size; j += 2) { + val = *(t_u16 *)(d[i].addr + j); + seq_printf(sfp, "0x%x ", val); + } + seq_printf(sfp, "\n"); + continue; + } + if (strstr(d[i].name, "id") || strstr(d[i].name, "bitmap") +#ifdef PCIE + || strstr(d[i].name, "ptr") +#endif + ) + seq_printf(sfp, "%s=0x%x\n", d[i].name, val); + else + seq_printf(sfp, "%s=%d\n", d[i].name, val); + } +#ifdef SDIO + if (IS_SD(priv->phandle->card_type)) { + mp_aggr_pkt_limit = info->mp_aggr_pkt_limit; + seq_printf(sfp, "last_recv_wr_bitmap=0x%x last_mp_index=%d\n", + info->last_recv_wr_bitmap, info->last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + seq_printf( + sfp, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n", + info->last_mp_wr_bitmap[i], + info->last_mp_wr_ports[i], + info->last_mp_wr_len[i], + info->last_curr_wr_port[i]); + for (j = 0; j < mp_aggr_pkt_limit; j++) { + seq_printf(sfp, "0x%02x ", + info->last_mp_wr_info + [i * mp_aggr_pkt_limit + j]); + } + seq_printf(sfp, "\n"); + } + seq_printf(sfp, "SDIO MPA Tx: "); + for (i = 0; i < mp_aggr_pkt_limit; i++) + seq_printf(sfp, "%d ", info->mpa_tx_count[i]); + seq_printf(sfp, "\n"); + seq_printf(sfp, "SDIO MPA Rx: "); + for (i = 0; i < mp_aggr_pkt_limit; i++) + seq_printf(sfp, "%d ", info->mpa_rx_count[i]); + seq_printf(sfp, "\n"); + seq_printf(sfp, "SDIO MP Update: "); + for (i = 0; i < (mp_aggr_pkt_limit * 2); i++) + seq_printf(sfp, "%d ", info->mp_update[i]); + seq_printf(sfp, "\n"); + } +#endif +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + seq_printf(sfp, "last_wr_index:%d\n", + info->txbd_wrptr & (MLAN_MAX_TXRX_BD - 1)); + seq_printf(sfp, "Tx pkt size:\n"); + for (i = 0; i < MLAN_MAX_TXRX_BD; i++) { + seq_printf(sfp, "%04d ", info->last_tx_pkt_size[i]); + if ((i + 1) % 16 == 0) + seq_printf(sfp, "\n"); + } + } +#endif + seq_printf(sfp, "tcp_ack_drop_cnt=%d\n", priv->tcp_ack_drop_cnt); + seq_printf(sfp, "tcp_ack_cnt=%d\n", priv->tcp_ack_cnt); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < 4; i++) + seq_printf(sfp, "wmm_tx_pending[%d]:%d\n", i, + atomic_read(&priv->wmm_tx_pending[i])); +#endif + if (info->tx_tbl_num) { + seq_printf(sfp, "Tx BA stream table:\n"); + for (i = 0; i < info->tx_tbl_num; i++) { + seq_printf( + sfp, + "tid = %d, ra = %02x:%02x:%02x:%02x:%02x:%02x amsdu=%d\n", + (int)info->tx_tbl[i].tid, info->tx_tbl[i].ra[0], + info->tx_tbl[i].ra[1], info->tx_tbl[i].ra[2], + info->tx_tbl[i].ra[3], info->tx_tbl[i].ra[4], + info->tx_tbl[i].ra[5], + (int)info->tx_tbl[i].amsdu); + } + } + if (info->rx_tbl_num) { + seq_printf(sfp, "Rx reorder table:\n"); + for (i = 0; i < info->rx_tbl_num; i++) { + unsigned int j; + + seq_printf( + sfp, + "tid = %d, ta = %02x:%02x:%02x:%02x:%02x:%02x, start_win = %d, " + "win_size = %d, amsdu=%d\n", + (int)info->rx_tbl[i].tid, info->rx_tbl[i].ta[0], + info->rx_tbl[i].ta[1], info->rx_tbl[i].ta[2], + info->rx_tbl[i].ta[3], info->rx_tbl[i].ta[4], + info->rx_tbl[i].ta[5], + (int)info->rx_tbl[i].start_win, + (int)info->rx_tbl[i].win_size, + (int)info->rx_tbl[i].amsdu); + seq_printf(sfp, "buffer: "); + for (j = 0; j < info->rx_tbl[i].win_size; j++) { + if (info->rx_tbl[i].buffer[j] == MTRUE) + seq_printf(sfp, "1 "); + else + seq_printf(sfp, "0 "); + } + seq_printf(sfp, "\n"); + } + } + for (i = 0; i < info->ralist_num; i++) { + seq_printf( + sfp, + "ralist ra: %02x:%02x:%02x:%02x:%02x:%02x tid=%d pkts=%d pause=%d\n", + info->ralist[i].ra[0], info->ralist[i].ra[1], + info->ralist[i].ra[2], info->ralist[i].ra[3], + info->ralist[i].ra[4], info->ralist[i].ra[5], + info->ralist[i].tid, info->ralist[i].total_pkts, + info->ralist[i].tx_pause); + } + +exit: + MODULE_PUT; + LEAVE(); + return 0; +} + +/** + * @brief Proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param count data number to write + * @param off Offset + * + * @return number of data + */ +static ssize_t woal_debug_write(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct seq_file *sfp = f->private_data; + struct debug_data_priv *items_priv = + (struct debug_data_priv *)sfp->private; + struct debug_data *d = items_priv->items; + moal_private *priv = items_priv->priv; + mlan_debug_info *info = &(priv->phandle->debug_info); +#ifdef DEBUG_LEVEL1 + t_u32 last_drvdbg = drvdbg; +#endif + gfp_t flag; + + ENTER(); + + if (MODULE_GET == 0) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + pdata = kzalloc(count + 1, flag); + if (pdata == NULL) { + MODULE_PUT; + LEAVE(); + return 0; + } + + if (copy_from_user(pdata, buf, count)) { + PRINTM(MERROR, "Copy from user failed\n"); + kfree(pdata); + MODULE_PUT; + LEAVE(); + return 0; + } + pdata[count] = '\0'; + + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) { + kfree(pdata); + MODULE_PUT; + LEAVE(); + return 0; + } + + p0 = pdata; + for (i = 0; i < items_priv->num_of_items; i++) { + do { + p = strstr(p0, d[i].name); + if (p == NULL) + break; + p1 = strchr(p, '\n'); + if (p1 == NULL) + break; + p0 = p1++; + p2 = strchr(p, '='); + if (!p2) + break; + p2++; + r = woal_string_to_number(p2); + if (d[i].size == 1) + *((t_u8 *)d[i].addr) = (t_u8)r; + else if (d[i].size == 2) + *((t_u16 *)d[i].addr) = (t_u16)r; + else if (d[i].size == 4) + *((t_u32 *)d[i].addr) = (t_u32)r; + break; + } while (MTRUE); + } + kfree(pdata); + +#ifdef DEBUG_LEVEL1 + if (last_drvdbg != drvdbg) + woal_set_drvdbg(priv, drvdbg); +#endif + + MODULE_PUT; + LEAVE(); + return count; +} + +static int woal_debug_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_debug_read, PDE_DATA(inode)); +#else + return single_open(file, woal_debug_read, PDE(inode)->data); +#endif +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops debug_proc_fops = { + .proc_open = woal_debug_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = woal_debug_write, +}; +#else +static const struct file_operations debug_proc_fops = { + .owner = THIS_MODULE, + .open = woal_debug_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = woal_debug_write, +}; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops histogram_proc_fops = { + .proc_open = woal_histogram_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = woal_histogram_write, +}; +#else +static const struct file_operations histogram_proc_fops = { + .owner = THIS_MODULE, + .open = woal_histogram_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = woal_histogram_write, +}; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops log_proc_fops = { + .proc_open = woal_log_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations log_proc_fops = { + .owner = THIS_MODULE, + .open = woal_log_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Create debug proc file + * + * @param priv A pointer to a moal_private structure + * + * @return N/A + */ +void woal_debug_entry(moal_private *priv) +{ + struct proc_dir_entry *r; + int i; + char hist_entry[50]; + struct debug_data *d = NULL; + + ENTER(); + + if (priv->proc_entry == NULL) { + LEAVE(); + return; + } + +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + priv->items_priv.items = kmalloc(sizeof(items), GFP_KERNEL); + if (!priv->items_priv.items) { + PRINTM(MERROR, + "Failed to allocate memory for debug data\n"); + LEAVE(); + return; + } + moal_memcpy_ext(priv->phandle, priv->items_priv.items, items, + sizeof(items), sizeof(items)); + priv->items_priv.num_of_items = ARRAY_SIZE(items); + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + priv->items_priv.items = kmalloc(sizeof(uap_items), GFP_KERNEL); + if (!priv->items_priv.items) { + PRINTM(MERROR, + "Failed to allocate memory for debug data\n"); + LEAVE(); + return; + } + moal_memcpy_ext(priv->phandle, priv->items_priv.items, + uap_items, sizeof(uap_items), + sizeof(uap_items)); + priv->items_priv.num_of_items = ARRAY_SIZE(uap_items); + } +#endif + + priv->items_priv.priv = priv; + + d = priv->items_priv.items; + for (i = 0; i < priv->items_priv.num_of_items; i++) { + if (IS_INFO_ADDR(d[i].attr)) + d[i].addr += (t_ptr) & (priv->phandle->debug_info); + else if (IS_HANDLE_ADDR(d[i].attr)) + d[i].addr += (t_ptr)(priv->phandle); + else if (IS_PRIV_ADDR(d[i].attr)) + d[i].addr += (t_ptr)(priv); + else if (IS_CARD_ADDR(d[i].attr)) + d[i].addr += (t_ptr)(priv->phandle->card); + } + + /* Create proc entry */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data("debug", 0644, priv->proc_entry, &debug_proc_fops, + &priv->items_priv); + if (r == NULL) +#else + r = create_proc_entry("debug", 0644, priv->proc_entry); + if (r) { + r->data = &priv->items_priv; + r->proc_fops = &debug_proc_fops; + } else +#endif + { + PRINTM(MMSG, "Fail to create proc debug entry\n"); + LEAVE(); + return; + } + if (priv->bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { + priv->hist_entry = proc_mkdir("histogram", priv->proc_entry); + if (!priv->hist_entry) { + PRINTM(MERROR, "Fail to mkdir histogram!\n"); + LEAVE(); + return; + } + for (i = 0; i < priv->phandle->card_info->histogram_table_num; + i++) { + priv->hist_proc[i].ant_idx = i; + priv->hist_proc[i].priv = priv; + snprintf(hist_entry, sizeof(hist_entry), "wlan-ant%d", + i); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data(hist_entry, 0644, priv->hist_entry, + &histogram_proc_fops, + &priv->hist_proc[i]); + if (r == NULL) +#else + r = create_proc_entry("histogram", 0644, + priv->hist_entry); + if (r) { + r->data = &priv->hist_proc[i]; + r->proc_fops = &histogram_proc_fops; + } else +#endif + { + PRINTM(MMSG, + "Fail to create proc histogram entry %s\n", + hist_entry); + LEAVE(); + return; + } + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data("log", 0644, priv->proc_entry, &log_proc_fops, + priv); + if (r == NULL) +#else + r = create_proc_entry("log", 0644, priv->proc_entry); + if (r) { + r->data = priv; + r->proc_fops = &log_proc_fops; + } else +#endif + { + PRINTM(MMSG, "Fail to create proc log entry\n"); + LEAVE(); + return; + } + + LEAVE(); +} + +/** + * @brief Remove proc file + * + * @param priv A pointer to a moal_private structure + * + * @return N/A + */ +void woal_debug_remove(moal_private *priv) +{ + char hist_entry[50]; + int i; + ENTER(); + + kfree(priv->items_priv.items); + /* Remove proc entry */ + remove_proc_entry("debug", priv->proc_entry); + if (priv->bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { + for (i = 0; i < priv->phandle->card_info->histogram_table_num; + i++) { + snprintf(hist_entry, sizeof(hist_entry), "wlan-ant%d", + i); + remove_proc_entry(hist_entry, priv->hist_entry); + } + remove_proc_entry("histogram", priv->proc_entry); + } + remove_proc_entry("log", priv->proc_entry); + + LEAVE(); +} +#endif diff --git a/mxm_wifiex/wlan_src/mlinux/moal_eth_ioctl.c b/mxm_wifiex/wlan_src/mlinux/moal_eth_ioctl.c new file mode 100644 index 0000000..60d229b --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_eth_ioctl.c @@ -0,0 +1,16895 @@ + +/** @file moal_eth_ioctl.c + * + * @brief This file contains private ioctl functions + + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 +#ifdef USB +#include "moal_usb.h" +#endif +#ifdef SDIO +#include "moal_sdio.h" +#endif +#ifdef PCIE +#include "moal_pcie.h" +#endif +#ifdef STA_CFG80211 +#include "moal_sta_cfg80211.h" +#endif +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#include "moal_cfg80211_util.h" +#endif +#endif + +/******************************************************** + Local Variables +********************************************************/ + +/** Bands supported in Infra mode */ +static t_u16 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, + BAND_GN | BAND_GAC, + BAND_B | BAND_G | BAND_GN | BAND_GAC, + BAND_G | BAND_GN | BAND_GAC, + BAND_GN | BAND_GAC | BAND_GAX, + BAND_B | BAND_G | BAND_GN | BAND_GAC | BAND_GAX, + BAND_G | BAND_GN | BAND_GAC | BAND_GAX, + BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN | BAND_AAC, + BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN | BAND_AAC | BAND_GAC, + BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC, + BAND_A | BAND_AN | BAND_AAC, + BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN | BAND_AAC | BAND_AAX, + BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN | BAND_AAC | BAND_GAC | + BAND_AAX, + BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC | BAND_AAX, + BAND_A | BAND_AN | BAND_AAC | BAND_AAX, +}; + +/** Bands supported in Ad-Hoc mode */ +static t_u8 SupportedAdhocBand[] = { + BAND_B, + BAND_B | BAND_G, + BAND_G, + BAND_A, +}; + +/******************************************************** + 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 + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Parse a string to extract numerical arguments + * + * @param pos Pointer to the arguments string + * @param data Pointer to the arguments buffer + * @param datalen Length of the arguments buffer + * @param user_data_len Pointer to the number of arguments extracted + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status parse_arguments(t_u8 *pos, int *data, int datalen, + int *user_data_len) +{ + unsigned int i, j, k; + char cdata[10]; + int is_hex = 0; + + if (strlen(pos) == 0) { + *user_data_len = 0; + return MLAN_STATUS_SUCCESS; + } + + memset(cdata, 0, sizeof(cdata)); + for (i = 0, j = 0, k = 0; i <= strlen(pos); i++) { + if ((k == 0) && (i <= (strlen(pos) - 2))) { + if ((pos[i] == '0') && (pos[i + 1] == 'x')) { + is_hex = 1; + i = i + 2; + } + } + if (pos[i] == '\0' || pos[i] == ' ') { + if (j >= datalen) { + j++; + break; + } + if (is_hex) { + data[j] = woal_atox(cdata); + is_hex = 0; + } else { + woal_atoi(&data[j], cdata); + } + j++; + k = 0; + memset(cdata, 0, sizeof(cdata)); + if (pos[i] == '\0') + break; + } else { + if (k >= sizeof(cdata)) { + PRINTM(MERROR, "Invalid numerical arguments\n"); + break; + } + cdata[k] = pos[i]; + k++; + } + } + + *user_data_len = j; + return MLAN_STATUS_SUCCESS; +} + +/** Convert character to integer */ +#define CHAR2INT(x) (((x) >= 'A') ? ((x) - 'A' + 10) : ((x) - '0')) +/** + * @brief Converts a string to hex value + * + * @param str A pointer to the string + * @param raw A pointer to the raw data buffer + * @param raw_size raw data buffer size + * @return Number of bytes read + **/ +int string2raw(unsigned char *str, unsigned char *raw, int raw_size) +{ + int len = (strlen(str) + 1) / 2; + int i = 0; + + do { + if (strlen(str) < 2) + return -1; + if (!isxdigit(*str) || !isxdigit(*(str + 1))) + return -1; + *str = toupper(*str); + *raw = CHAR2INT(*str) << 4; + ++str; + *str = toupper(*str); + *raw |= CHAR2INT(*str); + ++raw; + i++; + } while (*++str != '\0' && i < raw_size); + return len; +} + +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +/** + * @brief Set wps & p2p ie in AP mode + * + * @param priv Pointer to priv stucture + * @param ie Pointer to ies data + * @param len Length of data + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_ap_wps_p2p_ie(moal_private *priv, t_u8 *ie, size_t len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = ie; + int ie_len; + + ENTER(); + + ie_len = len - 2; + if (ie_len <= 0) { + PRINTM(MERROR, "IE len error: %d\n", ie_len); + ret = -EFAULT; + goto done; + } + + /* Android cmd format: + * "SET_AP_WPS_P2P_IE 1" -- beacon IE + * "SET_AP_WPS_P2P_IE 2" -- proberesp IE + * "SET_AP_WPS_P2P_IE 4" -- assocresp IE + */ + if (*pos == '1') { + /* set the beacon wps/p2p ies */ + pos += 2; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie( + priv, pos, ie_len, NULL, 0, NULL, 0, NULL, 0, + MGMT_MASK_BEACON_WPS_P2P, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); + ret = -EFAULT; + goto done; + } + } else if (*pos == '2') { + /* set the probe resp ies */ + pos += 2; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie( + priv, NULL, 0, pos, ie_len, NULL, 0, NULL, 0, + MGMT_MASK_PROBE_RESP, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Failed to set probe resp ie\n"); + ret = -EFAULT; + goto done; + } + } else if (*pos == '4') { + /* set the assoc resp ies */ + pos += 2; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie( + priv, NULL, 0, NULL, 0, pos, ie_len, NULL, 0, + MGMT_MASK_ASSOC_RESP, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Failed to set assoc resp ie\n"); + ret = -EFAULT; + goto done; + } + } + +done: + LEAVE(); + return ret; +} +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** + * @brief Set miracast mode + * + * @param priv Pointer to priv stucture + * @param pdata Pointer to cmd buffer + * @param len Length of data + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_miracast_mode(moal_private *priv, t_u8 *pdata, size_t len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = pdata; + + ENTER(); + if (!pos || (len == 0)) { + PRINTM(MERROR, "%s: Null buf!\n", __func__); + ret = MLAN_STATUS_FAILURE; + goto done; + } + while (!isdigit(*pos) && --len > 0) + pos++; + switch (*pos) { + case '0': + /* disable miracast mode */ + priv->phandle->miracast_mode = 0; + break; + case '1': + /* Source */ + priv->phandle->miracast_mode = 1; + break; + case '2': + /* Sink */ + priv->phandle->miracast_mode = 2; + break; + default: + PRINTM(MERROR, "%s: Unknown miracast mode (%c)\n", + priv->netdev->name, *pos); + ret = MLAN_STATUS_FAILURE; + break; + } +done: + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief Get Driver Version + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_get_priv_driver_version(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int len = 0, ret = -1; + char buf[MLAN_MAX_VER_STR_LEN]; + + ENTER(); + + if (!respbuf) { + LEAVE(); + return 0; + } + + memset(buf, 0, sizeof(buf)); + + /* Get version string to local buffer */ + woal_get_version(priv->phandle, buf, sizeof(buf) - 1); + len = strlen(buf); + + if (len) { + /* Copy back the retrieved version string */ + PRINTM(MINFO, "MOAL VERSION: %s\n", buf); + ret = MIN(len, (respbuflen - 1)); + moal_memcpy_ext(priv->phandle, respbuf, buf, ret, + respbuflen - 1); + } else { + ret = -1; + PRINTM(MERROR, "Get version failed!\n"); + } + + LEAVE(); + return ret; +} +/** + * @brief Hostcmd interface from application + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * @param wait_option Wait option + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_hostcmd(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen, + t_u8 wait_option) +{ + int ret = 0; + t_u8 *data_ptr; + t_u32 buf_len = 0; + HostCmd_Header cmd_header; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_HOSTCMD)); + buf_len = *((t_u32 *)data_ptr); + moal_memcpy_ext(priv->phandle, &cmd_header, data_ptr + sizeof(buf_len), + sizeof(HostCmd_Header), 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) > MRVDRV_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 */ + moal_memcpy_ext(priv->phandle, misc_cfg->param.hostcmd.cmd, + data_ptr + sizeof(buf_len), misc_cfg->param.hostcmd.len, + MRVDRV_SIZE_OF_CMD_BUFFER); + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + ret = misc_cfg->param.hostcmd.len + sizeof(buf_len) + strlen(CMD_NXP) + + strlen(PRIV_CMD_HOSTCMD); + if (ret > respbuflen) { + ret = -EFAULT; + goto error; + } + moal_memcpy_ext( + priv->phandle, data_ptr + sizeof(buf_len), + misc_cfg->param.hostcmd.cmd, misc_cfg->param.hostcmd.len, + respbuflen - (strlen(CMD_NXP) + strlen(PRIV_CMD_HOSTCMD) + + sizeof(buf_len))); + moal_memcpy_ext(priv->phandle, data_ptr, + (t_u8 *)&misc_cfg->param.hostcmd.len, sizeof(t_u32), + sizeof(t_u32)); + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief configure 11ax HE capability or HE operation + * + * + * @param priv Pointer to the mlan_private driver data struct + * @param respbuf A pointer to response buffer + * @param len length used + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_setget_priv_11axcmdcfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11ax_cmd_cfg *cfg = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + int header_len = 0, user_data_len = 0; + int data[3] = {0}; + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ax_cmd_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_11AX_CFG; + req->action = MLAN_ACT_SET; + + cfg = (mlan_ds_11ax_cmd_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_11AX_CMD_CFG; + + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + PRINTM(MINFO, "data_len=%d,data=%d,%d,%d\n", user_data_len, data[0], + data[1], data[2]); + + if (user_data_len > 3 || user_data_len == 0) { + PRINTM(MERROR, "Invalid parameters\n"); + ret = -EFAULT; + goto done; + } else if (user_data_len == 1) { + req->action = MLAN_ACT_GET; + } + + switch (data[0]) { + case MLAN_11AXCMD_CFG_ID_SR_OBSS_PD_OFFSET: + cfg->sub_id = MLAN_11AXCMD_SR_SUBID; + cfg->param.sr_cfg.type = MRVL_DOT11AX_OBSS_PD_OFFSET_TLV_ID; + cfg->param.sr_cfg.len = sizeof(mlan_11axcmdcfg_obss_pd_offset); + cfg->param.sr_cfg.param.obss_pd_offset.offset[0] = data[1]; + cfg->param.sr_cfg.param.obss_pd_offset.offset[1] = data[2]; + break; + case MLAN_11AXCMD_CFG_ID_SR_ENABLE: + cfg->sub_id = MLAN_11AXCMD_SR_SUBID; + cfg->param.sr_cfg.type = MRVL_DOT11AX_ENABLE_SR_TLV_ID; + cfg->param.sr_cfg.len = sizeof(mlan_11axcmdcfg_sr_control); + cfg->param.sr_cfg.param.sr_control.control = data[1]; + break; + case MLAN_11AXCMD_CFG_ID_BEAM_CHANGE: + cfg->sub_id = MLAN_11AXCMD_BEAM_SUBID; + cfg->param.beam_cfg.value = data[1]; + break; + case MLAN_11AXCMD_CFG_ID_HTC_ENABLE: + cfg->sub_id = MLAN_11AXCMD_HTC_SUBID; + cfg->param.htc_cfg.value = data[1]; + break; + case MLAN_11AXCMD_CFG_ID_TXOP_RTS: + cfg->sub_id = MLAN_11AXCMD_TXOPRTS_SUBID; + cfg->param.txop_cfg.rts_thres = data[1]; + break; + case MLAN_11AXCMD_CFG_ID_TX_OMI: + cfg->sub_id = MLAN_11AXCMD_TXOMI_SUBID; + cfg->param.txomi_cfg.omi = data[1]; + break; + case MLAN_11AXCMD_CFG_ID_OBSSNBRU_TOLTIME: + cfg->sub_id = MLAN_11AXCMD_OBSS_TOLTIME_SUBID; + cfg->param.toltime_cfg.tol_time = data[1]; + break; + default: + PRINTM(MERROR, "unknown 11axcmd\n"); + ret = -EFAULT; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, respbuf, &req->action, + sizeof(req->action), sizeof(req->action)); + respbuf += sizeof(req->action); + + cfg = (mlan_ds_11ax_cmd_cfg *)respbuf; + moal_memcpy_ext(priv->phandle, cfg, req->pbuf, + sizeof(mlan_ds_11ax_cmd_cfg), respbuflen); + + ret = sizeof(req->action) + sizeof(mlan_ds_11ax_cmd_cfg); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get range ext mode + * + * + * @param priv Pointer to the mlan_private driver data struct + * @param respbuf A pointer to response buffer + * @param len length used + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_setget_priv_range_ext(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + int data[1]; + int header_len = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_RANGE_EXT); + user_data_len = strlen(respbuf) - header_len; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_RANGE_EXT; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid Parameter\n"); + ret = -EFAULT; + goto done; + } + if (data[0] < 0 || data[0] > 2) { + PRINTM(MERROR, + "Invalid Parameter: range_ext mode 0-2\n"); + ret = -EFAULT; + goto done; + } + misc->param.range_ext_mode = (t_u8)data[0]; + req->action = MLAN_ACT_SET; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = misc->param.range_ext_mode; + moal_memcpy_ext(priv->phandle, respbuf, (t_u32 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Custom IE setting + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_customie(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + t_u8 *data_ptr; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *custom_ie = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_CUSTOMIE)); + + custom_ie = (mlan_ds_misc_custom_ie *)data_ptr; + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + if ((custom_ie->len == 0) || + (custom_ie->len == sizeof(custom_ie->ie_data_list[0].ie_index))) + ioctl_req->action = MLAN_ACT_GET; + else + ioctl_req->action = MLAN_ACT_SET; + + moal_memcpy_ext(priv->phandle, &misc->param.cust_ie, custom_ie, + sizeof(mlan_ds_misc_custom_ie), + sizeof(mlan_ds_misc_custom_ie)); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + custom_ie = (mlan_ds_misc_custom_ie *)data_ptr; + moal_memcpy_ext(priv->phandle, custom_ie, &misc->param.cust_ie, + sizeof(mlan_ds_misc_custom_ie), + respbuflen - + (strlen(CMD_NXP) + strlen(PRIV_CMD_CUSTOMIE))); + ret = sizeof(mlan_ds_misc_custom_ie); + if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) { + /* send a separate error code to indicate error from driver */ + ret = EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Band and Adhoc-band setting + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_bandcfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + unsigned int i; + int data[3]; + int user_data_len = 0; + t_u32 infra_band = 0; + t_u32 adhoc_band = 0; + t_u32 adhoc_channel = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + mlan_ds_band_cfg *band_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_BANDCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_BANDCFG), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len > 0) { + if (priv->media_connected == MTRUE) { + LEAVE(); + return -EOPNOTSUPP; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto error; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + if (user_data_len == 0) { + /* Get config_bands, adhoc_start_band and adhoc_channel values + * from MLAN + */ + req->action = MLAN_ACT_GET; + } else { + /* To support only */ + infra_band = data[0]; + for (i = 0; i < (sizeof(SupportedInfraBand) / + sizeof(SupportedInfraBand[0])); + 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; + } + } + /* 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; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + + band_cfg = (mlan_ds_band_cfg *)respbuf; + + moal_memcpy_ext(priv->phandle, band_cfg, &radio_cfg->param.band_cfg, + sizeof(mlan_ds_band_cfg), respbuflen); + + ret = sizeof(mlan_ds_band_cfg); + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11n configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_httxcfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + t_u32 data[2]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_HTTXCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_HTTXCFG), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG; + } else { + cfg_11n->param.tx_cfg.httxcap = data[0]; + PRINTM(MINFO, "SET: httxcap:0x%x\n", data[0]); + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BOTH; + if (user_data_len == 2) { + if (data[1] != BAND_SELECT_BG && + data[1] != BAND_SELECT_A && + data[1] != BAND_SELECT_BOTH) { + PRINTM(MERROR, "Invalid band selection\n"); + ret = -EINVAL; + goto done; + } + cfg_11n->param.tx_cfg.misc_cfg = data[1]; + PRINTM(MINFO, "SET: httxcap band:0x%x\n", data[1]); + } + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.tx_cfg.httxcap; + PRINTM(MINFO, "GET: httxcap:0x%x\n", data[0]); + + if (req->action == MLAN_ACT_GET) { + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[1] = cfg_11n->param.tx_cfg.httxcap; + PRINTM(MINFO, "GET: httxcap for 5GHz:0x%x\n", data[1]); + } + + moal_memcpy_ext(priv->phandle, respbuf, data, sizeof(data), respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11n capability information + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_htcapinfo(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[2]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + woal_ht_cap_info *ht_cap = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_HTCAPINFO))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_HTCAPINFO), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BG; + } else { + cfg_11n->param.htcap_cfg.htcap = data[0]; + PRINTM(MINFO, "SET: htcapinfo:0x%x\n", data[0]); + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BOTH; + if (user_data_len == 2) { + if (data[1] != BAND_SELECT_BG && + data[1] != BAND_SELECT_A && + data[1] != BAND_SELECT_BOTH) { + PRINTM(MERROR, "Invalid band selection\n"); + ret = -EINVAL; + goto done; + } + cfg_11n->param.htcap_cfg.misc_cfg = data[1]; + PRINTM(MINFO, "SET: htcapinfo band:0x%x\n", data[1]); + } + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.htcap_cfg.htcap; + PRINTM(MINFO, "GET: htcapinfo for 2.4GHz:0x%x\n", data[0]); + + if (req->action == MLAN_ACT_GET) { + cfg_11n->param.htcap_cfg.htcap = 0; + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_A; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[1] = cfg_11n->param.htcap_cfg.htcap; + PRINTM(MINFO, "GET: htcapinfo for 5GHz:0x%x\n", data[1]); + } + + ht_cap = (woal_ht_cap_info *)respbuf; + ht_cap->ht_cap_info_bg = data[0]; + ht_cap->ht_cap_info_a = data[1]; + ret = sizeof(woal_ht_cap_info); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get add BA parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_addbapara(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[5]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + woal_addba *addba = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_ADDBAPARA))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_ADDBAPARA), + data, ARRAY_SIZE(data), &user_data_len); + + if (user_data_len != ARRAY_SIZE(data)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + if (data[0] < 0 || data[0] > MLAN_DEFAULT_BLOCK_ACK_TIMEOUT) { + PRINTM(MERROR, "Incorrect addba timeout value.\n"); + ret = -EFAULT; + goto done; + } + if (data[1] <= 0 || data[1] > MLAN_AMPDU_MAX_TXWINSIZE || + data[2] <= 0 || data[2] > MLAN_AMPDU_MAX_RXWINSIZE) { + PRINTM(MERROR, "Incorrect Tx/Rx window size.\n"); + ret = -EFAULT; + goto done; + } + if (data[3] < 0 || data[3] > 1 || data[4] < 0 || data[4] > 1) { + PRINTM(MERROR, "Incorrect Tx/Rx amsdu.\n"); + ret = -EFAULT; + goto done; + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get add BA parameters from MLAN */ + req->action = MLAN_ACT_GET; + } else { + cfg_11n->param.addba_param.timeout = data[0]; + cfg_11n->param.addba_param.txwinsize = data[1]; + cfg_11n->param.addba_param.rxwinsize = data[2]; + cfg_11n->param.addba_param.txamsdu = data[3]; + cfg_11n->param.addba_param.rxamsdu = data[4]; + PRINTM(MINFO, + "SET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d rxamsdu=%d\n", + data[0], data[1], data[2], data[3], data[4]); + /* Update add BA parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + addba = (woal_addba *)respbuf; + + addba->time_out = cfg_11n->param.addba_param.timeout; + addba->tx_win_size = cfg_11n->param.addba_param.txwinsize; + addba->rx_win_size = cfg_11n->param.addba_param.rxwinsize; + addba->tx_amsdu = cfg_11n->param.addba_param.txamsdu; + addba->rx_amsdu = cfg_11n->param.addba_param.rxamsdu; + PRINTM(MINFO, + "GET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d, rxamsdu=%d\n", + addba->time_out, addba->tx_win_size, addba->rx_win_size, + addba->tx_amsdu, addba->rx_amsdu); + + ret = sizeof(woal_addba); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Delete selective BA based on parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_delba(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[2] = {0xFF, 0xFF}; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ds_11n_delba *del_ba = NULL; + int ret = 0; + int user_data_len = 0; + int header_len = 0; + t_u8 *mac_pos = NULL; + t_u8 peer_mac[ETH_ALEN] = {0}; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DELBA); + + if (strlen(respbuf) == header_len) { + /* Incorrect number of arguments */ + PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); + ret = -EINVAL; + goto done; + } + + mac_pos = strstr(respbuf + header_len, " "); + if (mac_pos) + mac_pos = strstr(mac_pos + 1, " "); + if (mac_pos) { +#define MAC_STRING_LENGTH 17 + if (strlen(mac_pos + 1) != MAC_STRING_LENGTH) { + PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); + ret = -EINVAL; + goto done; + } + woal_mac2u8(peer_mac, mac_pos + 1); + *mac_pos = '\0'; + } + + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (mac_pos) + user_data_len++; + + if (user_data_len > 3 || (!(data[0] & (DELBA_TX | DELBA_RX))) || + (data[1] != DELBA_ALL_TIDS && !(data[1] <= 7))) { + /* Incorrect number of arguments */ + PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + cfg_11n->sub_command = MLAN_OID_11N_CFG_DELBA; + + del_ba = &cfg_11n->param.del_ba; + memset(del_ba, 0, sizeof(mlan_ds_11n_delba)); + del_ba->direction = (t_u8)data[0]; + del_ba->tid = DELBA_ALL_TIDS; + if (user_data_len > 1) + del_ba->tid = (t_u8)data[1]; + if (user_data_len > 2) + moal_memcpy_ext(priv->phandle, del_ba->peer_mac_addr, peer_mac, + ETH_ALEN, MLAN_MAC_ADDR_LENGTH); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + ret = sprintf(respbuf, "OK. BA deleted successfully.\n") + 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get the reject addba requst conditions + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_rejectaddbareq(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + t_u32 data[1]; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_REJECTADDBAREQ))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_REJECTADDBAREQ), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_REJECT_ADDBA_REQ; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get the reject addba req conditions*/ + req->action = MLAN_ACT_GET; + } else { + /* Set the reject addba req conditions */ + cfg_11n->param.reject_addba_req.conditions = data[0]; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (req->action == MLAN_ACT_GET) { + sprintf(respbuf, "0x%x", + cfg_11n->param.reject_addba_req.conditions); + ret = strlen(respbuf) + 1; + } else { + ret = sprintf(respbuf, "OK\n") + 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get the addba reject setting + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param addba_reject A pointer to addba_reject array. + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_ioctl_addba_reject(moal_private *priv, t_u32 action, + t_u8 *addba_reject) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; + req->req_id = MLAN_IOCTL_11N_CFG; + + req->action = action; + if (action == MLAN_ACT_SET) + moal_memcpy_ext(priv->phandle, cfg_11n->param.addba_reject, + addba_reject, + sizeof(cfg_11n->param.addba_reject), + sizeof(cfg_11n->param.addba_reject)); + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, addba_reject, + cfg_11n->param.addba_reject, + sizeof(cfg_11n->param.addba_reject), + MAX_NUM_TID); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get addba prio_tbl + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param aggr_prio_tbl A pointer to mlan_ds_11n_aggr_prio_tbl. + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_ioctl_aggr_prio_tbl(moal_private *priv, t_u32 action, + mlan_ds_11n_aggr_prio_tbl *aggr_prio_tbl) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; + req->req_id = MLAN_IOCTL_11N_CFG; + + req->action = action; + if (action == MLAN_ACT_SET) + moal_memcpy_ext(priv->phandle, &cfg_11n->param.aggr_prio_tbl, + aggr_prio_tbl, + sizeof(mlan_ds_11n_aggr_prio_tbl), + sizeof(mlan_ds_11n_aggr_prio_tbl)); + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, aggr_prio_tbl, + &cfg_11n->param.aggr_prio_tbl, + sizeof(mlan_ds_11n_aggr_prio_tbl), + sizeof(mlan_ds_11n_aggr_prio_tbl)); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get addba_param + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param addba_param A pointer to mlan_ds_11n_addba_param. + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_ioctl_addba_param(moal_private *priv, t_u32 action, + mlan_ds_11n_addba_param *addba_param) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; + req->req_id = MLAN_IOCTL_11N_CFG; + + req->action = action; + if (action == MLAN_ACT_SET) + moal_memcpy_ext(priv->phandle, &cfg_11n->param.addba_param, + addba_param, sizeof(mlan_ds_11n_addba_param), + sizeof(mlan_ds_11n_addba_param)); + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, addba_param, + &cfg_11n->param.addba_param, + sizeof(mlan_ds_11n_addba_param), + sizeof(mlan_ds_11n_addba_param)); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Configuring rx block-ack window size + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise failure + */ +int woal_set_rx_ba_winsize(moal_private *priv, t_u8 *respbuf, int respbuflen) +{ + int data[2]; + t_u8 addba_reject[MAX_NUM_TID]; + mlan_ds_11n_addba_param addba_param; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + memset((char *)data, 0, sizeof(data)); + if (respbuf && strlen(respbuf) > 0) + parse_arguments(respbuf, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len != 2) { + PRINTM(MERROR, "Invalid arguments for ba_winsize command\n"); + ret = -EINVAL; + goto done; + } + if (data[0] > 7 || data[0] < 0) { + PRINTM(MERROR, "Invalid tid %d\n", data[0]); + ret = -EINVAL; + goto done; + } + if (data[1] < 0) { + PRINTM(MERROR, "Invalid winsize %d\n", data[1]); + ret = -EINVAL; + goto done; + } + memset(addba_reject, 0, sizeof(addba_reject)); + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_reject(priv, MLAN_ACT_GET, addba_reject)) { + ret = -EFAULT; + goto done; + } + /* disable tx ba */ + if (data[1] == 0) { + addba_reject[data[0]] = MTRUE; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_reject(priv, MLAN_ACT_SET, addba_reject)) + ret = -EFAULT; + } else { + if (addba_reject[data[0]] == MTRUE) { + addba_reject[data[0]] = MFALSE; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_reject(priv, MLAN_ACT_SET, + addba_reject)) { + ret = -EFAULT; + goto done; + } + } + memset(&addba_param, 0, sizeof(addba_param)); + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_param(priv, MLAN_ACT_GET, &addba_param)) { + ret = -EFAULT; + goto done; + } + if (data[1] != addba_param.rxwinsize) { + addba_param.rxwinsize = data[1]; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_param(priv, MLAN_ACT_SET, + &addba_param)) + ret = -EFAULT; + } + } +done: + LEAVE(); + return ret; +} +/** + * @brief Configuring trx block-ack window size + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise failure + */ +int woal_set_tx_ba_winsize(moal_private *priv, t_u8 *respbuf, int respbuflen) +{ + int data[2]; + mlan_ds_11n_aggr_prio_tbl aggr_prio_tbl; + mlan_ds_11n_addba_param addba_param; + t_u8 tos_to_tid_inv[] = {0x02, 0x00, 0x01, 0x03, + 0x04, 0x05, 0x06, 0x07}; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + memset((char *)data, 0, sizeof(data)); + if (respbuf && strlen(respbuf) > 0) + parse_arguments(respbuf, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len != 2) { + PRINTM(MERROR, "Invalid arguments for ba_winsize command\n"); + ret = -EINVAL; + goto done; + } + if (data[0] > 7 || data[0] < 0) { + PRINTM(MERROR, "Invalid tid %d\n", data[0]); + ret = -EINVAL; + goto done; + } + if (data[1] < 0) { + PRINTM(MERROR, "Invalid winsize %d\n", data[1]); + ret = -EINVAL; + goto done; + } + memset(&aggr_prio_tbl, 0, sizeof(aggr_prio_tbl)); + if (MLAN_STATUS_SUCCESS != + woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_GET, &aggr_prio_tbl)) { + ret = -EFAULT; + goto done; + } + /* disable tx ba */ + if (data[1] == 0) { + if (aggr_prio_tbl.ampdu[data[0]] != 0xff) { + aggr_prio_tbl.ampdu[data[0]] = 0xff; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_SET, + &aggr_prio_tbl)) + ret = -EFAULT; + } + } else { + if (aggr_prio_tbl.ampdu[data[0]] == 0xff) { + aggr_prio_tbl.ampdu[data[0]] = tos_to_tid_inv[data[0]]; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_aggr_prio_tbl(priv, MLAN_ACT_SET, + &aggr_prio_tbl)) { + ret = -EFAULT; + goto done; + } + } + memset(&addba_param, 0, sizeof(addba_param)); + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_param(priv, MLAN_ACT_GET, &addba_param)) { + ret = -EFAULT; + goto done; + } + if (data[1] != addba_param.txwinsize) { + addba_param.txwinsize = data[1]; + if (MLAN_STATUS_SUCCESS != + woal_ioctl_addba_param(priv, MLAN_ACT_SET, + &addba_param)) + ret = -EFAULT; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get aggregation priority table configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_aggrpriotbl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[MAX_NUM_TID * 2], i, j; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_AGGRPRIOTBL))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_AGGRPRIOTBL), + data, ARRAY_SIZE(data), &user_data_len); + + if (user_data_len != ARRAY_SIZE(data)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + for (i = 0, j = 0; i < user_data_len; i = i + 2, ++j) { + if ((data[i] > 7 && data[i] != 0xff) || + (data[i + 1] > 7 && data[i + 1] != 0xff)) { + PRINTM(MERROR, + "Invalid priority, valid value 0-7 or 0xff.\n"); + ret = -EFAULT; + goto done; + } + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get aggr priority table from MLAN */ + req->action = MLAN_ACT_GET; + } else { + for (i = 0, j = 0; i < user_data_len; i = i + 2, ++j) { + cfg_11n->param.aggr_prio_tbl.ampdu[j] = data[i]; + cfg_11n->param.aggr_prio_tbl.amsdu[j] = data[i + 1]; + } + /* Update aggr priority table in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + for (i = 0, j = 0; i < (MAX_NUM_TID * 2); i = i + 2, ++j) { + respbuf[i] = cfg_11n->param.aggr_prio_tbl.ampdu[j]; + respbuf[i + 1] = cfg_11n->param.aggr_prio_tbl.amsdu[j]; + } + + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Add BA reject configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_addbareject(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[MAX_NUM_TID], i; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_ADDBAREJECT))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_ADDBAREJECT), + data, ARRAY_SIZE(data), &user_data_len); + + if (user_data_len != ARRAY_SIZE(data)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + for (i = 0; i < user_data_len; i++) { + if (data[i] != 0 && data[i] != 1) { + PRINTM(MERROR, + "addba reject only takes argument as 0 or 1\n"); + ret = -EFAULT; + goto done; + } + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (user_data_len == 0) { + /* Get add BA reject configuration from MLAN */ + req->action = MLAN_ACT_GET; + } else { + for (i = 0; i < user_data_len; i++) + cfg_11n->param.addba_reject[i] = data[i]; + /* Update add BA reject configuration in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + for (i = 0; i < MAX_NUM_TID; i++) + respbuf[i] = cfg_11n->param.addba_reject[i]; + + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11AC configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_vhtcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[6]; + mlan_ioctl_req *req = NULL; + mlan_ds_11ac_cfg *cfg_11ac = NULL; + mlan_ds_11ac_vht_cfg *vhtcfg = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_VHTCFG))) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + strlen(PRIV_CMD_VHTCFG), + data, ARRAY_SIZE(data), &user_data_len); + + if ((user_data_len > 6) || (user_data_len < 2)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ac_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11ac = (mlan_ds_11ac_cfg *)req->pbuf; + cfg_11ac->sub_command = MLAN_OID_11AC_VHT_CFG; + req->req_id = MLAN_IOCTL_11AC_CFG; + + /* Band */ + if ((data[0] < 0) || (data[0] > 2)) { + PRINTM(MERROR, "Invalid band selection\n"); + ret = -EINVAL; + goto done; + } else { + if (data[0] == BAND_SELECT_BOTH) { + cfg_11ac->param.vht_cfg.band = + (BAND_SELECT_BG | BAND_SELECT_A); + } else { + cfg_11ac->param.vht_cfg.band = data[0]; + } + PRINTM(MINFO, "GET/SET: vhtcfg band: 0x%x\n", data[0]); + } + + /* Tx/Rx */ + if ((data[1] <= 0) || (data[1] > 3)) { + PRINTM(MERROR, "Invalid Tx/Rx selection\n"); + ret = -EINVAL; + goto done; + } else { + cfg_11ac->param.vht_cfg.txrx = data[1]; + PRINTM(MINFO, "GET/SET: vhtcfg txrx: 0x%x\n", data[1]); + } + + if (user_data_len == 2) { + /* GET operation */ + if (data[0] == BAND_SELECT_BOTH) { + /* if get both bands, get BG first */ + cfg_11ac->param.vht_cfg.band = BAND_SELECT_BG; + } + if (priv->bss_role == MLAN_BSS_ROLE_UAP) + cfg_11ac->param.vht_cfg.txrx = MLAN_RADIO_RX; + + req->action = MLAN_ACT_GET; + } else { + if (user_data_len == 3) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len >= 4) { + /* BW cfg */ + if ((data[2] < 0) || (data[2] > 1) || + ((data[2] == 1) && (data[0] & BAND_SELECT_BG))) { + PRINTM(MERROR, "Invalid BW cfg selection\n"); + ret = -EINVAL; + goto done; + } else { + cfg_11ac->param.vht_cfg.bwcfg = data[2]; + PRINTM(MINFO, "SET: vhtcfg bw cfg:0x%x\n", + data[2]); + } + + cfg_11ac->param.vht_cfg.vht_cap_info = data[3]; + PRINTM(MINFO, "SET: vhtcfg vht_cap_info:0x%x\n", + data[3]); + } + if (user_data_len == 4) { + data[4] = 0xffffffff; + data[5] = 0xffffffff; + } + if (user_data_len == 5) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len >= 4) { + cfg_11ac->param.vht_cfg.vht_tx_mcs = data[4]; + cfg_11ac->param.vht_cfg.vht_rx_mcs = data[5]; + } + /* Update 11AC parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* number of vhtcfg entries */ + *respbuf = 1; + vhtcfg = (mlan_ds_11ac_vht_cfg *)(respbuf + 1); + moal_memcpy_ext(priv->phandle, vhtcfg, &cfg_11ac->param.vht_cfg, + sizeof(mlan_ds_11ac_vht_cfg), respbuflen - 1); + ret = 1 + sizeof(mlan_ds_11ac_vht_cfg); + + if ((req->action == MLAN_ACT_GET) && (data[0] == BAND_SELECT_BOTH)) { + cfg_11ac->param.vht_cfg.band = BAND_SELECT_A; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + /* number of vhtcfg entries */ + *respbuf = 2; + vhtcfg++; + moal_memcpy_ext(priv->phandle, vhtcfg, &cfg_11ac->param.vht_cfg, + sizeof(mlan_ds_11ac_vht_cfg), + respbuflen - 1 - sizeof(mlan_ds_11ac_vht_cfg)); + ret += sizeof(mlan_ds_11ac_vht_cfg); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11AC Operating Mode Notification 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_opermodecfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[2]; + mlan_ioctl_req *req = NULL; + mlan_ds_11ac_cfg *cfg_11ac = NULL; + mlan_ds_11ac_opermode_cfg *opermodecfg = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_OPERMODECFG), + data, ARRAY_SIZE(data), &user_data_len); + + if ((user_data_len != 0) && (user_data_len != 2)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len == 2) { + /* Channel width */ + if ((data[0] < 1) || (data[0] > 4)) { + PRINTM(MERROR, "Invalid channel width: 0x%x\n", + data[0]); + ret = -EINVAL; + goto done; + } + /* nss */ + if ((data[1] < 1) || (data[1] > 8)) { + PRINTM(MERROR, "Invalid nss: 0x%x\n", data[1]); + ret = -EINVAL; + goto done; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ac_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11ac = (mlan_ds_11ac_cfg *)req->pbuf; + cfg_11ac->sub_command = MLAN_OID_11AC_OPERMODE_CFG; + req->req_id = MLAN_IOCTL_11AC_CFG; + + if (user_data_len == 0) { + req->action = MLAN_ACT_GET; + } else { + req->action = MLAN_ACT_SET; + cfg_11ac->param.opermode_cfg.bw = data[0]; + cfg_11ac->param.opermode_cfg.nss = data[1]; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + opermodecfg = (mlan_ds_11ac_opermode_cfg *)respbuf; + moal_memcpy_ext(priv->phandle, opermodecfg, + &(cfg_11ac->param.opermode_cfg), sizeof(*opermodecfg), + respbuflen); + ret = sizeof(*opermodecfg); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11AC configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_get_priv_datarate(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + mlan_data_rate *data_rate = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + rate = (mlan_ds_rate *)req->pbuf; + rate->sub_command = MLAN_OID_GET_DATA_RATE; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data_rate = (mlan_data_rate *)respbuf; + + moal_memcpy_ext(priv->phandle, data_rate, &rate->param.data_rate, + sizeof(mlan_data_rate), respbuflen); + + ret = sizeof(mlan_data_rate); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get tx rate configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_txratecfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + t_u32 data[4]; + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + woal_tx_rate_cfg *ratecfg = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + txrate_setting *rate_setting = NULL; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_TXRATECFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_TXRATECFG), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 4) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_RATE; + rate = (mlan_ds_rate *)req->pbuf; + rate->sub_command = MLAN_OID_RATE_CFG; + rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX; + + if (user_data_len == 0) { + /* Get operation */ + req->action = MLAN_ACT_GET; + } else { + /* Set operation */ + req->action = MLAN_ACT_SET; + /* format */ + if ((data[0] != AUTO_RATE) && (data[0] >= 4)) { + 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_HE)) { + PRINTM(MERROR, "Invalid format selection\n"); + ret = -EINVAL; + goto done; + } + rate->param.rate_cfg.rate_format = data[0]; + + if (user_data_len >= 2) { + 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)) || + ((data[0] == MLAN_RATE_FORMAT_VHT) && + (data[1] > MLAN_RATE_INDEX_MCS9)) || + ((data[0] == MLAN_RATE_FORMAT_HE) && + (data[1] > MLAN_RATE_INDEX_MCS11))) { + 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] == 2 || data[0] == 3) { + PRINTM(MINFO, "SET: txratefg nss: 0x%x\n", + data[2]); + /* NSS is supported up to 2 */ + if ((data[2] <= 0) || (data[2] >= 3)) { + PRINTM(MERROR, + "Invalid nss selection\n"); + ret = -EINVAL; + goto done; + } + rate->param.rate_cfg.nss = data[2]; + } + if (user_data_len == 4) { + rate->param.rate_cfg.rate_setting = + data[3] & ~0x0C00; + PRINTM(MIOCTL, + "SET: txratefg HE Rate Setting: 0x%x\n", + data[3]); + +/* HE Preamble type */ +#define HE_SU_PREAMBLE 0 +#define HE_ER_PREAMBLE 1 + +/* HE ER SU Type */ +#define HE_ER_SU_BANDWIDTH_TONE242 0 +#define HE_ER_SU_BANDWIDTH_TONE106 1 + + rate_setting = (txrate_setting *)&data[3]; + + if (data[0] == MLAN_RATE_FORMAT_HE) { + if (rate_setting->preamble == + HE_ER_PREAMBLE) { + if (rate_setting->bandwidth == + HE_ER_SU_BANDWIDTH_TONE242) { + if ((data[1] > + MLAN_RATE_INDEX_MCS4) || + data[2] > + MLAN_RATE_NSS1) { + PRINTM(MERROR, + "Invalid rate and MCS or NSS configuration for 242 tone\n"); + ret = -EINVAL; + goto done; + } + } else if (rate_setting + ->bandwidth == + HE_ER_SU_BANDWIDTH_TONE106) { + if ((data[1] != + MLAN_RATE_INDEX_MCS0) || + data[2] != + MLAN_RATE_NSS1) { + PRINTM(MERROR, + "Invalid rate and MCS or NSS configuration\n for 106 tone"); + ret = -EINVAL; + goto done; + } + } else { + PRINTM(MERROR, + "Invalid Bandwidth for HE ER Preamble\n"); + ret = -EINVAL; + goto done; + } + } + if (rate_setting->dcm) { + if ((data[1] == + MLAN_RATE_INDEX_MCS2) || + (data[1] > + MLAN_RATE_INDEX_MCS4)) { + PRINTM(MERROR, + "Invalid MCS configuration if DCM is supported\n"); + ret = -EINVAL; + goto done; + } + } + } + } else { + rate->param.rate_cfg.rate_setting = 0xffff; + } + } + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + ratecfg = (woal_tx_rate_cfg *)respbuf; + if (rate->param.rate_cfg.is_rate_auto == MTRUE) { + ratecfg->rate_format = 0xFF; + } else { + /* fixed rate */ + ratecfg->rate_format = rate->param.rate_cfg.rate_format; + ratecfg->rate_index = rate->param.rate_cfg.rate; + if (rate->param.rate_cfg.rate_format == MLAN_RATE_FORMAT_VHT || + rate->param.rate_cfg.rate_format == MLAN_RATE_FORMAT_HE) + ratecfg->nss = rate->param.rate_cfg.nss; + ratecfg->rate_setting = rate->param.rate_cfg.rate_setting; + } + + ret = sizeof(woal_tx_rate_cfg); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_SUPPORT) || defined(UAP_SUPPORT) +/** + * @brief Get statistics information + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param stats A pointer to mlan_ds_get_stats structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_get_stats_info(moal_private *priv, t_u8 wait_option, + mlan_ds_get_stats *stats) +{ + int ret = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + info->sub_command = MLAN_OID_GET_STATS; + else if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + info->sub_command = MLAN_OID_GET_UAP_STATS_LOG; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (stats) + moal_memcpy_ext(priv->phandle, stats, + &info->param.stats, + sizeof(mlan_ds_get_stats), + sizeof(mlan_ds_get_stats)); +#if defined(STA_WEXT) || defined(UAP_WEXT) + priv->w_stats.discard.fragment = info->param.stats.fcs_error; + priv->w_stats.discard.retries = info->param.stats.retry; + priv->w_stats.discard.misc = info->param.stats.ack_failure; +#endif + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get wireless stats information + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_get_priv_getlog(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_get_stats *stats; + ENTER(); + + if (respbuflen < sizeof(*stats)) { + PRINTM(MERROR, "Get log: respbuflen (%d) too small!", + (int)respbuflen); + ret = -EFAULT; + goto done; + } + stats = (mlan_ds_get_stats *)respbuf; + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, stats)) { + PRINTM(MERROR, "Get log: Failed to get stats info!"); + ret = -EFAULT; + goto done; + } + + if (priv->phandle->fw_getlog_enable) + ret = sizeof(mlan_ds_get_stats); + else + ret = sizeof(mlan_ds_get_stats_org); + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set/Get esupplicant mode configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_esuppmode(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + t_u32 data[3]; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + woal_esuppmode_cfg *esupp_mode = NULL; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv->phandle->card_info->embedded_supp) { + PRINTM(MERROR, "Not supported cmd on this card\n"); + ret = -EOPNOTSUPP; + goto done; + } + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_ESUPPMODE))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_ESUPPMODE), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len >= 4 || user_data_len == 1 || user_data_len == 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ESUPP_MODE; + + if (user_data_len == 0) { + /* Get operation */ + req->action = MLAN_ACT_GET; + } else { + /* Set operation */ + req->action = MLAN_ACT_SET; + /* RSN mode */ + sec->param.esupp_mode.rsn_mode = data[0]; + /* Pairwise cipher */ + sec->param.esupp_mode.act_paircipher = (data[1] & 0xFF); + /* Group cipher */ + sec->param.esupp_mode.act_groupcipher = (data[2] & 0xFF); + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + esupp_mode = (woal_esuppmode_cfg *)respbuf; + esupp_mode->rsn_mode = + (t_u16)((sec->param.esupp_mode.rsn_mode) & 0xFFFF); + esupp_mode->pairwise_cipher = + (t_u8)((sec->param.esupp_mode.act_paircipher) & 0xFF); + esupp_mode->group_cipher = + (t_u8)((sec->param.esupp_mode.act_groupcipher) & 0xFF); + + ret = sizeof(woal_esuppmode_cfg); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get esupplicant passphrase configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_setget_priv_passphrase(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0, action = -1, i = 0; + char *begin, *end, *opt; + t_u16 len = 0; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + t_u8 *mac = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv->phandle->card_info->embedded_supp) { + PRINTM(MERROR, "Not supported cmd on this card\n"); + ret = -EOPNOTSUPP; + goto done; + } + + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_PASSPHRASE))) { + PRINTM(MERROR, "No arguments provided\n"); + ret = -EINVAL; + goto done; + } + + /* Parse the buf to get the cmd_action */ + begin = respbuf + strlen(CMD_NXP) + strlen(PRIV_CMD_PASSPHRASE); + end = woal_strsep(&begin, ';', '/'); + if (end) + action = woal_atox(end); + if (action < 0 || action > 2 || end[1] != '\0') { + PRINTM(MERROR, "Invalid action argument %s\n", end); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_SEC_CFG; + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; + if (action == 0) + req->action = MLAN_ACT_GET; + else + req->action = MLAN_ACT_SET; + + while (begin) { + end = woal_strsep(&begin, ';', '/'); + opt = woal_strsep(&end, '=', '/'); + if (!opt || !end || !end[0]) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + break; + } else if (!strnicmp(opt, "ssid", strlen(opt))) { + if (strlen(end) > MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, + "SSID length exceeds max length\n"); + ret = -EFAULT; + break; + } + sec->param.passphrase.ssid.ssid_len = strlen(end); + moal_memcpy_ext(priv->phandle, + sec->param.passphrase.ssid.ssid, end, + strlen(end), MLAN_MAX_SSID_LENGTH); + PRINTM(MINFO, "ssid=%s, len=%d\n", + sec->param.passphrase.ssid.ssid, + (int)sec->param.passphrase.ssid.ssid_len); + } else if (!strnicmp(opt, "bssid", strlen(opt))) { + woal_mac2u8((t_u8 *)&sec->param.passphrase.bssid, end); + } else if (!strnicmp(opt, "psk", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) != MLAN_PMK_HEXSTR_LENGTH) { + PRINTM(MERROR, "Invalid PMK length\n"); + ret = -EINVAL; + break; + } + woal_ascii2hex( + (t_u8 *)(sec->param.passphrase.psk.pmk.pmk), + end, MLAN_PMK_HEXSTR_LENGTH / 2); + sec->param.passphrase.psk_type = MLAN_PSK_PMK; + } else if (!strnicmp(opt, "passphrase", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) < MLAN_MIN_PASSPHRASE_LENGTH || + strlen(end) > MLAN_MAX_PASSPHRASE_LENGTH) { + PRINTM(MERROR, + "Invalid length for passphrase\n"); + ret = -EINVAL; + break; + } + sec->param.passphrase.psk_type = MLAN_PSK_PASSPHRASE; + moal_memcpy_ext( + priv->phandle, + sec->param.passphrase.psk.passphrase.passphrase, + end, + sizeof(sec->param.passphrase.psk.passphrase + .passphrase), + 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 if (!strnicmp(opt, "sae_password", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) < MLAN_MIN_SAE_PASSWORD_LENGTH || + strlen(end) > MLAN_MAX_SAE_PASSWORD_LENGTH) { + PRINTM(MERROR, + "Invalid length for sae password\n"); + ret = -EINVAL; + break; + } + sec->param.passphrase.psk_type = MLAN_PSK_SAE_PASSWORD; + moal_memcpy_ext( + priv->phandle, + sec->param.passphrase.psk.sae_password + .sae_password, + end, + sizeof(sec->param.passphrase.psk.sae_password + .sae_password), + sizeof(sec->param.passphrase.psk.sae_password + .sae_password)); + sec->param.passphrase.psk.sae_password.sae_password_len = + strlen(end); + PRINTM(MINFO, "sae_password=%s, len=%d\n", + sec->param.passphrase.psk.sae_password + .sae_password, + (int)sec->param.passphrase.psk.sae_password + .sae_password_len); + } else { + PRINTM(MERROR, "Invalid option %s\n", opt); + ret = -EINVAL; + break; + } + } + if (ret) + goto done; + + if (action == 2) + sec->param.passphrase.psk_type = MLAN_PSK_CLEAR; + else if (action == 0) + sec->param.passphrase.psk_type = MLAN_PSK_QUERY; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(respbuf, 0, respbuflen); + if (sec->param.passphrase.ssid.ssid_len) { + len += sprintf(respbuf + len, "ssid:"); + moal_memcpy_ext(priv->phandle, respbuf + len, + sec->param.passphrase.ssid.ssid, + sec->param.passphrase.ssid.ssid_len, + respbuflen - len); + len += sec->param.passphrase.ssid.ssid_len; + len += sprintf(respbuf + len, " "); + } + if (memcmp(&sec->param.passphrase.bssid, zero_mac, sizeof(zero_mac))) { + mac = (t_u8 *)&sec->param.passphrase.bssid; + len += sprintf(respbuf + len, "bssid:"); + for (i = 0; i < ETH_ALEN - 1; ++i) + len += sprintf(respbuf + len, "%02x:", mac[i]); + len += sprintf(respbuf + len, "%02x ", mac[i]); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) { + len += sprintf(respbuf + len, "psk:"); + for (i = 0; i < MLAN_MAX_KEY_LENGTH; ++i) + len += sprintf(respbuf + len, "%02x", + sec->param.passphrase.psk.pmk.pmk[i]); + len += sprintf(respbuf + len, "\n"); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) + len += sprintf(respbuf + len, "passphrase:%s\n", + sec->param.passphrase.psk.passphrase.passphrase); + if (sec->param.passphrase.psk_type == MLAN_PSK_SAE_PASSWORD) + len += sprintf( + respbuf + len, "sae_password:%s\n", + sec->param.passphrase.psk.sae_password.sae_password); + + ret = len; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Deauthenticate + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_deauth(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + t_u8 mac[ETH_ALEN]; + + ENTER(); + + if (strlen(respbuf) > (strlen(CMD_NXP) + strlen(PRIV_CMD_DEAUTH))) { + /* Deauth mentioned BSSID */ + woal_mac2u8(mac, respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_DEAUTH)); + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, mac, + DEF_DEAUTH_REASON_CODE)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE)) + ret = -EFAULT; + } + +done: + LEAVE(); + return ret; +} + +#ifdef UAP_SUPPORT +/** + * @brief uap station deauth ioctl handler + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_ap_deauth(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + t_u8 *data_ptr; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_bss *bss = NULL; + mlan_deauth_param deauth_param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_AP_DEAUTH)); + memset(&deauth_param, 0, sizeof(mlan_deauth_param)); + moal_memcpy_ext(priv->phandle, &deauth_param, data_ptr, + sizeof(mlan_deauth_param), sizeof(mlan_deauth_param)); + + PRINTM(MIOCTL, "ioctl deauth station: " MACSTR ", reason=%d\n", + MAC2STR(deauth_param.mac_addr), deauth_param.reason_code); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)ioctl_req->pbuf; + + bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; + ioctl_req->req_id = MLAN_IOCTL_BSS; + ioctl_req->action = MLAN_ACT_SET; + + moal_memcpy_ext(priv->phandle, &bss->param.deauth_param, &deauth_param, + sizeof(mlan_deauth_param), sizeof(mlan_deauth_param)); + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext( + priv->phandle, data_ptr, &ioctl_req->status_code, sizeof(t_u32), + respbuflen - (strlen(CMD_NXP) + strlen(PRIV_CMD_AP_DEAUTH))); + ret = sizeof(t_u32); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap get station list handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_priv_get_sta_list(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_get_info *info = NULL; + mlan_ds_sta_list *sta_list = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + ioctl_req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + sta_list = (mlan_ds_sta_list *)(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_GET_STA_LIST)); + moal_memcpy_ext( + priv->phandle, sta_list, &info->param.sta_list, + sizeof(mlan_ds_sta_list), + respbuflen - (strlen(CMD_NXP) + strlen(PRIV_CMD_GET_STA_LIST))); + ret = sizeof(mlan_ds_sta_list); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap bss_config handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_priv_bss_config(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *ioctl_req = NULL; + t_u32 action = 0; + int offset = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + offset = strlen(CMD_NXP) + strlen(PRIV_CMD_BSS_CONFIG); + moal_memcpy_ext(priv->phandle, (u8 *)&action, respbuf + offset, + sizeof(action), sizeof(action)); + offset += sizeof(action); + + /* Allocate an IOCTL request buffer */ + ioctl_req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *)ioctl_req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_BSS; + if (action == 1) { + ioctl_req->action = MLAN_ACT_SET; + /* Get the BSS config from user */ + moal_memcpy_ext(priv->phandle, &bss->param.bss_config, + respbuf + offset, sizeof(mlan_uap_bss_param), + sizeof(mlan_uap_bss_param)); + } else { + ioctl_req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + moal_memcpy_ext(priv->phandle, respbuf + offset, + &bss->param.bss_config, + sizeof(mlan_uap_bss_param), + respbuflen - (t_u32)offset); + } + ret = sizeof(mlan_uap_bss_param); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief Set/Get BSS role + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_bssrole(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u32 data[1]; + int ret = 0; + int user_data_len = 0; + t_u8 action = MLAN_ACT_GET; + + ENTER(); + + memset((char *)data, 0, sizeof(data)); + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_BSSROLE))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_BSSROLE), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len >= 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto error; + } + + if (user_data_len == 0) { + action = MLAN_ACT_GET; + } else { + if ((data[0] != MLAN_BSS_ROLE_STA && + data[0] != MLAN_BSS_ROLE_UAP) || + priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MWARN, "Invalid BSS role\n"); + ret = -EINVAL; + goto error; + } + if (data[0] == GET_BSS_ROLE(priv)) { + PRINTM(MWARN, "Already BSS is in desired role\n"); + goto done; + } + action = MLAN_ACT_SET; + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(priv, action, MOAL_IOCTL_WAIT, (t_u8 *)data)) { + ret = -EFAULT; + goto error; + } + + if (user_data_len) { + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + /* Enable interfaces */ + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); + } + +done: + memset(respbuf, 0, respbuflen); + respbuf[0] = (t_u8)data[0]; + ret = 1; + +error: + LEAVE(); + return ret; +} +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT */ + +#ifdef STA_SUPPORT +/** + * @brief Set user scan + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_setuserscan(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + wlan_user_scan_cfg scan_cfg; + int ret = 0; + + ENTER(); + + /* Create the scan_cfg structure */ + memset(&scan_cfg, 0, sizeof(scan_cfg)); + + /* We expect the scan_cfg structure to be passed in respbuf */ + moal_memcpy_ext(priv->phandle, (char *)&scan_cfg, + respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_SETUSERSCAN), + sizeof(wlan_user_scan_cfg), sizeof(wlan_user_scan_cfg)); + moal_memcpy_ext(priv->phandle, scan_cfg.random_mac, priv->random_mac, + ETH_ALEN, MLAN_MAC_ADDR_LENGTH); + /* Call for scan */ + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_cfg)) + ret = -EFAULT; + + LEAVE(); + return ret; +} + +/** + * @brief Get channel statistics + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_get_chanstats(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_scan_resp scan_resp; + chan_stats *stats = NULL; + int ret = 0; + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + 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; + } + memset(respbuf, 0, respbuflen); + stats = (chan_stats *)respbuf; + stats->num_in_chan_stats = scan_resp.num_in_chan_stats; + ret = sizeof(ChanStatistics_t) * stats->num_in_chan_stats; + moal_memcpy_ext(priv->phandle, (t_u8 *)stats->stats, + (t_u8 *)scan_resp.pchan_stats, ret, respbuflen); + ret += sizeof(stats->num_in_chan_stats); +done: + LEAVE(); + return ret; +} + +/** + * @brief Retrieve the scan response/beacon table + * + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * @param scan_resp A pointer to mlan_scan_resp structure + * @param scan_start Argument + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +int moal_ret_get_scan_table_ioctl(t_u8 *respbuf, t_u32 respbuflen, + mlan_scan_resp *scan_resp, t_u32 scan_start) +{ + pBSSDescriptor_t pbss_desc, scan_table; + wlan_ioctl_get_scan_table_info *prsp_info; + int ret_code; + int ret_len; + int space_left; + t_u8 *pcurrent; + t_u8 *pbuffer_end; + t_u32 num_scans_done; + + ENTER(); + + num_scans_done = 0; + ret_code = MLAN_STATUS_SUCCESS; + + prsp_info = (wlan_ioctl_get_scan_table_info *)respbuf; + pcurrent = (t_u8 *)prsp_info->scan_table_entry_buf; + + pbuffer_end = respbuf + respbuflen - 1; + space_left = pbuffer_end - pcurrent; + scan_table = (BSSDescriptor_t *)(scan_resp->pscan_table); + + PRINTM(MINFO, "GetScanTable: scan_start req = %d\n", scan_start); + PRINTM(MINFO, "GetScanTable: length avail = %d\n", respbuflen); + + if (!scan_start) { + PRINTM(MINFO, "GetScanTable: get current BSS Descriptor\n"); + + /* Use to get current association saved descriptor */ + pbss_desc = scan_table; + + ret_code = wlan_get_scan_table_ret_entry(pbss_desc, &pcurrent, + &space_left); + + if (ret_code == MLAN_STATUS_SUCCESS) + num_scans_done = 1; + + } else { + scan_start--; + + while (space_left && + (scan_start + num_scans_done < + scan_resp->num_in_scan_table) && + (ret_code == MLAN_STATUS_SUCCESS)) { + pbss_desc = + (scan_table + (scan_start + num_scans_done)); + + PRINTM(MINFO, + "GetScanTable: get current BSS Descriptor [%d]\n", + scan_start + num_scans_done); + + ret_code = wlan_get_scan_table_ret_entry( + pbss_desc, &pcurrent, &space_left); + + if (ret_code == MLAN_STATUS_SUCCESS) + num_scans_done++; + } + } + + prsp_info->scan_number = num_scans_done; + ret_len = pcurrent - respbuf; + + LEAVE(); + return ret_len; +} + +/** + * @brief Get scan table + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_getscantable(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_scan *scan = NULL; + t_u32 scan_start; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + + ENTER(); + + /* First make sure scanning is not in progress */ + if (handle->scan_pending_on_block == MTRUE) { + ret = -EAGAIN; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + scan = (mlan_ds_scan *)req->pbuf; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_GET; + + /* Get the whole command from user */ + moal_memcpy_ext(handle, &scan_start, + respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_GETSCANTABLE), + sizeof(scan_start), sizeof(scan_start)); + if (scan_start) + scan->sub_command = MLAN_OID_SCAN_NORMAL; + else + scan->sub_command = MLAN_OID_SCAN_GET_CURRENT_BSS; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + ret = moal_ret_get_scan_table_ioctl(respbuf, respbuflen, + &scan->param.scan_resp, + scan_start); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +/** + * @brief Extended capabilities configuration + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_extcapcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret, header; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + IEEEtypes_Header_t *ie; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + LEAVE(); + return 0; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_EXT_CAP_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + header = strlen(CMD_NXP) + strlen(PRIV_CMD_EXTCAPCFG); + if (strlen(respbuf) == header) + /* GET operation */ + req->action = MLAN_ACT_GET; + else { + /* SET operation */ + ie = (IEEEtypes_Header_t *)(respbuf + header); + if (ie->len > sizeof(ExtCap_t)) { + PRINTM(MERROR, + "Extended Capability lenth is invalid\n"); + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + memset(&cfg->param.ext_cap, 0, sizeof(ExtCap_t)); + moal_memcpy_ext(priv->phandle, &cfg->param.ext_cap, ie + 1, + ie->len, sizeof(ExtCap_t)); + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(respbuf, 0, respbuflen); + ie = (IEEEtypes_Header_t *)respbuf; + ie->element_id = EXT_CAPABILITY; + ie->len = sizeof(ExtCap_t); + moal_memcpy_ext(priv->phandle, ie + 1, &cfg->param.ext_cap, + sizeof(ExtCap_t), + respbuflen - sizeof(IEEEtypes_Header_t)); + + ret = sizeof(IEEEtypes_Header_t) + ie->len; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set/Get deep sleep mode configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_setgetdeepsleep(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + t_u32 data[2]; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_DEEPSLEEP))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_DEEPSLEEP), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len >= 3) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, data)) { + ret = -EFAULT; + goto done; + } + sprintf(respbuf, "%d %d", data[0], data[1]); + ret = strlen(respbuf) + 1; + } else { + if (data[0] == DEEP_SLEEP_OFF) { + PRINTM(MINFO, "Exit Deep Sleep Mode\n"); + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MFALSE, + 0); + if (ret != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + } else if (data[0] == DEEP_SLEEP_ON) { + PRINTM(MINFO, "Enter Deep Sleep Mode\n"); + if (user_data_len != 2) + data[1] = 0; + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MTRUE, + data[1]); + if (ret != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + } else { + PRINTM(MERROR, "Unknown option = %u\n", data[0]); + ret = -EINVAL; + goto done; + } + ret = sprintf(respbuf, "OK\n") + 1; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get IP address configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_setgetipaddr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0, op_code = 0, data_length = 0, header = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MIOCTL, "Bss type[%d]: Not STA, ignore it\n", + priv->bss_type); + ret = sprintf(respbuf, "OK\n") + 1; + goto done; + } + + header = strlen(CMD_NXP) + strlen(PRIV_CMD_IPADDR); + data_length = strlen(respbuf) - header; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + + if (data_length < 1) { /* GET */ + req->action = MLAN_ACT_GET; + } else { + /* Make sure we have the operation argument */ + if (data_length > 2 && respbuf[header + 1] != ';') { + PRINTM(MERROR, + "No operation argument. Separate with ';'\n"); + ret = -EINVAL; + goto done; + } else { + respbuf[header + 1] = '\0'; + } + req->action = MLAN_ACT_SET; + + /* Only one IP is supported in current firmware */ + memset(misc->param.ipaddr_cfg.ip_addr[0], 0, IPADDR_LEN); + if (data_length > 2) + in4_pton(&respbuf[header + 2], + MIN((IPADDR_MAX_BUF - 3), (data_length - 2)), + misc->param.ipaddr_cfg.ip_addr[0], ' ', NULL); + misc->param.ipaddr_cfg.ip_addr_num = 1; + misc->param.ipaddr_cfg.ip_addr_type = IPADDR_TYPE_IPV4; + + if (woal_atoi(&op_code, &respbuf[header]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + misc->param.ipaddr_cfg.op_code = (t_u32)op_code; + } + + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_IP_ADDR; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + snprintf(respbuf, IPADDR_MAX_BUF, "%d;%d.%d.%d.%d", + misc->param.ipaddr_cfg.op_code, + misc->param.ipaddr_cfg.ip_addr[0][0], + misc->param.ipaddr_cfg.ip_addr[0][1], + misc->param.ipaddr_cfg.ip_addr[0][2], + misc->param.ipaddr_cfg.ip_addr[0][3]); + ret = IPADDR_MAX_BUF + 1; + } else { + ret = sprintf(respbuf, "OK\n") + 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WPS session configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_setwpssession(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wps_cfg *pwps = NULL; + t_u32 data[1]; + int ret = 0; + int user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset((char *)data, 0, sizeof(data)); + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_WPSSESSION))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_WPSSESSION), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pwps = (mlan_ds_wps_cfg *)req->pbuf; + + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + + if (data[0] == 1) + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + else + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + ret = sprintf(respbuf, "OK\n") + 1; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get OTP user data + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_otpuserdata(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[1]; + int user_data_len = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_otp_user_data *otp = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_OTPUSERDATA))) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_OTPUSERDATA), + data, ARRAY_SIZE(data), &user_data_len); + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + req->action = MLAN_ACT_GET; + req->req_id = MLAN_IOCTL_MISC_CFG; + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_OTP_USER_DATA; + misc->param.otp_user_data.user_data_length = data[0]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + otp = (mlan_ds_misc_otp_user_data *)req->pbuf; + + if (req->action == MLAN_ACT_GET) { + ret = MIN(otp->user_data_length, data[0]); + moal_memcpy_ext(priv->phandle, respbuf, otp->user_data, ret, + respbuflen); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set / Get country code + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_set_get_countrycode(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + /* char data[COUNTRY_CODE_LEN] = {0, 0, 0}; */ + int header = 0, data_length = 0; /* wrq->u.data.length; */ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + mlan_ds_misc_country_code *country_code = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header = strlen(CMD_NXP) + strlen(PRIV_CMD_COUNTRYCODE); + data_length = strlen(respbuf) - header; + + if (data_length > COUNTRY_CODE_LEN) { + PRINTM(MERROR, "Invalid argument!\n"); + ret = -EINVAL; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; + country_code = &pcfg_misc->param.country_code; + pcfg_misc->sub_command = MLAN_OID_MISC_COUNTRY_CODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (data_length <= 1) { + req->action = MLAN_ACT_GET; + } else { + memset(country_code->country_code, 0, COUNTRY_CODE_LEN); + moal_memcpy_ext(priv->phandle, country_code->country_code, + respbuf + header, COUNTRY_CODE_LEN, + COUNTRY_CODE_LEN); + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + ret = data_length = COUNTRY_CODE_LEN; + memset(respbuf + header, 0, COUNTRY_CODE_LEN); + moal_memcpy_ext(priv->phandle, respbuf, + country_code->country_code, COUNTRY_CODE_LEN, + respbuflen); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Get cfp information + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_get_cfpinfo(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfp_misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int header = 0, data_length = 0; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header = strlen(CMD_NXP) + strlen(PRIV_CMD_CFPINFO); + data_length = strlen(respbuf) - header; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + cfp_misc = (mlan_ds_misc_cfg *)req->pbuf; + cfp_misc->sub_command = MLAN_OID_MISC_CFP_INFO; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (respbuflen < req->data_read_written) { + PRINTM(MERROR, "response buffer length is too short!\n"); + ret = -EINVAL; + goto done; + } + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)req->pbuf, + req->data_read_written, respbuflen); + ret = req->data_read_written; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TCP Ack enhancement configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_setgettcpackenh(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + t_u32 data[1]; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_TCPACKENH))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_TCPACKENH), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len >= 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len == 0) { + /* get operation */ + respbuf[0] = priv->enable_tcp_ack_enh; + } else { + /* set operation */ + if (data[0] == MTRUE) { + PRINTM(MINFO, "Enabling TCP Ack enhancement\n"); + priv->enable_tcp_ack_enh = MTRUE; + } else if (data[0] == MFALSE) { + PRINTM(MINFO, "Disabling TCP Ack enhancement\n"); + priv->enable_tcp_ack_enh = MFALSE; + /* release the tcp sessions if any */ + woal_flush_tcp_sess_queue(priv); + } else { + PRINTM(MERROR, "Unknown option = %u\n", data[0]); + ret = -EINVAL; + goto done; + } + respbuf[0] = priv->enable_tcp_ack_enh; + } + ret = 1; + +done: + LEAVE(); + return ret; +} + +#ifdef REASSOCIATION +/** + * @brief Set Asynced ESSID + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * @param bBSSID A variable that bssid is set or not + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_assocessid(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen, + t_u8 bBSSID) +{ + mlan_ssid_bssid ssid_bssid; + moal_handle *handle = priv->phandle; + int ret = 0; + int header_len = 0; + int copy_len = 0; + char buf[64]; + t_u8 buflen = 0; + t_u8 i = 0; + t_u8 mac_idx = 0; + + ENTER(); + + if (bBSSID) + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_ASSOCBSSID); + else + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_ASSOCESSID); + + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "No argument, invalid operation!\n"); + ret = -EINVAL; + LEAVE(); + return ret; + } + copy_len = strlen(respbuf) - header_len; + buflen = MIN(copy_len, (sizeof(buf) - 1)); + memset(buf, 0, sizeof(buf)); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + moal_memcpy_ext(handle, buf, respbuf + header_len, buflen, sizeof(buf)); + priv->assoc_with_mac = MFALSE; + + /* check if has parameter BSSID */ + if (bBSSID) { + if (buflen < (3 * ETH_ALEN) + 2) { + PRINTM(MERROR, + "Associate: Insufficient length in IOCTL input\n"); + /* buffer should be at least 3 characters per BSSID + *octet "00:" + ** plus a space separater and at least 1 char in the + *SSID + */ + ret = -EINVAL; + goto setessid_ret; + } + for (; (i < buflen) && (mac_idx < ETH_ALEN) && (buf[i] != ' '); + i++) { + if (buf[i] == ':') { + mac_idx++; + } else { + ssid_bssid.bssid[mac_idx] = + (t_u8)woal_atox(buf + i); + while ((i < buflen) && isxdigit(buf[i + 1])) + /* Skip entire hex value */ + i++; + } + } + /* Skip one space between the BSSID and start of the SSID */ + i++; + PRINTM(MMSG, "Trying to associate AP BSSID = [" MACSTR "]\n", + MAC2STR(ssid_bssid.bssid)); + priv->assoc_with_mac = MTRUE; + } + + ssid_bssid.ssid.ssid_len = buflen - i; + /* Check the size of the ssid_len */ + if (ssid_bssid.ssid.ssid_len > MLAN_MAX_SSID_LENGTH + 1) { + PRINTM(MERROR, "ssid_bssid.ssid.ssid_len = %d\n", + ssid_bssid.ssid.ssid_len); + ret = -E2BIG; + goto setessid_ret; + } + + /* Copy the SSID */ + moal_memcpy_ext(handle, ssid_bssid.ssid.ssid, buf + i, + ssid_bssid.ssid.ssid_len, MLAN_MAX_SSID_LENGTH); + + if (!ssid_bssid.ssid.ssid_len || + (MFALSE == woal_ssid_valid(&ssid_bssid.ssid))) { + PRINTM(MERROR, "Invalid SSID - aborting set_essid\n"); + ret = -EINVAL; + goto setessid_ret; + } + + PRINTM(MMSG, "Trying to associate AP SSID = %s\n", + (char *)ssid_bssid.ssid.ssid); + + /* Cancel re-association */ + priv->reassoc_required = MFALSE; + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_set_essid\n"); + ret = -EBUSY; + LEAVE(); + return ret; + } + + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + + if (MTRUE == woal_is_connected(priv, &ssid_bssid)) { + PRINTM(MIOCTL, "Already connect to the network\n"); + ret = sprintf(respbuf, + "Has already connected to this ESSID!\n") + + 1; + goto setessid_ret; + } + moal_memcpy_ext(handle, &priv->prev_ssid_bssid, &ssid_bssid, + sizeof(mlan_ssid_bssid), sizeof(mlan_ssid_bssid)); + /* disconnect before driver assoc */ + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, DEF_DEAUTH_REASON_CODE); + priv->set_asynced_essid_flag = MTRUE; + priv->reassoc_required = MTRUE; + priv->phandle->is_reassoc_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->reassoc_timer, 0); + ret = sprintf(respbuf, "%s\n", buf) + 1; + +setessid_ret: + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get wakeup reason + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_getwakeupreason(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + t_u32 data; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_WAKEUPREASON))) { + /* GET operation */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_HS_WAKEUP_REASON; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(req); + goto done; + } else { + data = pm_cfg->param.wakeup_reason.hs_wakeup_reason; + sprintf(respbuf, " %d", data); + ret = strlen(respbuf) + 1; + kfree(req); + } + } else { + PRINTM(MERROR, "Not need argument, invalid operation!\n"); + ret = -EINVAL; + goto done; + } + +done: + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Set / Get listen interval + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_set_get_listeninterval(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[1]; + int user_data_len = 0; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_bss *pcfg_bss = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_LISTENINTERVAL))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_LISTENINTERVAL), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pcfg_bss = (mlan_ds_bss *)req->pbuf; + pcfg_bss->sub_command = MLAN_OID_BSS_LISTEN_INTERVAL; + req->req_id = MLAN_IOCTL_BSS; + + if (user_data_len) { + pcfg_bss->param.listen_interval = (t_u16)data[0]; + req->action = MLAN_ACT_SET; + } else { + req->action = MLAN_ACT_GET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (req->action == MLAN_ACT_GET) { + sprintf(respbuf, "%d", pcfg_bss->param.listen_interval); + ret = strlen(respbuf) + 1; + } else { + ret = sprintf(respbuf, "OK\n") + 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +#ifdef DEBUG_LEVEL1 +/** + * @brief Set / Get driver debug level + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_set_get_drvdbg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[4]; + int user_data_len = 0; + int ret = 0; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_DRVDBG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_DRVDBG), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len) { + /* Get the driver debug bit masks from user */ + drvdbg = data[0]; + /* Set the driver debug bit masks into mlan */ + if (woal_set_drvdbg(priv, drvdbg)) { + PRINTM(MERROR, "Set drvdbg failed!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + ret = sizeof(drvdbg); + + moal_memcpy_ext(priv->phandle, respbuf, &drvdbg, sizeof(drvdbg), + respbuflen); + + printk(KERN_ALERT "drvdbg = 0x%08x\n", drvdbg); +#ifdef DEBUG_LEVEL2 + printk(KERN_ALERT "MINFO (%08x) %s\n", MINFO, + (drvdbg & MINFO) ? "X" : ""); + printk(KERN_ALERT "MWARN (%08x) %s\n", MWARN, + (drvdbg & MWARN) ? "X" : ""); + printk(KERN_ALERT "MENTRY (%08x) %s\n", MENTRY, + (drvdbg & MENTRY) ? "X" : ""); +#endif + printk(KERN_ALERT "MMPA_D (%08x) %s\n", MMPA_D, + (drvdbg & MMPA_D) ? "X" : ""); + printk(KERN_ALERT "MIF_D (%08x) %s\n", MIF_D, + (drvdbg & MIF_D) ? "X" : ""); + printk(KERN_ALERT "MFW_D (%08x) %s\n", MFW_D, + (drvdbg & MFW_D) ? "X" : ""); + printk(KERN_ALERT "MEVT_D (%08x) %s\n", MEVT_D, + (drvdbg & MEVT_D) ? "X" : ""); + printk(KERN_ALERT "MCMD_D (%08x) %s\n", MCMD_D, + (drvdbg & MCMD_D) ? "X" : ""); + printk(KERN_ALERT "MDAT_D (%08x) %s\n", MDAT_D, + (drvdbg & MDAT_D) ? "X" : ""); + printk(KERN_ALERT "MREG_D (%08x) %s\n", MREG_D, + (drvdbg & MREG_D) ? "X" : ""); + printk(KERN_ALERT "MIOCTL (%08x) %s\n", MIOCTL, + (drvdbg & MIOCTL) ? "X" : ""); + printk(KERN_ALERT "MINTR (%08x) %s\n", MINTR, + (drvdbg & MINTR) ? "X" : ""); + printk(KERN_ALERT "MEVENT (%08x) %s\n", MEVENT, + (drvdbg & MEVENT) ? "X" : ""); + printk(KERN_ALERT "MCMND (%08x) %s\n", MCMND, + (drvdbg & MCMND) ? "X" : ""); + printk(KERN_ALERT "MDATA (%08x) %s\n", MDATA, + (drvdbg & MDATA) ? "X" : ""); + printk(KERN_ALERT "MERROR (%08x) %s\n", MERROR, + (drvdbg & MERROR) ? "X" : ""); + printk(KERN_ALERT "MFATAL (%08x) %s\n", MFATAL, + (drvdbg & MFATAL) ? "X" : ""); + printk(KERN_ALERT "MMSG (%08x) %s\n", MMSG, + (drvdbg & MMSG) ? "X" : ""); + +done: + LEAVE(); + return ret; +} + +#endif + +/** + * @brief management frame filter wakeup config + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int woal_priv_mgmt_filter(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int header_len = 0, data_len = 0; + int ret = 0; + t_u16 action; + t_u8 *argument; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + pm_cfg = (mlan_ds_pm_cfg *)ioctl_req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_MGMT_FILTER; + ioctl_req->req_id = MLAN_IOCTL_PM_CFG; + + header_len = strlen(PRIV_CMD_MGMT_FILTER) + strlen(CMD_NXP); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + action = MLAN_ACT_GET; + } else { + /* SET operation */ + argument = (t_u8 *)(respbuf + header_len); + data_len = respbuflen - header_len; + if (data_len > + MAX_MGMT_FRAME_FILTER * sizeof(mlan_mgmt_frame_wakeup)) { + PRINTM(MERROR, "%d: Invalid arguments\n", __LINE__); + ret = -EINVAL; + goto done; + } + moal_memcpy_ext(priv->phandle, + (t_u8 *)pm_cfg->param.mgmt_filter, argument, + data_len, sizeof(pm_cfg->param.mgmt_filter)); + action = MLAN_ACT_SET; + } + + ioctl_req->action = action; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +#define PARAMETER_GPIO_INDICATION 1 +#define PARAMETER_EXTEND_HSCFG 2 +#define PARAMETER_HS_WAKEUP_INTERVAL 3 +/** + * @brief Set/Get Host Sleep configuration + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * @param invoke_hostcmd MTRUE --invoke HostCmd, otherwise MFALSE + * + * @return 0 --success, otherwise fail + */ +int woal_priv_hscfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen, + BOOLEAN invoke_hostcmd) +{ + int data[13] = {0}; + int *temp_data, type; + int user_data_len = 0; + int ret = 0; + mlan_ds_hs_cfg hscfg, hscfg_temp; + t_u16 action; + mlan_bss_info bss_info; + int is_negative = MFALSE; + t_u8 *arguments = NULL; + + ENTER(); + + memset(data, 0, sizeof(data)); + memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); + memset(&hscfg_temp, 0, sizeof(mlan_ds_hs_cfg)); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_HSCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + arguments = respbuf + strlen(CMD_NXP) + strlen(PRIV_CMD_HSCFG); + if (*arguments == '-') { + is_negative = MTRUE; + arguments += 1; + } + parse_arguments(arguments, data, ARRAY_SIZE(data), + &user_data_len); + + if (is_negative == MTRUE) { + if (data[0] == 1) { + data[0] = -1; + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + } + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len == 0) { + action = MLAN_ACT_GET; + } else { + if (user_data_len >= 1 && user_data_len <= 13) { + action = MLAN_ACT_SET; + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + } + + /* HS config is blocked if HS is already activated */ + if (user_data_len && + (data[0] != HOST_SLEEP_CFG_CANCEL || invoke_hostcmd == MFALSE)) { + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (bss_info.is_hs_configured) { + PRINTM(MERROR, "HS already configured\n"); + ret = -EFAULT; + goto done; + } + } + + /* Do a GET first if some arguments are not provided */ + if (user_data_len >= 1 && user_data_len < 11) { + woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, + &hscfg_temp); + } + hscfg.conditions = hscfg_temp.conditions; + hscfg.gpio = hscfg_temp.gpio; + hscfg.gap = hscfg_temp.gap; + + if (user_data_len) + hscfg.conditions = data[0]; + if (user_data_len >= 2) + hscfg.gpio = data[1]; + if (user_data_len >= 3) + hscfg.gap = data[2]; + user_data_len = user_data_len - 3; + if (user_data_len > 0) { + temp_data = data + 3; + while ((user_data_len > 0) && temp_data) { + type = *temp_data; + switch (type) { + case PARAMETER_GPIO_INDICATION: + if (user_data_len >= 2) + hscfg.ind_gpio = *(++temp_data); + else { + PRINTM(MERROR, + "Invaild number of parameters\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len >= 3) { + hscfg.level = *(++temp_data); + if (hscfg.level != 0 && + hscfg.level != 1) { + PRINTM(MERROR, + "Invalid indication gpio arguments\n"); + ret = -EINVAL; + goto done; + } + } + hscfg.param_type_ind = type; + user_data_len = user_data_len - 3; + temp_data++; + break; + case PARAMETER_EXTEND_HSCFG: + if (user_data_len >= 4) { + hscfg.event_force_ignore = + *(++temp_data); + hscfg.event_use_ext_gap = + *(++temp_data); + hscfg.ext_gap = *(++temp_data); + hscfg.gpio_wave = *(++temp_data); + } else { + PRINTM(MERROR, + "Invaild number of parameters\n"); + ret = -EINVAL; + goto done; + } + /* Force_ignore_gpio and ext_gap_gpio should not + * set the same bit(s)*/ + if ((hscfg.event_force_ignore & + hscfg.event_use_ext_gap) || + (hscfg.gpio_wave != 1 && + hscfg.gpio_wave != 0)) { + PRINTM(MERROR, + "Invalid arguments for extend hscfg\n"); + ret = -EINVAL; + goto done; + } + hscfg.param_type_ext = type; + user_data_len = user_data_len - 5; + temp_data++; + break; + case PARAMETER_HS_WAKEUP_INTERVAL: + if (user_data_len >= 2) + hscfg.hs_wake_interval = *(++temp_data); + else { + PRINTM(MERROR, + "Invaild number of parameters\n"); + ret = -EINVAL; + goto done; + } + user_data_len = user_data_len - 2; + temp_data++; + break; + default: + PRINTM(MERROR, "Unsupported type\n"); + ret = -EINVAL; + goto done; + } + } + } + + if ((invoke_hostcmd == MTRUE) && (action == MLAN_ACT_SET)) { + /* Need to issue an extra IOCTL first to set up parameters */ + hscfg.is_invoke_hostcmd = MFALSE; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &hscfg)) { + ret = -EFAULT; + goto done; + } + } + hscfg.is_invoke_hostcmd = invoke_hostcmd; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, action, MOAL_IOCTL_WAIT, &hscfg)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_GET) { + /* Return the current driver host sleep configurations */ + moal_memcpy_ext(priv->phandle, respbuf, &hscfg, + sizeof(mlan_ds_hs_cfg), respbuflen); + ret = sizeof(mlan_ds_hs_cfg); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set Host Sleep parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int woal_priv_hssetpara(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[13] = {0}; + int user_data_len = 0; + int ret = 0; + + ENTER(); + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_HSSETPARA))) { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_HSSETPARA), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len >= 1 && user_data_len <= 13) { + sprintf(respbuf, "%s%s%s", CMD_NXP, PRIV_CMD_HSCFG, + respbuf + + (strlen(CMD_NXP) + strlen(PRIV_CMD_HSSETPARA))); + respbuflen = strlen(respbuf); + ret = woal_priv_hscfg(priv, respbuf, respbuflen, MFALSE); + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get scan configuration parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int woal_priv_set_get_scancfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0; + int data[9]; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_SCANCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_SCANCFG), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + + if (user_data_len) { + if ((data[0] < 0) || (data[0] > MLAN_SCAN_TYPE_PASSIVE)) { + PRINTM(MERROR, "Invalid argument for scan type\n"); + ret = -EINVAL; + goto done; + } + if ((data[1] < 0) || (data[1] > MLAN_SCAN_MODE_ANY)) { + PRINTM(MERROR, "Invalid argument for scan mode\n"); + ret = -EINVAL; + goto done; + } + if ((data[2] < 0) || (data[2] > MAX_PROBES)) { + PRINTM(MERROR, "Invalid argument for scan probes\n"); + ret = -EINVAL; + goto done; + } + if (((data[3] < 0) || + (data[3] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || + ((data[4] < 0) || + (data[4] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || + ((data[5] < 0) || + (data[5] > MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME))) { + PRINTM(MERROR, "Invalid argument for scan time\n"); + ret = -EINVAL; + goto done; + } + if ((data[6] < 0) || (data[6] > MLAN_PASS_TO_ACT_SCAN_DIS)) { + PRINTM(MERROR, + "Invalid argument for Passive to Active Scan\n"); + ret = -EINVAL; + goto done; + } + if ((data[7] < 0) || (data[7] > 3)) { + PRINTM(MERROR, "Invalid argument for extended scan\n"); + ret = -EINVAL; + goto done; + } + if ((data[8] < 0) || + (data[8] > MRVDRV_MAX_SCAN_CHAN_GAP_TIME)) { + PRINTM(MERROR, + "Invalid argument for scan channel gap\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + moal_memcpy_ext(priv->phandle, &scan->param.scan_cfg, data, + sizeof(data), sizeof(scan->param.scan_cfg)); + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, respbuf, &scan->param.scan_cfg, + sizeof(mlan_scan_cfg), respbuflen); + ret = sizeof(mlan_scan_cfg); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get Netlink Number + * + * @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_getnlnum(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + int data = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null in %s\n", __FUNCTION__); + ret = -EFAULT; + goto done; + } + + data = priv->phandle->netlink_num; + moal_memcpy_ext(priv->phandle, respbuf, &data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set / Get packet aggregation control + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_set_get_aggrctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[1]; + int user_data_len = 0; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + moal_handle *handle = priv->phandle; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!handle || !handle->card) { + PRINTM(MERROR, "Handle or card is null\n"); + ret = -EFAULT; + goto done; + } + + memset((char *)data, 0, sizeof(data)); + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_AGGRCTRL))) { + /* GET operation */ + user_data_len = 0; + } else { + if (woal_is_any_interface_active(priv->phandle)) { + PRINTM(MERROR, + "aggrctrl are not allowed to change after BSS active!\n"); + ret = -EFAULT; + goto done; + } + /* SET operation */ + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_AGGRCTRL), + data, ARRAY_SIZE(data), &user_data_len); + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; + pcfg_misc->sub_command = MLAN_OID_MISC_AGGR_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + /* Get the values first, then modify these values if user had modified + * them */ + if (user_data_len == 0) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + pcfg_misc->param.aggr_params.tx.enable = (t_u16)data[0]; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + /* MLAN will return CMD_INVALID if FW does not support this + * feature */ + if (MLAN_ERROR_CMD_INVALID == req->status_code) + ret = -EOPNOTSUPP; + else + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(handle, respbuf, (t_u8 *)&pcfg_misc->param.aggr_params, + sizeof(mlan_ds_misc_aggr_ctrl), respbuflen); + ret = sizeof(mlan_ds_misc_aggr_ctrl); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +#ifdef USB +/** + * @brief Set / Get USB packet aggregation control + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_set_get_usbaggrctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[8]; + int user_data_len = 0; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + moal_handle *handle = priv->phandle; + struct usb_card_rec *cardp = NULL; + int i = 0, usb_resubmit_urbs = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!handle || !handle->card) { + PRINTM(MERROR, "Handle or card is null\n"); + ret = -EFAULT; + goto done; + } + cardp = (struct usb_card_rec *)handle->card; + + memset((char *)data, 0, sizeof(data)); + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_USBAGGRCTRL))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_USBAGGRCTRL), + data, ARRAY_SIZE(data), &user_data_len); + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; + pcfg_misc->sub_command = MLAN_OID_MISC_USB_AGGR_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + /* Get the values first, then modify these values if user had modified + * them */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + /* MLAN will return CMD_INVALID if FW does not support this + * feature */ + if (MLAN_ERROR_CMD_INVALID == req->status_code) + ret = -EOPNOTSUPP; + else + ret = -EFAULT; + goto done; + } + + if (user_data_len == 0) { + moal_memcpy_ext(handle, respbuf, + (t_u8 *)&pcfg_misc->param.usb_aggr_params, + sizeof(mlan_ds_misc_usb_aggr_ctrl), respbuflen); + ret = sizeof(mlan_ds_misc_usb_aggr_ctrl); + goto done; + } + + switch (user_data_len) { + case 8: + if (data[7] < 0) { + PRINTM(MERROR, "Invalid Rx timeout value (%d)\n", + data[7]); + ret = -EINVAL; + goto done; + } + pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl.aggr_tmo = + (t_u16)data[7]; + /* fall through */ + case 7: + if (data[6] < 0 || (data[6] > 10000 && + data[6] != MLAN_USB_TX_AGGR_TIMEOUT_DYN)) { + PRINTM(MERROR, "Invalid Tx timeout value (%d)\n", + data[6]); + ret = -EINVAL; + goto done; + } + pcfg_misc->param.usb_aggr_params.tx_aggr_ctrl.aggr_tmo = + (t_u16)data[6]; + /* fall through */ + case 6: + if ((data[5] < 512) || ((data[5] % 512) != 0)) { + PRINTM(MERROR, "Invalid Rx alignment value (%d)\n", + data[5]); + ret = -EINVAL; + goto done; + } + if (cardp->rx_deaggr_ctrl.enable && + pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl.aggr_align != + (t_u16)data[5]) + usb_resubmit_urbs = 1; + pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl.aggr_align = + (t_u16)data[5]; + /* fall through */ + case 5: + if ((data[4] < 2048) || ((data[4] % 2048) != 0)) { + PRINTM(MERROR, "Invalid Tx alignment value (%d)\n", + data[4]); + ret = -EINVAL; + goto done; + } + pcfg_misc->param.usb_aggr_params.tx_aggr_ctrl.aggr_align = + (t_u16)data[4]; + /* fall through */ + case 4: + if ((data[3] == 2) || (data[3] == 4) || (data[3] == 8) || + (data[3] == 16)) { + pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl + .aggr_mode = MLAN_USB_AGGR_MODE_NUM; + } else if ((data[3] == 4096) || (data[3] == 8192) || + (data[3] == 16384) || (data[3] == 32768)) { + pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl + .aggr_mode = MLAN_USB_AGGR_MODE_LEN; + } else { + PRINTM(MERROR, "Invalid Rx max size/num value (%d)\n", + data[3]); + ret = -EINVAL; + goto done; + } + if (cardp->rx_deaggr_ctrl.enable && + pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl.aggr_max != + (t_u16)data[3]) + usb_resubmit_urbs = 1; + pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl.aggr_max = + (t_u16)data[3]; + /* fall through */ + case 3: + if ((data[2] == 2) || (data[2] == 4) || (data[2] == 8) || + (data[2] == 16)) { + pcfg_misc->param.usb_aggr_params.tx_aggr_ctrl.aggr_mode = + MLAN_USB_AGGR_MODE_NUM; + } else if ((data[2] == 4096) || (data[2] == 8192) || + (data[2] == 16384) || (data[2] == 32768)) { + pcfg_misc->param.usb_aggr_params.tx_aggr_ctrl.aggr_mode = + MLAN_USB_AGGR_MODE_LEN; + } else { + PRINTM(MERROR, "Invalid Tx max size/num value (%d)\n", + data[2]); + ret = -EINVAL; + goto done; + } + pcfg_misc->param.usb_aggr_params.tx_aggr_ctrl.aggr_max = + (t_u16)data[2]; + /* fall through */ + case 2: + if ((data[1] != 0) && (data[1] != 1)) { + PRINTM(MERROR, "Invalid Rx enable value (%d)\n", + data[1]); + ret = -EINVAL; + goto done; + } + if (pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl.enable != + (t_u16)data[1]) + usb_resubmit_urbs = 1; + pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl.enable = + (t_u16)data[1]; + /* fall through */ + case 1: + if ((data[0] != 0) && (data[0] != 1)) { + PRINTM(MERROR, "Invalid Tx enable value (%d)\n", + data[0]); + ret = -EINVAL; + goto done; + } + pcfg_misc->param.usb_aggr_params.tx_aggr_ctrl.enable = + (t_u16)data[0]; + default: + break; + } + + pcfg_misc->sub_command = MLAN_OID_MISC_USB_AGGR_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(handle, respbuf, + (t_u8 *)&pcfg_misc->param.usb_aggr_params, + sizeof(mlan_ds_misc_usb_aggr_ctrl), respbuflen); + ret = sizeof(mlan_ds_misc_usb_aggr_ctrl); + + /* Keep a copy of the latest Tx aggregation parameters in MOAL */ + moal_memcpy_ext(handle, &cardp->tx_aggr_ctrl, + &pcfg_misc->param.usb_aggr_params.tx_aggr_ctrl, + sizeof(usb_aggr_ctrl), sizeof(usb_aggr_ctrl)); + + if (usb_resubmit_urbs) { + /* Indicate resubmition from here */ + cardp->resubmit_urbs = 1; + /* Rx SG parameters has changed or disabled, kill the URBs, they + will be resubmitted after saving the parameters to USB card + */ + if (atomic_read(&cardp->rx_data_urb_pending)) { + for (i = 0; i < MVUSB_RX_DATA_URB; i++) { + if (cardp->rx_data_list[i].urb) { + usb_kill_urb( + cardp->rx_data_list[i].urb); + usb_init_urb( + cardp->rx_data_list[i].urb); + } + } + } + } + + /* Keep a copy of the latest Rx deaggregation parameters in MOAL */ + moal_memcpy_ext(handle, &cardp->rx_deaggr_ctrl, + &pcfg_misc->param.usb_aggr_params.rx_deaggr_ctrl, + sizeof(usb_aggr_ctrl), sizeof(usb_aggr_ctrl)); + + if (usb_resubmit_urbs) { + /* Ensure the next data URBs will use the modified parameters */ + if (!atomic_read(&cardp->rx_data_urb_pending)) { + /* Submit multiple Rx data URBs */ + woal_usb_submit_rx_data_urbs(handle); + } + cardp->resubmit_urbs = 0; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +#ifdef STA_SUPPORT +/** + * @brief Set AP settings + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int woal_priv_set_ap(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + t_u8 *data_ptr; + const t_u8 bcast[MLAN_MAC_ADDR_LENGTH] = {255, 255, 255, 255, 255, 255}; + const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0, 0, 0, 0, 0, 0}; + mlan_ssid_bssid ssid_bssid; + mlan_bss_info bss_info; + struct mwreq *mwr; + struct sockaddr *awrq; + + ENTER(); + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_SET_AP)); + + mwr = (struct mwreq *)data_ptr; + + if (mwr->u.ap_addr.sa_family != ARPHRD_ETHER) { + ret = -EINVAL; + goto done; + } + + awrq = (struct sockaddr *)&(mwr->u.ap_addr); + + PRINTM(MINFO, "ASSOC: WAP: sa_data: " MACSTR "\n", + MAC2STR((t_u8 *)awrq->sa_data)); + + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; +#endif + + /* zero_mac means disconnect */ + if (!memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE); + goto done; + } + + /* Broadcast MAC means search for best network */ + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + if (memcmp(bcast, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + /* Check if we are already assoicated to the AP */ + if (bss_info.media_connected == MTRUE) { + if (!memcmp(awrq->sa_data, &bss_info.bssid, ETH_ALEN)) + goto done; + } + moal_memcpy_ext(priv->phandle, &ssid_bssid.bssid, awrq->sa_data, + ETH_ALEN, sizeof(mlan_802_11_mac_addr)); + } + + 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; + } + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.ssid, + &bss_info.ssid, sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.bssid, + &bss_info.bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(mlan_802_11_mac_addr)); +#endif /* REASSOCIATION */ + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set BSS mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int woal_priv_set_bss_mode(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + struct mwreq *mwr; + t_u8 *data_ptr; + t_u32 mode; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_SET_BSS_MODE)); + + mwr = (struct mwreq *)data_ptr; + mode = mwr->u.mode; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + switch (mode) { + case MW_MODE_INFRA: + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + break; + case MW_MODE_ADHOC: + bss->param.bss_mode = MLAN_BSS_MODE_IBSS; + break; + case MW_MODE_AUTO: + bss->param.bss_mode = MLAN_BSS_MODE_AUTO; + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Set power management + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int woal_priv_set_power(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + struct mwreq *mwr; + t_u8 *data_ptr; + int ret = 0, disabled; + + ENTER(); + + if (moal_extflg_isset(priv->phandle, EXT_HW_TEST)) { + PRINTM(MIOCTL, "block set power in hw_test mode\n"); + LEAVE(); + return ret; + } + + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_SET_POWER)); + + mwr = (struct mwreq *)data_ptr; + disabled = mwr->u.power.disabled; + + if (MLAN_STATUS_SUCCESS != + woal_set_get_power_mgmt(priv, MLAN_ACT_SET, &disabled, + mwr->u.power.flags, MOAL_IOCTL_WAIT)) { + return -EFAULT; + } + LEAVE(); + return ret; +} + +/** + * @brief Set essid + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int woal_priv_set_essid(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; +#ifdef REASSOCIATION + moal_handle *handle = priv->phandle; + mlan_bss_info bss_info; +#endif + int ret = 0; + t_u32 mode = 0; + struct mwreq *mwr; + t_u8 *data_ptr; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_SET_ESSID)); + + mwr = (struct mwreq *)data_ptr; + +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_set_essid\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + + /* Check the size of the string */ + if (mwr->u.essid.length > MW_ESSID_MAX_SIZE - 1) { + ret = -E2BIG; + goto setessid_ret; + } + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + memset(&req_ssid, 0, sizeof(mlan_802_11_ssid)); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + req_ssid.ssid_len = mwr->u.essid.length; + + /* Check if we asked for 'any' or 'particular' */ + if (!mwr->u.essid.flags) { +#ifdef REASSOCIATION + if (!req_ssid.ssid_len) { + memset(&priv->prev_ssid_bssid.ssid, 0x00, + sizeof(mlan_802_11_ssid)); + memset(&priv->prev_ssid_bssid.bssid, 0x00, + MLAN_MAC_ADDR_LENGTH); + goto setessid_ret; + } +#endif + /* Do normal SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, NULL)) { + ret = -EFAULT; + goto setessid_ret; + } + } else { + /* Set the SSID */ + moal_memcpy_ext(handle, req_ssid.ssid, mwr->u.essid.pointer, + 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); + moal_memcpy_ext(handle, &ssid_bssid.ssid, &req_ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + if (MTRUE == woal_is_connected(priv, &ssid_bssid)) { + PRINTM(MIOCTL, "Already connect to the network\n"); + goto setessid_ret; + } + + if (mwr->u.essid.flags != 0xFFFF) { + if (MLAN_STATUS_SUCCESS != + woal_find_essid(priv, &ssid_bssid, + MOAL_IOCTL_WAIT)) { + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, + &req_ssid)) { + ret = -EFAULT; + goto setessid_ret; + } + } + } + } + + mode = woal_get_mode(priv, MOAL_IOCTL_WAIT); + + if (mode != MW_MODE_ADHOC) { + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, + &ssid_bssid)) { + ret = -EFAULT; + goto setessid_ret; + } + } +#ifdef UAP_SUPPORT + else if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) + /* Adhoc start, Check the channel command */ + woal_11h_channel_check_ioctl(priv, MOAL_IOCTL_WAIT); +#endif + + /* 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; + } + moal_memcpy_ext(handle, &priv->prev_ssid_bssid.ssid, &bss_info.ssid, + sizeof(mlan_802_11_ssid), sizeof(mlan_802_11_ssid)); + moal_memcpy_ext(handle, &priv->prev_ssid_bssid.bssid, &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH, sizeof(mlan_802_11_mac_addr)); +#endif /* REASSOCIATION */ + +setessid_ret: + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + LEAVE(); + return ret; +} + +/** + * @brief Set authentication mode parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful, otherwise fail + */ +static int woal_priv_set_auth(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + struct mwreq *mwr; + t_u8 *data_ptr; + int ret = 0; + t_u32 auth_mode = 0; + t_u32 encrypt_mode = 0; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_SET_AUTH)); + + mwr = (struct mwreq *)data_ptr; + + switch (mwr->u.param.flags & MW_AUTH_INDEX) { + case MW_AUTH_CIPHER_PAIRWISE: + case MW_AUTH_CIPHER_GROUP: + if (mwr->u.param.value & MW_AUTH_CIPHER_NONE) + encrypt_mode = MLAN_ENCRYPTION_MODE_NONE; + else if (mwr->u.param.value & MW_AUTH_CIPHER_WEP40) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40; + else if (mwr->u.param.value & MW_AUTH_CIPHER_WEP104) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104; + else if (mwr->u.param.value & MW_AUTH_CIPHER_TKIP) + encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP; + else if (mwr->u.param.value & MW_AUTH_CIPHER_CCMP) + encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP; + if (MLAN_STATUS_SUCCESS != + woal_set_encrypt_mode(priv, MOAL_IOCTL_WAIT, encrypt_mode)) + ret = -EFAULT; + break; + case MW_AUTH_80211_AUTH_ALG: + switch (mwr->u.param.value) { + case MW_AUTH_ALG_SHARED_KEY: + PRINTM(MINFO, "Auth mode shared key!\n"); + auth_mode = MLAN_AUTH_MODE_SHARED; + break; + case MW_AUTH_ALG_LEAP: + PRINTM(MINFO, "Auth mode LEAP!\n"); + auth_mode = MLAN_AUTH_MODE_NETWORKEAP; + break; + case MW_AUTH_ALG_OPEN_SYSTEM: + PRINTM(MINFO, "Auth mode open!\n"); + auth_mode = MLAN_AUTH_MODE_OPEN; + break; + case MW_AUTH_ALG_SHARED_KEY | MW_AUTH_ALG_OPEN_SYSTEM: + default: + PRINTM(MINFO, "Auth mode auto!\n"); + auth_mode = MLAN_AUTH_MODE_AUTO; + break; + } + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) + ret = -EFAULT; + break; + case MW_AUTH_WPA_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wpa_enable(priv, MOAL_IOCTL_WAIT, + mwr->u.param.value)) + ret = -EFAULT; + break; +#define MW_AUTH_WAPI_ENABLED 0x20 + case MW_AUTH_WAPI_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wapi_enable(priv, MOAL_IOCTL_WAIT, + mwr->u.param.value)) + ret = -EFAULT; + break; + case MW_AUTH_WPA_VERSION: + /* set WPA_VERSION_DISABLED/VERSION_WPA/VERSION_WP2 */ + priv->wpa_version = mwr->u.param.value; + break; + case MW_AUTH_KEY_MGMT: + /* set KEY_MGMT_802_1X/KEY_MGMT_PSK */ + priv->key_mgmt = mwr->u.param.value; + break; + case MW_AUTH_TKIP_COUNTERMEASURES: + case MW_AUTH_DROP_UNENCRYPTED: + case MW_AUTH_RX_UNENCRYPTED_EAPOL: + case MW_AUTH_ROAMING_CONTROL: + case MW_AUTH_PRIVACY_INVOKED: + break; + default: + ret = -EOPNOTSUPP; + break; + } + LEAVE(); + return ret; +} + +/** + * @brief Get current BSSID + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_priv_get_ap(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + struct mwreq *mwr; + t_u8 *data_ptr; + int ret = 0; + mlan_bss_info bss_info; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_GET_AP)); + + mwr = (struct mwreq *)data_ptr; + + memset(&bss_info, 0, sizeof(bss_info)); + + ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (ret != MLAN_STATUS_SUCCESS) + return -EFAULT; + + if (bss_info.media_connected == MTRUE) { + moal_memcpy_ext(priv->phandle, mwr->u.ap_addr.sa_data, + &bss_info.bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(mwr->u.ap_addr.sa_data)); + } else { + memset(mwr->u.ap_addr.sa_data, 0, MLAN_MAC_ADDR_LENGTH); + } + mwr->u.ap_addr.sa_family = ARPHRD_ETHER; + ret = strlen(CMD_NXP) + strlen(PRIV_CMD_GET_AP) + sizeof(struct mwreq); + + LEAVE(); + return ret; +} + +/** + * @brief Get power management + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_priv_get_power(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + struct mwreq *mwr; + t_u8 *data_ptr; + int ret = 0, ps_mode; + + ENTER(); + + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_GET_POWER)); + + mwr = (struct mwreq *)data_ptr; + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, MLAN_ACT_GET, + &ps_mode, 0, + MOAL_IOCTL_WAIT)) { + return -EFAULT; + } + + if (ps_mode) + mwr->u.power.disabled = 0; + else + mwr->u.power.disabled = 1; + + mwr->u.power.value = 0; + ret = strlen(CMD_NXP) + strlen(PRIV_CMD_GET_POWER) + + sizeof(struct mwreq); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get power save mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_set_get_psmode(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int data = 0; + int user_data_len = 0, header_len = 0; + t_u32 action = MLAN_ACT_GET; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_PSMODE); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + action = MLAN_ACT_SET; + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + /* Flip the value */ + data = !data; + + if (MLAN_STATUS_SUCCESS != + woal_set_get_power_mgmt(priv, action, &data, 0, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_SET) + data = !data; + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + LEAVE(); + return ret; +} +#endif /* STA_SUPPORT */ + +/** + * @brief Performs warm reset + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_priv_warmreset(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + moal_handle *handle = priv->phandle; + moal_handle *ref_handle; + moal_private *ref_priv; + ENTER(); + ret = woal_pre_warmreset(priv); + if (ret) + goto done; + ref_handle = (moal_handle *)handle->pref_mac; + if (ref_handle) { + ref_priv = woal_get_priv(ref_handle, MLAN_BSS_ROLE_ANY); + if (ref_priv) { + ret = woal_pre_warmreset(ref_priv); + if (ret) + goto done; + ret = woal_warmreset(ref_priv); + if (ret) + goto done; + } + } + ret = woal_warmreset(priv); +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TX power configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_txpowercfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[5]; + int user_data_len; + int ret = 0; + mlan_bss_info bss_info; + mlan_ds_power_cfg *pcfg = NULL; + mlan_ioctl_req *req = NULL; + t_u8 *arguments = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + memset(data, 0, sizeof(data)); + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + if (strlen(respbuf) == + (strlen(CMD_NXP) + strlen(PRIV_CMD_TXPOWERCFG))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + arguments = + respbuf + strlen(CMD_NXP) + strlen(PRIV_CMD_TXPOWERCFG); + parse_arguments(arguments, data, ARRAY_SIZE(data), + &user_data_len); + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pcfg = (mlan_ds_power_cfg *)req->pbuf; + pcfg->sub_command = MLAN_OID_POWER_CFG_EXT; + req->req_id = MLAN_IOCTL_POWER_CFG; + + if (!user_data_len) + req->action = MLAN_ACT_GET; + else { + /* SET operation */ + req->action = MLAN_ACT_SET; + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + switch (user_data_len) { + case 1: + if (data[0] == 0xFF) + pcfg->param.power_ext.power_group[0] + .rate_format = TX_PWR_CFG_AUTO_CTRL_OFF; + else + ret = -EINVAL; + break; + case 3: + case 5: + switch (data[0]) { + case 0: /* LG */ + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_LG; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + break; + case 1: /* 20 MHz HT */ + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_HT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + break; + case 2: /* 40 MHz HT */ + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_HT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW40; + break; + case 3: /* 1 NSS 20 MHZ VHT */ + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_VHT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + pcfg->param.power_ext.power_group[0].nss = 1; + break; + case 4: /* 2 NSS 20 MHZ VHT */ + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_VHT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + pcfg->param.power_ext.power_group[0].nss = 2; + break; + case 5: /* 1 NSS 40 MHZ VHT */ + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_VHT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW40; + pcfg->param.power_ext.power_group[0].nss = 1; + break; + case 6: /* 2 NSS 40 MHZ VHT */ + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_VHT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW40; + pcfg->param.power_ext.power_group[0].nss = 2; + break; + case 7: /* 1 NSS 80 MHZ VHT */ + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_VHT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_VHT_BW80; + pcfg->param.power_ext.power_group[0].nss = 1; + break; + case 8: /* 2 NSS 80 MHZ VHT */ + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_VHT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_VHT_BW80; + pcfg->param.power_ext.power_group[0].nss = 2; + break; + default: + ret = -EINVAL; + break; + } + pcfg->param.power_ext.power_group[0].first_rate_ind = + data[1]; + pcfg->param.power_ext.power_group[0].last_rate_ind = + data[1]; + if (data[2] < bss_info.min_power_level) { + PRINTM(MERROR, + "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", + data[2], (int)bss_info.min_power_level, + (int)bss_info.max_power_level); + ret = -EINVAL; + break; + } + if (data[2] > bss_info.max_power_level) { + PRINTM(MERROR, + "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", + data[2], (int)bss_info.min_power_level, + (int)bss_info.max_power_level); + ret = -EINVAL; + break; + } + pcfg->param.power_ext.power_group[0].power_min = + data[2]; + pcfg->param.power_ext.power_group[0].power_max = + data[2]; + pcfg->param.power_ext.power_group[0].power_step = 0; + pcfg->param.power_ext.num_pwr_grp = 1; + + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + /* GET operation */ + moal_memcpy_ext(priv->phandle, respbuf, + (t_u8 *)&pcfg->param.power_ext, + sizeof(pcfg->param.power_ext.num_pwr_grp) + + (MIN(pcfg->param.power_ext.num_pwr_grp, + MAX_POWER_GROUP) * + sizeof(mlan_power_group)), + respbuflen); + ret = sizeof(pcfg->param.power_ext.num_pwr_grp) + + (MIN(pcfg->param.power_ext.num_pwr_grp, MAX_POWER_GROUP) * + sizeof(mlan_power_group)); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get PS configuration parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_pscfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[7] = {0}, ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int allowed = 3; + int i = 3; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + allowed++; /* For beacon missing timeout parameter */ + allowed += 2; /* For delay to PS and PS mode parameters */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_PSCFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (user_data_len && user_data_len > allowed) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_CFG; + req->req_id = MLAN_IOCTL_PM_CFG; + if (user_data_len) { + if ((data[0] < PS_NULL_DISABLE)) { + PRINTM(MERROR, + "Invalid argument for PS null interval\n"); + ret = -EINVAL; + goto done; + } + if ((data[1] != MRVDRV_IGNORE_MULTIPLE_DTIM) && + (data[1] != MRVDRV_MATCH_CLOSEST_DTIM) && + ((data[1] < MRVDRV_MIN_MULTIPLE_DTIM) || + (data[1] > MRVDRV_MAX_MULTIPLE_DTIM))) { + PRINTM(MERROR, "Invalid argument for multiple DTIM\n"); + ret = -EINVAL; + goto done; + } + + if ((data[2] < MRVDRV_MIN_LISTEN_INTERVAL) && + (data[2] != MRVDRV_LISTEN_INTERVAL_DISABLE)) { + PRINTM(MERROR, + "Invalid argument for listen interval\n"); + ret = -EINVAL; + goto done; + } + + if ((data[i] != DISABLE_BCN_MISS_TO) && + ((data[i] < MIN_BCN_MISS_TO) || + (data[i] > MAX_BCN_MISS_TO))) { + PRINTM(MERROR, + "Invalid argument for beacon miss timeout\n"); + ret = -EINVAL; + goto done; + } + i++; + if (user_data_len < allowed - 1) + data[i] = DELAY_TO_PS_UNCHANGED; + else if ((data[i] < MIN_DELAY_TO_PS) || + (data[i] > MAX_DELAY_TO_PS)) { + PRINTM(MERROR, "Invalid argument for delay to PS\n"); + ret = -EINVAL; + goto done; + } + i++; + if ((data[i] != PS_MODE_UNCHANGED) && + (data[i] != PS_MODE_AUTO) && (data[i] != PS_MODE_POLL) && + (data[i] != PS_MODE_NULL)) { + PRINTM(MERROR, "Invalid argument for PS mode\n"); + ret = -EINVAL; + goto done; + } + i++; + req->action = MLAN_ACT_SET; + moal_memcpy_ext(priv->phandle, &pm_cfg->param.ps_cfg, data, + sizeof(data), sizeof(pm_cfg->param.ps_cfg)); + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, data, &pm_cfg->param.ps_cfg, + MIN((sizeof(int) * allowed), + sizeof(pm_cfg->param.ps_cfg)), + sizeof(data)); + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, + sizeof(int) * allowed, respbuflen); + ret = sizeof(int) * allowed; + if (req->action == MLAN_ACT_SET) { + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS; + pm_cfg->param.ps_mode = 1; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get PS configuration parameters + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_bcntimeoutcfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[4] = {0}, ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int allowed = 4; + int i = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_BCNTIMEOUTCFG); + + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != allowed) { + PRINTM(MERROR, "Invalid args num: input=%d allowed=%d\n", + user_data_len, allowed); + ret = -EINVAL; + goto done; + } + for (i = 0; i < allowed; i++) { + if (data[i] <= 0) { + PRINTM(MERROR, "Invalid data[%d]=%d\n", i, data[i]); + ret = -EINVAL; + goto done; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_BCN_TIMEOUT; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_SET; + pm_cfg->param.bcn_timeout.bcn_miss_tmo_window = (t_u16)data[0]; + pm_cfg->param.bcn_timeout.bcn_miss_tmo_period = (t_u16)data[1]; + pm_cfg->param.bcn_timeout.bcn_rq_tmo_window = (t_u16)data[2]; + pm_cfg->param.bcn_timeout.bcn_rq_tmo_period = (t_u16)data[3]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get sleep period + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_sleeppd(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD; + req->req_id = MLAN_IOCTL_PM_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_SLEEPPD); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len) { + if ((data <= MAX_SLEEP_PERIOD && data >= MIN_SLEEP_PERIOD) || + (data == 0) || (data == SLEEP_PERIOD_RESERVED_FF)) { + req->action = MLAN_ACT_SET; + pm_cfg->param.sleep_period = data; + } else { + ret = -EINVAL; + goto done; + } + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + data = pm_cfg->param.sleep_period; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&data, + sizeof(data), respbuflen); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Tx control flag + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_txcontrol(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_TXCONTROL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_TXCONTROL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len) { + req->action = MLAN_ACT_SET; + misc_cfg->param.tx_control = (t_u32)data; + } else { + req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + data = misc_cfg->param.tx_control; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&data, + sizeof(data), respbuflen); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read/Write adapter registers value + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_regrdwr(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[3]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + int user_data_len = 0, header_len = 0; + t_u8 *arguments = NULL, *space_ind = NULL; + t_u32 is_negative_val = MFALSE; + mlan_status status = MLAN_STATUS_SUCCESS; + gfp_t flag; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_REG_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_REGRDWR); + + if (strlen(respbuf) == header_len) { + ret = -EINVAL; + goto done; + } + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + arguments = kzalloc(strlen(respbuf) * sizeof(char), flag); + if (arguments == NULL) { + ret = -ENOMEM; + goto done; + } + strcpy(arguments, respbuf + header_len); + space_ind = strstr((char *)arguments, " "); + if (space_ind) + space_ind = strstr(space_ind + 1, " "); + if (space_ind) { + if (*(char *)(space_ind + 1) == '-') { + is_negative_val = MTRUE; + arguments[space_ind + 1 - arguments] = '\0'; + strcat(arguments, space_ind + 2); + } + } + parse_arguments(arguments, data, ARRAY_SIZE(data), &user_data_len); + if (is_negative_val == MTRUE) + data[2] *= -1; + + if (user_data_len == 2) { + req->action = MLAN_ACT_GET; + } else if (user_data_len == 3) { + req->action = MLAN_ACT_SET; + } else { + ret = -EINVAL; + goto done; + } + + reg_mem->param.reg_rw.type = (t_u32)data[0]; + reg_mem->param.reg_rw.offset = (t_u32)data[1]; + if (user_data_len == 3) + reg_mem->param.reg_rw.value = (t_u32)data[2]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + moal_memcpy_ext(priv->phandle, respbuf, ®_mem->param.reg_rw, + sizeof(reg_mem->param.reg_rw), respbuflen); + ret = sizeof(reg_mem->param.reg_rw); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + kfree(arguments); + LEAVE(); + return ret; +} + +/** + * @brief Read the EEPROM contents of the card + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_rdeeprom(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[2]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_EEPROM_RD; + req->req_id = MLAN_IOCTL_REG_MEM; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_RDEEPROM); + + if (strlen(respbuf) == header_len) { + ret = -EINVAL; + goto done; + } + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len == 2) { + req->action = MLAN_ACT_GET; + } else { + ret = -EINVAL; + goto done; + } + + reg_mem->param.rd_eeprom.offset = (t_u16)data[0]; + reg_mem->param.rd_eeprom.byte_count = (t_u16)data[1]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + moal_memcpy_ext(priv->phandle, respbuf, + ®_mem->param.rd_eeprom, + sizeof(reg_mem->param.rd_eeprom), respbuflen); + ret = sizeof(reg_mem->param.rd_eeprom); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read/Write device memory value + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_memrdwr(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[2]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_MEM_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_MEMRDWR); + + if (strlen(respbuf) == header_len) { + ret = -EINVAL; + goto done; + } + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len == 1) { + PRINTM(MINFO, "MEM_RW: GET\n"); + req->action = MLAN_ACT_GET; + } else if (user_data_len == 2) { + PRINTM(MINFO, "MEM_RW: SET\n"); + req->action = MLAN_ACT_SET; + } else { + ret = -EINVAL; + goto done; + } + + reg_mem->param.mem_rw.addr = (t_u32)data[0]; + if (user_data_len == 2) + reg_mem->param.mem_rw.value = (t_u32)data[1]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + moal_memcpy_ext(priv->phandle, respbuf, ®_mem->param.mem_rw, + sizeof(reg_mem->param.mem_rw), respbuflen); + ret = sizeof(reg_mem->param.mem_rw); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef SDIO +/** + * @brief Cmd52 read/write register + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int woal_priv_sdcmd52rw(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + t_u8 rw = 0, func, data = 0; + int buf[3], reg, ret = MLAN_STATUS_SUCCESS; + int user_data_len = 0, header_len = 0; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_SDCMD52RW); + memset((t_u8 *)buf, 0, sizeof(buf)); + + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + parse_arguments(respbuf + header_len, buf, ARRAY_SIZE(buf), + &user_data_len); + + if (user_data_len < 2 || user_data_len > 3) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + func = (t_u8)buf[0]; + if (func > 7) { + PRINTM(MERROR, "Invalid function number!\n"); + ret = -EINVAL; + goto done; + } + reg = (t_u32)buf[1]; + if (user_data_len == 2) { + rw = 0; /* CMD52 read */ + PRINTM(MINFO, "Cmd52 read, func=%d, reg=0x%08X\n", func, reg); + } + if (user_data_len == 3) { + rw = 1; /* CMD52 write */ + data = (t_u8)buf[2]; + PRINTM(MINFO, "Cmd52 write, func=%d, reg=0x%08X, data=0x%02X\n", + func, reg, data); + } + + if (!rw) { +#ifdef SDIO_MMC + 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 + if (sdio_read_ioreg(priv->phandle->card, func, reg, &data) < + 0) { + PRINTM(MERROR, + "sdio_read_ioreg: reading register 0x%X failed\n", + reg); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif /* SDIO_MMC */ + } else { +#ifdef SDIO_MMC + 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; + } +#else + if (sdio_write_ioreg(priv->phandle->card, func, reg, data) < + 0) { + PRINTM(MERROR, + "sdio_write_ioreg: writing register 0x%X failed\n", + reg); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif /* SDIO_MMC */ + } + + /* Action = GET */ + buf[0] = data; + + moal_memcpy_ext(priv->phandle, respbuf, &buf, sizeof(int), respbuflen); + ret = sizeof(int); + +done: + LEAVE(); + return ret; +} +#endif /* SDIO */ + +/** + * @brief Set / Get Auto ARP Response configuration + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_set_get_auto_arp(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[4]; + int user_data_len = 0; + int ret = 0; + moal_handle *handle = NULL; + + ENTER(); + + if (priv == NULL) { + PRINTM(MERROR, "Invalid priv\n"); + goto done; + } + handle = priv->phandle; + + if (strlen(respbuf) == (strlen(CMD_NXP) + strlen(PRIV_CMD_AUTO_ARP))) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_AUTO_ARP), + data, ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len) { + /* Get the enable/disable value from user */ + handle->hs_auto_arp = data[0]; + PRINTM(MIOCTL, "Auto ARP : %s\n", + handle->hs_auto_arp ? "enable" : "disable"); + } + + moal_memcpy_ext(handle, respbuf, &handle->hs_auto_arp, + sizeof(handle->hs_auto_arp), respbuflen); + ret = sizeof(handle->hs_auto_arp); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get/Set deauth control + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_deauth_ctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ds_snmp_mib *cfg = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0, header_len = 0, data = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + cfg = (mlan_ds_snmp_mib *)req->pbuf; + cfg->sub_command = MLAN_OID_SNMP_MIB_CTRL_DEAUTH; + req->req_id = MLAN_IOCTL_SNMP_MIB; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DEAUTH_CTRL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + cfg->param.deauthctrl = (t_u8)data; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = (int)cfg->param.deauthctrl; + moal_memcpy_ext(priv->phandle, respbuf, &data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#define MRVL_TLV_HEADER_SIZE 4 +/** + * @brief Get/Set per packet Txctl and Rxinfo configuration + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_per_pkt_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + t_u8 *pos = NULL; + int left_len, header_len = 0; + mlan_per_pkt_cfg *perpkt = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_PER_PKT_CFG); + pos = respbuf + header_len; + left_len = respbuflen - header_len; + + if (priv->phandle->card_info->per_pkt_cfg_support == 0) { + PRINTM(MERROR, "Device not support per packet configuration\n"); + ret = -EFAULT; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_PER_PKT_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (*pos == 0) { + /* GET operation */ + pos++; + if (priv->tx_protocols.protocol_num) { + perpkt = (mlan_per_pkt_cfg *)pos; + perpkt->type = TLV_TYPE_PER_PKT_CFG; + perpkt->tx_rx_control = TX_PKT_CTRL; + perpkt->proto_type_num = + priv->tx_protocols.protocol_num; + moal_memcpy_ext(priv->phandle, perpkt->ether_type, + priv->tx_protocols.protocols, + perpkt->proto_type_num * sizeof(t_u16), + MAX_NUM_ETHER_TYPE * sizeof(t_u16)); + perpkt->len = + (perpkt->proto_type_num + 1) * sizeof(t_u16); + pos += perpkt->len + MRVL_TLV_HEADER_SIZE; + } + if (priv->rx_protocols.protocol_num) { + perpkt = (mlan_per_pkt_cfg *)pos; + perpkt->type = TLV_TYPE_PER_PKT_CFG; + perpkt->tx_rx_control = RX_PKT_INFO; + perpkt->proto_type_num = + priv->rx_protocols.protocol_num; + moal_memcpy_ext(priv->phandle, perpkt->ether_type, + priv->rx_protocols.protocols, + perpkt->proto_type_num * sizeof(t_u16), + MAX_NUM_ETHER_TYPE * sizeof(t_u16)); + perpkt->len = + (perpkt->proto_type_num + 1) * sizeof(t_u16); + pos += perpkt->len + MRVL_TLV_HEADER_SIZE; + } + ret = pos - respbuf; + goto done; + } else if (*pos == 1) { + /* SET operation */ + req->action = MLAN_ACT_SET; + pos++; + left_len--; + while (*pos == TLV_TYPE_PER_PKT_CFG && (left_len > 2)) { + perpkt = (mlan_per_pkt_cfg *)pos; + if (perpkt->tx_rx_control & TX_PKT_CTRL) { + priv->tx_protocols.protocol_num = + perpkt->proto_type_num; + if (perpkt->proto_type_num <= + MAX_NUM_ETHER_TYPE) + moal_memcpy_ext( + priv->phandle, + priv->tx_protocols.protocols, + perpkt->ether_type, + perpkt->proto_type_num * + sizeof(t_u16), + MAX_NUM_ETHER_TYPE * + sizeof(t_u16)); + } + if (perpkt->tx_rx_control & RX_PKT_INFO) { + priv->rx_protocols.protocol_num = + perpkt->proto_type_num; + if (perpkt->proto_type_num <= + MAX_NUM_ETHER_TYPE) + moal_memcpy_ext( + priv->phandle, + priv->rx_protocols.protocols, + perpkt->ether_type, + perpkt->proto_type_num * + sizeof(t_u16), + MAX_NUM_ETHER_TYPE * + sizeof(t_u16)); + } + if (!perpkt->tx_rx_control) { + memset(&priv->tx_protocols, 0, + sizeof(dot11_protocol)); + memset(&priv->rx_protocols, 0, + sizeof(dot11_protocol)); + } + pos += perpkt->len + MRVL_TLV_HEADER_SIZE; + left_len -= (perpkt->len + MRVL_TLV_HEADER_SIZE); + } + } else + goto done; + + if (perpkt != NULL) + misc->param.txrx_pkt_ctrl = perpkt->tx_rx_control; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get Region Channel Power + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_get_chnrgpwr(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int header_len = 0; + t_u8 *pos = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + header_len = strlen(PRIV_CMD_GET_CHNRGPWR); + 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_GET_REGIONPWR_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + ret = header_len + sizeof(t_u16) + misc->param.rgchnpwr_cfg.length; + pos = respbuf + header_len; + moal_memcpy_ext(priv->phandle, pos, &misc->param.rgchnpwr_cfg, + sizeof(t_u16) + misc->param.rgchnpwr_cfg.length, + respbuflen - header_len); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get TX/RX histogram statistic + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_get_txpwrlimit(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_chan_trpc_cfg *trpc_cfg = NULL; + int header_len = 0; + t_u8 *pos = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + header_len = strlen(PRIV_CMD_GET_TXPWR_LIMIT); + trpc_cfg = (mlan_ds_misc_chan_trpc_cfg *)(respbuf + header_len); + if ((trpc_cfg->sub_band != 0) && (trpc_cfg->sub_band != 0x10) && + (trpc_cfg->sub_band != 0x11) && (trpc_cfg->sub_band != 0x12)) { + PRINTM(MERROR, "Invalid subband=0x%x\n", trpc_cfg->sub_band); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GET_CHAN_TRPC_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + misc->param.trpc_cfg.sub_band = trpc_cfg->sub_band; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + ret = header_len + sizeof(t_u16) + sizeof(t_u16) + + misc->param.trpc_cfg.length; + pos = respbuf + header_len; + moal_memcpy_ext(priv->phandle, pos, &misc->param.trpc_cfg, + sizeof(t_u16) + sizeof(t_u16) + + misc->param.trpc_cfg.length, + respbuflen - header_len); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** + * @brief Get TX/RX histogram statistic + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_getcfgchanlist(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int num_chan = 0; + wlan_ieee80211_chan_list *plist = NULL; + struct ieee80211_supported_band *sband; + struct wiphy *wiphy = NULL; + int i; + + ENTER(); + if (priv && priv->wdev) + wiphy = priv->wdev->wiphy; + if (!wiphy) { + PRINTM(MERROR, "wiphy is NULL\n"); + ret = -EFAULT; + goto done; + } + plist = (wlan_ieee80211_chan_list *)respbuf; + sband = wiphy->bands[NL80211_BAND_2GHZ]; + if (sband) { + num_chan += sband->n_channels; + for (i = 0; i < sband->n_channels; i++) { + plist->chan_list[i].center_freq = + sband->channels[i].center_freq; + plist->chan_list[i].hw_value = + sband->channels[i].hw_value; + plist->chan_list[i].flags = sband->channels[i].flags; + plist->chan_list[i].max_power = + sband->channels[i].max_power; + } + } + sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (sband) { + for (i = 0; i < sband->n_channels; i++) { + plist->chan_list[i + num_chan].center_freq = + sband->channels[i].center_freq; + plist->chan_list[i + num_chan].hw_value = + sband->channels[i].hw_value; + plist->chan_list[i + num_chan].flags = + sband->channels[i].flags; + plist->chan_list[i + num_chan].max_power = + sband->channels[i].max_power; +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 8, 13) + plist->chan_list[i + num_chan].dfs_state = + sband->channels[i].dfs_state; +#endif + } + num_chan += sband->n_channels; + } + plist->num_chan = num_chan; + ret = sizeof(wlan_ieee80211_chan_list) + + sizeof(wlan_ieee80211_chan) * num_chan; +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get TX/RX histogram statistic + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_get_rx_tx_histogram(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + tx_rx_histogram *tx_rx_info = NULL; + int header_len = 0; + t_u8 *pos = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + header_len = strlen(PRIV_CMD_TX_RX_HISTOGRAM); + tx_rx_info = (tx_rx_histogram *)(respbuf + header_len); + if (tx_rx_info->enable > 2 || + (tx_rx_info->enable == GET_TX_RX_HISTOGRAM && + (tx_rx_info->action > 3 || tx_rx_info->action <= 0))) { + PRINTM(MERROR, "Invalid parameters\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GET_TX_RX_HISTOGRAM; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (tx_rx_info->enable == GET_TX_RX_HISTOGRAM) { + misc->param.tx_rx_histogram.enable = ENABLE_TX_RX_HISTOGRAM; + misc->param.tx_rx_histogram.action = (t_u16)tx_rx_info->action; + } else { + misc->param.tx_rx_histogram.enable = tx_rx_info->enable; + misc->param.tx_rx_histogram.action |= + FLAG_TX_HISTOGRAM | FLAG_RX_HISTOGRAM; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + ret = header_len + 2 * sizeof(t_u8); + if (tx_rx_info->enable & GET_TX_RX_HISTOGRAM) { + pos = respbuf + header_len + 2 * sizeof(t_u8); + /* Save tx/rx histogram size */ + moal_memcpy_ext(priv->phandle, pos, + &misc->param.tx_rx_histogram.size, + sizeof(misc->param.tx_rx_histogram.size), + respbuflen - (header_len + 2 * sizeof(t_u8))); + ret += sizeof(misc->param.tx_rx_histogram.size); + pos += sizeof(misc->param.tx_rx_histogram.size); + moal_memcpy_ext( + priv->phandle, pos, &misc->param.tx_rx_histogram.value, + misc->param.tx_rx_histogram.size, + respbuflen - (header_len + 2 * sizeof(t_u8)) - + sizeof(misc->param.tx_rx_histogram.size)); + ret += misc->param.tx_rx_histogram.size; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get hotspot mode configurations + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int woal_priv_hotspotcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int data = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_HOTSPOTCFG); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + if (user_data_len >= 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } else { + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + if (user_data_len == 0) { + req->action = MLAN_ACT_GET; + } else { + cfg->param.hotspot_cfg = data; + req->action = MLAN_ACT_SET; + } + } + cfg->sub_command = MLAN_OID_MISC_HOTSPOT_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data = cfg->param.hotspot_cfg; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Mgmt Frame passthru mask + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int woal_priv_mgmt_frame_passthru_ctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int data = 0; + int user_data_len = 0, header_len = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *mgmt_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_MGMT_FRAME_CTRL); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + + if (user_data_len >= 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } else { + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + mgmt_cfg = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + mgmt_cfg->sub_command = MLAN_OID_MISC_RX_MGMT_IND; + + if (user_data_len == 0) { /* Get */ + req->action = MLAN_ACT_GET; + } else { /* Set */ + mgmt_cfg->param.mgmt_subtype_mask = data; + req->action = MLAN_ACT_SET; + } + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = mgmt_cfg->param.mgmt_subtype_mask; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to send an ADDTS TSPEC + * + * Receive a ADDTS command from the application. The command structure + * contains a TSPEC and timeout in milliseconds. The timeout is performed + * in the firmware after the ADDTS command frame is sent. + * + * The TSPEC is received in the API as an opaque block. The firmware will + * send the entire data block, including the bytes after the TSPEC. This + * is done to allow extra IEs to be packaged with the TSPEC in the ADDTS + * action frame. + * + * The IOCTL structure contains two return fields: + * - The firmware command result, which indicates failure and timeouts + * - The IEEE Status code which contains the corresponding value from + * any ADDTS response frame received. + * + * In addition, the opaque TSPEC data block passed in is replaced with the + * TSPEC received in the ADDTS response frame. In case of failure, the + * AP may modify the TSPEC on return and in the case of success, the + * medium time is returned as calculated by the AP. Along with the TSPEC, + * any IEs that are sent in the ADDTS response are also returned and can be + * parsed using the IOCTL length as an indicator of extra elements. + * + * The return value to the application layer indicates a driver execution + * success or failure. A successful return could still indicate a firmware + * failure or AP negotiation failure via the commandResult field copied + * back to the application. + * + * @param priv Pointer to the mlan_private driver data struct + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_priv_wmm_addts_req_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *cfg = NULL; + wlan_ioctl_wmm_addts_req_t addts_ioctl; + int ret = 0, header_len = 0, copy_len = sizeof(addts_ioctl); + t_u8 *data_ptr; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_ADDTS); + data_ptr = respbuf + header_len; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_ADDTS; + + memset(&addts_ioctl, 0x00, sizeof(addts_ioctl)); + + moal_memcpy_ext(priv->phandle, (t_u8 *)&addts_ioctl, data_ptr, + sizeof(addts_ioctl), sizeof(addts_ioctl)); + + cfg->param.addts.timeout = addts_ioctl.timeout_ms; + cfg->param.addts.ie_data_len = addts_ioctl.ie_data_len; + + moal_memcpy_ext(priv->phandle, cfg->param.addts.ie_data, + addts_ioctl.ie_data, cfg->param.addts.ie_data_len, + MLAN_WMM_TSPEC_SIZE + MLAN_WMM_ADDTS_EXTRA_IE_BYTES); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + addts_ioctl.cmd_result = cfg->param.addts.result; + addts_ioctl.ieee_status_code = (t_u8)cfg->param.addts.status_code; + addts_ioctl.ie_data_len = cfg->param.addts.ie_data_len; + + moal_memcpy_ext(priv->phandle, addts_ioctl.ie_data, + cfg->param.addts.ie_data, cfg->param.addts.ie_data_len, + MLAN_WMM_TSPEC_SIZE + MLAN_WMM_ADDTS_EXTRA_IE_BYTES); + + copy_len = (sizeof(addts_ioctl) - sizeof(addts_ioctl.ie_data) + + cfg->param.addts.ie_data_len); + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&addts_ioctl, copy_len, + respbuflen); + ret = copy_len; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to send a DELTS TSPEC + * + * Receive a DELTS command from the application. The command structure + * contains a TSPEC and reason code along with space for a command result + * to be returned. The information is packaged is sent to the wlan_cmd.c + * firmware command prep and send routines for execution in the firmware. + * + * The reason code is not used for WMM implementations but is indicated in + * the 802.11e specification. + * + * The return value to the application layer indicates a driver execution + * success or failure. A successful return could still indicate a firmware + * failure via the cmd_result field copied back to the application. + * + * @param priv Pointer to the mlan_private driver data struct + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_priv_wmm_delts_req_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *cfg = NULL; + wlan_ioctl_wmm_delts_req_t delts_ioctl; + int ret = 0, header_len = 0, copy_len = 0; + t_u8 *data_ptr; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DELTS); + data_ptr = respbuf + header_len; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_DELTS; + + memset(&delts_ioctl, 0x00, sizeof(delts_ioctl)); + + if (strlen(respbuf) > header_len) { + copy_len = MIN(strlen(data_ptr), sizeof(delts_ioctl)); + moal_memcpy_ext(priv->phandle, (t_u8 *)&delts_ioctl, data_ptr, + copy_len, sizeof(delts_ioctl)); + + 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; + + moal_memcpy_ext(priv->phandle, cfg->param.delts.ie_data, + delts_ioctl.ie_data, + cfg->param.delts.ie_data_len, + MLAN_WMM_TSPEC_SIZE); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Return the firmware command result back to the application + * layer */ + delts_ioctl.cmd_result = cfg->param.delts.result; + copy_len = sizeof(delts_ioctl); + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&delts_ioctl, + copy_len, respbuflen); + ret = copy_len; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get/set a specified AC Queue's parameters + * + * Receive a AC Queue configuration command which is used to get, set, or + * default the parameters associated with a specific WMM AC Queue. + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_qconfig(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + mlan_ds_wmm_queue_config *pqcfg = NULL; + wlan_ioctl_wmm_queue_config_t qcfg_ioctl; + t_u8 *data_ptr; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_CONFIG; + + memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); + pqcfg = (mlan_ds_wmm_queue_config *)&pwmm->param.q_cfg; + data_ptr = respbuf + (strlen(CMD_NXP) + strlen(PRIV_CMD_QCONFIG)); + + moal_memcpy_ext(priv->phandle, (t_u8 *)&qcfg_ioctl, data_ptr, + sizeof(qcfg_ioctl), sizeof(qcfg_ioctl)); + pqcfg->action = qcfg_ioctl.action; + pqcfg->access_category = qcfg_ioctl.access_category; + pqcfg->msdu_lifetime_expiry = qcfg_ioctl.msdu_lifetime_expiry; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); + qcfg_ioctl.action = pqcfg->action; + qcfg_ioctl.access_category = pqcfg->access_category; + qcfg_ioctl.msdu_lifetime_expiry = pqcfg->msdu_lifetime_expiry; + moal_memcpy_ext(priv->phandle, data_ptr, (t_u8 *)&qcfg_ioctl, + sizeof(qcfg_ioctl), + respbuflen - + (strlen(CMD_NXP) + strlen(PRIV_CMD_QCONFIG))); + ret = strlen(CMD_NXP) + strlen(PRIV_CMD_QCONFIG) + sizeof(qcfg_ioctl); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the status of the WMM queues + * + * Return the following information for each WMM AC: + * - WMM IE Acm Required + * - Firmware Flow Required + * - Firmware Flow Established + * - Firmware Queue Enabled + * - Firmware Delivery Enabled + * - Firmware Trigger Enabled + * + * @param priv Pointer to the moal_private driver data struct + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_priv_wmm_queue_status_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + wlan_ioctl_wmm_queue_status_t qstatus_ioctl; + int ret = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_QSTATUS); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATUS; + + if (strlen(respbuf) == header_len) { + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&qstatus_ioctl, 0x00, sizeof(qstatus_ioctl)); + moal_memcpy_ext(priv->phandle, (void *)&qstatus_ioctl, + (void *)&pwmm->param.q_status, + sizeof(qstatus_ioctl), sizeof(qstatus_ioctl)); + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&qstatus_ioctl, + sizeof(qstatus_ioctl), respbuflen); + ret = sizeof(qstatus_ioctl); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the status of the WMM Traffic Streams + * + * @param priv Pointer to the moal_private driver data struct + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_priv_wmm_ts_status_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + wlan_ioctl_wmm_ts_status_t ts_status_ioctl; + int ret = 0, header_len = 0; + t_u8 *data_ptr; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_TS_STATUS); + data_ptr = respbuf + header_len; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_TS_STATUS; + + memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); + + moal_memcpy_ext(priv->phandle, (t_u8 *)&ts_status_ioctl, data_ptr, + sizeof(ts_status_ioctl), sizeof(ts_status_ioctl)); + + memset(&pwmm->param.ts_status, 0x00, sizeof(ts_status_ioctl)); + pwmm->param.ts_status.tid = ts_status_ioctl.tid; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); + moal_memcpy_ext(priv->phandle, (void *)&ts_status_ioctl, + (void *)&pwmm->param.ts_status, sizeof(ts_status_ioctl), + sizeof(ts_status_ioctl)); + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&ts_status_ioctl, + sizeof(ts_status_ioctl), respbuflen); + ret = sizeof(ts_status_ioctl); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get MAC control + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_macctrl(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_MAC_CTRL); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_MAC_CONTROL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) + req->action = MLAN_ACT_GET; + else { + cfg->param.mac_ctrl = (t_u32)data; + req->action = MLAN_ACT_SET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&cfg->param.mac_ctrl, + sizeof(data), respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get connection status + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int woal_priv_getwap(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int ret = 0; +#ifdef STA_SUPPORT + mlan_bss_info bss_info; +#endif + + ENTER(); + +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + memset(&bss_info, 0, sizeof(bss_info)); + + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + if (bss_info.media_connected == MTRUE) { + moal_memcpy_ext(priv->phandle, respbuf, + (t_u8 *)&bss_info.bssid, + MLAN_MAC_ADDR_LENGTH, respbuflen); + } else { + memset(respbuf, 0, MLAN_MAC_ADDR_LENGTH); + } + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (priv->bss_started) { + moal_memcpy_ext(priv->phandle, respbuf, + priv->current_addr, + MLAN_MAC_ADDR_LENGTH, respbuflen); + } else { + memset(respbuf, 0, MLAN_MAC_ADDR_LENGTH); + } + } +#endif + ret = MLAN_MAC_ADDR_LENGTH; + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Region Code + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_region_code(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_REGION_CODE); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_REGION; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) + req->action = MLAN_ACT_GET; + else { + cfg->param.region_code = (t_u32)data; + req->action = MLAN_ACT_SET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&cfg->param.region_code, + sizeof(data), respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef RX_PACKET_COALESCE +/** + * @brief Set/Get RX packet coalesceing setting + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_rx_pkt_coalesce_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + t_u32 data[2]; + int user_data_len = 0, header_len = 0; + mlan_ds_misc_cfg *cfg = NULL; + t_u8 *data_ptr; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = respbuf + strlen(CMD_NXP) + strlen(PRIV_CMD_RX_COAL_CFG); + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_RX_COAL_CFG); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if ((user_data_len != 0) && (user_data_len != 2)) { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_RX_PACKET_COALESCE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) { + req->action = MLAN_ACT_GET; + } else { + req->action = MLAN_ACT_SET; + cfg->param.rx_coalesce.packet_threshold = data[0]; + cfg->param.rx_coalesce.delay = data[1]; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext( + priv->phandle, respbuf, + (mlan_ds_misc_rx_packet_coalesce *)&cfg->param.rx_coalesce, + req->buf_len, respbuflen); + ret = req->buf_len; + +done: + LEAVE(); + return ret; +} +#endif +/** + * @brief Set/Get FW side mac address + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int woal_priv_fwmacaddr(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + t_u8 data[ETH_ALEN]; + int ret = 0; + int header_len = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_FWMACADDR); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MAC_ADDR; + req->req_id = MLAN_IOCTL_BSS; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + req->action = MLAN_ACT_SET; + memset(data, 0, sizeof(data)); + woal_mac2u8(data, respbuf + header_len); + moal_memcpy_ext(priv->phandle, bss->param.mac_addr, data, + ETH_ALEN, sizeof(mlan_802_11_mac_addr)); + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, bss->param.mac_addr, + sizeof(data), respbuflen); + ret = sizeof(data); + HEXDUMP("FW MAC Addr:", respbuf, ETH_ALEN); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +/** + * @brief Set offchannel + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int woal_priv_offchannel(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int data[4]; + int ret = 0; + t_u8 status = 1; + t_u8 chan_type = CHAN_NO_HT; + int user_data_len = 0, header_len = 0; + + ENTER(); + + memset(data, 0, sizeof(data)); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_OFFCHANNEL); + + if (header_len == strlen(respbuf)) { + /* Query current remain on channel status */ + if (priv->phandle->remain_on_channel) + ret = sprintf(respbuf, + "There is pending remain on channel from bss %d\n", + priv->phandle->remain_bss_index) + + 1; + else + ret = sprintf(respbuf, + "There is no pending remain on channel\n") + + 1; + goto done; + } else + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (user_data_len >= 1) { + if ((data[0] != 0) && (data[0] != 1)) { + PRINTM(MERROR, "action (%d) must be either 0 or 1\n", + data[0]); + ret = -EINVAL; + goto done; + } + } + if (user_data_len == 2) { + if (data[0] == 1) { + PRINTM(MERROR, + "channel and duration must both the mentioned\n"); + ret = -EINVAL; + goto done; + } else { + PRINTM(MWARN, + "extra arguments are ignored since action is 'cancel'\n"); + } + } + if (user_data_len >= 3) { + if (data[0] == 1) { + if (data[1] < 0) { + PRINTM(MERROR, "channel cannot be negative\n"); + ret = -EINVAL; + goto done; + } + if (data[2] < 0) { + PRINTM(MERROR, "duration cannot be negative\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len == 4) { + if (data[3] && + (data[3] != CHANNEL_BW_40MHZ_ABOVE) && + (data[3] != CHANNEL_BW_40MHZ_BELOW) && + (data[3] != CHANNEL_BW_80MHZ)) { + PRINTM(MERROR, "invalid bandwidth"); + ret = -EINVAL; + goto done; + } + switch (data[3]) { + case CHANNEL_BW_40MHZ_ABOVE: + chan_type = CHAN_HT40PLUS; + break; + case CHANNEL_BW_40MHZ_BELOW: + chan_type = CHAN_HT40MINUS; + break; + case CHANNEL_BW_80MHZ: + chan_type = CHAN_VHT80; + break; + default: + break; + } + } + } + } + + if (data[0] == 0) { + if (!priv->phandle->remain_on_channel) { + ret = sprintf(respbuf, + "There is no pending remain on channel to be canceled\n") + + 1; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, + MTRUE, &status, NULL, 0, + 0)) { + PRINTM(MERROR, "remain_on_channel: Failed to cancel\n"); + ret = -EFAULT; + goto done; + } + if (status == MLAN_STATUS_SUCCESS) + priv->phandle->remain_on_channel = MFALSE; + } else if (data[0] == 1) { + if (woal_cfg80211_remain_on_channel_cfg( + priv, MOAL_IOCTL_WAIT, MFALSE, &status, + ieee80211_get_channel( + priv->wdev->wiphy, + ieee80211_channel_to_frequency( + data[1] +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + (data[1] <= 14 ? + IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ) +#endif + )), + chan_type, (t_u32)data[2])) { + PRINTM(MERROR, "remain_on_channel: Failed to start\n"); + ret = -EFAULT; + goto done; + } + if (status == MLAN_STATUS_SUCCESS) { + priv->phandle->remain_on_channel = MTRUE; + priv->phandle->remain_bss_index = priv->bss_index; + } + } + + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + else + ret = sprintf(respbuf, "OK\n") + 1; + +done: + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief Set/Get dscp map + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +int woal_priv_set_get_dscp_map(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = NULL; + int copy_size = 0, header_len = 0; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DSCP_MAP); + if (strlen(respbuf) != header_len) { + /* SET operation */ + pos = respbuf + header_len; + moal_memcpy_ext(priv->phandle, priv->dscp_map, pos, + sizeof(priv->dscp_map), sizeof(priv->dscp_map)); + } + + copy_size = MIN(sizeof(priv->dscp_map), respbuflen); + moal_memcpy_ext(priv->phandle, respbuf, priv->dscp_map, copy_size, + respbuflen); + ret = copy_size; + + LEAVE(); + return ret; +} + +/** + * @brief Get extended driver version + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_get_driver_verext(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int copy_size = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_VER_EXT; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_VEREXT); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + info->param.ver_ext.version_str_sel = data; + if (((t_s32)(info->param.ver_ext.version_str_sel)) < 0) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* + * Set the amount to copy back to the application as the minimum of the + * available assoc resp data or the buffer provided by the application + */ + copy_size = MIN(strlen(info->param.ver_ext.version_str), respbuflen); + moal_memcpy_ext(priv->phandle, respbuf, info->param.ver_ext.version_str, + copy_size, respbuflen); + ret = copy_size; + PRINTM(MINFO, "MOAL EXTENDED VERSION: %s\n", + info->param.ver_ext.version_str); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef USB +#ifdef CONFIG_USB_SUSPEND +/** + * @brief This function makes USB device to suspend. + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_enter_usb_suspend(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + + ENTER(); + ret = woal_enter_usb_suspend(priv->phandle); + moal_memcpy_ext(priv->phandle, respbuf, &ret, sizeof(int), respbuflen); + ret = sizeof(int); + + LEAVE(); + return ret; +} + +/** + * @brief This function makes USB device to resume. + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_exit_usb_suspend(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + + ENTER(); + ret = woal_exit_usb_suspend(priv->phandle); + moal_memcpy_ext(priv->phandle, respbuf, &ret, sizeof(int), respbuflen); + ret = sizeof(int); + + LEAVE(); + return ret; +} +#endif /* CONFIG_USB_SUSPEND */ +#endif + +#if defined(STA_SUPPORT) && defined(STA_WEXT) +/** + * @brief SET/Get radio + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_radio_ctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0, option = 0; + int user_data_len = 0, header_len = 0; + mlan_bss_info bss_info; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_RADIO_CTRL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &option, 1, + &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len == 1) { + /* Set radio */ + if (option < 0 || option > 1) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + if (MLAN_STATUS_SUCCESS != woal_set_radio(priv, (t_u8)option)) + ret = -EFAULT; + goto done; + } else { + /* Get radio status */ + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + moal_memcpy_ext(priv->phandle, respbuf, &bss_info.radio_on, + sizeof(bss_info.radio_on), respbuflen); + ret = sizeof(bss_info.radio_on); + } +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Implement WMM enable command + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_wmm_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data = 0; + mlan_ds_wmm_cfg *wmm = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + wmm = (mlan_ds_wmm_cfg *)req->pbuf; + wmm->sub_command = MLAN_OID_WMM_CFG_ENABLE; + req->req_id = MLAN_IOCTL_WMM_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_WMM_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + /* Set wmm */ + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + if (data == CMD_DISABLED) + wmm->param.wmm_enable = MFALSE; + else + wmm->param.wmm_enable = MTRUE; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, &wmm->param.wmm_enable, + sizeof(wmm->param.wmm_enable), respbuflen); + ret = sizeof(wmm->param.wmm_enable); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Implement Mininum BA Threshold cfg command + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_min_ba_threshold_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data = 0; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_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_MIN_BA_THRESHOLD; + req->req_id = MLAN_IOCTL_11N_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_MIN_BA_THRESH_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + /* Set minimum BA Threshold */ + if ((data < 0) || (data > 16)) { + PRINTM(MERROR, + "Error: Valid minimum BA threshold range (0-16)!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + cfg_11n->param.min_ba_threshold = data; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, + &cfg_11n->param.min_ba_threshold, + sizeof(cfg_11n->param.min_ba_threshold), respbuflen); + ret = sizeof(cfg_11n->param.min_ba_threshold); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_SUPPORT) +/** + * @brief Implement 802.11D enable command + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_11d_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data = 0; + mlan_ds_11d_cfg *pcfg_11d = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + pcfg_11d->sub_command = MLAN_OID_11D_CFG_ENABLE; + req->req_id = MLAN_IOCTL_11D_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_11D_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + if (data == CMD_DISABLED) + pcfg_11d->param.enable_11d = MFALSE; + else + pcfg_11d->param.enable_11d = MTRUE; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, &pcfg_11d->param.enable_11d, + sizeof(pcfg_11d->param.enable_11d), respbuflen); + ret = sizeof(pcfg_11d->param.enable_11d); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Implement 802.11D clear chan table command + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise fail + */ +static int woal_priv_11d_clr_chan_tbl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ds_11d_cfg *pcfg_11d = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_11D_CLR_TBL); + + if (strlen(respbuf) != header_len) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + pcfg_11d->sub_command = MLAN_OID_11D_CLR_CHAN_TABLE; + req->req_id = MLAN_IOCTL_11D_CFG; + req->action = MLAN_ACT_SET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +#ifndef OPCHAN +/** + * @brief Set/Get WWS mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_wws_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data = 0; + mlan_ds_misc_cfg *wws = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + wws = (mlan_ds_misc_cfg *)req->pbuf; + wws->sub_command = MLAN_OID_MISC_WWS; + req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_WWS_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + PRINTM(MERROR, + "Invalid arguments, WWS config not changed!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + wws->param.wws_cfg = (t_u16)data; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, &wws->param.wws_cfg, + sizeof(wws->param.wws_cfg), respbuflen); + ret = sizeof(wws->param.wws_cfg); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +#if defined(REASSOCIATION) +/** + * @brief Set/Get reassociation settings + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_set_get_reassoc(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + moal_handle *handle = priv->phandle; + int data = 0; + int ret = 0; + int user_data_len = 0, header_len = 0; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_REASSOCTRL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + data = (int)(priv->reassoc_on); + moal_memcpy_ext(handle, respbuf, &data, sizeof(data), + respbuflen); + ret = sizeof(data); + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if (data == 0) { + handle->reassoc_on &= ~MBIT(priv->bss_index); + priv->reassoc_on = MFALSE; + priv->reassoc_required = MFALSE; + if (!handle->reassoc_on && + handle->is_reassoc_timer_set == MTRUE) { + woal_cancel_timer( + &handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + } else if (data == 1) { + handle->reassoc_on |= MBIT(priv->bss_index); + priv->reassoc_on = MTRUE; + } else { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + } + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + } + } + + LEAVE(); + return ret; +} +#endif /* REASSOCIATION */ + +/** + * @brief Get Transmit buffer size + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_txbuf_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int buf_size = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE; + req->req_id = MLAN_IOCTL_11N_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_TXBUF_CFG); + + if (strlen(respbuf) != header_len) { + PRINTM(MERROR, + "Don't support set Tx buffer size after driver loaded!\n"); + ret = -EINVAL; + goto done; + } else { + /* Get Tx buffer size from MLAN */ + req->action = MLAN_ACT_GET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + buf_size = cfg_11n->param.tx_buf_size; + moal_memcpy_ext(priv->phandle, respbuf, &buf_size, sizeof(buf_size), + respbuflen); + ret = sizeof(buf_size); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Set/Get auth type + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_auth_type(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int auth_type = 0; + t_u32 auth_mode; + int ret = 0; + int user_data_len = 0, header_len = 0; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_AUTH_TYPE); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + if (MLAN_STATUS_SUCCESS != + woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) { + ret = -EFAULT; + goto done; + } + user_data_len = 0; + auth_type = auth_mode; + moal_memcpy_ext(priv->phandle, respbuf, &auth_type, + sizeof(auth_type), respbuflen); + ret = sizeof(auth_type); + goto done; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &auth_type, 1, + &user_data_len); + if (user_data_len == 1) { + PRINTM(MINFO, "SET: auth_type %d\n", auth_type); + if (((auth_type < MLAN_AUTH_MODE_OPEN) || + (auth_type > MLAN_AUTH_MODE_SAE)) && + (auth_type != MLAN_AUTH_MODE_AUTO)) { + ret = -EINVAL; + goto done; + } + auth_mode = auth_type; + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, + auth_mode)) { + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + } + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set/get user provisioned local power constraint + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_11h_local_pwr_constraint(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data = 0; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + ds_11hcfg->sub_command = MLAN_OID_11H_LOCAL_POWER_CONSTRAINT; + req->req_id = MLAN_IOCTL_11H_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_POWER_CONS); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + req->action = MLAN_ACT_SET; + ds_11hcfg->param.usr_local_power_constraint = + (t_s8)data; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + data = (int)ds_11hcfg->param.usr_local_power_constraint; + moal_memcpy_ext(priv->phandle, respbuf, &data, sizeof(data), + respbuflen); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get HT stream 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. + */ +static int woal_priv_ht_stream_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data = 0; + mlan_ds_11n_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + cfg = (mlan_ds_11n_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_11N_CFG_STREAM_CFG; + req->req_id = MLAN_IOCTL_11N_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_HT_STREAM_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if (data != HT_STREAM_MODE_1X1 && + data != HT_STREAM_MODE_2X2) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + cfg->param.stream_cfg = data; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = ((mlan_ds_11n_cfg *)req->pbuf)->param.stream_cfg; + moal_memcpy_ext(priv->phandle, respbuf, &data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set mimo switch 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. + */ +static int woal_priv_mimo_switch(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[2] = {0}; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_MIMO_SWITCH; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_MIMO_SWITCH); + + if (strlen(respbuf) > header_len) { + /* SET operation */ + req->action = MLAN_ACT_SET; + parse_arguments(respbuf + header_len, data, 2, &user_data_len); + if (user_data_len == 2) { + radio->param.mimo_switch_cfg.txpath_antmode = data[0]; + radio->param.mimo_switch_cfg.rxpath_antmode = data[1]; + } else { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get thermal reading + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_thermal(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ds_misc_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0, header_len = 0, data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_THERMAL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_THERMAL); + + if (strlen(respbuf) != header_len) { + PRINTM(MERROR, "Set is not supported for this command\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = (int)cfg->param.thermal; + moal_memcpy_ext(priv->phandle, respbuf, &data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get beacon interval + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_beacon_interval(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL; + req->req_id = MLAN_IOCTL_BSS; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_BCN_INTERVAL); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + req->action = MLAN_ACT_GET; + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + if (user_data_len == 1) { + if ((data < MLAN_MIN_BEACON_INTERVAL) || + (data > MLAN_MAX_BEACON_INTERVAL)) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + bss->param.bcn_interval = data; + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = ((mlan_ds_bss *)req->pbuf)->param.bcn_interval; + moal_memcpy_ext(priv->phandle, respbuf, &data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Get signal + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_get_signal(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ +/** Input data size */ +#define IN_DATA_SIZE 2 +/** Output data size */ +#define OUT_DATA_SIZE 12 + int ret = 0; + int in_data[IN_DATA_SIZE]; + int out_data[OUT_DATA_SIZE]; + mlan_ds_get_signal signal; + int data_length = 0; + int buflen = 0; + int user_data_len = 0, header_len = 0; + + ENTER(); + + memset(in_data, 0, sizeof(in_data)); + memset(out_data, 0, sizeof(out_data)); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_GET_SIGNAL); + + if (strlen(respbuf) != header_len) + parse_arguments(respbuf + header_len, in_data, IN_DATA_SIZE, + &user_data_len); + buflen = MIN(user_data_len, IN_DATA_SIZE); + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can not get RSSI in disconnected state\n"); + ret = -ENOTSUPP; + goto done; + } + + if (user_data_len) { + if (user_data_len > IN_DATA_SIZE) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } + + switch (user_data_len) { + case 0: /* No checking, get everything */ + break; + case 2: /* Check subtype range */ + if (in_data[1] < 1 || in_data[1] > 4) { + ret = -EINVAL; + goto done; + } + /* Fall through */ + case 1: /* Check type range */ + if (in_data[0] < 1 || in_data[0] > 3) { + ret = -EINVAL; + goto done; + } + break; + default: + ret = -EINVAL; + goto done; + } + + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + ret = -EFAULT; + goto done; + } + PRINTM(MINFO, "RSSI Beacon Last : %d\n", (int)signal.bcn_rssi_last); + PRINTM(MINFO, "RSSI Beacon Average: %d\n", (int)signal.bcn_rssi_avg); + PRINTM(MINFO, "RSSI Data Last : %d\n", (int)signal.data_rssi_last); + PRINTM(MINFO, "RSSI Data Average : %d\n", (int)signal.data_rssi_avg); + PRINTM(MINFO, "SNR Beacon Last : %d\n", (int)signal.bcn_snr_last); + PRINTM(MINFO, "SNR Beacon Average : %d\n", (int)signal.bcn_snr_avg); + PRINTM(MINFO, "SNR Data Last : %d\n", (int)signal.data_snr_last); + PRINTM(MINFO, "SNR Data Average : %d\n", (int)signal.data_snr_avg); + PRINTM(MINFO, "NF Beacon Last : %d\n", (int)signal.bcn_nf_last); + PRINTM(MINFO, "NF Beacon Average : %d\n", (int)signal.bcn_nf_avg); + PRINTM(MINFO, "NF Data Last : %d\n", (int)signal.data_nf_last); + PRINTM(MINFO, "NF Data Average : %d\n", (int)signal.data_nf_avg); + + /* Check type */ + switch (in_data[0]) { + case 0: /* Send everything */ + out_data[data_length++] = signal.bcn_rssi_last; + out_data[data_length++] = signal.bcn_rssi_avg; + out_data[data_length++] = signal.data_rssi_last; + out_data[data_length++] = signal.data_rssi_avg; + out_data[data_length++] = signal.bcn_snr_last; + out_data[data_length++] = signal.bcn_snr_avg; + out_data[data_length++] = signal.data_snr_last; + out_data[data_length++] = signal.data_snr_avg; + out_data[data_length++] = signal.bcn_nf_last; + out_data[data_length++] = signal.bcn_nf_avg; + out_data[data_length++] = signal.data_nf_last; + out_data[data_length++] = signal.data_nf_avg; + break; + case 1: /* RSSI */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_rssi_last; + out_data[data_length++] = signal.bcn_rssi_avg; + out_data[data_length++] = signal.data_rssi_last; + out_data[data_length++] = signal.data_rssi_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_rssi_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_rssi_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_rssi_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_rssi_avg; + break; + default: + break; + } + break; + case 2: /* SNR */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_snr_last; + out_data[data_length++] = signal.bcn_snr_avg; + out_data[data_length++] = signal.data_snr_last; + out_data[data_length++] = signal.data_snr_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_snr_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_snr_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_snr_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_snr_avg; + break; + default: + break; + } + break; + case 3: /* NF */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_nf_last; + out_data[data_length++] = signal.bcn_nf_avg; + out_data[data_length++] = signal.data_nf_last; + out_data[data_length++] = signal.data_nf_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_nf_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_nf_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_nf_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_nf_avg; + break; + default: + break; + } + break; + default: + break; + } + + moal_memcpy_ext(priv->phandle, respbuf, out_data, + (data_length * sizeof(int)), respbuflen); + ret = data_length * sizeof(int); + +done: + LEAVE(); + return ret; +} + +static int woal_signal_ext_enable(moal_private *priv, t_u8 enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *snmp = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + snmp = (mlan_ds_snmp_mib *)req->pbuf; + snmp->sub_command = MLAN_OID_SNMP_MIB_SIGNALEXT_ENABLE; + req->req_id = MLAN_IOCTL_SNMP_MIB; + req->action = MLAN_ACT_SET; + snmp->param.signalext_enable = enable; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get signal + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_get_signal_ext(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ +#define PATH_SIZE 13 + int ret = 0; + int data = 0, path = 0, data_len = 0; + int user_data_len = 0, header_len = 0; + int out_data[PATH_SIZE * MAX_PATH_NUM] = {0}; + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info = NULL; + mlan_ds_get_signal signal_get[MAX_PATH_NUM]; + mlan_status status = MLAN_STATUS_SUCCESS; + int path_num; + t_u8 enable = 1; + + ENTER(); + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can not get RSSI in disconnected state\n"); + ret = -ENOTSUPP; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_GET_SIGNAL_EXT); + + if (strlen(respbuf) != header_len) { + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (data < PATH_ALL || data > PATH_AB) { + PRINTM(MERROR, "Wrong arguments\n"); + ret = -EINVAL; + goto done; + } + + /** Enable signalext feature in firmware */ + if (MLAN_STATUS_SUCCESS != woal_signal_ext_enable(priv, enable)) { + ret = -EFAULT; + goto done; + } + woal_sched_timeout(1000); + enable = 0; + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_SIGNAL_EXT; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + info->param.path_id = (t_u16)data; + + /* Send IOCTL request to MLAN */ + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + woal_signal_ext_enable(priv, enable); + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != woal_signal_ext_enable(priv, enable)) { + ret = -EFAULT; + goto done; + } + path_num = 1; + if (data == PATH_ALL) { + moal_memcpy_ext(priv->phandle, signal_get, + info->param.signal_ext, sizeof(signal_get), + sizeof(signal_get)); + path_num = MAX_PATH_NUM; + } else + moal_memcpy_ext(priv->phandle, signal_get, + info->param.signal_ext, + sizeof(mlan_ds_get_signal), sizeof(signal_get)); + + for (path = 0; path < path_num; path++) { + if (signal_get[path].selector == PATH_AB) + PRINTM(MINFO, "PATH A+B:\n"); + else if (signal_get[path].selector == PATH_A) + PRINTM(MINFO, "PATH A:\n"); + else if (signal_get[path].selector == PATH_B) + PRINTM(MINFO, "PATH B:\n"); + PRINTM(MINFO, "RSSI Beacon Last : %d\n", + (int)signal_get[path].bcn_rssi_last); + PRINTM(MINFO, "RSSI Beacon Average: %d\n", + (int)signal_get[path].bcn_rssi_avg); + PRINTM(MINFO, "RSSI Data Last : %d\n", + (int)signal_get[path].data_rssi_last); + PRINTM(MINFO, "RSSI Data Average : %d\n", + (int)signal_get[path].data_rssi_avg); + PRINTM(MINFO, "SNR Beacon Last : %d\n", + (int)signal_get[path].bcn_snr_last); + PRINTM(MINFO, "SNR Beacon Average : %d\n", + (int)signal_get[path].bcn_snr_avg); + PRINTM(MINFO, "SNR Data Last : %d\n", + (int)signal_get[path].data_snr_last); + PRINTM(MINFO, "SNR Data Average : %d\n", + (int)signal_get[path].data_snr_avg); + PRINTM(MINFO, "NF Beacon Last : %d\n", + (int)signal_get[path].bcn_nf_last); + PRINTM(MINFO, "NF Beacon Average : %d\n", + (int)signal_get[path].bcn_nf_avg); + PRINTM(MINFO, "NF Data Last : %d\n", + (int)signal_get[path].data_nf_last); + PRINTM(MINFO, "NF Data Average : %d\n", + (int)signal_get[path].data_nf_avg); + out_data[data_len++] = (int)signal_get[path].selector; + out_data[data_len++] = (int)signal_get[path].bcn_rssi_last; + out_data[data_len++] = (int)signal_get[path].bcn_rssi_avg; + out_data[data_len++] = (int)signal_get[path].data_rssi_last; + out_data[data_len++] = (int)signal_get[path].data_rssi_avg; + out_data[data_len++] = (int)signal_get[path].bcn_snr_last; + out_data[data_len++] = (int)signal_get[path].bcn_snr_avg; + out_data[data_len++] = (int)signal_get[path].data_snr_last; + out_data[data_len++] = (int)signal_get[path].data_snr_avg; + out_data[data_len++] = (int)signal_get[path].bcn_nf_last; + out_data[data_len++] = (int)signal_get[path].bcn_nf_avg; + out_data[data_len++] = (int)signal_get[path].data_nf_last; + out_data[data_len++] = (int)signal_get[path].data_nf_avg; + } + moal_memcpy_ext( + priv->phandle, respbuf, out_data, + (MIN((PATH_SIZE * MAX_PATH_NUM), data_len) * sizeof(int)), + respbuflen); + ret = data_len * sizeof(int); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get signalext v2 + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_get_signal_ext_v2(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ +#define PATH_SIZE 13 + int ret = 0; + int data = 0, path = 0, data_len = 0; + int user_data_len = 0, header_len = 0; + int out_data[PATH_SIZE * MAX_PATH_NUM] = {0}; + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info = NULL; + mlan_ds_get_signal signal_get[MAX_PATH_NUM]; + mlan_status status = MLAN_STATUS_SUCCESS; + int path_num; + + ENTER(); + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can not get RSSI in disconnected state\n"); + ret = -ENOTSUPP; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_GET_SIGNAL_EXT_V2); + if (strlen(respbuf) != header_len) { + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + } + + if (user_data_len > 1) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (data < PATH_ALL || data > PATH_AB) { + PRINTM(MERROR, "Wrong arguments\n"); + ret = -EINVAL; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_SIGNAL_EXT; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + info->param.path_id = (t_u16)data; + + /* Send IOCTL request to MLAN */ + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, + "Enable signalextcfg: mlanutl mlanX signalextcfg 1" + " before issuing this command\n"); + ret = -EFAULT; + goto done; + } + path_num = 1; + + if (data == PATH_ALL) { + moal_memcpy_ext(priv->phandle, signal_get, + info->param.signal_ext, sizeof(signal_get), + sizeof(signal_get)); + path_num = MAX_PATH_NUM; + } else + moal_memcpy_ext(priv->phandle, signal_get, + info->param.signal_ext, + sizeof(mlan_ds_get_signal), sizeof(signal_get)); + + PRINTM(MMSG, "data=%d path_num=%d\n", data, path_num); + + for (path = 0; path < path_num; path++) { + if (signal_get[path].selector == PATH_AB) + PRINTM(MINFO, "PATH A+B:\n"); + else if (signal_get[path].selector == PATH_A) + PRINTM(MINFO, "PATH A:\n"); + else if (signal_get[path].selector == PATH_B) + PRINTM(MINFO, "PATH B:\n"); + PRINTM(MINFO, "RSSI Beacon Last : %d\n", + (int)signal_get[path].bcn_rssi_last); + PRINTM(MINFO, "RSSI Beacon Average: %d\n", + (int)signal_get[path].bcn_rssi_avg); + PRINTM(MINFO, "RSSI Data Last : %d\n", + (int)signal_get[path].data_rssi_last); + PRINTM(MINFO, "RSSI Data Average : %d\n", + (int)signal_get[path].data_rssi_avg); + PRINTM(MINFO, "SNR Beacon Last : %d\n", + (int)signal_get[path].bcn_snr_last); + PRINTM(MINFO, "SNR Beacon Average : %d\n", + (int)signal_get[path].bcn_snr_avg); + PRINTM(MINFO, "SNR Data Last : %d\n", + (int)signal_get[path].data_snr_last); + PRINTM(MINFO, "SNR Data Average : %d\n", + (int)signal_get[path].data_snr_avg); + PRINTM(MINFO, "NF Beacon Last : %d\n", + (int)signal_get[path].bcn_nf_last); + PRINTM(MINFO, "NF Beacon Average : %d\n", + (int)signal_get[path].bcn_nf_avg); + PRINTM(MINFO, "NF Data Last : %d\n", + (int)signal_get[path].data_nf_last); + PRINTM(MINFO, "NF Data Average : %d\n", + (int)signal_get[path].data_nf_avg); + out_data[data_len++] = (int)signal_get[path].selector; + out_data[data_len++] = (int)signal_get[path].bcn_rssi_last; + out_data[data_len++] = (int)signal_get[path].bcn_rssi_avg; + out_data[data_len++] = (int)signal_get[path].data_rssi_last; + out_data[data_len++] = (int)signal_get[path].data_rssi_avg; + out_data[data_len++] = (int)signal_get[path].bcn_snr_last; + out_data[data_len++] = (int)signal_get[path].bcn_snr_avg; + out_data[data_len++] = (int)signal_get[path].data_snr_last; + out_data[data_len++] = (int)signal_get[path].data_snr_avg; + out_data[data_len++] = (int)signal_get[path].bcn_nf_last; + out_data[data_len++] = (int)signal_get[path].bcn_nf_avg; + out_data[data_len++] = (int)signal_get[path].data_nf_last; + out_data[data_len++] = (int)signal_get[path].data_nf_avg; + } + moal_memcpy_ext( + priv->phandle, respbuf, out_data, + (MIN((PATH_SIZE * MAX_PATH_NUM), data_len) * sizeof(int)), + respbuflen); + ret = data_len * sizeof(int); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set signalext cfg + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return The result of this processing. + */ +static int woal_priv_signalext_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int enable = 0; + int ret = 0; + int user_data_len = 0, header_len = 0; + ENTER(); + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_SIGNALEXT_CFG); + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &enable, 1, + &user_data_len); + if (user_data_len == 1) { + if (enable != 0x0 && enable != 0x1) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + ret = woal_signal_ext_enable(priv, enable); + } else { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + } +done: + LEAVE(); + return ret; +} +#endif /* #ifdef STA_SUPPORT */ + +#if defined(STA_SUPPORT) +/** + * @brief Make PMF bit required/optional + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return 0 -- success, otherwise fail + */ +int woal_priv_set_get_pmfcfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[2] = {0, 0}; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_ds_misc_pmfcfg *pmfcfg; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv->phandle->card_info->embedded_supp) { + PRINTM(MERROR, "Not supported cmd on this card\n"); + ret = -EOPNOTSUPP; + goto done; + } + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_PMFCFG); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + pmfcfg = (mlan_ds_misc_pmfcfg *)&cfg->param.pmfcfg; + cfg->sub_command = MLAN_OID_MISC_PMFCFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len == 0) + req->action = MLAN_ACT_GET; + else { + pmfcfg->mfpc = (t_u8)data[0]; + pmfcfg->mfpr = (t_u8)data[1]; + req->action = MLAN_ACT_SET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&cfg->param.pmfcfg, + sizeof(mlan_ds_misc_pmfcfg), respbuflen); + ret = sizeof(mlan_ds_misc_pmfcfg); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get/Set inactivity timeout extend + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_inactivity_timeout_ext(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[4]; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pmcfg = NULL; + pmlan_ds_inactivity_to inac_to = NULL; + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_INACTIVITYTO); + memset(data, 0, sizeof(data)); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (user_data_len != 0 && user_data_len != 3 && user_data_len != 4) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pmcfg = (mlan_ds_pm_cfg *)req->pbuf; + inac_to = &pmcfg->param.inactivity_to; + pmcfg->sub_command = MLAN_OID_PM_CFG_INACTIVITY_TO; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (user_data_len) { + inac_to->timeout_unit = data[0]; + inac_to->unicast_timeout = data[1]; + inac_to->mcast_timeout = data[2]; + if (user_data_len == 4) + inac_to->ps_entry_timeout = data[3]; + req->action = MLAN_ACT_SET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } else { + data[0] = inac_to->timeout_unit; + data[1] = inac_to->unicast_timeout; + data[2] = inac_to->mcast_timeout; + data[3] = inac_to->ps_entry_timeout; + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, + sizeof(data), respbuflen); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable amsdu_aggr_ctrl + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_11n_amsdu_aggr_ctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0, data[2] = {0}; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_AMSDU_AGGR_CTRL); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, 1, &user_data_len); + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + cfg_11n->param.amsdu_aggr_ctrl.enable = data[0]; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = cfg_11n->param.amsdu_aggr_ctrl.enable; + data[1] = cfg_11n->param.amsdu_aggr_ctrl.curr_buf_size; + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit beamforming capabilities + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_tx_bf_cap_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *bf_cfg = NULL; + int ret = 0, bf_cap = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_TX_BF_CAP); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; + bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &bf_cap, 1, + &user_data_len); + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + bf_cfg->param.tx_bf_cap = bf_cap; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + bf_cap = bf_cfg->param.tx_bf_cap; + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&bf_cap, sizeof(bf_cap), + respbuflen); + ret = sizeof(bf_cap); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef SDIO +/** + * @brief Turn on/off the sdio clock + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_sdio_clock_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int data = 2; + int user_data_len = 0, header_len = 0; + /* Initialize the clock state as on */ + static int clock_state = 1; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_SDIO_CLOCK); + if (strlen(respbuf) == header_len) { + /* GET operation */ + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&clock_state, + sizeof(clock_state), respbuflen); + ret = sizeof(clock_state); + goto done; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, &user_data_len); + + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + } + switch (data) { + case CMD_DISABLED: + PRINTM(MINFO, "SDIO clock is turned off\n"); + ret = woal_sdio_set_bus_clock(priv->phandle, MFALSE); + clock_state = data; + break; + case CMD_ENABLED: + PRINTM(MINFO, "SDIO clock is turned on\n"); + ret = woal_sdio_set_bus_clock(priv->phandle, MTRUE); + clock_state = data; + break; + default: + ret = -EINVAL; + PRINTM(MINFO, "sdioclock: wrong parameter\n"); + break; + } +done: + LEAVE(); + return ret; +} +#endif + +#ifdef SDIO +/** + * @brief Set SDIO Multi-point aggregation control parameters + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_sdio_mpa_ctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0, data[6]; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_MPA_CTRL); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len > 6) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_SDIO_MPA_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + /* Get the values first, then modify these values if + * user had modified them */ + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); + ret = -EFAULT; + goto done; + } + + if (user_data_len == 0) { + data[0] = misc->param.mpa_ctrl.tx_enable; + data[1] = misc->param.mpa_ctrl.rx_enable; + data[2] = misc->param.mpa_ctrl.tx_buf_size; + data[3] = misc->param.mpa_ctrl.rx_buf_size; + data[4] = misc->param.mpa_ctrl.tx_max_ports; + data[5] = misc->param.mpa_ctrl.rx_max_ports; + + PRINTM(MINFO, "Get Param: %d %d %d %d %d %d\n", data[0], + data[1], data[2], data[3], data[4], data[5]); + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, + sizeof(data), respbuflen); + ret = sizeof(data); + goto done; + } + + switch (user_data_len) { + case 6: + misc->param.mpa_ctrl.rx_max_ports = data[5]; + /* fall through */ + case 5: + misc->param.mpa_ctrl.tx_max_ports = data[4]; + /* fall through */ + case 4: + misc->param.mpa_ctrl.rx_buf_size = data[3]; + /* fall through */ + case 3: + misc->param.mpa_ctrl.tx_buf_size = data[2]; + /* fall through */ + case 2: + misc->param.mpa_ctrl.rx_enable = data[1]; + /* fall through */ + case 1: + /* Set cmd */ + req->action = MLAN_ACT_SET; + + PRINTM(MINFO, "Set Param: %d %d %d %d %d %d\n", data[0], + data[1], data[2], data[3], data[4], data[5]); + + misc->param.mpa_ctrl.tx_enable = data[0]; + break; + default: + PRINTM(MERROR, "Default case error\n"); + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Configure sleep parameters + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_sleep_params_ioctl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm = NULL; + mlan_ds_sleep_params *psleep_params = NULL; + int data[6] = {0}, i; + int user_data_len = 0, header_len = 0; +#ifdef DEBUG_LEVEL1 + char err_str[][36] = {{"sleep clock error in ppm"}, + {"wakeup offset in usec"}, + {"clock stabilization time in usec"}, + {"control periodic calibration(0-2)"}, + {"control of external sleepClock(0-2)"}, + {"value of reserved for debug"}}; +#endif + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm = (mlan_ds_pm_cfg *)req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_SLEEP_PARAMS; + req->req_id = MLAN_IOCTL_PM_CFG; + psleep_params = (pmlan_ds_sleep_params)&pm->param.sleep_params; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_SLEEP_PARAMS); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != 6) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } +#define MIN_VAL 0x0000 +#define MAX_VAL 0xFFFF + for (i = 0; i < 6; i++) { + if ((i == 3) || (i == 4)) { + /* These two cases are handled below the loop */ + continue; + } + if (data[i] < MIN_VAL || data[i] > MAX_VAL) { + PRINTM(MERROR, "Invalid %s (0-65535)!\n", + err_str[i]); + ret = -EINVAL; + goto done; + } + } + if (data[3] < 0 || data[3] > 2) { + PRINTM(MERROR, + "Invalid control periodic calibration (0-2)!\n"); + ret = -EINVAL; + goto done; + } + if (data[4] < 0 || data[4] > 2) { + PRINTM(MERROR, + "Invalid control of external sleep clock (0-2)!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + psleep_params->error = data[0]; + psleep_params->offset = data[1]; + psleep_params->stable_time = data[2]; + psleep_params->cal_control = data[3]; + psleep_params->ext_sleep_clk = data[4]; + psleep_params->reserved = data[5]; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = psleep_params->error; + data[1] = psleep_params->offset; + data[2] = psleep_params->stable_time; + data[3] = psleep_params->cal_control; + data[4] = psleep_params->ext_sleep_clk; + data[5] = psleep_params->reserved; + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get DFS Testing settings + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_dfs_testing(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + int ret = 0; + int data[5] = {0}; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DFS_TESTING); + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + ds_11hcfg->sub_command = MLAN_OID_11H_DFS_TESTING; + req->req_id = MLAN_IOCTL_11H_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != 5) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if ((unsigned)data[0] > 1800) { + PRINTM(MERROR, + "The maximum user CAC is 1800 seconds (30 mins).\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; + } + if ((unsigned)data[4] != 0 && ((unsigned)data[4] != 1)) { + PRINTM(MERROR, "CAC restart should be 0/1\n"); + ret = -EINVAL; + goto done; + } + + ds_11hcfg->param.dfs_testing.usr_cac_period_msec = + (t_u32)data[0] * 1000; + 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]; + ds_11hcfg->param.dfs_testing.usr_cac_restart = (t_u8)data[4]; + priv->phandle->cac_restart = (t_u8)data[4]; + priv->phandle->cac_period_jiffies = (t_u32)data[0] * HZ / 1000; + priv->phandle->usr_nop_period_sec = (t_u16)data[1]; + req->action = MLAN_ACT_SET; +#ifdef UAP_SUPPORT + priv->user_cac_period_msec = + ds_11hcfg->param.dfs_testing.usr_cac_period_msec; +#endif + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + data[0] = + ds_11hcfg->param.dfs_testing.usr_cac_period_msec / 1000; + 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; + data[4] = ds_11hcfg->param.dfs_testing.usr_cac_restart; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, + sizeof(data), respbuflen); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get DFS W53 settings + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_dfs53cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + int ret = 0; + int data = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DFS53_CFG); + /* 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_W53_CFG; + req->req_id = MLAN_IOCTL_11H_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args !\n"); + ret = -EINVAL; + goto done; + } + if (data > DFS_W53_OLD) { + PRINTM(MERROR, "Invalid config !\n"); + ret = -EINVAL; + goto done; + } + + ds_11hcfg->param.dfs_w53_cfg.dfs53cfg = (t_u8)data; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + moal_memcpy_ext(priv->phandle, respbuf, + (t_u8 *)&ds_11hcfg->param.dfs_w53_cfg.dfs53cfg, + sizeof(ds_11hcfg->param.dfs_w53_cfg.dfs53cfg), + respbuflen); + ret = sizeof(t_u8); + } + + PRINTM(MIOCTL, "dfs w53 cfg %d\n", + ds_11hcfg->param.dfs_w53_cfg.dfs53cfg); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief determine the center frquency center index for bandwidth + * of 80 MHz and 160 MHz + * + ** @param priv Pointer to moal_private structure + * @param band band + * @param pri_chan primary channel + * @param chan_bw channel bandwidth + * + * @return channel center frequency center, if found; O, otherwise + */ + +t_u8 woal_get_center_freq_idx(moal_private *priv, t_u8 band, t_u32 pri_chan, + t_u8 chan_bw) +{ + t_u8 center_freq_idx = 0; + + if (band & BAND_AAC) { + switch (pri_chan) { + case 36: + case 40: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 38; + break; + } + /* fall through */ + case 44: + case 48: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 46; + break; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 42; + break; + } + /* fall through */ + case 52: + case 56: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 54; + break; + } + /* fall through */ + case 60: + case 64: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 62; + break; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 58; + break; + } else if (chan_bw == CHANNEL_BW_160MHZ) { + center_freq_idx = 50; + break; + } + /* fall through */ + case 68: + case 72: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 70; + break; + } + /* fall through */ + case 76: + case 80: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 78; + break; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 74; + break; + } + /* fall through */ + case 84: + case 88: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 86; + break; + } + /* fall through */ + case 92: + case 96: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 94; + break; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 90; + break; + } + /* fall through */ + case 100: + case 104: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 102; + break; + } + /* fall through */ + case 108: + case 112: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 110; + break; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 106; + break; + } + /* fall through */ + case 116: + case 120: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 118; + break; + } + /* fall through */ + case 124: + case 128: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 126; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 122; + } else if (chan_bw == CHANNEL_BW_160MHZ) { + center_freq_idx = 114; + } + break; + case 132: + case 136: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 134; + break; + } + /* fall through */ + case 140: + case 144: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 126; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 138; + } + break; + case 149: + case 153: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 151; + break; + } + /* fall through */ + case 157: + case 161: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 159; + break; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 155; + break; + } + /* fall through */ + case 165: + case 169: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 167; + break; + } + /* fall through */ + case 173: + case 177: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 175; + break; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 171; + break; + } + /* fall through */ + case 184: + case 188: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 186; + break; + } + /* fall through */ + case 192: + case 196: + if (chan_bw == CHANNEL_BW_40MHZ_ABOVE || + chan_bw == CHANNEL_BW_40MHZ_BELOW) { + center_freq_idx = 194; + break; + } else if (chan_bw == CHANNEL_BW_80MHZ) { + center_freq_idx = 190; + break; + } + /* fall through */ + default: /* error. go to the default */ + center_freq_idx = 42; + } + } + return center_freq_idx; +} + +/** + * @brief determine the center frquency center index for bandwidth + * of 80 MHz and 160 MHz + * + ** @param priv Pointer to moal_private structure + * @param block_tx 0-no need block traffic 1- need block traffic + * @param oper_class oper_class + * @param channel channel + * @param switch count how many csa/ecsa beacon will send out + * @param band_width 1-40Mhz above, 3-40Mhz below, 4-80Mhz, 5-160Mhz + * @param ecsa MTRUE/MFALSE; + * + * @return channel center frequency center, if found; O, otherwise + */ + +static int woal_channel_switch(moal_private *priv, t_u8 block_tx, + t_u8 oper_class, t_u8 channel, t_u8 switch_count, + t_u8 band_width, t_u8 ecsa) +{ + IEEEtypes_ExtChanSwitchAnn_t *ext_chan_switch = NULL; + IEEEtypes_ChanSwitchAnn_t *chan_switch = NULL; + custom_ie *pcust_chansw_ie = NULL; + t_u8 center_freq_idx = 0; + IEEEtypes_Header_t *pChanSwWrap_ie = NULL; + IEEEtypes_WideBWChanSwitch_t *pbwchansw_ie = NULL; + IEEEtypes_VhtTpcEnvelope_t *pvhttpcEnv_ie = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u8 bw; + t_u8 new_oper_class = oper_class; + int ret = 0; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + misc->param.cust_ie.type = TLV_TYPE_MGMT_IE; + misc->param.cust_ie.len = (sizeof(custom_ie) - MAX_IE_SIZE); + + pcust_chansw_ie = (custom_ie *)&misc->param.cust_ie.ie_data_list[0]; + pcust_chansw_ie->ie_index = 0xffff; /*Auto index */ + pcust_chansw_ie->ie_length = sizeof(IEEEtypes_ChanSwitchAnn_t); + pcust_chansw_ie->mgmt_subtype_mask = + MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP; /*Add IE for + BEACON/probe resp*/ + chan_switch = (IEEEtypes_ChanSwitchAnn_t *)pcust_chansw_ie->ie_buffer; + chan_switch->element_id = CHANNEL_SWITCH_ANN; + chan_switch->len = 3; + chan_switch->chan_switch_mode = block_tx; + chan_switch->new_channel_num = channel; + chan_switch->chan_switch_count = switch_count; + DBG_HEXDUMP(MCMD_D, "CSA IE", (t_u8 *)pcust_chansw_ie->ie_buffer, + pcust_chansw_ie->ie_length); + switch (band_width) { + case CHANNEL_BW_40MHZ_ABOVE: + case CHANNEL_BW_40MHZ_BELOW: + bw = 40; + break; + case CHANNEL_BW_80MHZ: + bw = 80; + break; + case CHANNEL_BW_160MHZ: + bw = 160; + break; + default: + bw = 20; + break; + } + if (!new_oper_class && ecsa) + woal_priv_get_nonglobal_operclass_by_bw_channel( + priv, bw, channel, &new_oper_class); + if (new_oper_class) { + pcust_chansw_ie->ie_length += + sizeof(IEEEtypes_ExtChanSwitchAnn_t); + ext_chan_switch = + (IEEEtypes_ExtChanSwitchAnn_t + *)(pcust_chansw_ie->ie_buffer + + sizeof(IEEEtypes_ChanSwitchAnn_t)); + ext_chan_switch->element_id = EXTEND_CHANNEL_SWITCH_ANN; + ext_chan_switch->len = 4; + ext_chan_switch->chan_switch_mode = block_tx; + ext_chan_switch->new_oper_class = new_oper_class; + ext_chan_switch->new_channel_num = channel; + ext_chan_switch->chan_switch_count = switch_count; + DBG_HEXDUMP(MCMD_D, "ECSA IE", + (t_u8 *)(pcust_chansw_ie->ie_buffer + + sizeof(IEEEtypes_ChanSwitchAnn_t)), + pcust_chansw_ie->ie_length - + sizeof(IEEEtypes_ChanSwitchAnn_t)); + } + /* bandwidth 40/80/160 should set channel switch wrapper ie for 11ac 5G + * channel*/ + if (band_width && channel > 14) { + pChanSwWrap_ie = + (IEEEtypes_Header_t *)(pcust_chansw_ie->ie_buffer + + pcust_chansw_ie->ie_length); + pChanSwWrap_ie->element_id = EXT_POWER_CONSTR; + pChanSwWrap_ie->len = sizeof(IEEEtypes_WideBWChanSwitch_t); + + pbwchansw_ie = (IEEEtypes_WideBWChanSwitch_t + *)((t_u8 *)pChanSwWrap_ie + + sizeof(IEEEtypes_Header_t)); + pbwchansw_ie->ieee_hdr.element_id = BW_CHANNEL_SWITCH; + pbwchansw_ie->ieee_hdr.len = + sizeof(IEEEtypes_WideBWChanSwitch_t) - + sizeof(IEEEtypes_Header_t); + + center_freq_idx = woal_get_center_freq_idx(priv, BAND_AAC, + channel, band_width); + if (band_width == CHANNEL_BW_40MHZ_ABOVE || + band_width == CHANNEL_BW_40MHZ_BELOW) { + pbwchansw_ie->new_channel_width = 0; + pbwchansw_ie->new_channel_center_freq0 = + center_freq_idx; + } else if (band_width == CHANNEL_BW_80MHZ) { + pbwchansw_ie->new_channel_width = 1; + pbwchansw_ie->new_channel_center_freq0 = + center_freq_idx - 4; + pbwchansw_ie->new_channel_center_freq1 = + center_freq_idx + 4; + } else if (band_width == CHANNEL_BW_160MHZ) { + pbwchansw_ie->new_channel_width = 2; + pbwchansw_ie->new_channel_center_freq0 = + center_freq_idx - 8; + pbwchansw_ie->new_channel_center_freq1 = + center_freq_idx + 8; + } else + PRINTM(MERROR, + "Invalid bandwidth.Support value 1/3/4/5 for 40+/40-/80/160MHZ\n"); + + /*prepare the VHT Transmit Power Envelope IE*/ + pvhttpcEnv_ie = + (IEEEtypes_VhtTpcEnvelope_t + *)((t_u8 *)pChanSwWrap_ie + + sizeof(IEEEtypes_Header_t) + + sizeof(IEEEtypes_WideBWChanSwitch_t)); + pvhttpcEnv_ie->ieee_hdr.element_id = VHT_TX_POWER_ENV; + pvhttpcEnv_ie->ieee_hdr.len = + sizeof(IEEEtypes_VhtTpcEnvelope_t) - + sizeof(IEEEtypes_Header_t); + /* Local Max TX Power Count= 3, + * Local TX Power Unit Inter=EIP(0) */ + pvhttpcEnv_ie->tpc_info = 3; + pvhttpcEnv_ie->local_max_tp_20mhz = 0xff; + pvhttpcEnv_ie->local_max_tp_40mhz = 0xff; + pvhttpcEnv_ie->local_max_tp_80mhz = 0xff; + pChanSwWrap_ie->len += sizeof(IEEEtypes_VhtTpcEnvelope_t); + pcust_chansw_ie->ie_length += + pChanSwWrap_ie->len + sizeof(IEEEtypes_Header_t); + DBG_HEXDUMP(MCMD_D, "Channel switch wrapper IE", + (t_u8 *)pChanSwWrap_ie, + pChanSwWrap_ie->len + sizeof(IEEEtypes_Header_t)); + } + if (block_tx) { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + woal_stop_queue(priv->netdev); + priv->uap_tx_blocked = MTRUE; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to set ECSA IE\n"); + ret = -EFAULT; + goto done; + } + + priv->phandle->chsw_wait_q_woken = MFALSE; + /* wait for channel switch to complete */ + wait_event_interruptible_timeout( + priv->phandle->chsw_wait_q, priv->phandle->chsw_wait_q_woken, + (u32)HZ * (switch_count + 2) * 110 / 1000); + + pcust_chansw_ie->ie_index = 0xffff; /*Auto index */ + pcust_chansw_ie->mgmt_subtype_mask = 0; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to clear ECSA IE\n"); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get CFP table codes + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_cfp_code(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0; + int data[2] = {0}; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ds_misc_cfp_code *cfp_code = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_CFP_CODE); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfp_code = &misc_cfg->param.cfp_code; + misc_cfg->sub_command = MLAN_OID_MISC_CFP_CODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + cfp_code->cfp_code_bg = data[0]; + if (user_data_len == 2) + cfp_code->cfp_code_a = data[1]; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + data[0] = cfp_code->cfp_code_bg; + data[1] = cfp_code->cfp_code_a; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, + sizeof(data), respbuflen); + ret = sizeof(data); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Tx/Rx antenna + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_set_get_tx_rx_ant(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + int data[3] = {0}; + mlan_status status = MLAN_STATUS_SUCCESS; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + struct wiphy *wiphy = priv->phandle->wiphy; +#endif + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_ANT_CFG); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if (priv->phandle->feature_control & FEATURE_CTRL_STREAM_2X2) { + radio->param.ant_cfg.tx_antenna = data[0]; + radio->param.ant_cfg.rx_antenna = data[0]; + if (user_data_len == 2) + radio->param.ant_cfg.rx_antenna = data[1]; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_CARD9098(priv->phandle->card_type) || + IS_CARD9097(priv->phandle->card_type)) { + if (IS_STA_OR_UAP_CFG80211( + priv->phandle->params + .cfg80211_wext) && + wiphy) { + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + if (((radio->param.ant_cfg + .tx_antenna & + 0xFF) != 3 && + (radio->param.ant_cfg + .tx_antenna & + 0xFF) != 0) || + ((radio->param.ant_cfg + .rx_antenna & + 0xFF) != 3 && + (radio->param.ant_cfg + .rx_antenna & + 0xFF) != 0)) + wiphy->bands[IEEE80211_BAND_2GHZ] + ->ht_cap.mcs + .rx_mask[1] = 0; + else if ((radio->param.ant_cfg + .tx_antenna & + 0xFF) == 3 || + (radio->param.ant_cfg + .rx_antenna & + 0xFF) == 3) + wiphy->bands[IEEE80211_BAND_2GHZ] + ->ht_cap.mcs + .rx_mask[1] = + 0xff; + wiphy->bands[IEEE80211_BAND_2GHZ] + ->ht_cap.mcs.rx_mask[4] = + 0; + } + if (wiphy->bands[IEEE80211_BAND_5GHZ]) { + if (((radio->param.ant_cfg + .tx_antenna & + 0xFF00) != 0x300 && + (radio->param.ant_cfg + .tx_antenna & + 0xFF00) != 0) || + ((radio->param.ant_cfg + .rx_antenna & + 0xFF00) != 0x300 && + (radio->param.ant_cfg + .rx_antenna & + 0xFF00) != 0)) { + wiphy->bands[IEEE80211_BAND_5GHZ] + ->ht_cap.mcs + .rx_mask[1] = 0; + wiphy->bands[IEEE80211_BAND_5GHZ] + ->vht_cap + .vht_mcs + .rx_mcs_map = + 0xfffe; + wiphy->bands[IEEE80211_BAND_5GHZ] + ->vht_cap + .vht_mcs + .tx_mcs_map = + 0xfffe; + } else if ((radio->param.ant_cfg + .tx_antenna & + 0xFF00) == 0x300 || + (radio->param.ant_cfg + .rx_antenna & + 0xFF00) == 0x300) { + wiphy->bands[IEEE80211_BAND_5GHZ] + ->ht_cap.mcs + .rx_mask[1] = + 0xff; + wiphy->bands[IEEE80211_BAND_5GHZ] + ->vht_cap + .vht_mcs + .rx_mcs_map = + 0xfffa; + wiphy->bands[IEEE80211_BAND_5GHZ] + ->vht_cap + .vht_mcs + .tx_mcs_map = + 0xfffa; + } + } + } + } +#endif + } else { + radio->param.ant_cfg_1x1.antenna = data[0]; + if (user_data_len == 2) + radio->param.ant_cfg_1x1.evaluate_time = + data[1]; + } + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + if (priv->phandle->feature_control & FEATURE_CTRL_STREAM_2X2) { + data[0] = radio->param.ant_cfg.tx_antenna; + data[1] = radio->param.ant_cfg.rx_antenna; + if (data[0] && data[1]) + ret = sizeof(int) * 2; + else + ret = sizeof(int) * 1; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, + sizeof(data), respbuflen); + } else { + data[0] = (int)radio->param.ant_cfg_1x1.antenna; + data[1] = (int)radio->param.ant_cfg_1x1.evaluate_time; + data[2] = (int)radio->param.ant_cfg_1x1.current_antenna; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, + sizeof(data), respbuflen); + ret = sizeof(data); + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/* + * @brief Set/Get CWMode + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_set_get_cwmode(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_cw_mode_ctrl *cwmode; + int ret = 0; + int header_len = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CWMODE_CTRL; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_CWMODE); + if (strlen(respbuf) == header_len) { + /* GET operation */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + ioctl_req->action = MLAN_ACT_SET; + + cwmode = (mlan_ds_cw_mode_ctrl *)(respbuf + header_len + + sizeof(t_u8)); + misc->param.cwmode.mode = cwmode->mode; + misc->param.cwmode.txPower = cwmode->txPower; + misc->param.cwmode.rateInfo = cwmode->rateInfo; + misc->param.cwmode.channel = cwmode->channel; + misc->param.cwmode.chanInfo = cwmode->chanInfo; + misc->param.cwmode.pktLength = cwmode->pktLength; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&misc->param.cwmode, + sizeof(misc->param.cwmode), respbuflen); + ret = sizeof(misc->param.cwmode); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get out band independent reset + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_ind_rst_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + int data[2] = {0}; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_IND_RST_CFG); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + memset(misc, 0, sizeof(mlan_ds_misc_cfg)); + misc->sub_command = MLAN_OID_MISC_IND_RST_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + + if ((user_data_len == 1) || (user_data_len == 2)) { + req->action = MLAN_ACT_SET; + + /* ir_mode */ + if (data[0] < 0 || data[0] > 2) { + PRINTM(MERROR, "Invalid ir mode parameter!\n"); + ret = -EINVAL; + goto done; + } + misc->param.ind_rst_cfg.ir_mode = data[0]; + + /* gpio_pin */ + if (user_data_len == 2) { + if ((data[1] != 0xFF) && (data[1] < 0)) { + PRINTM(MERROR, + "Invalid gpio pin no!\n"); + ret = -EINVAL; + goto done; + } + misc->param.ind_rst_cfg.gpio_pin = data[1]; + } + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = (int)misc->param.ind_rst_cfg.ir_mode; + data[1] = (int)misc->param.ind_rst_cfg.gpio_pin; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get/Set system clock + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_sysclock(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int data[65]; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int ret = 0, i = 0; + int user_data_len = 0, header_len = 0; + int data_length = 0, length_index = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_SYSCLOCK); + memset(data, 0, sizeof(data)); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + } + + if (user_data_len > MLAN_MAX_CLK_NUM) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_SYS_CLOCK; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (user_data_len) { + /* SET operation */ + req->action = MLAN_ACT_SET; + + /* Set configurable clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; + cfg->param.sys_clock.sys_clk_num = + MIN(MLAN_MAX_CLK_NUM, user_data_len); + for (i = 0; i < cfg->param.sys_clock.sys_clk_num; i++) + cfg->param.sys_clock.sys_clk[i] = (t_u16)data[i]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } else { + /* GET operation */ + req->action = MLAN_ACT_GET; + + /* Get configurable clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Current system clock */ + data[1] = (int)cfg->param.sys_clock.cur_sys_clk; + data_length = 1; + + length_index = + MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); + + /* Configurable clocks */ + for (i = 1; i <= length_index; i++) + data[i + data_length] = + (int)cfg->param.sys_clock.sys_clk[i - 1]; + + data_length += length_index; + + /* Get supported clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_SUPPORTED; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + length_index = + MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); + + /* Supported clocks */ + for (i = 1; i <= length_index; i++) + data[i + data_length] = + (int)cfg->param.sys_clock.sys_clk[i - 1]; + + data_length += length_index; + + /* Send length as first element */ + data[0] = data_length; + data_length++; + + moal_memcpy_ext(priv->phandle, respbuf, data, + sizeof(int) * data_length, respbuflen); + ret = data_length * sizeof(int); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get GTK/PTK + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_get_key(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0, copy_len = 0; + int header_len = 0; + unsigned int i; + t_u8 key_ascii[256]; + t_u8 *tmp; + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_GET_KEY); + if (strlen(respbuf) != header_len) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + memset(key_ascii, 0x00, sizeof(key_ascii)); + tmp = key_ascii; + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can't get key in un-associated state\n"); + ret = -EFAULT; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Get Unicast 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_QUERY_KEY; + sec->param.encrypt_key.key_index = 0; + sec->param.encrypt_key.key_flags = 0; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (sec->param.encrypt_key.key_len) { + sprintf((char *)tmp, "\n%s", "PTK: "); + tmp += 5; + for (i = 0; i < sec->param.encrypt_key.key_len; i++) + tmp += sprintf((char *)tmp, "%02x", + sec->param.encrypt_key.key_material[i]); + } + + /* Get Multicase 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_QUERY_KEY; + sec->param.encrypt_key.key_index = 0; + sec->param.encrypt_key.key_flags = KEY_FLAG_GROUP_KEY; + memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (sec->param.encrypt_key.key_len) { + sprintf((char *)tmp, "\n%s", "GTK: "); + tmp += 5; + for (i = 0; i < sec->param.encrypt_key.key_len; i++) + tmp += sprintf((char *)tmp, "%02x", + sec->param.encrypt_key.key_material[i]); + } + + /* Get IGTK 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_QUERY_KEY; + sec->param.encrypt_key.key_index = 0; + sec->param.encrypt_key.key_flags = KEY_FLAG_AES_MCAST_IGTK; + memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (sec->param.encrypt_key.key_len) { + sprintf((char *)tmp, "\n%s", "IGTK: "); + tmp += 6; + for (i = 0; i < sec->param.encrypt_key.key_len; i++) + tmp += sprintf((char *)tmp, "%02x", + sec->param.encrypt_key.key_material[i]); + } + + copy_len = tmp - key_ascii; + moal_memcpy_ext(priv->phandle, respbuf, &key_ascii, copy_len, + respbuflen); + ret = copy_len; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Associate to a specific indexed entry in the ScanTable + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_associate_ssid_bssid(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0, copy_len = 0; + int header_len = 0; + mlan_ssid_bssid ssid_bssid; +#ifdef REASSOCIATION + mlan_bss_info bss_info; +#endif + char buf[64]; + t_u8 buflen; + t_u8 mac_idx; + t_u8 i; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_ASSOCIATE); + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + copy_len = strlen(respbuf) - header_len; + mac_idx = 0; + buflen = MIN(copy_len, (sizeof(buf) - 1)); + memset(buf, 0, sizeof(buf)); + memset(&ssid_bssid, 0, sizeof(ssid_bssid)); + + if (buflen < (3 * ETH_ALEN) + 2) { + PRINTM(MERROR, + "Associate: Insufficient length in IOCTL input\n"); + + /* buffer should be at least 3 characters per BSSID octet "00:" + ** plus a space separater and at least 1 char in the SSID + */ + ret = -EINVAL; + goto done; + } + + moal_memcpy_ext(priv->phandle, buf, respbuf + header_len, buflen, + sizeof(buf)); + + /* Skip white space */ + for (i = 0; (i < buflen) && (buf[i] == ' '); i++) + ; + + /* Copy/Convert the BSSID */ + for (; (i < buflen) && (mac_idx < ETH_ALEN) && (buf[i] != ' '); i++) { + if (buf[i] == ':') { + mac_idx++; + } else { + ssid_bssid.bssid[mac_idx] = (t_u8)woal_atox(buf + i); + + while (((i < buflen) && isxdigit(buf[i + 1]))) + /* Skip entire hex value */ + i++; + } + } + + /* Skip one space between the BSSID and start of the SSID */ + i++; + + /* Copy the SSID */ + ssid_bssid.ssid.ssid_len = buflen - i; + moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid, buf + i, + sizeof(ssid_bssid.ssid.ssid), + sizeof(ssid_bssid.ssid.ssid)); + + PRINTM(MCMND, "iwpriv assoc: AP=[" MACSTR "], ssid(%d)=[%s]\n", + MAC2STR(ssid_bssid.bssid), (int)ssid_bssid.ssid.ssid_len, + ssid_bssid.ssid.ssid); + + if (MLAN_STATUS_SUCCESS != + woal_bss_start(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) { + ret = -EFAULT; + goto done; + } + +#ifdef REASSOCIATION + memset(&bss_info, 0x00, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS == + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.ssid, + &bss_info.ssid, sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.bssid, + &bss_info.bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(mlan_802_11_mac_addr)); + } +#endif /* REASSOCIATION */ + +done: + LEAVE(); + return 0; +} + +/* Maximum input output characters in group WOAL_SET_GET_256_CHAR */ +#define MAX_IN_OUT_CHAR 256 +/** Tx BF Global conf argument index */ +#define BF_ENABLE_PARAM 1 +#define SOUND_ENABLE_PARAM 2 +#define FB_TYPE_PARAM 3 +#define SNR_THRESHOLD_PARAM 4 +#define SOUND_INTVL_PARAM 5 +#define BF_MODE_PARAM 6 +#define MAX_TX_BF_GLOBAL_ARGS 6 +#define BF_CFG_ACT_GET 0 +#define BF_CFG_ACT_SET 1 + +/** + * @brief Set/Get Transmit beamforming configuration + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_tx_bf_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int header_len = 0; + int ret = 0, copy_len = 0; + int bf_action = 0, interval = 0; + int snr = 0, i, tmp_val = 0; + t_u8 buf[MAX_IN_OUT_CHAR], char_count = 0; + t_u8 *str, *token, *pos; + t_u16 action = 0; + + mlan_ds_11n_tx_bf_cfg bf_cfg; + mlan_trigger_sound_args *bf_sound = NULL; + mlan_tx_bf_peer_args *tx_bf_peer = NULL; + mlan_snr_thr_args *bf_snr = NULL; + mlan_bf_periodicity_args *bf_periodicity = NULL; + mlan_bf_global_cfg_args *bf_global = NULL; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_TX_BF_CFG); + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + memset(&bf_cfg, 0, sizeof(bf_cfg)); + /* Pointer to corresponding buffer */ + bf_sound = bf_cfg.body.bf_sound; + tx_bf_peer = bf_cfg.body.tx_bf_peer; + bf_snr = bf_cfg.body.bf_snr; + bf_periodicity = bf_cfg.body.bf_periodicity; + bf_global = &bf_cfg.body.bf_global_cfg; + + /* Total characters in buffer */ + char_count = strlen(respbuf) - header_len; + copy_len = char_count; + memset(buf, 0, sizeof(buf)); + if (char_count) { + if (copy_len > sizeof(buf)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + moal_memcpy_ext(priv->phandle, buf, respbuf + header_len, + copy_len, sizeof(buf)); + + if (char_count > 1 && buf[1] != ';') { + PRINTM(MERROR, + "No action argument. Separate with ';'\n"); + ret = -EINVAL; + goto done; + } + /* Replace ';' with NULL in the string to separate args */ + for (i = 0; i < char_count; i++) { + if (buf[i] == ';') + buf[i] = '\0'; + } + /* The first byte represents the beamforming action */ + if (woal_atoi(&bf_action, &buf[0]) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + switch (bf_action) { + case BF_GLOBAL_CONFIGURATION: + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + /* Eliminate action field */ + token = &buf[2]; + for (i = 1, str = &buf[2]; token != NULL; i++) { + token = strstr(str, " "); + pos = str; + if (token != NULL) { + *token = '\0'; + str = token + 1; + } + woal_atoi(&tmp_val, pos); + switch (i) { + case BF_ENABLE_PARAM: + bf_global->bf_enbl = + (t_u8)tmp_val; + break; + case SOUND_ENABLE_PARAM: + bf_global->sounding_enbl = + (t_u8)tmp_val; + break; + case FB_TYPE_PARAM: + bf_global->fb_type = + (t_u8)tmp_val; + break; + case SNR_THRESHOLD_PARAM: + bf_global->snr_threshold = + (t_u8)tmp_val; + break; + case SOUND_INTVL_PARAM: + bf_global->sounding_interval = + (t_u16)tmp_val; + break; + case BF_MODE_PARAM: + bf_global->bf_mode = + (t_u8)tmp_val; + break; + default: + PRINTM(MERROR, + "Invalid Argument\n"); + ret = -EINVAL; + goto done; + } + } + } + break; + case TRIGGER_SOUNDING_FOR_PEER: + /* First arg = 2 BfAction + * Second arg = 17 MAC "00:50:43:20:BF:64" */ + if (char_count != 19) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + woal_mac2u8(bf_sound->peer_mac, &buf[2]); + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + break; + case SET_GET_BF_PERIODICITY: + /* First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 1 (min char) TX BF interval + * 10 (max char) u32 maximum value + * 4294967295 */ + if (char_count < 19 || char_count > 30) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + + woal_mac2u8(bf_periodicity->peer_mac, &buf[2]); + if (char_count == 19) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + if (woal_atoi(&interval, &buf[20]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + bf_periodicity->interval = interval; + } + break; + case TX_BF_FOR_PEER_ENBL: + /* Handle only SET operation here + * First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 2 enable/disable bf + * Fourth arg = 2 enable/disable sounding + * Fifth arg = 1 FB Type */ + if (char_count != 25 && char_count != 1) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + woal_mac2u8(tx_bf_peer->peer_mac, &buf[2]); + woal_atoi(&tmp_val, &buf[20]); + tx_bf_peer->bf_enbl = (t_u8)tmp_val; + woal_atoi(&tmp_val, &buf[22]); + tx_bf_peer->sounding_enbl = (t_u8)tmp_val; + woal_atoi(&tmp_val, &buf[24]); + tx_bf_peer->fb_type = (t_u8)tmp_val; + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + } + break; + case SET_SNR_THR_PEER: + /* First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 1/2 SNR u8 - can be 1/2 charerters */ + if (char_count != 1 && + !(char_count == 21 || char_count == 22)) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + woal_mac2u8(bf_snr->peer_mac, &buf[2]); + if (woal_atoi(&snr, &buf[20]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + bf_snr->snr = snr; + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + } + break; + default: + ret = -EINVAL; + goto done; + } + + /* Save the value */ + bf_cfg.bf_action = bf_action; + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_bf_cfg(priv, action, &bf_cfg)) { + ret = -EFAULT; + goto done; + } + } else { + ret = -EINVAL; + goto done; + } + + switch (bf_action) { + case BF_GLOBAL_CONFIGURATION: + moal_memcpy_ext(priv->phandle, respbuf, bf_global, + sizeof(mlan_bf_global_cfg_args), respbuflen); + ret = sizeof(mlan_bf_global_cfg_args); + break; + case TRIGGER_SOUNDING_FOR_PEER: + moal_memcpy_ext(priv->phandle, respbuf, bf_sound, + sizeof(mlan_bf_global_cfg_args), respbuflen); + ret = sizeof(mlan_bf_global_cfg_args); + break; + case SET_GET_BF_PERIODICITY: + moal_memcpy_ext(priv->phandle, respbuf, bf_periodicity, + sizeof(mlan_bf_periodicity_args), respbuflen); + ret = sizeof(mlan_bf_periodicity_args); + break; + case TX_BF_FOR_PEER_ENBL: + moal_memcpy_ext(priv->phandle, respbuf, tx_bf_peer, + sizeof(mlan_tx_bf_peer_args), respbuflen); + ret = sizeof(mlan_tx_bf_peer_args); + break; + case SET_SNR_THR_PEER: + moal_memcpy_ext(priv->phandle, respbuf, bf_snr, + sizeof(mlan_snr_thr_args), respbuflen); + ret = sizeof(mlan_snr_thr_args); + break; + default: + ret = 0; + } + +done: + LEAVE(); + return ret; +} + +#ifdef SDIO +/** + * @brief Cmd53 read/write register + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_cmd53rdwr(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int header_len = 0; + int ret = 0; + t_u8 *buf = NULL; + t_u8 *data = NULL; + t_u8 rw, func, mode; + t_u16 blklen = 0, blknum = 0; + int reg = 0; + t_u32 pattern_len = 0, total_len = 0; + t_u16 cmd_len; + gfp_t flag; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_SD_CMD53_RW); + + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + data = kzalloc(WOAL_2K_BYTES, flag); + if (!data) { + PRINTM(MERROR, "Cannot allocate buffer for command!\n"); + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, &cmd_len, respbuf + header_len, + sizeof(cmd_len), sizeof(cmd_len)); + buf = respbuf + header_len + sizeof(cmd_len); + + rw = buf[0]; /* read/write (0/1) */ + func = buf[1]; /* func (0/1/2) */ + reg = buf[5]; /* address */ + reg = (reg << 8) | buf[4]; + reg = (reg << 8) | buf[3]; + reg = (reg << 8) | buf[2]; + mode = buf[6]; /* byte mode/block mode (0/1) */ + blklen = buf[8]; /* block size */ + blklen = (blklen << 8) | buf[7]; + blknum = buf[10]; /* block number or byte number */ + blknum = (blknum << 8) | buf[9]; + + if (mode == BYTE_MODE) + blklen = 1; + else + mode = BLOCK_MODE; + + total_len = (mode == BLOCK_MODE) ? blknum * blklen : blknum; + if (total_len > WOAL_2K_BYTES) { + PRINTM(MERROR, "Total data length is too large!\n"); + ret = -EINVAL; + goto done; + } + PRINTM(MINFO, + "CMD53 read/write, func = %d, addr = %#x, mode = %d, " + "block size = %d, block(byte) number = %d\n", + func, reg, mode, blklen, blknum); + + if (!rw) { + sdio_claim_host( + ((struct sdio_mmc_card *)priv->phandle->card)->func); + if (sdio_readsb( + ((struct sdio_mmc_card *)priv->phandle->card)->func, + respbuf, reg, total_len)) { + PRINTM(MERROR, + "sdio_readsb: reading memory 0x%x failed\n", + reg); + goto done; + } + sdio_release_host( + ((struct sdio_mmc_card *)priv->phandle->card)->func); + ret = total_len; + } else { + int pos = 0; + pattern_len = cmd_len - 11; + if (pattern_len > total_len) + pattern_len = total_len; + + /* Copy/duplicate the pattern to data buffer */ + for (pos = 0; pos < total_len; pos++) + data[pos] = buf[11 + (pos % pattern_len)]; + sdio_claim_host( + ((struct sdio_mmc_card *)priv->phandle->card)->func); + if (sdio_writesb( + ((struct sdio_mmc_card *)priv->phandle->card)->func, + reg, data, total_len)) + PRINTM(MERROR, + "sdio_writesb: writing memory 0x%x failed\n", + reg); + sdio_release_host( + ((struct sdio_mmc_card *)priv->phandle->card)->func); + } + +done: + kfree(data); + LEAVE(); + return ret; +} +#endif /* SDIO */ + +/** + * @brief Set/Get Port Control mode + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_port_ctrl(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0, data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_PORT_CTRL); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len == 1) { + sec->param.port_ctrl_enabled = data; + req->action = MLAN_ACT_SET; + } else { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + moal_memcpy_ext(priv->phandle, respbuf, + &sec->param.port_ctrl_enabled, sizeof(int), + respbuflen); + ret = sizeof(int); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the By-passed TX packet from + * upper layer + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_bypassed_packet(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int header_len = 0; + int ret = 0; + struct sk_buff *skb = NULL; + struct ethhdr *eth; + t_u16 moreLen = 0, copyLen = 0; + ENTER(); + +#define MLAN_BYPASS_PKT_EXTRA_OFFSET (4) + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_PB_BYPASS); + copyLen = strlen(respbuf) - header_len; + moreLen = MLAN_MIN_DATA_HEADER_LEN + MLAN_BYPASS_PKT_EXTRA_OFFSET + + sizeof(mlan_buffer); + + skb = alloc_skb(copyLen + moreLen, GFP_KERNEL); + if (skb == NULL) { + PRINTM(MERROR, "kmalloc no memory !!\n"); + LEAVE(); + return -ENOMEM; + } + + skb_reserve(skb, moreLen); + + moal_memcpy_ext(priv->phandle, skb_put(skb, copyLen), + respbuf + header_len, copyLen, copyLen); + + eth = (struct ethhdr *)skb->data; + eth->h_proto = __constant_htons(eth->h_proto); + skb->dev = priv->netdev; + + HEXDUMP("Bypass TX Data", skb->data, MIN(skb->len, 100)); + + woal_hard_start_xmit(skb, priv->netdev); + + LEAVE(); + return ret; +} + +/** + * @brief Set Robustcoex gpiocfg + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_robustcoex(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + int ret = 0, data[3] = {0}; + mlan_ds_misc_cfg *robust_coex_cfg = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + robust_coex_cfg = (mlan_ds_misc_cfg *)req->pbuf; + while (respbuf[0] == ' ') { + /** skip space */ + respbuf++; + } + + if (strncmp(respbuf, "gpiocfg", strlen("gpiocfg")) == 0) { + header_len = strlen("gpiocfg") + 1; + parse_arguments(respbuf + header_len, data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len > 3) { + PRINTM(MERROR, "Invalid parameter number\n"); + ret = -EINVAL; + goto done; + } + if (data[0] != ROBUSTCOEX_GPIOCFG_ENABLE && + data[0] != ROBUSTCOEX_GPIOCFG_DISABLE) { + PRINTM(MERROR, "Invalid parameter number\n"); + ret = -EINVAL; + goto done; + } + if (data[0] == ROBUSTCOEX_GPIOCFG_ENABLE) { + if (user_data_len != 3) { + PRINTM(MMSG, + "Please provide gpio num and gpio polarity for ROBUSTCOEX_GPIOCFG_ENABLE\n"); + ret = -EINVAL; + goto done; + } + robust_coex_cfg->param.robustcoexparams.method = + ROBUSTCOEX_GPIO_CFG; + robust_coex_cfg->param.robustcoexparams.enable = + ROBUSTCOEX_GPIOCFG_ENABLE; + robust_coex_cfg->param.robustcoexparams.gpio_num = + data[1]; + robust_coex_cfg->param.robustcoexparams.gpio_polarity = + data[2]; + } else { + robust_coex_cfg->param.robustcoexparams.method = + ROBUSTCOEX_GPIO_CFG; + robust_coex_cfg->param.robustcoexparams.enable = + ROBUSTCOEX_GPIOCFG_DISABLE; + robust_coex_cfg->param.robustcoexparams.gpio_num = 0; + robust_coex_cfg->param.robustcoexparams.gpio_polarity = + 0; + } + req->action = MLAN_ACT_SET; + req->req_id = MLAN_IOCTL_MISC_CFG; + robust_coex_cfg->sub_command = MLAN_OID_MISC_ROBUSTCOEX; + } else { + PRINTM(MERROR, "Invalid parameter\n"); + ret = -EFAULT; + goto done; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set DMCS mapping policy or get DMCS status + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_dmcs(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + int ret = 0, data[2] = {0}; + mlan_ds_misc_cfg *dmcs_cfg = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DMCS); + dmcs_cfg = (mlan_ds_misc_cfg *)req->pbuf; + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of args! %d\n", user_data_len); + ret = -EINVAL; + goto done; + } + req->req_id = MLAN_IOCTL_MISC_CFG; + dmcs_cfg->sub_command = MLAN_OID_MISC_DMCS_CONFIG; + dmcs_cfg->param.dmcs_policy.subcmd = data[0]; + switch (data[0]) { + case 0: + if (user_data_len != 2) { + PRINTM(MERROR, "Please provide mapping policy\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + dmcs_cfg->param.dmcs_policy.mapping_policy = data[1]; + break; + case 1: + req->action = MLAN_ACT_GET; + break; + default: + break; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (req->action == MLAN_ACT_GET) { + moal_memcpy_ext(priv->phandle, respbuf, + &dmcs_cfg->param.dmcs_status, + sizeof(mlan_ds_misc_dmcs_status), respbuflen); + } + ret = sizeof(mlan_ds_misc_dmcs_status); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set and get boot sleep configure + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_bootsleep(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = MLAN_STATUS_SUCCESS; + int user_data_len = 0; + int header_len = 0; + int allowed = 1; + int data[1] = {0}; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_BOOT_SLEEP; + req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_BOOTSLEEP); + + if (strlen(respbuf) == header_len) { + req->action = MLAN_ACT_GET; + } else { + req->action = MLAN_ACT_SET; + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != allowed) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + misc->param.boot_sleep = data[0] ? 1 : 0; + PRINTM(MIOCTL, "boot sleep cfg:%u\n", misc->param.boot_sleep); + } + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT_TIMEOUT); + if (ret != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, &misc->param.boot_sleep, + sizeof(misc->param.boot_sleep), respbuflen); + ret = sizeof(misc->param.boot_sleep); + + PRINTM(MIOCTL, "boot sleep cfg: %u\n", misc->param.boot_sleep); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(PCIE) +/** + * @brief Enable SSU support + * @param priv Pointer to moal_private structure + * @param used_len used length + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_ssu_cmd(moal_private *priv, t_u8 used_len, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ds_misc_cfg *ssu_cfg = NULL; + mlan_ioctl_req *req = NULL; + ssu_params_cfg *ssu_params; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + ssu_cfg = (mlan_ds_misc_cfg *)req->pbuf; + memset(&ssu_cfg->param.ssu_params, 0, sizeof(mlan_ds_ssu_params)); + if (!used_len) { + req->action = MLAN_ACT_SET; + ssu_cfg->param.ssu_params.nskip = 0; + ssu_cfg->param.ssu_params.nsel = 1; + ssu_cfg->param.ssu_params.adcdownsample = 3; + ssu_cfg->param.ssu_params.mask_adc_pkt = 0; + ssu_cfg->param.ssu_params.out_16bits = 1; + } else { + ssu_params = (ssu_params_cfg *)respbuf; + DBG_HEXDUMP(MCMD_D, "User SSU params:", respbuf, + sizeof(mlan_ds_ssu_params)); + if (ssu_params->ssu_mode == 2) + req->action = MLAN_ACT_DEFAULT; + else { + req->action = MLAN_ACT_SET; + ssu_cfg->param.ssu_params.nskip = ssu_params->nskip; + ssu_cfg->param.ssu_params.nsel = ssu_params->nsel; + ssu_cfg->param.ssu_params.adcdownsample = + ssu_params->adcdownsample; + ssu_cfg->param.ssu_params.mask_adc_pkt = + ssu_params->mask_adc_pkt; + ssu_cfg->param.ssu_params.out_16bits = + ssu_params->out_16bits; + ssu_cfg->param.ssu_params.spec_pwr_enable = + ssu_params->spec_pwr_enable; + ssu_cfg->param.ssu_params.rate_deduction = + ssu_params->rate_deduction; + ssu_cfg->param.ssu_params.n_pkt_avg = + ssu_params->n_pkt_avg; + } + } + req->req_id = MLAN_IOCTL_MISC_CFG; + ssu_cfg->sub_command = MLAN_OID_MISC_SSU; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief configure 11ax HE capability or HE operation + * + * + * @param priv Pointer to the mlan_private driver data struct + * @param respbuf A pointer to response buffer + * @param len length used + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative value + */ +static int woal_priv_11axcfg_cmd(moal_private *priv, t_u8 *respbuf, t_u8 len, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11ax_cfg *cfg = NULL; + mlan_ds_11ax_he_cfg *data_ptr = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + data_ptr = (mlan_ds_11ax_he_cfg *)respbuf; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ax_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_11AX_CFG; + req->action = MLAN_ACT_SET; + cfg = (mlan_ds_11ax_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_11AX_HE_CFG; + if (len) + moal_memcpy_ext(priv->phandle, (t_u8 *)&cfg->param.he_cfg, + respbuf, len, sizeof(mlan_ds_11ax_he_cfg)); + else + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&cfg->param.he_cfg, + sizeof(mlan_ds_11ax_he_cfg), respbuflen); + ret = sizeof(mlan_ds_11ax_he_cfg); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +/** + * @brief Configure TWT Setup parameters + * + * @param priv Pointer to the mlan_private driver data struct + * @param respbuf A pointer to response buffer + * @param len Length used + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative + * value + */ +static int woal_priv_twt_setup(moal_private *priv, t_u8 *respbuf, t_u8 len, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_twtcfg *cfg = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_twtcfg)); + if (req == NULL) { + PRINTM(MERROR, "Failed to allocate ioctl_req!\n"); + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_11AX_CFG; + req->action = MLAN_ACT_SET; + cfg = (mlan_ds_twtcfg *)req->pbuf; + cfg->sub_command = MLAN_OID_11AX_TWT_CFG; + cfg->sub_id = MLAN_11AX_TWT_SETUP_SUBID; + + if (len) { + moal_memcpy_ext(priv->phandle, (t_u8 *)&cfg->param.twt_setup, + respbuf, len, sizeof(mlan_ds_twt_setup)); + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "woal_request_ioctl failed!\n"); + ret = -EFAULT; + goto done; + } + + ret = sizeof(mlan_ds_twt_setup); +done: + if (status != MLAN_STATUS_PENDING) { + kfree(req); + } + LEAVE(); + return ret; +} + +/** + * @brief Configure TWT Tear down parameters + * + * @param priv Pointer to the mlan_private driver data struct + * @param respbuf A pointer to response buffer + * @param len Length used + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written if successful else negative + * value + */ +static int woal_priv_twt_teardown(moal_private *priv, t_u8 *respbuf, t_u8 len, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_twtcfg *cfg = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_twtcfg)); + if (req == NULL) { + PRINTM(MERROR, "Failed to allocate ioctl_req!\n"); + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_11AX_CFG; + req->action = MLAN_ACT_SET; + cfg = (mlan_ds_twtcfg *)req->pbuf; + cfg->sub_command = MLAN_OID_11AX_TWT_CFG; + cfg->sub_id = MLAN_11AX_TWT_TEARDOWN_SUBID; + + if (len) { + moal_memcpy_ext(priv->phandle, (t_u8 *)&cfg->param.twt_teardown, + respbuf, len, sizeof(mlan_ds_twt_teardown)); + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "woal_request_ioctl failed!\n"); + ret = -EFAULT; + goto done; + } + + ret = sizeof(mlan_ds_twt_teardown); +done: + if (status != MLAN_STATUS_PENDING) { + kfree(req); + } + LEAVE(); + return ret; +} + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(UAP_CFG80211) +/** + * @brief Set/Get P2P NoA (Notice of Absence) parameters + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_cfg_noa(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + int ret = 0, data[7]; + mlan_ds_wifi_direct_config noa_cfg; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_CFG_NOA); + memset(&noa_cfg, 0, sizeof(noa_cfg)); + + memset(data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len > 5) { + PRINTM(MERROR, "invalid parameters\n"); + ret = -EINVAL; + goto done; + } + + noa_cfg.flags |= WIFI_DIRECT_NOA; + + if (woal_p2p_config(priv, MLAN_ACT_GET, &noa_cfg) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not get P2P noa config\n"); + ret = -EINVAL; + goto done; + } + + if (strlen(respbuf) == header_len) { + /* GET operation */ + moal_memcpy_ext(priv->phandle, respbuf, &noa_cfg, + sizeof(noa_cfg), respbuflen); + ret = sizeof(noa_cfg); + } else { + switch (user_data_len) { + case 5: + noa_cfg.noa_interval = (t_u32)data[4]; + /* fall through */ + case 4: + noa_cfg.noa_duration = (t_u32)data[3]; + /* fall through */ + case 3: + if (data[2] < 1 || data[2] > 255) { + PRINTM(MERROR, + "Invalid number of absence intervals\n"); + ret = -EINVAL; + goto done; + } + noa_cfg.noa_count = (t_u8)data[2]; + /* fall through */ + case 2: + if (data[1] < 0 || data[1] > 255) { + PRINTM(MERROR, "Invalid Index\n"); + ret = -EINVAL; + goto done; + } + noa_cfg.index = (t_u16)data[1]; + /* fall through */ + case 1: + if (data[0] < 0 || data[0] > 1) { + PRINTM(MERROR, "Invalid noa enable\n"); + ret = -EINVAL; + goto done; + } + noa_cfg.noa_enable = (t_u8)data[0]; + noa_cfg.flags |= WIFI_DIRECT_NOA; + break; + default: + break; + } + woal_p2p_config(priv, MLAN_ACT_SET, &noa_cfg); + } + +done: + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get P2P OPP-PS parameters + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_cfg_opp_ps(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int header_len = 0, user_data_len = 0; + int ret = 0, data[7]; + mlan_ds_wifi_direct_config opp_ps_cfg; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_CFG_OPP_PS); + memset(&opp_ps_cfg, 0, sizeof(opp_ps_cfg)); + + memset(data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len > 2) { + PRINTM(MERROR, "invalid parameters\n"); + ret = -EINVAL; + goto done; + } + + opp_ps_cfg.flags |= WIFI_DIRECT_OPP_PS; + + if (woal_p2p_config(priv, MLAN_ACT_GET, &opp_ps_cfg) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not get P2P opp ps config\n"); + ret = -EINVAL; + goto done; + } + + if (strlen(respbuf) == header_len) { + /* GET operation */ + moal_memcpy_ext(priv->phandle, respbuf, &opp_ps_cfg, + sizeof(opp_ps_cfg), respbuflen); + ret = sizeof(opp_ps_cfg); + } else { + switch (user_data_len) { + case 2: + opp_ps_cfg.ct_window = (t_u8)data[1]; + /* fall through */ + case 1: + if (data[0] < 0 || data[0] > 1) { + PRINTM(MERROR, "Invalid ps enable\n"); + ret = -EINVAL; + goto done; + } + opp_ps_cfg.opp_ps_enable = (t_u8)data[0]; + opp_ps_cfg.flags |= WIFI_DIRECT_OPP_PS; + /* fall through */ + default: + break; + } + woal_p2p_config(priv, MLAN_ACT_SET, &opp_ps_cfg); + } + +done: + + LEAVE(); + return ret; +} +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#ifdef WIFI_DIRECT_SUPPORT +#define DEF_NOA_INTERVAL 100 +/** + ** @brief Set/Get P2P NoA (Notice of Absence) parameters + ** @param priv Pointer to moal_private structure + ** @param respbuf Pointer to response buffer + ** @param resplen Response buffer length + ** + ** @return Number of bytes written, negative for failure. + **/ +static int woal_p2p_ps_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + int user_data_len = 0; + int ret = 0, data[2]; + u32 duration = priv->phandle->noa_duration; + u32 interval = 0; + + ENTER(); + if (strlen(respbuf) > strlen("P2P_PERIODIC_SLEEP")) { + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen("P2P_PERIODIC_SLEEP") + 1, + data, ARRAY_SIZE(data), &user_data_len); + } + if ((user_data_len != 1) && (user_data_len != 2)) { + PRINTM(MERROR, + " Invalid parameter number for P2P_PERIODIC_SLEEP"); + ret = -EINVAL; + goto done; + } + if (data[0] < DEF_NOA_INTERVAL) + interval = DEF_NOA_INTERVAL; + else + interval = (data[0] + DEF_NOA_INTERVAL - 1) / DEF_NOA_INTERVAL * + DEF_NOA_INTERVAL; + + if (user_data_len == 2) + duration = data[1]; + if (duration >= interval) { + PRINTM(MERROR, + " Invalid noa duration/interval! duration=%d interval=%d\n", + duration, interval); + ret = -EINVAL; + goto done; + } + priv->phandle->noa_interval = interval; + priv->phandle->noa_duration = duration; + PRINTM(MIOCTL, "configure noa interval=%d, duration=%d\n", + priv->phandle->noa_interval, priv->phandle->noa_duration); +done: + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief Set/Get DFS repeater mode + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_dfs_repeater_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0, data[1] = {0}; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ds_misc_dfs_repeater *dfs_repeater = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DFS_REPEATER_CFG); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + req->req_id = MLAN_IOCTL_MISC_CFG; + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_DFS_REAPTER_MODE; + dfs_repeater = + (mlan_ds_misc_dfs_repeater *)&misc_cfg->param.dfs_repeater; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + if ((data[0] != MTRUE) && (data[0] != MFALSE)) { + PRINTM(MERROR, "Invalid DFS repeater mode %d\n", + data[0]); + ret = -EINVAL; + goto done; + } + dfs_repeater->mode = (t_u16)data[0]; + + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)dfs_repeater, + sizeof(mlan_ds_misc_dfs_repeater), respbuflen); + ret = sizeof(mlan_ds_misc_dfs_repeater); + } + + /* Store current value of DFS repeater mode for futher references. eg., + * for avoiding CAC timers + */ + priv->phandle->dfs_repeater_mode = dfs_repeater->mode; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** + * @brief Set/Get MIRACAST configuration parameters + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_miracast_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0, data[3] = {0, 0, 0}; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_MIRACAST_CFG); + + if (strlen(respbuf) == header_len) { + /* GET operation */ + data[0] = priv->phandle->miracast_mode; + data[1] = priv->phandle->miracast_scan_time; + data[2] = priv->phandle->scan_chan_gap; + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, + sizeof(data), respbuflen); + ret = sizeof(data); + } else { + /* SET operation */ + memset(data, 0, sizeof(data)); + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len > 3) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (data[0] < 0 || data[0] > 2 || data[1] < 0 || data[2] < 0) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + } + + if (user_data_len >= 1) + priv->phandle->miracast_mode = (t_u8)data[0]; + if (user_data_len >= 2) + priv->phandle->miracast_scan_time = (t_u16)data[1]; + if (user_data_len == 3) + priv->phandle->scan_chan_gap = (t_u16)data[2]; + +done: + LEAVE(); + return ret; +} + +/** + * @brief Configuring scan gap for miracast mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return 0 --success, otherwise failure + */ +int woal_set_scan_chan_gap(moal_private *priv, t_u8 *respbuf, int respbuflen) +{ + t_u32 data[2]; + int ret = 0; + int user_data_len = 0; + + ENTER(); + + if (strlen(respbuf) > strlen("SCAN_TIMING")) { + memset((char *)data, 0, sizeof(data)); + parse_arguments(respbuf + strlen("SCAN_TIMING") + 1, data, + ARRAY_SIZE(data), &user_data_len); + } + + if (user_data_len != 2) { + PRINTM(MERROR, "Invalid arguments for scan timing\n"); + ret = -EINVAL; + goto done; + } + priv->phandle->miracast_scan_time = (t_u16)data[0]; + priv->phandle->scan_chan_gap = (t_u16)data[1]; +done: + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief Set/Get control to coex RX window size configuration + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_coex_rx_winsize(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_COEX_RX_WINSIZE); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + req->req_id = MLAN_IOCTL_11N_CFG; + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_COEX_RX_WINSIZE; + + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + if ((data != MTRUE) && (data != MFALSE)) { + PRINTM(MERROR, + "Invalid coex RX window size parameter %d\n", + data); + ret = -EINVAL; + goto done; + } + cfg_11n->param.coex_rx_winsize = data; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!user_data_len) { + moal_memcpy_ext(priv->phandle, respbuf, + (t_u8 *)&cfg_11n->param.coex_rx_winsize, + sizeof(t_u32), respbuflen); + ret = sizeof(t_u32); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +#ifdef PCIE +/** + * @brief Read/Write PCIE register + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_pcie_reg_rw(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + moal_handle *handle = priv->phandle; + int data[3]; + t_u32 reg; + t_u32 value; + int ret = MLAN_STATUS_SUCCESS; + int user_data_len = 0, header_len = 0; + + ENTER(); + + memset(data, 0, sizeof(data)); + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_PCIE_REG_RW); + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if ((user_data_len != 1) && (user_data_len != 2)) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + + reg = (t_u32)data[0]; + if (user_data_len == 1) { + if (moal_read_reg(handle, reg, &value)) { + ret = -EFAULT; + goto done; + } + data[1] = value; + } else { + value = data[1]; + if (moal_write_reg(handle, reg, value)) { + ret = -EFAULT; + goto done; + } + } + moal_memcpy_ext(handle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Read/Write PCIE register/memory from BAR0 + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_pcie_bar0_reg_rw(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + moal_handle *handle = priv->phandle; + pcie_service_card *card = (pcie_service_card *)handle->card; + int data[3]; + t_u32 reg; + t_u32 value; + int ret = MLAN_STATUS_SUCCESS; + int user_data_len = 0, header_len = 0; + + ENTER(); + + memset(data, 0, sizeof(data)); + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_PCIE_BAR0_REG_RW); + if (strlen(respbuf) == header_len) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if ((user_data_len != 1) && (user_data_len != 2)) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + + reg = (t_u32)data[0]; + if (user_data_len == 1) { + value = ioread32(card->pci_mmap + reg); + if (value == MLAN_STATUS_FAILURE) { + ret = -EFAULT; + goto done; + } + data[1] = value; + } else { + value = data[1]; + iowrite32(value, card->pci_mmap + reg); + } + moal_memcpy_ext(handle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get SOC temperature + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_get_sensor_temp(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pcfg = (mlan_ds_misc_cfg *)req->pbuf; + pcfg->sub_command = MLAN_OID_MISC_GET_SENSOR_TEMP; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(respbuf, 0, respbuflen); + moal_memcpy_ext(priv->phandle, respbuf, + &pcfg->param.sensor_temp.temperature, sizeof(t_u32), + respbuflen); + + ret = sizeof(t_u32); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** + * @brief Enable/disable DFS offload + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + * + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_dfs_offload_enable(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + struct wiphy *wiphy = NULL; + int ret = 0, dfs_offload_en = 0, user_data_len = 0, header_len = 0, + dfs_offload; + + ENTER(); + + if (priv && priv->wdev) + wiphy = priv->wdev->wiphy; + if (!wiphy) { + PRINTM(MERROR, "wiphy is NULL\n"); + ret = -EFAULT; + goto done; + } + dfs_offload = moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD); + if (woal_is_any_interface_active(priv->phandle)) { + PRINTM(MERROR, + "DFS offload enable/disable do not allowed after BSS started!\n"); + ret = -EFAULT; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DFS_OFFLOAD); + parse_arguments(respbuf + header_len, &dfs_offload_en, + sizeof(dfs_offload_en) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", user_data_len); + ret = -EINVAL; + goto done; + } + + if (dfs_offload_en != 0 && dfs_offload_en != 1) { + PRINTM(MERROR, "Invalid args!\n"); + ret = -EINVAL; + goto done; + } + if (dfs_offload != dfs_offload_en) { + dfs_offload = dfs_offload_en; + if (dfs_offload) + moal_extflg_set(priv->phandle, EXT_DFS_OFFLOAD); + else + moal_extflg_clear(priv->phandle, EXT_DFS_OFFLOAD); + woal_update_radar_chans_dfs_state(wiphy); + } +done: + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief Set/Get dynamic bandwidth + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_config_dyn_bw(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + int user_data_len = 0, header_len = 0, data = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_DYN_BW; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DYN_BW); + if (strlen(respbuf) == header_len) { + /* GET operation */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, + sizeof(data) / sizeof(int), &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid number of args! %d\n", + user_data_len); + ret = -EINVAL; + goto done; + } + ioctl_req->action = MLAN_ACT_SET; + misc->param.dyn_bw = (t_u16)data; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&misc->param.dyn_bw, + sizeof(t_u16), respbuflen); + ret = sizeof(t_u16); + + PRINTM(MIOCTL, "Dynamic bandwidth %d\n", misc->param.dyn_bw); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +#if defined(UAP_SUPPORT) +/** + * @brief Check validation of channel and oper class + * + * @param priv Pointer to moal_private structure + * @param channel channel + * @param oper_class oper_class + + * @return SUCCESS/FAIL + */ +static int woal_check_valid_channel_operclass(moal_private *priv, int channel, + int oper_class) +{ + int ret = 0; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_OPER_CLASS_CHECK; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_GET; + misc->param.bw_chan_oper.oper_class = (t_u8)oper_class; + misc->param.bw_chan_oper.channel = (t_u8)channel; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + ** @brief Set extended channel switch ie + ** + ** @param priv Pointer to moal_private structure + ** @param respbuf Pointer to response buffer + ** @param resplen Response buffer length + ** + ** @return Number of bytes written, negative for failure. + **/ +static int woal_priv_extend_channel_switch(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0; + int data[5] = {0}; + ENTER(); + + if (!priv || !priv->phandle || (priv->bss_role != MLAN_BSS_ROLE_UAP) || + (priv->bss_started != MTRUE)) { + PRINTM(MERROR, + "priv or handle is null or interface is not AP/GO" + "or AP is not started\n"); + ret = -EFAULT; + LEAVE(); + return ret; + } + + parse_arguments(respbuf + strlen(CMD_NXP) + + strlen(PRIV_CMD_EXTEND_CHAN_SWITCH), + data, ARRAY_SIZE(data), &user_data_len); + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + LEAVE(); + return ret; + } + + if (data[1]) { + if (woal_check_valid_channel_operclass(priv, data[2], + data[1])) { + PRINTM(MERROR, "Wrong channel switch parameters!\n"); + ret = -EINVAL; + goto done; + } + } + woal_channel_switch(priv, data[0], data[1], data[2], data[3], data[4], + MFALSE); +done: + LEAVE(); + return ret; +} + +/** + * @brief P2P extended channel switch + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_p2p_ecsa(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int user_data_len = 0, header_len = 0; + int data[2] = {0}; + t_u8 bw = 0, oper_class = 0, channel = 0; + IEEEtypes_ExtChanSwitchAnn_t *ext_chan_switch = NULL; + custom_ie *pcust_chansw_ie = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + if (priv->bss_role != MLAN_BSS_ROLE_UAP) { + PRINTM(MERROR, + "Extended Channel Switch is only allowed for AP/GO mode\n"); + ret = -EFAULT; + goto done; + } + + if (priv->bss_started != MTRUE) { + PRINTM(MERROR, "AP is not started!\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + misc->param.cust_ie.type = TLV_TYPE_MGMT_IE; + misc->param.cust_ie.len = (sizeof(custom_ie) - MAX_IE_SIZE); + + pcust_chansw_ie = (custom_ie *)&misc->param.cust_ie.ie_data_list[0]; + pcust_chansw_ie->ie_index = 0xffff; /*Auto index */ + pcust_chansw_ie->ie_length = sizeof(IEEEtypes_ExtChanSwitchAnn_t); + pcust_chansw_ie->mgmt_subtype_mask = + MGMT_MASK_BEACON | MGMT_MASK_PROBE_RESP; /*Add IE for + BEACON/probe resp*/ + ext_chan_switch = + (IEEEtypes_ExtChanSwitchAnn_t *)pcust_chansw_ie->ie_buffer; + + header_len = strlen("P2P_ECSA"); + parse_arguments(respbuf + header_len + 1, data, ARRAY_SIZE(data), + &user_data_len); + + if (user_data_len != 2) { + PRINTM(MERROR, "Invalid parameters\n"); + ret = -EFAULT; + goto done; + } + + channel = data[0]; + /* bandwidth 20:20M 40:40M 80:80M*/ + bw = data[1]; + if (bw != 20 && bw != 40 && bw != 80) { + PRINTM(MERROR, "Unsupported bandwidth\n"); + ret = -EINVAL; + goto done; + } + if (channel >= 52 && channel <= 144) { + PRINTM(MERROR, "Switch to DFS channel is not allowed!\n"); + ret = -EINVAL; + goto done; + } + + woal_priv_get_nonglobal_operclass_by_bw_channel(priv, bw, channel, + &oper_class); + if (oper_class == 0) { + PRINTM(MERROR, "Wrong parameters!\n"); + ret = -EFAULT; + goto done; + } + ext_chan_switch->element_id = EXTEND_CHANNEL_SWITCH_ANN; + ext_chan_switch->len = 4; + ext_chan_switch->chan_switch_mode = 1; + ext_chan_switch->new_oper_class = oper_class; + ext_chan_switch->new_channel_num = channel; + ext_chan_switch->chan_switch_count = DEF_CHAN_SWITCH_COUNT; + + if (ext_chan_switch->chan_switch_mode) { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + woal_stop_queue(priv->netdev); + priv->uap_tx_blocked = MTRUE; + } + + DBG_HEXDUMP(MCMD_D, "ECSA IE", (t_u8 *)pcust_chansw_ie->ie_buffer, + pcust_chansw_ie->ie_length); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + priv->phandle->chsw_wait_q_woken = MFALSE; + /* wait for channel switch to complete */ + wait_event_interruptible_timeout( + priv->phandle->chsw_wait_q, priv->phandle->chsw_wait_q_woken, + (u32)HZ * (ext_chan_switch->chan_switch_count + 2) * 110 / + 1000); + + pcust_chansw_ie->ie_index = 0xffff; /*Auto index */ + pcust_chansw_ie->mgmt_subtype_mask = 0; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to clear ECSA IE\n"); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} +#endif + +/** +* @brief Set random mac configure value (ON/OFF) +* +* @param priv Pointer to moal_private structure +* @param respbuf Pointer to response buffer +* @param resplen Response buffer length + +* @return Number of bytes written, negative for failure. +*/ +static int woal_priv_config_random_mac(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + int header_len = 0, space_len = 0, i; + t_u8 rand_data[3]; + const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0, 0, 0, 0, 0, 0}; + char fakemac_sts[16]; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + header_len = strlen("FAKEMAC"); + if (strlen(respbuf) >= header_len) { + for (i = 0; i < (strlen(respbuf) - header_len - 1); i++) { + if (respbuf[header_len + 1 + i] != ' ') + break; + } + space_len = i; + + if (strncmp(respbuf + header_len + 1 + space_len, "On", + strlen("On")) == 0) { + if (memcmp(priv->random_mac, zero_mac, + MLAN_MAC_ADDR_LENGTH)) { + ret = sprintf(respbuf, + "FAKEMAC has been On\n") + + 1; + goto done; + } + moal_memcpy_ext(priv->phandle, priv->random_mac, + priv->current_addr, ETH_ALEN, + MLAN_MAC_ADDR_LENGTH); + get_random_bytes(rand_data, 3); + moal_memcpy_ext(priv->phandle, priv->random_mac + 3, + rand_data, 3, 3); + } else if (strncmp(respbuf + header_len + 1 + space_len, "Off", + strlen("Off")) == 0) { + memset(priv->random_mac, 0, ETH_ALEN); + } else { + PRINTM(MERROR, "Invalid parameter!\n"); + ret = -EINVAL; + goto done; + } + } else { + PRINTM(MERROR, "Invalid parameter!\n"); + ret = -EINVAL; + goto done; + } + + memset(fakemac_sts, 0, sizeof(fakemac_sts)); + snprintf(fakemac_sts, sizeof(fakemac_sts), "%s", + (respbuf + header_len + 1 + space_len)); + ret = snprintf(respbuf, respbuflen, "FAKEMAC parameter is %s\n", + fakemac_sts) + + 1; + PRINTM(MMSG, "FAKEMAC parameter is %s\n", + (t_u8 *)(respbuf + header_len + 1 + space_len)); + +done: + LEAVE(); + return ret; +} + +/** + * @brief Download start keep alive parameters + * + * @param priv Pointer to moal_private structure + * @param mkeep_alive_id keep alive ID number + * @param ip_pke IP packet from host + * @param ip_pke_len IP packet length from host + * @param src_mac Source MAC address + * @param dst_mac Destination MAC address + * @param period_msec Send keep alive packet interval + + * @return 0: success fail otherwise + */ +int woal_start_mkeep_alive(moal_private *priv, t_u8 mkeep_alive_id, + t_u8 *ip_pkt, t_u16 ip_pkt_len, t_u8 *src_mac, + t_u8 *dst_mac, t_u32 period_msec, + t_u32 retry_interval, t_u8 retry_cnt) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CLOUD_KEEP_ALIVE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + if (mkeep_alive_id >= MAX_KEEP_ALIVE_ID) { + PRINTM(MERROR, "Invalid parameters\n"); + ret = -EFAULT; + goto done; + } + + /* SET operation */ + ioctl_req->action = MLAN_ACT_SET; + misc->param.keep_alive.mkeep_alive_id = mkeep_alive_id; + misc->param.keep_alive.enable = true; + misc->param.keep_alive.send_interval = period_msec; + misc->param.keep_alive.retry_interval = retry_interval; + misc->param.keep_alive.retry_count = retry_cnt; + moal_memcpy_ext(priv->phandle, misc->param.keep_alive.dst_mac, dst_mac, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + moal_memcpy_ext(priv->phandle, misc->param.keep_alive.src_mac, src_mac, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + misc->param.keep_alive.pkt_len = + MIN(ip_pkt_len, MKEEP_ALIVE_IP_PKT_MAX); + moal_memcpy_ext(priv->phandle, misc->param.keep_alive.packet, ip_pkt, + ip_pkt_len, MKEEP_ALIVE_IP_PKT_MAX); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Download stop keep alive parameters + * + * @param priv Pointer to moal_private structure + * @param mkeep_alive_id keep alive ID number + * @param ip_pkt Last packet + * @param ip_pkt_len Last packet length + + * @return 0: success fail otherwise + */ +int woal_stop_mkeep_alive(moal_private *priv, t_u8 mkeep_alive_id, t_u8 reset, + t_u8 *ip_pkt, t_u8 *pkt_len) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_misc_keep_alive *misc_keep_alive = NULL; + int ret = 0; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CLOUD_KEEP_ALIVE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + misc_keep_alive = &misc->param.keep_alive; + + if (mkeep_alive_id >= MAX_KEEP_ALIVE_ID) { + PRINTM(MERROR, "Invalid parameters\n"); + ret = -EFAULT; + goto done; + } + + /* GET operation */ + ioctl_req->action = MLAN_ACT_GET; + misc_keep_alive->mkeep_alive_id = mkeep_alive_id; + misc_keep_alive->enable = false; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!misc_keep_alive->enable) { + PRINTM(MERROR, "ID %d is already stop\n", mkeep_alive_id); + goto done; + } + + if (reset) + ioctl_req->action = MLAN_ACT_RESET; + else + /* SET operation */ + ioctl_req->action = MLAN_ACT_SET; + misc_keep_alive->mkeep_alive_id = mkeep_alive_id; + misc_keep_alive->enable = false; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) { + ret = woal_mkeep_alive_vendor_event(priv, + &misc->param.keep_alive); + if (ret) + PRINTM(MERROR, + "Keep alive vendor event upload failed\n"); + } +#endif +#endif + if (pkt_len) { + *pkt_len = MIN(misc_keep_alive->pkt_len, + (MKEEP_ALIVE_IP_PKT_MAX - 1)); + PRINTM(MINFO, "keep alive stop pkt_len is %d\n", *pkt_len); + } + if (*pkt_len && ip_pkt) + moal_memcpy_ext(priv->phandle, ip_pkt, misc_keep_alive->packet, + *pkt_len, *pkt_len); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Save cloud keep alive params in driver handle + * + * @param priv Pointer to moal_private structure + * @params Other params for keep alive + + * @return Number of bytes written, negative for failure. + */ +int woal_priv_save_cloud_keep_alive_params(moal_private *priv, + t_u8 mkeep_alive_id, t_u8 enable, + t_u16 ether_type, t_u8 *ip_pkt, + t_u16 ip_pkt_len, t_u8 *src_mac, + t_u8 *dst_mac, t_u32 period_msec, + t_u32 retry_interval, t_u8 retry_cnt) + +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + mlan_ds_misc_keep_alive *keep_alive = NULL; + moal_handle *phandle = NULL; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is null\n"); + ret = -EFAULT; + goto done; + } + phandle = priv->phandle; + + 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_CLOUD_KEEP_ALIVE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + if (mkeep_alive_id >= MAX_KEEP_ALIVE_ID) { + PRINTM(MERROR, "Invalid parameters\n"); + ret = -EINVAL; + goto done; + } + + /* GET operation */ + ioctl_req->action = MLAN_ACT_GET; + misc->param.keep_alive.mkeep_alive_id = mkeep_alive_id; + misc->param.keep_alive.enable = true; + + ret = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (misc->param.keep_alive.enable) { + PRINTM(MERROR, "ID %d is in use\n", mkeep_alive_id); + ret = -EINVAL; + goto done; + } + + keep_alive = &phandle->keep_alive[mkeep_alive_id]; + keep_alive->mkeep_alive_id = mkeep_alive_id; + keep_alive->enable = enable; + if (enable) { + keep_alive->cached = true; + keep_alive->send_interval = period_msec; + keep_alive->retry_interval = retry_interval; + keep_alive->retry_count = retry_cnt; + moal_memcpy_ext(phandle, keep_alive->dst_mac, dst_mac, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + moal_memcpy_ext(phandle, keep_alive->src_mac, src_mac, + MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH); + keep_alive->pkt_len = MIN(ip_pkt_len, MKEEP_ALIVE_IP_PKT_MAX); + moal_memcpy_ext(phandle, keep_alive->packet, ip_pkt, ip_pkt_len, + MKEEP_ALIVE_IP_PKT_MAX); + if (ether_type) + keep_alive->ether_type = ether_type; + else + keep_alive->ether_type = 0; + } + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} + +/** + * @brief Cloud keep alive feature + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_cloud_keep_alive(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + cloud_keep_alive *keep_alive = NULL; + int header_len = 0; + + ENTER(); + + header_len = strlen(PRIV_CMD_CLOUD_KEEP_ALIVE); + + keep_alive = (cloud_keep_alive *)(respbuf + header_len); + + if (keep_alive->enable) { + ret = woal_priv_save_cloud_keep_alive_params( + priv, keep_alive->mkeep_alive_id, keep_alive->enable, 0, + keep_alive->pkt, keep_alive->pkt_len, + keep_alive->src_mac, keep_alive->dst_mac, + keep_alive->sendInterval, keep_alive->retryInterval, + keep_alive->retryCount); + } else { + if (0 != woal_stop_mkeep_alive(priv, keep_alive->mkeep_alive_id, + keep_alive->reset, + keep_alive->pkt, + &keep_alive->pkt_len)) { + ret = -EFAULT; + return ret; + } + ret = respbuflen; + } + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get static rx abort config + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_rx_abort_cfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + int data[2] = {0}; + int header_len = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_RX_ABORT_CFG); + user_data_len = strlen(respbuf) - header_len; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_RX_ABORT_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 2 || + (data[0] == MTRUE && user_data_len != 2)) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if (data[0] == MTRUE && data[1] > 0x7f) { + PRINTM(MERROR, "Invalid threshold value\n"); + ret = -EINVAL; + goto done; + } + misc->param.rx_abort_cfg.enable = (t_u8)data[0]; + if (user_data_len == 2) + misc->param.rx_abort_cfg.rssi_threshold = (t_s8)data[1]; + req->action = MLAN_ACT_SET; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = misc->param.rx_abort_cfg.enable; + data[1] = misc->param.rx_abort_cfg.rssi_threshold; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get dynamic rx abort config + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_rx_abort_cfg_ext(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + int data[3] = {0}; + int header_len = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_RX_ABORT_CFG_EXT); + user_data_len = strlen(respbuf) - header_len; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_RX_ABORT_CFG_EXT; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 3 || + (data[0] == MTRUE && user_data_len != 3)) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if (data[0] == MTRUE) { + if (data[1] > 0x7f) { + PRINTM(MERROR, "Invalid margin value\n"); + ret = -EINVAL; + goto done; + } + if (data[2] > 0x7f) { + PRINTM(MERROR, + "Invalid ceil threshold value\n"); + ret = -EINVAL; + goto done; + } + } + misc->param.rx_abort_cfg_ext.enable = (t_u8)data[0]; + if (user_data_len > 1) { + misc->param.rx_abort_cfg_ext.rssi_margin = + (t_s8)data[1]; + misc->param.rx_abort_cfg_ext.ceil_rssi_threshold = + (t_s8)data[2]; + } + req->action = MLAN_ACT_SET; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = misc->param.rx_abort_cfg_ext.enable; + data[1] = misc->param.rx_abort_cfg_ext.rssi_margin; + data[2] = misc->param.rx_abort_cfg_ext.ceil_rssi_threshold; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable Un-associated Dot11mc FTM Frame exchanges + * + * @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_dot11mc_unassoc_ftm_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + int data[1] = {0}; + int header_len = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_DOT11MC_UNASSOC_FTM_CFG); + user_data_len = strlen(respbuf) - header_len; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_DOT11MC_UNASSOC_FTM_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 1) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if ((data[0] != MTRUE) && (data[0] != MFALSE)) { + PRINTM(MERROR, "Invalid state for unassoc ftm\n"); + ret = -EINVAL; + goto done; + } + misc->param.dot11mc_unassoc_ftm_cfg.state = (t_u16)data[0]; + req->action = MLAN_ACT_SET; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = misc->param.dot11mc_unassoc_ftm_cfg.state; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Tx AMPDU protection mode + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_tx_ampdu_prot_mode(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + int data[1] = {0}; + int header_len = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_TX_AMPDU_PROT_MODE); + user_data_len = strlen(respbuf) - header_len; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_TX_AMPDU_PROT_MODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 1) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if (data[0] > TX_AMPDU_DYNAMIC_RTS_CTS) { + PRINTM(MERROR, "Invalid protection mode\n"); + ret = -EINVAL; + goto done; + } + misc->param.tx_ampdu_prot_mode.mode = (t_u16)data[0]; + req->action = MLAN_ACT_SET; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = misc->param.tx_ampdu_prot_mode.mode; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Tx rate adapt config + * + * @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_rate_adapt_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + int data[4] = {0}; + int header_len = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_RATE_ADAPT_CFG); + user_data_len = strlen(respbuf) - header_len; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_RATE_ADAPT_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len < 1) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if (data[0] > RATEADAPT_ALGO_SR) { + PRINTM(MERROR, "Invalid Rateadapt Algorithm\n"); + ret = -EINVAL; + goto done; + } + if (data[0] == RATEADAPT_ALGO_SR && user_data_len > 1) { + if ((data[1] & data[2]) == 0xff) { + /* dynamic CCA noise based rate adapation enable + * request nothing to do here + */ + } else if (data[1] > 100 || data[2] > 100) { + PRINTM(MERROR, + "Invalid success rate threshold value\n"); + ret = -EINVAL; + goto done; + } + if (data[3] < 10 || data[3] > 0xffff) { + PRINTM(MERROR, "Invalid interval value\n"); + ret = -EINVAL; + goto done; + } + } + misc->param.rate_adapt_cfg.sr_rateadapt = (t_u8)data[0]; + if (data[0] == RATEADAPT_ALGO_SR && user_data_len > 1) { + misc->param.rate_adapt_cfg.ra_low_thresh = + (t_u8)data[1]; + misc->param.rate_adapt_cfg.ra_high_thresh = + (t_u8)data[2]; + misc->param.rate_adapt_cfg.ra_interval = (t_u16)data[3]; + } + req->action = MLAN_ACT_SET; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = misc->param.rate_adapt_cfg.sr_rateadapt; + data[1] = misc->param.rate_adapt_cfg.ra_low_thresh; + data[2] = misc->param.rate_adapt_cfg.ra_high_thresh; + data[3] = misc->param.rate_adapt_cfg.ra_interval; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get global cck desense config + * + * @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_cck_desense_cfg(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + int data[5] = {0}; + int header_len = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_CCK_DESENSE_CFG); + user_data_len = strlen(respbuf) - header_len; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_CCK_DESENSE_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 5) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if (data[0] > CCK_DESENSE_MODE_DYN_ENH) { + PRINTM(MERROR, "Invalid cck desense mode\n"); + ret = -EINVAL; + goto done; + } + if ((data[0] == CCK_DESENSE_MODE_DISABLED && + user_data_len > 1) || + (data[0] == CCK_DESENSE_MODE_DYNAMIC && + user_data_len != 3) || + (data[0] == CCK_DESENSE_MODE_DYN_ENH && + (user_data_len < 3 || user_data_len == 4))) { + PRINTM(MERROR, + "Invalid number of args for requested mode\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len > 1) { + if (data[1] > 0x7f) { + PRINTM(MERROR, "Invalid margin value\n"); + ret = -EINVAL; + goto done; + } + if (data[2] > 0x7f) { + PRINTM(MERROR, + "Invalid ceil threshold value\n"); + ret = -EINVAL; + goto done; + } + } + if (user_data_len > 3) { + if (data[3] > 0xff || data[4] > 0xff) { + PRINTM(MERROR, + "Invalid ON/OFF intervals value\n"); + ret = -EINVAL; + goto done; + } + } + misc->param.cck_desense_cfg.mode = (t_u8)data[0]; + if (user_data_len > 1) { + misc->param.cck_desense_cfg.margin = (t_s8)data[1]; + misc->param.cck_desense_cfg.ceil_thresh = (t_s8)data[2]; + } + if (data[0] == CCK_DESENSE_MODE_DYN_ENH) { + if (user_data_len > 3) { + misc->param.cck_desense_cfg.num_on_intervals = + (t_u8)data[3]; + misc->param.cck_desense_cfg.num_off_intervals = + (t_u8)data[4]; + } else { + /* set these to 0xff. This will indicate the FW + * to use previously set values. + */ + misc->param.cck_desense_cfg.num_on_intervals = + 0xff; + misc->param.cck_desense_cfg.num_off_intervals = + 0xff; + } + } + req->action = MLAN_ACT_SET; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = misc->param.cck_desense_cfg.mode; + data[1] = misc->param.cck_desense_cfg.margin; + data[2] = misc->param.cck_desense_cfg.ceil_thresh; + data[3] = misc->param.cck_desense_cfg.num_on_intervals; + data[4] = misc->param.cck_desense_cfg.num_off_intervals; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief set/get low power mode + * + * @param priv Pointer to moal_private structure + * @param respbuf Pointer to response buffer + * @param resplen Response buffer length + + * @return Number of bytes written, negative for failure. + */ +static int woal_priv_set_get_lpm(moal_private *priv, t_u8 *respbuf, + t_u32 respbuflen) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_power_cfg *cfg = NULL; + int data = 0; + int user_data_len = 0, header_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (IS_CARD9098(priv->phandle->card_type) || + IS_CARD9097(priv->phandle->card_type)) { + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_LPM); + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, &data, 1, + &user_data_len); + } + if (user_data_len >= 2) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } else { + req = woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_power_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_power_cfg *)req->pbuf; + if (user_data_len == 0) { + req->action = MLAN_ACT_GET; + } else { + cfg->param.lpm = data; + req->action = MLAN_ACT_SET; + } + } + cfg->sub_command = MLAN_OID_POWER_LOW_POWER_MODE; + req->req_id = MLAN_IOCTL_POWER_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data = cfg->param.lpm; + moal_memcpy_ext(priv->phandle, respbuf, (t_u8 *)&data, + sizeof(data), respbuflen); + ret = sizeof(data); + } else + PRINTM(MERROR, "Low power mode command is not supported!\n"); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get HW ARB config + * + * @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_arbcfg(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + int data[1]; + int header_len = 0, user_data_len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_ARB_CFG); + user_data_len = strlen(respbuf) - header_len; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_ARB_CONFIG; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (strlen(respbuf) == header_len) { + /* GET operation */ + user_data_len = 0; + req->action = MLAN_ACT_GET; + } else { + /* SET operation */ + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len != 1) { + PRINTM(MERROR, "Invalid Parameter\n"); + ret = -EFAULT; + goto done; + } + if (data[0] < 0 || data[0] > 4) { + PRINTM(MERROR, "Invalid Parameter: arb mode 0-4\n"); + ret = -EFAULT; + goto done; + } + misc->param.arb_cfg.arb_mode = (t_u32)data[0]; + req->action = MLAN_ACT_SET; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = misc->param.arb_cfg.arb_mode; + moal_memcpy_ext(priv->phandle, respbuf, (t_u32 *)data, sizeof(data), + respbuflen); + ret = sizeof(data); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Timer function for TP state command. + * + * @param data pointer to a buffer + * + * @return N/A + */ +void woal_tp_acnt_timer_func(void *context) +{ + moal_handle *phandle = (moal_handle *)context; + int i = 0; + + if (phandle == NULL) + return; + PRINTM(MDATA, "####### CPU%d: tp acnt timer\n", smp_processor_id()); + /* Tx TP accounting */ + for (i = 0; i < MAX_TP_ACCOUNT_DROP_POINT_NUM; i++) { + phandle->tp_acnt.tx_bytes_rate[i] = + phandle->tp_acnt.tx_bytes[i] - + phandle->tp_acnt.tx_bytes_last[i]; + phandle->tp_acnt.tx_bytes_last[i] = + phandle->tp_acnt.tx_bytes[i]; + } + phandle->tp_acnt.tx_pending = atomic_read(&phandle->tx_pending); + /* Tx Interrupt accounting */ + phandle->tp_acnt.tx_intr_rate = + phandle->tp_acnt.tx_intr_cnt - phandle->tp_acnt.tx_intr_last; + phandle->tp_acnt.tx_intr_last = phandle->tp_acnt.tx_intr_cnt; + + /* Rx TP accounting */ + for (i = 0; i < MAX_TP_ACCOUNT_DROP_POINT_NUM; i++) { + phandle->tp_acnt.rx_bytes_rate[i] = + phandle->tp_acnt.rx_bytes[i] - + phandle->tp_acnt.rx_bytes_last[i]; + phandle->tp_acnt.rx_bytes_last[i] = + phandle->tp_acnt.rx_bytes[i]; + } + phandle->tp_acnt.rx_pending = atomic_read(&phandle->rx_pending); + // Interrupt accounting, RX + phandle->tp_acnt.rx_intr_rate = + phandle->tp_acnt.rx_intr_cnt - phandle->tp_acnt.rx_intr_last; + phandle->tp_acnt.rx_intr_last = phandle->tp_acnt.rx_intr_cnt; + + /* re-arm timer */ + woal_mod_timer(&phandle->tp_acnt.timer, 1000); +} + +/** + * @brief set tp state to mlan + * + * @param priv pointer to moal_private + * + * @return N/A + */ +void woal_set_tp_state(moal_private *priv) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + moal_handle *handle = priv->phandle; + mlan_status status = MLAN_STATUS_SUCCESS; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) + return; + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_TP_STATE; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->param.tp_state.on = handle->tp_acnt.on; + misc->param.tp_state.drop_point = handle->tp_acnt.drop_point; + req->action = MLAN_ACT_SET; + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_PENDING) + kfree(req); + return; +} + +/** + * @brief Set/Get TP statistics. + * + * @param priv A pointer to moal_private structure + * @param respbuf A pointer to response buffer + * @param respbuflen Available length of response buffer + * + * @return Number of bytes written, negative for failure. + */ +int woal_priv_set_tp_state(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen) +{ + moal_handle *handle = priv->phandle; + int ret = 0; + int data[2]; + int header_len = 0, user_data_len = 0; + + ENTER(); + + if (!respbuf) { + PRINTM(MERROR, "response buffer is not available!\n"); + ret = -EINVAL; + goto done; + } + header_len = strlen(CMD_NXP) + strlen(PRIV_CMD_TP_STATE); + user_data_len = strlen(respbuf) - header_len; + parse_arguments(respbuf + header_len, data, ARRAY_SIZE(data), + &user_data_len); + if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + if (user_data_len) { + handle->tp_acnt.on = data[0]; + /* Enable TP statistics collection */ + if (data[0] == 1) { + handle->tp_acnt.drop_point = data[1]; + if (handle->is_tp_acnt_timer_set == MFALSE) { + woal_initialize_timer(&handle->tp_acnt.timer, + woal_tp_acnt_timer_func, + handle); + handle->is_tp_acnt_timer_set = MTRUE; + woal_mod_timer(&handle->tp_acnt.timer, 1000); + } + } else { + if (handle->is_tp_acnt_timer_set) { + woal_cancel_timer(&handle->tp_acnt.timer); + handle->is_tp_acnt_timer_set = MFALSE; + } + memset((void *)&handle->tp_acnt, 0, + sizeof(moal_tp_acnt_t)); + } + woal_set_tp_state(priv); + } + /* Get command results */ + if (user_data_len == 0) { + moal_memcpy_ext(handle, respbuf, (t_u8 *)(&handle->tp_acnt), + sizeof(handle->tp_acnt), respbuflen); + ret = sizeof(handle->tp_acnt); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set priv command for Android + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +int woal_android_priv_cmd(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + android_wifi_priv_cmd priv_cmd; + moal_private *priv = (moal_private *)netdev_priv(dev); + char *buf = NULL; + char *pdata; +#ifdef STA_SUPPORT + int power_mode = 0; + int band = 0; + char *pband = NULL; + mlan_bss_info bss_info; + mlan_ds_get_signal signal; + mlan_rate_cfg_t rate; + t_u8 country_code[COUNTRY_CODE_LEN]; + int copy_len = 0; +#endif + int len = 0; + gfp_t flag; + char *cmd_buf = NULL; + int cfg80211_wext; + + ENTER(); + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is NULL\n"); + ret = -EFAULT; + goto done; + } + cfg80211_wext = priv->phandle->params.cfg80211_wext; + if (copy_from_user(&priv_cmd, req->ifr_data, + sizeof(android_wifi_priv_cmd))) { + ret = -EFAULT; + goto done; + } +#define CMD_BUF_LEN 3072 + if (priv_cmd.used_len < 0 || priv_cmd.total_len <= 0 || + priv_cmd.used_len > priv_cmd.total_len) { + PRINTM(MERROR, + "Invalid Android priv cmd len. used_len: %d, total_len: %d\n", + priv_cmd.used_len, priv_cmd.total_len); + ret = -EINVAL; + goto done; + } + if (priv_cmd.total_len + 1 > CMD_BUF_LEN) + priv_cmd.total_len = CMD_BUF_LEN - 1; + + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc(CMD_BUF_LEN, flag); + if (!buf) { + PRINTM(MERROR, "%s: failed to allocate memory\n", __FUNCTION__); + ret = -ENOMEM; + goto done; + } +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT + moal_memcpy_ext(priv->phandle, &cmd_buf, &priv_cmd.buf, sizeof(cmd_buf), + sizeof(cmd_buf)); +#else + cmd_buf = priv_cmd.buf; +#endif + if (copy_from_user(buf, cmd_buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + buf[CMD_BUF_LEN - 1] = '\0'; + + PRINTM(MIOCTL, "Android priv cmd: [%s] on [%s]\n", buf, req->ifr_name); + + if (strncmp(buf, CMD_NXP, strlen(CMD_NXP)) && + woal_check_driver_status(priv->phandle)) { + PRINTM(MERROR, "%s fail when driver hang\n", buf); + ret = -EFAULT; + goto done; + } + + if (strncmp(buf, CMD_NXP, strlen(CMD_NXP)) == 0) { + /* This command has come from mlanutl app */ + + /* Check command */ + if (strnicmp(buf + strlen(CMD_NXP), 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_NXP), PRIV_CMD_BANDCFG, + strlen(PRIV_CMD_BANDCFG)) == 0) { + /* Set/Get band configuration */ + len = woal_setget_priv_bandcfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_HOSTCMD, + strlen(PRIV_CMD_HOSTCMD)) == 0) { + /* hostcmd configuration */ + len = woal_priv_hostcmd(priv, buf, priv_cmd.total_len, + MOAL_IOCTL_WAIT); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_11AXCMDCFG, + strlen(PRIV_CMD_11AXCMDCFG)) == 0) { + /* 11ax command */ + pdata = buf + strlen(CMD_NXP) + + strlen(PRIV_CMD_11AXCMDCFG); + len = priv_cmd.total_len - strlen(CMD_NXP) + + strlen(PRIV_CMD_11AXCMDCFG); + len = woal_setget_priv_11axcmdcfg(priv, pdata, len, + MOAL_IOCTL_WAIT); + len += strlen(CMD_NXP) + strlen(PRIV_CMD_11AXCMDCFG); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_RANGE_EXT, + strlen(PRIV_CMD_RANGE_EXT)) == 0) { + len = woal_setget_priv_range_ext(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), 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_NXP), PRIV_CMD_HTCAPINFO, + strlen(PRIV_CMD_HTCAPINFO)) == 0) { + /* Set/Get HT Capability information */ + len = woal_setget_priv_htcapinfo(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ADDBAPARA, + strlen(PRIV_CMD_ADDBAPARA)) == 0) { + /* Set/Get Add BA parameters */ + len = woal_setget_priv_addbapara(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_AGGRPRIOTBL, + strlen(PRIV_CMD_AGGRPRIOTBL)) == 0) { + /* Set/Get Aggregation priority table parameters */ + len = woal_setget_priv_aggrpriotbl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ADDBAREJECT, + strlen(PRIV_CMD_ADDBAREJECT)) == 0) { + /* Set/Get Add BA reject parameters */ + len = woal_setget_priv_addbareject(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DELBA, + strlen(PRIV_CMD_DELBA)) == 0) { + /* Delete selective BA based on parameters */ + len = woal_priv_delba(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_REJECTADDBAREQ, + strlen(PRIV_CMD_REJECTADDBAREQ)) == 0) { + /* Set/Get the reject addba requst conditions*/ + len = woal_priv_rejectaddbareq(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_VHTCFG, + strlen(PRIV_CMD_VHTCFG)) == 0) { + /* Set/Get 11AC configuration */ + len = woal_setget_priv_vhtcfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_OPERMODECFG, + strlen(PRIV_CMD_OPERMODECFG)) == 0) { + /* Set/Get 11AC configuration */ + len = woal_setget_priv_opermodecfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), 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_NXP), PRIV_CMD_TXRATECFG, + strlen(PRIV_CMD_TXRATECFG)) == 0) { + /* Set/Get tx rate cfg */ + len = woal_setget_priv_txratecfg(priv, buf, + priv_cmd.total_len); + goto handled; +#if defined(STA_SUPPORT) || defined(UAP_SUPPORT) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_GETLOG, + strlen(PRIV_CMD_GETLOG)) == 0) { + /* Get wireless stats information */ + len = woal_get_priv_getlog(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_CUSTOMIE, + strlen(PRIV_CMD_CUSTOMIE)) == 0) { + /* Custom IE configuration */ + len = woal_priv_customie(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ESUPPMODE, + strlen(PRIV_CMD_ESUPPMODE)) == 0) { + /* Esupplicant mode configuration */ + len = woal_setget_priv_esuppmode(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_PASSPHRASE, + strlen(PRIV_CMD_PASSPHRASE)) == 0) { + /* Esupplicant passphrase configuration */ + len = woal_setget_priv_passphrase(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DEAUTH, + strlen(PRIV_CMD_DEAUTH)) == 0) { + /* Deauth */ + len = woal_priv_deauth(priv, buf, priv_cmd.total_len); + goto handled; +#ifdef UAP_SUPPORT + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_AP_DEAUTH, + strlen(PRIV_CMD_AP_DEAUTH)) == 0) { + /* AP Deauth */ + len = woal_priv_ap_deauth(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_GET_STA_LIST, + strlen(PRIV_CMD_GET_STA_LIST)) == 0) { + /* Get STA list */ + len = woal_priv_get_sta_list(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_BSS_CONFIG, + strlen(PRIV_CMD_BSS_CONFIG)) == 0) { + /* BSS config */ + len = woal_priv_bss_config(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_BSSROLE, + strlen(PRIV_CMD_BSSROLE)) == 0) { + /* BSS Role */ + len = woal_priv_bssrole(priv, buf, + (t_u32)priv_cmd.total_len); + goto handled; +#endif +#endif +#ifdef STA_SUPPORT + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SETUSERSCAN, + strlen(PRIV_CMD_SETUSERSCAN)) == 0) { + /* Set user scan */ + len = woal_priv_setuserscan(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_GETSCANTABLE, + strlen(PRIV_CMD_GETSCANTABLE)) == 0) { + /* Get scan table */ + len = woal_priv_getscantable(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_GETCHANSTATS, + strlen(PRIV_CMD_GETCHANSTATS)) == 0) { + /* Get channel statistics */ + len = woal_priv_get_chanstats(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_EXTCAPCFG, + strlen(PRIV_CMD_EXTCAPCFG)) == 0) { + /* Extended capabilities configure */ + len = woal_priv_extcapcfg(priv, buf, + (t_u32)priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_CANCELSCAN, + strlen(PRIV_CMD_CANCELSCAN)) == 0) { + /* Cancel scan */ + len = woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DEEPSLEEP, + strlen(PRIV_CMD_DEEPSLEEP)) == 0) { + /* Deep sleep */ + len = woal_priv_setgetdeepsleep(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_IPADDR, + strlen(PRIV_CMD_IPADDR)) == 0) { + /* IP address */ + len = woal_priv_setgetipaddr(priv, buf, + (t_u32)priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_WPSSESSION, + strlen(PRIV_CMD_WPSSESSION)) == 0) { + /* WPS Session */ + len = woal_priv_setwpssession(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_OTPUSERDATA, + strlen(PRIV_CMD_OTPUSERDATA)) == 0) { + /* OTP user data */ + len = woal_priv_otpuserdata(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_COUNTRYCODE, + strlen(PRIV_CMD_COUNTRYCODE)) == 0) { + /* Country code */ + len = woal_priv_set_get_countrycode(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_CFPINFO, + strlen(PRIV_CMD_CFPINFO)) == 0) { + /* CFP info */ + len = woal_priv_get_cfpinfo(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_TCPACKENH, + strlen(PRIV_CMD_TCPACKENH)) == 0) { + /* TCP ack enhancement */ + len = woal_priv_setgettcpackenh(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef REASSOCIATION + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ASSOCBSSID, + strlen(PRIV_CMD_ASSOCBSSID)) == 0) { + /* Associate to essid */ + len = woal_priv_assocessid(priv, buf, + priv_cmd.total_len, 1); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ASSOCESSID, + strlen(PRIV_CMD_ASSOCESSID)) == 0) { + /* Associate to essid */ + len = woal_priv_assocessid(priv, buf, + priv_cmd.total_len, 0); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_WAKEUPREASON, + strlen(PRIV_CMD_WAKEUPREASON)) == 0) { + /* wakeup reason */ + len = woal_priv_getwakeupreason(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_LISTENINTERVAL, + strlen(PRIV_CMD_LISTENINTERVAL)) == 0) { + /* Listen Interval */ + len = woal_priv_set_get_listeninterval( + priv, buf, priv_cmd.total_len); + goto handled; +#endif +#ifdef DEBUG_LEVEL1 + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DRVDBG, + strlen(PRIV_CMD_DRVDBG)) == 0) { + /* Driver debug bit mask */ + len = woal_priv_set_get_drvdbg(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_HSCFG, + strlen(PRIV_CMD_HSCFG)) == 0) { + /* HS configuration */ + len = woal_priv_hscfg(priv, buf, priv_cmd.total_len, + MTRUE); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_HSSETPARA, + strlen(PRIV_CMD_HSSETPARA)) == 0) { + /* Set HS parameter */ + len = woal_priv_hssetpara(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_MGMT_FILTER, + strlen(PRIV_CMD_MGMT_FILTER)) == 0) { + /* Management frame filter wakeup */ + len = woal_priv_mgmt_filter(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SCANCFG, + strlen(PRIV_CMD_SCANCFG)) == 0) { + /* Scan configuration */ + len = woal_priv_set_get_scancfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_GETNLNUM, + strlen(PRIV_CMD_GETNLNUM)) == 0) { + /* Scan configuration */ + len = woal_priv_getnlnum(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_AGGRCTRL, + strlen(PRIV_CMD_AGGRCTRL)) == 0) { + /* aggregation control */ + len = woal_priv_set_get_aggrctrl(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef USB + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_USBAGGRCTRL, + strlen(PRIV_CMD_USBAGGRCTRL)) == 0) { + /* USB aggregation control */ + len = woal_priv_set_get_usbaggrctrl(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_SET_BSS_MODE, + strlen(PRIV_CMD_SET_BSS_MODE)) == 0) { + /* Set bss mode */ + len = woal_priv_set_bss_mode(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SET_AP, + strlen(PRIV_CMD_SET_AP)) == 0) { + /* Set AP */ + len = woal_priv_set_ap(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SET_POWER, + strlen(PRIV_CMD_SET_POWER)) == 0) { + /* Set power management parameters */ + len = woal_priv_set_power(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SET_ESSID, + strlen(PRIV_CMD_SET_ESSID)) == 0) { + /* Set essid */ + len = woal_priv_set_essid(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SET_AUTH, + strlen(PRIV_CMD_SET_AUTH)) == 0) { + /* Set authentication mode parameters */ + len = woal_priv_set_auth(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_GET_AP, + strlen(PRIV_CMD_GET_AP)) == 0) { + /* Get AP */ + len = woal_priv_get_ap(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_GET_POWER, + strlen(PRIV_CMD_GET_POWER)) == 0) { + /* Get power management parameters */ + len = woal_priv_get_power(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_PSMODE, + strlen(PRIV_CMD_PSMODE)) == 0) { + /* Set/Get PS mode */ + len = woal_priv_set_get_psmode(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_WARMRESET, + strlen(PRIV_CMD_WARMRESET)) == 0) { + /* Performs warm reset */ + len = woal_priv_warmreset(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_TXPOWERCFG, + strlen(PRIV_CMD_TXPOWERCFG)) == 0) { + /* TX power configurations */ + len = woal_priv_txpowercfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_RX_ABORT_CFG_EXT, + strlen(PRIV_CMD_RX_ABORT_CFG_EXT)) == 0) { + /* dynamic Rx Abort config */ + len = woal_priv_rx_abort_cfg_ext(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_RX_ABORT_CFG, + strlen(PRIV_CMD_RX_ABORT_CFG)) == 0) { + /* static Rx Abort config */ + len = woal_priv_rx_abort_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_TX_AMPDU_PROT_MODE, + strlen(PRIV_CMD_TX_AMPDU_PROT_MODE)) == 0) { + /* tx ampdu protection mode setting */ + len = woal_priv_tx_ampdu_prot_mode(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_DOT11MC_UNASSOC_FTM_CFG, + strlen(PRIV_CMD_DOT11MC_UNASSOC_FTM_CFG)) == + 0) { + /* setting for dot11mc un-associated case FTM frame + * exchange */ + len = woal_priv_dot11mc_unassoc_ftm_cfg( + priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_RATE_ADAPT_CFG, + strlen(PRIV_CMD_RATE_ADAPT_CFG)) == 0) { + /* rate adapt config */ + len = woal_priv_rate_adapt_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_CCK_DESENSE_CFG, + strlen(PRIV_CMD_CCK_DESENSE_CFG)) == 0) { + /* cck desense config */ + len = woal_priv_cck_desense_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_PSCFG, + strlen(PRIV_CMD_PSCFG)) == 0) { + /* PS configurations */ + len = woal_priv_pscfg(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_BCNTIMEOUTCFG, + strlen(PRIV_CMD_BCNTIMEOUTCFG)) == 0) { + /* Beacon timeout configurations */ + len = woal_priv_bcntimeoutcfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SLEEPPD, + strlen(PRIV_CMD_SLEEPPD)) == 0) { + /* Sleep period */ + len = woal_priv_sleeppd(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_TXCONTROL, + strlen(PRIV_CMD_TXCONTROL)) == 0) { + /* Tx control */ + len = woal_priv_txcontrol(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_REGRDWR, + strlen(PRIV_CMD_REGRDWR)) == 0) { + /* Register Read/Write */ + len = woal_priv_regrdwr(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_RDEEPROM, + strlen(PRIV_CMD_RDEEPROM)) == 0) { + /* Read the EEPROM contents of the card */ + len = woal_priv_rdeeprom(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_MEMRDWR, + strlen(PRIV_CMD_MEMRDWR)) == 0) { + /* Memory Read/Write */ + len = woal_priv_memrdwr(priv, buf, priv_cmd.total_len); + goto handled; +#ifdef SDIO + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SDCMD52RW, + strlen(PRIV_CMD_SDCMD52RW)) == 0) { + /* Cmd52 read/write register */ + len = woal_priv_sdcmd52rw(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SDIO_CLOCK, + strlen(PRIV_CMD_SDIO_CLOCK)) == 0) { + /* Turn on/off the sdio clock */ + len = woal_priv_sdio_clock_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_MPA_CTRL, + strlen(PRIV_CMD_MPA_CTRL)) == 0) { + /* Set SDIO Multi-point aggregation + * control parameters */ + len = woal_priv_sdio_mpa_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SD_CMD53_RW, + strlen(PRIV_CMD_SD_CMD53_RW)) == 0) { + /* Cmd53 read/write register */ + len = woal_priv_cmd53rdwr(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ROBUSTCOEX, + strlen(PRIV_CMD_ROBUSTCOEX)) == 0) { + /* Set Robustcoex GPIOcfg */ + pdata = buf + strlen(CMD_NXP) + + strlen(PRIV_CMD_ROBUSTCOEX); + len = priv_cmd.total_len - strlen(PRIV_CMD_ROBUSTCOEX) - + strlen(CMD_NXP); + len = woal_priv_robustcoex(priv, pdata, len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DMCS, + strlen(PRIV_CMD_DMCS)) == 0) { + /* Set/Get DMCS config */ + len = woal_priv_dmcs(priv, buf, priv_cmd.total_len); + goto handled; +#if defined(PCIE) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SSU, + strlen(PRIV_CMD_SSU)) == 0) { + /* Set SSU config */ + pdata = buf + strlen(CMD_NXP) + strlen(PRIV_CMD_SSU); + len = priv_cmd.used_len - strlen(PRIV_CMD_SSU) - + strlen(CMD_NXP); + len = woal_priv_ssu_cmd(priv, len, pdata, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_AUTO_ARP, + strlen(PRIV_CMD_AUTO_ARP)) == 0) { + /* Auto ARP enable/disable */ + len = woal_priv_set_get_auto_arp(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_HOTSPOTCFG, + strlen(PRIV_CMD_HOTSPOTCFG)) == 0) { + /* Hotspot CFG */ + len = woal_priv_hotspotcfg(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef RX_PACKET_COALESCE + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_RX_COAL_CFG, + strlen(PRIV_CMD_RX_COAL_CFG)) == 0) { + /* RX packet coalescing Configuration */ + len = woal_priv_rx_pkt_coalesce_cfg(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_MGMT_FRAME_CTRL, + strlen(PRIV_CMD_MGMT_FRAME_CTRL)) == 0) { + /* Mgmt Frame Passthrough Ctrl */ + len = woal_priv_mgmt_frame_passthru_ctrl( + priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_QCONFIG, + strlen(PRIV_CMD_QCONFIG)) == 0) { + /* Queue config */ + len = woal_priv_qconfig(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ADDTS, + strlen(PRIV_CMD_ADDTS)) == 0) { + /* Send an ADDTS TSPEC */ + len = woal_priv_wmm_addts_req_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DELTS, + strlen(PRIV_CMD_DELTS)) == 0) { + /* Send a DELTS TSPE */ + len = woal_priv_wmm_delts_req_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_QSTATUS, + strlen(PRIV_CMD_QSTATUS)) == 0) { + /* Get the status of the WMM queues */ + len = woal_priv_wmm_queue_status_ioctl( + priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_TS_STATUS, + strlen(PRIV_CMD_TS_STATUS)) == 0) { + /* Get the status of the WMM Traffic Streams */ + len = woal_priv_wmm_ts_status_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_QOS_CFG, + strlen(PRIV_CMD_QOS_CFG)) == 0) { + t_u32 action = MLAN_ACT_GET; + if (strlen(buf) == + strlen(CMD_NXP) + strlen(PRIV_CMD_QOS_CFG)) { + pdata = buf; /* GET operation */ + } else { + pdata = buf + strlen(CMD_NXP) + + strlen(PRIV_CMD_QOS_CFG); + action = MLAN_ACT_SET; /* SET operation */ + } + if (MLAN_STATUS_SUCCESS != + woal_priv_qos_cfg(priv, action, pdata)) { + ret = -EFAULT; + goto done; + } + if (action == MLAN_ACT_GET) + len = sizeof(t_u8); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_MAC_CTRL, + strlen(PRIV_CMD_MAC_CTRL)) == 0) { + /* MAC CTRL */ + len = woal_priv_macctrl(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_GETWAP, + strlen(PRIV_CMD_GETWAP)) == 0) { + /* Get WAP */ + len = woal_priv_getwap(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_REGION_CODE, + strlen(PRIV_CMD_REGION_CODE)) == 0) { + /* Region Code */ + len = woal_priv_region_code(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_FWMACADDR, + strlen(PRIV_CMD_FWMACADDR)) == 0) { + /* Set FW MAC address */ + len = woal_priv_fwmacaddr(priv, buf, + priv_cmd.total_len); + goto handled; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_OFFCHANNEL, + strlen(PRIV_CMD_OFFCHANNEL)) == 0) { + if (IS_STA_CFG80211(cfg80211_wext)) { + /* Set offchannel */ + len = woal_priv_offchannel(priv, buf, + priv_cmd.total_len); + } else + len = sprintf(buf, + "CFG80211 is not enabled\n") + + 1; + goto handled; +#endif +#endif + + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DSCP_MAP, + strlen(PRIV_CMD_DSCP_MAP)) == 0) { + /* Set/Get DSCP Map */ + len = woal_priv_set_get_dscp_map(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_VEREXT, + strlen(PRIV_CMD_VEREXT)) == 0) { + /* Get Extended version */ + len = woal_priv_get_driver_verext(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef USB +#ifdef CONFIG_USB_SUSPEND + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_USB_SUSPEND, + strlen(PRIV_CMD_USB_SUSPEND)) == 0) { + /* Makes USB device to suspend */ + len = woal_priv_enter_usb_suspend(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_USB_RESUME, + strlen(PRIV_CMD_USB_RESUME)) == 0) { + /* Makes USB device to resume */ + len = woal_priv_exit_usb_suspend(priv, buf, + priv_cmd.total_len); + goto handled; +#endif /* CONFIG_USB_SUSPEND */ +#endif +#if defined(STA_SUPPORT) && defined(STA_WEXT) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_RADIO_CTRL, + strlen(PRIV_CMD_RADIO_CTRL)) == 0) { + /* Set/Get radio */ + len = woal_priv_radio_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_WMM_CFG, + strlen(PRIV_CMD_WMM_CFG)) == 0) { + /* Implement WMM enable command */ + len = woal_priv_wmm_cfg(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_MIN_BA_THRESH_CFG, + strlen(PRIV_CMD_MIN_BA_THRESH_CFG)) == 0) { + /* Implement Minimum BA threshold configuration command + */ + len = woal_priv_min_ba_threshold_cfg( + priv, buf, priv_cmd.total_len); + goto handled; +#if defined(STA_SUPPORT) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_11D_CFG, + strlen(PRIV_CMD_11D_CFG)) == 0) { + /* Implement 802.11D enable command */ + len = woal_priv_11d_cfg(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_11D_CLR_TBL, + strlen(PRIV_CMD_11D_CLR_TBL)) == 0) { + /* Implement 802.11D clear chan table command */ + len = woal_priv_11d_clr_chan_tbl(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#ifndef OPCHAN + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_WWS_CFG, + strlen(PRIV_CMD_WWS_CFG)) == 0) { + /* Set/Get WWS configuration */ + len = woal_priv_wws_cfg(priv, buf, priv_cmd.total_len); + goto handled; +#endif +#if defined(REASSOCIATION) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_REASSOCTRL, + strlen(PRIV_CMD_REASSOCTRL)) == 0) { + /* Set/Get reassociation settings */ + len = woal_priv_set_get_reassoc(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_TXBUF_CFG, + strlen(PRIV_CMD_TXBUF_CFG)) == 0) { + /* Get Transmit buffer size */ + len = woal_priv_txbuf_cfg(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_AUTH_TYPE, + strlen(PRIV_CMD_AUTH_TYPE)) == 0) { + /* Set/Get auth type */ + len = woal_priv_auth_type(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_POWER_CONS, + strlen(PRIV_CMD_POWER_CONS)) == 0) { + /* Set/get user provisioned local power constraint */ + len = woal_priv_11h_local_pwr_constraint( + priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_HT_STREAM_CFG, + strlen(PRIV_CMD_HT_STREAM_CFG)) == 0) { + /* Set/get HT stream configurations */ + len = woal_priv_ht_stream_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_MIMO_SWITCH, + strlen(PRIV_CMD_MIMO_SWITCH)) == 0) { + /* Set mimo switch configurations */ + len = woal_priv_mimo_switch(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_THERMAL, + strlen(PRIV_CMD_THERMAL)) == 0) { + /* Get thermal reading */ + len = woal_priv_thermal(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_BCN_INTERVAL, + strlen(PRIV_CMD_BCN_INTERVAL)) == 0) { + /* Set/Get beacon interval */ + len = woal_priv_beacon_interval(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef STA_SUPPORT + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_SIGNALEXT_CFG, + strlen(PRIV_CMD_SIGNALEXT_CFG)) == 0) { + /* Set signalext flag */ + len = woal_priv_signalext_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_GET_SIGNAL_EXT_V2, + strlen(PRIV_CMD_GET_SIGNAL_EXT_V2)) == 0) { + /* Get signal info */ + len = woal_priv_get_signal_ext_v2(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_GET_SIGNAL_EXT, + strlen(PRIV_CMD_GET_SIGNAL_EXT)) == 0) { + /* Get signal info */ + len = woal_priv_get_signal_ext(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_GET_SIGNAL, + strlen(PRIV_CMD_GET_SIGNAL)) == 0) { + /* Get signal */ + len = woal_priv_get_signal(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#if defined(STA_SUPPORT) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_PMFCFG, + strlen(PRIV_CMD_PMFCFG)) == 0) { + /* Configure PMF */ + len = woal_priv_set_get_pmfcfg(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_INACTIVITYTO, + strlen(PRIV_CMD_INACTIVITYTO)) == 0) { + /* Get/Set inactivity timeout extend */ + len = woal_priv_inactivity_timeout_ext( + priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_AMSDU_AGGR_CTRL, + strlen(PRIV_CMD_AMSDU_AGGR_CTRL)) == 0) { + /* Enable/Disable amsdu_aggr_ctrl */ + len = woal_priv_11n_amsdu_aggr_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_TX_BF_CAP, + strlen(PRIV_CMD_TX_BF_CAP)) == 0) { + /* Set/Get Transmit beamforming capabilities */ + len = woal_priv_tx_bf_cap_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_SLEEP_PARAMS, + strlen(PRIV_CMD_SLEEP_PARAMS)) == 0) { + /* Configure sleep parameters */ + len = woal_priv_sleep_params_ioctl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DFS_TESTING, + strlen(PRIV_CMD_DFS_TESTING)) == 0) { + /* Set/Get DFS Testing settings */ + len = woal_priv_dfs_testing(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DFS53_CFG, + strlen(PRIV_CMD_DFS53_CFG)) == 0) { + /* Set/Get DFS W53 settings */ + len = woal_priv_dfs53cfg(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ARB_CFG, + strlen(PRIV_CMD_ARB_CFG)) == 0) { + /* Set/Get CFP table codes */ + len = woal_priv_arbcfg(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_CFP_CODE, + strlen(PRIV_CMD_CFP_CODE)) == 0) { + /* Set/Get CFP table codes */ + len = woal_priv_cfp_code(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_CWMODE, + strlen(PRIV_CMD_CWMODE)) == 0) { + /* Set/Get Tx CWMode */ + len = woal_priv_set_get_cwmode(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ANT_CFG, + strlen(PRIV_CMD_ANT_CFG)) == 0) { + /* Set/Get Tx/Rx antenna */ + len = woal_priv_set_get_tx_rx_ant(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_SYSCLOCK, + strlen(PRIV_CMD_SYSCLOCK)) == 0) { + /* Get/Set system clock */ + len = woal_priv_sysclock(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_GET_KEY, + strlen(PRIV_CMD_GET_KEY)) == 0) { + /* Get GTK/PTK */ + len = woal_priv_get_key(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_ASSOCIATE, + strlen(PRIV_CMD_ASSOCIATE)) == 0) { + /* Associate to a specific indexed entry in the + * ScanTable */ + len = woal_priv_associate_ssid_bssid( + priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_TX_BF_CFG, + strlen(PRIV_CMD_TX_BF_CFG)) == 0) { + /* Set/Get Transmit beamforming configuration */ + len = woal_priv_tx_bf_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_BOOTSLEEP, + strlen(PRIV_CMD_BOOTSLEEP)) == 0) { + len = woal_priv_bootsleep(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_PORT_CTRL, + strlen(PRIV_CMD_PORT_CTRL)) == 0) { + /* Set/Get Port Control mode */ + len = woal_priv_port_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_PB_BYPASS, + strlen(PRIV_CMD_PB_BYPASS)) == 0) { + /* Private IOCTL entry to get the By-passed TX packet + * from upper layer */ + len = woal_priv_bypassed_packet(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef WIFI_DIRECT_SUPPORT +#if defined(UAP_CFG80211) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_CFG_NOA, + strlen(PRIV_CMD_CFG_NOA)) == 0) { + /* Set/Get P2P NoA (Notice of Absence) parameters */ + len = woal_priv_cfg_noa(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_CFG_OPP_PS, + strlen(PRIV_CMD_CFG_OPP_PS)) == 0) { + /* Set/Get P2P OPP-PS parameters */ + len = woal_priv_cfg_opp_ps(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_DFS_REPEATER_CFG, + strlen(PRIV_CMD_DFS_REPEATER_CFG)) == 0) { + /* Set/Get DFS_REPEATER mode */ + len = woal_priv_dfs_repeater_cfg(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_MIRACAST_CFG, + strlen(PRIV_CMD_MIRACAST_CFG)) == 0) { + /* Set/Get MIRACAST configuration parameters */ + len = woal_priv_miracast_cfg(priv, buf, + priv_cmd.total_len); + goto handled; +#endif +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_COEX_RX_WINSIZE, + strlen(PRIV_CMD_COEX_RX_WINSIZE)) == 0) { + /* Set/Get control to coex RX window size */ + len = woal_priv_coex_rx_winsize(priv, buf, + priv_cmd.total_len); + goto handled; +#ifdef PCIE + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_PCIE_REG_RW, + strlen(PRIV_CMD_PCIE_REG_RW)) == 0) { + /* Read/Write PCIE register */ + len = woal_priv_pcie_reg_rw(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_PCIE_BAR0_REG_RW, + strlen(PRIV_CMD_PCIE_BAR0_REG_RW)) == 0) { + /* Read/Write PCIE register/memory from BAR0 */ + len = woal_priv_pcie_bar0_reg_rw(priv, buf, + priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_GET_SENSOR_TEMP, + strlen(PRIV_CMD_GET_SENSOR_TEMP)) == 0) { + /* Get SOC temperature */ + len = woal_priv_get_sensor_temp(priv, buf, + priv_cmd.total_len); + goto handled; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DFS_OFFLOAD, + strlen(PRIV_CMD_DFS_OFFLOAD)) == 0) { + /* Enable/disable DFS offload */ + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + len = woal_priv_dfs_offload_enable( + priv, buf, priv_cmd.total_len); + else + len = sprintf(buf, + "CFG80211 is not enabled\n") + + 1; + goto handled; +#endif +#endif +#if defined(UAP_SUPPORT) + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_EXTEND_CHAN_SWITCH, + strlen(PRIV_CMD_EXTEND_CHAN_SWITCH)) == 0) { + /* Extended channel switch */ + len = woal_priv_extend_channel_switch( + priv, buf, priv_cmd.total_len); + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DYN_BW, + strlen(PRIV_CMD_DYN_BW)) == 0) { + /* Set/Get dynamic bandwidth */ + len = woal_priv_config_dyn_bw(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_IND_RST_CFG, + strlen(PRIV_CMD_IND_RST_CFG)) == 0) { + /* Set/Get out band independent reset */ + len = woal_priv_ind_rst_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_PER_PKT_CFG, + strlen(PRIV_CMD_PER_PKT_CFG)) == 0) { + /* Get/Set per packet Txctl and Rxinfo configuration */ + len = woal_priv_per_pkt_cfg(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_DEAUTH_CTRL, + strlen(PRIV_CMD_DEAUTH_CTRL)) == 0) { + len = woal_priv_deauth_ctrl(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_11AXCFG, + strlen(PRIV_CMD_11AXCFG)) == 0) { + pdata = buf + strlen(CMD_NXP) + + strlen(PRIV_CMD_11AXCFG); + len = priv_cmd.used_len - strlen(PRIV_CMD_11AXCFG) - + strlen(CMD_NXP); + len = woal_priv_11axcfg_cmd(priv, pdata, len, + priv_cmd.total_len); + len += strlen(PRIV_CMD_11AXCFG) + strlen(CMD_NXP); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_TWT_SETUP, + strlen(PRIV_CMD_TWT_SETUP)) == 0) { + pdata = buf + strlen(CMD_NXP) + + strlen(PRIV_CMD_TWT_SETUP); + len = priv_cmd.used_len - strlen(PRIV_CMD_TWT_SETUP) - + strlen(CMD_NXP); + len = woal_priv_twt_setup(priv, pdata, len, + priv_cmd.total_len); + len += strlen(PRIV_CMD_TWT_SETUP) + strlen(CMD_NXP); + goto handled; + + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_TWT_TEARDOWN, + strlen(PRIV_CMD_TWT_TEARDOWN)) == 0) { + pdata = buf + strlen(CMD_NXP) + + strlen(PRIV_CMD_TWT_TEARDOWN); + len = priv_cmd.used_len - + strlen(PRIV_CMD_TWT_TEARDOWN) - strlen(CMD_NXP); + len = woal_priv_twt_teardown(priv, pdata, len, + priv_cmd.total_len); + len += strlen(PRIV_CMD_TWT_TEARDOWN) + strlen(CMD_NXP); + goto handled; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + } else if (strnicmp(buf + strlen(CMD_NXP), + PRIV_CMD_GET_CFG_CHAN_LIST, + strlen(PRIV_CMD_GET_CFG_CHAN_LIST)) == 0) { + /* Get txpwrlimit */ + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + len = woal_priv_getcfgchanlist( + priv, buf, priv_cmd.total_len); + else + len = sprintf(buf, + "CFG80211 is not enabled\n") + + 1; + goto handled; +#endif + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_LPM, + strlen(PRIV_CMD_LPM)) == 0) { + /* Set/Get low power mode */ + len = woal_priv_set_get_lpm(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf + strlen(CMD_NXP), PRIV_CMD_TP_STATE, + strlen(PRIV_CMD_TP_STATE)) == 0) { + /* Set/Get TP accounting state */ + len = woal_priv_set_tp_state(priv, buf, + priv_cmd.total_len); + goto handled; + } else { + PRINTM(MERROR, + "Unknown NXP PRIVATE command %s, ignored\n", + buf); + ret = -EFAULT; + goto done; + } + } +#ifdef STA_SUPPORT + if (strncmp(buf, "RSSILOW-THRESHOLD", strlen("RSSILOW-THRESHOLD")) == + 0) { + pdata = buf + strlen("RSSILOW-THRESHOLD") + 1; + if (MLAN_STATUS_SUCCESS != + woal_set_rssi_low_threshold(priv, pdata, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SCAN-CFG", strlen("SCAN-CFG")) == 0) { + PRINTM(MIOCTL, "Set SCAN CFG\n"); + if (MLAN_STATUS_SUCCESS != + woal_set_scan_cfg(priv, buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RSSI", strlen("RSSI")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + if (bss_info.media_connected) { + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, + &signal)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "%.32s rssi %d\n", + bss_info.ssid.ssid, signal.bcn_rssi_avg) + + 1; + } else { + len = sprintf(buf, "OK\n") + 1; + } + } else if (strncmp(buf, "LINKSPEED", strlen("LINKSPEED")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_data_rate(priv, MLAN_ACT_GET, &rate)) { + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "tx rate=%d\n", (int)rate.rate); + len = sprintf(buf, "LinkSpeed %d\n", + (int)(rate.rate * 500000 / 1000000)) + + 1; + } else +#endif + if (strncmp(buf, "MACADDR", strlen("MACADDR")) == 0) { + len = sprintf(buf, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + priv->current_addr[0], priv->current_addr[1], + priv->current_addr[2], priv->current_addr[3], + priv->current_addr[4], priv->current_addr[5]) + + 1; + } +#ifdef STA_SUPPORT + else if (strncmp(buf, "GETPOWER", strlen("GETPOWER")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_get_powermode(priv, &power_mode)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "powermode = %d\n", power_mode) + 1; + } else if (strncmp(buf, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE)) { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_ACTIVE; + PRINTM(MIOCTL, "Set Active Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE)) { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_PASSIVE; + PRINTM(MIOCTL, "Set Passive Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "POWERMODE", strlen("POWERMODE")) == 0) { + pdata = buf + strlen("POWERMODE") + 1; + if (!moal_extflg_isset(priv->phandle, EXT_HW_TEST)) { + if (MLAN_STATUS_SUCCESS != + woal_set_powermode(priv, pdata)) { + ret = -EFAULT; + goto done; + } + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SETROAMING", strlen("SETROAMING")) == 0) { + pdata = buf + strlen("SETROAMING") + 1; +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) + goto done; +#endif +#endif +#ifdef STA_CFG80211 + if (*pdata == '1') { + priv->roaming_enabled = MTRUE; + PRINTM(MIOCTL, "Roaming enabled\n"); + } else if (*pdata == '0') { + priv->roaming_enabled = MFALSE; + PRINTM(MIOCTL, "Roaming disabled\n"); + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "ROAM", strlen("ROAM")) == 0) { + pdata = buf + strlen("ROAM") + 1; +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) + goto done; +#endif +#endif +#ifdef STA_CFG80211 + if (*pdata == '1') { + priv->roaming_enabled = MTRUE; + PRINTM(MIOCTL, "Roaming enabled\n"); + } else if (*pdata == '0') { + priv->roaming_enabled = MFALSE; + PRINTM(MIOCTL, "Roaming disabled\n"); + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "COUNTRY", strlen("COUNTRY")) == 0) { + copy_len = strlen(buf) - strlen("COUNTRY") - 1; + if (copy_len > COUNTRY_CODE_LEN || copy_len <= 0) { + PRINTM(MERROR, "Invalid country length\n"); + ret = -EFAULT; + goto done; + } + memset(country_code, 0, sizeof(country_code)); + moal_memcpy_ext(priv->phandle, country_code, + buf + strlen("COUNTRY") + 1, copy_len, + COUNTRY_CODE_LEN); + PRINTM(MIOCTL, "Set COUNTRY %s\n", country_code); + if (moal_extflg_isset(priv->phandle, EXT_CNTRY_TXPWR)) { + if (MLAN_STATUS_SUCCESS != + woal_request_country_power_table(priv, + country_code)) { + ret = -EFAULT; + goto done; + } + } +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + PRINTM(MIOCTL, "Notify country code=%s\n", + country_code); + if (!moal_extflg_isset(priv->phandle, + EXT_DISABLE_REGD_BY_DRIVER)) + regulatory_hint(priv->wdev->wiphy, + country_code); + len = sprintf(buf, "OK\n") + 1; + goto done; + } +#endif + if (MLAN_STATUS_SUCCESS != + woal_set_region_code(priv, country_code)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (memcmp(buf, WEXT_CSCAN_HEADER, WEXT_CSCAN_HEADER_SIZE) == + 0) { + PRINTM(MIOCTL, "Set Combo Scan\n"); + if (MLAN_STATUS_SUCCESS != + woal_set_combo_scan(priv, buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "GETBAND", strlen("GETBAND")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_band(priv, &band)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "Band %d\n", band) + 1; + } else if (strncmp(buf, "SETBAND", strlen("SETBAND")) == 0) { + pband = buf + strlen("SETBAND") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_band(priv, pband)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } +#endif + else if (strncmp(buf, "START", strlen("START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "STOP", strlen("STOP")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } +#ifdef UAP_SUPPORT + else if (strncmp(buf, "AP_BSS_START", strlen("AP_BSS_START")) == 0) { + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); + if (ret) + goto done; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "AP_BSS_STOP", strlen("AP_BSS_STOP")) == 0) { + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + if (ret) + goto done; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "AP_SET_CFG", strlen("AP_SET_CFG")) == 0) { + if (priv_cmd.total_len <= strlen("AP_SET_CFG") + 1) + goto done; + pdata = buf + strlen("AP_SET_CFG") + 1; + ret = woal_uap_set_ap_cfg(priv, pdata, + priv_cmd.total_len - + strlen("AP_SET_CFG") - 1); + if (ret) + goto done; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "WL_FW_RELOAD", strlen("WL_FW_RELOAD")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "AP_GET_STA_LIST", strlen("AP_GET_STA_LIST")) == + 0) { + /* TODO Add STA list support */ + len = sprintf(buf, "OK\n") + 1; + } +#endif + else if (strncmp(buf, "SETSUSPENDOPT", strlen("SETSUSPENDOPT")) == 0) { + /* it will be done by GUI */ + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SETSUSPENDMODE", strlen("SETSUSPENDMODE")) == + 0) { + /* it will be done by GUI */ + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-START", + strlen("BTCOEXSCAN-START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-STOP", strlen("BTCOEXSCAN-STOP")) == + 0) { + len = sprintf(buf, "OK\n") + 1; + } +#ifdef STA_SUPPORT + else if (strncmp(buf, "BGSCAN-START", strlen("BGSCAN-START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-CONFIG", strlen("BGSCAN-CONFIG")) == + 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_bg_scan(priv, buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MTRUE; + priv->bg_scan_reported = MFALSE; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-STOP", strlen("BGSCAN-STOP")) == 0) { + if (priv->bg_scan_start && !priv->scan_cfg.rssi_threshold) { + if (MLAN_STATUS_SUCCESS != + woal_stop_bg_scan(priv, MOAL_NO_WAIT)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-START", strlen("RXFILTER-START")) == + 0) { +#ifdef MEF_CFG_RX_FILTER + ret = woal_set_rxfilter(priv, MTRUE); + if (ret) + goto done; +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-STOP", strlen("RXFILTER-STOP")) == + 0) { +#ifdef MEF_CFG_RX_FILTER + ret = woal_set_rxfilter(priv, MFALSE); + if (ret) + goto done; +#endif + len = sprintf(buf, "OK\n") + 1; + } +#ifdef STA_CFG80211 + else if (strncmp(buf, "GET_EVENT", strlen("GET_EVENT")) == 0) { + if (IS_STA_CFG80211(cfg80211_wext)) { + if (priv->last_event & EVENT_BG_SCAN_REPORT) + woal_inform_bss_from_scan_result( + priv, NULL, MOAL_IOCTL_WAIT); + } + len = sprintf(buf, "EVENT=%d\n", priv->last_event) + 1; + priv->last_event = 0; + } else if (strncmp(buf, "GET_802_11W", strlen("GET_802_11W")) == 0) { + len = sprintf(buf, "802_11W=ENABLED\n") + 1; + } +#endif /* STA_CFG80211 */ + else if (strncmp(buf, "RXFILTER-ADD", strlen("RXFILTER-ADD")) == 0) { + pdata = buf + strlen("RXFILTER-ADD") + 1; + if (MLAN_STATUS_SUCCESS != woal_add_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-REMOVE", strlen("RXFILTER-REMOVE")) == + 0) { + pdata = buf + strlen("RXFILTER-REMOVE") + 1; + if (MLAN_STATUS_SUCCESS != woal_remove_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "QOSINFO", strlen("QOSINFO")) == 0) { + pdata = buf + strlen("QOSINFO") + 1; +#ifdef STA_SUPPORT + if (MLAN_STATUS_SUCCESS != + woal_priv_qos_cfg(priv, MLAN_ACT_SET, pdata)) { + ret = -EFAULT; + goto done; + } +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SLEEPPD", strlen("SLEEPPD")) == 0) { + pdata = buf + strlen("SLEEPPD") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_sleeppd(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SET_AP_WPS_P2P_IE", + strlen("SET_AP_WPS_P2P_IE")) == 0) { + pdata = buf + strlen("SET_AP_WPS_P2P_IE") + 1; + /* Android cmd format: + * "SET_AP_WPS_P2P_IE 1" -- beacon IE + * "SET_AP_WPS_P2P_IE 2" -- proberesp IE + * "SET_AP_WPS_P2P_IE 4" -- assocresp IE + */ +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 2, 0) + if (MLAN_STATUS_SUCCESS != + woal_set_ap_wps_p2p_ie(priv, (t_u8 *)pdata, + priv_cmd.used_len - + strlen("SET_AP_WPS_P2P_IE") - + 1)) { + ret = -EFAULT; + goto done; + } +#endif +#endif + len = sprintf(buf, "OK\n") + 1; + } +#endif + else if (strncmp(buf, "P2P_DEV_ADDR", strlen("P2P_DEV_ADDR")) == 0) { + memset(buf, 0x0, (size_t)priv_cmd.total_len); + moal_memcpy_ext(priv->phandle, buf, priv->current_addr, + ETH_ALEN, (t_u32)priv_cmd.total_len); + len = ETH_ALEN; + } else if (strncmp(buf, ("P2P_GET_NOA"), strlen("P2P_GET_NOA")) == 0) { + /* TODO + * Just return '\0' + */ + memset(buf, 0x0, (size_t)priv_cmd.total_len); + *buf = 0; + len = 1; + } else if (strnicmp(buf, "MIRACAST", strlen("MIRACAST")) == 0) { + pdata = buf + strlen("MIRACAST"); + /* Android cmd format: + * "MIRACAST 0" -- disabled + * "MIRACAST 1" -- operating as source + * "MIRACAST 2" -- operating as sink + */ +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (MLAN_STATUS_SUCCESS != + woal_set_miracast_mode(priv, (t_u8 *)pdata, + priv_cmd.used_len - + strlen("MIRACAST"))) { + ret = -EFAULT; + goto done; + } +#endif +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strnicmp(buf, "SCAN_TIMING", strlen("SCAN_TIMING")) == 0) { +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (MLAN_STATUS_SUCCESS != + woal_set_scan_chan_gap(priv, buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } +#endif +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strnicmp(buf, "BA_WSIZE_RX", strlen("BA_WSIZE_RX")) == 0) { + pdata = buf + strlen("BA_WSIZE_RX") + 1; + len = priv_cmd.total_len - strlen("BA_WSIZE_RX") - 1; + if (MLAN_STATUS_SUCCESS != + woal_set_rx_ba_winsize(priv, pdata, len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strnicmp(buf, "BA_WSIZE_TX", strlen("BA_WSIZE_TX")) == 0) { + pdata = buf + strlen("BA_WSIZE_TX") + 1; + len = priv_cmd.total_len - strlen("BA_WSIZE_TX") - 1; + if (MLAN_STATUS_SUCCESS != + woal_set_tx_ba_winsize(priv, pdata, len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "FAKE_SCAN_COMPLETE", + strlen("FAKE_SCAN_COMPLETE")) == 0) { + pdata = buf + strlen("FAKE_SCAN_COMPLETE") + 1; +#ifdef STA_CFG80211 + if (*pdata == '1') { + priv->fake_scan_complete = MTRUE; + PRINTM(MIOCTL, "fake scan complete enabled\n"); + } else if (*pdata == '0') { + priv->fake_scan_complete = MFALSE; + PRINTM(MIOCTL, "fake scan complete disabled\n"); + } +#endif + len = sprintf(buf, "OK\n") + 1; + } +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#ifdef WIFI_DIRECT_SUPPORT + else if (strncmp(buf, "P2P_PERIODIC_SLEEP", + strlen("P2P_PERIODIC_SLEEP")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_p2p_ps_cfg(priv, buf, priv_cmd.total_len)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } +#endif +#endif + else if (strncmp(buf, "WLS_BATCHING", strlen("WLS_BATCHING")) == 0) { + /* TODO */ + len = sprintf(buf, "OK\n") + 1; + } +#if defined(UAP_SUPPORT) + else if (strncmp(buf, "P2P_ECSA", strlen("P2P_ECSA")) == 0) { + len = woal_priv_p2p_ecsa(priv, buf, priv_cmd.total_len); + } +#endif + else if (strncmp(buf, "FAKEMAC", strlen("FAKEMAC")) == 0) { + len = woal_priv_config_random_mac(priv, buf, + priv_cmd.total_len); + } else if (strncmp(buf, PRIV_CMD_CLOUD_KEEP_ALIVE, + strlen(PRIV_CMD_CLOUD_KEEP_ALIVE)) == 0) { + len = woal_priv_cloud_keep_alive(priv, buf, priv_cmd.total_len); + } else if (strnicmp(buf, PRIV_CMD_TX_RX_HISTOGRAM, + strlen(PRIV_CMD_TX_RX_HISTOGRAM)) == 0) { + /* Get TX/RX histogram statistic */ + len = woal_priv_get_rx_tx_histogram(priv, buf, + priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf, PRIV_CMD_GET_CHNRGPWR, + strlen(PRIV_CMD_GET_CHNRGPWR)) == 0) { + /* Get chnrgpwr */ + len = woal_priv_get_chnrgpwr(priv, buf, priv_cmd.total_len); + goto handled; + } else if (strnicmp(buf, PRIV_CMD_GET_TXPWR_LIMIT, + strlen(PRIV_CMD_GET_TXPWR_LIMIT)) == 0) { + /* Get txpwrlimit */ + len = woal_priv_get_txpwrlimit(priv, buf, priv_cmd.total_len); + goto handled; + } else { + PRINTM(MIOCTL, "Unknown PRIVATE command: %s, ignored\n", buf); + ret = -EFAULT; + goto done; + } + +handled: + PRINTM(MIOCTL, "PRIV Command return: %s, length=%d\n", buf, len); + + if (len > 0) { + priv_cmd.used_len = len; + if (priv_cmd.used_len <= priv_cmd.total_len) { + memset(buf + priv_cmd.used_len, 0, + (size_t)(CMD_BUF_LEN - priv_cmd.used_len)); + if (copy_to_user(cmd_buf, buf, priv_cmd.total_len)) { + PRINTM(MERROR, + "%s: failed to copy data to user buffer\n", + __FUNCTION__); + ret = -EFAULT; + goto done; + } + if (copy_to_user(req->ifr_data, &priv_cmd, + sizeof(android_wifi_priv_cmd))) { + PRINTM(MERROR, + "%s: failed to copy command header to user buffer\n", + __FUNCTION__); + ret = -EFAULT; + } + } else { + PRINTM(MERROR, + "%s: the buffer supplied by appl is too small (supplied: %d, used: %d)\n", + __FUNCTION__, priv_cmd.total_len, + priv_cmd.used_len); + ret = -EFAULT; + } + } else { + ret = len; + } + +done: + kfree(buf); + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief Create a brief scan resp to relay basic BSS info to the app layer + * + * When the beacon/probe response has not been buffered, use the saved BSS + * information available to provide a minimum response for the application + * ioctl retrieval routines. Include: + * - Timestamp + * - Beacon Period + * - Capabilities (including WMM Element if available) + * - SSID + * + * @param ppbuffer Output parameter: Buffer used to create basic scan rsp + * @param pbss_desc Pointer to a BSS entry in the scan table to create + * scan response from for delivery to the application layer + * + * @return N/A + */ +void wlan_scan_create_brief_table_entry(t_u8 **ppbuffer, + BSSDescriptor_t *pbss_desc) +{ + t_u8 *ptmp_buf = *ppbuffer; + t_u8 tmp_ssid_hdr[2]; + t_u8 ie_len = 0; + + ENTER(); + + moal_memcpy_ext(NULL, ptmp_buf, pbss_desc->time_stamp, + sizeof(pbss_desc->time_stamp), + sizeof(pbss_desc->time_stamp)); + ptmp_buf += sizeof(pbss_desc->time_stamp); + + moal_memcpy_ext(NULL, ptmp_buf, &pbss_desc->beacon_period, + sizeof(pbss_desc->beacon_period), + sizeof(pbss_desc->beacon_period)); + ptmp_buf += sizeof(pbss_desc->beacon_period); + + moal_memcpy_ext(NULL, ptmp_buf, &pbss_desc->cap_info, + sizeof(pbss_desc->cap_info), + sizeof(pbss_desc->cap_info)); + ptmp_buf += sizeof(pbss_desc->cap_info); + + tmp_ssid_hdr[0] = 0; /* Element ID for SSID is zero */ + tmp_ssid_hdr[1] = pbss_desc->ssid.ssid_len; + moal_memcpy_ext(NULL, ptmp_buf, tmp_ssid_hdr, sizeof(tmp_ssid_hdr), + sizeof(tmp_ssid_hdr)); + ptmp_buf += sizeof(tmp_ssid_hdr); + + moal_memcpy_ext(NULL, ptmp_buf, pbss_desc->ssid.ssid, + pbss_desc->ssid.ssid_len, pbss_desc->ssid.ssid_len); + ptmp_buf += pbss_desc->ssid.ssid_len; + + if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) { + ie_len = sizeof(IEEEtypes_Header_t) + + pbss_desc->wmm_ie.vend_hdr.len; + moal_memcpy_ext(NULL, ptmp_buf, &pbss_desc->wmm_ie, ie_len, + ie_len); + ptmp_buf += ie_len; + } + + if (pbss_desc->pwpa_ie) { + if ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == WPA_IE) { + ie_len = sizeof(IEEEtypes_Header_t) + + (*(pbss_desc->pwpa_ie)).vend_hdr.len; + moal_memcpy_ext(NULL, ptmp_buf, pbss_desc->pwpa_ie, + ie_len, ie_len); + } + + ptmp_buf += ie_len; + } + if (pbss_desc->prsn_ie) { + if ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == RSN_IE) { + ie_len = sizeof(IEEEtypes_Header_t) + + (*(pbss_desc->prsn_ie)).ieee_hdr.len; + moal_memcpy_ext(NULL, ptmp_buf, pbss_desc->prsn_ie, + ie_len, ie_len); + } + + ptmp_buf += ie_len; + } + *ppbuffer = ptmp_buf; + LEAVE(); +} + +/** + * @brief Create a wlan_ioctl_get_scan_table_entry for a given BSS + * Descriptor for inclusion in the ioctl response to the user space + * application. + * + * + * @param pbss_desc Pointer to a BSS entry in the scan table to form + * scan response from for delivery to the application layer + * @param ppbuffer Output parameter: Buffer used to output scan return + * struct + * @param pspace_left Output parameter: Number of bytes available in the + * response buffer. + * + * @return MLAN_STATUS_SUCCESS, or < 0 with IOCTL error code + */ +int wlan_get_scan_table_ret_entry(BSSDescriptor_t *pbss_desc, t_u8 **ppbuffer, + int *pspace_left) +{ + wlan_ioctl_get_scan_table_entry *prsp_entry; + wlan_ioctl_get_scan_table_entry tmp_rsp_entry; + int space_needed; + t_u8 *pcurrent; + int variable_size; + + const int fixed_size = sizeof(wlan_ioctl_get_scan_table_entry); + + ENTER(); + + pcurrent = *ppbuffer; + + /* The variable size returned is the stored beacon size */ + variable_size = pbss_desc->beacon_buf_size; + + /* If we stored a beacon and its size was zero, set the variable + * size return value to the size of the brief scan response + * wlan_scan_create_brief_table_entry creates. Also used if + * we are not configured to store beacons in the first place + */ + if (!variable_size) { + variable_size = pbss_desc->ssid.ssid_len + 2; + variable_size += (sizeof(pbss_desc->beacon_period) + + sizeof(pbss_desc->time_stamp) + + sizeof(pbss_desc->cap_info)); + if (pbss_desc->wmm_ie.vend_hdr.element_id == WMM_IE) { + variable_size += (sizeof(IEEEtypes_Header_t) + + pbss_desc->wmm_ie.vend_hdr.len); + } + + if (pbss_desc->pwpa_ie) { + if ((*(pbss_desc->pwpa_ie)).vend_hdr.element_id == + WPA_IE) { + variable_size += + (sizeof(IEEEtypes_Header_t) + + (*(pbss_desc->pwpa_ie)).vend_hdr.len); + } + } + + if (pbss_desc->prsn_ie) { + if ((*(pbss_desc->prsn_ie)).ieee_hdr.element_id == + RSN_IE) { + variable_size += + (sizeof(IEEEtypes_Header_t) + + (*(pbss_desc->prsn_ie)).ieee_hdr.len); + } + } + } + + space_needed = fixed_size + variable_size; + + PRINTM(MINFO, "GetScanTable: need(%d), left(%d)\n", space_needed, + *pspace_left); + + if (space_needed >= *pspace_left) { + *pspace_left = 0; + LEAVE(); + return -E2BIG; + } + + *pspace_left -= space_needed; + + tmp_rsp_entry.fixed_field_length = + (sizeof(tmp_rsp_entry) - + sizeof(tmp_rsp_entry.fixed_field_length) - + sizeof(tmp_rsp_entry.bss_info_length)); + + moal_memcpy_ext(NULL, tmp_rsp_entry.fixed_fields.bssid, + pbss_desc->mac_address, + sizeof(prsp_entry->fixed_fields.bssid), + 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.chan_load = pbss_desc->chan_load; + 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 + */ + moal_memcpy_ext(NULL, pcurrent, &tmp_rsp_entry, fixed_size, fixed_size); + pcurrent += fixed_size; + + if (pbss_desc->pbeacon_buf) { + /* + * Copy variable length elements to user space + */ + moal_memcpy_ext(NULL, pcurrent, pbss_desc->pbeacon_buf, + pbss_desc->beacon_buf_size, + pbss_desc->beacon_buf_size); + + pcurrent += pbss_desc->beacon_buf_size; + } else { + wlan_scan_create_brief_table_entry(&pcurrent, pbss_desc); + } + + *ppbuffer = pcurrent; + + LEAVE(); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd Command + * + * @return 0 --success, otherwise fail + */ +int woal_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int ret = 0; + + ENTER(); + + PRINTM(MINFO, "woal_do_ioctl: ioctl cmd = 0x%x\n", cmd); + switch (cmd) { + case WOAL_ANDROID_DEF_CMD: + /** android default ioctl ID is SIOCDEVPRIVATE + 1 */ + ret = woal_android_priv_cmd(dev, req); + break; + case WOAL_CUSTOM_IE_CFG: + ret = woal_custom_ie_ioctl(dev, req); + break; + case WOAL_MGMT_FRAME_TX: + ret = woal_send_host_packet(dev, req); + break; + case WOAL_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/mxm_wifiex/wlan_src/mlinux/moal_eth_ioctl.h b/mxm_wifiex/wlan_src/mlinux/moal_eth_ioctl.h new file mode 100644 index 0000000..57b203e --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_eth_ioctl.h @@ -0,0 +1,675 @@ + +/** @file moal_eth_ioctl.h + * + * @brief This file contains definition for private IOCTL call. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 01/05/2012: initial version +********************************************************/ +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#endif + +#ifndef _WOAL_ETH_PRIV_H_ +#define _WOAL_ETH_PRIV_H_ + +/** Command disabled */ +#define CMD_DISABLED 0 +/** Command enabled */ +#define CMD_ENABLED 1 +/** Command get */ +#define CMD_GET 2 + +/** 2K bytes */ +#define WOAL_2K_BYTES 2000 + +/** NXP private command identifier string */ +#define CMD_NXP "MRVL_CMD" + +/** Private command: Version */ +#define PRIV_CMD_VERSION "version" +/** Private command: Band cfg */ +#define PRIV_CMD_BANDCFG "bandcfg" +/** Private command: Host cmd */ +#define PRIV_CMD_HOSTCMD "hostcmd" +/** Private command: Custom IE config*/ +#define PRIV_CMD_CUSTOMIE "customie" +/** Private command: HT Tx Cfg */ +#define PRIV_CMD_HTTXCFG "httxcfg" +/** Private command: HT Cap Info */ +#define PRIV_CMD_HTCAPINFO "htcapinfo" +/** Private command: Add BA para */ +#define PRIV_CMD_ADDBAPARA "addbapara" +/** Private command: Aggragation priority table */ +#define PRIV_CMD_AGGRPRIOTBL "aggrpriotbl" +/** Private command: Add BA reject cfg */ +#define PRIV_CMD_ADDBAREJECT "addbareject" +/** Private command: Delete BA */ +#define PRIV_CMD_DELBA "delba" +/** Private command: Reject Addba Req */ +#define PRIV_CMD_REJECTADDBAREQ "rejectaddbareq" +/** Private command: 11AC Cfg */ +#define PRIV_CMD_VHTCFG "vhtcfg" +/** Private command: 11AC Oper Mode Cfg */ +#define PRIV_CMD_OPERMODECFG "opermodecfg" +#define PRIV_CMD_DATARATE "getdatarate" +#define PRIV_CMD_TXRATECFG "txratecfg" +#define PRIV_CMD_GETLOG "getlog" +#define PRIV_CMD_ESUPPMODE "esuppmode" +#define PRIV_CMD_PASSPHRASE "passphrase" +#define PRIV_CMD_DEAUTH "deauth" +#ifdef UAP_SUPPORT +#define PRIV_CMD_AP_DEAUTH "apdeauth" +#define PRIV_CMD_GET_STA_LIST "getstalist" +#define PRIV_CMD_BSS_CONFIG "bssconfig" +#endif +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#define PRIV_CMD_BSSROLE "bssrole" +#endif +#endif +#ifdef STA_SUPPORT +#define PRIV_CMD_GETSCANTABLE "getscantable" +#define PRIV_CMD_GETCHANSTATS "getchanstats" +typedef struct _chan_stats { + /** Number of records in the chan_stats */ + t_u32 num_in_chan_stats; + /** channel statistics */ + ChanStatistics_t stats[]; +} chan_stats; +#define PRIV_CMD_SETUSERSCAN "setuserscan" +#define PRIV_CMD_EXTCAPCFG "extcapcfg" +#define PRIV_CMD_CANCELSCAN "cancelscan" +#endif +#define PRIV_CMD_DEEPSLEEP "deepsleep" +#define PRIV_CMD_IPADDR "ipaddr" +#define PRIV_CMD_WPSSESSION "wpssession" +#define PRIV_CMD_OTPUSERDATA "otpuserdata" +#define PRIV_CMD_COUNTRYCODE "countrycode" +#define PRIV_CMD_TCPACKENH "tcpackenh" +#ifdef REASSOCIATION +#define PRIV_CMD_ASSOCESSID "assocessid" +#define PRIV_CMD_ASSOCBSSID "assocessid_bssid" +#endif +#define PRIV_CMD_WAKEUPREASON "wakeupreason" +#ifdef STA_SUPPORT +#define PRIV_CMD_LISTENINTERVAL "listeninterval" +#endif +#ifdef DEBUG_LEVEL1 +#define PRIV_CMD_DRVDBG "drvdbg" +#endif +#define PRIV_CMD_HSCFG "hscfg" +#define PRIV_CMD_HSSETPARA "hssetpara" +#define PRIV_CMD_MGMT_FILTER "mgmtfilter" +#define PRIV_CMD_SCANCFG "scancfg" +#define PRIV_CMD_GETNLNUM "getnlnum" +#define PRIV_CMD_AGGRCTRL "aggrctrl" +#ifdef USB +#define PRIV_CMD_USBAGGRCTRL "usbaggrctrl" +#endif +#define PRIV_CMD_SET_BSS_MODE "setbssmode" +#ifdef STA_SUPPORT +#define PRIV_CMD_SET_AP "setap" +#define PRIV_CMD_SET_POWER "setpower" +#define PRIV_CMD_SET_ESSID "setessid" +#define PRIV_CMD_SET_AUTH "setauth" +#define PRIV_CMD_GET_AP "getap" +#define PRIV_CMD_GET_POWER "getpower" +#define PRIV_CMD_PSMODE "psmode" +#endif +#define PRIV_CMD_WARMRESET "warmreset" +#define PRIV_CMD_TXPOWERCFG "txpowercfg" +#define PRIV_CMD_PSCFG "pscfg" +#define PRIV_CMD_BCNTIMEOUTCFG "bcntimeoutcfg" +#define PRIV_CMD_SLEEPPD "sleeppd" +#define PRIV_CMD_TXCONTROL "txcontrol" +#define PRIV_CMD_REGRDWR "regrdwr" +#define PRIV_CMD_RDEEPROM "rdeeprom" +#define PRIV_CMD_MEMRDWR "memrdwr" +#ifdef SDIO +#define PRIV_CMD_SDCMD52RW "sdcmd52rw" +#endif +#define PRIV_CMD_HOTSPOTCFG "hotspotcfg" +#define PRIV_CMD_MGMT_FRAME_CTRL "mgmtframectrl" +#define PRIV_CMD_QCONFIG "qconfig" +#define PRIV_CMD_ADDTS "addts" +#define PRIV_CMD_DELTS "delts" +#define PRIV_CMD_QSTATUS "qstatus" +#define PRIV_CMD_TS_STATUS "ts_status" +#define PRIV_CMD_QOS_CFG "qoscfg" +#define PRIV_CMD_MAC_CTRL "macctrl" +#define PRIV_CMD_GETWAP "getwap" +#define PRIV_CMD_REGION_CODE "regioncode" +#define PRIV_CMD_CFPINFO "cfpinfo" +#define PRIV_CMD_FWMACADDR "fwmacaddr" +#define PRIV_CMD_OFFCHANNEL "offchannel" +#define PRIV_CMD_DSCP_MAP "dscpmap" +/** Private command: Verext */ +#define PRIV_CMD_VEREXT "verext" +#ifdef CONFIG_USB_SUSPEND +#define PRIV_CMD_USB_SUSPEND "usbsuspend" +#define PRIV_CMD_USB_RESUME "usbresume" +#endif /* CONFIG_USB_SUSPEND */ +#if defined(STA_SUPPORT) && defined(STA_WEXT) +#define PRIV_CMD_RADIO_CTRL "radioctrl" +#endif +#define PRIV_CMD_WMM_CFG "wmmcfg" +#define PRIV_CMD_MIN_BA_THRESH_CFG "min_ba_threshold" +#if defined(STA_SUPPORT) +#define PRIV_CMD_11D_CFG "11dcfg" +#define PRIV_CMD_11D_CLR_TBL "11dclrtbl" +#endif +#ifndef OPCHAN +#define PRIV_CMD_WWS_CFG "wwscfg" +#endif +#if defined(REASSOCIATION) +#define PRIV_CMD_REASSOCTRL "reassoctrl" +#endif +#define PRIV_CMD_TXBUF_CFG "txbufcfg" +#ifdef STA_SUPPORT +#define PRIV_CMD_AUTH_TYPE "authtype" +#endif +#define PRIV_CMD_POWER_CONS "powercons" +#define PRIV_CMD_HT_STREAM_CFG "htstreamcfg" +#define PRIV_CMD_MIMO_SWITCH "mimoswitch" +#define PRIV_CMD_THERMAL "thermal" +#define PRIV_CMD_BCN_INTERVAL "bcninterval" +#ifdef STA_SUPPORT +#define PRIV_CMD_GET_SIGNAL "getsignal" +#define PRIV_CMD_SIGNALEXT_CFG "signalextcfg" +#define PRIV_CMD_GET_SIGNAL_EXT_V2 "getsignalextv2" +#define PRIV_CMD_GET_SIGNAL_EXT "getsignalext" +#endif +#if defined(STA_SUPPORT) +#define PRIV_CMD_PMFCFG "pmfcfg" +#endif +#define PRIV_CMD_INACTIVITYTO "inactivityto" +#define PRIV_CMD_AMSDU_AGGR_CTRL "amsduaggrctrl" +#define PRIV_CMD_TX_BF_CAP "httxbfcap" +#ifdef SDIO +#define PRIV_CMD_SDIO_CLOCK "sdioclock" +#endif +#ifdef SDIO +#define PRIV_CMD_MPA_CTRL "mpactrl" +#endif +#define PRIV_CMD_SLEEP_PARAMS "sleepparams" +#define PRIV_CMD_DFS_TESTING "dfstesting" +#define PRIV_CMD_DFS53_CFG "dfs53cfg" +#define PRIV_CMD_CFP_CODE "cfpcode" +#define PRIV_CMD_CWMODE "cwmode" +#define PRIV_CMD_ANT_CFG "antcfg" +#define PRIV_CMD_SYSCLOCK "sysclock" +#define PRIV_CMD_GET_KEY "getkey" +#define PRIV_CMD_ASSOCIATE "associate" +#define PRIV_CMD_TX_BF_CFG "httxbfcfg" +#define PRIV_CMD_PORT_CTRL "port_ctrl" +#define PRIV_CMD_PB_BYPASS "pb_bypass" +#ifdef SDIO +#define PRIV_CMD_SD_CMD53_RW "sdcmd53rw" +#endif +#ifdef RX_PACKET_COALESCE +#define PRIV_CMD_RX_COAL_CFG "rxpktcoal_cfg" +#endif +#ifdef WIFI_DIRECT_SUPPORT +#if defined(UAP_CFG80211) +#define PRIV_CMD_CFG_NOA "cfg_noa" +#define PRIV_CMD_CFG_OPP_PS "cfg_opp_ps" +#endif +#endif +#define PRIV_CMD_DFS_REPEATER_CFG "dfs_repeater" +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#define PRIV_CMD_MIRACAST_CFG "miracastcfg" +#endif +#endif +#define PRIV_CMD_COEX_RX_WINSIZE "coex_rx_winsize" + +#ifdef PCIE +#define PRIV_CMD_PCIE_REG_RW "pcieregrw" +#define PRIV_CMD_PCIE_BAR0_REG_RW "pciebar0regrw" +#endif + +#define PRIV_CMD_GET_SENSOR_TEMP "get_sensor_temp" + +#define PRIV_CMD_GET_CHNRGPWR "get_chnrgpwr" +#define PRIV_CMD_GET_TXPWR_LIMIT "get_txpwrlimit" +#define PRIV_CMD_GET_CFG_CHAN_LIST "getcfgchanlist" +#if defined(UAP_SUPPORT) +#define PRIV_CMD_EXTEND_CHAN_SWITCH "channel_switch" +#endif + +#define PRIV_CMD_DYN_BW "dyn_bw" + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#define PRIV_CMD_DFS_OFFLOAD "dfs_offload" +#endif + +#define PRIV_CMD_AUTO_ARP "auto_arp" + +#define PRIV_CMD_PER_PKT_CFG "per_pkt_cfg" + +#define PRIV_CMD_DEAUTH_CTRL "ctrldeauth" + +#define PRIV_CMD_TX_RX_HISTOGRAM "txrxhistogram" + +/**Private command ID to set/get independent reset*/ +#define PRIV_CMD_IND_RST_CFG "indrstcfg" + +#define PRIV_CMD_ARB_CFG "arb" + +/**Private command to configure static rx abort config */ +#define PRIV_CMD_RX_ABORT_CFG "rx_abort_cfg" +/**Private command to configure dynamic rx abort config */ +#define PRIV_CMD_RX_ABORT_CFG_EXT "rx_abort_cfg_ext" +#define TX_AMPDU_RTS_CTS 0 +#define TX_AMPDU_CTS_2_SELF 1 +#define TX_AMPDU_DISABLE_PROTECTION 2 +#define TX_AMPDU_DYNAMIC_RTS_CTS 3 +/**Private command to set tx ampdu protection mode */ +#define PRIV_CMD_TX_AMPDU_PROT_MODE "tx_ampdu_prot_mode" +/**Private command to configure tx rate adapt config */ +#define PRIV_CMD_RATE_ADAPT_CFG "rate_adapt_cfg" +#define CCK_DESENSE_MODE_DISABLED 0 +#define CCK_DESENSE_MODE_DYNAMIC 1 +#define CCK_DESENSE_MODE_DYN_ENH 2 +/**Private command to configure cck desense config */ +#define PRIV_CMD_CCK_DESENSE_CFG "cck_desense_cfg" +#define PRIV_CMD_DOT11MC_UNASSOC_FTM_CFG "dot11mc_unassoc_ftm_cfg" + +/** Private command ID for Android default commands */ +#define WOAL_ANDROID_DEF_CMD (SIOCDEVPRIVATE + 1) + +/** Private command ID to pass mgmt frame */ +#define WOAL_MGMT_FRAME_TX WOAL_MGMT_FRAME_TX_IOCTL + +/** Private command ID to pass custom IE list */ +#define WOAL_CUSTOM_IE_CFG (SIOCDEVPRIVATE + 13) + +/** Private command ID for Android ICS priv CMDs */ +#define WOAL_ANDROID_PRIV_CMD (SIOCDEVPRIVATE + 14) + +/** Private command ID to get BSS type */ +#define WOAL_GET_BSS_TYPE (SIOCDEVPRIVATE + 15) + +/** Private command ID for robustcoex */ +#define PRIV_CMD_ROBUSTCOEX "robustcoex" + +#define PRIV_CMD_DMCS "dmcs" + +#if defined(PCIE) +#define PRIV_CMD_SSU "ssu" +/** ssu_params_ctrl */ +typedef struct _ssu_params_cfg { + /* ssu mode */ + t_u8 ssu_mode; + /* 0-3; # of FFT samples to skip*/ + t_u32 nskip; + /* 0-3: # of FFT samples selected to dump */ + t_u32 nsel; + /* 0-3: Down sample ADC input for buffering*/ + t_u32 adcdownsample; + /* 0-1: Mask out ADC Data from spectral packet */ + t_u32 mask_adc_pkt; + /* 0-1: Enable 16-Bit FFT output data precision in spectral packet */ + t_u32 out_16bits; + /* 0-1: Enable power spectrum in dB for spectral packe */ + t_u32 spec_pwr_enable; + /* 0-1: Enable spectral packet rate reduction in DB output format */ + t_u32 rate_deduction; + /* 0-7: Number of spectral packets over which spectral data is to be + * averaged. */ + t_u32 n_pkt_avg; +} __attribute__((packed)) ssu_params_cfg; +#endif + +#define PRIV_CMD_BOOTSLEEP "bootsleep" + +/** Private command: 11AX Cfg */ +#define PRIV_CMD_11AXCFG "11axcfg" +/** Private command: 11AX Cmd */ +#define PRIV_CMD_11AXCMDCFG "11axcmd" +/** Private command: Range ext Cmd */ +#define PRIV_CMD_RANGE_EXT "range_ext" +/** Private command: TWT Setup Cfg */ +#define PRIV_CMD_TWT_SETUP "twt_setup" +/** Private command: TWT Teardown Cfg */ +#define PRIV_CMD_TWT_TEARDOWN "twt_teardown" + +#define PRIV_CMD_LPM "lpm" + +int woal_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd); + +/* + * For android private commands, fixed value of ioctl is used. + * Internally commands are differentiated using strings. + * + * application needs to specify "total_len" of data for copy_from_user + * kernel updates "used_len" during copy_to_user + */ +/** Private command structure from app */ +#ifdef USERSPACE_32BIT_OVER_KERNEL_64BIT +typedef struct _android_wifi_priv_cmd { + /** Buffer pointer */ + t_u64 buf; + /** buffer updated by driver */ + int used_len; + /** buffer sent by application */ + int total_len; +} __attribute__((packed)) android_wifi_priv_cmd; +#else +typedef struct _android_wifi_priv_cmd { + /** Buffer pointer */ + char *buf; + /** buffer updated by driver */ + int used_len; + /** buffer sent by application */ + int total_len; +} android_wifi_priv_cmd; +#endif + +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif + +/* Maximum size of the ESSID and NICKN strings */ +#define MW_ESSID_MAX_SIZE 32 + +/* Modes of operation */ +#define MW_MODE_AUTO 0 /* Let the driver decides */ +#define MW_MODE_ADHOC 1 /* Single cell network */ +#define MW_MODE_INFRA 2 /* Multi cell network, roaming, ... */ +#define MW_MODE_MASTER 3 /* Synchronisation master or Access Point */ +#define MW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ +#define MW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ +#define MW_MODE_MONITOR 6 /* Passive monitor (listen only) */ +#define MW_MODE_MESH 7 /* Mesh (IEEE 802.11s) network */ + +#define MW_POWER_TYPE 0xF000 /* Type of parameter */ +#define MW_POWER_PERIOD 0x1000 /* Value is a period/duration of */ +#define MW_POWER_TIMEOUT 0x2000 /* Value is a timeout (to go asleep) */ + +#define MW_AUTH_INDEX 0x0FFF +#define MW_AUTH_FLAGS 0xF000 +#define MW_AUTH_WPA_VERSION 0 +#define MW_AUTH_CIPHER_PAIRWISE 1 +#define MW_AUTH_CIPHER_GROUP 2 +#define MW_AUTH_KEY_MGMT 3 +#define MW_AUTH_TKIP_COUNTERMEASURES 4 +#define MW_AUTH_DROP_UNENCRYPTED 5 +#define MW_AUTH_80211_AUTH_ALG 6 +#define MW_AUTH_WPA_ENABLED 7 +#define MW_AUTH_RX_UNENCRYPTED_EAPOL 8 +#define MW_AUTH_ROAMING_CONTROL 9 +#define MW_AUTH_PRIVACY_INVOKED 10 +#define MW_AUTH_CIPHER_GROUP_MGMT 11 +#define MW_AUTH_MFP 12 + +#define MW_AUTH_CIPHER_NONE 0x00000001 +#define MW_AUTH_CIPHER_WEP40 0x00000002 +#define MW_AUTH_CIPHER_TKIP 0x00000004 +#define MW_AUTH_CIPHER_CCMP 0x00000008 +#define MW_AUTH_CIPHER_WEP104 0x00000010 +#define MW_AUTH_CIPHER_AES_CMAC 0x00000020 + +#define MW_AUTH_ALG_OPEN_SYSTEM 0x00000001 +#define MW_AUTH_ALG_SHARED_KEY 0x00000002 +#define MW_AUTH_ALG_LEAP 0x00000004 + +/* Generic format for most parameters that fit in an int */ +struct mw_param { + t_s32 value; /* The value of the parameter itself */ + t_u8 fixed; /* Hardware should not use auto select */ + t_u8 disabled; /* Disable the feature */ + t_u16 flags; /* Various specifc flags (if any) */ +}; + +/* + * For all data larger than 16 octets, we need to use a + * pointer to memory allocated in user space. + */ +struct mw_point { + t_u8 *pointer; /* Pointer to the data (in user space) */ + t_u16 length; /* number of fields or size in bytes */ + t_u16 flags; /* Optional params */ +}; + +/* + * This structure defines the payload of an ioctl, and is used + * below. + */ +union mwreq_data { + /* Config - generic */ + char name[IFNAMSIZ]; + + struct mw_point essid; /* Extended network name */ + t_u32 mode; /* Operation mode */ + struct mw_param power; /* PM duration/timeout */ + struct sockaddr ap_addr; /* Access point address */ + struct mw_param param; /* Other small parameters */ + struct mw_point data; /* Other large parameters */ +}; + +/* The structure to exchange data for ioctl */ +struct mwreq { + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ + } ifr_ifrn; + + /* Data part */ + union mwreq_data u; +}; + +typedef struct woal_priv_ht_cap_info { + t_u32 ht_cap_info_bg; + t_u32 ht_cap_info_a; +} woal_ht_cap_info; + +typedef struct woal_priv_addba { + t_u32 time_out; + t_u32 tx_win_size; + t_u32 rx_win_size; + t_u32 tx_amsdu; + t_u32 rx_amsdu; +} woal_addba; + +typedef struct _txrate_setting { + t_u16 preamble : 2; /*BIT1-BIT0: + * For legacy 11b: preamble type + * 00 = long + * 01 = short + * 10/11 = reserved + * For legacy 11g: reserved + * For 11n: Green field PPDU indicator + * 00 = HT-mix + * 01 = HT-GF + * 10/11 = reserved. + * For 11ac: reserved. + * For 11ax: + * 00 = HE-SU + * 01 = HE-EXT-SU + * 10 = HE-MU + * 11 = HE trigger based + */ + t_u16 bandwidth : 3; /* BIT2- BIT4 + * For 11n and 11ac traffic: Bandwidth + * 0 = 20Mhz + * 1 = 40Mhz + * 2 = 80 Mhz + * 3 = 160 Mhz + * 4-7 = reserved + * For legacy rate : BW>0 implies non-HT + * duplicates. For HE SU PPDU: 0 = 20Mhz 1 = 40Mhz + * 2 = 80 Mhz + * 3 = 160 Mhz + * 4-7 = reserved + * For HE ER SU PPDU: + * 0 = 242-tone RU + * 1 = upper frequency 106 tone RU within the + * primary 20 Mhz. For HE MU PPDU: 0 = 20Mhz. 1 = + * 40Mhz. 2 = 80Mhz non-preamble puncturing mode 3 + * = 160Mhz and 80+80 Mhz non-preamble. 4 = for + * preemble puncturing in 80 Mhz , where in the + * preamble only the secondary 20Mhz is punctured. + * 5 = for preemble puncturing in 80 Mhz , + * where in the preamble only one of the two + * 20Mhz subchannels in the secondary 40Mhz is + * punctured. 6 = for preemble puncturing in 160 + * Mhz or 80 Mhz + 80 Mhz, where in the primary 80 + * Mhz of the preamble only the secondary 20 Mhz is + * punctured. 7 = for preemble puncturing in 160 + * Mhz or 80 Mhz + 80 Mhz, where in the primary 80 + * Mhz of the preamble the primary 40 Mhz is + * present. + */ + t_u16 shortGI : 2; /*BIT5- BIT6 + * For legacy: not used + * For 11n: 00 = normal, 01 =shortGI, 10/11 = + * reserved For 11ac: SGI map to VHT-SIG-A2[0] + * VHT-SIG-A2[1] is set to 1 if short guard + * interval is used and NSYM mod 10 = 9, otherwise + * set to 0. For 11ax: 00 = 1xHELTF+GI0.8usec 01 = + * 2xHELTF+GI0.8usec 10 = 2xHELTF+GI1.6usec 11 = + * 4xHELTF+GI0.8 usec if both DCM and STBC are 1 + * 4xHELTF+GI3.2 usec otherwise + */ + t_u16 stbc : 1; // BIT7, 0: no STBC; 1: STBC + t_u16 dcm : 1; // BIT8, 0: no DCM; 1: DCM used. + t_u16 adv_coding : 1; // BIT9, 0: BCC; 1: LDPC. + t_u16 doppler : 2; /* BIT11-BIT10, + 00: Doppler0 + 01: Doppler 1 with Mma =10 + 10: Doppler 1 with Mma =20 + */ + t_u16 max_pktext : 2; /*BIT12-BIT13: + * Max packet extension + * 0 - 0 usec + * 1 - 8 usec + * 2 - 16 usec. + */ + t_u16 reserverd : 2; // BIT14-BIT15 +} __ATTRIB_PACK__ txrate_setting; + +/** data structure for cmd txratecfg */ +typedef struct woal_priv_tx_rate_cfg { + /* LG rate: 0, HT rate: 1, VHT rate: 2 */ + t_u32 rate_format; + /** Rate/MCS index (0xFF: auto) */ + t_u32 rate_index; + /** Data rate */ + t_u32 rate; + /** NSS */ + t_u32 nss; + /** Rate Setting */ + t_u16 rate_setting; +} woal_tx_rate_cfg; + +typedef struct woal_priv_esuppmode_cfg { + /* RSN mode */ + t_u16 rsn_mode; + /* Pairwise cipher */ + t_u8 pairwise_cipher; + /* Group cipher */ + t_u8 group_cipher; +} woal_esuppmode_cfg; + +mlan_status woal_set_ap_wps_p2p_ie(moal_private *priv, t_u8 *ie, size_t len); +mlan_status woal_ioctl_aggr_prio_tbl(moal_private *priv, t_u32 action, + mlan_ds_11n_aggr_prio_tbl *aggr_prio_tbl); + +int woal_android_priv_cmd(struct net_device *dev, struct ifreq *req); + +#define PRIV_CMD_CLOUD_KEEP_ALIVE "cloud_keep_alive" +/** cloud keep alive parameters */ +typedef struct _cloud_keep_alive { + /** id */ + t_u8 mkeep_alive_id; + /** enable/disable of this id */ + t_u8 enable; + /** enable/disable reset*/ + t_u8 reset; + /** Reserved */ + t_u8 reserved; + /** Destination MAC address */ + t_u8 dst_mac[ETH_ALEN]; + /** Source MAC address */ + t_u8 src_mac[ETH_ALEN]; + /** packet send period */ + t_u32 sendInterval; + /** packet retry interval */ + t_u32 retryInterval; + /** packet retry count */ + t_u8 retryCount; + /** packet length */ + t_u8 pkt_len; + /** packet content */ + t_u8 pkt[255]; +} __ATTRIB_PACK__ cloud_keep_alive; + +#define TLV_TYPE_PER_PKT_CFG 0x0001 +#define TX_PKT_CTRL MBIT(0) +#define RX_PKT_INFO MBIT(1) + +#define FLAG_TX_HISTOGRAM 0x01 +#define FLAG_RX_HISTOGRAM 0x02 +#define DISABLE_TX_RX_HISTOGRAM 0x00 +#define ENABLE_TX_RX_HISTOGRAM 0x01 +#define GET_TX_RX_HISTOGRAM 0x02 +#define PRIV_CMD_TX_RX_HISTOGRAM "txrxhistogram" +/** TX and RX histogram statistic parameters*/ +typedef struct _tx_rx_histogram { + /** Enable or disable get tx/rx histogram statistic */ + t_u8 enable; + /** Choose to get TX, RX or both histogram statistic */ + t_u8 action; +} __ATTRIB_PACK__ tx_rx_histogram; + +/* Enum for different CW mode type */ +typedef enum _cw_modes_e { + CWMODE_DISABLE, + CWMODE_TXCONTPKT, + CWMODE_TXCONTWAVE, +} cw_modes_e; + +/** wlan_ieee80211_chan */ +typedef struct { + /** center freq */ + t_u16 center_freq; + /** chan num */ + t_u16 hw_value; + /** chan flags */ + t_u32 flags; + /** max power */ + int max_power; + /** dfs_state */ + t_u8 dfs_state; +} __ATTRIB_PACK__ wlan_ieee80211_chan; + +/** wlan_ieee80211_chan_list*/ +typedef struct { + /** num of chan */ + t_u8 num_chan; + /** chan_list */ + wlan_ieee80211_chan chan_list[]; +} __ATTRIB_PACK__ wlan_ieee80211_chan_list; + +#define PRIV_CMD_TP_STATE "tp_state" +#endif /* _WOAL_ETH_PRIV_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_init.c b/mxm_wifiex/wlan_src/mlinux/moal_init.c new file mode 100644 index 0000000..bfdc41b --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_init.c @@ -0,0 +1,2355 @@ +/** @file moal_init.c + * + * @brief This file contains the major functions in WLAN + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_main.h" + +/** Global moal_handle array */ +extern pmoal_handle m_handle[]; + +/** Firmware name */ +char *fw_name; +int req_fw_nowait; +int fw_reload; + +/** MAC address */ +char *mac_addr; +/** Module param cfg file */ +char *mod_para; + +#ifdef MFG_CMD_SUPPORT +/** Mfg mode */ +int mfg_mode; +#endif + +#if defined(SDIO) +/** SDIO interrupt mode (0: INT_MODE_SDIO, 1: INT_MODE_GPIO) */ +int intmode = INT_MODE_SDIO; +/** GPIO interrupt pin number */ +int gpiopin; +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +int disable_regd_by_driver = 1; +/** Region alpha2 string */ +char *reg_alpha2; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +int country_ie_ignore; +int beacon_hints; +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +int host_mlme; +#endif +#endif + +/** Auto deep sleep */ +int auto_ds; + +/** IEEE PS mode */ +int ps_mode; +/** passive to active scan */ +int p2a_scan; +/** scan chan gap */ +int scan_chan_gap; +/** Max Tx buffer size */ +int max_tx_buf; + +#ifdef STA_SUPPORT +/** Max STA interfaces */ +int max_sta_bss = DEF_STA_BSS; +/** STA interface name */ +char *sta_name; +#endif + +#ifdef UAP_SUPPORT +/** Max uAP interfaces */ +int max_uap_bss = DEF_UAP_BSS; +/** uAP interface name */ +char *uap_name; +/** Max uAP station number */ +int uap_max_sta; +#endif + +#ifdef WIFI_DIRECT_SUPPORT +/** Max WIFIDIRECT interfaces */ +int max_wfd_bss = DEF_WIFIDIRECT_BSS; +/** WIFIDIRECT interface name */ +char *wfd_name; +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +/** max VIRTUAL bss */ +int max_vir_bss = DEF_VIRTUAL_BSS; +#endif +#endif + +#ifdef SDIO_SUSPEND_RESUME +/** PM keep power */ +int pm_keep_power = 1; +/** HS when shutdown */ +int shutdown_hs; +#endif + +#if defined(SDIO) +/** SDIO slew rate */ +int slew_rate = 3; +#endif +int tx_work = 0; + +#if defined(STA_SUPPORT) +/** 802.11d configuration */ +int cfg_11d; +#endif + +/** fw serial download check */ +int fw_serial = 1; + +/** napi support*/ +int napi; + +/** DPD data config file */ +char *dpd_data_cfg; + +/** CAL data config file */ +char *cal_data_cfg; +/** Init config file (MAC address, register etc.) */ +char *init_cfg; + +/** Set configuration data of Tx power limitation */ +char *txpwrlimit_cfg; +/** Allow setting tx power table of country */ +int cntry_txpwr; + +/** Init hostcmd file */ +char *init_hostcmd_cfg; +char *band_steer_cfg; + +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** CFG80211 and WEXT mode */ +int cfg80211_wext = STA_WEXT_MASK | UAP_WEXT_MASK; +#else +/** CFG80211 mode */ +int cfg80211_wext = STA_CFG80211_MASK | UAP_CFG80211_MASK; +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +int fw_region = 1; +#endif +#endif + +/** Work queue priority */ +int wq_sched_prio; +/** Work queue scheduling policy */ +int wq_sched_policy = SCHED_NORMAL; +/** rx_work flag */ +int rx_work; + +#if defined(USB) +int skip_fwdnld; +#endif + +/* Enable/disable aggrctrl */ +int aggrctrl; + +#ifdef USB +/* Enable/disable USB aggregation feature */ +int usb_aggr; +#endif + +#ifdef PCIE +/* Enable/disable Message Signaled Interrupt (MSI) */ +int pcie_int_mode = PCIE_INT_MODE_MSI; +#endif /* PCIE */ + +int low_power_mode_enable; + +int hw_test; + +#ifdef CONFIG_OF +int dts_enable = 1; +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +int dfs_offload; +#endif + +#ifdef ANDROID_KERNEL +int wakelock_timeout = WAKE_LOCK_TIMEOUT; +#endif + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#ifdef 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 /* WIFI_DIRECT_SUPPORT */ +#else +#ifdef STA_SUPPORT +int drv_mode = DRV_MODE_STA; +#else +int drv_mode = DRV_MODE_UAP; +#endif /* STA_SUPPORT */ +#endif /* STA_SUPPORT & UAP_SUPPORT */ + +int gtk_rekey_offload = GTK_REKEY_OFFLOAD_DISABLE; + +int pmic; + +int antcfg; + +t_u32 uap_oper_ctrl; + +int hs_wake_interval = 400; +int indication_gpio = 0xff; +int disconnect_on_suspend; +int hs_mimo_switch; + +int indrstcfg = 0xffffffff; + +/** all the feature are enabled */ +#define DEFAULT_DEV_CAP_MASK 0xffffffff +t_u32 dev_cap_mask = DEFAULT_DEV_CAP_MASK; +#ifdef SDIO +int sdio_rx_aggr = MTRUE; +#endif + +/** The global variable of scan beacon buffer **/ +int fixed_beacon_buffer; + +#ifdef WIFI_DIRECT_SUPPORT +int GoAgeoutTime; +#endif + +t_u16 multi_dtim; + +t_u16 inact_tmo; + +#ifdef DEBUG_LEVEL1 +#ifdef DEBUG_LEVEL2 +#define DEFAULT_DEBUG_MASK (0xffffffff) +#else +#define DEFAULT_DEBUG_MASK (MMSG | MFATAL | MERROR | MREG_D) +#endif /* DEBUG_LEVEL2 */ +t_u32 drvdbg = DEFAULT_DEBUG_MASK; + +#endif /* DEBUG_LEVEL1 */ + +static card_type_entry card_type_map_tbl[] = { +#ifdef SD8887 + {CARD_TYPE_SD8887, 0, CARD_SD8887}, +#endif +#ifdef SD8897 + {CARD_TYPE_SD8897, 0, CARD_SD8897}, +#endif +#ifdef SD8977 + {CARD_TYPE_SD8977, 0, CARD_SD8977}, +#endif +#ifdef SD8978 + {CARD_TYPE_SD8978, 0, CARD_SD8978}, +#endif +#ifdef SD8997 + {CARD_TYPE_SD8997, 0, CARD_SD8997}, +#endif +#ifdef SD8987 + {CARD_TYPE_SD8987, 0, CARD_SD8987}, +#endif +#ifdef SD9097 + {CARD_TYPE_SD9097, 0, CARD_SD9097}, +#endif +#ifdef SD9098 + {CARD_TYPE_SD9098, 0, CARD_SD9098}, +#endif +#ifdef PCIE8897 + {CARD_TYPE_PCIE8897, 0, CARD_PCIE8897}, +#endif +#ifdef PCIE8997 + {CARD_TYPE_PCIE8997, 0, CARD_PCIE8997}, +#endif +#ifdef PCIE9097 + {CARD_TYPE_PCIE9097, 0, CARD_PCIE9097}, +#endif +#ifdef PCIE9098 + {CARD_TYPE_PCIE9098, 0, CARD_PCIE9098}, +#endif +#ifdef USB8897 + {CARD_TYPE_USB8897, 0, CARD_USB8897}, +#endif +#ifdef USB8997 + {CARD_TYPE_USB8997, 0, CARD_USB8997}, +#endif +#ifdef USB8978 + {CARD_TYPE_USB8978, 0, CARD_USB8978}, +#endif +#ifdef USB9098 + {CARD_TYPE_USB9098, 0, CARD_USB9098}, +#endif +#ifdef USB9097 + {CARD_TYPE_USB9097, 0, CARD_USB9097}, +#endif +}; + +int dfs53cfg = DFS_W53_DEFAULT_FW; + +/** + * @brief This function read a line in module parameter file + * + * @param data A pointer to module parameter data buffer + * @param size module parameter file size + * @param line_pos A pointer to offset of current line + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static t_size parse_cfg_get_line(t_u8 *data, t_size size, t_u8 *line_pos) +{ + t_u8 *src, *dest; + static t_s32 pos; + + ENTER(); + + if (pos >= size) { /* reach the end */ + pos = 0; /* Reset position for rfkill */ + LEAVE(); + return -1; + } + memset(line_pos, 0, MAX_LINE_LEN); + src = data + pos; + dest = line_pos; + + while (pos < size && *src != '\x0A' && *src != '\0') { + if (*src != ' ' && *src != '\t') /* parse space */ + *dest++ = *src++; + else + src++; + pos++; + } + /* parse new line */ + pos++; + *dest = '\0'; + LEAVE(); + return strlen(line_pos); +} + +/** + * @brief This function duplicate a string + * + * @param dst A pointer to destination string + * @param src A pointer to source string + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static void woal_dup_string(char **dst, char *src) +{ + size_t len = 0; + if (src) { + len = strlen(src); + if (len != 0) { + if (*dst != NULL) + kfree(*dst); + *dst = kzalloc(len + 1, GFP_KERNEL); + if (*dst == NULL) { + PRINTM(MERROR, + "Failed to alloc mem for param: %s\n", + src); + return; + } + moal_memcpy_ext(NULL, *dst, src, len, len); + } + } +} + +/** + * @brief This function read an integer value in module parameter file + * + * @param line A pointer to a line + * @param out_data A pointer to parsed integer value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status parse_line_read_int(t_u8 *line, int *out_data) +{ + t_u8 *p = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + if (line == NULL) { + ret = MLAN_STATUS_FAILURE; + goto out; + } + p = strstr(line, "="); + if (p == NULL) { + ret = MLAN_STATUS_FAILURE; + goto out; + } + p++; + ret = woal_atoi(out_data, p); +out: + if (ret != MLAN_STATUS_SUCCESS) + *out_data = 0; + return ret; +} + +/** + * @brief This function read a string in module parameter file + * + * @param line A pointer to a line + * @param out_str A pointer to parsed string + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status parse_line_read_string(t_u8 *line, char **out_str) +{ + t_u8 *p = NULL, *pstr = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + if (line == NULL) { + ret = MLAN_STATUS_FAILURE; + goto out; + } + p = strstr(line, "="); + if (p == NULL) { + ret = MLAN_STATUS_FAILURE; + goto out; + } + p++; + pstr = p; + while (*pstr) { + if (*pstr == '\"') + *pstr = '\0'; + pstr++; + } + if (*p == '\0') + p++; + *out_str = p; +out: + return ret; +} + +/** + * @brief This function read card info in module parameter file + * + * @param line A pointer to a line + * @param type A pointer to card type + * @param if_id A pointer to interface id + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status parse_line_read_card_info(t_u8 *line, char **type, + char **if_id) +{ + t_u8 *p = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + if (line == NULL) { + ret = MLAN_STATUS_FAILURE; + goto out; + } + + p = strstr(line, "="); + if (p == NULL) { + ret = MLAN_STATUS_FAILURE; + goto out; + } + *p = '\0'; + + p = strstr(line, "_"); + if (p != NULL) { + *p++ = '\0'; + *if_id = p; + } else { + *if_id = NULL; + } + *type = line; +out: + return ret; +} + +/** + * @brief This function read blocks in module parameter file + * + * @param data A pointer to a line + * @param size line size + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status parse_cfg_read_block(t_u8 *data, t_u32 size, + moal_handle *handle) +{ + int out_data = 0, end = 0; + char *out_str = NULL; + t_u8 line[MAX_LINE_LEN]; + moal_mod_para *params = &handle->params; + mlan_status ret = MLAN_STATUS_SUCCESS; + + while (parse_cfg_get_line(data, size, line) != -1) { + if (strncmp(line, "}", strlen("}")) == 0) { + end = 1; + break; + } + if (end == 0 && strstr(line, "{") != 0) + break; + if (strncmp(line, "hw_test", strlen("hw_test")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_HW_TEST); + else + moal_extflg_clear(handle, EXT_HW_TEST); + PRINTM(MMSG, "hw_test %s\n", + moal_extflg_isset(handle, EXT_HW_TEST) ? "on" : + "off"); + } +#ifdef CONFIG_OF + else if (strncmp(line, "dts_enable", strlen("dts_enable")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_DTS_ENABLE); + else + moal_extflg_clear(handle, EXT_DTS_ENABLE); + PRINTM(MMSG, "dts_enable %s\n", + moal_extflg_isset(handle, EXT_DTS_ENABLE) ? + "on" : + "off"); + } +#endif + else if (strncmp(line, "fw_name", strlen("fw_name")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->fw_name, out_str); + PRINTM(MMSG, "fw_name=%s\n", params->fw_name); + } else if (strncmp(line, "req_fw_nowait", + strlen("req_fw_nowait")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_REQ_FW_NOWAIT); + else + moal_extflg_clear(handle, EXT_REQ_FW_NOWAIT); + PRINTM(MMSG, "req fw nowait %s\n", + moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT) ? + "on" : + "off"); + } else if (strncmp(line, "fw_reload", strlen("fw_reload")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->fw_reload = out_data; + PRINTM(MMSG, "fw_reload %d\n", params->fw_reload); + } else if (strncmp(line, "fw_serial", strlen("fw_serial")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_FW_SERIAL); + else + moal_extflg_clear(handle, EXT_FW_SERIAL); + PRINTM(MMSG, "fw_serial %s\n", + moal_extflg_isset(handle, EXT_FW_SERIAL) ? + "on" : + "off"); + } else if (strncmp(line, "mac_addr", strlen("mac_addr")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->mac_addr, out_str); + PRINTM(MMSG, "mac_addr=%s\n", params->mac_addr); + } +#ifdef MFG_CMD_SUPPORT + else if (strncmp(line, "mfg_mode", strlen("mfg_mode")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->mfg_mode = out_data; + PRINTM(MMSG, "mfg_mode = %d\n", params->mfg_mode); + } +#endif + else if (strncmp(line, "drv_mode", strlen("drv_mode")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->drv_mode = out_data; + PRINTM(MMSG, "drv_mode = %d\n", params->drv_mode); + } +#ifdef STA_SUPPORT + else if (strncmp(line, "max_sta_bss", strlen("max_sta_bss")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->max_sta_bss = out_data; + PRINTM(MMSG, "max_sta_bss = %d\n", params->max_sta_bss); + } else if (strncmp(line, "sta_name", strlen("sta_name")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->sta_name, out_str); + PRINTM(MMSG, "sta_name=%s\n", params->sta_name); + } +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + else if (strncmp(line, "max_uap_bss", strlen("max_uap_bss")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->max_uap_bss = out_data; + PRINTM(MMSG, "max_uap_bss = %d\n", params->max_uap_bss); + } else if (strncmp(line, "uap_name", strlen("uap_name")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->uap_name, out_str); + PRINTM(MMSG, "uap_name=%s\n", params->uap_name); + } +#endif /* UAP_SUPPORT */ +#ifdef WIFI_DIRECT_SUPPORT + else if (strncmp(line, "wfd_name", strlen("wfd_name")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->wfd_name, out_str); + PRINTM(MMSG, "wfd_name=%s\n", params->wfd_name); + } +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + else if (strncmp(line, "max_vir_bss", strlen("max_vir_bss")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->max_vir_bss = out_data; + PRINTM(MMSG, "max_vir_bss=%d\n", params->max_vir_bss); + } +#endif +#endif + else if (strncmp(line, "auto_ds", strlen("auto_ds")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->auto_ds = out_data; + PRINTM(MMSG, "auto_ds = %d\n", params->auto_ds); + } else if (strncmp(line, "ps_mode", strlen("ps_mode")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->ps_mode = out_data; + PRINTM(MMSG, "ps_mode = %d\n", params->ps_mode); + } else if (strncmp(line, "p2a_scan", strlen("p2a_scan")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->p2a_scan = out_data; + PRINTM(MMSG, "p2a_scan = %d\n", params->p2a_scan); + } else if (strncmp(line, "scan_chan_gap", + strlen("scan_chan_gap")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->scan_chan_gap = out_data; + PRINTM(MMSG, "scan_chan_gap = %d\n", + params->scan_chan_gap); + } else if (strncmp(line, "max_tx_buf", strlen("max_tx_buf")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->max_tx_buf = out_data; + PRINTM(MMSG, "max_tx_buf = %d\n", params->max_tx_buf); + } +#if defined(SDIO) + else if (strncmp(line, "intmode", strlen("intmode")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_INTMODE); + else + moal_extflg_clear(handle, EXT_INTMODE); + PRINTM(MMSG, "intmode %s\n", + moal_extflg_isset(handle, EXT_INTMODE) ? "on" : + "off"); + } else if (strncmp(line, "gpiopin", strlen("gpiopin")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->gpiopin = out_data; + PRINTM(MMSG, "gpiopin = %d\n", params->gpiopin); + } +#endif +#if defined(SDIO) && defined(SDIO_SUSPEND_RESUME) + else if (strncmp(line, "pm_keep_power", + strlen("pm_keep_power")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_PM_KEEP_POWER); + else + moal_extflg_clear(handle, EXT_PM_KEEP_POWER); + PRINTM(MMSG, "pm_keep_power %s\n", + moal_extflg_isset(handle, EXT_PM_KEEP_POWER) ? + "on" : + "off"); + } else if (strncmp(line, "shutdown_hs", + strlen("shutdown_hs")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_SHUTDOWN_HS); + else + moal_extflg_clear(handle, EXT_SHUTDOWN_HS); + PRINTM(MMSG, "shutdown_hs %s\n", + moal_extflg_isset(handle, EXT_SHUTDOWN_HS) ? + "on" : + "off"); + } +#endif +#if defined(STA_SUPPORT) + else if (strncmp(line, "cfg_11d", strlen("cfg_11d")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->cfg_11d = out_data; + PRINTM(MMSG, "cfg_11d = %d\n", params->cfg_11d); + } +#endif +#if defined(SDIO) + else if (strncmp(line, "slew_rate", strlen("slew_rate")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->slew_rate = out_data; + PRINTM(MMSG, "slew_rate = %d\n", params->slew_rate); + } +#endif + else if (strncmp(line, "dpd_data_cfg", + strlen("dpd_data_cfg")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->dpd_data_cfg, out_str); + PRINTM(MMSG, "dpd_data_cfg=%s\n", params->dpd_data_cfg); + } else if (strncmp(line, "init_cfg", strlen("init_cfg")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->init_cfg, out_str); + PRINTM(MMSG, "init_cfg=%s\n", params->init_cfg); + } else if (strncmp(line, "cal_data_cfg", + strlen("cal_data_cfg")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->cal_data_cfg, out_str); + PRINTM(MMSG, "cal_data_cfg=%s\n", params->cal_data_cfg); + } else if (strncmp(line, "txpwrlimit_cfg", + strlen("txpwrlimit_cfg")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->txpwrlimit_cfg, out_str); + PRINTM(MMSG, "txpwrlimit_cfg=%s\n", + params->txpwrlimit_cfg); + } else if (strncmp(line, "cntry_txpwr", + strlen("cntry_txpwr")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_CNTRY_TXPWR); + else + moal_extflg_clear(handle, EXT_CNTRY_TXPWR); + PRINTM(MMSG, "cntry_txpwr %s\n", + moal_extflg_isset(handle, EXT_CNTRY_TXPWR) ? + "on" : + "off"); + } else if (strncmp(line, "init_hostcmd_cfg", + strlen("init_hostcmd_cfg")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->init_hostcmd_cfg, out_str); + PRINTM(MMSG, "init_hostcmd_cfg=%s\n", + params->init_hostcmd_cfg); + } else if (strncmp(line, "band_steer_cfg", + strlen("band_steer_cfg")) == 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->band_steer_cfg, out_str); + PRINTM(MMSG, "band_steer_cfg=%s\n", + params->band_steer_cfg); + } else if (strncmp(line, "cfg80211_wext", + strlen("cfg80211_wext")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->cfg80211_wext = out_data; + PRINTM(MMSG, "cfg80211_wext=0x%x\n", + params->cfg80211_wext); + } +#if defined(USB) + else if (IS_USB(handle->card_type) && + strncmp(line, "skip_fwdnld", strlen("skip_fwdnld")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_SKIP_FWDNLD); + else + moal_extflg_clear(handle, EXT_SKIP_FWDNLD); + PRINTM(MMSG, "skip_fwdnld %s\n", + moal_extflg_isset(handle, EXT_SKIP_FWDNLD) ? + "on" : + "off"); + } +#endif + else if (strncmp(line, "wq_sched_prio", + strlen("wq_sched_prio")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->wq_sched_prio = out_data; + PRINTM(MMSG, "wq_sched_prio=0x%x\n", + params->wq_sched_prio); + } else if (strncmp(line, "wq_sched_policy", + strlen("wq_sched_policy")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->wq_sched_policy = out_data; + PRINTM(MMSG, "wq_sched_policy=0x%x\n", + params->wq_sched_policy); + } else if (strncmp(line, "rx_work", strlen("rx_work")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->rx_work = out_data; + PRINTM(MMSG, "rx_work=0x%x\n", params->rx_work); + } else if (strncmp(line, "aggrctrl", strlen("aggrctrl")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_AGGR_CTRL); + else + moal_extflg_clear(handle, EXT_AGGR_CTRL); + PRINTM(MMSG, "aggrctrl %s\n", + moal_extflg_isset(handle, EXT_AGGR_CTRL) ? + "on" : + "off"); + } +#ifdef USB + else if (IS_USB(handle->card_type) && + strncmp(line, "usb_aggr", strlen("usb_aggr")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->usb_aggr = out_data; + PRINTM(MMSG, "usb_aggr=0x%x\n", params->usb_aggr); + } +#endif +#ifdef PCIE + else if (IS_PCIE(handle->card_type) && + strncmp(line, "pcie_int_mode", + strlen("pcie_int_mode")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->pcie_int_mode = out_data; + PRINTM(MMSG, "pcie_int_mode=%d\n", + params->pcie_int_mode); + } +#endif + else if (strncmp(line, "low_power_mode_enable", + strlen("low_power_mode_enable")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_LOW_PW_MODE); + else + moal_extflg_clear(handle, EXT_LOW_PW_MODE); + PRINTM(MMSG, "low_power_mode_enable %s\n", + moal_extflg_isset(handle, EXT_LOW_PW_MODE) ? + "on" : + "off"); + } +#ifdef ANDROID_KERNEL + else if (strncmp(line, "wakelock_timeout", + strlen("wakelock_timeout")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->wakelock_timeout = out_data; + PRINTM(MMSG, "wakelock_timeout=%d\n", + params->wakelock_timeout); + } +#endif + else if (strncmp(line, "dev_cap_mask", + strlen("dev_cap_mask")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->dev_cap_mask = out_data; + PRINTM(MMSG, "dev_cap_mask=%d\n", params->dev_cap_mask); + } +#ifdef SDIO + else if (strncmp(line, "sdio_rx_aggr", + strlen("sdio_rx_aggr")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_SDIO_RX_AGGR); + else + moal_extflg_clear(handle, EXT_SDIO_RX_AGGR); + PRINTM(MMSG, "sdio_rx_aggr %s\n", + moal_extflg_isset(handle, EXT_SDIO_RX_AGGR) ? + "on" : + "off"); + } +#endif +#if defined(SD8997) || defined(PCIE8997) || defined(USB8997) || \ + defined(SD8977) || defined(SD8987) || defined(SD9098) || \ + defined(USB9098) || defined(PCIE9098) || defined(SD9097) || \ + defined(USB9097) || defined(PCIE9097) || defined(SD8978) || \ + defined(USB8978) + else if (strncmp(line, "pmic", strlen("pmic")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_PMIC); + else + moal_extflg_clear(handle, EXT_PMIC); + PRINTM(MMSG, "pmic %s\n", + moal_extflg_isset(handle, EXT_PMIC) ? "on" : + "off"); + } +#endif + else if (strncmp(line, "antcfg", strlen("antcfg")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->antcfg = out_data; + PRINTM(MMSG, "antcfg=%d\n", params->antcfg); + } else if (strncmp(line, "uap_oper_ctrl", + strlen("uap_oper_ctrl")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->uap_oper_ctrl = out_data; + PRINTM(MMSG, "uap_oper_ctrl=%d\n", + params->uap_oper_ctrl); + } else if (strncmp(line, "hs_wake_interval", + strlen("hs_wake_interval")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->hs_wake_interval = out_data; + PRINTM(MMSG, "hs_wake_interval=%d\n", + params->hs_wake_interval); + } else if (strncmp(line, "indication_gpio", + strlen("indication_gpio")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->indication_gpio = out_data; + PRINTM(MMSG, "indication_gpio=%d\n", + params->indication_gpio); + } else if (strncmp(line, "disconnect_on_suspend", + strlen("disconnect_on_suspend")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, + EXT_DISCONNECT_ON_SUSPEND); + else + moal_extflg_clear(handle, + EXT_DISCONNECT_ON_SUSPEND); + PRINTM(MMSG, "disconnect_on_suspend %s\n", + moal_extflg_isset(handle, + EXT_DISCONNECT_ON_SUSPEND) ? + "on" : + "off"); + } else if (strncmp(line, "hs_mimo_switch", + strlen("hs_mimo_switch")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_HS_MIMO_SWITCH); + else + moal_extflg_clear(handle, EXT_HS_MIMO_SWITCH); + PRINTM(MMSG, "hs_mimo_switch %s\n", + moal_extflg_isset(handle, EXT_HS_MIMO_SWITCH) ? + "on" : + "off"); + } else if (strncmp(line, "indrstcfg", strlen("indrstcfg")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->indrstcfg = out_data; + PRINTM(MMSG, "indrstcfg=%d\n", params->indrstcfg); + } else if (strncmp(line, "fixed_beacon_buffer", + strlen("fixed_beacon_buffer")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_FIX_BCN_BUF); + else + moal_extflg_clear(handle, EXT_FIX_BCN_BUF); + PRINTM(MMSG, "fixed_beacon_buffer %s\n", + moal_extflg_isset(handle, EXT_FIX_BCN_BUF) ? + "on" : + "off"); + } +#ifdef WIFI_DIRECT_SUPPORT + else if (strncmp(line, "GoAgeoutTime", + strlen("GoAgeoutTime")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->GoAgeoutTime = out_data; + PRINTM(MMSG, "GoAgeoutTime=%d\n", params->GoAgeoutTime); + } +#endif + else if (strncmp(line, "gtk_rekey_offload", + strlen("gtk_rekey_offload")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->gtk_rekey_offload = out_data; + PRINTM(MMSG, "gtk_rekey_offload=%d\n", + params->gtk_rekey_offload); + } else if (strncmp(line, "multi_dtim", strlen("multi_dtim")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->multi_dtim = out_data; + PRINTM(MMSG, "multi_dtim=%d\n", params->multi_dtim); + } else if (strncmp(line, "inact_tmo", strlen("inact_tmo")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->inact_tmo = out_data; + PRINTM(MMSG, "inact_tmo=%d\n", params->inact_tmo); + } else if (strncmp(line, "napi", strlen("napi")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_NAPI); + else + moal_extflg_clear(handle, EXT_NAPI); + PRINTM(MMSG, "napi %s\n", + moal_extflg_isset(handle, EXT_NAPI) ? "on" : + "off"); + } else if (strncmp(line, "tx_work", strlen("tx_work")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_TX_WORK); + else + moal_extflg_clear(handle, EXT_TX_WORK); + PRINTM(MMSG, "tx_work %s\n", + moal_extflg_isset(handle, EXT_TX_WORK) ? "on" : + "off"); + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + else if (strncmp(line, "dfs_offload", strlen("dfs_offload")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_DFS_OFFLOAD); + else + moal_extflg_clear(handle, EXT_DFS_OFFLOAD); + PRINTM(MMSG, "dfs_offload %s\n", + moal_extflg_isset(handle, EXT_DFS_OFFLOAD) ? + "on" : + "off"); + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + else if (strncmp(line, "disable_regd_by_driver", + strlen("disable_regd_by_driver")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, + EXT_DISABLE_REGD_BY_DRIVER); + else + moal_extflg_clear(handle, + EXT_DISABLE_REGD_BY_DRIVER); + PRINTM(MMSG, "reg domain set by driver=%s\n", + moal_extflg_isset(handle, + EXT_DISABLE_REGD_BY_DRIVER) ? + "disable" : + "enable"); + } else if (strncmp(line, "reg_alpha2", strlen("reg_alpha2")) == + 0) { + if (parse_line_read_string(line, &out_str) != + MLAN_STATUS_SUCCESS) + goto err; + woal_dup_string(¶ms->reg_alpha2, out_str); + PRINTM(MMSG, "reg_alpha2=%s\n", params->reg_alpha2); + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + else if (strncmp(line, "country_ie_ignore", + strlen("country_ie_ignore")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_COUNTRY_IE_IGNORE); + else + moal_extflg_clear(handle, + EXT_COUNTRY_IE_IGNORE); + PRINTM(MMSG, "country_ie_ignore=%s\n", + moal_extflg_isset(handle, + EXT_COUNTRY_IE_IGNORE) ? + "on" : + "off"); + } else if (strncmp(line, "beacon_hints", + strlen("beacon_hints")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_BEACON_HINTS); + else + moal_extflg_clear(handle, EXT_BEACON_HINTS); + PRINTM(MMSG, "beacon_hints=%s\n", + moal_extflg_isset(handle, EXT_BEACON_HINTS) ? + "disable" : + "enable"); + } +#endif +#endif +#ifdef UAP_SUPPORT + else if (strncmp(line, "uap_max_sta", strlen("uap_max_sta")) == + 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->uap_max_sta = out_data; + PRINTM(MMSG, "uap_max_sta=%d\n", params->uap_max_sta); + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + else if (strncmp(line, "host_mlme", strlen("host_mlme")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + if (out_data) + moal_extflg_set(handle, EXT_HOST_MLME); + else + moal_extflg_clear(handle, EXT_HOST_MLME); + PRINTM(MMSG, "host_mlme=%s\n", + moal_extflg_isset(handle, EXT_HOST_MLME) ? + "disable" : + "enable"); + } +#endif +#endif + else if (strncmp(line, "dfs53cfg", strlen("dfs53cfg")) == 0) { + if (parse_line_read_int(line, &out_data) != + MLAN_STATUS_SUCCESS) + goto err; + params->dfs53cfg = out_data; + PRINTM(MMSG, "dfs53cfg= %d\n", params->dfs53cfg); + } + } + if (end) + return ret; +err: + PRINTM(MMSG, "Invalid line: %s\n", line); + ret = MLAN_STATUS_FAILURE; + return ret; +} + +/** + * @brief This function initialize module parameter + * + * @param handle A pointer to moal_handle structure + * @param params A pointer to moal_mod_para structure + * + * @return N/A + */ +static void woal_setup_module_param(moal_handle *handle, moal_mod_para *params) +{ + if (hw_test) + moal_extflg_set(handle, EXT_HW_TEST); +#ifdef CONFIG_OF + if (dts_enable) + moal_extflg_set(handle, EXT_DTS_ENABLE); +#endif + woal_dup_string(&handle->params.fw_name, fw_name); + if (params && params->fw_name) + woal_dup_string(&handle->params.fw_name, params->fw_name); + if (req_fw_nowait) + moal_extflg_set(handle, EXT_REQ_FW_NOWAIT); + handle->params.fw_reload = fw_reload; + if (fw_reload == FW_RELOAD_WITH_EMULATION) { + if (!IS_USB(handle->card_type)) + handle->params.fw_reload = 0; + else + fw_reload = 0; + } + if (params) + handle->params.fw_reload = params->fw_reload; + if (fw_serial) + moal_extflg_set(handle, EXT_FW_SERIAL); + woal_dup_string(&handle->params.mac_addr, mac_addr); + if (params && params->mac_addr) + woal_dup_string(&handle->params.mac_addr, params->mac_addr); +#ifdef MFG_CMD_SUPPORT + handle->params.mfg_mode = mfg_mode; + if (params) + handle->params.mfg_mode = params->mfg_mode; +#endif + handle->params.drv_mode = drv_mode; + if (params) + handle->params.drv_mode = params->drv_mode; +#ifdef STA_SUPPORT + handle->params.max_sta_bss = max_sta_bss; + woal_dup_string(&handle->params.sta_name, sta_name); + if (params) { + handle->params.max_sta_bss = params->max_sta_bss; + woal_dup_string(&handle->params.sta_name, params->sta_name); + } +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + handle->params.max_uap_bss = max_uap_bss; + woal_dup_string(&handle->params.uap_name, uap_name); + handle->params.uap_max_sta = uap_max_sta; + if (params) { + handle->params.max_uap_bss = params->max_uap_bss; + woal_dup_string(&handle->params.uap_name, params->uap_name); + handle->params.uap_max_sta = params->uap_max_sta; + } +#endif /* UAP_SUPPORT */ +#ifdef WIFI_DIRECT_SUPPORT + handle->params.max_wfd_bss = max_wfd_bss; + woal_dup_string(&handle->params.wfd_name, wfd_name); + if (params) { + handle->params.max_wfd_bss = params->max_wfd_bss; + woal_dup_string(&handle->params.wfd_name, params->wfd_name); + } +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + handle->params.max_vir_bss = max_vir_bss; + if (params) + handle->params.max_vir_bss = params->max_vir_bss; +#endif +#endif /* WIFI_DIRECT_SUPPORT */ + handle->params.auto_ds = auto_ds; + if (params) + handle->params.auto_ds = params->auto_ds; + handle->params.ps_mode = ps_mode; + handle->params.p2a_scan = p2a_scan; + handle->params.scan_chan_gap = scan_chan_gap; + handle->params.max_tx_buf = max_tx_buf; + if (params) { + handle->params.ps_mode = params->ps_mode; + handle->params.max_tx_buf = params->max_tx_buf; + handle->params.p2a_scan = params->p2a_scan; + handle->params.scan_chan_gap = params->scan_chan_gap; + } +#if defined(SDIO) + if (intmode) + moal_extflg_set(handle, EXT_INTMODE); + handle->params.gpiopin = gpiopin; + if (params) + handle->params.gpiopin = params->gpiopin; +#endif +#if defined(SDIO) && defined(SDIO_SUSPEND_RESUME) + if (pm_keep_power) + moal_extflg_set(handle, EXT_PM_KEEP_POWER); + if (shutdown_hs) + moal_extflg_set(handle, EXT_SHUTDOWN_HS); +#endif +#if defined(STA_SUPPORT) + handle->params.cfg_11d = cfg_11d; + if (params) + handle->params.cfg_11d = params->cfg_11d; +#endif +#if defined(SDIO) + handle->params.slew_rate = slew_rate; +#endif + woal_dup_string(&handle->params.dpd_data_cfg, dpd_data_cfg); + if (params) + woal_dup_string(&handle->params.dpd_data_cfg, + params->dpd_data_cfg); + woal_dup_string(&handle->params.init_cfg, init_cfg); + woal_dup_string(&handle->params.cal_data_cfg, cal_data_cfg); + if (params) { + woal_dup_string(&handle->params.init_cfg, params->init_cfg); + woal_dup_string(&handle->params.cal_data_cfg, + params->cal_data_cfg); + } + woal_dup_string(&handle->params.txpwrlimit_cfg, txpwrlimit_cfg); + if (params) + woal_dup_string(&handle->params.txpwrlimit_cfg, + params->txpwrlimit_cfg); + if (cntry_txpwr) + moal_extflg_set(handle, EXT_CNTRY_TXPWR); + woal_dup_string(&handle->params.init_hostcmd_cfg, init_hostcmd_cfg); + if (params) + woal_dup_string(&handle->params.init_hostcmd_cfg, + params->init_hostcmd_cfg); + + woal_dup_string(&handle->params.band_steer_cfg, band_steer_cfg); + if (params) + woal_dup_string(&handle->params.band_steer_cfg, + params->band_steer_cfg); + handle->params.cfg80211_wext = cfg80211_wext; + if (params) + handle->params.cfg80211_wext = params->cfg80211_wext; +#if defined(USB) + if (skip_fwdnld) + moal_extflg_set(handle, EXT_SKIP_FWDNLD); +#endif + handle->params.wq_sched_prio = wq_sched_prio; + handle->params.wq_sched_policy = wq_sched_policy; + handle->params.rx_work = rx_work; + if (params) { + handle->params.wq_sched_prio = params->wq_sched_prio; + handle->params.wq_sched_policy = params->wq_sched_policy; + handle->params.rx_work = params->rx_work; + } + if (aggrctrl) + moal_extflg_set(handle, EXT_AGGR_CTRL); +#ifdef USB + handle->params.usb_aggr = usb_aggr; + if (params) + handle->params.usb_aggr = params->usb_aggr; +#endif +#ifdef PCIE + handle->params.pcie_int_mode = pcie_int_mode; + if (params) + handle->params.pcie_int_mode = params->pcie_int_mode; +#endif /* PCIE */ + if (low_power_mode_enable) + moal_extflg_set(handle, EXT_LOW_PW_MODE); + +#ifdef ANDROID_KERNEL + handle->params.wakelock_timeout = wakelock_timeout; + if (params) + handle->params.wakelock_timeout = params->wakelock_timeout; +#endif + handle->params.dev_cap_mask = dev_cap_mask; + if (params) + handle->params.dev_cap_mask = params->dev_cap_mask; +#ifdef SDIO + if (sdio_rx_aggr) + moal_extflg_set(handle, EXT_SDIO_RX_AGGR); +#endif +#if defined(SD8997) || defined(PCIE8997) || defined(USB8997) || \ + defined(SD8977) || defined(SD8987) || defined(SD9098) || \ + defined(USB9098) || defined(PCIE9098) || defined(SD9097) || \ + defined(USB9097) || defined(PCIE9097) || defined(SD8978) || \ + defined(USB8978) + if (pmic) + moal_extflg_set(handle, EXT_PMIC); +#endif + handle->params.antcfg = antcfg; + if (params) + handle->params.antcfg = params->antcfg; + handle->params.uap_oper_ctrl = uap_oper_ctrl; + if (params) + handle->params.uap_oper_ctrl = params->uap_oper_ctrl; + handle->params.hs_wake_interval = hs_wake_interval; + handle->params.indication_gpio = indication_gpio; + if (params) { + handle->params.hs_wake_interval = params->hs_wake_interval; + handle->params.indication_gpio = params->indication_gpio; + } + if (disconnect_on_suspend) + moal_extflg_set(handle, EXT_DISCONNECT_ON_SUSPEND); + if (hs_mimo_switch) + moal_extflg_set(handle, EXT_HS_MIMO_SWITCH); + handle->params.indrstcfg = indrstcfg; + if (params) + handle->params.indrstcfg = params->indrstcfg; + if (fixed_beacon_buffer) + moal_extflg_set(handle, EXT_FIX_BCN_BUF); +#ifdef WIFI_DIRECT_SUPPORT + handle->params.GoAgeoutTime = GoAgeoutTime; + if (params) + handle->params.GoAgeoutTime = params->GoAgeoutTime; +#endif + handle->params.gtk_rekey_offload = gtk_rekey_offload; + if (params) + handle->params.gtk_rekey_offload = params->gtk_rekey_offload; + handle->params.multi_dtim = multi_dtim; + handle->params.inact_tmo = inact_tmo; + if (params) { + handle->params.multi_dtim = params->multi_dtim; + handle->params.inact_tmo = params->inact_tmo; + } + if (napi) + moal_extflg_set(handle, EXT_NAPI); + if (tx_work) + moal_extflg_set(handle, EXT_TX_WORK); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (dfs_offload) + moal_extflg_set(handle, EXT_DFS_OFFLOAD); +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (host_mlme) + moal_extflg_set(handle, EXT_HOST_MLME); +#endif +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (disable_regd_by_driver) + moal_extflg_set(handle, EXT_DISABLE_REGD_BY_DRIVER); + if (reg_alpha2) + woal_dup_string(&handle->params.reg_alpha2, reg_alpha2); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (country_ie_ignore) + moal_extflg_set(handle, EXT_COUNTRY_IE_IGNORE); + if (beacon_hints) + moal_extflg_set(handle, EXT_BEACON_HINTS); +#endif +#endif + if (params && params->reg_alpha2) + woal_dup_string(&handle->params.reg_alpha2, params->reg_alpha2); + if (params) + moal_memcpy_ext(handle, handle->params.ext_flgs, + params->ext_flgs, sizeof(params->ext_flgs), + sizeof(handle->params.ext_flgs)); + + /* do some special handle for MFG mode */ +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode) { +#if defined(STA_WEXT) || defined(UAP_WEXT) + handle->params.cfg80211_wext = STA_WEXT_MASK | UAP_WEXT_MASK; +#else + handle->params.cfg80211_wext = 0; +#endif + handle->params.drv_mode = DRV_MODE_STA; + } +#endif + if (dfs53cfg > DFS_W53_OLD) { + PRINTM(MERROR, "Invalid value for dfs53cfg !\n"); + handle->params.dfs53cfg = DFS_W53_DEFAULT_FW; + } else { + handle->params.dfs53cfg = dfs53cfg; + if (params) + handle->params.dfs53cfg = params->dfs53cfg; + } +} + +/** + * @brief This function free module parameter memory + * + * @param handle A pointer to moal_handle structure + * + * @return N/A + */ +void woal_free_module_param(moal_handle *handle) +{ + moal_mod_para *params = &handle->params; + PRINTM(MMSG, "Free module params\n"); + if (params->fw_name) { + kfree(params->fw_name); + params->fw_name = NULL; + } + if (params->mac_addr) { + kfree(params->mac_addr); + params->mac_addr = NULL; + } +#ifdef STA_SUPPORT + if (params->sta_name) { + kfree(params->sta_name); + params->sta_name = NULL; + } +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + if (params->uap_name) { + kfree(params->uap_name); + params->uap_name = NULL; + } +#endif /* UAP_SUPPORT */ +#ifdef WIFI_DIRECT_SUPPORT + if (params->wfd_name) { + kfree(params->wfd_name); + params->wfd_name = NULL; + } +#endif /* WIFI_DIRECT_SUPPORT */ + if (params->dpd_data_cfg) { + kfree(params->dpd_data_cfg); + params->dpd_data_cfg = NULL; + } + if (params->init_cfg) { + kfree(params->init_cfg); + params->init_cfg = NULL; + } + if (params->cal_data_cfg) { + kfree(params->cal_data_cfg); + params->cal_data_cfg = NULL; + } + if (params->txpwrlimit_cfg) { + kfree(params->txpwrlimit_cfg); + params->txpwrlimit_cfg = NULL; + } + if (params->init_hostcmd_cfg) { + kfree(params->init_hostcmd_cfg); + params->init_hostcmd_cfg = NULL; + } + if (params->band_steer_cfg) { + kfree(params->band_steer_cfg); + params->band_steer_cfg = NULL; + } + if (params->reg_alpha2) { + kfree(params->reg_alpha2); + params->reg_alpha2 = NULL; + } +} + +/** + * @brief This function request module parameter data from user space + * + * @param handle A pointer to moal_handle structure + * @param mod_file A pointer to module parameter file path + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_req_mod_param(moal_handle *handle, char *mod_file) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct device *dev = handle->hotplug_device; + + if (dev == NULL) { + PRINTM(MERROR, "No device attached\n"); + ret = MLAN_STATUS_FAILURE; + goto out; + } + + ret = request_firmware(&handle->param_data, mod_file, dev); + if (ret < 0) + PRINTM(MERROR, "Request firmware: %s failed, error: %d\n", + mod_file, ret); +out: + return ret; +} + +#ifdef CONFIG_OF +/** + * @brief This function read the initial parameter from device tress + * + * @param handle A pointer to moal_handle structure + * + * @return N/A + */ +void woal_init_from_dev_tree(void) +{ + struct device_node *dt_node = NULL; + struct property *prop; + t_u32 data; + const char *string_data; + + ENTER(); + + if (!dts_enable) { + PRINTM(MIOCTL, "DTS is disabled!"); + return; + } + + dt_node = of_find_node_by_name(NULL, "sdxxx-wlan"); + if (!dt_node) { + LEAVE(); + return; + } + for_each_property_of_node (dt_node, prop) { + if (!strncmp(prop->name, "drv_mode", strlen("drv_mode"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "drv_mode=0x%x\n", data); + drv_mode = data; + } + } +#ifdef DEBUG_LEVEL1 + else if (!strncmp(prop->name, "drvdbg", strlen("drvdbg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "drvdbg=0x%x\n", data); + drvdbg = data; + } + } +#endif + else if (!strncmp(prop->name, "dev_cap_mask", + strlen("dev_cap_mask"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "dev_cap_mask=0x%x\n", data); + dev_cap_mask = data; + } + } else if (!strncmp(prop->name, "hw_test", strlen("hw_test"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "hw_test=0x%x\n", data); + hw_test = data; + } + } +#if defined(SDIO) + else if (!strncmp(prop->name, "slew_rate", + strlen("slew_rate"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "slew_rate=0x%x\n", data); + slew_rate = data; + } + } +#endif + else if (!strncmp(prop->name, "tx_work", strlen("tx_work"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "tx_work=0x%x\n", data); + tx_work = data; + } + } +#ifdef MFG_CMD_SUPPORT + else if (!strncmp(prop->name, "mfg_mode", strlen("mfg_mode"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "mfg_mode=0x%x\n", data); + mfg_mode = data; + } + } +#endif + else if (!strncmp(prop->name, "mac_addr", strlen("mac_addr"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + mac_addr = (char *)string_data; + PRINTM(MIOCTL, "mac_addr=%s\n", mac_addr); + } + } else if (!strncmp(prop->name, "fw_name", strlen("fw_name"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + fw_name = (char *)string_data; + PRINTM(MIOCTL, "fw_name=%s\n", fw_name); + } + } +#if defined(STA_WEXT) || defined(UAP_WEXT) + else if (!strncmp(prop->name, "cfg80211_wext", + strlen("cfg80211_wext"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "cfg80211_wext=0x%x\n", data); + cfg80211_wext = data; + } + } +#endif +#ifdef STA_SUPPORT + else if (!strncmp(prop->name, "sta_name", strlen("sta_name"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + sta_name = (char *)string_data; + PRINTM(MIOCTL, "sta_name=%s\n", sta_name); + } + } +#endif +#ifdef WIFI_DIRECT_SUPPORT + else if (!strncmp(prop->name, "wfd_name", strlen("wfd_name"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + wfd_name = (char *)string_data; + PRINTM(MIOCTL, "wfd_name=%s\n", wfd_name); + } + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + else if (!strncmp(prop->name, "disable_regd_by_driver", + strlen("disable_regd_by_driver"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "disable_regd_by_driver=0x%x\n", + data); + disable_regd_by_driver = data; + } + } else if (!strncmp(prop->name, "reg_alpha2", + strlen("reg_alpha2"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + reg_alpha2 = (char *)string_data; + PRINTM(MIOCTL, "reg_alpha2=%s\n", reg_alpha2); + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + else if (!strncmp(prop->name, "country_ie_ignore", + strlen("country_ie_ignore"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "country_ie_ignore=0x%x\n", + data); + country_ie_ignore = data; + } + } else if (!strncmp(prop->name, "beacon_hints", + strlen("beacon_hints"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "beacon_hints=0x%x\n", data); + beacon_hints = data; + } + } +#endif +#endif +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + else if (!strncmp(prop->name, "max_vir_bss", + strlen("max_vir_bss"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "max_vir_bss=0x%x\n", data); + max_vir_bss = data; + } + } +#endif +#endif + else if (!strncmp(prop->name, "dpd_data_cfg", + strlen("dpd_data_cfg"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + dpd_data_cfg = (char *)string_data; + PRINTM(MIOCTL, "dpd_data_cfg=%s\n", + dpd_data_cfg); + } + } else if (!strncmp(prop->name, "init_cfg", + strlen("init_cfg"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + init_cfg = (char *)string_data; + PRINTM(MIOCTL, "init_cfg=%s\n", init_cfg); + } + } else if (!strncmp(prop->name, "cal_data_cfg", + strlen("cal_data_cfg"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + cal_data_cfg = (char *)string_data; + PRINTM(MIOCTL, "cal_data_cfg=%s\n", + cal_data_cfg); + } + } else if (!strncmp(prop->name, "txpwrlimit_cfg", + strlen("txpwrlimit_cfg"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + txpwrlimit_cfg = (char *)string_data; + PRINTM(MIOCTL, "txpwrlimit_cfg=%s\n", + txpwrlimit_cfg); + } + } else if (!strncmp(prop->name, "cntry_txpwr", + strlen("cntry_txpwr"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + cntry_txpwr = data; + PRINTM(MIOCTL, "cntry_txpwr=%d\n", cntry_txpwr); + } + } else if (!strncmp(prop->name, "init_hostcmd_cfg", + strlen("init_hostcmd_cfg"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + init_hostcmd_cfg = (char *)string_data; + PRINTM(MIOCTL, "init_hostcmd_cfg=%s\n", + init_hostcmd_cfg); + } + } else if (!strncmp(prop->name, "band_steer_cfg", + strlen("band_steer_cfg"))) { + if (!of_property_read_string(dt_node, prop->name, + &string_data)) { + band_steer_cfg = (char *)string_data; + PRINTM(MIOCTL, "band_steer_cfg=%s\n", + band_steer_cfg); + } + } else if (!strncmp(prop->name, "pmic", strlen("pmic"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + pmic = data; + PRINTM(MIOCTL, "pmic=%d\n", pmic); + } + } else if (!strncmp(prop->name, "antcfg", strlen("antcfg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + antcfg = data; + PRINTM(MIOCTL, "antcfg=%d\n", antcfg); + } + } else if (!strncmp(prop->name, "hs_wake_interval", + strlen("hs_wake_interval"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + hs_wake_interval = data; + PRINTM(MIOCTL, "hs_wake_interval=%d\n", + hs_wake_interval); + } + } else if (!strncmp(prop->name, "indication_gpio", + strlen("indication_gpio"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + indication_gpio = (t_u8)data; + PRINTM(MIOCTL, "indication_gpio=%d\n", + indication_gpio); + } + } else if (!strncmp(prop->name, "hs_mimo_switch", + strlen("hs_mimo_switch"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + hs_mimo_switch = data; + PRINTM(MIOCTL, "hs_mimo_switch=%d\n", + hs_mimo_switch); + } + } +#ifdef WIFI_DIRECT_SUPPORT + else if (!strncmp(prop->name, "GoAgeoutTime", + strlen("GoAgeoutTime"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + GoAgeoutTime = data; + PRINTM(MIOCTL, "GoAgeoutTime=%d\n", + GoAgeoutTime); + } + } +#endif + else if (!strncmp(prop->name, "indrstcfg", + strlen("indrstcfg"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + indrstcfg = data; + PRINTM(MIOCTL, "indrstcfg=%d\n", indrstcfg); + } + } else if (!strncmp(prop->name, "fixed_beacon_buffer", + strlen("fixed_beacon_buffer"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + fixed_beacon_buffer = data; + PRINTM(MIOCTL, "fixed_beacon_buffer=%d\n", + fixed_beacon_buffer); + } + } else if (!strncmp(prop->name, "multi_dtim", + strlen("multi_dtim"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + multi_dtim = data; + PRINTM(MIOCTL, "multi_dtim=%d\n", multi_dtim); + } + } else if (!strncmp(prop->name, "inact_tmo", + strlen("inact_tmo"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + inact_tmo = data; + PRINTM(MIOCTL, "inact_tmo=%d\n", inact_tmo); + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + else if (!strncmp(prop->name, "dfs_offload", + strlen("dfs_offload"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + dfs_offload = data; + PRINTM(MIOCTL, "dfs_offload=%d\n", dfs_offload); + } + } +#endif + else if (!strncmp(prop->name, "gtk_rekey_offload", + strlen("gtk_rekey_offload"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + gtk_rekey_offload = data; + PRINTM(MIOCTL, "gtk_rekey_offload=%d\n", + gtk_rekey_offload); + } + } +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + else if (!strncmp(prop->name, "host_mlme", + strlen("host_mlme"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MIOCTL, "host_mlme=0x%x\n", data); + host_mlme = data; + } + } +#endif +#endif +#ifdef UAP_SUPPORT + else if (!strncmp(prop->name, "uap_max_sta", + strlen("uap_max_sta"))) { + if (!of_property_read_u32(dt_node, prop->name, &data)) { + PRINTM(MERROR, "uap_max_sta=0x%x\n", data); + uap_max_sta = data; + } + } +#endif + } + LEAVE(); + return; +} +#endif + +/** + * @brief This function check if configuration block id could be used + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_validate_cfg_id(moal_handle *handle) +{ + int i; + mlan_status ret = MLAN_STATUS_SUCCESS; + for (i = 0; i < MAX_MLAN_ADAPTER; i++) { + if (m_handle[i] == NULL || m_handle[i] == handle) + continue; + if (m_handle[i]->card_type == handle->card_type) { + if (m_handle[i]->blk_id == handle->blk_id) { + ret = MLAN_STATUS_FAILURE; + } + } + } + return ret; +} + +/** + * @brief This function skip current configuration block + * + * @param data A pointer to buffer of module configuration file + * @param size Size of module configuration file + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status parse_skip_cfg_block(t_u8 *data, t_u32 size) +{ + int end = 0; + t_u8 line[MAX_LINE_LEN]; + while (parse_cfg_get_line(data, size, line) != -1) { + if (strncmp(line, "}", strlen("}")) == 0) { + end = 1; + break; + } + if (end == 0 && strstr(line, "{") != 0) + break; + } + return (end == 1) ? MLAN_STATUS_SUCCESS : MLAN_STATUS_FAILURE; +} + +/** + * @brief This function handle fallback processing for invalid + * block id with same card type + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_cfg_fallback_process(moal_handle *handle) +{ + int i, blk_id = 0x7fffffff, idx = -1; + mlan_status ret = MLAN_STATUS_FAILURE; + PRINTM(MMSG, "Configuration block, fallback processing\n"); + for (i = 0; i < MAX_MLAN_ADAPTER; i++) { + if (m_handle[i] == NULL || m_handle[i] == handle || + m_handle[i]->card_type != handle->card_type) + continue; + /* use configuratino with lowest blk_id value */ + if (m_handle[i]->blk_id >= 0 && m_handle[i]->blk_id <= blk_id) { + idx = i; + blk_id = m_handle[i]->blk_id; + } + } + if (idx >= 0 && idx < MAX_MLAN_ADAPTER) { + ret = MLAN_STATUS_SUCCESS; + handle->blk_id = m_handle[idx]->blk_id; + PRINTM(MMSG, + "Configuration fallback to, card_type: 0x%x, blk_id: 0x%x\n", + handle->card_type, handle->blk_id); + woal_setup_module_param(handle, &m_handle[idx]->params); + } + return ret; +} + +/** + * @brief This function parse and initialize module parameters + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_init_module_param(moal_handle *handle) +{ + int no_match = 1; + t_u32 size, i, tbl_size; + t_u8 line[MAX_LINE_LEN], *data = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + char *card_type = NULL, *blk_id = NULL; + + memset(line, 0, MAX_LINE_LEN); + woal_setup_module_param(handle, NULL); + if (mod_para == NULL) { + PRINTM(MMSG, "No module param cfg file specified\n"); + goto out; + } + if (woal_req_mod_param(handle, mod_para)) { + PRINTM(MERROR, "Failed to get module param file\n"); + ret = MLAN_STATUS_FAILURE; + goto out; + } + tbl_size = sizeof(card_type_map_tbl) / sizeof(card_type_map_tbl[0]); + for (i = 0; i < tbl_size; i++) + if (handle->card_type == card_type_map_tbl[i].card_type) + break; + if (i >= tbl_size) { + PRINTM(MERROR, "No card type entry found for card type: 0x%x\n", + handle->card_type); + ret = MLAN_STATUS_FAILURE; + goto out; + } + PRINTM(MMSG, "%s: init module param from usr cfg\n", + card_type_map_tbl[i].name); + size = handle->param_data->size; + data = (t_u8 *)handle->param_data->data; + while (parse_cfg_get_line(data, size, line) != -1) { + if (line[0] == '#') + continue; + if (strstr(line, "={")) { + ret = parse_line_read_card_info(line, &card_type, + &blk_id); + if (ret != MLAN_STATUS_SUCCESS) + goto out; + PRINTM(MINFO, + "Traverse, card_type: %s, config block: %s\n", + card_type, blk_id); + if (strcmp(card_type_map_tbl[i].name, card_type) == 0) { + /* parse config block id */ + if (blk_id == NULL) + handle->blk_id = 0; + else + woal_atoi(&handle->blk_id, blk_id); + PRINTM(MINFO, + "Validation check, %s, config block: %d\n", + card_type, handle->blk_id); + /* check validation of config id */ + if (woal_validate_cfg_id(handle) != + MLAN_STATUS_SUCCESS) { + ret = parse_skip_cfg_block(data, size); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MMSG, + "failed to skip block\n"); + goto out; + } + continue; + } + no_match = 0; + PRINTM(MMSG, + "card_type: %s, config block: %d\n", + card_type, handle->blk_id); + /* parse config block */ + ret = parse_cfg_read_block(data, size, handle); + if (ret != MLAN_STATUS_SUCCESS) + goto out; + break; + } + } + } + if (no_match) + ret = woal_cfg_fallback_process(handle); +out: + if (handle->param_data) { + release_firmware(handle->param_data); + /* rewind pos */ + parse_cfg_get_line(NULL, 0, NULL); + } + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Invalid block: %s\n", line); + woal_free_module_param(handle); + woal_setup_module_param(handle, NULL); + } + return ret; +} + +module_param(mod_para, charp, 0); +MODULE_PARM_DESC(mod_para, "Module parameters configuration file"); +module_param(hw_test, int, 0660); +MODULE_PARM_DESC(hw_test, "0: Disable hardware test; 1: Enable hardware test"); +#ifdef CONFIG_OF +module_param(dts_enable, int, 0); +MODULE_PARM_DESC(dts_enable, "0: Disable DTS; 1: Enable DTS"); +#endif +module_param(fw_name, charp, 0660); +MODULE_PARM_DESC(fw_name, "Firmware name"); +module_param(req_fw_nowait, int, 0); +MODULE_PARM_DESC( + req_fw_nowait, + "0: Use request_firmware API; 1: Use request_firmware_nowait API"); +module_param(fw_reload, int, 0); +MODULE_PARM_DESC(fw_reload, + "0: disable fw_reload; 1: enable fw reload feature"); +module_param(fw_serial, int, 0); +MODULE_PARM_DESC( + fw_serial, + "0: support parallel download FW; 1: support serial download FW"); +module_param(mac_addr, charp, 0660); +MODULE_PARM_DESC(mac_addr, "MAC address"); +#ifdef MFG_CMD_SUPPORT +module_param(mfg_mode, int, 0660); +MODULE_PARM_DESC(mfg_mode, + "0: Download normal firmware; 1: Download MFG firmware"); +#endif /* MFG_CMD_SUPPORT */ +module_param(drv_mode, int, 0660); +MODULE_PARM_DESC(drv_mode, "Bit 0: STA; Bit 1: uAP; Bit 2: WIFIDIRECT"); + +#ifdef STA_SUPPORT +module_param(max_sta_bss, int, 0); +MODULE_PARM_DESC(max_sta_bss, "Number of STA interfaces (1)"); +module_param(sta_name, charp, 0); +MODULE_PARM_DESC(sta_name, "STA interface name"); +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT +module_param(max_uap_bss, int, 0); +MODULE_PARM_DESC(max_uap_bss, "Number of uAP interfaces (1)"); +module_param(uap_name, charp, 0); +MODULE_PARM_DESC(uap_name, "uAP interface name"); +#endif /* UAP_SUPPORT */ +#ifdef WIFI_DIRECT_SUPPORT +module_param(max_wfd_bss, int, 0); +MODULE_PARM_DESC(max_wfd_bss, "Number of WIFIDIRECT interfaces (1)"); +module_param(wfd_name, charp, 0); +MODULE_PARM_DESC(wfd_name, "WIFIDIRECT interface name"); +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +module_param(max_vir_bss, int, 0); +MODULE_PARM_DESC(max_vir_bss, "Number of Virtual interfaces (0)"); +#endif +#endif /* WIFI_DIRECT_SUPPORT */ +#ifdef DEBUG_LEVEL1 +module_param(drvdbg, uint, 0660); +MODULE_PARM_DESC(drvdbg, "Driver debug"); +#endif /* DEBUG_LEVEL1 */ +module_param(auto_ds, int, 0660); +MODULE_PARM_DESC( + auto_ds, + "0: MLAN default; 1: Enable auto deep sleep; 2: Disable auto deep sleep"); +module_param(ps_mode, int, 0660); +MODULE_PARM_DESC( + ps_mode, + "0: MLAN default; 1: Enable IEEE PS mode; 2: Disable IEEE PS mode"); +module_param(p2a_scan, int, 0660); +MODULE_PARM_DESC( + p2a_scan, + "0: MLAN default; 1: Enable passive to active scan for DFS channel; 2: Disable passive to active scan for DFS channel"); +module_param(scan_chan_gap, int, 0660); +MODULE_PARM_DESC( + scan_chan_gap, + "Time gap between two scans in milliseconds when connected to AP(max value 500ms)"); +module_param(max_tx_buf, int, 0); +MODULE_PARM_DESC(max_tx_buf, "Maximum Tx buffer size (2048/4096/8192)"); + +#if defined(SDIO) +module_param(intmode, int, 0); +MODULE_PARM_DESC(intmode, "0: INT_MODE_SDIO, 1: INT_MODE_GPIO"); +module_param(gpiopin, int, 0); +MODULE_PARM_DESC(gpiopin, "255:new GPIO int mode, other vlue: gpio pin number"); +#endif + +#ifdef SDIO_SUSPEND_RESUME +module_param(pm_keep_power, int, 0); +MODULE_PARM_DESC(pm_keep_power, "1: PM keep power; 0: PM no power"); +module_param(shutdown_hs, int, 0); +MODULE_PARM_DESC(shutdown_hs, + "1: Enable HS when shutdown; 0: No HS when shutdown"); +#endif +#if defined(STA_SUPPORT) +module_param(cfg_11d, int, 0); +MODULE_PARM_DESC(cfg_11d, + "0: MLAN default; 1: Enable 802.11d; 2: Disable 802.11d"); +#endif +#if defined(SDIO) +module_param(slew_rate, int, 0); +MODULE_PARM_DESC( + slew_rate, + "0:has the slowest slew rate, then 01, then 02, and 03 has the highest slew rate"); +#endif +module_param(tx_work, uint, 0660); +MODULE_PARM_DESC(tx_work, "1: Enable tx_work; 0: Disable tx_work"); +module_param(dpd_data_cfg, charp, 0); +MODULE_PARM_DESC(dpd_data_cfg, "DPD data file name"); +module_param(init_cfg, charp, 0); +MODULE_PARM_DESC(init_cfg, "Init config file name"); +module_param(cal_data_cfg, charp, 0); +MODULE_PARM_DESC(cal_data_cfg, "Calibration data file name"); +module_param(txpwrlimit_cfg, charp, 0); +MODULE_PARM_DESC(txpwrlimit_cfg, + "Set configuration data of Tx power limitation"); +module_param(cntry_txpwr, int, 0); +MODULE_PARM_DESC( + cntry_txpwr, + "Allow setting tx power table of country; 0: disable (default), 1: enable."); +module_param(init_hostcmd_cfg, charp, 0); +MODULE_PARM_DESC(init_hostcmd_cfg, "Init hostcmd file name"); +module_param(band_steer_cfg, charp, 0); +MODULE_PARM_DESC(band_steer_cfg, "band steer cfg file name"); +module_param(cfg80211_wext, int, 0660); +MODULE_PARM_DESC(cfg80211_wext, +#ifdef STA_WEXT + "Bit 0: STA WEXT; " +#endif +#ifdef UAP_WEXT + "Bit 1: UAP WEXT; " +#endif +#ifdef STA_CFG80211 + "Bit 2: STA CFG80211; " +#endif +#ifdef UAP_CFG80211 + "Bit 3: UAP CFG80211;" +#endif +); +#if defined(USB) +module_param(skip_fwdnld, int, 0); +MODULE_PARM_DESC(skip_fwdnld, "0: Enable FW download; 1: Disable FW download"); +#endif +module_param(wq_sched_prio, int, 0); +module_param(wq_sched_policy, int, 0); +MODULE_PARM_DESC(wq_sched_prio, "Priority of work queue"); +MODULE_PARM_DESC( + wq_sched_policy, + "0: SCHED_NORMAL; 1: SCHED_FIFO; 2: SCHED_RR; 3: SCHED_BATCH; 5: SCHED_IDLE"); +module_param(rx_work, int, 0); +MODULE_PARM_DESC( + rx_work, + "0: default; 1: Enable rx_work_queue; 2: Disable rx_work_queue"); +module_param(aggrctrl, int, 0); +MODULE_PARM_DESC(aggrctrl, + "1: Enable Tx aggregation; 0: Disable Tx aggregation"); +#ifdef USB +module_param(usb_aggr, int, 0); +MODULE_PARM_DESC(usb_aggr, + "0: MLAN default; 1: Enable USB aggr; 2: Disable USB aggr"); +#endif +#ifdef PCIE +module_param(pcie_int_mode, int, 0); +MODULE_PARM_DESC(pcie_int_mode, "0: Legacy mode; 1: MSI mode; 2: MSI-X mode"); +#endif /* PCIE */ +module_param(low_power_mode_enable, int, 0); +MODULE_PARM_DESC(low_power_mode_enable, "0/1: Disable/Enable Low Power Mode"); + +#ifdef ANDROID_KERNEL +module_param(wakelock_timeout, int, 0); +MODULE_PARM_DESC(wakelock_timeout, "set wakelock_timeout value (ms)"); +#endif + +module_param(dev_cap_mask, uint, 0); +MODULE_PARM_DESC(dev_cap_mask, "Device capability mask"); + +#ifdef SDIO +module_param(sdio_rx_aggr, int, 0); +MODULE_PARM_DESC(sdio_rx_aggr, + "1: Enable SDIO rx aggr; 0: Disable SDIO rx aggr"); +#endif + +module_param(pmic, int, 0); +MODULE_PARM_DESC( + pmic, + "1: Send pmic configure cmd to firmware; 0: No pmic configure cmd sent to firmware"); + +module_param(antcfg, int, 0660); +MODULE_PARM_DESC( + antcfg, + "0:default; SD8887/SD8987-[1:Tx/Rx antenna 1, 2:Tx/Rx antenna 2, 0xffff:enable antenna diversity];SD8897/SD8997-[Bit0:Rx Path A, Bit1:Rx Path B, Bit 4:Tx Path A, Bit 5:Tx Path B];9098/9097-[Bit 0: 2G Tx/Rx path A, Bit 1: 2G Tx/Rx path B,Bit 8: 5G Tx/Rx path A, Bit 9: 5G Tx/Rx path B]"); + +module_param(uap_oper_ctrl, uint, 0); +MODULE_PARM_DESC(uap_oper_ctrl, "0:default; 0x20001:uap restarts on channel 6"); + +module_param(hs_wake_interval, int, 0660); +MODULE_PARM_DESC( + hs_wake_interval, + "Host sleep wakeup interval,it will round to nearest multiple dtim*beacon_period in fw"); +module_param(indication_gpio, int, 0); +MODULE_PARM_DESC( + indication_gpio, + "GPIO to indicate wakeup source; high four bits: level for normal wakeup; low four bits: GPIO pin number."); +module_param(disconnect_on_suspend, int, 0); +MODULE_PARM_DESC( + disconnect_on_suspend, + "1: Enable disconnect wifi on suspend; 0: Disable disconnect wifi on suspend"); +module_param(hs_mimo_switch, int, 0660); +MODULE_PARM_DESC( + hs_mimo_switch, + "Dynamic MIMO-SISO switch during host sleep; 0: disable (default), 1: enable"); + +module_param(indrstcfg, int, 0); +MODULE_PARM_DESC( + indrstcfg, + "Independent reset configuration; high byte: GPIO pin number; low byte: IR mode"); + +module_param(fixed_beacon_buffer, int, 0); +MODULE_PARM_DESC( + fixed_beacon_buffer, + "0: allocate default buffer size; 1: allocate max buffer size."); + +#ifdef WIFI_DIRECT_SUPPORT +module_param(GoAgeoutTime, int, 0); +MODULE_PARM_DESC(GoAgeoutTime, + "0: use default ageout time; set Go age out time (TU 100ms)"); +#endif + +module_param(gtk_rekey_offload, int, 0); +MODULE_PARM_DESC( + gtk_rekey_offload, + "0: disable gtk_rekey_offload; 1: enable gtk_rekey_offload (default); 2: enable gtk_rekey_offload in suspend mode only;"); + +module_param(multi_dtim, ushort, 0); +MODULE_PARM_DESC(multi_dtim, "DTIM interval"); + +module_param(inact_tmo, ushort, 0); +MODULE_PARM_DESC(inact_tmo, "IEEE ps inactivity timout value"); + +module_param(napi, int, 0); +MODULE_PARM_DESC(napi, "1: enable napi api; 0: disable napi"); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +module_param(dfs_offload, int, 0); +MODULE_PARM_DESC(dfs_offload, "1: enable dfs offload; 0: disable dfs offload."); +#endif + +#ifdef UAP_SUPPORT +module_param(uap_max_sta, int, 0); +MODULE_PARM_DESC(uap_max_sta, "Maximum station number for UAP/GO."); +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +module_param(host_mlme, int, 0); +MODULE_PARM_DESC(host_mlme, + "1: Enable Host MLME Support; 0: Disable Host MLME support"); +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +module_param(disable_regd_by_driver, int, 0); +MODULE_PARM_DESC( + disable_regd_by_driver, + "0: reg domain set by driver enable(default); 1: reg domain set by driver disable"); +module_param(reg_alpha2, charp, 0660); +MODULE_PARM_DESC(reg_alpha2, "Regulatory alpha2"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +module_param(country_ie_ignore, int, 0); +MODULE_PARM_DESC( + country_ie_ignore, + "0: Follow countryIE from AP and beacon hint enable; 1: Ignore countryIE from AP and beacon hint disable"); +module_param(beacon_hints, int, 0); +MODULE_PARM_DESC(beacon_hints, + "0: enable beacon hints(default); 1: disable beacon hints"); +#endif +#endif + +module_param(dfs53cfg, int, 0); +MODULE_PARM_DESC(dfs53cfg, "0: fw default; 1: new w53 dfs; 2: old w53 dfs"); diff --git a/mxm_wifiex/wlan_src/mlinux/moal_ioctl.c b/mxm_wifiex/wlan_src/mlinux/moal_ioctl.c new file mode 100644 index 0000000..8cb4af2 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_ioctl.c @@ -0,0 +1,6979 @@ +/** @file moal_ioctl.c + * + * @brief This file contains ioctl function to MLAN + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" +#ifdef SDIO +#include "moal_sdio.h" +#endif +#ifdef USB +#include "moal_usb.h" +#endif +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#include "moal_cfg80211_util.h" +#endif +#endif + +/******************************************************** + Local Variables +********************************************************/ +#define MRVL_TLV_HEADER_SIZE 4 +/* NXP Channel config TLV ID */ +#define MRVL_CHANNELCONFIG_TLV_ID (0x0100 + 0x2a) /* 0x012a */ + +typedef struct _hostcmd_header { + /** Command Header : Command */ + t_u16 command; + /** Command Header : Size */ + t_u16 size; + /** Command Header : Sequence number */ + t_u16 seq_num; + /** Command Header : Result */ + t_u16 result; + /** Command action */ + t_u16 action; +} hostcmd_header, *phostcmd_header; + +/** Region code mapping */ +typedef struct _region_code_mapping_t { + /** Region */ + t_u8 region[COUNTRY_CODE_LEN]; + /** Code */ + t_u8 code; +} region_code_mapping_t; + +#define EU_REGION_CODE 0x30 + +/** Region code mapping table */ +static region_code_mapping_t region_code_mapping[] = { + {"US", 0x10}, /* US FCC */ + {"CA", 0x20}, /* IC Canada */ + {"SG", 0x10}, /* Singapore */ + {"EU", 0x30}, /* ETSI */ + {"AU", 0x30}, /* Australia */ + {"KR", 0x30}, /* Republic Of Korea */ + {"JP", 0x40}, /* Japan */ + {"CN", 0x50}, /* China */ + {"BR", 0x09}, /* Brazil */ + {"RU", 0x0f}, /* Russia */ + {"IN", 0x06}, /* India */ + {"MY", 0x06}, /* Malaysia */ + {"MX", 0x07}, /* Mexico */ + {"NE", 0x30}, /* New Zeland */ +}; + +/** EEPROM Region code mapping table */ +static region_code_mapping_t hw_region_code_mapping[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x20}, /* IC Canada */ + {"KR ", 0x30}, /* Korea */ + {"CN ", 0x50}, /* China */ + {"ES ", 0x31}, /* Spain */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ + {"JP ", 0x41}, /* Japan */ +}; + +/** Country code for ETSI */ +static t_u8 eu_country_code_table[][COUNTRY_CODE_LEN] = { + "AL", "AD", "AT", "AU", "BY", "BE", "BA", "BG", "HR", "CY", "CZ", "DK", + "EE", "FI", "FR", "MK", "DE", "GR", "HU", "IS", "IE", "IT", "KR", "LV", + "LI", "LT", "LU", "MT", "MD", "MC", "ME", "NL", "NO", "PL", "RO", "RU", + "SM", "RS", "SI", "SK", "ES", "SE", "CH", "TR", "UA", "UK", "GB", "NE"}; + +/******************************************************** + 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 + +#ifdef MFG_CMD_SUPPORT +/** Mfg mode */ +extern int mfg_mode; +#endif + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function converts region string to region code + * + * @param country_code Region string + * + * @return Region code + */ +t_bool woal_is_country_code_supported(t_u8 *country_code) +{ + t_u8 i; + ENTER(); + + for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) { + if (!memcmp(country_code, region_code_mapping[i].region, + COUNTRY_CODE_LEN - 1)) { + PRINTM(MIOCTL, + "found country code in region_code table\n"); + LEAVE(); + return MTRUE; + } + } + + LEAVE(); + return MFALSE; +} +/** + * @brief This function converts region string to region code + * + * @param region_string Region string + * + * @return Region code + */ +t_u8 region_string_2_region_code(char *region_string) +{ + t_u8 i; + + ENTER(); + for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) { + if (!memcmp(region_string, region_code_mapping[i].region, + strlen(region_string))) { + LEAVE(); + return region_code_mapping[i].code; + } + } + + /* If still not found, look for code in EU country code table */ + for (i = 0; i < ARRAY_SIZE(eu_country_code_table); i++) { + if (!memcmp(region_string, eu_country_code_table[i], + COUNTRY_CODE_LEN - 1)) { + PRINTM(MIOCTL, "found region code=%d in EU table\n", + EU_REGION_CODE); + LEAVE(); + return EU_REGION_CODE; + } + } + + /* Default is US */ + LEAVE(); + return region_code_mapping[0].code; +} + +/** + * @brief This function converts region string to region code + * + * @param country_code Region string + * + * @return Region code + */ +t_bool woal_is_etsi_country(t_u8 *country_code) +{ + t_u8 i; + ENTER(); + + for (i = 0; i < ARRAY_SIZE(eu_country_code_table); i++) { + if (!memcmp(country_code, eu_country_code_table[i], + COUNTRY_CODE_LEN - 1)) { + PRINTM(MIOCTL, "found region code=%d in EU table\n", + EU_REGION_CODE); + LEAVE(); + return MTRUE; + } + } + + LEAVE(); + return MFALSE; +} + +/** + * @brief This function converts region string to region code + * + * @param region_code region code + * + * @return Region string or NULL + */ +char *region_code_2_string(t_u8 region_code) +{ + t_u8 i; + + ENTER(); + for (i = 0; i < ARRAY_SIZE(hw_region_code_mapping); i++) { + if (hw_region_code_mapping[i].code == region_code) { + LEAVE(); + return hw_region_code_mapping[i].region; + } + } + LEAVE(); + return NULL; +} + +t_u8 woal_is_valid_alpha2(char *alpha2) +{ + if (!alpha2 || strlen(alpha2) < 2) + return MFALSE; + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return MTRUE; + return MFALSE; +} + +/** + * @brief Get second channel offset + * + * @param chan channel num + * @return second channel offset + */ +t_u8 woal_get_second_channel_offset(int chan) +{ + t_u8 chan2Offset = SEC_CHAN_NONE; + + switch (chan) { + case 36: + case 44: + case 52: + case 60: + case 100: + case 108: + case 116: + case 124: + case 132: + case 140: + case 149: + case 157: + chan2Offset = SEC_CHAN_ABOVE; + break; + case 40: + case 48: + case 56: + case 64: + case 104: + case 112: + case 120: + case 128: + case 136: + case 144: + case 153: + case 161: + chan2Offset = SEC_CHAN_BELOW; + break; + case 165: + /* Special Case: 20Mhz-only Channel */ + chan2Offset = SEC_CHAN_NONE; + break; + } + return chan2Offset; +} + +/** + * @brief Copy mc address to the mlist + * + * @param mlist A pointer to mlan_multicast_list structure + * @param mac mc address + * + * @return N/A + */ +static inline void woal_copy_mc_addr(mlan_multicast_list *mlist, + mlan_802_11_mac_addr mac) +{ + int i = 0; + for (i = 0; i < mlist->num_multicast_addr; i++) { + if (!memcmp(&mlist->mac_list[i], mac, ETH_ALEN)) + return; + } + if (mlist->num_multicast_addr < MLAN_MAX_MULTICAST_LIST_SIZE) + moal_memcpy_ext(NULL, + &mlist->mac_list[mlist->num_multicast_addr], + mac, ETH_ALEN, sizeof(mlan_802_11_mac_addr)); + mlist->num_multicast_addr++; + return; +} + +/** + * @brief Copy multicast table + * + * @param mlist A pointer to mlan_multicast_list structure + * @param dev A pointer to net_device structure + * + * @return Number of multicast addresses + */ +static inline int woal_copy_mcast_addr(mlan_multicast_list *mlist, + struct net_device *dev) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + struct dev_mc_list *mcptr = dev->mc_list; + int i = 0; +#else + struct netdev_hw_addr *mcptr = NULL; +#endif /* < 2.6.35 */ + + ENTER(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + for (i = 0; i < dev->mc_count && mcptr; i++) { + woal_copy_mc_addr(mlist, mcptr->dmi_addr); + mcptr = mcptr->next; + } +#else + netdev_for_each_mc_addr (mcptr, dev) + woal_copy_mc_addr(mlist, mcptr->addr); +#endif /* < 2.6.35 */ + LEAVE(); + return mlist->num_multicast_addr; +} + +/** + * @brief copy mc list from all the active interface + * + * @param handle A pointer to moal_handle + * @param mlist A pointer to multicast list + * + * @return total_mc_count + */ +static int woal_copy_all_mc_list(moal_handle *handle, + mlan_multicast_list *mlist) +{ + int i; + moal_private *priv = NULL; +#ifdef STA_SUPPORT + int mc_count = 0; +#endif + ENTER(); + for (i = 0; i < handle->priv_num && (priv = handle->priv[i]); i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + mc_count = priv->netdev->mc_count; +#else + mc_count = netdev_mc_count(priv->netdev); +#endif + if (mc_count) + woal_copy_mcast_addr(mlist, + priv->netdev); + } + } +#endif + } + PRINTM(MIOCTL, "total mc_count=%d\n", mlist->num_multicast_addr); + LEAVE(); + return mlist->num_multicast_addr; +} + +/** + * @brief Fill in wait queue + * + * @param priv A pointer to moal_private structure + * @param wait A pointer to wait_queue structure + * @param wait_option Wait option + * + * @return N/A + */ +static inline void woal_fill_wait_queue(moal_private *priv, wait_queue *wait, + t_u8 wait_option) +{ + ENTER(); + wait->start_time = jiffies; + wait->condition = MFALSE; + wait->wait_timeout = MFALSE; + switch (wait_option) { + case MOAL_NO_WAIT: + break; + case MOAL_IOCTL_WAIT: + init_waitqueue_head(&wait->wait); + break; + case MOAL_IOCTL_WAIT_TIMEOUT: + init_waitqueue_head(&wait->wait); + wait->wait_timeout = MTRUE; + break; + } + LEAVE(); + return; +} + +/** + * @brief Wait mlan ioctl complete + * + * @param priv A pointer to moal_private structure + * @param req A pointer to mlan_ioctl_req structure + * @param wait_option Wait option + * + * @return N/A + */ +static inline mlan_status woal_wait_ioctl_complete(moal_private *priv, + mlan_ioctl_req *req, + t_u8 wait_option) +{ + mlan_status status; + wait_queue *wait = (wait_queue *)req->reserved_1; + unsigned long flags; + + ENTER(); + + priv->phandle->ioctl_timeout = MFALSE; + + switch (wait_option) { + case MOAL_NO_WAIT: + break; + case MOAL_IOCTL_WAIT: + while (wait_event_interruptible_exclusive( + wait->wait, wait->condition) == -ERESTARTSYS && + wait->retry < MAX_RETRY_CNT) { + wait->retry++; + } + break; + case MOAL_IOCTL_WAIT_TIMEOUT: + wait_event_timeout(wait->wait, wait->condition, + MOAL_IOCTL_TIMEOUT); + break; + } + spin_lock_irqsave(&priv->phandle->driver_lock, flags); + if (wait->condition == MFALSE) { + if (wait_option == MOAL_IOCTL_WAIT_TIMEOUT) { + priv->phandle->ioctl_timeout = MTRUE; + PRINTM(MMSG, + "wlan: IOCTL timeout %p id=0x%x, sub_id=0x%x, wait_option=%d, action=%d\n", + req, req->req_id, (*(t_u32 *)req->pbuf), + wait_option, (int)req->action); + } else { + PRINTM(MMSG, + "wlan: IOCTL by signal %p id=0x%x, sub_id=0x%x, wait_option=%d, action=%d\n", + req, req->req_id, (*(t_u32 *)req->pbuf), + wait_option, (int)req->action); + } + req->reserved_1 = 0; + status = MLAN_STATUS_PENDING; + } else { + status = wait->status; + } + spin_unlock_irqrestore(&priv->phandle->driver_lock, flags); + + LEAVE(); + return status; +} + +/** + * @brief CAC period block cmd handler + * + * @param priv A pointer to moal_private structure + * @param req A pointer to mlan_ioctl_req buffer + * + * @return MTRUE/MFALSE + */ +static inline t_bool woal_cac_period_block_cmd(moal_private *priv, + pmlan_ioctl_req req) +{ + mlan_status ret = MFALSE; + t_u32 sub_command; + + ENTER(); + if (req == NULL || req->pbuf == NULL) + goto done; + + sub_command = *(t_u32 *)req->pbuf; + + switch (req->req_id) { + case MLAN_IOCTL_SCAN: + if (sub_command == MLAN_OID_SCAN_NORMAL || + sub_command == MLAN_OID_SCAN_SPECIFIC_SSID || + sub_command == MLAN_OID_SCAN_USER_CONFIG) + ret = MTRUE; + break; + case MLAN_IOCTL_BSS: + if (sub_command == MLAN_OID_BSS_STOP || + sub_command == MLAN_OID_BSS_CHANNEL + /* sub_command == MLAN_OID_BSS_ROLE */) + ret = MTRUE; +#ifdef UAP_SUPPORT + else if (sub_command == MLAN_OID_UAP_BSS_CONFIG) { + mlan_ds_bss *bss = (mlan_ds_bss *)req->pbuf; + if (bss->param.bss_config.channel) + ret = MTRUE; + else + ret = MFALSE; + } +#endif + break; + case MLAN_IOCTL_RADIO_CFG: + if (sub_command == MLAN_OID_BAND_CFG || + sub_command == MLAN_OID_REMAIN_CHAN_CFG) + ret = MTRUE; + break; + case MLAN_IOCTL_SNMP_MIB: + if (sub_command == MLAN_OID_SNMP_MIB_DOT11D) + ret = MTRUE; +#if defined(UAP_SUPPORT) + if (sub_command == MLAN_OID_SNMP_MIB_DOT11H) + ret = MTRUE; +#endif + break; + case MLAN_IOCTL_11D_CFG: +#ifdef STA_SUPPORT + if (sub_command == MLAN_OID_11D_CFG_ENABLE) + ret = MTRUE; +#endif +#ifdef UAP_SUPPORT + if (sub_command == MLAN_OID_11D_DOMAIN_INFO) + ret = MTRUE; +#endif + if (sub_command == MLAN_OID_11D_DOMAIN_INFO_EXT) + 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 = NULL; + mlan_status status; + unsigned long flags; + t_u32 sub_command = 0; + + ENTER(); + + if (!priv || !priv->phandle || !priv->phandle->pmlan_adapter || !req) { + PRINTM(MINFO, + "priv or priv->phandle or priv->phandle->pmlan_adapter or req is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + sub_command = *(t_u32 *)req->pbuf; + + if (sub_command != MLAN_OID_GET_DEBUG_INFO) { + if (priv->phandle->surprise_removed == MTRUE || + priv->phandle->driver_status) { + PRINTM(MCMND, + "IOCTL is not allowed while the device is not present or hang\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (priv->phandle->is_suspended == MTRUE) { + PRINTM(MCMND, "IOCTL is not allowed while suspended\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#ifdef MFG_CMD_SUPPORT + if (mfg_mode && sub_command != MLAN_OID_MISC_HOST_CMD) { + PRINTM(MCMND, "IOCTL is not allowed while suspended\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#endif + } + /* For MLAN_OID_MISC_HOST_CMD, action is 0, "action set" is checked + * later */ + if ((req->action == MLAN_ACT_SET || req->action == 0) && + priv->phandle->cac_period == MTRUE) { + /* CAC checking period left to complete jiffies */ + long cac_left_jiffies; + + /* cac_left_jiffies will be negative if and only if + * event MLAN_EVENT_ID_DRV_MEAS_REPORT recieved from FW + * after CAC measure period ends, + * usually this could be considered as a FW bug + */ + cac_left_jiffies = + priv->phandle->cac_timer_jiffies - + (jiffies - priv->phandle->meas_start_jiffies); + if (priv->phandle->cac_period_jiffies) { + cac_left_jiffies = + priv->phandle->cac_period_jiffies - + (jiffies - priv->phandle->meas_start_jiffies); + } + if (priv->phandle->cac_restart) + cac_left_jiffies = DEF_CAC_DWELL_TIME * HZ / 1000; + if (cac_left_jiffies < 0) { + /* Avoid driver hang in FW died during CAC measure + * period */ + priv->phandle->cac_period = MFALSE; + PRINTM(MERROR, + "CAC measure period spends longer than scheduled time " + "or meas done event never received\n"); + status = MLAN_STATUS_FAILURE; +#ifdef UAP_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (priv->uap_host_based && + moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) + woal_cfg80211_dfs_vendor_event( + priv, event_dfs_cac_aborted, + &priv->chan); +#endif +#endif +#endif + + goto done; + } + + /* Check BSS START first */ + if (sub_command == MLAN_OID_BSS_START) { + mlan_ds_bss *bss; + bss = (mlan_ds_bss *)req->pbuf; + /* + * Bss delay start after channel report received, + * not block the driver by delay executing. This is + * because a BSS_START cmd is always executed right + * after channel check issued. + */ + if (priv->phandle->delay_bss_start == MFALSE) { + PRINTM(MMSG, + "Received BSS Start command during CAC period, delay executing %ld seconds\n", + cac_left_jiffies / HZ); + priv->phandle->delay_bss_start = MTRUE; + moal_memcpy_ext( + priv->phandle, + &priv->phandle->delay_ssid_bssid, + &bss->param.ssid_bssid, + sizeof(mlan_ssid_bssid), + sizeof(mlan_ssid_bssid)); + /* TODO: return success to allow the half below + * of routines of which calling BSS start to + * execute + */ + status = MLAN_STATUS_SUCCESS; + goto done; + } else { + /* TODO: not blocking it, just return failure */ + PRINTM(MMSG, + "Only one BSS Start command allowed for delay executing!\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + } + if (woal_cac_period_block_cmd(priv, req)) { + priv->phandle->meas_wait_q_woken = MFALSE; + PRINTM(MMSG, + "CAC check is on going... Blocking Command %ld seconds\n", + cac_left_jiffies / HZ); + /* blocking timeout set to 1.5 * CAC checking period + * left time */ + wait_event_interruptible_timeout( + priv->phandle->meas_wait_q, + priv->phandle->meas_wait_q_woken, + cac_left_jiffies * 3 / 2); + } + } +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + else if (priv->phandle->is_cac_timer_set && + (req->action == MLAN_ACT_SET || req->action == 0)) { + if (woal_cac_period_block_cmd(priv, req)) { + PRINTM(MMSG, + "CAC check is on going... Blocking Command\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + } +#endif +#endif + else if (priv->phandle->cac_period) { + PRINTM(MINFO, "Operation during CAC check period.\n"); + } + wait = (wait_queue *)req->reserved_1; + req->bss_index = priv->bss_index; + if (wait_option) + woal_fill_wait_queue(priv, wait, wait_option); + else + req->reserved_1 = 0; + + /* Call MLAN ioctl handle */ + atomic_inc(&priv->phandle->ioctl_pending); + spin_lock_irqsave(&priv->phandle->ioctl_lock, flags); + status = mlan_ioctl(priv->phandle->pmlan_adapter, req); + spin_unlock_irqrestore(&priv->phandle->ioctl_lock, flags); + switch (status) { + case MLAN_STATUS_PENDING: + if (wait_option == MOAL_NO_WAIT) + PRINTM(MIOCTL, "IOCTL MOAL_NO_WAIT: %p\n", req); + else + PRINTM(MIOCTL, + "IOCTL pending: %p id=0x%x, sub_id=0x%x wait_option=%d, action=%d\n", + req, req->req_id, (*(t_u32 *)req->pbuf), + wait_option, (int)req->action); + /* Status pending, wake up main process */ + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + + /* Wait for completion */ + if (wait_option) + status = woal_wait_ioctl_complete(priv, req, + wait_option); + break; + case MLAN_STATUS_SUCCESS: + case MLAN_STATUS_FAILURE: + case MLAN_STATUS_RESOURCE: + if (req) + PRINTM(MIOCTL, + "IOCTL: %p id=0x%x, sub_id=0x%x wait_option=%d, action=%d status=%d\n", + req, req->req_id, (*(t_u32 *)req->pbuf), + wait_option, (int)req->action, status); + atomic_dec(&priv->phandle->ioctl_pending); + break; + default: + atomic_dec(&priv->phandle->ioctl_pending); + break; + } + +done: + LEAVE(); + return status; +} + +/** + * @brief Send set MAC address request to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING + * -- success, otherwise fail + */ +mlan_status woal_request_set_mac_address(moal_private *priv, t_u8 wait_option) +{ + 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; + moal_memcpy_ext(priv->phandle, &bss->param.mac_addr, priv->current_addr, + sizeof(mlan_802_11_mac_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, wait_option); + if (status == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, + "set mac address failed! status=%d, error_code=0x%x\n", + status, req->status_code); + } else { + moal_memcpy_ext(priv->phandle, priv->netdev->dev_addr, + priv->current_addr, ETH_ALEN, ETH_ALEN); + HEXDUMP("priv->MacAddr:", priv->current_addr, ETH_ALEN); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Send multicast list request to MLAN + * + * @param priv A pointer to moal_private structure + * @param dev A pointer to net_device structure + * + * @return N/A + */ +void woal_request_set_multicast_list(moal_private *priv, struct net_device *dev) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + int mc_count = 0; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + PRINTM(MERROR, "%s:Fail to allocate ioctl req buffer\n", + __func__); + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MULTICAST_LIST; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + if (dev->flags & IFF_PROMISC) { + bss->param.multicast_list.mode = MLAN_PROMISC_MODE; + } else if (dev->flags & IFF_ALLMULTI) { + bss->param.multicast_list.mode = MLAN_ALL_MULTI_MODE; + } else { + bss->param.multicast_list.mode = MLAN_MULTICAST_MODE; + mc_count = woal_copy_all_mc_list(priv->phandle, + &bss->param.multicast_list); + if (mc_count > MLAN_MAX_MULTICAST_LIST_SIZE) + bss->param.multicast_list.mode = MLAN_ALL_MULTI_MODE; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_NO_WAIT); + if (status != MLAN_STATUS_PENDING) + kfree(req); +done: + LEAVE(); + return; +} + +/** + * @brief Send deauth command to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param mac MAC address to deauthenticate + * @param reason code reason code to deauthenticate + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_disconnect(moal_private *priv, t_u8 wait_option, t_u8 *mac, + t_u16 reason_code) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_STOP; + if (mac) + moal_memcpy_ext(priv->phandle, + &bss->param.deauth_param.mac_addr, mac, + sizeof(mlan_802_11_mac_addr), + sizeof(bss->param.deauth_param.mac_addr)); + bss->param.deauth_param.reason_code = reason_code; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); +#ifdef REASSOCIATION + priv->reassoc_required = MFALSE; +#endif /* REASSOCIATION */ + LEAVE(); + return status; +} + +#if defined(UAP_SUPPORT) +/** + * @brief Get non-global oper class + * + * @param priv Pointer to moal_private structure + * @param bw bandwidth + * @param channel channel + * @param oper_class pointer to oper_class + + * @return non-global operclass + */ +int woal_priv_get_nonglobal_operclass_by_bw_channel(moal_private *priv, + t_u8 bandwidth, + t_u8 channel, + t_u8 *oper_class) +{ + int ret = 0; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_OPER_CLASS; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_GET; + misc->param.bw_chan_oper.bandwidth = bandwidth; + misc->param.bw_chan_oper.channel = channel; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + *oper_class = misc->param.bw_chan_oper.oper_class; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Send bss_start command to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ssid_bssid A point to mlan_ssid_bssid structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_bss_start(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + + ENTER(); + + /* Stop the O.S. TX queue When we are roaming */ + woal_stop_queue(priv->netdev); + if (priv->media_connected == MFALSE) { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + } + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_START; + if (ssid_bssid) + moal_memcpy_ext(priv->phandle, &bss->param.ssid_bssid, + ssid_bssid, sizeof(mlan_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); + if (ssid_bssid) + moal_memcpy_ext(priv->phandle, ssid_bssid, + &bss->param.ssid_bssid, sizeof(mlan_ssid_bssid), + sizeof(mlan_ssid_bssid)); +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + priv->assoc_status = req->status_code; +#endif +#endif +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get BSS info + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param bss_info A pointer to mlan_bss_info structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_get_bss_info(moal_private *priv, t_u8 wait_option, + mlan_bss_info *bss_info) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + PRINTM(MERROR, + "Fail to allocate the buffer for get bss_info\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_BSS_INFO; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (bss_info) + moal_memcpy_ext(priv->phandle, bss_info, + &info->param.bss_info, + sizeof(mlan_bss_info), + sizeof(mlan_bss_info)); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set/Get generic IE + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param ie Information element + * @param ie_len Length of the IE + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_get_gen_ie(moal_private *priv, t_u32 action, t_u8 *ie, + int *ie_len, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + if ((action == MLAN_ACT_GET) && (ie == NULL || ie_len == NULL)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (action == MLAN_ACT_SET && *ie_len > MAX_IE_SIZE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GEN_IE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + misc->param.gen_ie.type = MLAN_IE_TYPE_GEN_IE; + + if (action == MLAN_ACT_SET) { + misc->param.gen_ie.len = *ie_len; + if (*ie_len) + moal_memcpy_ext(priv->phandle, + misc->param.gen_ie.ie_data, ie, *ie_len, + MAX_IE_SIZE); + } + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (action == MLAN_ACT_GET) { + *ie_len = misc->param.gen_ie.len; + if (*ie_len) + moal_memcpy_ext(priv->phandle, ie, + misc->param.gen_ie.ie_data, *ie_len, + *ie_len); + } + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +/** + * @brief Set/Get retry count + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param wait_option Wait option + * @param value Retry value + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_set_get_retry(moal_private *priv, t_u32 action, + t_u8 wait_option, int *value) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *mib = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + mib = (mlan_ds_snmp_mib *)req->pbuf; + mib->sub_command = MLAN_OID_SNMP_MIB_RETRY_COUNT; + req->req_id = MLAN_IOCTL_SNMP_MIB; + req->action = action; + + if (action == MLAN_ACT_SET) { + if (*value < MLAN_TX_RETRY_MIN || *value > MLAN_TX_RETRY_MAX) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + mib->param.retry_count = *value; + } + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) + *value = mib->param.retry_count; +#ifdef STA_CFG80211 + /* If set is invoked from other than iw i.e iwconfig, + * wiphy retry count should be updated as well */ + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext) && + priv->wdev && priv->wdev->wiphy && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (action == MLAN_ACT_SET)) { + priv->wdev->wiphy->retry_long = (t_u8)*value; + priv->wdev->wiphy->retry_short = (t_u8)*value; + } +#endif + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Performs pre-warm-reset + * + * @param priv A pointer to moal_private structure + * + * @return 0 if successful else negative value + */ +int woal_pre_warmreset(moal_private *priv) +{ + moal_handle *handle = priv->phandle; + int ret = 0; + int intf_num; +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + t_u8 bss_role = MLAN_BSS_ROLE_STA; +#endif +#endif +#endif /* WIFI_DIRECT_SUPPORT */ + + ENTER(); +#ifdef USB +#ifdef CONFIG_USB_SUSPEND + if (IS_USB(handle->card_type) && handle->is_suspended && + woal_exit_usb_suspend(handle)) { + PRINTM(MERROR, "Failed to resume the suspended device\n"); + LEAVE(); + return -EFAULT; + } +#endif /* CONFIG_USB_SUSPEND */ +#endif + 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); +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + if ((handle->priv[intf_num]->bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) && + (GET_BSS_ROLE(handle->priv[intf_num]) == + MLAN_BSS_ROLE_UAP)) { + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(handle->priv[intf_num], + MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &bss_role)) { + ret = -EFAULT; + goto done; + } + } +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT */ + } + woal_shutdown_fw(priv, MOAL_IOCTL_WAIT); +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) +done: +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT */ + LEAVE(); + return ret; +} + +/** + * @brief warm reset + * + * @param priv A pointer to moal_private structure + * + * @return 0 success, otherwise failure + */ +int woal_warmreset(moal_private *priv) +{ + moal_handle *handle = priv->phandle; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + int intf_num; + + /* Restart the firmware */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req) { + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_WARM_RESET; + misc->param.fw_reload = MTRUE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "warm reset failure!\n"); + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(req); + goto done; + } + kfree(req); + } +#ifdef USB + if (IS_USB(handle->card_type) && handle->params.usb_aggr == 1) { + /* Enable USB aggregation in FW */ + if (woal_usb_aggr_init(handle)) { + PRINTM(MERROR, "usb aggr init fail\n"); + ret = -EFAULT; + goto done; + } + } +#endif + + if (moal_extflg_isset(handle, EXT_AGGR_CTRL)) { + /* Enable aggregation in FW */ + if (woal_init_aggr_ctrl(handle, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Fail to init aggr ctrl\n"); + ret = -EFAULT; + goto done; + } + } + + /* Enable interfaces */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + netif_device_attach(handle->priv[intf_num]->netdev); + woal_start_queue(handle->priv[intf_num]->netdev); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 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(priv->phandle->params.cfg80211_wext) && + priv->wdev && priv->wdev->wiphy && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (action == MLAN_ACT_SET)) + priv->wdev->wiphy->rts_threshold = *value; +#endif + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Fragment threshold + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param wait_option Wait option + * @param value Fragment threshold value + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_set_get_frag(moal_private *priv, t_u32 action, + t_u8 wait_option, int *value) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *mib = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + mib = (mlan_ds_snmp_mib *)req->pbuf; + mib->sub_command = MLAN_OID_SNMP_MIB_FRAG_THRESHOLD; + req->req_id = MLAN_IOCTL_SNMP_MIB; + req->action = action; + + if (action == MLAN_ACT_SET) { + if (*value < MLAN_FRAG_MIN_VALUE || + *value > MLAN_FRAG_MAX_VALUE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + mib->param.frag_threshold = *value; + } + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) + *value = mib->param.frag_threshold; +#ifdef STA_CFG80211 + /* If set is invoked from other than iw i.e iwconfig, + * wiphy fragment threshold should be updated as well */ + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext) && + priv->wdev && priv->wdev->wiphy && + (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (action == MLAN_ACT_SET)) + priv->wdev->wiphy->frag_threshold = *value; +#endif + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TX power + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param power_cfg A pinter to mlan_power_cfg_t structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_set_get_tx_power(moal_private *priv, t_u32 action, + mlan_power_cfg_t *power_cfg) +{ + mlan_ds_power_cfg *pcfg = NULL; + mlan_ioctl_req *req = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pcfg = (mlan_ds_power_cfg *)req->pbuf; + pcfg->sub_command = MLAN_OID_POWER_CFG; + req->req_id = MLAN_IOCTL_POWER_CFG; + req->action = action; + if (action == MLAN_ACT_SET && power_cfg) + moal_memcpy_ext(priv->phandle, &pcfg->param.power_cfg, + power_cfg, sizeof(mlan_power_cfg_t), + sizeof(mlan_power_cfg_t)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (ret == MLAN_STATUS_SUCCESS && power_cfg) + moal_memcpy_ext(priv->phandle, power_cfg, + &pcfg->param.power_cfg, + sizeof(mlan_power_cfg_t), + sizeof(mlan_power_cfg_t)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get IEEE power management + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param disabled A pointer to disabled flag + * @param power_type IEEE power type + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_get_power_mgmt(moal_private *priv, t_u32 action, + int *disabled, int power_type, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = action; + + if (action == MLAN_ACT_SET) { + PRINTM(MINFO, "PS_MODE set power disabled=%d power type=%#x\n", + *disabled, power_type); + if (*disabled) + pm_cfg->param.ps_mode = 0; + else { + /* Check not support case only (vwrq->disabled == FALSE) + */ + if ((power_type & MW_POWER_TYPE) == MW_POWER_TIMEOUT) { + PRINTM(MERROR, + "Setting power timeout is not supported\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } else if ((power_type & MW_POWER_TYPE) == + MW_POWER_PERIOD) { + PRINTM(MERROR, + "Setting power period is not supported\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + pm_cfg->param.ps_mode = 1; + } + } + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) + *disabled = pm_cfg->param.ps_mode; + +#ifdef STA_CFG80211 + /* If set is invoked from other than iw i.e iwconfig, + * wiphy IEEE power save mode should be updated */ + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext) && + priv->wdev && (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (action == MLAN_ACT_SET)) { + if (*disabled) + priv->wdev->ps = MFALSE; + else + priv->wdev->ps = MTRUE; + } +#endif + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set Country 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 + */ +int woal_set_countrycode(moal_private *priv, char *country) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + mlan_ds_misc_country_code *country_code = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; + country_code = &pcfg_misc->param.country_code; + pcfg_misc->sub_command = MLAN_OID_MISC_COUNTRY_CODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + memset(country_code->country_code, 0, COUNTRY_CODE_LEN); + moal_memcpy_ext(priv->phandle, country_code->country_code, country, + COUNTRY_CODE_LEN - 1, COUNTRY_CODE_LEN - 1); + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set region code + * + * @param priv A pointer to moal_private structure + * @param region A pointer to region string + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING --success, otherwise + * fail + */ +mlan_status woal_set_region_code(moal_private *priv, char *region) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + if (woal_is_country_code_supported(region)) + return woal_set_countrycode(priv, region); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_REGION; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + cfg->param.region_code = region_string_2_region_code(region); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get data rate + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param datarate A pointer to mlan_rate_cfg_t structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_get_data_rate(moal_private *priv, t_u8 action, + mlan_rate_cfg_t *datarate) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + rate = (mlan_ds_rate *)req->pbuf; + rate->param.rate_cfg.rate_type = MLAN_RATE_VALUE; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + req->action = action; + + if (datarate && (action == MLAN_ACT_SET)) + moal_memcpy_ext(priv->phandle, &rate->param.rate_cfg, datarate, + sizeof(mlan_rate_cfg_t), + sizeof(mlan_rate_cfg_t)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS && datarate && action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, datarate, &rate->param.rate_cfg, + sizeof(mlan_rate_cfg_t), + sizeof(mlan_rate_cfg_t)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get assoc_resp buffer + * + * @param priv A pointer to moal_private structure + * @param assoc_rsp A pointer to mlan_ds_misc_assoc_rsp structure + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_get_assoc_rsp(moal_private *priv, + mlan_ds_misc_assoc_rsp *assoc_rsp, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + PRINTM(MERROR, "Fail to allocate buffer for get assoc resp\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + req->req_id = MLAN_IOCTL_MISC_CFG; + misc = (pmlan_ds_misc_cfg)req->pbuf; + misc->sub_command = MLAN_OID_MISC_ASSOC_RSP; + req->action = MLAN_ACT_GET; + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && assoc_rsp) + moal_memcpy_ext(priv->phandle, assoc_rsp, + &misc->param.assoc_resp, + sizeof(mlan_ds_misc_assoc_rsp), + sizeof(mlan_ds_misc_assoc_rsp)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Send get FW info request to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param fw_info FW information + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_request_get_fw_info(moal_private *priv, t_u8 wait_option, + mlan_fw_info *fw_info) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info; + mlan_status status; + ENTER(); + memset(priv->current_addr, 0xff, ETH_ALEN); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_get_info)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + info->sub_command = MLAN_OID_GET_FW_INFO; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + priv->phandle->fw_release_number = info->param.fw_info.fw_ver; + priv->phandle->fw_ecsa_enable = info->param.fw_info.ecsa_enable; + priv->phandle->fw_getlog_enable = + info->param.fw_info.getlog_enable; + if (priv->current_addr[0] == 0xff) + moal_memcpy_ext(priv->phandle, priv->current_addr, + &info->param.fw_info.mac_addr, + sizeof(mlan_802_11_mac_addr), ETH_ALEN); + moal_memcpy_ext(priv->phandle, priv->netdev->dev_addr, + priv->current_addr, ETH_ALEN, ETH_ALEN); + if (fw_info) + moal_memcpy_ext(priv->phandle, fw_info, + &info->param.fw_info, + sizeof(mlan_fw_info), + sizeof(mlan_fw_info)); + DBG_HEXDUMP(MCMD_D, "mac", priv->current_addr, 6); + } else + PRINTM(MERROR, + "get fw info failed! status=%d, error_code=0x%x\n", + status, req->status_code); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get current channel of active interface + * + * @param priv A pointer to moal_private + * @param channel A pointer to chan_band_info structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_get_active_intf_channel(moal_private *priv, + chan_band_info *channel) +{ + moal_handle *handle = priv->phandle; + int i; + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) + return woal_get_sta_channel(handle->priv[i], + MOAL_IOCTL_WAIT, + channel); + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) { + if (handle->priv[i]->bss_started == MTRUE) + return woal_set_get_ap_channel(handle->priv[i], + MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + channel); + } +#endif + } + return MLAN_STATUS_FAILURE; +} + +#ifdef STA_SUPPORT +/** + * @brief Send get ext cap info request to MLAN + * + * @param priv A pointer to moal_private structure + * @param buf data buffer + * @param len data buffer length + * + * @return number of bytes of extended capability -- success, + * otherwise error + */ +int woal_request_extcap(moal_private *priv, t_u8 *buf, t_u8 len) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (buf == NULL || len <= 0 || len < sizeof(ExtCap_t)) { + ret = -EINVAL; + goto out; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto out; + } + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_EXT_CAP_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto out; + } + memset(buf, 0, len); + moal_memcpy_ext(priv->phandle, buf, &cfg->param.ext_cap, + sizeof(ExtCap_t), len); + ret = sizeof(ExtCap_t); +out: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get debug info + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param debug_info A pointer to mlan_debug_info structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_get_debug_info(moal_private *priv, t_u8 wait_option, + mlan_debug_info *debug_info) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_get_info *info = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(t_u32) + + sizeof(mlan_debug_info)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_DEBUG_INFO; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (debug_info) { + moal_memcpy_ext(priv->phandle, debug_info, + &info->param.debug_info, + sizeof(mlan_debug_info), + sizeof(mlan_debug_info)); + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** + * @brief host command ioctl function + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +int woal_host_command(moal_private *priv, struct iwreq *wrq) +{ + HostCmd_Header cmd_header; + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (wrq->u.data.pointer == NULL) { + PRINTM(MERROR, "hostcmd IOCTL corrupt data\n"); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + memset(&cmd_header, 0, sizeof(cmd_header)); + + /* get command header */ + if (copy_from_user(&cmd_header, wrq->u.data.pointer, + sizeof(HostCmd_Header))) { + PRINTM(MERROR, "copy from user failed: Host command header\n"); + ret = -EFAULT; + goto done; + } + misc->param.hostcmd.len = woal_le16_to_cpu(cmd_header.size); + + PRINTM(MINFO, "Host command len = %u\n", misc->param.hostcmd.len); + + if (!misc->param.hostcmd.len || + misc->param.hostcmd.len > MRVDRV_SIZE_OF_CMD_BUFFER) { + PRINTM(MERROR, "Invalid data buffer length\n"); + ret = -EINVAL; + goto done; + } + + /* get the whole command from user */ + if (copy_from_user(misc->param.hostcmd.cmd, wrq->u.data.pointer, + woal_le16_to_cpu(cmd_header.size))) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + misc->sub_command = MLAN_OID_MISC_HOST_CMD; + req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (copy_to_user(wrq->u.data.pointer, (t_u8 *)misc->param.hostcmd.cmd, + misc->param.hostcmd.len)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = misc->param.hostcmd.len; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +#if defined(WIFI_DIRECT_SUPPORT) || defined(UAP_SUPPORT) +/** + * @brief host command ioctl function + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +/********* format of ifr_data *************/ +/* buf_len + Hostcmd_body */ +/* buf_len: 4 bytes */ +/* the length of the buf which */ +/* can be used to return data */ +/* to application */ +/* Hostcmd_body */ +/*******************************************/ +int woal_hostcmd_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u32 buf_len = 0; + HostCmd_Header cmd_header; + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_hostcmd_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + if (copy_from_user(&buf_len, req->ifr_data, sizeof(buf_len))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + memset(&cmd_header, 0, sizeof(cmd_header)); + + /* get command header */ + if (copy_from_user(&cmd_header, req->ifr_data + sizeof(buf_len), + sizeof(HostCmd_Header))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MINFO, "Host command len = %d\n", + woal_le16_to_cpu(cmd_header.size)); + + if (woal_le16_to_cpu(cmd_header.size) > MRVDRV_SIZE_OF_CMD_BUFFER) { + ret = -EINVAL; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + + misc->param.hostcmd.len = woal_le16_to_cpu(cmd_header.size); + + /* get the whole command from user */ + if (copy_from_user(misc->param.hostcmd.cmd, + req->ifr_data + sizeof(buf_len), + misc->param.hostcmd.len)) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + misc->sub_command = MLAN_OID_MISC_HOST_CMD; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (misc->param.hostcmd.len > buf_len) { + PRINTM(MERROR, + "buf_len is too small, resp_len=%d, buf_len=%d\n", + (int)misc->param.hostcmd.len, (int)buf_len); + ret = -EFAULT; + goto done; + } + if (copy_to_user(req->ifr_data + sizeof(buf_len), + (t_u8 *)misc->param.hostcmd.cmd, + misc->param.hostcmd.len)) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief CUSTOM_IE ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +int woal_custom_ie_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *custom_ie = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + gfp_t flag; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_custom_ie_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + custom_ie = kzalloc(sizeof(mlan_ds_misc_custom_ie), flag); + if (!custom_ie) { + ret = -ENOMEM; + goto done; + } + + if (copy_from_user(custom_ie, req->ifr_data, + sizeof(mlan_ds_misc_custom_ie))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + if ((custom_ie->len == 0) || + (custom_ie->len == sizeof(custom_ie->ie_data_list[0].ie_index))) + ioctl_req->action = MLAN_ACT_GET; + else + ioctl_req->action = MLAN_ACT_SET; + + moal_memcpy_ext(priv->phandle, &misc->param.cust_ie, custom_ie, + sizeof(mlan_ds_misc_custom_ie), + sizeof(mlan_ds_misc_custom_ie)); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + if (copy_to_user(req->ifr_data, &misc->param.cust_ie, + sizeof(mlan_ds_misc_custom_ie))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } else if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) { + /* send a separate error code to indicate error from driver */ + ret = EFAULT; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + kfree(custom_ie); + LEAVE(); + return ret; +} + +/** + * @brief send raw data packet ioctl function + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +int woal_send_host_packet(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u32 packet_len = 0; + int ret = 0; + pmlan_buffer pmbuf = NULL; + mlan_status status; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is NULL\n"); + ret = -EFAULT; + goto done; + } + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_send_host_packet() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + if (copy_from_user(&packet_len, req->ifr_data, sizeof(packet_len))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } +#define PACKET_HEADER_LEN 8 +#define MV_ETH_FRAME_LEN 1514 + if (packet_len > MV_ETH_FRAME_LEN) { + PRINTM(MERROR, "Invalid packet length %d\n", packet_len); + ret = -EFAULT; + goto done; + } + pmbuf = woal_alloc_mlan_buffer( + priv->phandle, (int)(MLAN_MIN_DATA_HEADER_LEN + + (int)packet_len + PACKET_HEADER_LEN)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + + /* get whole packet and header */ + if (copy_from_user(pmbuf->pbuf + pmbuf->data_offset, + req->ifr_data + sizeof(packet_len), + PACKET_HEADER_LEN + packet_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + woal_free_mlan_buffer(priv->phandle, pmbuf); + goto done; + } + pmbuf->data_len = PACKET_HEADER_LEN + packet_len; + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } +done: + LEAVE(); + return ret; +} + +#if defined(UAP_WEXT) +/** + * @brief Set/Get CUSTOM_IE ioctl handler + * + * @param priv A pointer to moal_private structure + * @param mask Mask to set or clear from caller + * @param ie IE buffer to set for beacon + * @param ie_len Length of the IE + * + * @return 0 --success, otherwise fail + */ +int woal_set_get_custom_ie(moal_private *priv, t_u16 mask, t_u8 *ie, int ie_len) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *misc_ie = NULL; + int ret = 0; + custom_ie *pcust_bcn_ie = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + misc_ie = &misc->param.cust_ie; + + misc_ie->type = TLV_TYPE_MGMT_IE; + misc_ie->len = (sizeof(custom_ie) - MAX_IE_SIZE) + ie_len; + pcust_bcn_ie = misc_ie->ie_data_list; + pcust_bcn_ie->ie_index = 0xffff; + pcust_bcn_ie->mgmt_subtype_mask = mask; + pcust_bcn_ie->ie_length = ie_len; + moal_memcpy_ext(priv->phandle, pcust_bcn_ie->ie_buffer, ie, ie_len, + MAX_IE_SIZE); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} +#endif /* defined(HOST_TXRX_MGMT_FRAME) && defined(UAP_WEXT) */ + +/** + * @brief ioctl function get BSS type + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +int woal_get_bss_type(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + int bss_type; + + ENTER(); + + bss_type = (int)priv->bss_type; + if (copy_to_user(req->ifr_data, &bss_type, sizeof(int))) { + PRINTM(MINFO, "Copy to user failed!\n"); + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief Swithces BSS role of interface + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT) + * @param bss_role A pointer to bss role + * + * @return 0 --success, otherwise fail + */ +mlan_status woal_bss_role_cfg(moal_private *priv, t_u8 action, t_u8 wait_option, + t_u8 *bss_role) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + struct net_device *dev = priv->netdev; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_ROLE; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + if (action == MLAN_ACT_SET) { + if (priv->bss_role == *bss_role) { + PRINTM(MWARN, "BSS is in desired role already\n"); + goto done; + } else { + bss->param.bss_role = *bss_role; + } + } + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_GET) { + *bss_role = bss->param.bss_role; + } else { + /* Update moal_private */ + priv->bss_role = *bss_role; + if (priv->bss_type == MLAN_BSS_TYPE_UAP) + priv->bss_type = MLAN_BSS_TYPE_STA; + else if (priv->bss_type == MLAN_BSS_TYPE_STA) + priv->bss_type = MLAN_BSS_TYPE_UAP; + + if (*bss_role == MLAN_BSS_ROLE_UAP) { + /* Switch: STA -> uAP */ + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->do_ioctl = woal_uap_do_ioctl; + dev->set_multicast_list = woal_uap_set_multicast_list; +#else + dev->netdev_ops = &woal_uap_netdev_ops; +#endif +#ifdef UAP_WEXT + if (IS_UAP_WEXT(priv->phandle->params.cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = + woal_get_uap_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def + *)&woal_uap_handler_def; + } +#endif /* UAP_WEXT */ + } else if (*bss_role == MLAN_BSS_ROLE_STA) { + /* Switch: uAP -> STA */ + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->do_ioctl = woal_do_ioctl; + dev->set_multicast_list = woal_set_multicast_list; +#else + dev->netdev_ops = &woal_netdev_ops; +#endif +#ifdef STA_WEXT + if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = + woal_get_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def + *)&woal_handler_def; + } +#endif /* STA_WEXT */ + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** + * @brief Set/Get BSS role + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +int woal_set_get_bss_role(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int bss_role = 0; + t_u8 action = MLAN_ACT_GET; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(&bss_role, wrq->u.data.pointer, + sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((bss_role != MLAN_BSS_ROLE_STA && + bss_role != MLAN_BSS_ROLE_UAP) +#ifdef WIFI_DIRECT_SUPPORT + || (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) +#endif + ) { + PRINTM(MWARN, "Invalid BSS role\n"); + ret = -EINVAL; + goto done; + } + if (bss_role == GET_BSS_ROLE(priv)) { + PRINTM(MWARN, "Already BSS is in desired role\n"); + ret = -EINVAL; + goto done; + } + action = MLAN_ACT_SET; + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + + if (MLAN_STATUS_SUCCESS != woal_bss_role_cfg(priv, action, + MOAL_IOCTL_WAIT, + (t_u8 *)&bss_role)) { + ret = -EFAULT; + goto done; + } + + if (!wrq->u.data.length) { + if (copy_to_user(wrq->u.data.pointer, &bss_role, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } else { +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(priv->phandle->params.cfg80211_wext)) + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); +#endif + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + + /* Enable interfaces */ + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); + } + +done: + LEAVE(); + return ret; +} +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ + +/** + * @brief Set/Get DTIM period + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param wait_option Wait option + * @param value DTIM period + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_set_get_dtim_period(moal_private *priv, t_u32 action, + t_u8 wait_option, t_u8 *value) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *mib = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + mib = (mlan_ds_snmp_mib *)req->pbuf; + mib->sub_command = MLAN_OID_SNMP_MIB_DTIM_PERIOD; + req->req_id = MLAN_IOCTL_SNMP_MIB; + req->action = action; + + if (action == MLAN_ACT_SET) + mib->param.dtim_period = *value; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS && action == MLAN_ACT_GET) + *value = (t_u8)mib->param.dtim_period; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get Host Sleep parameters + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param wait_option Wait option (MOAL_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) + moal_memcpy_ext(priv->phandle, &pmcfg->param.hs_cfg, hscfg, + sizeof(mlan_ds_hs_cfg), 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) { + moal_memcpy_ext(priv->phandle, hscfg, + &pmcfg->param.hs_cfg, + sizeof(mlan_ds_hs_cfg), + sizeof(mlan_ds_hs_cfg)); + } + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get wakeup reason + * + * @param priv A pointer to moal_private structure + * @param wakeup_reason A pointer to mlan_ds_hs_wakeup_reason structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_get_wakeup_reason(moal_private *priv, + mlan_ds_hs_wakeup_reason *wakeup_reason) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pmcfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + pmcfg = (mlan_ds_pm_cfg *)req->pbuf; + pmcfg->sub_command = MLAN_OID_PM_HS_WAKEUP_REASON; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT_TIMEOUT); + if (ret == MLAN_STATUS_SUCCESS) { + wakeup_reason->hs_wakeup_reason = + pmcfg->param.wakeup_reason.hs_wakeup_reason; + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set or Get wowlan config + * + * @param priv A pointer to moal_private structure + * @param action Action: action to config wowlan + * @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT) + * @param mefcfg A pointer to mlan_ds_misc_mef_flt_cfg structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE + */ +mlan_status woal_set_get_wowlan_config(moal_private *priv, t_u16 action, + t_u8 wait_option, + mlan_ds_misc_mef_flt_cfg *mefcfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misccfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + /** 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 */ + misccfg = (mlan_ds_misc_cfg *)req->pbuf; + misccfg->sub_command = MLAN_OID_MISC_MEF_FLT_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + moal_memcpy_ext(priv->phandle, &misccfg->param.mef_flt_cfg, mefcfg, + sizeof(mlan_ds_misc_mef_flt_cfg), + sizeof(mlan_ds_misc_mef_flt_cfg)); + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS) { + if (mefcfg && action == MLAN_ACT_GET) { + moal_memcpy_ext(priv->phandle, mefcfg, + &misccfg->param.mef_flt_cfg, + sizeof(mlan_ds_misc_mef_flt_cfg), + sizeof(mlan_ds_misc_mef_flt_cfg)); + } + } + if (ret != MLAN_STATUS_SUCCESS && ret != MLAN_STATUS_PENDING) + PRINTM(MIOCTL, "Set Get wowlan IOCTL failed!\n"); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Cancel Host Sleep configuration + * + * @param priv A pointer to moal_private structure + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS, MLAN_STATUS_PENDING, + * or MLAN_STATUS_FAILURE + */ +mlan_status woal_cancel_hs(moal_private *priv, t_u8 wait_option) +{ + moal_handle *handle = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_hs_cfg hscfg; +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + int i; +#endif +#endif + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle = priv->phandle; + /* Cancel Host Sleep */ + + hscfg.conditions = HOST_SLEEP_CFG_CANCEL; + hscfg.is_invoke_hostcmd = MTRUE; + ret = woal_set_get_hs_params(priv, MLAN_ACT_SET, wait_option, &hscfg); + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + if (GTK_REKEY_OFFLOAD_SUSPEND == handle->params.gtk_rekey_offload) { + PRINTM(MIOCTL, + "Cancel Host Sleep... clear gtk rekey offload of FW\n"); + for (i = 0; i < handle->priv_num; i++) { + if (handle->priv[i] && + handle->priv[i]->gtk_data_ready) { + PRINTM(MCMND, "clear GTK in resume\n"); + woal_set_rekey_data(handle->priv[i], NULL, + MLAN_ACT_CLEAR, + wait_option); + } + } + } +#endif +#endif + + 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; + int i; +#ifdef SDIO_SUSPEND_RESUME + mlan_ds_ps_info pm_info; +#endif + pmlan_ds_misc_keep_alive keep_alive = NULL; + + ENTER(); + + if (priv == NULL) { + PRINTM(MERROR, "Invalid priv\n"); + goto done; + } + handle = priv->phandle; + if (handle->hs_activated == MTRUE) { + PRINTM(MIOCTL, "HS Already actived\n"); + hs_actived = MTRUE; + goto done; + } + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) { + if (moal_extflg_isset(handle, + EXT_DISCONNECT_ON_SUSPEND) && + handle->priv[i]->media_connected == MTRUE) { + PRINTM(MIOCTL, "disconnect on suspend\n"); + woal_disconnect(handle->priv[i], MOAL_NO_WAIT, + NULL, DEF_DEAUTH_REASON_CODE); + } + } + if (handle->priv[i]) { + PRINTM(MIOCTL, "woal_delba_all on priv[%d]\n", i); + woal_delba_all(handle->priv[i], MOAL_NO_WAIT); + } + } + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (priv->phandle->is_remain_timer_set) { + woal_cancel_timer(&priv->phandle->remain_timer); + woal_remain_timer_func(priv->phandle); + } + /* cancel pending remain on channel */ + if (priv->phandle->remain_on_channel) { + t_u8 channel_status; + moal_private *remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (remain_priv) { + woal_cfg80211_remain_on_channel_cfg(remain_priv, + MOAL_NO_WAIT, MTRUE, + &channel_status, + NULL, 0, 0); + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv->netdev, +#else + remain_priv->wdev, +#endif + priv->phandle->cookie, + &priv->phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv->phandle->channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + } + priv->phandle->remain_on_channel = MFALSE; + } +#endif +#endif + +#ifdef STA_SUPPORT + woal_reconfig_bgscan(priv->phandle); +#endif + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + if (GTK_REKEY_OFFLOAD_SUSPEND == handle->params.gtk_rekey_offload) { + PRINTM(MIOCTL, + "Host Sleep enabled... set gtk rekey offload to FW\n"); + for (i = 0; i < handle->priv_num; i++) { + if (handle->priv[i] && + handle->priv[i]->gtk_data_ready) { + PRINTM(MCMND, "set GTK before suspend\n"); + woal_set_rekey_data( + handle->priv[i], + &handle->priv[i]->gtk_rekey_data, + MLAN_ACT_SET, MOAL_NO_WAIT); + } + } + } +#endif +#endif + + for (i = 0; i < MAX_KEEP_ALIVE_ID; i++) { + keep_alive = &handle->keep_alive[i]; + if (keep_alive && keep_alive->cached && keep_alive->enable) { + keep_alive->cached = false; + woal_start_mkeep_alive( + woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + keep_alive->mkeep_alive_id, keep_alive->packet, + keep_alive->pkt_len, keep_alive->src_mac, + keep_alive->dst_mac, keep_alive->send_interval, + keep_alive->retry_interval, + keep_alive->retry_count); + keep_alive->pkt_len = 0; + memset(keep_alive->packet, 0, MKEEP_ALIVE_IP_PKT_MAX); + } + } + /* 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_timeout(handle->hs_activate_wait_q, + handle->hs_activate_wait_q_woken, + HS_ACTIVE_TIMEOUT); +#ifdef SDIO_MMC + if (IS_SD(handle->card_type)) { + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + } +#endif + +#ifdef SDIO_SUSPEND_RESUME + memset(&pm_info, 0, sizeof(mlan_ds_ps_info)); +#endif + 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 if (IS_SD(handle->card_type)) { + 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*/ +#ifdef SDIO_MMC + if (IS_SD(handle->card_type)) { + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + } +#endif + if (hs_actived != MTRUE) { + handle->hs_skip_count++; +#ifdef SDIO_SUSPEND_RESUME + if (IS_SD(handle->card_type)) { + 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; +} + +#ifdef CONFIG_PROC_FS +/** + * @brief This function send soft_reset command to firmware + * + * @param handle A pointer to moal_handle structure + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING on success, + * otherwise failure code + */ +mlan_status woal_request_soft_reset(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req) { + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_SOFT_RESET; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + ret = woal_request_ioctl(woal_get_priv(handle, + MLAN_BSS_ROLE_ANY), + req, MOAL_IOCTL_WAIT); + } + + handle->surprise_removed = MTRUE; + woal_sched_timeout(5); + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif /* CONFIG_PROC_FS */ + +/** + * @brief Set wapi enable + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param enable MTRUE or MFALSE + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_set_wapi_enable(moal_private *priv, t_u8 wait_option, + t_u32 enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_WAPI_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.wapi_enabled = enable; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get version + * + * @param handle A pointer to moal_handle structure + * @param version A pointer to version buffer + * @param max_len max length of version buffer + * + * @return N/A + */ +void woal_get_version(moal_handle *handle, char *version, int max_len) +{ + union { + t_u32 l; + t_u8 c[4]; + } ver; + char fw_ver[32]; + + ENTER(); + + ver.l = handle->fw_release_number; + snprintf(fw_ver, sizeof(fw_ver), "%u.%u.%u.p%u", ver.c[2], ver.c[1], + ver.c[0], ver.c[3]); + + snprintf(version, max_len, handle->driver_version, fw_ver); + + LEAVE(); +} + +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** + * @brief Get Driver Version + * + * @param priv A pointer to moal_private structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +int woal_get_driver_version(moal_private *priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + int len; + char buf[MLAN_MAX_VER_STR_LEN]; + ENTER(); + + woal_get_version(priv->phandle, buf, sizeof(buf) - 1); + + len = strlen(buf); + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, buf, len)) { + PRINTM(MERROR, "Copy to user failed\n"); + LEAVE(); + return -EFAULT; + } + wrq->u.data.length = len; + } + PRINTM(MINFO, "MOAL VERSION: %s\n", buf); + LEAVE(); + return 0; +} + +/** + * @brief Get extended driver version + * + * @param priv A pointer to moal_private structure + * @param ireq A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +int woal_get_driver_verext(moal_private *priv, struct ifreq *ireq) +{ + struct iwreq *wrq = (struct iwreq *)ireq; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_VER_EXT; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + if (!wrq->u.data.flags) { + info->param.ver_ext.version_str_sel = + *((int *)(wrq->u.name + SUBCMD_OFFSET)); + } else { + if (copy_from_user( + &info->param.ver_ext.version_str_sel, + wrq->u.data.pointer, + sizeof(info->param.ver_ext.version_str_sel))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } else { + if (((t_s32)(info->param.ver_ext.version_str_sel)) < + 0) { + PRINTM(MERROR, "Invalid arguments!\n"); + ret = -EINVAL; + goto done; + } + } + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, + info->param.ver_ext.version_str, + strlen(info->param.ver_ext.version_str))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = strlen(info->param.ver_ext.version_str); + } + + PRINTM(MINFO, "MOAL EXTENDED VERSION: %s\n", + info->param.ver_ext.version_str); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +#ifdef DEBUG_LEVEL1 +/** + * @brief Set driver debug bit masks to mlan in order to enhance performance + * + * @param priv A pointer to moal_private structure + * @param drvdbg Driver debug level + * + * @return 0 --success, otherwise fail + */ +int woal_set_drvdbg(moal_private *priv, t_u32 drvdbg) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_DRVDBG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + misc->param.drvdbg = drvdbg; + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Mgmt frame forward registration + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param pmgmt_subtype_mask A Pointer to mgmt frame subtype mask + * @param wait_option wait option (MOAL_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) + moal_memcpy_ext(priv->phandle, &misc->param.mgmt_subtype_mask, + pmgmt_subtype_mask, + sizeof(misc->param.mgmt_subtype_mask), + sizeof(misc->param.mgmt_subtype_mask)); + + ret = woal_request_ioctl(priv, req, wait_option); + + if (req->action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, pmgmt_subtype_mask, + &misc->param.mgmt_subtype_mask, + sizeof(misc->param.mgmt_subtype_mask), + sizeof(misc->param.mgmt_subtype_mask)); + + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit beamforming capabilities + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param tx_bf_cap A pointer to tx_buf_cap buffer + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_get_tx_bf_cap(moal_private *priv, t_u16 action, + t_u32 *tx_bf_cap) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *bf_cfg = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!tx_bf_cap) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP; + req->action = action; + if (action == MLAN_ACT_SET) + bf_cfg->param.tx_bf_cap = *tx_bf_cap; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) { + goto done; + } + + if (action == MLAN_ACT_GET) + *tx_bf_cap = bf_cfg->param.tx_bf_cap; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit beamforming configuration + * + * @param priv A pointer to moal_private structure + * @param action Action: set or get + * @param tx_bf_cfg A pointer to tx_bf_cfg structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_get_tx_bf_cfg(moal_private *priv, t_u16 action, + mlan_ds_11n_tx_bf_cfg *tx_bf_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *bf_cfg = NULL; + + ENTER(); + + /* Sanity test */ + if (tx_bf_cfg == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CFG; + + req->action = action; + moal_memcpy_ext(priv->phandle, &bf_cfg->param.tx_bf, tx_bf_cfg, + sizeof(mlan_ds_11n_tx_bf_cfg), + sizeof(mlan_ds_11n_tx_bf_cfg)); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, tx_bf_cfg, &bf_cfg->param.tx_bf, + sizeof(mlan_ds_11n_tx_bf_cfg), + sizeof(mlan_ds_11n_tx_bf_cfg)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Handle ioctl resp + * + * @param priv Pointer to moal_private structure + * @param req Pointer to mlan_ioctl_req structure + * + * @return N/A + */ +void woal_process_ioctl_resp(moal_private *priv, mlan_ioctl_req *req) +{ + int cfg80211_wext; + + ENTER(); + + if (priv == NULL) { + LEAVE(); + return; + } + cfg80211_wext = priv->phandle->params.cfg80211_wext; + switch (req->req_id) { + case MLAN_IOCTL_GET_INFO: +#ifdef STA_WEXT +#ifdef STA_SUPPORT + if (IS_STA_WEXT(cfg80211_wext) && + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + woal_ioctl_get_info_resp(priv, + (mlan_ds_get_info *)req->pbuf); +#endif +#endif +#ifdef UAP_WEXT +#ifdef UAP_SUPPORT + if (IS_UAP_WEXT(cfg80211_wext) && + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + woal_ioctl_get_uap_info_resp( + priv, (mlan_ds_get_info *)req->pbuf); +#endif +#endif + break; +#ifdef STA_WEXT +#ifdef STA_SUPPORT + case MLAN_IOCTL_BSS: + if (IS_STA_WEXT(cfg80211_wext) && + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + woal_ioctl_get_bss_resp(priv, (mlan_ds_bss *)req->pbuf); + break; +#endif +#endif + case MLAN_IOCTL_MISC_CFG: + woal_ioctl_get_misc_conf(priv, (mlan_ds_misc_cfg *)req->pbuf); + default: + break; + } + + LEAVE(); + return; +} + +/** + * @brief Get PM info + * + * @param priv A pointer to moal_private structure + * @param pm_info A pointer to mlan_ds_ps_info structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_get_pm_info(moal_private *priv, mlan_ds_ps_info *pm_info) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pmcfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + PRINTM(MERROR, "Fail to alloc mlan_ds_pm_cfg buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + pmcfg = (mlan_ds_pm_cfg *)req->pbuf; + pmcfg->sub_command = MLAN_OID_PM_INFO; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS) { + if (pm_info) { + moal_memcpy_ext(priv->phandle, pm_info, + &pmcfg->param.ps_info, + sizeof(mlan_ds_ps_info), + sizeof(mlan_ds_ps_info)); + } + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get Deep Sleep + * + * @param priv Pointer to the moal_private driver data struct + * @param data Pointer to return deep_sleep setting + * + * @return 0 --success, otherwise fail + */ +int woal_get_deep_sleep(moal_private *priv, t_u32 *data) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + pm = (mlan_ds_pm_cfg *)req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP; + req->req_id = MLAN_IOCTL_PM_CFG; + + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + *data = pm->param.auto_deep_sleep.auto_ds; + *(data + 1) = pm->param.auto_deep_sleep.idletime; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set Deep Sleep + * + * @param priv Pointer to the moal_private driver data struct + * @param wait_option wait option + * @param bdeep_sleep TRUE--enalbe deepsleep, FALSE--disable deepsleep + * @param idletime Idle time for optimized PS API + * + * @return 0 --success, otherwise fail + */ +int woal_set_deep_sleep(moal_private *priv, t_u8 wait_option, + BOOLEAN bdeep_sleep, t_u16 idletime) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pm = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + pm = (mlan_ds_pm_cfg *)req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP; + req->req_id = MLAN_IOCTL_PM_CFG; + + req->action = MLAN_ACT_SET; + if (bdeep_sleep == MTRUE) { + PRINTM(MIOCTL, "Deep Sleep: sleep\n"); + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_ON; + if (idletime) + pm->param.auto_deep_sleep.idletime = idletime; + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS && ret != MLAN_STATUS_PENDING) { + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MIOCTL, "%lu : Deep Sleep: wakeup\n", jiffies); + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_OFF; + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS && ret != MLAN_STATUS_PENDING) { + ret = -EFAULT; + goto done; + } + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Cancel CAC period block + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void woal_cancel_cac_block(moal_private *priv) +{ + ENTER(); + /* if during CAC period, wake up wait queue */ + if (priv->phandle->cac_period == MTRUE) { + priv->phandle->cac_period = MFALSE; + /* Make sure Chan Report is cancelled */ + woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT); + priv->phandle->meas_start_jiffies = 0; + if (priv->phandle->delay_bss_start == MTRUE) + priv->phandle->delay_bss_start = MFALSE; + if (priv->phandle->meas_wait_q_woken == MFALSE) { + priv->phandle->meas_wait_q_woken = MTRUE; + wake_up_interruptible(&priv->phandle->meas_wait_q); + } +#ifdef UAP_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (priv->uap_host_based && + moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) + woal_cfg80211_dfs_vendor_event( + priv, event_dfs_cac_aborted, &priv->chan); +#endif +#endif +#endif + } + LEAVE(); +} + +/** MEAS report timeout value in seconds */ + +/** + * @brief Issue MLAN_OID_11H_CHANNEL_CHECK ioctl + * + * @param priv Pointer to the moal_private driver data struct + * @param wait_option wait option + * + * @return 0 --success, otherwise fail + */ +int woal_11h_channel_check_ioctl(moal_private *priv, t_u8 wait_option) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + +#ifdef UAP_SUPPORT + if (priv->skip_cac) { + LEAVE(); + return ret; + } +#endif + + /* Skip sending request/report query when DFS_REPEATER_MODE is on. This + * would get rid of CAC timers before starting BSSes in + * DFS_REPEATER_MODE + */ + if (priv->phandle->dfs_repeater_mode) { + LEAVE(); + return ret; + } + + if (woal_is_any_interface_active(priv->phandle)) { + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + + ds_11hcfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK; + ds_11hcfg->param.chan_rpt_req.host_based = MFALSE; + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_SET; + /* Send Channel Check command and wait until the report is ready */ + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + goto done; + } + + /* set flag from here */ + priv->phandle->cac_period = MTRUE; + priv->phandle->meas_start_jiffies = jiffies; + priv->phandle->cac_timer_jiffies = + ds_11hcfg->param.chan_rpt_req.millisec_dwell_time * HZ / 1000; + +#ifdef UAP_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (priv->uap_host_based && + moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) + woal_cfg80211_dfs_vendor_event(priv, event_dfs_cac_started, + &priv->chan); +#endif +#endif +#endif +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Issue MLAN_OID_11H_CHAN_REPORT_REQUEST ioctl to cancel dozer + * + * @param priv Pointer to the moal_private driver data struct + * @param wait_option wait option + * + * @return 0 --success, otherwise fail + */ +int woal_11h_cancel_chan_report_ioctl(moal_private *priv, t_u8 wait_option) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + + ds_11hcfg->sub_command = MLAN_OID_11H_CHAN_REPORT_REQUEST; + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_SET; + ds_11hcfg->param.chan_rpt_req.millisec_dwell_time = 0; + /* Send Channel Check command and wait until the report is ready */ + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set remain channel + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param pchan A pointer to mlan_ds_remain_chan structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_remain_channel_ioctl(moal_private *priv, t_u8 wait_option, + mlan_ds_remain_chan *pchan) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_REMAIN_CHAN_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + req->action = MLAN_ACT_SET; + moal_memcpy_ext(priv->phandle, &radio_cfg->param.remain_chan, pchan, + sizeof(mlan_ds_remain_chan), + sizeof(mlan_ds_remain_chan)); + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS) { + moal_memcpy_ext(priv->phandle, pchan, + &radio_cfg->param.remain_chan, + sizeof(mlan_ds_remain_chan), + sizeof(mlan_ds_remain_chan)); + PRINTM(MCMND, + "Remain_chan_cfg: remove=%d, channel=%d, remain_period=%d\n", + pchan->remove, pchan->channel, pchan->remain_period); + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief set/get wifi direct mode + * + * @param priv A pointer to moal_private structure + * @param action set or get + * @param mode A pointer to wifi direct mode + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_wifi_direct_mode_cfg(moal_private *priv, t_u16 action, + t_u16 *mode) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_WIFI_DIRECT_MODE; + req->req_id = MLAN_IOCTL_BSS; + + req->action = action; + if (action == MLAN_ACT_SET) + bss->param.wfd_mode = *mode; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS) { + *mode = bss->param.wfd_mode; + PRINTM(MIOCTL, "ACT=%d, wifi_direct_mode=%d\n", action, *mode); + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set p2p config + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param p2p_config A pointer to mlan_ds_wifi_direct_config structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_p2p_config(moal_private *priv, t_u32 action, + mlan_ds_wifi_direct_config *p2p_config) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + + ENTER(); + if (!p2p_config) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_WIFI_DIRECT_CONFIG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + moal_memcpy_ext(priv->phandle, &misc_cfg->param.p2p_config, p2p_config, + sizeof(mlan_ds_wifi_direct_config), + sizeof(mlan_ds_wifi_direct_config)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS) { + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, p2p_config, + &misc_cfg->param.p2p_config, + sizeof(mlan_ds_wifi_direct_config), + sizeof(mlan_ds_wifi_direct_config)); + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif /* WIFI_DIRECT_SUPPORT */ + +#ifdef STA_SUPPORT +/** + * @brief Get STA Channel Info + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param channel A pointer to channel info + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_get_sta_channel(moal_private *priv, t_u8 wait_option, + chan_band_info *channel) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + PRINTM(MERROR, "woal_get_sta_channel req alloc fail\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_CHAN_INFO; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS && channel) + moal_memcpy_ext(priv->phandle, channel, + &(bss->param.sta_channel), + sizeof(chan_band_info), sizeof(chan_band_info)); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get RSSI info + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param signal A pointer tp mlan_ds_get_signal structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_get_signal_info(moal_private *priv, t_u8 wait_option, + mlan_ds_get_signal *signal) +{ + int ret = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_SIGNAL; + info->param.signal.selector = ALL_RSSI_INFO_MASK; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (signal) + moal_memcpy_ext(priv->phandle, signal, + &info->param.signal, + sizeof(mlan_ds_get_signal), + sizeof(mlan_ds_get_signal)); +#ifdef STA_WEXT + if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) { + if (info->param.signal.selector & BCN_RSSI_AVG_MASK) + priv->w_stats.qual.level = + info->param.signal.bcn_rssi_avg; + if (info->param.signal.selector & BCN_NF_AVG_MASK) + priv->w_stats.qual.noise = + info->param.signal.bcn_nf_avg; + } +#endif + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get scan table + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param scan_resp A pointer to mlan_scan_resp structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_get_scan_table(moal_private *priv, t_u8 wait_option, + mlan_scan_resp *scan_resp) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_scan *scan = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + if (!scan_resp) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_NORMAL; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_GET; + moal_memcpy_ext(priv->phandle, (void *)&scan->param.scan_resp, + (void *)scan_resp, sizeof(mlan_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) { + moal_memcpy_ext(priv->phandle, scan_resp, + &scan->param.scan_resp, + sizeof(mlan_scan_resp), + sizeof(mlan_scan_resp)); + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Request a scan + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param req_ssid A pointer to mlan_802_11_ssid structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_request_scan(moal_private *priv, t_u8 wait_option, + mlan_802_11_ssid *req_ssid) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_scan *scan = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) { + PRINTM(MERROR, "Acquire semaphore error, request_scan\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle->scan_pending_on_block = MTRUE; + handle->scan_priv = priv; + + /* Allocate an IOCTL request buffer */ + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (ioctl_req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + scan = (mlan_ds_scan *)ioctl_req->pbuf; + + if (req_ssid && req_ssid->ssid_len != 0) { + /* Specific SSID scan */ + ioctl_req->req_id = MLAN_IOCTL_SCAN; + ioctl_req->action = MLAN_ACT_SET; + + scan->sub_command = MLAN_OID_SCAN_SPECIFIC_SSID; + + moal_memcpy_ext(handle, scan->param.scan_req.scan_ssid.ssid, + req_ssid->ssid, req_ssid->ssid_len, + MLAN_MAX_SSID_LENGTH); + scan->param.scan_req.scan_ssid.ssid_len = + MIN(MLAN_MAX_SSID_LENGTH, req_ssid->ssid_len); + } else { + /* Normal scan */ + ioctl_req->req_id = MLAN_IOCTL_SCAN; + ioctl_req->action = MLAN_ACT_SET; + + scan->sub_command = MLAN_OID_SCAN_NORMAL; + } + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, ioctl_req, wait_option); + + if (status == MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + if (ret == MLAN_STATUS_FAILURE) { + handle->scan_pending_on_block = MFALSE; + handle->scan_priv = NULL; + MOAL_REL_SEMAPHORE(&handle->async_sem); + } + LEAVE(); + return ret; +} + +/** + * @brief Change Adhoc Channel + * + * @param priv A pointer to moal_private structure + * @param channel The channel to be set. + * @param wait_option wait_option + * + * @return MLAN_STATUS_SUCCESS--success, MLAN_STATUS_FAILURE--fail + */ +mlan_status woal_change_adhoc_chan(moal_private *priv, int channel, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_bss_info bss_info; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + /* Get BSS information */ + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, wait_option, &bss_info)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (bss_info.bss_mode == MLAN_BSS_MODE_INFRA) { + ret = MLAN_STATUS_SUCCESS; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Get current channel */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_IBSS_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (bss->param.bss_chan.channel == (unsigned int)channel) { + ret = MLAN_STATUS_SUCCESS; + goto done; + } + PRINTM(MINFO, "Updating Channel from %d to %d\n", + (int)bss->param.bss_chan.channel, channel); + + if (bss_info.media_connected != MTRUE) { + ret = MLAN_STATUS_SUCCESS; + goto done; + } + + /* Do disonnect*/ + bss->sub_command = MLAN_OID_BSS_STOP; + memset((t_u8 *)&bss->param.bssid, 0, ETH_ALEN); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, wait_option, &bss_info.ssid)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Start/Join Adhoc network */ + bss->sub_command = MLAN_OID_BSS_START; + memset(&bss->param.ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + moal_memcpy_ext(priv->phandle, &bss->param.ssid_bssid.ssid, + &bss_info.ssid, sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Find the best network to associate + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_find_best_network(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *mac = 0; + + ENTER(); + + if (!ssid_bssid) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + bss->sub_command = MLAN_OID_BSS_FIND_BSS; + + moal_memcpy_ext(priv->phandle, &bss->param.ssid_bssid, ssid_bssid, + sizeof(mlan_ssid_bssid), sizeof(mlan_ssid_bssid)); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_SUCCESS) { + moal_memcpy_ext(priv->phandle, ssid_bssid, + &bss->param.ssid_bssid, sizeof(mlan_ssid_bssid), + sizeof(mlan_ssid_bssid)); + mac = (t_u8 *)&ssid_bssid->bssid; + PRINTM(MINFO, "Find network: ssid=%s, " MACSTR ", idx=%d\n", + ssid_bssid->ssid.ssid, MAC2STR(mac), + (int)ssid_bssid->idx); + } + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Find the best network to associate + * + * @param priv A pointer to moal_private structure + * @param bssid A pointer to bssid + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_find_bssid(moal_private *priv, mlan_802_11_mac_addr bssid) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + /* 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_BSSID; + + moal_memcpy_ext(priv->phandle, &bss->param.bssid, bssid, + sizeof(mlan_802_11_mac_addr), + sizeof(mlan_802_11_mac_addr)); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Check if AP channel is valid for STA Region + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_11d_check_ap_channel(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *mac = 0; + + ENTER(); + + if (!ssid_bssid) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + bss->sub_command = MLAN_OID_BSS_11D_CHECK_CHANNEL; + + moal_memcpy_ext(priv->phandle, &bss->param.ssid_bssid, ssid_bssid, + sizeof(mlan_ssid_bssid), sizeof(mlan_ssid_bssid)); + + mac = (t_u8 *)&ssid_bssid->bssid; + PRINTM(MINFO, "ssid=%s, " MACSTR ", idx=%d\n", ssid_bssid->ssid.ssid, + MAC2STR(mac), (int)ssid_bssid->idx); + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get authentication mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param auth_mode A pointer to authentication mode + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_get_auth_mode(moal_private *priv, t_u8 wait_option, + t_u32 *auth_mode) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_AUTH_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS && auth_mode) + *auth_mode = sec->param.auth_mode; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get encrypt mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param encrypt_mode A pointer to encrypt mode + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_get_encrypt_mode(moal_private *priv, t_u8 wait_option, + t_u32 *encrypt_mode) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS && encrypt_mode) + *encrypt_mode = sec->param.encrypt_mode; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get WPA enable + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param enable A pointer to wpa enable status + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_get_wpa_enable(moal_private *priv, t_u8 wait_option, + t_u32 *enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_WPA_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS && enable) + *enable = sec->param.wpa_enabled; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set authentication mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param auth_mode Authentication mode + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_set_auth_mode(moal_private *priv, t_u8 wait_option, + t_u32 auth_mode) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_AUTH_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.auth_mode = auth_mode; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set encrypt mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param encrypt_mode Encryption mode + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_set_encrypt_mode(moal_private *priv, t_u8 wait_option, + t_u32 encrypt_mode) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.encrypt_mode = encrypt_mode; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set wpa enable + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param enable MTRUE or MFALSE + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_set_wpa_enable(moal_private *priv, t_u8 wait_option, + t_u32 enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_WPA_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.wpa_enabled = enable; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief enable wep key + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_enable_wep_key(moal_private *priv, t_u8 wait_option) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + sec->param.encrypt_key.key_disable = MFALSE; + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_DEFAULT; + sec->param.encrypt_key.is_current_wep_key = MTRUE; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Request user scan + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param scan_cfg A pointer to wlan_user_scan_config structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_request_userscan(moal_private *priv, t_u8 wait_option, + wlan_user_scan_cfg *scan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_scan *scan = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) { + PRINTM(MERROR, "Acquire semaphore error, request_scan\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle->scan_pending_on_block = MTRUE; + handle->scan_priv = priv; + + /* Allocate an IOCTL request buffer */ + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) + + sizeof(wlan_user_scan_cfg)); + if (ioctl_req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + scan = (mlan_ds_scan *)ioctl_req->pbuf; + scan->sub_command = MLAN_OID_SCAN_USER_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_SCAN; + ioctl_req->action = MLAN_ACT_SET; + moal_memcpy_ext(handle, scan->param.user_scan.scan_cfg_buf, scan_cfg, + sizeof(wlan_user_scan_cfg), sizeof(wlan_user_scan_cfg)); + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, ioctl_req, wait_option); + if (status == MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + else if (wait_option != MOAL_NO_WAIT) { + PRINTM(MMSG, "scan interrupted by Signal, Cancel it..."); + woal_cancel_scan(priv, MOAL_IOCTL_WAIT_TIMEOUT); + } + if (ret == MLAN_STATUS_FAILURE) { + handle->scan_pending_on_block = MFALSE; + handle->scan_priv = NULL; + MOAL_REL_SEMAPHORE(&handle->async_sem); + } + LEAVE(); + return ret; +} + +/** + * @brief woal_get_scan_config + * + * @param priv A pointer to moal_private structure + * @param scan_cfg A pointer to scan_cfg structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_get_scan_config(moal_private *priv, mlan_scan_cfg *scan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_GET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret == MLAN_STATUS_SUCCESS && scan_cfg) + moal_memcpy_ext(priv->phandle, scan_cfg, &scan->param.scan_cfg, + sizeof(mlan_scan_cfg), sizeof(mlan_scan_cfg)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set scan time + * + * @param priv A pointer to moal_private structure + * @param active_scan_time Active scan time + * @param passive_scan_time Passive scan time + * @param specific_scan_time Specific scan time + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_scan_time(moal_private *priv, t_u16 active_scan_time, + t_u16 passive_scan_time, + t_u16 specific_scan_time) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + mlan_scan_cfg scan_cfg; + + ENTER(); + + memset(&scan_cfg, 0, sizeof(scan_cfg)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + scan_cfg.scan_time.active_scan_time = active_scan_time; + scan_cfg.scan_time.specific_scan_time = specific_scan_time; + scan_cfg.scan_time.passive_scan_time = passive_scan_time; + PRINTM(MIOCTL, "Set specific=%d, active=%d, passive=%d\n", + (int)active_scan_time, (int)passive_scan_time, + (int)specific_scan_time); + moal_memcpy_ext(priv->phandle, &scan->param.scan_cfg, &scan_cfg, + sizeof(mlan_scan_cfg), sizeof(mlan_scan_cfg)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief request scan + * + * @param priv A pointer to moal_private structure + * @param scan_cfg A pointer to wlan_user_scan_cfg structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_do_scan(moal_private *priv, wlan_user_scan_cfg *scan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + + ENTER(); + if (handle->scan_pending_on_block == MTRUE) { + PRINTM(MINFO, "scan already in processing...\n"); + LEAVE(); + return ret; + } +#ifdef REASSOCIATION + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + priv->report_scan_result = MTRUE; + + if (!scan_cfg) + ret = woal_request_scan(priv, MOAL_NO_WAIT, NULL); + else + ret = woal_request_userscan(priv, MOAL_NO_WAIT, scan_cfg); + +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + LEAVE(); + return ret; +} + +/** + * @brief Cancel pending scan + * + * @param priv A pointer to moal_private structure + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_cancel_scan(moal_private *priv, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + moal_private *scan_priv = handle->scan_priv; +#ifdef STA_CFG80211 + unsigned long flags; +#endif + /* If scan is in process, cancel the scan command */ + if (!handle->scan_pending_on_block || !scan_priv) + return ret; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + ((mlan_ds_scan *)req->pbuf)->sub_command = MLAN_OID_SCAN_CANCEL; + ret = woal_request_ioctl(scan_priv, req, wait_option); + handle->scan_pending_on_block = MFALSE; + MOAL_REL_SEMAPHORE(&handle->async_sem); +#ifdef STA_CFG80211 + spin_lock_irqsave(&handle->scan_req_lock, flags); + if (IS_STA_CFG80211(handle->params.cfg80211_wext) && + handle->scan_request) { + /** some supplicant can not handle SCAN abort event */ + if (scan_priv->bss_type == MLAN_BSS_TYPE_STA) + woal_cfg80211_scan_done(handle->scan_request, MTRUE); + else + woal_cfg80211_scan_done(handle->scan_request, MFALSE); + handle->scan_request = NULL; + } + spin_unlock_irqrestore(&handle->scan_req_lock, flags); +#endif + /* add 10ms delay, incase firmware delay 0x7f event after scan cancel + * command response */ + woal_sched_timeout(10); + handle->scan_priv = NULL; +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + return ret; +} + +/** + * @brief find ssid in scan_table + * + * @param priv A pointer to moal_private + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE + */ +int woal_find_essid(moal_private *priv, mlan_ssid_bssid *ssid_bssid, + t_u8 wait_option) +{ + int ret = 0; + mlan_scan_resp scan_resp; + wifi_timeval t; + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_get_scan_table(priv, wait_option, &scan_resp)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#ifdef STA_CFG80211 + if (priv->ft_pre_connect || priv->ft_ie_len) { + /** skip check the scan age out */ + ret = woal_find_best_network(priv, wait_option, ssid_bssid); + LEAVE(); + return ret; + } +#endif + woal_get_monotonic_time(&t); +/** scan result timeout value */ +#define SCAN_RESULT_AGEOUT 10 + if (t.time_sec > (scan_resp.age_in_secs + SCAN_RESULT_AGEOUT)) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + ret = woal_find_best_network(priv, wait_option, ssid_bssid); + LEAVE(); + return ret; +} + +/** + * @brief 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; + moal_memcpy_ext(priv->phandle, scan->param.user_scan.scan_cfg_buf, + scan_cfg, sizeof(wlan_bgscan_cfg), + sizeof(wlan_bgscan_cfg)); + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, ioctl_req, wait_option); + if (status == MLAN_STATUS_FAILURE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief set bgscan config + * + * @param priv A pointer to moal_private structure + * @param buf A pointer to scan command buf + * @param length buf length + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_bg_scan(moal_private *priv, char *buf, int length) +{ + t_u8 *ptr = buf + strlen("BGSCAN-CONFIG") + 1; + int buf_left = length - (strlen("BGSCAN-CONFIG") + 1); + int band = 0; + int num_ssid = 0; + int ssid_len = 0; + mlan_status ret = MLAN_STATUS_FAILURE; + + ENTER(); + memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg)); + priv->scan_cfg.report_condition = + BG_SCAN_SSID_MATCH | BG_SCAN_WAIT_ALL_CHAN_DONE; + while (buf_left >= 2) { + switch (*ptr) { + case WEXT_CSCAN_SSID_SECTION: + ssid_len = *(ptr + 1); + if ((buf_left < (ssid_len + 2)) || + (ssid_len > MLAN_MAX_SSID_LENGTH)) { + PRINTM(MERROR, + "Invalid ssid, buf_left=%d, ssid_len=%d\n", + buf_left, ssid_len); + buf_left = 0; + break; + } + if (ssid_len && + (num_ssid < (MRVDRV_MAX_SSID_LIST_LENGTH - 1))) { + strncpy(priv->scan_cfg.ssid_list[num_ssid].ssid, + ptr + 2, ssid_len); + priv->scan_cfg.ssid_list[num_ssid].max_len = 0; + PRINTM(MIOCTL, "BG scan: ssid=%s\n", + priv->scan_cfg.ssid_list[num_ssid].ssid); + num_ssid++; + } + buf_left -= ssid_len + 2; + ptr += ssid_len + 2; + break; + case WEXT_BGSCAN_RSSI_SECTION: + priv->scan_cfg.report_condition = + BG_SCAN_SSID_RSSI_MATCH | + BG_SCAN_WAIT_ALL_CHAN_DONE; + priv->scan_cfg.rssi_threshold = ptr[1]; + PRINTM(MIOCTL, "BG scan: rssi_threshold=%d\n", + (int)priv->scan_cfg.rssi_threshold); + ptr += 2; + buf_left -= 2; + break; + case WEXT_BGSCAN_REPEAT_SECTION: + priv->scan_cfg.repeat_count = (t_u16)ptr[1]; + PRINTM(MIOCTL, "BG scan: repeat_count=%d\n", + (int)priv->scan_cfg.repeat_count); + ptr += 2; + buf_left -= 2; + break; + case WEXT_BGSCAN_INTERVAL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid scan_interval, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + priv->scan_cfg.scan_interval = + (ptr[2] << 8 | ptr[1]) * 1000; + PRINTM(MIOCTL, "BG scan: scan_interval=%d\n", + (int)priv->scan_cfg.scan_interval); + ptr += 3; + buf_left -= 3; + break; + default: + buf_left = 0; + break; + } + } + /** set bgscan when ssid_num > 0 */ + if (num_ssid) { + if (MLAN_STATUS_SUCCESS != woal_get_band(priv, &band)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + switch (band) { + case WIFI_FREQUENCY_BAND_2GHZ: + priv->scan_cfg.chan_list[0].radio_type = + 0 | BAND_SPECIFIED; + break; + case WIFI_FREQUENCY_BAND_5GHZ: + priv->scan_cfg.chan_list[0].radio_type = + 1 | BAND_SPECIFIED; + break; + default: + break; + } + priv->scan_cfg.bss_type = MLAN_BSS_MODE_INFRA; + priv->scan_cfg.action = BG_SCAN_ACT_SET; + priv->scan_cfg.enable = MTRUE; + moal_memcpy_ext(priv->phandle, priv->scan_cfg.random_mac, + priv->random_mac, ETH_ALEN, + sizeof(priv->scan_cfg.random_mac)); + ret = woal_request_bgscan(priv, MOAL_IOCTL_WAIT, + &priv->scan_cfg); + } +done: + LEAVE(); + return ret; +} + +#ifdef STA_CFG80211 +/** + * @brief set bgscan and new rssi_low_threshold + * + * @param priv A pointer to moal_private structure + * @param set_rssi flag for set rssi_low_threshold + * + * @return N/A + */ +void woal_config_bgscan_and_rssi(moal_private *priv, t_u8 set_rssi) +{ + char rssi_low[11]; + mlan_bss_info bss_info; + int band = 0; + + ENTER(); + if (!priv->rssi_low) { + LEAVE(); + return; + } + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (!bss_info.media_connected) { + PRINTM(MIOCTL, "We already lost connection\n"); + LEAVE(); + return; + } + memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg)); + strncpy(priv->scan_cfg.ssid_list[0].ssid, bss_info.ssid.ssid, + bss_info.ssid.ssid_len); + priv->scan_cfg.ssid_list[0].max_len = 0; + + priv->scan_cfg.report_condition = + BG_SCAN_SSID_RSSI_MATCH | BG_SCAN_WAIT_ALL_CHAN_DONE; + priv->scan_cfg.rssi_threshold = priv->rssi_low - RSSI_HYSTERESIS; + priv->scan_cfg.repeat_count = DEF_REPEAT_COUNT; + priv->scan_cfg.scan_interval = MIN_BGSCAN_INTERVAL; + woal_get_band(priv, &band); + switch (band) { + case WIFI_FREQUENCY_BAND_2GHZ: + priv->scan_cfg.chan_list[0].radio_type = 0 | BAND_SPECIFIED; + break; + case WIFI_FREQUENCY_BAND_5GHZ: + priv->scan_cfg.chan_list[0].radio_type = 1 | BAND_SPECIFIED; + break; + default: + break; + } + priv->scan_cfg.bss_type = MLAN_BSS_MODE_INFRA; + priv->scan_cfg.action = BG_SCAN_ACT_SET; + priv->scan_cfg.enable = MTRUE; + moal_memcpy_ext(priv->phandle, priv->scan_cfg.random_mac, + priv->random_mac, ETH_ALEN, + sizeof(priv->scan_cfg.random_mac)); + woal_request_bgscan(priv, MOAL_NO_WAIT, &priv->scan_cfg); + if (set_rssi && + ((priv->rssi_low + RSSI_HYSTERESIS) <= LOWEST_RSSI_THRESHOLD)) { + priv->rssi_low += RSSI_HYSTERESIS; + snprintf(rssi_low, sizeof(rssi_low), "%d", priv->rssi_low); + woal_set_rssi_low_threshold(priv, rssi_low, MOAL_NO_WAIT); + } + LEAVE(); +} +#endif + +/** + * @brief stop bg scan + * + * @param priv A pointer to moal_private structure + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_stop_bg_scan(moal_private *priv, t_u8 wait_option) +{ + wlan_bgscan_cfg scan_cfg; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + + memset(&scan_cfg, 0, sizeof(scan_cfg)); + scan_cfg.action = BG_SCAN_ACT_SET; + scan_cfg.enable = MFALSE; + ret = woal_request_bgscan(priv, wait_option, &scan_cfg); + + LEAVE(); + return ret; +} + +/** + * @brief set bgscan config + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +void woal_reconfig_bgscan(moal_handle *handle) +{ + int i; + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) { + if (handle->priv[i]->bg_scan_start && + handle->priv[i]->bg_scan_reported) { + PRINTM(MIOCTL, "Reconfig BGSCAN\n"); + woal_request_bgscan(handle->priv[i], + MOAL_NO_WAIT, + &handle->priv[i]->scan_cfg); + handle->priv[i]->bg_scan_reported = MFALSE; + } + } + } +} + +/** + * @brief set rssi low threshold + * + * @param priv A pointer to moal_private structure + * @param rssi A pointer to low rssi + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_rssi_low_threshold(moal_private *priv, char *rssi, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int low_rssi = 0; + + ENTER(); + if (priv->media_connected == MFALSE) + goto done; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_SUBSCRIBE_EVENT; + req->action = MLAN_ACT_SET; + misc->param.subscribe_event.evt_action = SUBSCRIBE_EVT_ACT_BITWISE_SET; + misc->param.subscribe_event.evt_bitmap = SUBSCRIBE_EVT_RSSI_LOW; + misc->param.subscribe_event.evt_bitmap |= SUBSCRIBE_EVT_PRE_BEACON_LOST; + misc->param.subscribe_event.pre_beacon_miss = DEFAULT_PRE_BEACON_MISS; + + if (MLAN_STATUS_SUCCESS != woal_atoi(&low_rssi, rssi)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } +#ifdef STA_CFG80211 + priv->mrvl_rssi_low = low_rssi; +#endif + misc->param.subscribe_event.low_rssi = low_rssi; + misc->param.subscribe_event.low_rssi_freq = 0; + ret = woal_request_ioctl(priv, req, wait_option); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, "request set rssi_low_threshold fail!\n"); + goto done; + } +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(STA_CFG80211) +/** + * @brief set rssi low threshold + * + * @param priv A pointer to moal_private structure + * @param event_id event id. + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_rssi_threshold(moal_private *priv, t_u32 event_id, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + + ENTER(); + if (priv->media_connected == MFALSE) + goto done; + if (priv->mrvl_rssi_low || !priv->cqm_rssi_thold) + goto done; + if (event_id == MLAN_EVENT_ID_FW_BCN_RSSI_LOW) { + if (priv->last_rssi_low < 100) + priv->last_rssi_low += priv->cqm_rssi_hyst; + priv->last_rssi_high = abs(priv->cqm_rssi_high_thold); + } else if (event_id == MLAN_EVENT_ID_FW_BCN_RSSI_HIGH) { + priv->last_rssi_low = abs(priv->cqm_rssi_thold); + if (priv->last_rssi_high > priv->cqm_rssi_hyst) + priv->last_rssi_high -= priv->cqm_rssi_hyst; + } else { + priv->last_rssi_low = abs(priv->cqm_rssi_thold); + priv->last_rssi_high = abs(priv->cqm_rssi_high_thold); + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_SUBSCRIBE_EVENT; + req->action = MLAN_ACT_SET; + if (!event_id && !priv->cqm_rssi_thold && !priv->cqm_rssi_hyst) + misc->param.subscribe_event.evt_action = + SUBSCRIBE_EVT_ACT_BITWISE_CLR; + else + misc->param.subscribe_event.evt_action = + SUBSCRIBE_EVT_ACT_BITWISE_SET; + misc->param.subscribe_event.evt_bitmap = + SUBSCRIBE_EVT_RSSI_LOW | SUBSCRIBE_EVT_RSSI_HIGH; + misc->param.subscribe_event.low_rssi_freq = 0; + misc->param.subscribe_event.low_rssi = priv->last_rssi_low; + misc->param.subscribe_event.high_rssi_freq = 0; + misc->param.subscribe_event.high_rssi = priv->last_rssi_high; + PRINTM(MIOCTL, "rssi_low=%d, rssi_high=%d action=%d\n", + (int)priv->last_rssi_low, (int)priv->last_rssi_high, + misc->param.subscribe_event.evt_action); + ret = woal_request_ioctl(priv, req, wait_option); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Get power mode + * + * @param priv A pointer to moal_private structure + * @param powermode A pointer to powermode buf + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_get_powermode(moal_private *priv, int *powermode) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int ps_mode; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, MLAN_ACT_GET, + &ps_mode, 0, + MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (ps_mode) + *powermode = MFALSE; + else + *powermode = MTRUE; + +done: + LEAVE(); + return ret; +} + +/** + * @brief set scan type + * + * @param priv A pointer to moal_private structure + * @param scan_type MLAN_SCAN_TYPE_ACTIVE/MLAN_SCAN_TYPE_PASSIVE + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_scan_type(moal_private *priv, t_u32 scan_type) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + mlan_scan_cfg scan_cfg; + + ENTER(); + memset(&scan_cfg, 0, sizeof(scan_cfg)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + scan_cfg.scan_type = scan_type; + PRINTM(MIOCTL, "Set scan_type=%d\n", (int)scan_type); + moal_memcpy_ext(priv->phandle, &scan->param.scan_cfg, &scan_cfg, + sizeof(mlan_scan_cfg), sizeof(mlan_scan_cfg)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief enable/disable ext_scan + * + * @param priv A pointer to moal_private structure + * @param enable MTRUE -- enable, MFALSE --disable + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_enable_ext_scan(moal_private *priv, t_u8 enable) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + mlan_scan_cfg scan_cfg; + + ENTER(); + memset(&scan_cfg, 0, sizeof(scan_cfg)); + if (MLAN_STATUS_SUCCESS != woal_get_scan_config(priv, &scan_cfg)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + memset(&scan->param.scan_cfg, 0, sizeof(mlan_scan_cfg)); + scan_cfg.ext_scan = enable; + PRINTM(MIOCTL, "Set ext_scan=%d\n", (int)enable); + moal_memcpy_ext(priv->phandle, &scan->param.scan_cfg, &scan_cfg, + sizeof(mlan_scan_cfg), sizeof(mlan_scan_cfg)); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set power mode + * + * @param priv A pointer to moal_private structure + * @param powermode A pointer to powermode string. + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_powermode(moal_private *priv, char *powermode) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int disabled; + + ENTER(); + + if (*powermode == '1') { + PRINTM(MIOCTL, "Disable power save\n"); + disabled = 1; + } else if (*powermode == '0') { + PRINTM(MIOCTL, "Enable power save\n"); + disabled = 0; + } else { + PRINTM(MERROR, "unsupported power mode\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, MLAN_ACT_SET, + &disabled, 0, + MOAL_IOCTL_WAIT)) + ret = MLAN_STATUS_FAILURE; + +done: + LEAVE(); + return ret; +} + +/** + * @brief set combo scan + * + * @param priv A pointer to moal_private structure + * @param buf A pointer to scan command buf + * @param length buf length + * + * @return 0 -- success, otherwise fail + */ +int woal_set_combo_scan(moal_private *priv, char *buf, int length) +{ + int ret = 0; + wlan_user_scan_cfg scan_cfg; + t_u8 *ptr = buf + WEXT_CSCAN_HEADER_SIZE; + int buf_left = length - WEXT_CSCAN_HEADER_SIZE; + int num_ssid = 0; + int num_chan = 0; + int ssid_len = 0; + int i = 0; + t_u16 passive_scan_time = 0; + t_u16 specific_scan_time = 0; + + ENTER(); + memset(&scan_cfg, 0, sizeof(scan_cfg)); + while (buf_left >= 2) { + switch (*ptr) { + case WEXT_CSCAN_SSID_SECTION: + ssid_len = *(ptr + 1); + if ((buf_left < (ssid_len + 2)) || + (ssid_len > MLAN_MAX_SSID_LENGTH)) { + PRINTM(MERROR, + "Invalid ssid, buf_left=%d, ssid_len=%d\n", + buf_left, ssid_len); + buf_left = 0; + break; + } + if (ssid_len && + (num_ssid < (MRVDRV_MAX_SSID_LIST_LENGTH - 1))) { + strncpy(scan_cfg.ssid_list[num_ssid].ssid, + ptr + 2, ssid_len); + scan_cfg.ssid_list[num_ssid].max_len = 0; + PRINTM(MIOCTL, "Combo scan: ssid=%s\n", + scan_cfg.ssid_list[num_ssid].ssid); + num_ssid++; + } + buf_left -= ssid_len + 2; + ptr += ssid_len + 2; + break; + case WEXT_CSCAN_CHANNEL_SECTION: + num_chan = ptr[1]; + if ((buf_left < (num_chan + 2)) || + (num_chan > WLAN_USER_SCAN_CHAN_MAX)) { + PRINTM(MERROR, + "Invalid channel list, buf_left=%d, num_chan=%d\n", + buf_left, num_chan); + buf_left = 0; + break; + } + for (i = 0; i < num_chan; i++) { + scan_cfg.chan_list[i].chan_number = ptr[2 + i]; + PRINTM(MIOCTL, "Combo scan: chan=%d\n", + scan_cfg.chan_list[i].chan_number); + } + buf_left -= 2 + num_chan; + ptr += 2 + num_chan; + break; + case WEXT_CSCAN_PASV_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid PASV_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + passive_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + case WEXT_CSCAN_HOME_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid HOME_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + specific_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + default: + buf_left = 0; + break; + } + } + if (passive_scan_time || specific_scan_time) { + PRINTM(MIOCTL, + "Set passive_scan_time=%d specific_scan_time=%d\n", + passive_scan_time, specific_scan_time); + if (MLAN_STATUS_FAILURE == + woal_set_scan_time(priv, 0, passive_scan_time, + specific_scan_time)) { + ret = -EFAULT; + goto done; + } + } + if (num_ssid || num_chan) { + if (num_ssid) { + /* Add broadcast scan to ssid_list */ + scan_cfg.ssid_list[num_ssid].max_len = 0xff; + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + } + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_cfg)) + ret = -EFAULT; + if (num_ssid && (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE)) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); + } else { + /* request broadcast scan */ + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, NULL)) + ret = -EFAULT; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Get band + * + * @param priv A pointer to moal_private structure + * @param band A pointer to band buf + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_get_band(moal_private *priv, int *band) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + int support_band = 0; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + /* Get config_bands, adhoc_start_band and adhoc_channel values from MLAN + */ + req->action = MLAN_ACT_GET; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (radio_cfg->param.band_cfg.config_bands & + (BAND_B | BAND_G | BAND_GN)) + support_band |= WIFI_FREQUENCY_BAND_2GHZ; + if (radio_cfg->param.band_cfg.config_bands & (BAND_A | BAND_AN)) + support_band |= WIFI_FREQUENCY_BAND_5GHZ; + *band = support_band; + if (support_band == WIFI_FREQUENCY_ALL_BAND) + *band = WIFI_FREQUENCY_BAND_AUTO; +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set band + * + * @param priv A pointer to moal_private structure + * @param pband A pointer to band string. + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_band(moal_private *priv, char *pband) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int band = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + int cfg80211_wext = priv->phandle->params.cfg80211_wext; +#endif + + ENTER(); + if (priv->media_connected == MTRUE) { + PRINTM(MERROR, "Set band is not allowed in connected state\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + /* Get fw supported values from MLAN */ + req->action = MLAN_ACT_GET; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (*pband == '0') { + PRINTM(MIOCTL, "Set band to AUTO\n"); + band = radio_cfg->param.band_cfg.fw_bands; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev && + priv->wdev->wiphy) { + if (radio_cfg->param.band_cfg.fw_bands & BAND_A) + priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = + &cfg80211_band_5ghz; + priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = + &cfg80211_band_2ghz; + } +#endif + } else if (*pband == '1') { + PRINTM(MIOCTL, "Set band to 5G\n"); + if (!(radio_cfg->param.band_cfg.fw_bands & BAND_A)) { + PRINTM(MERROR, "Don't support 5G band\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + band = BAND_A; + if (radio_cfg->param.band_cfg.fw_bands & BAND_AN) + band |= BAND_AN; + if (radio_cfg->param.band_cfg.fw_bands & BAND_AAC) + band |= BAND_AAC; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev && + priv->wdev->wiphy) { + priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = + &cfg80211_band_5ghz; + priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; + } +#endif + } else if (*pband == '2') { + PRINTM(MIOCTL, "Set band to 2G\n"); + band = BAND_B | BAND_G; + band |= BAND_GN; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext) && priv->wdev && + priv->wdev->wiphy) { + priv->wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + priv->wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = + &cfg80211_band_2ghz; + } +#endif + } else { + PRINTM(MERROR, "unsupported band\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* Set config_bands to MLAN */ + req->action = MLAN_ACT_SET; + memset(&radio_cfg->param.band_cfg, 0, sizeof(mlan_ds_band_cfg)); + radio_cfg->param.band_cfg.config_bands = band; + radio_cfg->param.band_cfg.adhoc_start_band = band; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Add RX Filter + * + * @param priv A pointer to moal_private structure + * @param rxfilter A pointer to rxfilter string. + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_add_rxfilter(moal_private *priv, char *rxfilter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + /* Android command: + "DRIVER RXFILTER-ADD 0" + "DRIVER RXFILTER-ADD 1" + "DRIVER RXFILTER-ADD 3" */ + if (*rxfilter == '0') { + PRINTM(MIOCTL, "Add IPV4 multicast filter\n"); + priv->rx_filter |= RX_FILTER_IPV4_MULTICAST; + } else if (*rxfilter == '1') { + PRINTM(MIOCTL, "Add broadcast filter\n"); + priv->rx_filter |= RX_FILTER_BROADCAST; + } else if (*rxfilter == '2') { + PRINTM(MIOCTL, "Add unicast filter\n"); + priv->rx_filter |= RX_FILTER_UNICAST; + } else if (*rxfilter == '3') { + PRINTM(MIOCTL, "Add IPV6 multicast fitler\n"); + priv->rx_filter |= RX_FILTER_IPV6_MULTICAST; + } else { + PRINTM(MERROR, "unsupported rx fitler\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Remove RX Filter + * + * @param priv A pointer to moal_private structure + * @param rxfilter A pointer to rxfilter string. + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_remove_rxfilter(moal_private *priv, char *rxfilter) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + if (*rxfilter == '0') { + PRINTM(MIOCTL, "Remove IPV4 multicast filter\n"); + priv->rx_filter &= ~RX_FILTER_IPV4_MULTICAST; + } else if (*rxfilter == '1') { + PRINTM(MIOCTL, "Remove broadcast filter\n"); + priv->rx_filter &= ~RX_FILTER_BROADCAST; + } else if (*rxfilter == '2') { + PRINTM(MIOCTL, "Remove unicast filter\n"); + priv->rx_filter &= ~RX_FILTER_UNICAST; + } else if (*rxfilter == '3') { + PRINTM(MIOCTL, "Remove IPV6 multicast fitler\n"); + priv->rx_filter &= ~RX_FILTER_IPV6_MULTICAST; + } else { + PRINTM(MERROR, "unsupported rx fitler\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get WMM IE QoS configuration + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param qos_cfg A pointer to QoS configuration structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_priv_qos_cfg(moal_private *priv, t_u32 action, char *qos_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_wmm_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + int qosinfo = 0; + + ENTER(); + + if (qos_cfg == NULL) { + PRINTM(MERROR, "QOS info buffer is null\n"); + return MLAN_STATUS_FAILURE; + } + if ((action == MLAN_ACT_SET) && + (MLAN_STATUS_SUCCESS != woal_atoi(&qosinfo, qos_cfg))) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_QOS; + req->req_id = MLAN_IOCTL_WMM_CFG; + req->action = action; + if (action == MLAN_ACT_SET) { + cfg->param.qos_cfg = (t_u8)qosinfo; + PRINTM(MIOCTL, "set qosinfo=%d\n", qosinfo); + } + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + + if (action == MLAN_ACT_GET) + *qos_cfg = cfg->param.qos_cfg; +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set sleep period + * + * @param priv A pointer to moal_private structure + * @param psleeppd A pointer to sleep period configuration structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_set_sleeppd(moal_private *priv, char *psleeppd) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int sleeppd = 0; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_atoi(&sleeppd, psleeppd)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MIOCTL, "set sleeppd=%d\n", sleeppd); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD; + req->req_id = MLAN_IOCTL_PM_CFG; + if ((sleeppd <= MAX_SLEEP_PERIOD && sleeppd >= MIN_SLEEP_PERIOD) || + (sleeppd == 0) || (sleeppd == SLEEP_PERIOD_RESERVED_FF)) { + req->action = MLAN_ACT_SET; + pm_cfg->param.sleep_period = sleeppd; + } else { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set scan period function + * + * @param priv A pointer to moal_private structure + * @param buf A pointer to scan command buf + * @param length buf length + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +int woal_set_scan_cfg(moal_private *priv, char *buf, int length) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *ptr = buf + NL80211_SCANCFG_HEADER_SIZE; + int buf_left = length - NL80211_SCANCFG_HEADER_SIZE; + t_u16 active_scan_time = 0; + t_u16 passive_scan_time = 0; + t_u16 specific_scan_time = 0; + + ENTER(); + while (buf_left >= 2) { + switch (*ptr) { + case NL80211_SCANCFG_ACTV_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid ACTV_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + active_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + case NL80211_SCANCFG_PASV_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid PASV_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + passive_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + case NL80211_SCANCFG_SPCF_DWELL_SECTION: + if (buf_left < 3) { + PRINTM(MERROR, + "Invalid SPCF_DWELL_SECTION, buf_left=%d\n", + buf_left); + buf_left = 0; + break; + } + specific_scan_time = ptr[2] << 8 | ptr[1]; + ptr += 3; + buf_left -= 3; + break; + default: + buf_left = 0; + break; + } + } + + if (active_scan_time || passive_scan_time || specific_scan_time) { + PRINTM(MIOCTL, + "Set active_scan_time= %d passive_scan_time=%d specific_scan_time=%d\n", + active_scan_time, passive_scan_time, specific_scan_time); + if (MLAN_STATUS_FAILURE == + woal_set_scan_time(priv, active_scan_time, + passive_scan_time, specific_scan_time)) { + ret = -EFAULT; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Set Radio On/OFF + * + * @param priv A pointer to moal_private structure + * @param option Radio Option + * + * @return 0 --success, otherwise fail + */ +int woal_set_radio(moal_private *priv, t_u8 option) +{ + int ret = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + if ((option != 0) && (option != 1)) { + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_RADIO_CTRL; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_SET; + radio->param.radio_on_off = option; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#endif /* STA_SUPPORT */ + +#ifdef USB +/** + * @brief Initialize USB packet aggregation control + * + * @param handle A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_usb_aggr_init(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + mlan_ds_misc_usb_aggr_ctrl *aggr_param = NULL; + t_u8 usb_aggr_enable; + struct usb_card_rec *cardp = NULL; + int i = 0, resubmit_urbs = 0; + + ENTER(); + + if (!handle) { + PRINTM(MERROR, "Handle is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_STA); + if (!priv) { + PRINTM(MERROR, "STA priv is null\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (handle->card == NULL) { + PRINTM(MERROR, "Card is null in %s()\n", __func__); + ret = MLAN_STATUS_FAILURE; + goto done; + } + cardp = (struct usb_card_rec *)handle->card; + + /* Check module parameter */ + if (handle->params.usb_aggr == 0 || handle->params.usb_aggr == 2) { + usb_aggr_enable = MFALSE; + } else if (handle->params.usb_aggr == 1) { + usb_aggr_enable = MTRUE; + } else { + PRINTM(MWARN, + "Invalid module param (usb_aggr) value %d, " + "using MLAN default\n", + handle->params.usb_aggr); + handle->params.usb_aggr = 0; + usb_aggr_enable = MFALSE; + } + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; + pcfg_misc->sub_command = MLAN_OID_MISC_USB_AGGR_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + aggr_param = &pcfg_misc->param.usb_aggr_params; + /* Initialize TX aggr default values */ + aggr_param->tx_aggr_ctrl.enable = usb_aggr_enable; + aggr_param->tx_aggr_ctrl.aggr_mode = MLAN_USB_AGGR_MODE_NUM; + aggr_param->tx_aggr_ctrl.aggr_align = + MAX(handle->params.max_tx_buf, MLAN_USB_TX_AGGR_ALIGN); + aggr_param->tx_aggr_ctrl.aggr_max = MLAN_USB_TX_MAX_AGGR_NUM; + aggr_param->tx_aggr_ctrl.aggr_tmo = + MLAN_USB_TX_AGGR_TIMEOUT_MSEC * 1000; + + /* Initialize RX deaggr default values */ + aggr_param->rx_deaggr_ctrl.enable = usb_aggr_enable; + aggr_param->rx_deaggr_ctrl.aggr_mode = MLAN_USB_AGGR_MODE_NUM; + aggr_param->rx_deaggr_ctrl.aggr_align = MLAN_USB_RX_ALIGN_SIZE; + aggr_param->rx_deaggr_ctrl.aggr_max = MLAN_USB_RX_MAX_AGGR_NUM; + aggr_param->rx_deaggr_ctrl.aggr_tmo = MLAN_USB_RX_DEAGGR_TIMEOUT_USEC; + + /* Do not fail driver init due to command failure */ + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + /* Disable the feature if FW return failure/unsupported */ + if (req->status_code) + cardp->tx_aggr_ctrl.enable = MFALSE; + else + moal_memcpy_ext(handle, &cardp->tx_aggr_ctrl, + &aggr_param->tx_aggr_ctrl, + sizeof(usb_aggr_ctrl), sizeof(usb_aggr_ctrl)); + + if (req->status_code) { + /* Disable the feature if FW return failure/unsupported */ + cardp->rx_deaggr_ctrl.enable = MFALSE; + } else { + /* Default is disable, resubmit URBs only for enable */ + if (cardp->rx_deaggr_ctrl.enable != usb_aggr_enable) + resubmit_urbs = 1; + } + if (resubmit_urbs) { + /* Indicate resubmition from here */ + cardp->resubmit_urbs = 1; + /* Rx SG feature is disabled due to FW failure/unsupported, + * kill the URBs, they will be resubmitted after saving the + * parameters to USB card */ + if (atomic_read(&cardp->rx_data_urb_pending)) { + for (i = 0; i < MVUSB_RX_DATA_URB; i++) { + if (cardp->rx_data_list[i].urb) { + usb_kill_urb( + cardp->rx_data_list[i].urb); + usb_init_urb( + cardp->rx_data_list[i].urb); + } + } + } + + /* Default is disable, update only for enable case */ + moal_memcpy_ext(handle, &cardp->rx_deaggr_ctrl, + &aggr_param->rx_deaggr_ctrl, + sizeof(usb_aggr_ctrl), sizeof(usb_aggr_ctrl)); + + /* Ensure the next data URBs will use the modified parameters */ + if (!atomic_read(&cardp->rx_data_urb_pending)) { + /* Submit multiple Rx data URBs */ + woal_usb_submit_rx_data_urbs(handle); + } + cardp->resubmit_urbs = 0; + } + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set hotspot configuration value to mlan layer + * + * @param priv A pointer to moal_private structure + * @param wait_option wait option + * @param hotspotcfg Hotspot configuration value + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_set_hotspotcfg(moal_private *priv, t_u8 wait_option, + t_u32 hotspotcfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + 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 = MLAN_STATUS_FAILURE; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->param.hotspot_cfg = hotspotcfg; + req->action = MLAN_ACT_SET; + + cfg->sub_command = MLAN_OID_MISC_HOTSPOT_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Send delelte all BA command to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_delba_all(moal_private *priv, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ds_11n_delba *del_ba = NULL; + t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0}; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + cfg_11n->sub_command = MLAN_OID_11N_CFG_DELBA; + + del_ba = &cfg_11n->param.del_ba; + memset(del_ba, 0, sizeof(mlan_ds_11n_delba)); + del_ba->direction = DELBA_RX | DELBA_TX; + del_ba->tid = DELBA_ALL_TIDS; + moal_memcpy_ext(priv->phandle, del_ba->peer_mac_addr, zero_mac, + ETH_ALEN, sizeof(del_ba->peer_mac_addr)); + + status = woal_request_ioctl(priv, req, wait_option); + + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Send 11d enable/disable command to firmware. + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param enable Enable/Disable 11d + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_set_11d(moal_private *priv, t_u8 wait_option, t_u8 enable) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_snmp_mib *snmp = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + req->action = MLAN_ACT_SET; + req->req_id = MLAN_IOCTL_SNMP_MIB; + + snmp = (mlan_ds_snmp_mib *)req->pbuf; + snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11D; + snmp->param.oid_value = enable; + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Handle miscellaneous ioctls for asynchronous command response + * + * @param priv Pointer to moal_private structure + * @param info Pointer to mlan_ds_misc_cfg structure + * + * @return N/A + */ +void woal_ioctl_get_misc_conf(moal_private *priv, mlan_ds_misc_cfg *info) +{ + ENTER(); + switch (info->sub_command) { + default: + break; + } +} + +#ifdef CONFIG_PROC_FS +#define TX_PWR_STR_LEN 20 +#define TX_CONT_STR_LEN 50 +#define TX_FRAME_STR_LEN 80 +/* + * @brief Parse mfg cmd tx pwr string + * + * @param s A pointer to user buffer + * @param len Length of user buffer + * @param d A pointer to mfg_cmd_generic_cfg struct + * @return 0 on success, -EINVAL otherwise + */ +static int parse_tx_pwr_string(const char *s, size_t len, + struct mfg_cmd_generic_cfg *d) +{ + int ret = MLAN_STATUS_SUCCESS; + char *string = NULL; + char *tmp = NULL; + char *pos = NULL; + gfp_t flag; + + ENTER(); + if (!s || !d) { + LEAVE(); + return -EINVAL; + } + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + string = kzalloc(TX_PWR_STR_LEN, flag); + if (string == NULL) { + LEAVE(); + return -ENOMEM; + } + + moal_memcpy_ext(NULL, string, s + strlen("tx_power="), + len - strlen("tx_power="), TX_PWR_STR_LEN - 1); + + tmp = string; + string = strstrip(string); + + /* tx power value */ + pos = strsep(&string, " \t"); + if (pos) + d->data1 = (t_u32)woal_string_to_number(pos); + + /* modulation */ + pos = strsep(&string, " \t"); + if (pos) + d->data2 = (t_u32)woal_string_to_number(pos); + + /* path id */ + pos = strsep(&string, " \t"); + if (pos) + d->data3 = (t_u32)woal_string_to_number(pos); + + if ((d->data1 > 24) || (d->data2 > 2)) + ret = -EINVAL; + + kfree(tmp); + LEAVE(); + return ret; +} +/* + * @brief Parse mfg cmd tx cont string + * + * @param s A pointer to user buffer + * @param len Length of user buffer + * @param d A pointer to mfg_cmd_tx_cont struct + * @return 0 on success, -EINVAL otherwise + */ +static int parse_tx_cont_string(const char *s, size_t len, + struct mfg_cmd_tx_cont *d) +{ + int ret = MLAN_STATUS_SUCCESS; + char *string = NULL; + char *tmp = NULL; + char *pos = NULL; + gfp_t flag; + + ENTER(); + if (!s || !d) { + LEAVE(); + return -EINVAL; + } + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + string = kzalloc(TX_CONT_STR_LEN, flag); + if (string == NULL) { + LEAVE(); + return -ENOMEM; + } + + moal_memcpy_ext(NULL, string, s + strlen("tx_continuous="), + len - strlen("tx_continuous="), TX_CONT_STR_LEN - 1); + + tmp = string; + string = strstrip(string); + + pos = strsep(&string, " \t"); + if (pos) + d->enable_tx = (t_u32)woal_string_to_number(pos); + + if (d->enable_tx == MFALSE) + goto done; + + pos = strsep(&string, " \t"); + if (pos) + d->cw_mode = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->payload_pattern = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->cs_mode = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->act_sub_ch = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->tx_rate = (t_u32)woal_string_to_number(pos); + + if ((d->enable_tx > 1) || (d->cw_mode > 1) || (d->cs_mode > 1) || + (d->act_sub_ch == 2 || d->act_sub_ch > 3)) + ret = -EINVAL; +done: + kfree(tmp); + LEAVE(); + return ret; +} + +/* + * @brief Parse mfg cmd tx frame string + * + * @param s A pointer to user buffer + * @param len Length of user buffer + * @param d A pointer to mfg_cmd_tx_frame2 struct + * @return 0 on success, -EINVAL otherwise + */ +static int parse_tx_frame_string(const char *s, size_t len, + struct mfg_cmd_tx_frame2 *d) +{ + int ret = MLAN_STATUS_SUCCESS; + char *string = NULL; + char *tmp = NULL; + char *pos = NULL; + int i; + gfp_t flag; + + ENTER(); + if (!s || !d) { + LEAVE(); + return -EINVAL; + } + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + string = kzalloc(TX_FRAME_STR_LEN, flag); + if (string == NULL) + return -ENOMEM; + + moal_memcpy_ext(NULL, string, s + strlen("tx_frame="), + len - strlen("tx_frame="), TX_FRAME_STR_LEN - 1); + + tmp = string; + string = strstrip(string); + + pos = strsep(&string, " \t"); + if (pos) + d->enable = (t_u32)woal_string_to_number(pos); + + if (d->enable == MFALSE) + goto done; + + pos = strsep(&string, " \t"); + if (pos) + d->data_rate = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->frame_pattern = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->frame_length = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->adjust_burst_sifs = (t_u16)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->burst_sifs_in_us = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->short_preamble = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->act_sub_ch = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->short_gi = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->adv_coding = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->tx_bf = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->gf_mode = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) + d->stbc = (t_u32)woal_string_to_number(pos); + + pos = strsep(&string, " \t"); + if (pos) { + for (i = 0; i < ETH_ALEN; i++) { + pos = strsep(&string, ":"); + if (pos) + d->bssid[i] = woal_atox(pos); + } + } + + if ((d->enable > 1) || (d->frame_length == 0) || + (d->adjust_burst_sifs > 1) || (d->burst_sifs_in_us > 255) || + (d->short_preamble > 1) || + (d->act_sub_ch == 2 || d->act_sub_ch > 3) || (d->short_gi > 1) || + (d->adv_coding > 1) || (d->tx_bf > 1) || (d->gf_mode > 1) || + (d->stbc > 1)) + ret = -EINVAL; +done: + kfree(tmp); + LEAVE(); + return ret; +} +/** + * @brief This function enables/disables RF test mode in firmware + * + * @param handle A pointer to moal_handle structure + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING on success, + * otherwise failure code + */ +mlan_status woal_process_rf_test_mode(moal_handle *handle, t_u32 mode) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + t_u32 flag = 0; + + ENTER(); +#ifdef MFG_CMD_SUPPORT + if (mfg_mode) { + LEAVE(); + return ret; + } +#endif + if (mode != MFG_CMD_SET_TEST_MODE && mode != MFG_CMD_UNSET_TEST_MODE) { + LEAVE(); + return ret; + } + + 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_RF_TEST_GENERIC; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + if (mode == MFG_CMD_SET_TEST_MODE) + misc->param.mfg_generic_cfg.mfg_cmd = + MFG_CMD_SET_TEST_MODE; + ret = woal_request_ioctl(woal_get_priv(handle, + MLAN_BSS_ROLE_ANY), + req, MOAL_IOCTL_WAIT); + } + + if (ret == MLAN_STATUS_SUCCESS && mode == MFG_CMD_SET_TEST_MODE && + handle->rf_data == NULL) { + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : + GFP_KERNEL; + /* Allocate memory to hold RF test mode data */ + handle->rf_data = + kzalloc(sizeof(struct rf_test_mode_data), flag); + if (!handle->rf_data) + PRINTM(MERROR, + "Couldn't allocate memory for RF test mode\n"); + handle->rf_test_mode = MTRUE; + if (handle->rf_data) { + /* antenna is set to 1 by default */ + handle->rf_data->tx_antenna = 1; + handle->rf_data->rx_antenna = 1; + } + } else if (mode == MFG_CMD_UNSET_TEST_MODE) { + if (handle->rf_data) { + /* Free RF test mode data memory */ + kfree(handle->rf_data); + handle->rf_data = NULL; + } + handle->rf_test_mode = MFALSE; + } + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief This function sends RF test mode command in firmware + * + * @param handle A pointer to moal_handle structure + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING on success, + * otherwise failure code + */ +mlan_status woal_process_rf_test_mode_cmd(moal_handle *handle, t_u32 cmd, + const char *buffer, size_t len, + t_u32 action, t_u32 val) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int err = MFALSE; + int i; + + ENTER(); + + if (!handle->rf_test_mode || !handle->rf_data) + goto done; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (!req) + goto done; + + misc = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_RF_TEST_GENERIC; + req->action = action; + + switch (cmd) { + case MFG_CMD_TX_ANT: + if (val != 1 && val != 2 && val != 3) + err = MTRUE; + break; + case MFG_CMD_RX_ANT: + if (val != 1 && val != 2 && val != 3) + err = MTRUE; + break; + case MFG_CMD_RF_BAND_AG: + if (val != 0 && val != 1) + err = MTRUE; + break; + case MFG_CMD_RF_CHANNELBW: + if (val != 0 && val != 1 && + (val != 4 || + (val == 4 && handle->rf_data->band == BAND_2GHZ))) + err = MTRUE; + break; + case MFG_CMD_RF_CHAN: + break; + case MFG_CMD_CLR_RX_ERR: + break; + case MFG_CMD_RFPWR: + if (parse_tx_pwr_string(buffer, len, + &misc->param.mfg_generic_cfg)) + err = MTRUE; + break; + case MFG_CMD_TX_CONT: + misc->sub_command = MLAN_OID_MISC_RF_TEST_TX_CONT; + if (parse_tx_cont_string(buffer, len, &misc->param.mfg_tx_cont)) + err = MTRUE; + break; + case MFG_CMD_TX_FRAME: + misc->sub_command = MLAN_OID_MISC_RF_TEST_TX_FRAME; + if (parse_tx_frame_string(buffer, len, + &misc->param.mfg_tx_frame2)) + err = MTRUE; + break; + default: + err = MTRUE; + } + + if (!err) { + misc->param.mfg_generic_cfg.mfg_cmd = (t_u32)cmd; + if (cmd != MFG_CMD_RFPWR && + misc->sub_command == MLAN_OID_MISC_RF_TEST_GENERIC) + misc->param.mfg_generic_cfg.data1 = val; + + ret = woal_request_ioctl(woal_get_priv(handle, + MLAN_BSS_ROLE_ANY), + req, MOAL_IOCTL_WAIT); + } + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + switch (cmd) { + case MFG_CMD_TX_ANT: + handle->rf_data->tx_antenna = misc->param.mfg_generic_cfg.data1; + break; + case MFG_CMD_RX_ANT: + handle->rf_data->rx_antenna = misc->param.mfg_generic_cfg.data1; + break; + case MFG_CMD_RF_BAND_AG: + handle->rf_data->band = misc->param.mfg_generic_cfg.data1; + /* set fw default bw and channel config on band change */ + handle->rf_data->bandwidth = CHANNEL_BW_20MHZ; + if (handle->rf_data->band == BAND_2GHZ) + handle->rf_data->channel = 6; + else if (handle->rf_data->band == BAND_5GHZ) + handle->rf_data->channel = 36; + break; + case MFG_CMD_RF_CHANNELBW: + handle->rf_data->bandwidth = misc->param.mfg_generic_cfg.data1; + break; + case MFG_CMD_RF_CHAN: + handle->rf_data->channel = misc->param.mfg_generic_cfg.data1; + break; + case MFG_CMD_CLR_RX_ERR: + handle->rf_data->rx_tot_pkt_count = + misc->param.mfg_generic_cfg.data1; + handle->rf_data->rx_mcast_bcast_pkt_count = + misc->param.mfg_generic_cfg.data2; + handle->rf_data->rx_pkt_fcs_err_count = + misc->param.mfg_generic_cfg.data3; + break; + case MFG_CMD_RFPWR: + handle->rf_data->tx_power_data[0] = + misc->param.mfg_generic_cfg.data1; + handle->rf_data->tx_power_data[1] = + misc->param.mfg_generic_cfg.data2; + handle->rf_data->tx_power_data[2] = + misc->param.mfg_generic_cfg.data3; + break; + case MFG_CMD_TX_CONT: + handle->rf_data->tx_cont_data[0] = + misc->param.mfg_tx_cont.enable_tx; + handle->rf_data->tx_cont_data[1] = + misc->param.mfg_tx_cont.cw_mode; + handle->rf_data->tx_cont_data[2] = + misc->param.mfg_tx_cont.payload_pattern; + handle->rf_data->tx_cont_data[3] = + misc->param.mfg_tx_cont.cs_mode; + handle->rf_data->tx_cont_data[4] = + misc->param.mfg_tx_cont.act_sub_ch; + handle->rf_data->tx_cont_data[5] = + misc->param.mfg_tx_cont.tx_rate; + break; + case MFG_CMD_TX_FRAME: + handle->rf_data->tx_frame_data[0] = + misc->param.mfg_tx_frame2.enable; + handle->rf_data->tx_frame_data[1] = + misc->param.mfg_tx_frame2.data_rate; + handle->rf_data->tx_frame_data[2] = + misc->param.mfg_tx_frame2.frame_pattern; + handle->rf_data->tx_frame_data[3] = + misc->param.mfg_tx_frame2.frame_length; + handle->rf_data->tx_frame_data[4] = + misc->param.mfg_tx_frame2.adjust_burst_sifs; + handle->rf_data->tx_frame_data[5] = + misc->param.mfg_tx_frame2.burst_sifs_in_us; + handle->rf_data->tx_frame_data[6] = + misc->param.mfg_tx_frame2.short_preamble; + handle->rf_data->tx_frame_data[7] = + misc->param.mfg_tx_frame2.act_sub_ch; + handle->rf_data->tx_frame_data[8] = + misc->param.mfg_tx_frame2.short_gi; + handle->rf_data->tx_frame_data[9] = + misc->param.mfg_tx_frame2.adv_coding; + handle->rf_data->tx_frame_data[10] = + misc->param.mfg_tx_frame2.tx_bf; + handle->rf_data->tx_frame_data[11] = + misc->param.mfg_tx_frame2.gf_mode; + handle->rf_data->tx_frame_data[12] = + misc->param.mfg_tx_frame2.stbc; + for (i = 0; i < ETH_ALEN; i++) { + handle->rf_data->bssid[i] = + misc->param.mfg_tx_frame2.bssid[i]; + } + break; + } +done: + if (err || ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif /* RF_TEST_MODE */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_main.c b/mxm_wifiex/wlan_src/mlinux/moal_main.c new file mode 100644 index 0000000..dbc55ee --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_main.c @@ -0,0 +1,9628 @@ +/** @file moal_main.c + * + * @brief This file contains the major functions in WLAN + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 USB +#include "moal_usb.h" +#endif +#ifdef SDIO +#include "moal_sdio.h" +#endif +#ifdef PCIE +#include "moal_pcie.h" +#endif +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#include "moal_cfg80211_util.h" +#endif +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT +#include "moal_sta_cfg80211.h" +#endif +#endif +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT +#include "moal_uap_cfg80211.h" +#endif +#endif +#include "moal_eth_ioctl.h" + +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#endif + +/******************************************************** + Global Variables + ********************************************************/ +/** the pointer of new fwdump fname for each dump**/ +char *fwdump_fname; +/** Semaphore for add/remove card */ +struct semaphore AddRemoveCardSem; +/** + * The global variable of a pointer to moal_handle + * structure variable + **/ +moal_handle *m_handle[MAX_MLAN_ADAPTER]; +/** Global veriable for usb independent reset */ +extern int fw_reload; + +extern int wifi_status; + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int fw_region; +#endif +#endif +/******************************************************** + Local Variables +********************************************************/ + +#ifdef SD8887 +static struct _card_info card_info_SD8887 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 0, + .pmic = 0, + .cal_data_cfg = 1, + .low_power_enable = 1, + .rx_rate_max = 196, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .rev_id_reg = 0xc8, + .fw_name = SD8887_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8887_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V2, + .dump_fw_ctrl_reg = 0xa2, + .dump_fw_start_reg = 0xa3, + .dump_fw_end_reg = 0xaa, + .dump_fw_host_ready = 0xee, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0x90, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0B6, + .fw_reset_val = 1, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 0, +}; +#endif + +#ifdef SD8897 +static struct _card_info card_info_SD8897 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 0, + .pmic = 0, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0xbc, + .fw_name = SD8897_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8897_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V2, + .dump_fw_ctrl_reg = 0xe2, + .dump_fw_start_reg = 0xe3, + .dump_fw_end_reg = 0xea, + .dump_fw_host_ready = 0xee, + .dump_reg.reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, 0x59, 0x5c, 0x5d}, + .dump_reg.reg_table_size = 8, + .scratch_reg = 0xc0, + .func1_reg_start = 0x04, + .func1_reg_end = 0x0b, + .fw_reset_reg = 0x0E8, + .fw_reset_val = 1, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 0, +}; +#endif + +#ifdef PCIE8897 +static struct _card_info card_info_PCIE8897 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 0, + .pmic = 0, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0x0c58, + .fw_name = PCIE8897_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = PCIE8897_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 0, +}; +#endif + +#ifdef USB8897 +static struct _card_info card_info_USB8897 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 0, + .pmic = 0, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT, + .fw_name = USB8897_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = USB8897_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 0, +}; +#endif + +#ifdef SD8977 +static struct _card_info card_info_SD8977 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 76, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD8977_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8977_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef SD8978 +static struct _card_info card_info_SD8978 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 76, + .histogram_table_num = 1, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD8978_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8978_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef SD8997 +static struct _card_info card_info_SD8997 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD8997_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8997_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef SD9098 +static struct _card_info card_info_SD9098 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD9098_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD9098_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x90002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef SD9097 +static struct _card_info card_info_SD9097 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0xc8, + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .fw_name = SD9097_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD9097_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x90002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef PCIE8997 +static struct _card_info card_info_PCIE8997 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 196, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0x8, + .host_strap_reg = 0x0cd0, + .magic_reg = 0x0cd4, + .fw_name = PCIE8997_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = PCIE8997_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef PCIE9097 +static struct _card_info card_info_PCIE9097 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0x8, + .host_strap_reg = 0x1c70, + .magic_reg = 0x1c74, + .fw_name = PCIE9097_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = PCIE9097_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef PCIE9098 +static struct _card_info card_info_PCIE9098 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .histogram_table_num = 3, + .feature_control = FEATURE_CTRL_DEFAULT, + .rev_id_reg = 0x8, + .host_strap_reg = 0x1c70, + .magic_reg = 0x1c74, + .fw_name = PCIE9098_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = PCIE9098_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef USB8978 +static struct _card_info card_info_USB8978 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 76, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .histogram_table_num = 1, + .fw_name = USB8978_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = USB8978_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef USB8997 +static struct _card_info card_info_USB8997 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 0, + .rx_rate_max = 196, + .feature_control = FEATURE_CTRL_DEFAULT, + .histogram_table_num = 3, + .fw_name = USB8997_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = USB8997_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef USB9098 +static struct _card_info card_info_USB9098 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .feature_control = FEATURE_CTRL_DEFAULT, + .histogram_table_num = 3, + .fw_name = USB9098_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = USB9098_DEFAULT_WLAN_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif + +#ifdef USB9097 +static struct _card_info card_info_USB9097 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 1, + .v16_fw_api = 1, + .v17_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 0, + .low_power_enable = 0, + .rx_rate_max = 412, + .feature_control = FEATURE_CTRL_DEFAULT, + .histogram_table_num = 3, + .fw_name = USBUSB9097_COMBO_V1_FW_NAME, + .fw_name_wlan = USB9097_WLAN_V1_FW_NAME, + .per_pkt_cfg_support = 1, +}; +#endif +#ifdef SD8987 +static struct _card_info card_info_SD8987 = { + .embedded_supp = 1, + .drcs = 1, + .go_noa = 0, + .v16_fw_api = 1, + .pmic = 1, + .cal_data_cfg = 1, + .low_power_enable = 1, + .rx_rate_max = 196, + .feature_control = FEATURE_CTRL_DEFAULT & (~FEATURE_CTRL_STREAM_2X2), + .host_strap_reg = 0xf4, + .magic_reg = 0xf0, + .histogram_table_num = 3, + .fw_name = SD8987_DEFAULT_COMBO_FW_NAME, + .fw_name_wlan = SD8987_DEFAULT_WLAN_FW_NAME, +#ifdef SDIO + .dump_fw_info = DUMP_FW_SDIO_V3, + .dump_fw_ctrl_reg = 0xf9, + .dump_fw_start_reg = 0xf1, + .dump_fw_end_reg = 0xf8, + .dump_fw_host_ready = 0xcc, + .dump_reg.reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, 0x6a}, + .dump_reg.reg_table_size = 13, + .scratch_reg = 0xe8, + .func1_reg_start = 0x10, + .func1_reg_end = 0x17, + .fw_reset_reg = 0x0EE, + .fw_reset_val = 0x99, + .slew_rate_reg = 0x80002328, + .slew_rate_bit_offset = 12, +#endif + .per_pkt_cfg_support = 1, +}; +#endif + +/** Driver version */ +char driver_version[] = + INTF_CARDTYPE KERN_VERSION "--" MLAN_RELEASE_VERSION "-GPL" + "-(" + "FP" FPNUM ")" +#ifdef DEBUG_LEVEL2 + "-dbg" +#endif + " "; + +/** woal_callbacks */ +static mlan_callbacks woal_callbacks = { + .moal_get_fw_data = moal_get_fw_data, + .moal_get_vdll_data = moal_get_vdll_data, + .moal_get_hw_spec_complete = moal_get_hw_spec_complete, + .moal_init_fw_complete = moal_init_fw_complete, + .moal_shutdown_fw_complete = moal_shutdown_fw_complete, + .moal_send_packet_complete = moal_send_packet_complete, + .moal_recv_packet = moal_recv_packet, + .moal_recv_event = moal_recv_event, + .moal_ioctl_complete = moal_ioctl_complete, + .moal_alloc_mlan_buffer = moal_alloc_mlan_buffer, + .moal_free_mlan_buffer = moal_free_mlan_buffer, +#ifdef USB + .moal_recv_complete = moal_recv_complete, + .moal_write_data_async = moal_write_data_async, +#endif /* USB */ + +#if defined(SDIO) || defined(PCIE) + .moal_write_reg = moal_write_reg, + .moal_read_reg = moal_read_reg, +#endif /* SDIO || PCIE */ + .moal_write_data_sync = moal_write_data_sync, + .moal_read_data_sync = moal_read_data_sync, + .moal_malloc = moal_malloc, + .moal_mfree = moal_mfree, + .moal_vmalloc = moal_vmalloc, + .moal_vfree = moal_vfree, +#ifdef PCIE + .moal_malloc_consistent = moal_malloc_consistent, + .moal_mfree_consistent = moal_mfree_consistent, + .moal_map_memory = moal_map_memory, + .moal_unmap_memory = moal_unmap_memory, +#endif /* PCIE */ + .moal_memset = moal_memset, + .moal_memcpy = moal_memcpy, + .moal_memcpy_ext = moal_memcpy_ext, + .moal_memmove = moal_memmove, + .moal_memcmp = moal_memcmp, + .moal_udelay = moal_udelay, + .moal_usleep_range = moal_usleep_range, + .moal_get_system_time = moal_get_system_time, + .moal_get_boot_ktime = moal_get_boot_ktime, + .moal_init_timer = moal_init_timer, + .moal_free_timer = moal_free_timer, + .moal_start_timer = moal_start_timer, + .moal_stop_timer = moal_stop_timer, + .moal_init_lock = moal_init_lock, + .moal_free_lock = moal_free_lock, + .moal_spin_lock = moal_spin_lock, + .moal_spin_unlock = moal_spin_unlock, + .moal_print = moal_print, + .moal_print_netintf = moal_print_netintf, + .moal_assert = moal_assert, + .moal_hist_data_add = moal_hist_data_add, +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + .moal_wait_hostcmd_complete = moal_wait_hostcmd_complete, + .moal_notify_hostcmd_complete = moal_notify_hostcmd_complete, +#endif + .moal_tp_accounting = moal_tp_accounting, + .moal_tp_accounting_rx_param = moal_tp_accounting_rx_param, +}; + +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 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) + , + unsigned int txqueue +#endif +); +struct net_device_stats *woal_get_stats(struct net_device *dev); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev); +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + struct net_device *sb_dev, + select_queue_fallback_t fallback); +#endif +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback); +#endif +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv); +#endif +#else +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb); +#endif +#endif + +static moal_handle *reset_handle; +/** Hang workqueue */ +static struct workqueue_struct *hang_workqueue; +/** Hang work */ +static struct work_struct hang_work; + +/** + * @brief This function process FW hang + * + * @param handle Pointer to structure moal_handle + * + * @return N/A + */ +static void woal_hang_work_queue(struct work_struct *work) +{ + int i; + ENTER(); + if (!reset_handle) { + LEAVE(); + return; + } + for (i = 0; i < reset_handle->priv_num; i++) { + if (reset_handle->priv[i] && reset_handle->priv[i]->netdev) { + PRINTM(MMSG, "Close netdev %s\n", + reset_handle->priv[i]->netdev->name); + rtnl_lock(); + dev_close(reset_handle->priv[i]->netdev); + rtnl_unlock(); + break; + } + } + reset_handle = NULL; + LEAVE(); +} + +/** + * @brief This function process FW hang + * + * @param handle Pointer to structure moal_handle + * + * @return N/A + */ +void woal_process_hang(moal_handle *handle) +{ + ENTER(); + if (reset_handle == NULL) { + PRINTM(MMSG, "Start to process hanging\n"); + reset_handle = handle; + mlan_ioctl(handle->pmlan_adapter, NULL); + queue_work(hang_workqueue, &hang_work); +#ifdef ANDROID_KERNEL +#define WAKE_LOCK_HANG 5000 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + __pm_wakeup_event(&reset_handle->ws, WAKE_LOCK_HANG); +#else + wake_lock_timeout(&reset_handle->wake_lock, + msecs_to_jiffies(WAKE_LOCK_HANG)); +#endif +#endif + } + LEAVE(); +} + +/** + * @brief Check if any interface is active + * + * @param handle A pointer to moal_handle + * + * + * @return MTRUE/MFALSE; + */ +t_u8 woal_is_any_interface_active(moal_handle *handle) +{ + int i; + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) + return MTRUE; + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) { + if (handle->priv[i]->bss_started == MTRUE) + return MTRUE; + } +#endif + } + return MFALSE; +} + +/** + * @brief This function validates a SSID as being able to be printed + * + * @param pssid SSID structure to validate + * + * @return MTRUE or MFALSE + */ +BOOLEAN woal_ssid_valid(mlan_802_11_ssid *pssid) +{ +#ifdef ASCII_SSID_CHECK + unsigned int ssid_idx; + + ENTER(); + + for (ssid_idx = 0; ssid_idx < pssid->ssid_len; ssid_idx++) { + if ((pssid->ssid[ssid_idx] < 0x20) || + (pssid->ssid[ssid_idx] > 0x7e)) { + LEAVE(); + return MFALSE; + } + } + LEAVE(); +#endif + return MTRUE; +} + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +/** + * @brief Remain on Channel timeout function + * + * @param context A pointer to context + * @return N/A + */ +void woal_remain_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + moal_private *priv = handle->priv[handle->remain_bss_index]; + + ENTER(); + + PRINTM(MEVENT, "remain_timer fired.\n"); + if (handle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + priv->netdev, +#else + priv->wdev, +#endif + handle->cookie, &handle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + handle->channel_type, +#endif + GFP_ATOMIC); + handle->cookie = 0; + } + handle->is_remain_timer_set = MFALSE; + + LEAVE(); + return; +} +#endif +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +/** + * @brief GO timeout function + * + * @param context A pointer to context + * @return N/A + */ +void woal_go_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + + ENTER(); + + PRINTM(MEVENT, "go_timer fired.\n"); + handle->is_go_timer_set = MFALSE; + + LEAVE(); + return; +} + +#endif +#endif + +/** + * @brief check if we already connect to the AP. + * @param priv A pointer to moal_private structure + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * @return MTRUE/MFALSE; + */ +int woal_is_connected(moal_private *priv, mlan_ssid_bssid *ssid_bssid) +{ + mlan_bss_info bss_info; + int ret = MFALSE; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + ENTER(); + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) + goto done; + if (bss_info.media_connected) { + if (memcmp(ssid_bssid->bssid, zero_mac, sizeof(zero_mac))) { + if (ssid_bssid->ssid.ssid_len) { /* compare ssid and + bssid */ + if ((ssid_bssid->ssid.ssid_len == + bss_info.ssid.ssid_len) && + !memcmp(ssid_bssid->ssid.ssid, + bss_info.ssid.ssid, + bss_info.ssid.ssid_len) && + !memcmp(ssid_bssid->bssid, bss_info.bssid, + MLAN_MAC_ADDR_LENGTH)) + ret = MTRUE; + } else { /* compare bssid */ + if (!memcmp(ssid_bssid->bssid, bss_info.bssid, + MLAN_MAC_ADDR_LENGTH)) { + moal_memcpy_ext( + priv->phandle, + &ssid_bssid->ssid, + &bss_info.ssid, + sizeof(bss_info.ssid), + sizeof(ssid_bssid->ssid)); + ret = MTRUE; + } + } + } else { /* compare ssid */ + if (ssid_bssid->ssid.ssid_len && + (ssid_bssid->ssid.ssid_len == + bss_info.ssid.ssid_len) && + !memcmp(ssid_bssid->ssid.ssid, bss_info.ssid.ssid, + bss_info.ssid.ssid_len)) { + moal_memcpy_ext(priv->phandle, + &ssid_bssid->bssid, + &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH, + sizeof(ssid_bssid->bssid)); + ret = MTRUE; + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Look up specific IE in a buf + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param id Element id to lookup + * + * @return Pointer of the specific IE -- success, NULL -- fail + */ +const t_u8 *woal_parse_ie_tlv(const t_u8 *ie, int len, t_u8 id) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + + /* IE format: + * | u8 | id | + * | u8 | len | + * | var | data | + */ + while (left_len >= 2) { + length = *(pos + 1); + if ((*pos == id) && (length + 2) <= left_len) + return pos; + pos += (length + 2); + left_len -= (length + 2); + } + + return NULL; +} + +/** + * @brief Look up specific IE in Extension IE + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ext_id Extended Element id to lookup + * + * @return Pointer of the specific Extended IE -- success, NULL + * -- fail + */ +const t_u8 *woal_parse_ext_ie_tlv(const t_u8 *ie, int len, t_u8 ext_id) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + + /* Extension IE format: + * | u8 | id | + * | u8 | len | + * | u8 | ext_id | + * | var | data | + */ + while (left_len >= 2) { + length = *(pos + 1); + if ((*pos == EXTENSION) && (length + 2) <= left_len) { + if (*(pos + 2) == ext_id) + return pos; + } + pos += (length + 2); + left_len -= (length + 2); + } + return NULL; +} + +/** + * @brief Get mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT) + * + * @return Wireless mode + */ +t_u32 woal_get_mode(moal_private *priv, t_u8 wait_option) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 mode = 0; + + ENTER(); + +#if defined(STA_WEXT) || defined(UAP_WEXT) + mode = priv->w_stats.status; +#endif + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + switch (bss->param.bss_mode) { + case MLAN_BSS_MODE_INFRA: + mode = MW_MODE_INFRA; + break; + case MLAN_BSS_MODE_IBSS: + mode = MW_MODE_ADHOC; + break; + default: + mode = MW_MODE_AUTO; + break; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return mode; +} + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief This function update the default firmware name + * + * @param handle A pointer to moal_handle structure + * + * @return N/A + */ +void woal_update_firmware_name(moal_handle *handle) +{ + if (handle->params.fw_name) { + handle->drv_mode.fw_name = handle->params.fw_name; + } else { + if (!moal_extflg_isset(handle, EXT_FW_SERIAL) || + handle->fw_reload || handle->params.fw_reload) { + handle->drv_mode.fw_name = + handle->card_info->fw_name_wlan; + } else + handle->drv_mode.fw_name = handle->card_info->fw_name; + } +} +/** + * @brief This function dynamically populates the driver mode table + * + * @param handle A pointer to moal_handle structure + * @param drv_mode_local Driver mode + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_update_drv_tbl(moal_handle *handle, int drv_mode_local) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + unsigned int intf_num = 0; + int i = 0, j = 0; + mlan_bss_attr *bss_tbl = NULL; +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + int last_wfd_index = 0; + int max_vir_bss = handle->params.max_vir_bss; +#endif +#endif +#ifdef STA_SUPPORT + int max_sta_bss = handle->params.max_sta_bss; +#endif +#ifdef UAP_SUPPORT + int max_uap_bss = handle->params.max_uap_bss; +#endif +#ifdef WIFI_DIRECT_SUPPORT + int max_wfd_bss = handle->params.max_wfd_bss; +#endif + + ENTER(); + + /* Calculate number of interfaces */ +#ifdef STA_SUPPORT + if (drv_mode_local & DRV_MODE_STA) { + if ((max_sta_bss < 1) || (max_sta_bss > MAX_STA_BSS)) { + PRINTM(MWARN, + "Unsupported max_sta_bss (%d), setting to default\n", + max_sta_bss); + max_sta_bss = DEF_STA_BSS; + } + intf_num += max_sta_bss; + handle->params.max_sta_bss = 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; + handle->params.max_uap_bss = max_uap_bss; + } +#endif /* UAP_SUPPORT */ + +#ifdef 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; + handle->params.max_wfd_bss = max_wfd_bss; +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + intf_num += max_vir_bss; +#endif + } + +#endif /* WIFI_DIRECT_SUPPORT */ + + /* Create BSS attribute table */ + if ((intf_num == 0) || (intf_num > MLAN_MAX_BSS_NUM)) { + PRINTM(MERROR, "Unsupported number of BSS %d\n", intf_num); + ret = MLAN_STATUS_FAILURE; + goto done; + } else { + /* Create new table */ + bss_tbl = kmalloc(sizeof(mlan_bss_attr) * intf_num, GFP_KERNEL); + if (!bss_tbl) { + PRINTM(MERROR, + "Could not create BSS attribute table\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* Populate BSS attribute table */ +#ifdef STA_SUPPORT + if (drv_mode_local & DRV_MODE_STA) { + for (j = 0; j < max_sta_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_STA; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } + } +#endif /* STA_SUPPORT */ + +#ifdef UAP_SUPPORT + if (drv_mode_local & DRV_MODE_UAP) { + for (j = 0; j < max_uap_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_UAP; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } + } +#endif /* UAP_SUPPORT */ + +#ifdef WIFI_DIRECT_SUPPORT + if (drv_mode_local & DRV_MODE_WIFIDIRECT) { + for (j = 0; j < max_wfd_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_WIFIDIRECT; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j; + bss_tbl[i].bss_virtual = MFALSE; + i++; + } +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + last_wfd_index = j; +#endif + } +#endif /* WIFI_DIRECT_SUPPORT */ + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + /** append virtual interface at the end of table */ + for (j = 0; j < max_vir_bss; j++) { + if (i >= intf_num) + break; + bss_tbl[i].bss_type = MLAN_BSS_TYPE_WIFIDIRECT; + bss_tbl[i].frame_type = MLAN_DATA_FRAME_TYPE_ETH_II; + bss_tbl[i].active = MTRUE; + bss_tbl[i].bss_priority = 0; + bss_tbl[i].bss_num = j + last_wfd_index; + bss_tbl[i].bss_virtual = MTRUE; + i++; + } +#endif +#endif + + /* Clear existing table, if any */ + kfree(handle->drv_mode.bss_attr); + handle->drv_mode.bss_attr = NULL; + + /* Create moal_drv_mode entry */ + handle->drv_mode.drv_mode = handle->params.drv_mode; + handle->drv_mode.intf_num = intf_num; + handle->drv_mode.bss_attr = bss_tbl; + + /* update default firmware name */ + woal_update_firmware_name(handle); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function initializes software + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_init_sw(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + unsigned int i; + mlan_device device; + t_void *pmlan; + int cfg80211_wext = handle->params.cfg80211_wext; + + ENTER(); + + /* Initialize moal_handle structure */ + handle->hardware_status = HardwareStatusInitializing; + handle->main_state = MOAL_STATE_IDLE; + +#ifdef STA_SUPPORT + if ((handle->params.drv_mode & DRV_MODE_STA) +#ifdef STA_WEXT + && !IS_STA_WEXT(cfg80211_wext) +#endif +#ifdef STA_CFG80211 + && !IS_STA_CFG80211(cfg80211_wext) +#endif +#ifdef MFG_CMD_SUPPORT + && !handle->params.mfg_mode +#endif + ) { + PRINTM(MERROR, + "STA without WEXT or CFG80211 bit definition!\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#endif /* STA_SUPPORT */ + +#if defined(STA_CFG80211) && defined(STA_SUPPORT) + if (IS_STA_CFG80211(cfg80211_wext)) + cfg80211_wext |= STA_CFG80211_MASK | UAP_CFG80211_MASK; +#endif + +#if defined(UAP_CFG80211) && defined(UAP_SUPPORT) + if (IS_UAP_CFG80211(cfg80211_wext)) + cfg80211_wext |= STA_CFG80211_MASK | UAP_CFG80211_MASK; +#endif + handle->params.cfg80211_wext = cfg80211_wext; + moal_memcpy_ext(handle, handle->driver_version, driver_version, + strlen(driver_version), MLAN_MAX_VER_STR_LEN - 1); + + if (woal_update_drv_tbl(handle, handle->params.drv_mode) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not update driver mode table\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /** user config file */ + init_waitqueue_head(&handle->init_user_conf_wait_q); + + /* PnP and power profile */ + handle->surprise_removed = MFALSE; + init_waitqueue_head(&handle->init_wait_q); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + spin_lock_init(&handle->queue_lock); +#endif + spin_lock_init(&handle->driver_lock); + spin_lock_init(&handle->ioctl_lock); + spin_lock_init(&handle->scan_req_lock); + + handle->is_suspended = MFALSE; + handle->hs_activated = MFALSE; + handle->hs_auto_arp = MTRUE; + handle->suspend_fail = MFALSE; + handle->hs_skip_count = 0; + handle->hs_force_count = 0; +#ifdef SDIO + if (IS_SD(handle->card_type)) { +#ifdef SDIO_SUSPEND_RESUME + handle->suspend_notify_req = MFALSE; +#endif + handle->cmd52_func = 0; + handle->cmd52_reg = 0; + handle->cmd52_val = 0; + } +#endif + + if (handle->params.scan_chan_gap && + (handle->params.scan_chan_gap <= MRVDRV_MAX_SCAN_CHAN_GAP_TIME)) + handle->scan_chan_gap = handle->params.scan_chan_gap; + else + handle->scan_chan_gap = DEF_SCAN_CHAN_GAP; + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#ifdef WIFI_DIRECT_SUPPORT + handle->miracast_scan_time = DEF_MIRACAST_SCAN_TIME; +#define DEF_NOA_DURATION 0 +#define DEF_NOA_INTERVAL 100 + handle->noa_duration = DEF_NOA_DURATION; + handle->noa_interval = DEF_NOA_INTERVAL; +#endif +#endif + + if (IS_USB(handle->card_type)) + init_waitqueue_head(&handle->suspend_wait_q); + init_waitqueue_head(&handle->hs_activate_wait_q); + + /* Initialize measurement wait queue */ + handle->meas_wait_q_woken = MFALSE; + handle->meas_start_jiffies = 0; + handle->cac_period = MFALSE; + handle->delay_bss_start = MFALSE; + init_waitqueue_head(&handle->meas_wait_q); +#if defined(UAP_SUPPORT) + handle->chsw_wait_q_woken = MFALSE; + init_waitqueue_head(&handle->chsw_wait_q); +#endif + + handle->cac_period_jiffies = 0; + handle->usr_nop_period_sec = 0; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + memset(&handle->dfs_channel, 0, sizeof(struct cfg80211_chan_def)); + woal_initialize_timer(&handle->cac_timer, woal_cac_timer_func, handle); + handle->is_cac_timer_set = MFALSE; + handle->cac_bss_index = 0xff; +#endif +#endif + +#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 */ + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + /* Initialize the timer for GO timeout */ + woal_initialize_timer(&handle->go_timer, woal_go_timer_func, handle); + + handle->is_go_timer_set = MFALSE; +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + handle->remain_on_channel = MFALSE; + + /* Initialize the timer for remain on channel */ + woal_initialize_timer(&handle->remain_timer, woal_remain_timer_func, + handle); + + handle->is_remain_timer_set = MFALSE; +#endif +#endif + + /* Register to MLAN */ + memset(&device, 0, sizeof(mlan_device)); + device.pmoal_handle = handle; + device.card_type = handle->card_type; + device.card_rev = handle->card_rev; +#ifdef USB + if (IS_USB(handle->card_type)) { + struct usb_card_rec *cardp = handle->card; + device.tx_cmd_ep = cardp->tx_cmd_ep; + device.rx_cmd_ep = cardp->rx_cmd_ep; + device.tx_data_ep = cardp->tx_data_ep; + device.rx_data_ep = cardp->rx_data_ep; + } +#endif +#ifdef MFG_CMD_SUPPORT + device.mfg_mode = (t_u32)handle->params.mfg_mode; +#endif +#ifdef DEBUG_LEVEL1 + device.drvdbg = drvdbg; +#endif + device.fixed_beacon_buffer = + (t_u32)moal_extflg_isset(handle, EXT_FIX_BCN_BUF); + device.auto_ds = (t_u32)handle->params.auto_ds; + device.ps_mode = (t_u32)handle->params.ps_mode; + device.passive_to_active_scan = (t_u8)handle->params.p2a_scan; + device.max_tx_buf = (t_u32)handle->params.max_tx_buf; +#if defined(STA_SUPPORT) + device.cfg_11d = (t_u32)handle->params.cfg_11d; +#endif + device.indrstcfg = (t_u32)handle->params.indrstcfg; +#ifdef SDIO + if (IS_SD(handle->card_type)) { + device.sdio_rx_aggr_enable = + moal_extflg_isset(handle, EXT_SDIO_RX_AGGR); + device.int_mode = (t_u32)moal_extflg_isset(handle, EXT_INTMODE); + device.gpio_pin = (t_u32)handle->params.gpiopin; +#ifdef SDIO_MMC +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36) + device.max_segs = ((struct sdio_mmc_card *)handle->card) + ->func->card->host->max_segs; + device.max_seg_size = ((struct sdio_mmc_card *)handle->card) + ->func->card->host->max_seg_size; +#endif + PRINTM(MMSG, "SDIO: max_segs=%d max_seg_size=%d\n", + device.max_segs, device.max_seg_size); +#ifdef MMC_QUIRK_BLKSZ_FOR_BYTE_MODE + device.mpa_tx_cfg = MLAN_INIT_PARA_ENABLED; + device.mpa_rx_cfg = MLAN_INIT_PARA_ENABLED; +#else + device.mpa_tx_cfg = MLAN_INIT_PARA_DISABLED; + device.mpa_rx_cfg = MLAN_INIT_PARA_DISABLED; +#endif +#else + device.mpa_tx_cfg = MLAN_INIT_PARA_ENABLED; + device.mpa_rx_cfg = MLAN_INIT_PARA_ENABLED; +#endif /* SDIO_MMC */ + } +#endif /* SDIO */ + device.feature_control = handle->card_info->feature_control; + handle->feature_control = device.feature_control; + + if (handle->params.rx_work == MLAN_INIT_PARA_ENABLED) + device.rx_work = MTRUE; + else if (handle->params.rx_work == MLAN_INIT_PARA_DISABLED) + device.rx_work = MFALSE; + else { + if (num_possible_cpus() > 1) + device.rx_work = MTRUE; + else + device.rx_work = MFALSE; + } + PRINTM(MMSG, "rx_work=%d cpu_num=%d\n", device.rx_work, + num_possible_cpus()); + if (moal_extflg_isset(handle, EXT_NAPI)) + device.rx_work = MTRUE; + + device.dev_cap_mask = handle->params.dev_cap_mask; + + device.multi_dtim = handle->params.multi_dtim; + + device.inact_tmo = handle->params.inact_tmo; +#ifdef UAP_SUPPORT + device.uap_max_sta = handle->params.uap_max_sta; +#endif + device.hs_wake_interval = handle->params.hs_wake_interval; + device.indication_gpio = handle->params.indication_gpio; + device.hs_mimo_switch = moal_extflg_isset(handle, EXT_HS_MIMO_SWITCH); + + device.dfs53cfg = handle->params.dfs53cfg; + + for (i = 0; i < handle->drv_mode.intf_num; i++) { + device.bss_attr[i].bss_type = + handle->drv_mode.bss_attr[i].bss_type; + device.bss_attr[i].frame_type = + handle->drv_mode.bss_attr[i].frame_type; + device.bss_attr[i].active = handle->drv_mode.bss_attr[i].active; + device.bss_attr[i].bss_priority = + handle->drv_mode.bss_attr[i].bss_priority; + device.bss_attr[i].bss_num = + handle->drv_mode.bss_attr[i].bss_num; + device.bss_attr[i].bss_virtual = + handle->drv_mode.bss_attr[i].bss_virtual; + } + moal_memcpy_ext(handle, &device.callbacks, &woal_callbacks, + sizeof(mlan_callbacks), sizeof(mlan_callbacks)); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + device.fw_region = fw_region; +#endif +#endif + device.drv_mode = handle->params.drv_mode; + if (MLAN_STATUS_SUCCESS == mlan_register(&device, &pmlan)) + handle->pmlan_adapter = pmlan; + else + ret = MLAN_STATUS_FAILURE; + + LEAVE(); + return ret; +} + +/** + * @brief This function frees the structure of moal_handle + * + * @param handle A pointer to moal_handle structure + * + * @return N/A + */ +void woal_free_moal_handle(moal_handle *handle) +{ + moal_handle *ref_handle = NULL; + + ENTER(); + if (!handle) { + PRINTM(MERROR, "The handle is NULL\n"); + LEAVE(); + return; + } +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /* Unregister wiphy device and free */ + if (handle->wiphy) { + wiphy_unregister(handle->wiphy); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + woal_cfg80211_free_iftype_data(handle->wiphy); +#endif + wiphy_free(handle->wiphy); + handle->wiphy = NULL; + } +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) + if ((handle->nl_sk) && ((handle->nl_sk)->sk_socket)) { + sock_release((handle->nl_sk)->sk_socket); + handle->nl_sk = NULL; + } +#else + netlink_kernel_release(handle->nl_sk); +#endif + + if (handle->pmlan_adapter) { + mlan_unregister(handle->pmlan_adapter); + handle->pmlan_adapter = NULL; + } + + /* Free BSS attribute table */ + kfree(handle->drv_mode.bss_attr); + handle->drv_mode.bss_attr = NULL; + PRINTM(MINFO, "Free Adapter\n"); + if (atomic_read(&handle->lock_count) || + atomic_read(&handle->malloc_count) || + atomic_read(&handle->mbufalloc_count)) { + PRINTM(MERROR, + "mlan has memory leak: lock_count=%d, malloc_count=%d, mbufalloc_count=%d\n", + atomic_read(&handle->lock_count), + atomic_read(&handle->malloc_count), + atomic_read(&handle->mbufalloc_count)); + } +#ifdef PCIE + if (IS_PCIE(handle->card_type) && + atomic_read(&handle->malloc_cons_count)) { + PRINTM(MERROR, "mlan has memory leak: malloc_cons_count=%d\n", + atomic_read(&handle->malloc_cons_count)); + } +#endif + + /* Free allocated memory for fwdump filename */ + kfree(handle->fwdump_fname); + if (fwdump_fname) { + kfree(fwdump_fname); + fwdump_fname = NULL; + } + /* Free module params */ + woal_free_module_param(handle); + /** clear pref_mac to avoid later crash */ + if (handle->pref_mac) { + ref_handle = (moal_handle *)handle->pref_mac; + if (ref_handle->pref_mac && (ref_handle->pref_mac == handle)) + ref_handle->pref_mac = NULL; + } + /* Free the moal handle itself */ + kfree(handle); + LEAVE(); +} + +/** + * @brief WOAL get one line data from ASCII format data + * + * @param data Source data + * @param size Source data length + * @param line_pos Destination data + * @return routnine status + */ +static t_size parse_cfg_get_line(t_u8 *data, t_size size, t_u8 *line_pos) +{ + t_u8 *src, *dest; + static t_s32 pos; + + ENTER(); + + if (pos >= size) { /* reach the end */ + pos = 0; /* Reset position for rfkill */ + LEAVE(); + return -1; + } + memset(line_pos, 0, MAX_LINE_LEN); + src = data + pos; + dest = line_pos; + + while ((dest - line_pos < MAX_LINE_LEN - 1) && pos < size && + *src != '\x0A' && *src != '\0') { + if (*src != ' ' && *src != '\t') /* parse space */ + *dest++ = *src++; + else + src++; + pos++; + } + /* parse new line */ + pos++; + *dest = '\0'; + LEAVE(); + return strlen(line_pos); +} + +/** + * @brief Process register access request + * @param type_string String format Register type + * @param offset_string String format Register offset + * @param value_string String format Pointer to value + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_process_regrdwr(moal_handle *handle, t_u8 *type_string, + t_u8 *offset_string, t_u8 *value_string) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + int type, offset, value; + pmlan_ioctl_req ioctl_req = NULL; + mlan_ds_reg_mem *reg = NULL; + + ENTER(); + + /* Alloc ioctl_req */ + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + + if (ioctl_req == NULL) { + PRINTM(MERROR, "Can't alloc memory\n"); + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_atoi(&type, type_string)) + goto done; + if (MLAN_STATUS_SUCCESS != woal_atoi(&offset, offset_string)) + goto done; + if (MLAN_STATUS_SUCCESS != woal_atoi(&value, value_string)) + goto done; + + ioctl_req->req_id = MLAN_IOCTL_REG_MEM; + ioctl_req->action = MLAN_ACT_SET; + + reg = (mlan_ds_reg_mem *)ioctl_req->pbuf; + reg->sub_command = MLAN_OID_REG_RW; + if (type < 5) { + reg->param.reg_rw.type = type; + } else { + PRINTM(MERROR, "Unsupported Type\n"); + goto done; + } + reg->param.reg_rw.offset = offset; + reg->param.reg_rw.value = value; + + /* request ioctl for STA */ + ret = woal_request_ioctl(handle->priv[0], ioctl_req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + PRINTM(MINFO, "Register type: %d, offset: 0x%x, value: 0x%x\n", type, + offset, value); + ret = MLAN_STATUS_SUCCESS; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#if defined(SDIO) +/** + * @brief Read/Write registers value + * + * @param priv A pointer to moal_private structure + * @param action get / set action + * @param type type of register + * @param offset offset of register + * @param value value of registere + * + * @return 0 --success, otherwise fail + */ +static int woal_getset_regrdwr(moal_private *priv, t_u32 action, t_u32 type, + t_u32 offset, t_u32 *value) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_REG_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + req->action = action; + + reg_mem->param.reg_rw.type = type; + reg_mem->param.reg_rw.offset = offset; + if (req->action == MLAN_ACT_SET) + reg_mem->param.reg_rw.value = *value; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + *value = reg_mem->param.reg_rw.value; + PRINTM(MINFO, "woal_getset_regrdwr value=%x\n", *value); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set slew rate mode + * + * @param handle MOAL handle + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_set_sdio_slew_rate(moal_handle *handle) +{ + t_u32 value = 0; + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + t_u32 new_value = 0; + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) + return MLAN_STATUS_FAILURE; + + if ((handle->card_info->slew_rate_reg != 0) && + (handle->params.slew_rate > 3 || handle->params.slew_rate < 0)) + return MLAN_STATUS_FAILURE; + + ret = woal_getset_regrdwr(priv, MLAN_ACT_GET, MLAN_REG_MAC, + handle->card_info->slew_rate_reg, &value); + if (ret < 0) { + PRINTM(MERROR, "woal_getset_regrdwr get REG_MAC failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + new_value = value & ~(0x3 << handle->card_info->slew_rate_bit_offset); + new_value |= (t_u32)handle->params.slew_rate + << handle->card_info->slew_rate_bit_offset; + + if (value != new_value) { + PRINTM(MMSG, "Set REG 0x%8x: 0x%x slew_rate=%d\n", + handle->card_info->slew_rate_reg, new_value, + handle->params.slew_rate); + ret = woal_getset_regrdwr(priv, MLAN_ACT_SET, MLAN_REG_MAC, + handle->card_info->slew_rate_reg, + &new_value); + if (ret < 0) { + PRINTM(MERROR, + "woal_getset_regrdwr get REG_MAC failed\n"); + ret = MLAN_STATUS_FAILURE; + } + } +done: + return ret; +} +#endif /* SDIO */ + +#ifdef UAP_SUPPORT +/** + * @brief set uap operation contrl value + * + * @param handle MOAL handle + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_set_uap_operation_ctrl(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + int uap_oper_ctrl; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_UAP); + if (!priv) { + PRINTM(MERROR, + "woal_set_uap_operation_ctrl failed, no uap interface\n"); + LEAVE(); + return ret; + } + uap_oper_ctrl = handle->params.uap_oper_ctrl; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_OPER_CTRL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + bss->param.ap_oper_ctrl.ctrl_value = + (t_u16)((uap_oper_ctrl & 0xffff0000) >> 16); + bss->param.ap_oper_ctrl.chan_opt = (t_u16)(uap_oper_ctrl & 0xffff); + PRINTM(MMSG, "Uap oper_ctrl=0x%x chan_opt=0x%x\n", + bss->param.ap_oper_ctrl.ctrl_value, + bss->param.ap_oper_ctrl.chan_opt); + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} +#endif + +/** + * @brief WOAL parse ASCII format data to MAC address + * + * @param handle MOAL handle + * @param data Source data + * @param size data length + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_process_init_cfg(moal_handle *handle, t_u8 *data, t_size size) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *pos; + t_u8 *intf_s, *intf_e; + t_u8 s[MAX_LINE_LEN]; /* 1 line data */ + t_size line_len; + t_u8 index = 0; + t_u32 i; + t_u8 bss_mac_addr[MAX_MAC_ADDR_LEN]; + t_u8 bss_mac_name[MAX_PARAM_LEN]; + t_u8 type[MAX_PARAM_LEN]; + t_u8 offset[MAX_PARAM_LEN]; + t_u8 value[MAX_PARAM_LEN]; + + ENTER(); + + while ((line_len = parse_cfg_get_line(data, size, s)) != -1) { + pos = s; + while (*pos == ' ' || *pos == '\t') + pos++; + + if (*pos == '#' || (*pos == '\r' && *(pos + 1) == '\n') || + *pos == '\n' || *pos == '\0') + continue; /* Needn't process this line */ + + /* Process MAC addr */ + if (strncmp(pos, "mac_addr", 8) == 0) { + intf_s = strchr(pos, '='); + if (intf_s != NULL) + intf_e = strchr(intf_s, ':'); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + strncpy(bss_mac_addr, intf_e + 1, + MAX_MAC_ADDR_LEN - 1); + bss_mac_addr[MAX_MAC_ADDR_LEN - 1] = '\0'; + if ((intf_e - intf_s) > MAX_PARAM_LEN) { + PRINTM(MERROR, + "Too long interface name %d\n", + __LINE__); + goto done; + } + strncpy(bss_mac_name, intf_s + 1, + intf_e - intf_s - 1); + bss_mac_name[intf_e - intf_s - 1] = '\0'; + for (i = 0; i < handle->priv_num; i++) { + if (strcmp(bss_mac_name, + handle->priv[i] + ->netdev->name) == + 0) { + memset(handle->priv[i] + ->current_addr, + 0, ETH_ALEN); + PRINTM(MINFO, + "Interface name: %s mac: %s\n", + bss_mac_name, + bss_mac_addr); + woal_mac2u8( + handle->priv[i] + ->current_addr, + bss_mac_addr); +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (handle->priv[i]->bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) { + handle->priv[i] + ->current_addr[0] |= + 0x02; + PRINTM(MCMND, + "Set WFD device addr: " MACSTR + "\n", + MAC2STR(handle->priv[i] + ->current_addr)); + } +#endif +#endif +#endif + /* Set WLAN MAC addresses */ + if (MLAN_STATUS_SUCCESS != + woal_request_set_mac_address( + handle->priv[i], + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, + "Set MAC address failed\n"); + goto done; + } + moal_memcpy_ext( + handle, + handle->priv[i] + ->netdev + ->dev_addr, + handle->priv[i] + ->current_addr, + ETH_ALEN, ETH_ALEN); + index++; /* Mark found one + interface matching + */ + } + } + } else { + PRINTM(MERROR, "Wrong config file format %d\n", + __LINE__); + goto done; + } + } + /* Process REG value */ + else if (strncmp(pos, "wlan_reg", 8) == 0) { + intf_s = strchr(pos, '='); + if (intf_s != NULL) + intf_e = strchr(intf_s, ','); + else + intf_e = NULL; + if (intf_s != NULL && intf_e != NULL) { + /* Copy type */ + strncpy(type, intf_s + 1, 1); + type[1] = '\0'; + } else { + PRINTM(MERROR, "Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + intf_e = strchr(intf_s, ','); + if (intf_e != NULL) { + if ((intf_e - intf_s) >= MAX_PARAM_LEN) { + PRINTM(MERROR, + "Regsier offset is too long %d\n", + __LINE__); + goto done; + } + /* Copy offset */ + strncpy(offset, intf_s, intf_e - intf_s); + offset[intf_e - intf_s] = '\0'; + } else { + PRINTM(MERROR, "Wrong config file format %d\n", + __LINE__); + goto done; + } + intf_s = intf_e + 1; + if ((strlen(intf_s) >= MAX_PARAM_LEN)) { + PRINTM(MERROR, "Regsier value is too long %d\n", + __LINE__); + goto done; + } + /* Copy value */ + strncpy(value, intf_s, + MIN((MAX_PARAM_LEN - 1), strlen(intf_s))); + + if (MLAN_STATUS_SUCCESS != + woal_process_regrdwr(handle, type, offset, value)) { + PRINTM(MERROR, "Access Reg failed\n"); + goto done; + } + PRINTM(MINFO, "Reg type: %s, offset: %s, value: %s\n", + type, offset, value); + } + } + + if (index == 0) + PRINTM(MINFO, "Can't find any matching MAC Address"); + ret = MLAN_STATUS_SUCCESS; + +done: + LEAVE(); + return ret; +} + +/** + * @brief WOAL parse ASCII format raw data to hex format + * + * @param handle MOAL handle + * @param data Source data + * @param size data length + * @param wait_option wait option + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_process_hostcmd_cfg(moal_handle *handle, t_u8 *data, + t_size size, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 *pos = data; + t_u8 *intf_s, *intf_e; + t_u8 *buf = NULL; + t_u8 *ptr = NULL; + t_u32 cmd_len = 0; + t_u8 start_raw = MFALSE; + gfp_t flag; + +#define CMD_STR "MRVL_CMDhostcmd" +#define CMD_BUF_LEN 2048 + + ENTER(); + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc(CMD_BUF_LEN, flag); + if (!buf) { + PRINTM(MERROR, "Could not allocate buffer space!\n"); + goto done; + } + ptr = buf; + strcpy(ptr, CMD_STR); + ptr = buf + strlen(CMD_STR) + sizeof(t_u32); + while ((pos - data) < size) { + while (*pos == ' ' || *pos == '\t') + pos++; + if (*pos == '#') { /* Line comment */ + while (*pos != '\n') + pos++; + pos++; + } + if ((*pos == '\r' && *(pos + 1) == '\n') || *pos == '\n' || + *pos == '\0') { + pos++; + continue; /* Needn't process this line */ + } + + if (*pos == '}') { + cmd_len = *((t_u16 *)(buf + strlen(CMD_STR) + + sizeof(t_u32) + sizeof(t_u16))); + moal_memcpy_ext(handle, buf + strlen(CMD_STR), &cmd_len, + sizeof(t_u32), + CMD_BUF_LEN - strlen(CMD_STR)); + + /* fire the hostcommand from here */ + woal_priv_hostcmd(handle->priv[0], buf, CMD_BUF_LEN, + wait_option); + memset(buf + strlen(CMD_STR), 0, + CMD_BUF_LEN - strlen(CMD_STR)); + ptr = buf + strlen(CMD_STR) + sizeof(t_u32); + start_raw = MFALSE; + pos++; + continue; + } + + if (start_raw == MFALSE) { + intf_s = strchr(pos, '='); + if (intf_s) + intf_e = strchr(intf_s, '{'); + else + intf_e = NULL; + + if (intf_s && intf_e) { + start_raw = MTRUE; + pos = intf_e + 1; + continue; + } + } + + if (start_raw) { + /* Raw data block exists */ + while (*pos != '\n') { + if ((*pos <= 'f' && *pos >= 'a') || + (*pos <= 'F' && *pos >= 'A') || + (*pos <= '9' && *pos >= '0')) { + *ptr++ = woal_atox(pos); + pos += 2; + } else + pos++; + } + } + } + +done: + kfree(buf); + LEAVE(); + return ret; +} +#define INIT_CFG_DATA 0x00 +#define INIT_HOSTCMD_CFG_DATA 0x02 +#define COUNTRY_POWER_TABLE 0x04 +#define BAND_STEER_CFG_DATA 0x08 + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +void woal_request_init_user_conf_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware) + handle->user_data = firmware; + else + PRINTM(MERROR, "User init config request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void woal_request_init_dpd_conf_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->dpd_data = firmware; + else + PRINTM(MERROR, "User init cfg data request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void woal_request_vdll_fw_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->firmware = firmware; + else + PRINTM(MERROR, "VDLL: Request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request firmware image for VDLL + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_vdll_req_fw(moal_handle *handle) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *vdll_fw = handle->drv_mode.fw_name; + + ENTER(); + if (vdll_fw) { + PRINTM(MMSG, "VDLL: Request firmware: %s\n", vdll_fw); + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, vdll_fw, + handle->hotplug_device, GFP_KERNEL, handle, + woal_request_vdll_fw_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, vdll_fw, + handle->hotplug_device, handle, + woal_request_vdll_fw_callback)) < 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, vdll_fw, + handle->hotplug_device, handle, + woal_request_vdll_fw_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "VDLL: request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->firmware, vdll_fw, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "VDLL: request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void +woal_request_init_txpwr_conf_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->txpwr_data = firmware; + else + PRINTM(MERROR, "User init cfg data request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief Request init conf firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void woal_request_init_cfg_data_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + if (!handle) { + LEAVE(); + return; + } + if (firmware && handle) + handle->init_cfg_data = firmware; + else + PRINTM(MERROR, "User init cfg data request firmware failed\n"); + + handle->init_user_conf_wait_flag = MTRUE; + wake_up_interruptible(&handle->init_user_conf_wait_q); + + LEAVE(); + return; +} + +/** + * @brief WOAL set user defined init data and param + * + * @param handle MOAL handle structure + * @param type type argument + * @param wait_option wait option + * @param country_txpwrlimit Configure Tx Power Limit + * @return MLAN_STATUS_SUCCESS--success, otherwise--fail + */ +static t_u32 woal_set_user_init_data(moal_handle *handle, int type, + t_u8 wait_option, char *country_txpwrlimit) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *cfg_data = NULL; + t_size len; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *init_cfg = handle->params.init_cfg; + char *init_hostcmd_cfg = handle->params.init_hostcmd_cfg; + char *band_steer_cfg = handle->params.band_steer_cfg; + + ENTER(); + + if (type == INIT_CFG_DATA) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, init_cfg, + handle->hotplug_device, GFP_KERNEL, handle, + woal_request_init_cfg_data_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, init_cfg, + handle->hotplug_device, handle, + woal_request_init_cfg_data_callback)) < 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, init_cfg, + handle->hotplug_device, handle, + woal_request_init_cfg_data_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "Init config file request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->init_cfg_data, init_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Init config file request_firmware() failed\n"); + goto done; + } + } + } else if (type == COUNTRY_POWER_TABLE) { + if (country_txpwrlimit == NULL) { + PRINTM(MERROR, + "The parameter 'country_txpwrlimit' is NULL\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + /* 'country_txpwrlimit' holds the value of Configured Tx Power + * Limit */ + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + country_txpwrlimit, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + country_txpwrlimit, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, country_txpwrlimit, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "country txpwrlimit config file request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + int status = request_firmware(&handle->user_data, + country_txpwrlimit, + handle->hotplug_device); + /* File does not exist, skip download */ + if (status == -ENOENT) { + ret = MLAN_STATUS_FILE_ERR; + PRINTM(MIOCTL, + "Country power table file does not exist\n"); + goto done; + } else if (status) { + PRINTM(MERROR, + "country txpwrlimit config file request_firmware() failed\n"); + goto done; + } + } + } else if (type == INIT_HOSTCMD_CFG_DATA) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + init_hostcmd_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + init_hostcmd_cfg, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, init_hostcmd_cfg, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "Init hostcmd config file request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->user_data, + init_hostcmd_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Init hostcmd config file request_firmware() failed\n"); + goto done; + } + } + } + if (type == BAND_STEER_CFG_DATA) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + band_steer_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + band_steer_cfg, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, band_steer_cfg, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "band_steer_cfg request_firmware_nowait() failed\n"); + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->user_data, + band_steer_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "band_steer_cfg file request_firmware() failed\n"); + goto done; + } + } + } + if (handle->user_data) { + cfg_data = (t_u8 *)(handle->user_data)->data; + len = (handle->user_data)->size; + if (type == INIT_HOSTCMD_CFG_DATA || + type == BAND_STEER_CFG_DATA || + type == COUNTRY_POWER_TABLE) { + if (MLAN_STATUS_SUCCESS != + woal_process_hostcmd_cfg(handle, cfg_data, len, + wait_option)) { + PRINTM(MERROR, + "Can't process hostcmd config file\n"); + goto done; + } + } + ret = MLAN_STATUS_SUCCESS; + } else if (type == INIT_CFG_DATA && handle->init_cfg_data) { + PRINTM(MIOCTL, "Load init_cfg success\n"); + ret = MLAN_STATUS_SUCCESS; + } +done: + if (handle->user_data) { + release_firmware(handle->user_data); + handle->user_data = NULL; + } + + LEAVE(); + return ret; +} + +/** + * @brief Configure aggrctrl + * + * @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_init_aggr_ctrl(moal_handle *handle, t_u8 wait_option) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + mlan_status status; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; + pcfg_misc->sub_command = MLAN_OID_MISC_AGGR_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + req->action = MLAN_ACT_SET; + pcfg_misc->param.aggr_params.tx.enable = MTRUE; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Add interfaces DPC + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_add_card_dpc(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int i; + char str_buf[MLAN_MAX_VER_STR_LEN]; + ENTER(); + +#if defined(USB) + if (IS_USB(handle->card_type) && handle->boot_state == USB_FW_DNLD) { + /* Return now */ + LEAVE(); + return ret; + } +#endif /* USB_NEW_FW_DNLD */ + +#ifdef CONFIG_PROC_FS + /* Initialize proc fs */ + woal_proc_init(handle); +#endif /* CONFIG_PROC_FS */ + + /* Add interfaces */ + for (i = 0; i < handle->drv_mode.intf_num; i++) { + if (handle->drv_mode.bss_attr[i].bss_virtual) + continue; + if (!woal_add_interface(handle, handle->priv_num, + handle->drv_mode.bss_attr[i].bss_type)) { + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + woal_get_version(handle, str_buf, sizeof(str_buf) - 1); + PRINTM(MMSG, "wlan: version = %s\n", str_buf); + +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode == MLAN_INIT_PARA_ENABLED) + goto done; +#endif + + if (handle->params.init_cfg && handle->init_cfg_data) { + if (MLAN_STATUS_SUCCESS != + woal_process_init_cfg(handle, + (t_u8 *)(handle->init_cfg_data)->data, + (handle->init_cfg_data)->size)) { + PRINTM(MERROR, "Can't process init config file\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + + if (moal_extflg_isset(handle, EXT_AGGR_CTRL)) { + /* Enable aggregation in FW */ + if (woal_init_aggr_ctrl(handle, MOAL_IOCTL_WAIT)) { + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + +#ifdef USB + if (handle->params.usb_aggr == 1) { + /* Enable USB aggregation in FW */ + if (woal_usb_aggr_init(handle)) { + ret = MLAN_STATUS_FAILURE; + goto err; + } + } +#endif + /* Add low power mode check */ + if (moal_extflg_isset(handle, EXT_LOW_PW_MODE) && + handle->card_info->low_power_enable && + woal_set_low_pwr_mode(handle, MOAL_IOCTL_WAIT)) { + /* Proceed with Warning */ + PRINTM(MERROR, "Unable to set Low Power Mode\n"); + } + +#if defined(SDIO) + if (IS_SD(handle->card_type)) + woal_set_sdio_slew_rate(handle); +#endif + + if (moal_extflg_isset(handle, EXT_PMIC) && handle->card_info->pmic) { + if (MLAN_STATUS_SUCCESS != + woal_pmic_configure(handle, MOAL_IOCTL_WAIT)) { + PRINTM(MFATAL, "Failed to configure PMIC\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + + if (handle->params.antcfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_antcfg(handle, MOAL_IOCTL_WAIT)) { + PRINTM(MFATAL, "Set user antcfg data failed\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + } + +#ifdef UAP_SUPPORT + if (handle->params.uap_oper_ctrl) + woal_set_uap_operation_ctrl(handle); +#endif + +#ifdef MFG_CMD_SUPPORT +done: +#endif +err: + if (handle->params.init_cfg && handle->init_cfg_data) { + release_firmware(handle->init_cfg_data); + handle->init_cfg_data = NULL; + } + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to add interface\n"); + + for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++) + woal_remove_interface(handle, i); + handle->priv_num = 0; +#ifdef CONFIG_PROC_FS + woal_proc_exit(handle); +#endif + } + LEAVE(); + return ret; +} + +/** + * @brief Request dpd data + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_req_dpd_data(moal_handle *handle, + mlan_init_param *param) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *dpd_data_cfg = handle->params.dpd_data_cfg; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (dpd_data_cfg && strncmp(dpd_data_cfg, "none", strlen("none"))) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + dpd_data_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_dpd_conf_callback)) < 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + dpd_data_cfg, handle->hotplug_device, + handle, + woal_request_init_dpd_conf_callback)) < 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, dpd_data_cfg, + handle->hotplug_device, handle, + woal_request_init_dpd_conf_callback)) < 0) { +#endif +#endif + PRINTM(MERROR, + "DPD data request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + status = request_firmware(&handle->dpd_data, + dpd_data_cfg, + handle->hotplug_device); + if (status < 0 && status != -ENOENT) { + PRINTM(MERROR, + "DPD data request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (handle->dpd_data) { + param->pdpd_data_buf = (t_u8 *)handle->dpd_data->data; + param->dpd_data_len = handle->dpd_data->size; + } else { + param->dpd_data_len = UNKNOW_DPD_LENGTH; + } + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Request TX Power data + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_req_txpwr_data(moal_handle *handle, + mlan_init_param *param) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *txpwrlimit_cfg = handle->params.txpwrlimit_cfg; + + ENTER(); + + if (txpwrlimit_cfg && strncmp(txpwrlimit_cfg, "none", strlen("none"))) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + txpwrlimit_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_txpwr_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + txpwrlimit_cfg, handle->hotplug_device, + handle, + woal_request_init_txpwr_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, txpwrlimit_cfg, + handle->hotplug_device, handle, + woal_request_init_txpwr_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "Region txpwrlimit cfg data " + "request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->txpwr_data, + txpwrlimit_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, "Region txpwrlimit cfg data " + "request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + if (handle->txpwr_data) { + param->ptxpwr_data_buf = + (t_u8 *)handle->txpwr_data->data; + param->txpwr_data_len = handle->txpwr_data->size; + } + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Request Cal data + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_req_cal_data(moal_handle *handle, + mlan_init_param *param) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + char *cal_data_cfg = handle->params.cal_data_cfg; + + ENTER(); + /** Cal data request */ + if (cal_data_cfg && strncmp(cal_data_cfg, "none", strlen("none"))) { + if (req_fw_nowait) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + cal_data_cfg, handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_init_user_conf_callback)) < + 0) { +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + if ((request_firmware_nowait( + THIS_MODULE, FW_ACTION_HOTPLUG, + cal_data_cfg, handle->hotplug_device, + handle, + woal_request_init_user_conf_callback)) < + 0) { +#else + if ((request_firmware_nowait( + THIS_MODULE, cal_data_cfg, + handle->hotplug_device, handle, + woal_request_init_user_conf_callback)) < + 0) { +#endif +#endif + PRINTM(MERROR, + "Cal data request_firmware_nowait() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + handle->init_user_conf_wait_flag = MFALSE; + wait_event_interruptible( + handle->init_user_conf_wait_q, + handle->init_user_conf_wait_flag); + } else { + if ((request_firmware(&handle->user_data, cal_data_cfg, + handle->hotplug_device)) < 0) { + PRINTM(MERROR, + "Cal data request_firmware() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + } else if (!cal_data_cfg && handle->card_info->cal_data_cfg) { + PRINTM(MERROR, + "Please add cal_data_cfg for 8887/8977/8997/8987/8978\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; + } +done: + return ret; +} + +#if defined(USB) +/** + * @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_reset_usb_dev(moal_handle *handle) +{ + int ret = MLAN_STATUS_SUCCESS; + struct usb_device *udev = ((struct usb_card_rec *)(handle->card))->udev; + int usbRstDev_ret = 0; + + ENTER(); + + /* Return now */ + PRINTM(MMSG, "WLAN FW is downloaded\n"); + + /* Reset USB device to get re-enumeration */ + if (udev) { +#define USB_WAIT_FW_READY (500) + mdelay(USB_WAIT_FW_READY); + usbRstDev_ret = usb_reset_device(udev); + if ((usbRstDev_ret == 0) || + (usbRstDev_ret == -ENODEV) || /* expected + since + chip + re-enumerates + */ + (usbRstDev_ret == -EINVAL)) { /* expected if USB FW detaches + first */ + PRINTM(MMSG, "usb_reset_device() successful.\n"); + } else { + PRINTM(MERROR, + "usb_reset_device() failed with error code =%d\n", + usbRstDev_ret); + ret = MLAN_STATUS_FAILURE; + } + } else { + PRINTM(MERROR, "ERR: No handle to call usb_reset_device()!\n"); + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} +#endif + +/** + * @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; + if (handle->params.fw_reload == FW_RELOAD_SDIO_INBAND_RESET) + fw.fw_reload = handle->params.fw_reload; + else + fw.fw_reload = 0; + wifi_status = 0; + ret = mlan_dnld_fw(handle->pmlan_adapter, &fw); + if (ret == MLAN_STATUS_FAILURE) { + wifi_status = 1; + PRINTM(MERROR, + "WLAN: Fail download FW with nowwait: %u\n", + moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT)); + if (handle->ops.reg_dbg) + handle->ops.reg_dbg(handle); + goto done; + } + wifi_status = 0; + +#if defined(USB) + if (handle->boot_state == USB_FW_DNLD) { + if (!IS_USB8997(handle->card_type) && + !IS_USB9098(handle->card_type) && + !IS_USB9097(handle->card_type) && + !IS_USB8978(handle->card_type)) + ret = woal_reset_usb_dev(handle); + goto done; + } +#endif /* USB_NEW_FW_DNLD */ + PRINTM(MMSG, "WLAN FW is active\n"); + handle->driver_status = MFALSE; + } + + moal_get_boot_ktime(handle, &handle->on_time); + PRINTM(MMSG, "on_time is %llu\n", handle->on_time); + + /** data request */ + memset(¶m, 0, sizeof(mlan_init_param)); + + ret = woal_req_dpd_data(handle, ¶m); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + ret = woal_req_txpwr_data(handle, ¶m); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + ret = woal_req_cal_data(handle, ¶m); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + handle->hardware_status = HardwareStatusFwReady; +#ifdef USB + if (IS_USB(handle->card_type) && !handle->fw_reload) { + if (handle->skip_fw_dnld != MTRUE) { + ret = woal_usb_rx_init(handle); + if (ret == MLAN_STATUS_SUCCESS) + ret = woal_usb_tx_init(handle); + } + if (ret != MLAN_STATUS_SUCCESS) + goto done; + } +#endif /* USB */ + if (handle->fw_reload) + goto done; + handle->init_wait_q_woken = MFALSE; + + ret = mlan_set_init_param(handle->pmlan_adapter, ¶m); + ret = mlan_init_fw(handle->pmlan_adapter); + if (ret == MLAN_STATUS_FAILURE) { + wifi_status = 2; + goto done; + } else if (ret == MLAN_STATUS_SUCCESS) { + handle->hardware_status = HardwareStatusReady; + goto done; + } + /* Wait for mlan_init to complete */ + wait_event_timeout(handle->init_wait_q, handle->init_wait_q_woken, + 5 * HZ); + 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->dpd_data) { + release_firmware(handle->dpd_data); + handle->dpd_data = NULL; + } + if (handle->txpwr_data) { + release_firmware(handle->txpwr_data); + handle->txpwr_data = NULL; + } + if (handle->user_data) { + release_firmware(handle->user_data); + handle->user_data = NULL; + } + + LEAVE(); + return ret; +} + +/** + * @brief Request firmware DPC + * + * @param handle A pointer to moal_handle structure + * @param firmware A pointer to firmware image + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_request_fw_dpc(moal_handle *handle, + const struct firmware *firmware) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + wifi_timeval tstamp; + ENTER(); + + if (!firmware) { + woal_get_monotonic_time(&tstamp); + if (tstamp.time_sec > + (handle->req_fw_time.time_sec + REQUEST_FW_TIMEOUT)) { + PRINTM(MERROR, + "No firmware image found. Skipping download\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MERROR, + "request_firmware_nowait failed for %s. Retrying..\n", + handle->drv_mode.fw_name); + woal_sched_timeout(MOAL_TIMER_1S); + woal_request_fw(handle); + LEAVE(); + return ret; + } + handle->firmware = firmware; + + ret = woal_init_fw_dpc(handle); + if (ret) + goto done; + ret = woal_add_card_dpc(handle); + if (ret) + goto done; + +done: + /* We should hold the semaphore until callback finishes execution */ + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); + LEAVE(); + return ret; +} + +/** + * @brief Request firmware callback + * This function is invoked by request_firmware_nowait system call + * + * @param firmware A pointer to firmware image + * @param context A pointer to moal_handle structure + * + * @return N/A + */ +static void woal_request_fw_callback(const struct firmware *firmware, + void *context) +{ + moal_handle *handle; + + ENTER(); + + handle = (moal_handle *)context; + handle->firmware = firmware; + woal_request_fw_dpc((moal_handle *)context, firmware); + if (firmware) { + release_firmware(firmware); + handle->firmware = NULL; + } + 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) +{ + int err; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 req_fw_nowait = moal_extflg_isset(handle, EXT_REQ_FW_NOWAIT); + + ENTER(); + + PRINTM(MMSG, "Request firmware: %s\n", handle->drv_mode.fw_name); + + if (req_fw_nowait && !handle->fw_reload) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + handle->drv_mode.fw_name, + handle->hotplug_device, + GFP_KERNEL, handle, + woal_request_fw_callback); +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 13) + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + handle->drv_mode.fw_name, + handle->hotplug_device, handle, + woal_request_fw_callback); +#else + err = request_firmware_nowait(THIS_MODULE, + handle->drv_mode.fw_name, + handle->hotplug_device, handle, + woal_request_fw_callback); +#endif +#endif + if (err < 0) { + PRINTM(MFATAL, + "WLAN: request_firmware_nowait() failed, error code = %d\n", + err); + ret = MLAN_STATUS_FAILURE; + } + } else { + err = request_firmware(&handle->firmware, + handle->drv_mode.fw_name, + handle->hotplug_device); + if (err < 0) { + PRINTM(MFATAL, + "WLAN: request_firmware() failed, error code = %d\n", + err); + ret = MLAN_STATUS_FAILURE; + } else { + if (handle->fw_reload) + ret = woal_init_fw_dpc(handle); + else + ret = woal_request_fw_dpc(handle, + handle->firmware); + release_firmware(handle->firmware); + handle->firmware = NULL; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This function initializes firmware + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_init_fw(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + +#ifdef USB + if (IS_USB(handle->card_type)) { + if (MFALSE || (handle->skip_fw_dnld == MTRUE) +#if defined(USB) + || (handle->boot_state == USB_FW_READY) +#endif /* USB_NEW_FW_DNLD */ + ) { + PRINTM(MMSG, "WLAN FW is active\n"); + /* Set it to NULL to skip downloading firmware to card + */ + handle->firmware = NULL; + ret = woal_init_fw_dpc(handle); + if (ret) + goto done; + ret = woal_add_card_dpc(handle); + if (ret) + goto done; + /* Release semaphore if download is not required */ + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); + done: + LEAVE(); + return ret; + } + } +#endif /* USB */ + + woal_get_monotonic_time(&handle->req_fw_time); + ret = woal_request_fw(handle); + if (ret == MLAN_STATUS_FAILURE) { + PRINTM(MFATAL, "woal_request_fw failed\n"); + } + + 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 + */ +void woal_fill_mlan_buffer(moal_private *priv, mlan_buffer *pmbuf, + struct sk_buff *skb) +{ + wifi_timeval tstamp; + struct ethhdr *eth; + t_u8 tid = 0; + dot11_txcontrol *txcontrol; + t_u8 tx_ctrl_flag = MFALSE; + int i = 0; + ENTER(); + /* + * skb->priority values from 256->263 are magic values to + * directly indicate a specific 802.1d priority. This is used + * to allow 802.1d priority to be passed directly in from VLAN + * tags, etc. + */ + if (IS_SKB_MAGIC_VLAN(skb)) { + tid = GET_VLAN_PRIO(skb); + } else { + eth = (struct ethhdr *)skb->data; + + switch (eth->h_proto) { + case __constant_htons(ETH_P_IP): + tid = priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET]; + if (tid == 0xFF) + tid = (IPTOS_PREC(SKB_TOS(skb)) >> + IPTOS_OFFSET); + PRINTM(MDAT_D, + "packet type ETH_P_IP: dscp[%x], map[%x], tid=%d\n", + SKB_TOS(skb) >> DSCP_OFFSET, + priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET], + tid); + break; + case __constant_htons(ETH_P_IPV6): + tid = SKB_TIDV6(skb); + PRINTM(MDAT_D, + "packet type ETH_P_IPV6: %04x, tid=%#x prio=%#x\n", + eth->h_proto, tid, skb->priority); + break; + case __constant_htons(ETH_P_ARP): + tid = 0; + PRINTM(MDATA, "ARP packet %04x\n", eth->h_proto); + break; + default: + tid = 0; + if (priv->tx_protocols.protocol_num) { + for (i = 0; i < priv->tx_protocols.protocol_num; + i++) { + if (eth->h_proto == + __constant_htons( + priv->tx_protocols + .protocols[i])) + tx_ctrl_flag = MTRUE; + } + } + if (tx_ctrl_flag) { + txcontrol = (dot11_txcontrol + *)(skb->data + + sizeof(struct ethhdr)); + pmbuf->u.tx_info.data_rate = + txcontrol->datarate; + pmbuf->u.tx_info.channel = txcontrol->channel; + pmbuf->u.tx_info.bw = txcontrol->bw; + pmbuf->u.tx_info.tx_power.val = + txcontrol->power; + pmbuf->u.tx_info.retry_limit = + txcontrol->retry_limit; + tid = txcontrol->priority; + memmove(skb->data + sizeof(dot11_txcontrol), + skb->data, sizeof(struct ethhdr)); + skb_pull(skb, sizeof(dot11_txcontrol)); + pmbuf->flags |= MLAN_BUF_FLAG_TX_CTRL; + } + break; + } + } + + skb->priority = tid; + + /* Record the current time the packet was queued; used to determine + * the amount of time the packet was queued in the driver before it + * was sent to the firmware. The delay is then sent along with the + * packet to the firmware for aggregate delay calculation for stats + * and MSDU lifetime expiry. + */ + woal_get_monotonic_time(&tstamp); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) + skb->tstamp = ktime_get_raw(); +#else + skb->tstamp = timeval_to_ktime(tstamp); +#endif +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 14) + skb_set_timestamp(skb, &tstamp); +#else + moal_memcpy_ext(priv->phandle, &skb->stamp, &tstamp, sizeof(skb->stamp), + sizeof(skb->stamp)); +#endif + + pmbuf->pdesc = skb; + pmbuf->pbuf = skb->head + sizeof(mlan_buffer); +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + pmbuf->buf_pa = 0; + } +#endif + 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 = tstamp.time_sec; + pmbuf->in_ts_usec = tstamp.time_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 + .ndo_select_queue = woal_select_queue, + .ndo_validate_addr = eth_validate_addr, +}; +#endif + +/** + * @brief This function initializes the private structure + * and dev structure for station mode + * + * @param dev A pointer to net_device structure + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status woal_init_sta_dev(struct net_device *dev, moal_private *priv) +{ + ENTER(); + + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->open = woal_open; + dev->hard_start_xmit = woal_hard_start_xmit; + dev->stop = woal_close; + dev->do_ioctl = woal_do_ioctl; + dev->set_mac_address = woal_set_mac_address; + dev->tx_timeout = woal_tx_timeout; + dev->get_stats = woal_get_stats; + dev->set_multicast_list = woal_set_multicast_list; +#else + dev->netdev_ops = &woal_netdev_ops; +#endif + dev->watchdog_timeo = MRVDRV_DEFAULT_WATCHDOG_TIMEOUT; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + dev->needed_headroom += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#else + dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#endif +#ifdef STA_WEXT + if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = woal_get_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def *)&woal_handler_def; + } +#endif + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) + init_waitqueue_head(&priv->ft_wait_q); +#endif + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif /* STA_SUPPORT */ + +#ifdef UAP_SUPPORT +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +/** Network device handlers */ +const struct net_device_ops woal_uap_netdev_ops = { + .ndo_open = woal_open, + .ndo_start_xmit = woal_hard_start_xmit, + .ndo_stop = woal_close, + .ndo_do_ioctl = woal_uap_do_ioctl, + .ndo_set_mac_address = woal_set_mac_address, + .ndo_tx_timeout = woal_tx_timeout, + .ndo_get_stats = woal_get_stats, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .ndo_set_rx_mode = woal_uap_set_multicast_list, +#else + .ndo_set_multicast_list = woal_uap_set_multicast_list, +#endif + .ndo_select_queue = woal_select_queue, + .ndo_validate_addr = eth_validate_addr, +}; +#endif + +/** + * @brief This function initializes the private structure + * and dev structure for uap mode + * + * @param dev A pointer to net_device structure + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status woal_init_uap_dev(struct net_device *dev, moal_private *priv) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Setup the OS Interface to our functions */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + dev->open = woal_open; + dev->hard_start_xmit = woal_hard_start_xmit; + dev->stop = woal_close; + dev->set_mac_address = woal_set_mac_address; + dev->tx_timeout = woal_tx_timeout; + dev->get_stats = woal_get_stats; + dev->do_ioctl = woal_uap_do_ioctl; + dev->set_multicast_list = woal_uap_set_multicast_list; +#else + dev->netdev_ops = &woal_uap_netdev_ops; +#endif + dev->watchdog_timeo = MRVDRV_DEFAULT_UAP_WATCHDOG_TIMEOUT; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + dev->needed_headroom += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#else + dev->hard_header_len += MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len; +#endif +#ifdef UAP_WEXT + if (IS_UAP_WEXT(priv->phandle->params.cfg80211_wext)) { +#if WIRELESS_EXT < 21 + dev->get_wireless_stats = woal_get_uap_wireless_stats; +#endif + dev->wireless_handlers = + (struct iw_handler_def *)&woal_uap_handler_def; + } +#endif /* UAP_WEXT */ + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + + LEAVE(); + return status; +} +#endif /* UAP_SUPPORT */ + +/** + * @brief This function adds a new interface. It will + * allocate, initialize and register the device. + * + * @param handle A pointer to moal_handle structure + * @param bss_index BSS index number + * @param bss_type BSS type + * + * @return A pointer to the new priv structure + */ +moal_private *woal_add_interface(moal_handle *handle, t_u8 bss_index, + t_u8 bss_type) +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + char name[256]; + int i = 0; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + char csa_str[256]; +#endif +#endif + ENTER(); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#define MAX_WMM_QUEUE 4 + /* Allocate an Ethernet device */ + dev = alloc_etherdev_mq(sizeof(moal_private), MAX_WMM_QUEUE); +#else + dev = alloc_etherdev(sizeof(moal_private)); +#endif + if (!dev) { + PRINTM(MFATAL, "Init virtual ethernet device failed\n"); + goto error; + } + /* Allocate device name */ +#ifdef STA_SUPPORT + memset(name, 0, sizeof(name)); + if (handle->params.sta_name) + snprintf(name, sizeof(name), "%s%%d", handle->params.sta_name); + else if (handle->second_mac) + snprintf(name, sizeof(name), "m%s", default_mlan_name); + else + snprintf(name, sizeof(name), "%s", default_mlan_name); + + if ((bss_type == MLAN_BSS_TYPE_STA) && + (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate mlan device name\n"); + goto error; + } +#endif +#ifdef UAP_SUPPORT + memset(name, 0, sizeof(name)); + if (handle->params.uap_name) + snprintf(name, sizeof(name), "%s%%d", handle->params.uap_name); + else if (handle->second_mac) + snprintf(name, sizeof(name), "m%s", default_uap_name); + else + snprintf(name, sizeof(name), "%s", default_uap_name); + if ((bss_type == MLAN_BSS_TYPE_UAP) && + (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate uap device name\n"); + goto error; + } +#endif +#ifdef WIFI_DIRECT_SUPPORT + memset(name, 0, sizeof(name)); + if (handle->params.wfd_name) + snprintf(name, sizeof(name), "%s%%d", handle->params.wfd_name); + else if (handle->second_mac) + snprintf(name, sizeof(name), "m%s", default_wfd_name); + else + snprintf(name, sizeof(name), "%s", default_wfd_name); + if ((bss_type == MLAN_BSS_TYPE_WIFIDIRECT) && + (dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate wifidirect device name\n"); + goto error; + } +#endif + priv = (moal_private *)netdev_priv(dev); + /* Save the priv to handle */ + handle->priv[bss_index] = priv; + + /* Use the same handle structure */ + priv->phandle = handle; + priv->netdev = dev; + priv->bss_index = bss_index; + priv->bss_type = bss_type; + priv->extra_tx_head_len = 0; + if (bss_type == MLAN_BSS_TYPE_STA) + priv->bss_role = MLAN_BSS_ROLE_STA; + else if (bss_type == MLAN_BSS_TYPE_UAP) + priv->bss_role = MLAN_BSS_ROLE_UAP; +#ifdef WIFI_DIRECT_SUPPORT + else if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + priv->bss_role = MLAN_BSS_ROLE_STA; +#endif + + INIT_LIST_HEAD(&priv->tcp_sess_queue); + spin_lock_init(&priv->tcp_sess_lock); + + INIT_LIST_HEAD(&priv->tx_stat_queue); + spin_lock_init(&priv->tx_stat_lock); + skb_queue_head_init(&priv->tx_q); + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + spin_lock_init(&priv->connect_lock); +#endif +#endif + +#ifdef STA_SUPPORT + INIT_LIST_HEAD(&priv->pmksa_cache_list); + if (bss_type == MLAN_BSS_TYPE_STA) { + init_waitqueue_head(&priv->okc_wait_q); + spin_lock_init(&priv->pmksa_list_lock); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } +#endif +#if defined(DRV_EMBEDDED_AUTHENTICATOR) + init_waitqueue_head(&priv->hostcmd_wait_q); +#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 +#ifdef 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 + if (!handle->priv_num +#ifdef MFG_CMD_SUPPORT + && (handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + ) { + if (handle->params.init_cfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_init_data(handle, INIT_CFG_DATA, + MOAL_IOCTL_WAIT, NULL)) { + PRINTM(MFATAL, + "Set user init data and param failed\n"); + goto error; + } + } + if (handle->params.init_hostcmd_cfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_init_data(handle, + INIT_HOSTCMD_CFG_DATA, + MOAL_IOCTL_WAIT, NULL)) { + PRINTM(MFATAL, + "Set user init hostcmd data and param failed\n"); + goto error; + } + } + } +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) { +#endif + if (bss_type == MLAN_BSS_TYPE_UAP && + priv->phandle->params.band_steer_cfg) { + if (MLAN_STATUS_SUCCESS != + woal_set_user_init_data(priv->phandle, + BAND_STEER_CFG_DATA, + MOAL_IOCTL_WAIT, NULL)) { + PRINTM(MFATAL, + "Set band steering configure param failed\n"); + } + } +#ifdef MFG_CMD_SUPPORT + } +#endif + + handle->priv_num++; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (!priv->phandle->wiphy && + IS_STA_OR_UAP_CFG80211(handle->params.cfg80211_wext)) { + if (woal_register_cfg80211(priv)) { + PRINTM(MERROR, "Cannot register with cfg80211\n"); + goto error; + } + } +#endif + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + if ((priv->bss_role == MLAN_BSS_ROLE_STA) && + IS_STA_CFG80211(handle->params.cfg80211_wext)) { + if (bss_type == MLAN_BSS_TYPE_STA +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + || 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(handle->params.cfg80211_wext)) { + /* Register cfg80211 for UAP */ + if (woal_register_uap_cfg80211(dev, bss_type)) { + PRINTM(MERROR, "Cannot register UAP with cfg80211\n"); + goto error; + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + strcpy(csa_str, "CSA"); + strcat(csa_str, name); + priv->csa_workqueue = alloc_workqueue( + csa_str, WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); + if (!priv->csa_workqueue) { + PRINTM(MERROR, "cannot alloc csa workqueue \n"); + goto error; + } + INIT_DELAYED_WORK(&priv->csa_work, woal_csa_work_queue); +#endif +#endif +#endif /*UAP_CFG80211 */ + + /* Initialize priv structure */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + SET_NETDEV_DEV(dev, handle->hotplug_device); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) + SET_NETDEV_DEVTYPE(dev, &wlan_type); +#endif + + /* Register network device */ + if (register_netdev(dev)) { + PRINTM(MERROR, "Cannot register virtual network device\n"); + goto error; + } + netif_carrier_off(dev); + woal_stop_queue(dev); + + PRINTM(MINFO, "%s: NXP 802.11 Adapter\n", dev->name); + + if (bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { +#if defined(SD8887) || defined(SD8987) + mlan_fw_info fw_info; + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if (MFALSE +#ifdef SD8887 + || IS_SD8887(handle->card_type) +#endif +#ifdef SD8987 + || IS_SD8987(handle->card_type) +#endif + ) { + if ((fw_info.antinfo & ANT_DIVERSITY_2G) && + (fw_info.antinfo & ANT_DIVERSITY_5G)) + handle->card_info->histogram_table_num = 4; + } +#endif + + for (i = 0; i < handle->card_info->histogram_table_num; i++) { + priv->hist_data[i] = + kmalloc(sizeof(hgm_data) + + handle->card_info->rx_rate_max * + sizeof(atomic_t), + GFP_KERNEL); + if (!(priv->hist_data[i])) { + PRINTM(MERROR, + "kmalloc priv->hist_data[%d] failed\n", + i); + goto error; + } + } + woal_hist_data_reset(priv); + } +#ifdef CONFIG_PROC_FS + woal_create_proc_entry(priv); + woal_debug_entry(priv); +#endif /* CONFIG_PROC_FS */ + + LEAVE(); + return priv; +error: + handle->priv_num = bss_index; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /* Unregister wiphy device and free */ + if (priv) { + if (priv->wdev && + IS_STA_OR_UAP_CFG80211(handle->params.cfg80211_wext)) + priv->wdev = NULL; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->csa_workqueue) { + destroy_workqueue(priv->csa_workqueue); + priv->csa_workqueue = NULL; + } +#endif +#endif + } +#endif + if (dev && dev->reg_state == NETREG_REGISTERED) + unregister_netdev(dev); + if (dev) + free_netdev(dev); + LEAVE(); + return NULL; +} + +/** + * @brief This function removes an interface. + * + * @param handle A pointer to the moal_handle structure + * @param bss_index BSS index number + * + * @return N/A + */ +void woal_remove_interface(moal_handle *handle, t_u8 bss_index) +{ + struct net_device *dev = NULL; + moal_private *priv = handle->priv[bss_index]; +#if defined(STA_WEXT) || defined(UAP_WEXT) + union iwreq_data wrqu; +#endif + int i = 0; + ENTER(); + + if (!priv || !priv->netdev) + goto error; + dev = priv->netdev; + + if (priv->media_connected == MTRUE) { + priv->media_connected = MFALSE; +#if defined(STA_WEXT) || defined(UAP_WEXT) + if (IS_STA_OR_UAP_WEXT(handle->params.cfg80211_wext) && + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, + NULL); + } +#endif + } + woal_flush_tcp_sess_queue(priv); + + woal_flush_tx_stat_queue(priv); + +#ifdef STA_CFG80211 + if (priv->bss_type == MLAN_BSS_TYPE_STA && + IS_STA_CFG80211(handle->params.cfg80211_wext)) { + woal_flush_pmksa_list(priv); + if (priv->okc_roaming_ie) { + kfree(priv->okc_roaming_ie); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } + } +#endif + + if (priv->bss_type == MLAN_BSS_TYPE_STA || + priv->bss_type == MLAN_BSS_TYPE_UAP) { + for (i = 0; i < handle->card_info->histogram_table_num; i++) { + kfree(priv->hist_data[i]); + priv->hist_data[i] = NULL; + } + } +#ifdef CONFIG_PROC_FS + /* Remove proc debug */ + woal_debug_remove(priv); + 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(handle->params.cfg80211_wext)) + priv->wdev = NULL; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->csa_workqueue) { + flush_workqueue(priv->csa_workqueue); + destroy_workqueue(priv->csa_workqueue); + priv->csa_workqueue = NULL; + } +#endif +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + woal_deinit_wifi_hal(priv); +#endif + + /* Clear the priv in handle */ +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 8, 13) + if (IS_STA_OR_UAP_CFG80211(handle->params.cfg80211_wext)) + priv->phandle->wiphy->extended_capabilities = NULL; +#endif +#endif + priv->phandle->priv[priv->bss_index] = NULL; + priv->phandle = NULL; + priv->netdev = NULL; + free_netdev(dev); +error: + LEAVE(); + return; +} + +/** + * @brief Configure pmic in firmware + * + * @param handle A pointer to moal_handle + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_pmic_configure(moal_handle *handle, t_u8 wait_option) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_PMIC_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Configure antcfg + * + * @param handle A pointer to moal_handle + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_set_user_antcfg(moal_handle *handle, t_u8 wait_option) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio = NULL; + mlan_status status; + int antcfg; + + ENTER(); + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + antcfg = handle->params.antcfg; + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_SET; + + if (handle->feature_control & FEATURE_CTRL_STREAM_2X2) { + if (IS_CARD9098(handle->card_type) || + IS_CARD9097(handle->card_type)) + radio->param.ant_cfg.tx_antenna = + radio->param.ant_cfg.rx_antenna = antcfg; + else { + radio->param.ant_cfg.tx_antenna = + (antcfg & 0x0030) >> 4; + radio->param.ant_cfg.rx_antenna = antcfg & 0x0003; + } + } else + radio->param.ant_cfg_1x1.antenna = antcfg; + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Configure MLAN for low power mode + * + * @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_set_low_pwr_mode(moal_handle *handle, t_u8 wait_option) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_LOW_PWR_MODE; + misc->param.low_pwr_mode = moal_extflg_isset(handle, EXT_LOW_PW_MODE); + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); +done: + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Send FW shutdown command to MLAN + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_shutdown_fw(moal_private *priv, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_INIT_SHUTDOWN; + misc->param.func_init_shutdown = MLAN_FUNC_SHUTDOWN; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + /* add 100 ms delay to avoid back to back init/shutdown */ + mdelay(100); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Return hex value of a give character + * + * @param chr Character to be converted + * + * @return The converted character if chr is a valid hex, else 0 + */ +int woal_hexval(char chr) +{ + if (chr >= '0' && chr <= '9') + return chr - '0'; + if (chr >= 'A' && chr <= 'F') + return chr - 'A' + 10; + if (chr >= 'a' && chr <= 'f') + return chr - 'a' + 10; + + return 0; +} + +#ifdef STA_SUPPORT +#endif + +/** + * @brief This function flush event queue + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void woal_flush_evt_queue(moal_handle *handle) +{ + struct woal_event *evt = NULL, *tmp_node; + unsigned long flags; + spin_lock_irqsave(&handle->evt_lock, flags); + list_for_each_entry_safe (evt, tmp_node, &handle->evt_queue, link) { + list_del(&evt->link); + spin_unlock_irqrestore(&handle->evt_lock, flags); + kfree(evt); + spin_lock_irqsave(&handle->evt_lock, flags); + } + INIT_LIST_HEAD(&handle->evt_queue); + spin_unlock_irqrestore(&handle->evt_lock, flags); +} + +/** + * @brief This function cancel all works in the queue + * and destroy the main workqueue. + * + * @param handle A pointer to moal_handle + * + * @return N/A + */ +void woal_terminate_workqueue(moal_handle *handle) +{ + ENTER(); + + /* Terminate main workqueue */ + if (handle->workqueue) { + flush_workqueue(handle->workqueue); + destroy_workqueue(handle->workqueue); + handle->workqueue = NULL; + } + if (handle->rx_workqueue) { + flush_workqueue(handle->rx_workqueue); + destroy_workqueue(handle->rx_workqueue); + handle->rx_workqueue = NULL; + } + if (handle->evt_workqueue) { + woal_flush_evt_queue(handle); + flush_workqueue(handle->evt_workqueue); + destroy_workqueue(handle->evt_workqueue); + handle->evt_workqueue = NULL; + } + if (handle->tx_workqueue) { + flush_workqueue(handle->tx_workqueue); + destroy_workqueue(handle->tx_workqueue); + handle->tx_workqueue = NULL; + } + LEAVE(); +} + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief This function opens the network device + * + * @param dev A pointer to net_device structure + * + * @return 0 --success, otherwise fail + */ +int woal_open(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); +#if defined(USB) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + struct usb_interface *intf = + ((struct usb_card_rec *)priv->phandle->card)->intf; +#endif /* < 2.6.34 */ +#endif /* USB_SUSPEND_RESUME */ + t_u8 carrier_on = MFALSE; + + ENTER(); + + if (priv->phandle->surprise_removed == MTRUE) { + PRINTM(MERROR, + "open is not allowed in surprise remove state.\n"); + LEAVE(); + return -EFAULT; + } +#if defined(USB) + if (IS_USB(priv->phandle->card_type)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + intf->pm_usage_cnt = 1; +#else + atomic_set(&intf->pm_usage_cnt, 1); +#endif /* < 2.6.32 */ + usb_autopm_put_interface(intf); +#else + struct usb_device *udev = + ((struct usb_card_rec *)(priv->phandle->card))->udev; + usb_lock_device(udev); + atomic_set(&udev->dev.power.usage_count, 1); + usb_enable_autosuspend(udev); + usb_unlock_device(udev); +#endif /* < 2.6.34 */ + } +#endif /* USB_SUSPEND_RESUME */ + +#if defined(USB) || defined(SYSKT) + /* On some systems the device open handler will be called before HW + ready. + Use the following flag check and wait function to work around + the issue. */ + if (MTRUE +#ifdef SDIO + && !IS_SD(priv->phandle->card_type) +#endif +#ifdef PCIE + && !IS_PCIE(priv->phandle->card_type) +#endif + ) { + int i = 0; + + while ((priv->phandle->hardware_status != + HardwareStatusReady) && + (i < MAX_WAIT_DEVICE_READY_COUNT)) { + i++; + woal_sched_timeout(100); + } + if (i >= MAX_WAIT_DEVICE_READY_COUNT) { + PRINTM(MFATAL, + "HW not ready, wlan_open() return failure\n"); + LEAVE(); + return -EFAULT; + } + } +#endif /* USB || SYSKT || SYSKT_MULTI */ + if (!MODULE_GET) { + LEAVE(); + return -EFAULT; + } +#ifdef UAP_SUPPORT + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) && + (priv->media_connected)) + carrier_on = MTRUE; +#endif +#ifdef STA_SUPPORT + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) && + (priv->media_connected || priv->is_adhoc_link_sensed)) + carrier_on = MTRUE; +#endif + if (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); +#if defined(USB) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + struct usb_interface *intf = + ((struct usb_card_rec *)priv->phandle->card)->intf; +#endif /* < 2.6.34 */ +#endif /* USB_SUSPEND_RESUME */ +#ifdef STA_CFG80211 + int cfg80211_wext = priv->phandle->params.cfg80211_wext; +#endif + ENTER(); + + woal_flush_tx_stat_queue(priv); + +#ifdef STA_SUPPORT +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext) && + (priv->bss_type == MLAN_BSS_TYPE_STA)) + woal_clear_conn_params(priv); + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + if (IS_STA_CFG80211(cfg80211_wext) && priv->wdev->current_bss) { + priv->cfg_disconnect = MTRUE; + cfg80211_disconnected(priv->netdev, 0, NULL, 0, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + true, +#endif + GFP_KERNEL); + } +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(cfg80211_wext) && priv->sched_scanning) { + woal_stop_bg_scan(priv, MOAL_IOCTL_WAIT); + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + cfg80211_sched_scan_stopped(priv->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , + priv->bg_scan_reqid +#endif + ); + priv->sched_scanning = MFALSE; + } +#endif +#endif +#endif + if (!priv->bss_virtual) + woal_stop_queue(priv->netdev); + MODULE_PUT; +#if defined(USB) + if (IS_USB(priv->phandle->card_type)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + intf->pm_usage_cnt = 0; +#else + atomic_set(&intf->pm_usage_cnt, 0); +#endif /* < 2.6.32 */ + usb_autopm_get_interface(intf); +#else + struct usb_device *udev = + ((struct usb_card_rec *)(priv->phandle->card))->udev; + usb_lock_device(udev); + atomic_set(&udev->dev.power.usage_count, 0); + usb_disable_autosuspend(udev); + usb_unlock_device(udev); +#endif /* < 2.6.34 */ + } +#endif /* USB_SUSPEND_RESUME */ + + LEAVE(); + return 0; +} + +/** + * @brief This function sets the MAC address to firmware. + * + * @param dev A pointer to mlan_private structure + * @param addr MAC address to set + * + * @return 0 --success, otherwise fail + */ +int woal_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct sockaddr *phw_addr = (struct sockaddr *)addr; + t_u8 prev_addr[ETH_ALEN]; + + ENTER(); + + if (priv->phandle->surprise_removed == MTRUE) { + PRINTM(MERROR, + "Set mac address is not allowed in surprise remove state.\n"); + LEAVE(); + return -EFAULT; + } + + moal_memcpy_ext(priv->phandle, prev_addr, priv->current_addr, ETH_ALEN, + 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); + moal_memcpy_ext(priv->phandle, priv->current_addr, phw_addr->sa_data, + ETH_ALEN, ETH_ALEN); +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + priv->current_addr[0] |= 0x02; + PRINTM(MCMND, "Set WFD device addr: " MACSTR "\n", + MAC2STR(priv->current_addr)); + } +#endif +#endif +#endif + if (MLAN_STATUS_SUCCESS != + woal_request_set_mac_address(priv, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Set MAC address failed\n"); + /* For failure restore the MAC address */ + moal_memcpy_ext(priv->phandle, priv->current_addr, prev_addr, + ETH_ALEN, ETH_ALEN); + ret = -EFAULT; + goto done; + } + HEXDUMP("priv->MacAddr:", priv->current_addr, ETH_ALEN); + moal_memcpy_ext(priv->phandle, dev->dev_addr, priv->current_addr, + ETH_ALEN, ETH_ALEN); +done: + LEAVE(); + return ret; +} + +/** + * @brief Check driver status + * + * @param handle A pointer to moal_handle + * + * @return MTRUE/MFALSE + */ +t_u8 woal_check_driver_status(moal_handle *handle) +{ + moal_private *priv = NULL; + wifi_timeval t; + int i = 0; + mlan_debug_info *info = &(handle->debug_info); + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv || woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) { + PRINTM(MERROR, + "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return MTRUE; + } +#define MOAL_CMD_TIMEOUT_MAX 9 +#define MOAL_CMD_TIMEOUT 20 + woal_get_monotonic_time(&t); + if (info->dnld_cmd_in_secs && info->pending_cmd && + (t.time_sec > (info->dnld_cmd_in_secs + MOAL_CMD_TIMEOUT_MAX))) { + if (t.time_sec > (info->dnld_cmd_in_secs + MOAL_CMD_TIMEOUT) && + !info->num_cmd_timeout) { + PRINTM(MERROR, "Ignore invalid time, wait=%d\n", + (int)(t.time_sec - info->dnld_cmd_in_secs)); + } else { + PRINTM(MERROR, "Timeout cmd id = 0x%x wait=%d\n", + info->pending_cmd, + (int)(t.time_sec - info->dnld_cmd_in_secs)); + LEAVE(); + return MTRUE; + } + } + if (info->num_cmd_timeout) { + PRINTM(MERROR, "num_cmd_timeout = %d\n", info->num_cmd_timeout); + PRINTM(MERROR, "Timeout cmd id = 0x%x, act = 0x%x\n", + info->timeout_cmd_id, info->timeout_cmd_act); + LEAVE(); + return MTRUE; + } + if (info->num_cmd_host_to_card_failure) { + PRINTM(MERROR, "num_cmd_host_to_card_failure = %d\n", + info->num_cmd_host_to_card_failure); + LEAVE(); + return MTRUE; + } + if (info->num_no_cmd_node) { + PRINTM(MERROR, "num_no_cmd_node = %d\n", info->num_no_cmd_node); + LEAVE(); + return MTRUE; + } + for (i = 0; i < handle->priv_num; i++) { + priv = handle->priv[i]; + if (priv) { + if (priv->num_tx_timeout >= NUM_TX_TIMEOUT_THRESHOLD) { + PRINTM(MERROR, "num_tx_timeout = %d\n", + priv->num_tx_timeout); + LEAVE(); + return MTRUE; + } + } + } + if (info->pm_wakeup_card_req && info->pm_wakeup_fw_try) { +#define MAX_WAIT_TIME 3 + if (t.time_sec > (info->pm_wakeup_in_secs + MAX_WAIT_TIME)) { + PRINTM(MERROR, + "wakeup_dev_req=%d wakeup_tries=%d wait=%d\n", + info->pm_wakeup_card_req, info->pm_wakeup_fw_try, + (int)(t.time_sec - info->pm_wakeup_in_secs)); + LEAVE(); + return MTRUE; + } + } + if (info->fw_hang_report) { + PRINTM(MERROR, "fw_hang_report = %d\n", info->fw_hang_report); + LEAVE(); + return MTRUE; + } + + if (priv && priv->phandle->driver_status) { + LEAVE(); + return MTRUE; + } + + LEAVE(); + return MFALSE; +} + +/** + * @brief Display MLAN debug information + * + * @param priv A pointer to moal_private + * + * @return N/A + */ +void woal_mlan_debug_info(moal_private *priv) +{ + int i; +#ifdef SDIO + int j; + t_u8 mp_aggr_pkt_limit = 0; +#endif + char str[512] = {0}; + char *s; + mlan_debug_info *info = NULL; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or priv->phandle is null\n"); + LEAVE(); + return; + } + + info = &(priv->phandle->debug_info); + + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) { + PRINTM(MERROR, + "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return; + } + PRINTM(MERROR, "------------mlan_debug_info-------------\n"); + PRINTM(MERROR, "mlan_processing =%d\n", info->mlan_processing); + PRINTM(MERROR, "main_lock_flag =%d\n", info->main_lock_flag); + PRINTM(MERROR, "main_process_cnt =%d\n", info->main_process_cnt); + PRINTM(MERROR, "delay_task_flag =%d\n", info->delay_task_flag); + PRINTM(MERROR, "mlan_rx_processing =%d\n", info->mlan_rx_processing); + PRINTM(MERROR, "rx_pkts_queued=%d\n", info->rx_pkts_queued); + PRINTM(MERROR, "tx_pkts_queued=%d\n", info->tx_pkts_queued); + PRINTM(MERROR, "fw_hang_report = %d\n", info->fw_hang_report); + 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); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_id[i]); + PRINTM(MERROR, "last_cmd_id = %s\n", str); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_act[i]); + PRINTM(MERROR, "last_cmd_act = %s\n", str); + PRINTM(MERROR, "last_cmd_resp_index = %d\n", info->last_cmd_resp_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_resp_id[i]); + PRINTM(MERROR, "last_cmd_resp_id = %s\n", str); + PRINTM(MERROR, "last_event_index = %d\n", info->last_event_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_event[i]); + PRINTM(MERROR, "last_event = %s", str); + + PRINTM(MERROR, "num_data_h2c_failure = %d\n", + info->num_tx_host_to_card_failure); + PRINTM(MERROR, "num_cmd_h2c_failure = %d\n", + info->num_cmd_host_to_card_failure); + PRINTM(MERROR, "num_alloc_buffer_failure = %d\n", + info->num_alloc_buffer_failure); + PRINTM(MERROR, "num_pkt_dropped = %d\n", info->num_pkt_dropped); + +#ifdef SDIO + if (IS_SD(priv->phandle->card_type)) { + 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, "mp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + (unsigned int)info->mp_rd_bitmap, info->curr_rd_port); + PRINTM(MERROR, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + (unsigned int)info->mp_wr_bitmap, info->curr_wr_port); + PRINTM(MERROR, "mp_invalid_update=%d\n", + info->mp_invalid_update); + mp_aggr_pkt_limit = info->mp_aggr_pkt_limit; + PRINTM(MERROR, "last_recv_wr_bitmap=0x%x last_mp_index = %d\n", + info->last_recv_wr_bitmap, info->last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + for (s = str, j = 0; j < mp_aggr_pkt_limit; j++) + s += sprintf( + s, "0x%02x ", + info->last_mp_wr_info + [i * mp_aggr_pkt_limit + j]); + + PRINTM(MERROR, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n%s\n", + info->last_mp_wr_bitmap[i], + info->last_mp_wr_ports[i], + info->last_mp_wr_len[i], + info->last_curr_wr_port[i], str); + } + } +#endif +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + PRINTM(MERROR, "txbd_rdptr=0x%x txbd_wrptr=0x%x\n", + info->txbd_rdptr, info->txbd_wrptr); + PRINTM(MERROR, "rxbd_rdptr=0x%x rxbd_wrptr=0x%x\n", + info->rxbd_rdptr, info->rxbd_wrptr); + PRINTM(MERROR, "eventbd_rdptr=0x%x event_wrptr=0x%x\n", + info->eventbd_rdptr, info->eventbd_wrptr); + PRINTM(MERROR, "last_wr_index:%d\n", + info->txbd_wrptr & (MLAN_MAX_TXRX_BD - 1)); + PRINTM(MERROR, "Tx pkt size:\n"); + for (s = str, i = 0; i < MLAN_MAX_TXRX_BD; i++) { + s += sprintf(s, "%d ", info->last_tx_pkt_size[i]); + if (((i + 1) % 16) == 0) { + PRINTM(MERROR, "%s\n", str); + s = str; + } + } + } +#endif + 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, "num_cons_assoc_failure = %d\n", + info->num_cons_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 wakeup_timeout=%d\n", + info->pm_wakeup_card_req, info->pm_wakeup_fw_try, + info->pm_wakeup_timeout); + 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); + for (i = 0; i < info->ralist_num; i++) { + PRINTM(MERROR, + "ralist ra: %02x:%02x:%02x:%02x:%02x:%02x tid=%d pkts=%d pause=%d\n", + info->ralist[i].ra[0], info->ralist[i].ra[1], + info->ralist[i].ra[2], info->ralist[i].ra[3], + info->ralist[i].ra[4], info->ralist[i].ra[5], + info->ralist[i].tid, info->ralist[i].total_pkts, + info->ralist[i].tx_pause); + } + +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + PRINTM(MERROR, "txbd: rdptr=0x%x wrptr=0x%x\n", + info->txbd_rdptr, info->txbd_wrptr); + PRINTM(MERROR, "rxbd: rdptr=0x%x wrptr=0x%x\n", + info->rxbd_rdptr, info->rxbd_wrptr); + PRINTM(MERROR, "eventbd: rdptr=0x%x wrptr=0x%x\n", + info->eventbd_rdptr, info->eventbd_wrptr); + } +#endif + PRINTM(MERROR, "------------mlan_debug_info End-------------\n"); + LEAVE(); +} + +/** + * @brief This function handle the shutdown timeout issue + * + * @param handle Pointer to structure moal_handle + * + * @return N/A + */ +void woal_ioctl_timeout(moal_handle *handle) +{ + moal_private *priv = NULL; + + ENTER(); + + PRINTM(MMSG, "woal_ioctl_timout.\n"); + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (priv) { + woal_mlan_debug_info(priv); + woal_moal_debug_info(priv, NULL, MFALSE); + } + LEAVE(); + return; +} + +/** + * @brief This function handles the timeout of packet + * transmission + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +void woal_tx_timeout(struct net_device *dev +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) + , + unsigned int txqueue +#endif +) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + priv->num_tx_timeout++; + PRINTM(MERROR, "%lu : %s (bss=%d): Tx timeout (%d)\n", jiffies, + dev->name, priv->bss_index, priv->num_tx_timeout); + woal_set_trans_start(dev); + + if (priv->num_tx_timeout == NUM_TX_TIMEOUT_THRESHOLD) { + woal_mlan_debug_info(priv); + woal_moal_debug_info(priv, NULL, MFALSE); + woal_broadcast_event(priv, CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (IS_STA_OR_UAP_CFG80211(priv->phandle->params.cfg80211_wext)) + woal_cfg80211_vendor_event(priv, event_hang, + CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#endif +#endif + priv->phandle->driver_status = MTRUE; + woal_process_hang(priv->phandle); + + wifi_status = 3; + } + + LEAVE(); +} + +/** + * @brief This function returns the network statistics + * + * @param dev A pointer to net_device structure + * + * @return A pointer to net_device_stats structure + */ +struct net_device_stats *woal_get_stats(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + return &priv->stats; +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +/** + * @brief This function handles wmm queue select + * + * @param dev A pointer to net_device structure + * @param skb A pointer to sk_buff structure + * + * @return tx_queue index (0-3) + */ +u16 woal_select_queue(struct net_device *dev, struct sk_buff *skb +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + , + struct net_device *sb_dev +#else + , + void *accel_priv +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) && \ + LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) + , + select_queue_fallback_t fallback +#endif +#endif +) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct ethhdr *eth = NULL; + t_u8 tid = 0; + t_u8 index = 0; + + ENTER(); + if (priv->phandle->surprise_removed == MTRUE) { + LEAVE(); + return index; + } + /* + * skb->priority values from 256->263 are magic values to + * directly indicate a specific 802.1d priority. This is used + * to allow 802.1d priority to be passed directly in from VLAN + * tags, etc. + */ + if (IS_SKB_MAGIC_VLAN(skb)) { + tid = GET_VLAN_PRIO(skb); + } else { + eth = (struct ethhdr *)skb->data; + switch (eth->h_proto) { + case __constant_htons(ETH_P_IP): + tid = priv->dscp_map[SKB_TOS(skb) >> DSCP_OFFSET]; + if (tid == 0xFF) + tid = (IPTOS_PREC(SKB_TOS(skb)) >> + IPTOS_OFFSET); + break; + case __constant_htons(ETH_P_IPV6): + tid = SKB_TIDV6(skb); + break; + case __constant_htons(ETH_P_ARP): + default: + break; + } + } + + index = mlan_select_wmm_queue(priv->phandle->pmlan_adapter, + priv->bss_index, tid); + PRINTM(MDATA, "select queue: tid=%d, index=%d\n", tid, index); + LEAVE(); + return index; +} +#endif + +/** + * @brief This function flush tx status queue + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void woal_flush_tx_stat_queue(moal_private *priv) +{ + struct tx_status_info *tx_info = NULL, *tmp_node; + unsigned long flags; + struct sk_buff *skb = NULL; + spin_lock_irqsave(&priv->tx_stat_lock, flags); + list_for_each_entry_safe (tx_info, tmp_node, &priv->tx_stat_queue, + link) { + list_del(&tx_info->link); + spin_unlock_irqrestore(&priv->tx_stat_lock, flags); + skb = (struct sk_buff *)tx_info->tx_skb; + if (tx_info->tx_cookie) { +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + cfg80211_mgmt_tx_status(priv->netdev, + tx_info->tx_cookie, skb->data, + skb->len, true, GFP_ATOMIC); +#else + cfg80211_mgmt_tx_status(priv->wdev, tx_info->tx_cookie, + skb->data, skb->len, true, + GFP_ATOMIC); +#endif +#endif +#endif + } + dev_kfree_skb_any(skb); + kfree(tx_info); + spin_lock_irqsave(&priv->tx_stat_lock, flags); + } + INIT_LIST_HEAD(&priv->tx_stat_queue); + spin_unlock_irqrestore(&priv->tx_stat_lock, flags); + spin_lock_bh(&(priv->tx_q.lock)); + __skb_queue_purge(&priv->tx_q); + spin_unlock_bh(&(priv->tx_q.lock)); +} + +/** + * @brief This function gets tx info from tx_stat_queue + * + * @param priv A pointer to moal_private structure + * @param tx_seq_num tx seq number + * + * @return A pointer to the tcp tx_status_info structure, if found. + * Otherwise, null + */ +struct tx_status_info *woal_get_tx_info(moal_private *priv, t_u8 tx_seq_num) +{ + struct tx_status_info *tx_info = NULL; + ENTER(); + + list_for_each_entry (tx_info, &priv->tx_stat_queue, link) { + if (tx_info->tx_seq_num == tx_seq_num) { + LEAVE(); + return tx_info; + } + } + LEAVE(); + return NULL; +} + +/** + * @brief This function remove tx info from queue + * + * @param priv A pointer to moal_private structure + * @param tx_seq_num tx seq number + * + * @return N/A + */ +void woal_remove_tx_info(moal_private *priv, t_u8 tx_seq_num) +{ + struct tx_status_info *tx_info, *tmp = NULL; + unsigned long flags; + ENTER(); + + spin_lock_irqsave(&priv->tx_stat_lock, flags); + list_for_each_entry_safe (tx_info, tmp, &priv->tx_stat_queue, link) { + if (tx_info->tx_seq_num == tx_seq_num) { + list_del(&tx_info->link); + dev_kfree_skb_any((struct sk_buff *)tx_info->tx_skb); + kfree(tx_info); + break; + } + } + spin_unlock_irqrestore(&priv->tx_stat_lock, flags); + + LEAVE(); +} + +/** + * @brief This function flush tcp session queue + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void woal_flush_tcp_sess_queue(moal_private *priv) +{ + struct tcp_sess *tcp_sess = NULL, *tmp_node; + unsigned long flags; + struct sk_buff *skb; + spin_lock_irqsave(&priv->tcp_sess_lock, flags); + list_for_each_entry_safe (tcp_sess, tmp_node, &priv->tcp_sess_queue, + link) { + list_del(&tcp_sess->link); + if (tcp_sess->is_timer_set) + woal_cancel_timer(&tcp_sess->ack_timer); + skb = (struct sk_buff *)tcp_sess->ack_skb; + if (skb) + dev_kfree_skb_any(skb); + kfree(tcp_sess); + } + INIT_LIST_HEAD(&priv->tcp_sess_queue); + priv->tcp_ack_drop_cnt = 0; + priv->tcp_ack_cnt = 0; + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); +} + +/** + * @brief This function gets tcp session from the tcp session queue + * + * @param priv A pointer to moal_private structure + * @param src_ip IP address of the device + * @param src_port TCP port of the device + * @param dst_ip IP address of the client + * @param dst_port TCP port of the client + * + * @return A pointer to the tcp session data structure, if found. + * Otherwise, null + */ +static inline struct tcp_sess *woal_get_tcp_sess(moal_private *priv, + t_u32 src_ip, t_u16 src_port, + t_u32 dst_ip, t_u16 dst_port) +{ + struct tcp_sess *tcp_sess = NULL; + ENTER(); + + list_for_each_entry (tcp_sess, &priv->tcp_sess_queue, link) { + if ((tcp_sess->src_ip_addr == src_ip) && + (tcp_sess->src_tcp_port == src_port) && + (tcp_sess->dst_ip_addr == dst_ip) && + (tcp_sess->dst_tcp_port == dst_port)) { + LEAVE(); + return tcp_sess; + } + } + LEAVE(); + return NULL; +} + +/** + * @brief This function send the holding tcp ack packet + * re-assoc thread. + * + * @param context A pointer to context + * @return N/A + */ +void woal_tcp_ack_timer_func(void *context) +{ + struct tcp_sess *tcp_session = (struct tcp_sess *)context; + moal_private *priv = (moal_private *)tcp_session->priv; + unsigned long flags; + mlan_buffer *pmbuf; + struct sk_buff *skb; + mlan_status status; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + ENTER(); + spin_lock_irqsave(&priv->tcp_sess_lock, flags); + tcp_session->is_timer_set = MFALSE; + skb = (struct sk_buff *)tcp_session->ack_skb; + pmbuf = (mlan_buffer *)tcp_session->pmbuf; + tcp_session->ack_skb = NULL; + tcp_session->pmbuf = NULL; + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + if (skb && pmbuf) { + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_inc(&priv->wmm_tx_pending[index]); + if (atomic_read(&priv->wmm_tx_pending[index]) >= + MAX_TX_PENDING) { + struct netdev_queue *txq = netdev_get_tx_queue( + priv->netdev, index); + netif_tx_stop_queue(txq); + PRINTM(MINFO, "Stop Kernel Queue : %d\n", + index); + } +#else + if (atomic_read(&priv->phandle->tx_pending) >= + MAX_TX_PENDING) + woal_stop_queue(priv->netdev); +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)*/ + queue_work(priv->phandle->workqueue, + &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + break; + case MLAN_STATUS_FAILURE: + default: + priv->stats.tx_dropped++; + dev_kfree_skb_any(skb); + break; + } + } + LEAVE(); + return; +} + +/** + * @brief This function send the tcp ack + * + * + * @param priv A pointer to moal_private structure + * @param tcp_session A pointer to tcp_session + * @return N/A + */ +void woal_send_tcp_ack(moal_private *priv, struct tcp_sess *tcp_session) +{ + mlan_status status; + struct sk_buff *skb = (struct sk_buff *)tcp_session->ack_skb; + mlan_buffer *pmbuf = (mlan_buffer *)tcp_session->pmbuf; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + ENTER(); + if (tcp_session->is_timer_set) { + woal_cancel_timer(&tcp_session->ack_timer); + tcp_session->is_timer_set = MFALSE; + } + tcp_session->ack_skb = NULL; + tcp_session->pmbuf = NULL; + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_inc(&priv->wmm_tx_pending[index]); + if (atomic_read(&priv->wmm_tx_pending[index]) >= + MAX_TX_PENDING) { + struct netdev_queue *txq = + netdev_get_tx_queue(priv->netdev, index); + netif_tx_stop_queue(txq); + PRINTM(MINFO, "Stop Kernel Queue : %d\n", index); + } +#else + if (atomic_read(&priv->phandle->tx_pending) >= MAX_TX_PENDING) + woal_stop_queue(priv->netdev); +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)*/ + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + break; + case MLAN_STATUS_FAILURE: + default: + priv->stats.tx_dropped++; + dev_kfree_skb_any(skb); + break; + } + LEAVE(); +} + +/** + * @brief This function get the tcp ack session node + * + * @param priv A pointer to moal_private structure + * @param pmbuf A pointer to mlan_buffer associated with a skb + * + * @return 1, if it's dropped; 0, if not dropped + */ +int woal_process_tcp_ack(moal_private *priv, mlan_buffer *pmbuf) +{ + int ret = 0; + unsigned long flags; + struct tcp_sess *tcp_session; + struct ethhdr *ethh = NULL; + struct iphdr *iph = NULL; + struct tcphdr *tcph = NULL; + t_u32 ack_seq; + struct sk_buff *skb; + + ENTER(); + + /** check the tcp packet */ + ethh = (struct ethhdr *)(pmbuf->pbuf + pmbuf->data_offset); + if (ntohs(ethh->h_proto) != ETH_P_IP) { + LEAVE(); + return 0; + } + iph = (struct iphdr *)((t_u8 *)ethh + sizeof(struct ethhdr)); + if (iph->protocol != IPPROTO_TCP) { + LEAVE(); + return 0; + } + tcph = (struct tcphdr *)((t_u8 *)iph + iph->ihl * 4); + + if (*((t_u8 *)tcph + 13) == 0x10) { + /* Only replace ACK */ + if (ntohs(iph->tot_len) > (iph->ihl + tcph->doff) * 4) { + /* Don't drop ACK with payload */ + /* TODO: should we delete previous TCP session */ + LEAVE(); + return ret; + } + priv->tcp_ack_cnt++; + spin_lock_irqsave(&priv->tcp_sess_lock, flags); + tcp_session = woal_get_tcp_sess(priv, iph->saddr, tcph->source, + iph->daddr, tcph->dest); + if (!tcp_session) { + tcp_session = + kmalloc(sizeof(struct tcp_sess), GFP_ATOMIC); + if (!tcp_session) { + PRINTM(MERROR, "Fail to allocate tcp_sess.\n"); + spin_unlock_irqrestore(&priv->tcp_sess_lock, + flags); + goto done; + } + tcp_session->ack_skb = pmbuf->pdesc; + tcp_session->pmbuf = pmbuf; + pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; + tcp_session->src_ip_addr = iph->saddr; + tcp_session->dst_ip_addr = iph->daddr; + tcp_session->src_tcp_port = tcph->source; + tcp_session->dst_tcp_port = tcph->dest; + tcp_session->ack_seq = ntohl(tcph->ack_seq); + tcp_session->priv = (void *)priv; + skb = (struct sk_buff *)pmbuf->pdesc; + skb->cb[0] = 0; + /* Initialize the timer for tcp ack */ + woal_initialize_timer(&tcp_session->ack_timer, + woal_tcp_ack_timer_func, + tcp_session); + tcp_session->is_timer_set = MTRUE; + woal_mod_timer(&tcp_session->ack_timer, MOAL_TIMER_1MS); + list_add_tail(&tcp_session->link, + &priv->tcp_sess_queue); + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + ret = HOLD_TCP_ACK; + LEAVE(); + return ret; + } else if (!tcp_session->ack_skb) { + tcp_session->ack_skb = pmbuf->pdesc; + tcp_session->pmbuf = pmbuf; + pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; + tcp_session->ack_seq = ntohl(tcph->ack_seq); + tcp_session->priv = (void *)priv; + skb = (struct sk_buff *)pmbuf->pdesc; + skb->cb[0] = 0; + tcp_session->is_timer_set = MTRUE; + woal_mod_timer(&tcp_session->ack_timer, MOAL_TIMER_1MS); + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + ret = HOLD_TCP_ACK; + LEAVE(); + return ret; + } + ack_seq = ntohl(tcph->ack_seq); + skb = (struct sk_buff *)tcp_session->ack_skb; + if (likely(ack_seq > tcp_session->ack_seq) && + (skb->len == pmbuf->data_len)) { + moal_memcpy_ext(priv->phandle, skb->data, + pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len, skb->len); + tcp_session->ack_seq = ack_seq; + ret = DROP_TCP_ACK; + skb->cb[0]++; +// We will drop 90% tcp ack +#define TCP_ACK_MAX_HOLD 9 + if (skb->cb[0] >= TCP_ACK_MAX_HOLD) + woal_send_tcp_ack(priv, tcp_session); + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + skb = (struct sk_buff *)pmbuf->pdesc; + dev_kfree_skb_any(skb); + priv->tcp_ack_drop_cnt++; + } else { + pmbuf->flags |= MLAN_BUF_FLAG_TCP_ACK; + spin_unlock_irqrestore(&priv->tcp_sess_lock, flags); + LEAVE(); + return ret; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief This function handles packet transmission + * + * @param skb A pointer to sk_buff structure + * @param dev A pointer to net_device structure + * + * @return 0 --success + */ +int woal_start_xmit(moal_private *priv, struct sk_buff *skb) +{ + mlan_buffer *pmbuf = NULL; + mlan_status status; + struct sk_buff *new_skb = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + int ret = 0; + + ENTER(); + + 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) + + priv->extra_tx_head_len))) { + PRINTM(MWARN, + "Tx: skb cloned %d or Insufficient skb headroom %d\n", + skb->cloned, skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + new_skb = skb_realloc_headroom( + skb, MLAN_MIN_DATA_HEADER_LEN + sizeof(mlan_buffer) + + priv->extra_tx_head_len); + if (unlikely(!new_skb)) { + PRINTM(MERROR, "Tx: Cannot allocate skb\n"); + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + goto done; + } + if (new_skb != skb) + dev_kfree_skb_any(skb); + skb = new_skb; + PRINTM(MINFO, "new skb headroom %d\n", skb_headroom(skb)); + } + pmbuf = (mlan_buffer *)skb->head; + memset((t_u8 *)pmbuf, 0, sizeof(mlan_buffer)); + pmbuf->bss_index = priv->bss_index; + woal_fill_mlan_buffer(priv, pmbuf, skb); + if (priv->enable_tcp_ack_enh == MTRUE) { + ret = woal_process_tcp_ack(priv, pmbuf); + if (ret) + goto done; + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); +#endif + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + switch (status) { + case MLAN_STATUS_PENDING: + if (is_zero_timeval(priv->phandle->tx_time_start)) { + priv->phandle->tx_time_start.time_sec = + pmbuf->in_ts_sec; + priv->phandle->tx_time_start.time_usec = + pmbuf->in_ts_usec; + PRINTM(MINFO, "%s : start_timeval=%d:%d \n", __func__, + priv->phandle->tx_time_start.time_sec, + priv->phandle->tx_time_start.time_usec); + } + atomic_inc(&priv->phandle->tx_pending); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + atomic_inc(&priv->wmm_tx_pending[index]); + if (atomic_read(&priv->wmm_tx_pending[index]) >= + MAX_TX_PENDING) { + struct netdev_queue *txq = + netdev_get_tx_queue(priv->netdev, index); + netif_tx_stop_queue(txq); + PRINTM(MINFO, "Stop Kernel Queue : %d\n", index); + } +#else + if (atomic_read(&priv->phandle->tx_pending) >= MAX_TX_PENDING) + woal_stop_queue(priv->netdev); +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)*/ + if (!mlan_is_main_process_running(priv->phandle->pmlan_adapter)) + queue_work(priv->phandle->workqueue, + &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + dev_kfree_skb_any(skb); + break; + case MLAN_STATUS_FAILURE: + default: + priv->stats.tx_dropped++; + dev_kfree_skb_any(skb); + break; + } +done: + LEAVE(); + return 0; +} + +/** + * @brief This function handles packet transmission + * + * @param skb A pointer to sk_buff structure + * @param dev A pointer to net_device structure + * + * @return 0 --success + */ +netdev_tx_t woal_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + ENTER(); + PRINTM(MDATA, "%lu : %s (bss=%d): Data <= kernel\n", jiffies, dev->name, + priv->bss_index); + + /* Collect TP statistics */ + if (priv->phandle->tp_acnt.on) + moal_tp_accounting(priv->phandle, skb, 1); + /* Drop Tx packets at drop point 1 */ + if (priv->phandle->tp_acnt.drop_point == 1) { + dev_kfree_skb_any(skb); + LEAVE(); + return 0; + } + if (priv->phandle->surprise_removed == MTRUE) { + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + goto done; + } + if (moal_extflg_isset(priv->phandle, EXT_TX_WORK)) { + spin_lock_bh(&(priv->tx_q.lock)); + __skb_queue_tail(&(priv->tx_q), skb); + spin_unlock_bh(&(priv->tx_q.lock)); + + queue_work(priv->phandle->tx_workqueue, + &priv->phandle->tx_work); + goto done; + } + woal_start_xmit(priv, skb); +done: + LEAVE(); + return 0; +} + +/** + * @brief Convert ascii string to Hex integer + * + * @param d A pointer to integer buf + * @param s A pointer to ascii string + * @param dlen The byte number of ascii string in hex + * + * @return Number of integer + */ +int woal_ascii2hex(t_u8 *d, char *s, t_u32 dlen) +{ + unsigned int i; + t_u8 n; + + ENTER(); + + memset(d, 0x00, dlen); + + for (i = 0; i < dlen * 2; i++) { + if ((s[i] >= 48) && (s[i] <= 57)) + n = s[i] - 48; + else if ((s[i] >= 65) && (s[i] <= 70)) + n = s[i] - 55; + else if ((s[i] >= 97) && (s[i] <= 102)) + n = s[i] - 87; + else + break; + if (!(i % 2)) + n = n * 16; + d[i / 2] += n; + } + + LEAVE(); + return i; +} + +/** + * @brief Return integer value of a given ascii string + * + * @param data Converted data to be returned + * @param a String to be converted + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_atoi(int *data, char *a) +{ + int i, val = 0, len; + int mul = 1; + + ENTER(); + + len = strlen(a); + if (len > 2) { + if (!strncmp(a, "0x", 2)) { + a = a + 2; + len -= 2; + *data = woal_atox(a); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } + for (i = 0; i < len; i++) { + if (isdigit(a[i])) { + val = val * 10 + (a[i] - '0'); + } else { + if ((i == 0) && (a[i] == '-')) { + mul = -1; + } else { + PRINTM(MERROR, "Invalid char %c in string %s\n", + a[i], a); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + } + } + *data = (mul * val); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Return hex value of a given ascii string + * + * @param a String to be converted to ascii + * + * @return The converted character if a is a valid hex, else 0 + */ +int woal_atox(char *a) +{ + int i = 0; + + ENTER(); + + while (isxdigit(*a)) + i = i * 16 + woal_hexval(*a++); + + LEAVE(); + return i; +} + +/** + * @brief Extension of strsep lib command. This function will also take care + * escape character + * + * @param s A pointer to array of chars to process + * @param delim The delimiter character to end the string + * @param esc The escape character to ignore for delimiter + * + * @return Pointer to the separated string if delim found, else NULL + */ +char *woal_strsep(char **s, char delim, char esc) +{ + char *se = *s, *sb; + + ENTER(); + + if (!(*s) || (*se == '\0')) { + LEAVE(); + return NULL; + } + + for (sb = *s; *sb != '\0'; ++sb) { + if (*sb == esc && *(sb + 1) == esc) { + /* + * We get a esc + esc seq then keep the one esc + * and chop off the other esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == esc && *(sb + 1) == delim) { + /* + * We get a delim + esc seq then keep the delim + * and chop off the esc character + */ + memmove(sb, sb + 1, strlen(sb)); + continue; + } + if (*sb == delim) + break; + } + + if (*sb == '\0') + sb = NULL; + else + *sb++ = '\0'; + + *s = sb; + + LEAVE(); + return se; +} + +/** + * @brief Convert mac address from string to t_u8 buffer. + * + * @param mac_addr The buffer to store the mac address in. + * @param buf The source of mac address which is a string. + * + * @return N/A + */ +void woal_mac2u8(t_u8 *mac_addr, char *buf) +{ + char *begin, *end, *mac_buff; + int i; + + ENTER(); + + if (!buf) { + LEAVE(); + return; + } + + mac_buff = kzalloc(strlen(buf) + 1, GFP_KERNEL); + if (!mac_buff) { + LEAVE(); + return; + } + moal_memcpy_ext(NULL, mac_buff, buf, strlen(buf), strlen(buf) + 1); + + begin = mac_buff; + for (i = 0; i < ETH_ALEN; ++i) { + end = woal_strsep(&begin, ':', '/'); + if (end) + mac_addr[i] = woal_atox(end); + } + + kfree(mac_buff); + LEAVE(); +} + +#ifdef STA_SUPPORT +/** + * @brief This function sets multicast addresses to firmware + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +void woal_set_multicast_list(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + ENTER(); + woal_request_set_multicast_list(priv, dev); + LEAVE(); +} +#endif + +/** + * @brief This function initializes the private structure + * and set default value to the member of moal_private. + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return N/A + */ +void woal_init_priv(moal_private *priv, t_u8 wait_option) +{ + ENTER(); +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + priv->current_key_index = 0; + priv->rate_index = AUTO_RATE; + priv->is_adhoc_link_sensed = MFALSE; + priv->scan_type = MLAN_SCAN_TYPE_ACTIVE; + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + memset(&priv->nick_name, 0, sizeof(priv->nick_name)); + priv->num_tx_timeout = 0; + priv->rx_filter = 0; + +#ifdef REASSOCIATION + priv->reassoc_on = MFALSE; + priv->set_asynced_essid_flag = MFALSE; +#endif +#ifdef STA_CFG80211 + memset(&priv->sme_current, 0, + sizeof(struct cfg80211_connect_params)); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_init_wifi_hal(priv); +#endif + } +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + priv->bss_started = MFALSE; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + memset(&priv->chan, 0, sizeof(struct cfg80211_chan_def)); + memset(&priv->csa_chan, 0, sizeof(struct cfg80211_chan_def)); + priv->uap_tx_blocked = MFALSE; + memset(&priv->beacon_after, 0, + sizeof(struct cfg80211_beacon_data)); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_init_wifi_hal(priv); +#endif +#endif + } +#endif + + memset(&priv->tx_protocols, 0, sizeof(dot11_protocol)); + memset(&priv->rx_protocols, 0, sizeof(dot11_protocol)); + priv->media_connected = MFALSE; + + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->assocresp_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; +#endif +#ifdef STA_SUPPORT +#endif + + priv->enable_tcp_ack_enh = MTRUE; + + priv->gtk_data_ready = MFALSE; + memset(&priv->gtk_rekey_data, 0, sizeof(mlan_ds_misc_gtk_rekey_data)); + + woal_request_get_fw_info(priv, wait_option, NULL); + + /* Set MAC address from the insmod command line */ + if (priv->phandle->set_mac_addr) { + memset(priv->current_addr, 0, ETH_ALEN); + moal_memcpy_ext(priv->phandle, priv->current_addr, + priv->phandle->mac_addr, ETH_ALEN, ETH_ALEN); + } + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +#ifdef MFG_CMD_SUPPORT + if (priv->phandle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if (priv->bss_virtual) { + if (priv->pa_netdev) { + moal_memcpy_ext( + priv->phandle, + priv->current_addr, + priv->pa_netdev->dev_addr, + ETH_ALEN, ETH_ALEN); + priv->current_addr[4] ^= 0x80; + if (priv->phandle->second_mac) + priv->current_addr[5] ^= 0x80; + PRINTM(MCMND, + "Set WFD interface addr: " MACSTR + "\n", + MAC2STR(priv->current_addr)); + } + } else { + priv->current_addr[0] |= 0x02; + PRINTM(MCMND, + "Set WFD device addr: " MACSTR "\n", + MAC2STR(priv->current_addr)); + } + } +#endif +#endif +#endif + + /* Set MAC address for UAPx/MLANx/WFDx/OCBx and let them + * different with each other */ +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) +#endif + { + priv->current_addr[4] += priv->bss_index; + PRINTM(MCMND, "Set %s interface addr: " MACSTR "\n", + priv->netdev->name, MAC2STR(priv->current_addr)); + } + + woal_request_set_mac_address(priv, MOAL_IOCTL_WAIT); + moal_memcpy_ext(priv->phandle, priv->netdev->dev_addr, + priv->current_addr, ETH_ALEN, ETH_ALEN); + +#ifdef UAP_SUPPORT + priv->user_cac_period_msec = 0; +#endif + LEAVE(); +} + +/** + * @brief Reset all interfaces if all_intf flag is TRUE, + * otherwise specified interface only + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param all_intf TRUE : all interfaces + * FALSE : current interface only + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +int woal_reset_intf(moal_private *priv, t_u8 wait_option, int all_intf) +{ + int ret = MLAN_STATUS_SUCCESS; + int intf_num; + moal_handle *handle = NULL; + mlan_bss_info bss_info; + + ENTER(); + + if (!priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle = priv->phandle; + + if (handle->rf_test_mode) + woal_process_rf_test_mode(handle, MFG_CMD_UNSET_TEST_MODE); +#ifdef STA_SUPPORT + woal_cancel_scan(priv, wait_option); +#endif + + /* 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); + + /* Cancel host sleep */ + if (bss_info.is_hs_configured) { + if (MLAN_STATUS_SUCCESS != woal_cancel_hs(priv, wait_option)) { + ret = -EFAULT; + goto done; + } + } + + /* Disconnect from network */ + if (!all_intf) { + /* Disconnect specified interface only */ + if ((priv->media_connected == MTRUE) +#ifdef UAP_SUPPORT + || (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) +#endif + ) { + woal_disconnect(priv, wait_option, NULL, + DEF_DEAUTH_REASON_CODE); + priv->media_connected = MFALSE; + } + } else { + /* Disconnect all interfaces */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + if (handle->priv[intf_num]->media_connected == MTRUE +#ifdef UAP_SUPPORT + || (GET_BSS_ROLE(handle->priv[intf_num]) == + MLAN_BSS_ROLE_UAP) +#endif + ) { + woal_disconnect(handle->priv[intf_num], + wait_option, NULL, + DEF_DEAUTH_REASON_CODE); + handle->priv[intf_num]->media_connected = + MFALSE; + } + } + } + +#ifdef REASSOCIATION + /* Reset the reassoc timer and status */ + if (!all_intf) { + handle->reassoc_on &= ~MBIT(priv->bss_index); + priv->reassoc_on = MFALSE; + priv->set_asynced_essid_flag = MFALSE; + } else { + handle->reassoc_on = 0; + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + handle->priv[intf_num]->reassoc_on = MFALSE; + handle->priv[intf_num]->set_asynced_essid_flag = MFALSE; + } + } + if (!handle->reassoc_on && handle->is_reassoc_timer_set) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } +#endif /* REASSOCIATION */ + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + if (handle->is_go_timer_set) { + woal_cancel_timer(&handle->go_timer); + handle->is_go_timer_set = MFALSE; + } +#endif +#endif + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (handle->is_remain_timer_set) { + woal_cancel_timer(&handle->remain_timer); + woal_remain_timer_func(handle); + } +#endif +#endif + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function return the point to structure moal_private + * + * @param handle Pointer to structure moal_handle + * @param bss_index BSS index number + * + * @return moal_private pointer or NULL + */ +moal_private *woal_bss_index_to_priv(moal_handle *handle, t_u8 bss_index) +{ + int i; + + ENTER(); + if (!handle) { + LEAVE(); + return NULL; + } + for (i = 0; i < MLAN_MAX_BSS_NUM; i++) { + if (handle->priv[i] && + (handle->priv[i]->bss_index == bss_index)) { + LEAVE(); + return handle->priv[i]; + } + } + + LEAVE(); + return NULL; +} + +/** + * @brief This function alloc mlan_buffer. + * @param handle A pointer to moal_handle structure + * @param size buffer size to allocate + * + * @return mlan_buffer pointer or NULL + */ +pmlan_buffer woal_alloc_mlan_buffer(moal_handle *handle, int size) +{ + mlan_buffer *pmbuf = NULL; + struct sk_buff *skb; + gfp_t flag; + + ENTER(); + + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + if (size <= 0) { + PRINTM(MERROR, "Buffer size must be positive\n"); + LEAVE(); + return NULL; + } + + skb = __dev_alloc_skb(size + sizeof(mlan_buffer), flag); + if (!skb) { + PRINTM(MERROR, "%s: No free skb\n", __func__); + LEAVE(); + return NULL; + } + skb_reserve(skb, sizeof(mlan_buffer)); + pmbuf = (mlan_buffer *)skb->head; + memset((u8 *)pmbuf, 0, sizeof(mlan_buffer)); + pmbuf->pdesc = (t_void *)skb; + pmbuf->pbuf = (t_u8 *)skb->data; + atomic_inc(&handle->mbufalloc_count); + LEAVE(); + return pmbuf; +} + +/** + * @brief This function alloc mlan_ioctl_req. + * + * @param size buffer size to allocate + * + * @return mlan_ioctl_req pointer or NULL + */ +pmlan_ioctl_req woal_alloc_mlan_ioctl_req(int size) +{ + mlan_ioctl_req *req = NULL; + gfp_t flag; + + ENTER(); + + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + req = kzalloc((sizeof(mlan_ioctl_req) + size + sizeof(int) + + sizeof(wait_queue)), + flag); + if (!req) { + PRINTM(MERROR, "%s: Fail to alloc ioctl buffer\n", __func__); + LEAVE(); + return NULL; + } + req->pbuf = (t_u8 *)req + sizeof(mlan_ioctl_req) + sizeof(wait_queue); + req->buf_len = (t_u32)size; + req->reserved_1 = (t_ptr)((t_u8 *)req + sizeof(mlan_ioctl_req)); + + LEAVE(); + return req; +} + +/** + * @brief This function frees mlan_buffer. + * @param handle A pointer to moal_handle structure + * @param pmbuf Pointer to mlan_buffer + * + * @return N/A + */ +void woal_free_mlan_buffer(moal_handle *handle, pmlan_buffer pmbuf) +{ + ENTER(); + if (!pmbuf) { + LEAVE(); + return; + } + if (pmbuf->pdesc) + dev_kfree_skb_any((struct sk_buff *)pmbuf->pdesc); + else + PRINTM(MERROR, "free mlan buffer without pdesc\n"); + atomic_dec(&handle->mbufalloc_count); + LEAVE(); + return; +} + +/** + * @brief This function get card info from card type + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +static int woal_get_card_info(moal_handle *phandle) +{ + int ret = 0; + + ENTER(); + + switch (phandle->card_type) { +#ifdef SD8887 + case CARD_TYPE_SD8887: + phandle->card_info = &card_info_SD8887; + break; +#endif +#if defined(SD8897) + case CARD_TYPE_SD8897: + phandle->card_info = &card_info_SD8897; + break; +#endif +#if defined(PCIE8897) + case CARD_TYPE_PCIE8897: + phandle->card_info = &card_info_PCIE8897; + break; +#endif +#if defined(USB8897) + case CARD_TYPE_USB8897: + phandle->card_info = &card_info_USB8897; + break; +#endif +#ifdef SD8977 + case CARD_TYPE_SD8977: + phandle->card_info = &card_info_SD8977; + break; +#endif +#ifdef SD8978 + case CARD_TYPE_SD8978: + phandle->card_info = &card_info_SD8978; + break; +#endif +#ifdef SD8997 + case CARD_TYPE_SD8997: + phandle->card_info = &card_info_SD8997; + break; +#endif +#ifdef SD9098 + case CARD_TYPE_SD9098: + phandle->card_info = &card_info_SD9098; + break; +#endif +#ifdef SD9097 + case CARD_TYPE_SD9097: + phandle->card_info = &card_info_SD9097; + break; +#endif +#ifdef PCIE8997 + case CARD_TYPE_PCIE8997: + phandle->card_info = &card_info_PCIE8997; + break; +#endif +#ifdef PCIE9097 + case CARD_TYPE_PCIE9097: + phandle->card_info = &card_info_PCIE9097; + break; +#endif +#ifdef PCIE9098 + case CARD_TYPE_PCIE9098: + phandle->card_info = &card_info_PCIE9098; + phandle->event_fw_dump = MTRUE; + break; +#endif +#ifdef USB8997 + case CARD_TYPE_USB8997: + phandle->card_info = &card_info_USB8997; + break; +#endif +#ifdef USB8978 + case CARD_TYPE_USB8978: + phandle->card_info = &card_info_USB8978; + break; +#endif +#ifdef USB9098 + case CARD_TYPE_USB9098: + phandle->card_info = &card_info_USB9098; + break; +#endif +#ifdef USB9097 + case CARD_TYPE_USB9097: + phandle->card_info = &card_info_USB9097; + break; +#endif +#ifdef SD8987 + case CARD_TYPE_SD8987: + phandle->card_info = &card_info_SD8987; + break; +#endif + default: + PRINTM(MERROR, + "woal_get_card_info can't get right card type \n"); + ret = -1; + break; + } + + LEAVE(); + return ret; +} + +#ifdef STA_SUPPORT +#endif /* STA_SUPPORT */ + +/** + * @brief This function handles events generated by firmware + * + * @param priv A pointer to moal_private structure + * @param payload A pointer to payload buffer + * @param len Length of the payload + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_broadcast_event(moal_private *priv, t_u8 *payload, t_u32 len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + moal_handle *handle = priv->phandle; + struct net_device *netdev = priv->netdev; + struct sock *sk = handle->nl_sk; + + ENTER(); + + /* interface name to be prepended to event */ + if ((len + IFNAMSIZ) > NL_MAX_PAYLOAD +#ifdef WIFI_DIRECT_SUPPORT + * 2 +#endif + ) { + PRINTM(MERROR, "event size is too big, len=%d\n", (int)len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (sk) { + /* Allocate skb */ +#ifdef WIFI_DIRECT_SUPPORT + if ((len + IFNAMSIZ) > NL_MAX_PAYLOAD) { + skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD * 2), + GFP_ATOMIC); + if (!skb) { + PRINTM(MERROR, + "Could not allocate skb for netlink\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } else { +#endif + skb = alloc_skb(NLMSG_SPACE(NL_MAX_PAYLOAD), + GFP_ATOMIC); + if (!skb) { + PRINTM(MERROR, + "Could not allocate skb for netlink\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#ifdef WIFI_DIRECT_SUPPORT + } +#endif + memset(skb->data, 0, NLMSG_SPACE(NL_MAX_PAYLOAD)); + + 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); + moal_memcpy_ext(handle, NLMSG_DATA(nlh), netdev->name, IFNAMSIZ, + nlh->nlmsg_len - NLMSG_LENGTH(0)); + moal_memcpy_ext(handle, ((t_u8 *)(NLMSG_DATA(nlh))) + IFNAMSIZ, + payload, len, + nlh->nlmsg_len - NLMSG_LENGTH(IFNAMSIZ)); + + /* From Kernel */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + NETLINK_CB(skb).pid = 0; +#else + NETLINK_CB(skb).portid = 0; +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + /* Multicast message */ + NETLINK_CB(skb).dst_pid = 0; +#endif + + /* Multicast group number */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + NETLINK_CB(skb).dst_groups = NL_MULTICAST_GROUP; +#else + NETLINK_CB(skb).dst_group = NL_MULTICAST_GROUP; +#endif + + /* Send message */ + ret = netlink_broadcast(sk, skb, 0, NL_MULTICAST_GROUP, + GFP_ATOMIC); + if (ret) { + PRINTM(MWARN, "netlink_broadcast failed: ret=%d\n", + ret); + goto done; + } + + ret = MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, + "Could not send event through NETLINK. Link down.\n"); + ret = MLAN_STATUS_FAILURE; + } +done: + LEAVE(); + return ret; +} + +#ifdef REASSOCIATION +/** + * @brief This function handles re-association. it is triggered + * by re-assoc timer. + * + * @param data A pointer to wlan_thread structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +int woal_reassociation_thread(void *data) +{ + moal_thread *pmoal_thread = data; + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)pmoal_thread->handle; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 13, 0) + wait_queue_t wait; +#else + wait_queue_entry_t wait; +#endif + int i; + BOOLEAN reassoc_timer_req; + mlan_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; + mlan_status status; + mlan_bss_info bss_info; + t_u32 timer_val = MOAL_TIMER_10S; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + ENTER(); + + woal_activate_thread(pmoal_thread); + init_waitqueue_entry(&wait, current); + + current->flags |= PF_NOFREEZE; + + for (;;) { + add_wait_queue(&pmoal_thread->wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + schedule(); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&pmoal_thread->wait_q, &wait); +#if defined(USB) + if (IS_USB(handle->card_type)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 13) + try_to_freeze(0); /* Argument is not used by the kernel + */ +#else + try_to_freeze(); +#endif + } +#endif + + /* Cancel re-association timer */ + if (handle->is_reassoc_timer_set == MTRUE) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + + if (handle->surprise_removed) + break; + if (kthread_should_stop()) + break; + + if (handle->hardware_status != HardwareStatusReady) { + PRINTM(MINFO, + "Reassoc: Hardware status is not correct\n"); + continue; + } + + PRINTM(MEVENT, "Reassoc: Thread waking up...\n"); + reassoc_timer_req = MFALSE; +#ifdef STA_CFG80211 + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM) && + (priv = handle->priv[i]); + i++) { + if (priv->roaming_required) { + priv->roaming_required = MFALSE; + PRINTM(MEVENT, "Try to roaming......\n"); + woal_start_roaming(priv); + break; + } + } +#endif + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM) && + (priv = handle->priv[i]); + i++) { + if (priv->reassoc_required == MFALSE) { + priv->set_asynced_essid_flag = MFALSE; + continue; + } + + memset(&bss_info, 0x00, sizeof(bss_info)); + + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, + &bss_info)) { + PRINTM(MINFO, "Ressoc: Fail to get bss info\n"); + priv->reassoc_required = MFALSE; + priv->set_asynced_essid_flag = MFALSE; + continue; + } + + if (bss_info.bss_mode != MLAN_BSS_MODE_INFRA || + priv->media_connected != MFALSE) { + PRINTM(MINFO, + "Reassoc: ad-hoc mode or media connected\n"); + priv->reassoc_required = MFALSE; + priv->set_asynced_essid_flag = MFALSE; + continue; + } + /** avoid on going scan from other thread */ + if (handle->scan_pending_on_block) { + reassoc_timer_req = MTRUE; + break; + } + + /* The semaphore is used to avoid reassociation + thread and wlan_set_scan/wlan_set_essid + interrupting each other. Reassociation should + be disabled completely by application if + wlan_set_user_scan_ioctl/wlan_set_wap is + used. + */ + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, + "Acquire semaphore error, reassociation thread\n"); + reassoc_timer_req = MTRUE; + break; + } + + PRINTM(MINFO, "Reassoc: Required ESSID: %s\n", + priv->prev_ssid_bssid.ssid.ssid); + PRINTM(MINFO, "Reassoc: Performing Active Scan\n"); + + memset(&req_ssid, 0x00, sizeof(mlan_802_11_ssid)); + moal_memcpy_ext(priv->phandle, &req_ssid, + &priv->prev_ssid_bssid.ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, + &req_ssid)) { + PRINTM(MERROR, + "Reassoc: Fail to do specific scan\n"); + reassoc_timer_req = MTRUE; + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + break; + } + + if (handle->surprise_removed) { + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + break; + } + + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + if (priv->set_asynced_essid_flag == MTRUE) { + if (priv->assoc_with_mac && + memcmp(priv->prev_ssid_bssid.bssid, + zero_mac, MLAN_MAC_ADDR_LENGTH)) { + /* Search AP by BSSID & SSID */ + PRINTM(MINFO, + "Reassoc: Search AP by BSSID & SSID\n"); + moal_memcpy_ext( + priv->phandle, + &ssid_bssid.bssid, + &priv->prev_ssid_bssid.bssid, + MLAN_MAC_ADDR_LENGTH, + sizeof(mlan_802_11_mac_addr)); + } else { + /* Search AP by ESSID for asynced essid + * setting */ + PRINTM(MINFO, + "Set asynced essid: Search AP by ESSID\n"); + } + + moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid, + &priv->prev_ssid_bssid.ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + } else { + /* Search AP by BSSID first */ + PRINTM(MINFO, + "Reassoc: Search AP by BSSID first\n"); + moal_memcpy_ext(priv->phandle, + &ssid_bssid.bssid, + &priv->prev_ssid_bssid.bssid, + MLAN_MAC_ADDR_LENGTH, + sizeof(mlan_802_11_mac_addr)); + } + + status = woal_find_best_network(priv, MOAL_IOCTL_WAIT, + &ssid_bssid); +#ifdef STA_WEXT + if (status == MLAN_STATUS_SUCCESS) { + if (MLAN_STATUS_SUCCESS != + woal_11d_check_ap_channel(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "Reassoc: The AP's channel is invalid for current region\n"); + status = MLAN_STATUS_FAILURE; + } + } +#endif + /** The find AP without ssid, we need re-search + */ + if (status == MLAN_STATUS_SUCCESS && + !ssid_bssid.ssid.ssid_len) { + PRINTM(MINFO, + "Reassoc: Skip AP without ssid\n"); + status = MLAN_STATUS_FAILURE; + } + + if (priv->set_asynced_essid_flag != MTRUE && + MLAN_STATUS_SUCCESS != status) { + PRINTM(MINFO, + "Reassoc: AP not found in scan list\n"); + PRINTM(MINFO, "Reassoc: Search AP by SSID\n"); + /* Search AP by SSID */ + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid, + &priv->prev_ssid_bssid.ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + status = woal_find_best_network( + priv, MOAL_IOCTL_WAIT, &ssid_bssid); +#ifdef STA_WEXT + if (status == MLAN_STATUS_SUCCESS) { + if (MLAN_STATUS_SUCCESS != + woal_11d_check_ap_channel( + priv, MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "Reassoc: The AP's channel is invalid for current region\n"); + status = MLAN_STATUS_FAILURE; + } + } +#endif + } + + if (status == MLAN_STATUS_SUCCESS) { + /* set the wep key */ + if (bss_info.wep_status) + woal_enable_wep_key(priv, + MOAL_IOCTL_WAIT); + /* Zero SSID implies use BSSID to + * connect */ + memset(&ssid_bssid.ssid, 0, + sizeof(mlan_802_11_ssid)); + status = woal_bss_start(priv, MOAL_IOCTL_WAIT, + &ssid_bssid); + } + + if (priv->media_connected == MFALSE) + reassoc_timer_req = MTRUE; + else { + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + + reassoc_timer_req = MFALSE; + if (priv->set_asynced_essid_flag == MTRUE) { + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, + MOAL_IOCTL_WAIT, + &bss_info)) { + PRINTM(MINFO, + "Set asynced essid: Fail to get bss info after assoc\n"); + } else { + moal_memcpy_ext( + priv->phandle, + &priv->prev_ssid_bssid + .ssid, + &bss_info.ssid, + sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + moal_memcpy_ext( + priv->phandle, + &priv->prev_ssid_bssid + .bssid, + &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH, + sizeof(priv->prev_ssid_bssid + .bssid)); + } + priv->set_asynced_essid_flag = MFALSE; + } + if (priv->rate_index != AUTO_RATE) { + req = woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_rate)); + + if (req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + rate = (mlan_ds_rate *)req->pbuf; + rate->param.rate_cfg.rate_type = + MLAN_RATE_INDEX; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + + req->action = MLAN_ACT_SET; + + rate->param.rate_cfg.rate = + priv->rate_index; + + status = woal_request_ioctl( + priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + if (status != + MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + kfree(req); + } + } + + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); + } + + if (handle->surprise_removed) + break; + + if (reassoc_timer_req == MTRUE) { + handle->is_reassoc_timer_set = MTRUE; + if (priv && (priv->set_asynced_essid_flag == MTRUE)) { + PRINTM(MERROR, + "Set Async ESSID: No AP found or assoc failed.\n"); + priv->set_asynced_essid_flag = MFALSE; + } else { + PRINTM(MEVENT, + "Reassoc: No AP found or assoc failed. " + "Restarting re-assoc Timer: %d\n", + (int)timer_val); + woal_mod_timer(&handle->reassoc_timer, + timer_val); + } + } else { + if (priv) { + priv->set_asynced_essid_flag = MFALSE; + } + } + } + woal_deactivate_thread(pmoal_thread); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function triggers re-association by waking up + * re-assoc thread. + * + * @param context A pointer to context + * @return N/A + */ +void woal_reassoc_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + + ENTER(); + + PRINTM(MINFO, "reassoc_timer fired.\n"); + handle->is_reassoc_timer_set = MFALSE; + + PRINTM(MINFO, "Waking Up the Reassoc Thread\n"); + wake_up_interruptible(&handle->reassoc_thread.wait_q); + + LEAVE(); + return; +} +#endif /* REASSOCIATION */ + +#ifdef STA_SUPPORT +/** + * @brief update dscp mapping from assoc_resp/reassoc_resp + * + * @param priv Pointer to the moal_private driver data struct + * + * @return N/A + */ +void woal_update_dscp_mapping(moal_private *priv) +{ + mlan_ds_misc_assoc_rsp assoc_rsp; + IEEEtypes_AssocRsp_t *passoc_rsp = NULL; + IEEEtypes_Header_t *qos_mapping_ie = NULL; + DSCP_Range_t *pdscp_range = NULL; + t_u8 dscp_except_num = 0; + DSCP_Exception_t dscp_except[MAX_DSCP_EXCEPTION_NUM]; + int i, j; + ENTER(); + + memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp)); + woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_NO_WAIT); + passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp.assoc_resp_buf; + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + qos_mapping_ie = (IEEEtypes_Header_t *)woal_parse_ie_tlv( + passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - ASSOC_RESP_FIXED_SIZE, QOS_MAPPING); + if (qos_mapping_ie && + (qos_mapping_ie->len >= (sizeof(DSCP_Range_t) * MAX_NUM_TID))) { + dscp_except_num = (qos_mapping_ie->len - + sizeof(DSCP_Range_t) * MAX_NUM_TID) / + sizeof(DSCP_Exception_t); + if (dscp_except_num > MAX_DSCP_EXCEPTION_NUM) { + PRINTM(MERROR, "dscp_except_num exceeds MAX limit\n"); + LEAVE(); + return; + } + moal_memcpy_ext(priv->phandle, dscp_except, + (t_u8 *)qos_mapping_ie + + sizeof(IEEEtypes_Header_t), + dscp_except_num * sizeof(DSCP_Exception_t), + sizeof(dscp_except)); + pdscp_range = + (DSCP_Range_t *)((t_u8 *)qos_mapping_ie + + sizeof(IEEEtypes_Header_t) + + dscp_except_num * + sizeof(DSCP_Exception_t)); + for (i = 0; i < MAX_NUM_TID; i++) { + PRINTM(MEVENT, "TID %d: dscp_low=%d, dscp_high=%d\n", i, + pdscp_range->dscp_low_value, + pdscp_range->dscp_high_value); + if (pdscp_range->dscp_low_value != 0xff && + pdscp_range->dscp_high_value != 0xff && + pdscp_range->dscp_high_value <= 63) { + for (j = pdscp_range->dscp_low_value; + j <= pdscp_range->dscp_high_value; j++) + priv->dscp_map[j] = i; + } + pdscp_range++; + } + for (i = 0; i < dscp_except_num; i++) { + if ((dscp_except[i].dscp_value <= 63) && + (dscp_except[i].user_priority <= 7)) { + PRINTM(MEVENT, + "dscp excpt: value=%d priority=%d\n", + dscp_except[i].dscp_value, + dscp_except[i].user_priority); + priv->dscp_map[dscp_except[i].dscp_value] = + dscp_except[i].user_priority; + } + } + } + LEAVE(); +} + +/** + * @brief Sends disconnect event + * + * @param priv A pointer to moal_private struct + * @param disconnect_reason disconnect reason code + * @return N/A + */ +t_void woal_send_disconnect_to_system(moal_private *priv, + t_u16 disconnect_reason) +{ + int custom_len = 0; + t_u8 event_buf[32]; +#ifdef STA_WEXT + union iwreq_data wrqu; +#endif +#ifdef STA_CFG80211 + unsigned long flags; +#endif + int cfg80211_wext = priv->phandle->params.cfg80211_wext; +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + mlan_ds_misc_gtk_rekey_data zero_gtk; +#endif +#endif + t_u16 reason_code = 0; + + ENTER(); + priv->media_connected = MFALSE; + if (!disconnect_reason) + reason_code = MLAN_REASON_DEAUTH_LEAVING; + else + reason_code = disconnect_reason; + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + woal_flush_tcp_sess_queue(priv); + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + priv->gtk_data_ready = MFALSE; + memset(&zero_gtk, 0x00, sizeof(zero_gtk)); + if (priv->phandle->params.gtk_rekey_offload == + GTK_REKEY_OFFLOAD_ENABLE && + memcmp(&priv->gtk_rekey_data, &zero_gtk, + sizeof(priv->gtk_rekey_data)) != 0) { + PRINTM(MCMND, "clear GTK in woal_send_disconnect_to_system\n"); + woal_set_rekey_data(priv, NULL, MLAN_ACT_CLEAR, MOAL_NO_WAIT); + } + memset(&priv->gtk_rekey_data, 0, sizeof(mlan_ds_misc_gtk_rekey_data)); +#endif +#endif + +#ifdef STA_CFG80211 + if (priv->bss_type == MLAN_BSS_TYPE_STA && + IS_STA_CFG80211(cfg80211_wext)) { + woal_flush_pmksa_list(priv); + if (priv->okc_roaming_ie) { + kfree(priv->okc_roaming_ie); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } + } +#endif + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_hist_data_reset(priv); + +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->netdev, SIOCGIWAP, &wrqu, NULL); + } +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + spin_lock_irqsave(&priv->connect_lock, flags); + if (!priv->cfg_disconnect && !priv->cfg_connect && priv->wdev && + priv->wdev->current_bss) { + PRINTM(MMSG, + "wlan: Disconnected from " MACSTR + ": Reason code %d\n", + MAC2STR(priv->cfg_bssid), reason_code); + spin_unlock_irqrestore(&priv->connect_lock, flags); + priv->cfg_disconnect = MTRUE; + /* 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 */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (priv->host_mlme) + woal_host_mlme_disconnect(priv, reason_code, + NULL); + else +#endif + cfg80211_disconnected(priv->netdev, reason_code, + NULL, 0, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + false, +#endif + GFP_KERNEL); + } else { + spin_unlock_irqrestore(&priv->connect_lock, flags); + } + if (!woal_is_any_interface_active(priv->phandle)) + woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + SPECIFIC_SCAN_CHAN_TIME); + priv->ft_ie_len = 0; + priv->ft_pre_connect = MFALSE; + priv->ft_md = 0; + priv->ft_cap = 0; + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + } +#endif /* STA_CFG80211 */ + + memset(event_buf, 0, sizeof(event_buf)); + custom_len = strlen(CUS_EVT_AP_CONNECTED); + strncpy(event_buf, CUS_EVT_AP_CONNECTED, + MIN((sizeof(event_buf) - 1), custom_len)); + woal_broadcast_event(priv, event_buf, custom_len + ETH_ALEN); + LEAVE(); +} +#endif /* STA_SUPPORT */ + +#if defined(PCIE) +/** + * @brief This function stores the SSU dumps in a file + * + * @param phandle A pointer to moal_handle + * @param pmevent A pointer to mlan_event structure + * + * @return N/A + */ +t_void woal_store_ssu_dump(moal_handle *phandle, mlan_event *pmevent) +{ + struct dentry *dentry; + struct path path; + struct file *pfile_ssudump = NULL; + char dw_string[10]; + loff_t pos = 0; + t_u32 i; + t_u32 *tmpbuf; + + ENTER(); + dentry = kern_path_create(AT_FDCWD, "/data", &path, 1); + if (IS_ERR(dentry)) { + goto save_ssudump; + } + vfs_mkdir(path.dentry->d_inode, dentry, 0777); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) + mutex_unlock(&path.dentry->d_inode->i_mutex); +#else + inode_unlock(path.dentry->d_inode); +#endif + +save_ssudump: + pfile_ssudump = filp_open("/data/ssudump.txt", + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_ssudump)) { + PRINTM(MERROR, "Cannot create ssu dump file\n"); + LEAVE(); + return; + } + DBG_HEXDUMP(MEVT_D, "SSU addr", pmevent->event_buf, 8); + moal_memcpy_ext(phandle, + &tmpbuf, + pmevent->event_buf, + sizeof(t_ptr), + sizeof(t_ptr)); + PRINTM(MEVENT, "woal_store_ssu_dump: tmpbuf %p\n", tmpbuf); + for (i = 0; i < pmevent->event_len / 4; i++) { + if ((i + 1) % 8 == 0) + snprintf(dw_string, sizeof(dw_string), "%08x\n", + *tmpbuf); + else + snprintf(dw_string, sizeof(dw_string), "%08x ", + *tmpbuf); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile_ssudump, dw_string, 9, &pos); +#else + kernel_write(pfile_ssudump, dw_string, 9, &pos); +#endif + tmpbuf++; + } + filp_close(pfile_ssudump, NULL); + LEAVE(); + return; +} +#endif /* SSU_SUPPORT */ + +#define OFFSET_SEQNUM 4 +#define OFFSET_TYPE 8 +#define DUMP_TYPE_ENDE 2 +t_void woal_store_firmware_dump(moal_handle *phandle, mlan_event *pmevent) +{ + struct file *pfile_fwdump = NULL; + loff_t pos = 0; + t_u16 seqnum; + t_u16 type = 0; + t_u8 path_name[64]; + moal_handle *ref_handle = NULL; + + ENTER(); + if (phandle->fwdump_fname) + pfile_fwdump = filp_open(phandle->fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + else { + seqnum = woal_le16_to_cpu( + *(t_u16 *)(pmevent->event_buf + OFFSET_SEQNUM)); + type = woal_le16_to_cpu( + *(t_u16 *)(pmevent->event_buf + OFFSET_TYPE)); + + if (seqnum == 1) { + if (drvdbg & MFW_D) + drvdbg &= ~MFW_D; + phandle->fw_dump_len = 0; + PRINTM(MMSG, + "==== Start Receive FW dump event ====\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /** Create dump directort*/ + woal_create_dump_dir(phandle, path_name, + sizeof(path_name)); +#else + memset(path_name, 0, sizeof(path_name)); + strcpy(path_name, "/data"); +#endif + PRINTM(MMSG, "Firmware Dump directory name is %s\n", + path_name); + ref_handle = (moal_handle *)phandle->pref_mac; + if (ref_handle) + woal_dump_drv_info(ref_handle, path_name); + woal_dump_drv_info(phandle, path_name); + if (fwdump_fname) { + memset(fwdump_fname, 0, 64); + } else { + gfp_t flag; + flag = (in_atomic() || irqs_disabled()) ? + GFP_ATOMIC : + GFP_KERNEL; + fwdump_fname = kzalloc(64, flag); + } + sprintf(fwdump_fname, "%s/file_fwdump", path_name); + pfile_fwdump = + filp_open(fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile_fwdump)) { + memset(fwdump_fname, 0, 64); + sprintf(fwdump_fname, "%s/%s", "/var", + "file_fwdump"); + pfile_fwdump = + filp_open(fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, + 0644); + } + } else + pfile_fwdump = + filp_open(fwdump_fname, + O_CREAT | O_WRONLY | O_APPEND, 0644); + } + if (IS_ERR(pfile_fwdump)) { + PRINTM(MERROR, "Cannot create firmware dump file\n"); + LEAVE(); + return; + } + phandle->fw_dump_len += pmevent->event_len - OFFSET_SEQNUM; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile_fwdump, pmevent->event_buf + OFFSET_SEQNUM, + pmevent->event_len - OFFSET_SEQNUM, &pos); +#else + kernel_write(pfile_fwdump, pmevent->event_buf + OFFSET_SEQNUM, + pmevent->event_len - OFFSET_SEQNUM, &pos); +#endif + filp_close(pfile_fwdump, NULL); + if (type == DUMP_TYPE_ENDE) + PRINTM(MMSG, "==== FW DUMP END: %ld bytes ====\n", + (long int)phandle->fw_dump_len); + LEAVE(); + return; +} + +#define DRV_INFO_SIZE 0x60000 +#define DRV_INFO_PER_INTF 0x11000 +#define ROW_SIZE_16 16 +#define ROW_SIZE_32 32 + +/** + * @brief This function dump hex to file + * + * @param phandle A pointer to moal_handle + * @param buf A pointer to buffer to dump + * @param len lengh of buf + * @param ascii Whether add ascii at the end + * @param save_buf Buffer which is saved to + * + * @return The length of this log + */ +static int woal_save_hex_dump(int rowsize, const void *buf, size_t len, + bool ascii, t_u8 *save_buf) +{ + const u8 *ptr = buf; + int i, linelen, remaining = len; + unsigned char linebuf[ROW_SIZE_32 * 3 + 2 + ROW_SIZE_32 + 1]; + char *pos = (char *)save_buf; + + if (rowsize != ROW_SIZE_16 && rowsize != ROW_SIZE_32) + rowsize = ROW_SIZE_16; + + for (i = 0; i < len; i += rowsize) { + linelen = min(remaining, rowsize); + remaining -= rowsize; + + hex_dump_to_buffer(ptr + i, linelen, rowsize, 1, linebuf, + sizeof(linebuf), false); + + pos += sprintf(pos, "%s\n", linebuf); + } + + return pos - (char *)save_buf; +} + +/** + * @brief This function save moal_priv's debug log + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int woal_dump_priv_drv_info(moal_handle *handle, t_u8 *buf) +{ + char *ptr = (char *)buf; + int index; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + int i = 0; +#endif + moal_private *priv; + + ENTER(); + if (!handle || !buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + for (index = 0; index < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); + index++) { + priv = handle->priv[index]; + if (priv) { + ptr += sprintf(ptr, "[Interface : %s]\n", + priv->proc_entry_name); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + ptr += sprintf(ptr, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + ptr += sprintf(ptr, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + ptr += sprintf(ptr, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + ptr += sprintf(ptr, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); +#endif + ptr += sprintf(ptr, "Media state = \"%s\"\n", + ((priv->media_connected == MFALSE) ? + "Disconnected" : + "Connected")); + ptr += sprintf(ptr, "carrier %s\n", + ((netif_carrier_ok(priv->netdev)) ? + "on" : + "off")); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < (priv->netdev->num_tx_queues); i++) { + ptr += sprintf( + ptr, "tx queue %d: %s\n", i, + ((netif_tx_queue_stopped( + netdev_get_tx_queue( + priv->netdev, i))) ? + "stopped" : + "started")); + } +#else + ptr += sprintf(ptr, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? + "stopped" : + "started")); +#endif + ptr += sprintf(ptr, "%s: num_tx_timeout = %d\n", + priv->netdev->name, + priv->num_tx_timeout); + } + } + + LEAVE(); + return ptr - (char *)buf; +} + +/** + * @brief This function save moal_handle's info + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int woal_dump_moal_drv_info(moal_handle *phandle, t_u8 *buf) +{ + char *ptr; +#ifdef USB + struct usb_card_rec *cardp = NULL; +#endif + char str_buf[MLAN_MAX_VER_STR_LEN]; + + ENTER(); + if (!phandle || !buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } +#ifdef USB + if (IS_USB(phandle->card_type)) + cardp = (struct usb_card_rec *)phandle->card; +#endif + ptr = (char *)buf; + ptr += sprintf(ptr, "------------moal_debug_info-------------\n"); + woal_get_version(phandle, str_buf, sizeof(str_buf) - 1); + ptr += sprintf(ptr, "Driver version = %s\n", str_buf); + ptr += sprintf(ptr, "main_state = %d\n", phandle->main_state); +#ifdef USB + if (IS_USB(phandle->card_type)) { + ptr += sprintf(ptr, "tx_cmd_urb_pending = %d\n", + atomic_read(&cardp->tx_cmd_urb_pending)); + ptr += sprintf(ptr, "tx_data_urb_pending = %d\n", + atomic_read(&cardp->tx_data_urb_pending)); +#ifdef USB_CMD_DATA_EP + ptr += sprintf(ptr, "rx_cmd_urb_pending = %d\n", + atomic_read(&cardp->rx_cmd_urb_pending)); +#endif + ptr += sprintf(ptr, "rx_data_urb_pending = %d\n", + atomic_read(&cardp->rx_data_urb_pending)); + } +#endif + ptr += sprintf(ptr, "ioctl_pending = %d\n", + atomic_read(&phandle->ioctl_pending)); + ptr += sprintf(ptr, "tx_pending = %d\n", + atomic_read(&phandle->tx_pending)); + ptr += sprintf(ptr, "rx_pending = %d\n", + atomic_read(&phandle->rx_pending)); + ptr += sprintf(ptr, "lock_count = %d\n", + atomic_read(&phandle->lock_count)); + ptr += sprintf(ptr, "malloc_count = %d\n", + atomic_read(&phandle->malloc_count)); + ptr += sprintf(ptr, "mbufalloc_count = %d\n", + atomic_read(&phandle->mbufalloc_count)); +#ifdef PCIE + if (IS_PCIE(phandle->card_type)) { + ptr += sprintf(ptr, "malloc_cons_count = %d\n", + atomic_read(&phandle->malloc_cons_count)); + } +#endif + ptr += sprintf(ptr, "hs_skip_count = %u\n", phandle->hs_skip_count); + ptr += sprintf(ptr, "hs_force_count = %u\n", phandle->hs_force_count); + + ptr += woal_dump_priv_drv_info(phandle, ptr); + ptr += sprintf(ptr, "------------moal_debug_info End-------------\n"); + + if (phandle->ops.dump_reg_info) + ptr += phandle->ops.dump_reg_info(phandle, ptr); + + LEAVE(); + return ptr - (char *)buf; +} + +/** + * @brief This function save mlan's info + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int woal_dump_mlan_drv_info(moal_private *priv, t_u8 *buf) +{ + char *ptr = (char *)buf; + int i; +#ifdef SDIO + int j; + t_u8 mp_aggr_pkt_limit = 0; +#endif + char str[11 * DBG_CMD_NUM + 1] = {0}; + char *s; + mlan_debug_info *info = NULL; + + ENTER(); + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or priv->phandle is null\n"); + LEAVE(); + return 0; + } + info = &(priv->phandle->debug_info); + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) { + PRINTM(MERROR, + "Could not retrieve debug information from MLAN\n"); + LEAVE(); + return 0; + } + ptr += sprintf(ptr, "------------mlan_debug_info-------------\n"); + ptr += sprintf(ptr, "mlan_processing =%d\n", info->mlan_processing); + ptr += sprintf(ptr, "main_lock_flag =%d\n", info->main_lock_flag); + ptr += sprintf(ptr, "main_process_cnt =%d\n", info->main_process_cnt); + ptr += sprintf(ptr, "delay_task_flag =%d\n", info->delay_task_flag); + ptr += sprintf(ptr, "mlan_rx_processing =%d\n", + info->mlan_rx_processing); + ptr += sprintf(ptr, "rx_pkts_queued =%d\n", info->rx_pkts_queued); + ptr += sprintf(ptr, "tx_pkts_queued =%d\n", info->tx_pkts_queued); + ptr += sprintf(ptr, "fw_hang_report = %d\n", info->fw_hang_report); + ptr += sprintf(ptr, "num_cmd_timeout = %d\n", info->num_cmd_timeout); + ptr += sprintf(ptr, "Timeout cmd id = 0x%x, act = 0x%x\n", + info->timeout_cmd_id, info->timeout_cmd_act); + ptr += sprintf(ptr, "last_cmd_index = %d\n", info->last_cmd_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_id[i]); + ptr += sprintf(ptr, "last_cmd_id = %s\n", str); + + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_act[i]); + + ptr += sprintf(ptr, "last_cmd_act = %s\n", str); + ptr += sprintf(ptr, "last_cmd_resp_index = %d\n", + info->last_cmd_resp_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_cmd_resp_id[i]); + + ptr += sprintf(ptr, "last_cmd_resp_id = %s\n", str); + ptr += sprintf(ptr, "last_event_index = %d\n", info->last_event_index); + for (s = str, i = 0; i < DBG_CMD_NUM; i++) + s += sprintf(s, "0x%x ", info->last_event[i]); + + ptr += sprintf(ptr, "last_event = %s\n", str); + ptr += sprintf(ptr, "num_data_h2c_failure = %d\n", + info->num_tx_host_to_card_failure); + ptr += sprintf(ptr, "num_cmd_h2c_failure = %d\n", + info->num_cmd_host_to_card_failure); + ptr += sprintf(ptr, "num_alloc_buffer_failure = %d\n", + info->num_alloc_buffer_failure); + ptr += sprintf(ptr, "num_pkt_dropped = %d\n", info->num_pkt_dropped); +#ifdef SDIO + if (IS_SD(priv->phandle->card_type)) { + ptr += sprintf(ptr, "num_data_c2h_failure = %d\n", + info->num_rx_card_to_host_failure); + ptr += sprintf(ptr, "num_cmdevt_c2h_failure = %d\n", + info->num_cmdevt_card_to_host_failure); + ptr += sprintf(ptr, "num_int_read_failure = %d\n", + info->num_int_read_failure); + ptr += sprintf(ptr, "last_int_status = %d\n", + info->last_int_status); + ptr += sprintf(ptr, "mp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + (unsigned int)info->mp_rd_bitmap, + info->curr_rd_port); + ptr += sprintf(ptr, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + (unsigned int)info->mp_wr_bitmap, + info->curr_wr_port); + ptr += sprintf(ptr, "mp_invalid_update=%d\n", + info->mp_invalid_update); + mp_aggr_pkt_limit = info->mp_aggr_pkt_limit; + ptr += sprintf(ptr, + "last_recv_wr_bitmap=0x%x last_mp_index = %d\n", + info->last_recv_wr_bitmap, info->last_mp_index); + for (i = 0; i < SDIO_MP_DBG_NUM; i++) { + for (s = str, j = 0; j < mp_aggr_pkt_limit; j++) + s += sprintf( + s, "0x%02x ", + info->last_mp_wr_info + [i * mp_aggr_pkt_limit + j]); + + ptr += sprintf( + ptr, + "mp_wr_bitmap: 0x%x mp_wr_ports=0x%x len=%d curr_wr_port=0x%x\n%s\n", + info->last_mp_wr_bitmap[i], + info->last_mp_wr_ports[i], + info->last_mp_wr_len[i], + info->last_curr_wr_port[i], str); + } + } +#endif +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + ptr += sprintf(ptr, "txbd_rdptr=0x%x txbd_wrptr=0x%x\n", + info->txbd_rdptr, info->txbd_wrptr); + ptr += sprintf(ptr, "rxbd_rdptr=0x%x rxbd_wrptr=0x%x\n", + info->rxbd_rdptr, info->rxbd_wrptr); + ptr += sprintf(ptr, "eventbd_rdptr=0x%x event_wrptr=0x%x\n", + info->eventbd_rdptr, info->eventbd_wrptr); + ptr += sprintf(ptr, "last_wr_index:%d\n", + info->txbd_wrptr & (MLAN_MAX_TXRX_BD - 1)); + ptr += sprintf(ptr, "Tx pkt size:\n"); + for (i = 0; i < MLAN_MAX_TXRX_BD; i++) { + ptr += sprintf(ptr, "%04d ", info->last_tx_pkt_size[i]); + if (((i + 1) % 16) == 0) + ptr += sprintf(ptr, "\n"); + } + } +#endif + ptr += sprintf(ptr, "num_event_deauth = %d\n", info->num_event_deauth); + ptr += sprintf(ptr, "num_event_disassoc = %d\n", + info->num_event_disassoc); + ptr += sprintf(ptr, "num_event_link_lost = %d\n", + info->num_event_link_lost); + ptr += sprintf(ptr, "num_cmd_deauth = %d\n", info->num_cmd_deauth); + ptr += sprintf(ptr, "num_cmd_assoc_success = %d\n", + info->num_cmd_assoc_success); + ptr += sprintf(ptr, "num_cmd_assoc_failure = %d\n", + info->num_cmd_assoc_failure); + ptr += sprintf(ptr, "num_cons_assoc_failure = %d\n", + info->num_cons_assoc_failure); + ptr += sprintf(ptr, "cmd_resp_received = %d\n", + info->cmd_resp_received); + ptr += sprintf(ptr, "event_received = %d\n", info->event_received); + ptr += sprintf(ptr, "max_tx_buf_size = %d\n", info->max_tx_buf_size); + ptr += sprintf(ptr, "tx_buf_size = %d\n", info->tx_buf_size); + ptr += sprintf(ptr, "curr_tx_buf_size = %d\n", info->curr_tx_buf_size); + + ptr += sprintf(ptr, "data_sent=%d cmd_sent=%d\n", info->data_sent, + info->cmd_sent); + ptr += sprintf(ptr, "ps_mode=%d ps_state=%d\n", info->ps_mode, + info->ps_state); + ptr += sprintf( + ptr, "wakeup_dev_req=%d wakeup_tries=%d pm_wakeup_timeout=%d\n", + info->pm_wakeup_card_req, info->pm_wakeup_fw_try, + info->pm_wakeup_timeout); + ptr += sprintf(ptr, "hs_configured=%d hs_activated=%d\n", + info->is_hs_configured, info->hs_activated); + ptr += sprintf(ptr, "pps_uapsd_mode=%d sleep_pd=%d\n", + info->pps_uapsd_mode, info->sleep_pd); + ptr += sprintf(ptr, "tx_lock_flag = %d\n", info->tx_lock_flag); + ptr += sprintf(ptr, "port_open = %d\n", info->port_open); + ptr += sprintf(ptr, "scan_processing = %d\n", info->scan_processing); + +#ifdef PCIE + if (IS_PCIE(priv->phandle->card_type)) { + ptr += sprintf(ptr, "txbd: rdptr=0x%x wrptr=0x%x\n", + info->txbd_rdptr, info->txbd_wrptr); + ptr += sprintf(ptr, "rxbd: rdptr=0x%x wrptr=0x%x\n", + info->rxbd_rdptr, info->rxbd_wrptr); + ptr += sprintf(ptr, "eventbd: rdptr=0x%x wrptr=0x%x\n", + info->eventbd_rdptr, info->eventbd_wrptr); + ptr += sprintf(ptr, "TXBD Ring:\n"); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->txbd_ring_vbase, + info->txbd_ring_size, MTRUE, ptr); + ptr += sprintf(ptr, "RXBD Ring:\n"); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->rxbd_ring_vbase, + info->rxbd_ring_size, MTRUE, ptr); + ptr += sprintf(ptr, "EVTBD Ring:\n"); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->evtbd_ring_vbase, + info->evtbd_ring_size, MTRUE, ptr); + } +#endif + ptr += sprintf(ptr, "------------mlan_debug_info End-------------\n"); + + LEAVE(); + return ptr - (char *)buf; +} +#define HostCmd_CMD_CFG_DATA 0x008f +#define DEF_FW_PATH "/lib/firmware/" +#define DEF_HOSTCMD_PATH "/lib/firmware/nxp/hostcmd.conf" +/** + * @brief This function save the hostcmd response to file + * + * @param phandle A pointer to moal_handle + * @param pevent A pointer to mlan_cmdresp_event + * + * @return N/A + */ +t_void woal_save_host_cmdresp(moal_handle *phandle, mlan_cmdresp_event *pevent) +{ + HostCmd_DS_GEN *resp; + char file_path[256]; + struct file *pfile = NULL; + char *dpd_data_cfg = phandle->params.dpd_data_cfg; + int ret; + t_u8 *buf; + t_u16 command; + int len = 0; + char *ptr; + loff_t pos = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + + resp = (HostCmd_DS_GEN *)pevent->resp; + command = woal_le16_to_cpu(resp->command); + memset(file_path, 0, sizeof(file_path)); + ret = moal_vmalloc(phandle, pevent->event_len * 5, &buf); + if (ret != MLAN_STATUS_SUCCESS || !buf) { + PRINTM(MERROR, "Fail to allocate memory to save hostcmd\n"); + return; + } + memset(buf, 0, pevent->event_len * 5); + ptr = (char *)buf; + switch (command) { + case HostCmd_CMD_CFG_DATA: + if (dpd_data_cfg) + sprintf(file_path, "%s%s", DEF_FW_PATH, dpd_data_cfg); + else + sprintf(file_path, "%s", DEF_HOSTCMD_PATH); + break; + default: + sprintf(file_path, "%s", DEF_HOSTCMD_PATH); + break; + } + pfile = filp_open(file_path, O_CREAT | O_WRONLY | O_APPEND, 0644); + if (IS_ERR(pfile)) { + PRINTM(MERROR, "Cannot create file %s\n", file_path); + moal_vfree(phandle, buf); + return; + } + ptr += sprintf(ptr, "hostcmd_%02x=={\n", command); + ptr += woal_save_hex_dump(ROW_SIZE_16, resp, pevent->event_len, MFALSE, + ptr); + ptr += sprintf(ptr, "}\n"); + len = ptr - (char *)buf; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(pfile, buf, len, &pos); + set_fs(fs); +#else + kernel_write(pfile, buf, len, &pos); +#endif + PRINTM(MMSG, "Save hostcmd 0x%02x, cmd len=%d file len=%d to file %s\n", + command, pevent->event_len, len, file_path); + if (buf) + moal_vfree(phandle, buf); + filp_close(pfile, NULL); + return; +} + +/** + * @brief This function dump moal hex to file + * + * @param phandle A pointer to moal_handle + * @param buf A pointer to buffer + * + * @return The length of this log + */ +static int woal_dump_moal_hex(moal_handle *phandle, t_u8 *buf) +{ + char *ptr = (char *)buf; + int i; + ENTER(); + + if (!phandle || !buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + + ptr += sprintf(ptr, "<--moal_handle-->\n"); + ptr += sprintf(ptr, "moal_handle=%p, size=%ld(0x%lx)\n", phandle, + (long int)sizeof(*phandle), + (long unsigned int)sizeof(*phandle)); + ptr += woal_save_hex_dump(ROW_SIZE_16, phandle, sizeof(*phandle), MTRUE, + ptr); + ptr += sprintf(ptr, "<--moal_handle End-->\n"); + + for (i = 0; i < phandle->priv_num; i++) { + ptr += sprintf(ptr, "<--moal_private(%d)-->\n", i); + ptr += sprintf(ptr, "moal_private=%p, size=%ld(0x%lx)\n", + phandle->priv[i], + (long int)sizeof(*(phandle->priv[i])), + (long unsigned int)sizeof(*(phandle->priv[i]))); + ptr += woal_save_hex_dump(ROW_SIZE_16, phandle->priv[i], + sizeof(*(phandle->priv[i])), MTRUE, + ptr); + ptr += sprintf(ptr, "<--moal_private(%d) End-->\n", i); + } + LEAVE(); + return ptr - (char *)buf; +} + +/** + * @brief This function dump mlan hex to file + * + * @param priv A pointer to moal_private structure + * @param buf A pointer to buffer + * + * @return The length of this log + */ +static int woal_dump_mlan_hex(moal_private *priv, t_u8 *buf) +{ + char *ptr = (char *)buf; + int i; + mlan_debug_info *info = NULL; + + ENTER(); + + if (!buf || !priv || !priv->phandle) { + PRINTM(MERROR, "buf priv or priv->phandle is null\n"); + LEAVE(); + return 0; + } + info = &(priv->phandle->debug_info); + if (woal_get_debug_info(priv, MOAL_IOCTL_WAIT, info)) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + + ptr += sprintf(ptr, "<--mlan_adapter-->\n"); + ptr += sprintf(ptr, "mlan_adapter=%p, size=%d(0x%x)\n", + info->mlan_adapter, info->mlan_adapter_size, + info->mlan_adapter_size); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->mlan_adapter, + info->mlan_adapter_size, MTRUE, ptr); + ptr += sprintf(ptr, "<--mlan_adapter End-->\n"); +#ifdef SDIO + if (IS_SD(priv->phandle->card_type) && info->mpa_buf && + info->mpa_buf_size) { + ptr += sprintf(ptr, "<--mlan_mpa_buf-->\n"); + ptr += sprintf(ptr, "mlan_mpa_buf=%p, size=%d(0x%x)\n", + info->mpa_buf, info->mpa_buf_size, + info->mpa_buf_size); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->mpa_buf, + info->mpa_buf_size, MTRUE, ptr); + ptr += sprintf(ptr, "<--mlan_mpa_buf End-->\n"); + } +#endif + for (i = 0; i < info->mlan_priv_num; i++) { + ptr += sprintf(ptr, "<--mlan_private(%d)-->\n", i); + ptr += sprintf(ptr, "mlan_private=%p, size=%d(0x%x)\n", + info->mlan_priv[i], info->mlan_priv_size[i], + info->mlan_priv_size[i]); + ptr += woal_save_hex_dump(ROW_SIZE_16, info->mlan_priv[i], + info->mlan_priv_size[i], MTRUE, ptr); + ptr += sprintf(ptr, "<--mlan_private(%d) End-->\n", i); + } + + LEAVE(); + return ptr - (char *)buf; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +/** + * @brief This function create dump directory + * + * @param phandle A pointer to moal_handle + * @param dir_buf A pointer to dir_buf buffer + * @param buf_size Size of dir_buf buffer + * + * @return N/A + */ +void woal_create_dump_dir(moal_handle *phandle, char *dir_buf, int buf_size) +{ + struct dentry *dentry; + struct path path; + t_u32 sec, usec; + int ret; + + ENTER(); + + if (!phandle || !dir_buf) { + PRINTM(MERROR, "Can't create directory\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + moal_get_system_time(phandle, &sec, &usec); + memset(dir_buf, 0, buf_size); + sprintf(dir_buf, "%s%u", "/data/dump_", sec); + + dentry = kern_path_create(AT_FDCWD, dir_buf, &path, 1); + if (IS_ERR(dentry)) { + PRINTM(MERROR, + "Create directory %s error, try create dir in /var", + dir_buf); + memset(dir_buf, 0, buf_size); + sprintf(dir_buf, "%s%u", "/var/dump_", sec); + dentry = kern_path_create(AT_FDCWD, dir_buf, &path, 1); + } + if (IS_ERR(dentry)) { + PRINTM(MERROR, "Create directory %s error, use default folder", + dir_buf); + goto default_dir; + } + ret = vfs_mkdir(path.dentry->d_inode, dentry, 0777); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) + mutex_unlock(&path.dentry->d_inode->i_mutex); +#else + inode_unlock(path.dentry->d_inode); +#endif + + if (ret < 0) { + PRINTM(MERROR, + "Create directory failure, use default folder\n"); + PRINTM(MERROR, "Create directory failure, ret = %d\n", ret); + goto default_dir; + } else { + PRINTM(MMSG, "Create directory %s successfully\n", dir_buf); + goto done; + } + +default_dir: + memset(dir_buf, 0, buf_size); + sprintf(dir_buf, "%s", "/data"); +done: + LEAVE(); +} +#endif + +/** + * @brief This function save dump buf to file + * + * @param dir_name A pointer to directory name + * @param file_name A pointer to file name + * @param buf A pointer to dump data + * @param buf_len The length of dump buf + * + * @return SUCCESS OR FAILURE + */ +mlan_status woal_save_dump_info_to_file(char *dir_name, char *file_name, + t_u8 *buf, t_u32 buf_len) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct file *pfile = NULL; + t_u8 name[64]; + loff_t pos; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + + ENTER(); + + if (!dir_name || !file_name || !buf) { + PRINTM(MERROR, "Can't save dump info to file\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + memset(name, 0, sizeof(name)); + sprintf(name, "%s/%s", dir_name, file_name); + pfile = filp_open(name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MMSG, + "Create file %s error, try to save dump file in /var\n", + name); + memset(name, 0, sizeof(name)); + sprintf(name, "%s/%s", "/var", file_name); + pfile = filp_open(name, O_CREAT | O_RDWR, 0644); + } + if (IS_ERR(pfile)) { + PRINTM(MERROR, "Create Dump file for %s error\n", name); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + PRINTM(MMSG, "Dump data %s saved in %s\n", file_name, name); + + pos = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(pfile, buf, buf_len, &pos); + set_fs(fs); +#else + kernel_write(pfile, buf, buf_len, &pos); +#endif + filp_close(pfile, NULL); + + PRINTM(MMSG, "Dump data %s saved in %s successfully\n", file_name, + name); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function dump drv info to file + * + * @param phandle A pointer to moal_handle + * @param dir_name A pointer to directory name + * + * @return N/A + */ +void woal_dump_drv_info(moal_handle *phandle, t_u8 *dir_name) +{ + int ret = 0; + struct file *pfile = NULL; + t_u8 *drv_buf = NULL; + t_u8 file_name[64]; + t_u32 len = 0; + t_u32 total_len = 0; + moal_private *woal_handle = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + t_u32 drv_info_size = DRV_INFO_SIZE; + + ENTER(); + + PRINTM(MMSG, "=== START DRIVER INFO DUMP==="); + memset(file_name, 0, sizeof(file_name)); + if (phandle->second_mac) + sprintf(file_name, "%s/%s", dir_name, "file_drv_info_2"); + else + sprintf(file_name, "%s/%s", dir_name, "file_drv_info"); + pfile = filp_open(file_name, O_CREAT | O_RDWR, 0644); + if (IS_ERR(pfile)) { + PRINTM(MMSG, + "Create file %s error, try create /var/file_drv_info", + file_name); + pfile = filp_open("/var/file_drv_info", O_CREAT | O_RDWR, 0644); + } else { + PRINTM(MMSG, "DRV dump data in %s\n", file_name); + } + if (IS_ERR(pfile)) { + PRINTM(MMSG, "Create file_drv_info file failed\n"); + goto done; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); +#endif + woal_handle = woal_get_priv(phandle, MLAN_BSS_ROLE_ANY); + + if (woal_handle != NULL) { + if (phandle->priv_num > 3) + drv_info_size += + (phandle->priv_num - 3) * DRV_INFO_PER_INTF; + ret = moal_vmalloc(phandle, drv_info_size, &drv_buf); + if ((ret != MLAN_STATUS_SUCCESS) || !drv_buf) { + PRINTM(MERROR, "Error: vmalloc drv buffer failed!\n"); + goto done; + } + len = woal_dump_moal_drv_info(phandle, drv_buf); + total_len += len; + len = woal_dump_mlan_drv_info(woal_handle, drv_buf + total_len); + total_len += len; + len = woal_dump_moal_hex(phandle, drv_buf + total_len); + total_len += len; + len = woal_dump_mlan_hex(woal_handle, drv_buf + total_len); + total_len += len; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + vfs_write(pfile, drv_buf, total_len, &pfile->f_pos); +#else + kernel_write(pfile, drv_buf, total_len, &pfile->f_pos); +#endif + } + PRINTM(MMSG, "Drv info total bytes = %ld (0x%lx)\n", + (long int)total_len, (long unsigned int)total_len); + filp_close(pfile, NULL); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + set_fs(fs); +#endif + PRINTM(MMSG, "=== DRIVER INFO DUMP END==="); +done: + if (drv_buf) + moal_vfree(phandle, drv_buf); + LEAVE(); +} + +/** + * @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; +#ifdef USB + struct usb_card_rec *cardp = NULL; +#endif + char buf[MLAN_MAX_VER_STR_LEN]; + int i = 0; + + ENTER(); + + if (!priv) { + if (handle) { + phandle = handle; + } else { + PRINTM(MERROR, + "Could not retrieve debug information from MOAL\n"); + LEAVE(); + return; + } + } else { + phandle = priv->phandle; + } +#ifdef USB + if (IS_USB(phandle->card_type)) { + cardp = (struct usb_card_rec *)phandle->card; + PRINTM(MERROR, "tx_cmd_urb_pending = %d\n", + atomic_read(&cardp->tx_cmd_urb_pending)); + PRINTM(MERROR, "tx_data_urb_pending = %d\n", + atomic_read(&cardp->tx_data_urb_pending)); +#ifdef USB_CMD_DATA_EP + PRINTM(MERROR, "rx_cmd_urb_pending = %d\n", + atomic_read(&cardp->rx_cmd_urb_pending)); +#endif + PRINTM(MERROR, "rx_data_urb_pending = %d\n", + atomic_read(&cardp->rx_data_urb_pending)); + } +#endif + + woal_get_version(phandle, buf, sizeof(buf) - 1); + PRINTM(MERROR, "Driver version = %s\n", buf); + PRINTM(MERROR, "main_state = %d\n", phandle->main_state); + PRINTM(MERROR, "ioctl_pending = %d\n", + atomic_read(&phandle->ioctl_pending)); + PRINTM(MERROR, "tx_pending = %d\n", atomic_read(&phandle->tx_pending)); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + if (priv) { + PRINTM(MERROR, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + PRINTM(MERROR, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + PRINTM(MERROR, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + PRINTM(MERROR, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); + } +#endif + PRINTM(MERROR, "rx_pending = %d\n", atomic_read(&phandle->rx_pending)); + PRINTM(MERROR, "lock_count = %d\n", atomic_read(&phandle->lock_count)); + PRINTM(MERROR, "malloc_count = %d\n", + atomic_read(&phandle->malloc_count)); + PRINTM(MERROR, "mbufalloc_count = %d\n", + atomic_read(&phandle->mbufalloc_count)); +#ifdef PCIE + if (IS_PCIE(phandle->card_type)) { + PRINTM(MERROR, "malloc_cons_count = %d\n", + atomic_read(&phandle->malloc_cons_count)); + } +#endif + PRINTM(MERROR, "hs_skip_count = %u\n", phandle->hs_skip_count); + PRINTM(MERROR, "hs_force_count = %u\n", phandle->hs_force_count); + + if (priv && priv->netdev) { + PRINTM(MERROR, "Media state = \"%s\"\n", + ((priv->media_connected == MFALSE) ? "Disconnected" : + "Connected")); + PRINTM(MERROR, "carrier %s\n", + ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < (priv->netdev->num_tx_queues); i++) { + PRINTM(MERROR, "tx queue %d: %s\n", i, + ((netif_tx_queue_stopped( + netdev_get_tx_queue(priv->netdev, i))) ? + "stopped" : + "started")); + } +#else + PRINTM(MERROR, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? "stopped" : + "started")); +#endif + } + + for (i = 0; i < phandle->priv_num; i++) { + priv = phandle->priv[i]; + if (priv && priv->netdev) + PRINTM(MERROR, "%s: num_tx_timeout = %d\n", + priv->netdev->name, priv->num_tx_timeout); + } + + if (phandle->is_suspended == MTRUE) { + LEAVE(); + return; + } + +#ifdef PCIE + if (IS_PCIE(phandle->card_type)) { + if (phandle->ops.reg_dbg && (drvdbg & (MREG_D | MFW_D))) + phandle->ops.reg_dbg(phandle); + } +#endif +#ifdef SDIO + if (IS_SD(phandle->card_type)) { + if (flag && ((phandle->main_state == MOAL_END_MAIN_PROCESS) || + (phandle->main_state == MOAL_STATE_IDLE))) { + if (phandle->ops.reg_dbg && (drvdbg & (MREG_D | MFW_D))) + phandle->ops.reg_dbg(phandle); + } else { + if (drvdbg & (MREG_D | MFW_D)) { + phandle->reg_dbg = MTRUE; + queue_work(phandle->workqueue, + &phandle->main_work); + } + } + } +#endif +#ifdef DEBUG_LEVEL1 + if (drvdbg & MFW_D) { + drvdbg &= ~MFW_D; + phandle->fw_dbg = MTRUE; + queue_work(phandle->workqueue, &phandle->main_work); + } +#endif + LEAVE(); + return; +} + +/** + * @brief Download power table to firmware for a specific country + * + * @param priv A pointer to moal_private + * @param country ISO 3166-1 alpha-2 country code + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_request_country_power_table(moal_private *priv, char *country) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = NULL; + char country_name[] = "txpower_XX.bin"; + char file_path[256]; + char *last_slash = NULL; + char *fw_name = NULL; + + ENTER(); + + if (!priv || !priv->phandle) { + PRINTM(MERROR, "Priv or handle is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (!country) { + PRINTM(MERROR, "Country is null\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (fw_region) + strncpy(country_name, "rgpower_XX.bin", + strlen("rgpower_XX.bin")); +#endif +#endif + handle = priv->phandle; + + /* Replace XX with ISO 3166-1 alpha-2 country code */ + strncpy(strstr(country_name, "XX"), country, strlen(country)); + fw_name = handle->params.fw_name; + memset(file_path, 0, sizeof(file_path)); + /* file_path should be Null terminated */ + if (fw_name) { + strncpy(file_path, fw_name, sizeof(file_path) - 1); + last_slash = strrchr(file_path, '/'); + if (last_slash) + memset(last_slash + 1, 0, + sizeof(file_path) - 1 - + (last_slash - file_path)); + else + memset(file_path, 0, sizeof(file_path)); + } else { + strncpy(file_path, "nxp/", + MIN((sizeof(file_path) - 1), strlen("nxp/"))); + } + + if ((strlen(file_path) + strlen(country_name)) < + (sizeof(file_path) - 1)) + strncpy(file_path + strlen(file_path), country_name, + sizeof(file_path) - strlen(file_path)); + else { + PRINTM(MERROR, + "file path buffer too small, fail to dnld power table\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + PRINTM(MMSG, "Trying download country_power_tble: %s\n", file_path); + ret = woal_set_user_init_data(handle, COUNTRY_POWER_TABLE, + MOAL_IOCTL_WAIT, file_path); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + /* Try download WW rgpowertable */ + if (fw_region && (ret == MLAN_STATUS_FILE_ERR)) { + strncpy(country_name, "rgpower_WW.bin", + strlen("rgpower_WW.bin")); + last_slash = strrchr(file_path, '/'); + if (last_slash) + memset(last_slash + 1, 0, + sizeof(file_path) - 1 - + (last_slash - file_path)); + else + memset(file_path, 0, sizeof(file_path)); + strncpy(file_path + strlen(file_path), country_name, + strlen(country_name)); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + handle->country_code[0] = '0'; + handle->country_code[1] = '0'; +#endif + PRINTM(MMSG, "Trying again download country_power_tble: %s\n", + file_path); + ret = woal_set_user_init_data(handle, COUNTRY_POWER_TABLE, + MOAL_IOCTL_WAIT, file_path); + } +#endif +#endif + LEAVE(); + return ret; +} + +/** + * @brief napi polling call back function. + * + * @param napi A pointer to napi_struct + * @param budget the limit of packets driver should poll + * + * @return packets received + */ +int woal_netdev_poll_rx(struct napi_struct *napi, int budget) +{ + moal_handle *handle = container_of(napi, moal_handle, napi_rx); + t_u8 recv = budget; + + ENTER(); + if (handle->surprise_removed == MTRUE) { + napi_complete(napi); + LEAVE(); + return 0; + } + mlan_rx_process(handle->pmlan_adapter, &recv); + if (recv < budget) + napi_complete(napi); + LEAVE(); + return recv; +} + +/** + * @brief This workqueue function handles woal event queue + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void woal_evt_work_queue(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, evt_work); + struct woal_event *evt; + unsigned long flags; +#if defined(UAP_CFG80211) || defined(STA_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + moal_private *priv; +#endif +#endif + ENTER(); + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + spin_lock_irqsave(&handle->evt_lock, flags); + while (!list_empty(&handle->evt_queue)) { + evt = list_first_entry(&handle->evt_queue, struct woal_event, + link); + list_del(&evt->link); + spin_unlock_irqrestore(&handle->evt_lock, flags); + switch (evt->type) { + case WOAL_EVENT_CHAN_SWITCH: +#if defined(UAP_SUPPORT) || defined(STA_SUPPORT) +#if defined(UAP_CFG80211) || defined(STA_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + woal_cfg80211_notify_channel((moal_private *)evt->priv, + &evt->chan_info); +#endif +#endif +#endif + break; + case WOAL_EVENT_BGSCAN_STOP: +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + woal_cfg80211_notify_sched_scan_stop( + (moal_private *)evt->priv); +#endif +#endif + break; +#if defined(UAP_CFG80211) || defined(STA_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + case WOAL_EVENT_DEAUTH: + priv = evt->priv; + woal_host_mlme_disconnect(evt->priv, evt->reason_code, + priv->cfg_bssid); + break; + + case WOAL_EVENT_ASSOC_RESP: + woal_host_mlme_process_assoc_resp( + (moal_private *)evt->priv, &evt->assoc_resp); + break; +#endif +#endif + } + kfree(evt); + spin_lock_irqsave(&handle->evt_lock, flags); + } + spin_unlock_irqrestore(&handle->evt_lock, flags); + LEAVE(); +} +/** + * @brief This workqueue function handles rx_process + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void woal_rx_work_queue(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, rx_work); +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 14, 6) + moal_private *priv; +#endif +#endif +#endif + wifi_timeval start_timeval; + wifi_timeval end_timeval; + + ENTER(); + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + + woal_get_monotonic_time(&start_timeval); + mlan_rx_process(handle->pmlan_adapter, NULL); + + woal_get_monotonic_time(&end_timeval); + handle->rx_time += (t_u64)(timeval_to_usec(end_timeval) - + timeval_to_usec(start_timeval)); + PRINTM(MINFO, + "%s : start_timeval=%d:%d end_timeval=%d:%d inter=%llu rx_time=%llu\n", + __func__, start_timeval.time_sec, start_timeval.time_usec, + end_timeval.time_sec, end_timeval.time_usec, + (t_u64)(timeval_to_usec(end_timeval) - + timeval_to_usec(start_timeval)), + handle->rx_time); + LEAVE(); +} + +/** + * @brief This function dequeue pkt from list + * + * @param list A pointer to struct sk_buff_head + * + * @return skb buffer + */ + +struct sk_buff *woal_skb_dequeue_spinlock(struct sk_buff_head *list) +{ + struct sk_buff *result; + + spin_lock_bh(&list->lock); + result = __skb_dequeue(list); + spin_unlock_bh(&list->lock); + return result; +} + +/** + * @brief This workqueue function handles rx_work_process + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void woal_tx_work_handler(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, tx_work); + moal_private *priv = NULL; + int i = 0; + struct sk_buff *skb = NULL; + + ENTER(); + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + priv = handle->priv[i]; + while ((skb = woal_skb_dequeue_spinlock(&priv->tx_q)) != NULL) { + woal_start_xmit(priv, skb); + } + } + + LEAVE(); +} + +/** + * @brief This workqueue function handles main_process + * + * @param work A pointer to work_struct + * + * @return N/A + */ +t_void woal_main_work_queue(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, main_work); +#ifdef USB + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + struct sched_param sp = {.sched_priority = wq_sched_prio}; +#endif + + ENTER(); + + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + if (handle->reg_dbg == MTRUE) { + handle->reg_dbg = MFALSE; + if (handle->ops.reg_dbg) + handle->ops.reg_dbg(handle); + } + if (handle->fw_dbg == MTRUE) { + handle->fw_dbg = MFALSE; + handle->ops.dump_fw_info(handle); + LEAVE(); + return; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + /* Change the priority and scheduling policy of main work queue + */ + if ((handle->params.wq_sched_prio != current->rt_priority) || + (handle->params.wq_sched_policy != current->policy)) { + PRINTM(MMSG, + "Set work queue priority %d and scheduling policy %d\n", + handle->params.wq_sched_prio, + handle->params.wq_sched_policy); + sched_setscheduler(current, handle->params.wq_sched_policy, + &sp); + } +#endif + + handle->main_state = MOAL_ENTER_WORK_QUEUE; +#ifdef USB + /* WAR for no free skb issue */ + if (IS_USB(handle->card_type) && !atomic_read(&handle->rx_pending) && + atomic_read(&cardp->rx_data_urb_pending) < MVUSB_RX_DATA_URB) { + PRINTM(MWARN, "Try to resubmit Rx data URBs\n"); + woal_usb_submit_rx_data_urbs(handle); + } +#endif + handle->main_state = MOAL_START_MAIN_PROCESS; + /* Call MLAN main process */ + mlan_main_process(handle->pmlan_adapter); + handle->main_state = MOAL_END_MAIN_PROCESS; + + LEAVE(); +} + +/** + * @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 + * @param dev A pointer to device structure + * @param if_ops A pointer to interface ops + * @param card_type card type id + * + * @return A pointer to moal_handle structure + */ +moal_handle *woal_add_card(void *card, struct device *dev, moal_if_ops *if_ops, + t_u16 card_type) +{ + moal_handle *handle = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int netlink_num = NETLINK_NXP; + int index = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct netlink_kernel_cfg cfg = { + .groups = NL_MULTICAST_GROUP, + }; +#endif + + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] == NULL) + break; + } + if (index >= MAX_MLAN_ADAPTER) + goto err_handle; + + /* Allocate buffer for moal_handle */ + handle = kzalloc(sizeof(moal_handle), GFP_KERNEL); + if (!handle) { + PRINTM(MERROR, "Allocate buffer for moal_handle failed!\n"); + goto err_handle; + } + + /* Init moal_handle */ + handle->card = card; + + /* Save the handle */ + m_handle[index] = handle; + handle->handle_idx = index; + + handle->hotplug_device = dev; + handle->card_type = card_type; + /* Attach moal handle ops */ + PRINTM(MMSG, "Attach moal handle ops, card interface type: 0x%x\n", + handle->card_type); + moal_memcpy_ext(handle, &handle->ops, if_ops, sizeof(*if_ops), + sizeof(handle->ops)); + handle->second_mac = handle->ops.is_second_mac(handle); + if (handle->second_mac) { + if ((index >= 1) && m_handle[index - 1]) { + handle->pref_mac = (void *)m_handle[index - 1]; + m_handle[index - 1]->pref_mac = (void *)handle; + } + } + + /* Init module parameters */ + woal_init_module_param(handle); + + if (handle->params.mac_addr +#ifdef MFG_CMD_SUPPORT + && handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED +#endif + ) { + t_u8 temp[20]; + t_u8 len = strlen(handle->params.mac_addr) + 1; + if (len < sizeof(temp)) { + moal_memcpy_ext(handle, temp, handle->params.mac_addr, + len, sizeof(temp)); + handle->set_mac_addr = 1; + /* note: the following function overwrites the + * temp buffer */ + woal_mac2u8(handle->mac_addr, temp); + } + } + + /* Get card info */ + woal_get_card_info(handle); + /** Get card revision */ + handle->ops.get_fw_name(handle); +#ifdef STA_SUPPORT + handle->scan_pending_on_block = MFALSE; + MOAL_INIT_SEMAPHORE(&handle->async_sem); +#endif + +#if defined(USB) + if (IS_USB(handle->card_type)) + handle->boot_state = ((struct usb_card_rec *)card)->boot_state; +#endif /* USB_NEW_FW_DNLD */ + /* Init SW */ + if (MLAN_STATUS_SUCCESS != woal_init_sw(handle)) { + PRINTM(MFATAL, "Software Init Failed\n"); + goto err_kmalloc; + } + + do { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + handle->nl_sk = netlink_kernel_create(netlink_num, NULL); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) + handle->nl_sk = netlink_kernel_create( + netlink_num, NL_MULTICAST_GROUP, NULL, THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + handle->nl_sk = + netlink_kernel_create(netlink_num, NL_MULTICAST_GROUP, + NULL, NULL, THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + handle->nl_sk = netlink_kernel_create(&init_net, netlink_num, + NL_MULTICAST_GROUP, NULL, + NULL, THIS_MODULE); +#else +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) + handle->nl_sk = netlink_kernel_create(&init_net, netlink_num, + THIS_MODULE, &cfg); +#else + handle->nl_sk = + netlink_kernel_create(&init_net, netlink_num, &cfg); +#endif +#endif +#endif +#endif +#endif + if (handle->nl_sk) { + PRINTM(MINFO, "Netlink number = %d\n", netlink_num); + handle->netlink_num = netlink_num; + break; + } + netlink_num--; + } while (netlink_num > 0); + + if (handle->nl_sk == NULL) { + PRINTM(MERROR, + "Could not initialize netlink event passing mechanism!\n"); + goto err_kmalloc; + } + + /* Create workqueue for main process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be + * greater than 10 characters */ + handle->workqueue = create_workqueue("MOAL_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->workqueue = alloc_workqueue( + "MOAL_WORK_QUEUE", WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + handle->workqueue = create_workqueue("MOAL_WORK_QUEUE"); +#endif +#endif + if (!handle->workqueue) + goto err_kmalloc; + + MLAN_INIT_WORK(&handle->main_work, woal_main_work_queue); + + /* Create workqueue for event 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->evt_workqueue = create_workqueue("MOAL_EVT_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->evt_workqueue = + alloc_workqueue("MOAL_EVT_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + handle->evt_workqueue = create_workqueue("MOAL_EVT_WORK_QUEUE"); +#endif +#endif + if (!handle->evt_workqueue) { + woal_terminate_workqueue(handle); + goto err_kmalloc; + } + + MLAN_INIT_WORK(&handle->evt_work, woal_evt_work_queue); + INIT_LIST_HEAD(&handle->evt_queue); + spin_lock_init(&handle->evt_lock); + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + MLAN_INIT_WORK(&handle->host_mlme_work, woal_host_mlme_work_queue); +#endif +#endif + + if (!moal_extflg_isset(handle, EXT_NAPI)) { + /* Create workqueue for rx process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be + * greater than 10 characters */ + handle->rx_workqueue = create_workqueue("MOAL_RX_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->rx_workqueue = alloc_workqueue( + "MOAL_RX_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + handle->rx_workqueue = create_workqueue("MOAL_RX_WORK_QUEUE"); +#endif +#endif + if (!handle->rx_workqueue) { + woal_terminate_workqueue(handle); + goto err_kmalloc; + } + MLAN_INIT_WORK(&handle->rx_work, woal_rx_work_queue); + } +#define NAPI_BUDGET 64 + if (moal_extflg_isset(handle, EXT_NAPI)) { + init_dummy_netdev(&handle->napi_dev); + netif_napi_add(&handle->napi_dev, &handle->napi_rx, + woal_netdev_poll_rx, NAPI_BUDGET); + napi_enable(&handle->napi_rx); + } + + if (moal_extflg_isset(handle, EXT_TX_WORK)) { + /* Create workqueue for tx process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + handle->tx_workqueue = create_workqueue("MOAL_TX_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + handle->tx_workqueue = alloc_workqueue( + "MOAL_TX_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + handle->tx_workqueue = create_workqueue("MOAL_TX_WORK_QUEUE"); +#endif +#endif + if (!handle->tx_workqueue) { + woal_terminate_workqueue(handle); + goto err_kmalloc; + } + MLAN_INIT_WORK(&handle->tx_work, woal_tx_work_handler); + } + +#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 (handle->ops.register_dev(handle) != MLAN_STATUS_SUCCESS) { + PRINTM(MFATAL, "Failed to register wlan device!\n"); + goto err_registerdev; + } + woal_update_firmware_name(handle); +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_init(&handle->ws, "mwlan"); +#else + wake_lock_init(&handle->wake_lock, WAKE_LOCK_SUSPEND, "mwlan"); +#endif +#endif + + /* Init FW and HW */ + if (MLAN_STATUS_SUCCESS != woal_init_fw(handle)) { + PRINTM(MFATAL, "Firmware Init Failed\n"); + goto err_init_fw; + } + +#ifdef SD8887 + if (IS_SD8887(handle->card_type)) { + union { + t_u32 l; + t_u8 c[4]; + } ver; + ver.l = handle->fw_release_number; + if (ver.c[1] == 75) { + handle->card_info->embedded_supp = 0; + PRINTM(MMSG, + "Disable EMBEDED Supplicant for SD8887-FP75\n"); + } + } +#endif + LEAVE(); + return handle; + +err_init_fw: + if ((handle->hardware_status == HardwareStatusFwReady) || + (handle->hardware_status == HardwareStatusReady)) { + PRINTM(MINFO, "shutdown mlan\n"); + handle->init_wait_q_woken = MFALSE; + status = mlan_shutdown_fw(handle->pmlan_adapter); + if (status == MLAN_STATUS_PENDING) + wait_event_interruptible(handle->init_wait_q, + handle->init_wait_q_woken); + } +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_trash(&handle->ws); +#else + wake_lock_destroy(&handle->wake_lock); +#endif +#endif + /* Unregister device */ + PRINTM(MINFO, "unregister device\n"); + handle->ops.unregister_dev(handle); +err_registerdev: + handle->surprise_removed = MTRUE; +#ifdef REASSOCIATION + if (handle->reassoc_thread.pid) + wake_up_interruptible(&handle->reassoc_thread.wait_q); + /* waiting for main thread quit */ + while (handle->reassoc_thread.pid) + woal_sched_timeout(2); +#endif /* REASSOCIATION */ + if (moal_extflg_isset(handle, EXT_NAPI)) + netif_napi_del(&handle->napi_rx); + woal_terminate_workqueue(handle); +err_kmalloc: + woal_free_moal_handle(handle); + if (index < MAX_MLAN_ADAPTER) + m_handle[index] = NULL; +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; +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode == MLAN_INIT_PARA_ENABLED +#if defined(USB) + && handle->boot_state == USB_FW_READY +#endif + ) { + if (handle->params.fw_name) { + kfree(handle->params.fw_name); + handle->params.fw_name = NULL; + } + } +#endif + if (handle->rf_test_mode) + woal_process_rf_test_mode(handle, MFG_CMD_UNSET_TEST_MODE); + handle->surprise_removed = MTRUE; + + flush_workqueue(handle->workqueue); + flush_workqueue(handle->evt_workqueue); + if (handle->rx_workqueue) + flush_workqueue(handle->rx_workqueue); + if (handle->tx_workqueue) + flush_workqueue(handle->tx_workqueue); + + if (moal_extflg_isset(handle, EXT_NAPI)) { + napi_disable(&handle->napi_rx); + netif_napi_del(&handle->napi_rx); + } + + /* Stop data */ + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + priv = handle->priv[i]; + if (priv) { + if (priv->netdev) { + 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"); + } + /* Unregister mlan */ + if (handle->pmlan_adapter) { + mlan_unregister(handle->pmlan_adapter); + handle->pmlan_adapter = NULL; + } + 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)); + } + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (handle->is_remain_timer_set) { + woal_cancel_timer(&handle->remain_timer); + woal_remain_timer_func(handle); + } +#endif +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + if (handle->is_go_timer_set) { + woal_cancel_timer(&handle->go_timer); + handle->is_go_timer_set = MFALSE; + } +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + /* Remove virtual interface */ + woal_remove_virtual_interface(handle); +#endif +#endif +#endif + if (handle->tp_acnt.on) { + handle->tp_acnt.on = 0; + handle->tp_acnt.drop_point = 0; + if (handle->is_tp_acnt_timer_set) { + woal_cancel_timer(&handle->tp_acnt.timer); + handle->is_tp_acnt_timer_set = MFALSE; + } + } + + /* Remove interface */ + for (i = 0; i < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++) + woal_remove_interface(handle, i); + + woal_terminate_workqueue(handle); + +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (handle->is_cac_timer_set) { + woal_cancel_timer(&handle->cac_timer); + handle->is_cac_timer_set = MFALSE; + } +#endif +#endif +#ifdef REASSOCIATION + PRINTM(MINFO, "Free reassoc_timer\n"); + if (handle->is_reassoc_timer_set) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + if (handle->reassoc_thread.pid) + wake_up_interruptible(&handle->reassoc_thread.wait_q); + + /* waiting for main thread quit */ + while (handle->reassoc_thread.pid) + woal_sched_timeout(2); +#endif /* REASSOCIATION */ +#ifdef CONFIG_PROC_FS + woal_proc_exit(handle); +#endif + /* Unregister device */ + PRINTM(MINFO, "unregister device\n"); + handle->ops.unregister_dev(handle); +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_trash(&handle->ws); +#else + wake_lock_destroy(&handle->wake_lock); +#endif +#endif + /* Free adapter structure */ + PRINTM(MINFO, "Free Adapter\n"); + woal_free_moal_handle(handle); + + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] == handle) { + m_handle[index] = NULL; + break; + } + } +exit_remove: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef CONFIG_PROC_FS +/** + * @brief This function switch the drv_mode + * + * @param handle A pointer to moal_handle structure + * @param mode new drv_mode to switch. + * + * @return MLAN_STATUS_SUCCESS /MLAN_STATUS_FAILURE + * /MLAN_STATUS_PENDING + */ +mlan_status woal_switch_drv_mode(moal_handle *handle, t_u32 mode) +{ + unsigned int i; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + + ENTER(); + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + if (woal_update_drv_tbl(handle, mode) != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not update driver mode table!\n"); + status = MLAN_STATUS_FAILURE; + goto exit; + } + + /* Reset all interfaces */ + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE); + + status = woal_shutdown_fw(priv, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "func shutdown failed!\n"); + goto exit; + } +#ifdef USB + handle->skip_fw_dnld = MTRUE; +#endif + + /* 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"); + /* Unregister mlan */ + if (handle->pmlan_adapter) { + mlan_unregister(handle->pmlan_adapter); + handle->pmlan_adapter = NULL; + } + 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 < MIN(MLAN_MAX_BSS_NUM, handle->priv_num); i++) + woal_remove_interface(handle, i); + + if (atomic_read(&handle->lock_count) || + atomic_read(&handle->malloc_count) || + atomic_read(&handle->mbufalloc_count)) { + PRINTM(MERROR, + "mlan has memory leak: lock_count=%d, malloc_count=%d, mbufalloc_count=%d\n", + atomic_read(&handle->lock_count), + atomic_read(&handle->malloc_count), + atomic_read(&handle->mbufalloc_count)); + } +#ifdef PCIE + if (IS_PCIE(handle->card_type) && + atomic_read(&handle->malloc_cons_count)) { + PRINTM(MERROR, "mlan has memory leak: malloc_cons_count=%d\n", + atomic_read(&handle->malloc_cons_count)); + } +#endif + + handle->priv_num = 0; + handle->params.drv_mode = mode; + /* Init SW */ + if (woal_init_sw(handle)) { + PRINTM(MFATAL, "Software Init Failed\n"); + goto exit; + } + /* Init FW and HW */ + if (woal_init_fw(handle)) { + PRINTM(MFATAL, "Firmware Init Failed\n"); + goto exit; + } + LEAVE(); + return status; +exit: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + LEAVE(); + return status; +} +#endif + +#ifdef SDIO_MMC +#define FW_POLL_TRIES 100 + +/** + * @brief This function reload fw + * + * @param handle A pointer to moal_handle structure + * + * @return 0--success, otherwise failure + */ +static int woal_reset_and_reload_fw(moal_handle *handle) +{ + int ret = 0, tries = 0; + t_u32 value = 1; + t_u32 reset_reg = handle->card_info->fw_reset_reg; + t_u8 reset_val = handle->card_info->fw_reset_val; + + ENTER(); + + if (!IS_SD9098(handle->card_type) && !IS_SD9097(handle->card_type)) { + mlan_pm_wakeup_card(handle->pmlan_adapter, MTRUE); + /** wait SOC fully wake up */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + ret = handle->ops.write_reg(handle, reset_reg, 0xba); + if (ret == MLAN_STATUS_SUCCESS) { + handle->ops.read_reg(handle, reset_reg, &value); + if (value == 0xba) { + PRINTM(MMSG, "FW wake up\n"); + break; + } + } + udelay(1000); + } + } + /* Write register to notify FW */ + if (handle->ops.write_reg(handle, reset_reg, reset_val) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to write register.\n"); + ret = -EFAULT; + goto done; + } +#if defined(SD9098) || defined(SD9097) + if (IS_SD9098(handle->card_type) || IS_SD9097(handle->card_type)) + handle->ops.write_reg(handle, 0x00, 0x10); +#endif + /* Poll register around 100 ms */ + for (tries = 0; tries < FW_POLL_TRIES; ++tries) { + handle->ops.read_reg(handle, reset_reg, &value); + if (value == 0) + /* FW is ready */ + break; + udelay(1000); + } + + if (value) { + PRINTM(MERROR, "Failed to poll FW reset register %X=0x%x\n", + reset_reg, value); + ret = -EFAULT; + goto done; + } + if (!IS_SD9098(handle->card_type) && !IS_SD9097(handle->card_type)) + mlan_pm_wakeup_card(handle->pmlan_adapter, MFALSE); + /* Download FW */ + ret = woal_request_fw(handle); + if (ret) { + ret = -EFAULT; + goto done; + } + PRINTM(MMSG, "FW Reload successfully."); +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function reload fw + * + * @param handle A pointer to moal_handle structure + * + * @return 0--success, otherwise failure + */ +static int woal_reload_fw(moal_handle *handle) +{ + int ret = 0; + ENTER(); + /* Download FW */ + ret = woal_request_fw(handle); + if (ret) { + ret = -EFAULT; + goto done; + } + PRINTM(MMSG, "FW Reload successfully."); +done: + LEAVE(); + return ret; +} + +void woal_pre_reset(moal_handle *handle) +{ + int intf_num; + ENTER(); + /** detach network interface */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_stop_queue(handle->priv[intf_num]->netdev); + netif_device_detach(handle->priv[intf_num]->netdev); + } + handle->fw_reload = MTRUE; + woal_update_firmware_name(handle); +#ifdef USB + if (IS_USB(handle->card_type)) + woal_kill_urbs(handle); +#endif + LEAVE(); +} + +void woal_post_reset(moal_handle *handle) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int intf_num; +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + t_u8 bss_role = MLAN_BSS_ROLE_STA; +#endif +#endif +#endif /* WIFI_DIRECT_SUPPORT */ + + ENTER(); + /** un-block IOCTL */ + handle->fw_reload = MFALSE; + handle->driver_status = MFALSE; + /* Restart the firmware */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req) { + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_WARM_RESET; + misc->param.fw_reload = MTRUE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + if (MLAN_STATUS_SUCCESS != + woal_request_ioctl(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + req, MOAL_IOCTL_WAIT)) { + kfree(req); + goto done; + } + kfree(req); + } + handle->hardware_status = HardwareStatusReady; + /* Reset all interfaces */ + woal_reset_intf(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + MOAL_IOCTL_WAIT, MTRUE); + /* Initialize private structures */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + woal_init_priv(handle->priv[intf_num], MOAL_IOCTL_WAIT); +#ifdef WIFI_DIRECT_SUPPORT +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +#if defined(STA_WEXT) || defined(UAP_WEXT) + if ((handle->priv[intf_num]->bss_type == + MLAN_BSS_TYPE_WIFIDIRECT) && + (GET_BSS_ROLE(handle->priv[intf_num]) == + MLAN_BSS_ROLE_UAP)) { + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(handle->priv[intf_num], + MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &bss_role)) { + goto done; + } + } +#endif /* STA_WEXT || UAP_WEXT */ +#endif /* STA_SUPPORT && UAP_SUPPORT */ +#endif /* WIFI_DIRECT_SUPPORT */ + } + + /* Enable interfaces */ + for (intf_num = 0; intf_num < handle->priv_num; intf_num++) { + netif_device_attach(handle->priv[intf_num]->netdev); + woal_start_queue(handle->priv[intf_num]->netdev); + } +done: + LEAVE(); + return; +} + +/** + * @brief This function reload fw + * + * @param phandle A pointer to moal_handle structure + * @param mode FW reload mode + * + * @return 0--success, otherwise failure + */ +void woal_request_fw_reload(moal_handle *phandle, t_u8 mode) +{ + int ret = 0; + +#ifdef PCIE + ppcie_service_card card = NULL; + struct pci_dev *pdev = NULL; +#endif + moal_handle *handle = phandle; + moal_handle *ref_handle = NULL; + + ENTER(); +#ifdef PCIE + if (mode == FW_RELOAD_PCIE_RESET) { + card = (pcie_service_card *)handle->card; + pdev = card->dev; + pci_reset_function(pdev); + LEAVE(); + return; + } +#endif + + // handle-> mac0 , ref_handle->second mac + if (handle->pref_mac) { + if (phandle->second_mac) { + handle = (moal_handle *)handle->pref_mac; + ref_handle = phandle; + } else { + ref_handle = (moal_handle *)handle->pref_mac; + } + ref_handle->driver_status = MTRUE; + } + /** start block IOCTL */ + handle->driver_status = MTRUE; + + if (mode == FW_RELOAD_WITH_EMULATION) { + fw_reload = FW_RELOAD_WITH_EMULATION; + PRINTM(MMSG, "FW reload with re-emulation...\n"); + LEAVE(); + return; + } + woal_pre_reset(handle); + if (ref_handle) + woal_pre_reset(ref_handle); + if (mode == FW_RELOAD_NO_EMULATION) { + ret = woal_reload_fw(handle); + if (ref_handle) + woal_reload_fw(ref_handle); + } +#ifdef SDIO_MMC + else if (mode == FW_RELOAD_SDIO_INBAND_RESET && + IS_SD(handle->card_type)) { + ret = woal_reset_and_reload_fw(handle); + if (ref_handle) + woal_reload_fw(ref_handle); + } +#endif + else + ret = -EFAULT; + if (ret) { + PRINTM(MERROR, "FW reload fail\n"); + goto done; + } + woal_post_reset(handle); + if (ref_handle) + woal_post_reset(ref_handle); +done: + LEAVE(); + return; +} + +/** Register to bus driver function */ +static mlan_status woal_bus_register(void) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef SDIO +#ifdef SDIO_MMC + /* Register SDIO driver */ + ret = woal_sdiommc_bus_register(); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to register sdio_mmc bus driver\n"); + goto out; + } +#else + ret = woal_sdio_bus_register(); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to register sdio_mmc bus driver\n"); + goto out; + } +#endif +#endif +#ifdef USB + /* Register USB driver */ + ret = woal_usb_bus_register(); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to register usb bus driver\n"); + goto out; + } +#endif +#ifdef PCIE + /* Register PCIE driver */ + ret = woal_pcie_bus_register(); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Failed to register pcie bus driver\n"); + goto out; + } +#endif +out: + return ret; +} + +/** Unregister from bus driver function */ +static void woal_bus_unregister(void) +{ +#ifdef SDIO +#ifdef SDIO_MMC + /* Unregister SDIO driver */ + woal_sdiommc_bus_unregister(); +#else + woal_sdio_bus_unregister(); +#endif +#endif +#ifdef USB + /* Unregister USB driver */ + woal_usb_bus_unregister(); +#endif +#ifdef PCIE + /* Unregister PCIE driver */ + woal_pcie_bus_unregister(); +#endif +} + +/** + * @brief This function initializes module. + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static int woal_init_module(void) +{ + int ret = (int)MLAN_STATUS_SUCCESS; + int index = 0; + + ENTER(); + + PRINTM(MMSG, "wlan: Loading MWLAN driver\n"); + /* Init the wlan_private pointer array first */ + for (index = 0; index < MAX_MLAN_ADAPTER; index++) + m_handle[index] = NULL; + /* Init mutex */ + MOAL_INIT_SEMAPHORE(&AddRemoveCardSem); + + if (woal_root_proc_init() != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "woal_init_module: Unable to create /proc/mwlan/ directory\n"); + LEAVE(); + return -EFAULT; + } + +#ifdef CONFIG_OF + woal_init_from_dev_tree(); +#endif + + /* Create workqueue for hang process */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) + /* For kernel less than 2.6.14 name can not be greater than 10 + characters */ + hang_workqueue = create_workqueue("MOAL_HANG_WORKQ"); +#else +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + hang_workqueue = + alloc_workqueue("MOAL_HANG_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); +#else + hang_workqueue = create_workqueue("MOAL_HANG_WORK_QUEUE"); +#endif +#endif + MLAN_INIT_WORK(&hang_work, woal_hang_work_queue); + + /* Register with bus */ + ret = woal_bus_register(); + if (ret == MLAN_STATUS_SUCCESS) + PRINTM(MMSG, "wlan: Driver loaded successfully\n"); + else + PRINTM(MMSG, "wlan: Driver loading failed\n"); + + LEAVE(); + return ret; +} + +/** + * @brief This function cleans module + * + * @return N/A + */ +static void woal_cleanup_module(void) +{ + moal_handle *handle = NULL; + int index = 0; + int i; +#if defined(STA_SUPPORT) && defined(STA_CFG80211) + unsigned long flags; +#endif + + ENTER(); + + PRINTM(MMSG, "wlan: Unloading MWLAN driver\n"); + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + handle = m_handle[index]; + if (!handle) + continue; + if (!handle->priv_num) + goto exit; + if (MTRUE == woal_check_driver_status(handle)) + goto exit; + +#ifdef USB +#ifdef CONFIG_USB_SUSPEND + if (IS_USB(handle->card_type) && + handle->is_suspended == MTRUE) { + woal_exit_usb_suspend(handle); + } +#endif /* CONFIG_USB_SUSPEND */ +#endif + +#ifdef SDIO +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + if (handle->is_suspended == MTRUE) { + woal_sdio_resume( + &(((struct sdio_mmc_card *)handle->card)->func) + ->dev); + } +#endif /* MMC_PM_KEEP_POWER */ +#endif /* SDIO_SUSPEND_RESUME */ +#endif + + if (handle->rf_test_mode) + woal_process_rf_test_mode(handle, + MFG_CMD_UNSET_TEST_MODE); + + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == + MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) { + woal_disconnect(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT, + NULL, + DEF_DEAUTH_REASON_CODE); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + } +#ifdef STA_CFG80211 + if (IS_STA_CFG80211( + handle->params.cfg80211_wext) && + (handle->priv[i]->bss_type == + MLAN_BSS_TYPE_STA)) + woal_clear_conn_params(handle->priv[i]); + spin_lock_irqsave(&handle->scan_req_lock, + flags); + if (IS_STA_CFG80211( + handle->params.cfg80211_wext) && + handle->scan_request) { + woal_cfg80211_scan_done( + handle->scan_request, MTRUE); + handle->scan_request = NULL; + handle->scan_priv = NULL; + } + spin_unlock_irqrestore(&handle->scan_req_lock, + flags); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211( + handle->params.cfg80211_wext) && + handle->priv[i]->sched_scanning) { + woal_stop_bg_scan( + handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + handle->priv[i]->bg_scan_start = MFALSE; + handle->priv[i]->bg_scan_reported = + MFALSE; + cfg80211_sched_scan_stopped( + handle->priv[i]->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , + handle->priv[i]->bg_scan_reqid +#endif + ); + handle->priv[i]->sched_scanning = + MFALSE; + } +#endif +#endif + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == + MLAN_BSS_ROLE_UAP) { +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode != + MLAN_INIT_PARA_ENABLED) +#endif + { + woal_disconnect(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT, + NULL, + DEF_DEAUTH_REASON_CODE); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + } + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + woal_clear_all_mgmt_ies(handle->priv[i], + MOAL_IOCTL_WAIT_TIMEOUT); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + woal_flush_tx_stat_queue(handle->priv[i]); +#endif + } + +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + { + woal_set_deep_sleep(woal_get_priv(handle, + MLAN_BSS_ROLE_ANY), + MOAL_IOCTL_WAIT_TIMEOUT, MFALSE, 0); + } + +#ifdef MFG_CMD_SUPPORT + if (handle->params.mfg_mode != MLAN_INIT_PARA_ENABLED) +#endif + { + woal_shutdown_fw(woal_get_priv(handle, + MLAN_BSS_ROLE_ANY), + MOAL_IOCTL_WAIT_TIMEOUT); + if (handle->ioctl_timeout) { + woal_ioctl_timeout(handle); + goto exit; + } + } + } + +exit: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); +exit_sem_err: + /* Unregister from bus */ + woal_bus_unregister(); + PRINTM(MMSG, "wlan: Driver unloaded\n"); + if (hang_workqueue) { + flush_workqueue(hang_workqueue); + destroy_workqueue(hang_workqueue); + hang_workqueue = NULL; + } + woal_root_proc_remove(); + + LEAVE(); +} + +#ifndef MODULE +#ifdef MFG_CMD_SUPPORT +/** + * @brief This function handle the mfg_mode from kernel boot command + * + * @param str buffer for mfg_mode + * @return N/A + */ +static int __init mfg_mode_setup(char *str) +{ + int val = -1; + get_option(&str, &val); + if (val > 0) + mfg_mode = 1; + PRINTM(MMSG, "mfg_mode=%d\n", mfg_mode); + return 1; +} +__setup("mfg_mode=", mfg_mode_setup); +#endif +#endif + +module_init(woal_init_module); +module_exit(woal_cleanup_module); + +MODULE_DESCRIPTION("M-WLAN Driver"); +MODULE_AUTHOR("NXP"); +MODULE_VERSION(MLAN_RELEASE_VERSION); +MODULE_LICENSE("GPL"); diff --git a/mxm_wifiex/wlan_src/mlinux/moal_main.h b/mxm_wifiex/wlan_src/mlinux/moal_main.h new file mode 100644 index 0000000..3a4599d --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_main.h @@ -0,0 +1,3148 @@ +/** @file moal_main.h + * + * @brief This file contains wlan driver specific defines etc. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#ifndef _MOAL_MAIN_H +#define _MOAL_MAIN_H + +/* warnfix for FS redefination if any? */ +#ifdef FS +#undef FS +#endif + +/* Linux header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +#include +#include +#endif +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18) +#include +#endif + +#ifdef USB +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#include +#endif +#include +#endif /* USB */ + +/* ASM files */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#include +#else +#include +#endif +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +#include +#else +#include +#endif + +#include + +/* Net header files */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +#include +#include +#else +#include +#endif +#endif + +#include "mlan.h" +#include "moal_shim.h" +/* Wireless header */ +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include +#include +#include +#endif +#if defined(STA_WEXT) || defined(UAP_WEXT) +#include +#include +#include "moal_wext.h" +#endif +#ifdef STA_WEXT +#include "moal_priv.h" +#endif + +#ifndef MIN +/** Find minimum */ +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/** Find maximum */ +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +#define COMPAT_VERSION_CODE KERNEL_VERSION(0, 0, 0) +#define CFG80211_VERSION_CODE MAX(LINUX_VERSION_CODE, COMPAT_VERSION_CODE) + +/** + * Reason Code 3: STA is leaving (or has left) IBSS or ESS + */ +#define DEF_DEAUTH_REASON_CODE (0x3) + +/** + * 802.1 Local Experimental 1. + */ + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24) +#define REFDATA __refdata +#else +#define REFDATA +#endif + +/** + * Linux Kernels later 3.9 use CONFIG_PM_RUNTIME instead of + * CONFIG_USB_SUSPEND + * Linux Kernels later 3.19 use CONFIG_PM instead of + * CONFIG_PM_RUNTIME + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +#ifdef CONFIG_PM +#ifndef CONFIG_USB_SUSPEND +#define CONFIG_USB_SUSPEND +#endif +#ifndef CONFIG_PM_RUNTIME +#define CONFIG_PM_RUNTIME +#endif +#endif +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */ +#ifdef CONFIG_PM_RUNTIME +#ifndef CONFIG_USB_SUSPEND +#define CONFIG_USB_SUSPEND +#endif +#endif +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */ +#endif + +/** + * Linux kernel later 3.10 use strncasecmp instead of strnicmp + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) +#define strnicmp strncasecmp +#endif + +/** + * Linux kernel later 4.7 use nl80211_band instead of ieee80211_band + * Linux kernel later 4.7 use new macro + */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) +#define ieee80211_band nl80211_band +#define IEEE80211_BAND_2GHZ NL80211_BAND_2GHZ +#define IEEE80211_BAND_5GHZ NL80211_BAND_5GHZ +#define IEEE80211_NUM_BANDS NUM_NL80211_BANDS +#endif + +/** + * interface name + */ +#define default_mlan_name "mlan%d" +#define default_uap_name "uap%d" +#define default_wfd_name "wfd%d" +#define default_nan_name "nan%d" +#define default_mpl_name "mpl%d" +#define default_11p_name "ocb%d" +#define mwiphy_name "mwiphy%d" + +/** Define BOOLEAN */ +typedef t_u8 BOOLEAN; + +#define INTF_CARDTYPE "---------%s-MXM" + +#define KERN_VERSION "5X" + +#define V15 "15" +#define V16 "16" +#define V17 "17" + +/** Chip Magic Value */ +#define CHIP_MAGIC_VALUE 0x24 +/** card type SD_UART */ +#define CARD_TYPE_SD_UART 0 +/** card type SD_SD */ +#define CARD_TYPE_SD_SD 1 +/** card type PCIE_PCIE */ +#define CARD_TYPE_PCIE_PCIE 2 +/** card type PCIE_UART */ +#define CARD_TYPE_PCIE_UART 3 +/** card type USB_UART */ +#define CARD_TYPE_USB_UART 4 +/** card type USB_USB */ +#define CARD_TYPE_USB_USB 6 +/** card type PCIE_USB */ +#define CARD_TYPE_PCIE_USB 7 + +/** Driver version */ +extern char driver_version[]; + +/** Private structure for MOAL */ +typedef struct _moal_private moal_private, *pmoal_private; +/** Handle data structure for MOAL */ +typedef struct _moal_handle moal_handle, *pmoal_handle; + +/** Hardware status codes */ +typedef enum _MOAL_HARDWARE_STATUS { + HardwareStatusReady, + HardwareStatusInitializing, + HardwareStatusFwReady, + HardwareStatusReset, + HardwareStatusClosing, + HardwareStatusNotReady +} MOAL_HARDWARE_STATUS; + +/** fw cap info 11p */ +#define FW_CAPINFO_80211P MBIT(24) +/** fw cap info disable nan */ +#define FW_CAPINFO_DISABLE_NAN MBIT(29) +/** fw cap info BGA */ +#define FW_CAPINFO_80211BGA (MBIT(8) | MBIT(9) | MBIT(10)) + +/** moal_wait_option */ +enum { MOAL_NO_WAIT, MOAL_IOCTL_WAIT, MOAL_IOCTL_WAIT_TIMEOUT }; + +/** moal_main_state */ +enum { MOAL_STATE_IDLE, + MOAL_RECV_INT, + MOAL_ENTER_WORK_QUEUE, + MOAL_START_MAIN_PROCESS, + MOAL_END_MAIN_PROCESS }; + +/** HostCmd_Header */ +typedef struct _HostCmd_Header { + /** Command */ + t_u16 command; + /** Size */ + t_u16 size; +} HostCmd_Header; + +/* + * OS timer specific + */ + +/** Timer structure */ +typedef struct _moal_drv_timer { + /** Timer list */ + struct timer_list tl; + /** Timer function */ + void (*timer_function)(void *context); + /** Timer function context */ + void *function_context; + /** Time period */ + t_u32 time_period; + /** Is timer periodic ? */ + t_u32 timer_is_periodic; + /** Is timer cancelled ? */ + t_u32 timer_is_canceled; +} moal_drv_timer, *pmoal_drv_timer; + +/** + * @brief Timer handler + * + * @param fcontext Timer context + * + * @return N/A + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +static inline void woal_timer_handler(struct timer_list *t) +{ + pmoal_drv_timer timer = from_timer(timer, t, tl); +#else +static inline void woal_timer_handler(unsigned long fcontext) +{ + pmoal_drv_timer timer = (pmoal_drv_timer)fcontext; +#endif + + 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 */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) + timer_setup(&timer->tl, woal_timer_handler, 0); +#else + init_timer(&timer->tl); + timer->tl.function = woal_timer_handler; + timer->tl.data = (t_ptr)timer; +#endif + + /* Then tell the proxy which function to call and what to pass it */ + timer->timer_function = TimerFunction; + timer->function_context = FunctionContext; + timer->timer_is_canceled = MTRUE; + timer->time_period = 0; + timer->timer_is_periodic = MFALSE; +} + +/** + * @brief Modify timer + * + * @param timer Timer structure + * @param millisecondperiod Time period in millisecond + * + * @return N/A + */ +static inline void woal_mod_timer(pmoal_drv_timer timer, + t_u32 millisecondperiod) +{ + timer->time_period = millisecondperiod; + mod_timer(&timer->tl, jiffies + (millisecondperiod * HZ) / 1000); + timer->timer_is_canceled = MFALSE; +} + +/** + * @brief Cancel timer + * + * @param timer Timer structure + * + * @return N/A + */ +static inline void woal_cancel_timer(moal_drv_timer *timer) +{ + if (timer->timer_is_periodic || in_atomic() || irqs_disabled()) + del_timer(&timer->tl); + else + del_timer_sync(&timer->tl); + timer->timer_is_canceled = MTRUE; + timer->time_period = 0; +} + +#ifdef REASSOCIATION +/* + * OS Thread Specific + */ + +#include + +/** Kernel thread structure */ +typedef struct _moal_thread { + /** Task control structrue */ + struct task_struct *task; + /** Pointer to wait_queue_head */ + wait_queue_head_t wait_q; + /** PID */ + pid_t pid; + /** Pointer to moal_handle */ + void *handle; +} moal_thread; + +/** + * @brief Activate thread + * + * @param thr Thread structure + * @return N/A + */ +static inline void woal_activate_thread(moal_thread *thr) +{ + /** Initialize the wait queue */ + init_waitqueue_head(&thr->wait_q); + + /** Record the thread pid */ + thr->pid = current->pid; +} + +/** + * @brief De-activate thread + * + * @param thr Thread structure + * @return N/A + */ +static inline void woal_deactivate_thread(moal_thread *thr) +{ + /* Reset the pid */ + thr->pid = 0; +} + +/** + * @brief Create and run the thread + * + * @param threadfunc Thread function + * @param thr Thread structure + * @param name Thread name + * @return N/A + */ +static inline void woal_create_thread(int (*threadfunc)(void *), + moal_thread *thr, char *name) +{ + /* Create and run the thread */ + thr->task = kthread_run(threadfunc, thr, "%s", name); +} +#endif /* REASSOCIATION */ + +/* The following macros are neccessary to retain compatibility + * around the workqueue chenges happened in kernels >= 2.6.20: + * - INIT_WORK changed to take 2 arguments and let the work function + * get its own data through the container_of macro + * - delayed works have been split from normal works to save some + * memory usage in struct work_struct + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +/** Work_queue work initialization */ +#define MLAN_INIT_WORK(_work, _fun) \ + INIT_WORK(_work, ((void (*)(void *))_fun), _work) +/** Work_queue delayed work initialization */ +#define MLAN_INIT_DELAYED_WORK(_work, _fun) \ + INIT_WORK(_work, ((void (*)(void *))_fun), _work) +/** Work_queue container parameter */ +#define MLAN_DELAYED_CONTAINER_OF(_ptr, _type, _m) container_of(_ptr, _type, _m) +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */ +/** Work_queue work initialization */ +#define MLAN_INIT_WORK INIT_WORK +/** Work_queue delayed work initialization */ +#define MLAN_INIT_DELAYED_WORK INIT_DELAYED_WORK +/** Work_queue container parameter */ +#define MLAN_DELAYED_CONTAINER_OF(_ptr, _type, _m) \ + container_of(_ptr, _type, _m.work) +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */ + +/** + * @brief Schedule timeout + * + * @param millisec Timeout duration in milli second + * + * @return N/A + */ +static inline void woal_sched_timeout(t_u32 millisec) +{ + set_current_state(TASK_INTERRUPTIBLE); + + schedule_timeout((millisec * HZ) / 1000); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +#define IN6PTON_XDIGIT 0x00010000 +#define IN6PTON_DIGIT 0x00020000 +#define IN6PTON_COLON_MASK 0x00700000 +#define IN6PTON_COLON_1 0x00100000 /* single : requested */ +#define IN6PTON_COLON_2 0x00200000 /* second : requested */ +#define IN6PTON_COLON_1_2 0x00400000 /* :: requested */ +#define IN6PTON_DOT 0x00800000 /* . */ +#define IN6PTON_DELIM 0x10000000 +#define IN6PTON_NULL 0x20000000 /* first/tail */ +#define IN6PTON_UNKNOWN 0x40000000 + +static inline int xdigit2bin(char c, int delim) +{ + if (c == delim || c == '\0') + return IN6PTON_DELIM; + if (c == ':') + return IN6PTON_COLON_MASK; + if (c == '.') + return IN6PTON_DOT; + if (c >= '0' && c <= '9') + return IN6PTON_XDIGIT | IN6PTON_DIGIT | (c - '0'); + if (c >= 'a' && c <= 'f') + return IN6PTON_XDIGIT | (c - 'a' + 10); + if (c >= 'A' && c <= 'F') + return IN6PTON_XDIGIT | (c - 'A' + 10); + if (delim == -1) + return IN6PTON_DELIM; + return IN6PTON_UNKNOWN; +} + +static inline int in4_pton(const char *src, int srclen, u8 *dst, int delim, + const char **end) +{ + const char *s; + u8 *d; + u8 dbuf[4]; + int ret = 0; + int i; + int w = 0; + + if (srclen < 0) + srclen = strlen(src); + s = src; + d = dbuf; + i = 0; + while (1) { + int c; + c = xdigit2bin(srclen > 0 ? *s : '\0', delim); + if (!(c & (IN6PTON_DIGIT | IN6PTON_DOT | IN6PTON_DELIM | + IN6PTON_COLON_MASK))) { + goto out; + } + if (c & (IN6PTON_DOT | IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + if (w == 0) + goto out; + *d++ = w & 0xff; + w = 0; + i++; + if (c & (IN6PTON_DELIM | IN6PTON_COLON_MASK)) { + if (i != 4) + goto out; + break; + } + goto cont; + } + w = (w * 10) + c; + if ((w & 0xffff) > 255) + goto out; + cont: + if (i >= 4) + goto out; + s++; + srclen--; + } + ret = 1; + moal_memcpy_ext(NULL, dst, dbuf, sizeof(dbuf), sizeof(dbuf)); +out: + if (end) + *end = s; + return ret; +} +#endif /* < 2.6.19 */ + +#ifndef __ATTRIB_ALIGN__ +#define __ATTRIB_ALIGN__ __attribute__((aligned(4))) +#endif + +#ifndef __ATTRIB_PACK__ +#define __ATTRIB_PACK__ __attribute__((packed)) +#endif + +/** Get module */ +#define MODULE_GET try_module_get(THIS_MODULE) +/** Put module */ +#define MODULE_PUT module_put(THIS_MODULE) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) +/** Initialize semaphore */ +#define MOAL_INIT_SEMAPHORE(x) init_MUTEX(x) +/** Initialize semaphore */ +#define MOAL_INIT_SEMAPHORE_LOCKED(x) init_MUTEX_LOCKED(x) +#else +/** Initialize semaphore */ +#define MOAL_INIT_SEMAPHORE(x) sema_init(x, 1) +/** Initialize semaphore */ +#define MOAL_INIT_SEMAPHORE_LOCKED(x) sema_init(x, 0) +#endif + +/** Acquire semaphore and with blocking */ +#define MOAL_ACQ_SEMAPHORE_BLOCK(x) down_interruptible(x) +/** Acquire semaphore without blocking */ +#define MOAL_ACQ_SEMAPHORE_NOBLOCK(x) down_trylock(x) +/** Release semaphore */ +#define MOAL_REL_SEMAPHORE(x) up(x) + +/** Request FW timeout in second */ +#define REQUEST_FW_TIMEOUT 30 + +#if defined(USB) || defined(SYSKT) +/** Max loop count (* 100ms) for waiting device ready at init time */ +#define MAX_WAIT_DEVICE_READY_COUNT 50 +#endif + +/** Default watchdog timeout */ +#define MRVDRV_DEFAULT_WATCHDOG_TIMEOUT (10 * HZ) + +#ifdef UAP_SUPPORT +/** Default watchdog timeout + Increase the value to avoid kernel Tx timeout message in case + station in PS mode or left. + The default value of PS station ageout timer is 40 seconds. + Hence, the watchdog timer is set to a value higher than it. +*/ +#define MRVDRV_DEFAULT_UAP_WATCHDOG_TIMEOUT (41 * HZ) +#endif + +/* IOCTL Timeout */ +#define MOAL_IOCTL_TIMEOUT (20 * HZ) + +#ifdef ANDROID_KERNEL +/** Wake lock timeout in msec */ +#define WAKE_LOCK_TIMEOUT 3000 +/** Roaming Wake lock timeout in msec */ +#define ROAMING_WAKE_LOCK_TIMEOUT 10000 +#endif + +/** Threshold value of number of times the Tx timeout happened */ +/* WAR For EDMAC Test */ +#define NUM_TX_TIMEOUT_THRESHOLD 10 +/** Custom event : DRIVER HANG */ +#define CUS_EVT_DRIVER_HANG "EVENT=DRIVER_HANG" + +/** AP connected event */ +#define CUS_EVT_AP_CONNECTED "EVENT=AP_CONNECTED" + +/** Custom event : BW changed */ +#define CUS_EVT_BW_CHANGED "EVENT=BW_CHANGED" +/** Custom event : OBSS scan parameter */ +#define CUS_EVT_OBSS_SCAN_PARAM "EVENT=OBSS_SCAN_PARAM" + +/** Custom event : AdHoc link sensed */ +#define CUS_EVT_ADHOC_LINK_SENSED "EVENT=ADHOC_LINK_SENSED" +/** Custom event : AdHoc link lost */ +#define CUS_EVT_ADHOC_LINK_LOST "EVENT=ADHOC_LINK_LOST" +/** Custom event : MIC failure, unicast */ +#define CUS_EVT_MLME_MIC_ERR_UNI "MLME-MICHAELMICFAILURE.indication unicast" +/** Custom event : MIC failure, multicast */ +#define CUS_EVT_MLME_MIC_ERR_MUL "MLME-MICHAELMICFAILURE.indication multicast" +/** Custom event : Beacon RSSI low */ +#define CUS_EVT_BEACON_RSSI_LOW "EVENT=BEACON_RSSI_LOW" +/** Custom event : Beacon SNR low */ +#define CUS_EVT_BEACON_SNR_LOW "EVENT=BEACON_SNR_LOW" +/** Custom event : Beacon RSSI high */ +#define CUS_EVT_BEACON_RSSI_HIGH "EVENT=BEACON_RSSI_HIGH" +/** Custom event : Beacon SNR high */ +#define CUS_EVT_BEACON_SNR_HIGH "EVENT=BEACON_SNR_HIGH" +/** Custom event : Max fail */ +#define CUS_EVT_MAX_FAIL "EVENT=MAX_FAIL" +/** Custom event : Data RSSI low */ +#define CUS_EVT_DATA_RSSI_LOW "EVENT=DATA_RSSI_LOW" +/** Custom event : Data SNR low */ +#define CUS_EVT_DATA_SNR_LOW "EVENT=DATA_SNR_LOW" +/** Custom event : Data RSSI high */ +#define CUS_EVT_DATA_RSSI_HIGH "EVENT=DATA_RSSI_HIGH" +/** Custom event : Data SNR high */ +#define CUS_EVT_DATA_SNR_HIGH "EVENT=DATA_SNR_HIGH" +/** Custom event : Link Quality */ +#define CUS_EVT_LINK_QUALITY "EVENT=LINK_QUALITY" +/** Custom event : Port Release */ +#define CUS_EVT_PORT_RELEASE "EVENT=PORT_RELEASE" +/** Custom event : Pre-Beacon Lost */ +#define CUS_EVT_PRE_BEACON_LOST "EVENT=PRE_BEACON_LOST" + +/** Custom event : Deep Sleep awake */ +#define CUS_EVT_DEEP_SLEEP_AWAKE "EVENT=DS_AWAKE" + +/** Custom event : Host Sleep activated */ +#define CUS_EVT_HS_ACTIVATED "HS_ACTIVATED" +/** Custom event : Host Sleep deactivated */ +#define CUS_EVT_HS_DEACTIVATED "HS_DEACTIVATED" +/** Custom event : Host Sleep wakeup */ +#define CUS_EVT_HS_WAKEUP "HS_WAKEUP" + +/** Wakeup Reason */ +typedef enum { + NO_HSWAKEUP_REASON = 0, // 0.unknown + BCAST_DATA_MATCHED, // 1. Broadcast data matched + MCAST_DATA_MATCHED, // 2. Multicast data matched + UCAST_DATA_MATCHED, // 3. Unicast data matched + MASKTABLE_EVENT_MATCHED, // 4. Maskable event matched + NON_MASKABLE_EVENT_MATCHED, // 5. Non-maskable event matched + NON_MASKABLE_CONDITION_MATCHED, // 6. Non-maskable condition matched + // (EAPoL rekey) + MAGIC_PATTERN_MATCHED, // 7. Magic pattern matched + CONTROL_FRAME_MATCHED, // 8. Control frame matched + MANAGEMENT_FRAME_MATCHED, // 9. Management frame matched + GTK_REKEY_FAILURE, // 10. GTK rekey failure + RESERVED // Others: reserved +} HSWakeupReason_t; + +/** Custom event : WEP ICV error */ +#define CUS_EVT_WEP_ICV_ERR "EVENT=WEP_ICV_ERR" + +/** Custom event : Channel Switch Announcment */ +#define CUS_EVT_CHANNEL_SWITCH_ANN "EVENT=CHANNEL_SWITCH_ANN" + +/** Custom indiciation message sent to the application layer for WMM changes */ +#define WMM_CONFIG_CHANGE_INDICATION "WMM_CONFIG_CHANGE.indication" + +#ifdef UAP_SUPPORT +/** Custom event : STA connected */ +#define CUS_EVT_STA_CONNECTED "EVENT=STA_CONNECTED" +/** Custom event : STA disconnected */ +#define CUS_EVT_STA_DISCONNECTED "EVENT=STA_DISCONNECTED" +#endif +#define FW_DEBUG_INFO "EVENT=FW_DEBUG_INFO" + +/** 10 seconds */ +#define MOAL_TIMER_10S 10000 +/** 5 seconds */ +#define MOAL_TIMER_5S 5000 +/** 1 second */ +#define MOAL_TIMER_1S 1000 +/** 1 milisecond */ +#define MOAL_TIMER_1MS 1 + +/** passive scan time */ +#define PASSIVE_SCAN_CHAN_TIME 110 +/** active scan time */ +#define ACTIVE_SCAN_CHAN_TIME 110 +/** specific scan time */ +#define SPECIFIC_SCAN_CHAN_TIME 110 +/** passive scan time */ +#define INIT_PASSIVE_SCAN_CHAN_TIME 80 +/** active scan time */ +#define INIT_ACTIVE_SCAN_CHAN_TIME 80 +/** specific scan time */ +#define INIT_SPECIFIC_SCAN_CHAN_TIME 80 +/** specific scan time after connected */ +#define MIN_SPECIFIC_SCAN_CHAN_TIME 40 + +/** Default value of re-assoc timer */ +#define REASSOC_TIMER_DEFAULT 500 + +/** Netlink protocol number */ +#define NETLINK_NXP (MAX_LINKS - 1) +/** Netlink maximum payload size */ +#define NL_MAX_PAYLOAD 1024 +/** Netlink multicast group number */ +#define NL_MULTICAST_GROUP 1 + +#define MAX_RX_PENDING_THRHLD 50 + +/** high rx pending packets */ +#define HIGH_RX_PENDING 100 +/** low rx pending packets */ +#define LOW_RX_PENDING 80 + +/** MAX Tx Pending count */ +#define MAX_TX_PENDING 400 + +/** LOW Tx Pending count */ +#define LOW_TX_PENDING 380 + +/** Offset for subcommand */ +#define SUBCMD_OFFSET 4 + +/** default scan channel gap */ +#define DEF_SCAN_CHAN_GAP 50 +/** default scan time per channel in miracast mode */ +#define DEF_MIRACAST_SCAN_TIME 20 +/** GAP value is optional */ +#define GAP_FLAG_OPTIONAL MBIT(15) + +/** Macro to extract the TOS field from a skb */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#define SKB_TOS(skb) (ip_hdr(skb)->tos) +#else +#define SKB_TOS(skb) (skb->nh.iph->tos) +#endif +#define SKB_TIDV6(skb) (ipv6_get_dsfield(ipv6_hdr(skb))) +#define IS_SKB_MAGIC_VLAN(skb) (skb->priority >= 256 && skb->priority <= 263) +#define GET_VLAN_PRIO(skb) (skb->priority - 256) + +/** Offset for TOS field in the IP header */ +#define IPTOS_OFFSET 5 + +/** Offset for DSCP in the tos field */ +#define DSCP_OFFSET 2 + +/** max retry count for wait_event_interupptible_xx while loop */ +#define MAX_RETRY_CNT 100 +/** wait_queue structure */ +typedef struct _wait_queue { + /** wait_queue_head */ + wait_queue_head_t wait; + /** Wait condition */ + BOOLEAN condition; + /** Start time */ + long start_time; + /** Status from MLAN */ + mlan_status status; + /** flag for wait_timeout */ + t_u8 wait_timeout; + /** retry count */ + t_u8 retry; +} wait_queue, *pwait_queue; + +/** Auto Rate */ +#define AUTO_RATE 0xFF + +#define STA_WEXT_MASK MBIT(0) +#define UAP_WEXT_MASK MBIT(1) +#define STA_CFG80211_MASK MBIT(2) +#define UAP_CFG80211_MASK MBIT(3) +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT +/** Is STA CFG80211 enabled in module param */ +#define IS_STA_CFG80211(x) (x & STA_CFG80211_MASK) +#endif +#endif +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT +/** Is UAP CFG80211 enabled in module param */ +#define IS_UAP_CFG80211(x) (x & UAP_CFG80211_MASK) +#endif +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +/** Is UAP or STA CFG80211 enabled in module param */ +#define IS_STA_OR_UAP_CFG80211(x) (x & (STA_CFG80211_MASK | UAP_CFG80211_MASK)) +#endif + +#ifdef STA_WEXT +/** Is STA WEXT enabled in module param */ +#define IS_STA_WEXT(x) (x & STA_WEXT_MASK) +#endif /* STA_WEXT */ +#ifdef UAP_WEXT +/** Is UAP WEXT enabled in module param */ +#define IS_UAP_WEXT(x) (x & UAP_WEXT_MASK) +#endif /* UAP_WEXT */ +#if defined(STA_WEXT) || defined(UAP_WEXT) +/** Is UAP or STA WEXT enabled in module param */ +#define IS_STA_OR_UAP_WEXT(x) (x & (STA_WEXT_MASK | UAP_WEXT_MASK)) +#endif + +#ifdef STA_SUPPORT +/** Driver mode STA bit */ +#define DRV_MODE_STA MBIT(0) +/** Maximum STA BSS */ +#define MAX_STA_BSS 1 +/** Default STA BSS */ +#define DEF_STA_BSS 1 +#endif +#ifdef UAP_SUPPORT +/** Driver mode uAP bit */ +#define DRV_MODE_UAP MBIT(1) +/** Maximum uAP BSS */ +#define MAX_UAP_BSS 1 +/** Default uAP BSS */ +#define DEF_UAP_BSS 1 +#endif +#ifdef WIFI_DIRECT_SUPPORT +/** Driver mode WIFIDIRECT bit */ +#define DRV_MODE_WIFIDIRECT MBIT(2) +/** Maximum WIFIDIRECT BSS */ +#define MAX_WIFIDIRECT_BSS 2 +/** Default WIFIDIRECT BSS */ +#define DEF_WIFIDIRECT_BSS 1 +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +#define DEF_VIRTUAL_BSS 0 +#endif +#endif /* WIFI_DIRECT_SUPPORT */ + +#define DRV_MODE_WLAN (MBIT(0) | MBIT(1) | MBIT(2) | MBIT(3) | MBIT(4)) + +/** + * the maximum number of adapter supported + **/ +#define MAX_MLAN_ADAPTER 3 + +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; + +/** Indicate if handle->info's address */ +#define INFO_ADDR BIT(0) +#define IS_INFO_ADDR(attr) (attr & INFO_ADDR) +/** Indicate if handle's address */ +#define HANDLE_ADDR BIT(1) +#define IS_HANDLE_ADDR(attr) (attr & HANDLE_ADDR) +/** Indicate if card's address */ +#define CARD_ADDR BIT(2) +#define IS_CARD_ADDR(attr) (attr & CARD_ADDR) +/** indicate if priv's address */ +#define PRIV_ADDR BIT(3) +#define IS_PRIV_ADDR(attr) (attr & PRIV_ADDR) + +/** Debug data */ +struct debug_data { + /** Name */ + char name[32]; + /** Size */ + t_u32 size; + /** Address */ + t_ptr addr; + /** Attribute: + 0-7bit: start address for addr to add to, 0 means common(no specific) + 8-15bit: interface type, 0 means common(no interface specific) + other: unused + */ + t_u32 attr; +}; + +/** 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; +}; + +/** Maximum IP address buffer length */ +#define IPADDR_MAX_BUF 20 +/** IP address operation: Remove */ +#define IPADDR_OP_REMOVE 0 + +#define DROP_TCP_ACK 1 +#define HOLD_TCP_ACK 2 +struct tcp_sess { + struct list_head link; + /** tcp session info */ + t_u32 src_ip_addr; + t_u32 dst_ip_addr; + t_u16 src_tcp_port; + t_u16 dst_tcp_port; + /** tx ack packet info */ + t_u32 ack_seq; + /** tcp ack buffer */ + void *ack_skb; + /** priv structure */ + void *priv; + /** pmbuf */ + void *pmbuf; + /** timer for ack */ + moal_drv_timer ack_timer __ATTRIB_ALIGN__; + /** timer is set */ + BOOLEAN is_timer_set; +}; + +struct tx_status_info { + struct list_head link; + /** cookie */ + t_u64 tx_cookie; + /** seq_num */ + t_u8 tx_seq_num; + /** cancel remain on channel when receive tx status */ + t_u8 cancel_remain_on_channel; + /** skb */ + void *tx_skb; +}; + +/** woal event type */ +enum woal_event_type { + WOAL_EVENT_CHAN_SWITCH, + WOAL_EVENT_BGSCAN_STOP, +#if defined(UAP_CFG80211) || defined(STA_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + WOAL_EVENT_DEAUTH, + WOAL_EVENT_ASSOC_RESP, +#endif +#endif +}; + +/** woal event */ +struct woal_event { + /*list head */ + struct list_head link; + /** type */ + enum woal_event_type type; + /** priv pointer */ + void *priv; + union { + chan_band_info chan_info; + mlan_ds_misc_assoc_rsp assoc_resp; + int reason_code; + }; +}; + +#define MAX_NUM_ETHER_TYPE 8 +typedef struct { + /** number of protocols in protocol array*/ + t_u8 protocol_num; + /** protocols supported */ + t_u16 protocols[MAX_NUM_ETHER_TYPE]; +} __ATTRIB_PACK__ dot11_protocol; +typedef struct { + /** Data rate in unit of 0.5Mbps */ + t_u16 datarate; + /** Channel number to transmit the frame */ + t_u8 channel; + /** Bandwidth to transmit the frame */ + t_u8 bw; + /** Power to be used for transmission */ + t_u8 power; + /** Priority of the packet to be transmitted */ + t_u8 priority; + /** retry time of tx transmission*/ + t_u8 retry_limit; + /** Reserved fields*/ + t_u8 reserved[1]; +} __ATTRIB_PACK__ dot11_txcontrol; + +typedef struct { + /** Data rate of received paccket*/ + t_u16 datarate; + /** Channel on which packet was received*/ + t_u8 channel; + /** Rx antenna*/ + t_u8 antenna; + /** RSSI */ + t_u8 rssi; + /** Reserved */ + t_u8 reserved[3]; +} __ATTRIB_PACK__ dot11_rxcontrol; + +#define OKC_WAIT_TARGET_PMKSA_TIMEOUT (4 * HZ / 1000) +#define PMKID_LEN 16 +struct pmksa_entry { + struct list_head link; + u8 bssid[ETH_ALEN]; + u8 pmkid[PMKID_LEN]; +}; + +struct rf_test_mode_data { + /* tx antenna num */ + t_u32 tx_antenna; + /* rx antenna num */ + t_u32 rx_antenna; + /* RF band */ + t_u32 band; + /* RF bandwidth */ + t_u32 bandwidth; + /* RF channel */ + t_u32 channel; + /* Total Rx ucast/mcast/bcast pkt count */ + t_u32 rx_tot_pkt_count; + /* Rx mcast/bcast pkt count */ + t_u32 rx_mcast_bcast_pkt_count; + /* Rx fcs error count */ + t_u32 rx_pkt_fcs_err_count; + /* Tx power config values */ + t_u32 tx_power_data[3]; + /* Tx continuous config values */ + t_u32 tx_cont_data[6]; + /* Tx frame config values */ + t_u32 tx_frame_data[13]; + /* BSSID */ + t_u8 bssid[ETH_ALEN]; +}; + +/** Number of samples in histogram (/proc/mwlan/adapterX/mlan0/histogram).*/ +#define HIST_MAX_SAMPLES 1048576 +#define RX_RATE_MAX 196 + +/** SRN MAX */ +#define SNR_MAX 256 +/** NOISE FLR MAX */ +#define NOISE_FLR_MAX 256 +/** SIG STRENTGH MAX */ +#define SIG_STRENGTH_MAX 256 +/** historgram data */ +typedef struct _hgm_data { + /** snr */ + atomic_t snr[SNR_MAX]; + /** noise flr */ + atomic_t noise_flr[NOISE_FLR_MAX]; + /** sig_str */ + atomic_t sig_str[SIG_STRENGTH_MAX]; + /** num sample */ + atomic_t num_samples; + /** rx rate */ + atomic_t rx_rate[]; +} hgm_data, *phgm_data; + +/** max antenna number */ +#define MAX_ANTENNA_NUM 4 + +/* wlan_hist_proc_data */ +typedef struct _wlan_hist_proc_data { + /** antenna */ + u8 ant_idx; + /** Private structure */ + struct _moal_private *priv; +} wlan_hist_proc_data; + +enum ring_id { + VERBOSE_RING_ID, + EVENT_RING_ID, + RING_ID_MAX, +}; + +/** Private structure for MOAL */ +struct _moal_private { + /** Handle structure */ + moal_handle *phandle; + /** Tx timeout count */ + t_u32 num_tx_timeout; + /** BSS index */ + t_u8 bss_index; + /** BSS type */ + t_u8 bss_type; + /** BSS role */ + t_u8 bss_role; + /** bss virtual flag */ + t_u8 bss_virtual; + /** MAC address information */ + t_u8 current_addr[ETH_ALEN]; + /** Media connection status */ + BOOLEAN media_connected; + /** Statistics of tcp ack tx dropped */ + t_u32 tcp_ack_drop_cnt; + /** Statistics of tcp ack tx in total from kernel */ + t_u32 tcp_ack_cnt; +#ifdef UAP_SUPPORT + /** uAP started or not */ + BOOLEAN bss_started; + /** host based uap flag */ + BOOLEAN uap_host_based; + /** uAP skip CAC*/ + BOOLEAN skip_cac; + /** tx block flag */ + BOOLEAN uap_tx_blocked; + /** user cac period */ + t_u32 user_cac_period_msec; + /** channel under nop */ + BOOLEAN chan_under_nop; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /** current working channel */ + struct cfg80211_chan_def chan; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + /** switch channel */ + struct cfg80211_chan_def csa_chan; + /** beacon after channel switch */ + struct cfg80211_beacon_data beacon_after; + /** CSA work queue */ + struct workqueue_struct *csa_workqueue; + /** csa work */ + struct delayed_work csa_work; +#endif +#endif +#endif +#ifdef STA_SUPPORT + /** scan type */ + t_u8 scan_type; + /** extended capabilities */ + ExtCap_t extended_capabilities; + /** bg_scan_start */ + t_u8 bg_scan_start; + /** bg_scan reported */ + t_u8 bg_scan_reported; + /** bg_scan config */ + wlan_bgscan_cfg scan_cfg; + /** sched scaning flag */ + t_u8 sched_scanning; + /** bgscan request id */ + t_u64 bg_scan_reqid; +#ifdef STA_CFG80211 + /** roaming enabled flag */ + t_u8 roaming_enabled; + /** roaming required flag */ + t_u8 roaming_required; +#endif +#ifdef STA_CFG80211 + /** rssi low threshold */ + int rssi_low; + /** channel for connect */ + struct ieee80211_channel conn_chan; + /** bssid for connect */ + t_u8 conn_bssid[ETH_ALEN]; + /** ssid for connect */ + t_u8 conn_ssid[MLAN_MAX_SSID_LENGTH]; + /** length of ssid for connect */ + t_u8 conn_ssid_len; + /** key data */ + t_u8 conn_wep_key[MAX_WEP_KEY_SIZE]; + /** connection param */ + struct cfg80211_connect_params sme_current; +#endif + t_u8 wait_target_ap_pmkid; + wait_queue_head_t okc_wait_q __ATTRIB_ALIGN__; + struct list_head pmksa_cache_list; + spinlock_t pmksa_list_lock; + struct pmksa_entry *target_ap_pmksa; + t_u8 okc_ie_len; + t_u8 *okc_roaming_ie; +#endif + /** Net device pointer */ + struct net_device *netdev; + /** Net device statistics structure */ + struct net_device_stats stats; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + /** Wireless device pointer */ + struct wireless_dev *wdev; + /** Wireless device */ + struct wireless_dev w_dev; + /** Net device pointer */ + struct net_device *pa_netdev; + /** channel parameter for UAP/GO */ + t_u16 channel; +#ifdef UAP_SUPPORT + /** wep key */ + wep_key uap_wep_key[4]; + /** cipher */ + t_u32 cipher; +#endif + /** beacon ie index */ + t_u16 beacon_index; + /** proberesp ie index */ + t_u16 proberesp_index; + /** proberesp_p2p_index */ + t_u16 proberesp_p2p_index; + /** assocresp ie index */ + t_u16 assocresp_index; + /** assocresp qos map ie index */ + t_u16 assocresp_qos_map_index; + /** probereq index for mgmt ie */ + t_u16 probereq_index; + /** mgmt_subtype_mask */ + t_u32 mgmt_subtype_mask; + /** beacon wps index for mgmt ie */ + t_u16 beacon_wps_index; + /** beacon/proberesp vendor ie index */ + t_u16 beacon_vendor_index; +#endif +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + /** CFG80211 association description */ + t_u8 cfg_bssid[ETH_ALEN]; + /** Disconnect request from CFG80211 */ + bool cfg_disconnect; + /** connect request from CFG80211 */ + bool cfg_connect; + /** lock for cfg connect */ + spinlock_t connect_lock; + /** assoc status */ + t_u32 assoc_status; + /** rssi_threshold */ + s32 cqm_rssi_thold; + /** rssi_high_threshold */ + s32 cqm_rssi_high_thold; + /** rssi hysteresis */ + u32 cqm_rssi_hyst; + /** last rssi_low */ + u8 last_rssi_low; + /** last rssi_high */ + u8 last_rssi_high; + /** mrvl rssi threshold */ + u8 mrvl_rssi_low; + /** last event */ + u32 last_event; + /** fake scan flag */ + u8 fake_scan_complete; + /**ft ie*/ + t_u8 ft_ie[MAX_IE_SIZE]; + /**ft ie len*/ + t_u8 ft_ie_len; + /**ft ie*/ + t_u8 pre_ft_ie[MAX_IE_SIZE]; + /**ft ie len*/ + t_u8 pre_ft_ie_len; + /**mobility domain value*/ + t_u16 ft_md; + /**ft capability*/ + t_u8 ft_cap; + /** set true when receive ft auth or action ft roaming */ + t_bool ft_pre_connect; + /**ft roaming triggered by driver or not*/ + t_bool ft_roaming_triggered_by_driver; + /**target ap mac address for Fast Transition*/ + t_u8 target_ap_bssid[ETH_ALEN]; + /** IOCTL wait queue for FT*/ + wait_queue_head_t ft_wait_q __ATTRIB_ALIGN__; + /** ft wait condition */ + t_bool ft_wait_condition; +#endif /* STA_SUPPORT */ +#endif /* STA_CFG80211 */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /** flag for host_mlme */ + t_u8 host_mlme; + /** flag for auth */ + t_u8 auth_flag; + /** flag for auth algorithm */ + t_u16 auth_alg; +#endif +#ifdef CONFIG_PROC_FS + /** Proc entry */ + struct proc_dir_entry *proc_entry; + /** Proc entry name */ + char proc_entry_name[IFNAMSIZ]; + /** proc entry for hist */ + struct proc_dir_entry *hist_entry; + /** ant_hist_proc_data */ + wlan_hist_proc_data hist_proc[MAX_ANTENNA_NUM]; +#endif /* CONFIG_PROC_FS */ +#ifdef STA_SUPPORT + /** Nickname */ + t_u8 nick_name[16]; + /** AdHoc link sensed flag */ + BOOLEAN is_adhoc_link_sensed; + /** Current WEP key index */ + t_u16 current_key_index; +#ifdef REASSOCIATION + mlan_ssid_bssid prev_ssid_bssid; + /** Re-association required */ + BOOLEAN reassoc_required; + /** Flag of re-association on/off */ + BOOLEAN reassoc_on; + /** Set asynced essid flag */ + BOOLEAN set_asynced_essid_flag; +#endif /* REASSOCIATION */ + /** Report scan result */ + t_u8 report_scan_result; + /** wpa_version */ + t_u8 wpa_version; + /** key mgmt */ + t_u8 key_mgmt; + /** rx_filter */ + t_u8 rx_filter; +#endif /* STA_SUPPORT */ + /** Rate index */ + t_u16 rate_index; +#if defined(STA_WEXT) || defined(UAP_WEXT) + /** IW statistics */ + struct iw_statistics w_stats; +#endif +#ifdef UAP_WEXT + /** Pairwise Cipher used for WPA/WPA2 mode */ + t_u16 pairwise_cipher; + /** Group Cipher */ + t_u16 group_cipher; + /** Protocol stored during uap wext configuratoin */ + t_u16 uap_protocol; + /** Key Mgmt whether PSK or 1x */ + t_u16 uap_key_mgmt; + /** Beacon IE length from hostapd */ + t_u16 bcn_ie_len; + /** Beacon IE buffer from hostapd */ + t_u8 bcn_ie_buf[MAX_IE_SIZE]; +#endif + + /** dscp mapping */ + t_u8 dscp_map[64]; + /** MLAN debug info */ + struct debug_data_priv items_priv; + + /** tcp session queue */ + struct list_head tcp_sess_queue; + /** TCP Ack enhance flag */ + t_u8 enable_tcp_ack_enh; + /** TCP session spin lock */ + spinlock_t tcp_sess_lock; +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + atomic_t wmm_tx_pending[4]; +#endif + struct sk_buff_head tx_q; + /** per interface extra headroom */ + t_u16 extra_tx_head_len; + /** TX status spin lock */ + spinlock_t tx_stat_lock; + /** tx_seq_num */ + t_u8 tx_seq_num; + /** tx status queue */ + struct list_head tx_stat_queue; + /** rx hgm data */ + phgm_data hist_data[MAX_ANTENNA_NUM]; + t_u8 random_mac[MLAN_MAC_ADDR_LENGTH]; + BOOLEAN assoc_with_mac; + t_u8 gtk_data_ready; + mlan_ds_misc_gtk_rekey_data gtk_rekey_data; + dot11_protocol tx_protocols; + dot11_protocol rx_protocols; +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) + /** hostcmd_wait_q */ + wait_queue_head_t hostcmd_wait_q __ATTRIB_ALIGN__; + /** hostcmd_wait_condition */ + t_bool hostcmd_wait_condition; +#endif + void *rings[RING_ID_MAX]; + t_u8 pkt_fate_monitor_enable; + void *packet_filter; +}; + +#ifdef SDIO +#define DUMP_FW_SDIO_V2 2 +#define DUMP_FW_SDIO_V3 3 + +#define DUMP_REG_MAX 13 + +typedef struct _dump_reg_t { + t_u8 reg_table[DUMP_REG_MAX]; + t_u8 reg_table_size; +} dump_reg_t; +#endif + +#define FW_NAMW_MAX_LEN 64 + +/** card info */ +typedef struct _card_info { + /** support embeded supp */ + t_bool embedded_supp; + /** support drcs */ + t_bool drcs; + /** support Go NOA*/ + t_bool go_noa; + /** support V16_FW_API*/ + t_bool v16_fw_api; + /** support V17_FW_API*/ + t_bool v17_fw_api; + /** support pmic */ + t_bool pmic; + /** support antcfg */ + t_bool antcfg; + /** support cal_data_cfg */ + t_bool cal_data_cfg; + /** support WLAN_LOW_POWER_ENABLE */ + t_bool low_power_enable; + /** rx_rate_max for hist_data: 11N: 76 11AC:196 11AX: 412 */ + t_u16 rx_rate_max; + t_u8 histogram_table_num; + /* feature_control */ + t_u32 feature_control; + /* Revision id register */ + t_u32 rev_id_reg; + /* host interface selection reg*/ + t_u32 host_strap_reg; + /* Chip Magic Register */ + t_u32 magic_reg; + /* FW Name */ + char fw_name[FW_NAMW_MAX_LEN]; + char fw_name_wlan[FW_NAMW_MAX_LEN]; +#ifdef SDIO + t_u8 dump_fw_info; + t_u8 dump_fw_ctrl_reg; + t_u8 dump_fw_start_reg; + t_u8 dump_fw_end_reg; + t_u8 dump_fw_host_ready; + dump_reg_t dump_reg; + t_u8 scratch_reg; + t_u8 func1_reg_start; + t_u8 func1_reg_end; + t_u32 fw_reset_reg; + t_u8 fw_reset_val; + t_u32 slew_rate_reg; + t_u8 slew_rate_bit_offset; +#endif + t_u8 per_pkt_cfg_support; +} card_info; + +#define GTK_REKEY_OFFLOAD_DISABLE 0 +#define GTK_REKEY_OFFLOAD_ENABLE 1 +#define GTK_REKEY_OFFLOAD_SUSPEND 2 + +#define MAX_KEEP_ALIVE_ID 4 + +/** Operation data structure for MOAL bus interfaces */ +typedef struct _moal_if_ops { + mlan_status (*register_dev)(moal_handle *handle); + void (*unregister_dev)(moal_handle *handle); + mlan_status (*read_reg)(moal_handle *handle, t_u32 reg, t_u32 *data); + mlan_status (*write_reg)(moal_handle *handle, t_u32 reg, t_u32 data); + mlan_status (*read_data_sync)(moal_handle *handle, mlan_buffer *pmbuf, + t_u32 port, t_u32 timeout); + mlan_status (*write_data_sync)(moal_handle *handle, mlan_buffer *pmbuf, + t_u32 port, t_u32 timeout); + mlan_status (*get_fw_name)(moal_handle *handle); + void (*dump_fw_info)(moal_handle *handle); + int (*dump_reg_info)(moal_handle *handle, t_u8 *buf); + void (*reg_dbg)(moal_handle *handle); + t_u8 (*is_second_mac)(moal_handle *handle); +} moal_if_ops; + +#define WIFI_DIRECT_KERNEL_VERSION KERNEL_VERSION(2, 6, 39) + +/** Extended flags */ +enum ext_mod_params { + EXT_HW_TEST, +#ifdef CONFIG_OF + EXT_DTS_ENABLE, +#endif + EXT_REQ_FW_NOWAIT, + EXT_FW_SERIAL, +#ifdef SDIO + EXT_INTMODE, +#ifdef SDIO_SUSPEND_RESUME + EXT_PM_KEEP_POWER, + EXT_SHUTDOWN_HS, +#endif +#endif + EXT_CNTRY_TXPWR, +#if defined(USB) + EXT_SKIP_FWDNLD, +#endif + EXT_AGGR_CTRL, + EXT_LOW_PW_MODE, +#ifdef SDIO + EXT_SDIO_RX_AGGR, +#endif + EXT_PMIC, + EXT_DISCONNECT_ON_SUSPEND, + EXT_HS_MIMO_SWITCH, + EXT_FIX_BCN_BUF, + EXT_NAPI, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + EXT_DFS_OFFLOAD, +#endif + EXT_DISABLE_REGD_BY_DRIVER, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + EXT_COUNTRY_IE_IGNORE, + EXT_BEACON_HINTS, +#endif +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + EXT_HOST_MLME, +#endif +#endif + EXT_TX_WORK, + EXT_MAX_PARAM, +}; + +/** Module parameter data structure for MOAL */ +typedef struct _moal_mod_para { + t_u8 ext_flgs[DIV_ROUND_UP(EXT_MAX_PARAM, 8)]; + /* BIT24 ~ BIT32 reserved */ + t_u8 flag; + char *fw_name; + int fw_reload; + char *mac_addr; +#ifdef MFG_CMD_SUPPORT + int mfg_mode; +#endif /* MFG_CMD_SUPPORT */ + int drv_mode; +#ifdef STA_SUPPORT + int max_sta_bss; + char *sta_name; +#endif /* STA_SUPPORT */ +#ifdef UAP_SUPPORT + int max_uap_bss; + char *uap_name; + int uap_max_sta; +#endif /* UAP_SUPPORT */ +#ifdef WIFI_DIRECT_SUPPORT + int max_wfd_bss; + char *wfd_name; +#if defined(STA_CFG80211) && defined(UAP_CFG80211) + int max_vir_bss; +#endif +#endif /* WIFI_DIRECT_SUPPORT */ + int auto_ds; + int ps_mode; + int p2a_scan; + /** scan chan gap */ + int scan_chan_gap; + int max_tx_buf; +#if defined(SDIO) + int gpiopin; +#endif +#if defined(STA_SUPPORT) + int cfg_11d; +#endif +#if defined(SDIO) + int slew_rate; +#endif + char *dpd_data_cfg; + char *init_cfg; + char *cal_data_cfg; + char *txpwrlimit_cfg; + char *init_hostcmd_cfg; + char *band_steer_cfg; + int cfg80211_wext; + int wq_sched_prio; + int wq_sched_policy; + int rx_work; +#ifdef USB + int usb_aggr; +#endif +#ifdef PCIE + int pcie_int_mode; +#endif /* PCIE */ +#ifdef ANDROID_KERNEL + int wakelock_timeout; +#endif + unsigned int dev_cap_mask; +#if defined(SD8997) || defined(PCIE8997) || defined(USB8997) || \ + defined(SD8977) || defined(SD8987) || defined(SD9098) || \ + defined(USB9098) || defined(PCIE9098) || defined(SD9097) || \ + defined(USB9097) || defined(PCIE9097) || defined(SD8978) || \ + defined(USB8978) + int pmic; +#endif + int antcfg; + unsigned int uap_oper_ctrl; + int hs_wake_interval; + int indication_gpio; + int indrstcfg; +#ifdef WIFI_DIRECT_SUPPORT + int GoAgeoutTime; +#endif + int gtk_rekey_offload; + t_u16 multi_dtim; + t_u16 inact_tmo; + char *reg_alpha2; + int dfs53cfg; +} moal_mod_para; + +void woal_tp_acnt_timer_func(void *context); +void woal_set_tp_state(moal_private *priv); +#define MAX_TP_ACCOUNT_DROP_POINT_NUM 5 +#define RX_DROP_P1 (MAX_TP_ACCOUNT_DROP_POINT_NUM) +#define RX_DROP_P2 (MAX_TP_ACCOUNT_DROP_POINT_NUM + 1) +#define RX_DROP_P3 (MAX_TP_ACCOUNT_DROP_POINT_NUM + 2) +#define RX_DROP_P4 (MAX_TP_ACCOUNT_DROP_POINT_NUM + 3) +#define RX_DROP_P5 (MAX_TP_ACCOUNT_DROP_POINT_NUM + 4) +typedef struct _moal_tp_acnt_t { + /* TX accounting */ + unsigned long tx_packets[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long tx_packets_last[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long tx_packets_rate[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long tx_bytes[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long tx_bytes_last[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long tx_bytes_rate[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long tx_intr_cnt; + unsigned long tx_intr_last; + unsigned long tx_intr_rate; + unsigned long tx_pending; + /** RX accounting */ + unsigned long rx_packets[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long rx_packets_last[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long rx_packets_rate[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long rx_bytes[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long rx_bytes_last[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long rx_bytes_rate[MAX_TP_ACCOUNT_DROP_POINT_NUM]; + unsigned long rx_intr_cnt; + unsigned long rx_intr_last; + unsigned long rx_intr_rate; + unsigned long rx_pending; + unsigned long rx_paused_cnt; + /* TP account mode 0-disable 1-enable */ + unsigned int on; + /* drop point */ + unsigned int drop_point; + /* periodic timer */ + moal_drv_timer timer; +} moal_tp_acnt_t; + +/** 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_wlan; + /** Proc top level directory entry name */ + char proc_wlan_name[32]; +#endif +#ifdef USB + /** Firmware download skip flag */ + t_u8 skip_fw_dnld; +#endif /* USB */ + /** Firmware */ + const struct firmware *firmware; + /** Firmware request start time */ + wifi_timeval req_fw_time; + /** Init config file */ + const struct firmware *init_cfg_data; + /** Init config file */ + const struct firmware *user_data; + /** Init user configure wait queue token */ + t_u16 init_user_conf_wait_flag; + /** Init user configure file wait queue */ + wait_queue_head_t init_user_conf_wait_q __ATTRIB_ALIGN__; + /** dpd config file */ + const struct firmware *dpd_data; + /** txpwr data file */ + const struct firmware *txpwr_data; + /** Hotplug device */ + struct device *hotplug_device; + /** STATUS variables */ + MOAL_HARDWARE_STATUS hardware_status; + BOOLEAN fw_reload; + /** POWER MANAGEMENT AND PnP SUPPORT */ + BOOLEAN surprise_removed; + /** Firmware release number */ + t_u32 fw_release_number; + /** ECSA support */ + t_u8 fw_ecsa_enable; + /** Getlog support */ + t_u8 fw_getlog_enable; + /** Init wait queue token */ + t_u16 init_wait_q_woken; + /** Init wait queue */ + wait_queue_head_t init_wait_q __ATTRIB_ALIGN__; + /** Device suspend flag */ + BOOLEAN is_suspended; +#ifdef SDIO_SUSPEND_RESUME + /** suspend notify flag */ + BOOLEAN suspend_notify_req; + /** hs_shutdown in process flag */ + BOOLEAN shutdown_hs_in_process; +#endif + /** Suspend wait queue token */ + t_u16 suspend_wait_q_woken; + /** Suspend wait queue */ + wait_queue_head_t suspend_wait_q __ATTRIB_ALIGN__; + /** Host Sleep activated flag */ + t_u8 hs_activated; + /** Host Sleep activated event wait queue token */ + t_u16 hs_activate_wait_q_woken; + /** Host Sleep activated event wait queue */ + wait_queue_head_t hs_activate_wait_q __ATTRIB_ALIGN__; + /** auto_arp and ipv6 offload enable/disable flag */ + t_u8 hs_auto_arp; + /** Card pointer */ + t_void *card; + /** Rx pending in MLAN */ + atomic_t rx_pending; + /** Tx packet pending count in mlan */ + atomic_t tx_pending; + /** IOCTL pending count in mlan */ + atomic_t ioctl_pending; + /** lock count */ + atomic_t lock_count; + /** Malloc count */ + atomic_t malloc_count; + /** vmalloc count */ + atomic_t vmalloc_count; + /** mlan buffer alloc count */ + atomic_t mbufalloc_count; +#ifdef PCIE + /** Malloc consistent count */ + atomic_t malloc_cons_count; +#endif + /** 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 CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /** host_mlme_priv */ + moal_private *host_mlme_priv; + /** Host Mlme Work struct**/ + struct work_struct host_mlme_work; +#endif + /** Driver workqueue */ + struct workqueue_struct *rx_workqueue; + /** main work */ + struct work_struct rx_work; + /** Driver event workqueue */ + struct workqueue_struct *evt_workqueue; + /** event work */ + struct work_struct evt_work; + /** event spin lock */ + spinlock_t evt_lock; + /** event queue */ + struct list_head evt_queue; + /** tx workqueue */ + struct workqueue_struct *tx_workqueue; + /** tx work */ + struct work_struct tx_work; + /** remain on channel flag */ + t_u8 remain_on_channel; + /** bss index for remain on channel */ + t_u8 remain_bss_index; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + struct wiphy *wiphy; + /** Country code for regulatory domain */ + t_u8 country_code[COUNTRY_CODE_LEN]; + /** band */ + enum ieee80211_band band; + /** first scan done flag */ + t_u8 first_scan_done; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + /** remain_on_channel timer set flag */ + BOOLEAN is_remain_timer_set; + /** remani_on_channel_timer */ + moal_drv_timer remain_timer __ATTRIB_ALIGN__; + /** ieee802_11_channel */ + struct ieee80211_channel chan; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + /** channel type */ + enum nl80211_channel_type channel_type; +#endif + /** cookie */ + t_u64 cookie; +#endif + +#ifdef WIFI_DIRECT_SUPPORT + /** NoA duration */ + t_u32 noa_duration; + /** NoA interval */ + t_u32 noa_interval; + /** miracast mode */ + t_u8 miracast_mode; + /** scan time in miracast mode */ + t_u16 miracast_scan_time; + /** GO timer set flag */ + BOOLEAN is_go_timer_set; + /** GO timer */ + moal_drv_timer go_timer __ATTRIB_ALIGN__; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + /** cfg80211_suspend status */ + t_u8 cfg80211_suspend; +#endif +#endif + /** FW debug flag */ + t_u8 fw_dbg; + /** reg debug flag */ + t_u8 reg_dbg; +#ifdef SDIO +#endif /* SDIO */ + /** Netlink kernel socket */ + struct sock *nl_sk; + /** Netlink kernel socket number */ + t_u32 netlink_num; + /** w_stats wait queue token */ + BOOLEAN meas_wait_q_woken; + /** w_stats wait queue */ + wait_queue_head_t meas_wait_q __ATTRIB_ALIGN__; + /** Measurement start jiffes */ + long meas_start_jiffies; + /** CAC checking period flag */ + BOOLEAN cac_period; + /** CAC timer jiffes */ + long cac_timer_jiffies; + /** User NOP Period in sec */ + t_u16 usr_nop_period_sec; + /** BSS START command delay executing flag */ + BOOLEAN delay_bss_start; + /** SSID,BSSID parameter of delay executing */ + mlan_ssid_bssid delay_ssid_bssid; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + /* CAC channel info */ + struct cfg80211_chan_def dfs_channel; + /* time set flag*/ + BOOLEAN is_cac_timer_set; + /** cac_timer */ + moal_drv_timer cac_timer __ATTRIB_ALIGN__; + /** cac bss index */ + t_u8 cac_bss_index; +#endif +#endif +#if defined(UAP_SUPPORT) + /** channel switch wait queue token */ + BOOLEAN chsw_wait_q_woken; + /** channel switch wait queue */ + wait_queue_head_t chsw_wait_q __ATTRIB_ALIGN__; +#endif + /** cac period length, valid only when dfs testing is enabled */ + long cac_period_jiffies; + /** cac restart*/ + t_u8 cac_restart; + /** handle index - for multiple card supports */ + t_u8 handle_idx; +#if defined(USB) + /** Flag to indicate boot state */ + t_u8 boot_state; +#endif /* USB_NEW_FW_DNLD */ +#ifdef SDIO_MMC_DEBUG + /** cmd53 write state */ + u8 cmd53w; + /** cmd53 read state */ + u8 cmd53r; +#endif +#ifdef STA_SUPPORT + /** Scan pending on blocked flag */ + t_u8 scan_pending_on_block; + /** Scan Private pointer */ + moal_private *scan_priv; + /** Async scan semaphore */ + struct semaphore async_sem; + /** scan channel gap */ + t_u16 scan_chan_gap; +#ifdef STA_CFG80211 + /** CFG80211 scan request description */ + struct cfg80211_scan_request *scan_request; +#endif +#endif + /** main state */ + t_u8 main_state; + /** driver status */ + t_u8 driver_status; + /** driver state */ + t_u8 driver_state; + /** ioctl timeout */ + t_u8 ioctl_timeout; + /** FW dump state */ + t_u8 fw_dump; + /** event fw dump */ + t_u8 event_fw_dump; + /** fw dump buffer total len */ + t_u64 fw_dump_len; + /** FW dump full name */ + t_u8 firmware_dump_file[128]; +#ifdef SDIO + /** cmd52 function */ + t_u8 cmd52_func; + /** cmd52 register */ + t_u8 cmd52_reg; + /** cmd52 value */ + t_u8 cmd52_val; +#endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + /** spinlock to stop_queue/wake_queue*/ + spinlock_t queue_lock; +#endif + /** Driver spin lock */ + spinlock_t driver_lock; + /** Driver ioctl spin lock */ + spinlock_t ioctl_lock; + /** lock for scan_request */ + spinlock_t scan_req_lock; + /** Interface type: 1 byte, Card type: 1 byte */ + t_u16 card_type; + /** card revsion */ + t_u8 card_rev; + /** card info */ + card_info *card_info; + /** Card specific driver version */ + t_s8 driver_version[MLAN_MAX_VER_STR_LEN]; + char *fwdump_fname; +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + struct wakeup_source ws; +#else + struct wake_lock wake_lock; +#endif +#endif + t_u16 dfs_repeater_mode; + /* feature_control */ + t_u32 feature_control; + struct notifier_block woal_notifier; + mlan_ds_misc_keep_alive keep_alive[MAX_KEEP_ALIVE_ID]; + struct net_device napi_dev; + struct napi_struct napi_rx; + /* bus interface operations */ + moal_if_ops ops; + /* module parameter data */ + const struct firmware *param_data; + /* wrapped module parameters */ + moal_mod_para params; + /* debug info */ + mlan_debug_info debug_info; + /* block id in module param config file */ + int blk_id; + /** time when FW is active, time is get from boot time, in Nanosecond */ + t_u64 on_time; + /** tx time, in usecs */ + t_u64 tx_time; + /** systime when tx start */ + wifi_timeval tx_time_start; + /** systime when tx end */ + wifi_timeval tx_time_end; + /** rx time, in usecs */ + t_u64 rx_time; + /** scan time, in usecs */ + t_u64 scan_time; + /** systime when scan cmd response return success */ + wifi_timeval scan_time_start; + /** systime when scan event has no more event */ + wifi_timeval scan_time_end; + /** seecond mac flag */ + t_u8 second_mac; + /** moal handle for another mac */ + void *pref_mac; + /** RF test mode status */ + t_u8 rf_test_mode; + /** pointer to rf test mode data struct */ + struct rf_test_mode_data *rf_data; + /** TP accounting parameters */ + moal_tp_acnt_t tp_acnt; + BOOLEAN is_tp_acnt_timer_set; +}; + +/** + * @brief set extended flag in bitmap + * + * @param dev A pointer to moal_handle structure + * @param idx extended flags id + * @return N/A + */ +static inline void moal_extflg_set(moal_handle *handle, enum ext_mod_params idx) +{ + t_u8 *ext_fbyte; + ext_fbyte = &handle->params.ext_flgs[idx / 8]; + *ext_fbyte |= MBIT(idx % 8); +} + +/** + * @brief clear extended flag in bitmap + * + * @param dev A pointer to moal_handle structure + * @param idx extended flags id + * @return N/A + */ +static inline void moal_extflg_clear(moal_handle *handle, + enum ext_mod_params idx) +{ + t_u8 *ext_fbyte; + ext_fbyte = &handle->params.ext_flgs[idx / 8]; + *ext_fbyte &= ~MBIT(idx % 8); +} + +/** + * @brief check value of extended flag in bitmap + * + * @param dev A pointer to moal_handle structure + * @param idx extended flags id + * @return value of extended flag + */ +static inline t_u8 moal_extflg_isset(moal_handle *handle, + enum ext_mod_params idx) +{ + t_u8 ext_fbyte; + ext_fbyte = handle->params.ext_flgs[idx / 8]; + return (ext_fbyte & MBIT(idx % 8)) != 0; +} + +/** + * @brief set trans_start for each TX queue. + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +static inline void woal_set_trans_start(struct net_device *dev) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) + unsigned int i; + for (i = 0; i < dev->num_tx_queues; i++) + netdev_get_tx_queue(dev, i)->trans_start = jiffies; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0) + dev->trans_start = jiffies; +#else + netif_trans_update(dev); +#endif +} + +/** + * @brief Start queue + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +static inline void woal_start_queue(struct net_device *dev) +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 29) + netif_start_queue(dev); +#else + if (dev->reg_state == NETREG_REGISTERED) + netif_tx_wake_all_queues(dev); + else + netif_tx_start_all_queues(dev); +#endif +} + +/** + * @brief Stop queue + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +static inline void woal_stop_queue(struct net_device *dev) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + unsigned long flags; + moal_private *priv = (moal_private *)netdev_priv(dev); + spin_lock_irqsave(&priv->phandle->queue_lock, flags); + woal_set_trans_start(dev); + if (!netif_queue_stopped(dev)) + netif_tx_stop_all_queues(dev); + spin_unlock_irqrestore(&priv->phandle->queue_lock, flags); +#else + woal_set_trans_start(dev); + if (!netif_queue_stopped(dev)) + netif_stop_queue(dev); +#endif +} + +/** + * @brief wake queue + * + * @param dev A pointer to net_device structure + * + * @return N/A + */ +static inline void woal_wake_queue(struct net_device *dev) +{ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + unsigned long flags; + moal_private *priv = (moal_private *)netdev_priv(dev); + spin_lock_irqsave(&priv->phandle->queue_lock, flags); + if (netif_queue_stopped(dev)) + netif_tx_wake_all_queues(dev); + spin_unlock_irqrestore(&priv->phandle->queue_lock, flags); +#else + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); +#endif +} + +/** Debug Macro definition*/ +#ifdef DEBUG_LEVEL1 +extern t_u32 drvdbg; + +#define LOG_CTRL(level) (0) + +#ifdef DEBUG_LEVEL2 +#define PRINTM_MINFO(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MINFO) \ + printk(KERN_DEBUG msg); \ + } while (0) +#define PRINTM_MWARN(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MWARN) \ + printk(KERN_DEBUG msg); \ + } while (0) +#define PRINTM_MENTRY(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MENTRY) \ + printk(KERN_DEBUG msg); \ + } while (0) +#else +#define PRINTM_MINFO(level, msg...) \ + do { \ + } while (0) +#define PRINTM_MWARN(level, msg...) \ + do { \ + } while (0) +#define PRINTM_MENTRY(level, msg...) \ + do { \ + } while (0) +#endif /* DEBUG_LEVEL2 */ + +#define PRINTM_MFW_D(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MFW_D) \ + printk(KERN_DEBUG msg); \ + } while (0) +#define PRINTM_MCMD_D(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MCMD_D) \ + printk(KERN_DEBUG msg); \ + } while (0) +#define PRINTM_MDAT_D(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MDAT_D) \ + printk(KERN_DEBUG msg); \ + } while (0) +#define PRINTM_MIF_D(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MIF_D) \ + printk(KERN_DEBUG msg); \ + } while (0) + +#define PRINTM_MIOCTL(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MIOCTL) \ + printk(KERN_DEBUG msg); \ + } while (0) +#define PRINTM_MINTR(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MINTR) \ + printk(KERN_DEBUG msg); \ + } while (0) +#define PRINTM_MEVENT(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MEVENT) \ + printk(msg); \ + } while (0) +#define PRINTM_MCMND(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MCMND) \ + printk(KERN_DEBUG msg); \ + } while (0) +#define PRINTM_MDATA(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MDATA) \ + printk(KERN_DEBUG msg); \ + } while (0) +#define PRINTM_MERROR(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MERROR) \ + printk(KERN_ERR msg); \ + } while (0) +#define PRINTM_MFATAL(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MFATAL) \ + printk(KERN_ERR msg); \ + } while (0) +#define PRINTM_MMSG(level, msg...) \ + do { \ + woal_print(level, msg); \ + if (drvdbg & MMSG) \ + printk(KERN_ALERT msg); \ + } while (0) + +static inline void woal_print(t_u32 level, char *fmt, ...) +{ +} + +#define PRINTM(level, msg...) PRINTM_##level(level, msg) + +#else + +#define PRINTM(level, msg...) \ + do { \ + } while (0) + +#endif /* DEBUG_LEVEL1 */ + +/** Wait until a condition becomes true */ +#define MASSERT(cond) \ + do { \ + if (!(cond)) { \ + PRINTM(MFATAL, "ASSERT: %s: %i\n", __func__, \ + __LINE__); \ + panic("Assert failed: Panic!"); \ + } \ + } while (0) + +/** Log entry point for debugging */ +#define ENTER() PRINTM(MENTRY, "Enter: %s\n", __func__) +/** Log exit point for debugging */ +#define LEAVE() PRINTM(MENTRY, "Leave: %s\n", __func__) + +#ifdef DEBUG_LEVEL1 +#define DBG_DUMP_BUF_LEN 64 +#define MAX_DUMP_PER_LINE 16 + +static inline void hexdump(t_u32 level, char *prompt, t_u8 *buf, int len) +{ + int i; + char dbgdumpbuf[DBG_DUMP_BUF_LEN]; + char *ptr = dbgdumpbuf; + + if (drvdbg & level) + printk(KERN_DEBUG "%s:\n", prompt); + for (i = 1; i <= len; i++) { + ptr += snprintf(ptr, 4, "%02x ", *buf); + buf++; + if (i % MAX_DUMP_PER_LINE == 0) { + *ptr = 0; + if (drvdbg & level) + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + ptr = dbgdumpbuf; + } + } + if (len % MAX_DUMP_PER_LINE) { + *ptr = 0; + if (drvdbg & level) + printk(KERN_DEBUG "%s\n", dbgdumpbuf); + } +} + +#define DBG_HEXDUMP_MERROR(x, y, z) \ + do { \ + if ((drvdbg & MERROR) || LOG_CTRL(MERROR)) \ + hexdump(MERROR, x, y, z); \ + } while (0) +#define DBG_HEXDUMP_MCMD_D(x, y, z) \ + do { \ + if ((drvdbg & MCMD_D) || LOG_CTRL(MCMD_D)) \ + hexdump(MCMD_D, x, y, z); \ + } while (0) +#define DBG_HEXDUMP_MDAT_D(x, y, z) \ + do { \ + if ((drvdbg & MDAT_D) || LOG_CTRL(MDAT_D)) \ + hexdump(MDAT_D, x, y, z); \ + } while (0) +#define DBG_HEXDUMP_MIF_D(x, y, z) \ + do { \ + if ((drvdbg & MIF_D) || LOG_CTRL(MIF_D)) \ + hexdump(MIF_D, x, y, z); \ + } while (0) +#define DBG_HEXDUMP_MEVT_D(x, y, z) \ + do { \ + if ((drvdbg & MEVT_D) || LOG_CTRL(MEVT_D)) \ + hexdump(MEVT_D, x, y, z); \ + } while (0) +#define DBG_HEXDUMP_MFW_D(x, y, z) \ + do { \ + if ((drvdbg & MFW_D) || LOG_CTRL(MFW_D)) \ + hexdump(MFW_D, x, y, z); \ + } while (0) +#define DBG_HEXDUMP(level, x, y, z) DBG_HEXDUMP_##level(x, y, z) + +#else +/** Do nothing since debugging is not turned on */ +#define DBG_HEXDUMP(level, x, y, z) \ + do { \ + } while (0) +#endif + +#ifdef DEBUG_LEVEL2 +#define HEXDUMP(x, y, z) \ + do { \ + if ((drvdbg & MINFO) || LOG_CTRL(MINFO)) \ + hexdump(MINFO, x, y, z); \ + } while (0) +#else +/** Do nothing since debugging is not turned on */ +#define HEXDUMP(x, y, z) \ + do { \ + } while (0) +#endif + +#ifdef BIG_ENDIAN_SUPPORT +/** Convert from 16 bit little endian format to CPU format */ +#define woal_le16_to_cpu(x) le16_to_cpu(x) +/** Convert from 32 bit little endian format to CPU format */ +#define woal_le32_to_cpu(x) le32_to_cpu(x) +/** Convert from 64 bit little endian format to CPU format */ +#define woal_le64_to_cpu(x) le64_to_cpu(x) +/** Convert to 16 bit little endian format from CPU format */ +#define woal_cpu_to_le16(x) cpu_to_le16(x) +/** Convert to 32 bit little endian format from CPU format */ +#define woal_cpu_to_le32(x) cpu_to_le32(x) +/** Convert to 64 bit little endian format from CPU format */ +#define woal_cpu_to_le64(x) cpu_to_le64(x) +#else +/** Do nothing */ +#define woal_le16_to_cpu(x) x +/** Do nothing */ +#define woal_le32_to_cpu(x) x +/** Do nothing */ +#define woal_le64_to_cpu(x) x +/** Do nothing */ +#define woal_cpu_to_le16(x) x +/** Do nothing */ +#define woal_cpu_to_le32(x) x +/** Do nothing */ +#define woal_cpu_to_le64(x) x +#endif + +/** + * @brief This function returns first available priv + * based on the BSS role + * + * @param handle A pointer to moal_handle + * @param bss_role BSS role or MLAN_BSS_ROLE_ANY + * + * @return Pointer to moal_private + */ +static inline moal_private *woal_get_priv(moal_handle *handle, + mlan_bss_role bss_role) +{ + int i; + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i]) { + if (bss_role == MLAN_BSS_ROLE_ANY || + GET_BSS_ROLE(handle->priv[i]) == bss_role) + return handle->priv[i]; + } + } + return NULL; +} + +/** + * @brief This function returns first available priv + * based on the BSS type + * + * @param handle A pointer to moal_handle + * @param bss_type BSS type or MLAN_BSS_TYPE_ANY + * + * @return Pointer to moal_private + */ +static inline moal_private *woal_get_priv_bss_type(moal_handle *handle, + mlan_bss_type bss_type) +{ + int i; + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i]) { + if (bss_type == MLAN_BSS_TYPE_ANY || + handle->priv[i]->bss_type == bss_type) + return handle->priv[i]; + } + } + return NULL; +} + +static inline moal_private *woal_get_vir_priv_bss_type(moal_handle *handle, + mlan_bss_type bss_type) +{ + int i; + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i]) { + if (handle->priv[i]->bss_type == bss_type && + handle->priv[i]->bss_virtual) + return handle->priv[i]; + } + } + return NULL; +} + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#endif + +static inline void woal_get_monotonic_time(wifi_timeval *tv) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) + struct timespec64 ts; +#else + struct timespec ts; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) + ktime_get_raw_ts64(&ts); +#else + getrawmonotonic(&ts); +#endif + if (tv) { + tv->time_sec = (t_u32)ts.tv_sec; + tv->time_usec = (t_u32)ts.tv_nsec / 1000; + } +} + +/* CAC Measure report default time 60 seconds */ +#define MEAS_REPORT_TIME (60 * HZ) + +/** Max line length allowed in init config file */ +#define MAX_LINE_LEN 256 +/** Max MAC address string length allowed */ +#define MAX_MAC_ADDR_LEN 18 +/** Max register type/offset/value etc. parameter length allowed */ +#define MAX_PARAM_LEN 12 + +/** HostCmd_CMD_CFG_DATA for CAL data */ +#define HostCmd_CMD_CFG_DATA 0x008f +/** HostCmd action set */ +#define HostCmd_ACT_GEN_SET 0x0001 +/** HostCmd CAL data header length */ +#define CFG_DATA_HEADER_LEN 6 + +typedef struct _HostCmd_DS_GEN { + t_u16 command; + t_u16 size; + t_u16 seq_num; + t_u16 result; +} HostCmd_DS_GEN; + +typedef struct _HostCmd_DS_802_11_CFG_DATA { + /** Action */ + t_u16 action; + /** Type */ + t_u16 type; + /** Data length */ + t_u16 data_len; + /** Data */ + t_u8 data[1]; +} __ATTRIB_PACK__ HostCmd_DS_802_11_CFG_DATA; + +/** combo scan header */ +#define WEXT_CSCAN_HEADER "CSCAN S\x01\x00\x00S\x00" +/** combo scan header size */ +#define WEXT_CSCAN_HEADER_SIZE 12 +/** combo scan ssid section */ +#define WEXT_CSCAN_SSID_SECTION 'S' +/** commbo scan channel section */ +#define WEXT_CSCAN_CHANNEL_SECTION 'C' +/** commbo scan passive dwell section */ +#define WEXT_CSCAN_PASV_DWELL_SECTION 'P' +/** commbo scan home dwell section */ +#define WEXT_CSCAN_HOME_DWELL_SECTION 'H' +/** BGSCAN RSSI section */ +#define WEXT_BGSCAN_RSSI_SECTION 'R' +/** BGSCAN SCAN INTERVAL SECTION */ +#define WEXT_BGSCAN_INTERVAL_SECTION 'T' +/** BGSCAN REPEAT SECTION */ +#define WEXT_BGSCAN_REPEAT_SECTION 'E' +/** Min BGSCAN interval 30 second */ +#define MIN_BGSCAN_INTERVAL 30000 +/** default repeat count */ +#define DEF_REPEAT_COUNT 6 + +/** default rssi low threshold */ +#define DEFAULT_RSSI_LOW_THRESHOLD 70 +/** RSSI HYSTERSIS */ +#define RSSI_HYSTERESIS 6 +/** lowest rssi threshold */ +#define LOWEST_RSSI_THRESHOLD 82 +/** delta rssi */ +#define DELTA_RSSI 10 + +/** NL80211 scan configuration header */ +#define NL80211_SCANCFG_HEADER "SCAN-CFG " +/** NL80211 scan configuration header length */ +#define NL80211_SCANCFG_HEADER_SIZE 9 +/** NL80211 scan configuration active scan section */ +#define NL80211_SCANCFG_ACTV_DWELL_SECTION 'A' +/** NL80211 scan configuration passive scan section */ +#define NL80211_SCANCFG_PASV_DWELL_SECTION 'P' +/** NL80211 scan configuration specific scan section */ +#define NL80211_SCANCFG_SPCF_DWELL_SECTION 'S' + +/** band AUTO */ +#define WIFI_FREQUENCY_BAND_AUTO 0 +/** band 5G */ +#define WIFI_FREQUENCY_BAND_5GHZ 1 +/** band 2G */ +#define WIFI_FREQUENCY_BAND_2GHZ 2 +/** All band */ +#define WIFI_FREQUENCY_ALL_BAND 3 + +/** Rx filter: IPV4 multicast */ +#define RX_FILTER_IPV4_MULTICAST 1 +/** Rx filter: broadcast */ +#define RX_FILTER_BROADCAST 2 +/** Rx filter: unicast */ +#define RX_FILTER_UNICAST 4 +/** Rx filter: IPV6 multicast */ +#define RX_FILTER_IPV6_MULTICAST 8 + +/** Convert ASCII string to hex value */ +int woal_ascii2hex(t_u8 *d, char *s, t_u32 dlen); +/** parse ie */ +const t_u8 *woal_parse_ie_tlv(const t_u8 *ie, int len, t_u8 id); +/** parse extension ie */ +const t_u8 *woal_parse_ext_ie_tlv(const t_u8 *ie, int len, t_u8 ext_id); +/** Convert mac address from string to t_u8 buffer */ +void woal_mac2u8(t_u8 *mac_addr, char *buf); +/** Extract token from string */ +char *woal_strsep(char **s, char delim, char esc); +/** Return int value of a given ASCII string */ +mlan_status woal_atoi(int *data, char *a); +/** Return hex value of a given ASCII string */ +int woal_atox(char *a); +/** Allocate buffer */ +pmlan_buffer woal_alloc_mlan_buffer(moal_handle *handle, int size); +/** Allocate IOCTL request buffer */ +pmlan_ioctl_req woal_alloc_mlan_ioctl_req(int size); +/** Free buffer */ +void woal_free_mlan_buffer(moal_handle *handle, pmlan_buffer pmbuf); +/** Get private structure of a BSS by index */ +moal_private *woal_bss_index_to_priv(moal_handle *handle, t_u8 bss_index); +/* Functions in init module */ +/** init module parameters */ +mlan_status woal_init_module_param(moal_handle *handle); +/** free module parameters */ +void woal_free_module_param(moal_handle *handle); +/** init module parameters from device tree */ +void woal_init_from_dev_tree(void); +/** initializes software */ +mlan_status woal_init_sw(moal_handle *handle); +/** update the default firmware name */ +void woal_update_firmware_name(moal_handle *handle); +/** cancel all works in the queue */ +void woal_terminate_workqueue(moal_handle *handle); +/** initializes firmware */ +mlan_status woal_init_fw(moal_handle *handle); +/** frees the structure of moal_handle */ +void woal_free_moal_handle(moal_handle *handle); +/** shutdown fw */ +mlan_status woal_shutdown_fw(moal_private *priv, t_u8 wait_option); +/* Functions in interface module */ +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) +static inline void wakeup_source_init(struct wakeup_source *ws, + const char *name) +{ + ENTER(); + + if (ws) { + memset(ws, 0, sizeof(*ws)); + ws->name = name; + } + wakeup_source_add(ws); + + LEAVE(); +} + +static inline void wakeup_source_trash(struct wakeup_source *ws) +{ + ENTER(); + + if (!ws) { + PRINTM(MERROR, "ws is null!\n"); + return; + } + wakeup_source_remove(ws); + __pm_relax(ws); + + LEAVE(); +} +#endif +#endif +/** Add card */ +moal_handle *woal_add_card(void *card, struct device *dev, moal_if_ops *if_ops, + t_u16 card_type); +/** Remove card */ +mlan_status woal_remove_card(void *card); +/** broadcast event */ +mlan_status woal_broadcast_event(moal_private *priv, t_u8 *payload, t_u32 len); +#ifdef CONFIG_PROC_FS +/** switch driver mode */ +mlan_status woal_switch_drv_mode(moal_handle *handle, t_u32 mode); +#endif + +/** check if any interface is up */ +t_u8 woal_is_any_interface_active(moal_handle *handle); +/** Get version */ +void woal_get_version(moal_handle *handle, char *version, int maxlen); +/** Get Driver Version */ +int woal_get_driver_version(moal_private *priv, struct ifreq *req); +/** Get extended driver version */ +int woal_get_driver_verext(moal_private *priv, struct ifreq *ireq); +/** check driver status */ +t_u8 woal_check_driver_status(moal_handle *handle); +/** Mgmt frame forward registration */ +int woal_reg_rx_mgmt_ind(moal_private *priv, t_u16 action, + t_u32 *pmgmt_subtype_mask, t_u8 wait_option); +#ifdef DEBUG_LEVEL1 +/** Set driver debug bit masks */ +int woal_set_drvdbg(moal_private *priv, t_u32 drvdbg); +#endif + +mlan_status woal_set_get_tx_bf_cap(moal_private *priv, t_u16 action, + t_u32 *tx_bf_cap); +/** Set/Get TX beamforming configurations */ +mlan_status woal_set_get_tx_bf_cfg(moal_private *priv, t_u16 action, + mlan_ds_11n_tx_bf_cfg *bf_cfg); +/** Request MAC address setting */ +mlan_status woal_request_set_mac_address(moal_private *priv, t_u8 wait_option); +/** 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); +/** Set/Get generic element */ +mlan_status woal_set_get_gen_ie(moal_private *priv, t_u32 action, t_u8 *ie, + int *ie_len, t_u8 wait_option); +#ifdef CONFIG_PROC_FS +mlan_status woal_request_soft_reset(moal_handle *handle); +#endif +void woal_request_fw_reload(moal_handle *phandle, t_u8 mode); + +/** 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); +/** Disconnect */ +mlan_status woal_disconnect(moal_private *priv, t_u8 wait_option, t_u8 *mac, + t_u16 reason_code); +/** associate */ +mlan_status woal_bss_start(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid); +/** Request firmware information */ +mlan_status woal_request_get_fw_info(moal_private *priv, t_u8 wait_option, + mlan_fw_info *fw_info); +/** Get channel of active intf */ +mlan_status woal_get_active_intf_channel(moal_private *priv, + chan_band_info *channel); +#ifdef STA_SUPPORT +/** Request Exented Capability information */ +int woal_request_extcap(moal_private *priv, t_u8 *buf, t_u8 len); +#endif +mlan_status woal_set_get_dtim_period(moal_private *priv, t_u32 action, + t_u8 wait_option, t_u8 *value); +/** Set/get Host Sleep parameters */ +mlan_status woal_set_get_hs_params(moal_private *priv, t_u16 action, + t_u8 wait_option, mlan_ds_hs_cfg *hscfg); +/** Cancel Host Sleep configuration */ +mlan_status woal_cancel_hs(moal_private *priv, t_u8 wait_option); +/** Enable Host Sleep configuration */ +int woal_enable_hs(moal_private *priv); +/** hs active timeout 2 second */ +#define HS_ACTIVE_TIMEOUT (2 * HZ) +/** Get wakeup reason */ +mlan_status woal_get_wakeup_reason(moal_private *priv, + mlan_ds_hs_wakeup_reason *wakeup_reason); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +void woal_create_dump_dir(moal_handle *phandle, char *dir_buf, int buf_size); +#endif +mlan_status woal_save_dump_info_to_file(char *dir_name, char *file_name, + t_u8 *buf, t_u32 buf_len); +void woal_dump_drv_info(moal_handle *phandle, t_u8 *dir_name); + +#define FW_DUMP_TYPE_ENDED 0x002 +#define FW_DUMP_TYPE_MEM_ITCM 0x004 +#define FW_DUMP_TYPE_MEM_DTCM 0x005 +#define FW_DUMP_TYPE_MEM_SQRAM 0x006 +#define FW_DUMP_TYPE_MEM_IRAM 0x007 +#define FW_DUMP_TYPE_REG_MAC 0x009 +#define FW_DUMP_TYPE_REG_CIU 0x00E +#define FW_DUMP_TYPE_REG_APU 0x00F +#define FW_DUMP_TYPE_REG_ICU 0x014 +#ifdef SDIO_MMC +void woal_dump_firmware_info(moal_handle *phandle); +void woal_dump_firmware_info_v2(moal_handle *phandle); +void woal_dump_firmware_info_v3(moal_handle *phandle); +#endif /* SDIO_MMC */ +/* Store the FW dumps received from events in a file */ +void woal_store_firmware_dump(moal_handle *phandle, pmlan_event pmevent); + +#if defined(PCIE) +void woal_store_ssu_dump(moal_handle *phandle, pmlan_event pmevent); +#endif /* SSU_SUPPORT */ + +/** save hostcmd response to file */ +t_void woal_save_host_cmdresp(moal_handle *phandle, mlan_cmdresp_event *pevent); +int woal_pre_warmreset(moal_private *priv); +int woal_warmreset(moal_private *priv); + +/** get deep sleep */ +int woal_get_deep_sleep(moal_private *priv, t_u32 *data); +/** set deep sleep */ +int woal_set_deep_sleep(moal_private *priv, t_u8 wait_option, + BOOLEAN bdeep_sleep, t_u16 idletime); +/** process hang */ +void woal_process_hang(moal_handle *handle); +/** Get BSS information */ +mlan_status woal_get_bss_info(moal_private *priv, t_u8 wait_option, + mlan_bss_info *bss_info); +void woal_process_ioctl_resp(moal_private *priv, mlan_ioctl_req *req); +char *region_code_2_string(t_u8 region_code); +t_bool woal_is_etsi_country(t_u8 *country_code); +t_u8 woal_is_valid_alpha2(char *alpha2); +#ifdef STA_SUPPORT +void woal_send_disconnect_to_system(moal_private *priv, t_u16 reason_code); +void woal_send_mic_error_event(moal_private *priv, t_u32 event); +void woal_ioctl_get_bss_resp(moal_private *priv, mlan_ds_bss *bss); +void woal_ioctl_get_info_resp(moal_private *priv, mlan_ds_get_info *info); +mlan_status woal_get_assoc_rsp(moal_private *priv, + mlan_ds_misc_assoc_rsp *assoc_rsp, + t_u8 wait_option); +/** Get signal information */ +mlan_status woal_get_signal_info(moal_private *priv, t_u8 wait_option, + mlan_ds_get_signal *signal); +/** Get mode */ +t_u32 woal_get_mode(moal_private *priv, t_u8 wait_option); +mlan_status woal_get_sta_channel(moal_private *priv, t_u8 wait_option, + chan_band_info *channel); +#ifdef STA_WEXT +/** Get data rates */ +mlan_status woal_get_data_rates(moal_private *priv, t_u8 wait_option, + pmoal_802_11_rates m_rates); +void woal_send_iwevcustom_event(moal_private *priv, char *str); +/** Get channel list */ +mlan_status woal_get_channel_list(moal_private *priv, t_u8 wait_option, + mlan_chan_list *chanlist); +mlan_status woal_11d_check_ap_channel(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid); +#endif +/** Set/Get retry count */ +mlan_status woal_set_get_retry(moal_private *priv, t_u32 action, + t_u8 wait_option, int *value); +/** Set/Get RTS threshold */ +mlan_status woal_set_get_rts(moal_private *priv, t_u32 action, t_u8 wait_option, + int *value); +/** Set/Get fragment threshold */ +mlan_status woal_set_get_frag(moal_private *priv, t_u32 action, + t_u8 wait_option, int *value); +/** Set/Get TX power */ +mlan_status woal_set_get_tx_power(moal_private *priv, t_u32 action, + mlan_power_cfg_t *pwr); +/** Set/Get power IEEE management */ +mlan_status woal_set_get_power_mgmt(moal_private *priv, t_u32 action, + int *disabled, int type, t_u8 wait_option); +/** Get data rate */ +mlan_status woal_set_get_data_rate(moal_private *priv, t_u8 action, + mlan_rate_cfg_t *datarate); +/** Request a network scan */ +mlan_status woal_request_scan(moal_private *priv, t_u8 wait_option, + mlan_802_11_ssid *req_ssid); +/** Set radio on/off */ +int woal_set_radio(moal_private *priv, t_u8 option); +/** Set region code */ +mlan_status woal_set_region_code(moal_private *priv, char *region); +/** Set authentication mode */ +mlan_status woal_set_auth_mode(moal_private *priv, t_u8 wait_option, + t_u32 auth_mode); +/** Set encryption mode */ +mlan_status woal_set_encrypt_mode(moal_private *priv, t_u8 wait_option, + t_u32 encrypt_mode); +/** Enable wep key */ +mlan_status woal_enable_wep_key(moal_private *priv, t_u8 wait_option); +/** Set WPA enable */ +mlan_status woal_set_wpa_enable(moal_private *priv, t_u8 wait_option, + t_u32 enable); +/** cancel scan command */ +mlan_status woal_cancel_scan(moal_private *priv, t_u8 wait_option); +/** Find best network to connect */ +mlan_status woal_find_best_network(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid); +/** Set Ad-Hoc channel */ +mlan_status woal_change_adhoc_chan(moal_private *priv, int channel, + t_u8 wait_option); + +/** Get scan table */ +mlan_status woal_get_scan_table(moal_private *priv, t_u8 wait_option, + mlan_scan_resp *scanresp); +/** Get authentication mode */ +mlan_status woal_get_auth_mode(moal_private *priv, t_u8 wait_option, + t_u32 *auth_mode); +/** Get encryption mode */ +mlan_status woal_get_encrypt_mode(moal_private *priv, t_u8 wait_option, + t_u32 *encrypt_mode); +/** Get WPA state */ +mlan_status woal_get_wpa_enable(moal_private *priv, t_u8 wait_option, + t_u32 *enable); +#endif /**STA_SUPPORT */ + +mlan_status woal_set_11d(moal_private *priv, t_u8 wait_option, t_u8 enable); + +mlan_status woal_process_rf_test_mode(moal_handle *handle, t_u32 mode); +mlan_status woal_process_rf_test_mode_cmd(moal_handle *handle, t_u32 cmd, + const char *buffer, size_t len, + t_u32 action, t_u32 val); +#if defined(STA_SUPPORT) || defined(UAP_SUPPORT) +/** Get statistics information */ +mlan_status woal_get_stats_info(moal_private *priv, t_u8 wait_option, + pmlan_ds_get_stats stats); +#endif /**STA_SUPPORT||UAP_SUPPORT*/ + +mlan_status woal_set_wapi_enable(moal_private *priv, t_u8 wait_option, + t_u32 enable); + +/** Initialize priv */ +void woal_init_priv(moal_private *priv, t_u8 wait_option); +/** Reset interface(s) */ +int woal_reset_intf(moal_private *priv, t_u8 wait_option, int all_intf); +#define TLV_TYPE_MGMT_IE (0x169) +#define MGMT_MASK_ASSOC_REQ 0x01 +#define MGMT_MASK_REASSOC_REQ 0x04 +#define MGMT_MASK_ASSOC_RESP 0x02 +#define MGMT_MASK_REASSOC_RESP 0x08 +#define MGMT_MASK_PROBE_REQ 0x10 +#define MGMT_MASK_PROBE_RESP 0x20 +#define MGMT_MASK_BEACON 0x100 +#define MGMT_MASK_ASSOC_RESP_QOS_MAP 0x4000 +#define MGMT_MASK_BEACON_WPS_P2P 0x8000 +#define MLAN_CUSTOM_IE_DELETE_MASK 0x0 +/** common ioctl for uap, station */ +int woal_custom_ie_ioctl(struct net_device *dev, struct ifreq *req); +#ifdef UAP_SUPPORT +int woal_priv_get_nonglobal_operclass_by_bw_channel(moal_private *priv, + t_u8 bandwidth, + t_u8 channel, + t_u8 *oper_class); +#endif +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(STA_SUPPORT) && defined(UAP_SUPPORT) +mlan_status woal_bss_role_cfg(moal_private *priv, t_u8 action, t_u8 wait_option, + t_u8 *bss_role); +#if defined(STA_CFG80211) && defined(UAP_CFG80211) +void woal_go_timer_func(void *context); +#endif +#if defined(STA_WEXT) || defined(UAP_WEXT) +int woal_set_get_bss_role(moal_private *priv, struct iwreq *wrq); +#endif +#endif +#if defined(WIFI_DIRECT_SUPPORT) || defined(UAP_SUPPORT) +/** hostcmd ioctl for uap, wifidirect */ +int woal_hostcmd_ioctl(struct net_device *dev, struct ifreq *req); +#endif + +mlan_status woal_set_remain_channel_ioctl(moal_private *priv, t_u8 wait_option, + pmlan_ds_remain_chan pchan); +void woal_remain_timer_func(void *context); +#ifdef WIFI_DIRECT_SUPPORT +mlan_status woal_wifi_direct_mode_cfg(moal_private *priv, t_u16 action, + t_u16 *mode); +mlan_status woal_p2p_config(moal_private *priv, t_u32 action, + mlan_ds_wifi_direct_config *p2p_config); +#endif /* WIFI_DIRECT_SUPPORT */ + +int woal_11h_cancel_chan_report_ioctl(moal_private *priv, t_u8 wait_option); + +#ifdef CONFIG_PROC_FS +/** Initialize /proc/mwlan */ +mlan_status woal_root_proc_init(void); +/** Remove /proc/mwlan */ +void woal_root_proc_remove(void); +/** 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 + +/** Create debug proc fs */ +void woal_debug_entry(moal_private *priv); +/** Remove debug proc fs */ +void woal_debug_remove(moal_private *priv); + +/** check pm info */ +mlan_status woal_get_pm_info(moal_private *priv, mlan_ds_ps_info *pm_info); +/** get mlan debug info */ +void woal_mlan_debug_info(moal_private *priv); + +#ifdef USB +#ifdef CONFIG_USB_SUSPEND +/** Enter USB Suspend */ +int woal_enter_usb_suspend(moal_handle *handle); +/** Exit from USB Suspend */ +int woal_exit_usb_suspend(moal_handle *handle); +#endif /* CONFIG_USB_SUSPEND */ +#endif + +#ifdef REASSOCIATION +int woal_reassociation_thread(void *data); +void woal_reassoc_timer_func(void *context); +#endif /* REASSOCIATION */ + +t_void woal_main_work_queue(struct work_struct *work); +t_void woal_rx_work_queue(struct work_struct *work); +t_void woal_evt_work_queue(struct work_struct *work); + +netdev_tx_t woal_hard_start_xmit(struct sk_buff *skb, struct net_device *dev); +#ifdef STA_SUPPORT +mlan_status woal_init_sta_dev(struct net_device *dev, moal_private *priv); +#endif +#ifdef UAP_SUPPORT +mlan_status woal_init_uap_dev(struct net_device *dev, moal_private *priv); +#endif +mlan_status woal_update_drv_tbl(moal_handle *handle, int drv_mode_local); +moal_private *woal_add_interface(moal_handle *handle, t_u8 bss_num, + t_u8 bss_type); +void woal_remove_interface(moal_handle *handle, t_u8 bss_index); +void woal_set_multicast_list(struct net_device *dev); +mlan_status woal_request_fw(moal_handle *handle); +int woal_11h_channel_check_ioctl(moal_private *priv, t_u8 wait_option); +void woal_cancel_cac_block(moal_private *priv); +void woal_moal_debug_info(moal_private *priv, moal_handle *handle, u8 flag); + +#ifdef STA_SUPPORT +mlan_status woal_get_powermode(moal_private *priv, int *powermode); +mlan_status woal_set_scan_type(moal_private *priv, t_u32 scan_type); +mlan_status woal_get_scan_config(moal_private *priv, mlan_scan_cfg *scan_cfg); +mlan_status woal_enable_ext_scan(moal_private *priv, t_u8 enable); +mlan_status woal_set_powermode(moal_private *priv, char *powermode); +int woal_find_essid(moal_private *priv, mlan_ssid_bssid *ssid_bssid, + t_u8 wait_option); +mlan_status woal_find_bssid(moal_private *priv, mlan_802_11_mac_addr bssid); +mlan_status woal_request_userscan(moal_private *priv, t_u8 wait_option, + wlan_user_scan_cfg *scan_cfg); +mlan_status woal_do_scan(moal_private *priv, wlan_user_scan_cfg *scan_cfg); +int woal_set_combo_scan(moal_private *priv, char *buf, int length); +mlan_status woal_set_scan_time(moal_private *priv, t_u16 active_scan_time, + t_u16 passive_scan_time, + t_u16 specific_scan_time); +mlan_status woal_get_band(moal_private *priv, int *band); +mlan_status woal_set_band(moal_private *priv, char *pband); +mlan_status woal_add_rxfilter(moal_private *priv, char *rxfilter); +mlan_status woal_remove_rxfilter(moal_private *priv, char *rxfilter); +mlan_status woal_priv_qos_cfg(moal_private *priv, t_u32 action, char *qos_cfg); +mlan_status woal_set_sleeppd(moal_private *priv, char *psleeppd); +int woal_set_scan_cfg(moal_private *priv, char *buf, int length); +void woal_update_dscp_mapping(moal_private *priv); + +/* EVENT: BCN_RSSI_LOW */ +#define EVENT_BCN_RSSI_LOW 0x0001 +/* EVENT: PRE_BCN_LOST */ +#define EVENT_PRE_BCN_LOST 0x0002 +mlan_status woal_set_rssi_low_threshold(moal_private *priv, char *rssi, + t_u8 wait_option); +mlan_status woal_set_rssi_threshold(moal_private *priv, t_u32 event_id, + t_u8 wait_option); +/* EVENT: BG_SCAN_REPORT */ +#define EVENT_BG_SCAN_REPORT 0x0004 +mlan_status woal_set_bg_scan(moal_private *priv, char *buf, int length); +mlan_status woal_stop_bg_scan(moal_private *priv, t_u8 wait_option); +void woal_reconfig_bgscan(moal_handle *handle); +#ifdef STA_CFG80211 +void woal_config_bgscan_and_rssi(moal_private *priv, t_u8 set_rssi); +void woal_start_roaming(moal_private *priv); +#endif +mlan_status woal_request_bgscan(moal_private *priv, t_u8 wait_option, + wlan_bgscan_cfg *scan_cfg); +#endif +#ifdef STA_CFG80211 +void woal_save_conn_params(moal_private *priv, + struct cfg80211_connect_params *sme); +void woal_clear_conn_params(moal_private *priv); +#endif + +void woal_flush_tcp_sess_queue(moal_private *priv); +void wlan_scan_create_brief_table_entry(t_u8 **ppbuffer, + BSSDescriptor_t *pbss_desc); +int wlan_get_scan_table_ret_entry(BSSDescriptor_t *pbss_desc, t_u8 **ppbuffer, + int *pspace_left); +BOOLEAN woal_ssid_valid(mlan_802_11_ssid *pssid); +int woal_is_connected(moal_private *priv, mlan_ssid_bssid *ssid_bssid); +int woal_priv_hostcmd(moal_private *priv, t_u8 *respbuf, t_u32 respbuflen, + t_u8 wait_option); +void woal_flush_tx_stat_queue(moal_private *priv); +struct tx_status_info *woal_get_tx_info(moal_private *priv, t_u8 tx_seq_num); +void woal_remove_tx_info(moal_private *priv, t_u8 tx_seq_num); + +mlan_status woal_request_country_power_table(moal_private *priv, char *region); +#ifdef RX_PACKET_COALESCE +mlan_status woal_rx_pkt_coalesce_cfg(moal_private *priv, t_u16 *enable, + t_u8 wait_option, t_u8 action); +#endif +mlan_status woal_set_low_pwr_mode(moal_handle *handle, t_u8 wait_option); +int woal_hexval(char chr); +mlan_status woal_pmic_configure(moal_handle *handle, t_u8 wait_option); +mlan_status woal_set_user_antcfg(moal_handle *handle, t_u8 wait_option); +void woal_hist_data_reset(moal_private *priv); +void woal_hist_do_reset(moal_private *priv, void *data); +void woal_hist_reset_table(moal_private *priv, t_u8 antenna); +void woal_hist_data_add(moal_private *priv, t_u16 rx_rate, t_s8 snr, t_s8 nflr, + t_u8 antenna); +mlan_status woal_set_hotspotcfg(moal_private *priv, t_u8 wait_option, + t_u32 hotspotcfg); +mlan_status woal_set_get_wowlan_config(moal_private *priv, t_u16 action, + t_u8 wait_option, + mlan_ds_misc_mef_flt_cfg *mefcfg); +mlan_status woal_set_auto_arp_ext(moal_handle *handle, t_u8 enable); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) +mlan_status woal_do_flr(moal_handle *handle, bool prepare); +#endif +mlan_status woal_delba_all(moal_private *priv, t_u8 wait_option); +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +int woal_mkeep_alive_vendor_event(moal_private *priv, + pmlan_ds_misc_keep_alive mkeep_alive); +#endif +#endif +int woal_start_mkeep_alive(moal_private *priv, t_u8 mkeep_alive_id, + t_u8 *ip_pkt, t_u16 ip_pkt_len, t_u8 *src_mac, + t_u8 *dst_mac, t_u32 period_msec, + t_u32 retry_interval, t_u8 retry_cnt); +int woal_stop_mkeep_alive(moal_private *priv, t_u8 mkeep_alive_id, t_u8 reset, + t_u8 *ip_pkt, t_u8 *pkt_len); +int woal_priv_save_cloud_keep_alive_params( + moal_private *priv, t_u8 mkeep_alive_id, t_u8 enable, t_u16 ether_type, + t_u8 *ip_pkt, t_u16 ip_pkt_len, t_u8 *src_mac, t_u8 *dst_mac, + t_u32 period_msec, t_u32 retry_interval, t_u8 retry_cnt); +mlan_status woal_init_aggr_ctrl(moal_handle *handle, t_u8 wait_option); + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +mlan_status woal_set_rekey_data(moal_private *priv, + mlan_ds_misc_gtk_rekey_data *gtk_rekey, + t_u8 action, t_u8 wait_option); +#endif +#endif + +mlan_status woal_vdll_req_fw(moal_handle *handle); + +void woal_ioctl_get_misc_conf(moal_private *priv, mlan_ds_misc_cfg *info); +t_u8 woal_get_second_channel_offset(int chan); +#endif /* _MOAL_MAIN_H */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_pcie.c b/mxm_wifiex/wlan_src/mlinux/moal_pcie.c new file mode 100644 index 0000000..45bc241 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_pcie.c @@ -0,0 +1,2436 @@ +/** @file moal_pcie.c + * + * @brief This file contains PCIE IF (interface) module + * related functions. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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/01/2012: initial version +********************************************************/ + +#include + +#include "moal_pcie.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) +#include +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 70) +#include +#endif +/******************************************************** + Local Variables +********************************************************/ +#define DRV_NAME "NXP mdriver PCIe" + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 6, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) +static struct pm_qos_request woal_pcie_pm_qos_req; +#endif +#endif + +/* PCIE resume handler */ +static int woal_pcie_resume(struct pci_dev *pdev); +static void woal_pcie_reg_dbg(moal_handle *phandle); +static void woal_pcie_unregister_dev(moal_handle *handle); +static void woal_pcie_cleanup(pcie_service_card *card); +static mlan_status woal_pcie_init(pcie_service_card *card); +extern int pcie_int_mode; +extern struct semaphore AddRemoveCardSem; +extern moal_handle **m_handle; + +/** WLAN IDs */ +static const struct pci_device_id wlan_ids[] = { +#ifdef PCIE8897 + { + PCIE_VENDOR_ID_NXP, + PCIE_DEVICE_ID_NXP_88W8897P, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + }, +#endif +#ifdef PCIE8997 + { + PCIE_VENDOR_ID_NXP, + PCIE_DEVICE_ID_NXP_88W8997P, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + }, + { + PCIE_VENDOR_ID_V2_NXP, + PCIE_DEVICE_ID_NXP_88W8997P, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + }, +#endif +#ifdef PCIE9097 + { + PCIE_VENDOR_ID_V2_NXP, + PCIE_DEVICE_ID_NXP_88W9097, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + }, +#endif +#ifdef PCIE9098 + { + PCIE_VENDOR_ID_V2_NXP, + PCIE_DEVICE_ID_NXP_88W9098P_FN0, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + }, + { + PCIE_VENDOR_ID_V2_NXP, + PCIE_DEVICE_ID_NXP_88W9098P_FN1, + PCI_ANY_ID, + PCI_ANY_ID, + 0, + 0, + }, +#endif + {}, +}; +/* moal interface ops */ +static moal_if_ops pcie_ops; + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ +static mlan_status woal_pcie_preinit(struct pci_dev *pdev); + +/** @brief This function updates the card types + * + * @param handle A Pointer to the moal_handle structure + * @param card A Pointer to card + * + * @return N/A + */ +static t_u16 woal_update_card_type(t_void *card) +{ + pcie_service_card *cardp_pcie = (pcie_service_card *)card; + t_u16 card_type = 0; + + /* Update card type */ +#ifdef PCIE8897 + if (cardp_pcie->dev->device == PCIE_DEVICE_ID_NXP_88W8897P) { + card_type = CARD_TYPE_PCIE8897; + moal_memcpy_ext(NULL, driver_version, CARD_PCIE8897, + strlen(CARD_PCIE8897), strlen(driver_version)); + moal_memcpy_ext(NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V15, strlen(V15), + strlen(driver_version) - strlen(INTF_CARDTYPE) - + strlen(KERN_VERSION)); + } +#endif +#ifdef PCIE8997 + if (cardp_pcie->dev->device == PCIE_DEVICE_ID_NXP_88W8997P) { + card_type = CARD_TYPE_PCIE8997; + moal_memcpy_ext(NULL, driver_version, CARD_PCIE8997, + strlen(CARD_PCIE8997), strlen(driver_version)); + moal_memcpy_ext(NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V16, strlen(V16), + strlen(driver_version) - strlen(INTF_CARDTYPE) - + strlen(KERN_VERSION)); + } +#endif +#ifdef PCIE9097 + if (cardp_pcie->dev->device == PCIE_DEVICE_ID_NXP_88W9097) { + card_type = CARD_TYPE_PCIE9097; + moal_memcpy_ext(NULL, driver_version, CARD_PCIE9097, + strlen(CARD_PCIE9097), strlen(driver_version)); + moal_memcpy_ext(NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V17, strlen(V17), + strlen(driver_version) - strlen(INTF_CARDTYPE) - + strlen(KERN_VERSION)); + } +#endif +#ifdef PCIE9098 + if (cardp_pcie->dev->device == PCIE_DEVICE_ID_NXP_88W9098P_FN0 || + cardp_pcie->dev->device == PCIE_DEVICE_ID_NXP_88W9098P_FN1) { + card_type = CARD_TYPE_PCIE9098; + moal_memcpy_ext(NULL, driver_version, CARD_PCIE9098, + strlen(CARD_PCIE9098), strlen(driver_version)); + moal_memcpy_ext(NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V17, strlen(V17), + strlen(driver_version) - strlen(INTF_CARDTYPE) - + strlen(KERN_VERSION)); + } +#endif + return card_type; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) +/** + * @brief Function to process pre/post PCIe function level reset + * + * @param handle A pointer to moal_handle structure + * @param prepare True :- its a pre FLR call from the kernel + * False :- its a post FLR call from the kernel + * + * Note: This function is mix of woal_switch_drv_mode() and + * remove_card(). Idea is to cleanup the software only without + * touching the PCIe specific code. Likewise, during init init + * everything, including hw, but do not reinitiate PCIe stack + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_do_flr(moal_handle *handle, bool prepare) +{ + unsigned int i; + int index = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + pcie_service_card *card = NULL; + int fw_serial_bkp; + + ENTER(); + + if (!handle) { + PRINTM(MINFO, "\n Handle null during prepare=%d\n", prepare); + LEAVE(); + return status; + } + + card = (pcie_service_card *)handle->card; + + if (card == NULL) { + PRINTM(MERROR, "The parameter 'card' is NULL\n"); + LEAVE(); + return (mlan_status)MLAN_STATUS_FAILURE; + } + + if (!IS_PCIE8997(handle->card_type) && + !IS_PCIE9097(handle->card_type) && + !IS_PCIE9098(handle->card_type)) { + LEAVE(); + return status; + } + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&AddRemoveCardSem)) + goto exit_sem_err; + + if (!prepare) + goto perform_init; + + /* Reset all interfaces */ + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MTRUE); + + /* Shutdown firmware */ + 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); + + 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 (atomic_read(&handle->lock_count) || + atomic_read(&handle->malloc_count) || + atomic_read(&handle->mbufalloc_count)) { + PRINTM(MERROR, + "mlan has memory leak: lock_count=%d," + " malloc_count=%d, mbufalloc_count=%d\n", + atomic_read(&handle->lock_count), + atomic_read(&handle->malloc_count), + atomic_read(&handle->mbufalloc_count)); + } + if (atomic_read(&handle->malloc_cons_count)) { + PRINTM(MERROR, + "mlan has memory leak: malloc_cons_count=%d\n", + atomic_read(&handle->malloc_cons_count)); + } + handle->pmlan_adapter = NULL; + } + + goto exit; + +perform_init: + handle->priv_num = 0; + + /* Init SW */ + if (woal_init_sw(handle)) { + PRINTM(MFATAL, "Software Init Failed\n"); + goto err_init_fw; + } + +#ifdef PCIE9098 + if (card->dev->device == PCIE_DEVICE_ID_NXP_88W9098P_FN1) + mlan_set_int_mode(handle->pmlan_adapter, pcie_int_mode, 1); + else +#endif + /* Update pcie_int_mode in mlan adapter */ + mlan_set_int_mode(handle->pmlan_adapter, + handle->params.pcie_int_mode, 0); + + /* Init FW and HW */ + /* Load wlan only binary */ + fw_serial_bkp = moal_extflg_isset(handle, EXT_FW_SERIAL); + moal_extflg_clear(handle, EXT_FW_SERIAL); + woal_update_firmware_name(handle); + if (woal_init_fw(handle)) { + PRINTM(MFATAL, "Firmware Init Failed\n"); + woal_pcie_reg_dbg(handle); + if (fw_serial_bkp) + moal_extflg_set(handle, EXT_FW_SERIAL); + goto err_init_fw; + } + if (fw_serial_bkp) + moal_extflg_set(handle, EXT_FW_SERIAL); +exit: + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); + +exit_sem_err: + LEAVE(); + return status; + +err_init_fw: + if ((handle->hardware_status == HardwareStatusFwReady) || + (handle->hardware_status == HardwareStatusReady)) { + PRINTM(MINFO, "shutdown mlan\n"); + handle->init_wait_q_woken = MFALSE; + status = mlan_shutdown_fw(handle->pmlan_adapter); + if (status == MLAN_STATUS_PENDING) + wait_event_interruptible(handle->init_wait_q, + handle->init_wait_q_woken); + } +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + wakeup_source_trash(&handle->ws); +#else + wake_lock_destroy(&handle->wake_lock); +#endif +#endif +#ifdef CONFIG_PROC_FS + woal_proc_exit(handle); +#endif + /* Unregister device */ + PRINTM(MINFO, "unregister device\n"); + woal_pcie_unregister_dev(handle); + 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); + woal_free_moal_handle(handle); + + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] == handle) + break; + } + if (index < MAX_MLAN_ADAPTER) + m_handle[index] = NULL; + card->handle = NULL; + MOAL_REL_SEMAPHORE(&AddRemoveCardSem); + LEAVE(); + return (mlan_status)MLAN_STATUS_FAILURE; +} +#endif + +/** + * @brief This function handles PCIE driver probe + * + * @param pdev A pointer to pci_dev structure + * @param id A pointer to pci_device_id structure + * + * @return error code + */ +int woal_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + pcie_service_card *card = NULL; + t_u16 card_type = 0; + int ret = 0; + + ENTER(); + + PRINTM(MINFO, "vendor=0x%4.04X device=0x%4.04X rev=%d\n", pdev->vendor, + pdev->device, pdev->revision); + + /* Preinit PCIE device so allocate PCIE memory can be successful */ + if (woal_pcie_preinit(pdev)) { + PRINTM(MFATAL, "MOAL PCIE preinit failed\n"); + LEAVE(); + return -EFAULT; + } + + card = kzalloc(sizeof(pcie_service_card), GFP_KERNEL); + if (!card) { + PRINTM(MERROR, "%s: failed to alloc memory\n", __func__); + ret = -ENOMEM; + goto err; + } + + card->dev = pdev; + + card_type = woal_update_card_type(card); + if (!card_type) { + PRINTM(MERROR, "pcie probe: woal_update_card_type() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + woal_pcie_init(card); + + if (woal_add_card(card, &card->dev->dev, &pcie_ops, card_type) == + NULL) { + woal_pcie_cleanup(card); + PRINTM(MERROR, "%s: failed\n", __func__); + ret = -EFAULT; + goto err; + } + + LEAVE(); + return ret; +err: + kfree(card); + if (pci_is_enabled(pdev)) + pci_disable_device(pdev); + + LEAVE(); + return ret; +} + +/** + * @brief This function handles PCIE driver remove + * + * @param pdev A pointer to pci_dev structure + * + * @return error code + */ +static void woal_pcie_remove(struct pci_dev *dev) +{ + pcie_service_card *card; + moal_handle *handle; + + ENTER(); + card = pci_get_drvdata(dev); + if (!card) { + PRINTM(MINFO, "PCIE card removed from slot\n"); + LEAVE(); + return; + } + + handle = card->handle; + if (!handle || !handle->priv_num) { + PRINTM(MINFO, "PCIE card handle removed\n"); + LEAVE(); + return; + } + handle->surprise_removed = MTRUE; + woal_remove_card(card); + woal_pcie_cleanup(card); + kfree(card); + + LEAVE(); + return; +} + +/** + * @brief Handle suspend + * + * @param pdev A pointer to pci_dev structure + * @param state PM state message + * + * @return error code + */ +static int woal_pcie_suspend(struct pci_dev *pdev, pm_message_t state) +{ + pcie_service_card *cardp; + moal_handle *handle = NULL; + int i; + int ret = MLAN_STATUS_SUCCESS; + int hs_actived; + mlan_ds_ps_info pm_info; + + ENTER(); + PRINTM(MCMND, "<--- Enter woal_pcie_suspend --->\n"); + if (pdev) { + cardp = (pcie_service_card *)pci_get_drvdata(pdev); + if (!cardp || !cardp->handle) { + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } else { + PRINTM(MERROR, "PCIE device is not specified\n"); + LEAVE(); + return -ENOSYS; + } + + handle = cardp->handle; + if (handle->is_suspended == MTRUE) { + PRINTM(MWARN, "Device already suspended\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + if (handle->fw_dump) { + PRINTM(MMSG, "suspend not allowed while FW dump!"); + ret = -EBUSY; + goto done; + } + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) + woal_cancel_scan(handle->priv[i], MOAL_IOCTL_WAIT); + } + handle->suspend_fail = MFALSE; + memset(&pm_info, 0, sizeof(pm_info)); +#define MAX_RETRY_NUM 8 + for (i = 0; i < MAX_RETRY_NUM; i++) { + if (MLAN_STATUS_SUCCESS == + woal_get_pm_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + &pm_info)) { + if (pm_info.is_suspend_allowed == MTRUE) + break; + else + PRINTM(MMSG, + "Suspend not allowed and retry again\n"); + } + woal_sched_timeout(100); + } + if (pm_info.is_suspend_allowed == MFALSE) { + PRINTM(MMSG, "Suspend not allowed\n"); + ret = -EBUSY; + goto done; + } + + for (i = 0; i < handle->priv_num; i++) + netif_device_detach(handle->priv[i]->netdev); + + /* Enable Host Sleep */ + hs_actived = woal_enable_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY)); + if (hs_actived == MTRUE) { + /* Indicate device suspended */ + handle->is_suspended = MTRUE; + } else { + PRINTM(MMSG, "HS not actived, suspend fail!"); + handle->suspend_fail = MTRUE; + for (i = 0; i < handle->priv_num; i++) + netif_device_attach(handle->priv[i]->netdev); + ret = -EBUSY; + goto done; + } + flush_workqueue(handle->workqueue); + flush_workqueue(handle->evt_workqueue); + if (handle->rx_workqueue) + flush_workqueue(handle->rx_workqueue); + if (handle->tx_workqueue) + flush_workqueue(handle->tx_workqueue); + pci_enable_wake(pdev, pci_choose_state(pdev, state), 1); + pci_save_state(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); +done: + PRINTM(MCMND, "<--- Leave woal_pcie_suspend --->\n"); + LEAVE(); + return ret; +} + +/** + * @brief Handle resume + * + * @param pdev A pointer to pci_dev structure + * + * @return error code + */ +static int woal_pcie_resume(struct pci_dev *pdev) +{ + moal_handle *handle; + pcie_service_card *card; + int i; + + ENTER(); + + PRINTM(MCMND, "<--- Enter woal_pcie_resume --->\n"); + if (pdev) { + card = (pcie_service_card *)pci_get_drvdata(pdev); + if (!card || !card->handle) { + PRINTM(MERROR, "Card or handle is not valid\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + } else { + PRINTM(MERROR, "PCIE device is not specified\n"); + LEAVE(); + return -ENOSYS; + } + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D0, 0); + + handle = card->handle; + + if (handle->is_suspended == MFALSE) { + PRINTM(MWARN, "Device already resumed\n"); + goto done; + } + + handle->is_suspended = MFALSE; + + if (woal_check_driver_status(handle)) { + PRINTM(MERROR, "Resuem, device is in hang state\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + for (i = 0; i < handle->priv_num; i++) + netif_device_attach(handle->priv[i]->netdev); + + woal_cancel_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), MOAL_NO_WAIT); + +done: + PRINTM(MCMND, "<--- Leave woal_pcie_resume --->\n"); + LEAVE(); + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) +/** + * @brief Pcie reset prepare handler + * + * @param pdev A pointer to pci_dev structure + */ +static void woal_pcie_reset_prepare(struct pci_dev *pdev) +{ + pcie_service_card *card; + moal_handle *handle; + moal_handle *ref_handle = NULL; + + ENTER(); + + card = pci_get_drvdata(pdev); + if (!card) { + PRINTM(MINFO, "PCIE card removed from slot\n"); + LEAVE(); + return; + } + + handle = card->handle; + + if (!handle) { + PRINTM(MINFO, "Invalid handle\n"); + LEAVE(); + return; + } + + PRINTM(MMSG, "%s: vendor=0x%4.04X device=0x%4.04X rev=%d Pre-FLR\n", + __func__, pdev->vendor, pdev->device, pdev->revision); + + /* Kernel would be performing FLR after this notification. + * Cleanup up all software withouth cleaning anything related to + * PCIe and HW. + * Note. FW might not be healthy. + */ + // handle-> mac0 , ref_handle->second mac + if (handle->pref_mac) { + if (handle->second_mac) { + handle = (moal_handle *)handle->pref_mac; + ref_handle = (moal_handle *)handle->pref_mac; + } else { + ref_handle = (moal_handle *)handle->pref_mac; + } + } + handle->surprise_removed = MTRUE; + woal_do_flr(handle, true); + if (ref_handle) { + ref_handle->surprise_removed = MTRUE; + woal_do_flr(ref_handle, true); + } + + LEAVE(); +} +/** + * @brief Pcie reset done handler + * + * @param pdev A pointer to pci_dev structure + */ +static void woal_pcie_reset_done(struct pci_dev *pdev) +{ + pcie_service_card *card; + moal_handle *handle; + moal_handle *ref_handle = NULL; + ENTER(); + + card = pci_get_drvdata(pdev); + if (!card) { + PRINTM(MINFO, "PCIE card removed from slot\n"); + LEAVE(); + return; + } + + handle = card->handle; + if (!handle) { + PRINTM(MINFO, "Invalid handle\n"); + LEAVE(); + return; + } + + PRINTM(MMSG, "%s: vendor=0x%4.04X device=0x%4.04X rev=%d Post-FLR\n", + __func__, pdev->vendor, pdev->device, pdev->revision); + + /* Kernel stores and restores PCIe function context before and + * after performing FLR, respectively. + * + * Reconfigure the sw and fw including fw redownload + */ + // handle-> mac0 , ref_handle->second mac + if (handle->pref_mac) { + if (handle->second_mac) { + handle = (moal_handle *)handle->pref_mac; + ref_handle = (moal_handle *)handle->pref_mac; + } else { + ref_handle = (moal_handle *)handle->pref_mac; + } + } + handle->surprise_removed = MFALSE; + woal_do_flr(handle, false); + if (ref_handle) { + ref_handle->surprise_removed = MFALSE; + woal_do_flr(ref_handle, false); + } + + LEAVE(); +} +#else +void woal_pcie_reset_notify(struct pci_dev *pdev, bool prepare) +{ + pcie_service_card *card; + moal_handle *handle; + moal_handle *ref_handle = NULL; + + ENTER(); + + card = pci_get_drvdata(pdev); + if (!card) { + PRINTM(MINFO, "PCIE card removed from slot\n"); + LEAVE(); + return; + } + + handle = card->handle; + if (!handle) { + PRINTM(MINFO, "Invalid handle\n"); + LEAVE(); + return; + } + + PRINTM(MMSG, "%s: vendor=0x%4.04X device=0x%4.04X rev=%d %s\n", + __func__, pdev->vendor, pdev->device, pdev->revision, + prepare ? "Pre-FLR" : "Post-FLR"); + + // handle-> mac0 , ref_handle->second mac + if (handle->pref_mac) { + if (handle->second_mac) { + handle = (moal_handle *)handle->pref_mac; + ref_handle = (moal_handle *)handle->pref_mac; + } else { + ref_handle = (moal_handle *)handle->pref_mac; + } + } + + if (prepare) { + /* Kernel would be performing FLR after this notification. + * Cleanup up all software withouth cleaning anything related to + * PCIe and HW. + * Note. FW might not be healthy. + */ + handle->surprise_removed = MTRUE; + woal_do_flr(handle, prepare); + if (ref_handle) { + ref_handle->surprise_removed = MTRUE; + woal_do_flr(ref_handle, prepare); + } + } else { + /* Kernel stores and restores PCIe function context before and + * after performing FLR, respectively. + * + * Reconfigure the sw and fw including fw redownload + */ + handle->surprise_removed = MFALSE; + woal_do_flr(handle, prepare); + if (ref_handle) { + ref_handle->surprise_removed = MFALSE; + woal_do_flr(ref_handle, prepare); + } + } + LEAVE(); +} +#endif + +static const struct pci_error_handlers woal_pcie_err_handler[] = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) + { + .reset_prepare = woal_pcie_reset_prepare, + .reset_done = woal_pcie_reset_done, + }, +#else + { + .reset_notify = woal_pcie_reset_notify, + }, +#endif +}; +#endif // KERNEL_VERSION(3.18.0) + +/* PCI Device Driver */ +static struct pci_driver REFDATA wlan_pcie = { + .name = "wlan_pcie", + .id_table = wlan_ids, + .probe = woal_pcie_probe, + .remove = woal_pcie_remove, +#ifdef CONFIG_PM + /* Power Management Hooks */ + .suspend = woal_pcie_suspend, + .resume = woal_pcie_resume, +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + .err_handler = woal_pcie_err_handler, +#endif +}; + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @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 + */ +static mlan_status woal_pcie_write_reg(moal_handle *handle, t_u32 reg, + t_u32 data) +{ + pcie_service_card *card = (pcie_service_card *)handle->card; + + iowrite32(data, card->pci_mmap1 + reg); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @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 + */ +static mlan_status woal_pcie_read_reg(moal_handle *handle, t_u32 reg, + t_u32 *data) +{ + pcie_service_card *card = (pcie_service_card *)handle->card; + *data = ioread32(card->pci_mmap1 + reg); + + if (*data == MLAN_STATUS_FAILURE) + return MLAN_STATUS_FAILURE; + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function writes multiple bytes into card memory + * + * @param handle A Pointer to the moal_handle structure + * @param pmbuf Pointer to mlan_buffer structure + * @param port Port + * @param timeout Time out value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_pcie_write_data_sync(moal_handle *handle, mlan_buffer *pmbuf, + t_u32 port, t_u32 timeout) +{ + return MLAN_STATUS_SUCCESS; +} + +/** + * @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_pcie_read_data_sync(moal_handle *handle, mlan_buffer *pmbuf, + t_u32 port, t_u32 timeout) +{ + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function handles the interrupt. + * + * @param irq The irq no. of PCIE device + * @param dev_id A pointer to the pci_dev structure + * + * @return IRQ_HANDLED + */ +static irqreturn_t woal_pcie_interrupt(int irq, void *dev_id) +{ + struct pci_dev *pdev; + pcie_service_card *card; + moal_handle *handle; + mlan_status ret = MLAN_STATUS_SUCCESS; + + pdev = (struct pci_dev *)dev_id; + if (!pdev) { + PRINTM(MFATAL, "%s: pdev is NULL\n", (t_u8 *)pdev); + goto exit; + } + + card = (pcie_service_card *)pci_get_drvdata(pdev); + if (!card || !card->handle) { + PRINTM(MFATAL, "%s: card=%p handle=%p\n", __func__, card, + card ? card->handle : NULL); + goto exit; + } + handle = card->handle; + if (handle->surprise_removed == MTRUE) { + ret = MLAN_STATUS_FAILURE; + goto exit; + } + PRINTM(MINFO, "*** IN PCIE IRQ ***\n"); + handle->main_state = MOAL_RECV_INT; + PRINTM(MINTR, "*\n"); + + ret = mlan_interrupt(0xffff, handle->pmlan_adapter); + queue_work(handle->workqueue, &handle->main_work); + +exit: + if (ret == MLAN_STATUS_SUCCESS) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + +/** + * @brief This function handles the MSI-X interrupt. + * + * @param irq The irq no. of PCIE device + * @param dev_id A pointer to the msix_context structure + * + * @return IRQ_HANDLED + */ +static irqreturn_t woal_pcie_msix_interrupt(int irq, void *dev_id) +{ + struct pci_dev *pdev; + pcie_service_card *card; + moal_handle *handle; + msix_context *ctx = (msix_context *)dev_id; + mlan_status ret = MLAN_STATUS_SUCCESS; + + if (!ctx) { + PRINTM(MFATAL, "%s: ctx=%p is NULL\n", __func__, ctx); + goto exit; + } + + pdev = ctx->dev; + + if (!pdev) { + PRINTM(MFATAL, "%s: pdev is NULL\n", (t_u8 *)pdev); + goto exit; + } + + card = (pcie_service_card *)pci_get_drvdata(pdev); + if (!card || !card->handle) { + PRINTM(MFATAL, "%s: card=%p handle=%p\n", __func__, card, + card ? card->handle : NULL); + goto exit; + } + handle = card->handle; + if (handle->surprise_removed == MTRUE) { + ret = MLAN_STATUS_FAILURE; + goto exit; + } + PRINTM(MINFO, "*** IN PCIE IRQ ***\n"); + handle->main_state = MOAL_RECV_INT; + PRINTM(MINTR, "*\n"); + ret = mlan_interrupt(ctx->msg_id, handle->pmlan_adapter); + queue_work(handle->workqueue, &handle->main_work); + +exit: + if (ret == MLAN_STATUS_SUCCESS) + return IRQ_HANDLED; + else + return IRQ_NONE; +} +/** + * @brief This function pre-initializes the PCI-E host + * memory space, etc. + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_pcie_preinit(struct pci_dev *pdev) +{ + int ret; + + ret = pci_enable_device(pdev); + if (ret) + goto err_enable_dev; + + pci_set_master(pdev); + + PRINTM(MINFO, "Try set_consistent_dma_mask(32)\n"); + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + PRINTM(MERROR, "set_dma_mask(32) failed\n"); + goto err_set_dma_mask; + } + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + PRINTM(MERROR, "set_consistent_dma_mask(64) failed\n"); + goto err_set_dma_mask; + } + return MLAN_STATUS_SUCCESS; + +err_set_dma_mask: + pci_disable_device(pdev); +err_enable_dev: + return MLAN_STATUS_FAILURE; +} + +/** + * @brief This function initializes the PCI-E host + * memory space, etc. + * + * @param card A pointer to pcie_service_card structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_pcie_init(pcie_service_card *card) +{ + struct pci_dev *pdev = NULL; + int ret; + + pdev = card->dev; + pci_set_drvdata(pdev, card); +#if 0 + ret = pci_enable_device(pdev); + if (ret) + goto err_enable_dev; + + pci_set_master(pdev); + + PRINTM(MINFO, "Try set_consistent_dma_mask(32)\n"); + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + PRINTM(MERROR, "set_dma_mask(32) failed\n"); + goto err_set_dma_mask; + } + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + PRINTM(MERROR, "set_consistent_dma_mask(64) failed\n"); + goto err_set_dma_mask; + } +#endif + + ret = pci_request_region(pdev, 0, DRV_NAME); + if (ret) { + PRINTM(MERROR, "req_reg(0) error\n"); + goto err_req_region0; + } + card->pci_mmap = pci_iomap(pdev, 0, 0); + if (!card->pci_mmap) { + PRINTM(MERROR, "iomap(0) error\n"); + goto err_iomap0; + } + ret = pci_request_region(pdev, 2, DRV_NAME); + if (ret) { + PRINTM(MERROR, "req_reg(2) error\n"); + goto err_req_region2; + } + card->pci_mmap1 = pci_iomap(pdev, 2, 0); + if (!card->pci_mmap1) { + PRINTM(MERROR, "iomap(2) error\n"); + goto err_iomap2; + } + + PRINTM(MINFO, + "PCI memory map Virt0: %p PCI memory map Virt2: " + "%p\n", + card->pci_mmap, card->pci_mmap1); + + return MLAN_STATUS_SUCCESS; + +err_iomap2: + pci_release_region(pdev, 2); +err_req_region2: + pci_iounmap(pdev, card->pci_mmap); +err_iomap0: + pci_release_region(pdev, 0); +err_req_region0: +#if 0 +err_set_dma_mask: +#endif + +#if 0 +err_enable_dev: +#endif + pci_set_drvdata(pdev, NULL); + return MLAN_STATUS_FAILURE; +} + +/** + * @brief This function registers the PCIE device + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_pcie_register_dev(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + pcie_service_card *card = NULL; + struct pci_dev *pdev = NULL; + unsigned char nvec; + unsigned char i, j; + ENTER(); + + if (!handle || !handle->card) { + PRINTM(MINFO, "%s: handle=%p card=%p\n", __FUNCTION__, handle, + handle ? handle->card : NULL); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + card = (pcie_service_card *)handle->card; + pdev = card->dev; + /* save adapter pointer in card */ + card->handle = handle; + + switch (pcie_int_mode) { + case PCIE_INT_MODE_MSIX: + pcie_int_mode = PCIE_INT_MODE_MSIX; + nvec = PCIE_NUM_MSIX_VECTORS; + + for (i = 0; i < nvec; i++) { + card->msix_entries[i].entry = i; + } + + /* Try to enable msix */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + ret = pci_enable_msix_exact(pdev, card->msix_entries, nvec); +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) */ + ret = pci_enable_msix(pdev, card->msix_entries, nvec); +#endif + + if (ret == 0) { + for (i = 0; i < nvec; i++) { + card->msix_contexts[i].dev = pdev; + card->msix_contexts[i].msg_id = i; + ret = request_irq(card->msix_entries[i].vector, + woal_pcie_msix_interrupt, 0, + "mrvl_pcie_msix", + &(card->msix_contexts[i])); + + if (ret) { + PRINTM(MFATAL, + "request_irq failed: ret=%d\n", + ret); + for (j = 0; j < i; j++) + free_irq(card->msix_entries[j] + .vector, + &(card->msix_contexts + [i])); + pci_disable_msix(pdev); + break; + } + } + if (i == nvec) + break; + } + // follow through + + /* fall through */ + case PCIE_INT_MODE_MSI: + pcie_int_mode = PCIE_INT_MODE_MSI; + ret = pci_enable_msi(pdev); + if (ret == 0) { + ret = request_irq(pdev->irq, woal_pcie_interrupt, 0, + "mrvl_pcie_msi", pdev); + if (ret) { + PRINTM(MFATAL, "request_irq failed: ret=%d\n", + ret); + pci_disable_msi(pdev); + } else { + break; + } + } + // follow through + + /* fall through */ + case PCIE_INT_MODE_LEGACY: + pcie_int_mode = PCIE_INT_MODE_LEGACY; + ret = request_irq(pdev->irq, woal_pcie_interrupt, IRQF_SHARED, + "mrvl_pcie", pdev); + if (ret) { + PRINTM(MFATAL, "request_irq failed: ret=%d\n", ret); + handle->card = NULL; + ret = MLAN_STATUS_FAILURE; + goto done; + } + + break; + + default: + PRINTM(MFATAL, "pcie_int_mode %d failed\n", pcie_int_mode); + handle->card = NULL; + ret = MLAN_STATUS_FAILURE; + goto done; + break; + } + +#ifdef PCIE9098 + if (card->dev->device == PCIE_DEVICE_ID_NXP_88W9098P_FN1) + mlan_set_int_mode(handle->pmlan_adapter, pcie_int_mode, 1); + else +#endif + mlan_set_int_mode(handle->pmlan_adapter, pcie_int_mode, 0); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function cleans up the host memory spaces + * + * @param card A pointer to pcie_service_card structure + * + * @return N/A + */ +void woal_pcie_cleanup(pcie_service_card *card) +{ + struct pci_dev *pdev = NULL; + pdev = card->dev; + PRINTM(MINFO, "Clearing driver ready signature\n"); + + if (pdev) { + pci_iounmap(pdev, card->pci_mmap); + pci_iounmap(pdev, card->pci_mmap1); + + if (pci_is_enabled(pdev)) + pci_disable_device(pdev); + + pci_release_region(pdev, 0); + pci_release_region(pdev, 2); + pci_set_drvdata(pdev, NULL); + } +} + +/** + * @brief This function unregisters the PCIE device + * + * @param handle A pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static void woal_pcie_unregister_dev(moal_handle *handle) +{ + pcie_service_card *card = + handle ? (pcie_service_card *)handle->card : NULL; + struct pci_dev *pdev = NULL; + unsigned char i; + unsigned char nvec; + ENTER(); + + if (card) { + pdev = card->dev; + PRINTM(MINFO, "%s(): calling free_irq()\n", __func__); + + switch (pcie_int_mode) { + case PCIE_INT_MODE_MSIX: + nvec = PCIE_NUM_MSIX_VECTORS; + + for (i = 0; i < nvec; i++) + synchronize_irq(card->msix_entries[i].vector); + + for (i = 0; i < nvec; i++) + free_irq(card->msix_entries[i].vector, + &(card->msix_contexts[i])); + + pci_disable_msix(pdev); + + break; + + case PCIE_INT_MODE_MSI: + free_irq(card->dev->irq, pdev); + pci_disable_msi(pdev); + break; + + case PCIE_INT_MODE_LEGACY: + free_irq(card->dev->irq, pdev); + break; + + default: + PRINTM(MFATAL, "pcie_int_mode %d failed\n", + pcie_int_mode); + break; + } + card->handle = NULL; + } + LEAVE(); +} + +/** + * @brief This function registers the IF module in bus driver + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_pcie_bus_register(void) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 6, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + pm_qos_add_request(&woal_pcie_pm_qos_req, PM_QOS_CPU_DMA_LATENCY, 0); +#endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 70) + request_bus_freq(BUS_FREQ_HIGH); +#endif + /* API registers the NXP PCIE driver */ + if (pci_register_driver(&wlan_pcie)) { + PRINTM(MFATAL, "PCIE Driver Registration Failed \n"); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 6, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + pm_qos_remove_request(&woal_pcie_pm_qos_req); +#endif +#endif + ret = MLAN_STATUS_FAILURE; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function de-registers the IF module in bus driver + * + * @return N/A + */ +void woal_pcie_bus_unregister(void) +{ + ENTER(); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 70) + release_bus_freq(BUS_FREQ_HIGH); +#endif + /* PCIE Driver Unregistration */ + pci_unregister_driver(&wlan_pcie); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 6, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35) + pm_qos_remove_request(&woal_pcie_pm_qos_req); +#endif +#endif + + LEAVE(); +} + +#if defined(PCIE9098) || defined(PCIE9097) +#define PCIE9098_DUMP_CTRL_REG 0x1C94 +#define PCIE9098_DUMP_START_REG 0x1C98 +#define PCIE9098_DUMP_END_REG 0x1C9F +#endif +#if defined(PCIE8897) || defined(PCIE8997) +#define DEBUG_DUMP_CTRL_REG 0xCF4 +#define DEBUG_DUMP_START_REG 0xCF8 +#define DEBUG_DUMP_END_REG 0xCFF +#endif + +#if defined(PCIE9098) || defined(PCIE9097) +#define PCIE9098_SCRATCH_12_REG 0x1C90 +#define PCIE9098_SCRATCH_14_REG 0x1C98 +#define PCIE9098_SCRATCH_15_REG 0x1C9C +#define PCIE9098_DUMP_REG_START 0x1C20 +#define PCIE9098_DUMP_REG_END 0x1C9C +#endif + +#if defined(PCIE8997) || defined(PCIE8897) +#define PCIE_SCRATCH_12_REG 0x0CF0; +#define PCIE_SCRATCH_14_REG 0x0CF8; +#define PCIE_SCRATCH_15_REG 0x0CFC; +#define PCIE_DUMP_START_REG 0xC00 +#define PCIE_DUMP_END_REG 0xCFC +#endif +/** + * @brief This function save the log of pcie register value + * + * @param phandle A pointer to moal_handle + * @param buffer A pointer to buffer saving log + * + * @return The length of this log + */ +int woal_pcie_dump_reg_info(moal_handle *phandle, t_u8 *buffer) +{ + char *drv_ptr = (char *)buffer; + t_u32 reg = 0, value = 0; + t_u8 i; + char buf[256], *ptr; + pcie_service_card *card = (pcie_service_card *)phandle->card; + int config_reg_table[] = {0x00, 0x04, 0x10, 0x18, 0x2c, + 0x3c, 0x44, 0x80, 0x98, 0x170}; + t_u32 dump_start_reg = 0; + t_u32 dump_end_reg = 0; + t_u32 scratch_14_reg = 0; + t_u32 scratch_15_reg = 0; +#if defined(PCIE9098) || defined(PCIE9097) + /* Tx/Rx/Event AMDA start address */ + t_u32 adma_reg_table[] = {0x10000, 0x10800, 0x10880, 0x11000, 0x11080}; + t_u8 j; +#endif + ENTER(); + mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); + drv_ptr += sprintf(drv_ptr, + "------------PCIe Registers dump-------------\n"); + drv_ptr += sprintf(drv_ptr, "Config Space Registers:\n"); + for (i = 0; i < ARRAY_SIZE(config_reg_table); i++) { + pci_read_config_dword(card->dev, config_reg_table[i], &value); + drv_ptr += sprintf(drv_ptr, "reg:0x%02x value=0x%08x\n", + config_reg_table[i], value); + } + drv_ptr += sprintf(drv_ptr, "FW Scrach Registers:\n"); + +#if defined(PCIE8897) || defined(PCIE8997) + if (IS_PCIE8897(phandle->card_type) || + IS_PCIE8997(phandle->card_type)) { + reg = PCIE_SCRATCH_12_REG; + dump_start_reg = PCIE_DUMP_START_REG; + dump_end_reg = PCIE_DUMP_END_REG; + scratch_14_reg = PCIE_SCRATCH_14_REG; + scratch_15_reg = PCIE_SCRATCH_15_REG; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (IS_PCIE9098(phandle->card_type) || + IS_PCIE9097(phandle->card_type)) { + reg = PCIE9098_SCRATCH_12_REG; + dump_start_reg = PCIE9098_DUMP_REG_START; + dump_end_reg = PCIE9098_DUMP_REG_END; + scratch_14_reg = PCIE9098_SCRATCH_14_REG; + scratch_15_reg = PCIE9098_SCRATCH_15_REG; + } +#endif + + woal_pcie_read_reg(phandle, reg, &value); + drv_ptr += sprintf(drv_ptr, "reg:0x%x value=0x%x\n", reg, value); + for (i = 0; i < 2; i++) { + reg = scratch_14_reg; + woal_pcie_read_reg(phandle, reg, &value); + drv_ptr += + sprintf(drv_ptr, "reg:0x%x value=0x%x\n", reg, value); + + reg = scratch_15_reg; + woal_pcie_read_reg(phandle, reg, &value); + drv_ptr += + sprintf(drv_ptr, "reg:0x%x value=0x%x\n", reg, value); + + mdelay(100); + } + drv_ptr += + sprintf(drv_ptr, + "Interface registers dump from offset 0x%x to 0x%x\n", + dump_start_reg, dump_end_reg); + memset(buf, 0, sizeof(buf)); + ptr = buf; + i = 1; + for (reg = dump_start_reg; reg <= dump_end_reg; reg += 4) { + woal_pcie_read_reg(phandle, reg, &value); + ptr += sprintf(ptr, "%08x ", value); + if (!(i % 8)) { + drv_ptr += sprintf(drv_ptr, "%s\n", buf); + memset(buf, 0, sizeof(buf)); + ptr = buf; + } + i++; + } +#if defined(PCIE9098) || defined(PCIE9097) + if (IS_PCIE9098(phandle->card_type) || + IS_PCIE9097(phandle->card_type)) { + drv_ptr += sprintf( + drv_ptr, + "PCIE registers from offset 0x1c20 to 0x1c9c:\n"); + memset(buf, 0, sizeof(buf)); + ptr = buf; + i = 1; + for (reg = 0x1c20; reg <= 0x1c9c; reg += 4) { + woal_pcie_read_reg(phandle, reg, &value); + ptr += sprintf(ptr, "%08x ", value); + if (!(i % 8)) { + drv_ptr += sprintf(drv_ptr, "%s\n", buf); + memset(buf, 0, sizeof(buf)); + ptr = buf; + } + i++; + } + drv_ptr += sprintf(drv_ptr, "%s\n", buf); + } + if (IS_PCIE9098(phandle->card_type) || + IS_PCIE9097(phandle->card_type)) { + drv_ptr += sprintf(drv_ptr, + "ADMA Tx/Rx/Event/Cmd/CmdResp registers:\n"); + for (j = 0; j < ARRAY_SIZE(adma_reg_table); j++) { + drv_ptr += sprintf( + drv_ptr, + "ADMA registers dump from offset 0x%x to 0x%x\n", + adma_reg_table[j], adma_reg_table[j] + 0x68); + memset(buf, 0, sizeof(buf)); + ptr = buf; + i = 1; + for (reg = adma_reg_table[j]; + reg <= (adma_reg_table[j] + 0x68); reg += 4) { + woal_pcie_read_reg(phandle, reg, &value); + ptr += sprintf(ptr, "%08x ", value); + if (!(i % 8)) { + drv_ptr += + sprintf(drv_ptr, "%s\n", buf); + memset(buf, 0, sizeof(buf)); + ptr = buf; + } + i++; + } + drv_ptr += sprintf(drv_ptr, "%s\n", buf); + } + } +#endif + drv_ptr += sprintf(drv_ptr, + "-----------PCIe Registers dump End-----------\n"); + mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); + LEAVE(); + return drv_ptr - (char *)buffer; +} + +/** + * @brief This function reads and displays PCIE scratch registers for debugging + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +static void woal_pcie_reg_dbg(moal_handle *phandle) +{ + t_u32 reg = 0, value = 0; + t_u8 i; + char buf[256], *ptr; + pcie_service_card *card = (pcie_service_card *)phandle->card; + int config_reg_table[] = {0x00, 0x04, 0x10, 0x18, 0x2c, + 0x3c, 0x44, 0x80, 0x98, 0x170}; + t_u32 dump_start_reg = 0; + t_u32 dump_end_reg = 0; + t_u32 scratch_14_reg = 0; + t_u32 scratch_15_reg = 0; +#if defined(PCIE9098) || defined(PCIE9097) + /* Tx/Rx/Event AMDA start address */ + t_u32 adma_reg_table[] = {0x10000, 0x10800, 0x10880, 0x11000, 0x11080}; + t_u8 j; +#endif + mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); + PRINTM(MMSG, "Config Space Registers:\n"); + for (i = 0; i < ARRAY_SIZE(config_reg_table); i++) { + pci_read_config_dword(card->dev, config_reg_table[i], &value); + PRINTM(MERROR, "reg:0x%02x value=0x%08x\n", config_reg_table[i], + value); + } + PRINTM(MMSG, "FW Scrach Registers:\n"); +#if defined(PCIE8897) || defined(PCIE8997) + if (IS_PCIE8897(phandle->card_type) || + IS_PCIE8997(phandle->card_type)) { + reg = PCIE_SCRATCH_12_REG; + dump_start_reg = PCIE_DUMP_START_REG; + dump_end_reg = PCIE_DUMP_END_REG; + scratch_14_reg = PCIE_SCRATCH_14_REG; + scratch_15_reg = PCIE_SCRATCH_15_REG; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (IS_PCIE9098(phandle->card_type) || + IS_PCIE9097(phandle->card_type)) { + reg = PCIE9098_SCRATCH_12_REG; + dump_start_reg = PCIE9098_DUMP_START_REG; + dump_end_reg = PCIE9098_DUMP_END_REG; + scratch_14_reg = PCIE9098_SCRATCH_14_REG; + scratch_15_reg = PCIE9098_SCRATCH_15_REG; + } +#endif + woal_pcie_read_reg(phandle, reg, &value); + PRINTM(MERROR, "reg:0x%x value=0x%x\n", reg, value); + for (i = 0; i < 2; i++) { + reg = scratch_14_reg; + woal_pcie_read_reg(phandle, reg, &value); + PRINTM(MERROR, "reg:0x%x value=0x%x\n", reg, value); + + reg = scratch_15_reg; + woal_pcie_read_reg(phandle, reg, &value); + PRINTM(MERROR, "reg:0x%x value=0x%x\n", reg, value); + + mdelay(100); + } + PRINTM(MMSG, "Interface registers dump from offset 0x%x to 0x%x\n", + dump_start_reg, dump_end_reg); + memset(buf, 0, sizeof(buf)); + ptr = buf; + i = 1; + for (reg = dump_start_reg; reg <= dump_end_reg; reg += 4) { + woal_pcie_read_reg(phandle, reg, &value); + ptr += sprintf(ptr, "%08x ", value); + if (!(i % 8)) { + PRINTM(MMSG, "%s\n", buf); + memset(buf, 0, sizeof(buf)); + ptr = buf; + } + i++; + } +#if defined(PCIE9098) || defined(PCIE9097) + if (IS_PCIE9098(phandle->card_type) || + IS_PCIE9097(phandle->card_type)) { + PRINTM(MMSG, "PCIE registers from offset 0x1c20 to 0x1c9c:\n"); + memset(buf, 0, sizeof(buf)); + ptr = buf; + i = 1; + for (reg = 0x1c20; reg <= 0x1c9c; reg += 4) { + woal_pcie_read_reg(phandle, reg, &value); + ptr += sprintf(ptr, "%08x ", value); + if (!(i % 8)) { + PRINTM(MMSG, "%s\n", buf); + memset(buf, 0, sizeof(buf)); + ptr = buf; + } + i++; + } + PRINTM(MMSG, "%s\n", buf); + } + if (IS_PCIE9098(phandle->card_type) || + IS_PCIE9097(phandle->card_type)) { + PRINTM(MMSG, "ADMA Tx/Rx/Event/Cmd/CmdResp registers:\n"); + for (j = 0; j < ARRAY_SIZE(adma_reg_table); j++) { + PRINTM(MMSG, + "ADMA registers dump from offset 0x%x to 0x%x\n", + adma_reg_table[j], adma_reg_table[j] + 0x68); + memset(buf, 0, sizeof(buf)); + ptr = buf; + i = 1; + for (reg = adma_reg_table[j]; + reg <= (adma_reg_table[j] + 0x68); reg += 4) { + woal_pcie_read_reg(phandle, reg, &value); + ptr += sprintf(ptr, "%08x ", value); + if (!(i % 8)) { + PRINTM(MMSG, "%s\n", buf); + memset(buf, 0, sizeof(buf)); + ptr = buf; + } + i++; + } + PRINTM(MMSG, "%s\n", buf); + } + } +#endif + mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); +} + +#define DEBUG_FW_DONE 0xFF +#define MAX_POLL_TRIES 100 + +typedef enum { + DUMP_TYPE_ITCM = 0, + DUMP_TYPE_DTCM = 1, + DUMP_TYPE_SQRAM = 2, + DUMP_TYPE_IRAM = 3, + DUMP_TYPE_APU = 4, + DUMP_TYPE_CIU = 5, + DUMP_TYPE_ICU = 6, + DUMP_TYPE_MAC = 7, +} dumped_mem_type; + +#define MAX_NAME_LEN 8 +#define MAX_FULL_NAME_LEN 32 +t_u8 *name_prefix = "/data/file_"; + +typedef struct { + t_u8 mem_name[MAX_NAME_LEN]; + t_u8 *mem_Ptr; + struct file *pfile_mem; + t_u8 done_flag; + t_u8 type; +} memory_type_mapping; + +typedef enum { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +} rdwr_status; + +#ifdef PCIE8897 +#define DEBUG_HOST_READY_8897 0xEE +#define DEBUG_MEMDUMP_FINISH_8897 0xFE +memory_type_mapping mem_type_mapping_tbl_8897[] = { + {"ITCM", NULL, NULL, 0xF0, FW_DUMP_TYPE_MEM_ITCM}, + {"DTCM", NULL, NULL, 0xF1, FW_DUMP_TYPE_MEM_DTCM}, + {"SQRAM", NULL, NULL, 0xF2, FW_DUMP_TYPE_MEM_SQRAM}, + {"IRAM", NULL, NULL, 0xF3, FW_DUMP_TYPE_MEM_IRAM}, + {"APU", NULL, NULL, 0xF4, FW_DUMP_TYPE_REG_APU}, + {"CIU", NULL, NULL, 0xF5, FW_DUMP_TYPE_REG_CIU}, + {"ICU", NULL, NULL, 0xF6, FW_DUMP_TYPE_REG_ICU}, + {"MAC", NULL, NULL, 0xF7, FW_DUMP_TYPE_REG_MAC}, +}; +#endif + +#if defined(PCIE8997) || defined(PCIE9098) || defined(PCIE9097) +#define DEBUG_HOST_READY_8997 0xCC +#define DEBUG_HOST_EVENT_READY 0xAA +#define DEBUG_MEMDUMP_FINISH_8997 0xDD +memory_type_mapping mem_type_mapping_tbl_8997 = {"DUMP", NULL, NULL, 0xDD, + 0x00}; + +#endif + +#if defined(PCIE8897) || defined(PCIE8997) || defined(PCIE9098) || \ + defined(PCIE9097) +/** + * @brief This function reads data by 8 bit 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_eight_bit(moal_handle *handle, t_u32 reg, t_u8 *data) +{ + pcie_service_card *card = (pcie_service_card *)handle->card; + *data = ioread8(card->pci_mmap1 + reg); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function read/write firmware + * + * @param phandle A pointer to moal_handle + * @param doneflag done flag + * + * @return MLAN_STATUS_SUCCESS + */ +rdwr_status woal_pcie_rdwr_firmware(moal_handle *phandle, t_u8 doneflag) +{ + int ret = 0; + int tries = 0; + t_u8 ctrl_data = 0; + t_u32 reg_data = 0; + t_u32 debug_host_ready = 0; + t_u32 dump_ctrl_reg = 0; + +#ifdef PCIE8897 + if (IS_PCIE8897(phandle->card_type)) { + debug_host_ready = DEBUG_HOST_READY_8897; + dump_ctrl_reg = DEBUG_DUMP_CTRL_REG; + } +#endif +#if defined(PCIE8997) + if (IS_PCIE8997(phandle->card_type)) { + debug_host_ready = DEBUG_HOST_READY_8997; + dump_ctrl_reg = DEBUG_DUMP_CTRL_REG; + } +#endif + +#if defined(PCIE9098) || defined(PCIE9097) + if (IS_PCIE9098(phandle->card_type) || + IS_PCIE9097(phandle->card_type)) { + if (phandle->event_fw_dump) + debug_host_ready = DEBUG_HOST_EVENT_READY; + else + debug_host_ready = DEBUG_HOST_READY_8997; + dump_ctrl_reg = PCIE9098_DUMP_CTRL_REG; + } +#endif + + ret = woal_pcie_write_reg(phandle, dump_ctrl_reg, debug_host_ready); + if (ret) { + PRINTM(MERROR, "PCIE Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + ret = woal_pcie_read_reg(phandle, dump_ctrl_reg, ®_data); + if (ret) { + PRINTM(MERROR, "PCIE Read DEBUG_DUMP_CTRL_REG fail\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = woal_read_reg_eight_bit(phandle, dump_ctrl_reg, + &ctrl_data); + if (ret) { + PRINTM(MERROR, "PCIE READ ERR\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == DEBUG_FW_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != debug_host_ready) { + PRINTM(MMSG, "The ctrl reg was changed, try again!\n"); + ret = woal_pcie_write_reg(phandle, dump_ctrl_reg, + debug_host_ready); + if (ret) { + PRINTM(MERROR, "PCIE Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + usleep_range(99, 100); +#else + udelay(100); +#endif + } + if (ctrl_data == debug_host_ready) { + PRINTM(MERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + return RDWR_STATUS_SUCCESS; +} +#endif + +#ifdef PCIE8897 +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void woal_pcie_dump_fw_info_v1(moal_handle *phandle) +{ + int ret = 0; + unsigned int reg, reg_start, reg_end; + t_u8 *dbg_ptr = NULL; + t_u32 sec, usec; + t_u8 dump_num = 0; + t_u8 idx = 0; + t_u8 doneflag = 0; + rdwr_status stat; + t_u8 i = 0; + t_u8 read_reg = 0; + t_u32 memory_size = 0, DEBUG_MEMDUMP_FINISH; + t_u8 path_name[64], file_name[32], firmware_dump_file[128]; + t_u8 *end_ptr = NULL; + memory_type_mapping *mem_type_mapping_tbl = mem_type_mapping_tbl_8897; + + if (!phandle) { + PRINTM(MERROR, "Could not dump firmwware info\n"); + return; + } + DEBUG_MEMDUMP_FINISH = DEBUG_MEMDUMP_FINISH_8897; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /** Create dump directory*/ + woal_create_dump_dir(phandle, path_name, sizeof(path_name)); +#else + memset(path_name, 0, sizeof(path_name)); + strcpy(path_name, "/data"); +#endif + PRINTM(MMSG, "Directory name is %s\n", path_name); + woal_dump_drv_info(phandle, path_name); + /* start dump fw memory */ + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "====PCIE DEBUG MODE OUTPUT START: %u.%06u ====\n", sec, + usec); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == woal_pcie_rdwr_firmware(phandle, doneflag)) + goto done; + reg = DEBUG_DUMP_START_REG; + ret = woal_read_reg_eight_bit(phandle, reg, &dump_num); + if (ret) { + PRINTM(MMSG, "PCIE READ MEM NUM ERR\n"); + goto done; + } + + /* read the length of every memory which will dump */ + for (idx = 0; + idx < dump_num && idx < ARRAY_SIZE(mem_type_mapping_tbl_8897); + idx++) { + if (RDWR_STATUS_FAILURE == + woal_pcie_rdwr_firmware(phandle, doneflag)) + goto done; + memory_size = 0; + reg = DEBUG_DUMP_START_REG; + for (i = 0; i < 4; i++) { + ret = woal_read_reg_eight_bit(phandle, reg, &read_reg); + if (ret) { + PRINTM(MMSG, "PCIE READ ERR\n"); + goto done; + } + memory_size |= (read_reg << i * 8); + reg++; + } + if (memory_size == 0) { + PRINTM(MMSG, "Firmware Dump Finished!\n"); + ret = woal_pcie_write_reg(phandle, DEBUG_DUMP_CTRL_REG, + DEBUG_MEMDUMP_FINISH); + if (ret) { + PRINTM(MERROR, + "PCIE Write MEMDUMP_FINISH ERR\n"); + goto done; + } + break; + } else { + PRINTM(MMSG, "%s_SIZE=0x%x\n", + mem_type_mapping_tbl[idx].mem_name, memory_size); + ret = moal_vmalloc( + phandle, memory_size + 1, + (t_u8 **)&mem_type_mapping_tbl[idx].mem_Ptr); + if ((ret != MLAN_STATUS_SUCCESS) || + !mem_type_mapping_tbl[idx].mem_Ptr) { + PRINTM(MERROR, + "Error: vmalloc %s buffer failed!!!\n", + mem_type_mapping_tbl[idx].mem_name); + goto done; + } + dbg_ptr = mem_type_mapping_tbl[idx].mem_Ptr; + end_ptr = dbg_ptr + memory_size; + } + doneflag = mem_type_mapping_tbl[idx].done_flag; + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "Start %s output %u.%06u, please wait...\n", + mem_type_mapping_tbl[idx].mem_name, sec, usec); + do { + stat = woal_pcie_rdwr_firmware(phandle, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + + reg_start = DEBUG_DUMP_START_REG; + reg_end = DEBUG_DUMP_END_REG; + for (reg = reg_start; reg <= reg_end; reg++) { + ret = woal_read_reg_eight_bit(phandle, reg, + dbg_ptr); + if (ret) { + PRINTM(MMSG, "PCIE READ ERR\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + PRINTM(MINFO, + "pre-allocced buf is not enough\n"); + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MMSG, "%s done: size=0x%x\n", + mem_type_mapping_tbl[idx].mem_name, + (unsigned int)(dbg_ptr - + mem_type_mapping_tbl[idx] + .mem_Ptr)); + memset(file_name, 0, sizeof(file_name)); + sprintf(file_name, "%s%s", "file_pcie_", + mem_type_mapping_tbl[idx].mem_name); + if (MLAN_STATUS_SUCCESS != + woal_save_dump_info_to_file( + path_name, file_name, + mem_type_mapping_tbl[idx].mem_Ptr, + memory_size)) + PRINTM(MMSG, + "Can't save dump file %s in %s\n", + file_name, path_name); + moal_vfree(phandle, + mem_type_mapping_tbl[idx].mem_Ptr); + mem_type_mapping_tbl[idx].mem_Ptr = NULL; + break; + } + } while (1); + } + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "====PCIE DEBUG MODE OUTPUT END: %u.%06u ====\n", sec, + usec); + /* end dump fw memory */ + memset(firmware_dump_file, 0, sizeof(firmware_dump_file)); + sprintf(firmware_dump_file, "%s/%s", path_name, file_name); + moal_memcpy_ext(phandle, phandle->firmware_dump_file, + firmware_dump_file, sizeof(firmware_dump_file), + sizeof(phandle->firmware_dump_file)); +done: + for (idx = 0; + idx < dump_num && idx < ARRAY_SIZE(mem_type_mapping_tbl_8897); + idx++) { + if (mem_type_mapping_tbl[idx].mem_Ptr) { + moal_vfree(phandle, mem_type_mapping_tbl[idx].mem_Ptr); + mem_type_mapping_tbl[idx].mem_Ptr = NULL; + } + } + + return; +} +#endif + +#if defined(PCIE8997) || defined(PCIE9098) || defined(PCIE9097) +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void woal_pcie_dump_fw_info_v2(moal_handle *phandle) +{ + int ret = 0; + unsigned int reg, reg_start, reg_end; + t_u8 *dbg_ptr = NULL; + t_u8 *tmp_ptr = NULL; + t_u32 sec, usec; + t_u8 dump_num = 0; + t_u8 doneflag = 0; + rdwr_status stat; + t_u32 memory_size = 0; + t_u8 path_name[64], file_name[32], firmware_dump_file[128]; + moal_handle *ref_handle; + t_u8 *end_ptr = NULL; + memory_type_mapping *mem_type_mapping_tbl = &mem_type_mapping_tbl_8997; + t_u32 dump_start_reg = 0; + t_u32 dump_end_reg = 0; + + if (!phandle) { + PRINTM(MERROR, "Could not dump firmwware info\n"); + return; + } +#if defined(PCIE9098) || defined(PCIE9097) + if (IS_PCIE9098(phandle->card_type) || + IS_PCIE9097(phandle->card_type)) { + if (phandle->event_fw_dump) { + if (RDWR_STATUS_FAILURE != + woal_pcie_rdwr_firmware(phandle, doneflag)) + PRINTM(MMSG, + "====PCIE FW DUMP EVENT MODE START ====\n"); + phandle->event_fw_dump = MFALSE; + return; + } + } +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /** Create dump directory*/ + woal_create_dump_dir(phandle, path_name, sizeof(path_name)); +#else + memset(path_name, 0, sizeof(path_name)); + strcpy(path_name, "/data"); +#endif + PRINTM(MMSG, "Create DUMP directory success:dir_name=%s\n", path_name); + ref_handle = (moal_handle *)phandle->pref_mac; + if (ref_handle) + woal_dump_drv_info(ref_handle, path_name); + woal_dump_drv_info(phandle, path_name); + + /* start dump fw memory */ + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "====PCIE DEBUG MODE OUTPUT START: %u.%06u ====\n", sec, + usec); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == woal_pcie_rdwr_firmware(phandle, doneflag)) + goto done; +#if defined(PCIE9098) || defined(PCIE9097) + if (IS_PCIE9098(phandle->card_type) || + IS_PCIE9097(phandle->card_type)) { + dump_start_reg = PCIE9098_DUMP_START_REG; + dump_end_reg = PCIE9098_DUMP_END_REG; + } +#endif +#ifdef PCIE8997 + if (IS_PCIE8997(phandle->card_type)) { + dump_start_reg = DEBUG_DUMP_START_REG; + dump_end_reg = DEBUG_DUMP_END_REG; + } +#endif + reg = dump_start_reg; + ret = woal_read_reg_eight_bit(phandle, reg, &dump_num); + if (ret) { + PRINTM(MMSG, "PCIE READ MEM NUM ERR\n"); + goto done; + } + + memory_size = 0x80000; + ret = moal_vmalloc(phandle, memory_size + 1, + (t_u8 **)&mem_type_mapping_tbl->mem_Ptr); + if ((ret != MLAN_STATUS_SUCCESS) || !mem_type_mapping_tbl->mem_Ptr) { + PRINTM(MERROR, "Error: vmalloc %s buffer failed!!!\n", + mem_type_mapping_tbl->mem_name); + goto done; + } + dbg_ptr = mem_type_mapping_tbl->mem_Ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = mem_type_mapping_tbl->done_flag; + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "Start %s output %u.%06u, please wait...\n", + mem_type_mapping_tbl->mem_name, sec, usec); + do { + stat = woal_pcie_rdwr_firmware(phandle, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + + reg_start = dump_start_reg; + reg_end = dump_end_reg; + for (reg = reg_start; reg <= reg_end; reg++) { + ret = woal_read_reg_eight_bit(phandle, reg, dbg_ptr); + if (ret) { + PRINTM(MMSG, "PCIE READ ERR\n"); + goto done; + } + dbg_ptr++; + if (dbg_ptr >= end_ptr) { + PRINTM(MINFO, + "pre-allocced buf is not enough\n"); + ret = moal_vmalloc(phandle, + memory_size + 0x4000 + 1, + (t_u8 **)&tmp_ptr); + if ((ret != MLAN_STATUS_SUCCESS) || !tmp_ptr) { + PRINTM(MERROR, + "Error: vmalloc buffer failed!!!\n"); + goto done; + } + moal_memcpy_ext(phandle, tmp_ptr, + mem_type_mapping_tbl->mem_Ptr, + memory_size, + memory_size + 0x4000); + moal_vfree(phandle, + mem_type_mapping_tbl->mem_Ptr); + mem_type_mapping_tbl->mem_Ptr = tmp_ptr; + tmp_ptr = NULL; + dbg_ptr = mem_type_mapping_tbl->mem_Ptr + + memory_size; + memory_size += 0x4000; + end_ptr = mem_type_mapping_tbl->mem_Ptr + + memory_size; + } + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MMSG, + "%s done:" +#ifdef MLAN_64BIT + "size = 0x%lx\n", +#else + "size = 0x%x\n", +#endif + mem_type_mapping_tbl->mem_name, + dbg_ptr - mem_type_mapping_tbl->mem_Ptr); + memset(file_name, 0, sizeof(file_name)); + sprintf(file_name, "%s%s", "file_pcie_", + mem_type_mapping_tbl->mem_name); + if (MLAN_STATUS_SUCCESS != + woal_save_dump_info_to_file( + path_name, file_name, + mem_type_mapping_tbl->mem_Ptr, + dbg_ptr - mem_type_mapping_tbl->mem_Ptr)) + PRINTM(MMSG, "Can't save dump file %s in %s\n", + file_name, path_name); + moal_vfree(phandle, mem_type_mapping_tbl->mem_Ptr); + mem_type_mapping_tbl->mem_Ptr = NULL; + break; + } + } while (1); + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "====PCIE DEBUG MODE OUTPUT END: %u.%06u ====\n", sec, + usec); + /* end dump fw memory */ + memset(firmware_dump_file, 0, sizeof(firmware_dump_file)); + sprintf(firmware_dump_file, "%s/%s", path_name, file_name); + moal_memcpy_ext(phandle, phandle->firmware_dump_file, + firmware_dump_file, sizeof(firmware_dump_file), + sizeof(phandle->firmware_dump_file)); +done: + if (mem_type_mapping_tbl->mem_Ptr) { + moal_vfree(phandle, mem_type_mapping_tbl->mem_Ptr); + mem_type_mapping_tbl->mem_Ptr = NULL; + } + + return; +} +#endif + +/** + * @brief This function check if this is second mac + * + * @param handle A pointer to moal_handle structure + * @return MTRUE/MFALSE + * + */ +static t_u8 woal_pcie_is_second_mac(moal_handle *handle) +{ +#ifdef PCIE9098 + pcie_service_card *card = (pcie_service_card *)handle->card; + if (card->dev->device == PCIE_DEVICE_ID_NXP_88W9098P_FN1) + return MTRUE; +#endif + return MFALSE; +} + +void woal_pcie_dump_fw_info(moal_handle *phandle) +{ + mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); + phandle->fw_dump = MTRUE; +#ifdef PCIE8897 + if (IS_PCIE8897(phandle->card_type)) + woal_pcie_dump_fw_info_v1(phandle); +#endif +#if defined(PCIE8997) || defined(PCIE9098) || defined(PCIE9097) + if (IS_PCIE8997(phandle->card_type) || + IS_PCIE9098(phandle->card_type) || IS_PCIE9097(phandle->card_type)) + woal_pcie_dump_fw_info_v2(phandle); +#endif + phandle->fw_dump = MFALSE; + mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); + queue_work(phandle->workqueue, &phandle->main_work); +} + +static mlan_status woal_pcie_get_fw_name(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef PCIE9098 + pcie_service_card *card = (pcie_service_card *)handle->card; + moal_handle *ref_handle = NULL; +#endif + +#if defined(PCIE8997) || defined(PCIE9098) || defined(PCIE9097) + t_u32 rev_id_reg = handle->card_info->rev_id_reg; + t_u32 revision_id = 0; +#endif + +#if defined(PCIE8997) || defined(PCIE9098) || defined(PCIE9097) + t_u32 host_strap_reg = handle->card_info->host_strap_reg; + t_u32 magic_reg = handle->card_info->magic_reg; + t_u32 strap = 0; + t_u32 magic = 0; +#endif + + ENTER(); + + if (handle->params.fw_name) { +#ifdef PCIE9097 + if (IS_PCIE9097(handle->card_type)) { + woal_pcie_read_reg(handle, rev_id_reg, &revision_id); + revision_id &= 0xff; + PRINTM(MCMND, "revision_id=0x%x\n", revision_id); + switch (revision_id) { + case PCIE9097_A0: + break; + case PCIE9097_B0: + case PCIE9097_B1: + handle->card_rev = CHIP_9097_REV_B0; + break; + default: + break; + } + } +#endif + goto done; + } + +#ifdef PCIE8997 + if (IS_PCIE8997(handle->card_type)) { + woal_pcie_read_reg(handle, rev_id_reg, &revision_id); + woal_pcie_read_reg(handle, host_strap_reg, &strap); + woal_pcie_read_reg(handle, magic_reg, &magic); + revision_id &= 0xff; + strap &= 0x7; + magic &= 0xff; + PRINTM(MCMND, "magic=0x%x, strap=0x%x, revision_id=0x%x\n", + magic, strap, revision_id); + if ((revision_id == PCIE8997_A1) && + (magic == CHIP_MAGIC_VALUE)) { + if (strap == CARD_TYPE_PCIE_UART) + strcpy(handle->card_info->fw_name, + PCIEUART8997_DEFAULT_COMBO_FW_NAME); + else + strcpy(handle->card_info->fw_name, + PCIEUSB8997_DEFAULT_COMBO_FW_NAME); + } + } +#endif +#ifdef PCIE9098 + if (IS_PCIE9098(handle->card_type)) { + if (card->dev->device == PCIE_DEVICE_ID_NXP_88W9098P_FN0) { + woal_pcie_read_reg(handle, rev_id_reg, &revision_id); + woal_pcie_read_reg(handle, host_strap_reg, &strap); + woal_pcie_read_reg(handle, magic_reg, &magic); + revision_id &= 0xff; + strap &= 0x7; + magic &= 0xff; + PRINTM(MCMND, + "magic=0x%x, strap=0x%x, revision_id=0x%x\n", + magic, strap, revision_id); + switch (revision_id) { + case PCIE9098_Z1Z2: + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_PCIE_UART) + strcpy(handle->card_info + ->fw_name, + PCIEUART9098_DEFAULT_COMBO_FW_NAME); + else if (strap == CARD_TYPE_PCIE_PCIE) + strcpy(handle->card_info + ->fw_name, + PCIEPCIE9098_DEFAULT_COMBO_FW_NAME); + else + strcpy(handle->card_info + ->fw_name, + PCIEUSB9098_DEFAULT_COMBO_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + PCIE9098_DEFAULT_WLAN_FW_NAME); + break; + case PCIE9098_A0: + case PCIE9098_A1: + case PCIE9098_A2: + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_PCIE_UART) + strcpy(handle->card_info + ->fw_name, + PCIEUART9098_COMBO_V1_FW_NAME); + else if (strap == CARD_TYPE_PCIE_PCIE) + strcpy(handle->card_info + ->fw_name, + PCIEPCIE9098_COMBO_V1_FW_NAME); + else + strcpy(handle->card_info + ->fw_name, + PCIEUSB9098_COMBO_V1_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + PCIE9098_WLAN_V1_FW_NAME); + break; + default: + break; + } + } else { + ref_handle = (moal_handle *)handle->pref_mac; + if (ref_handle) { + strcpy(handle->card_info->fw_name, + ref_handle->card_info->fw_name); + strcpy(handle->card_info->fw_name_wlan, + ref_handle->card_info->fw_name_wlan); + } + } + } +#endif +#ifdef PCIE9097 + if (IS_PCIE9097(handle->card_type)) { + woal_pcie_read_reg(handle, rev_id_reg, &revision_id); + woal_pcie_read_reg(handle, host_strap_reg, &strap); + woal_pcie_read_reg(handle, magic_reg, &magic); + revision_id &= 0xff; + strap &= 0x7; + magic &= 0xff; + PRINTM(MCMND, "magic=0x%x, strap=0x%x, revision_id=0x%x\n", + magic, strap, revision_id); + switch (revision_id) { + case PCIE9097_A0: + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_PCIE_UART) + strcpy(handle->card_info->fw_name, + PCIEUART9097_DEFAULT_COMBO_FW_NAME); + else + strcpy(handle->card_info->fw_name, + PCIEUSB9097_DEFAULT_COMBO_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + PCIE9097_DEFAULT_WLAN_FW_NAME); + break; + case PCIE9097_B0: + case PCIE9097_B1: + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_PCIE_UART) + strcpy(handle->card_info->fw_name, + PCIEUART9097_COMBO_V1_FW_NAME); + else + strcpy(handle->card_info->fw_name, + PCIEUSB9097_COMBO_V1_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + PCIE9097_WLAN_V1_FW_NAME); + handle->card_rev = CHIP_9097_REV_B0; + break; + default: + break; + } + } +#endif +done: + PRINTM(MCMND, "combo fw:%s wlan fw:%s \n", handle->card_info->fw_name, + handle->card_info->fw_name_wlan); + LEAVE(); + return ret; +} + +static moal_if_ops pcie_ops = { + .register_dev = woal_pcie_register_dev, + .unregister_dev = woal_pcie_unregister_dev, + .read_reg = woal_pcie_read_reg, + .write_reg = woal_pcie_write_reg, + .read_data_sync = woal_pcie_read_data_sync, + .write_data_sync = woal_pcie_write_data_sync, + .get_fw_name = woal_pcie_get_fw_name, + .dump_fw_info = woal_pcie_dump_fw_info, + .reg_dbg = woal_pcie_reg_dbg, + .dump_reg_info = woal_pcie_dump_reg_info, + .is_second_mac = woal_pcie_is_second_mac, +}; diff --git a/mxm_wifiex/wlan_src/mlinux/moal_pcie.h b/mxm_wifiex/wlan_src/mlinux/moal_pcie.h new file mode 100644 index 0000000..ae22f4d --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_pcie.h @@ -0,0 +1,142 @@ +/** @file moal_pcie.h + * + * @brief This file contains definitions for PCIE interface. + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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/01/2012: initial version +********************************************************/ + +#ifndef _MOAL_PCIE_H_ +#define _MOAL_PCIE_H_ + +#define PCIE_VENDOR_ID_NXP (0x11ab) +#define PCIE_VENDOR_ID_V2_NXP (0x1b4b) +#ifdef PCIE8997 +/** PCIE device ID for 8997 card */ +#define PCIE_DEVICE_ID_NXP_88W8997P (0x2b42) +#endif +#ifdef PCIE8897 +/** PCIE device ID for 8897 card */ +#define PCIE_DEVICE_ID_NXP_88W8897P (0x2b38) +#endif + +#ifdef PCIE9097 +/** PCIE device ID for 9097 card */ +#define PCIE_DEVICE_ID_NXP_88W9097 (0x2b56) +#endif + +#ifdef PCIE9098 +/** PCIE device ID for 9098 card FN0 */ +#define PCIE_DEVICE_ID_NXP_88W9098P_FN0 (0x2b43) +/** PCIE device ID for 9098 card FN1 */ +#define PCIE_DEVICE_ID_NXP_88W9098P_FN1 (0x2b44) +#endif + +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) +#include +#endif +#include + +#include "moal_main.h" + +/** Default firmware name */ +#ifdef PCIE8997 +#define PCIE8997_DEFAULT_COMBO_FW_NAME "nxp/pcieusb8997_combo_v4.bin" +#define PCIEUART8997_DEFAULT_COMBO_FW_NAME "nxp/pcieuart8997_combo_v4.bin" +#define PCIEUSB8997_DEFAULT_COMBO_FW_NAME "nxp/pcieusb8997_combo_v4.bin" +#define PCIE8997_DEFAULT_WLAN_FW_NAME "nxp/pcie8997_wlan_v4.bin" +/** PCIE8997 chip revision ID */ +#define PCIE8997_A0 0x10 +#define PCIE8997_A1 0x11 +#endif /* PCIE8997 */ + +#ifdef PCIE8897 +#define PCIE8897_DEFAULT_COMBO_FW_NAME "nxp/pcie8897_uapsta.bin" +#define PCIE8897_DEFAULT_WLAN_FW_NAME "nxp/pcie8897_wlan.bin" +#endif /* PCIE8897*/ + +#ifdef PCIE9098 +#define PCIE9098_Z1Z2 0x00 +#define PCIE9098_A0 0x01 +#define PCIE9098_A1 0x02 +#define PCIE9098_A2 0x03 +#define PCIE9098_DEFAULT_COMBO_FW_NAME "nxp/pcieusb9098_combo.bin" +#define PCIEUART9098_DEFAULT_COMBO_FW_NAME "nxp/pcieuart9098_combo.bin" +#define PCIEUSB9098_DEFAULT_COMBO_FW_NAME "nxp/pcieusb9098_combo.bin" +#define PCIEPCIE9098_DEFAULT_COMBO_FW_NAME "nxp/pciepcie9098_combo.bin" +#define PCIEUART9098_COMBO_V1_FW_NAME "nxp/pcieuart9098_combo_v1.bin" +#define PCIEUSB9098_COMBO_V1_FW_NAME "nxp/pcieusb9098_combo_v1.bin" +#define PCIEPCIE9098_COMBO_V1_FW_NAME "nxp/pciepcie9098_combo_v1.bin" +#define PCIE9098_DEFAULT_WLAN_FW_NAME "nxp/pcie9098_wlan.bin" +#define PCIE9098_WLAN_V1_FW_NAME "nxp/pcie9098_wlan_v1.bin" +#endif /* PCIE9098 */ + +#ifdef PCIE9097 +#define PCIE9097_A0 0x00 +#define PCIE9097_B0 0x01 +#define PCIE9097_B1 0x02 +#define PCIE9097_DEFAULT_COMBO_FW_NAME "nxp/pcieusb9097_combo.bin" +#define PCIEUART9097_DEFAULT_COMBO_FW_NAME "nxp/pcieuart9097_combo.bin" +#define PCIEUSB9097_DEFAULT_COMBO_FW_NAME "nxp/pcieusb9097_combo.bin" +#define PCIEUART9097_COMBO_V1_FW_NAME "nxp/pcieuart9097_combo_v1.bin" +#define PCIEUSB9097_COMBO_V1_FW_NAME "nxp/pcieusb9097_combo_v1.bin" +#define PCIE9097_DEFAULT_WLAN_FW_NAME "nxp/pcie9097_wlan.bin" +#define PCIE9097_WLAN_V1_FW_NAME "nxp/pcie9097_wlan_v1.bin" +#endif /* PCIE9097 */ + +#if defined(PCIE9098) || defined(PCIE9097) +#define PCIE_NUM_MSIX_VECTORS 32 +#else +#define PCIE_NUM_MSIX_VECTORS 4 +#endif + +typedef struct _msix_context { + /** pci_dev structure pointer */ + struct pci_dev *dev; + /** message id related to msix vector */ + t_u16 msg_id; +} msix_context; + +/** Structure: PCIE service card */ +typedef struct _pcie_service_card { + /** pci_dev structure pointer */ + struct pci_dev *dev; + /** moal_handle structure pointer */ + moal_handle *handle; + /** I/O memory regions pointer to the bus */ + void __iomem *pci_mmap; + /** I/O memory regions pointer to the bus */ + void __iomem *pci_mmap1; +#if defined(PCIE) + struct msix_entry msix_entries[PCIE_NUM_MSIX_VECTORS]; + msix_context msix_contexts[PCIE_NUM_MSIX_VECTORS]; +#endif +} pcie_service_card, *ppcie_service_card; + +/** Register to bus driver function */ +mlan_status woal_pcie_bus_register(void); +/** Unregister from bus driver function */ +void woal_pcie_bus_unregister(void); +#endif /* _MOAL_PCIE_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_priv.c b/mxm_wifiex/wlan_src/mlinux/moal_priv.c new file mode 100644 index 0000000..8397ab6 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_priv.c @@ -0,0 +1,6863 @@ +/** @file moal_priv.c + * + * @brief This file contains standard ioctl functions + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" +#ifdef SDIO +#include "moal_sdio.h" +#endif /* SDIO */ + +#include "moal_eth_ioctl.h" +#ifdef USB +#include "moal_usb.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ +/** Bands supported in Infra mode */ +static t_u8 SupportedInfraBand[] = { + BAND_B, + BAND_B | BAND_G, + BAND_G, + BAND_GN, + BAND_B | BAND_G | BAND_GN, + BAND_G | BAND_GN, + BAND_A, + BAND_B | BAND_A, + BAND_B | BAND_G | BAND_A, + BAND_G | BAND_A, + BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN, + BAND_A | BAND_G | BAND_AN | BAND_GN, + BAND_A | BAND_AN, + BAND_GN | BAND_GAC, + BAND_B | BAND_G | BAND_GN | BAND_GAC, + BAND_G | BAND_GN | BAND_GAC, + BAND_A | BAND_B | BAND_G | BAND_AN | BAND_GN | BAND_AAC, + BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC, + BAND_A | BAND_AN | BAND_AAC, +}; + +/** Bands supported in Ad-Hoc mode */ +static t_u8 SupportedAdhocBand[] = { + BAND_B, + BAND_B | BAND_G, + BAND_G, + BAND_A, +}; + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Associated to a specific indexed entry in the ScanTable + * + * @param priv A pointer to moal_private structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_associate_ssid_bssid(moal_private *priv, struct iwreq *wrq) +{ + mlan_ssid_bssid ssid_bssid; +#ifdef REASSOCIATION + mlan_bss_info bss_info; +#endif + char buf[64]; + t_u8 buflen; + t_u8 mac_idx; + t_u8 i; + + ENTER(); + + memset(&ssid_bssid, 0, sizeof(ssid_bssid)); + mac_idx = 0; + buflen = MIN(wrq->u.data.length, (sizeof(buf) - 1)); + memset(buf, 0, sizeof(buf)); + + if (buflen < (3 * ETH_ALEN) + 2) { + PRINTM(MERROR, + "Associate: Insufficient length in IOCTL input\n"); + + /* buffer should be at least 3 characters per BSSID octet "00:" + ** plus a space separater and at least 1 char in the SSID + */ + LEAVE(); + return -EINVAL; + } + + if (copy_from_user(buf, wrq->u.data.pointer, buflen) != 0) { + /* copy_from_user failed */ + PRINTM(MERROR, "Associate: copy from user failed\n"); + LEAVE(); + return -EINVAL; + } + + for (i = 0; (i < buflen) && (buf[i] == ' '); i++) { + /* Skip white space */ + } + + /* Copy/Convert the BSSID */ + for (; (i < buflen) && (mac_idx < ETH_ALEN) && (buf[i] != ' '); i++) { + if (buf[i] == ':') { + mac_idx++; + } else { + if (mac_idx < ETH_ALEN) + ssid_bssid.bssid[mac_idx] = + (t_u8)woal_atox(buf + i); + + while ((i < buflen) && (isxdigit(buf[i + 1]))) { + /* Skip entire hex value */ + i++; + } + } + } + + /* Skip one space between the BSSID and start of the SSID */ + i++; + + /* Copy the SSID */ + ssid_bssid.ssid.ssid_len = buflen - i - 1; + moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid, buf + i, + sizeof(ssid_bssid.ssid.ssid), + sizeof(ssid_bssid.ssid.ssid)); + + PRINTM(MCMND, "iwpriv assoc: AP=[" MACSTR "], ssid(%d)=[%s]\n", + MAC2STR(ssid_bssid.bssid), (int)ssid_bssid.ssid.ssid_len, + ssid_bssid.ssid.ssid); + + if (MLAN_STATUS_SUCCESS != + woal_bss_start(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) { + LEAVE(); + return -EFAULT; + } + +#ifdef REASSOCIATION + memset(&bss_info, 0x00, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS == + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.ssid, + &bss_info.ssid, sizeof(mlan_802_11_ssid), + sizeof(mlan_802_11_ssid)); + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.bssid, + &bss_info.bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(mlan_802_11_mac_addr)); + } +#endif /* REASSOCIATION */ + + LEAVE(); + return 0; +} + +/** + * @brief Copy Rates + * + * @param dest A pointer to destination buffer + * @param pos The position for copy + * @param src A pointer to source buffer + * @param len Length of the source buffer + * + * @return Number of rates copied + */ +static inline int woal_copy_rates(t_u8 *dest, int pos, t_u8 *src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= MLAN_SUPPORTED_RATES) + break; + dest[pos] = src[i]; + } + return pos; +} + +/** + * @brief Performs warm reset + * + * @param priv A pointer to moal_private structure + * + * @return 0/MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int woal_warm_reset(moal_private *priv) +{ + int ret = 0; + moal_handle *handle = priv->phandle; + moal_handle *ref_handle; + moal_private *ref_priv; + ENTER(); + ret = woal_pre_warmreset(priv); + if (ret) + goto done; + ref_handle = (moal_handle *)handle->pref_mac; + if (ref_handle) { + ref_priv = woal_get_priv(ref_handle, MLAN_BSS_ROLE_ANY); + if (ref_priv) { + ret = woal_pre_warmreset(ref_priv); + if (ret) + goto done; + ret = woal_warmreset(ref_priv); + if (ret) + goto done; + } + } + ret = woal_warmreset(priv); +done: + LEAVE(); + return ret; +} + +/** + * @brief Get signal + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_get_signal(moal_private *priv, struct iwreq *wrq) +{ +/** Input data size */ +#define IN_DATA_SIZE 2 +/** Output data size */ +#define OUT_DATA_SIZE 12 + int ret = 0; + int in_data[IN_DATA_SIZE]; + int out_data[OUT_DATA_SIZE]; + mlan_ds_get_signal signal; + int data_length = 0; + int buflen = 0; + + ENTER(); + + memset(in_data, 0, sizeof(in_data)); + memset(out_data, 0, sizeof(out_data)); + buflen = MIN(wrq->u.data.length, IN_DATA_SIZE); + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can not get RSSI in disconnected state\n"); + ret = -ENOTSUPP; + goto done; + } + + if (wrq->u.data.length) { + if (sizeof(int) * wrq->u.data.length > sizeof(in_data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user(in_data, wrq->u.data.pointer, + sizeof(int) * buflen)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + + switch (wrq->u.data.length) { + case 0: /* No checking, get everything */ + break; + case 2: /* Check subtype range */ + if (in_data[1] < 1 || in_data[1] > 4) { + ret = -EINVAL; + goto done; + } + /* Fall through */ + case 1: /* Check type range */ + if (in_data[0] < 1 || in_data[0] > 3) { + ret = -EINVAL; + goto done; + } + break; + default: + ret = -EINVAL; + goto done; + } + + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + ret = -EFAULT; + goto done; + } + PRINTM(MINFO, "RSSI Beacon Last : %d\n", (int)signal.bcn_rssi_last); + PRINTM(MINFO, "RSSI Beacon Average: %d\n", (int)signal.bcn_rssi_avg); + PRINTM(MINFO, "RSSI Data Last : %d\n", (int)signal.data_rssi_last); + PRINTM(MINFO, "RSSI Data Average : %d\n", (int)signal.data_rssi_avg); + PRINTM(MINFO, "SNR Beacon Last : %d\n", (int)signal.bcn_snr_last); + PRINTM(MINFO, "SNR Beacon Average : %d\n", (int)signal.bcn_snr_avg); + PRINTM(MINFO, "SNR Data Last : %d\n", (int)signal.data_snr_last); + PRINTM(MINFO, "SNR Data Average : %d\n", (int)signal.data_snr_avg); + PRINTM(MINFO, "NF Beacon Last : %d\n", (int)signal.bcn_nf_last); + PRINTM(MINFO, "NF Beacon Average : %d\n", (int)signal.bcn_nf_avg); + PRINTM(MINFO, "NF Data Last : %d\n", (int)signal.data_nf_last); + PRINTM(MINFO, "NF Data Average : %d\n", (int)signal.data_nf_avg); + + /* Check type */ + switch (in_data[0]) { + case 0: /* Send everything */ + out_data[data_length++] = signal.bcn_rssi_last; + out_data[data_length++] = signal.bcn_rssi_avg; + out_data[data_length++] = signal.data_rssi_last; + out_data[data_length++] = signal.data_rssi_avg; + out_data[data_length++] = signal.bcn_snr_last; + out_data[data_length++] = signal.bcn_snr_avg; + out_data[data_length++] = signal.data_snr_last; + out_data[data_length++] = signal.data_snr_avg; + out_data[data_length++] = signal.bcn_nf_last; + out_data[data_length++] = signal.bcn_nf_avg; + out_data[data_length++] = signal.data_nf_last; + out_data[data_length++] = signal.data_nf_avg; + break; + case 1: /* RSSI */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_rssi_last; + out_data[data_length++] = signal.bcn_rssi_avg; + out_data[data_length++] = signal.data_rssi_last; + out_data[data_length++] = signal.data_rssi_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_rssi_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_rssi_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_rssi_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_rssi_avg; + break; + default: + break; + } + break; + case 2: /* SNR */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_snr_last; + out_data[data_length++] = signal.bcn_snr_avg; + out_data[data_length++] = signal.data_snr_last; + out_data[data_length++] = signal.data_snr_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_snr_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_snr_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_snr_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_snr_avg; + break; + default: + break; + } + break; + case 3: /* NF */ + /* Check subtype */ + switch (in_data[1]) { + case 0: /* Everything */ + out_data[data_length++] = signal.bcn_nf_last; + out_data[data_length++] = signal.bcn_nf_avg; + out_data[data_length++] = signal.data_nf_last; + out_data[data_length++] = signal.data_nf_avg; + break; + case 1: /* bcn last */ + out_data[data_length++] = signal.bcn_nf_last; + break; + case 2: /* bcn avg */ + out_data[data_length++] = signal.bcn_nf_avg; + break; + case 3: /* data last */ + out_data[data_length++] = signal.data_nf_last; + break; + case 4: /* data avg */ + out_data[data_length++] = signal.data_nf_avg; + break; + default: + break; + } + break; + default: + break; + } + + wrq->u.data.length = data_length; + if (copy_to_user(wrq->u.data.pointer, out_data, + wrq->u.data.length * sizeof(out_data[0]))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Get/Set DeepSleep mode + * + * @param priv Pointer to the moal_private driver data struct + * @param wreq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_deep_sleep_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int user_data_len; + t_u32 deep_sleep = DEEP_SLEEP_OFF; + t_u32 data[2] = {0}; + int copy_len; + t_u16 idletime = DEEP_SLEEP_IDLE_TIME; + + ENTER(); + + user_data_len = wrq->u.data.length; + copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); + if (user_data_len == 1 || user_data_len == 2) { + if (copy_from_user(&data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + deep_sleep = data[0]; + if (deep_sleep == DEEP_SLEEP_OFF) { + PRINTM(MINFO, "Exit Deep Sleep Mode\n"); + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MFALSE, + 0); + if (ret != MLAN_STATUS_SUCCESS) { + LEAVE(); + return -EINVAL; + } + } else if (deep_sleep == DEEP_SLEEP_ON) { + PRINTM(MINFO, "Enter Deep Sleep Mode\n"); + if (user_data_len == 2) + idletime = data[1]; + else + idletime = 0; + ret = woal_set_deep_sleep(priv, MOAL_IOCTL_WAIT, MTRUE, + idletime); + if (ret != MLAN_STATUS_SUCCESS) { + LEAVE(); + return -EINVAL; + } + } else { + PRINTM(MERROR, "Unknown option = %u\n", deep_sleep); + LEAVE(); + return -EINVAL; + } + } else if (user_data_len > 2) { + PRINTM(MERROR, "Invalid number of arguments %d\n", + user_data_len); + LEAVE(); + return -EINVAL; + } else { /* Display Deep Sleep settings */ + PRINTM(MINFO, "Get Deep Sleep Mode\n"); + if (MLAN_STATUS_SUCCESS != woal_get_deep_sleep(priv, data)) { + LEAVE(); + return -EFAULT; + } + if (data[0] == 0) + wrq->u.data.length = 1; + else + wrq->u.data.length = 2; + } + + /* Copy the Deep Sleep setting to user */ + if (copy_to_user(wrq->u.data.pointer, data, + wrq->u.data.length * sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + LEAVE(); + return -EINVAL; + } + + LEAVE(); + return 0; +} + +/** + * @brief Set/Get Usr 11n configuration request + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_11n_htcap_cfg(moal_private *priv, struct iwreq *wrq) +{ + int data[2], copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG; + req->req_id = MLAN_IOCTL_11N_CFG; + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + if (data_length == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BG; + } else { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + cfg_11n->param.htcap_cfg.htcap = data[0]; + PRINTM(MINFO, "SET: htcapinfo:0x%x\n", data[0]); + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_BOTH; + if (data_length == 2) { + if (data[1] != BAND_SELECT_BG && + data[1] != BAND_SELECT_A && + data[1] != BAND_SELECT_BOTH) { + PRINTM(MERROR, "Invalid band selection\n"); + ret = -EINVAL; + goto done; + } + cfg_11n->param.htcap_cfg.misc_cfg = data[1]; + PRINTM(MINFO, "SET: htcapinfo band:0x%x\n", data[1]); + } + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.htcap_cfg.htcap; + + if (req->action == MLAN_ACT_GET) { + data_length = 1; + cfg_11n->param.htcap_cfg.htcap = 0; + cfg_11n->param.htcap_cfg.misc_cfg = BAND_SELECT_A; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (cfg_11n->param.htcap_cfg.htcap != data[0]) { + data_length = 2; + data[1] = cfg_11n->param.htcap_cfg.htcap; + PRINTM(MINFO, "GET: htcapinfo for 2.4GHz:0x%x\n", + data[0]); + PRINTM(MINFO, "GET: htcapinfo for 5GHz:0x%x\n", + data[1]); + } else + PRINTM(MINFO, "GET: htcapinfo:0x%x\n", data[0]); + } + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + + wrq->u.data.length = data_length; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable amsdu_aggr_ctrl + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_11n_amsdu_aggr_ctrl(moal_private *priv, struct iwreq *wrq) +{ + int data[2], copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if ((data_length != 0) && (data_length != 1)) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AMSDU_AGGR_CTRL; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + } else if (data_length == 1) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + cfg_11n->param.amsdu_aggr_ctrl.enable = data[0]; + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.amsdu_aggr_ctrl.enable; + data[1] = cfg_11n->param.amsdu_aggr_ctrl.curr_buf_size; + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 2; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11n configuration request + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_11n_tx_cfg(moal_private *priv, struct iwreq *wrq) +{ + int data[2], copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 2) { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + /* Get 11n tx parameters from MLAN */ + req->action = MLAN_ACT_GET; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG; + } else { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + cfg_11n->param.tx_cfg.httxcap = data[0]; + PRINTM(MINFO, "SET: httxcap:0x%x\n", data[0]); + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BOTH; + if (data_length == 2) { + if (data[1] != BAND_SELECT_BG && + data[1] != BAND_SELECT_A && + data[1] != BAND_SELECT_BOTH) { + PRINTM(MERROR, "Invalid band selection\n"); + ret = -EINVAL; + goto done; + } + cfg_11n->param.tx_cfg.misc_cfg = data[1]; + PRINTM(MINFO, "SET: httxcap band:0x%x\n", data[1]); + } + /* Update 11n tx parameters in MLAN */ + req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + data[0] = cfg_11n->param.tx_cfg.httxcap; + + if (req->action == MLAN_ACT_GET) { + data_length = 1; + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (cfg_11n->param.tx_cfg.httxcap != data[0]) { + data_length = 2; + data[1] = cfg_11n->param.tx_cfg.httxcap; + PRINTM(MINFO, "GET: httxcap for 2.4GHz:0x%x\n", + data[0]); + PRINTM(MINFO, "GET: httxcap for 5GHz:0x%x\n", data[1]); + } else + PRINTM(MINFO, "GET: httxcap:0x%x\n", data[0]); + } + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = data_length; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable TX Aggregation + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_11n_prio_tbl(moal_private *priv, struct iwreq *wrq) +{ + int data[MAX_NUM_TID * 2], i, j, copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if ((wrq->u.data.pointer == NULL)) { + LEAVE(); + return -EINVAL; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + /* Get aggr priority table from MLAN */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + wrq->u.data.length = MAX_NUM_TID * 2; + for (i = 0, j = 0; i < (wrq->u.data.length); i = i + 2, ++j) { + data[i] = cfg_11n->param.aggr_prio_tbl.ampdu[j]; + data[i + 1] = cfg_11n->param.aggr_prio_tbl.amsdu[j]; + } + + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto error; + } + } else if (data_length == 16) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + for (i = 0, j = 0; i < (data_length); i = i + 2, ++j) { + if ((data[i] > 7 && data[i] != 0xff) || + (data[i + 1] > 7 && data[i + 1] != 0xff)) { + PRINTM(MERROR, + "Invalid priority, valid value 0-7 or 0xff.\n"); + ret = -EFAULT; + goto error; + } + cfg_11n->param.aggr_prio_tbl.ampdu[j] = data[i]; + cfg_11n->param.aggr_prio_tbl.amsdu[j] = data[i + 1]; + } + + /* Update aggr priority table in MLAN */ + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + } else { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get add BA Reject parameters + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_addba_reject(moal_private *priv, struct iwreq *wrq) +{ + int data[MAX_NUM_TID], ret = 0, i, copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + PRINTM(MERROR, "Addba reject moal\n"); + /* Get aggr priority table from MLAN */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + + wrq->u.data.length = MAX_NUM_TID; + for (i = 0; i < (wrq->u.data.length); ++i) + data[i] = cfg_11n->param.addba_reject[i]; + + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto error; + } + } else if (data_length == 8) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + for (i = 0; i < (data_length); ++i) { + if (data[i] != 0 && data[i] != 1) { + PRINTM(MERROR, + "addba reject only takes argument as 0 or 1\n"); + ret = -EFAULT; + goto error; + } + cfg_11n->param.addba_reject[i] = data[i]; + } + + /* Update aggr priority table in MLAN */ + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + } else { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get add BA parameters + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_addba_para_updt(moal_private *priv, struct iwreq *wrq) +{ + int data[5], ret = 0, copy_len; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (data_length == 0) { + /* Get Add BA parameters from MLAN */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + data[0] = cfg_11n->param.addba_param.timeout; + data[1] = cfg_11n->param.addba_param.txwinsize; + data[2] = cfg_11n->param.addba_param.rxwinsize; + data[3] = cfg_11n->param.addba_param.txamsdu; + data[4] = cfg_11n->param.addba_param.rxamsdu; + PRINTM(MINFO, + "GET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d, rxamsdu=%d\n", + data[0], data[1], data[2], data[3], data[4]); + wrq->u.data.length = 5; + if (copy_to_user(wrq->u.data.pointer, data, + wrq->u.data.length * sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto error; + } + } else if (data_length == 5) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + if (data[0] < 0 || data[0] > MLAN_DEFAULT_BLOCK_ACK_TIMEOUT) { + PRINTM(MERROR, "Incorrect addba timeout value.\n"); + ret = -EFAULT; + goto error; + } + if (data[1] <= 0 || data[1] > MLAN_AMPDU_MAX_TXWINSIZE || + data[2] <= 0 || data[2] > MLAN_AMPDU_MAX_RXWINSIZE) { + PRINTM(MERROR, "Incorrect Tx/Rx window size.\n"); + ret = -EFAULT; + goto error; + } + cfg_11n->param.addba_param.timeout = data[0]; + cfg_11n->param.addba_param.txwinsize = data[1]; + cfg_11n->param.addba_param.rxwinsize = data[2]; + if (data[3] < 0 || data[3] > 1 || data[4] < 0 || data[4] > 1) { + PRINTM(MERROR, "Incorrect Tx/Rx amsdu.\n"); + ret = -EFAULT; + goto error; + } + cfg_11n->param.addba_param.txamsdu = data[3]; + cfg_11n->param.addba_param.rxamsdu = data[4]; + PRINTM(MINFO, + "SET: timeout:%d txwinsize:%d rxwinsize:%d txamsdu=%d rxamsdu=%d\n", + data[0], data[1], data[2], data[3], data[4]); + /* Update Add BA parameters in MLAN */ + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + goto error; + } + } else { + PRINTM(MERROR, "Invalid number of arguments\n"); + ret = -EINVAL; + goto error; + } + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit buffer size + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_txbuf_cfg(moal_private *priv, struct iwreq *wrq) +{ + int buf_size; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_MAX_TX_BUF_SIZE; + req->req_id = MLAN_IOCTL_11N_CFG; + + if (wrq->u.data.length == 0) { + /* Get Tx buffer size from MLAN */ + req->action = MLAN_ACT_GET; + } else { + ret = -EINVAL; + PRINTM(MERROR, + "Don't support set Tx buffer size after driver loaded!\n"); + goto done; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + buf_size = cfg_11n->param.tx_buf_size; + if (copy_to_user(wrq->u.data.pointer, &buf_size, sizeof(buf_size))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Host Sleep configuration + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @param invoke_hostcmd MTRUE --invoke HostCmd, otherwise MFALSE + * + * @return 0 --success, otherwise fail + */ +static int woal_hs_cfg(moal_private *priv, struct iwreq *wrq, + BOOLEAN invoke_hostcmd) +{ + int data[3], copy_len; + int ret = 0; + mlan_ds_hs_cfg hscfg; + t_u16 action; + mlan_bss_info bss_info; + int data_length = wrq->u.data.length; + + ENTER(); + + memset(data, 0, sizeof(data)); + memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + if (data_length == 0) { + action = MLAN_ACT_GET; + } else { + action = MLAN_ACT_SET; + if (data_length >= 1 && data_length <= 3) { + if (copy_from_user(data, wrq->u.data.pointer, + copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + } + + /* HS config is blocked if HS is already activated */ + if (data_length && + (data[0] != HOST_SLEEP_CFG_CANCEL || invoke_hostcmd == MFALSE)) { + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (bss_info.is_hs_configured) { + PRINTM(MERROR, "HS already configured\n"); + ret = -EFAULT; + goto done; + } + } + + /* Do a GET first if some arguments are not provided */ + if (data_length >= 1 && data_length < 3) { + woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, + &hscfg); + } + + if (data_length) + hscfg.conditions = data[0]; + if (data_length >= 2) + hscfg.gpio = data[1]; + if (data_length == 3) + hscfg.gap = data[2]; + + if ((invoke_hostcmd == MTRUE) && (action == MLAN_ACT_SET)) { + /* Need to issue an extra IOCTL first to set up parameters */ + hscfg.is_invoke_hostcmd = MFALSE; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &hscfg)) { + ret = -EFAULT; + goto done; + } + } + hscfg.is_invoke_hostcmd = invoke_hostcmd; + if (MLAN_STATUS_SUCCESS != + woal_set_get_hs_params(priv, action, MOAL_IOCTL_WAIT, &hscfg)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_GET) { + data[0] = hscfg.conditions; + data[1] = hscfg.gpio; + data[2] = hscfg.gap; + wrq->u.data.length = 3; + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set Host Sleep parameters + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_hs_setpara(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int data_length = wrq->u.data.length; + + ENTER(); + + if (data_length >= 1 && data_length <= 3) { + ret = woal_hs_cfg(priv, wrq, MFALSE); + goto done; + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Get/Set inactivity timeout extend + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_inactivity_timeout_ext(moal_private *priv, struct iwreq *wrq) +{ + int data[4], copy_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_pm_cfg *pmcfg = NULL; + pmlan_ds_inactivity_to inac_to = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + memset(data, 0, sizeof(data)); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pmcfg = (mlan_ds_pm_cfg *)req->pbuf; + inac_to = &pmcfg->param.inactivity_to; + pmcfg->sub_command = MLAN_OID_PM_CFG_INACTIVITY_TO; + req->req_id = MLAN_IOCTL_PM_CFG; + + if ((data_length != 0 && data_length != 3 && data_length != 4) || + sizeof(int) * data_length > sizeof(data)) { + PRINTM(MERROR, "Invalid number of parameters\n"); + ret = -EINVAL; + goto done; + } + + req->action = MLAN_ACT_GET; + if (data_length) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + inac_to->timeout_unit = data[0]; + inac_to->unicast_timeout = data[1]; + inac_to->mcast_timeout = data[2]; + inac_to->ps_entry_timeout = data[3]; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy back current values regardless of GET/SET */ + data[0] = inac_to->timeout_unit; + data[1] = inac_to->unicast_timeout; + data[2] = inac_to->mcast_timeout; + data[3] = inac_to->ps_entry_timeout; + + if (req->action == MLAN_ACT_GET) { + wrq->u.data.length = 4; + if (copy_to_user(wrq->u.data.pointer, data, + wrq->u.data.length * sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get system clock + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_ecl_sys_clock(moal_private *priv, struct iwreq *wrq) +{ + int data[64], copy_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int data_length = wrq->u.data.length; + int i = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_SYS_CLOCK; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (!data_length) + req->action = MLAN_ACT_GET; + else if (data_length <= MLAN_MAX_CLK_NUM) { + req->action = MLAN_ACT_SET; + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MERROR, "Invalid arguments\n"); + ret = -EINVAL; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + /* Get configurable clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Current system clock */ + data[0] = (int)cfg->param.sys_clock.cur_sys_clk; + wrq->u.data.length = 1; + + data_length = + MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); + + /* Configurable clocks */ + for (i = 0; i < data_length; i++) { + data[i + wrq->u.data.length] = + (int)cfg->param.sys_clock.sys_clk[i]; + } + wrq->u.data.length += data_length; + + /* Get supported clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_SUPPORTED; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data_length = + MIN(cfg->param.sys_clock.sys_clk_num, MLAN_MAX_CLK_NUM); + + /* Supported clocks */ + for (i = 0; i < data_length; i++) { + data[i + wrq->u.data.length] = + (int)cfg->param.sys_clock.sys_clk[i]; + } + + wrq->u.data.length += data_length; + + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } else { + /* Set configurable clocks */ + cfg->param.sys_clock.sys_clk_type = MLAN_CLK_CONFIGURABLE; + cfg->param.sys_clock.sys_clk_num = + MIN(MLAN_MAX_CLK_NUM, data_length); + for (i = 0; i < cfg->param.sys_clock.sys_clk_num; i++) + cfg->param.sys_clock.sys_clk[i] = (t_u16)data[i]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Band and Adhoc-band setting + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_band_cfg(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + unsigned int i; + int data[3]; + int user_data_len = wrq->u.data.length, copy_len; + t_u32 infra_band = 0; + t_u32 adhoc_band = 0; + t_u32 adhoc_channel = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + LEAVE(); + return -EINVAL; + } + + if (user_data_len > 0) { + if (priv->media_connected == MTRUE) { + LEAVE(); + return -EOPNOTSUPP; + } + } + + copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto error; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + + if (wrq->u.data.length == 0) { + /* Get config_bands, adhoc_start_band and adhoc_channel values + * from MLAN */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + /* Infra Band */ + data[0] = radio_cfg->param.band_cfg.config_bands; + /* Adhoc Band */ + data[1] = radio_cfg->param.band_cfg.adhoc_start_band; + /* Adhoc Channel */ + data[2] = radio_cfg->param.band_cfg.adhoc_channel; + wrq->u.data.length = 3; + + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + ret = -EFAULT; + goto error; + } + } else { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto error; + } + + /* To support only */ + infra_band = data[0]; + for (i = 0; i < sizeof(SupportedInfraBand); i++) + if (infra_band == SupportedInfraBand[i]) + break; + if (i == sizeof(SupportedInfraBand)) { + ret = -EINVAL; + goto error; + } + + /* Set Adhoc band */ + if (user_data_len >= 2) { + adhoc_band = data[1]; + for (i = 0; i < sizeof(SupportedAdhocBand); i++) + if (adhoc_band == SupportedAdhocBand[i]) + break; + if (i == sizeof(SupportedAdhocBand)) { + ret = -EINVAL; + goto error; + } + } + + /* Set Adhoc channel */ + if (user_data_len >= 3) { + adhoc_channel = data[2]; + if (adhoc_channel == 0) { + /* Check if specified adhoc channel is non-zero + */ + ret = -EINVAL; + goto error; + } + } + /* 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; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + } + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read/Write adapter registers value + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_reg_read_write(moal_private *priv, struct iwreq *wrq) +{ + int data[3], copy_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg = (mlan_ds_reg_mem *)req->pbuf; + reg->sub_command = MLAN_OID_REG_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + + if (data_length == 2) { + req->action = MLAN_ACT_GET; + } else if (data_length == 3) { + req->action = MLAN_ACT_SET; + } else { + ret = -EINVAL; + goto done; + } + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + reg->param.reg_rw.type = (t_u32)data[0]; + reg->param.reg_rw.offset = (t_u32)data[1]; + if (data_length == 3) + reg->param.reg_rw.value = (t_u32)data[2]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + if (copy_to_user(wrq->u.data.pointer, ®->param.reg_rw.value, + sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read the EEPROM contents of the card + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_read_eeprom(moal_private *priv, struct iwreq *wrq) +{ + int data[2], copy_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg = (mlan_ds_reg_mem *)req->pbuf; + reg->sub_command = MLAN_OID_EEPROM_RD; + req->req_id = MLAN_IOCTL_REG_MEM; + + if (data_length == 2) { + req->action = MLAN_ACT_GET; + } else { + ret = -EINVAL; + goto done; + } + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + reg->param.rd_eeprom.offset = (t_u16)data[0]; + reg->param.rd_eeprom.byte_count = (t_u16)data[1]; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + wrq->u.data.length = reg->param.rd_eeprom.byte_count; + if (copy_to_user(wrq->u.data.pointer, + reg->param.rd_eeprom.value, + MIN(wrq->u.data.length, MAX_EEPROM_DATA))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Read/Write device memory value + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_mem_read_write(moal_private *priv, struct iwreq *wrq) +{ + t_u32 data[2]; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_reg_mem *reg_mem = NULL; + int data_length = wrq->u.data.length, copy_len; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(data, 0, sizeof(data)); + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_reg_mem)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + reg_mem = (mlan_ds_reg_mem *)req->pbuf; + reg_mem->sub_command = MLAN_OID_MEM_RW; + req->req_id = MLAN_IOCTL_REG_MEM; + + if (data_length == 1) { + PRINTM(MINFO, "MEM_RW: GET\n"); + req->action = MLAN_ACT_GET; + } else if (data_length == 2) { + PRINTM(MINFO, "MEM_RW: SET\n"); + req->action = MLAN_ACT_SET; + } else { + ret = -EINVAL; + goto done; + } + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + reg_mem->param.mem_rw.addr = (t_u32)data[0]; + if (data_length == 2) + reg_mem->param.mem_rw.value = (t_u32)data[1]; + + PRINTM(MINFO, "MEM_RW: Addr=0x%x, Value=0x%x\n", + (int)reg_mem->param.mem_rw.addr, + (int)reg_mem->param.mem_rw.value); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (req->action == MLAN_ACT_GET) { + if (copy_to_user(wrq->u.data.pointer, + ®_mem->param.mem_rw.value, sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get LOG + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_get_log(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_get_stats stats; + char *buf = NULL; + int i = 0; + + ENTER(); + + PRINTM(MINFO, " GET STATS\n"); + buf = kmalloc(GETLOG_BUFSIZE, GFP_KERNEL); + if (!buf) { + PRINTM(MERROR, "kmalloc failed!\n"); + ret = -ENOMEM; + goto done; + } + + memset(&stats, 0, sizeof(mlan_ds_get_stats)); + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { + ret = -EFAULT; + goto done; + } + + if (wrq->u.data.pointer) { + sprintf(buf, + "\n" + "mcasttxframe %u\n" + "failed %u\n" + "retry %u\n" + "multiretry %u\n" + "framedup %u\n" + "rtssuccess %u\n" + "rtsfailure %u\n" + "ackfailure %u\n" + "rxfrag %u\n" + "mcastrxframe %u\n" + "fcserror %u\n" + "txframe %u\n" + "wepicverrcnt-1 %u\n" + "wepicverrcnt-2 %u\n" + "wepicverrcnt-3 %u\n" + "wepicverrcnt-4 %u\n" + "beacon_rcnt %u\n" + "beacon_mcnt %u\n", + stats.mcast_tx_frame, stats.failed, stats.retry, + stats.multi_retry, stats.frame_dup, stats.rts_success, + stats.rts_failure, stats.ack_failure, stats.rx_frag, + stats.mcast_rx_frame, stats.fcs_error, stats.tx_frame, + stats.wep_icv_error[0], stats.wep_icv_error[1], + stats.wep_icv_error[2], stats.wep_icv_error[3], + stats.bcn_rcv_cnt, stats.bcn_miss_cnt); + if (priv->phandle->fw_getlog_enable) { + sprintf(buf + strlen(buf), "tx_frag_cnt %u\n", + stats.tx_frag_cnt); + sprintf(buf + strlen(buf), "qos_tx_frag_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_tx_frag_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_failed_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_failed_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_retry_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_retry_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_multi_retry_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_multi_retry_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_frm_dup_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_frm_dup_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_rts_suc_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_rts_suc_cnt[i]); + } + sprintf(buf + strlen(buf), + "\nqos_rts_failure_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_rts_failure_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_ack_failure_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_ack_failure_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_rx_frag_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_rx_frag_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_tx_frm_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_tx_frm_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_discarded_frm_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_discarded_frm_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_mpdus_rx_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_mpdus_rx_cnt[i]); + } + sprintf(buf + strlen(buf), "\nqos_retries_rx_cnt "); + for (i = 0; i < 8; i++) { + sprintf(buf + strlen(buf), "%u ", + stats.qos_retries_rx_cnt[i]); + } + sprintf(buf + strlen(buf), + "\nmgmt_ccmp_replays %u\n" + "tx_amsdu_cnt %u\n" + "failed_amsdu_cnt %u\n" + "retry_amsdu_cnt %u\n" + "multi_retry_amsdu_cnt %u\n" + "tx_octets_in_amsdu_cnt %llu\n" + "amsdu_ack_failure_cnt %u\n" + "rx_amsdu_cnt %u\n" + "rx_octets_in_amsdu_cnt %llu\n" + "tx_ampdu_cnt %u\n" + "tx_mpdus_in_ampdu_cnt %u\n" + "tx_octets_in_ampdu_cnt %llu\n" + "ampdu_rx_cnt %u\n" + "mpdu_in_rx_ampdu_cnt %u\n" + "rx_octets_in_ampdu_cnt %llu\n" + "ampdu_delimiter_crc_error_cnt %u\n", + stats.mgmt_ccmp_replays, stats.tx_amsdu_cnt, + stats.failed_amsdu_cnt, stats.retry_amsdu_cnt, + stats.multi_retry_amsdu_cnt, + stats.tx_octets_in_amsdu_cnt, + stats.amsdu_ack_failure_cnt, stats.rx_amsdu_cnt, + stats.rx_octets_in_amsdu_cnt, + stats.tx_ampdu_cnt, stats.tx_mpdus_in_ampdu_cnt, + stats.tx_octets_in_ampdu_cnt, + stats.ampdu_rx_cnt, stats.mpdu_in_rx_ampdu_cnt, + stats.rx_octets_in_ampdu_cnt, + stats.ampdu_delimiter_crc_error_cnt); + } + wrq->u.data.length = MIN(GETLOG_BUFSIZE - 1, strlen(buf) + 1); + if (copy_to_user(wrq->u.data.pointer, buf, + wrq->u.data.length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + kfree(buf); + LEAVE(); + return ret; +} + +/** + * @brief Deauthenticate + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_deauth(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + struct sockaddr saddr; + + ENTER(); + if (wrq->u.data.length) { + /* Deauth mentioned BSSID */ + if (copy_from_user(&saddr, wrq->u.data.pointer, + sizeof(saddr))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, + (t_u8 *)saddr.sa_data, + DEF_DEAUTH_REASON_CODE)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE)) + ret = -EFAULT; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TX power configurations + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_tx_power_cfg(moal_private *priv, struct iwreq *wrq) +{ + int data[5], user_data_len, copy_len; + int ret = 0; + mlan_bss_info bss_info; + mlan_ds_power_cfg *pcfg = NULL; + mlan_ioctl_req *req = NULL; + int power_data[MAX_POWER_TABLE_SIZE]; + int i, power_ext_len = 0; + int *ptr = power_data; + mlan_power_group *pwr_grp = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + memset(data, 0, sizeof(data)); + user_data_len = wrq->u.data.length; + copy_len = MIN(sizeof(data), sizeof(int) * user_data_len); + + if (user_data_len) { + if (sizeof(int) * user_data_len > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + switch (user_data_len) { + case 1: + if (data[0] != 0xFF) + ret = -EINVAL; + break; + case 2: + case 4: + if (data[0] == 0xFF) { + ret = -EINVAL; + break; + } + if (data[1] < bss_info.min_power_level) { + PRINTM(MERROR, + "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", + data[1], (int)bss_info.min_power_level, + (int)bss_info.max_power_level); + ret = -EINVAL; + break; + } + if (user_data_len == 4) { + if (data[1] > data[2]) { + PRINTM(MERROR, + "Min power should be less than maximum!\n"); + ret = -EINVAL; + break; + } + if (data[3] < 0) { + PRINTM(MERROR, + "Step should not less than 0!\n"); + ret = -EINVAL; + break; + } + if (data[2] > bss_info.max_power_level) { + PRINTM(MERROR, + "The set powercfg rate value %d dBm is out of range (%d dBm-%d dBm)!\n", + data[2], + (int)bss_info.min_power_level, + (int)bss_info.max_power_level); + ret = -EINVAL; + break; + } + if (data[3] > data[2] - data[1]) { + PRINTM(MERROR, + "Step should not greater than power difference!\n"); + ret = -EINVAL; + break; + } + } + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_power_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pcfg = (mlan_ds_power_cfg *)req->pbuf; + pcfg->sub_command = MLAN_OID_POWER_CFG_EXT; + req->req_id = MLAN_IOCTL_POWER_CFG; + if (!user_data_len) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + if (data[0] == 0xFF) + pcfg->param.power_ext.power_group[0].rate_format = + TX_PWR_CFG_AUTO_CTRL_OFF; + else { + pcfg->param.power_ext.power_group[0].power_step = 0; + pcfg->param.power_ext.power_group[0].first_rate_ind = + data[0]; + pcfg->param.power_ext.power_group[0].last_rate_ind = + data[0]; + if (data[0] <= 11) { + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_LG; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + } else if (data[0] <= 27) { + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_HT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW20; + pcfg->param.power_ext.power_group[0] + .first_rate_ind -= 12; + pcfg->param.power_ext.power_group[0] + .last_rate_ind -= 12; + } else if ((140 <= data[0]) && (data[0] <= 155)) { + pcfg->param.power_ext.power_group[0] + .rate_format = MLAN_RATE_FORMAT_HT; + pcfg->param.power_ext.power_group[0].bandwidth = + MLAN_HT_BW40; + pcfg->param.power_ext.power_group[0] + .first_rate_ind -= 140; + pcfg->param.power_ext.power_group[0] + .last_rate_ind -= 140; + } + if (user_data_len == 2) { + pcfg->param.power_ext.power_group[0].power_min = + data[1]; + pcfg->param.power_ext.power_group[0].power_max = + data[1]; + } else if (user_data_len == 4) { + pcfg->param.power_ext.power_group[0].power_min = + data[1]; + pcfg->param.power_ext.power_group[0].power_max = + data[2]; + pcfg->param.power_ext.power_group[0].power_step = + data[3]; + } + pcfg->param.power_ext.num_pwr_grp = 1; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!user_data_len) { + /* GET operation */ + i = 0; + power_ext_len = 0; + ptr = power_data; + while ((i < pcfg->param.power_ext.num_pwr_grp) && + ((power_ext_len + 5) < MAX_POWER_TABLE_SIZE)) { + pwr_grp = &pcfg->param.power_ext.power_group[i]; + if (pwr_grp->rate_format == MLAN_RATE_FORMAT_HT) { + if (pwr_grp->bandwidth == MLAN_HT_BW20) { + pwr_grp->first_rate_ind += 12; + pwr_grp->last_rate_ind += 12; + } else if (pwr_grp->bandwidth == MLAN_HT_BW40) { + pwr_grp->first_rate_ind += 140; + pwr_grp->last_rate_ind += 140; + } + } + + if ((pwr_grp->rate_format == MLAN_RATE_FORMAT_LG) || + (pwr_grp->rate_format == MLAN_RATE_FORMAT_HT)) { + *ptr = pwr_grp->first_rate_ind; + ptr++; + *ptr = pwr_grp->last_rate_ind; + ptr++; + *ptr = pwr_grp->power_min; + ptr++; + *ptr = pwr_grp->power_max; + ptr++; + *ptr = pwr_grp->power_step; + ptr++; + power_ext_len += 5; + } + i++; + } + if (copy_to_user(wrq->u.data.pointer, (t_u8 *)power_data, + sizeof(int) * power_ext_len)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = power_ext_len; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get Tx/Rx data rates + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_get_txrx_rate(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + rate = (mlan_ds_rate *)req->pbuf; + rate->sub_command = MLAN_OID_GET_DATA_RATE; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user(wrq->u.data.pointer, (t_u8 *)&rate->param.data_rate, + sizeof(int) * 2)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 2; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifdef SDIO +/** + * @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; +} +#endif /* SDIO */ + +/** + * @brief Set/Get beacon interval + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_beacon_interval(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + int bcn = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(&bcn, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((bcn < MLAN_MIN_BEACON_INTERVAL) || + (bcn > MLAN_MAX_BEACON_INTERVAL)) { + ret = -EINVAL; + goto done; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL; + req->req_id = MLAN_IOCTL_BSS; + if (!wrq->u.data.length) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + bss->param.bcn_interval = bcn; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user(wrq->u.data.pointer, (t_u8 *)&bss->param.bcn_interval, + sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get TX data rate + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_set_get_txrate(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + int rateindex = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (wrq->u.data.length) { + if (copy_from_user(&rateindex, wrq->u.data.pointer, + sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + rate = (mlan_ds_rate *)req->pbuf; + rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + if (!wrq->u.data.length) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + if (rateindex == AUTO_RATE) + rate->param.rate_cfg.is_rate_auto = 1; + else { + if ((rateindex != MLAN_RATE_INDEX_MCS32) && + ((rateindex < 0) || + (rateindex > MLAN_RATE_INDEX_MCS15))) { + ret = -EINVAL; + goto done; + } + } + rate->param.rate_cfg.rate = rateindex; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } else { + if (wrq->u.data.length) + priv->rate_index = rateindex; + } + if (!wrq->u.data.length) { + if (rate->param.rate_cfg.is_rate_auto) + rateindex = AUTO_RATE; + else + rateindex = rate->param.rate_cfg.rate; + wrq->u.data.length = 1; + if (copy_to_user(wrq->u.data.pointer, &rateindex, sizeof(int))) + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get region code + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_set_get_regioncode(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_misc_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + int region = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(®ion, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_MISC_REGION; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (!wrq->u.data.length) + req->action = MLAN_ACT_GET; + else { + req->action = MLAN_ACT_SET; + cfg->param.region_code = region; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!wrq->u.data.length) { + wrq->u.data.length = 1; + if (copy_to_user(wrq->u.data.pointer, &cfg->param.region_code, + sizeof(int))) + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get radio + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_set_get_radio(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_bss_info bss_info; + int option = 0; + + ENTER(); + + memset(&bss_info, 0, sizeof(bss_info)); + + if (wrq->u.data.length) { + /* Set radio */ + if (copy_from_user(&option, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != woal_set_radio(priv, (t_u8)option)) + ret = -EFAULT; + } else { + /* Get radio status */ + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + wrq->u.data.length = 1; + if (copy_to_user(wrq->u.data.pointer, &bss_info.radio_on, + sizeof(bss_info.radio_on))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + LEAVE(); + return ret; +} + +#ifdef DEBUG_LEVEL1 +/** + * @brief Get/Set the bit mask of driver debug message control + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to wrq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_drv_dbg(moal_private *priv, struct iwreq *wrq) +{ + int data[4], copy_len; + int ret = 0; + int data_length = wrq->u.data.length; + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + if (!data_length) { + data[0] = drvdbg; + /* Return the current driver debug bit masks */ + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto drvdbgexit; + } + wrq->u.data.length = 1; + } else if (data_length < 3) { + /* Get the driver debug bit masks from user */ + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto drvdbgexit; + } + drvdbg = data[0]; + /* Set the driver debug bit masks into mlan */ + woal_set_drvdbg(priv, drvdbg); + } else { + PRINTM(MERROR, "Invalid parameter number\n"); + goto drvdbgexit; + } + + printk(KERN_ALERT "drvdbg = 0x%08x\n", drvdbg); +#ifdef DEBUG_LEVEL2 + printk(KERN_ALERT "MINFO (%08x) %s\n", MINFO, + (drvdbg & MINFO) ? "X" : ""); + printk(KERN_ALERT "MWARN (%08x) %s\n", MWARN, + (drvdbg & MWARN) ? "X" : ""); + printk(KERN_ALERT "MENTRY (%08x) %s\n", MENTRY, + (drvdbg & MENTRY) ? "X" : ""); +#endif + printk(KERN_ALERT "MMPA_D (%08x) %s\n", MMPA_D, + (drvdbg & MMPA_D) ? "X" : ""); + printk(KERN_ALERT "MIF_D (%08x) %s\n", MIF_D, + (drvdbg & MIF_D) ? "X" : ""); + printk(KERN_ALERT "MFW_D (%08x) %s\n", MFW_D, + (drvdbg & MFW_D) ? "X" : ""); + printk(KERN_ALERT "MEVT_D (%08x) %s\n", MEVT_D, + (drvdbg & MEVT_D) ? "X" : ""); + printk(KERN_ALERT "MCMD_D (%08x) %s\n", MCMD_D, + (drvdbg & MCMD_D) ? "X" : ""); + printk(KERN_ALERT "MDAT_D (%08x) %s\n", MDAT_D, + (drvdbg & MDAT_D) ? "X" : ""); + printk(KERN_ALERT "MREG_D (%08x) %s\n", MREG_D, + (drvdbg & MREG_D) ? "X" : ""); + printk(KERN_ALERT "MIOCTL (%08x) %s\n", MIOCTL, + (drvdbg & MIOCTL) ? "X" : ""); + printk(KERN_ALERT "MINTR (%08x) %s\n", MINTR, + (drvdbg & MINTR) ? "X" : ""); + printk(KERN_ALERT "MEVENT (%08x) %s\n", MEVENT, + (drvdbg & MEVENT) ? "X" : ""); + printk(KERN_ALERT "MCMND (%08x) %s\n", MCMND, + (drvdbg & MCMND) ? "X" : ""); + printk(KERN_ALERT "MDATA (%08x) %s\n", MDATA, + (drvdbg & MDATA) ? "X" : ""); + printk(KERN_ALERT "MERROR (%08x) %s\n", MERROR, + (drvdbg & MERROR) ? "X" : ""); + printk(KERN_ALERT "MFATAL (%08x) %s\n", MFATAL, + (drvdbg & MFATAL) ? "X" : ""); + printk(KERN_ALERT "MMSG (%08x) %s\n", MMSG, + (drvdbg & MMSG) ? "X" : ""); + +drvdbgexit: + LEAVE(); + return ret; +} +#endif /* DEBUG_LEVEL1 */ + +/** + * @brief Set/Get QoS configuration + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_set_get_qos_cfg(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_wmm_cfg *cfg = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_QOS; + req->req_id = MLAN_IOCTL_WMM_CFG; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + cfg->param.qos_cfg = (t_u8)data; + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!wrq->u.data.length) { + data = (int)cfg->param.qos_cfg; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#ifndef OPCHAN +/** + * @brief Set/Get WWS mode + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_wws_cfg(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_misc_cfg *wws = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + wws = (mlan_ds_misc_cfg *)req->pbuf; + wws->sub_command = MLAN_OID_MISC_WWS; + req->req_id = MLAN_IOCTL_MISC_CFG; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (data != CMD_DISABLED && data != CMD_ENABLED) { + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + wws->param.wws_cfg = data; + } else + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.data.length) { + data = wws->param.wws_cfg; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set/Get sleep period + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_sleep_pd(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int data = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_SLEEP_PD; + req->req_id = MLAN_IOCTL_PM_CFG; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data <= MAX_SLEEP_PERIOD && data >= MIN_SLEEP_PERIOD) || + (data == 0) || (data == SLEEP_PERIOD_RESERVED_FF)) { + req->action = MLAN_ACT_SET; + pm_cfg->param.sleep_period = data; + } else { + ret = -EINVAL; + goto done; + } + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.data.length) { + data = pm_cfg->param.sleep_period; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief 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, copy_len; + int data_length = wrq->u.data.length; +#ifdef DEBUG_LEVEL1 + char err_str[][36] = {{"sleep clock error in ppm"}, + {"wakeup offset in usec"}, + {"clock stabilization time in usec"}, + {"control periodic calibration(0-2)"}, + {"control of external sleepClock(0-2)"}, + {"value of reserved for debug"}}; +#endif + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + + pm = (mlan_ds_pm_cfg *)req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_SLEEP_PARAMS; + req->req_id = MLAN_IOCTL_PM_CFG; + psleep_params = (pmlan_ds_sleep_params)&pm->param.sleep_params; + + if (data_length == 0) { + req->action = MLAN_ACT_GET; + } else if (data_length == 6) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + /* copy_from_user failed */ + PRINTM(MERROR, "S_PARAMS: copy from user failed\n"); + ret = -EINVAL; + goto done; + } + +#define MIN_VAL 0x0000 +#define MAX_VAL 0xFFFF + for (i = 0; i < 6; i++) { + if ((i == 3) || (i == 4)) { + /* These two cases are handled below the loop */ + continue; + } + if (data[i] < MIN_VAL || data[i] > MAX_VAL) { + PRINTM(MERROR, "Invalid %s (0-65535)!\n", + err_str[i]); + ret = -EINVAL; + goto done; + } + } + if (data[3] < 0 || data[3] > 2) { + PRINTM(MERROR, + "Invalid control periodic calibration (0-2)!\n"); + ret = -EINVAL; + goto done; + } + if (data[4] < 0 || data[4] > 2) { + PRINTM(MERROR, + "Invalid control of external sleep clock (0-2)!\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + psleep_params->error = data[0]; + psleep_params->offset = data[1]; + psleep_params->stable_time = data[2]; + psleep_params->cal_control = data[3]; + psleep_params->ext_sleep_clk = data[4]; + psleep_params->reserved = data[5]; + } else { + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = psleep_params->error; + data[1] = psleep_params->offset; + data[2] = psleep_params->stable_time; + data[3] = psleep_params->cal_control; + data[4] = psleep_params->ext_sleep_clk; + data[5] = psleep_params->reserved; + wrq->u.data.length = 6; + + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "QCONFIG: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get user provisioned local power constraint + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int woal_set_get_11h_local_pwr_constraint(moal_private *priv, + struct iwreq *wrq) +{ + int ret = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + ds_11hcfg->param.usr_local_power_constraint = (t_s8)data; + req->action = MLAN_ACT_SET; + } else + req->action = MLAN_ACT_GET; + + ds_11hcfg->sub_command = MLAN_OID_11H_LOCAL_POWER_CONSTRAINT; + req->req_id = MLAN_IOCTL_11H_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy response to user */ + if (req->action == MLAN_ACT_GET) { + data = (int)ds_11hcfg->param.usr_local_power_constraint; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + PRINTM(MINFO, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get 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; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (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; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy response to user */ + data = (int)cfg->param.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 (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get MAC control configuration + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int woal_mac_control_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (mlan_ds_misc_cfg *)req->pbuf; + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + /* Validation will be done later */ + cfg->param.mac_ctrl = data; + req->action = MLAN_ACT_SET; + } else + req->action = MLAN_ACT_GET; + + cfg->sub_command = MLAN_OID_MISC_MAC_CONTROL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy response to user */ + data = (int)cfg->param.mac_ctrl; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + PRINTM(MINFO, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get thermal reading + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int woal_thermal_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (mlan_ds_misc_cfg *)req->pbuf; + if (wrq->u.data.length) { + PRINTM(MERROR, "Set is not supported for this command\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_GET; + + cfg->sub_command = MLAN_OID_MISC_THERMAL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy response to user */ + data = (int)cfg->param.thermal; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + PRINTM(MINFO, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get hotspot enable state + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to user data + * + * @return 0 --success, otherwise fail + */ +static int woal_cfg_hotspot(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *cfg = NULL; + int config; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (wrq->u.data.length > 1) { + PRINTM(MERROR, "Invalid no of arguments!\n"); + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (mlan_ds_misc_cfg *)req->pbuf; + if (wrq->u.data.length == 0) + req->action = MLAN_ACT_GET; + else { + if (copy_from_user(&config, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + cfg->param.hotspot_cfg = config; + req->action = MLAN_ACT_SET; + } + + cfg->sub_command = MLAN_OID_MISC_HOTSPOT_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + config = cfg->param.hotspot_cfg; + if (copy_to_user(wrq->u.data.pointer, &config, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if defined(REASSOCIATION) +/** + * @brief Set/Get reassociation settings + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_set_get_reassoc(moal_private *priv, struct iwreq *wrq) +{ + moal_handle *handle = priv->phandle; + int ret = 0; + int data = 0; + + ENTER(); + + if (wrq->u.data.length) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (data == 0) { + handle->reassoc_on &= ~MBIT(priv->bss_index); + priv->reassoc_on = MFALSE; + priv->reassoc_required = MFALSE; + if (!handle->reassoc_on && + handle->is_reassoc_timer_set == MTRUE) { + woal_cancel_timer(&handle->reassoc_timer); + handle->is_reassoc_timer_set = MFALSE; + } + } else { + handle->reassoc_on |= MBIT(priv->bss_index); + priv->reassoc_on = MTRUE; + } + } else { + data = (int)(priv->reassoc_on); + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + LEAVE(); + return ret; +} +#endif /* REASSOCIATION */ + +/** + * @brief implement WMM enable command + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to 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; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + wmm = (mlan_ds_wmm_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_WMM_CFG; + wmm->sub_command = MLAN_OID_WMM_CFG_ENABLE; + + if (wrq->u.data.length) { + /* Set WMM configuration */ + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + if (data == CMD_DISABLED) + wmm->param.wmm_enable = MFALSE; + else + wmm->param.wmm_enable = MTRUE; + } else { + /* Get WMM status */ + req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, &wmm->param.wmm_enable, + sizeof(wmm->param.wmm_enable))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Implement 802.11D enable command + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to 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; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11D_CFG; + pcfg_11d->sub_command = MLAN_OID_11D_CFG_ENABLE; + if (wrq->u.data.length) { + /* Set 11D configuration */ + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data < CMD_DISABLED) || (data > CMD_ENABLED)) { + ret = -EINVAL; + goto done; + } + if (data == CMD_DISABLED) + pcfg_11d->param.enable_11d = MFALSE; + else + pcfg_11d->param.enable_11d = MTRUE; + req->action = MLAN_ACT_SET; + } else { + req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, + &pcfg_11d->param.enable_11d, + sizeof(pcfg_11d->param.enable_11d))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Implement 802.11D clear chan table command + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to 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; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pcfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11D_CFG; + pcfg_11d->sub_command = MLAN_OID_11D_CLR_CHAN_TABLE; + req->action = MLAN_ACT_SET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Control WPS Session Enable/Disable + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq Pointer to 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; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + PRINTM(MINFO, "WOAL_WPS_SESSION\n"); + + memset(buf, 0, sizeof(buf)); + if (copy_from_user(buf, wreq->u.data.pointer, + MIN(sizeof(buf) - 1, wreq->u.data.length))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pwps = (mlan_ds_wps_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + if (buf[0] == 1) + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + else + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set WPA passphrase and SSID + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to user data + * + * @return 0 --success, otherwise fail + */ +static int woal_passphrase(moal_private *priv, struct iwreq *wrq) +{ + t_u16 len = 0; + char buf[256]; + char *begin = NULL, *end = NULL, *opt = NULL; + int ret = 0, action = -1, i; + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + t_u8 zero_mac[] = {0, 0, 0, 0, 0, 0}; + t_u8 *mac = NULL; + int data_length = wrq->u.data.length, copy_len; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv->phandle->card_info->embedded_supp) { + PRINTM(MERROR, "Not supported cmd on this card\n"); + ret = -EOPNOTSUPP; + goto done; + } + if (!data_length || data_length >= sizeof(buf) - 1) { + PRINTM(MERROR, + "Argument missing or too long for setpassphrase\n"); + ret = -EINVAL; + goto done; + } + memset(buf, 0, sizeof(buf)); + copy_len = data_length; + + if (copy_from_user(buf, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + buf[copy_len] = '\0'; + + /* Parse the buf to get the cmd_action */ + begin = buf; + end = woal_strsep(&begin, ';', '/'); + if (!end) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + goto done; + } + action = woal_atox(end); + if (action < 0 || action > 2 || end[1] != '\0') { + PRINTM(MERROR, "Invalid action argument %s\n", end); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PASSPHRASE; + req->req_id = MLAN_IOCTL_SEC_CFG; + if (action == 0) + req->action = MLAN_ACT_GET; + else + req->action = MLAN_ACT_SET; + while (begin) { + end = woal_strsep(&begin, ';', '/'); + opt = woal_strsep(&end, '=', '/'); + if (!opt || !end || !end[0]) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + break; + } else if (!strnicmp(opt, "ssid", strlen(opt))) { + if (strlen(end) > MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, + "SSID length exceeds max length\n"); + ret = -EFAULT; + break; + } + sec->param.passphrase.ssid.ssid_len = strlen(end); + moal_memcpy_ext(priv->phandle, + sec->param.passphrase.ssid.ssid, end, + strlen(end), MLAN_MAX_SSID_LENGTH); + PRINTM(MINFO, "ssid=%s, len=%d\n", + sec->param.passphrase.ssid.ssid, + (int)sec->param.passphrase.ssid.ssid_len); + } else if (!strnicmp(opt, "bssid", strlen(opt))) { + woal_mac2u8(sec->param.passphrase.bssid, end); + } else if (!strnicmp(opt, "psk", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) != MLAN_PMK_HEXSTR_LENGTH) { + PRINTM(MERROR, "Invalid PMK length\n"); + ret = -EINVAL; + break; + } + woal_ascii2hex( + (t_u8 *)(sec->param.passphrase.psk.pmk.pmk), + end, MLAN_PMK_HEXSTR_LENGTH / 2); + sec->param.passphrase.psk_type = MLAN_PSK_PMK; + } else if (!strnicmp(opt, "passphrase", strlen(opt)) && + req->action == MLAN_ACT_SET) { + if (strlen(end) < MLAN_MIN_PASSPHRASE_LENGTH || + strlen(end) > MLAN_MAX_PASSPHRASE_LENGTH) { + PRINTM(MERROR, + "Invalid length for passphrase\n"); + ret = -EINVAL; + break; + } + sec->param.passphrase.psk_type = MLAN_PSK_PASSPHRASE; + moal_memcpy_ext( + priv->phandle, + sec->param.passphrase.psk.passphrase.passphrase, + end, + sizeof(sec->param.passphrase.psk.passphrase + .passphrase), + sizeof(sec->param.passphrase.psk.passphrase + .passphrase)); + sec->param.passphrase.psk.passphrase.passphrase_len = + strlen(end); + PRINTM(MINFO, "passphrase=%s, len=%d\n", + sec->param.passphrase.psk.passphrase.passphrase, + (int)sec->param.passphrase.psk.passphrase + .passphrase_len); + } else { + PRINTM(MERROR, "Invalid option %s\n", opt); + ret = -EINVAL; + break; + } + } + if (ret) + goto done; + + if (action == 2) + sec->param.passphrase.psk_type = MLAN_PSK_CLEAR; + else if (action == 0) + sec->param.passphrase.psk_type = MLAN_PSK_QUERY; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (action == 0) { + memset(buf, 0, sizeof(buf)); + if (sec->param.passphrase.ssid.ssid_len) { + len += sprintf(buf + len, "ssid:"); + moal_memcpy_ext(priv->phandle, buf + len, + sec->param.passphrase.ssid.ssid, + sec->param.passphrase.ssid.ssid_len, + sizeof(buf) - len); + len += sec->param.passphrase.ssid.ssid_len; + len += sprintf(buf + len, " "); + } + if (memcmp(&sec->param.passphrase.bssid, zero_mac, + sizeof(zero_mac))) { + mac = (t_u8 *)&sec->param.passphrase.bssid; + len += sprintf(buf + len, "bssid:"); + for (i = 0; i < ETH_ALEN - 1; ++i) + len += sprintf(buf + len, "%02x:", mac[i]); + len += sprintf(buf + len, "%02x ", mac[i]); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) { + len += sprintf(buf + len, "psk:"); + for (i = 0; i < MLAN_MAX_KEY_LENGTH; ++i) + len += sprintf( + buf + len, "%02x", + sec->param.passphrase.psk.pmk.pmk[i]); + len += sprintf(buf + len, "\n"); + } + if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) { + len += sprintf( + buf + len, "passphrase:%s\n", + sec->param.passphrase.psk.passphrase.passphrase); + } + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, buf, + MIN(len, sizeof(buf)))) { + PRINTM(MERROR, "Copy to user failed, len %d\n", + len); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = len; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get esupp mode + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_get_esupp_mode(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv->phandle->card_info->embedded_supp) { + PRINTM(MERROR, "Not supported cmd on this card\n"); + ret = -EOPNOTSUPP; + 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_ESUPP_MODE; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (copy_to_user(wrq->u.data.pointer, (t_u8 *)&sec->param.esupp_mode, + sizeof(int) * 3)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 3; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get GTK/PTK + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to user data + * + * @return 0 --success, otherwise fail + */ +static int woal_get_key_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + unsigned int i; + t_u8 key_ascii[256]; + t_u8 *tmp; + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(key_ascii, 0x00, sizeof(key_ascii)); + tmp = key_ascii; + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can't get key in un-associated state\n"); + ret = -EFAULT; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Get Unicast 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_QUERY_KEY; + sec->param.encrypt_key.key_index = 0; + sec->param.encrypt_key.key_flags = 0; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (sec->param.encrypt_key.key_len) { + sprintf((char *)tmp, "\n%s", "PTK: "); + tmp += 5; + for (i = 0; i < sec->param.encrypt_key.key_len; i++) + tmp += sprintf((char *)tmp, "%02x", + sec->param.encrypt_key.key_material[i]); + } + + /* Get Multicase 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_QUERY_KEY; + sec->param.encrypt_key.key_index = 0; + sec->param.encrypt_key.key_flags = KEY_FLAG_GROUP_KEY; + memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (sec->param.encrypt_key.key_len) { + sprintf((char *)tmp, "\n%s", "GTK: "); + tmp += 5; + for (i = 0; i < sec->param.encrypt_key.key_len; i++) + tmp += sprintf((char *)tmp, "%02x", + sec->param.encrypt_key.key_material[i]); + } + + /* Get IGTK 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_QUERY_KEY; + sec->param.encrypt_key.key_index = 0; + sec->param.encrypt_key.key_flags = KEY_FLAG_AES_MCAST_IGTK; + memset(sec->param.encrypt_key.mac_addr, 0x0, MLAN_MAC_ADDR_LENGTH); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (sec->param.encrypt_key.key_len) { + sprintf((char *)tmp, "\n%s", "IGTK: "); + tmp += 6; + for (i = 0; i < sec->param.encrypt_key.key_len; i++) + tmp += sprintf((char *)tmp, "%02x", + sec->param.encrypt_key.key_material[i]); + } + + wrq->u.data.length = sizeof(key_ascii) + 1; + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, &key_ascii, + tmp - key_ascii)) { + PRINTM(MERROR, "copy_to_user failed\n"); + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/get IP address + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int woal_set_get_ip_addr(moal_private *priv, struct iwreq *wrq) +{ + char buf[IPADDR_MAX_BUF]; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + int ret = 0, op_code = 0, data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(buf, 0, IPADDR_MAX_BUF); + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + + if (data_length <= 1) { /* GET */ + ioctl_req->action = MLAN_ACT_GET; + } else { + if (copy_from_user(buf, wrq->u.data.pointer, + MIN(IPADDR_MAX_BUF - 1, data_length))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + /* Make sure we have the operation argument */ + if (data_length > 2 && buf[1] != ';') { + PRINTM(MERROR, + "No operation argument. Separate with ';'\n"); + ret = -EINVAL; + goto done; + } else { + buf[1] = '\0'; + } + ioctl_req->action = MLAN_ACT_SET; + /* only one IP is supported in current firmware */ + memset(misc->param.ipaddr_cfg.ip_addr[0], 0, IPADDR_LEN); + in4_pton(&buf[2], MIN((IPADDR_MAX_BUF - 3), (data_length - 2)), + misc->param.ipaddr_cfg.ip_addr[0], ' ', NULL); + /* only one IP is supported in current firmware */ + misc->param.ipaddr_cfg.ip_addr_num = 1; + misc->param.ipaddr_cfg.ip_addr_type = IPADDR_TYPE_IPV4; + } + if (woal_atoi(&op_code, buf) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + misc->param.ipaddr_cfg.op_code = (t_u32)op_code; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_IP_ADDR; + + /* Send ioctl to mlan */ + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + snprintf(buf, IPADDR_MAX_BUF, "%d;%d.%d.%d.%d", + misc->param.ipaddr_cfg.op_code, + misc->param.ipaddr_cfg.ip_addr[0][0], + misc->param.ipaddr_cfg.ip_addr[0][1], + misc->param.ipaddr_cfg.ip_addr[0][2], + misc->param.ipaddr_cfg.ip_addr[0][3]); + wrq->u.data.length = IPADDR_MAX_BUF; + if (copy_to_user(wrq->u.data.pointer, buf, IPADDR_MAX_BUF)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Transmit beamforming capabilities + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_tx_bf_cap_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data_length = wrq->u.data.length; + mlan_ioctl_req *req = NULL; + mlan_ds_11n_cfg *bf_cfg = NULL; + int bf_cap = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 1) { + PRINTM(MERROR, "Invalid no of arguments!\n"); + ret = -EINVAL; + goto done; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + bf_cfg = (mlan_ds_11n_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_11N_CFG; + bf_cfg->sub_command = MLAN_OID_11N_CFG_TX_BF_CAP; + req->action = MLAN_ACT_GET; + if (data_length) { /* SET */ + if (copy_from_user(&bf_cap, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + bf_cfg->param.tx_bf_cap = bf_cap; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + bf_cap = bf_cfg->param.tx_bf_cap; + if (copy_to_user(wrq->u.data.pointer, &bf_cap, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/* Maximum input output characters in group WOAL_SET_GET_256_CHAR */ +#define MAX_IN_OUT_CHAR 256 +/** Tx BF Global conf argument index */ +#define BF_ENABLE_PARAM 1 +#define SOUND_ENABLE_PARAM 2 +#define FB_TYPE_PARAM 3 +#define SNR_THRESHOLD_PARAM 4 +#define SOUND_INTVL_PARAM 5 +#define BF_MODE_PARAM 6 +#define MAX_TX_BF_GLOBAL_ARGS 6 +#define BF_CFG_ACT_GET 0 +#define BF_CFG_ACT_SET 1 + +/** + * @brief Set/Get Transmit beamforming configuration + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_tx_bf_cfg_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data_length = wrq->u.data.length; + int bf_action = 0, interval = 0; + int snr = 0, i, tmp_val = 0; + t_u8 buf[MAX_IN_OUT_CHAR], char_count = 0; + t_u8 *str, *token, *pos; + t_u16 action = 0; + + mlan_ds_11n_tx_bf_cfg bf_cfg; + mlan_trigger_sound_args *bf_sound = NULL; + mlan_tx_bf_peer_args *tx_bf_peer = NULL; + mlan_snr_thr_args *bf_snr = NULL; + mlan_bf_periodicity_args *bf_periodicity = NULL; + mlan_bf_global_cfg_args *bf_global = NULL; + + ENTER(); + + memset(&bf_cfg, 0, sizeof(bf_cfg)); + /* Pointer to corresponding buffer */ + bf_sound = bf_cfg.body.bf_sound; + tx_bf_peer = bf_cfg.body.tx_bf_peer; + bf_snr = bf_cfg.body.bf_snr; + bf_periodicity = bf_cfg.body.bf_periodicity; + bf_global = &bf_cfg.body.bf_global_cfg; + + /* Total characters in buffer */ + char_count = data_length - 1; + memset(buf, 0, sizeof(buf)); + if (char_count) { + if (data_length > sizeof(buf)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user(buf, wrq->u.data.pointer, data_length)) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + if (char_count > 1 && buf[1] != ';') { + PRINTM(MERROR, + "No action argument. Separate with ';'\n"); + ret = -EINVAL; + goto done; + } + /* Replace ';' with NULL in the string to separate args */ + for (i = 0; i < char_count; i++) { + if (buf[i] == ';') + buf[i] = '\0'; + } + /* The first byte represents the beamforming action */ + if (woal_atoi(&bf_action, &buf[0]) != MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + switch (bf_action) { + case BF_GLOBAL_CONFIGURATION: + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + /* Eliminate action field */ + token = &buf[2]; + for (i = 1, str = &buf[2]; token != NULL; i++) { + token = strstr(str, " "); + pos = str; + if (token != NULL) { + *token = '\0'; + str = token + 1; + } + woal_atoi(&tmp_val, pos); + switch (i) { + case BF_ENABLE_PARAM: + bf_global->bf_enbl = + (t_u8)tmp_val; + break; + case SOUND_ENABLE_PARAM: + bf_global->sounding_enbl = + (t_u8)tmp_val; + break; + case FB_TYPE_PARAM: + bf_global->fb_type = + (t_u8)tmp_val; + break; + case SNR_THRESHOLD_PARAM: + bf_global->snr_threshold = + (t_u8)tmp_val; + break; + case SOUND_INTVL_PARAM: + bf_global->sounding_interval = + (t_u16)tmp_val; + break; + case BF_MODE_PARAM: + bf_global->bf_mode = + (t_u8)tmp_val; + break; + default: + PRINTM(MERROR, + "Invalid Argument\n"); + ret = -EINVAL; + goto done; + } + } + } + break; + case TRIGGER_SOUNDING_FOR_PEER: + /* + * First arg = 2 BfAction + * Second arg = 17 MAC "00:50:43:20:BF:64" + */ + if (char_count != 19) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + woal_mac2u8(bf_sound->peer_mac, &buf[2]); + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + break; + case SET_GET_BF_PERIODICITY: + /* + * First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 1 (min char) TX BF interval + * 10 (max char) u32 maximum value + * 4294967295 + */ + if (char_count < 19 || char_count > 30) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + + woal_mac2u8(bf_periodicity->peer_mac, &buf[2]); + if (char_count == 19) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + if (woal_atoi(&interval, &buf[20]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + bf_periodicity->interval = interval; + } + break; + case TX_BF_FOR_PEER_ENBL: + /* + * Handle only SET operation here + * First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 2 enable/disable bf + * Fourth arg = 2 enable/disable sounding + * Fifth arg = 1 FB Type + */ + if (char_count != 25 && char_count != 1) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + woal_mac2u8(tx_bf_peer->peer_mac, &buf[2]); + woal_atoi(&tmp_val, &buf[20]); + tx_bf_peer->bf_enbl = (t_u8)tmp_val; + woal_atoi(&tmp_val, &buf[22]); + tx_bf_peer->sounding_enbl = (t_u8)tmp_val; + woal_atoi(&tmp_val, &buf[24]); + tx_bf_peer->fb_type = (t_u8)tmp_val; + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + } + break; + case SET_SNR_THR_PEER: + /* + * First arg = 2 BfAction + * Second arg = 18 MAC "00:50:43:20:BF:64;" + * Third arg = 1/2 SNR u8 - can be 1/2 charerters + */ + if (char_count != 1 && + !(char_count == 21 || char_count == 22)) { + PRINTM(MERROR, "Invalid argument\n"); + ret = -EINVAL; + goto done; + } + if (char_count == 1) { + action = MLAN_ACT_GET; + bf_cfg.action = BF_CFG_ACT_GET; + } else { + woal_mac2u8(bf_snr->peer_mac, &buf[2]); + if (woal_atoi(&snr, &buf[20]) != + MLAN_STATUS_SUCCESS) { + ret = -EINVAL; + goto done; + } + bf_snr->snr = snr; + action = MLAN_ACT_SET; + bf_cfg.action = BF_CFG_ACT_SET; + } + break; + default: + ret = -EINVAL; + goto done; + } + + /* Save the value */ + bf_cfg.bf_action = bf_action; + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_bf_cfg(priv, action, &bf_cfg)) { + ret = -EFAULT; + goto done; + } + } else { + ret = -EINVAL; + goto done; + } + + if (action == MLAN_ACT_GET) { + data_length = 0; + memset(buf, 0, sizeof(buf)); + switch (bf_action) { + case BF_GLOBAL_CONFIGURATION: + data_length += sprintf(buf + data_length, "%d ", + (int)bf_global->bf_enbl); + data_length += sprintf(buf + data_length, "%d ", + (int)bf_global->sounding_enbl); + data_length += sprintf(buf + data_length, "%d ", + (int)bf_global->fb_type); + data_length += sprintf(buf + data_length, "%d ", + (int)bf_global->snr_threshold); + data_length += + sprintf(buf + data_length, "%d ", + (int)bf_global->sounding_interval); + data_length += sprintf(buf + data_length, "%d ", + (int)bf_global->bf_mode); + break; + case SET_GET_BF_PERIODICITY: + data_length += sprintf(buf + data_length, + "%02x:%02x:%02x:%02x:%02x:%02x", + bf_periodicity->peer_mac[0], + bf_periodicity->peer_mac[1], + bf_periodicity->peer_mac[2], + bf_periodicity->peer_mac[3], + bf_periodicity->peer_mac[4], + bf_periodicity->peer_mac[5]); + data_length += sprintf(buf + data_length, "%c", ' '); + data_length += sprintf(buf + data_length, "%d", + bf_periodicity->interval); + break; + case TX_BF_FOR_PEER_ENBL: + for (i = 0; i < bf_cfg.no_of_peers; i++) { + data_length += + sprintf(buf + data_length, + "%02x:%02x:%02x:%02x:%02x:%02x", + tx_bf_peer->peer_mac[0], + tx_bf_peer->peer_mac[1], + tx_bf_peer->peer_mac[2], + tx_bf_peer->peer_mac[3], + tx_bf_peer->peer_mac[4], + tx_bf_peer->peer_mac[5]); + data_length += + sprintf(buf + data_length, "%c", ' '); + data_length += sprintf(buf + data_length, "%d;", + tx_bf_peer->bf_enbl); + data_length += + sprintf(buf + data_length, "%d;", + tx_bf_peer->sounding_enbl); + data_length += sprintf(buf + data_length, "%d ", + tx_bf_peer->fb_type); + tx_bf_peer++; + } + break; + case SET_SNR_THR_PEER: + for (i = 0; i < bf_cfg.no_of_peers; i++) { + data_length += + sprintf(buf + data_length, + "%02x:%02x:%02x:%02x:%02x:%02x", + bf_snr->peer_mac[0], + bf_snr->peer_mac[1], + bf_snr->peer_mac[2], + bf_snr->peer_mac[3], + bf_snr->peer_mac[4], + bf_snr->peer_mac[5]); + data_length += + sprintf(buf + data_length, "%c", ';'); + data_length += sprintf(buf + data_length, "%d", + bf_snr->snr); + data_length += + sprintf(buf + data_length, "%c", ' '); + bf_snr++; + } + break; + } + buf[data_length] = '\0'; + } + + wrq->u.data.length = data_length; + if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Retrieve the scan response/beacon table + * + * @param wrq A pointer to iwreq structure + * @param scan_resp A pointer to mlan_scan_resp structure + * @param scan_start argument + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int moal_ret_get_scan_table_ioctl(struct iwreq *wrq, + mlan_scan_resp *scan_resp, + t_u32 scan_start) +{ + pBSSDescriptor_t pbss_desc, scan_table; + wlan_ioctl_get_scan_table_info *prsp_info; + int ret_code; + int ret_len; + int space_left; + t_u8 *pcurrent; + t_u8 *pbuffer_end; + t_u32 num_scans_done; + + ENTER(); + + num_scans_done = 0; + ret_code = MLAN_STATUS_SUCCESS; + + prsp_info = (wlan_ioctl_get_scan_table_info *)wrq->u.data.pointer; + pcurrent = (t_u8 *)prsp_info->scan_table_entry_buf; + + pbuffer_end = wrq->u.data.pointer + wrq->u.data.length - 1; + space_left = pbuffer_end - pcurrent; + scan_table = (BSSDescriptor_t *)(scan_resp->pscan_table); + + PRINTM(MINFO, "GetScanTable: scan_start req = %d\n", scan_start); + PRINTM(MINFO, "GetScanTable: length avail = %d\n", wrq->u.data.length); + + if (!scan_start) { + PRINTM(MINFO, "GetScanTable: get current BSS Descriptor\n"); + + /* Use to get current association saved descriptor */ + pbss_desc = scan_table; + + ret_code = wlan_get_scan_table_ret_entry(pbss_desc, &pcurrent, + &space_left); + + if (ret_code == MLAN_STATUS_SUCCESS) + num_scans_done = 1; + } else { + scan_start--; + + while (space_left && + (scan_start + num_scans_done < + scan_resp->num_in_scan_table) && + (ret_code == MLAN_STATUS_SUCCESS)) { + pbss_desc = + (scan_table + (scan_start + num_scans_done)); + + PRINTM(MINFO, + "GetScanTable: get current BSS Descriptor [%d]\n", + scan_start + num_scans_done); + + ret_code = wlan_get_scan_table_ret_entry( + pbss_desc, &pcurrent, &space_left); + + if (ret_code == MLAN_STATUS_SUCCESS) + num_scans_done++; + } + } + + prsp_info->scan_number = num_scans_done; + ret_len = pcurrent - (t_u8 *)wrq->u.data.pointer; + + wrq->u.data.length = ret_len; + + /* Return ret_code (EFAULT or E2BIG) in the case where no scan results + * were successfully encoded. + */ + LEAVE(); + return num_scans_done ? MLAN_STATUS_SUCCESS : ret_code; +} + +/** + * @brief Get scan table ioctl + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +static mlan_status woal_get_scan_table_ioctl(moal_private *priv, + struct iwreq *wrq) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_scan *scan = NULL; + int scan_start = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + scan = (mlan_ds_scan *)req->pbuf; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_GET; + + /* get the whole command from user */ + if (copy_from_user(&scan_start, wrq->u.data.pointer, + sizeof(scan_start))) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (scan_start > 0) + scan->sub_command = MLAN_OID_SCAN_NORMAL; + else + scan->sub_command = MLAN_OID_SCAN_GET_CURRENT_BSS; + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + status = moal_ret_get_scan_table_ioctl( + wrq, &scan->param.scan_resp, scan_start); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set user scan ext -- Async mode, without wait + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_set_user_scan_ext_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_user_scan_cfg scan_req; + ENTER(); + memset(&scan_req, 0x00, sizeof(scan_req)); + if (copy_from_user(&scan_req, wrq->u.data.pointer, + MIN(wrq->u.data.length, sizeof(scan_req)))) { + PRINTM(MINFO, "Copy from user failed\n"); + LEAVE(); + return -EFAULT; + } + if (MLAN_STATUS_FAILURE == woal_do_scan(priv, &scan_req)) + ret = -EFAULT; + LEAVE(); + return ret; +} + +/** + * @brief Set user scan + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +static mlan_status woal_set_user_scan_ioctl(moal_private *priv, + struct iwreq *wrq) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_scan *scan = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + union iwreq_data wrqu; + moal_handle *handle = priv->phandle; + + ENTER(); + + if (handle->scan_pending_on_block == MTRUE) { + PRINTM(MINFO, "scan already in processing...\n"); + LEAVE(); + return ret; + } + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->async_sem)) { + PRINTM(MERROR, "Acquire semaphore error, request_scan\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + handle->scan_pending_on_block = MTRUE; + handle->scan_priv = priv; + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan) + + wrq->u.data.length); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_USER_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + req->action = MLAN_ACT_SET; + + if (copy_from_user(scan->param.user_scan.scan_cfg_buf, + wrq->u.data.pointer, wrq->u.data.length)) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + /* 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); + } + +done: + handle->scan_pending_on_block = MFALSE; + handle->scan_priv = NULL; + MOAL_REL_SEMAPHORE(&handle->async_sem); + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +#ifdef SDIO +/** + * @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) { +#ifdef SDIO_MMC + 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 + if (sdio_read_ioreg(priv->phandle->card, func, reg, &data) < + 0) { + PRINTM(MERROR, + "sdio_read_ioreg: reading register 0x%X failed\n", + reg); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif + } else { +#ifdef SDIO_MMC + 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; + } +#else + if (sdio_write_ioreg(priv->phandle->card, func, reg, data) < + 0) { + PRINTM(MERROR, + "sdio_write_ioreg: writing register 0x%X failed\n", + reg); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif + } + + buf[0] = data; + wrq->u.data.length = 1; + if (copy_to_user(wrq->u.data.pointer, buf, sizeof(int))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Cmd53 read/write register + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int woal_cmd53rdwr_ioctl(moal_private *priv, struct iwreq *wrq) +{ + t_u8 *buf = NULL; + t_u8 rw, func, mode; + t_u16 blklen = 0, blknum = 0; + int reg = 0, pattern_len = 0, pos = 0, ret = MLAN_STATUS_SUCCESS; + t_u32 total_len = 0; + t_u8 *data = NULL; + + ENTER(); + + buf = kmalloc(WOAL_2K_BYTES, GFP_KERNEL); + if (!buf) { + PRINTM(MERROR, "Cannot allocate buffer for command!\n"); + ret = -EFAULT; + goto done; + } + data = kmalloc(WOAL_2K_BYTES, GFP_KERNEL); + if (!data) { + PRINTM(MERROR, "Cannot allocate buffer for command!\n"); + ret = -EFAULT; + goto done; + } + if (wrq->u.data.length > WOAL_2K_BYTES) { + PRINTM(MERROR, "Data lengh is too large!\n"); + ret = -EINVAL; + goto done; + } + if (copy_from_user(buf, wrq->u.data.pointer, wrq->u.data.length)) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + rw = buf[0]; /* read/write (0/1) */ + func = buf[1]; /* func (0/1/2) */ + reg = buf[5]; /* address */ + reg = (reg << 8) + buf[4]; + reg = (reg << 8) + buf[3]; + reg = (reg << 8) + buf[2]; + mode = buf[6]; /* byte mode/block mode (0/1) */ + blklen = buf[8]; /* block size */ + blklen = (blklen << 8) + buf[7]; + blknum = buf[10]; /* block number or byte number */ + blknum = (blknum << 8) + buf[9]; + + if (mode != BYTE_MODE) + mode = BLOCK_MODE; + total_len = (mode == BLOCK_MODE) ? blknum * blklen : blknum; + if (total_len > WOAL_2K_BYTES) { + PRINTM(MERROR, "Total data length is too large!\n"); + ret = -EINVAL; + goto done; + } + PRINTM(MINFO, + "CMD53 read/write, func = %d, addr = %#x, mode = %d, block size = %d, block(byte) number = %d\n", + func, reg, mode, blklen, blknum); + + if (!rw) { + sdio_claim_host( + ((struct sdio_mmc_card *)priv->phandle->card)->func); + if (sdio_readsb( + ((struct sdio_mmc_card *)priv->phandle->card)->func, + data, reg, total_len)) + PRINTM(MERROR, + "sdio_readsb: reading memory 0x%x failed\n", + reg); + sdio_release_host( + ((struct sdio_mmc_card *)priv->phandle->card)->func); + + if (copy_to_user(wrq->u.data.pointer, data, total_len)) { + PRINTM(MINFO, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = total_len; + } else { + pattern_len = wrq->u.data.length - 11; + if (pattern_len > total_len) + pattern_len = total_len; + memset(data, 0, WOAL_2K_BYTES); + + /* Copy/duplicate the pattern to data buffer */ + for (pos = 0; pos < total_len; pos++) + data[pos] = buf[11 + (pos % pattern_len)]; + + sdio_claim_host( + ((struct sdio_mmc_card *)priv->phandle->card)->func); + if (sdio_writesb( + ((struct sdio_mmc_card *)priv->phandle->card)->func, + reg, data, total_len)) + PRINTM(MERROR, + "sdio_writesb: writing memory 0x%x failed\n", + reg); + sdio_release_host( + ((struct sdio_mmc_card *)priv->phandle->card)->func); + } + +done: + kfree(buf); + kfree(data); + LEAVE(); + return ret; +} +#endif /* SDIO */ + +#ifdef SDIO +/** + * @brief Set SDIO Multi-point aggregation control parameters + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0/MLAN_STATUS_PENDING --success, otherwise fail + */ +static int woal_do_sdio_mpa_ctrl(moal_private *priv, struct iwreq *wrq) +{ + int data[6], data_length = wrq->u.data.length, copy_len; + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (sizeof(int) * wrq->u.data.length > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + memset(misc, 0, sizeof(mlan_ds_misc_cfg)); + + misc->sub_command = MLAN_OID_MISC_SDIO_MPA_CTRL; + req->req_id = MLAN_IOCTL_MISC_CFG; + + /* Get the values first, then modify these values if + * user had modified them */ + + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); + ret = -EFAULT; + goto done; + } + + if (data_length == 0) { + data[0] = misc->param.mpa_ctrl.tx_enable; + data[1] = misc->param.mpa_ctrl.rx_enable; + data[2] = misc->param.mpa_ctrl.tx_buf_size; + data[3] = misc->param.mpa_ctrl.rx_buf_size; + data[4] = misc->param.mpa_ctrl.tx_max_ports; + data[5] = misc->param.mpa_ctrl.rx_max_ports; + + PRINTM(MINFO, "Get Param: %d %d %d %d %d %d\n", data[0], + data[1], data[2], data[3], data[4], data[5]); + + if (copy_to_user(wrq->u.data.pointer, data, sizeof(data))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = ARRAY_SIZE(data); + goto done; + } + + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MINFO, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + switch (data_length) { + case 6: + misc->param.mpa_ctrl.rx_max_ports = data[5]; + /* fall through */ + case 5: + misc->param.mpa_ctrl.tx_max_ports = data[4]; + /* fall through */ + case 4: + misc->param.mpa_ctrl.rx_buf_size = data[3]; + /* fall through */ + case 3: + misc->param.mpa_ctrl.tx_buf_size = data[2]; + /* fall through */ + case 2: + misc->param.mpa_ctrl.rx_enable = data[1]; + /* fall through */ + case 1: + /* Set cmd */ + req->action = MLAN_ACT_SET; + + PRINTM(MINFO, "Set Param: %d %d %d %d %d %d\n", data[0], + data[1], data[2], data[3], data[4], data[5]); + + misc->param.mpa_ctrl.tx_enable = data[0]; + break; + default: + PRINTM(MERROR, "Default case error\n"); + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "woal_request_ioctl returned %d\n", ret); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @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 data[7], copy_len; + mlan_ds_scan *scan = NULL; + mlan_ioctl_req *req = NULL; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + int arg_len = 0; + + ENTER(); + arg_len = 7; + if (data_length > arg_len) { + PRINTM(MERROR, "Too much arguments\n"); + LEAVE(); + return -EINVAL; + } + copy_len = sizeof(int) * data_length; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_scan)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + scan = (mlan_ds_scan *)req->pbuf; + scan->sub_command = MLAN_OID_SCAN_CONFIG; + req->req_id = MLAN_IOCTL_SCAN; + memset(data, 0, sizeof(data)); + + if (data_length) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data[0] < 0) || (data[0] > MLAN_SCAN_TYPE_PASSIVE)) { + PRINTM(MERROR, "Invalid argument for scan type\n"); + ret = -EINVAL; + goto done; + } + if ((data[1] < 0) || (data[1] > MLAN_SCAN_MODE_ANY)) { + PRINTM(MERROR, "Invalid argument for scan mode\n"); + ret = -EINVAL; + goto done; + } + if ((data[2] < 0) || (data[2] > MAX_PROBES)) { + PRINTM(MERROR, "Invalid argument for scan probes\n"); + ret = -EINVAL; + goto done; + } + if (((data[3] < 0) || + (data[3] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || + ((data[4] < 0) || + (data[4] > MRVDRV_MAX_ACTIVE_SCAN_CHAN_TIME)) || + ((data[5] < 0) || + (data[5] > MRVDRV_MAX_PASSIVE_SCAN_CHAN_TIME))) { + PRINTM(MERROR, "Invalid argument for scan time\n"); + ret = -EINVAL; + goto done; + } + if ((data[6] < 0) || (data[6] > 1)) { + PRINTM(MERROR, "Invalid argument for extended scan\n"); + ret = -EINVAL; + goto done; + } + req->action = MLAN_ACT_SET; + moal_memcpy_ext(priv->phandle, &scan->param.scan_cfg, data, + sizeof(data), sizeof(scan->param.scan_cfg)); + } else + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!data_length) { + moal_memcpy_ext(priv->phandle, data, &scan->param.scan_cfg, + sizeof(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 = arg_len; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get PS configuration parameters + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_set_get_ps_cfg(moal_private *priv, struct iwreq *wrq) +{ + int data[7], copy_len, ret = 0; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ioctl_req *req = NULL; + int allowed = 3; + int i = 3; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + allowed++; /* For beacon missing timeout parameter */ + allowed += 2; /* For delay to PS and PS mode parameters */ + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + if (data_length > allowed) { + ret = -EINVAL; + goto done; + } + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_CFG; + req->req_id = MLAN_IOCTL_PM_CFG; + memset(data, 0, sizeof(data)); + + if (data_length) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((data[0] < PS_NULL_DISABLE)) { + PRINTM(MERROR, + "Invalid argument for PS null interval\n"); + ret = -EINVAL; + goto done; + } + if ((data[1] != MRVDRV_IGNORE_MULTIPLE_DTIM) && + (data[1] != MRVDRV_MATCH_CLOSEST_DTIM) && + ((data[1] < MRVDRV_MIN_MULTIPLE_DTIM) || + (data[1] > MRVDRV_MAX_MULTIPLE_DTIM))) { + PRINTM(MERROR, "Invalid argument for multiple DTIM\n"); + ret = -EINVAL; + goto done; + } + + if ((data[2] < MRVDRV_MIN_LISTEN_INTERVAL) && + (data[2] != MRVDRV_LISTEN_INTERVAL_DISABLE)) { + PRINTM(MERROR, + "Invalid argument for listen interval\n"); + ret = -EINVAL; + goto done; + } + + if ((data[i] != DISABLE_BCN_MISS_TO) && + ((data[i] < MIN_BCN_MISS_TO) || + (data[i] > MAX_BCN_MISS_TO))) { + PRINTM(MERROR, + "Invalid argument for beacon miss timeout\n"); + ret = -EINVAL; + goto done; + } + i++; + if (data_length < allowed - 1) + data[i] = DELAY_TO_PS_UNCHANGED; + else if ((data[i] < MIN_DELAY_TO_PS) || + (data[i] > MAX_DELAY_TO_PS)) { + PRINTM(MERROR, "Invalid argument for delay to PS\n"); + ret = -EINVAL; + goto done; + } + i++; + if ((data[i] != PS_MODE_UNCHANGED) && + (data[i] != PS_MODE_AUTO) && (data[i] != PS_MODE_POLL) && + (data[i] != PS_MODE_NULL)) { + PRINTM(MERROR, "Invalid argument for PS mode\n"); + ret = -EINVAL; + goto done; + } + i++; + req->action = MLAN_ACT_SET; + moal_memcpy_ext(priv->phandle, &pm_cfg->param.ps_cfg, data, + sizeof(data), sizeof(pm_cfg->param.ps_cfg)); + } else + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, data, &pm_cfg->param.ps_cfg, + MIN((sizeof(int) * allowed), + sizeof(pm_cfg->param.ps_cfg)), + sizeof(data)); + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * allowed)) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = allowed; + + if (req->action == MLAN_ACT_SET) { + pm_cfg = (mlan_ds_pm_cfg *)req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_IEEE_PS; + pm_cfg->param.ps_mode = 1; + req->req_id = MLAN_IOCTL_PM_CFG; + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to send an ADDTS TSPEC + * + * Receive a ADDTS command from the application. The command structure + * contains a TSPEC and timeout in milliseconds. The timeout is performed + * in the firmware after the ADDTS command frame is sent. + * + * The TSPEC is received in the API as an opaque block. The firmware will + * send the entire data block, including the bytes after the TSPEC. This + * is done to allow extra IEs to be packaged with the TSPEC in the ADDTS + * action frame. + * + * The IOCTL structure contains two return fields: + * - The firmware command result, which indicates failure and timeouts + * - The IEEE Status code which contains the corresponding value from + * any ADDTS response frame received. + * + * In addition, the opaque TSPEC data block passed in is replaced with the + * TSPEC received in the ADDTS response frame. In case of failure, the + * AP may modify the TSPEC on return and in the case of success, the + * medium time is returned as calculated by the AP. Along with the TSPEC, + * any IEs that are sent in the ADDTS response are also returned and can be + * parsed using the IOCTL length as an indicator of extra elements. + * + * The return value to the application layer indicates a driver execution + * success or failure. A successful return could still indicate a firmware + * failure or AP negotiation failure via the commandResult field copied + * back to the application. + * + * @param priv Pointer to the mlan_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_addts_req_t struct for this ADDTS request + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int woal_wmm_addts_req_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *cfg = NULL; + wlan_ioctl_wmm_addts_req_t addts_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_ADDTS; + + memset(&addts_ioctl, 0x00, sizeof(addts_ioctl)); + + if (wrq->u.data.length) { + if (copy_from_user(&addts_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(addts_ioctl)))) { + PRINTM(MERROR, "TSPEC: ADDTS copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + cfg->param.addts.timeout = addts_ioctl.timeout_ms; + cfg->param.addts.ie_data_len = addts_ioctl.ie_data_len; + + if (cfg->param.addts.ie_data_len > + sizeof(cfg->param.addts.ie_data)) { + PRINTM(MERROR, "IE data length too large\n"); + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, cfg->param.addts.ie_data, + addts_ioctl.ie_data, + cfg->param.addts.ie_data_len, + sizeof(cfg->param.addts.ie_data)); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + addts_ioctl.cmd_result = cfg->param.addts.result; + addts_ioctl.ieee_status_code = + (t_u8)cfg->param.addts.status_code; + addts_ioctl.ie_data_len = cfg->param.addts.ie_data_len; + + moal_memcpy_ext(priv->phandle, addts_ioctl.ie_data, + cfg->param.addts.ie_data, + cfg->param.addts.ie_data_len, + sizeof(addts_ioctl.ie_data)); + + wrq->u.data.length = + (sizeof(addts_ioctl) - sizeof(addts_ioctl.ie_data) + + cfg->param.addts.ie_data_len); + + if (copy_to_user(wrq->u.data.pointer, &addts_ioctl, + wrq->u.data.length)) { + PRINTM(MERROR, "TSPEC: ADDTS copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to send a DELTS TSPEC + * + * Receive a DELTS command from the application. The command structure + * contains a TSPEC and reason code along with space for a command result + * to be returned. The information is packaged is sent to the wlan_cmd.c + * firmware command prep and send routines for execution in the firmware. + * + * The reason code is not used for WMM implementations but is indicated in + * the 802.11e specification. + * + * The return value to the application layer indicates a driver execution + * success or failure. A successful return could still indicate a firmware + * failure via the cmd_result field copied back to the application. + * + * @param priv Pointer to the mlan_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_delts_req_t struct for this DELTS request + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int woal_wmm_delts_req_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *cfg = NULL; + wlan_ioctl_wmm_delts_req_t delts_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + cfg = (mlan_ds_wmm_cfg *)req->pbuf; + cfg->sub_command = MLAN_OID_WMM_CFG_DELTS; + + memset(&delts_ioctl, 0x00, sizeof(delts_ioctl)); + + if (wrq->u.data.length) { + if (copy_from_user(&delts_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(delts_ioctl)))) { + PRINTM(MERROR, "TSPEC: DELTS copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + cfg->param.delts.status_code = + (t_u32)delts_ioctl.ieee_reason_code; + cfg->param.delts.ie_data_len = (t_u8)delts_ioctl.ie_data_len; + + if ((cfg->param.delts.ie_data_len) > + sizeof(cfg->param.delts.ie_data)) { + PRINTM(MERROR, "IE data length too large\n"); + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, cfg->param.delts.ie_data, + delts_ioctl.ie_data, + cfg->param.delts.ie_data_len, + sizeof(cfg->param.delts.ie_data)); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Return the firmware command result back to the application + * layer */ + delts_ioctl.cmd_result = cfg->param.delts.result; + wrq->u.data.length = sizeof(delts_ioctl); + + if (copy_to_user(wrq->u.data.pointer, &delts_ioctl, + wrq->u.data.length)) { + PRINTM(MERROR, "TSPEC: DELTS copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get/set a specified AC Queue's parameters + * + * Receive a AC Queue configuration command which is used to get, set, or + * default the parameters associated with a specific WMM AC Queue. + * + * @param priv Pointer to the mlan_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_queue_config_t struct + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int woal_wmm_queue_config_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + mlan_ds_wmm_queue_config *pqcfg = NULL; + wlan_ioctl_wmm_queue_config_t qcfg_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_CONFIG; + + memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); + pqcfg = (mlan_ds_wmm_queue_config *)&pwmm->param.q_cfg; + + if (wrq->u.data.length) { + if (copy_from_user(&qcfg_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(qcfg_ioctl)))) { + PRINTM(MERROR, "QCONFIG: copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + pqcfg->action = qcfg_ioctl.action; + pqcfg->access_category = qcfg_ioctl.access_category; + pqcfg->msdu_lifetime_expiry = qcfg_ioctl.msdu_lifetime_expiry; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + memset(&qcfg_ioctl, 0x00, sizeof(qcfg_ioctl)); + qcfg_ioctl.action = pqcfg->action; + qcfg_ioctl.access_category = pqcfg->access_category; + qcfg_ioctl.msdu_lifetime_expiry = pqcfg->msdu_lifetime_expiry; + wrq->u.data.length = sizeof(qcfg_ioctl); + + if (copy_to_user(wrq->u.data.pointer, &qcfg_ioctl, + wrq->u.data.length)) { + PRINTM(MERROR, "QCONFIG: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get and start/stop queue stats on a WMM AC + * + * Receive a AC Queue statistics command from the application for a specific + * WMM AC. The command can: + * - Turn stats on + * - Turn stats off + * - Collect and clear the stats + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_queue_stats_t struct + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int woal_wmm_queue_stats_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + mlan_ds_wmm_queue_stats *pqstats = NULL; + wlan_ioctl_wmm_queue_stats_t qstats_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATS; + + memset(&qstats_ioctl, 0x00, sizeof(qstats_ioctl)); + pqstats = (mlan_ds_wmm_queue_stats *)&pwmm->param.q_stats; + + if (wrq->u.data.length) { + if (copy_from_user(&qstats_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(qstats_ioctl)))) { + PRINTM(MERROR, "QSTATS: copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + moal_memcpy_ext(priv->phandle, (void *)pqstats, + (void *)&qstats_ioctl, sizeof(qstats_ioctl), + sizeof(qstats_ioctl)); + PRINTM(MINFO, "QSTATS: IOCTL [%d,%d]\n", qstats_ioctl.action, + qstats_ioctl.user_priority); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&qstats_ioctl, 0x00, sizeof(qstats_ioctl)); + moal_memcpy_ext(priv->phandle, (void *)&qstats_ioctl, + (void *)pqstats, sizeof(qstats_ioctl), + sizeof(qstats_ioctl)); + wrq->u.data.length = sizeof(qstats_ioctl); + + if (copy_to_user(wrq->u.data.pointer, &qstats_ioctl, + wrq->u.data.length)) { + PRINTM(MERROR, "QSTATS: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the status of the WMM queues + * + * Return the following information for each WMM AC: + * - WMM IE Acm Required + * - Firmware Flow Required + * - Firmware Flow Established + * - Firmware Queue Enabled + * - Firmware Delivery Enabled + * - Firmware Trigger Enabled + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_queue_status_t struct for request + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int woal_wmm_queue_status_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + wlan_ioctl_wmm_queue_status_t qstatus_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_QUEUE_STATUS; + + if (wrq->u.data.length == sizeof(qstatus_ioctl)) { + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&qstatus_ioctl, 0x00, sizeof(qstatus_ioctl)); + moal_memcpy_ext(priv->phandle, (void *)&qstatus_ioctl, + (void *)&pwmm->param.q_status, + sizeof(qstatus_ioctl), sizeof(qstatus_ioctl)); + wrq->u.data.length = sizeof(qstatus_ioctl); + } else { + wrq->u.data.length = 0; + } + + if (copy_to_user(wrq->u.data.pointer, &qstatus_ioctl, + wrq->u.data.length)) { + PRINTM(MERROR, "QSTATUS: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the status of the WMM Traffic Streams + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure containing the + * wlan_ioctl_wmm_ts_status_t struct for request + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int woal_wmm_ts_status_ioctl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_wmm_cfg *pwmm = NULL; + wlan_ioctl_wmm_ts_status_t ts_status_ioctl; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wmm_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_WMM_CFG; + pwmm = (mlan_ds_wmm_cfg *)req->pbuf; + pwmm->sub_command = MLAN_OID_WMM_CFG_TS_STATUS; + + memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); + + if (wrq->u.data.length == sizeof(ts_status_ioctl)) { + if (copy_from_user(&ts_status_ioctl, wrq->u.data.pointer, + MIN(wrq->u.data.length, + sizeof(ts_status_ioctl)))) { + PRINTM(MERROR, "TS_STATUS: copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + memset(&pwmm->param.ts_status, 0x00, sizeof(ts_status_ioctl)); + pwmm->param.ts_status.tid = ts_status_ioctl.tid; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + memset(&ts_status_ioctl, 0x00, sizeof(ts_status_ioctl)); + moal_memcpy_ext(priv->phandle, (void *)&ts_status_ioctl, + (void *)&pwmm->param.ts_status, + sizeof(ts_status_ioctl), + sizeof(ts_status_ioctl)); + wrq->u.data.length = sizeof(ts_status_ioctl); + } else { + wrq->u.data.length = 0; + } + + if (copy_to_user(wrq->u.data.pointer, &ts_status_ioctl, + wrq->u.data.length)) { + PRINTM(MERROR, "TS_STATUS: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Private IOCTL entry to get the By-passed TX packet from upper layer + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure containing the packet + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int woal_bypassed_packet_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + struct sk_buff *skb = NULL; + struct ethhdr *eth; + t_u16 moreLen = 0, copyLen = 0; + ENTER(); + +#define MLAN_BYPASS_PKT_EXTRA_OFFSET (4) + + copyLen = wrq->u.data.length; + moreLen = MLAN_MIN_DATA_HEADER_LEN + MLAN_BYPASS_PKT_EXTRA_OFFSET + + sizeof(mlan_buffer); + + skb = alloc_skb(copyLen + moreLen, GFP_KERNEL); + if (skb == NULL) { + PRINTM(MERROR, "kmalloc no memory !!\n"); + LEAVE(); + return -ENOMEM; + } + + skb_reserve(skb, moreLen); + + if (copy_from_user(skb_put(skb, copyLen), wrq->u.data.pointer, + copyLen)) { + PRINTM(MERROR, "PortBlock: copy from user failed\n"); + dev_kfree_skb_any(skb); + ret = -EFAULT; + goto done; + } + + eth = (struct ethhdr *)skb->data; + eth->h_proto = __constant_htons(eth->h_proto); + skb->dev = priv->netdev; + + HEXDUMP("Bypass TX Data", skb->data, MIN(skb->len, 100)); + + woal_hard_start_xmit(skb, priv->netdev); +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get auth type + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_auth_type(moal_private *priv, struct iwreq *wrq) +{ + int auth_type; + t_u32 auth_mode; + int ret = 0; + + ENTER(); + if (wrq->u.data.length == 0) { + if (MLAN_STATUS_SUCCESS != + woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) { + ret = -EFAULT; + goto done; + } + auth_type = auth_mode; + if (copy_to_user(wrq->u.data.pointer, &auth_type, + sizeof(auth_type))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } else { + if (copy_from_user(&auth_type, wrq->u.data.pointer, + sizeof(auth_type))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MINFO, "SET: auth_type %d\n", auth_type); + if (((auth_type < MLAN_AUTH_MODE_OPEN) || + (auth_type > MLAN_AUTH_MODE_SHARED)) && + (auth_type != MLAN_AUTH_MODE_AUTO)) { + ret = -EINVAL; + goto done; + } + auth_mode = auth_type; + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) { + ret = -EFAULT; + goto done; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Port Control mode + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_port_ctrl(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_PORT_CTRL_ENABLED; + req->req_id = MLAN_IOCTL_SEC_CFG; + + if (wrq->u.data.length) { + if (copy_from_user(&sec->param.port_ctrl_enabled, + wrq->u.data.pointer, sizeof(int)) != 0) { + PRINTM(MERROR, "port_ctrl:Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + } else { + req->action = MLAN_ACT_GET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!wrq->u.data.length) { + if (copy_to_user(wrq->u.data.pointer, + &sec->param.port_ctrl_enabled, sizeof(int))) { + PRINTM(MERROR, "port_ctrl:Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get DFS Testing settings + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_dfs_testing(moal_private *priv, struct iwreq *wrq) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + int ret = 0; + int data[4], copy_len; + int data_length = wrq->u.data.length; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + ds_11hcfg->sub_command = MLAN_OID_11H_DFS_TESTING; + req->req_id = MLAN_IOCTL_11H_CFG; + + if (!data_length) { + req->action = MLAN_ACT_GET; + } else if (data_length == 4) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if ((unsigned)data[0] > 0xFFFF) { + PRINTM(MERROR, "The maximum user CAC is 65535 msec.\n"); + ret = -EINVAL; + goto done; + } + if ((unsigned)data[1] > 0xFFFF) { + PRINTM(MERROR, "The maximum user NOP is 65535 sec.\n"); + ret = -EINVAL; + goto done; + } + if ((unsigned)data[3] > 0xFF) { + PRINTM(MERROR, + "The maximum user fixed channel is 255.\n"); + ret = -EINVAL; + goto done; + } + ds_11hcfg->param.dfs_testing.usr_cac_period_msec = + (t_u16)data[0]; + ds_11hcfg->param.dfs_testing.usr_nop_period_sec = + (t_u16)data[1]; + ds_11hcfg->param.dfs_testing.usr_no_chan_change = + data[2] ? 1 : 0; + ds_11hcfg->param.dfs_testing.usr_fixed_new_chan = (t_u8)data[3]; + priv->phandle->cac_period_jiffies = (t_u16)data[0] * HZ / 1000; + req->action = MLAN_ACT_SET; + } else { + PRINTM(MERROR, "Invalid number of args!\n"); + ret = -EINVAL; + goto done; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!data_length) { + data[0] = ds_11hcfg->param.dfs_testing.usr_cac_period_msec; + data[1] = ds_11hcfg->param.dfs_testing.usr_nop_period_sec; + data[2] = ds_11hcfg->param.dfs_testing.usr_no_chan_change; + data[3] = ds_11hcfg->param.dfs_testing.usr_fixed_new_chan; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int) * 4)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 4; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Mgmt Frame passthru mask + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_mgmt_frame_passthru_ctrl(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0, data_length = wrq->u.data.length; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *mgmt_cfg = NULL; + int mask = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 1) { + PRINTM(MERROR, "Invalid no of arguments!\n"); + ret = -EINVAL; + goto done; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + mgmt_cfg = (mlan_ds_misc_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_MISC_CFG; + mgmt_cfg->sub_command = MLAN_OID_MISC_RX_MGMT_IND; + + if (data_length) { /* SET */ + if (copy_from_user(&mask, wrq->u.data.pointer, sizeof(int))) { + PRINTM(MERROR, "copy from user failed\n"); + ret = -EFAULT; + goto done; + } + mgmt_cfg->param.mgmt_subtype_mask = mask; + req->action = MLAN_ACT_SET; + } else { + req->action = MLAN_ACT_GET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + mask = mgmt_cfg->param.mgmt_subtype_mask; + if (copy_to_user(wrq->u.data.pointer, &mask, sizeof(int))) { + ret = -EFAULT; + goto done; + } + wrq->u.data.length = 1; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get CFP table codes + * + * @param priv Pointer to the moal_private driver data struct + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_cfp_code(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + int data[2], copy_len; + int data_length = wrq->u.data.length; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ds_misc_cfp_code *cfp_code = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (data_length > 2) { + PRINTM(MERROR, "Invalid number of argument!\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + cfp_code = &misc_cfg->param.cfp_code; + misc_cfg->sub_command = MLAN_OID_MISC_CFP_CODE; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (!data_length) { + req->action = MLAN_ACT_GET; + } else { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + cfp_code->cfp_code_bg = data[0]; + if (data_length == 2) + cfp_code->cfp_code_a = data[1]; + req->action = MLAN_ACT_SET; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!data_length) { + data[0] = cfp_code->cfp_code_bg; + data[1] = cfp_code->cfp_code_a; + data_length = 2; + if (copy_to_user(wrq->u.data.pointer, &data, + sizeof(int) * data_length)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + goto done; + } + wrq->u.data.length = data_length; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Tx/Rx antenna + * + * @param priv A pointer to moal_private structure + * @param wrq A pointer to iwreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_set_get_tx_rx_ant(moal_private *priv, struct iwreq *wrq) +{ + int ret = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + int data[3] = {0}; + mlan_status status = MLAN_STATUS_SUCCESS; + int copy_len; + + ENTER(); + + if (wrq->u.data.length > 2) { + PRINTM(MERROR, "Invalid number of argument!\n"); + ret = -EFAULT; + goto done; + } + if (wrq->u.data.length * sizeof(int) > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EFAULT; + goto done; + } + copy_len = MIN(sizeof(data), wrq->u.data.length * sizeof(int)); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + if (wrq->u.data.length) { + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + if (priv->phandle->feature_control & FEATURE_CTRL_STREAM_2X2) { + 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]; + } else { + radio->param.ant_cfg_1x1.antenna = data[0]; + if (wrq->u.data.length == 2) + radio->param.ant_cfg_1x1.evaluate_time = + data[1]; + } + req->action = MLAN_ACT_SET; + } else + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (!wrq->u.data.length) { + wrq->u.data.length = 1; + if (priv->phandle->feature_control & FEATURE_CTRL_STREAM_2X2) { + 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; + } else { + data[0] = (int)radio->param.ant_cfg_1x1.antenna; + data[1] = (int)radio->param.ant_cfg_1x1.evaluate_time; + data[2] = (int)radio->param.ant_cfg_1x1.current_antenna; + if (data[0] == 0xffff && data[2] > 0) + wrq->u.data.length = 3; + else if (data[0] == 0xffff) + wrq->u.data.length = 2; + } + if (copy_to_user(wrq->u.data.pointer, data, + wrq->u.data.length * sizeof(int))) { + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Configure gpio independent reset + * + * @param priv A pointer to moal_private structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_ind_rst_ioctl(moal_private *priv, struct iwreq *wrq) +{ + int data[2], data_length = wrq->u.data.length, copy_len; + int ret = 0; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (sizeof(int) * wrq->u.data.length > sizeof(data)) { + PRINTM(MERROR, "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + copy_len = MIN(sizeof(data), sizeof(int) * data_length); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + memset(misc, 0, sizeof(mlan_ds_misc_cfg)); + + misc->sub_command = MLAN_OID_MISC_IND_RST_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + + if (data_length == 0) { + req->action = MLAN_ACT_GET; + } else if ((data_length == 1) || (data_length == 2)) { + req->action = MLAN_ACT_SET; + + if (copy_from_user(data, wrq->u.data.pointer, copy_len)) { + /* copy_from_user failed */ + PRINTM(MERROR, "S_PARAMS: copy from user failed\n"); + ret = -EINVAL; + goto done; + } + + /* ir_mode */ + if (data[0] < 0 || data[0] > 2) { + PRINTM(MERROR, "Invalid ir mode parameter (0/1/2)!\n"); + ret = -EINVAL; + goto done; + } + misc->param.ind_rst_cfg.ir_mode = data[0]; + + /* gpio_pin */ + if (data_length == 2) { + if ((data[1] != 0xFF) && (data[1] < 0)) { + PRINTM(MERROR, "Invalid gpio pin no !\n"); + ret = -EINVAL; + goto done; + } + misc->param.ind_rst_cfg.gpio_pin = data[1]; + } + + } else { + ret = -EINVAL; + goto done; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + data[0] = misc->param.ind_rst_cfg.ir_mode; + data[1] = misc->param.ind_rst_cfg.gpio_pin; + wrq->u.data.length = 2; + + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * wrq->u.data.length)) { + PRINTM(MERROR, "QCONFIG: copy to user failed\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd Command + * + * @return 0 --success, otherwise fail + */ +int woal_wext_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iwreq *wrq = (struct iwreq *)req; + int ret = 0; + + if (!IS_STA_WEXT(priv->phandle->params.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; +#ifdef USB +#ifdef CONFIG_USB_SUSPEND + case WOAL_USB_SUSPEND: + ret = woal_enter_usb_suspend(priv->phandle); + break; + case WOAL_USB_RESUME: + ret = woal_exit_usb_suspend(priv->phandle); + break; +#endif /* CONFIG_USB_SUSPEND */ +#endif + 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; +#ifndef OPCHAN + case WOAL_SET_GET_WWS_CFG: + ret = woal_wws_cfg(priv, wrq); + break; +#endif + 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; +#ifdef 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; + case WOAL_CFG_HOTSPOT: + ret = woal_cfg_hotspot(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_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; +#ifdef SDIO + 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_SDIO_MPA_CTRL: + ret = woal_do_sdio_mpa_ctrl(priv, wrq); + break; +#endif + 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; + case WOAL_SLEEP_PARAMS: + ret = woal_sleep_params_ioctl(priv, wrq); + break; + case WOAL_DFS_TESTING: + ret = woal_dfs_testing(priv, wrq); + break; + case WOAL_MGMT_FRAME_CTRL: + ret = woal_mgmt_frame_passthru_ctrl(priv, wrq); + break; + case WOAL_CFP_CODE: + ret = woal_cfp_code(priv, wrq); + break; + case WOAL_SET_GET_TX_RX_ANT: + ret = woal_set_get_tx_rx_ant(priv, wrq); + break; + case WOAL_IND_RST_CFG: + ret = woal_ind_rst_ioctl(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + + case WOALGETLOG: + ret = woal_get_log(priv, wrq); + break; + + case WOAL_SET_GET_256_CHAR: + switch (wrq->u.data.flags) { + case WOAL_PASSPHRASE: + ret = woal_passphrase(priv, wrq); + break; + case WOAL_GET_KEY: + ret = woal_get_key_ioctl(priv, wrq); + break; + case WOAL_ASSOCIATE: + ret = woal_associate_ssid_bssid(priv, wrq); + break; + case WOAL_WMM_QUEUE_STATUS: + ret = woal_wmm_queue_status_ioctl(priv, wrq); + break; + + case WOAL_WMM_TS_STATUS: + ret = woal_wmm_ts_status_ioctl(priv, wrq); + break; + case WOAL_IP_ADDRESS: + ret = woal_set_get_ip_addr(priv, wrq); + break; + case WOAL_TX_BF_CFG: + ret = woal_tx_bf_cfg_ioctl(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + + case WOAL_SETADDR_GETNONE: + switch ((int)wrq->u.data.flags) { + case WOAL_DEAUTH: + ret = woal_deauth(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + + case WOAL_SETNONE_GETTWELVE_CHAR: + /* + * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is + * in flags of iwreq structure, otherwise it will be in + * mode member of iwreq structure. + */ + switch ((int)wrq->u.data.flags) { + case WOAL_WPS_SESSION: + ret = woal_wps_cfg_ioctl(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + case WOAL_SETNONE_GET_FOUR_INT: + switch ((int)wrq->u.data.flags) { + case WOAL_DATA_RATE: + ret = woal_get_txrx_rate(priv, wrq); + break; + case WOAL_ESUPP_MODE: + ret = woal_get_esupp_mode(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + + case WOAL_SET_GET_64_INT: + switch ((int)wrq->u.data.flags) { + case WOAL_ECL_SYS_CLOCK: + ret = woal_ecl_sys_clock(priv, wrq); + break; + } + + break; + + case WOAL_HOST_CMD: + ret = woal_host_command(priv, wrq); + break; + case WOAL_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) { +#ifdef SDIO + case WOAL_CMD_53RDWR: + ret = woal_cmd53rdwr_ioctl(priv, wrq); + break; +#endif + case WOAL_SET_USER_SCAN: + ret = woal_set_user_scan_ioctl(priv, wrq); + break; + case WOAL_GET_SCAN_TABLE: + ret = woal_get_scan_table_ioctl(priv, wrq); + break; + case WOAL_SET_USER_SCAN_EXT: + ret = woal_set_user_scan_ext_ioctl(priv, wrq); + break; + case WOAL_WMM_ADDTS: + ret = woal_wmm_addts_req_ioctl(priv, wrq); + break; + case WOAL_WMM_DELTS: + ret = woal_wmm_delts_req_ioctl(priv, wrq); + break; + case WOAL_WMM_QUEUE_CONFIG: + ret = woal_wmm_queue_config_ioctl(priv, wrq); + break; + case WOAL_WMM_QUEUE_STATS: + ret = woal_wmm_queue_stats_ioctl(priv, wrq); + break; + case WOAL_BYPASSED_PACKET: + ret = woal_bypassed_packet_ioctl(priv, wrq); + break; + default: + ret = -EINVAL; + break; + } + break; + +#ifdef UAP_WEXT + case WOAL_FROYO_START: + break; + case WOAL_FROYO_WL_FW_RELOAD: + break; + case WOAL_FROYO_STOP: + if (IS_UAP_WEXT(priv->phandle->params.cfg80211_wext) && + MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE)) { + ret = -EFAULT; + } + break; +#endif + default: + ret = -EINVAL; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get data rates + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param m_rates A pointer to moal_802_11_rates structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_get_data_rates(moal_private *priv, t_u8 wait_option, + moal_802_11_rates *m_rates) +{ + int ret = 0; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + rate = (mlan_ds_rate *)req->pbuf; + rate->sub_command = MLAN_OID_SUPPORTED_RATES; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (m_rates) + m_rates->num_of_rates = woal_copy_rates( + m_rates->rates, m_rates->num_of_rates, + rate->param.rates, MLAN_SUPPORTED_RATES); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Get channel list + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param chan_list A pointer to mlan_chan_list structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_get_channel_list(moal_private *priv, t_u8 wait_option, + mlan_chan_list *chan_list) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_CHANNEL_LIST; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (chan_list) { + moal_memcpy_ext(priv->phandle, chan_list, + &bss->param.chanlist, + sizeof(mlan_chan_list), + sizeof(mlan_chan_list)); + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Handle get info resp + * + * @param priv Pointer to moal_private structure + * @param info Pointer to mlan_ds_get_info structure + * + * @return N/A + */ +void woal_ioctl_get_info_resp(moal_private *priv, mlan_ds_get_info *info) +{ + ENTER(); + switch (info->sub_command) { + case MLAN_OID_GET_STATS: + priv->w_stats.discard.fragment = info->param.stats.fcs_error; + priv->w_stats.discard.retries = info->param.stats.retry; + priv->w_stats.discard.misc = info->param.stats.ack_failure; + break; + case MLAN_OID_GET_SIGNAL: + if (info->param.signal.selector & BCN_RSSI_AVG_MASK) + priv->w_stats.qual.level = + info->param.signal.bcn_rssi_avg; + if (info->param.signal.selector & BCN_NF_AVG_MASK) + priv->w_stats.qual.noise = + info->param.signal.bcn_nf_avg; + break; + default: + break; + } + LEAVE(); +} + +/** + * @brief Handle get BSS resp + * + * @param priv Pointer to moal_private structure + * @param bss Pointer to mlan_ds_bss structure + * + * @return N/A + */ +void woal_ioctl_get_bss_resp(moal_private *priv, mlan_ds_bss *bss) +{ + t_u32 mode = 0; + + ENTER(); + + switch (bss->sub_command) { + case MLAN_OID_BSS_MODE: + if (bss->param.bss_mode == MLAN_BSS_MODE_INFRA) + mode = IW_MODE_INFRA; + else if (bss->param.bss_mode == MLAN_BSS_MODE_IBSS) + mode = IW_MODE_ADHOC; + else + mode = IW_MODE_AUTO; + priv->w_stats.status = mode; + break; + default: + break; + } + + LEAVE(); + return; +} diff --git a/mxm_wifiex/wlan_src/mlinux/moal_priv.h b/mxm_wifiex/wlan_src/mlinux/moal_priv.h new file mode 100644 index 0000000..22355f7 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_priv.h @@ -0,0 +1,485 @@ + +/** @file moal_priv.h + * + * @brief This file contains definition for extended private IOCTL call. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/31/2008: initial version +********************************************************/ + +#ifndef _WOAL_PRIV_H_ +#define _WOAL_PRIV_H_ + +/** 2K bytes */ +#define WOAL_2K_BYTES 2000 + +/** PRIVATE CMD ID */ +#define WOAL_IOCTL (SIOCIWFIRSTPRIV) /* 0x8BE0 defined in wireless.h */ + +/** Private command ID to set one int/get word char */ +#define WOAL_SETONEINT_GETWORDCHAR (WOAL_IOCTL + 1) +/** Private command ID to get version */ +#define WOAL_VERSION 1 +/** Private command ID to get extended version */ +#define WOAL_VEREXT 2 + +/** Private command ID to set/get none */ +#define WOAL_SETNONE_GETNONE (WOAL_IOCTL + 2) +/** Private command ID for warm reset */ +#define WOAL_WARMRESET 1 + +/** + * Linux Kernels later 3.9 use CONFIG_PM_RUNTIME instead of + * CONFIG_USB_SUSPEND + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +#ifdef CONFIG_PM +#ifndef CONFIG_USB_SUSPEND +#define CONFIG_USB_SUSPEND +#endif +#endif +#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */ +#ifdef CONFIG_PM_RUNTIME +#ifndef CONFIG_USB_SUSPEND +#define CONFIG_USB_SUSPEND +#endif +#endif +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */ +#endif + +#ifdef CONFIG_USB_SUSPEND +/** Private command ID for usb suspend */ +#define WOAL_USB_SUSPEND 2 +/** Private command ID for usb resume */ +#define WOAL_USB_RESUME 3 +#endif /* CONFIG_USB_SUSPEND */ +/** 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 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 +#ifdef SDIO +/** 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 +#endif +/** 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 +#ifdef SDIO +/** 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 +/** Private command ID to set/get dfs testing settings */ +#define WOAL_DFS_TESTING 33 +/** Private command ID to set/get CFP table codes */ +#define WOAL_CFP_CODE 34 +/** Private command ID to set/get tx/rx antenna */ +#define WOAL_SET_GET_TX_RX_ANT 35 +/** Private command ID to set/get management frame passthru mask */ +#define WOAL_MGMT_FRAME_CTRL 36 + +/** Private command ID to configure gpio independent reset */ +#define WOAL_IND_RST_CFG 37 + +/** Private command ID to set one int/get one int */ +#define WOAL_SETONEINT_GETONEINT (WOAL_IOCTL + 5) +/** Private command ID to set/get Tx rate */ +#define WOAL_SET_GET_TXRATE 1 +/** Private command ID to set/get region code */ +#define WOAL_SET_GET_REGIONCODE 2 +/** Private command ID to turn on/off radio */ +#define WOAL_SET_RADIO 3 +/** Private command ID to enable WMM */ +#define WOAL_WMM_ENABLE 4 +/** Private command ID to enable 802.11D */ +#define WOAL_11D_ENABLE 5 +/** Private command ID to set/get QoS configuration */ +#define WOAL_SET_GET_QOS_CFG 7 +#if defined(REASSOCIATION) +/** Private command ID to set/get reassociation setting */ +#define WOAL_SET_GET_REASSOC 9 +#endif /* REASSOCIATION */ +/** Private command ID for Updating Transmit buffer configration */ +#define WOAL_TXBUF_CFG 10 +/** Private command ID to set/get WWS mode */ +#define WOAL_SET_GET_WWS_CFG 12 +/** Private command ID to set/get sleep period */ +#define WOAL_SLEEP_PD 13 +/** Private command ID to set/get auth type */ +#define WOAL_AUTH_TYPE 18 +/** Private command ID to set/get port control */ +#define WOAL_PORT_CTRL 19 +#ifdef 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 set/get hs cfg param */ +#define WOAL_CFG_HOTSPOT 26 + +/** 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 +#define WOAL_ASSOCIATE 3 +/** Private command ID to get WMM queue status */ +#define WOAL_WMM_QUEUE_STATUS 4 +/** Private command ID to get Traffic stream status */ +#define WOAL_WMM_TS_STATUS 5 +#define WOAL_IP_ADDRESS 7 +/** Private command ID to set/get TX bemaforming */ +#define WOAL_TX_BF_CFG 8 +/** Private command ID to get PTK/GTK */ +#define WOAL_GET_KEY 9 + +/** Get log buffer size */ +#define GETLOG_BUFSIZE 1500 + +/** 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 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) + +#ifdef SDIO +/** Private command ID to read/write Command 53 */ +#define WOAL_CMD_53RDWR 2 +#endif + +/** Private command ID for setuserscan */ +#define WOAL_SET_USER_SCAN 3 +/** Private command ID for getscantable */ +#define WOAL_GET_SCAN_TABLE 4 +/** Private command ID for setuserscanext: async without wait */ +#define WOAL_SET_USER_SCAN_EXT 5 + +/** Private command ID to request ADDTS */ +#define WOAL_WMM_ADDTS 7 +/** Private command ID to request DELTS */ +#define WOAL_WMM_DELTS 8 +/** Private command ID to queue configuration */ +#define WOAL_WMM_QUEUE_CONFIG 9 +/** Private command ID to queue stats */ +#define WOAL_WMM_QUEUE_STATS 10 +/** Private command ID to Bypass auth packet */ +#define WOAL_BYPASSED_PACKET 11 + +#ifdef UAP_WEXT +/** The following command IDs are for Froyo app */ +/** Private command ID to start driver */ +#define WOAL_FROYO_START (WOAL_IOCTL + 28) +/** Private command ID to reload FW */ +#define WOAL_FROYO_WL_FW_RELOAD (WOAL_IOCTL + 29) +/** Private command ID to stop driver */ +#define WOAL_FROYO_STOP (WOAL_IOCTL + 30) +#endif + +/** + * iwpriv ioctl handlers + */ +static const struct iw_priv_args woal_private_args[] = { + {WOAL_SETONEINT_GETWORDCHAR, IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_CHAR | 128, ""}, + {WOAL_VERSION, IW_PRIV_TYPE_INT | 1, IW_PRIV_TYPE_CHAR | 128, + "version"}, + {WOAL_VEREXT, IW_PRIV_TYPE_INT | 1, IW_PRIV_TYPE_CHAR | 128, "verext"}, + {WOAL_SETNONE_GETNONE, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, ""}, + {WOAL_WARMRESET, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "warmreset"}, +#ifdef CONFIG_USB_SUSPEND + {WOAL_USB_SUSPEND, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "usbsuspend"}, + {WOAL_USB_RESUME, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "usbresume"}, +#endif /* CONFIG_USB_SUSPEND */ + {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"}, +#ifndef OPCHAN + {WOAL_SET_GET_WWS_CFG, IW_PRIV_TYPE_INT | 1, IW_PRIV_TYPE_INT | 1, + "wwscfg"}, +#endif +#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"}, +#ifdef 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_CFG_HOTSPOT, IW_PRIV_TYPE_INT | 1, IW_PRIV_TYPE_INT | 1, + "hotspotcfg"}, + {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_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"}, +#ifdef SDIO + {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"}, +#endif + {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"}, +#ifdef SDIO + {WOAL_SDIO_MPA_CTRL, IW_PRIV_TYPE_INT | 16, IW_PRIV_TYPE_INT | 16, + "mpactrl"}, +#endif + {WOAL_SLEEP_PARAMS, IW_PRIV_TYPE_INT | 16, IW_PRIV_TYPE_INT | 16, + "sleepparams"}, + {WOAL_DFS_TESTING, IW_PRIV_TYPE_INT | 16, IW_PRIV_TYPE_INT | 16, + "dfstesting"}, + {WOAL_MGMT_FRAME_CTRL, IW_PRIV_TYPE_INT | 16, IW_PRIV_TYPE_INT | 16, + "mgmtframectrl"}, + {WOAL_CFP_CODE, IW_PRIV_TYPE_INT | 16, IW_PRIV_TYPE_INT | 16, + "cfpcode"}, + {WOAL_SET_GET_TX_RX_ANT, IW_PRIV_TYPE_INT | 16, IW_PRIV_TYPE_INT | 16, + "antcfg"}, + {WOAL_IND_RST_CFG, IW_PRIV_TYPE_INT | 16, IW_PRIV_TYPE_INT | 16, + "indrstcfg"}, + {WOALGETLOG, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_CHAR | GETLOG_BUFSIZE, + "getlog"}, + {WOAL_SETADDR_GETNONE, IW_PRIV_TYPE_ADDR | 1, IW_PRIV_TYPE_NONE, ""}, + {WOAL_DEAUTH, IW_PRIV_TYPE_ADDR | 1, IW_PRIV_TYPE_NONE, "deauth"}, + {WOAL_SET_GET_256_CHAR, IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, ""}, + {WOAL_PASSPHRASE, IW_PRIV_TYPE_CHAR | 256, IW_PRIV_TYPE_CHAR | 256, + "passphrase"}, + {WOAL_GET_KEY, IW_PRIV_TYPE_CHAR | 256, IW_PRIV_TYPE_CHAR | 256, + "getkey"}, + {WOAL_ASSOCIATE, IW_PRIV_TYPE_CHAR | 256, IW_PRIV_TYPE_CHAR | 256, + "associate"}, + {WOAL_WMM_QUEUE_STATUS, IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, "qstatus"}, + {WOAL_WMM_TS_STATUS, IW_PRIV_TYPE_CHAR | 256, IW_PRIV_TYPE_CHAR | 256, + "ts_status"}, + {WOAL_IP_ADDRESS, IW_PRIV_TYPE_CHAR | 256, IW_PRIV_TYPE_CHAR | 256, + "ipaddr"}, + {WOAL_TX_BF_CFG, IW_PRIV_TYPE_CHAR | 256, IW_PRIV_TYPE_CHAR | 256, + "httxbfcfg"}, + {WOAL_SETNONE_GETTWELVE_CHAR, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_CHAR | 12, + ""}, + {WOAL_WPS_SESSION, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_CHAR | 12, + "wpssession"}, + {WOAL_SETNONE_GET_FOUR_INT, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_INT | 4, + ""}, + {WOAL_DATA_RATE, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_INT | 4, + "getdatarate"}, + {WOAL_ESUPP_MODE, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_INT | 4, "esuppmode"}, + {WOAL_SET_GET_64_INT, IW_PRIV_TYPE_INT | 64, IW_PRIV_TYPE_INT | 64, ""}, + {WOAL_ECL_SYS_CLOCK, IW_PRIV_TYPE_INT | 64, IW_PRIV_TYPE_INT | 64, + "sysclock"}, + {WOAL_HOST_CMD, IW_PRIV_TYPE_BYTE | 2047, IW_PRIV_TYPE_BYTE | 2047, + "hostcmd"}, + {WOAL_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, ""}, +#if defined(SDIO) + {WOAL_CMD_53RDWR, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "sdcmd53rw"}, +#endif + {WOAL_SET_USER_SCAN, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "setuserscan"}, + {WOAL_GET_SCAN_TABLE, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "getscantable"}, + {WOAL_SET_USER_SCAN_EXT, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "setuserscanext"}, + {WOAL_WMM_ADDTS, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "addts"}, + {WOAL_WMM_DELTS, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "delts"}, + {WOAL_WMM_QUEUE_CONFIG, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "qconfig"}, + {WOAL_WMM_QUEUE_STATS, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "qstats"}, + {WOAL_BYPASSED_PACKET, IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, + IW_PRIV_TYPE_BYTE | WOAL_2K_BYTES, "pb_bypass"}, +#ifdef UAP_WEXT + {WOAL_FROYO_START, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "START"}, + {WOAL_FROYO_STOP, IW_PRIV_TYPE_NONE, IW_PRIV_TYPE_NONE, "STOP"}, + {WOAL_FROYO_WL_FW_RELOAD, IW_PRIV_TYPE_CHAR | 256, + IW_PRIV_TYPE_CHAR | 256, "WL_FW_RELOAD"}, +#endif +}; + +/** moal_802_11_rates */ +typedef struct _moal_802_11_rates { + /** Num of rates */ + t_u8 num_of_rates; + /** Rates */ + t_u8 rates[MLAN_SUPPORTED_RATES]; +} moal_802_11_rates, *pmoal_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/mxm_wifiex/wlan_src/mlinux/moal_proc.c b/mxm_wifiex/wlan_src/mlinux/moal_proc.c new file mode 100644 index 0000000..d60ae7f --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_proc.c @@ -0,0 +1,1099 @@ +/** @file moal_proc.c + * + * @brief This file contains functions for proc file. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 +#ifdef SDIO +#include "moal_sdio.h" +#endif + +/******************************************************** + Local Variables +********************************************************/ +#ifdef CONFIG_PROC_FS +#define STATUS_PROC "wifi_status" +#define MWLAN_PROC "mwlan" +#define WLAN_PROC "adapter%d" +/** Proc mwlan directory entry */ +struct proc_dir_entry *proc_mwlan; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#define PROC_DIR NULL +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +#define PROC_DIR (&proc_root) +#else +#define PROC_DIR proc_net +#endif + +#ifdef STA_SUPPORT +static char *szModes[] = { + "Unknown", + "Managed", + "Ad-hoc", + "Auto", +}; +#endif + +/******************************************************** + Global Variables +********************************************************/ +int wifi_status; + +/******************************************************** + Local Functions +********************************************************/ +/** + * @brief Proc read function for info + * + * @param sfp pointer to seq_file structure + * @param data + * + * @return Number of output data + */ +static int woal_info_proc_read(struct seq_file *sfp, void *data) +{ + struct net_device *netdev = (struct net_device *)sfp->private; + char fmt[MLAN_MAX_VER_STR_LEN]; + moal_private *priv = (moal_private *)netdev_priv(netdev); +#ifdef STA_SUPPORT + int i = 0; + moal_handle *handle = NULL; + mlan_bss_info info; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + struct dev_mc_list *mcptr = netdev->mc_list; + int mc_count = netdev->mc_count; +#else + struct netdev_hw_addr *mcptr = NULL; + int mc_count = netdev_mc_count(netdev); +#endif /* < 2.6.35 */ +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + int i = 0; +#endif /* >= 2.6.29 */ +#endif +#ifdef UAP_SUPPORT + mlan_ds_uap_stats ustats; +#endif + union { + t_u32 l; + t_u8 c[4]; + } ver; + + ENTER(); + + if (priv == NULL) + goto exit; +#ifdef STA_SUPPORT + handle = priv->phandle; + if (handle == NULL) + goto exit; +#endif + + if (!MODULE_GET) { + LEAVE(); + return 0; + } + + memset(fmt, 0, sizeof(fmt)); +#ifdef UAP_SUPPORT + memset(&ustats, 0, sizeof(ustats)); + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + seq_printf(sfp, "driver_name = " + "\"uap\"\n"); + woal_uap_get_version(priv, fmt, sizeof(fmt) - 1); + if (MLAN_STATUS_SUCCESS != + woal_uap_get_stats(priv, MOAL_IOCTL_WAIT, &ustats)) { + MODULE_PUT; + LEAVE(); + return -EFAULT; + } + } +#endif /* UAP_SUPPORT*/ +#ifdef STA_SUPPORT + memset(&info, 0, sizeof(info)); + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + woal_get_version(handle, fmt, sizeof(fmt) - 1); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &info)) { + MODULE_PUT; + LEAVE(); + return -EFAULT; + } + seq_printf(sfp, "driver_name = " + "\"wlan\"\n"); + } +#endif + seq_printf(sfp, "driver_version = %s", fmt); + seq_printf(sfp, "\ninterface_name=\"%s\"\n", netdev->name); + ver.l = handle->fw_release_number; + seq_printf(sfp, "firmware_major_version=%u.%u.%u\n", ver.c[2], ver.c[1], + ver.c[0]); +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) + seq_printf(sfp, "bss_mode = \"WIFIDIRECT-Client\"\n"); + else + seq_printf(sfp, "bss_mode = \"WIFIDIRECT-GO\"\n"); + } +#endif +#ifdef STA_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_STA) + seq_printf(sfp, "bss_mode =\"%s\"\n", szModes[info.bss_mode]); +#endif + seq_printf(sfp, "media_state=\"%s\"\n", + ((priv->media_connected == MFALSE) ? "Disconnected" : + "Connected")); + seq_printf(sfp, "mac_address=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + netdev->dev_addr[0], netdev->dev_addr[1], + netdev->dev_addr[2], netdev->dev_addr[3], + netdev->dev_addr[4], netdev->dev_addr[5]); +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + seq_printf(sfp, "multicast_count=\"%d\"\n", mc_count); + seq_printf(sfp, "essid=\"%s\"\n", info.ssid.ssid); + seq_printf(sfp, "bssid=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + info.bssid[0], info.bssid[1], info.bssid[2], + info.bssid[3], info.bssid[4], info.bssid[5]); + seq_printf(sfp, "channel=\"%d\"\n", (int)info.bss_chan); + seq_printf(sfp, "region_code = \"%02x\"\n", + (t_u8)info.region_code); + + /* + * Put out the multicast list + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + for (i = 0; i < netdev->mc_count; i++) { + seq_printf( + sfp, + "multicast_address[%d]=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + i, mcptr->dmi_addr[0], mcptr->dmi_addr[1], + mcptr->dmi_addr[2], mcptr->dmi_addr[3], + mcptr->dmi_addr[4], mcptr->dmi_addr[5]); + + mcptr = mcptr->next; + } +#else + netdev_for_each_mc_addr (mcptr, netdev) + seq_printf( + sfp, + "multicast_address[%d]=\"%02x:%02x:%02x:%02x:%02x:%02x\"\n", + i++, mcptr->addr[0], mcptr->addr[1], + mcptr->addr[2], mcptr->addr[3], mcptr->addr[4], + mcptr->addr[5]); +#endif /* < 2.6.35 */ + } +#endif + seq_printf(sfp, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); + seq_printf(sfp, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); + seq_printf(sfp, "num_tx_pkts = %lu\n", priv->stats.tx_packets); + seq_printf(sfp, "num_rx_pkts = %lu\n", priv->stats.rx_packets); + seq_printf(sfp, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); + seq_printf(sfp, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); + seq_printf(sfp, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); + seq_printf(sfp, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); + seq_printf(sfp, "carrier %s\n", + ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + for (i = 0; i < netdev->num_tx_queues; i++) { + seq_printf(sfp, "tx queue %d: %s\n", i, + ((netif_tx_queue_stopped( + netdev_get_tx_queue(netdev, 0))) ? + "stopped" : + "started")); + } +#else + seq_printf(sfp, "tx queue %s\n", + ((netif_queue_stopped(priv->netdev)) ? "stopped" : + "started")); +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + seq_printf(sfp, "tkip_mic_failures = %u\n", + ustats.tkip_mic_failures); + seq_printf(sfp, "ccmp_decrypt_errors = %u\n", + ustats.ccmp_decrypt_errors); + seq_printf(sfp, "wep_undecryptable_count = %u\n", + ustats.wep_undecryptable_count); + seq_printf(sfp, "wep_icv_error_count = %u\n", + ustats.wep_icv_error_count); + seq_printf(sfp, "decrypt_failure_count = %u\n", + ustats.decrypt_failure_count); + seq_printf(sfp, "mcast_tx_count = %u\n", ustats.mcast_tx_count); + seq_printf(sfp, "failed_count = %u\n", ustats.failed_count); + seq_printf(sfp, "retry_count = %u\n", ustats.retry_count); + seq_printf(sfp, "multiple_retry_count = %u\n", + ustats.multi_retry_count); + seq_printf(sfp, "frame_duplicate_count = %u\n", + ustats.frame_dup_count); + seq_printf(sfp, "rts_success_count = %u\n", + ustats.rts_success_count); + seq_printf(sfp, "rts_failure_count = %u\n", + ustats.rts_failure_count); + seq_printf(sfp, "ack_failure_count = %u\n", + ustats.ack_failure_count); + seq_printf(sfp, "rx_fragment_count = %u\n", + ustats.rx_fragment_count); + seq_printf(sfp, "mcast_rx_frame_count = %u\n", + ustats.mcast_rx_frame_count); + seq_printf(sfp, "fcs_error_count = %u\n", + ustats.fcs_error_count); + seq_printf(sfp, "tx_frame_count = %u\n", ustats.tx_frame_count); + seq_printf(sfp, "rsna_tkip_cm_invoked = %u\n", + ustats.rsna_tkip_cm_invoked); + seq_printf(sfp, "rsna_4way_hshk_failures = %u\n", + ustats.rsna_4way_hshk_failures); + } +#endif /* UAP_SUPPORT */ + seq_printf(sfp, "=== tp_acnt.on:%d drop_point:%d ===\n", + handle->tp_acnt.on, handle->tp_acnt.drop_point); + seq_printf(sfp, "====Tx accounting====\n"); + for (i = 0; i < MAX_TP_ACCOUNT_DROP_POINT_NUM; i++) { + seq_printf(sfp, "[%d] Tx packets : %lu\n", i, + handle->tp_acnt.tx_packets[i]); + seq_printf(sfp, "[%d] Tx packets last: %lu\n", i, + handle->tp_acnt.tx_packets_last[i]); + seq_printf(sfp, "[%d] Tx packets rate: %lu\n", i, + handle->tp_acnt.tx_packets_rate[i]); + seq_printf(sfp, "[%d] Tx bytes : %lu\n", i, + handle->tp_acnt.tx_bytes[i]); + seq_printf(sfp, "[%d] Tx bytes last : %lu\n", i, + handle->tp_acnt.tx_bytes_last[i]); + seq_printf(sfp, "[%d] Tx bytes rate : %luMbps\n", i, + handle->tp_acnt.tx_bytes_rate[i] * 8 / 1024 / 1024); + } + seq_printf(sfp, "Tx intr cnt : %lu\n", + handle->tp_acnt.tx_intr_cnt); + seq_printf(sfp, "Tx intr last : %lu\n", + handle->tp_acnt.tx_intr_last); + seq_printf(sfp, "Tx intr rate : %lu\n", + handle->tp_acnt.tx_intr_rate); + seq_printf(sfp, "Tx pending : %lu\n", + handle->tp_acnt.tx_pending); + seq_printf(sfp, "====Rx accounting====\n"); + for (i = 0; i < MAX_TP_ACCOUNT_DROP_POINT_NUM; i++) { + seq_printf(sfp, "[%d] Rx packets : %lu\n", i, + handle->tp_acnt.rx_packets[i]); + seq_printf(sfp, "[%d] Rx packets last: %lu\n", i, + handle->tp_acnt.rx_packets_last[i]); + seq_printf(sfp, "[%d] Rx packets rate: %lu\n", i, + handle->tp_acnt.rx_packets_rate[i]); + seq_printf(sfp, "[%d] Rx bytes : %lu\n", i, + handle->tp_acnt.rx_bytes[i]); + seq_printf(sfp, "[%d] Rx bytes last : %lu\n", i, + handle->tp_acnt.rx_bytes_last[i]); + seq_printf(sfp, "[%d] Rx bytes rate : %luMbps\n", i, + handle->tp_acnt.rx_bytes_rate[i] * 8 / 1024 / 1024); + } + seq_printf(sfp, "Rx intr cnt : %lu\n", + handle->tp_acnt.rx_intr_cnt); + seq_printf(sfp, "Rx intr last : %lu\n", + handle->tp_acnt.rx_intr_last); + seq_printf(sfp, "Rx intr rate : %lu\n", + handle->tp_acnt.rx_intr_rate); + seq_printf(sfp, "Rx pending : %lu\n", + handle->tp_acnt.rx_pending); + seq_printf(sfp, "Rx pause : %lu\n", + handle->tp_acnt.rx_paused_cnt); +exit: + LEAVE(); + MODULE_PUT; + return 0; +} + +static int woal_info_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_info_proc_read, PDE_DATA(inode)); +#else + return single_open(file, woal_info_proc_read, PDE(inode)->data); +#endif +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops info_proc_fops = { + .proc_open = woal_info_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations info_proc_fops = { + .owner = THIS_MODULE, + .open = woal_info_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +#ifdef SDIO +#define CMD52_STR_LEN 50 +/* + * @brief Parse cmd52 string + * + * @param buffer A pointer user buffer + * @param len Length user buffer + * @param func Parsed func number + * @param reg Parsed reg value + * @param val Parsed value to set + * @return BT_STATUS_SUCCESS + */ +static int parse_cmd52_string(const char *buffer, size_t len, int *func, + int *reg, int *val) +{ + int ret = MLAN_STATUS_SUCCESS; + char *string = NULL; + char *pos = NULL; + gfp_t flag; + + ENTER(); + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + string = kzalloc(CMD52_STR_LEN, flag); + if (string == NULL) + return -ENOMEM; + + moal_memcpy_ext(NULL, string, buffer + strlen("sdcmd52rw="), + len - strlen("sdcmd52rw="), CMD52_STR_LEN - 1); + string = strstrip(string); + + *func = -1; + *reg = -1; + *val = -1; + + /* Get func */ + pos = strsep(&string, " \t"); + if (pos) + *func = woal_string_to_number(pos); + + /* Get reg */ + pos = strsep(&string, " \t"); + if (pos) + *reg = woal_string_to_number(pos); + + /* Get val (optional) */ + pos = strsep(&string, " \t"); + if (pos) + *val = woal_string_to_number(pos); + kfree(string); + LEAVE(); + return ret; +} +#endif + +/** + * @brief config proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param count data number to write + * @param off Offset + * + * @return number of data + */ +static ssize_t woal_config_write(struct file *f, const char __user *buf, + size_t count, loff_t *off) +{ + char databuf[101]; + char *line = NULL; + t_u32 config_data = 0; + struct seq_file *sfp = f->private_data; + moal_handle *handle = (moal_handle *)sfp->private; + +#ifdef SDIO + int func = 0, reg = 0, val = 0; +#endif + moal_handle *ref_handle = NULL; + t_u32 cmd = 0; + int copy_len; + moal_private *priv = NULL; + + ENTER(); + if (!MODULE_GET) { + LEAVE(); + return 0; + } + + if (count >= sizeof(databuf)) { + MODULE_PUT; + LEAVE(); + return (int)count; + } + memset(databuf, 0, sizeof(databuf)); + copy_len = MIN((sizeof(databuf) - 1), count); + if (copy_from_user(databuf, buf, copy_len)) { + MODULE_PUT; + LEAVE(); + return 0; + } + line = databuf; + if (!strncmp(databuf, "soft_reset", strlen("soft_reset"))) { + line += strlen("soft_reset") + 1; + config_data = (t_u32)woal_string_to_number(line); + PRINTM(MINFO, "soft_reset: %d\n", (int)config_data); + if (woal_request_soft_reset(handle) == MLAN_STATUS_SUCCESS) + handle->hardware_status = HardwareStatusReset; + else + PRINTM(MERROR, "Could not perform soft reset\n"); + } + if (!strncmp(databuf, "drv_mode", strlen("drv_mode"))) { + line += strlen("drv_mode") + 1; + config_data = (t_u32)woal_string_to_number(line); + PRINTM(MINFO, "drv_mode: %d\n", (int)config_data); + if (config_data != (t_u32)handle->params.drv_mode) + if (woal_switch_drv_mode(handle, config_data) != + MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Could not switch drv mode\n"); + } + } +#ifdef SDIO + if (IS_SD(handle->card_type)) { + if (!strncmp(databuf, "sdcmd52rw=", strlen("sdcmd52rw=")) && + count > strlen("sdcmd52rw=")) { + parse_cmd52_string((const char *)databuf, (size_t)count, + &func, ®, &val); + woal_sdio_read_write_cmd52(handle, func, reg, val); + } + } +#endif /* SD */ + if (!strncmp(databuf, "debug_dump", strlen("debug_dump"))) { + ref_handle = (moal_handle *)handle->pref_mac; + if (ref_handle) { + priv = woal_get_priv(ref_handle, MLAN_BSS_ROLE_ANY); + if (priv) { +#ifdef DEBUG_LEVEL1 + drvdbg &= ~MFW_D; +#endif + woal_mlan_debug_info(priv); + woal_moal_debug_info(priv, NULL, MFALSE); + } + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (priv) { + PRINTM(MERROR, "Recevie debug_dump command\n"); +#ifdef DEBUG_LEVEL1 + drvdbg &= ~MFW_D; +#endif + woal_mlan_debug_info(priv); + woal_moal_debug_info(priv, NULL, MFALSE); + handle->ops.dump_fw_info(handle); + } + } + + if (!strncmp(databuf, "fwdump_file=", strlen("fwdump_file="))) { + int len = copy_len - strlen("fwdump_file="); + gfp_t flag; + if (len) { + kfree(handle->fwdump_fname); + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : + GFP_KERNEL; + handle->fwdump_fname = kzalloc(len, flag); + if (handle->fwdump_fname) + moal_memcpy_ext(handle, handle->fwdump_fname, + databuf + + strlen("fwdump_file="), + len - 1, len - 1); + } + } + if (!strncmp(databuf, "fw_reload", strlen("fw_reload"))) { + if (!strncmp(databuf, "fw_reload=", strlen("fw_reload="))) { + line += strlen("fw_reload") + 1; + config_data = (t_u32)woal_string_to_number(line); + } +#ifdef SDIO_MMC + else if (IS_SD(handle->card_type)) + config_data = FW_RELOAD_SDIO_INBAND_RESET; +#endif + PRINTM(MMSG, "Request fw_reload=%d\n", config_data); + woal_request_fw_reload(handle, config_data); + } + if (!strncmp(databuf, "drop_point=", strlen("drop_point="))) { + line += strlen("drop_point") + 1; + config_data = (t_u32)woal_string_to_number(line); + if (config_data) { + handle->tp_acnt.on = 1; + handle->tp_acnt.drop_point = config_data; + if (handle->is_tp_acnt_timer_set == MFALSE) { + woal_initialize_timer(&handle->tp_acnt.timer, + woal_tp_acnt_timer_func, + handle); + handle->is_tp_acnt_timer_set = MTRUE; + woal_mod_timer(&handle->tp_acnt.timer, 1000); + } + } else { + if (handle->is_tp_acnt_timer_set) { + woal_cancel_timer(&handle->tp_acnt.timer); + handle->is_tp_acnt_timer_set = MFALSE; + } + memset((void *)&handle->tp_acnt, 0, + sizeof(moal_tp_acnt_t)); + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (priv) + woal_set_tp_state(priv); + PRINTM(MMSG, "on=%d drop_point=%d\n", handle->tp_acnt.on, + handle->tp_acnt.drop_point); + } + if (!strncmp(databuf, "rf_test_mode", strlen("rf_test_mode"))) { + line += strlen("rf_test_mode") + 1; + config_data = (t_u32)woal_string_to_number(line); + PRINTM(MINFO, "RF test mode: %d\n", (int)config_data); + if (config_data != (t_u32)handle->rf_test_mode) + if (woal_process_rf_test_mode(handle, config_data) != + MLAN_STATUS_SUCCESS) + PRINTM(MERROR, "Could not set RF test mode\n"); + } + if (!strncmp(databuf, "tx_antenna", strlen("tx_antenna"))) { + line += strlen("tx_antenna") + 1; + config_data = (t_u32)woal_string_to_number(line); + cmd = MFG_CMD_TX_ANT; + } + if (!strncmp(databuf, "rx_antenna", strlen("rx_antenna"))) { + line += strlen("rx_antenna") + 1; + config_data = (t_u32)woal_string_to_number(line); + cmd = MFG_CMD_RX_ANT; + } + if (!strncmp(databuf, "channel", strlen("channel"))) { + line += strlen("channel") + 1; + config_data = (t_u32)woal_string_to_number(line); + cmd = MFG_CMD_RF_CHAN; + } + if (!strncmp(databuf, "band", strlen("band"))) { + line += strlen("band") + 1; + config_data = (t_u32)woal_string_to_number(line); + cmd = MFG_CMD_RF_BAND_AG; + } + if (!strncmp(databuf, "bw", strlen("bw"))) { + line += strlen("bw") + 1; + config_data = (t_u32)woal_string_to_number(line); + cmd = MFG_CMD_RF_CHANNELBW; + } + if (!strncmp(databuf, "get_and_reset_per", strlen("get_and_reset_per"))) + cmd = MFG_CMD_CLR_RX_ERR; + if (!strncmp(databuf, "tx_power=", strlen("tx_power=")) && + count > strlen("tx_power=")) + cmd = MFG_CMD_RFPWR; + if (!strncmp(databuf, "tx_frame=", strlen("tx_frame=")) && + count > strlen("tx_frame=")) + cmd = MFG_CMD_TX_FRAME; + if (!strncmp(databuf, "tx_continuous=", strlen("tx_continuous=")) && + count > strlen("tx_continuous=")) + cmd = MFG_CMD_TX_CONT; + + if (cmd && handle->rf_test_mode && + (woal_process_rf_test_mode_cmd( + handle, cmd, (const char *)databuf, (size_t)count, + MLAN_ACT_SET, config_data) != MLAN_STATUS_SUCCESS)) { + PRINTM(MERROR, "RF test mode cmd error\n"); + } + if (cmd && !handle->rf_test_mode) + PRINTM(MERROR, "RF test mode is disabled\n"); + MODULE_PUT; + LEAVE(); + return (int)count; +} + +/** + * @brief config proc read function + * + * @param sfp pointer to seq_file structure + * @param data + * + * @return number of output data + */ +static int woal_config_read(struct seq_file *sfp, void *data) +{ + moal_handle *handle = (moal_handle *)sfp->private; + int i; + + ENTER(); + + if (!MODULE_GET) { + LEAVE(); + return 0; + } + + seq_printf(sfp, "hardware_status=%d\n", (int)handle->hardware_status); + seq_printf(sfp, "netlink_num=%d\n", (int)handle->netlink_num); + seq_printf(sfp, "drv_mode=%d\n", (int)handle->params.drv_mode); +#ifdef SDIO + if (IS_SD(handle->card_type)) { + seq_printf(sfp, "sdcmd52rw=%d 0x%0x 0x%02X\n", + handle->cmd52_func, handle->cmd52_reg, + handle->cmd52_val); + } +#endif /* SD */ + seq_printf(sfp, "rf_test_mode=%u\n", handle->rf_test_mode); + if (handle->rf_test_mode && handle->rf_data) { + seq_printf(sfp, "tx_antenna=%u\n", handle->rf_data->tx_antenna); + seq_printf(sfp, "rx_antenna=%u\n", handle->rf_data->rx_antenna); + seq_printf(sfp, "band=%u\n", handle->rf_data->band); + seq_printf(sfp, "bw=%u\n", handle->rf_data->bandwidth); + if (handle->rf_data->channel) + seq_printf(sfp, "channel=%u\n", + handle->rf_data->channel); + else + seq_printf(sfp, "channel=\n"); + seq_printf(sfp, "total rx pkt count=%u\n", + handle->rf_data->rx_tot_pkt_count); + seq_printf(sfp, "rx multicast/broadcast pkt count=%u\n", + handle->rf_data->rx_mcast_bcast_pkt_count); + seq_printf(sfp, "rx fcs error pkt count=%u\n", + handle->rf_data->rx_pkt_fcs_err_count); + if (handle->rf_data->tx_power_data[0]) { + seq_printf(sfp, "tx_power=%u", + handle->rf_data->tx_power_data[0]); + seq_printf(sfp, " %u", + handle->rf_data->tx_power_data[1]); + seq_printf(sfp, " %u\n", + handle->rf_data->tx_power_data[2]); + } else + seq_printf(sfp, "tx_power=\n"); + seq_printf(sfp, "tx_continuous=%u", + handle->rf_data->tx_cont_data[0]); + if (handle->rf_data->tx_cont_data[0] == MTRUE) { + seq_printf(sfp, " %u", + handle->rf_data->tx_cont_data[1]); + seq_printf(sfp, " 0x%x", + handle->rf_data->tx_cont_data[2]); + for (i = 3; i < 6; i++) + seq_printf(sfp, " %u", + handle->rf_data->tx_cont_data[i]); + } + seq_printf(sfp, "\n"); + seq_printf(sfp, "tx_frame=%u", + handle->rf_data->tx_frame_data[0]); + if (handle->rf_data->tx_frame_data[0] == MTRUE) { + seq_printf(sfp, " %u", + handle->rf_data->tx_frame_data[1]); + seq_printf(sfp, " 0x%x", + handle->rf_data->tx_frame_data[2]); + for (i = 3; i < 13; i++) + seq_printf(sfp, " %u", + handle->rf_data->tx_frame_data[i]); + seq_printf(sfp, " %02x:%02x:%02x:%02x:%02x:%02x", + handle->rf_data->bssid[0], + handle->rf_data->bssid[1], + handle->rf_data->bssid[2], + handle->rf_data->bssid[3], + handle->rf_data->bssid[4], + handle->rf_data->bssid[5]); + } + seq_printf(sfp, "\n"); + } + MODULE_PUT; + LEAVE(); + return 0; +} + +static int woal_config_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_config_read, PDE_DATA(inode)); +#else + return single_open(file, woal_config_read, PDE(inode)->data); +#endif +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops config_proc_fops = { + .proc_open = woal_config_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = woal_config_write, +}; +#else +static const struct file_operations config_proc_fops = { + .owner = THIS_MODULE, + .open = woal_config_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = woal_config_write, +}; +#endif + +/** + * @brief wifi status proc read function + * + * @param sfp pointer to seq_file structure + * @param data + * + * @return number of output data + */ +static int woal_wifi_status_read(struct seq_file *sfp, void *data) +{ + ENTER(); + + if (!MODULE_GET) { + LEAVE(); + return 0; + } + + seq_printf(sfp, "%d\n", wifi_status); + + MODULE_PUT; + LEAVE(); + return 0; +} + +static int woal_wifi_status_proc_open(struct inode *inode, struct file *file) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + return single_open(file, woal_wifi_status_read, PDE_DATA(inode)); +#else + return single_open(file, woal_wifi_status_read, PDE(inode)->data); +#endif +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0) +static const struct proc_ops wifi_status_proc_fops = { + .proc_open = woal_wifi_status_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations wifi_status_proc_fops = { + .owner = THIS_MODULE, + .open = woal_wifi_status_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +/******************************************************** + 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 This function creates proc mwlan directory + * directory structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_root_proc_init(void) +{ + ENTER(); + + PRINTM(MINFO, "Create /proc/mwlan directory\n"); + + proc_mwlan = proc_mkdir(MWLAN_PROC, PROC_DIR); + if (!proc_mwlan) { + PRINTM(MERROR, + "woal_root_proc_init: Cannot create /proc/mwlan\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + /* create /proc/mwlan/wifi_status */ + proc_create_data(STATUS_PROC, 0644, proc_mwlan, &wifi_status_proc_fops, + NULL); + + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function removes proc mwlan directory + * directory structure + * + * @return N/A + */ +void woal_root_proc_remove(void) +{ + ENTER(); + + remove_proc_entry(STATUS_PROC, proc_mwlan); + + remove_proc_entry(MWLAN_PROC, PROC_DIR); + proc_mwlan = NULL; + + LEAVE(); +} + +/** + * @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_mwlan; +#endif + char config_proc_dir[20]; + + ENTER(); + + if (handle->proc_wlan) { + PRINTM(MMSG, "woal_proc_init: proc_wlan is already exist %s\n", + handle->proc_wlan_name); + goto done; + } + + snprintf(handle->proc_wlan_name, sizeof(handle->proc_wlan_name), + WLAN_PROC, handle->handle_idx); + PRINTM(MINFO, "Create Proc Interface %s\n", handle->proc_wlan_name); + +#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(handle->proc_wlan_name, pde->name)) { + /* Directory exists */ + PRINTM(MWARN, "proc interface already exists!\n"); + handle->proc_wlan = pde; + break; + } + } + if (pde == NULL) { + handle->proc_wlan = + proc_mkdir(handle->proc_wlan_name, proc_mwlan); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + if (handle->proc_wlan) + atomic_set(&handle->proc_wlan->count, 1); +#endif + } +#else + handle->proc_wlan = proc_mkdir(handle->proc_wlan_name, proc_mwlan); +#endif + if (!handle->proc_wlan) { + PRINTM(MERROR, "Cannot create proc interface %s!\n", + handle->proc_wlan_name); + goto done; + } + + strcpy(config_proc_dir, "config"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data(config_proc_dir, 0644, handle->proc_wlan, + &config_proc_fops, handle); +#else + r = create_proc_entry(config_proc_dir, 0644, handle->proc_wlan); + if (r) { + r->data = handle; + r->proc_fops = &config_proc_fops; + } +#endif + if (!r) + PRINTM(MERROR, "Fail to create proc config\n"); + +done: + LEAVE(); +} + +/** + * @brief Remove the top level proc directory + * + * @param handle pointer moal_handle + * + * @return N/A + */ +void woal_proc_exit(moal_handle *handle) +{ + char config_proc_dir[20]; + + ENTER(); + + PRINTM(MINFO, "Remove Proc Interface %s\n", handle->proc_wlan_name); + if (handle->proc_wlan) { + strcpy(config_proc_dir, "config"); + remove_proc_entry(config_proc_dir, handle->proc_wlan); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + /* Remove only if we are the only instance using this */ + if (atomic_read(&(handle->proc_wlan->count)) > 1) { + PRINTM(MWARN, "More than one interface using proc!\n"); + } else { +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + atomic_dec(&(handle->proc_wlan->count)); +#endif + remove_proc_entry(handle->proc_wlan_name, proc_mwlan); + + handle->proc_wlan = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + } +#endif + } + LEAVE(); +} + +/** + * @brief Create proc file for interface + * + * @param priv pointer moal_private + * + * @return N/A + */ +void woal_create_proc_entry(moal_private *priv) +{ + struct proc_dir_entry *r; + struct net_device *dev = priv->netdev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + char proc_dir_name[22]; +#endif + + ENTER(); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + if (!priv->proc_entry) { + memset(proc_dir_name, 0, sizeof(proc_dir_name)); + strncpy(proc_dir_name, priv->phandle->proc_wlan_name, + sizeof(proc_dir_name) - 2); + proc_dir_name[strlen(proc_dir_name)] = '/'; + + if (strlen(dev->name) > + ((sizeof(proc_dir_name) - 1) - (strlen(proc_dir_name)))) { + PRINTM(MERROR, + "Failed to create proc entry, device name is too long\n"); + LEAVE(); + return; + } + strcat(proc_dir_name, dev->name); + /* Try to create adapterX/dev_name directory first under + * /proc/mwlan/ */ + priv->proc_entry = proc_mkdir(proc_dir_name, proc_mwlan); + if (priv->proc_entry) { + /* Success. Continue normally */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + if (!priv->phandle->proc_wlan) { + priv->phandle->proc_wlan = + priv->proc_entry->parent; + } + atomic_inc(&(priv->phandle->proc_wlan->count)); +#endif + } else { + /* Failure. adapterX/ may not exist. Try to create that + * first */ + priv->phandle->proc_wlan = proc_mkdir( + priv->phandle->proc_wlan_name, proc_mwlan); + if (!priv->phandle->proc_wlan) { + /* Failure. Something broken */ + LEAVE(); + return; + } else { + /* Success. Now retry creating mlanX */ + priv->proc_entry = + proc_mkdir(proc_dir_name, proc_mwlan); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + atomic_inc(&(priv->phandle->proc_wlan->count)); +#endif + } + } +#else + if (priv->phandle->proc_wlan && !priv->proc_entry) { + priv->proc_entry = + proc_mkdir(dev->name, priv->phandle->proc_wlan); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + atomic_inc(&(priv->phandle->proc_wlan->count)); +#endif /* < 3.10.0 */ +#endif /* < 2.6.26 */ + strcpy(priv->proc_entry_name, dev->name); + if (priv->proc_entry) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + r = proc_create_data("info", 0, priv->proc_entry, + &info_proc_fops, dev); +#else + r = create_proc_entry("info", 0, priv->proc_entry); + if (r) { + r->data = dev; + r->proc_fops = &info_proc_fops; + } +#endif + if (!r) + PRINTM(MMSG, "Fail to create proc info\n"); + } + } + + LEAVE(); +} + +/** + * @brief Remove proc file + * + * @param priv Pointer moal_private + * + * @return N/A + */ +void woal_proc_remove(moal_private *priv) +{ + ENTER(); + if (priv->phandle->proc_wlan && priv->proc_entry) { + remove_proc_entry("info", priv->proc_entry); + remove_proc_entry(priv->proc_entry_name, + priv->phandle->proc_wlan); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0) + atomic_dec(&(priv->phandle->proc_wlan->count)); +#endif + priv->proc_entry = NULL; + } + LEAVE(); +} +#endif diff --git a/mxm_wifiex/wlan_src/mlinux/moal_sdio.h b/mxm_wifiex/wlan_src/mlinux/moal_sdio.h new file mode 100644 index 0000000..1453dbb --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_sdio.h @@ -0,0 +1,174 @@ +/** @file moal_sdio.h + * + * @brief This file contains definitions for SDIO interface. + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 + +#if defined(SD8977) +#define SD8977_V0 0x0 +#define SD8977_V1 0x8 +#define SD8977_V2 0x9 +#define SD8977_V0_FW_NAME "nxp/sdsd8977_combo.bin" +#define SD8977_V1_FW_NAME "nxp/sdsd8977_combo_v1.bin" +#define SD8977_V2_FW_NAME "nxp/sdsd8977_combo_v2.bin" +#define SD8977_WLAN_V2_FW_NAME "nxp/sd8977_wlan_v2.bin" +#define SD8977_WLAN_V1_FW_NAME "nxp/sd8977_wlan_v1.bin" +#define SD8977_WLAN_V0_FW_NAME "nxp/sd8977_wlan.bin" +#endif /* SD8977_MULTI_FW */ + +#if defined(SD8887) +/** SD8887 chip revision ID */ +#define SD8887_A0 0x0 +#define SD8887_A2 0x2 + +#define SD8887_A0_FW_NAME "nxp/sd8887_uapsta.bin" +#define SD8887_A2_FW_NAME "nxp/sd8887_uapsta_a2.bin" +#define SD8887_WLAN_A2_FW_NAME "nxp/sd8887_wlan_a2.bin" +#define SD8887_WLAN_A0_FW_NAME "nxp/sd8887_wlan.bin" +#endif /* SD8887_MULTI_FW */ + +/** Default firmware name */ +#ifdef SD8887 +#define SD8887_DEFAULT_COMBO_FW_NAME "nxp/sd8887_uapsta_a2.bin" +#define SD8887_DEFAULT_WLAN_FW_NAME "nxp/sd8887_wlan_a2.bin" +#endif /* SD8887 */ + +#ifdef SD8977 +#define SD8977_DEFAULT_COMBO_FW_NAME "nxp/sdsd8977_combo_v2.bin" +#define SD8977_DEFAULT_WLAN_FW_NAME "nxp/sd8977_wlan_v2.bin" +#endif /* SD8977 */ + +#ifdef SD8997 +#define SD8997_DEFAULT_COMBO_FW_NAME "nxp/sdsd8997_combo_v4.bin" +#define SDUART8997_DEFAULT_COMBO_FW_NAME "nxp/sduart8997_combo_v4.bin" +#define SDSD8997_DEFAULT_COMBO_FW_NAME "nxp/sdsd8997_combo_v4.bin" +#define SD8997_DEFAULT_WLAN_FW_NAME "nxp/sd8997_wlan_v4.bin" +#endif /* SD8997 */ + +#ifdef SD8987 +#define SD8987_DEFAULT_COMBO_FW_NAME "nxp/sdsd8987_combo.bin" +#define SDUART8987_DEFAULT_COMBO_FW_NAME "nxp/sduart8987_combo.bin" +#define SDSD8987_DEFAULT_COMBO_FW_NAME "nxp/sdsd8987_combo.bin" +#define SD8987_DEFAULT_WLAN_FW_NAME "nxp/sd8987_wlan.bin" +#endif /* SD8987 */ + +#ifdef SD8897 +#define SD8897_DEFAULT_COMBO_FW_NAME "nxp/sdsd8897_uapsta.bin" +#define SD8897_DEFAULT_WLAN_FW_NAME "nxp/sd8897_wlan.bin" +#endif /* SD8897 */ + +#ifdef SD8978 +#define SD8978_DEFAULT_COMBO_FW_NAME "nxp/sdsd8978_combo.bin" +#define SDUART8978_DEFAULT_COMBO_FW_NAME "nxp/sduart8978_combo.bin" +#define SDSD8978_DEFAULT_COMBO_FW_NAME "nxp/sdsd8978_combo.bin" +#define SD8978_DEFAULT_WLAN_FW_NAME "nxp/sd8978_wlan.bin" +#endif /* SD8978 */ + +#ifdef SD9098 +#define SD9098_Z1Z2 0x00 +#define SD9098_A0 0x01 +#define SD9098_A1 0x02 +#define SD9098_A2 0x03 +#define SD9098_DEFAULT_COMBO_FW_NAME "nxp/sdsd9098_combo.bin" +#define SDUART9098_DEFAULT_COMBO_FW_NAME "nxp/sduart9098_combo.bin" +#define SDSD9098_DEFAULT_COMBO_FW_NAME "nxp/sdsd9098_combo.bin" +#define SD9098_DEFAULT_WLAN_FW_NAME "nxp/sd9098_wlan.bin" +#define SDUART9098_COMBO_V1_FW_NAME "nxp/sduart9098_combo_v1.bin" +#define SDSD9098_COMBO_V1_FW_NAME "nxp/sdsd9098_combo_v1.bin" +#define SD9098_WLAN_V1_FW_NAME "nxp/sd9098_wlan_v1.bin" +#endif /* SD9098 */ + +#ifdef SD9097 +#define SD9097_B0 0x01 +#define SD9097_B1 0x02 +#define SD9097_DEFAULT_COMBO_FW_NAME "nxp/sdsd9097_combo_v1.bin" + +#define SD9097_DEFAULT_WLAN_FW_NAME "nxp/sd9097_wlan_v1.bin" +#define SDUART9097_COMBO_V1_FW_NAME "nxp/sduart9097_combo_v1.bin" +#define SDSD9097_COMBO_V1_FW_NAME "nxp/sdsd9097_combo_v1.bin" +#define SD9097_WLAN_V1_FW_NAME "nxp/sd9097_wlan_v1.bin" +#endif /* SD9097 */ + +/******************************************************** + Global Functions +********************************************************/ + +/** Register to bus driver function */ +mlan_status woal_sdiommc_bus_register(void); +/** Unregister from bus driver function */ +void woal_sdiommc_bus_unregister(void); + +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 */ + +#ifdef SDIO_MMC +/** 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; +}; +#endif /* SDIO_MMC */ + +/** 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/mxm_wifiex/wlan_src/mlinux/moal_sdio_mmc.c b/mxm_wifiex/wlan_src/mlinux/moal_sdio_mmc.c new file mode 100644 index 0000000..4312b45 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_sdio_mmc.c @@ -0,0 +1,2106 @@ +/** @file moal_sdio_mmc.c + * + * @brief This file contains SDIO MMC IF (interface) module + * related functions. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 nxp vendor id */ +#define NXP_VENDOR_ID 0x02df + +/******************************************************** + Local Variables +********************************************************/ +/* moal interface ops */ +static moal_if_ops sdiommc_ops; +/******************************************************** + Global Variables +********************************************************/ + +#ifdef SD8887 +/** Device ID for SD8887 */ +#define SD_DEVICE_ID_8887 (0x9135) +#endif +#ifdef SD8897 +/** Device ID for SD8897 */ +#define SD_DEVICE_ID_8897 (0x912d) +#endif +#ifdef SD8977 +/** Device ID for SD8977 */ +#define SD_DEVICE_ID_8977 (0x9145) +#endif +#ifdef SD8978 +/** Device ID for SD8978 */ +#define SD_DEVICE_ID_8978 (0x9159) +#endif +#ifdef SD8997 +/** Device ID for SD8997 */ +#define SD_DEVICE_ID_8997 (0x9141) +#endif +#ifdef SD8987 +/** Device ID for SD8987 */ +#define SD_DEVICE_ID_8987 (0x9149) +#endif +#ifdef SD9098 +/** Device ID for SD9098 */ +#define SD_DEVICE_ID_9098_FN1 (0x914D) +/** Device ID for SD9098 */ +#define SD_DEVICE_ID_9098_FN2 (0x914E) +#endif +#ifdef SD9097 +/** Device ID for SD9097 */ +#define SD_DEVICE_ID_9097 (0x9155) +#endif +/** Device ID any */ +#ifndef SD_DEVICE_ANY +#define SD_DEVICE_ANY (0xffff) +#endif /* SD_DEVICE_ANY */ + +/** WLAN IDs */ +static const struct sdio_device_id wlan_ids[] = { +#ifdef SD8887 + {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_8887)}, +#endif +#ifdef SD8897 + {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_8897)}, +#endif +#ifdef SD8977 + {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_8977)}, +#endif +#ifdef SD8978 + {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_8978)}, +#endif +#ifdef SD8997 + {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_8997)}, +#endif +#ifdef SD8987 + {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_8987)}, +#endif +#ifdef SD9098 + {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_9098_FN1)}, + {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_9098_FN2)}, +#endif +#ifdef SD9097 + {SDIO_DEVICE(NXP_VENDOR_ID, SD_DEVICE_ID_9097)}, +#endif + {}, +}; + +int woal_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id); +void woal_sdio_remove(struct sdio_func *func); +#ifdef SDIO +static void woal_sdiommc_reg_dbg(pmoal_handle handle); +#endif + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +int woal_sdio_suspend(struct device *dev); +int woal_sdio_resume(struct device *dev); + +static struct dev_pm_ops wlan_sdio_pm_ops = { + .suspend = woal_sdio_suspend, + .resume = woal_sdio_resume, +}; + +void woal_sdio_shutdown(struct device *dev); +#endif +#endif + +// clang-format off +static struct sdio_driver REFDATA wlan_sdio = { + .name = "wlan_sdio", + .id_table = wlan_ids, + .probe = woal_sdio_probe, + .remove = woal_sdio_remove, +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + .drv = { + .owner = THIS_MODULE, +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .pm = &wlan_sdio_pm_ops, + .shutdown = woal_sdio_shutdown, +#endif +#endif + + } +#else +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER + .drv = { + .pm = &wlan_sdio_pm_ops, + .shutdown = woal_sdio_shutdown, + } +#endif +#endif +#endif +}; +// clang-format on + +/******************************************************** + Local Functions +********************************************************/ +static void woal_sdiommc_dump_fw_info(moal_handle *phandle); + +/** @brief This function dump the sdio register + * + * @param handle A Pointer to the moal_handle structure + * @return N/A + */ +void woal_dump_sdio_reg(moal_handle *handle) +{ + int ret = 0; + t_u8 data, i; + int fun0_reg[] = {0x05, 0x04}; + t_u8 array_size = 0; +#ifdef SD8897 + int fun1_reg_8897[] = {0x03, 0x04, 0x05, 0x06, 0x07, 0xC0, 0xC1}; +#endif + int fun1_reg_other[] = {0x03, 0x04, 0x05, 0x60, 0x61}; + int *fun1_reg = NULL; + + for (i = 0; i < ARRAY_SIZE(fun0_reg); i++) { + data = sdio_f0_readb( + ((struct sdio_mmc_card *)handle->card)->func, + fun0_reg[i], &ret); + PRINTM(MMSG, "fun0: reg 0x%02x=0x%02x ret=%d\n", fun0_reg[i], + data, ret); + } + +#ifdef SD8897 + if (IS_SD8897(handle->card_type)) { + fun1_reg = fun1_reg_8897; + array_size = sizeof(fun1_reg_8897) / sizeof(int); + } else { +#endif + fun1_reg = fun1_reg_other; + array_size = sizeof(fun1_reg_other) / sizeof(int); +#ifdef SD8897 + } +#endif + for (i = 0; i < array_size; i++) { + data = sdio_readb(((struct sdio_mmc_card *)handle->card)->func, + fun1_reg[i], &ret); + PRINTM(MMSG, "fun1: reg 0x%02x=0x%02x ret=%d\n", fun1_reg[i], + data, ret); + } + return; +} +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief This function handles the interrupt. + * + * @param func A pointer to the sdio_func structure + * @return N/A + */ +static void woal_sdio_interrupt(struct sdio_func *func) +{ + moal_handle *handle; + struct sdio_mmc_card *card; + + ENTER(); + + card = sdio_get_drvdata(func); + if (!card || !card->handle) { + PRINTM(MINFO, + "sdio_mmc_interrupt(func = %p) card or handle is NULL, card=%p\n", + func, card); + LEAVE(); + return; + } + handle = card->handle; + if (handle->surprise_removed == MTRUE) { + LEAVE(); + return; + } + handle->main_state = MOAL_RECV_INT; + PRINTM(MINFO, "*** IN SDIO IRQ ***\n"); + PRINTM(MINTR, "*\n"); + + /* call mlan_interrupt to read int status */ + mlan_interrupt(0, handle->pmlan_adapter); +#ifdef SDIO_SUSPEND_RESUME + if (handle->is_suspended) { + PRINTM(MINTR, "Receive interrupt in hs_suspended\n"); + LEAVE(); + return; + } +#endif + handle->main_state = MOAL_START_MAIN_PROCESS; + /* Call MLAN main process */ + mlan_main_process(handle->pmlan_adapter); + handle->main_state = MOAL_END_MAIN_PROCESS; + LEAVE(); +} + +/** @brief This function updates the card types + * + * @param handle A Pointer to the moal_handle structure + * @param card A Pointer to card + * + * @return N/A + */ +static t_u16 woal_update_card_type(t_void *card) +{ + struct sdio_mmc_card *cardp_sd = (struct sdio_mmc_card *)card; + t_u16 card_type = 0; + + /* Update card type */ +#ifdef SD8887 + if (cardp_sd->func->device == SD_DEVICE_ID_8887) { + card_type = CARD_TYPE_SD8887; + moal_memcpy_ext(NULL, driver_version, CARD_SD8887, + strlen(CARD_SD8887), strlen(driver_version)); + moal_memcpy_ext( + NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V15, strlen(V15), + strlen(driver_version) - + (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); + } +#endif +#ifdef SD8897 + if (cardp_sd->func->device == SD_DEVICE_ID_8897) { + card_type = CARD_TYPE_SD8897; + moal_memcpy_ext(NULL, driver_version, CARD_SD8897, + strlen(CARD_SD8897), strlen(driver_version)); + moal_memcpy_ext( + NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V15, strlen(V15), + strlen(driver_version) - + (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); + } +#endif +#ifdef SD8977 + if (cardp_sd->func->device == SD_DEVICE_ID_8977) { + card_type = CARD_TYPE_SD8977; + moal_memcpy_ext(NULL, driver_version, CARD_SD8977, + strlen(CARD_SD8977), strlen(driver_version)); + moal_memcpy_ext( + NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V16, strlen(V16), + strlen(driver_version) - + (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); + } +#endif +#ifdef SD8978 + if (cardp_sd->func->device == SD_DEVICE_ID_8978) { + card_type = CARD_TYPE_SD8978; + moal_memcpy_ext(NULL, driver_version, CARD_SD8978, + strlen(CARD_SD8978), strlen(driver_version)); + moal_memcpy_ext( + NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V16, strlen(V16), + strlen(driver_version) - + (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); + } +#endif +#ifdef SD8997 + if (cardp_sd->func->device == SD_DEVICE_ID_8997) { + card_type = CARD_TYPE_SD8997; + moal_memcpy_ext(NULL, driver_version, CARD_SD8997, + strlen(CARD_SD8997), strlen(driver_version)); + moal_memcpy_ext( + NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V16, strlen(V16), + strlen(driver_version) - + (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); + } +#endif +#ifdef SD8987 + if (cardp_sd->func->device == SD_DEVICE_ID_8987) { + card_type = CARD_TYPE_SD8987; + moal_memcpy_ext(NULL, driver_version, CARD_SD8987, + strlen(CARD_SD8987), strlen(driver_version)); + moal_memcpy_ext( + NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V16, strlen(V16), + strlen(driver_version) - + (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); + } +#endif +#ifdef SD9097 + if (cardp_sd->func->device == SD_DEVICE_ID_9097) { + card_type = CARD_TYPE_SD9097; + moal_memcpy_ext(NULL, driver_version, CARD_SD9097, + strlen(CARD_SD9097), strlen(driver_version)); + moal_memcpy_ext( + NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V17, strlen(V17), + strlen(driver_version) - + (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); + } +#endif +#ifdef SD9098 + if (cardp_sd->func->device == SD_DEVICE_ID_9098_FN1 || + cardp_sd->func->device == SD_DEVICE_ID_9098_FN2) { + card_type = CARD_TYPE_SD9098; + moal_memcpy_ext(NULL, driver_version, CARD_SD9098, + strlen(CARD_SD9098), strlen(driver_version)); + moal_memcpy_ext( + NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V17, strlen(V17), + strlen(driver_version) - + (strlen(INTF_CARDTYPE) + strlen(KERN_VERSION))); + } +#endif + return card_type; +} + +/** @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; + t_u16 card_type = 0; + + ENTER(); + + PRINTM(MMSG, "vendor=0x%4.04X device=0x%4.04X class=%d function=%d\n", + func->vendor, func->device, func->class, func->num); + + card = kzalloc(sizeof(struct sdio_mmc_card), GFP_KERNEL); + if (!card) { + PRINTM(MFATAL, + "Failed to allocate memory in probe function!\n"); + LEAVE(); + return -ENOMEM; + } + + card->func = func; + +#ifdef MMC_QUIRK_BLKSZ_FOR_BYTE_MODE + /* The byte mode patch is available in kernel MMC driver + * which fixes one issue in MP-A transfer. + * bit1: use func->cur_blksize for byte mode + */ + func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + func->card->quirks |= MMC_QUIRK_LENIENT_FN0; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + /* wait for chip fully wake up */ + if (!func->enable_timeout) + func->enable_timeout = 200; +#endif + sdio_claim_host(func); + ret = sdio_enable_func(func); + if (ret) { + sdio_release_host(func); + PRINTM(MFATAL, "sdio_enable_func() failed: ret=%d\n", ret); + ret = -EIO; + goto err; + } + sdio_release_host(func); + + card_type = woal_update_card_type(card); + if (!card_type) { + PRINTM(MERROR, "sdmmc probe: woal_update_card_type() failed\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + + if (NULL == + woal_add_card(card, &card->func->dev, &sdiommc_ops, card_type)) { + PRINTM(MERROR, "woal_add_card failed\n"); + ret = MLAN_STATUS_FAILURE; + goto err; + } + + LEAVE(); + return ret; +err: + kfree(card); + sdio_claim_host(func); + sdio_disable_func(func); + sdio_release_host(func); + + LEAVE(); + return ret; +} + +/** @brief This function handles client driver remove. + * + * @param func A pointer to sdio_func structure. + * @return N/A + */ +void woal_sdio_remove(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + + ENTER(); + + if (func) { + PRINTM(MINFO, "SDIO func=%d\n", func->num); + card = sdio_get_drvdata(func); + if (card) { + woal_remove_card(card); + kfree(card); + } + } + + LEAVE(); +} + +#ifdef SDIO_SUSPEND_RESUME +#ifdef MMC_PM_KEEP_POWER +#ifdef MMC_PM_FUNC_SUSPENDED +/** @brief This function tells lower driver that WLAN is suspended + * + * @param handle A Pointer to the moal_handle structure + * @return N/A + */ +void woal_wlan_is_suspended(moal_handle *handle) +{ + ENTER(); + if (handle->suspend_notify_req == MTRUE) { + handle->is_suspended = MTRUE; + sdio_func_suspended( + ((struct sdio_mmc_card *)handle->card)->func); + } + LEAVE(); +} +#endif + +#define SHUTDOWN_HOST_SLEEP_DEF_GAP 100 +#define SHUTDOWN_HOST_SLEEP_DEF_GPIO 0x3 +#define SHUTDOWN_HOST_SLEEP_DEF_COND 0x0 + +/** @brief This function handles client driver shutdown + * + * @param dev A pointer to device structure + * @return N/A + */ +void woal_sdio_shutdown(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + moal_handle *handle = NULL; + struct sdio_mmc_card *cardp; + mlan_ds_ps_info pm_info; + int timeout = 0; + int i, retry_num = 8; + + ENTER(); + PRINTM(MCMND, "<--- Enter woal_sdio_shutdown --->\n"); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->handle) { + PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); + LEAVE(); + return; + } + handle = cardp->handle; + for (i = 0; i < handle->priv_num; i++) + netif_device_detach(handle->priv[i]->netdev); + + if (moal_extflg_isset(handle, EXT_SHUTDOWN_HS)) { + handle->shutdown_hs_in_process = MTRUE; + memset(&pm_info, 0, sizeof(pm_info)); + for (i = 0; i < retry_num; i++) { + if (MLAN_STATUS_SUCCESS == + woal_get_pm_info(woal_get_priv(handle, + MLAN_BSS_ROLE_ANY), + &pm_info)) { + if (pm_info.is_suspend_allowed == MTRUE) + break; + else + PRINTM(MMSG, + "Shutdown not allowed and retry again\n"); + } + woal_sched_timeout(100); + } + if (pm_info.is_suspend_allowed == MFALSE) { + PRINTM(MMSG, "Shutdown not allowed\n"); + goto done; + } + woal_enable_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY)); + + timeout = wait_event_interruptible_timeout( + handle->hs_activate_wait_q, + handle->hs_activate_wait_q_woken, HS_ACTIVE_TIMEOUT); + if (handle->hs_activated == MTRUE) + PRINTM(MMSG, "HS actived in shutdown\n"); + else + PRINTM(MMSG, "Fail to enable HS in shutdown\n"); + } else { + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i]) { + if (handle->priv[i]->media_connected == MTRUE +#ifdef UAP_SUPPORT + || (GET_BSS_ROLE(handle->priv[i]) == + MLAN_BSS_ROLE_UAP) +#endif + ) { + PRINTM(MIOCTL, + "disconnect on suspend\n"); + woal_disconnect(handle->priv[i], + MOAL_NO_WAIT, NULL, + DEF_DEAUTH_REASON_CODE); + } + } + } + } + +done: + PRINTM(MCMND, "<--- Leave woal_sdio_shutdown --->\n"); + LEAVE(); + return; +} + +/** @brief This function handles client driver suspend + * + * @param dev A pointer to device structure + * @return MLAN_STATUS_SUCCESS or error code + */ +int woal_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + moal_handle *handle = NULL; + struct sdio_mmc_card *cardp; + int i, retry_num = 8; + int ret = MLAN_STATUS_SUCCESS; + int hs_actived = 0; + mlan_ds_ps_info pm_info; + + ENTER(); + PRINTM(MCMND, "<--- Enter woal_sdio_suspend --->\n"); + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(MCMND, "%s: suspend: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->handle) { + PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + handle = cardp->handle; + + if (moal_extflg_isset(handle, EXT_PM_KEEP_POWER) && + !(pm_flags & MMC_PM_KEEP_POWER)) { + PRINTM(MERROR, + "%s: cannot remain alive while host is suspended\n", + sdio_func_id(func)); + LEAVE(); + return -ENOSYS; + } + if (handle->is_suspended == MTRUE) { + PRINTM(MWARN, "Device already suspended\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + if (handle->fw_dump) { + PRINTM(MMSG, "suspend not allowed while FW dump!"); + ret = -EBUSY; + goto done; + } +#ifdef STA_SUPPORT + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) + woal_cancel_scan(handle->priv[i], MOAL_IOCTL_WAIT); + } +#endif + handle->suspend_fail = MFALSE; + memset(&pm_info, 0, sizeof(pm_info)); + for (i = 0; i < retry_num; i++) { + if (MLAN_STATUS_SUCCESS == + woal_get_pm_info(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + &pm_info)) { + if (pm_info.is_suspend_allowed == MTRUE) + break; + else + PRINTM(MMSG, + "Suspend not allowed and retry again\n"); + } + woal_sched_timeout(100); + } + if (pm_info.is_suspend_allowed == MFALSE) { + PRINTM(MMSG, "Suspend not allowed\n"); + ret = -EBUSY; + goto done; + } + + for (i = 0; i < handle->priv_num; i++) + netif_device_detach(handle->priv[i]->netdev); + + if (moal_extflg_isset(handle, EXT_PM_KEEP_POWER)) { + /* Enable the Host Sleep */ +#ifdef MMC_PM_FUNC_SUSPENDED + handle->suspend_notify_req = MTRUE; +#endif + hs_actived = woal_enable_hs( + woal_get_priv(handle, MLAN_BSS_ROLE_ANY)); +#ifdef MMC_PM_FUNC_SUSPENDED + handle->suspend_notify_req = MFALSE; +#endif + if (hs_actived) { +#ifdef MMC_PM_SKIP_RESUME_PROBE + PRINTM(MCMND, + "suspend with MMC_PM_KEEP_POWER and MMC_PM_SKIP_RESUME_PROBE\n"); + ret = sdio_set_host_pm_flags( + func, + MMC_PM_KEEP_POWER | MMC_PM_SKIP_RESUME_PROBE); +#else + PRINTM(MCMND, "suspend with MMC_PM_KEEP_POWER\n"); + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +#endif + } else { + PRINTM(MMSG, "HS not actived, suspend fail!"); + handle->suspend_fail = MTRUE; + for (i = 0; i < handle->priv_num; i++) + netif_device_attach(handle->priv[i]->netdev); + ret = -EBUSY; + goto done; + } + } + + /* Indicate device suspended */ + handle->is_suspended = MTRUE; +done: + PRINTM(MCMND, "<--- Leave woal_sdio_suspend --->\n"); + LEAVE(); + return ret; +} + +/** @brief This function handles client driver resume + * + * @param dev A pointer to device structure + * @return MLAN_STATUS_SUCCESS + */ +int woal_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + mmc_pm_flag_t pm_flags = 0; + moal_handle *handle = NULL; + struct sdio_mmc_card *cardp; + int i; + + ENTER(); + PRINTM(MCMND, "<--- Enter woal_sdio_resume --->\n"); + pm_flags = sdio_get_host_pm_caps(func); + PRINTM(MCMND, "%s: resume: PM flags = 0x%x\n", sdio_func_id(func), + pm_flags); + cardp = sdio_get_drvdata(func); + if (!cardp || !cardp->handle) { + PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + handle = cardp->handle; + + if (handle->is_suspended == MFALSE) { + PRINTM(MWARN, "Device already resumed\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + handle->is_suspended = MFALSE; + if (woal_check_driver_status(handle)) { + PRINTM(MERROR, "Resuem, device is in hang state\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + for (i = 0; i < handle->priv_num; i++) + netif_device_attach(handle->priv[i]->netdev); + + /* Disable Host Sleep */ + woal_cancel_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), MOAL_NO_WAIT); + PRINTM(MCMND, "<--- Leave woal_sdio_resume --->\n"); + LEAVE(); + return MLAN_STATUS_SUCCESS; +} +#endif +#endif /* SDIO_SUSPEND_RESUME */ + +/** + * @brief This function writes data into card register + * + * @param handle A Pointer to the moal_handle structure + * @param reg Register offset + * @param data Value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_sdiommc_write_reg(moal_handle *handle, t_u32 reg, + t_u32 data) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + sdio_writeb(((struct sdio_mmc_card *)handle->card)->func, (t_u8)data, + reg, (int *)&ret); + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + 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 + */ +static mlan_status woal_sdiommc_read_reg(moal_handle *handle, t_u32 reg, + t_u32 *data) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 val; + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + val = sdio_readb(((struct sdio_mmc_card *)handle->card)->func, reg, + (int *)&ret); + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + *data = val; + + return ret; +} + +/** + * @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_sdio_writeb(moal_handle *handle, t_u32 reg, t_u8 data) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + sdio_writeb(((struct sdio_mmc_card *)handle->card)->func, (t_u8)data, + reg, (int *)&ret); + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + 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_sdio_readb(moal_handle *handle, t_u32 reg, t_u8 *data) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 val; + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + val = sdio_readb(((struct sdio_mmc_card *)handle->card)->func, reg, + (int *)&ret); + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + *data = val; + + return ret; +} + +/** + * @brief This function reads data from card register FN0 + * + * @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_sdio_f0_readb(moal_handle *handle, t_u32 reg, t_u8 *data) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 val; + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + val = sdio_f0_readb(((struct sdio_mmc_card *)handle->card)->func, reg, + (int *)&ret); + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + *data = val; + + return ret; +} + +/** + * @brief This function use SG mode to read/write data into card memory + * + * @param handle A Pointer to the moal_handle structure + * @param pmbuf_list Pointer to a linked list of mlan_buffer structure + * @param port Port + * @param write write flag + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_sdio_rw_mb(moal_handle *handle, pmlan_buffer pmbuf_list, + t_u32 port, t_u8 write) +{ + struct scatterlist sg_list[SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX]; + int num_sg = pmbuf_list->use_count; + int i = 0; + mlan_buffer *pmbuf = NULL; + struct mmc_request mmc_req; + struct mmc_command mmc_cmd; + struct mmc_data mmc_dat; + struct sdio_func *func = ((struct sdio_mmc_card *)handle->card)->func; + t_u32 ioport = (port & MLAN_SDIO_IO_PORT_MASK); + t_u32 blkcnt = pmbuf_list->data_len / MLAN_SDIO_BLOCK_SIZE; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + int status; +#endif + + if (num_sg > SDIO_MP_AGGR_DEF_PKT_LIMIT_MAX) { + PRINTM(MERROR, "ERROR: num_sg=%d", num_sg); + return MLAN_STATUS_FAILURE; + } + sg_init_table(sg_list, num_sg); + pmbuf = pmbuf_list->pnext; + for (i = 0; i < num_sg; i++) { + if (pmbuf == pmbuf_list) + break; + sg_set_buf(&sg_list[i], pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + pmbuf = pmbuf->pnext; + } + memset(&mmc_req, 0, sizeof(struct mmc_request)); + memset(&mmc_cmd, 0, sizeof(struct mmc_command)); + memset(&mmc_dat, 0, sizeof(struct mmc_data)); + + mmc_dat.sg = sg_list; + mmc_dat.sg_len = num_sg; + mmc_dat.blksz = MLAN_SDIO_BLOCK_SIZE; + mmc_dat.blocks = blkcnt; + mmc_dat.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; + + mmc_cmd.opcode = SD_IO_RW_EXTENDED; + mmc_cmd.arg = write ? 1 << 31 : 0; + mmc_cmd.arg |= (func->num & 0x7) << 28; + mmc_cmd.arg |= 1 << 27; /* block basic */ + mmc_cmd.arg |= 0; /* fix address */ + mmc_cmd.arg |= (ioport & 0x1FFFF) << 9; + mmc_cmd.arg |= blkcnt & 0x1FF; + mmc_cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; + + mmc_req.cmd = &mmc_cmd; + mmc_req.data = &mmc_dat; + + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + mmc_set_data_timeout( + &mmc_dat, ((struct sdio_mmc_card *)handle->card)->func->card); + mmc_wait_for_req( + ((struct sdio_mmc_card *)handle->card)->func->card->host, + &mmc_req); + + if (mmc_cmd.error || mmc_dat.error) { + PRINTM(MERROR, "CMD53 %s cmd_error = %d data_error=%d\n", + write ? "write" : "read", mmc_cmd.error, mmc_dat.error); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0*/ + sdio_f0_writeb(((struct sdio_mmc_card *)handle->card)->func, + 0x01, SDIO_CCCR_ABORT, &status); +#endif + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + return MLAN_STATUS_FAILURE; + } + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function writes multiple bytes into card memory + * + * @param handle A Pointer to the moal_handle structure + * @param pmbuf Pointer to mlan_buffer structure + * @param port Port + * @param timeout Time out value + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_sdiommc_write_data_sync(moal_handle *handle, + mlan_buffer *pmbuf, t_u32 port, + t_u32 timeout) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *buffer = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + t_u8 blkmode = + (port & MLAN_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; + t_u32 blksz = (blkmode == BLOCK_MODE) ? MLAN_SDIO_BLOCK_SIZE : 1; + t_u32 blkcnt = (blkmode == BLOCK_MODE) ? + (pmbuf->data_len / MLAN_SDIO_BLOCK_SIZE) : + pmbuf->data_len; + t_u32 ioport = (port & MLAN_SDIO_IO_PORT_MASK); + int status = 0; + if (pmbuf->use_count > 1) + return woal_sdio_rw_mb(handle, pmbuf, port, MTRUE); +#ifdef SDIO_MMC_DEBUG + handle->cmd53w = 1; +#endif + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + status = sdio_writesb(((struct sdio_mmc_card *)handle->card)->func, + ioport, buffer, blkcnt * blksz); + if (!status) + ret = MLAN_STATUS_SUCCESS; + else { + PRINTM(MERROR, "cmd53 write error=%d\n", status); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0*/ + sdio_f0_writeb(((struct sdio_mmc_card *)handle->card)->func, + 0x01, SDIO_CCCR_ABORT, &status); +#endif + } + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#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 + */ +static mlan_status woal_sdiommc_read_data_sync(moal_handle *handle, + mlan_buffer *pmbuf, t_u32 port, + t_u32 timeout) +{ + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *buffer = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + t_u8 blkmode = + (port & MLAN_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; + t_u32 blksz = (blkmode == BLOCK_MODE) ? MLAN_SDIO_BLOCK_SIZE : 1; + t_u32 blkcnt = (blkmode == BLOCK_MODE) ? + (pmbuf->data_len / MLAN_SDIO_BLOCK_SIZE) : + pmbuf->data_len; + t_u32 ioport = (port & MLAN_SDIO_IO_PORT_MASK); + int status = 0; + if (pmbuf->use_count > 1) + return woal_sdio_rw_mb(handle, pmbuf, port, MFALSE); +#ifdef SDIO_MMC_DEBUG + handle->cmd53r = 1; +#endif + sdio_claim_host(((struct sdio_mmc_card *)handle->card)->func); + status = sdio_readsb(((struct sdio_mmc_card *)handle->card)->func, + buffer, ioport, blkcnt * blksz); + if (!status) { + ret = MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, "cmd53 read error=%d\n", status); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* issue abort cmd52 command through F0*/ + sdio_f0_writeb(((struct sdio_mmc_card *)handle->card)->func, + 0x01, SDIO_CCCR_ABORT, &status); +#endif + } + sdio_release_host(((struct sdio_mmc_card *)handle->card)->func); +#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_sdiommc_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_sdiommc_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 + */ +static void woal_sdiommc_unregister_dev(moal_handle *handle) +{ + ENTER(); + if (handle->card) { + struct sdio_mmc_card *card = handle->card; + /* Release the SDIO IRQ */ + sdio_claim_host(card->func); + sdio_release_irq(card->func); + sdio_disable_func(card->func); + sdio_release_host(card->func); + + sdio_set_drvdata(card->func, NULL); + + PRINTM(MWARN, "Making the sdio dev card as NULL\n"); + card->handle = NULL; + } + + LEAVE(); +} + +/** + * @brief This function registers the device + * + * @param handle A pointer to moal_handle structure + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_sdiommc_register_dev(moal_handle *handle) +{ + int ret = MLAN_STATUS_SUCCESS; + struct sdio_mmc_card *card = handle->card; + struct sdio_func *func; + + ENTER(); + + /* save adapter pointer in card */ + card->handle = handle; + 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); + + 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; +} + +/** + * @brief This function check if this is second mac + * + * @param handle A pointer to moal_handle structure + * @return MTRUE/MFALSE + * + */ +static t_u8 woal_sdiommc_is_second_mac(moal_handle *handle) +{ +#ifdef SD9098 + struct sdio_mmc_card *card = (struct sdio_mmc_card *)handle->card; + if (card->func->device == SD_DEVICE_ID_9098_FN2) + return MTRUE; +#endif + return MFALSE; +} + +static mlan_status woal_sdiommc_get_fw_name(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#ifdef SD9098 + struct sdio_mmc_card *card = (struct sdio_mmc_card *)handle->card; +#endif + t_u32 revision_id = 0; + t_u32 rev_id_reg = handle->card_info->rev_id_reg; + +#if defined(SD8987) || defined(SD8997) || defined(SD9098) || \ + defined(SD9097) || defined(SD8978) + t_u32 magic_reg = handle->card_info->magic_reg; + t_u32 magic = 0; + t_u32 host_strap_reg = handle->card_info->host_strap_reg; + t_u32 strap = 0; +#endif + + ENTER(); + + if (handle->params.fw_name) + goto done; + /** Revision ID register */ + woal_sdiommc_read_reg(handle, rev_id_reg, &revision_id); + PRINTM(MCMND, "revision_id=0x%x\n", revision_id); + +#if defined(SD8987) || defined(SD8997) || defined(SD9098) || \ + defined(SD9097) || defined(SD8978) + /** Revision ID register */ + woal_sdiommc_read_reg(handle, magic_reg, &magic); + /** Revision ID register */ + woal_sdiommc_read_reg(handle, host_strap_reg, &strap); + strap &= 0x1; + magic &= 0xFF; + /* 1 = SDSD, 0 --SD UART */ + PRINTM(MCMND, "magic=0x%x strap=0x%x\n", magic, strap); +#endif +#if defined(SD8977) + if (IS_SD8977(handle->card_type)) { + switch (revision_id) { + case SD8977_V0: + strcpy(handle->card_info->fw_name, SD8977_V0_FW_NAME); + strcpy(handle->card_info->fw_name_wlan, + SD8977_WLAN_V0_FW_NAME); + break; + case SD8977_V1: + strcpy(handle->card_info->fw_name, SD8977_V1_FW_NAME); + strcpy(handle->card_info->fw_name_wlan, + SD8977_WLAN_V1_FW_NAME); + break; + case SD8977_V2: + strcpy(handle->card_info->fw_name, SD8977_V2_FW_NAME); + strcpy(handle->card_info->fw_name_wlan, + SD8977_WLAN_V2_FW_NAME); + break; + default: + break; + } + } +#endif +#if defined(SD8887) + if (IS_SD8887(handle->card_type)) { + /* Check revision ID */ + switch (revision_id) { + case SD8887_A0: + strcpy(handle->card_info->fw_name, SD8887_A0_FW_NAME); + strcpy(handle->card_info->fw_name_wlan, + SD8887_WLAN_A0_FW_NAME); + break; + case SD8887_A2: + strcpy(handle->card_info->fw_name, SD8887_A2_FW_NAME); + strcpy(handle->card_info->fw_name_wlan, + SD8887_WLAN_A2_FW_NAME); + break; + default: + break; + } + } +#endif + +#ifdef SD8997 + if (IS_SD8997(handle->card_type)) { + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_SD_UART) + strcpy(handle->card_info->fw_name, + SDUART8997_DEFAULT_COMBO_FW_NAME); + else + strcpy(handle->card_info->fw_name, + SDSD8997_DEFAULT_COMBO_FW_NAME); + } + } +#endif + +#ifdef SD8987 + if (IS_SD8987(handle->card_type)) { + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_SD_UART) + strcpy(handle->card_info->fw_name, + SDUART8987_DEFAULT_COMBO_FW_NAME); + else + strcpy(handle->card_info->fw_name, + SDSD8987_DEFAULT_COMBO_FW_NAME); + } + } +#endif + +#ifdef SD8978 + if (IS_SD8978(handle->card_type)) { + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_SD_UART) + strcpy(handle->card_info->fw_name, + SDUART8978_DEFAULT_COMBO_FW_NAME); + else + strcpy(handle->card_info->fw_name, + SDSD8978_DEFAULT_COMBO_FW_NAME); + } + } +#endif + +#ifdef SD9098 + if (IS_SD9098(handle->card_type) && + (card->func->device == SD_DEVICE_ID_9098_FN1)) { + switch (revision_id) { + case SD9098_Z1Z2: + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_SD_UART) + strcpy(handle->card_info->fw_name, + SDUART9098_DEFAULT_COMBO_FW_NAME); + else + strcpy(handle->card_info->fw_name, + SDSD9098_DEFAULT_COMBO_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + SD9098_DEFAULT_WLAN_FW_NAME); + break; + case SD9098_A0: + case SD9098_A1: + case SD9098_A2: + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_SD_UART) + strcpy(handle->card_info->fw_name, + SDUART9098_COMBO_V1_FW_NAME); + else + strcpy(handle->card_info->fw_name, + SDSD9098_COMBO_V1_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + SD9098_WLAN_V1_FW_NAME); + break; + default: + break; + } + } +#endif +#ifdef SD9097 + if (IS_SD9097(handle->card_type)) { + switch (revision_id) { + case SD9097_B0: + case SD9097_B1: + if (magic == CHIP_MAGIC_VALUE) { + if (strap == CARD_TYPE_SD_UART) + strcpy(handle->card_info->fw_name, + SDUART9097_COMBO_V1_FW_NAME); + else + strcpy(handle->card_info->fw_name, + SDSD9097_COMBO_V1_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + SD9097_WLAN_V1_FW_NAME); + break; + default: + break; + } + } +#endif +done: + PRINTM(MCMND, "combo fw:%s wlan fw:%s \n", handle->card_info->fw_name, + handle->card_info->fw_name_wlan); + LEAVE(); + return ret; +} + +#define DEBUG_FW_DONE 0xFF +#define DEBUG_MEMDUMP_FINISH 0xFE +#define MAX_POLL_TRIES 100 + +typedef enum { + DUMP_TYPE_ITCM = 0, + DUMP_TYPE_DTCM = 1, + DUMP_TYPE_SQRAM = 2, + DUMP_TYPE_APU_REGS = 3, + DUMP_TYPE_CIU_REGS = 4, + DUMP_TYPE_ICU_REGS = 5, + DUMP_TYPE_MAC_REGS = 6, + DUMP_TYPE_EXTEND_7 = 7, + DUMP_TYPE_EXTEND_8 = 8, + DUMP_TYPE_EXTEND_9 = 9, + DUMP_TYPE_EXTEND_10 = 10, + DUMP_TYPE_EXTEND_11 = 11, + DUMP_TYPE_EXTEND_12 = 12, + DUMP_TYPE_EXTEND_13 = 13, + DUMP_TYPE_EXTEND_LAST = 14 +} dumped_mem_type; + +#define MAX_NAME_LEN 8 +#define MAX_FULL_NAME_LEN 32 + +typedef struct { + t_u8 mem_name[MAX_NAME_LEN]; + t_u8 *mem_Ptr; + struct file *pfile_mem; + t_u8 done_flag; + t_u8 type; +} memory_type_mapping; + +memory_type_mapping mem_type_mapping_tbl[] = { + {"ITCM", NULL, NULL, 0xF0, FW_DUMP_TYPE_MEM_ITCM}, + {"DTCM", NULL, NULL, 0xF1, FW_DUMP_TYPE_MEM_DTCM}, + {"SQRAM", NULL, NULL, 0xF2, FW_DUMP_TYPE_MEM_SQRAM}, + {"APU", NULL, NULL, 0xF3, FW_DUMP_TYPE_REG_APU}, + {"CIU", NULL, NULL, 0xF4, FW_DUMP_TYPE_REG_CIU}, + {"ICU", NULL, NULL, 0xF5, FW_DUMP_TYPE_REG_ICU}, + {"MAC", NULL, NULL, 0xF6, FW_DUMP_TYPE_REG_MAC}, + {"EXT7", NULL, NULL, 0xF7, 0}, + {"EXT8", NULL, NULL, 0xF8, 0}, + {"EXT9", NULL, NULL, 0xF9, 0}, + {"EXT10", NULL, NULL, 0xFA, 0}, + {"EXT11", NULL, NULL, 0xFB, 0}, + {"EXT12", NULL, NULL, 0xFC, 0}, + {"EXT13", NULL, NULL, 0xFD, 0}, + {"EXTLAST", NULL, NULL, 0xFE, 0}, +}; +memory_type_mapping mem_type_mapping_tbl_8977_8997 = {"DUMP", NULL, NULL, 0xDD, + 0}; + +typedef enum { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +} rdwr_status; + +/** + * @brief This function read/write firmware via cmd52 + * + * @param phandle A pointer to moal_handle + * @param doneflag A flag + * + * @return MLAN_STATUS_SUCCESS + */ +rdwr_status woal_cmd52_rdwr_firmware(moal_handle *phandle, t_u8 doneflag) +{ + int ret = 0; + int tries = 0; + t_u8 ctrl_data = 0; + t_u8 dbg_dump_ctrl_reg = phandle->card_info->dump_fw_ctrl_reg; + t_u8 debug_host_ready = phandle->card_info->dump_fw_host_ready; + + ret = woal_sdio_writeb(phandle, dbg_dump_ctrl_reg, debug_host_ready); + if (ret) { + PRINTM(MERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = woal_sdio_readb(phandle, dbg_dump_ctrl_reg, &ctrl_data); + if (ret) { + PRINTM(MERROR, "SDIO READ ERR\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == DEBUG_FW_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != debug_host_ready) { + PRINTM(MMSG, + "The ctrl reg was changed, re-try again!\n"); + ret = woal_sdio_writeb(phandle, dbg_dump_ctrl_reg, + debug_host_ready); + if (ret) { + PRINTM(MERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + } + udelay(100); + } + if (ctrl_data == debug_host_ready) { + PRINTM(MERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + return RDWR_STATUS_SUCCESS; +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void woal_dump_firmware_info_v2(moal_handle *phandle) +{ + int ret = 0; + unsigned int reg, reg_start, reg_end; + t_u8 *dbg_ptr = NULL; + t_u32 sec, usec; + t_u8 dump_num = 0; + t_u8 idx = 0; + t_u8 doneflag = 0; + rdwr_status stat; + t_u8 i = 0; + t_u8 read_reg = 0; + t_u32 memory_size = 0; + t_u8 path_name[64], file_name[32], firmware_dump_file[128]; + t_u8 *end_ptr = NULL; + t_u8 dbg_dump_start_reg = 0; + t_u8 dbg_dump_end_reg = 0; + t_u8 dbg_dump_ctrl_reg = 0; + + if (!phandle) { + PRINTM(MERROR, "Could not dump firmwware info\n"); + return; + } + + dbg_dump_start_reg = phandle->card_info->dump_fw_start_reg; + dbg_dump_end_reg = phandle->card_info->dump_fw_end_reg; + dbg_dump_ctrl_reg = phandle->card_info->dump_fw_ctrl_reg; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /** Create dump directort*/ + woal_create_dump_dir(phandle, path_name, sizeof(path_name)); +#else + memset(path_name, 0, sizeof(path_name)); + strcpy(path_name, "/data"); +#endif + PRINTM(MMSG, "Directory name is %s\n", path_name); + + woal_dump_drv_info(phandle, path_name); + + /* start dump fw memory */ + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "==== DEBUG MODE OUTPUT START: %u.%06u ====\n", sec, usec); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == woal_cmd52_rdwr_firmware(phandle, doneflag)) + goto done; + reg = dbg_dump_start_reg; + ret = woal_sdio_readb(phandle, reg, &dump_num); + if (ret) { + PRINTM(MMSG, "SDIO READ MEM NUM ERR\n"); + goto done; + } + + /* read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + if (RDWR_STATUS_FAILURE == + woal_cmd52_rdwr_firmware(phandle, doneflag)) + goto done; + memory_size = 0; + reg = dbg_dump_start_reg; + for (i = 0; i < 4; i++) { + ret = woal_sdio_readb(phandle, reg, &read_reg); + if (ret) { + PRINTM(MMSG, "SDIO READ ERR\n"); + goto done; + } + memory_size |= (read_reg << i * 8); + reg++; + } + if (memory_size == 0) { + PRINTM(MMSG, "Firmware Dump Finished!\n"); + ret = woal_sdiommc_write_reg(phandle, dbg_dump_ctrl_reg, + DEBUG_MEMDUMP_FINISH); + if (ret) { + PRINTM(MERROR, + "SDIO Write MEMDUMP_FINISH ERR\n"); + goto done; + } + break; + } else { + PRINTM(MMSG, "%s_SIZE=0x%x\n", + mem_type_mapping_tbl[idx].mem_name, memory_size); + ret = moal_vmalloc( + phandle, memory_size + 1, + (t_u8 **)&mem_type_mapping_tbl[idx].mem_Ptr); + if ((ret != MLAN_STATUS_SUCCESS) || + !mem_type_mapping_tbl[idx].mem_Ptr) { + PRINTM(MERROR, + "Error: vmalloc %s buffer failed!!!\n", + mem_type_mapping_tbl[idx].mem_name); + goto done; + } + dbg_ptr = mem_type_mapping_tbl[idx].mem_Ptr; + end_ptr = dbg_ptr + memory_size; + } + doneflag = mem_type_mapping_tbl[idx].done_flag; + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "Start %s output %u.%06u, please wait...\n", + mem_type_mapping_tbl[idx].mem_name, sec, usec); + do { + stat = woal_cmd52_rdwr_firmware(phandle, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + reg_start = dbg_dump_start_reg; + reg_end = dbg_dump_end_reg; + for (reg = reg_start; reg <= reg_end; reg++) { + ret = woal_sdio_readb(phandle, reg, dbg_ptr); + if (ret) { + PRINTM(MMSG, "SDIO READ ERR\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + PRINTM(MMSG, + "pre-allocced buf is not enough\n"); + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MMSG, + "%s done:" +#ifdef MLAN_64BIT + "size = 0x%lx\n", +#else + "size = 0x%x\n", +#endif + mem_type_mapping_tbl[idx].mem_name, + dbg_ptr - mem_type_mapping_tbl[idx] + .mem_Ptr); + memset(file_name, 0, sizeof(file_name)); + sprintf(file_name, "%s%s", "file_sdio_", + mem_type_mapping_tbl[idx].mem_name); + if (MLAN_STATUS_SUCCESS != + woal_save_dump_info_to_file( + path_name, file_name, + mem_type_mapping_tbl[idx].mem_Ptr, + memory_size)) + PRINTM(MERROR, + "Can't save dump file %s in %s\n", + file_name, path_name); + moal_vfree(phandle, + mem_type_mapping_tbl[idx].mem_Ptr); + mem_type_mapping_tbl[idx].mem_Ptr = NULL; + break; + } + } while (1); + } + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "==== DEBUG MODE OUTPUT END: %u.%06u ====\n", sec, usec); + /* end dump fw memory */ + memset(firmware_dump_file, 0, sizeof(firmware_dump_file)); + sprintf(firmware_dump_file, "%s/%s", path_name, file_name); + moal_memcpy_ext(phandle, phandle->firmware_dump_file, + firmware_dump_file, sizeof(firmware_dump_file), + sizeof(phandle->firmware_dump_file)); +done: + for (idx = 0; idx < dump_num; idx++) { + if (mem_type_mapping_tbl[idx].mem_Ptr) { + moal_vfree(phandle, mem_type_mapping_tbl[idx].mem_Ptr); + mem_type_mapping_tbl[idx].mem_Ptr = NULL; + } + } + PRINTM(MMSG, "==== DEBUG MODE END ====\n"); + return; +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void woal_dump_firmware_info_v3(moal_handle *phandle) +{ + int ret = 0; + int tries = 0; + unsigned int reg, reg_start, reg_end; + t_u8 *dbg_ptr = NULL; + t_u8 *temp_Ptr = NULL; + t_u32 sec, usec; + t_u8 start_flag = 0; + t_u8 doneflag = 0; + rdwr_status stat; + t_u32 memory_size = 0; + t_u8 path_name[64], file_name[32], firmware_dump_file[128]; + moal_handle *ref_handle; + t_u8 *end_ptr = NULL; + t_u8 dbg_dump_start_reg = 0; + t_u8 dbg_dump_end_reg = 0; + memory_type_mapping *pmem_type_mapping_tbl = + &mem_type_mapping_tbl_8977_8997; + + if (!phandle) { + PRINTM(MERROR, "Could not dump firmwware info\n"); + return; + } + + dbg_dump_start_reg = phandle->card_info->dump_fw_start_reg; + dbg_dump_end_reg = phandle->card_info->dump_fw_end_reg; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + /** Create dump directort*/ + woal_create_dump_dir(phandle, path_name, sizeof(path_name)); +#else + memset(path_name, 0, sizeof(path_name)); + strcpy(path_name, "/data"); +#endif + PRINTM(MMSG, "Directory name is %s\n", path_name); + ref_handle = (moal_handle *)phandle->pref_mac; + if (ref_handle) + woal_dump_drv_info(ref_handle, path_name); + woal_dump_drv_info(phandle, path_name); + + /* start dump fw memory */ + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "==== DEBUG MODE OUTPUT START: %u.%06u ====\n", sec, usec); + /* read the number of the memories which will dump */ + if (RDWR_STATUS_FAILURE == woal_cmd52_rdwr_firmware(phandle, doneflag)) + goto done; + + /** check the reg which indicate dump starting */ + for (reg = dbg_dump_start_reg; reg <= dbg_dump_end_reg; reg++) { + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = woal_sdio_readb(phandle, reg, &start_flag); + if (ret) { + PRINTM(MMSG, "SDIO READ ERR\n"); + goto done; + } + /** 0 means dump starting*/ + if (start_flag == 0) + break; + udelay(100); + } + if (tries == MAX_POLL_TRIES) { + PRINTM(MMSG, "FW not ready to dump\n"); + goto done; + } + } + memory_size = 0xF0000; + PRINTM(MMSG, "%s_SIZE=0x%x\n", pmem_type_mapping_tbl->mem_name, + memory_size); + ret = moal_vmalloc(phandle, memory_size + 1, + (t_u8 **)&pmem_type_mapping_tbl->mem_Ptr); + if ((ret != MLAN_STATUS_SUCCESS) || !pmem_type_mapping_tbl->mem_Ptr) { + PRINTM(MERROR, "Error: vmalloc %s buffer failed!!!\n", + pmem_type_mapping_tbl->mem_name); + goto done; + } + dbg_ptr = pmem_type_mapping_tbl->mem_Ptr; + end_ptr = dbg_ptr + memory_size; + doneflag = pmem_type_mapping_tbl->done_flag; + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "Start %s output %u.%06u, please wait...\n", + pmem_type_mapping_tbl->mem_name, sec, usec); + do { + stat = woal_cmd52_rdwr_firmware(phandle, doneflag); + if (RDWR_STATUS_FAILURE == stat) + goto done; + reg_start = dbg_dump_start_reg; + reg_end = dbg_dump_end_reg; + for (reg = reg_start; reg <= reg_end; reg++) { + ret = woal_sdio_readb(phandle, reg, dbg_ptr); + if (ret) { + PRINTM(MMSG, "SDIO READ ERR\n"); + goto done; + } + dbg_ptr++; + if (dbg_ptr >= end_ptr) { + PRINTM(MMSG, + "pre-allocced buf is not enough\n"); + ret = moal_vmalloc(phandle, + memory_size + 0x4000 + 1, + (t_u8 **)&temp_Ptr); + if ((ret != MLAN_STATUS_SUCCESS) || !temp_Ptr) { + PRINTM(MERROR, + "Error: vmalloc buffer failed!!!\n"); + goto done; + } + moal_memcpy_ext(phandle, temp_Ptr, + pmem_type_mapping_tbl->mem_Ptr, + memory_size, + memory_size + 0x4000); + moal_vfree(phandle, + pmem_type_mapping_tbl->mem_Ptr); + pmem_type_mapping_tbl->mem_Ptr = temp_Ptr; + temp_Ptr = NULL; + dbg_ptr = pmem_type_mapping_tbl->mem_Ptr + + memory_size; + memory_size += 0x4000; + end_ptr = pmem_type_mapping_tbl->mem_Ptr + + memory_size; + } + } + if (RDWR_STATUS_DONE == stat) { + PRINTM(MMSG, + "%s done:" +#ifdef MLAN_64BIT + "size = 0x%lx\n", +#else + "size = 0x%x\n", +#endif + pmem_type_mapping_tbl->mem_name, + dbg_ptr - pmem_type_mapping_tbl->mem_Ptr); + memset(file_name, 0, sizeof(file_name)); + sprintf(file_name, "%s%s", "file_sdio_", + pmem_type_mapping_tbl->mem_name); + if (MLAN_STATUS_SUCCESS != + woal_save_dump_info_to_file( + path_name, file_name, + pmem_type_mapping_tbl->mem_Ptr, + dbg_ptr - pmem_type_mapping_tbl->mem_Ptr)) + PRINTM(MERROR, + "Can't save dump file %s in %s\n", + file_name, path_name); + moal_vfree(phandle, pmem_type_mapping_tbl->mem_Ptr); + pmem_type_mapping_tbl->mem_Ptr = NULL; + break; + } + } while (1); + moal_get_system_time(phandle, &sec, &usec); + PRINTM(MMSG, "==== DEBUG MODE OUTPUT END: %u.%06u ====\n", sec, usec); + /* end dump fw memory */ + memset(firmware_dump_file, 0, sizeof(firmware_dump_file)); + sprintf(firmware_dump_file, "%s/%s", path_name, file_name); + moal_memcpy_ext(phandle, phandle->firmware_dump_file, + firmware_dump_file, sizeof(firmware_dump_file), + sizeof(phandle->firmware_dump_file)); +done: + if (pmem_type_mapping_tbl->mem_Ptr) { + moal_vfree(phandle, pmem_type_mapping_tbl->mem_Ptr); + pmem_type_mapping_tbl->mem_Ptr = NULL; + } + PRINTM(MMSG, "==== DEBUG MODE END ====\n"); + return; +} + +/** + * @brief This function reads and displays SDIO registers for debugging + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +static void woal_sdiommc_reg_dbg(moal_handle *phandle) +{ + int ret = 0; + t_u8 loop, index = 0, func, data; + unsigned int reg, reg_start, reg_end; + unsigned int scratch_reg = phandle->card_info->scratch_reg; + t_u8 *reg_table = phandle->card_info->dump_reg.reg_table; + t_u8 reg_table_size = phandle->card_info->dump_reg.reg_table_size; + char buf[256], *ptr; + + mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); + for (loop = 0; loop < 5; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + } else if (loop == 1) { + /* Read the registers of SDIO function1 */ + func = loop; + reg_start = phandle->card_info->func1_reg_start; + reg_end = phandle->card_info->func1_reg_end; + } else if (loop == 2) { + /* Read specific registers of SDIO function1 */ + index = 0; + func = 1; + reg_start = reg_table[index++]; + reg_end = reg_table[reg_table_size - 1]; + } else { + /* Read the scratch registers of SDIO function1 */ + if (loop == 4) + mdelay(100); + func = 1; + reg_start = scratch_reg; + reg_end = scratch_reg + 10; + } + if (loop != 2) + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, + reg_start, reg_end); + else + ptr += sprintf(ptr, "SDIO Func%d: ", func); + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + ret = woal_sdio_f0_readb(phandle, reg, &data); + else + ret = woal_sdio_readb(phandle, reg, &data); + if (loop == 2) + ptr += sprintf(ptr, "(%#x) ", reg); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + if (loop == 2 && reg < reg_end) + reg = reg_table[index++]; + else + reg++; + } + PRINTM(MMSG, "%s\n", buf); + } + mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +static void woal_sdiommc_dump_fw_info(moal_handle *phandle) +{ + if (!phandle) { + PRINTM(MERROR, "Could not dump firmwware info\n"); + return; + } + mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); + phandle->fw_dump = MTRUE; + if (phandle->card_info->dump_fw_info == DUMP_FW_SDIO_V2) { + woal_dump_firmware_info_v2(phandle); + } else if (phandle->card_info->dump_fw_info == DUMP_FW_SDIO_V3) { + woal_dump_firmware_info_v3(phandle); + } + phandle->fw_dump = MFALSE; + mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); + queue_work(phandle->workqueue, &phandle->main_work); + return; +} + +/** + * @brief This function save sdio reg info + * + * @param phandle A pointer to moal_handle + * @param buf A pointer buffer saving log + * + * @return The length of this log + */ +static int woal_sdiommc_dump_reg_info(moal_handle *phandle, t_u8 *drv_buf) +{ + char *drv_ptr = (char *)drv_buf; + int ret = 0; + t_u8 loop, index = 0, func, data; + unsigned int reg, reg_start, reg_end; + unsigned int scratch_reg = 0; + t_u8 *reg_table = NULL; + t_u8 reg_table_size = 0; + char buf[256], *ptr; + + ENTER(); + + if (!phandle || !drv_buf) { + PRINTM(MMSG, "%s: can't retreive info\n", __func__); + LEAVE(); + return 0; + } + + scratch_reg = phandle->card_info->scratch_reg; + reg_table = phandle->card_info->dump_reg.reg_table; + reg_table_size = phandle->card_info->dump_reg.reg_table_size; + + mlan_pm_wakeup_card(phandle->pmlan_adapter, MTRUE); + + drv_ptr += sprintf(drv_ptr, "--------sdio_reg_debug_info---------\n"); + for (loop = 0; loop < 5; loop++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + if (loop == 0) { + /* Read the registers of SDIO function0 */ + func = loop; + reg_start = 0; + reg_end = 9; + + } else if (loop == 1) { + /* Read the registers of SDIO function1 */ + func = loop; + reg_start = phandle->card_info->func1_reg_start; + reg_end = phandle->card_info->func1_reg_end; + } else if (loop == 2) { + /* Read specific registers of SDIO function1 */ + index = 0; + func = 1; + reg_start = reg_table[index++]; + reg_end = reg_table[reg_table_size - 1]; + } else { + /* Read the scratch registers of SDIO function1 */ + if (loop == 4) + mdelay(100); + func = 1; + reg_start = scratch_reg; + reg_end = scratch_reg + 10; + } + if (loop != 2) + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", func, + reg_start, reg_end); + else + ptr += sprintf(ptr, "SDIO Func%d: ", func); + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + ret = woal_sdio_f0_readb(phandle, reg, &data); + else + ret = woal_sdio_readb(phandle, reg, &data); + + if (loop == 2) + ptr += sprintf(ptr, "(%#x) ", reg); + if (!ret) + ptr += sprintf(ptr, "%02x ", data); + else { + ptr += sprintf(ptr, "ERR"); + break; + } + if (loop == 2 && reg < reg_end) + reg = reg_table[index++]; + else + reg++; + } + drv_ptr += sprintf(drv_ptr, "%s\n", buf); + } + + drv_ptr += + sprintf(drv_ptr, "--------sdio_reg_debug_info End---------\n"); + mlan_pm_wakeup_card(phandle->pmlan_adapter, MFALSE); + + LEAVE(); + return drv_ptr - (char *)drv_buf; +} + +static moal_if_ops sdiommc_ops = { + .register_dev = woal_sdiommc_register_dev, + .unregister_dev = woal_sdiommc_unregister_dev, + .read_reg = woal_sdiommc_read_reg, + .write_reg = woal_sdiommc_write_reg, + .read_data_sync = woal_sdiommc_read_data_sync, + .write_data_sync = woal_sdiommc_write_data_sync, + .get_fw_name = woal_sdiommc_get_fw_name, + .dump_fw_info = woal_sdiommc_dump_fw_info, + .dump_reg_info = woal_sdiommc_dump_reg_info, + .reg_dbg = woal_sdiommc_reg_dbg, + .is_second_mac = woal_sdiommc_is_second_mac, +}; diff --git a/mxm_wifiex/wlan_src/mlinux/moal_shim.c b/mxm_wifiex/wlan_src/mlinux/moal_shim.c new file mode 100644 index 0000000..6432b76 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_shim.c @@ -0,0 +1,3231 @@ +/** @file moal_shim.c + * + * @brief This file contains the callback functions registered to MLAN + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 USB +#include "moal_usb.h" +#endif +#ifdef SDIO +#include "moal_sdio.h" +#endif +#ifdef PCIE +#include "moal_pcie.h" +#endif +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#include "moal_cfg80211.h" +#include "moal_cfg80211_util.h" +#endif +#include + +/******************************************************** + Local Variables +********************************************************/ +/** moal_lock */ +typedef struct _moal_lock { + /** Lock */ + spinlock_t lock; + /** Flags */ + unsigned long flags; +} moal_lock; + +/******************************************************** + Global Variables +********************************************************/ + +extern int wifi_status; + +/******************************************************** + 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(t_void *pmoal_handle, t_u32 size, t_u32 flag, + t_u8 **ppbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + t_u32 mem_flag = + (in_interrupt() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + +#ifdef USB + if (!IS_USB(handle->card_type)) +#endif + { + if (flag & MLAN_MEM_DMA) + mem_flag |= GFP_DMA; + } + *ppbuf = kzalloc(size, mem_flag); + if (*ppbuf == NULL) { + PRINTM(MERROR, "%s: allocate memory (%d bytes) failed!\n", + __func__, (int)size); + return MLAN_STATUS_FAILURE; + } + atomic_inc(&handle->malloc_count); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Free a buffer + * + * @param pmoal_handle Pointer to the MOAL context + * @param pbuf Pointer to the buffer to be freed + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_mfree(t_void *pmoal_handle, t_u8 *pbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + + if (!pbuf) + return MLAN_STATUS_FAILURE; + kfree(pbuf); + atomic_dec(&handle->malloc_count); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Alloc a vitual-address-continuous buffer + * + * @param pmoal_handle Pointer to the MOAL context + * @param size The size of the buffer to be allocated + * @param ppbuf Pointer to a buffer location to store buffer pointer + * allocated + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_vmalloc(t_void *pmoal_handle, t_u32 size, t_u8 **ppbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + + *ppbuf = vmalloc(size); + if (*ppbuf == NULL) { + PRINTM(MERROR, "%s: vmalloc (%d bytes) failed!", __func__, + (int)size); + return MLAN_STATUS_FAILURE; + } + atomic_inc(&handle->vmalloc_count); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Free a buffer allocated by vmalloc + * + * @param pmoal_handle Pointer to the MOAL context + * @param pbuf Pointer to the buffer to be freed + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_vfree(t_void *pmoal_handle, t_u8 *pbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + + if (!pbuf) + return MLAN_STATUS_FAILURE; + vfree(pbuf); + atomic_dec(&handle->vmalloc_count); + return MLAN_STATUS_SUCCESS; +} + +#ifdef PCIE +/** + * @brief Alloc a consistent block of memory + * + * @param pmoal_handle Pointer to the MOAL context + * @param size The size of the buffer to be allocated + * @param ppbuf Pointer to a buffer location to store memory allocated + * @param pbuf_pa Pointer to a buffer location to store physical address + * of above memory + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_malloc_consistent(t_void *pmoal_handle, t_u32 size, + t_u8 **ppbuf, t_pu64 pbuf_pa) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + pcie_service_card *card = (pcie_service_card *)handle->card; + *pbuf_pa = 0; + *ppbuf = (t_u8 *)pci_alloc_consistent(card->dev, size, + (dma_addr_t *)pbuf_pa); + if (*ppbuf == NULL) { + PRINTM(MERROR, + "%s: allocate consistent memory (%d bytes) failed!\n", + __func__, (int)size); + return MLAN_STATUS_FAILURE; + } + atomic_inc(&handle->malloc_cons_count); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Free a consistent block of memory + * + * @param pmoal_handle Pointer to the MOAL context + * @param size Size of them memory to be freed + * @param pbuf Pointer to the memory to be freed + * @param buf_pa Physical address of the memory to be freed + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_mfree_consistent(t_void *pmoal_handle, t_u32 size, t_u8 *pbuf, + t_u64 buf_pa) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + pcie_service_card *card = handle->card; + + if (!pbuf) + return MLAN_STATUS_FAILURE; + + pci_free_consistent(card->dev, size, pbuf, buf_pa); + atomic_dec(&handle->malloc_cons_count); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Map a block of memory to device + * + * @param pmoal_handle Pointer to the MOAL context + * @param pbuf Pointer to the buffer to be mapped + * @param pbuf_pa Pointer to store the physical address of buffer + * @param size Size of the buffer to be mapped + * @param flag Flags for mapping IO + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_map_memory(t_void *pmoal_handle, t_u8 *pbuf, t_u64 *pbuf_pa, + t_u32 size, t_u32 flag) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + pcie_service_card *card = (pcie_service_card *)handle->card; + + dma_addr_t dma; + + /* Init memory to device */ + dma = pci_map_single(card->dev, pbuf, size, flag); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) + if (pci_dma_mapping_error(card->dev, dma)) { +#else + if (pci_dma_mapping_error(dma)) { +#endif + PRINTM(MERROR, "Tx ring: failed to pci_map_single\n"); + return MLAN_STATUS_FAILURE; + } + + *pbuf_pa = dma; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Unmap a block of memory from device + * + * @param pmoal_handle Pointer to the MOAL context + * @param pbuf Pointer to the buffer to unmap + * @param buf_pa Physical address of buffer to unmap + * @param size Size of the buffer to unmap + * @param flag Flags for mapping IO + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_unmap_memory(t_void *pmoal_handle, t_u8 *pbuf, t_u64 buf_pa, + t_u32 size, t_u32 flag) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + pcie_service_card *card = (pcie_service_card *)handle->card; + + pci_unmap_single(card->dev, buf_pa, size, flag); + + return MLAN_STATUS_SUCCESS; +} +#endif /* PCIE */ + +/** + * @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(t_void *pmoal_handle, t_void *pmem, t_u8 byte, 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(t_void *pmoal_handle, t_void *pdest, const t_void *psrc, + t_u32 num) +{ + t_void *p = pdest; + + if (pdest && psrc && num) + p = memcpy(pdest, psrc, 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 + * @param dest_size size of dest memory. + * + * @return Pointer to the dest memory + */ +t_void *moal_memcpy_ext(t_void *pmoal_handle, t_void *pdest, const t_void *psrc, + t_u32 num, t_u32 dest_size) +{ + t_void *p = pdest; + if (pdest && psrc && num && dest_size) + p = memcpy(pdest, psrc, MIN(num, dest_size)); + + 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(t_void *pmoal_handle, t_void *pdest, const t_void *psrc, + 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(t_void *pmoal_handle, const t_void *pmem1, + const t_void *pmem2, 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(t_void *pmoal_handle, t_u32 delay) +{ + if (delay >= 1000) + msleep(delay / 1000); + if (delay % 1000) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + usleep_range(delay % 1000 - 1, delay % 1000); +#else + udelay(delay % 1000); +#endif +} + +/** + * @brief usleep_range function + * + * @param pmoal_handle Pointer to the MOAL context + * @param min_delay minimal delay in micro-second + * @param max_delay delay in micro-second + * + * @return N/A + */ +t_void moal_usleep_range(t_void *pmoal_handle, t_u32 min_delay, t_u32 max_delay) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) + usleep_range(min_delay, max_delay); +#endif +} +/** + * @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(t_void *pmoal_handle, t_u32 *psec, + t_u32 *pusec) +{ + wifi_timeval t; + + woal_get_monotonic_time(&t); + *psec = t.time_sec; + *pusec = t.time_usec; + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Retrieves the current boot time + * + * @param pmoal_handle Pointer to the MOAL context + * @param pnsec Pointer to buf for the Nanoseconds of boot time + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status moal_get_boot_ktime(t_void *pmoal_handle, t_u64 *pnsec) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) + ktime_t time; + + time = ktime_get_with_offset(TK_OFFS_BOOT); + *pnsec = *(t_u64 *)&(time); +#endif + 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(t_void *pmoal_handle, t_void **pptimer, + IN t_void (*callback)(t_void *pcontext), + t_void *pcontext) +{ + moal_drv_timer *timer = NULL; + t_u32 mem_flag = + (in_interrupt() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + + timer = kmalloc(sizeof(moal_drv_timer), mem_flag); + if (timer == NULL) + return MLAN_STATUS_FAILURE; + woal_initialize_timer(timer, callback, pcontext); + *pptimer = (t_void *)timer; + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Free the timer + * + * @param pmoal_handle Pointer to the MOAL context + * @param ptimer Pointer to the timer + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status moal_free_timer(t_void *pmoal_handle, 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(t_void *pmoal_handle, t_void *ptimer, + t_u8 periodic, 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(t_void *pmoal_handle, 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(t_void *pmoal_handle, t_void **pplock) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + moal_lock *mlock = NULL; + + mlock = kmalloc(sizeof(moal_lock), GFP_ATOMIC); + if (!mlock) + return MLAN_STATUS_FAILURE; + spin_lock_init(&mlock->lock); + *pplock = (t_void *)mlock; + + atomic_inc(&handle->lock_count); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Free the lock + * + * @param pmoal_handle Pointer to the MOAL context + * @param plock Lock + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status moal_free_lock(t_void *pmoal_handle, t_void *plock) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + moal_lock *mlock = plock; + + kfree(mlock); + if (mlock) + atomic_dec(&handle->lock_count); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief Request a spin lock + * + * @param pmoal_handle Pointer to the MOAL context + * @param plock Pointer to the lock + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_spin_lock(t_void *pmoal_handle, 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 This function collects TP statistics. + * + * @param pmoal_handle Pointer to the MOAL context + * @param buf pointer to the buffer of a packet + * @param drop_point Drop pointer user set + * + * @return N/A + */ +void moal_tp_accounting(t_void *pmoal_handle, void *buf, t_u32 drop_point) +{ + struct sk_buff *skb = NULL; + moal_handle *handle = (moal_handle *)pmoal_handle; + pmlan_buffer pmbuf = (pmlan_buffer)buf; + + if (drop_point < MAX_TP_ACCOUNT_DROP_POINT_NUM) { + if (drop_point == 4) { + handle->tp_acnt.tx_bytes[drop_point] += pmbuf->data_len; + } else { + skb = (struct sk_buff *)buf; + handle->tp_acnt.tx_bytes[drop_point] += skb->len; + } + handle->tp_acnt.tx_packets[drop_point]++; + } else if (drop_point <= RX_DROP_P5) { + t_u16 rx_len = 0; + if (drop_point == RX_DROP_P1 || drop_point == RX_DROP_P2) + rx_len = pmbuf->data_len - + *((t_u16 *)(pmbuf->pbuf + pmbuf->data_offset) + + 2); // remove rx_pkt_offset + else if (drop_point == RX_DROP_P3) // aggr pkt + rx_len = pmbuf->data_len; + else if (drop_point == RX_DROP_P4) { // before to kernel + skb = (struct sk_buff *)buf; + rx_len = skb->len; + } + handle->tp_acnt + .rx_bytes[drop_point - MAX_TP_ACCOUNT_DROP_POINT_NUM] += + rx_len; + handle->tp_acnt.rx_packets[drop_point - + MAX_TP_ACCOUNT_DROP_POINT_NUM]++; + } +} + +void moal_tp_accounting_rx_param(t_void *pmoal_handle, unsigned int type, + unsigned int rsvd1) +{ + moal_handle *phandle = (moal_handle *)pmoal_handle; + switch (type) { + case 0: // Rx interrupt + phandle->tp_acnt.rx_intr_cnt++; + break; + case 1: // rx_pkts_queued + phandle->tp_acnt.rx_pending = rsvd1; + break; + case 2: // paused + phandle->tp_acnt.rx_paused_cnt++; + break; + default: + break; + } +} + +/** + * @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(t_void *pmoal_handle, 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(t_void *pmoal_handle, t_u32 offset, t_u32 len, + t_u8 *pbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + + if (!pbuf || !len || !handle->firmware) + return MLAN_STATUS_FAILURE; + + if (offset + len > handle->firmware->size) + return MLAN_STATUS_FAILURE; + + moal_memcpy_ext(handle, pbuf, handle->firmware->data + offset, len, + len); + + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function get vdll data from moal + * + * @param pmoal_handle Pointer to the MOAL context + * @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_vdll_data(t_void *pmoal_handle, t_u32 len, t_u8 *pbuf) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + mlan_status status = MLAN_STATUS_FAILURE; + t_u32 offset = 0; + t_u8 req_fw = MFALSE; + + if (!handle->firmware) { + req_fw = MTRUE; + woal_vdll_req_fw(handle); + } + + if (handle->firmware) { + if (len < handle->firmware->size) { + offset = handle->firmware->size - len; + moal_memcpy_ext(handle, pbuf, + handle->firmware->data + offset, len, + len); + status = MLAN_STATUS_SUCCESS; + } else { + PRINTM(MERROR, "Invalid VDLL length = %d, fw_len=%d\n", + len, (int)handle->firmware->size); + } + if (req_fw) { + release_firmware(handle->firmware); + handle->firmware = NULL; + } + } + return status; +} + +/** + * @brief This function is called when MLAN completes the initialization + * firmware. + * + * @param pmoal_handle Pointer to the MOAL context + * @param status The status code for mlan_init_fw request + * @param phw pointer to mlan_hw_info + * @param ptbl pointer to mplan_bss_tbl + * @return MLAN_STATUS_SUCCESS + */ +mlan_status moal_get_hw_spec_complete(t_void *pmoal_handle, mlan_status status, + mlan_hw_info *phw, pmlan_bss_tbl ptbl) +{ + ENTER(); + if (status == MLAN_STATUS_SUCCESS) { + PRINTM(MCMND, "Get Hw Spec done, fw_cap=0x%x\n", phw->fw_cap); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function is called when MLAN completes the initialization + * firmware. + * + * @param pmoal_handle Pointer to the MOAL context + * @param status The status code for mlan_init_fw request + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status moal_init_fw_complete(t_void *pmoal_handle, 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(&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(t_void *pmoal_handle, 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(t_void *pmoal_handle, + pmlan_ioctl_req pioctl_req, mlan_status status) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + moal_private *priv = NULL; + wait_queue *wait; + unsigned long flags = 0; + ENTER(); + + if (!atomic_read(&handle->ioctl_pending)) + PRINTM(MERROR, "ERR: Unexpected IOCTL completed: %p\n", + pioctl_req); + else + atomic_dec(&handle->ioctl_pending); + priv = woal_bss_index_to_priv(handle, pioctl_req->bss_index); + if (!priv) { + PRINTM(MERROR, + "IOCTL %p complete with NULL priv, bss_index=%d\n", + pioctl_req, pioctl_req->bss_index); + LEAVE(); + return MLAN_STATUS_SUCCESS; + } + + if (status != MLAN_STATUS_SUCCESS && status != MLAN_STATUS_COMPLETE) + PRINTM(MERROR, + "IOCTL failed: %p id=0x%x, sub_id=0x%x action=%d, status_code=0x%x\n", + pioctl_req, pioctl_req->req_id, + (*(t_u32 *)pioctl_req->pbuf), (int)pioctl_req->action, + pioctl_req->status_code); + else + PRINTM(MIOCTL, + "IOCTL completed: %p id=0x%x sub_id=0x%x, action=%d, status=%d, status_code=0x%x\n", + pioctl_req, pioctl_req->req_id, + (*(t_u32 *)pioctl_req->pbuf), (int)pioctl_req->action, + status, pioctl_req->status_code); + + spin_lock_irqsave(&handle->driver_lock, flags); + wait = (wait_queue *)pioctl_req->reserved_1; + if (wait) { + wait->condition = MTRUE; + wait->status = status; + if (wait->wait_timeout) { + wake_up(&wait->wait); + } else { + if ((status != MLAN_STATUS_SUCCESS) && + (pioctl_req->status_code == + MLAN_ERROR_CMD_TIMEOUT)) { + PRINTM(MERROR, "IOCTL: command timeout\n"); + } else { + wake_up_interruptible(&wait->wait); + } + } + spin_unlock_irqrestore(&handle->driver_lock, flags); + } else { + spin_unlock_irqrestore(&handle->driver_lock, flags); + if ((status == MLAN_STATUS_SUCCESS) && + (pioctl_req->action == MLAN_ACT_GET)) + woal_process_ioctl_resp(priv, pioctl_req); + kfree(pioctl_req); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function allocates mlan_buffer. + * + * @param pmoal_handle Pointer to the MOAL context + * @param size allocation size requested + * @param pmbuf pointer to pointer to the allocated buffer + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_alloc_mlan_buffer(t_void *pmoal_handle, t_u32 size, + pmlan_buffer *pmbuf) +{ + *pmbuf = woal_alloc_mlan_buffer((moal_handle *)pmoal_handle, size); + if (NULL == *pmbuf) + return MLAN_STATUS_FAILURE; + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function frees mlan_buffer. + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmbuf pointer to buffer to be freed + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status moal_free_mlan_buffer(t_void *pmoal_handle, 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(t_void *pmoal_handle, pmlan_buffer pmbuf, + mlan_status status) +{ + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)pmoal_handle; + struct sk_buff *skb = NULL; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + t_u32 index = 0; +#endif + + ENTER(); + if (pmbuf && pmbuf->buf_type == MLAN_BUF_TYPE_RAW_DATA) { + woal_free_mlan_buffer(handle, pmbuf); + atomic_dec(&handle->tx_pending); + goto done; + } + 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; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_packet_fate_monitor( + priv, PACKET_TYPE_TX, + TX_PKT_FATE_SENT, + FRAME_TYPE_ETHERNET_II, 0, 0, + skb->data, skb->data_len); +#endif + } else { + priv->stats.tx_errors++; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_packet_fate_monitor( + priv, PACKET_TYPE_TX, + TX_PKT_FATE_DRV_DROP_OTHER, + FRAME_TYPE_ETHERNET_II, 0, 0, + skb->data, skb->data_len); +#endif + } +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + index = skb_get_queue_mapping(skb); + atomic_dec(&handle->tx_pending); + if (atomic_dec_return( + &priv->wmm_tx_pending[index]) == + LOW_TX_PENDING) { + struct netdev_queue *txq = + netdev_get_tx_queue( + priv->netdev, index); + if (netif_tx_queue_stopped(txq)) { + netif_tx_wake_queue(txq); + PRINTM(MINFO, + "Wakeup Kernel Queue:%d\n", + index); + } + } +#else /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)*/ + if (atomic_dec_return(&handle->tx_pending) < + LOW_TX_PENDING) { + int i; + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if ((GET_BSS_ROLE( + handle->priv[i]) == + MLAN_BSS_ROLE_STA) && + (handle->priv[i] + ->media_connected || + priv->is_adhoc_link_sensed)) { + woal_wake_queue( + handle->priv[i] + ->netdev); + } +#endif +#ifdef UAP_SUPPORT + if ((GET_BSS_ROLE( + handle->priv[i]) == + MLAN_BSS_ROLE_UAP) && + (handle->priv[i] + ->media_connected)) { + woal_wake_queue( + handle->priv[i] + ->netdev); + } +#endif + } + } +#endif /*#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,29)*/ + } + } + if (skb) + dev_kfree_skb_any(skb); + } + +done: + if ((atomic_read(&handle->tx_pending) == 0) && + !is_zero_timeval(handle->tx_time_start)) { + woal_get_monotonic_time(&handle->tx_time_end); + handle->tx_time += + (t_u64)(timeval_to_usec(handle->tx_time_end) - + timeval_to_usec(handle->tx_time_start)); + PRINTM(MINFO, + "%s : start_timeval=%d:%d end_timeval=%d:%d inter=%llu tx_time=%llu\n", + __func__, handle->tx_time_start.time_sec, + handle->tx_time_start.time_usec, + handle->tx_time_end.time_sec, + handle->tx_time_end.time_usec, + (t_u64)(timeval_to_usec(handle->tx_time_end) - + timeval_to_usec(handle->tx_time_start)), + handle->tx_time); + handle->tx_time_start.time_sec = 0; + handle->tx_time_start.time_usec = 0; + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +#ifdef USB +/** + * @brief This function is called when MLAN complete receiving + * data/event/command + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmbuf Pointer to the mlan buffer structure + * @param port Port number for receive + * @param status The status code for mlan_receive request + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status moal_recv_complete(t_void *pmoal_handle, pmlan_buffer pmbuf, + t_u32 port, mlan_status status) +{ + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)pmoal_handle; + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; + ENTER(); + + if ((pmbuf && (pmbuf->flags & MLAN_BUF_FLAG_RX_DEAGGR)) || !pmbuf) + atomic_dec(&handle->rx_pending); + + if (pmbuf) { + priv = woal_bss_index_to_priv(handle, pmbuf->bss_index); + if (priv && (pmbuf->buf_type == MLAN_BUF_TYPE_DATA) && + (status == MLAN_STATUS_FAILURE)) { + priv->stats.rx_dropped++; + } + /* Reuse the buffer in case of command/event */ + if (port == cardp->rx_cmd_ep) + woal_submit_rx_urb(handle, port); + else { + woal_free_mlan_buffer(handle, pmbuf); + if ((atomic_read(&handle->rx_pending) < + LOW_RX_PENDING) && + atomic_read(&cardp->rx_data_urb_pending) < + MVUSB_RX_DATA_URB) + woal_usb_submit_rx_data_urbs(handle); + } + } else if (port == cardp->rx_data_ep) { + if ((atomic_read(&handle->rx_pending) < LOW_RX_PENDING) && + atomic_read(&cardp->rx_data_urb_pending) < + MVUSB_RX_DATA_URB) + woal_usb_submit_rx_data_urbs(handle); + } + LEAVE(); + return MLAN_STATUS_SUCCESS; +} + +/** + * @brief This function write a command/data packet to card. + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmbuf Pointer to the mlan buffer structure + * @param port Port number for sent + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or + * MLAN_STATUS_PENDING or MLAN_STATUS_RESOURCE + */ +mlan_status moal_write_data_async(t_void *pmoal_handle, pmlan_buffer pmbuf, + t_u32 port) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + if (handle->is_suspended == MTRUE) { + PRINTM(MERROR, + "write_data_async is not allowed while suspended\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + ret = woal_write_data_async((moal_handle *)pmoal_handle, pmbuf, + (t_u8)port); + LEAVE(); + return ret; +} +#endif /* USB */ + +/** + * @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(t_void *pmoal_handle, pmlan_buffer pmbuf, + t_u32 port, t_u32 timeout) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + return handle->ops.write_data_sync(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(t_void *pmoal_handle, pmlan_buffer pmbuf, + t_u32 port, t_u32 timeout) +{ + moal_handle *handle = (moal_handle *)pmoal_handle; + return handle->ops.read_data_sync(handle, pmbuf, port, timeout); +} + +#if defined(SDIO) || defined(PCIE) +/** + * @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(t_void *pmoal_handle, t_u32 reg, t_u32 data) +{ + int ret = MLAN_STATUS_FAILURE; + moal_handle *handle = (moal_handle *)pmoal_handle; + if (handle->ops.write_reg) + ret = handle->ops.write_reg(handle, reg, data); + return ret; +} + +/** + * @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(t_void *pmoal_handle, t_u32 reg, t_u32 *data) +{ + int ret = MLAN_STATUS_FAILURE; + moal_handle *handle = (moal_handle *)pmoal_handle; + if (handle->ops.read_reg) + ret = handle->ops.read_reg(handle, reg, data); + return ret; +} + +#endif /* SDIO || PCIE */ + +/** + * @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(t_void *pmoal_handle, pmlan_buffer pmbuf) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + moal_private *priv = NULL; + struct sk_buff *skb = NULL; + moal_handle *handle = (moal_handle *)pmoal_handle; +#if defined(USB) || defined(PCIE) + t_u32 max_rx_data_size = MLAN_RX_DATA_BUF_SIZE; +#endif + dot11_rxcontrol rxcontrol; + t_u8 rx_info_flag = MFALSE; + int j; + struct ethhdr *ethh = NULL; + + ENTER(); + if (pmbuf) { +#ifdef USB +#ifdef STA_SUPPORT + if (IS_USB(handle->card_type)) { + struct usb_card_rec *cardp = + (struct usb_card_rec *)handle->card; + if (cardp->rx_deaggr_ctrl.enable) { + max_rx_data_size = + cardp->rx_deaggr_ctrl.aggr_max; + if (cardp->rx_deaggr_ctrl.aggr_mode == + MLAN_USB_AGGR_MODE_NUM) { + max_rx_data_size *= + MAX(MLAN_USB_MAX_PKT_SIZE, + cardp->rx_deaggr_ctrl + .aggr_align); + max_rx_data_size = + MAX(max_rx_data_size, + MLAN_RX_DATA_BUF_SIZE); + } + } + } +#endif +#endif + + 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); + if (skb_tailroom(skb) < pmbuf->data_len) { + PRINTM(MERROR, + "skb overflow: tail room=%d, data_len=%d\n", + skb_tailroom(skb), + pmbuf->data_len); + status = MLAN_STATUS_FAILURE; + priv->stats.rx_dropped++; + goto done; + } + skb_put(skb, pmbuf->data_len); + pmbuf->pdesc = NULL; + pmbuf->pbuf = NULL; + pmbuf->data_offset = pmbuf->data_len = 0; + /* pkt been submit to kernel, no need to + * free by mlan*/ + status = MLAN_STATUS_PENDING; + atomic_dec(&handle->mbufalloc_count); + } else { + PRINTM(MERROR, "%s without skb attach!!!\n", + __func__); + skb = dev_alloc_skb(pmbuf->data_len + + MLAN_NET_IP_ALIGN); + if (!skb) { + PRINTM(MERROR, "%s fail to alloc skb\n", + __func__); + status = MLAN_STATUS_FAILURE; + priv->stats.rx_dropped++; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_packet_fate_monitor( + priv, PACKET_TYPE_RX, + RX_PKT_FATE_DRV_DROP_NOBUFS, + FRAME_TYPE_ETHERNET_II, 0, 0, + (t_u8 *)(pmbuf->pbuf + + pmbuf->data_offset), + pmbuf->data_len); +#endif + goto done; + } + skb_reserve(skb, MLAN_NET_IP_ALIGN); + moal_memcpy_ext(handle, skb->data, + (t_u8 *)(pmbuf->pbuf + + pmbuf->data_offset), + pmbuf->data_len, + pmbuf->data_len); + skb_put(skb, pmbuf->data_len); + } + ethh = (struct ethhdr *)(skb->data); + if (ntohs(ethh->h_proto) == ETH_P_PAE) + PRINTM(MEVENT, + "wlan: %s Rx EAPOL pkt from " MACSTR + "\n", + priv->netdev->name, + MAC2STR(ethh->h_source)); + skb->dev = priv->netdev; + skb->protocol = eth_type_trans(skb, priv->netdev); + skb->ip_summed = CHECKSUM_NONE; + +#if defined(USB) || defined(PCIE) + /* This is only required only in case of 11n and + USB as we alloc if(skb_tailroom(skb) < + pmbuf->data_len){ PRINTM(MERROR,"skb overflow: + tail room=%d, data_len\n", skb_tailroom(skb), + pmbuf->data_len); status = MLAN_STATUS_FAILURE; + priv->stats.rx_dropped++; + goto done; + } + * a buffer of 4K only if its 11N (to be able to + receive 4K AMSDU + * packets). In case of SD we allocate buffers + based on the size + * of packet and hence this is not needed. + */ + /* Modifying the truesize here as our allocation + * for each skb is 4K but we only receive 2K + * packets and this cause the kernel to start + * dropping packets in case where application + * has allocated buffer based on 2K size i.e. if + * there a 64K packet received (in IP fragments + * and application allocates 64K to receive this + * packet but this packet would almost double up + * because we allocate each 1.5K fragment in 4K + * and pass it up. As soon as the 64K limit hits + * kernel will start to drop rest of the + * fragments. Currently we fail the + * Filesndl-ht.scr script for UDP, hence this + * fix + */ + if (!IS_SD(priv->phandle->card_type)) { + if (skb->truesize > max_rx_data_size) + skb->truesize += + (skb->len - max_rx_data_size); + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (!woal_filter_packet(priv, skb->data, skb->len, 0)) { + PRINTM(MEVENT, "drop filtered packet %s\n", + priv->netdev->name); + status = MLAN_STATUS_FAILURE; + priv->stats.rx_dropped++; + woal_packet_fate_monitor( + priv, PACKET_TYPE_RX, + RX_PKT_FATE_DRV_DROP_FILTER, + FRAME_TYPE_ETHERNET_II, 0, 0, skb->data, + skb->len); + dev_kfree_skb(skb); + goto done; + } +#endif + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_packet_fate_monitor(priv, PACKET_TYPE_RX, + RX_PKT_FATE_SUCCESS, + FRAME_TYPE_ETHERNET_II, 0, 0, + skb->data, skb->len); +#endif +#ifdef ANDROID_KERNEL + if (handle->params.wakelock_timeout) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + __pm_wakeup_event( + &handle->ws, + handle->params.wakelock_timeout); +#else + wake_lock_timeout( + &handle->wake_lock, + msecs_to_jiffies( + handle->params + .wakelock_timeout)); +#endif + } +#endif + if (priv->rx_protocols.protocol_num) { + for (j = 0; j < priv->rx_protocols.protocol_num; + j++) { + if (htons(skb->protocol) == + priv->rx_protocols.protocols[j]) + rx_info_flag = MTRUE; + } + } + if (rx_info_flag && + (skb_tailroom(skb) > sizeof(rxcontrol))) { + memset(&rxcontrol, 0, sizeof(dot11_rxcontrol)); + rxcontrol.datarate = pmbuf->u.rx_info.data_rate; + rxcontrol.channel = pmbuf->u.rx_info.channel; + rxcontrol.antenna = pmbuf->u.rx_info.antenna; + rxcontrol.rssi = pmbuf->u.rx_info.rssi; + skb_put(skb, sizeof(dot11_rxcontrol)); + memmove(skb->data + sizeof(dot11_rxcontrol), + skb->data, + skb->len - sizeof(dot11_rxcontrol)); + moal_memcpy_ext(handle, skb->data, &rxcontrol, + sizeof(dot11_rxcontrol), + sizeof(dot11_rxcontrol)); + } + // rx_trace 8 + if (priv->phandle->tp_acnt.on) + moal_tp_accounting(handle, skb, RX_DROP_P4); + if (priv->phandle->tp_acnt.drop_point == RX_DROP_P4) { + status = MLAN_STATUS_PENDING; + dev_kfree_skb(skb); + } else if (in_interrupt()) + netif_rx(skb); + else { + if (atomic_read(&handle->rx_pending) > + MAX_RX_PENDING_THRHLD) + netif_rx(skb); + else + netif_rx_ni(skb); + } + } + } +done: + LEAVE(); + return status; +} + +/** + * @brief This function handles event receive + * + * @param pmoal_handle Pointer to the MOAL context + * @param pmevent Pointer to the mlan event structure + * + * @return MLAN_STATUS_SUCCESS + */ +mlan_status moal_recv_event(t_void *pmoal_handle, pmlan_event pmevent) +{ +#ifdef STA_SUPPORT + int custom_len = 0; +#ifdef STA_CFG80211 + unsigned long flags; +#endif +#endif + moal_private *priv = NULL; +#if defined(STA_SUPPORT) || defined(UAP_SUPPORT) + moal_private *pmpriv = NULL; +#endif +#if defined(STA_WEXT) || defined(UAP_WEXT) +#if defined(STA_SUPPORT) || defined(UAP_WEXT) +#if defined(UAP_SUPPORT) || defined(STA_WEXT) + union iwreq_data wrqu; +#endif +#endif +#endif + mlan_ds_ps_info pm_info; + t_u8 hw_test; + int cfg80211_wext; + +#ifdef STA_CFG80211 + t_u8 channel_status; + moal_private *remain_priv = NULL; +#endif +#if defined(UAP_CFG80211) || defined(STA_CFG80211) + pchan_band_info pchan_info = NULL; +#endif + t_u8 radar_detected; + + ENTER(); + if (pmevent->event_id == MLAN_EVENT_ID_FW_DUMP_INFO) { + woal_store_firmware_dump(pmoal_handle, pmevent); + goto done; + } + if ((pmevent->event_id != MLAN_EVENT_ID_DRV_DEFER_RX_WORK) && + (pmevent->event_id != MLAN_EVENT_ID_DRV_DEFER_HANDLING) && + (pmevent->event_id != MLAN_EVENT_ID_DRV_MGMT_FRAME)) + PRINTM(MEVENT, "event id:0x%x\n", pmevent->event_id); +#if defined(PCIE) + if (pmevent->event_id == MLAN_EVENT_ID_SSU_DUMP_FILE) { + woal_store_ssu_dump(pmoal_handle, pmevent); + goto done; + } +#endif /* SSU_SUPPORT */ + if (pmevent->event_id == MLAN_EVENT_ID_STORE_HOST_CMD_RESP) { + woal_save_host_cmdresp((moal_handle *)pmoal_handle, + (mlan_cmdresp_event *)pmevent); + goto done; + } + priv = woal_bss_index_to_priv(pmoal_handle, pmevent->bss_index); + if (priv == NULL) { + PRINTM(MERROR, "%s: priv is null\n", __func__); + goto done; + } + if (priv->netdev == NULL) { + PRINTM(MERROR, "%s: netdev is null\n", __func__); + goto done; + } + + cfg80211_wext = priv->phandle->params.cfg80211_wext; + hw_test = moal_extflg_isset(pmoal_handle, EXT_HW_TEST); + switch (pmevent->event_id) { +#ifdef STA_SUPPORT + case MLAN_EVENT_ID_FW_ADHOC_LINK_SENSED: + priv->is_adhoc_link_sensed = MTRUE; + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_ADHOC_LINK_SENSED); +#endif + woal_broadcast_event(priv, CUS_EVT_ADHOC_LINK_SENSED, + strlen(CUS_EVT_ADHOC_LINK_SENSED)); + break; + + case MLAN_EVENT_ID_FW_ADHOC_LINK_LOST: + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + priv->is_adhoc_link_sensed = MFALSE; +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_ADHOC_LINK_LOST); +#endif + woal_broadcast_event(priv, CUS_EVT_ADHOC_LINK_LOST, + strlen(CUS_EVT_ADHOC_LINK_LOST)); + break; + + case MLAN_EVENT_ID_DRV_CONNECTED: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext) && + pmevent->event_len == ETH_ALEN) { + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + moal_memcpy_ext(priv->phandle, wrqu.ap_addr.sa_data, + pmevent->event_buf, ETH_ALEN, + sizeof(wrqu.ap_addr.sa_data)); + 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)) { + moal_memcpy_ext(priv->phandle, priv->cfg_bssid, + pmevent->event_buf, ETH_ALEN, ETH_ALEN); + woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + MIN_SPECIFIC_SCAN_CHAN_TIME); + } +#endif + custom_len = strlen(CUS_EVT_AP_CONNECTED); + memmove(pmevent->event_buf + custom_len, pmevent->event_buf, + pmevent->event_len); + moal_memcpy_ext(priv->phandle, pmevent->event_buf, + CUS_EVT_AP_CONNECTED, custom_len, custom_len); + pmevent->event_len += custom_len; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + woal_update_dscp_mapping(priv); + priv->media_connected = MTRUE; + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); + + break; + + case MLAN_EVENT_ID_DRV_ASSOC_SUCC_LOGGER: + case MLAN_EVENT_ID_DRV_ASSOC_FAILURE_LOGGER: + case MLAN_EVENT_ID_DRV_DISCONNECT_LOGGER: +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (IS_STA_CFG80211(cfg80211_wext)) + woal_ring_event_logger(priv, VERBOSE_RING_ID, pmevent); +#endif +#endif + break; + + case MLAN_EVENT_ID_DRV_SCAN_REPORT: + PRINTM(MINFO, "Scan report\n"); + + if (priv->report_scan_result) { + priv->report_scan_result = MFALSE; +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + if (priv->phandle->scan_request) { + PRINTM(MINFO, + "Reporting scan results\n"); + woal_inform_bss_from_scan_result( + priv, NULL, MOAL_NO_WAIT); + if (!priv->phandle->first_scan_done) { + priv->phandle->first_scan_done = + MTRUE; + woal_set_scan_time( + priv, + ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + SPECIFIC_SCAN_CHAN_TIME); + } + spin_lock_irqsave( + &priv->phandle->scan_req_lock, + flags); + if (priv->phandle->scan_request) { + woal_cfg80211_scan_done( + priv->phandle + ->scan_request, + MFALSE); + priv->phandle->scan_request = + NULL; + } + spin_unlock_irqrestore( + &priv->phandle->scan_req_lock, + flags); + } + } +#endif /* STA_CFG80211 */ + +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->netdev, SIOCGIWSCAN, + &wrqu, NULL); + } +#endif + woal_broadcast_event(priv, (t_u8 *)&pmevent->event_id, + sizeof(mlan_event_id)); + } + + if (!is_zero_timeval(priv->phandle->scan_time_start)) { + woal_get_monotonic_time(&priv->phandle->scan_time_end); + priv->phandle->scan_time += (t_u64)( + timeval_to_usec(priv->phandle->scan_time_end) - + timeval_to_usec( + priv->phandle->scan_time_start)); + PRINTM(MINFO, + "%s : start_timeval=%d:%d end_timeval=%d:%d inter=%llu scan_time=%llu\n", + __func__, + priv->phandle->scan_time_start.time_sec, + priv->phandle->scan_time_start.time_usec, + priv->phandle->scan_time_end.time_sec, + priv->phandle->scan_time_end.time_usec, + (t_u64)(timeval_to_usec( + priv->phandle->scan_time_end) - + timeval_to_usec( + priv->phandle->scan_time_start)), + priv->phandle->scan_time); + priv->phandle->scan_time_start.time_sec = 0; + priv->phandle->scan_time_start.time_usec = 0; + } + + if (priv->phandle->scan_pending_on_block == MTRUE) { + priv->phandle->scan_pending_on_block = MFALSE; + priv->phandle->scan_priv = NULL; + MOAL_REL_SEMAPHORE(&priv->phandle->async_sem); + } + break; + + case MLAN_EVENT_ID_DRV_OBSS_SCAN_PARAM: + memmove((pmevent->event_buf + strlen(CUS_EVT_OBSS_SCAN_PARAM) + + 1), + pmevent->event_buf, pmevent->event_len); + moal_memcpy_ext(priv->phandle, pmevent->event_buf, + (t_u8 *)CUS_EVT_OBSS_SCAN_PARAM, + strlen(CUS_EVT_OBSS_SCAN_PARAM), + strlen(CUS_EVT_OBSS_SCAN_PARAM)); + pmevent->event_buf[strlen(CUS_EVT_OBSS_SCAN_PARAM)] = 0; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len + + strlen(CUS_EVT_OBSS_SCAN_PARAM)); + +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wrqu.data.pointer = pmevent->event_buf; + wrqu.data.length = pmevent->event_len + + strlen(CUS_EVT_OBSS_SCAN_PARAM) + 1; + wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, + pmevent->event_buf); + } +#endif + break; + case MLAN_EVENT_ID_FW_BW_CHANGED: + memmove((pmevent->event_buf + strlen(CUS_EVT_BW_CHANGED) + 1), + pmevent->event_buf, pmevent->event_len); + moal_memcpy_ext(priv->phandle, pmevent->event_buf, + (t_u8 *)CUS_EVT_BW_CHANGED, + strlen(CUS_EVT_BW_CHANGED), + strlen(CUS_EVT_BW_CHANGED)); + pmevent->event_buf[strlen(CUS_EVT_BW_CHANGED)] = 0; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len + + strlen(CUS_EVT_BW_CHANGED)); + +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wrqu.data.pointer = pmevent->event_buf; + wrqu.data.length = pmevent->event_len + + strlen(CUS_EVT_BW_CHANGED) + 1; + wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, + pmevent->event_buf); + } +#endif + break; + + case MLAN_EVENT_ID_FW_DISCONNECTED: + woal_send_disconnect_to_system(priv, + (t_u16)*pmevent->event_buf); +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + priv->auth_flag = 0; + priv->host_mlme = MFALSE; + priv->auth_alg = 0xFFFF; +#endif +#endif +#ifdef STA_WEXT + /* Reset wireless stats signal info */ + if (IS_STA_WEXT(cfg80211_wext)) { + priv->w_stats.qual.level = 0; + priv->w_stats.qual.noise = 0; + } +#endif +#ifdef REASSOCIATION + if (priv->reassoc_on == MTRUE) { + PRINTM(MINFO, "Reassoc: trigger the timer\n"); + priv->reassoc_required = MTRUE; + priv->phandle->is_reassoc_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->reassoc_timer, + REASSOC_TIMER_DEFAULT); + } else { + priv->rate_index = AUTO_RATE; + } +#endif /* REASSOCIATION */ + break; + + case MLAN_EVENT_ID_FW_MIC_ERR_UNI: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { +#if WIRELESS_EXT >= 18 + woal_send_mic_error_event(priv, + MLAN_EVENT_ID_FW_MIC_ERR_UNI); +#else + woal_send_iwevcustom_event(priv, + CUS_EVT_MLME_MIC_ERR_UNI); +#endif + } +#endif /* STA_WEXT */ +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + cfg80211_michael_mic_failure(priv->netdev, + priv->cfg_bssid, + NL80211_KEYTYPE_PAIRWISE, + -1, NULL, GFP_KERNEL); + } +#endif + woal_broadcast_event(priv, CUS_EVT_MLME_MIC_ERR_UNI, + strlen(CUS_EVT_MLME_MIC_ERR_UNI)); + break; + case MLAN_EVENT_ID_FW_MIC_ERR_MUL: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { +#if WIRELESS_EXT >= 18 + woal_send_mic_error_event(priv, + MLAN_EVENT_ID_FW_MIC_ERR_MUL); +#else + woal_send_iwevcustom_event(priv, + CUS_EVT_MLME_MIC_ERR_MUL); +#endif + } +#endif /* STA_WEXT */ +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + cfg80211_michael_mic_failure(priv->netdev, + priv->cfg_bssid, + NL80211_KEYTYPE_GROUP, -1, + NULL, GFP_KERNEL); + } +#endif + woal_broadcast_event(priv, CUS_EVT_MLME_MIC_ERR_MUL, + strlen(CUS_EVT_MLME_MIC_ERR_MUL)); + break; + case MLAN_EVENT_ID_FW_BCN_RSSI_LOW: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_BEACON_RSSI_LOW); +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + cfg80211_cqm_rssi_notify( + priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + *(t_s16 *)pmevent->event_buf, +#endif + GFP_KERNEL); + priv->last_event |= EVENT_BCN_RSSI_LOW; +#endif + if (!hw_test && priv->roaming_enabled) + woal_config_bgscan_and_rssi(priv, MTRUE); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_cfg80211_rssi_monitor_event( + priv, *(t_s16 *)pmevent->event_buf); +#endif + } +#endif + woal_broadcast_event(priv, CUS_EVT_BEACON_RSSI_LOW, + strlen(CUS_EVT_BEACON_RSSI_LOW)); + break; + case MLAN_EVENT_ID_FW_BCN_RSSI_HIGH: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_BEACON_RSSI_HIGH); +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + if (!priv->mrvl_rssi_low) { +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + cfg80211_cqm_rssi_notify( + priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + *(t_s16 *)pmevent->event_buf, +#endif + GFP_KERNEL); +#endif + woal_set_rssi_threshold( + priv, MLAN_EVENT_ID_FW_BCN_RSSI_HIGH, + MOAL_NO_WAIT); + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_cfg80211_rssi_monitor_event( + priv, *(t_s16 *)pmevent->event_buf); +#endif + } +#endif + woal_broadcast_event(priv, CUS_EVT_BEACON_RSSI_HIGH, + strlen(CUS_EVT_BEACON_RSSI_HIGH)); + break; + case MLAN_EVENT_ID_FW_BCN_SNR_LOW: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_BEACON_SNR_LOW); +#endif + woal_broadcast_event(priv, CUS_EVT_BEACON_SNR_LOW, + strlen(CUS_EVT_BEACON_SNR_LOW)); + break; + case MLAN_EVENT_ID_FW_BCN_SNR_HIGH: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_BEACON_SNR_HIGH); +#endif + woal_broadcast_event(priv, CUS_EVT_BEACON_SNR_HIGH, + strlen(CUS_EVT_BEACON_SNR_HIGH)); + break; + case MLAN_EVENT_ID_FW_MAX_FAIL: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_MAX_FAIL); +#endif + woal_broadcast_event(priv, CUS_EVT_MAX_FAIL, + strlen(CUS_EVT_MAX_FAIL)); + break; + case MLAN_EVENT_ID_FW_DATA_RSSI_LOW: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_DATA_RSSI_LOW); +#endif + woal_broadcast_event(priv, CUS_EVT_DATA_RSSI_LOW, + strlen(CUS_EVT_DATA_RSSI_LOW)); + break; + case MLAN_EVENT_ID_FW_DATA_SNR_LOW: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_DATA_SNR_LOW); +#endif + woal_broadcast_event(priv, CUS_EVT_DATA_SNR_LOW, + strlen(CUS_EVT_DATA_SNR_LOW)); + break; + case MLAN_EVENT_ID_FW_DATA_RSSI_HIGH: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_DATA_RSSI_HIGH); +#endif + woal_broadcast_event(priv, CUS_EVT_DATA_RSSI_HIGH, + strlen(CUS_EVT_DATA_RSSI_HIGH)); + break; + case MLAN_EVENT_ID_FW_DATA_SNR_HIGH: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_DATA_SNR_HIGH); +#endif + woal_broadcast_event(priv, CUS_EVT_DATA_SNR_HIGH, + strlen(CUS_EVT_DATA_SNR_HIGH)); + break; + case MLAN_EVENT_ID_FW_LINK_QUALITY: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_LINK_QUALITY); +#endif + woal_broadcast_event(priv, CUS_EVT_LINK_QUALITY, + strlen(CUS_EVT_LINK_QUALITY)); + break; + case MLAN_EVENT_ID_FW_PORT_RELEASE: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_PORT_RELEASE); +#endif + woal_broadcast_event(priv, CUS_EVT_PORT_RELEASE, + strlen(CUS_EVT_PORT_RELEASE)); + break; + case MLAN_EVENT_ID_FW_PRE_BCN_LOST: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, + CUS_EVT_PRE_BEACON_LOST); +#endif +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + if (IS_STA_CFG80211(cfg80211_wext)) { + struct cfg80211_bss *bss = NULL; + bss = cfg80211_get_bss(priv->wdev->wiphy, NULL, + priv->cfg_bssid, NULL, 0, + WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); + if (bss) + cfg80211_unlink_bss(priv->wdev->wiphy, bss); + if (!hw_test && priv->roaming_enabled) + woal_config_bgscan_and_rssi(priv, MFALSE); + priv->last_event |= EVENT_PRE_BCN_LOST; + } +#endif +#endif + woal_broadcast_event(priv, CUS_EVT_PRE_BEACON_LOST, + strlen(CUS_EVT_PRE_BEACON_LOST)); + break; + case MLAN_EVENT_ID_FW_DEBUG_INFO: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, pmevent->event_buf); +#endif + memmove((pmevent->event_buf + strlen(FW_DEBUG_INFO) + 1), + pmevent->event_buf, pmevent->event_len); + moal_memcpy_ext(priv->phandle, pmevent->event_buf, + (t_u8 *)FW_DEBUG_INFO, strlen(FW_DEBUG_INFO), + strlen(FW_DEBUG_INFO)); + pmevent->event_buf[strlen(FW_DEBUG_INFO)] = 0; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len + + strlen(FW_DEBUG_INFO) + 1); + break; + case MLAN_EVENT_ID_FW_WMM_CONFIG_CHANGE: +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event( + priv, WMM_CONFIG_CHANGE_INDICATION); +#endif + woal_broadcast_event(priv, WMM_CONFIG_CHANGE_INDICATION, + strlen(WMM_CONFIG_CHANGE_INDICATION)); + break; + + case MLAN_EVENT_ID_DRV_REPORT_STRING: + PRINTM(MINFO, "Report string %s\n", pmevent->event_buf); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, pmevent->event_buf); +#endif + woal_broadcast_event(priv, pmevent->event_buf, + strlen(pmevent->event_buf)); + break; + case MLAN_EVENT_ID_FW_WEP_ICV_ERR: + DBG_HEXDUMP(MCMD_D, "WEP ICV error", pmevent->event_buf, + pmevent->event_len); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) + woal_send_iwevcustom_event(priv, CUS_EVT_WEP_ICV_ERR); +#endif + woal_broadcast_event(priv, CUS_EVT_WEP_ICV_ERR, + strlen(CUS_EVT_WEP_ICV_ERR)); + break; + + case MLAN_EVENT_ID_DRV_DEFER_HANDLING: + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_EVENT_ID_DRV_FLUSH_RX_WORK: + if (moal_extflg_isset(priv->phandle, EXT_NAPI)) { + napi_synchronize(&priv->phandle->napi_rx); + break; + } + flush_workqueue(priv->phandle->rx_workqueue); + break; + case MLAN_EVENT_ID_DRV_FLUSH_MAIN_WORK: + flush_workqueue(priv->phandle->workqueue); + break; + case MLAN_EVENT_ID_DRV_DEFER_RX_WORK: + if (moal_extflg_isset(priv->phandle, EXT_NAPI)) { + napi_schedule(&priv->phandle->napi_rx); + break; + } + queue_work(priv->phandle->rx_workqueue, + &priv->phandle->rx_work); + break; + case MLAN_EVENT_ID_DRV_DBG_DUMP: + priv->phandle->driver_status = MTRUE; + woal_moal_debug_info(priv, NULL, MFALSE); + woal_broadcast_event(priv, CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + woal_cfg80211_vendor_event(priv, event_hang, + CUS_EVT_DRIVER_HANG, + strlen(CUS_EVT_DRIVER_HANG)); +#endif +#endif + woal_process_hang(priv->phandle); + wifi_status = 2; + break; + case MLAN_EVENT_ID_DRV_WIFI_STATUS: + wifi_status = *(t_u16 *)(pmevent->event_buf + sizeof(t_u32)); + break; + case MLAN_EVENT_ID_FW_BG_SCAN: + if (priv->media_connected == MTRUE) + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MTRUE; +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->netdev, SIOCGIWSCAN, &wrqu, + NULL); + } +#endif +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + priv->last_event |= EVENT_BG_SCAN_REPORT; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (priv->sched_scanning && + !priv->phandle->cfg80211_suspend) { + mlan_scan_resp scan_resp; + if (MLAN_STATUS_SUCCESS == + woal_get_scan_table(priv, MOAL_NO_WAIT, + &scan_resp)) + PRINTM(MIOCTL, + "Triggered mlan get bgscan result\n"); + } +#endif + if (!hw_test && priv->roaming_enabled +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + && !priv->phandle->cfg80211_suspend +#endif + ) { + priv->roaming_required = MTRUE; +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + __pm_wakeup_event(&priv->phandle->ws, + ROAMING_WAKE_LOCK_TIMEOUT); +#else + wake_lock_timeout( + &priv->phandle->wake_lock, + msecs_to_jiffies( + ROAMING_WAKE_LOCK_TIMEOUT)); +#endif +#endif + wake_up_interruptible( + &priv->phandle->reassoc_thread.wait_q); + } + } +#endif + break; + case MLAN_EVENT_ID_FW_BG_SCAN_STOPPED: +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(cfg80211_wext)) { + if (priv->sched_scanning) + woal_bgscan_stop_event(priv); + } +#endif +#endif + break; + case MLAN_EVENT_ID_DRV_BGSCAN_RESULT: +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(cfg80211_wext)) { + if (priv->sched_scanning && + !priv->phandle->cfg80211_suspend) { + woal_inform_bss_from_scan_result(priv, NULL, + MOAL_NO_WAIT); + cfg80211_sched_scan_results(priv->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , + priv->bg_scan_reqid +#endif + ); + priv->last_event = 0; + woal_bgscan_stop_event(priv); + PRINTM(MEVENT, + "Reporting Sched_Scan results\n"); + } + } +#endif +#endif + break; +#endif /* STA_SUPPORT */ + + case MLAN_EVENT_ID_FW_CHANNEL_REPORT_RDY: + radar_detected = pmevent->event_buf[0]; + +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (!IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + break; + + if (priv->phandle->is_cac_timer_set) { + PRINTM(MEVENT, "%s radar found when CAC \n", + radar_detected ? "" : "No"); + moal_stop_timer(priv->phandle, + &priv->phandle->cac_timer); + priv->phandle->is_cac_timer_set = MFALSE; + if (radar_detected) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, + &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#endif + cfg80211_radar_event( + priv->wdev->wiphy, + &priv->phandle->dfs_channel, + GFP_KERNEL); + } else { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, + &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_FINISHED, + GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, + NL80211_RADAR_CAC_FINISHED, + GFP_KERNEL); +#endif + } + memset(&priv->phandle->dfs_channel, 0, + sizeof(struct cfg80211_chan_def)); + priv->phandle->cac_bss_index = 0xff; + } +#endif /* CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) */ +#endif /* UAP_CFG80211 */ + break; + case MLAN_EVENT_ID_FW_RADAR_DETECTED: + +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (!IS_STA_OR_UAP_CFG80211(cfg80211_wext)) + break; + if (priv->phandle->is_cac_timer_set) { + if (priv->bss_index == priv->phandle->cac_bss_index) { + PRINTM(MEVENT, "radar detected during CAC \n"); + woal_cancel_timer(&priv->phandle->cac_timer); + priv->phandle->is_cac_timer_set = MFALSE; + /* downstream: cancel the unfinished CAC in + * Firmware*/ + woal_11h_cancel_chan_report_ioctl(priv, + MOAL_NO_WAIT); + /* upstream: inform cfg80211 */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, + &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, + NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#endif + cfg80211_radar_event( + priv->wdev->wiphy, + &priv->phandle->dfs_channel, + GFP_KERNEL); + + memset(&priv->phandle->dfs_channel, 0, + sizeof(priv->phandle->dfs_channel)); + priv->phandle->cac_bss_index = 0xff; + } else { + PRINTM(MERROR, + " Radar event for incorrect inferface \n"); + } + } else { + PRINTM(MEVENT, "radar detected during BSS active \n"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) + woal_cfg80211_dfs_vendor_event( + priv, event_dfs_radar_detected, + &priv->chan); + else { +#endif + if (priv->uap_host_based && priv->bss_started) + cfg80211_radar_event(priv->wdev->wiphy, + &priv->chan, + GFP_KERNEL); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + } +#endif + } +#endif /* CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0 */ +#endif /* UAP_CFG80211 */ + 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 + woal_broadcast_event(priv, CUS_EVT_CHANNEL_SWITCH_ANN, + strlen(CUS_EVT_CHANNEL_SWITCH_ANN)); + break; + + case MLAN_EVENT_ID_FW_CHAN_SWITCH_COMPLETE: +#if defined(UAP_CFG80211) || defined(STA_CFG80211) + pchan_info = (chan_band_info *)pmevent->event_buf; + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { + PRINTM(MMSG, + "CSA/ECSA: Switch to new channel %d complete!\n", + pchan_info->channel); + priv->channel = pchan_info->channel; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->csa_chan.chan && + (pchan_info->channel == + priv->csa_chan.chan->hw_value)) { + moal_memcpy_ext( + priv->phandle, &priv->chan, + &priv->csa_chan, + sizeof(struct cfg80211_chan_def), + sizeof(struct cfg80211_chan_def)); + } +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (MFALSE +#ifdef UAP_CFG80211 + || priv->uap_host_based +#endif +#ifdef STA_CFG80211 + || priv->sme_current.ssid_len +#endif + ) { + PRINTM(MEVENT, + "CHAN_SWITCH: 11n=%d, chan=%d, center_chan=%d, band=%d, width=%d, 2Offset=%d\n", + pchan_info->is_11n_enabled, + pchan_info->channel, + pchan_info->center_chan, + pchan_info->bandcfg.chanBand, + pchan_info->bandcfg.chanWidth, + pchan_info->bandcfg.chan2Offset); + woal_cfg80211_notify_channel(priv, pchan_info); + } +#endif + } +#endif +#ifdef UAP_SUPPORT + if (priv->bss_role == MLAN_BSS_ROLE_UAP) { + if (priv->uap_tx_blocked) { + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_start_queue(priv->netdev); + priv->uap_tx_blocked = MFALSE; + } + priv->phandle->chsw_wait_q_woken = MTRUE; + wake_up_interruptible(&priv->phandle->chsw_wait_q); + } +#endif + break; + case MLAN_EVENT_ID_FW_STOP_TX: + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + break; + case MLAN_EVENT_ID_FW_START_TX: + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); + break; + case MLAN_EVENT_ID_FW_HS_WAKEUP: + /* simulate HSCFG_CANCEL command */ + woal_cancel_hs(priv, MOAL_NO_WAIT); +#ifdef STA_SUPPORT + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_STA); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext) && pmpriv) + woal_send_iwevcustom_event(pmpriv, CUS_EVT_HS_WAKEUP); +#endif /* STA_WEXT */ + if (pmpriv) + woal_broadcast_event(pmpriv, CUS_EVT_HS_WAKEUP, + strlen(CUS_EVT_HS_WAKEUP)); +#endif /*STA_SUPPORT */ +#ifdef UAP_SUPPORT + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_UAP); + if (pmpriv) { + pmevent->event_id = UAP_EVENT_ID_HS_WAKEUP; + woal_broadcast_event(pmpriv, (t_u8 *)&pmevent->event_id, + sizeof(t_u32)); + } +#endif /* UAP_SUPPORT */ + break; + case MLAN_EVENT_ID_DRV_HS_ACTIVATED: +#ifdef STA_SUPPORT + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_STA); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext) && pmpriv) + woal_send_iwevcustom_event(pmpriv, + CUS_EVT_HS_ACTIVATED); +#endif /* STA_WEXT */ + if (pmpriv) + woal_broadcast_event(pmpriv, CUS_EVT_HS_ACTIVATED, + strlen(CUS_EVT_HS_ACTIVATED)); +#endif /* STA_SUPPORT */ +#if defined(UAP_SUPPORT) + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_UAP); + if (pmpriv) { + pmevent->event_id = UAP_EVENT_ID_DRV_HS_ACTIVATED; + woal_broadcast_event(pmpriv, (t_u8 *)&pmevent->event_id, + sizeof(t_u32)); + } +#endif + memset(&pm_info, 0, sizeof(mlan_ds_ps_info)); + 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(&priv->phandle->hs_activate_wait_q); + } + break; + case MLAN_EVENT_ID_DRV_HS_DEACTIVATED: +#ifdef STA_SUPPORT + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_STA); +#ifdef STA_WEXT + if (IS_STA_WEXT(cfg80211_wext) && pmpriv) + woal_send_iwevcustom_event(pmpriv, + CUS_EVT_HS_DEACTIVATED); +#endif /* STA_WEXT */ + if (pmpriv) + woal_broadcast_event(pmpriv, CUS_EVT_HS_DEACTIVATED, + strlen(CUS_EVT_HS_DEACTIVATED)); +#endif /* STA_SUPPORT */ +#if defined(UAP_SUPPORT) + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_UAP); + if (pmpriv) { + pmevent->event_id = UAP_EVENT_ID_DRV_HS_DEACTIVATED; + woal_broadcast_event(pmpriv, (t_u8 *)&pmevent->event_id, + sizeof(t_u32)); + } +#endif + priv->phandle->hs_activated = MFALSE; + break; +#ifdef UAP_SUPPORT + case MLAN_EVENT_ID_UAP_FW_BSS_START: + woal_hist_data_reset(priv); + priv->bss_started = MTRUE; + priv->skip_cac = MFALSE; + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_start_queue(priv->netdev); + moal_memcpy_ext(priv->phandle, priv->current_addr, + pmevent->event_buf + 6, ETH_ALEN, ETH_ALEN); + moal_memcpy_ext(priv->phandle, priv->netdev->dev_addr, + priv->current_addr, ETH_ALEN, ETH_ALEN); + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); +#ifdef STA_SUPPORT +#ifdef STA_CFG80211 + pmpriv = woal_get_priv((moal_handle *)pmoal_handle, + MLAN_BSS_ROLE_STA); + if (IS_STA_CFG80211(cfg80211_wext) && pmpriv) + woal_set_scan_time(pmpriv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + MIN_SPECIFIC_SCAN_CHAN_TIME); +#endif +#endif +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->chan_under_nop) { + PRINTM(MMSG, + "Channel Under Nop: notify cfg80211 new channel=%d\n", + priv->channel); + cfg80211_ch_switch_notify(priv->netdev, &priv->chan); + priv->chan_under_nop = MFALSE; + } +#endif +#endif + break; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + case MLAN_EVENT_ID_DRV_UAP_CHAN_INFO: +#ifdef UAP_CFG80211 + if (IS_UAP_CFG80211(cfg80211_wext)) { + chan_band_info *pchan_info = + (chan_band_info *)pmevent->event_buf; + PRINTM(MEVENT, + "UAP: 11n=%d, chan=%d, center_chan=%d, band=%d, width=%d, 2Offset=%d\n", + pchan_info->is_11n_enabled, pchan_info->channel, + pchan_info->center_chan, + pchan_info->bandcfg.chanBand, + pchan_info->bandcfg.chanWidth, + pchan_info->bandcfg.chan2Offset); + if (priv->uap_host_based && + (priv->channel != pchan_info->channel)) + woal_channel_switch_event(priv, pchan_info); + } +#endif + break; +#endif + case MLAN_EVENT_ID_UAP_FW_BSS_ACTIVE: + priv->media_connected = MTRUE; + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_wake_queue(priv->netdev); + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + break; + case MLAN_EVENT_ID_UAP_FW_BSS_IDLE: + priv->media_connected = MFALSE; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + break; + case MLAN_EVENT_ID_UAP_FW_MIC_COUNTERMEASURES: { + t_u16 status = 0; + status = *(t_u16 *)(pmevent->event_buf + 4); + if (status) { + priv->media_connected = MFALSE; + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + } else { + 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; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + case MLAN_EVENT_ID_FW_REMAIN_ON_CHAN_EXPIRED: + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { + PRINTM(MEVENT, + "FW_REMAIN_ON_CHANNEL_EXPIRED cookie = %#llx\n", + priv->phandle->cookie); + if (priv->host_mlme && + (priv->auth_flag & HOST_MLME_AUTH_PENDING)) { + priv->auth_flag = 0; + priv->host_mlme = MFALSE; + priv->auth_alg = 0xFFFF; + } + priv->phandle->remain_on_channel = MFALSE; + if (priv->phandle->cookie && + !priv->phandle->is_remain_timer_set) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + priv->netdev, +#else + priv->wdev, +#endif + priv->phandle->cookie, + &priv->phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv->phandle->channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + } + break; +#endif +#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 = {0}; + t_u8 addr[ETH_ALEN]; + + sinfo.filled = 0; + sinfo.generation = 0; + /* copy the station mac address */ + memset(addr, 0xFF, ETH_ALEN); + moal_memcpy_ext(priv->phandle, addr, pmevent->event_buf, + ETH_ALEN, ETH_ALEN); + /** these field add in kernel 3.2, but some + * kernel do have the pacth to support it, + * like T3T and pxa978T 3.0.31 JB, these + * patch are needed to support + * wpa_supplicant 2.x */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 31) + if (pmevent->event_len > ETH_ALEN) { +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 0, 0) + /* set station info filled flag */ + sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) + sinfo.pertid = NULL; +#endif + /* get the assoc request ies and length */ + sinfo.assoc_req_ies = + (const t_u8 *)(pmevent->event_buf + + ETH_ALEN); + sinfo.assoc_req_ies_len = + pmevent->event_len - ETH_ALEN; + } +#endif /* KERNEL_VERSION */ + if (priv->netdev && priv->wdev) + cfg80211_new_sta(priv->netdev, (t_u8 *)addr, + &sinfo, GFP_KERNEL); + } +#endif /* UAP_CFG80211 */ + memmove((pmevent->event_buf + strlen(CUS_EVT_STA_CONNECTED) + + 1), + pmevent->event_buf, pmevent->event_len); + moal_memcpy_ext(priv->phandle, pmevent->event_buf, + (t_u8 *)CUS_EVT_STA_CONNECTED, + strlen(CUS_EVT_STA_CONNECTED), + strlen(CUS_EVT_STA_CONNECTED)); + pmevent->event_buf[strlen(CUS_EVT_STA_CONNECTED)] = 0; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len + + strlen(CUS_EVT_STA_CONNECTED)); +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wrqu.data.pointer = pmevent->event_buf; + if ((pmevent->event_len + + strlen(CUS_EVT_STA_CONNECTED) + 1) > IW_CUSTOM_MAX) + wrqu.data.length = + ETH_ALEN + + strlen(CUS_EVT_STA_CONNECTED) + 1; + else + wrqu.data.length = + pmevent->event_len + + strlen(CUS_EVT_STA_CONNECTED) + 1; + wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, + pmevent->event_buf); + } +#endif /* UAP_WEXT */ + break; + case MLAN_EVENT_ID_UAP_FW_STA_DISCONNECT: +#ifdef UAP_CFG80211 + if (IS_UAP_CFG80211(cfg80211_wext)) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /**Forward Deauth, Auth and disassoc frame to Host*/ + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) { + t_u16 reason_code = woal_le16_to_cpu( + *(t_u16 *)pmevent->event_buf); + PRINTM(MCMND, "deauth reason code =0x%x\n", + reason_code); + /** BIT 14 indicate deauth is initiated by FW */ + if (reason_code & MBIT(14)) + woal_host_mlme_disconnect( + priv, 0, + pmevent->event_buf + 2); + } else +#endif + if (priv->netdev && priv->wdev) + cfg80211_del_sta(priv->netdev, + pmevent->event_buf + 2, + GFP_KERNEL); + +#endif /* KERNEL_VERSION */ + } +#endif /* UAP_CFG80211 */ + memmove((pmevent->event_buf + strlen(CUS_EVT_STA_DISCONNECTED) + + 1), + pmevent->event_buf, pmevent->event_len); + moal_memcpy_ext(priv->phandle, pmevent->event_buf, + (t_u8 *)CUS_EVT_STA_DISCONNECTED, + strlen(CUS_EVT_STA_DISCONNECTED), + strlen(CUS_EVT_STA_DISCONNECTED)); + pmevent->event_buf[strlen(CUS_EVT_STA_DISCONNECTED)] = 0; + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len + + strlen(CUS_EVT_STA_DISCONNECTED)); + +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wrqu.data.pointer = pmevent->event_buf; + wrqu.data.length = pmevent->event_len + + strlen(CUS_EVT_STA_DISCONNECTED) + 1; + wireless_send_event(priv->netdev, IWEVCUSTOM, &wrqu, + pmevent->event_buf); + } +#endif /* UAP_WEXT */ + break; + case MLAN_EVENT_ID_DRV_MGMT_FRAME: +#ifdef UAP_WEXT + if (IS_UAP_WEXT(cfg80211_wext)) { + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + } +#endif /* UAP_WEXT */ + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (IS_STA_OR_UAP_CFG80211(cfg80211_wext)) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if (priv->netdev && + priv->netdev->ieee80211_ptr->wiphy->mgmt_stypes && + priv->mgmt_subtype_mask) { + /* frmctl + durationid + addr1 + addr2 + addr3 + + * seqctl */ +#define PACKET_ADDR4_POS (2 + 2 + 6 + 6 + 6 + 2) + t_u8 *pkt; + int freq = + priv->phandle->remain_on_channel ? + priv->phandle->chan.center_freq : + woal_get_active_intf_freq(priv); + if (!freq) { + if (!priv->phandle->chan.center_freq) { + PRINTM(MINFO, + "Skip to report mgmt packet to cfg80211\n"); + break; + } + freq = priv->phandle->chan.center_freq; + } + + pkt = ((t_u8 *)pmevent->event_buf + + sizeof(pmevent->event_id)); + + /* move addr4 */ + memmove(pkt + PACKET_ADDR4_POS, + pkt + PACKET_ADDR4_POS + ETH_ALEN, + pmevent->event_len - + sizeof(pmevent->event_id) - + PACKET_ADDR4_POS - ETH_ALEN); +#ifdef WIFI_DIRECT_SUPPORT + if (ieee80211_is_action( + ((struct ieee80211_mgmt *)pkt) + ->frame_control)) + woal_cfg80211_display_p2p_actframe( + pkt, + pmevent->event_len - + sizeof(pmevent->event_id) - + MLAN_MAC_ADDR_LENGTH, + ieee80211_get_channel( + priv->wdev->wiphy, + freq), + MFALSE); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /**Forward Deauth, Auth and disassoc frame to + * Host*/ + if (priv->host_mlme && + (GET_BSS_ROLE(priv) != MLAN_BSS_ROLE_UAP) && + (ieee80211_is_deauth( + ((struct ieee80211_mgmt *)pkt) + ->frame_control) || + ieee80211_is_auth( + ((struct ieee80211_mgmt *)pkt) + ->frame_control) || + ieee80211_is_disassoc( + ((struct ieee80211_mgmt *)pkt) + ->frame_control))) { + if (ieee80211_is_auth( + ((struct ieee80211_mgmt *) + pkt) + ->frame_control)) { + PRINTM(MEVENT, + "HostMlme %s: Received auth frame type = 0x%x\n", + priv->netdev->name, + priv->auth_alg); + + if (priv->auth_flag & + HOST_MLME_AUTH_PENDING) { + if (priv->auth_alg != + WLAN_AUTH_SAE) { + priv->auth_flag &= + ~HOST_MLME_AUTH_PENDING; + priv->auth_flag |= + HOST_MLME_AUTH_DONE; + priv->phandle + ->host_mlme_priv = + priv; + queue_work( + priv->phandle + ->evt_workqueue, + &priv->phandle + ->host_mlme_work); + } + } else { + PRINTM(MERROR, + "HostMlme %s: Drop auth frame, auth_flag=0x%x auth_alg=0x%x\n", + priv->netdev + ->name, + priv->auth_flag, + priv->auth_alg); + break; + } + } else { + PRINTM(MEVENT, + "HostMlme %s: Receive deauth/disassociate\n", + priv->netdev->name); + priv->cfg_disconnect = MTRUE; + woal_mgmt_frame_register( + priv, + IEEE80211_STYPE_DEAUTH, + MFALSE); + woal_mgmt_frame_register( + priv, + IEEE80211_STYPE_DISASSOC, + MFALSE); + priv->host_mlme = MFALSE; + priv->auth_flag = 0; + priv->auth_alg = 0xFFFF; + if (!priv->wdev->current_bss) { + PRINTM(MEVENT, + "HostMlme: Drop deauth/disassociate, we already disconnected\n"); + break; + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + mutex_lock(&priv->wdev->mtx); + cfg80211_rx_mlme_mgmt( + priv->netdev, pkt, + pmevent->event_len - + sizeof(pmevent->event_id) - + MLAN_MAC_ADDR_LENGTH); + mutex_unlock(&priv->wdev->mtx); +#else + if (ieee80211_is_deauth( + ((struct ieee80211_mgmt *) + pkt) + ->frame_control)) + cfg80211_send_deauth( + priv->netdev, pkt, + pmevent->event_len - + sizeof(pmevent->event_id) - + MLAN_MAC_ADDR_LENGTH); + else if (ieee80211_is_auth( + ((struct ieee80211_mgmt + *)pkt) + ->frame_control)) + cfg80211_send_rx_auth( + priv->netdev, pkt, + pmevent->event_len - + sizeof(pmevent->event_id) - + MLAN_MAC_ADDR_LENGTH); + else if (ieee80211_is_disassoc( + ((struct ieee80211_mgmt + *)pkt) + ->frame_control)) + cfg80211_send_disassoc( + priv->netdev, pkt, + pmevent->event_len - + sizeof(pmevent->event_id) - + MLAN_MAC_ADDR_LENGTH); + +#endif + } else +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + cfg80211_rx_mgmt( +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + priv->wdev, +#else + priv->netdev, +#endif + freq, 0, + ((const t_u8 *) + pmevent->event_buf) + + sizeof(pmevent->event_id), + pmevent->event_len - + sizeof(pmevent->event_id) - + MLAN_MAC_ADDR_LENGTH +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + , + 0 +#endif +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + , + GFP_ATOMIC +#endif + ); +#else + cfg80211_rx_mgmt( + priv->netdev, freq, + ((const t_u8 *)pmevent->event_buf) + + sizeof(pmevent->event_id), + pmevent->event_len - + sizeof(pmevent->event_id) - + MLAN_MAC_ADDR_LENGTH, + GFP_ATOMIC); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_packet_fate_monitor( + priv, PACKET_TYPE_RX, + RX_PKT_FATE_SUCCESS, + FRAME_TYPE_80211_MGMT, 0, 0, + ((t_u8 *)pmevent->event_buf) + + sizeof(pmevent->event_id), + pmevent->event_len - + sizeof(pmevent->event_id) - + MLAN_MAC_ADDR_LENGTH); +#endif + } +#endif /* KERNEL_VERSION */ + } +#endif /* STA_CFG80211 || UAP_CFG80211 */ + break; +#endif /* UAP_SUPPORT */ + case MLAN_EVENT_ID_DRV_PASSTHRU: + woal_broadcast_event(priv, pmevent->event_buf, + pmevent->event_len); + break; + case MLAN_EVENT_ID_DRV_ASSOC_FAILURE_REPORT: + PRINTM(MINFO, "Assoc result\n"); + + if (priv->media_connected) { + PRINTM(MINFO, "Assoc_Rpt: Media Connected\n"); + if (!netif_carrier_ok(priv->netdev)) { + PRINTM(MINFO, "Assoc_Rpt: Carrier On\n"); + netif_carrier_on(priv->netdev); + } + PRINTM(MINFO, "Assoc_Rpt: Queue Start\n"); + woal_wake_queue(priv->netdev); + } + break; + case MLAN_EVENT_ID_DRV_MEAS_REPORT: + /* We have received measurement report, wakeup measurement wait + * queue */ + PRINTM(MINFO, "Measurement Report\n"); + /* Going out of CAC checking period */ + if (priv->phandle->cac_period == MTRUE) { + priv->phandle->cac_period = MFALSE; + if (priv->phandle->meas_wait_q_woken == MFALSE) { + priv->phandle->meas_wait_q_woken = MTRUE; + wake_up_interruptible( + &priv->phandle->meas_wait_q); + } + + /* Execute delayed BSS START command */ + if (priv->phandle->delay_bss_start == MTRUE) { + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + + /* Clear flag */ + priv->phandle->delay_bss_start = MFALSE; + + PRINTM(MMSG, + "Now CAC measure period end. Execute delayed BSS Start command.\n"); + + req = woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_bss)); + if (!req) { + PRINTM(MERROR, + "Failed to allocate ioctl request buffer\n"); + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + bss->sub_command = MLAN_OID_BSS_START; + moal_memcpy_ext( + priv->phandle, &bss->param.ssid_bssid, + &priv->phandle->delay_ssid_bssid, + sizeof(mlan_ssid_bssid), + sizeof(mlan_ssid_bssid)); + + if (woal_request_ioctl(priv, req, + MOAL_NO_WAIT) != + MLAN_STATUS_PENDING) { + PRINTM(MERROR, + "Delayed BSS Start operation failed!\n"); + kfree(req); + } + + PRINTM(MMSG, "BSS START Complete!\n"); + } +#ifdef UAP_SUPPORT +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (priv->uap_host_based && + moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) + woal_cfg80211_dfs_vendor_event( + priv, event_dfs_cac_finished, + &priv->chan); +#endif +#endif +#endif + } + break; + case MLAN_EVENT_ID_FW_TX_STATUS: { +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + unsigned long flag; + tx_status_event *tx_status = + (tx_status_event *)(pmevent->event_buf + 4); + struct tx_status_info *tx_info = NULL; + PRINTM(MINFO, + "Receive Tx status: tx_token=%d, pkt_type=0x%x, status=%d tx_seq_num=%d\n", + tx_status->tx_token_id, tx_status->packet_type, + tx_status->status, priv->tx_seq_num); + spin_lock_irqsave(&priv->tx_stat_lock, flag); + tx_info = woal_get_tx_info(priv, tx_status->tx_token_id); + if (tx_info) { + bool ack; + struct sk_buff *skb = (struct sk_buff *)tx_info->tx_skb; + list_del(&tx_info->link); + spin_unlock_irqrestore(&priv->tx_stat_lock, flag); + if (!tx_status->status) + ack = true; + else + ack = false; +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (priv->phandle->remain_on_channel && + tx_info->cancel_remain_on_channel) { + remain_priv = + priv->phandle->priv + [priv->phandle->remain_bss_index]; + if (remain_priv) { + woal_cfg80211_remain_on_channel_cfg( + remain_priv, MOAL_NO_WAIT, + MTRUE, &channel_status, NULL, 0, + 0); + priv->phandle->remain_on_channel = + MFALSE; + } + } +#endif + PRINTM(MEVENT, "Wlan: Tx status=%d\n", ack); +#if defined(STA_CFG80211) || defined(UAP_CFG80211) + if (tx_info->tx_cookie) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + cfg80211_mgmt_tx_status(priv->netdev, + tx_info->tx_cookie, + skb->data, skb->len, + ack, GFP_ATOMIC); +#else + cfg80211_mgmt_tx_status(priv->wdev, + tx_info->tx_cookie, + skb->data, skb->len, + ack, GFP_ATOMIC); +#endif +#endif + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_packet_fate_monitor(priv, PACKET_TYPE_TX, + ack ? TX_PKT_FATE_ACKED : + TX_PKT_FATE_SENT, + FRAME_TYPE_80211_MGMT, 0, 0, + skb->data, skb->len); +#endif +#endif + dev_kfree_skb_any(skb); + kfree(tx_info); + } else + spin_unlock_irqrestore(&priv->tx_stat_lock, flag); +#endif + } break; + case MLAN_EVENT_ID_DRV_FT_RESPONSE: +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(cfg80211_wext)) { + struct cfg80211_ft_event_params ft_event; + if (priv->ft_pre_connect) + break; + memset(&ft_event, 0, + sizeof(struct cfg80211_ft_event_params)); + PRINTM(MMSG, + "wlan : FT response target AP " MACSTR "\n", + MAC2STR((t_u8 *)pmevent->event_buf)); + DBG_HEXDUMP(MDAT_D, "FT-event ", pmevent->event_buf, + pmevent->event_len); + moal_memcpy_ext(priv->phandle, priv->target_ap_bssid, + pmevent->event_buf, ETH_ALEN, ETH_ALEN); + ft_event.target_ap = priv->target_ap_bssid; + ft_event.ies = pmevent->event_buf + ETH_ALEN; + ft_event.ies_len = pmevent->event_len - ETH_ALEN; + /*TSPEC info is needed by RIC, However the TS operation + * is configured by mlanutl*/ + /*So do not add RIC temporally*/ + /*when add RIC, 1. query TS status, 2. copy tspec from + * addts command*/ + ft_event.ric_ies = NULL; + ft_event.ric_ies_len = 0; + + cfg80211_ft_event(priv->netdev, &ft_event); + priv->ft_pre_connect = MTRUE; + + if (priv->ft_roaming_triggered_by_driver || + !(priv->ft_cap & MBIT(0))) { + priv->ft_wait_condition = MTRUE; + wake_up(&priv->ft_wait_q); + } + } +#endif +#endif + break; + 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(t_void *pmoal_handle, t_u32 level, char *pformat, IN...) +{ +#ifdef DEBUG_LEVEL1 + va_list args; + + if (level & MHEX_DUMP) { + t_u8 *buf = NULL; + int len = 0; + + va_start(args, pformat); + buf = (t_u8 *)va_arg(args, t_u8 *); + len = (int)va_arg(args, int); + va_end(args); + +#ifdef DEBUG_LEVEL2 + if (level & MINFO) + HEXDUMP((char *)pformat, buf, len); + else +#endif /* DEBUG_LEVEL2 */ + { + if (level & MERROR) + DBG_HEXDUMP(MERROR, (char *)pformat, buf, len); + if (level & MCMD_D) + DBG_HEXDUMP(MCMD_D, (char *)pformat, buf, len); + if (level & MDAT_D) + DBG_HEXDUMP(MDAT_D, (char *)pformat, buf, len); + if (level & MIF_D) + DBG_HEXDUMP(MIF_D, (char *)pformat, buf, len); + if (level & MFW_D) + DBG_HEXDUMP(MFW_D, (char *)pformat, buf, len); + if (level & MEVT_D) + DBG_HEXDUMP(MEVT_D, (char *)pformat, buf, len); + } + } else { + if (drvdbg & level) { + va_start(args, pformat); + vprintk(pformat, args); + va_end(args); + } + } +#endif /* DEBUG_LEVEL1 */ +} + +/** + * @brief This function prints the network interface name + * + * @param pmoal_handle Pointer to the MOAL context + * @param bss_index BSS index + * @param level debug level + * + * @return N/A + */ +t_void moal_print_netintf(t_void *pmoal_handle, t_u32 bss_index, t_u32 level) +{ +#ifdef DEBUG_LEVEL1 + moal_handle *phandle = (moal_handle *)pmoal_handle; + + if (phandle) { + if ((bss_index < MLAN_MAX_BSS_NUM) && + phandle->priv[bss_index] && + phandle->priv[bss_index]->netdev) { + if (drvdbg & level) + printk("%s: ", + phandle->priv[bss_index]->netdev->name); + } + } +#endif /* DEBUG_LEVEL1 */ +} + +/** + * @brief This function asserts the existence of the passed argument + * + * @param pmoal_handle A pointer to moal_private structure + * @param cond Condition to check + * + * @return N/A + */ +t_void moal_assert(t_void *pmoal_handle, t_u32 cond) +{ + if (!cond) { + panic("Assert failed: Panic!"); + } +} + +/** + * @brief This function save the histogram data + * + * @param pmoal_handle A pointer to moal_private structure + * @param bss_index BSS index + * @param rx_rate rx rate index + * @param snr snr + * @param nflr noise floor + * @param antenna antenna + * + * @return N/A + */ +t_void moal_hist_data_add(t_void *pmoal_handle, t_u32 bss_index, t_u16 rx_rate, + t_s8 snr, t_s8 nflr, t_u8 antenna) +{ + moal_private *priv = NULL; + priv = woal_bss_index_to_priv(pmoal_handle, bss_index); + if (priv && antenna >= priv->phandle->card_info->histogram_table_num) + antenna = 0; + if (priv && priv->hist_data[antenna]) + woal_hist_data_add(priv, rx_rate, snr, nflr, antenna); +} + +/** + * @brief Performs division of 64-bit num with base + * @brief do_div does two things + * @brief 1. modifies the 64-bit num in place with + * @brief the quotient, i.e., num becomes quotient + * @brief 2. do_div() returns the 32-bit reminder + * + * @param num dividend + * @param base divisor + * @return returns 64-bit quotient + */ +t_u64 moal_do_div(t_u64 num, t_u32 base) +{ + t_u64 val = num; + do_div(val, base); + return val; +} + +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) +/** + * @brief Performs wait event + * + * @param pmoal_handle t_void + * @param bss_index index of priv + * @return MLAN_STATUS_SUCCESS + */ +mlan_status moal_wait_hostcmd_complete(t_void *pmoal_handle, t_u32 bss_index) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + moal_handle *handle = (moal_handle *)pmoal_handle; + moal_private *priv = woal_bss_index_to_priv(handle, bss_index); + long time_left = 0; + + ENTER(); + + if (!priv) { + PRINTM(MERROR, "moal_wait_event: priv is null!\n"); + goto done; + } + + priv->hostcmd_wait_condition = MFALSE; + time_left = wait_event_timeout(priv->hostcmd_wait_q, + priv->hostcmd_wait_condition, + MOAL_IOCTL_TIMEOUT); + + if (!time_left) { + PRINTM(MERROR, "moal_wait_event: wait timeout "); + status = MLAN_STATUS_FAILURE; + } + +done: + LEAVE(); + return status; +} + +/** + * @brief wake up esa wait_q + * + * @param pmoal_handle t_void + * @param bss_index index of priv + * @return MLAN_STATUS_SUCCESS + */ +mlan_status moal_notify_hostcmd_complete(t_void *pmoal_handle, t_u32 bss_index) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + moal_handle *handle = (moal_handle *)pmoal_handle; + moal_private *priv = woal_bss_index_to_priv(handle, bss_index); + + ENTER(); + + priv->hostcmd_wait_condition = MTRUE; + wake_up(&priv->hostcmd_wait_q); + + LEAVE(); + return status; +} +#endif diff --git a/mxm_wifiex/wlan_src/mlinux/moal_shim.h b/mxm_wifiex/wlan_src/mlinux/moal_shim.h new file mode 100644 index 0000000..ddcf9c0 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_shim.h @@ -0,0 +1,122 @@ +/** @file moal_shim.h + * + * @brief This file contains declaration referring to + * functions defined in moal module + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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(t_void *pmoal_handle, t_u32 offset, t_u32 len, + t_u8 *pbuf); +mlan_status moal_get_vdll_data(t_void *pmoal_handle, t_u32 len, t_u8 *pbuf); +mlan_status moal_get_hw_spec_complete(t_void *pmoal_handle, mlan_status status, + mlan_hw_info *phw, pmlan_bss_tbl ptbl); +mlan_status moal_init_fw_complete(t_void *pmoal_handle, mlan_status status); +mlan_status moal_shutdown_fw_complete(t_void *pmoal_handle, mlan_status status); +mlan_status moal_ioctl_complete(t_void *pmoal_handle, + pmlan_ioctl_req pioctl_req, mlan_status status); +mlan_status moal_alloc_mlan_buffer(t_void *pmoal_handle, t_u32 size, + pmlan_buffer *pmbuf); +mlan_status moal_free_mlan_buffer(t_void *pmoal_handle, pmlan_buffer pmbuf); +mlan_status moal_send_packet_complete(t_void *pmoal_handle, pmlan_buffer pmbuf, + mlan_status status); +#ifdef USB +mlan_status moal_recv_complete(t_void *pmoal_handle, pmlan_buffer pmbuf, + t_u32 port, mlan_status status); +mlan_status moal_write_data_async(t_void *pmoal_handle, pmlan_buffer pmbuf, + t_u32 port); +#endif + +#if defined(SDIO) || defined(PCIE) +/** moal_write_reg */ +mlan_status moal_write_reg(t_void *pmoal_handle, t_u32 reg, t_u32 data); +/** moal_read_reg */ +mlan_status moal_read_reg(t_void *pmoal_handle, t_u32 reg, t_u32 *data); +#endif /* SDIO || PCIE */ +mlan_status moal_write_data_sync(t_void *pmoal_handle, pmlan_buffer pmbuf, + t_u32 port, t_u32 timeout); +mlan_status moal_read_data_sync(t_void *pmoal_handle, pmlan_buffer pmbuf, + t_u32 port, t_u32 timeout); +mlan_status moal_recv_packet(t_void *pmoal_handle, pmlan_buffer pmbuf); +mlan_status moal_recv_event(t_void *pmoal_handle, pmlan_event pmevent); +mlan_status moal_malloc(t_void *pmoal_handle, t_u32 size, t_u32 flag, + t_u8 **ppbuf); +mlan_status moal_mfree(t_void *pmoal_handle, t_u8 *pbuf); +mlan_status moal_vmalloc(t_void *pmoal_handle, t_u32 size, t_u8 **ppbuf); +mlan_status moal_vfree(t_void *pmoal_handle, t_u8 *pbuf); +#ifdef PCIE +mlan_status moal_malloc_consistent(t_void *pmoal_handle, t_u32 size, + t_u8 **ppbuf, t_pu64 pbuf_pa); +mlan_status moal_mfree_consistent(t_void *pmoal_handle, t_u32 size, t_u8 *pbuf, + t_u64 buf_pa); +mlan_status moal_map_memory(t_void *pmoal_handle, t_u8 *pbuf, t_u64 *pbuf_pa, + t_u32 size, t_u32 flag); +mlan_status moal_unmap_memory(t_void *pmoal_handle, t_u8 *pbuf, t_u64 buf_pa, + t_u32 size, t_u32 flag); +#endif /* PCIE */ +t_void *moal_memset(t_void *pmoal_handle, t_void *pmem, t_u8 byte, t_u32 num); +t_void *moal_memcpy(t_void *pmoal_handle, t_void *pdest, const t_void *psrc, + t_u32 num); +t_void *moal_memcpy_ext(t_void *pmoal_handle, t_void *pdest, const t_void *psrc, + t_u32 num, t_u32 dest_size); + +t_void *moal_memmove(t_void *pmoal_handle, t_void *pdest, const t_void *psrc, + t_u32 num); +t_s32 moal_memcmp(t_void *pmoal_handle, const t_void *pmem1, + const t_void *pmem2, t_u32 num); +/** moal_udelay */ +t_void moal_udelay(t_void *pmoal_handle, t_u32 udelay); +t_void moal_usleep_range(t_void *pmoal_handle, t_u32 min_delay, + t_u32 max_delay); +mlan_status moal_get_boot_ktime(t_void *pmoal_handle, t_u64 *pnsec); +mlan_status moal_get_system_time(t_void *pmoal_handle, t_u32 *psec, + t_u32 *pusec); +mlan_status moal_init_lock(t_void *pmoal_handle, t_void **pplock); +mlan_status moal_free_lock(t_void *pmoal_handle, t_void *plock); +mlan_status moal_spin_lock(t_void *pmoal_handle, t_void *plock); +mlan_status moal_spin_unlock(t_void *pmoal_handle, t_void *plock); +#if defined(DRV_EMBEDDED_AUTHENTICATOR) || defined(DRV_EMBEDDED_SUPPLICANT) +mlan_status moal_wait_hostcmd_complete(t_void *pmoal_handle, t_u32 bss_index); +mlan_status moal_notify_hostcmd_complete(t_void *pmoal_handle, t_u32 bss_index); +#endif +t_void moal_print(t_void *pmoal_handle, t_u32 level, char *pformat, IN...); +t_void moal_print_netintf(t_void *pmoal_handle, t_u32 bss_index, t_u32 level); +t_void moal_assert(t_void *pmoal_handle, t_u32 cond); +t_void moal_hist_data_add(t_void *pmoal_handle, t_u32 bss_index, t_u16 rx_rate, + t_s8 snr, t_s8 nflr, t_u8 antenna); + +t_u64 moal_do_div(t_u64 num, t_u32 base); + +mlan_status moal_init_timer(t_void *pmoal_handle, t_void **pptimer, + IN t_void (*callback)(t_void *pcontext), + t_void *pcontext); +mlan_status moal_free_timer(t_void *pmoal_handle, t_void *ptimer); +mlan_status moal_start_timer(t_void *pmoal_handle, t_void *ptimer, + t_u8 periodic, t_u32 msec); +mlan_status moal_stop_timer(t_void *pmoal_handle, t_void *ptimer); +void moal_tp_accounting(t_void *pmoal_handle, void *buf, t_u32 drop_point); +void moal_tp_accounting_rx_param(t_void *pmoal_handle, unsigned int type, + unsigned int rsvd1); +#endif /*_MOAL_H */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.c b/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.c new file mode 100644 index 0000000..72f7040 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.c @@ -0,0 +1,7893 @@ +/** @file moal_sta_cfg80211.c + * + * @brief This file contains the functions for STA CFG80211. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_cfg80211_util.h" +#include "moal_sta_cfg80211.h" +#include "moal_eth_ioctl.h" +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#include + +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +extern int fw_region; +#endif +#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, WLAN_CIPHER_SUITE_AES_CMAC, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + WLAN_CIPHER_SUITE_BIP_GMAC_256, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + WLAN_CIPHER_SUITE_GCMP, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + WLAN_CIPHER_SUITE_GCMP_256, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + WLAN_CIPHER_SUITE_CCMP_256, +#endif +}; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +static void +#else +static int +#endif +woal_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +static int woal_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request); +#else +static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +static void woal_cfg80211_abort_scan(struct wiphy *wiphy, + struct wireless_dev *wdev); +#endif +static int woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme); + +static int woal_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + t_u16 reason_code); + +static int woal_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_info *sinfo); + +static int woal_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, int idx, + t_u8 *mac, struct station_info *sinfo); + +static int woal_cfg80211_dump_survey(struct wiphy *wiphy, + struct net_device *dev, int idx, + struct survey_info *survey); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +static int woal_cfg80211_get_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef); +#endif +static int woal_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, bool enabled, + int timeout); +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) +static int woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) +static int woal_cfg80211_get_tx_power(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct wireless_dev *wdev, +#endif + int *dbm); + +static int woal_cfg80211_set_tx_power(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct wireless_dev *wdev, +#endif +#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + enum tx_power_setting type, +#else + enum nl80211_tx_power_setting type, +#endif + int dbm); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif + u64 cookie); + +static int +woal_cfg80211_remain_on_channel(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif + struct ieee80211_channel *chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + enum nl80211_channel_type channel_type, +#endif + unsigned int duration, u64 *cookie); + +static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct wireless_dev *wdev, +#else + struct net_device *dev, +#endif + u64 cookie); +#endif /* KERNEL_VERSION */ + +#ifdef CONFIG_NL80211_TESTMODE +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +static int woal_testmode_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + void *data, int len); +#else +static int woal_testmode_cmd(struct wiphy *wiphy, void *data, int len); +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +int woal_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_sched_scan_request *request); +int woal_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , + u64 reqid +#endif +); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +int woal_cfg80211_resume(struct wiphy *wiphy); +int woal_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) +static void woal_cfg80211_set_wakeup(struct wiphy *wiphy, bool enabled); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +static int woal_cfg80211_change_station(struct wiphy *wiphy, + struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +int woal_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +static int woal_cfg80211_authenticate(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_auth_request *req); + +static int woal_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req); +#ifdef UAP_SUPPORT +int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_parameters *params); +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#ifdef UAP_SUPPORT +static int woal_cfg80211_add_station(struct wiphy *wiphy, + struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params); +#endif +#endif + +static int woal_cfg80211_deauthenticate(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_deauth_request *req); + +static int woal_cfg80211_disassociate(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_disassoc_request *req); + +/** cfg80211 operations */ +static struct cfg80211_ops woal_cfg80211_ops = { + .change_virtual_intf = woal_cfg80211_change_virtual_intf, + .scan = woal_cfg80211_scan, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + .abort_scan = woal_cfg80211_abort_scan, +#endif + .connect = woal_cfg80211_connect, + .disconnect = woal_cfg80211_disconnect, + .deauth = woal_cfg80211_deauthenticate, + .disassoc = woal_cfg80211_disassociate, + .get_station = woal_cfg80211_get_station, + .dump_station = woal_cfg80211_dump_station, + .dump_survey = woal_cfg80211_dump_survey, + .set_wiphy_params = woal_cfg80211_set_wiphy_params, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 5, 0) + .set_channel = woal_cfg80211_set_channel, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + .get_channel = woal_cfg80211_get_channel, +#endif + .add_key = woal_cfg80211_add_key, + .del_key = woal_cfg80211_del_key, + .set_default_key = woal_cfg80211_set_default_key, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + .set_default_mgmt_key = woal_cfg80211_set_default_mgmt_key, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + .set_rekey_data = woal_cfg80211_set_rekey_data, +#endif + .set_pmksa = woal_cfg80211_set_pmksa, + .del_pmksa = woal_cfg80211_del_pmksa, + .flush_pmksa = woal_cfg80211_flush_pmksa, + .set_power_mgmt = woal_cfg80211_set_power_mgmt, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) + .set_tx_power = woal_cfg80211_set_tx_power, + .get_tx_power = woal_cfg80211_get_tx_power, +#endif + .set_bitrate_mask = woal_cfg80211_set_bitrate_mask, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .sched_scan_start = woal_cfg80211_sched_scan_start, + .sched_scan_stop = woal_cfg80211_sched_scan_stop, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .suspend = woal_cfg80211_suspend, + .resume = woal_cfg80211_resume, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + .set_wakeup = woal_cfg80211_set_wakeup, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) + .set_antenna = woal_cfg80211_set_antenna, + .get_antenna = woal_cfg80211_get_antenna, +#endif +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) + .set_cqm_rssi_config = woal_cfg80211_set_cqm_rssi_config, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .change_station = woal_cfg80211_change_station, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + .update_ft_ies = woal_cfg80211_update_ft_ies, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + .set_qos_map = woal_cfg80211_set_qos_map, +#endif +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + .set_coalesce = woal_cfg80211_set_coalesce, +#endif + .add_virtual_intf = woal_cfg80211_add_virtual_intf, + .del_virtual_intf = woal_cfg80211_del_virtual_intf, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + .start_ap = woal_cfg80211_add_beacon, + .change_beacon = woal_cfg80211_set_beacon, + .stop_ap = woal_cfg80211_del_beacon, +#else + .add_beacon = woal_cfg80211_add_beacon, + .set_beacon = woal_cfg80211_set_beacon, + .del_beacon = woal_cfg80211_del_beacon, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + .change_bss = woal_cfg80211_change_bss, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + .add_station = woal_cfg80211_add_station, +#endif + .del_station = woal_cfg80211_del_station, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) + .set_txq_params = woal_cfg80211_set_txq_params, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + .set_mac_acl = woal_cfg80211_set_mac_acl, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + .start_radar_detection = woal_cfg80211_start_radar_detection, + + .channel_switch = woal_cfg80211_channel_switch, +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) + .update_mgmt_frame_registrations = woal_cfg80211_mgmt_frame_register, +#else + .mgmt_frame_register = woal_cfg80211_mgmt_frame_register, +#endif + .mgmt_tx = woal_cfg80211_mgmt_tx, +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + .mgmt_tx_cancel_wait = woal_cfg80211_mgmt_tx_cancel_wait, + .remain_on_channel = woal_cfg80211_remain_on_channel, + .cancel_remain_on_channel = woal_cfg80211_cancel_remain_on_channel, +#endif + +#ifdef CONFIG_NL80211_TESTMODE + .testmode_cmd = woal_testmode_cmd, +#endif +}; + +/** Region code mapping */ +typedef struct _region_code_t { + /** Region */ + t_u8 region[COUNTRY_CODE_LEN]; +} region_code_t; + +static const struct ieee80211_regdomain mrvl_regdom = { + .n_reg_rules = 4, + .alpha2 = "99", + .reg_rules = { + /* IEEE 802.11b/g, channels 1..11 */ + REG_RULE(2412 - 10, 2472 + 10, 40, 6, 20, 0), + /* If any */ + /* IEEE 802.11 channel 14 - Only JP enables + * this and for 802.11b only + */ + REG_RULE(2484 - 10, 2484 + 10, 20, 6, 20, 0), + /* IEEE 802.11a, channel 36..64 */ + REG_RULE(5150 - 10, 5350 + 10, 80, 6, 20, 0), + /* IEEE 802.11a, channel 100..165 */ + REG_RULE(5470 - 10, 5850 + 10, 80, 6, 20, 0), + }}; +/******************************************************** + Local Variables +********************************************************/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +// clang-format off +static const struct ieee80211_txrx_stypes + ieee80211_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = MBIT(IEEE80211_STYPE_ACTION >> 4) | + MBIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = MBIT(IEEE80211_STYPE_ACTION >> 4) | + MBIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_AP] = { + .tx = 0xffff, + .rx = MBIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + MBIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + MBIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + MBIT(IEEE80211_STYPE_DISASSOC >> 4) | + MBIT(IEEE80211_STYPE_AUTH >> 4) | + MBIT(IEEE80211_STYPE_DEAUTH >> 4) | + MBIT(IEEE80211_STYPE_ACTION >> 4), + }, + [NL80211_IFTYPE_AP_VLAN] = { + .tx = 0x0000, + .rx = 0x0000, + }, +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = MBIT(IEEE80211_STYPE_ACTION >> 4) | + MBIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = MBIT(IEEE80211_STYPE_ACTION >> 4) | + MBIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = MBIT(IEEE80211_STYPE_ACTION >> 4) | + MBIT(IEEE80211_STYPE_AUTH >> 4) | + MBIT(IEEE80211_STYPE_ASSOC_RESP >> 4) | + MBIT(IEEE80211_STYPE_REASSOC_RESP >> 4) | + MBIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = MBIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + MBIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + MBIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + MBIT(IEEE80211_STYPE_DISASSOC >> 4) | + MBIT(IEEE80211_STYPE_AUTH >> 4) | + MBIT(IEEE80211_STYPE_DEAUTH >> 4) | + MBIT(IEEE80211_STYPE_ACTION >> 4), + }, +#endif +#endif + [NL80211_IFTYPE_MESH_POINT] = { + .tx = 0x0000, + .rx = 0x0000, + }, + +}; +// clang-format on +#endif + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(3, 0, 0) +/** + * NOTE: types in all the sets must be equals to the + * initial value of wiphy->interface_modes + */ +static const struct ieee80211_iface_limit cfg80211_ap_sta_limits[] = { + {.max = 4, + .types = MBIT(NL80211_IFTYPE_STATION) +#ifdef UAP_CFG80211 + | MBIT(NL80211_IFTYPE_AP) +#endif +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + | MBIT(NL80211_IFTYPE_P2P_GO) | + MBIT(NL80211_IFTYPE_P2P_CLIENT) +#endif +#endif + }}; + +static struct ieee80211_iface_combination cfg80211_iface_comb_ap_sta = { + .limits = cfg80211_ap_sta_limits, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(cfg80211_ap_sta_limits), + .max_interfaces = 4, + .beacon_int_infra_match = MTRUE, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + .radar_detect_widths = + MBIT(NL80211_CHAN_WIDTH_20_NOHT) | MBIT(NL80211_CHAN_WIDTH_20), +#endif +}; +#endif + +extern pmoal_handle m_handle[]; + +#ifdef CONFIG_PM +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +static const struct wiphy_wowlan_support wowlan_support = { + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT, + .n_patterns = MAX_NUM_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = WOWLAN_MAX_PATTERN_LEN, + .max_pkt_offset = WOWLAN_MAX_OFFSET_LEN, +}; +static const struct wiphy_wowlan_support wowlan_support_with_gtk = { + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE, + .n_patterns = MAX_NUM_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = WOWLAN_MAX_PATTERN_LEN, + .max_pkt_offset = WOWLAN_MAX_OFFSET_LEN, +}; +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +static const struct wiphy_coalesce_support coalesce_support = { + .n_rules = COALESCE_MAX_RULES, + .max_delay = MAX_COALESCING_DELAY, + .n_patterns = COALESCE_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MAX_PATTERN_LEN, + .max_pkt_offset = MAX_OFFSET_LEN, +}; +#endif + +/******************************************************** + Global Variables +********************************************************/ + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief This function check cfg80211 special region code. + * + * @param region_string Region string + * + * @return MTRUE/MFALSE + */ +t_u8 is_cfg80211_special_region_code(t_u8 *region_string) +{ + t_u8 i; + region_code_t cfg80211_special_region_code[] = { + {"00 "}, {"99 "}, {"98 "}, {"97 "}}; + + for (i = 0; i < COUNTRY_CODE_LEN && region_string[i]; i++) + region_string[i] = toupper(region_string[i]); + + for (i = 0; i < ARRAY_SIZE(cfg80211_special_region_code); i++) { + if (!memcmp(region_string, + cfg80211_special_region_code[i].region, + COUNTRY_CODE_LEN)) { + PRINTM(MIOCTL, "special region code=%s\n", + region_string); + return MTRUE; + } + } + return MFALSE; +} + +/** + * @brief Get the encryption mode from cipher + * + * @param cipher Cipher cuite + * @param wpa_enabled WPA enable or disable + * + * @return MLAN_ENCRYPTION_MODE_* + */ +static int woal_cfg80211_get_encryption_mode(t_u32 cipher, int *wpa_enabled) +{ + int encrypt_mode; + + ENTER(); + + *wpa_enabled = 0; + switch (cipher) { + case MW_AUTH_CIPHER_NONE: + encrypt_mode = MLAN_ENCRYPTION_MODE_NONE; + break; + case WLAN_CIPHER_SUITE_WEP40: + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40; + break; + case WLAN_CIPHER_SUITE_WEP104: + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104; + break; + case WLAN_CIPHER_SUITE_TKIP: + encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP; + *wpa_enabled = 1; + break; + case WLAN_CIPHER_SUITE_CCMP: + encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP; + *wpa_enabled = 1; + break; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + case WLAN_CIPHER_SUITE_CCMP_256: + encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP_256; + *wpa_enabled = 1; + break; +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + case WLAN_CIPHER_SUITE_GCMP: + encrypt_mode = MLAN_ENCRYPTION_MODE_GCMP; + *wpa_enabled = 1; + break; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + case WLAN_CIPHER_SUITE_GCMP_256: + encrypt_mode = MLAN_ENCRYPTION_MODE_GCMP_256; + *wpa_enabled = 1; + break; +#endif + default: + encrypt_mode = -1; + } + + LEAVE(); + return encrypt_mode; +} + +/** + * @brief get associate failure status code + * + * @param priv Pointer to the moal_private driver data struct + * + * @return IEEE status code + */ +static int woal_get_assoc_status(moal_private *priv) +{ + int ret = WLAN_STATUS_UNSPECIFIED_FAILURE; + t_u16 status = (t_u16)(priv->assoc_status & 0xffff); + t_u16 cap = (t_u16)(priv->assoc_status >> 16); + + switch (cap) { + case 0xfffd: + case 0xfffe: + ret = status; + break; + case 0xfffc: + ret = WLAN_STATUS_AUTH_TIMEOUT; + break; + default: + break; + } + PRINTM(MCMND, "Assoc fail: status=%d, cap=0x%x, IEEE status=%d\n", + status, cap, ret); + return ret; +} + +/** + * @brief Check the pairwise or group cipher for + * WEP enabled or not + * + * @param cipher MLAN Cipher cuite + * + * @return 1 -- enable or 0 -- disable + */ +static int woal_cfg80211_is_alg_wep(t_u32 cipher) +{ + int alg = 0; + ENTER(); + + if (cipher == MLAN_ENCRYPTION_MODE_WEP40 || + cipher == MLAN_ENCRYPTION_MODE_WEP104) + alg = 1; + + LEAVE(); + return alg; +} + +/** + * @brief Convert 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_STATION: + return MLAN_BSS_MODE_INFRA; + case NL80211_IFTYPE_UNSPECIFIED: + default: + return MLAN_BSS_MODE_AUTO; + } +} + +/** + * @brief Control WPS Session Enable/Disable + * + * @param priv Pointer to the moal_private driver data struct + * @param enable enable/disable flag + * + * @return 0 --success, otherwise fail + */ +static int woal_wps_cfg(moal_private *priv, int enable) +{ + int ret = 0; + mlan_ds_wps_cfg *pwps = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + PRINTM(MINFO, "WOAL_WPS_SESSION\n"); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pwps = (mlan_ds_wps_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + if (enable) + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + else + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_END; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief configure ASSOC IE + * + * @param priv A pointer to moal private structure + * @param ie A pointer to ie data + * @param ie_len The length of ie data + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_assoc_ies_cfg(moal_private *priv, t_u8 *ie, int ie_len, + t_u8 wait_option) +{ + int bytes_left = ie_len; + t_u8 *pcurrent_ptr = ie; + int total_ie_len; + t_u8 element_len; + int ret = MLAN_STATUS_SUCCESS; + IEEEtypes_ElementId_e element_id; + IEEEtypes_VendorSpecific_t *pvendor_ie; + t_u8 wps_oui[] = {0x00, 0x50, 0xf2, 0x04}; + t_u8 hs20_oui[] = {0x50, 0x6f, 0x9a, 0x10}; + + while (bytes_left >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + if (bytes_left < total_ie_len) { + PRINTM(MERROR, + "InterpretIE: Error in processing IE, bytes left < IE length\n"); + bytes_left = 0; + continue; + } + switch (element_id) { + case RSN_IE: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, "Fail to set RSN IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set RSN IE\n"); + break; + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; + if (!memcmp(pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + (pvendor_ie->vend_hdr.oui_type == wps_oui[3])) { + PRINTM(MIOCTL, "Enable WPS session\n"); + woal_wps_cfg(priv, MTRUE); + } + if (!memcmp(pvendor_ie->vend_hdr.oui, hs20_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + (pvendor_ie->vend_hdr.oui_type == hs20_oui[3])) { + PRINTM(MIOCTL, + "Hotspot2.0 is enabled for this bss\n"); + if (MLAN_STATUS_SUCCESS != + woal_set_hotspotcfg(priv, wait_option, + (HOTSPOT_BY_SUPPLICANT | + HOTSPOT_ENABLED))) { + PRINTM(MERROR, + "Fail to enable hotspot 2.0\n"); + ret = -EFAULT; + goto done; + } + } + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, + "Fail to Set VENDOR SPECIFIC IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, + "Set VENDOR SPECIFIC IE, OUI: %02x:%02x:%02x:%02x\n", + pvendor_ie->vend_hdr.oui[0], + pvendor_ie->vend_hdr.oui[1], + pvendor_ie->vend_hdr.oui[2], + pvendor_ie->vend_hdr.oui_type); + break; + case MOBILITY_DOMAIN: + break; + case FAST_BSS_TRANSITION: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, "Fail to set" + "FAST_BSS_TRANSITION IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set FAST_BSS_TRANSITION IE\n"); + break; + case RIC: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, + "Fail to set" + "RESOURCE INFORMATION CONTAINER IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, + "Set RESOURCE INFORMATION CONTAINER IE\n"); + break; + case EXT_CAPABILITY: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, + "Fail to set Extended Capabilites IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set Extended Capabilities IE\n"); + break; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + case EXTENSION: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, "Fail to set Extension IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set Extension IE\n"); + break; + case FRAGMENT: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, "Fail to set Fragmented IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set Fragmented IE\n"); + break; +#endif + default: + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + pcurrent_ptr, &total_ie_len, + wait_option)) { + PRINTM(MERROR, "Fail to set GEN IE\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Set GEN IE\n"); + break; + } + pcurrent_ptr += element_len + 2; + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + } +done: + return ret; +} + +#ifdef CONFIG_NL80211_TESTMODE +enum moal_tm_attr { + __MOAL_TM_ATTR_INVALID = 0, + MOAL_TM_ATTR_CMD = 1, + MOAL_TM_ATTR_DATA = 2, + + /* keep last */ + __MOAL_TM_ATTR_AFTER_LAST, + MOAL_TM_ATTR_MAX = __MOAL_TM_ATTR_AFTER_LAST - 1, +}; + +static const struct nla_policy moal_tm_policy[MOAL_TM_ATTR_MAX + 1] = { + [MOAL_TM_ATTR_CMD] = {.type = NLA_U32}, + [MOAL_TM_ATTR_DATA] = {.type = NLA_BINARY, + .len = MRVDRV_SIZE_OF_CMD_BUFFER}, +}; + +enum moal_tm_command { + MOAL_TM_CMD_HOSTCMD = 0, +}; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +static int woal_testmode_cmd(struct wiphy *wiphy, struct wireless_dev *wdev, + void *data, int len) +#else +static int woal_testmode_cmd(struct wiphy *wiphy, void *data, int len) +#endif +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = + (moal_private *)woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + struct nlattr *tb[MOAL_TM_ATTR_MAX + 1]; + struct sk_buff *skb; + int err; + + if (!priv) + return -EINVAL; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + err = nla_parse(tb, MOAL_TM_ATTR_MAX, data, len, moal_tm_policy, NULL); +#else + err = nla_parse(tb, MOAL_TM_ATTR_MAX, data, len, moal_tm_policy); +#endif + if (err) + return err; + + if (!tb[MOAL_TM_ATTR_CMD]) + return -EINVAL; + + switch (nla_get_u32(tb[MOAL_TM_ATTR_CMD])) { + case MOAL_TM_CMD_HOSTCMD: + if (!tb[MOAL_TM_ATTR_DATA]) + return -EINVAL; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) + return -ENOMEM; + 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 = nla_len(tb[MOAL_TM_ATTR_DATA]); + moal_memcpy_ext(priv->phandle, misc_cfg->param.hostcmd.cmd, + nla_data(tb[MOAL_TM_ATTR_DATA]), + misc_cfg->param.hostcmd.len, + MRVDRV_SIZE_OF_CMD_BUFFER); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + err = -EFAULT; + goto error; + } + /* process hostcmd response*/ + skb = cfg80211_testmode_alloc_reply_skb( + wiphy, misc_cfg->param.hostcmd.len); + if (!skb) { + kfree(req); + return -ENOMEM; + } + err = nla_put(skb, MOAL_TM_ATTR_DATA, + misc_cfg->param.hostcmd.len, + misc_cfg->param.hostcmd.cmd); + if (err) { + kfree(req); + kfree_skb(skb); + return -EMSGSIZE; + } + err = cfg80211_testmode_reply(skb); + kfree(req); + return err; + default: + return -EOPNOTSUPP; + } +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + return err; +} +#endif + +/** + * @brief Send domain info command to FW + * + * @param priv A pointer to moal_private structure + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_send_domain_info_cmd_fw(moal_private *priv, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + enum ieee80211_band band; + struct ieee80211_supported_band *sband = NULL; + struct ieee80211_channel *channel = NULL; + t_u8 no_of_sub_band = 0; + t_u8 no_of_parsed_chan = 0; + t_u8 first_chan = 0, next_chan = 0, max_pwr = 0; + t_u8 i, flag = 0; + mlan_ds_11d_cfg *cfg_11d = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv->wdev || !priv->wdev->wiphy) { + PRINTM(MERROR, "No wdev or wiphy in priv\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + band = priv->phandle->band; + if (!priv->wdev->wiphy->bands[band]) { + PRINTM(MERROR, "11D: setting domain info in FW failed band=%d", + band); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (MTRUE == + is_cfg80211_special_region_code(priv->phandle->country_code)) { + PRINTM(MIOCTL, + "skip region code config, cfg80211 special region code: %s\n", + priv->phandle->country_code); + goto done; + } + PRINTM(MIOCTL, "Send domain info: country=%c%c band=%d\n", + priv->phandle->country_code[0], priv->phandle->country_code[1], + band); + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + cfg_11d->sub_command = MLAN_OID_11D_DOMAIN_INFO_EXT; + req->req_id = MLAN_IOCTL_11D_CFG; + req->action = MLAN_ACT_SET; + + /* Set country code */ + cfg_11d->param.domain_info.country_code[0] = + priv->phandle->country_code[0]; + cfg_11d->param.domain_info.country_code[1] = + priv->phandle->country_code[1]; + cfg_11d->param.domain_info.country_code[2] = ' '; + cfg_11d->param.domain_info.band = band; + + sband = priv->wdev->wiphy->bands[band]; + for (i = 0; (i < sband->n_channels) && + (no_of_sub_band < MRVDRV_MAX_SUBBAND_802_11D); + i++) { + channel = &sband->channels[i]; + if (channel->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (!flag) { + flag = 1; + next_chan = first_chan = (t_u32)channel->hw_value; + max_pwr = channel->max_power; + no_of_parsed_chan = 1; + continue; + } + + if (channel->hw_value == next_chan + 1 && + channel->max_power == max_pwr) { + next_chan++; + no_of_parsed_chan++; + } else { + cfg_11d->param.domain_info.sub_band[no_of_sub_band] + .first_chan = first_chan; + cfg_11d->param.domain_info.sub_band[no_of_sub_band] + .no_of_chan = no_of_parsed_chan; + cfg_11d->param.domain_info.sub_band[no_of_sub_band] + .max_tx_pwr = max_pwr; + no_of_sub_band++; + next_chan = first_chan = (t_u32)channel->hw_value; + max_pwr = channel->max_power; + no_of_parsed_chan = 1; + } + } + + if (flag && (no_of_sub_band < MRVDRV_MAX_SUBBAND_802_11D)) { + cfg_11d->param.domain_info.sub_band[no_of_sub_band].first_chan = + first_chan; + cfg_11d->param.domain_info.sub_band[no_of_sub_band].no_of_chan = + no_of_parsed_chan; + cfg_11d->param.domain_info.sub_band[no_of_sub_band].max_tx_pwr = + max_pwr; + no_of_sub_band++; + } + cfg_11d->param.domain_info.no_of_sub_band = no_of_sub_band; + + PRINTM(MCMND, "CFG80211: Country=%c%c, band=%d, no_of_sub_band=%d\n", + priv->phandle->country_code[0], priv->phandle->country_code[1], + priv->phandle->band, cfg_11d->param.domain_info.no_of_sub_band); + /* Send domain info command to FW */ + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "11D: Error setting domain info in FW\n"); + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to change the channel and + * change domain info according to that channel + * + * @param priv A pointer to moal_private structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type of nl80211_channel_type + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +int woal_set_rf_channel(moal_private *priv, struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + t_u8 wait_option) +{ + int ret = 0; + t_u32 mode, config_bands = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!chan) { + LEAVE(); + return -EINVAL; + } + mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio_cfg = (mlan_ds_radio_cfg *)req->pbuf; + radio_cfg->sub_command = MLAN_OID_BAND_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + /* Get config_bands, adhoc_start_band and adhoc_channel values from MLAN + */ + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + req->action = MLAN_ACT_SET; + priv->phandle->band = chan->band; + /* Set appropriate bands */ + if (chan->band == IEEE80211_BAND_2GHZ) + config_bands = BAND_B | BAND_G | BAND_GN; + else { + config_bands = BAND_AN | BAND_A; + } + if (mode == MLAN_BSS_MODE_IBSS) { + radio_cfg->param.band_cfg.adhoc_start_band = config_bands; + radio_cfg->param.band_cfg.adhoc_channel = + ieee80211_frequency_to_channel(chan->center_freq); + } + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + woal_send_domain_info_cmd_fw(priv, wait_option); + + PRINTM(MINFO, "Setting band %d, and mode = %d channel=%d\n", + config_bands, mode, + ieee80211_frequency_to_channel(chan->center_freq)); + + if (MLAN_STATUS_SUCCESS != + woal_change_adhoc_chan( + priv, ieee80211_frequency_to_channel(chan->center_freq), + wait_option)) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set ewpa mode + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_set_ewpa_mode(moal_private *priv, t_u8 wait_option, + mlan_ssid_bssid *ssid_bssid) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!priv->phandle->card_info->embedded_supp) { + ret = -EOPNOTSUPP; + goto error; + } + /* 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)); + moal_memcpy_ext(priv->phandle, &sec->param.passphrase.ssid, + &ssid_bssid->ssid, sizeof(sec->param.passphrase.ssid), + sizeof(sec->param.passphrase.ssid)); + moal_memcpy_ext(priv->phandle, &sec->param.passphrase.bssid, + &ssid_bssid->bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(sec->param.passphrase.bssid)); + sec->param.passphrase.psk_type = MLAN_PSK_QUERY; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) + goto error; + sec->param.ewpa_enabled = MFALSE; + if (sec->param.passphrase.psk_type == MLAN_PSK_PASSPHRASE) { + if (sec->param.passphrase.psk.passphrase.passphrase_len > 0) + sec->param.ewpa_enabled = MTRUE; + } else if (sec->param.passphrase.psk_type == MLAN_PSK_PMK) + sec->param.ewpa_enabled = MTRUE; + + sec->sub_command = MLAN_OID_SEC_CFG_EWPA_ENABLED; + req->action = MLAN_ACT_SET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, wait_option); + +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set encryption mode and enable WPA + * + * @param priv A pointer to moal_private structure + * @param encrypt_mode Encryption mode + * @param wpa_enabled WPA enable or not + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_set_auth(moal_private *priv, int encrypt_mode, + int wpa_enabled, t_u8 wait_option) +{ + int ret = 0; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_encrypt_mode(priv, wait_option, encrypt_mode)) + ret = -EFAULT; + + if (wpa_enabled) { + if (MLAN_STATUS_SUCCESS != + woal_set_wpa_enable(priv, wait_option, 1)) + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Informs the CFG802.11 subsystem of a new BSS connection. + * + * The following information are sent to the CFG802.11 subsystem + * to register the new BSS connection. If we do not register the new BSS, + * a kernel panic will result. + * - MAC address + * - Capabilities + * - Beacon period + * - RSSI value + * - Channel + * - Supported rates IE + * - Extended capabilities IE + * - DS parameter set IE + * - HT Capability IE + * - Vendor Specific IE (221) + * - WPA IE + * - RSN IE + * + * @param priv A pointer to moal_private structure + * @param ssid_bssid A pointer to A pointer to mlan_ssid_bssid structure + * @param wait_option wait_option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_inform_bss_from_scan_result(moal_private *priv, + mlan_ssid_bssid *ssid_bssid, + t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct ieee80211_channel *chan; + mlan_scan_resp scan_resp; + BSSDescriptor_t *scan_table; + t_u64 ts = 0; + u16 cap_info = 0; + int i = 0; + struct cfg80211_bss *pub = NULL; + + ENTER(); + if (!priv->wdev || !priv->wdev->wiphy) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + memset(&scan_resp, 0, sizeof(scan_resp)); + if (MLAN_STATUS_SUCCESS != + woal_get_scan_table(priv, wait_option, &scan_resp)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (scan_resp.num_in_scan_table) { + scan_table = (BSSDescriptor_t *)scan_resp.pscan_table; + for (i = 0; i < scan_resp.num_in_scan_table; i++) { + if (ssid_bssid) { + /* Inform specific BSS only */ + if (memcmp(ssid_bssid->ssid.ssid, + scan_table[i].ssid.ssid, + ssid_bssid->ssid.ssid_len) || + memcmp(ssid_bssid->bssid, + scan_table[i].mac_address, ETH_ALEN)) + continue; + } + if (!scan_table[i].freq) { + scan_table[i].freq = + ieee80211_channel_to_frequency( + (int)scan_table[i].channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + woal_band_cfg_to_ieee_band( + scan_table[i].bss_band) +#endif + ); + } + chan = ieee80211_get_channel(priv->wdev->wiphy, + scan_table[i].freq); + if (!chan) { + PRINTM(MCMND, + "Fail to get chan with freq: channel=%d freq=%d\n", + (int)scan_table[i].channel, + (int)scan_table[i].freq); + continue; + } +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + !ssid_bssid) { + if (!strncmp(scan_table[i].ssid.ssid, "DIRECT-", + strlen("DIRECT-"))) { + PRINTM(MCMND, + "wlan: P2P device " MACSTR + " found, channel=%d\n", + MAC2STR(scan_table[i] + .mac_address), + (int)chan->hw_value); + } + } +#endif +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + /** Andorid's Location service is expecting timestamp to + * be local time (in microsecond) since boot; and not + * the TSF found in the beacon. */ + ts = ktime_to_us(ktime_get_boottime()); +#else + moal_memcpy_ext(priv->phandle, &ts, + scan_table[i].time_stamp, sizeof(ts), + sizeof(ts)); +#endif + moal_memcpy_ext(priv->phandle, &cap_info, + &scan_table[i].cap_info, + sizeof(cap_info), sizeof(cap_info)); + pub = cfg80211_inform_bss( + priv->wdev->wiphy, chan, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + CFG80211_BSS_FTYPE_UNKNOWN, +#endif + scan_table[i].mac_address, ts, cap_info, + scan_table[i].beacon_period, + scan_table[i].pbeacon_buf + + WLAN_802_11_FIXED_IE_SIZE, + scan_table[i].beacon_buf_size - + WLAN_802_11_FIXED_IE_SIZE, + -RSSI_DBM_TO_MDM(scan_table[i].rssi), + GFP_KERNEL); + if (pub) { +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + pub->len_information_elements = + pub->len_beacon_ies; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + cfg80211_put_bss(priv->wdev->wiphy, pub); +#else + cfg80211_put_bss(pub); +#endif + } + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief Informs the CFG802.11 subsystem of a new IBSS connection. + * + * The following information are sent to the CFG802.11 subsystem + * to register the new IBSS connection. If we do not register the + * new IBSS, a kernel panic will result. + * - MAC address + * - Capabilities + * - Beacon period + * - RSSI value + * - Channel + * - Supported rates IE + * - Extended capabilities IE + * - DS parameter set IE + * - HT Capability IE + * - Vendor Specific IE (221) + * - WPA IE + * - RSN IE + * + * @param priv A pointer to moal_private structure + * @param cahn A pointer to ieee80211_channel structure + * @param beacon_interval Beacon interval + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_cfg80211_inform_ibss_bss(moal_private *priv, + struct ieee80211_channel *chan, + t_u16 beacon_interval) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_bss_info bss_info; + mlan_ds_get_signal signal; + t_u8 ie_buf[MLAN_MAX_SSID_LENGTH + sizeof(IEEEtypes_Header_t)]; + int ie_len = 0; + struct cfg80211_bss *bss = NULL; + + ENTER(); + + ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (ret) + goto done; + + memset(ie_buf, 0, sizeof(ie_buf)); + ie_buf[0] = WLAN_EID_SSID; + ie_buf[1] = bss_info.ssid.ssid_len; + + moal_memcpy_ext(priv->phandle, &ie_buf[sizeof(IEEEtypes_Header_t)], + &bss_info.ssid.ssid, bss_info.ssid.ssid_len, + sizeof(ie_buf) - sizeof(IEEEtypes_Header_t)); + ie_len = ie_buf[1] + sizeof(IEEEtypes_Header_t); + + /* Get signal information from the firmware */ + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + PRINTM(MERROR, "Error getting signal information\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = cfg80211_inform_bss(priv->wdev->wiphy, chan, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + CFG80211_BSS_FTYPE_UNKNOWN, +#endif + bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, + beacon_interval, ie_buf, ie_len, + signal.bcn_rssi_avg, GFP_KERNEL); + if (bss) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + cfg80211_put_bss(priv->wdev->wiphy, bss); +#else + cfg80211_put_bss(bss); +#endif +done: + LEAVE(); + return ret; +} + +/** + * @brief Process country IE before assoicate + * + * @param priv A pointer to moal_private structure + * @param bss A pointer to cfg80211_bss structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_process_country_ie(moal_private *priv, struct cfg80211_bss *bss) +{ + u8 *country_ie, country_ie_len; + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_11d_cfg *cfg_11d = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + country_ie = (u8 *)ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + if (!country_ie) { + PRINTM(MIOCTL, "No country IE found!\n"); + woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT); + LEAVE(); + return 0; + } + + country_ie_len = country_ie[1]; + if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { + PRINTM(MIOCTL, "Wrong Country IE length!\n"); + woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT); + LEAVE(); + return 0; + } + priv->phandle->country_code[0] = country_ie[2]; + priv->phandle->country_code[1] = country_ie[3]; + priv->phandle->country_code[2] = ' '; + if (is_cfg80211_special_region_code(priv->phandle->country_code)) { + PRINTM(MIOCTL, "Skip special region code in CountryIE"); + LEAVE(); + return 0; + } + if (MLAN_STATUS_SUCCESS != + woal_set_region_code(priv, priv->phandle->country_code)) + PRINTM(MERROR, "Set country code failed!\n"); + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (req == NULL) { + PRINTM(MERROR, "Fail to allocate mlan_ds_11d_cfg buffer\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + cfg_11d = (mlan_ds_11d_cfg *)req->pbuf; + cfg_11d->sub_command = MLAN_OID_11D_DOMAIN_INFO_EXT; + req->req_id = MLAN_IOCTL_11D_CFG; + req->action = MLAN_ACT_SET; + + /* Set country code */ + cfg_11d->param.domain_info.country_code[0] = + priv->phandle->country_code[0]; + cfg_11d->param.domain_info.country_code[1] = + priv->phandle->country_code[1]; + cfg_11d->param.domain_info.country_code[2] = ' '; + + /** IEEE80211_BAND_2GHZ or IEEE80211_BAND_5GHZ */ + cfg_11d->param.domain_info.band = priv->phandle->band; + + country_ie_len -= COUNTRY_CODE_LEN; + cfg_11d->param.domain_info.no_of_sub_band = MIN( + MRVDRV_MAX_SUBBAND_802_11D, + (country_ie_len / sizeof(struct ieee80211_country_ie_triplet))); + moal_memcpy_ext(priv->phandle, + (u8 *)cfg_11d->param.domain_info.sub_band, + &country_ie[2] + COUNTRY_CODE_LEN, + cfg_11d->param.domain_info.no_of_sub_band * + sizeof(mlan_ds_subband_set_t), + sizeof(cfg_11d->param.domain_info.sub_band)); + + PRINTM(MCMND, "11D: Country IE: %c%c band=%d no_of_sub_band=%d\n", + country_ie[2], country_ie[3], priv->phandle->band, + cfg_11d->param.domain_info.no_of_sub_band); + /* Send domain info command to FW */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "11D: Error setting domain info in FW\n"); + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request scan based on connect parameter + * + * @param priv A pointer to moal_private structure + * @param conn_param A pointer to connect parameters + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_connect_scan(moal_private *priv, + struct cfg80211_connect_params *conn_param, + t_u8 wait_option) +{ + moal_handle *handle = priv->phandle; + int ret = 0; + wlan_user_scan_cfg scan_req; + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + int chan_idx = 0, i; + + ENTER(); + if (handle->scan_pending_on_block == MTRUE) { + PRINTM(MINFO, "scan already in processing...\n"); + LEAVE(); + return ret; + } +#ifdef REASSOCIATION + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + priv->report_scan_result = MTRUE; + memset(&scan_req, 0x00, sizeof(scan_req)); + moal_memcpy_ext(priv->phandle, scan_req.ssid_list[0].ssid, + conn_param->ssid, conn_param->ssid_len, + sizeof(scan_req.ssid_list[0].ssid)); + scan_req.ssid_list[0].max_len = 0; + if (conn_param->channel) { + scan_req.chan_list[0].chan_number = + conn_param->channel->hw_value; + scan_req.chan_list[0].radio_type = conn_param->channel->band; + if (conn_param->channel->flags & IEEE80211_CHAN_PASSIVE_SCAN) + scan_req.chan_list[0].scan_type = + MLAN_SCAN_TYPE_PASSIVE; + else if (conn_param->channel->flags & IEEE80211_CHAN_RADAR) + scan_req.chan_list[0].scan_type = + MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE; + else + scan_req.chan_list[0].scan_type = MLAN_SCAN_TYPE_ACTIVE; + scan_req.chan_list[0].scan_time = 0; + } else { + for (band = 0; (band < IEEE80211_NUM_BANDS); band++) { + if (!priv->wdev->wiphy->bands[band]) + continue; + sband = priv->wdev->wiphy->bands[band]; + for (i = 0; (i < sband->n_channels); i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + scan_req.chan_list[chan_idx].radio_type = band; + if (ch->flags & IEEE80211_CHAN_PASSIVE_SCAN) + scan_req.chan_list[chan_idx].scan_type = + MLAN_SCAN_TYPE_PASSIVE; + else if (ch->flags & IEEE80211_CHAN_RADAR) + scan_req.chan_list[chan_idx].scan_type = + MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE; + else + scan_req.chan_list[chan_idx].scan_type = + MLAN_SCAN_TYPE_ACTIVE; + scan_req.chan_list[chan_idx].chan_number = + (u32)ch->hw_value; + chan_idx++; + } + } + } + moal_memcpy_ext(priv->phandle, scan_req.random_mac, priv->random_mac, + ETH_ALEN, sizeof(scan_req.random_mac)); + ret = woal_request_userscan(priv, wait_option, &scan_req); +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief Save assoc parameters for roaming + * + * @param priv A pointer to moal_private + * @param req A pointer to cfg80211_assoc_request structure + */ +void woal_save_assoc_params(moal_private *priv, + struct cfg80211_assoc_request *req, + mlan_ssid_bssid *ssid_bssid) +{ + ENTER(); + + if (req->bss->channel) { + priv->sme_current.channel = &priv->conn_chan; + moal_memcpy_ext(priv->phandle, priv->sme_current.channel, + req->bss->channel, + sizeof(struct ieee80211_channel), + sizeof(struct ieee80211_channel)); + } + priv->sme_current.bssid = priv->conn_bssid; + moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid, + req->bss->bssid, MLAN_MAC_ADDR_LENGTH, + MLAN_MAC_ADDR_LENGTH); + if (req->ie && req->ie_len) { + priv->sme_current.ie = kzalloc(req->ie_len, GFP_KERNEL); + priv->sme_current.ie_len = req->ie_len; + moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ie, + req->ie, req->ie_len, priv->sme_current.ie_len); + } + moal_memcpy_ext(priv->phandle, &priv->sme_current.crypto, &req->crypto, + sizeof(struct cfg80211_crypto_settings), + sizeof(struct cfg80211_crypto_settings)); + priv->sme_current.flags = req->flags; + moal_memcpy_ext(priv->phandle, &priv->sme_current.ht_capa, + &req->ht_capa, sizeof(struct ieee80211_ht_cap), + sizeof(struct ieee80211_ht_cap)); + moal_memcpy_ext(priv->phandle, &priv->sme_current.ht_capa_mask, + &req->ht_capa_mask, sizeof(struct ieee80211_ht_cap), + sizeof(struct ieee80211_ht_cap)); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) + moal_memcpy_ext(priv->phandle, &priv->sme_current.vht_capa, + &req->vht_capa, sizeof(struct ieee80211_vht_cap), + sizeof(struct ieee80211_vht_cap)); + moal_memcpy_ext(priv->phandle, &priv->sme_current.vht_capa_mask, + &req->vht_capa_mask, sizeof(struct ieee80211_vht_cap), + sizeof(struct ieee80211_vht_cap)); +#endif + if (ssid_bssid && ssid_bssid->ssid.ssid_len) { + priv->sme_current.ssid = priv->conn_ssid; + memset(priv->conn_ssid, 0, MLAN_MAX_SSID_LENGTH); + moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ssid, + ssid_bssid->ssid.ssid, + ssid_bssid->ssid.ssid_len, + sizeof(priv->conn_ssid)); + priv->conn_ssid_len = ssid_bssid->ssid.ssid_len; + } + LEAVE(); +} + +/** + * @brief Save auth parameters for roaming + * + * @param priv A pointer to moal_private + * @param req A pointer to struct cfg80211_auth_request + */ +void woal_save_auth_params(moal_private *priv, + struct cfg80211_auth_request *req) +{ + ENTER(); + woal_clear_conn_params(priv); + priv->sme_current.auth_type = req->auth_type; + priv->sme_current.key_idx = req->key_idx; + priv->sme_current.key_len = req->key_len; + if (req->key && req->key_len && (req->key_len <= MAX_WEP_KEY_SIZE)) { + priv->sme_current.key = priv->conn_wep_key; + moal_memcpy_ext(priv->phandle, (t_u8 *)priv->sme_current.key, + req->key, req->key_len, + sizeof(priv->conn_wep_key)); + } + LEAVE(); +} + +/** + * @brief This function is authentication handler when host MLME + * enable. + * In this case driver will prepare and send Auth Req. + * + * @param wiphy A pointer to wiphy. + * + * @param dev A pointer to net_device + * + * @param req A pointer to cfg80211_auth_request + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_authenticate(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_auth_request *req) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + IEEE80211_MGMT *mgmt = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + pmlan_buffer pmbuf = NULL; + t_u32 pkt_type, tx_control; + t_u16 packet_len = 0, auth_alg; + t_u8 addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + int ret = 0; + + t_u8 trans = 1, status_code = 0; + t_u8 *varptr = NULL; + mlan_ssid_bssid *ssid_bssid; + moal_handle *handle = priv->phandle; + int i; + + ENTER(); + + priv->cfg_disconnect = MFALSE; +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return -EFAULT; + } +#endif + if (priv->wdev->iftype != NL80211_IFTYPE_STATION +#ifdef WIFI_DIRECT_SUPPORT + && priv->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT + +#endif /* WIFI_DIRECT_SUPPORT */ + ) { + PRINTM(MERROR, + "Received infra auth request when interface not in infra mode\n"); + LEAVE(); + return -EINVAL; + } + + ssid_bssid = kzalloc(sizeof(mlan_ssid_bssid), GFP_ATOMIC); + if (!ssid_bssid) { + PRINTM(MERROR, "Fail to allocate ssid_bssid buffer\n"); + LEAVE(); + return -ENOMEM; + } + moal_memcpy_ext(priv->phandle, ssid_bssid->bssid, req->bss->bssid, + ETH_ALEN, sizeof(ssid_bssid->bssid)); + /* Not allowed to connect to the same AP which is already connected + with other interface */ + for (i = 0; i < handle->priv_num; i++) { + if (handle->priv[i] != priv && + MTRUE == woal_is_connected(handle->priv[i], ssid_bssid)) { + PRINTM(MMSG, + "wlan: already connected with other interface, bssid " MACSTR + "\n", + MAC2STR(handle->priv[i]->cfg_bssid)); + kfree(ssid_bssid); + LEAVE(); + return -EINVAL; + } + } + if (MLAN_STATUS_SUCCESS != woal_find_bssid(priv, req->bss->bssid)) { + PRINTM(MMSG, "bssid not find in scan list\n"); + kfree(ssid_bssid); + LEAVE(); + return -EFAULT; + } + kfree(ssid_bssid); + + if ((priv->auth_alg != WLAN_AUTH_SAE) && + (priv->auth_flag & HOST_MLME_AUTH_PENDING)) { + PRINTM(MERROR, "pending auth on going\n"); + LEAVE(); + return -EBUSY; + } + + /** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + (priv->wdev->iftype == NL80211_IFTYPE_STATION || + priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + /* if bsstype == wifi direct, and iftype == station or p2p + * client, that means wpa_supplicant wants to enable wifi direct + * functionality, so we should init p2p client. + * + * Note that due to kernel iftype check, ICS wpa_supplicant + * could not updaet iftype to init p2p client, so we have to + * done it here. + * */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_init_p2p_client(priv)) { + PRINTM(MERROR, + "Init p2p client for wpa_supplicant failed.\n"); + ret = -EFAULT; + LEAVE(); + return ret; + } + } + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + /* WAR for P2P connection with vendor TV */ + woal_sched_timeout(200); + } +#endif + + /*enable auth register frame*/ + if (priv->auth_flag == 0) { + woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MTRUE); + woal_mgmt_frame_register(priv, IEEE80211_STYPE_DEAUTH, MTRUE); + woal_mgmt_frame_register(priv, IEEE80211_STYPE_DISASSOC, MTRUE); + } + +#define HEADER_SIZE 8 + // frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4 +#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 + 6) + // 6 = auth_alg + auth_transaction +auth_status +#define AUTH_BODY_LEN 6 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + packet_len = (t_u16)req->ie_len + req->auth_data_len + MGMT_HEADER_LEN + + AUTH_BODY_LEN; +#else + packet_len = (t_u16)req->ie_len + req->sae_data_len + MGMT_HEADER_LEN + + AUTH_BODY_LEN; +#endif + pmbuf = woal_alloc_mlan_buffer(priv->phandle, + MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + + packet_len + sizeof(packet_len)); + + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, + KEY_INDEX_CLEAR_ALL, NULL, 1, + MOAL_IOCTL_WAIT)) { + /* Disable keys and clear all previous security settings */ + ret = -EFAULT; + goto done; + } + + switch (req->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + auth_alg = WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + auth_alg = WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_FT: + auth_alg = WLAN_AUTH_FT; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + auth_alg = WLAN_AUTH_LEAP; + break; + case NL80211_AUTHTYPE_SAE: + auth_alg = WLAN_AUTH_SAE; + break; + default: + PRINTM(MERROR, "Unsupported auth type=%d\n", req->auth_type); + ret = -EOPNOTSUPP; + break; + } + if (ret) + goto done; + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_alg)) { + ret = -EFAULT; + goto done; + } + + if (req->key && ((auth_alg == WLAN_AUTH_OPEN) || + (auth_alg == WLAN_AUTH_SHARED_KEY))) { + PRINTM(MMSG, "Setting wep encryption with key len %d\n", + req->key_len); + /* Set the WEP key */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_wep_keys(priv, req->key, req->key_len, + req->key_idx, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + /* Enable the WEP key by key index */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_wep_keys(priv, NULL, 0, req->key_idx, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + } + +#define AUTH_TX_DEFAULT_WAIT_TIME 1200 + if (priv->auth_flag == 0) { + if (woal_cfg80211_remain_on_channel_cfg( + priv, MOAL_IOCTL_WAIT, MFALSE, (t_u8 *)&status, + req->bss->channel, 0, AUTH_TX_DEFAULT_WAIT_TIME)) { + PRINTM(MERROR, "Fail to configure remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (status == MLAN_STATUS_SUCCESS) { + priv->phandle->remain_on_channel = MTRUE; + moal_memcpy_ext(priv->phandle, &(priv->phandle->chan), + req->bss->channel, + sizeof(struct ieee80211_channel), + sizeof(priv->phandle->chan)); + } else { + PRINTM(MERROR, + "HostMlme %s: Set remain on Channel: with status=%d\n", + dev->name, status); + } + } + + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + pkt_type = MRVL_PKT_TYPE_MGMT_FRAME; + tx_control = 0; + /* Add pkt_type and tx_control */ + moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset, + &pkt_type, sizeof(pkt_type), sizeof(pkt_type)); + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), + &tx_control, sizeof(tx_control), sizeof(tx_control)); + + mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset + + HEADER_SIZE + sizeof(packet_len)); + memset(mgmt, 0, MGMT_HEADER_LEN); + /**Authentication Frame: Frame Control*/ + mgmt->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); + /**Authentication Frame: Destination Address*/ + moal_memcpy_ext(priv->phandle, mgmt->da, req->bss->bssid, ETH_ALEN, + sizeof(mgmt->da)); + /**Authentication Frame: Source Address*/ + moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN, + sizeof(mgmt->sa)); + /**Authentication Frame: BSSID*/ + moal_memcpy_ext(priv->phandle, mgmt->bssid, req->bss->bssid, ETH_ALEN, + sizeof(mgmt->bssid)); + moal_memcpy_ext(priv->phandle, mgmt->addr4, addr, ETH_ALEN, + sizeof(mgmt->addr4)); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) + if (req->auth_data_len >= 4) { + if (req->auth_type == NL80211_AUTHTYPE_SAE) { + __le16 *pos = (__le16 *)req->auth_data; + + trans = le16_to_cpu(pos[0]); + status_code = le16_to_cpu(pos[1]); + } + moal_memcpy_ext(priv->phandle, (t_u8 *)(&mgmt->u.auth.variable), + req->auth_data + 4, req->auth_data_len - 4, + req->auth_data_len - 4); + varptr = (t_u8 *)&mgmt->u.auth.variable + + (req->auth_data_len - 4); + packet_len -= 4; + } +#else + if (req->sae_data_len >= 4) { + if (req->auth_type == NL80211_AUTHTYPE_SAE) { + __le16 *pos = (__le16 *)req->sae_data; + + trans = le16_to_cpu(pos[0]); + status_code = le16_to_cpu(pos[1]); + } + moal_memcpy_ext(priv->phandle, (t_u8 *)(&mgmt->u.auth.variable), + req->sae_data + 4, req->sae_data_len - 4, + req->sae_data_len - 4); + varptr = (t_u8 *)&mgmt->u.auth.variable + + (req->sae_data_len - 4); + packet_len -= 4; + } +#endif + /*Add packet len*/ + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE, + &packet_len, sizeof(packet_len), sizeof(packet_len)); + + /**Authentication Frame: Authentication Alg*/ + mgmt->u.auth.auth_alg = cpu_to_le16(auth_alg); + mgmt->u.auth.auth_transaction = trans; + /**Authentication Frame: Status code*/ + mgmt->u.auth.status_code = status_code; + + if (req->ie && req->ie_len) { + if (!varptr) { + varptr = (t_u8 *)&mgmt->u.auth.variable; + } + moal_memcpy_ext(priv->phandle, (t_u8 *)varptr, req->ie, + req->ie_len, req->ie_len); + } + + pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len); + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + pmbuf->priority = 7; + + priv->host_mlme = MTRUE; + priv->auth_flag = HOST_MLME_AUTH_PENDING; + priv->auth_alg = cpu_to_le16(auth_alg); + + PRINTM(MCMND, "wlan: HostMlme %s send auth to bssid " MACSTR "\n", + dev->name, MAC2STR(req->bss->bssid)); + DBG_HEXDUMP(MDAT_D, "Auth:", pmbuf->pbuf + pmbuf->data_offset, + pmbuf->data_len); + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_save_auth_params(priv, req); + 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); + priv->host_mlme = MFALSE; + priv->auth_flag = 0; + priv->auth_alg = 0xFFFF; + ret = -EFAULT; + break; + } +done: + if (ret) { + woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE); + if (priv->phandle->remain_on_channel) { + woal_cfg80211_remain_on_channel_cfg( + priv, MOAL_IOCTL_WAIT, MTRUE, (t_u8 *)&status, + NULL, 0, 0); + priv->phandle->remain_on_channel = MFALSE; + } + } + LEAVE(); + return ret; +} + +/** + * @brief This workqueue function handles association response in host mlme + * case + * + * @param work A pointer to work_struct + * + * @return N/A + */ +void woal_host_mlme_work_queue(struct work_struct *work) +{ + moal_handle *handle = container_of(work, moal_handle, host_mlme_work); + moal_private *priv = (moal_private *)handle->host_mlme_priv; + mlan_status status = MLAN_STATUS_SUCCESS; + + if (priv) { + if (priv->auth_flag & HOST_MLME_AUTH_DONE) { + priv->auth_flag = 0; + woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, + MFALSE); + + if (priv->phandle->remain_on_channel) { + woal_cfg80211_remain_on_channel_cfg( + priv, MOAL_IOCTL_WAIT, MTRUE, + (t_u8 *)&status, NULL, 0, 0); + priv->phandle->remain_on_channel = MFALSE; + } + PRINTM(MCMND, "wlan: HostMlme %s auth success\n", + priv->netdev->name); + } + } +} + +/** + * @brief This workqueue function handles association response in event queue + * case + * + * @param priv pointer to moal_private + * @param assoc_rsp pointer to mlan_ds_misc_assoc_rsp + * + * @return N/A + */ +void woal_host_mlme_process_assoc_resp(moal_private *priv, + mlan_ds_misc_assoc_rsp *assoc_rsp) +{ + struct cfg80211_bss *bss = NULL; + unsigned long flags; + + if (priv) { + if (priv->auth_flag & HOST_MLME_ASSOC_DONE) { + priv->auth_flag = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + bss = cfg80211_get_bss( + priv->wdev->wiphy, NULL, priv->conn_bssid, + priv->conn_ssid, priv->conn_ssid_len, + IEEE80211_BSS_TYPE_ESS, IEEE80211_PRIVACY_ANY); +#else + bss = cfg80211_get_bss( + priv->wdev->wiphy, NULL, priv->conn_bssid, + priv->conn_ssid, priv->conn_ssid_len, + WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); +#endif + if (!bss) { + PRINTM(MERROR, "HostMlme %s:Fail to get bss\n", + priv->netdev->name); + return; + } + + if (assoc_rsp->assoc_resp_len) { + PRINTM(MCMND, + "HostMlme: %s assoc_resp_len=%d, frame_control=0x%x\n", + priv->netdev->name, + assoc_rsp->assoc_resp_len, + ((struct ieee80211_mgmt *) + assoc_rsp->assoc_resp_buf) + ->frame_control); + if (ieee80211_is_assoc_resp( + ((struct ieee80211_mgmt *) + assoc_rsp->assoc_resp_buf) + ->frame_control) || + ieee80211_is_reassoc_resp( + ((struct ieee80211_mgmt *) + assoc_rsp->assoc_resp_buf) + ->frame_control)) { + spin_lock_irqsave(&priv->connect_lock, + flags); + if (le16_to_cpu( + ((struct ieee80211_mgmt + *)assoc_rsp + ->assoc_resp_buf) + ->u.assoc_resp + .status_code) != + WLAN_STATUS_SUCCESS) { + memset(priv->cfg_bssid, 0, + ETH_ALEN); + if (priv->bss_type == + MLAN_BSS_TYPE_STA) + woal_clear_conn_params( + priv); + } + spin_unlock_irqrestore( + &priv->connect_lock, flags); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) + cfg80211_rx_assoc_resp( + priv->netdev, bss, + assoc_rsp->assoc_resp_buf, + assoc_rsp->assoc_resp_len, -1, + NULL, 0); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) + cfg80211_rx_assoc_resp( + priv->netdev, bss, + assoc_rsp->assoc_resp_buf, + assoc_rsp->assoc_resp_len, -1); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + cfg80211_rx_assoc_resp( + priv->netdev, bss, + assoc_rsp->assoc_resp_buf, + assoc_rsp->assoc_resp_len); +#else + cfg80211_send_rx_assoc( + priv->netdev, bss, + assoc_rsp->assoc_resp_buf, + assoc_rsp->assoc_resp_len); +#endif +#endif +#endif + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + cfg80211_put_bss(priv->wdev->wiphy, bss); +#else + cfg80211_put_bss(bss); +#endif + } + } +} + +/** + * @brief Handle assoc response event + * + * @param priv A pointer moal_private structure + * @param pchan_info A pointer to mlan_ds_misc_assoc_rsp structure + * + * @return N/A + */ + +void woal_assoc_resp_event(moal_private *priv, + mlan_ds_misc_assoc_rsp *passoc_rsp) +{ + struct woal_event *evt; + unsigned long flags; + moal_handle *handle = priv->phandle; + + evt = kzalloc(sizeof(struct woal_event), GFP_ATOMIC); + if (evt) { + evt->priv = priv; + evt->type = WOAL_EVENT_ASSOC_RESP; + moal_memcpy_ext(priv->phandle, &evt->assoc_resp, passoc_rsp, + sizeof(mlan_ds_misc_assoc_rsp), + sizeof(mlan_ds_misc_assoc_rsp)); + INIT_LIST_HEAD(&evt->link); + spin_lock_irqsave(&handle->evt_lock, flags); + list_add_tail(&evt->link, &handle->evt_queue); + spin_unlock_irqrestore(&handle->evt_lock, flags); + queue_work(handle->evt_workqueue, &handle->evt_work); + } +} + +/** + * @brief This function is association handler when host MLME + * enable. + * In this case driver will prepare and send Assoc Req. + * + * @param wiphy A pointer to wiphy. + * + * @param dev A pointer to net_device + * + * @param req A pointer to cfg80211_assoc_request + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_associate(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_assoc_request *req) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + mlan_ssid_bssid ssid_bssid; + unsigned long flags; + const u8 *ssid_ie; + int wpa_enabled = 0, group_enc_mode = 0, pairwise_enc_mode = 0; + mlan_bss_info bss_info; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (priv->auth_alg == WLAN_AUTH_SAE) { + priv->auth_flag = HOST_MLME_AUTH_DONE; + + woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE); + PRINTM(MINFO, "wlan: HostMlme %s auth exchange successful\n", + priv->netdev->name); + + if (priv->phandle->remain_on_channel) { + if (woal_cfg80211_remain_on_channel_cfg( + priv, MOAL_IOCTL_WAIT, MTRUE, + (t_u8 *)&status, NULL, 0, 0)) { + PRINTM(MERROR, + "Failed to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + priv->phandle->remain_on_channel = MFALSE; + } + } + + if (priv->auth_flag && !(priv->auth_flag & HOST_MLME_AUTH_DONE)) { + LEAVE(); + return -EBUSY; + } + + priv->cfg_connect = MTRUE; + priv->assoc_status = 0; + priv->auth_alg = 0xFFFF; + + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + ssid_ie = ieee80211_bss_get_ie(req->bss, WLAN_EID_SSID); + moal_memcpy_ext(priv->phandle, ssid_bssid.bssid, req->bss->bssid, + ETH_ALEN, sizeof(ssid_bssid.bssid)); + + if (!ssid_ie) { + ret = -EINVAL; + goto done; + } + + moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid, ssid_ie + 2, + ssid_ie[1], sizeof(ssid_bssid.ssid.ssid)); + ssid_bssid.ssid.ssid_len = ssid_ie[1]; + + if (ssid_bssid.ssid.ssid_len > MW_ESSID_MAX_SIZE) { + PRINTM(MERROR, "Invalid SSID - aborting\n"); + ret = -EINVAL; + goto done; + } + + if (!ssid_bssid.ssid.ssid_len || ssid_bssid.ssid.ssid[0] < 0x20) { + PRINTM(MERROR, "Invalid SSID - aborting\n"); + ret = -EINVAL; + goto done; + } + +#ifdef STA_WEXT + if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) { + switch (req->crypto.wpa_versions) { + case NL80211_WPA_VERSION_2: + priv->wpa_version = IW_AUTH_WPA_VERSION_WPA2; + break; + case NL80211_WPA_VERSION_1: + priv->wpa_version = IW_AUTH_WPA_VERSION_WPA; + break; + default: + priv->wpa_version = 0; + break; + } + if (req->crypto.n_akm_suites) { + switch (req->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_PSK: + priv->key_mgmt = IW_AUTH_KEY_MGMT_PSK; + break; + case WLAN_AKM_SUITE_8021X: + priv->key_mgmt = IW_AUTH_KEY_MGMT_802_1X; + break; + default: + priv->key_mgmt = 0; + break; + } + } + } +#endif + + if (req->ie && req->ie_len) { /* Set the IE */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_assoc_ies_cfg(priv, (t_u8 *)req->ie, + req->ie_len, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + } + + if (req->crypto.n_ciphers_pairwise) { + pairwise_enc_mode = woal_cfg80211_get_encryption_mode( + req->crypto.ciphers_pairwise[0], &wpa_enabled); + ret = woal_cfg80211_set_auth(priv, pairwise_enc_mode, + wpa_enabled, MOAL_IOCTL_WAIT); + if (ret) + goto done; + } + + if (req->crypto.cipher_group) { + group_enc_mode = woal_cfg80211_get_encryption_mode( + req->crypto.cipher_group, &wpa_enabled); + ret = woal_cfg80211_set_auth(priv, group_enc_mode, wpa_enabled, + MOAL_IOCTL_WAIT); + if (ret) + goto done; + } + ssid_bssid.host_mlme = priv->host_mlme; + + if (req->bss->channel) { + ssid_bssid.channel_flags = req->bss->channel->flags; + ssid_bssid.channel_flags |= CHAN_FLAGS_MAX; + PRINTM(MCMND, "channel flags=0x%x\n", req->bss->channel->flags); + } + + PRINTM(MCMND, "wlan: HostMlme %s send assoicate to bssid " MACSTR "\n", + priv->netdev->name, MAC2STR(req->bss->bssid)); + if (MLAN_STATUS_SUCCESS != + woal_bss_start(priv, MOAL_IOCTL_WAIT_TIMEOUT, &ssid_bssid)) { + PRINTM(MERROR, "HostMlme %s: bss_start Fails\n", + priv->netdev->name); + priv->host_mlme = MFALSE; + priv->auth_flag = 0; + ret = -EFAULT; + } + +done: + + if (!ret) { + priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD; + if (priv->bss_type == MLAN_BSS_TYPE_STA +#ifdef WIFI_DIRECT_SUPPORT + || priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT +#endif + ) + woal_save_assoc_params(priv, req, &ssid_bssid); + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + priv->channel = bss_info.bss_chan; + } + + spin_lock_irqsave(&priv->connect_lock, flags); + priv->cfg_connect = MFALSE; + if (!ret && priv->media_connected) { + PRINTM(MMSG, + "wlan: HostMlme %s Connected to bssid " MACSTR + " successfully\n", + priv->netdev->name, MAC2STR(priv->cfg_bssid)); + spin_unlock_irqrestore(&priv->connect_lock, flags); + } else { + PRINTM(MERROR, + "wlan: HostMlme %s Failed to connect to bssid " MACSTR + "\n", + priv->netdev->name, MAC2STR(req->bss->bssid)); + if (ssid_bssid.assoc_rsp.assoc_resp_len && + ssid_bssid.assoc_rsp.assoc_resp_len > + sizeof(IEEEtypes_MgmtHdr_t)) { + // save the connection param when send assoc_resp to + // kernel + woal_save_assoc_params(priv, req, &ssid_bssid); + ret = 0; + } else { + ssid_bssid.assoc_rsp.assoc_resp_len = 0; + ret = -EFAULT; + memset(priv->cfg_bssid, 0, ETH_ALEN); + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_clear_conn_params(priv); + } + priv->host_mlme = MFALSE; + priv->auth_flag = 0; + spin_unlock_irqrestore(&priv->connect_lock, flags); + } + /*Association Response should also be send when ret is non-zero. + We also need to return success when we have association response + available*/ + if (ssid_bssid.assoc_rsp.assoc_resp_len) { + priv->auth_flag |= HOST_MLME_ASSOC_DONE; + woal_assoc_resp_event(priv, &ssid_bssid.assoc_rsp); + } + + LEAVE(); + return ret; +} +#endif + +/** + * @brief Request the driver for (re)association + * + * @param priv A pointer to moal_private structure + * @param sme A pointer to connect parameters + * @param wait_option wait option + * @param assoc_resp A pointer to assoc_rsp structure; + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_assoc(moal_private *priv, void *sme, t_u8 wait_option, + mlan_ds_misc_assoc_rsp *assoc_rsp) +{ + struct cfg80211_ibss_params *ibss_param = NULL; + struct cfg80211_connect_params *conn_param = NULL; + mlan_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; + mlan_ioctl_req *req = NULL; + int ret = 0; + t_u32 auth_type = 0, mode; + int wpa_enabled = 0; + int group_enc_mode = 0, pairwise_enc_mode = 0; + int alg_is_wep = 0; + + t_u8 *ssid, ssid_len = 0, *bssid; + t_u8 *ie = NULL; + int ie_len = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct cfg80211_chan_def *chan_def = NULL; +#endif + struct ieee80211_channel *channel = NULL; + t_u16 beacon_interval = 0; + bool privacy; + struct cfg80211_bss *bss = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!sme) { + LEAVE(); + return -EFAULT; + } + + mode = woal_nl80211_iftype_to_mode(priv->wdev->iftype); + + if (mode == MLAN_BSS_MODE_IBSS) { + ibss_param = (struct cfg80211_ibss_params *)sme; + ssid = (t_u8 *)ibss_param->ssid; + ssid_len = ibss_param->ssid_len; + bssid = (t_u8 *)ibss_param->bssid; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + channel = ibss_param->channel; +#else + chan_def = &ibss_param->chandef; + channel = ibss_param->chandef.chan; +#endif + if (channel) + priv->phandle->band = channel->band; + if (ibss_param->ie_len) + ie = (t_u8 *)ibss_param->ie; + ie_len = ibss_param->ie_len; + beacon_interval = ibss_param->beacon_interval; + privacy = ibss_param->privacy; + + } else { + conn_param = (struct cfg80211_connect_params *)sme; + ssid = (t_u8 *)conn_param->ssid; + ssid_len = conn_param->ssid_len; + bssid = (t_u8 *)conn_param->bssid; + channel = conn_param->channel; + if (channel) + priv->phandle->band = channel->band; + if (conn_param->ie_len) + ie = (t_u8 *)conn_param->ie; + ie_len = conn_param->ie_len; + privacy = conn_param->privacy; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + bss = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid, + ssid_len, IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); +#else + bss = cfg80211_get_bss(priv->wdev->wiphy, channel, bssid, ssid, + ssid_len, WLAN_CAPABILITY_ESS, + WLAN_CAPABILITY_ESS); +#endif + if (bss) { + if ((!priv->phandle->params.reg_alpha2 || + strncmp(priv->phandle->params.reg_alpha2, "99", + strlen("99"))) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + && (!moal_extflg_isset(priv->phandle, + EXT_COUNTRY_IE_IGNORE)) +#endif + ) + woal_process_country_ie(priv, bss); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + cfg80211_put_bss(priv->wdev->wiphy, bss); +#else + cfg80211_put_bss(bss); +#endif + } else + woal_send_domain_info_cmd_fw(priv, wait_option); +#ifdef STA_WEXT + if (IS_STA_WEXT(priv->phandle->params.cfg80211_wext)) { + switch (conn_param->crypto.wpa_versions) { + case NL80211_WPA_VERSION_2: + priv->wpa_version = IW_AUTH_WPA_VERSION_WPA2; + break; + case NL80211_WPA_VERSION_1: + priv->wpa_version = IW_AUTH_WPA_VERSION_WPA; + break; + default: + priv->wpa_version = 0; + break; + } + if (conn_param->crypto.n_akm_suites) { + switch (conn_param->crypto.akm_suites[0]) { + case WLAN_AKM_SUITE_PSK: + priv->key_mgmt = IW_AUTH_KEY_MGMT_PSK; + break; + case WLAN_AKM_SUITE_8021X: + priv->key_mgmt = + IW_AUTH_KEY_MGMT_802_1X; + break; + default: + priv->key_mgmt = 0; + break; + } + } + } +#endif + } + + memset(&req_ssid, 0, sizeof(mlan_802_11_ssid)); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + req_ssid.ssid_len = ssid_len; + if (ssid_len > MW_ESSID_MAX_SIZE) { + PRINTM(MERROR, "Invalid SSID - aborting\n"); + ret = -EINVAL; + goto done; + } + + moal_memcpy_ext(priv->phandle, req_ssid.ssid, ssid, ssid_len, + sizeof(req_ssid.ssid)); + if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { + PRINTM(MERROR, "Invalid SSID - aborting\n"); + ret = -EINVAL; + goto done; + } + + if (priv->phandle->card_info->embedded_supp) + if (MLAN_STATUS_SUCCESS != + woal_set_ewpa_mode(priv, wait_option, &ssid_bssid)) { + ret = -EFAULT; + goto done; + } + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, + KEY_INDEX_CLEAR_ALL, NULL, 1, wait_option)) { + /* Disable keys and clear all previous security settings */ + ret = -EFAULT; + goto done; + } +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) { + /** Check if current roaming support OKC offload roaming */ + if (conn_param && conn_param->crypto.n_akm_suites && + conn_param->crypto.akm_suites[0] == WLAN_AKM_SUITE_8021X) { + if (priv->okc_roaming_ie && priv->okc_ie_len) { + ie = priv->okc_roaming_ie; + ie_len = priv->okc_ie_len; + } + } + } +#endif + + if ((priv->ft_pre_connect || + (conn_param && conn_param->auth_type == NL80211_AUTHTYPE_FT)) && + priv->ft_ie_len) { + ie = priv->ft_ie; + ie_len = priv->ft_ie_len; + priv->ft_ie_len = 0; + } + if (ie && ie_len) { /* Set the IE */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_assoc_ies_cfg(priv, ie, ie_len, + wait_option)) { + ret = -EFAULT; + goto done; + } + } + + if (conn_param && mode != MLAN_BSS_MODE_IBSS) { + /* These parameters are only for managed mode */ + if (conn_param->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) + auth_type = MLAN_AUTH_MODE_OPEN; + else if (conn_param->auth_type == NL80211_AUTHTYPE_SHARED_KEY) + auth_type = MLAN_AUTH_MODE_SHARED; + else if (conn_param->auth_type == NL80211_AUTHTYPE_NETWORK_EAP) + auth_type = MLAN_AUTH_MODE_NETWORKEAP; + else if (conn_param->auth_type == NL80211_AUTHTYPE_FT) + auth_type = MLAN_AUTH_MODE_FT; + else + auth_type = MLAN_AUTH_MODE_AUTO; + if (priv->ft_pre_connect) + auth_type = MLAN_AUTH_MODE_FT; + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, wait_option, auth_type)) { + ret = -EFAULT; + goto done; + } + + if (conn_param->crypto.n_ciphers_pairwise) { + pairwise_enc_mode = woal_cfg80211_get_encryption_mode( + conn_param->crypto.ciphers_pairwise[0], + &wpa_enabled); + ret = woal_cfg80211_set_auth(priv, pairwise_enc_mode, + wpa_enabled, wait_option); + if (ret) + goto done; + } + + if (conn_param->crypto.cipher_group) { + group_enc_mode = woal_cfg80211_get_encryption_mode( + conn_param->crypto.cipher_group, &wpa_enabled); + ret = woal_cfg80211_set_auth(priv, group_enc_mode, + wpa_enabled, wait_option); + if (ret) + goto done; + } + + if (conn_param->key) { + alg_is_wep = + woal_cfg80211_is_alg_wep(pairwise_enc_mode) | + woal_cfg80211_is_alg_wep(group_enc_mode); + if (alg_is_wep) { + PRINTM(MINFO, + "Setting wep encryption with key len %d\n", + conn_param->key_len); + /* Set the WEP key */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_wep_keys( + priv, conn_param->key, + conn_param->key_len, + conn_param->key_idx, wait_option)) { + ret = -EFAULT; + goto done; + } + /* Enable the WEP key by key index */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_wep_keys( + priv, NULL, 0, conn_param->key_idx, + wait_option)) { + ret = -EFAULT; + goto done; + } + } + } + } + + if (mode == MLAN_BSS_MODE_IBSS) { + mlan_ds_bss *bss = NULL; + /* Change beacon interval */ + if ((beacon_interval < MLAN_MIN_BEACON_INTERVAL) || + (beacon_interval > MLAN_MAX_BEACON_INTERVAL)) { + ret = -EINVAL; + goto done; + } + kfree(req); + req = NULL; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + bss->sub_command = MLAN_OID_IBSS_BCN_INTERVAL; + bss->param.bcn_interval = beacon_interval; + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* "privacy" is set only for ad-hoc mode */ + if (privacy) { + /* + * Keep MLAN_ENCRYPTION_MODE_WEP40 for now so that + * the firmware can find a matching network from the + * scan. cfg80211 does not give us the encryption + * mode at this stage so just setting it to wep here + */ + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, wait_option, + MLAN_AUTH_MODE_OPEN)) { + ret = -EFAULT; + goto done; + } + + wpa_enabled = 0; + ret = woal_cfg80211_set_auth( + priv, MLAN_ENCRYPTION_MODE_WEP104, wpa_enabled, + wait_option); + if (ret) + goto done; + } + } + moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid, &req_ssid, + sizeof(mlan_802_11_ssid), sizeof(ssid_bssid.ssid)); + if (bssid) + moal_memcpy_ext(priv->phandle, &ssid_bssid.bssid, bssid, + ETH_ALEN, sizeof(ssid_bssid.bssid)); + if (MLAN_STATUS_SUCCESS != + woal_find_essid(priv, &ssid_bssid, wait_option)) { + /* Do specific SSID scanning */ + if (mode != MLAN_BSS_MODE_IBSS) + ret = woal_cfg80211_connect_scan(priv, conn_param, + wait_option); + else + ret = woal_request_scan(priv, wait_option, &req_ssid); + if (ret) { + ret = -EFAULT; + goto done; + } + } + + /* Disconnect before try to associate */ + if (mode == MLAN_BSS_MODE_IBSS) + woal_disconnect(priv, wait_option, NULL, + DEF_DEAUTH_REASON_CODE); + + if (mode != MLAN_BSS_MODE_IBSS) { + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, wait_option, &ssid_bssid)) { + ret = -EFAULT; + goto done; + } + /* Inform the BSS information to kernel, otherwise + * kernel will give a panic after successful assoc */ + if (MLAN_STATUS_SUCCESS != + woal_inform_bss_from_scan_result(priv, &ssid_bssid, + wait_option)) { + ret = -EFAULT; + goto done; + } + } else if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, wait_option, &ssid_bssid)) + /* Adhoc start, Check the channel command */ + woal_11h_channel_check_ioctl(priv, wait_option); + + PRINTM(MINFO, "Trying to associate to %s and bssid " MACSTR "\n", + (char *)req_ssid.ssid, MAC2STR(ssid_bssid.bssid)); + + /* Zero SSID implies use BSSID to connect */ + if (bssid) + memset(&ssid_bssid.ssid, 0, sizeof(mlan_802_11_ssid)); + else /* Connect to BSS by ESSID */ + memset(&ssid_bssid.bssid, 0, MLAN_MAC_ADDR_LENGTH); + if (channel) { + ssid_bssid.channel_flags = channel->flags; + ssid_bssid.channel_flags |= CHAN_FLAGS_MAX; + PRINTM(MCMND, "channel flags=0x%x\n", channel->flags); + } + if (MLAN_STATUS_SUCCESS != + woal_bss_start(priv, MOAL_IOCTL_WAIT_TIMEOUT, &ssid_bssid)) { + ret = -EFAULT; + goto done; + } + + /* Inform the IBSS information to kernel, otherwise + * kernel will give a panic after successful assoc */ + if (mode == MLAN_BSS_MODE_IBSS) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_inform_ibss_bss(priv, channel, + beacon_interval)) { + ret = -EFAULT; + goto done; + } + } else if (assoc_rsp) { + moal_memcpy_ext(priv->phandle, assoc_rsp, &ssid_bssid.assoc_rsp, + sizeof(mlan_ds_misc_assoc_rsp), + sizeof(mlan_ds_misc_assoc_rsp)); + PRINTM(MCMND, "assoc_rsp ie len=%d\n", + assoc_rsp->assoc_resp_len); + } +done: + if (ret) { + /* clear the encryption mode */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_auth(priv, MLAN_ENCRYPTION_MODE_NONE, + MFALSE, wait_option)) { + PRINTM(MERROR, "Could not clear encryption \n"); + ret = -EFAULT; + } + /* clear IE */ + ie_len = 0; + if (MLAN_STATUS_SUCCESS != + woal_set_get_gen_ie(priv, MLAN_ACT_SET, NULL, &ie_len, + wait_option)) { + PRINTM(MERROR, "Could not clear RSN IE\n"); + ret = -EFAULT; + } + } + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to fill the tx/rx rate info + * + * @param priv A pointer to moal_private structure + * @param sinfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +void woal_cfg80211_fill_rate_info(moal_private *priv, + struct station_info *sinfo) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + t_u16 Rates[12] = {0x02, 0x04, 0x0B, 0x16, 0x0C, 0x12, + 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c}; + 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->sub_command = MLAN_OID_GET_DATA_RATE; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_GET; + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (rate->param.data_rate.tx_rate_format != MLAN_RATE_FORMAT_LG) { + if (rate->param.data_rate.tx_rate_format == + MLAN_RATE_FORMAT_HT) { + sinfo->txrate.flags = RATE_INFO_FLAGS_MCS; + if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->txrate.bw = RATE_INFO_BW_40; +#else + sinfo->txrate.flags |= + RATE_INFO_FLAGS_40_MHZ_WIDTH; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + else + sinfo->txrate.bw = RATE_INFO_BW_20; +#endif + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + else if (rate->param.data_rate.tx_rate_format == + MLAN_RATE_FORMAT_VHT) { + sinfo->txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + sinfo->txrate.nss = rate->param.data_rate.tx_nss + 1; + if (rate->param.data_rate.tx_ht_bw == MLAN_VHT_BW80) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->txrate.bw = RATE_INFO_BW_80; +#else + sinfo->txrate.flags |= + RATE_INFO_FLAGS_80_MHZ_WIDTH; +#endif + else if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->txrate.bw = RATE_INFO_BW_40; +#else + sinfo->txrate.flags |= + RATE_INFO_FLAGS_40_MHZ_WIDTH; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + else + sinfo->txrate.bw = RATE_INFO_BW_20; +#endif + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 18) + else if (rate->param.data_rate.tx_rate_format == + MLAN_RATE_FORMAT_HE) { + sinfo->txrate.flags = RATE_INFO_FLAGS_HE_MCS; + sinfo->txrate.nss = rate->param.data_rate.tx_nss + 1; + sinfo->txrate.mcs = rate->param.data_rate.tx_mcs_index; + sinfo->txrate.he_gi = rate->param.data_rate.tx_ht_gi; + if (rate->param.data_rate.tx_ht_bw == MLAN_VHT_BW80) + sinfo->txrate.bw = RATE_INFO_BW_80; + else if (rate->param.data_rate.tx_ht_bw == MLAN_HT_BW40) + sinfo->txrate.bw = RATE_INFO_BW_40; + else + sinfo->txrate.bw = RATE_INFO_BW_20; + } +#endif + if (rate->param.data_rate.tx_ht_gi == MLAN_HT_SGI) + sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + sinfo->txrate.mcs = rate->param.data_rate.tx_mcs_index; + } else { + /* Bit rate is in 500 kb/s units. Convert it to 100kb/s units */ + sinfo->txrate.legacy = + Rates[rate->param.data_rate.tx_data_rate] * 5; + } + // Fill Rx rate +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + if (rate->param.data_rate.rx_rate_format != MLAN_RATE_FORMAT_LG) { + if (rate->param.data_rate.rx_rate_format == + MLAN_RATE_FORMAT_HT) { + sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS; + if (rate->param.data_rate.rx_ht_bw == MLAN_HT_BW40) + sinfo->rxrate.bw = RATE_INFO_BW_40; + else + sinfo->rxrate.bw = RATE_INFO_BW_20; + } else if (rate->param.data_rate.rx_rate_format == + MLAN_RATE_FORMAT_VHT) { + sinfo->rxrate.flags = RATE_INFO_FLAGS_VHT_MCS; + sinfo->rxrate.nss = rate->param.data_rate.rx_nss + 1; + if (rate->param.data_rate.rx_ht_bw == MLAN_VHT_BW80) + sinfo->rxrate.bw = RATE_INFO_BW_80; + else if (rate->param.data_rate.rx_ht_bw == MLAN_HT_BW40) + sinfo->rxrate.bw = RATE_INFO_BW_40; + else + sinfo->rxrate.bw = RATE_INFO_BW_20; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 1, 18) + else if (rate->param.data_rate.rx_rate_format == + MLAN_RATE_FORMAT_HE) { + sinfo->rxrate.flags = RATE_INFO_FLAGS_HE_MCS; + sinfo->rxrate.nss = rate->param.data_rate.rx_nss + 1; + sinfo->rxrate.mcs = rate->param.data_rate.rx_mcs_index; + sinfo->rxrate.he_gi = rate->param.data_rate.rx_ht_gi; + if (rate->param.data_rate.rx_ht_bw == MLAN_VHT_BW80) + sinfo->rxrate.bw = RATE_INFO_BW_80; + else if (rate->param.data_rate.rx_ht_bw == MLAN_HT_BW40) + sinfo->rxrate.bw = RATE_INFO_BW_40; + else + sinfo->rxrate.bw = RATE_INFO_BW_20; + } +#endif + if (rate->param.data_rate.rx_ht_gi == MLAN_HT_SGI) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + sinfo->rxrate.mcs = rate->param.data_rate.rx_mcs_index; + } else { + /* Bit rate is in 500 kb/s units. Convert it to 100kb/s units */ + sinfo->rxrate.legacy = + Rates[rate->param.data_rate.rx_data_rate] * 5; + } +#endif +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return; +} +/** + * @brief Request the driver to dump the station information + * + * @param priv A pointer to moal_private structure + * @param sinfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +static mlan_status woal_cfg80211_dump_station_info(moal_private *priv, + struct station_info *sinfo) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_get_signal signal; + mlan_ds_get_stats stats; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + mlan_bss_info bss_info; + t_u8 dtim_period = 0; +#endif + + ENTER(); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->filled = MBIT(NL80211_STA_INFO_RX_BYTES) | + MBIT(NL80211_STA_INFO_TX_BYTES) | + MBIT(NL80211_STA_INFO_RX_PACKETS) | + MBIT(NL80211_STA_INFO_TX_PACKETS) | + MBIT(NL80211_STA_INFO_SIGNAL) | + MBIT(NL80211_STA_INFO_TX_BITRATE) | + MBIT(NL80211_STA_INFO_RX_BITRATE); +#else + sinfo->filled = STATION_INFO_RX_BYTES | STATION_INFO_TX_BYTES | + STATION_INFO_RX_PACKETS | STATION_INFO_TX_PACKETS | + STATION_INFO_SIGNAL | STATION_INFO_TX_BITRATE; +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->filled |= MBIT(NL80211_STA_INFO_TX_FAILED); +#else + sinfo->filled |= STATION_INFO_TX_FAILED; +#endif +#endif + + /* Get signal information from the firmware */ + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + PRINTM(MERROR, "Error getting signal information\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Get stats information from the firmware */ + memset(&stats, 0, sizeof(mlan_ds_get_stats)); + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { + PRINTM(MERROR, "Error getting stats information\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + sinfo->rx_bytes = priv->stats.rx_bytes; + sinfo->tx_bytes = priv->stats.tx_bytes; + sinfo->rx_packets = priv->stats.rx_packets; + sinfo->tx_packets = priv->stats.tx_packets; + sinfo->signal = signal.bcn_rssi_avg; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + sinfo->tx_failed = stats.failed; +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* Update BSS information */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->filled |= MBIT(NL80211_STA_INFO_BSS_PARAM); +#else + sinfo->filled |= STATION_INFO_BSS_PARAM; +#endif + sinfo->bss_param.flags = 0; + ret = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (ret) + goto done; + if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_PREAMBLE) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (bss_info.capability_info & WLAN_CAPABILITY_SHORT_SLOT_TIME) + sinfo->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.beacon_interval = bss_info.beacon_interval; + /* Get DTIM period */ + ret = woal_set_get_dtim_period(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, + &dtim_period); + if (ret) { + PRINTM(MERROR, "Get DTIM period failed\n"); + goto done; + } + sinfo->bss_param.dtim_period = dtim_period; +#endif + woal_cfg80211_fill_rate_info(priv, sinfo); +done: + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +/** + * @brief Set all radar channel's dfs_state + * + * @param wiphy A pointer to wiphy structure + * + * @return N/A + */ +void woal_update_radar_chans_dfs_state(struct wiphy *wiphy) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + int i; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + for (i = 0; i < sband->n_channels; i++) { + if (sband->channels[i].flags & IEEE80211_CHAN_RADAR) { + if (moal_extflg_isset(handle, EXT_DFS_OFFLOAD)) + sband->channels[i].dfs_state = + NL80211_DFS_AVAILABLE; + else + sband->channels[i].dfs_state = + NL80211_DFS_USABLE; + } + } + } + PRINTM(MCMND, "Set radar dfs_state: dfs_offload=%d\n", + moal_extflg_isset(handle, EXT_DFS_OFFLOAD)); +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + +/** + * @brief This function will be used by sort, to compare LHS & RHS + * + * @param lhs LHS value + * @param rhs RHS value + * @return 0 + */ +static int compare(const void *lhs, const void *rhs) +{ + const chan_freq_power_t *lhs_cfp = (const chan_freq_power_t *)(lhs); + const chan_freq_power_t *rhs_cfp = (const chan_freq_power_t *)(rhs); + + if (lhs_cfp->channel < rhs_cfp->channel) + return -1; + if (lhs_cfp->channel > rhs_cfp->channel) + return 1; + + return 0; +} +/** + * @brief This function update channel region config + * + * @param buf Buffer containing channel region config + * @param num_chan Length of buffer + * @param regd ieee80211_regdomain to be updated + * + * @return N/A + */ +static struct ieee80211_regdomain * +create_custom_regdomain(mlan_ds_custom_reg_domain *custom_reg) +{ + struct ieee80211_reg_rule *rule; + bool new_rule; + int idx, freq, prev_freq = 0; + t_u8 chan; + t_u16 num_chan = 0; + t_u32 bw, prev_bw = 0; + t_u16 chflags, prev_chflags = 0, valid_rules = 0; + struct ieee80211_regdomain *regd = NULL; + int regd_size; + const struct ieee80211_freq_range *freq_range = NULL; + + num_chan = custom_reg->num_bg_chan; + num_chan += custom_reg->num_a_chan; + + sort(&custom_reg->cfp_tbl[custom_reg->num_bg_chan], + custom_reg->num_a_chan, sizeof(chan_freq_power_t), &compare, NULL); + + regd_size = sizeof(struct ieee80211_regdomain) + + num_chan * sizeof(struct ieee80211_reg_rule); + + regd = kzalloc(regd_size, GFP_KERNEL); + if (!regd) { + return NULL; + } +#define NXP_CHANNEL_TMP_NOHT40 MBIT(15) + /* preprocess 2.4G 40MHz support */ + for (idx = 0; idx < num_chan; idx++) { + chan = custom_reg->cfp_tbl[idx].channel; + if (!chan) { + if (regd) + kfree(regd); + return NULL; + } + + if (chan > 14) + continue; + + chflags = custom_reg->cfp_tbl[idx].dynamic.flags; + + if (chflags & NXP_CHANNEL_DISABLED) + continue; + + /* duplicate a temp flag */ + if (chflags & NXP_CHANNEL_NOHT40) + chflags |= NXP_CHANNEL_TMP_NOHT40; + } + for (idx = 0; idx < num_chan; idx++) { + chan = custom_reg->cfp_tbl[idx].channel; + if (!chan) { + if (regd) + kfree(regd); + return NULL; + } + + if (chan > 14) + continue; + + chflags = custom_reg->cfp_tbl[idx].dynamic.flags; + + if (chflags & NXP_CHANNEL_DISABLED) + continue; + + /* 40 MHz band of one center channel will spread to upper and + * lower 2 channels */ + /* Mark HT40 flag for all channels of this 40 MHz band */ + if (!(chflags & NXP_CHANNEL_TMP_NOHT40)) { + if (idx >= 2) + custom_reg->cfp_tbl[idx - 2].dynamic.flags &= + ~NXP_CHANNEL_NOHT40; + if (idx >= 1) + custom_reg->cfp_tbl[idx - 1].dynamic.flags &= + ~NXP_CHANNEL_NOHT40; + if (idx < (num_chan - 1)) + custom_reg->cfp_tbl[idx + 1].dynamic.flags &= + ~NXP_CHANNEL_NOHT40; + if (idx < (num_chan - 2)) + custom_reg->cfp_tbl[idx + 2].dynamic.flags &= + ~NXP_CHANNEL_NOHT40; + } else { + custom_reg->cfp_tbl[idx].dynamic.flags &= + ~NXP_CHANNEL_TMP_NOHT40; + } + } + for (idx = 0; idx < num_chan; idx++) { + enum ieee80211_band band; + + chan = custom_reg->cfp_tbl[idx].channel; + if (!chan) { + if (regd) + kfree(regd); + return NULL; + } + chflags = custom_reg->cfp_tbl[idx].dynamic.flags; + band = (chan <= 14) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + freq = ieee80211_channel_to_frequency(chan, band); + PRINTM(MINFO, "chan=%d freq=%d chan_flag=0x%x\n", chan, freq, + chflags); + new_rule = false; + + if (chflags & NXP_CHANNEL_DISABLED) + continue; + + if (band == IEEE80211_BAND_5GHZ) { + if (!(chflags & NXP_CHANNEL_NOHT80)) + bw = MHZ_TO_KHZ(80); + else if (!(chflags & NXP_CHANNEL_NOHT40)) + bw = MHZ_TO_KHZ(40); + else + bw = MHZ_TO_KHZ(20); + } else { + if (!(chflags & NXP_CHANNEL_NOHT40)) + bw = MHZ_TO_KHZ(40); + else + bw = MHZ_TO_KHZ(20); + } + + if (idx == 0 || prev_chflags != chflags || prev_bw != bw || + freq - prev_freq > 20) { + valid_rules++; + new_rule = true; + } + + rule = ®d->reg_rules[valid_rules - 1]; + + rule->freq_range.end_freq_khz = MHZ_TO_KHZ(freq + 10); + + prev_chflags = chflags; + prev_freq = freq; + prev_bw = bw; + + if (!new_rule) + continue; + + rule->freq_range.start_freq_khz = MHZ_TO_KHZ(freq - 10); + rule->power_rule.max_eirp = DBM_TO_MBM(19); + rule->flags = 0; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (chflags & NXP_CHANNEL_PASSIVE) + rule->flags |= NL80211_RRF_NO_IR; +#endif + if (chflags & NXP_CHANNEL_DFS) + rule->flags |= NL80211_RRF_DFS; + if (chflags & NXP_CHANNEL_NO_OFDM) + rule->flags |= NL80211_RRF_NO_OFDM; + rule->freq_range.max_bandwidth_khz = bw; + } + + regd->n_reg_rules = valid_rules; + + if (custom_reg->region.country_code[0] == 'W' && + custom_reg->region.country_code[1] == 'W') { + regd->alpha2[0] = '0'; + regd->alpha2[1] = '0'; + } else { + /* set alpha2 from FW. */ + regd->alpha2[0] = custom_reg->region.country_code[0]; + regd->alpha2[1] = custom_reg->region.country_code[1]; + } + PRINTM(MCMND, "create_custom_regdomain: %c%c rules=%d\n", + regd->alpha2[0], regd->alpha2[1], valid_rules); + for (idx = 0; idx < regd->n_reg_rules; idx++) { + rule = ®d->reg_rules[idx]; + freq_range = &rule->freq_range; + PRINTM(MCMND, + "flags=0x%x star_freq=%d end_freq=%d freq_diff=%d max_bandwidth=%d\n", + rule->flags, freq_range->start_freq_khz, + freq_range->end_freq_khz, + freq_range->end_freq_khz - freq_range->start_freq_khz, + freq_range->max_bandwidth_khz); + } + return regd; +} + +/** + * @brief create custom channel regulatory config + * + * @param priv A pointer to moal_private structure + * + * @return 0-success, otherwise failure + */ +static int woal_update_custom_regdomain(moal_private *priv, struct wiphy *wiphy) +{ + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u8 country_code[COUNTRY_CODE_LEN]; + + int ret = 0; + struct ieee80211_regdomain *regd = NULL; + + ENTER(); + + if (!priv || !wiphy) { + LEAVE(); + return -EFAULT; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -EFAULT; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GET_CHAN_REGION_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + memset(&misc->param.custom_reg_domain, 0, + sizeof(misc->param.custom_reg_domain)); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + memset(country_code, 0, sizeof(country_code)); + if (MTRUE == + is_cfg80211_special_region_code(priv->phandle->country_code)) { + country_code[0] = 'W'; + country_code[1] = 'W'; + } else { + country_code[0] = priv->phandle->country_code[0]; + country_code[1] = priv->phandle->country_code[1]; + } + if (misc->param.custom_reg_domain.region.country_code[0] != + country_code[0] || + misc->param.custom_reg_domain.region.country_code[1] != + country_code[1]) { + PRINTM(MERROR, "country code %c%c not match %c%c\n", + misc->param.custom_reg_domain.region.country_code[0], + misc->param.custom_reg_domain.region.country_code[1], + country_code[0], country_code[1]); + ret = -EFAULT; + goto done; + } + regd = create_custom_regdomain(&misc->param.custom_reg_domain); + if (regd) { + PRINTM(MMSG, "call regulatory_set_wiphy_regd %c%c", + misc->param.custom_reg_domain.region.country_code[0], + misc->param.custom_reg_domain.region.country_code[1]); + wiphy->regulatory_flags &= + ~(REGULATORY_STRICT_REG | REGULATORY_CUSTOM_REG); + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; + regulatory_set_wiphy_regd(wiphy, regd); + kfree(regd); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Request the driver to change regulatory domain + * + * @param wiphy A pointer to wiphy structure + * @param request A pointer to regulatory_request structure + * + * @return 0 + */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +static void +#else +static int +#endif +woal_cfg80211_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + t_u8 region[COUNTRY_CODE_LEN]; + enum ieee80211_band band; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + int ret = 0; +#endif + mlan_fw_info fw_info; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__); + LEAVE(); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + return -EINVAL; +#else + return; +#endif + } + + PRINTM(MCMND, + "cfg80211 regulatory domain callback " + "%c%c initiator=%d\n", + request->alpha2[0], request->alpha2[1], request->initiator); + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if (fw_info.force_reg) { + PRINTM(MINFO, + "Regulatory domain is enforced in the on-chip OTP\n"); + LEAVE(); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + return -EINVAL; +#else + return; +#endif + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (moal_extflg_isset(handle, EXT_DFS_OFFLOAD)) + woal_update_radar_chans_dfs_state(wiphy); +#endif + memset(region, 0, sizeof(region)); + moal_memcpy_ext(priv->phandle, region, request->alpha2, + sizeof(request->alpha2), sizeof(region)); + region[2] = ' '; + if ((handle->country_code[0] != request->alpha2[0]) || + (handle->country_code[1] != request->alpha2[1])) { + if (moal_extflg_isset(handle, EXT_CNTRY_TXPWR)) { + t_u8 country_code[COUNTRY_CODE_LEN]; + handle->country_code[0] = request->alpha2[0]; + handle->country_code[1] = request->alpha2[1]; + handle->country_code[2] = ' '; + memset(country_code, 0, sizeof(country_code)); + if (MTRUE == is_cfg80211_special_region_code(region)) { + country_code[0] = 'W'; + country_code[1] = 'W'; + } else { + country_code[0] = request->alpha2[0]; + country_code[1] = request->alpha2[1]; + } + if (MLAN_STATUS_SUCCESS != + woal_request_country_power_table(priv, + country_code)) { +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + return -EFAULT; +#else + return; +#endif + } + } + } + if (MTRUE != is_cfg80211_special_region_code(region)) { + if (!moal_extflg_isset(handle, EXT_CNTRY_TXPWR)) { + handle->country_code[0] = request->alpha2[0]; + handle->country_code[1] = request->alpha2[1]; + handle->country_code[2] = ' '; + } + if (MLAN_STATUS_SUCCESS != + woal_set_region_code(priv, handle->country_code)) + PRINTM(MERROR, "Set country code failed!\n"); + } + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_DRIVER: + PRINTM(MCMND, "Regulatory domain BY_DRIVER\n"); + break; + case NL80211_REGDOM_SET_BY_CORE: + PRINTM(MCMND, "Regulatory domain BY_CORE\n"); + break; + case NL80211_REGDOM_SET_BY_USER: + PRINTM(MCMND, "Regulatory domain BY_USER\n"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + if (fw_region && moal_extflg_isset(handle, EXT_CNTRY_TXPWR)) + woal_update_custom_regdomain(priv, wiphy); +#endif + break; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + PRINTM(MCMND, "Regulatory domain BY_COUNTRY_IE\n"); + break; + } + if (priv->wdev && priv->wdev->wiphy && + (request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE) && + (MTRUE != is_cfg80211_special_region_code(region))) { + band = priv->phandle->band; + priv->phandle->band = IEEE80211_BAND_2GHZ; + woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT); + priv->phandle->band = IEEE80211_BAND_5GHZ; + woal_send_domain_info_cmd_fw(priv, MOAL_IOCTL_WAIT); + priv->phandle->band = band; + } + + LEAVE(); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 9, 0) + return ret; +#endif +} + +#ifdef UAP_CFG80211 +/** + * @brief Swithces BSS role of interface + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option (MOAL_WAIT or MOAL_NO_WAIT) + * @param bss_role bss role + * + * @return 0 --success, otherwise fail + */ +mlan_status woal_role_switch(moal_private *priv, t_u8 wait_option, + t_u8 bss_role) +{ + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_ROLE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + bss->param.bss_role = bss_role; + + status = woal_request_ioctl(priv, req, wait_option); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief set/get bandcfg + * + * @param priv A pointer to moal_private structure + * @param action get or set action + * @param band_cfg A pointer to mlan_ds_band_cfg structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_setget_bandcfg(moal_private *priv, t_u8 action, + mlan_ds_band_cfg *band_cfg) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_radio_cfg *radio_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + 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; + req->action = action; + + if (req->action == MLAN_ACT_SET) + moal_memcpy_ext(priv->phandle, &radio_cfg->param.band_cfg, + band_cfg, sizeof(mlan_ds_band_cfg), + sizeof(radio_cfg->param.band_cfg)); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto error; + } + moal_memcpy_ext(priv->phandle, band_cfg, &radio_cfg->param.band_cfg, + sizeof(mlan_ds_band_cfg), sizeof(mlan_ds_band_cfg)); +error: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief request scan + * + * @param priv A pointer to moal_private structure + * @param scan_cfg A pointer to wlan_user_scan_cfg structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status woal_uap_scan(moal_private *priv, wlan_user_scan_cfg *scan_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_handle *handle = priv->phandle; + moal_private *tmp_priv; + u8 role; + mlan_ds_band_cfg org_bandcfg; + mlan_ds_band_cfg bandcfg; + u8 band_change = MFALSE; + ENTER(); + if (priv->bss_index > 0) + tmp_priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + else + tmp_priv = priv; + if (!tmp_priv) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + role = GET_BSS_ROLE(tmp_priv); + if (role == MLAN_BSS_ROLE_UAP) + woal_role_switch(tmp_priv, MOAL_IOCTL_WAIT, MLAN_BSS_ROLE_STA); + if (tmp_priv != priv) { + woal_setget_bandcfg(priv, MLAN_ACT_GET, &bandcfg); + woal_setget_bandcfg(tmp_priv, MLAN_ACT_GET, &org_bandcfg); + if (bandcfg.config_bands != org_bandcfg.config_bands) { + woal_setget_bandcfg(tmp_priv, MLAN_ACT_SET, &bandcfg); + band_change = MTRUE; + } + } +#ifdef REASSOCIATION + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_do_combo_scan\n"); + goto done; + } +#endif /* REASSOCIATION */ + tmp_priv->report_scan_result = MTRUE; + ret = woal_request_userscan(tmp_priv, MOAL_IOCTL_WAIT, scan_cfg); + woal_sched_timeout(5); +#ifdef REASSOCIATION + MOAL_REL_SEMAPHORE(&handle->reassoc_sem); +#endif +done: + if (role == MLAN_BSS_ROLE_UAP) + woal_role_switch(tmp_priv, MOAL_IOCTL_WAIT, MLAN_BSS_ROLE_UAP); + if (band_change) + woal_setget_bandcfg(tmp_priv, MLAN_ACT_SET, &org_bandcfg); + LEAVE(); + return ret; +} +#endif + +static int woal_find_wps_ie_in_probereq(const t_u8 *ie, int len) +{ + int left_len = len; + const t_u8 *pos = ie; + t_u8 ie_id, ie_len; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = {0x00, 0x50, 0xf2, 0x04}; + + while (left_len >= 2) { + ie_id = *pos; + ie_len = *(pos + 1); + if ((ie_len + 2) > left_len) + break; + if (ie_id == VENDOR_SPECIFIC_221) { + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp(pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wps_oui[3]) + return MTRUE; + } + + pos += (ie_len + 2); + left_len -= (ie_len + 2); + } + + return MFALSE; +} + +/** + * @brief check if the scan result expired + * + * @param priv A pointer to moal_private + * + * + * @return MTRUE/MFALSE; + */ +t_u8 woal_is_scan_result_expired(moal_private *priv) +{ + mlan_scan_resp scan_resp; + wifi_timeval t; + ENTER(); + // Don't block ACS scan + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return MTRUE; + } + // Don't block scan when non any interface active + if (!woal_is_any_interface_active(priv->phandle)) { + LEAVE(); + return MTRUE; + } +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + // Do not skip p2p interface connect scan + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + LEAVE(); + return MTRUE; + } +#endif +#endif + + if (MLAN_STATUS_SUCCESS != + woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) { + LEAVE(); + return MTRUE; + } + woal_get_monotonic_time(&t); +/** scan result expired value */ +#define SCAN_RESULT_EXPIRTED 1 + if (t.time_sec > (scan_resp.age_in_secs + SCAN_RESULT_EXPIRTED)) { + LEAVE(); + return MTRUE; + } + LEAVE(); + return MFALSE; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +/** + * @brief Request the driver to do a scan. Always returning + * zero meaning that the scan request is given to driver, + * and will be valid until passed to cfg80211_scan_done(). + * To inform scan results, call cfg80211_inform_bss(). + * + * @param wiphy A pointer to wiphy structure + * @param request A pointer to cfg80211_scan_request structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +#else +/** + * @brief Request the driver to do a scan. Always returning + * zero meaning that the scan request is given to driver, + * and will be valid until passed to cfg80211_scan_done(). + * To inform scan results, call cfg80211_inform_bss(). + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param request A pointer to cfg80211_scan_request structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_scan(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_scan_request *request) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = request->wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + wlan_user_scan_cfg *scan_req = NULL; + mlan_bss_info bss_info; + mlan_scan_cfg scan_cfg; + struct ieee80211_channel *chan; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + t_u8 buf[ETH_ALEN]; +#endif + int ret = 0, i; + unsigned long flags; + + ENTER(); + + PRINTM(MINFO, "Received scan request on %s\n", dev->name); + if (priv->phandle->scan_pending_on_block == MTRUE) { + PRINTM(MCMND, "scan already in processing...\n"); + LEAVE(); + return -EAGAIN; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (priv->last_event & EVENT_BG_SCAN_REPORT) { + PRINTM(MCMND, "block scan while pending BGSCAN result\n"); + priv->last_event = 0; + LEAVE(); + return -EAGAIN; + } +#endif +#if defined(STA_CFG80211) || defined(UAP_CFG80211) +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->phandle->is_go_timer_set && + priv->wdev->iftype != NL80211_IFTYPE_P2P_GO) { + PRINTM(MCMND, "block scan in go timer....\n"); + LEAVE(); + return -EAGAIN; + } +#endif +#endif +#endif + if (priv->fake_scan_complete || !woal_is_scan_result_expired(priv)) { + PRINTM(MEVENT, + "scan result not expired or fake scan complete flag is on\n"); + return -EAGAIN; + } + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS == + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + if (bss_info.scan_block) { + PRINTM(MEVENT, "Block scan in mlan module\n"); + return -EAGAIN; + } + } + if (priv->phandle->scan_request && + priv->phandle->scan_request != request) { + PRINTM(MCMND, + "different scan_request is coming before previous one is finished on %s...\n", + dev->name); + LEAVE(); + return -EBUSY; + } + + spin_lock_irqsave(&priv->phandle->scan_req_lock, flags); + priv->phandle->scan_request = request; + spin_unlock_irqrestore(&priv->phandle->scan_req_lock, flags); + if (is_zero_timeval(priv->phandle->scan_time_start)) { + woal_get_monotonic_time(&priv->phandle->scan_time_start); + PRINTM(MINFO, "%s : start_timeval=%d:%d \n", __func__, + priv->phandle->scan_time_start.time_sec, + priv->phandle->scan_time_start.time_usec); + } + scan_req = kmalloc(sizeof(wlan_user_scan_cfg), GFP_KERNEL); + memset(scan_req, 0x00, sizeof(wlan_user_scan_cfg)); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) + if (!is_broadcast_ether_addr(request->bssid)) { + moal_memcpy_ext(priv->phandle, scan_req->specific_bssid, + request->bssid, ETH_ALEN, + sizeof(scan_req->specific_bssid)); + PRINTM(MIOCTL, "scan: bssid=" MACSTR "\n", + MAC2STR(scan_req->specific_bssid)); + } +#endif + + if (priv->phandle->scan_request->n_channels <= 38) + scan_req->ext_scan_type = EXT_SCAN_ENHANCE; + + memset(&scan_cfg, 0, sizeof(mlan_scan_cfg)); +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->miracast_mode) + scan_req->scan_chan_gap = priv->phandle->scan_chan_gap; + else { +#endif + woal_get_scan_config(priv, &scan_cfg); + if (scan_cfg.scan_chan_gap) + scan_req->scan_chan_gap = scan_cfg.scan_chan_gap; + else if (woal_is_any_interface_active(priv->phandle)) + scan_req->scan_chan_gap = priv->phandle->scan_chan_gap; + else + scan_req->scan_chan_gap = 0; +#ifdef WIFI_DIRECT_SUPPORT + } +#endif + /** indicate FW, gap is optional */ + if (scan_req->scan_chan_gap && priv->phandle->pref_mac) + scan_req->scan_chan_gap |= GAP_FLAG_OPTIONAL; + + for (i = 0; i < priv->phandle->scan_request->n_ssids; i++) { + moal_memcpy_ext(priv->phandle, scan_req->ssid_list[i].ssid, + priv->phandle->scan_request->ssids[i].ssid, + priv->phandle->scan_request->ssids[i].ssid_len, + sizeof(scan_req->ssid_list[i].ssid)); + if (priv->phandle->scan_request->ssids[i].ssid_len) + scan_req->ssid_list[i].max_len = 0; + else + scan_req->ssid_list[i].max_len = 0xff; + PRINTM(MIOCTL, "scan: ssid=%s\n", scan_req->ssid_list[i].ssid); + } +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + priv->phandle->scan_request->n_ssids) { + if (!memcmp(scan_req->ssid_list[0].ssid, "DIRECT-", 7)) + scan_req->ssid_list[0].max_len = 0xfe; + } +#endif +#endif + for (i = 0; i < MIN(WLAN_USER_SCAN_CHAN_MAX, + priv->phandle->scan_request->n_channels); + i++) { + chan = priv->phandle->scan_request->channels[i]; + scan_req->chan_list[i].chan_number = chan->hw_value; + scan_req->chan_list[i].radio_type = chan->band; + if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) || + !priv->phandle->scan_request->n_ssids) + scan_req->chan_list[i].scan_type = + MLAN_SCAN_TYPE_PASSIVE; + else if (chan->flags & IEEE80211_CHAN_RADAR) + scan_req->chan_list[i].scan_type = + MLAN_SCAN_TYPE_PASSIVE_TO_ACTIVE; + else + scan_req->chan_list[i].scan_type = + MLAN_SCAN_TYPE_ACTIVE; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) + scan_req->chan_list[i].scan_time = + priv->phandle->scan_request->duration; +#else + scan_req->chan_list[i].scan_time = 0; +#endif +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + priv->phandle->scan_request->n_ssids) { + if (!memcmp(scan_req->ssid_list[0].ssid, "DIRECT-", 7)) + scan_req->chan_list[i].scan_time = + MIN_SPECIFIC_SCAN_CHAN_TIME; + } +#endif +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->miracast_mode) + scan_req->chan_list[i].scan_time = + priv->phandle->miracast_scan_time; + else if (woal_is_any_interface_active(priv->phandle)) { + if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) + scan_req->chan_list[i].scan_time = + INIT_PASSIVE_SCAN_CHAN_TIME; + else + scan_req->chan_list[i].scan_time = + MIN_SPECIFIC_SCAN_CHAN_TIME; + } +#endif +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) + scan_req->chan_list[i].scan_time = + MIN_SPECIFIC_SCAN_CHAN_TIME; +#endif + } + if (priv->phandle->scan_request->ie && + priv->phandle->scan_request->ie_len) { + if (woal_find_wps_ie_in_probereq( + (t_u8 *)priv->phandle->scan_request->ie, + priv->phandle->scan_request->ie_len)) { + PRINTM(MIOCTL, + "Notify firmware only keep probe response\n"); + scan_req->proberesp_only = MTRUE; + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie( + priv, NULL, 0, NULL, 0, NULL, 0, + (t_u8 *)priv->phandle->scan_request->ie, + priv->phandle->scan_request->ie_len, + MGMT_MASK_PROBE_REQ, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Fail to set scan request IE\n"); + ret = -EFAULT; + goto done; + } + } else { + /** Clear SCAN IE in Firmware */ + if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, + NULL, 0, NULL, 0, + MGMT_MASK_PROBE_REQ, + MOAL_IOCTL_WAIT); + } +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + /** use sync scan for uap */ + ret = woal_uap_scan(priv, scan_req); + if (!ret) { + kfree(scan_req); + LEAVE(); + return ret; + } else { + PRINTM(MERROR, "Uap SCAN failure\n"); + goto done; + } + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + PRINTM(MIOCTL, "NL80211_SCAN_FLAG_RANDOM_ADDR is set\n"); + get_random_bytes(buf, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) { + buf[i] &= ~request->mac_addr_mask[i]; + buf[i] |= request->mac_addr[i] & + request->mac_addr_mask[i]; + } + moal_memcpy_ext(priv->phandle, scan_req->random_mac, buf, + ETH_ALEN, sizeof(scan_req->random_mac)); + } else +#endif + moal_memcpy_ext(priv->phandle, scan_req->random_mac, + priv->random_mac, ETH_ALEN, + sizeof(scan_req->random_mac)); + + PRINTM(MCMND, "wlan:random_mac " MACSTR "\n", + MAC2STR(scan_req->random_mac)); + if (MLAN_STATUS_SUCCESS != woal_do_scan(priv, scan_req)) { + PRINTM(MERROR, "woal_do_scan fails!\n"); + ret = -EAGAIN; + goto done; + } +done: + if (ret) { + spin_lock_irqsave(&priv->phandle->scan_req_lock, flags); + woal_cfg80211_scan_done(request, MTRUE); + priv->phandle->scan_request = NULL; + priv->phandle->scan_priv = NULL; + spin_unlock_irqrestore(&priv->phandle->scan_req_lock, flags); + } else + PRINTM(MMSG, "wlan: %s START SCAN\n", dev->name); + kfree(scan_req); + LEAVE(); + return ret; +} +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) +static void woal_cfg80211_abort_scan(struct wiphy *wiphy, + struct wireless_dev *wdev) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev); + ENTER(); + PRINTM(MMSG, "wlan: ABORT SCAN start\n"); + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + LEAVE(); + return; +} +#endif +/** + * @brief construct and send ft action request + * + * @param priv A pointer to moal_private structure + * @param ie A pointer to ft ie + * @param le Value of ie len + * @param bssid A pointer to target ap bssid + * @ + * @return 0 -- success, otherwise fail + */ +static int woal_send_ft_action_requst(moal_private *priv, t_u8 *ie, t_u8 len, + t_u8 *bssid, t_u8 *target_ap) +{ + IEEE80211_MGMT *mgmt = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + pmlan_buffer pmbuf = NULL; + t_u32 pkt_type; + t_u32 tx_control; + t_u16 packet_len = 0; + t_u8 addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + int ret = 0; + + ENTER(); + + /* pkt_type + tx_control */ +#define HEADER_SIZE 8 + /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4*/ +#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 + 6) + /* 14 = category + action + sta addr + target ap */ +#define FT_REQUEST_LEN 14 + packet_len = (t_u16)len + MGMT_HEADER_LEN + FT_REQUEST_LEN; + pmbuf = woal_alloc_mlan_buffer(priv->phandle, + MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + + packet_len + sizeof(packet_len)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } + + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + pkt_type = MRVL_PKT_TYPE_MGMT_FRAME; + tx_control = 0; + /* Add pkt_type and tx_control */ + moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset, + &pkt_type, sizeof(pkt_type), sizeof(pkt_type)); + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), + &tx_control, sizeof(tx_control), sizeof(tx_control)); + /*Add packet len*/ + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE, + &packet_len, sizeof(packet_len), sizeof(packet_len)); + + mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset + + HEADER_SIZE + sizeof(packet_len)); + memset(mgmt, 0, MGMT_HEADER_LEN); + mgmt->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION); + moal_memcpy_ext(priv->phandle, mgmt->da, bssid, ETH_ALEN, + sizeof(mgmt->da)); + moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN, + sizeof(mgmt->sa)); + moal_memcpy_ext(priv->phandle, mgmt->bssid, bssid, ETH_ALEN, + sizeof(mgmt->bssid)); + moal_memcpy_ext(priv->phandle, mgmt->addr4, addr, ETH_ALEN, + sizeof(mgmt->addr4)); + + mgmt->u.ft_req.category = 0x06; /**ft action code 0x6*/ + mgmt->u.ft_req.action = 0x1; /**ft action request*/ + moal_memcpy_ext(priv->phandle, mgmt->u.ft_req.sta_addr, + priv->current_addr, ETH_ALEN, + sizeof(mgmt->u.ft_req.sta_addr)); + moal_memcpy_ext(priv->phandle, mgmt->u.ft_req.target_ap_addr, target_ap, + ETH_ALEN, sizeof(mgmt->u.ft_req.target_ap_addr)); + + if (ie && len) + moal_memcpy_ext(priv->phandle, + (t_u8 *)(&mgmt->u.ft_req.variable), ie, len, + len); + + pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len); + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + pmbuf->priority = 7; + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } + +done: + LEAVE(); + return ret; +} +/** + * @brief construct and send ft auth request + * + * @param priv A pointer to moal_private structure + * @param ie A pointer to ft ie + * @param le Value of ie len + * @param bssid A pointer to target ap bssid + * @ + * @return 0 -- success, otherwise fail + */ +static int woal_send_ft_auth_requst(moal_private *priv, t_u8 *ie, t_u8 len, + t_u8 *bssid) +{ + IEEE80211_MGMT *mgmt = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + pmlan_buffer pmbuf = NULL; + t_u32 pkt_type; + t_u32 tx_control; + t_u16 packet_len = 0; + t_u8 addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + int ret = 0; + + ENTER(); + /* pkt_type + tx_control */ +#define HEADER_SIZE 8 + /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl + addr4*/ +#define MGMT_HEADER_LEN (2 + 2 + 6 + 6 + 6 + 2 + 6) + /* 6 = auth_alg + auth_transaction +auth_status*/ +#define AUTH_BODY_LEN 6 + packet_len = (t_u16)len + MGMT_HEADER_LEN + AUTH_BODY_LEN; + pmbuf = woal_alloc_mlan_buffer(priv->phandle, + MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + + packet_len + sizeof(packet_len)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } + + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + pkt_type = MRVL_PKT_TYPE_MGMT_FRAME; + tx_control = 0; + /* Add pkt_type and tx_control */ + moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset, + &pkt_type, sizeof(pkt_type), sizeof(pkt_type)); + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), + &tx_control, sizeof(tx_control), sizeof(tx_control)); + /*Add packet len*/ + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE, + &packet_len, sizeof(packet_len), sizeof(packet_len)); + + mgmt = (IEEE80211_MGMT *)(pmbuf->pbuf + pmbuf->data_offset + + HEADER_SIZE + sizeof(packet_len)); + memset(mgmt, 0, MGMT_HEADER_LEN); + mgmt->frame_control = + cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); + moal_memcpy_ext(priv->phandle, mgmt->da, bssid, ETH_ALEN, + sizeof(mgmt->da)); + moal_memcpy_ext(priv->phandle, mgmt->sa, priv->current_addr, ETH_ALEN, + sizeof(mgmt->sa)); + moal_memcpy_ext(priv->phandle, mgmt->bssid, bssid, ETH_ALEN, + sizeof(mgmt->bssid)); + moal_memcpy_ext(priv->phandle, mgmt->addr4, addr, ETH_ALEN, + sizeof(mgmt->addr4)); + + mgmt->u.auth.auth_alg = cpu_to_le16(WLAN_AUTH_FT); + mgmt->u.auth.auth_transaction = cpu_to_le16(1); + mgmt->u.auth.status_code = cpu_to_le16(0); + if (ie && len) + moal_memcpy_ext(priv->phandle, (t_u8 *)(&mgmt->u.auth.variable), + ie, len, len); + + pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len); + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + pmbuf->priority = 7; + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief connect the AP through ft over air. + * + * @param priv A pointer to moal_private structure + * @param bssid A pointer to bssid + * @param chan struct ieee80211_channel + * + * @return 0 -- success, otherwise fail + */ +static int woal_connect_ft_over_air(moal_private *priv, t_u8 *bssid, + struct ieee80211_channel *chan) +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + t_u8 status = 0; +#endif + + t_u8 wait_option = MOAL_IOCTL_WAIT; + int ret = 0; + long timeout = 0; + + ENTER(); + + if (!bssid) { + PRINTM(MERROR, + "Invalid bssid, unable to connect AP to through FT\n"); + LEAVE(); + return -EFAULT; + } + + /*enable auth register frame*/ + woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MTRUE); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +#define AUTH_TX_DEFAULT_WAIT_TIME 1200 + woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MFALSE, &status, + chan, 0, AUTH_TX_DEFAULT_WAIT_TIME); +#endif + + /*construct auth request and send out*/ + woal_send_ft_auth_requst(priv, priv->ft_ie, priv->ft_ie_len, bssid); + PRINTM(MMSG, "wlan: send out FT auth,wait for auth response\n"); + /*wait until received auth response*/ + priv->ft_wait_condition = MFALSE; + timeout = wait_event_timeout(priv->ft_wait_q, priv->ft_wait_condition, + 1 * HZ); + if (!timeout) { + /*connet fail */ + if (!priv->ft_roaming_triggered_by_driver) { + woal_inform_bss_from_scan_result(priv, NULL, + wait_option); + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_SUCCESS, + GFP_KERNEL); + } + priv->ft_roaming_triggered_by_driver = MFALSE; + PRINTM(MMSG, "wlan: keep connected to bssid " MACSTR "\n", + MAC2STR(priv->cfg_bssid)); + } else { + PRINTM(MMSG, "wlan: FT auth received \n"); + moal_memcpy_ext(priv->phandle, priv->target_ap_bssid, bssid, + ETH_ALEN, sizeof(priv->target_ap_bssid)); + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MTRUE, &status, + NULL, 0, 0); +#endif + + woal_mgmt_frame_register(priv, IEEE80211_STYPE_AUTH, MFALSE); + + LEAVE(); + return ret; +} + +/** + * @brief connect the AP through ft over DS. + * + * @param priv A pointer to moal_private structure + * @param bssid A pointer to bssid + * @param chan struct ieee80211_channel + * + * @return 0 -- success, otherwise fail + */ +static int woal_connect_ft_over_ds(moal_private *priv, t_u8 *bssid, + struct ieee80211_channel *pchan) +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + t_u8 status = 0; +#endif + t_u8 wait_option = MOAL_IOCTL_WAIT; + int ret = 0; + long timeout = 0; + + ENTER(); + + if (priv->media_connected) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MFALSE, + &status, pchan, 0, 1200); +#endif + /*construct ft action request and send out*/ + woal_send_ft_action_requst(priv, priv->ft_ie, priv->ft_ie_len, + (t_u8 *)priv->cfg_bssid, bssid); + PRINTM(MMSG, + "wlan: send out FT request,wait for FT response\n"); + /*wait until received auth response*/ + priv->ft_wait_condition = MFALSE; + timeout = wait_event_timeout(priv->ft_wait_q, + priv->ft_wait_condition, 1 * HZ); + if (!timeout) { + /*go over air, as current AP may be unreachable */ + PRINTM(MMSG, "wlan: go over air\n"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + woal_cfg80211_remain_on_channel_cfg( + priv, wait_option, MTRUE, &status, NULL, 0, 0); +#endif + woal_connect_ft_over_air(priv, bssid, pchan); + LEAVE(); + return ret; + } else { + PRINTM(MMSG, "wlan: received FT response\n"); + moal_memcpy_ext(priv->phandle, priv->target_ap_bssid, + bssid, ETH_ALEN, + sizeof(priv->target_ap_bssid)); + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + woal_cfg80211_remain_on_channel_cfg(priv, wait_option, MTRUE, + &status, NULL, 0, 0); +#endif + } + + LEAVE(); + return ret; +} + +/** + * @brief start FT Roaming. + * + * @param priv A pointer to moal_private structure + * @param ssid_bssid A pointer to mlan_ssid_bssid structure + * + * + * @return 0 -- success, otherwise fail + */ +static int woal_start_ft_roaming(moal_private *priv, + mlan_ssid_bssid *ssid_bssid) +{ + struct ieee80211_channel chan; + int ret = 0; + + ENTER(); + + PRINTM(MEVENT, "Try to start FT roaming......\n"); + chan.band = (ssid_bssid->channel < 36) ? IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ; + chan.center_freq = ieee80211_channel_to_frequency(ssid_bssid->channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + chan.band +#endif + ); + + if (!(priv->last_event & EVENT_PRE_BCN_LOST) && + (ssid_bssid->ft_cap & MBIT(0))) { + woal_connect_ft_over_ds(priv, (t_u8 *)&ssid_bssid->bssid, + &chan); + } else { + /*if pre beacon lost, it need to send auth request instead ft + * action request when ft over ds */ + woal_connect_ft_over_air(priv, (t_u8 *)&ssid_bssid->bssid, + &chan); + } + + LEAVE(); + return ret; +} +/** + * @brief Request the driver to connect to the ESS with + * the specified parameters from kernel + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param sme A pointer to cfg80211_connect_params structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + mlan_bss_info bss_info; + unsigned long flags; + mlan_ds_misc_assoc_rsp *assoc_rsp = NULL; + IEEEtypes_AssocRsp_t *passoc_rsp = NULL; + mlan_ssid_bssid ssid_bssid; + moal_handle *handle = priv->phandle; + int i; + + ENTER(); + + PRINTM(MINFO, "Received association request on %s\n", dev->name); + priv->cfg_disconnect = MFALSE; +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return 0; + } +#endif + if (priv->wdev->iftype != NL80211_IFTYPE_STATION +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + && priv->wdev->iftype != NL80211_IFTYPE_P2P_CLIENT +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + ) { + PRINTM(MERROR, + "Received infra assoc request when station not in infra mode\n"); + LEAVE(); + return -EINVAL; + } + memset(&ssid_bssid, 0, sizeof(ssid_bssid)); + moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid.ssid, sme->ssid, + sme->ssid_len, sizeof(ssid_bssid.ssid.ssid)); + ssid_bssid.ssid.ssid_len = sme->ssid_len; + if (sme->bssid) + moal_memcpy_ext(priv->phandle, &ssid_bssid.bssid, sme->bssid, + ETH_ALEN, sizeof(ssid_bssid.bssid)); + /* Not allowed to connect to the same AP which is already connected + with other interface */ + for (i = 0; i < handle->priv_num; i++) { + if (handle->priv[i] != priv && + MTRUE == woal_is_connected(handle->priv[i], &ssid_bssid)) { + PRINTM(MMSG, + "wlan: already connected with other interface, bssid " MACSTR + "\n", + MAC2STR(handle->priv[i]->cfg_bssid)); + LEAVE(); + return -EINVAL; + } + } + + /** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); + +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && + (priv->wdev->iftype == NL80211_IFTYPE_STATION || + priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + /* if bsstype == wifi direct, and iftype == station or p2p + * client, that means wpa_supplicant wants to enable wifi direct + * functionality, so we should init p2p client. + * + * Note that due to kernel iftype check, ICS wpa_supplicant + * could not updaet iftype to init p2p client, so we have to + * done it here. + * */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_init_p2p_client(priv)) { + PRINTM(MERROR, + "Init p2p client for wpa_supplicant failed.\n"); + ret = -EFAULT; + + LEAVE(); + return ret; + } + } + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + /* WAR for P2P connection with vendor TV */ + woal_sched_timeout(200); + } +#endif +#endif + /*11r roaming triggered by supplicant */ + if (priv->media_connected && priv->ft_ie_len && + !(priv->ft_cap & MBIT(0))) { + /** get current bss info */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + /** get target bss info */ + if (MLAN_STATUS_SUCCESS != + woal_find_essid(priv, &ssid_bssid, MOAL_IOCTL_WAIT)) { + ret = woal_cfg80211_connect_scan(priv, sme, + MOAL_IOCTL_WAIT); + if (!ret) { + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, + MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "can't find targe AP \n"); + // LEAVE(); + // return -EFAULT; + } + } + } + if (bss_info.mdid == ssid_bssid.ft_md && + bss_info.ft_cap == ssid_bssid.ft_cap) { + ret = woal_start_ft_roaming(priv, &ssid_bssid); + LEAVE(); + return 0; + } + } + + priv->cfg_connect = MTRUE; + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + priv->assoc_status = 0; + assoc_rsp = kzalloc(sizeof(mlan_ds_misc_assoc_rsp), GFP_ATOMIC); + if (!assoc_rsp) { + PRINTM(MERROR, "Failed to allocate memory for assoc_rsp\n"); + ret = -ENOMEM; + LEAVE(); + return ret; + } + ret = woal_cfg80211_assoc(priv, (void *)sme, MOAL_IOCTL_WAIT, + assoc_rsp); + + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE); + if (!ret) { + passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp->assoc_resp_buf; + priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD; + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_save_conn_params(priv, sme); + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + priv->channel = bss_info.bss_chan; + if (!ssid_bssid.ft_md) { + priv->ft_ie_len = 0; + priv->ft_pre_connect = MFALSE; + priv->ft_md = 0; + priv->ft_cap = 0; + } + } + spin_lock_irqsave(&priv->connect_lock, flags); + priv->cfg_connect = MFALSE; + if (!ret && priv->media_connected) { + PRINTM(MMSG, + "wlan: Connected to bssid " MACSTR " successfully\n", + MAC2STR(priv->cfg_bssid)); + spin_unlock_irqrestore(&priv->connect_lock, flags); + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, + passoc_rsp->ie_buffer, + assoc_rsp->assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, + WLAN_STATUS_SUCCESS, GFP_KERNEL); + } else { + PRINTM(MINFO, "wlan: Failed to connect to bssid " MACSTR "\n", + MAC2STR(priv->cfg_bssid)); + memset(priv->cfg_bssid, 0, ETH_ALEN); + spin_unlock_irqrestore(&priv->connect_lock, flags); + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, + NULL, 0, woal_get_assoc_status(priv), + GFP_KERNEL); + } + + kfree(assoc_rsp); + assoc_rsp = NULL; + LEAVE(); + return 0; +} + +/** + * @brief This function will print diconnect reason code according + * to IEEE 802.11 spec + * + * @param reason_code reason code for the deauth/disaccoc + * received from firmware + * @return N/A + */ +static void woal_print_disconnect_reason(t_u16 reason_code) +{ + ENTER(); + + switch (reason_code) { + case MLAN_REASON_UNSPECIFIED: + PRINTM(MMSG, "wlan: REASON: Unspecified reason\n"); + break; + case MLAN_REASON_PREV_AUTH_NOT_VALID: + PRINTM(MMSG, + "wlan: REASON: Previous authentication no longer valid\n"); + break; + case MLAN_REASON_DEAUTH_LEAVING: + PRINTM(MMSG, + "wlan: REASON: (Deauth) Sending STA is leaving (or has left) IBSS or ESS\n"); + break; + case MLAN_REASON_DISASSOC_DUE_TO_INACTIVITY: + PRINTM(MMSG, + "wlan: REASON: Disassociated due to inactivity \n"); + break; + case MLAN_REASON_DISASSOC_AP_BUSY: + PRINTM(MMSG, + "wlan: REASON: (Disassociated) AP unable to handle all connected STAs\n"); + break; + case MLAN_REASON_CLASS2_FRAME_FROM_NOAUTH_STA: + PRINTM(MMSG, + "wlan: REASON: Class 2 frame was received from nonauthenticated STA\n"); + break; + case MLAN_REASON_CLASS3_FRAME_FROM_NOASSOC_STA: + PRINTM(MMSG, + "wlan: REASON: Class 3 frame was received from nonassociated STA\n"); + break; + case MLAN_REASON_DISASSOC_STA_HAS_LEFT: + PRINTM(MMSG, + "wlan: REASON: (Disassocated) Sending STA is leaving (or has left) BSS\n"); + break; + case MLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH: + PRINTM(MMSG, + "wlan: REASON: STA requesting (re)assoc is not authenticated with responding STA\n"); + break; + default: + break; + } + + LEAVE(); + return; +} + +/** + * @brief Request the driver to disconnect + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param reason_code Reason code + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + t_u16 reason_code) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + PRINTM(MMSG, + "wlan: Received disassociation request on %s, reason: %u\n", + dev->name, reason_code); + woal_print_disconnect_reason(reason_code); +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return 0; + } +#endif + if (priv->phandle->driver_status) { + PRINTM(MERROR, + "Block woal_cfg80211_disconnect in abnormal driver state\n"); + LEAVE(); + return -EFAULT; + } + + if (priv->cfg_disconnect) { + PRINTM(MERROR, "Disassociation already in progress\n"); + LEAVE(); + return 0; + } + + if (priv->media_connected == MFALSE) { + PRINTM(MMSG, " Already disconnected\n"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + if (priv->wdev->current_bss && + (priv->wdev->iftype == NL80211_IFTYPE_STATION || + priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + priv->cfg_disconnect = MTRUE; + cfg80211_disconnected(priv->netdev, 0, NULL, 0, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + true, +#endif + GFP_KERNEL); + } +#endif + LEAVE(); + return 0; + } + + priv->cfg_disconnect = MTRUE; + if (woal_disconnect(priv, MOAL_IOCTL_WAIT_TIMEOUT, priv->cfg_bssid, + reason_code) != MLAN_STATUS_SUCCESS) { + priv->cfg_disconnect = MFALSE; + LEAVE(); + return -EFAULT; + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + if (priv->wdev->iftype == NL80211_IFTYPE_STATION || + priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) + cfg80211_disconnected(priv->netdev, 0, NULL, 0, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + true, +#endif + GFP_KERNEL); +#endif + + memset(priv->cfg_bssid, 0, ETH_ALEN); + if (priv->bss_type == MLAN_BSS_TYPE_STA) + woal_clear_conn_params(priv); + priv->channel = 0; + + LEAVE(); + return 0; +} + +/** + * @brief This function is deauthentication handler when host MLME + * enable. + * In this case driver will prepare and send Deauth Req. + * + * @param wiphy A pointer to wiphy. + * + * @param dev A pointer to net_device + * + * @param req A pointer to cfg80211_deauth_request + * + * @return 0 -- success, otherwise fail + */ + +static int woal_cfg80211_deauthenticate(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_deauth_request *req) +{ + int ret = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + if (priv->host_mlme) { + priv->host_mlme = MFALSE; + priv->auth_flag = 0; + priv->auth_alg = 0xFFFF; + /*send deauth packet to notify disconnection to wpa_supplicant + */ + woal_deauth_event(priv, req->reason_code); + } +#endif + + ret = woal_cfg80211_disconnect(wiphy, dev, req->reason_code); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 11, 0) + if (priv->wdev->iftype == NL80211_IFTYPE_STATION || + priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) + cfg80211_disconnected(priv->netdev, 0, NULL, 0, GFP_KERNEL); +#endif + return ret; +} + +/** + * @brief This function is disassociation handler when host MLME + * enable. + * In this case driver will prepare and send Disassoc frame. + * + * @param wiphy A pointer to wiphy. + * + * @param dev A pointer to net_device + * + * @param req A pointer to cfg80211_disassoc_request + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_disassociate(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_disassoc_request *req) +{ + int ret = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + if (priv->host_mlme) { + priv->host_mlme = MFALSE; + priv->auth_flag = 0; + priv->auth_alg = 0xFFFF; + /*send deauth packet to notify disconnection to wpa_supplicant + */ + woal_deauth_event(priv, req->reason_code); + } +#endif + + ret = woal_cfg80211_disconnect(wiphy, dev, req->reason_code); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 11, 0) + if (priv->wdev->iftype == NL80211_IFTYPE_STATION || + priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT) + cfg80211_disconnected(priv->netdev, 0, NULL, 0, GFP_KERNEL); +#endif + return ret; +} + +/** + * @brief Request the driver to get the station information + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac MAC address of the station + * @param sinfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_get_station(struct wiphy *wiphy, + struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_info *sinfo) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return woal_uap_cfg80211_get_station(wiphy, dev, mac, sinfo); + } +#endif + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return -ENOENT; + } + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_dump_station_info(priv, sinfo)) { + PRINTM(MERROR, "cfg80211: Failed to get station info\n"); + ret = -EFAULT; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#endif + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to dump the station information + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param idx Station index + * @param mac MAC address of the station + * @param sinfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_dump_station(struct wiphy *wiphy, + struct net_device *dev, int idx, + t_u8 *mac, struct station_info *sinfo) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return woal_uap_cfg80211_dump_station(wiphy, dev, idx, mac, + sinfo); + } +#endif + + if (!priv->media_connected || idx != 0) { + PRINTM(MINFO, + "cfg80211: Media not connected or not for this station!\n"); + LEAVE(); + return -ENOENT; + } + + moal_memcpy_ext(priv->phandle, mac, priv->cfg_bssid, ETH_ALEN, + ETH_ALEN); + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_dump_station_info(priv, sinfo)) { + PRINTM(MERROR, "cfg80211: Failed to get station info\n"); + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Convert driver band configuration to IEEE band type + * + * @param band Driver band configuration + * + * @return IEEE band type + */ +t_u8 woal_bandcfg_to_ieee_band(Band_Config_t bandcfg) +{ + t_u8 ret_radio_type = 0; + + ENTER(); + + switch (bandcfg.chanBand) { + case BAND_5GHZ: + ret_radio_type = IEEE80211_BAND_5GHZ; + break; + case BAND_2GHZ: + default: + ret_radio_type = IEEE80211_BAND_2GHZ; + break; + } + LEAVE(); + return ret_radio_type; +} + +/** + * @brief Request the driver to dump survey info + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param idx Station index + * @param survey A pointer to survey_info structure + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_dump_survey(struct wiphy *wiphy, + struct net_device *dev, int idx, + struct survey_info *survey) +{ + int ret = -ENOENT; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + enum ieee80211_band band; + ChanStatistics_t *pchan_stats = NULL; + mlan_scan_resp scan_resp; + + ENTER(); + PRINTM(MIOCTL, "dump_survey idx=%d\n", idx); + + memset(&scan_resp, 0, sizeof(scan_resp)); + if (MLAN_STATUS_SUCCESS != + woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) { + ret = -EFAULT; + goto done; + } + pchan_stats = (ChanStatistics_t *)scan_resp.pchan_stats; + if (idx > scan_resp.num_in_chan_stats || idx < 0) { + ret = -EFAULT; + goto done; + } + if (idx == scan_resp.num_in_chan_stats || + !pchan_stats[idx].cca_scan_duration) + goto done; + ret = 0; + memset(survey, 0, sizeof(*survey)); + band = woal_bandcfg_to_ieee_band(pchan_stats[idx].bandcfg); + survey->channel = ieee80211_get_channel( + wiphy, ieee80211_channel_to_frequency(pchan_stats[idx].chan_num +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + band +#endif + )); + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = pchan_stats[idx].noise; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + survey->filled |= SURVEY_INFO_TIME | SURVEY_INFO_TIME_BUSY; + survey->time = pchan_stats[idx].cca_scan_duration; + survey->time_busy = pchan_stats[idx].cca_busy_duration; +#else + survey->filled |= + SURVEY_INFO_CHANNEL_TIME | SURVEY_INFO_CHANNEL_TIME_BUSY; + survey->channel_time = pchan_stats[idx].cca_scan_duration; + survey->channel_time_busy = pchan_stats[idx].cca_busy_duration; +#endif +#endif +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +static int woal_cfg80211_get_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(wdev->netdev); + chan_band_info channel; + + memset(&channel, 0x00, sizeof(channel)); + +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (priv->bss_started == MTRUE) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_ap_channel(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + &channel)) { + PRINTM(MERROR, "Fail to get ap channel \n"); + return -EFAULT; + } + } else { + PRINTM(MIOCTL, "get_channel when AP is not started\n"); + return -EFAULT; + } + } else +#endif + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (priv->media_connected == MTRUE) { + if (MLAN_STATUS_SUCCESS != + woal_get_sta_channel(priv, MOAL_IOCTL_WAIT, + &channel)) { + PRINTM(MERROR, "Fail to get sta channel \n"); + return -EFAULT; + } + } else { + PRINTM(MIOCTL, + "get_channel when STA is not connected\n"); + return -EFAULT; + } + } else { + PRINTM(MERROR, "BssRole not support %d.\n", GET_BSS_ROLE(priv)); + return -EFAULT; + } + + if (MLAN_STATUS_FAILURE == woal_chandef_create(priv, chandef, &channel)) + return -EFAULT; + else + return 0; +} +#endif + +/** + * @brief Request the driver to change the IEEE power save + * mdoe + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param enabled Enable or disable + * @param timeout Timeout value + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, bool enabled, + int timeout) +{ + int ret = 0, disabled; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + if (moal_extflg_isset(priv->phandle, EXT_HW_TEST) || + (priv->phandle->params.ps_mode == MLAN_INIT_PARA_DISABLED)) { + PRINTM(MIOCTL, "block set power hw_test=%d ps_mode=%d\n", + moal_extflg_isset(priv->phandle, EXT_HW_TEST), + priv->phandle->params.ps_mode); + LEAVE(); + return -EFAULT; + } + + if (priv->phandle->driver_status) { + PRINTM(MERROR, + "Block woal_cfg80211_set_power_mgmt in abnormal driver state\n"); + LEAVE(); + return -EFAULT; + } +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MIOCTL, "skip set power for p2p interface\n"); + LEAVE(); + return ret; + } +#endif +#endif + if (enabled) + disabled = 0; + else + disabled = 1; + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, MLAN_ACT_SET, + &disabled, timeout, + MOAL_IOCTL_WAIT)) { + ret = -EOPNOTSUPP; + } + + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) +/** + * @brief Request the driver to get the transmit power info + * + * @param wiphy A pointer to wiphy structure + * @param type TX power adjustment type + * @param dbm TX power in dbm + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_get_tx_power(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct wireless_dev *wdev, +#endif + int *dbm) +{ + int ret = 0; + moal_private *priv = NULL; + mlan_power_cfg_t power_cfg; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + LEAVE(); + return -EFAULT; + } + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + + if (!priv) { + PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__); + LEAVE(); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_GET, &power_cfg)) { + LEAVE(); + return -EFAULT; + } + + *dbm = power_cfg.power_level; + + LEAVE(); + return ret; +} +/** + * @brief Request the driver to change the transmit power + * + * @param wiphy A pointer to wiphy structure + * @param type TX power adjustment type + * @param dbm TX power in dbm + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_set_tx_power(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct wireless_dev *wdev, +#endif +#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 36) + enum tx_power_setting type, +#else + enum nl80211_tx_power_setting type, +#endif + int dbm) +{ + int ret = 0; + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + mlan_power_cfg_t power_cfg; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + PRINTM(MFATAL, "Unable to get priv in %s()\n", __func__); + LEAVE(); + return -EFAULT; + } + + if (type) { + power_cfg.is_power_auto = 0; + power_cfg.power_level = dbm; + } else + power_cfg.is_power_auto = 1; + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_SET, &power_cfg)) + ret = -EFAULT; + + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 35) +/** + * CFG802.11 operation handler for connection quality monitoring. + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param rssi_thold rssi threshold + * @param rssi_hyst rssi hysteresis + */ +static int woal_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + ENTER(); + priv->cqm_rssi_thold = rssi_thold; + priv->cqm_rssi_high_thold = rssi_thold; + priv->cqm_rssi_hyst = rssi_hyst; + + PRINTM(MIOCTL, "rssi_thold=%d rssi_hyst=%d\n", (int)rssi_thold, + (int)rssi_hyst); + woal_set_rssi_threshold(priv, 0, MOAL_IOCTL_WAIT); + LEAVE(); + return 0; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) +/** + * @brief remain on channel config + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param cancel cancel remain on channel flag + * @param status A pointer to status, success, in process or reject + * @param chan A pointer to ieee80211_channel structure + * @param channel_type channel_type, + * @param duration Duration wait to receive frame + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_remain_on_channel_cfg(moal_private *priv, t_u8 wait_option, + t_u8 remove, t_u8 *status, + struct ieee80211_channel *chan, + enum mlan_channel_type channel_type, + t_u32 duration) +{ + mlan_ds_remain_chan chan_cfg; + int ret = 0; + + ENTER(); + + if (!status || (!chan && !remove)) { + PRINTM(MERROR, + "Invalid parameter status=%p, chan=%p, remove=%d\n", + status, chan, remove); + LEAVE(); + return -EFAULT; + } + memset(&chan_cfg, 0, sizeof(mlan_ds_remain_chan)); + if (remove) { + chan_cfg.remove = MTRUE; + } else { +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->is_go_timer_set) { + PRINTM(MINFO, + "block remain on channel while go timer is on\n"); + LEAVE(); + return -EBUSY; + } +#endif + if (chan->band == IEEE80211_BAND_2GHZ) + chan_cfg.bandcfg.chanBand = BAND_2GHZ; + else if (chan->band == IEEE80211_BAND_5GHZ) + chan_cfg.bandcfg.chanBand = BAND_5GHZ; + switch (channel_type) { + case CHAN_HT40MINUS: + chan_cfg.bandcfg.chan2Offset = SEC_CHAN_BELOW; + chan_cfg.bandcfg.chanWidth = CHAN_BW_40MHZ; + break; + case CHAN_HT40PLUS: + chan_cfg.bandcfg.chan2Offset = SEC_CHAN_ABOVE; + chan_cfg.bandcfg.chanWidth = CHAN_BW_40MHZ; + break; + case CHAN_VHT80: + chan_cfg.bandcfg.chanWidth = CHAN_BW_80MHZ; + break; + case CHAN_NO_HT: + case CHAN_HT20: + default: + break; + } + chan_cfg.channel = + ieee80211_frequency_to_channel(chan->center_freq); + chan_cfg.remain_period = duration; + PRINTM(MCMND, + "Remain on Channel: chan=%d, offset=%d width=%d\n", + chan_cfg.channel, chan_cfg.bandcfg.chan2Offset, + chan_cfg.bandcfg.chanWidth); + } + if (MLAN_STATUS_SUCCESS == + woal_set_remain_channel_ioctl(priv, wait_option, &chan_cfg)) + *status = chan_cfg.status; + else + ret = -EFAULT; + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, + struct net_device *dev, u64 cookie) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + t_u8 status = 1; + moal_private *remain_priv = NULL; + + ENTER(); + + if (priv->phandle->remain_on_channel) { + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "mgmt_tx_cancel_wait: Wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg(remain_priv, + MOAL_IOCTL_WAIT, MTRUE, + &status, NULL, 0, 0)) { + PRINTM(MERROR, + "mgmt_tx_cancel_wait: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv->netdev, +#else + remain_priv->wdev, +#endif + priv->phandle->cookie, &priv->phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv->phandle->channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +/** + * @brief Make chip remain on channel + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type + * @param duration Duration for timer + * @param cookie A pointer to timer cookie + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, + struct ieee80211_channel *chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + enum nl80211_channel_type channel_type, +#endif + unsigned int duration, u64 *cookie) +#else +/** + * @brief Make chip remain on channel + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type + * @param duration Duration for timer + * @param cookie A pointer to timer cookie + * + * @return 0 -- success, otherwise fail + */ +static int +woal_cfg80211_remain_on_channel(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int duration, u64 *cookie) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + t_u8 status = 1; + moal_private *remain_priv = NULL; + + ENTER(); + + if (!chan || !cookie) { + PRINTM(MERROR, "Invalid parameter for remain on channel\n"); + ret = -EFAULT; + goto done; + } + /** cancel previous remain on channel */ + if (priv->phandle->remain_on_channel && + ((priv->phandle->chan.center_freq != chan->center_freq))) { + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "remain_on_channel: Wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg(remain_priv, + MOAL_IOCTL_WAIT, MTRUE, + &status, NULL, 0, 0)) { + PRINTM(MERROR, + "remain_on_channel: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + priv->phandle->cookie = 0; + priv->phandle->remain_on_channel = MFALSE; + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, MFALSE, + &status, chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + channel_type, +#else + 0, +#endif + (t_u32)duration)) { + ret = -EFAULT; + goto done; + } + + if (status) { + PRINTM(MMSG, + "%s: Set remain on Channel: channel=%d with status=%d\n", + dev->name, + ieee80211_frequency_to_channel(chan->center_freq), + status); + if (!priv->phandle->remain_on_channel) { + priv->phandle->is_remain_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->remain_timer, duration); + } + } + + /* remain on channel operation success */ + /* we need update the value cookie */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + *cookie = (u64)random32() | 1; +#else + *cookie = (u64)prandom_u32() | 1; +#endif + priv->phandle->remain_on_channel = MTRUE; + priv->phandle->remain_bss_index = priv->bss_index; + priv->phandle->cookie = *cookie; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv->phandle->channel_type = channel_type; +#endif + moal_memcpy_ext(priv->phandle, &priv->phandle->chan, chan, + sizeof(struct ieee80211_channel), + sizeof(priv->phandle->chan)); + + if (status == 0) + PRINTM(MIOCTL, + "%s: Set remain on Channel: channel=%d cookie = %#llx\n", + dev->name, + ieee80211_frequency_to_channel(chan->center_freq), + priv->phandle->cookie); + + cfg80211_ready_on_channel( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + dev, +#else + priv->wdev, +#endif + *cookie, chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + channel_type, +#endif + duration, GFP_KERNEL); + +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +/** + * @brief Cancel remain on channel + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param cookie A pointer to timer cookie + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + u64 cookie) +#else +/** + * @brief Cancel remain on channel + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param cookie A pointer to timer cookie + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct net_device *dev, + u64 cookie) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + moal_private *remain_priv = NULL; + int ret = 0; + t_u8 status = 1; + + ENTER(); + PRINTM(MIOCTL, "Cancel remain on Channel: cookie = %#llx\n", cookie); + remain_priv = priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "cancel_remain_on_channel: Wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg(remain_priv, MOAL_IOCTL_WAIT, + MTRUE, &status, NULL, 0, 0)) { + PRINTM(MERROR, + "cancel_remain_on_channel: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + + priv->phandle->remain_on_channel = MFALSE; + if (priv->phandle->cookie) + priv->phandle->cookie = 0; +done: + LEAVE(); + return ret; +} +#endif /* KERNEL_VERSION */ + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +/** + * @brief start sched scan + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param request A pointer to struct cfg80211_sched_scan_request + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_sched_scan_start(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_sched_scan_request *request) +{ + struct ieee80211_channel *chan = NULL; + int i = 0; + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct cfg80211_ssid *ssid = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + t_u8 buf[ETH_ALEN]; +#endif + ENTER(); + +#ifdef UAP_CFG80211 + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + LEAVE(); + return -EFAULT; + } +#endif + + memset(&priv->scan_cfg, 0, sizeof(priv->scan_cfg)); + if (!request) { + PRINTM(MERROR, "Invalid sched_scan req parameter\n"); + LEAVE(); + return -EINVAL; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + PRINTM(MIOCTL, + "%s sched scan: n_ssids=%d n_match_sets=%d n_channels=%d interval=%d ie_len=%d\n", + priv->netdev->name, request->n_ssids, request->n_match_sets, + request->n_channels, request->scan_plans[0].interval, + (int)request->ie_len); +#else + PRINTM(MIOCTL, + "%s sched scan: n_ssids=%d n_match_sets=%d n_channels=%d interval=%d ie_len=%d\n", + priv->netdev->name, request->n_ssids, request->n_match_sets, + request->n_channels, request->interval, (int)request->ie_len); +#endif + /** We have pending scan, start bgscan later */ + if (priv->phandle->scan_pending_on_block) + priv->scan_cfg.start_later = MTRUE; + for (i = 0; i < request->n_match_sets; i++) { + ssid = &request->match_sets[i].ssid; + strncpy(priv->scan_cfg.ssid_list[i].ssid, ssid->ssid, + ssid->ssid_len); + priv->scan_cfg.ssid_list[i].max_len = 0; + PRINTM(MIOCTL, "sched scan: ssid=%s\n", ssid->ssid); + } + /** Add broadcast scan, when n_match_sets = 0 */ + if (!request->n_match_sets) + priv->scan_cfg.ssid_list[0].max_len = 0xff; + for (i = 0; i < MIN(WLAN_BG_SCAN_CHAN_MAX, request->n_channels); i++) { + chan = request->channels[i]; + priv->scan_cfg.chan_list[i].chan_number = chan->hw_value; + priv->scan_cfg.chan_list[i].radio_type = chan->band; + if (chan->flags & + (IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_RADAR)) + priv->scan_cfg.chan_list[i].scan_type = + MLAN_SCAN_TYPE_PASSIVE; + else + priv->scan_cfg.chan_list[i].scan_type = + MLAN_SCAN_TYPE_ACTIVE; + priv->scan_cfg.chan_list[i].scan_time = 0; +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->miracast_mode) + priv->scan_cfg.chan_list[i].scan_time = + priv->phandle->miracast_scan_time; +#endif + } + priv->scan_cfg.chan_per_scan = + MIN(WLAN_BG_SCAN_CHAN_MAX, request->n_channels); + + /** set scan request IES */ + if (request->ie && request->ie_len) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_mgmt_frame_ie( + priv, NULL, 0, NULL, 0, NULL, 0, + (t_u8 *)request->ie, request->ie_len, + MGMT_MASK_PROBE_REQ, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Fail to set sched scan IE\n"); + ret = -EFAULT; + goto done; + } + } else { + /** Clear SCAN IE in Firmware */ + if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, + NULL, 0, NULL, 0, + MGMT_MASK_PROBE_REQ, + MOAL_IOCTL_WAIT); + } + + /* Interval between scan cycles in milliseconds,supplicant set to 10 + * second */ + /* We want to use 30 second for per scan cycle */ + priv->scan_cfg.scan_interval = MIN_BGSCAN_INTERVAL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + if (request->scan_plans[0].interval * 1000 > MIN_BGSCAN_INTERVAL) + priv->scan_cfg.scan_interval = + request->scan_plans[0].interval * 1000; + if (request->n_scan_plans >= 2) { + priv->scan_cfg.config_ees = MTRUE; + priv->scan_cfg.ees_mode = + MBIT(EES_MODE_HIGH) | MBIT(EES_MODE_MID); + priv->scan_cfg.high_period = + request->scan_plans[0].interval * 1000; + priv->scan_cfg.high_period_count = + request->scan_plans[0].iterations; + priv->scan_cfg.mid_period = request->scan_plans[1].interval; + if (request->scan_plans[1].iterations == 0) + priv->scan_cfg.mid_period_count = DEF_REPEAT_COUNT; + else + priv->scan_cfg.mid_period_count = + request->scan_plans[1].iterations; + if (request->n_scan_plans == 3) { + priv->scan_cfg.ees_mode |= MBIT(EES_MODE_LOW); + priv->scan_cfg.low_period = + request->scan_plans[2].interval; + priv->scan_cfg.low_period_count = DEF_REPEAT_COUNT; + } + } +#else + if (request->interval > MIN_BGSCAN_INTERVAL) + priv->scan_cfg.scan_interval = request->interval; +#endif + priv->scan_cfg.repeat_count = DEF_REPEAT_COUNT; + priv->scan_cfg.report_condition = + BG_SCAN_SSID_MATCH | BG_SCAN_WAIT_ALL_CHAN_DONE; + priv->scan_cfg.bss_type = MLAN_BSS_MODE_INFRA; + priv->scan_cfg.action = BG_SCAN_ACT_SET; + priv->scan_cfg.enable = MTRUE; +#ifdef WIFI_DIRECT_SUPPORT + if (priv->phandle->miracast_mode) + priv->scan_cfg.scan_chan_gap = priv->phandle->scan_chan_gap; + else + priv->scan_cfg.scan_chan_gap = 0; +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + if (request->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + PRINTM(MIOCTL, "NL80211_SCAN_FLAG_RANDOM_ADDR is set\n"); + get_random_bytes(buf, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) { + buf[i] &= ~request->mac_addr_mask[i]; + buf[i] |= request->mac_addr[i] & + request->mac_addr_mask[i]; + } + moal_memcpy_ext(priv->phandle, priv->scan_cfg.random_mac, buf, + ETH_ALEN, sizeof(priv->scan_cfg.random_mac)); + } else +#endif + moal_memcpy_ext(priv->phandle, priv->scan_cfg.random_mac, + priv->random_mac, ETH_ALEN, + sizeof(priv->scan_cfg.random_mac)); + + PRINTM(MCMND, "wlan:random_mac " MACSTR "\n", + MAC2STR(priv->scan_cfg.random_mac)); + if (MLAN_STATUS_SUCCESS == + woal_request_bgscan(priv, MOAL_IOCTL_WAIT, &priv->scan_cfg)) { + priv->sched_scanning = MTRUE; + priv->bg_scan_start = MTRUE; + priv->bg_scan_reported = MFALSE; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + priv->bg_scan_reqid = request->reqid; +#endif + } else + ret = -EFAULT; +done: + LEAVE(); + return ret; +} + +/** + * @brief stop sched scan + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_sched_scan_stop(struct wiphy *wiphy, struct net_device *dev +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , + u64 reqid +#endif +) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + ENTER(); + PRINTM(MIOCTL, "sched scan stop\n"); + priv->sched_scanning = MFALSE; + woal_stop_bg_scan(priv, MOAL_NO_WAIT); + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + LEAVE(); + return 0; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +/** + * @brief cfg80211_resume handler + * + * @param wiphy A pointer to wiphy structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_resume(struct wiphy *wiphy) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) && defined(CONFIG_PM) + struct cfg80211_wowlan_wakeup wakeup_report; +#endif + mlan_ds_hs_wakeup_reason wakeup_reason; + int i; + + PRINTM(MCMND, "<--- Enter woal_cfg80211_resume --->\n"); + + if (!priv) { + PRINTM(MERROR, "woal_cfg80211_resume: priv is NULL\n"); + goto done; + } + + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) { + if (handle->priv[i]->last_event & + EVENT_BG_SCAN_REPORT) { + if (handle->priv[i]->sched_scanning) { + woal_inform_bss_from_scan_result( + handle->priv[i], NULL, + MOAL_IOCTL_WAIT); + cfg80211_sched_scan_results( + handle->priv[i]->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , + 0 +#endif + ); + woal_bgscan_stop_event(handle->priv[i]); + handle->priv[i]->last_event = 0; + PRINTM(MIOCTL, + "Report sched scan result in cfg80211 resume\n"); + } + if (!moal_extflg_isset(handle, EXT_HW_TEST) && + handle->priv[i]->roaming_enabled) { + handle->priv[i]->roaming_required = + MTRUE; +#ifdef ANDROID_KERNEL +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + __pm_wakeup_event( + &handle->ws, + ROAMING_WAKE_LOCK_TIMEOUT); +#else + wake_lock_timeout( + &handle->wake_lock, + msecs_to_jiffies( + ROAMING_WAKE_LOCK_TIMEOUT)); +#endif +#endif + wake_up_interruptible( + &handle->reassoc_thread.wait_q); + } + } + } + } + + woal_get_wakeup_reason(priv, &wakeup_reason); + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) + woal_wake_reason_logger(priv, wakeup_reason); +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) && defined(CONFIG_PM) + memset(&wakeup_report, 0, sizeof(struct cfg80211_wowlan_wakeup)); + wakeup_report.pattern_idx = -1; + + switch (wakeup_reason.hs_wakeup_reason) { + case NO_HSWAKEUP_REASON: + break; + case BCAST_DATA_MATCHED: + break; + case MCAST_DATA_MATCHED: + break; + case UCAST_DATA_MATCHED: + break; + case MASKTABLE_EVENT_MATCHED: + break; + case NON_MASKABLE_EVENT_MATCHED: + break; + case NON_MASKABLE_CONDITION_MATCHED: + if (wiphy->wowlan_config && wiphy->wowlan_config->disconnect) + wakeup_report.disconnect = true; + break; + case MAGIC_PATTERN_MATCHED: + if (wiphy->wowlan_config && wiphy->wowlan_config->magic_pkt) + wakeup_report.magic_pkt = true; + if (wiphy->wowlan_config && wiphy->wowlan_config->n_patterns) + wakeup_report.pattern_idx = 1; + break; + case CONTROL_FRAME_MATCHED: + break; + case MANAGEMENT_FRAME_MATCHED: + break; + case GTK_REKEY_FAILURE: + if (wiphy->wowlan_config && + wiphy->wowlan_config->gtk_rekey_failure) + wakeup_report.gtk_rekey_failure = true; + break; + default: + break; + } + + if ((wakeup_reason.hs_wakeup_reason > 0) && + (wakeup_reason.hs_wakeup_reason <= 10)) { + cfg80211_report_wowlan_wakeup(priv->wdev, &wakeup_report, + GFP_KERNEL); + } +#endif + +done: + handle->cfg80211_suspend = MFALSE; + PRINTM(MCMND, "<--- Leave woal_cfg80211_resume --->\n"); + return 0; +} + +/** + * @brief is_wowlan_pattern_supported + * + * @param priv A pointer to moal_private + * @param pat A pointer to wowlan pattern + * @param byte_seq A pointer to byte_seq + * + * @return 1 -- support, 0 -- not support + */ +static t_bool is_wowlan_pattern_supported(moal_private *priv, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + struct cfg80211_pkt_pattern *pat, +#else + struct cfg80211_wowlan_trig_pkt_pattern + *pat, +#endif + s8 *byte_seq) +{ + int j, k, valid_byte_cnt = 0; + t_bool dont_care_byte = MFALSE; + + for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { + for (k = 0; k < 8; k++) { + if (pat->mask[j] & 1 << k) { + moal_memcpy_ext(priv->phandle, + byte_seq + valid_byte_cnt, + &pat->pattern[j * 8 + k], 1, 1); + valid_byte_cnt++; + if (dont_care_byte) + return MFALSE; + } else { + if (valid_byte_cnt) + dont_care_byte = MTRUE; + } + + if (valid_byte_cnt > MAX_NUM_BYTE_SEQ) + return MFALSE; + } + } + + byte_seq[MAX_NUM_BYTE_SEQ] = valid_byte_cnt; + + return MTRUE; +} + +/** + * @brief cfg80211_suspend handler + * + * @param wiphy A pointer to wiphy structure + * @param wow A pointer to cfg80211_wowlan + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_suspend(struct wiphy *wiphy, struct cfg80211_wowlan *wow) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + int i; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_misc_mef_flt_cfg mef_cfg; + mef_entry_t *mef_entry = NULL; + int filt_num = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + t_bool first_pat = MTRUE; +#endif + t_u8 byte_seq[MAX_NUM_BYTE_SEQ + 1]; + const t_u8 ipv4_mc_mac[] = {0x33, 0x33}; + const t_u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + moal_private *priv = woal_get_priv(handle, MLAN_BSS_ROLE_STA); + mlan_ds_hs_cfg hscfg; + + PRINTM(MCMND, "<--- Enter woal_cfg80211_suspend --->\n"); + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) { + if (handle->scan_request) { + PRINTM(MIOCTL, + "Cancel pending scan in woal_cfg80211_suspend\n"); + woal_cancel_scan(handle->priv[i], + MOAL_IOCTL_WAIT); + } + handle->priv[i]->last_event = 0; + } + } + + handle->cfg80211_suspend = MTRUE; + if (!wow) { + PRINTM(MERROR, "None of the WOWLAN triggers enabled\n"); + ret = 0; + goto done; + } + + if (!priv || !priv->media_connected) { + PRINTM(MERROR, + "Can not configure WOWLAN in disconnected state\n"); + ret = 0; + goto done; + } + + PRINTM(MCMND, "wow->n_patterns=%d\n", wow->n_patterns); + PRINTM(MCMND, "wow->any=%d\n", wow->any); + PRINTM(MCMND, "wow->disconnect=%d\n", wow->disconnect); + PRINTM(MCMND, "wow->magic_pkt=%d\n", wow->magic_pkt); + PRINTM(MCMND, "wow->gtk_rekey_failure=%d\n", wow->gtk_rekey_failure); + PRINTM(MCMND, "wow->eap_identity_req=%d\n", wow->eap_identity_req); + PRINTM(MCMND, "wow->four_way_handshake=%d\n", wow->four_way_handshake); + PRINTM(MCMND, "wow->rfkill_release=%d\n", wow->rfkill_release); + + if (!(wow->n_patterns) && !(wow->magic_pkt)) { + PRINTM(MCMND, "No pattern or magic packet configured\n"); + ret = 0; + goto done; + } + + memset(&mef_cfg, 0, sizeof(mef_cfg)); + mef_cfg.mef_act_type = MEF_ACT_WOWLAN; + mef_entry = &mef_cfg.mef_entry; + + mef_entry->mode = MEF_MODE_HOST_SLEEP; + mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST; + + for (i = 0; i < wow->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!is_wowlan_pattern_supported(priv, &wow->patterns[i], + byte_seq)) { + PRINTM(MERROR, "Pattern not supported\n"); + ret = -EOPNOTSUPP; + goto done; + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + if (!wow->patterns[i].pkt_offset) { +#endif + if (!(byte_seq[0] & 0x01) && + (byte_seq[MAX_NUM_BYTE_SEQ] == 1)) { + mef_cfg.criteria |= CRITERIA_UNICAST; + continue; + } else if (is_broadcast_ether_addr(byte_seq)) { + mef_cfg.criteria |= CRITERIA_BROADCAST; + continue; + } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + (byte_seq[MAX_NUM_BYTE_SEQ] == 2)) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + (byte_seq[MAX_NUM_BYTE_SEQ] == 3))) { + mef_cfg.criteria |= CRITERIA_MULTICAST; + continue; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + } + + mef_entry->filter_item[filt_num].fill_flag = + (FILLING_TYPE | FILLING_REPEAT | FILLING_BYTE_SEQ | + FILLING_OFFSET); + mef_entry->filter_item[filt_num].repeat = 1; + mef_entry->filter_item[filt_num].offset = + wow->patterns[i].pkt_offset; + moal_memcpy_ext( + priv->phandle, + mef_entry->filter_item[filt_num].byte_seq, byte_seq, + MAX_NUM_BYTE_SEQ, + sizeof(mef_entry->filter_item[filt_num].byte_seq)); + mef_entry->filter_item[filt_num].num_byte_seq = + byte_seq[MAX_NUM_BYTE_SEQ]; + mef_entry->filter_item[filt_num].type = TYPE_BYTE_EQ; + + if (first_pat) + first_pat = MFALSE; + else + mef_entry->rpn[filt_num] = RPN_TYPE_OR; + + filt_num++; +#endif + } + + if (wow->magic_pkt) { + mef_cfg.criteria |= CRITERIA_UNICAST | CRITERIA_BROADCAST | + CRITERIA_MULTICAST; + mef_entry->filter_item[filt_num].fill_flag = + (FILLING_TYPE | FILLING_REPEAT | FILLING_BYTE_SEQ | + FILLING_OFFSET); + mef_entry->filter_item[filt_num].repeat = 16; + moal_memcpy_ext( + priv->phandle, + mef_entry->filter_item[filt_num].byte_seq, + priv->current_addr, ETH_ALEN, + sizeof(mef_entry->filter_item[filt_num].byte_seq)); + mef_entry->filter_item[filt_num].num_byte_seq = ETH_ALEN; + mef_entry->filter_item[filt_num].offset = 56; + mef_entry->filter_item[filt_num].type = TYPE_BYTE_EQ; + if (filt_num) + mef_entry->rpn[filt_num] = RPN_TYPE_OR; + filt_num++; + mef_entry->filter_item[filt_num].fill_flag = + (FILLING_TYPE | FILLING_REPEAT | FILLING_BYTE_SEQ | + FILLING_OFFSET); + mef_entry->filter_item[filt_num].repeat = 16; + moal_memcpy_ext( + priv->phandle, + mef_entry->filter_item[filt_num].byte_seq, + priv->current_addr, ETH_ALEN, + sizeof(mef_entry->filter_item[filt_num].byte_seq)); + mef_entry->filter_item[filt_num].num_byte_seq = ETH_ALEN; + mef_entry->filter_item[filt_num].offset = 28; + mef_entry->filter_item[filt_num].type = TYPE_BYTE_EQ; + if (filt_num) + mef_entry->rpn[filt_num] = RPN_TYPE_OR; + filt_num++; + } + + mef_entry->filter_num = filt_num; + + if (!mef_cfg.criteria) + mef_cfg.criteria = CRITERIA_BROADCAST | CRITERIA_UNICAST | + CRITERIA_MULTICAST; + + status = woal_set_get_wowlan_config(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &mef_cfg); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "woal_set_get_wowlan_config fail!\n"); + ret = -EFAULT; + goto done; + } + + memset(&hscfg, 0, sizeof(mlan_ds_hs_cfg)); + status = woal_set_get_hs_params(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, + &hscfg); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Fail to get HS parameter in woal_cfg80211_suspend: 0x%x 0x%x 0x%x\n", + hscfg.conditions, hscfg.gap, hscfg.gpio); + ret = -EFAULT; + goto done; + } + hscfg.is_invoke_hostcmd = MFALSE; + if (wow->n_patterns || wow->magic_pkt) + hscfg.conditions = 0; + status = woal_set_get_hs_params(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &hscfg); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Fail to set HS parameter in woal_cfg80211_suspend: 0x%x 0x%x 0x%x\n", + hscfg.conditions, hscfg.gap, hscfg.gpio); + ret = -EFAULT; + goto done; + } + +done: + PRINTM(MCMND, "<--- Leave woal_cfg80211_suspend --->\n"); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) +static void woal_cfg80211_set_wakeup(struct wiphy *wiphy, bool enabled) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + + device_set_wakeup_enable(handle->hotplug_device, enabled); +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +/** + * @brief change station info + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac A pointer to peer mac + * @param params station parameters + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_change_station(struct wiphy *wiphy, + struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params) +{ + int ret = 0; + + ENTER(); + + /**do nothing*/ + + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#ifdef UAP_SUPPORT +/** + * @brief add station + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac A pointer to peer mac + * @param params station parameters + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_add_station(struct wiphy *wiphy, + struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_parameters *params) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + + ENTER(); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +#ifdef UAP_SUPPORT + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME) && + (priv->bss_role == MLAN_BSS_ROLE_UAP)) { + ret = woal_cfg80211_uap_add_station(wiphy, dev, (u8 *)mac, + params); + LEAVE(); + return ret; + } +#endif +#endif + LEAVE(); + return ret; +} +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +/** + * @brief Update ft ie for Fast BSS Transition + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param ftie A pointer to cfg80211_update_ft_ies_params structure + * + * @return 0 success , other failure + */ +int woal_cfg80211_update_ft_ies(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_update_ft_ies_params *ftie) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + IEEEtypes_MobilityDomain_t *md_ie = NULL; + int ret = 0; + mlan_ds_misc_assoc_rsp assoc_rsp; + IEEEtypes_AssocRsp_t *passoc_rsp = NULL; + mlan_bss_info bss_info; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + struct cfg80211_roam_info roam_info = {}; +#endif + + ENTER(); + + if (!ftie) { + LEAVE(); + return ret; + } +#ifdef MLAN_64BIT + PRINTM(MINFO, "==>woal_cfg80211_update_ft_ies %lx \n", ftie->ie_len); +#else + PRINTM(MINFO, "==>woal_cfg80211_update_ft_ies %x \n", ftie->ie_len); +#endif + md_ie = (IEEEtypes_MobilityDomain_t *)woal_parse_ie_tlv( + ftie->ie, ftie->ie_len, MOBILITY_DOMAIN); + if (!md_ie) { + PRINTM(MERROR, "No Mobility domain IE\n"); + LEAVE(); + return ret; + } + priv->ft_cap = md_ie->ft_cap; + if (priv->ft_ie_len) { + priv->pre_ft_ie_len = priv->ft_ie_len; + moal_memcpy_ext(priv->phandle, priv->pre_ft_ie, priv->ft_ie, + priv->ft_ie_len, MAX_IE_SIZE); + } + memset(priv->ft_ie, 0, MAX_IE_SIZE); + moal_memcpy_ext(priv->phandle, priv->ft_ie, ftie->ie, + MIN(ftie->ie_len, MAX_IE_SIZE), sizeof(priv->ft_ie)); + priv->ft_ie_len = ftie->ie_len; + priv->ft_md = ftie->md; + + if (!priv->ft_pre_connect) { + LEAVE(); + return ret; + } + /* check if is different AP */ + if (!memcmp(&priv->target_ap_bssid, priv->cfg_bssid, + MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MMSG, "This is the same AP, no Fast bss transition\n"); + priv->ft_pre_connect = MFALSE; + priv->ft_ie_len = 0; + LEAVE(); + return 0; + } + + /* start fast BSS transition to target AP */ + priv->assoc_status = 0; + priv->sme_current.bssid = priv->conn_bssid; + moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid, + &priv->target_ap_bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(priv->conn_bssid)); + memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp)); + ret = woal_cfg80211_assoc(priv, (void *)&priv->sme_current, + MOAL_IOCTL_WAIT, &assoc_rsp); + + if ((priv->ft_cap & MBIT(0)) || priv->ft_roaming_triggered_by_driver) { + if (!ret) { + woal_inform_bss_from_scan_result(priv, NULL, + MOAL_IOCTL_WAIT); + passoc_rsp = (IEEEtypes_AssocRsp_t *) + assoc_rsp.assoc_resp_buf; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + roam_info.bssid = priv->cfg_bssid; + roam_info.req_ie = priv->sme_current.ie; + roam_info.req_ie_len = priv->sme_current.ie_len; + roam_info.resp_ie = passoc_rsp->ie_buffer; + roam_info.resp_ie_len = assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE; + cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid, + priv->sme_current.ie, + priv->sme_current.ie_len, + passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, + GFP_KERNEL); +#else + cfg80211_roamed(priv->netdev, priv->cfg_bssid, + priv->sme_current.ie, + priv->sme_current.ie_len, + passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, + GFP_KERNEL); +#endif +#endif + PRINTM(MMSG, + "Fast BSS transition to bssid " MACSTR + " successfully\n", + MAC2STR(priv->cfg_bssid)); + } else { + PRINTM(MMSG, + "Fast BSS transition failed, keep connect to " MACSTR + " \n", + MAC2STR(priv->cfg_bssid)); + moal_memcpy_ext(priv->phandle, + (void *)priv->sme_current.bssid, + &priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(priv->conn_bssid)); + priv->ft_ie_len = priv->pre_ft_ie_len; + moal_memcpy_ext(priv->phandle, priv->ft_ie, + priv->pre_ft_ie, priv->pre_ft_ie_len, + MAX_IE_SIZE); + } + priv->ft_roaming_triggered_by_driver = MFALSE; + + } else { + if (!ret) { + memset(&assoc_rsp, 0, sizeof(mlan_ds_misc_assoc_rsp)); + woal_get_assoc_rsp(priv, &assoc_rsp, MOAL_IOCTL_WAIT); + passoc_rsp = (IEEEtypes_AssocRsp_t *) + assoc_rsp.assoc_resp_buf; + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, passoc_rsp->ie_buffer, + assoc_rsp.assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, + WLAN_STATUS_SUCCESS, + GFP_KERNEL); + PRINTM(MMSG, + "wlan: Fast Bss transition to bssid " MACSTR + " successfully\n", + MAC2STR(priv->cfg_bssid)); + + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + priv->channel = bss_info.bss_chan; + } else { + PRINTM(MMSG, + "wlan: Failed to connect to bssid " MACSTR "\n", + MAC2STR(priv->target_ap_bssid)); + cfg80211_connect_result(priv->netdev, + priv->target_ap_bssid, NULL, 0, + NULL, 0, + woal_get_assoc_status(priv), + GFP_KERNEL); + moal_memcpy_ext(priv->phandle, + (void *)priv->sme_current.bssid, + &priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(priv->conn_bssid)); + memset(priv->target_ap_bssid, 0, ETH_ALEN); + priv->ft_ie_len = priv->pre_ft_ie_len; + moal_memcpy_ext(priv->phandle, priv->ft_ie, + priv->pre_ft_ie, priv->pre_ft_ie_len, + MAX_IE_SIZE); + // priv->ft_ie_len = 0; + } + } + + priv->ft_pre_connect = MFALSE; + LEAVE(); + return 0; +} +#endif + +/** + * @brief Save connect parameters for roaming + * + * @param priv A pointer to moal_private + * @param sme A pointer to cfg80211_connect_params structure + */ +void woal_save_conn_params(moal_private *priv, + struct cfg80211_connect_params *sme) +{ + ENTER(); + woal_clear_conn_params(priv); + moal_memcpy_ext(priv->phandle, &priv->sme_current, sme, + sizeof(struct cfg80211_connect_params), + sizeof(priv->sme_current)); + if (sme->channel) { + priv->sme_current.channel = &priv->conn_chan; + moal_memcpy_ext(priv->phandle, priv->sme_current.channel, + sme->channel, sizeof(struct ieee80211_channel), + sizeof(priv->conn_chan)); + } + if (sme->bssid) { + priv->sme_current.bssid = priv->conn_bssid; + moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid, + sme->bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(priv->conn_bssid)); + } + if (sme->ssid && sme->ssid_len) { + priv->sme_current.ssid = priv->conn_ssid; + memset(priv->conn_ssid, 0, MLAN_MAX_SSID_LENGTH); + moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ssid, + sme->ssid, sme->ssid_len, + sizeof(priv->conn_ssid)); + } + if (sme->ie && sme->ie_len) { + priv->sme_current.ie = kzalloc(sme->ie_len, GFP_KERNEL); + moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.ie, + sme->ie, sme->ie_len, sme->ie_len); + } + if (sme->key && sme->key_len && (sme->key_len <= MAX_WEP_KEY_SIZE)) { + priv->sme_current.key = priv->conn_wep_key; + moal_memcpy_ext(priv->phandle, (t_u8 *)priv->sme_current.key, + sme->key, sme->key_len, + sizeof(priv->conn_wep_key)); + } +} + +/** + * @brief clear connect parameters for ing + * + * @param priv A pointer to moal_private + */ +void woal_clear_conn_params(moal_private *priv) +{ + ENTER(); + if (priv->sme_current.ie_len) + kfree(priv->sme_current.ie); + memset(&priv->sme_current, 0, sizeof(struct cfg80211_connect_params)); + priv->roaming_required = MFALSE; + LEAVE(); +} + +/** + * @brief Build new roaming connect ie for okc + * + * @param priv A pointer to moal_private + * @param entry A pointer to pmksa_entry + **/ +int woal_update_okc_roaming_ie(moal_private *priv, struct pmksa_entry *entry) +{ + struct cfg80211_connect_params *sme = &priv->sme_current; + int ret = MLAN_STATUS_SUCCESS; + const t_u8 *sme_pos, *sme_ptr; + t_u8 *okc_ie_pos; + t_u8 id, ie_len; + int left_len; + + ENTER(); + + if (!sme->ie || !sme->ie_len) { + PRINTM(MERROR, "No connect ie saved in driver\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (!entry) { + PRINTM(MERROR, "No roaming ap pmkid\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (!priv->okc_roaming_ie) { + int okc_ie_len = sme->ie_len + sizeof(t_u16) + PMKID_LEN; + + /** Alloc new buffer for okc roaming ie */ + priv->okc_roaming_ie = kzalloc(okc_ie_len, GFP_KERNEL); + if (!priv->okc_roaming_ie) { + PRINTM(MERROR, "Fail to allocate assoc req ie\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + } + + /* Build OKC RSN IE with PMKID list + * Format of RSN IE: length(bytes) and container + * | 1| 1 | 2 | 4 | 2 | + * |id|len|version|group data cipher suite|pairwise cipher suite count| + * | 4 * m | 2 | 4 * n | 2 | + * |pairwise cipher suite list|AKM suite count|AKM suite list|RSN Cap | + * | 2 | 16 * s | 4 | + * |PMKIDCount|PMKID List|Group Management Cipher Suite| + */ +#define PAIRWISE_CIPHER_COUNT_OFFSET 8 +#define AKM_SUITE_COUNT_OFFSET(n) (10 + (n)*4) +#define PMKID_COUNT_OFFSET(n) (14 + (n)*4) + + sme_pos = sme->ie; + left_len = sme->ie_len; + okc_ie_pos = priv->okc_roaming_ie; + priv->okc_ie_len = 0; + + while (left_len >= 2) { + id = *sme_pos; + ie_len = *(sme_pos + 1); + if ((ie_len + 2) > left_len) { + PRINTM(MERROR, "Invalid ie len %d\n", ie_len); + ret = MLAN_STATUS_FAILURE; + goto done; + } + + if (id == RSN_IE) { + t_u16 pairwise_count, akm_count; + t_u8 *rsn_ie_len; + int rsn_offset; + + pairwise_count = + *(t_u16 *)(sme_pos + + PAIRWISE_CIPHER_COUNT_OFFSET); + akm_count = + *(t_u16 *)(sme_pos + AKM_SUITE_COUNT_OFFSET( + pairwise_count)); + rsn_offset = + PMKID_COUNT_OFFSET(pairwise_count + akm_count); + sme_ptr = (t_u8 *)(sme_pos + rsn_offset); + + moal_memcpy_ext(priv->phandle, okc_ie_pos, sme_pos, + rsn_offset, rsn_offset); + rsn_ie_len = okc_ie_pos + 1; + okc_ie_pos += rsn_offset; + *(t_u16 *)okc_ie_pos = 1; + okc_ie_pos += sizeof(t_u16); + moal_memcpy_ext(priv->phandle, okc_ie_pos, entry->pmkid, + PMKID_LEN, PMKID_LEN); + okc_ie_pos += PMKID_LEN; + priv->okc_ie_len += + rsn_offset + sizeof(t_u16) + PMKID_LEN; + *rsn_ie_len = + rsn_offset - 2 + sizeof(t_u16) + PMKID_LEN; + + if ((ie_len + 2) > rsn_offset) { + /** Previous conn ie include pmkid list */ + u16 pmkid_count = *(t_u16 *)sme_ptr; + rsn_offset += (sizeof(t_u16) + + PMKID_LEN * pmkid_count); + if ((ie_len + 2) > rsn_offset) { + sme_ptr += (sizeof(t_u16) + + PMKID_LEN * pmkid_count); + moal_memcpy_ext( + priv->phandle, okc_ie_pos, + sme_ptr, + (ie_len + 2 - rsn_offset), + (ie_len + 2 - rsn_offset)); + okc_ie_pos += (ie_len + 2 - rsn_offset); + priv->okc_ie_len += + (ie_len + 2 - rsn_offset); + *rsn_ie_len += + (ie_len + 2 - rsn_offset); + } + } + } else { + moal_memcpy_ext(priv->phandle, okc_ie_pos, sme_pos, + ie_len + 2, ie_len + 2); + okc_ie_pos += ie_len + 2; + priv->okc_ie_len += ie_len + 2; + } + + sme_pos += (ie_len + 2); + left_len -= (ie_len + 2); + } + +done: + if (ret != MLAN_STATUS_SUCCESS) { + if (priv->okc_roaming_ie) { + kfree(priv->okc_roaming_ie); + priv->okc_roaming_ie = NULL; + priv->okc_ie_len = 0; + } + } + + LEAVE(); + return ret; +} + +/** + * @brief Start roaming: driver handle roaming + * + * @param priv A pointer to moal_private structure + * + * @return N/A + */ +void woal_start_roaming(moal_private *priv) +{ + mlan_ds_get_signal signal; + mlan_ssid_bssid ssid_bssid; + char rssi_low[10]; + int ret = 0; + mlan_ds_misc_assoc_rsp *assoc_rsp; + IEEEtypes_AssocRsp_t *passoc_rsp = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + struct cfg80211_roam_info roam_info = {}; +#endif + + ENTER(); + if (priv->ft_roaming_triggered_by_driver) { + PRINTM(MIOCTL, "FT roaming is in processing ...... \n"); + LEAVE(); + return; + } + + if (priv->last_event & EVENT_BG_SCAN_REPORT) { + woal_inform_bss_from_scan_result(priv, NULL, MOAL_IOCTL_WAIT); + PRINTM(MIOCTL, "Report bgscan result\n"); + } + if (priv->media_connected == MFALSE || !priv->sme_current.ssid_len) { + PRINTM(MIOCTL, "Not connected, ignore roaming\n"); + LEAVE(); + return; + } + + /* Get signal information from the firmware */ + memset(&signal, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, &signal)) { + PRINTM(MERROR, "Error getting signal information\n"); + ret = -EFAULT; + goto done; + } + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + ssid_bssid.ssid.ssid_len = priv->sme_current.ssid_len; + moal_memcpy_ext(priv->phandle, ssid_bssid.ssid.ssid, + priv->sme_current.ssid, priv->sme_current.ssid_len, + sizeof(ssid_bssid.ssid.ssid)); + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) { + PRINTM(MIOCTL, "Can not find better network\n"); + ret = -EFAULT; + goto done; + } + /* check if we found different AP */ + if (!memcmp(&ssid_bssid.bssid, priv->cfg_bssid, MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MIOCTL, "This is the same AP, no roaming\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "Find AP: bssid=" MACSTR ", signal=%d\n", + MAC2STR(ssid_bssid.bssid), ssid_bssid.rssi); + /* check signal */ + if (!(priv->last_event & EVENT_PRE_BCN_LOST)) { + if ((abs(signal.bcn_rssi_avg) - abs(ssid_bssid.rssi)) < + DELTA_RSSI) { + PRINTM(MERROR, "New AP's signal is not good too.\n"); + ret = -EFAULT; + goto done; + } + } + /**check if need start FT Roaming*/ + if (priv->ft_ie_len && (priv->ft_md == ssid_bssid.ft_md) && + (priv->ft_cap == ssid_bssid.ft_cap)) { + priv->ft_roaming_triggered_by_driver = MTRUE; + woal_start_ft_roaming(priv, &ssid_bssid); + goto done; + } + /* start roaming to new AP */ + priv->sme_current.bssid = priv->conn_bssid; + moal_memcpy_ext(priv->phandle, (void *)priv->sme_current.bssid, + &ssid_bssid.bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(priv->conn_bssid)); + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) { + /** Check if current roaming support OKC offload roaming */ + if (priv->sme_current.crypto.n_akm_suites && + priv->sme_current.crypto.akm_suites[0] == + WLAN_AKM_SUITE_8021X) { + struct pmksa_entry *entry = NULL; + + /** Get OKC PMK Cache entry + * Firstly try to get pmksa from cfg80211 + */ + priv->wait_target_ap_pmkid = MTRUE; + cfg80211_pmksa_candidate_notify(priv->netdev, 0, + priv->sme_current.bssid, + MTRUE, GFP_ATOMIC); + if (wait_event_interruptible_timeout( + priv->okc_wait_q, + !priv->wait_target_ap_pmkid, + OKC_WAIT_TARGET_PMKSA_TIMEOUT)) { + PRINTM(MIOCTL, "OKC Roaming is ready\n"); + entry = priv->target_ap_pmksa; + } else { + /** Try to get pmksa from pmksa list */ + priv->wait_target_ap_pmkid = MFALSE; + entry = woal_get_pmksa_entry( + priv, priv->sme_current.bssid); + } + /** Build okc roaming ie */ + woal_update_okc_roaming_ie(priv, entry); + priv->target_ap_pmksa = NULL; + } + } +#endif +#endif + assoc_rsp = kzalloc(sizeof(mlan_ds_misc_assoc_rsp), GFP_ATOMIC); + if (!assoc_rsp) { + PRINTM(MERROR, "Failed to allocate memory for assoc_rsp\n"); + ret = -ENOMEM; + goto done; + } + ret = woal_cfg80211_assoc(priv, (void *)&priv->sme_current, + MOAL_IOCTL_WAIT, assoc_rsp); + if (!ret) { + const t_u8 *ie; + int ie_len; + + woal_inform_bss_from_scan_result(priv, NULL, MOAL_IOCTL_WAIT); + passoc_rsp = (IEEEtypes_AssocRsp_t *)assoc_rsp->assoc_resp_buf; + + /** Update connect ie in roam event */ + ie = priv->sme_current.ie; + ie_len = priv->sme_current.ie_len; +#ifdef STA_CFG80211 + if (IS_STA_CFG80211(priv->phandle->params.cfg80211_wext)) { + /** Check if current roaming support OKC offload roaming + */ + if (priv->sme_current.crypto.n_akm_suites && + priv->sme_current.crypto.akm_suites[0] == + WLAN_AKM_SUITE_8021X) { + if (priv->okc_roaming_ie && priv->okc_ie_len) { + ie = priv->okc_roaming_ie; + ie_len = priv->okc_ie_len; + } + } + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + roam_info.bssid = priv->cfg_bssid; + roam_info.req_ie = ie; + roam_info.req_ie_len = ie_len; + roam_info.resp_ie = passoc_rsp->ie_buffer; + roam_info.resp_ie_len = + assoc_rsp->assoc_resp_len - ASSOC_RESP_FIXED_SIZE; + cfg80211_roamed(priv->netdev, &roam_info, GFP_KERNEL); +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + cfg80211_roamed(priv->netdev, NULL, priv->cfg_bssid, ie, ie_len, + passoc_rsp->ie_buffer, + assoc_rsp->assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, + GFP_KERNEL); +#else + cfg80211_roamed(priv->netdev, priv->cfg_bssid, ie, ie_len, + passoc_rsp->ie_buffer, + assoc_rsp->assoc_resp_len - + ASSOC_RESP_FIXED_SIZE, + GFP_KERNEL); +#endif +#endif + PRINTM(MMSG, "Roamed to bssid " MACSTR " successfully\n", + MAC2STR(priv->cfg_bssid)); + } else { + PRINTM(MIOCTL, "Roaming to bssid " MACSTR " failed\n", + MAC2STR(ssid_bssid.bssid)); + } + kfree(assoc_rsp); +done: + /* config rssi low threshold again */ + priv->last_event = 0; + priv->rssi_low = DEFAULT_RSSI_LOW_THRESHOLD; + sprintf(rssi_low, "%d", priv->rssi_low); + woal_set_rssi_low_threshold(priv, rssi_low, MOAL_IOCTL_WAIT); + LEAVE(); + return; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +#ifdef UAP_SUPPORT +/** + * @brief add uap station + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac A pointer to peer mac + * @param params station parameters + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_uap_add_station(struct wiphy *wiphy, struct net_device *dev, + u8 *mac, struct station_parameters *params) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_ioctl_req *req = NULL; + t_u32 req_len = 0; + mlan_ds_bss *bss = NULL; + t_u8 *pos; + t_u8 qosinfo; + MrvlIEtypes_Data_t *tlv; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + MrvlExtIEtypes_Data_t *ext_tlv; +#endif + mlan_status status; + int ret = 0; + + ENTER(); + + req_len = sizeof(mlan_ds_bss); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + if (params->ext_capab_len) + req_len += sizeof(MrvlIEtypesHeader_t) + params->ext_capab_len; +#endif + if (params->supported_rates_len) + req_len += sizeof(MrvlIEtypesHeader_t) + + params->supported_rates_len; + if (params->uapsd_queues || params->max_sp) + req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(qosinfo); + if (params->ht_capa) + req_len += sizeof(MrvlIEtypesHeader_t) + + sizeof(struct ieee80211_ht_cap); + if (params->vht_capa) + req_len += sizeof(MrvlIEtypesHeader_t) + + sizeof(struct ieee80211_vht_cap); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (params->opmode_notif_used) + req_len += sizeof(MrvlIEtypesHeader_t) + sizeof(u8); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + if (params->he_capa_len) + req_len += sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len; +#endif + req = woal_alloc_mlan_ioctl_req(req_len); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_ADD_STATION; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + bss->param.sta_info.listen_interval = params->listen_interval; + bss->param.sta_info.aid = params->aid; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + bss->param.sta_info.cap_info = params->capability; +#else + bss->param.sta_info.cap_info = 0; +#endif + bss->param.sta_info.tlv_len = 0; + bss->param.sta_info.sta_flags = params->sta_flags_set; + moal_memcpy_ext(priv->phandle, bss->param.sta_info.peer_mac, mac, + MLAN_MAC_ADDR_LENGTH, + sizeof(bss->param.sta_info.peer_mac)); + PRINTM(MMSG, "wlan: UAP/GO add peer station, address =" MACSTR "\n", + MAC2STR(mac)); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + PRINTM(MCMND, + "sta_flags=0x%x listen_interval=%d aid=%d cap_info=0x%x\n", + params->sta_flags_set, params->listen_interval, params->aid, + params->capability); +#else + PRINTM(MCMND, "sta_flags=0x%x listen_interval=%d aid=%d\n", + params->sta_flags_set, params->listen_interval, params->aid); +#endif + pos = &bss->param.sta_info.tlv[0]; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + if (params->ext_capab_len) { + tlv = (MrvlIEtypes_Data_t *)pos; + tlv->header.type = EXT_CAPABILITY; + tlv->header.len = params->ext_capab_len; + moal_memcpy_ext(priv->phandle, tlv->data, params->ext_capab, + tlv->header.len, tlv->header.len); + pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + bss->param.sta_info.tlv_len += + sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + tlv = (MrvlIEtypes_Data_t *)pos; + } +#endif + if (params->supported_rates_len) { + tlv = (MrvlIEtypes_Data_t *)pos; + tlv->header.type = SUPPORTED_RATES; + tlv->header.len = params->supported_rates_len; + moal_memcpy_ext(priv->phandle, tlv->data, + params->supported_rates, tlv->header.len, + tlv->header.len); + pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + bss->param.sta_info.tlv_len += + sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + tlv = (MrvlIEtypes_Data_t *)pos; + } + if (params->uapsd_queues || params->max_sp) { + tlv = (MrvlIEtypes_Data_t *)pos; + tlv->header.type = QOS_INFO; + tlv->header.len = sizeof(qosinfo); + qosinfo = params->uapsd_queues | (params->max_sp << 5); + moal_memcpy_ext(priv->phandle, tlv->data, &qosinfo, + tlv->header.len, tlv->header.len); + pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + bss->param.sta_info.tlv_len += + sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + tlv = (MrvlIEtypes_Data_t *)pos; + } + if (params->ht_capa) { + tlv = (MrvlIEtypes_Data_t *)pos; + tlv->header.type = HT_CAPABILITY; + tlv->header.len = sizeof(struct ieee80211_ht_cap); + moal_memcpy_ext(priv->phandle, tlv->data, params->ht_capa, + tlv->header.len, tlv->header.len); + pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + bss->param.sta_info.tlv_len += + sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + tlv = (MrvlIEtypes_Data_t *)pos; + } + if (params->vht_capa) { + tlv = (MrvlIEtypes_Data_t *)pos; + tlv->header.type = VHT_CAPABILITY; + tlv->header.len = sizeof(struct ieee80211_vht_cap); + moal_memcpy_ext(priv->phandle, tlv->data, params->vht_capa, + tlv->header.len, tlv->header.len); + pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + bss->param.sta_info.tlv_len += + sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + tlv = (MrvlIEtypes_Data_t *)pos; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (params->opmode_notif_used) { + tlv = (MrvlIEtypes_Data_t *)pos; + tlv->header.type = OPER_MODE_NTF; + tlv->header.len = sizeof(u8); + moal_memcpy_ext(priv->phandle, tlv->data, ¶ms->opmode_notif, + tlv->header.len, tlv->header.len); + pos += sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + bss->param.sta_info.tlv_len += + sizeof(MrvlIEtypesHeader_t) + tlv->header.len; + tlv = (MrvlIEtypes_Data_t *)pos; + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + if (params->he_capa_len) { + ext_tlv = (MrvlExtIEtypes_Data_t *)pos; + ext_tlv->header.type = EXTENSION; + ext_tlv->header.len = params->he_capa_len + sizeof(u8); + ext_tlv->header.ext_id = HE_CAPABILITY; + moal_memcpy_ext(priv->phandle, ext_tlv->data, + (u8 *)params->he_capa, params->he_capa_len, + params->he_capa_len); + pos += sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len; + bss->param.sta_info.tlv_len += + sizeof(MrvlExtIEtypesHeader_t) + params->he_capa_len; + tlv = (MrvlIEtypes_Data_t *)pos; + } +#endif + DBG_HEXDUMP(MCMD_D, "sta tlv", &bss->param.sta_info.tlv[0], + bss->param.sta_info.tlv_len); + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_PENDING) + kfree(req); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function is probe client handle. + * + * @param wiphy A pointer to wiphy. + * + * @param dev A pointer to net_device + * + * @param peer A pointer to peer + * + * @param cookie A pointer to cookie + * + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_probe_client(struct wiphy *wiphy, + struct net_device *dev, const u8 *peer, + u64 *cookie) +{ + return -1; +} +#endif + +/** + * @brief Sends deauth packet to kernel + * + * @param priv A pointer to moal_private struct + * @param sa A pointer to source address + * @param reason_code disconnect reason code + * @return N/A + */ +void woal_host_mlme_disconnect(moal_private *priv, u16 reason_code, u8 *sa) +{ + t_u8 broadcast_addr[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + t_u8 frame_buf[26]; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)frame_buf; + ENTER(); + + mgmt->frame_control = IEEE80211_STYPE_DEAUTH; + mgmt->duration = 0; + mgmt->seq_ctrl = 0; + mgmt->u.deauth.reason_code = reason_code; + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + moal_memcpy_ext(priv->phandle, mgmt->da, broadcast_addr, + ETH_ALEN, sizeof(mgmt->da)); + moal_memcpy_ext(priv->phandle, mgmt->sa, + priv->sme_current.bssid, ETH_ALEN, + sizeof(mgmt->sa)); + moal_memcpy_ext(priv->phandle, mgmt->bssid, priv->cfg_bssid, + ETH_ALEN, sizeof(mgmt->bssid)); + priv->host_mlme = MFALSE; + priv->auth_flag = 0; + } else { + moal_memcpy_ext(priv->phandle, mgmt->da, priv->current_addr, + ETH_ALEN, sizeof(mgmt->da)); + moal_memcpy_ext(priv->phandle, mgmt->sa, sa, ETH_ALEN, + sizeof(mgmt->sa)); + moal_memcpy_ext(priv->phandle, mgmt->bssid, priv->current_addr, + ETH_ALEN, sizeof(mgmt->bssid)); + PRINTM(MMSG, + "wlan: hostmlme notify deauth station " MACSTR "\n", + MAC2STR(sa)); + } + + if (GET_BSS_ROLE(priv) != MLAN_BSS_ROLE_UAP) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + mutex_lock(&priv->wdev->mtx); + cfg80211_rx_mlme_mgmt(priv->netdev, frame_buf, 26); + mutex_unlock(&priv->wdev->mtx); +#else + cfg80211_send_deauth(priv->netdev, frame_buf, 26); +#endif + + } else { + int freq = ieee80211_channel_to_frequency( + priv->channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + (priv->channel <= 14 ? IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ) +#endif + ); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + cfg80211_rx_mgmt( +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + priv->wdev, +#else + priv->netdev, +#endif + freq, 0, frame_buf, 26 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + , + 0 +#endif +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 18, 0) + , + GFP_ATOMIC +#endif + ); +#else + cfg80211_rx_mgmt(priv->netdev, freq, frame_buf, 26, GFP_ATOMIC); +#endif + } + + LEAVE(); + return; +} +#endif + +/** + * @brief Register the device with cfg80211 + * + * @param dev A pointer to net_device structure + * @param bss_type BSS type + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_register_sta_cfg80211(struct net_device *dev, t_u8 bss_type) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct wireless_dev *wdev = NULL; + int psmode = 0; + + ENTER(); + + wdev = (struct wireless_dev *)&priv->w_dev; + memset(wdev, 0, sizeof(struct wireless_dev)); + wdev->wiphy = priv->phandle->wiphy; + if (!wdev->wiphy) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (bss_type == MLAN_BSS_TYPE_STA) { + wdev->iftype = NL80211_IFTYPE_STATION; + priv->roaming_enabled = MFALSE; + priv->roaming_required = MFALSE; + } +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + wdev->iftype = NL80211_IFTYPE_STATION; +#endif +#endif + dev_net_set(dev, wiphy_net(wdev->wiphy)); + dev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); + priv->wdev = wdev; + /* Get IEEE power save mode */ + if (MLAN_STATUS_SUCCESS == woal_set_get_power_mgmt(priv, MLAN_ACT_GET, + &psmode, 0, + MOAL_IOCTL_WAIT)) { + /* Save the IEEE power save mode to wiphy, because after + * warmreset wiphy power save should be updated instead + * of using the last saved configuration */ + if (psmode) + priv->wdev->ps = MTRUE; + else + priv->wdev->ps = MFALSE; + } + LEAVE(); + return ret; +} + +/** + * @brief Initialize the wiphy + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_cfg80211_init_wiphy(moal_private *priv, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + int retry_count, rts_thr, frag_thr; + struct wiphy *wiphy = NULL; + mlan_ioctl_req *req = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) + mlan_ds_radio_cfg *radio = NULL; +#endif + pmlan_ds_11n_cfg cfg_11n = NULL; + t_u32 hw_dev_cap; +#ifdef UAP_SUPPORT + pmlan_uap_bss_param sys_cfg = NULL; +#endif + int mcs_supp = 0; + + ENTER(); + wiphy = priv->phandle->wiphy; + /* Get 11n tx parameters from MLAN */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_HTCAP_CFG; + req->req_id = MLAN_IOCTL_11N_CFG; + req->action = MLAN_ACT_GET; + cfg_11n->param.htcap_cfg.hw_cap_req = MTRUE; + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + hw_dev_cap = cfg_11n->param.htcap_cfg.htcap; + + /* Get supported MCS sets */ + memset(req->pbuf, 0, sizeof(mlan_ds_11n_cfg)); + cfg_11n->sub_command = MLAN_OID_11N_CFG_SUPPORTED_MCS_SET; + req->req_id = MLAN_IOCTL_11N_CFG; + req->action = MLAN_ACT_GET; + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* Initialize parameters for 2GHz and 5GHz bands */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + if (IS_CARD9098(priv->phandle->card_type) || + IS_CARD9097(priv->phandle->card_type)) { + mcs_supp = priv->phandle->params.antcfg & 0xf; + if (mcs_supp != 3 && mcs_supp != 0) + cfg_11n->param.supported_mcs_set[1] = 0; + cfg_11n->param.supported_mcs_set[4] = 0; + } + woal_cfg80211_setup_ht_cap( + &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, hw_dev_cap, + cfg_11n->param.supported_mcs_set); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + woal_cfg80211_setup_he_cap(priv, + wiphy->bands[IEEE80211_BAND_2GHZ]); +#endif + } + /* For 2.4G band only card, this shouldn't be set */ + if (wiphy->bands[IEEE80211_BAND_5GHZ]) { + if (IS_CARD9098(priv->phandle->card_type) || + IS_CARD9097(priv->phandle->card_type)) { + mcs_supp = (priv->phandle->params.antcfg & 0xf00) >> 8; + if (mcs_supp != 3 && mcs_supp != 0) + cfg_11n->param.supported_mcs_set[1] = 0; + } + woal_cfg80211_setup_ht_cap( + &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, hw_dev_cap, + cfg_11n->param.supported_mcs_set); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + woal_cfg80211_setup_vht_cap( + priv, &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + woal_cfg80211_setup_he_cap(priv, + wiphy->bands[IEEE80211_BAND_5GHZ]); +#endif + } + kfree(req); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) + /* Get antenna modes */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_GET; + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + /* Set available antennas to wiphy */ + wiphy->available_antennas_tx = radio->param.ant_cfg.tx_antenna; + wiphy->available_antennas_rx = radio->param.ant_cfg.rx_antenna; +#endif /* CFG80211_VERSION_CODE */ + + /* Set retry limit count to wiphy */ + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_retry(priv, MLAN_ACT_GET, wait_option, + &retry_count)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + } +#ifdef UAP_SUPPORT + else { + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, + "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = MLAN_STATUS_FAILURE; + kfree(sys_cfg); + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_set_get_sys_config(priv, MLAN_ACT_GET, wait_option, + sys_cfg)) { + ret = MLAN_STATUS_FAILURE; + kfree(sys_cfg); + goto done; + } + retry_count = sys_cfg->retry_limit; + kfree(sys_cfg); + } +#endif + wiphy->retry_long = (t_u8)retry_count; + wiphy->retry_short = (t_u8)retry_count; + wiphy->max_scan_ie_len = MAX_IE_SIZE; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) + wiphy->mgmt_stypes = ieee80211_mgmt_stypes; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + wiphy->max_remain_on_channel_duration = MAX_REMAIN_ON_CHANNEL_DURATION; +#endif /* KERNEL_VERSION */ + + /* Set RTS threshold to wiphy */ + if (MLAN_STATUS_SUCCESS != + woal_set_get_rts(priv, MLAN_ACT_GET, wait_option, &rts_thr)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (rts_thr < MLAN_RTS_MIN_VALUE || rts_thr > MLAN_RTS_MAX_VALUE) + rts_thr = MLAN_FRAG_RTS_DISABLED; + wiphy->rts_threshold = (t_u32)rts_thr; + + /* Set fragment threshold to wiphy */ + if (MLAN_STATUS_SUCCESS != + woal_set_get_frag(priv, MLAN_ACT_GET, wait_option, &frag_thr)) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (frag_thr < MLAN_RTS_MIN_VALUE || frag_thr > MLAN_RTS_MAX_VALUE) + frag_thr = MLAN_FRAG_RTS_DISABLED; + wiphy->frag_threshold = (t_u32)frag_thr; + +done: + LEAVE(); + if (ret != MLAN_STATUS_PENDING) + kfree(req); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +/** + * @brief Update channel flag + * + * @param wiphy A pointer to wiphy structure + * + * @return N/A + */ +void woal_update_channel_flag(struct wiphy *wiphy, mlan_fw_info *fw_info) +{ + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + int i; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + sband = wiphy->bands[band]; + if (!sband) + continue; + if (sband->band & IEEE80211_BAND_5GHZ && + fw_info->prohibit_80mhz) { + for (i = 0; i < sband->n_channels; i++) { + sband->channels[i].flags |= + IEEE80211_CHAN_NO_80MHZ; + PRINTM(MCMND, "hw_value=%d channel flag %x\n", + sband->channels[i].hw_value, + sband->channels[i].flags); + } + } + } +} +#endif + +/* + * This function registers the device with CFG802.11 subsystem. + * + * @param priv A pointer to moal_private + * + */ +mlan_status woal_register_cfg80211(moal_private *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + struct wiphy *wiphy; + void *wdev_priv = NULL; + mlan_fw_info fw_info; + char *country = NULL, *reg_alpha2 = NULL; + int index = 0; + + ENTER(); + + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + reg_alpha2 = priv->phandle->params.reg_alpha2; + wiphy = wiphy_new(&woal_cfg80211_ops, sizeof(moal_handle *)); + if (!wiphy) { + PRINTM(MERROR, "Could not allocate wiphy device\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wiphy; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) { + woal_cfg80211_ops.auth = woal_cfg80211_authenticate; + woal_cfg80211_ops.assoc = woal_cfg80211_associate; + woal_cfg80211_ops.disconnect = NULL; + woal_cfg80211_ops.connect = NULL; +#ifdef UAP_SUPPORT + woal_cfg80211_ops.probe_client = woal_cfg80211_probe_client; +#endif + } +#endif +#ifdef CONFIG_PM +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + if (fw_info.fw_supplicant_support) + wiphy->wowlan = &wowlan_support_with_gtk; + else + wiphy->wowlan = &wowlan_support; +#else + wiphy->wowlan.flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_MAGIC_PKT; + if (fw_info.fw_supplicant_support) { + wiphy->wowlan.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE; + } + wiphy->wowlan.n_patterns = MAX_NUM_FILTERS; + wiphy->wowlan.pattern_min_len = 1; + wiphy->wowlan.pattern_max_len = WOWLAN_MAX_PATTERN_LEN; + wiphy->wowlan.max_pkt_offset = WOWLAN_MAX_OFFSET_LEN; +#endif +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + wiphy->coalesce = &coalesce_support; +#endif + wiphy->max_scan_ssids = MRVDRV_MAX_SSID_LIST_LENGTH; + wiphy->max_scan_ie_len = MAX_IE_SIZE; + wiphy->interface_modes = 0; + wiphy->interface_modes = + MBIT(NL80211_IFTYPE_STATION) | MBIT(NL80211_IFTYPE_AP); + +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + wiphy->interface_modes |= + MBIT(NL80211_IFTYPE_P2P_GO) | MBIT(NL80211_IFTYPE_P2P_CLIENT); +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + woal_register_cfg80211_vendor_command(wiphy); +#endif + /* Make this wiphy known to this driver only */ + wiphy->privid = mrvl_wiphy_privid; + + if (!fw_info.fw_bands) + fw_info.fw_bands = BAND_B | BAND_G; + if (fw_info.fw_bands & BAND_A) { + if (priv->phandle->second_mac) + wiphy->bands[IEEE80211_BAND_5GHZ] = + &mac1_cfg80211_band_5ghz; + else + + wiphy->bands[IEEE80211_BAND_5GHZ] = &cfg80211_band_5ghz; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + woal_update_channel_flag(wiphy, &fw_info); +#endif + priv->phandle->band = IEEE80211_BAND_5GHZ; + } + /* Supported bands */ + if (fw_info.fw_bands & (BAND_B | BAND_G | BAND_GN | BAND_GAC)) { + if (priv->phandle->second_mac) + wiphy->bands[IEEE80211_BAND_2GHZ] = + &mac1_cfg80211_band_2ghz; + else + wiphy->bands[IEEE80211_BAND_2GHZ] = &cfg80211_band_2ghz; + /* If 2.4G enable, it will overwrite default to 2.4G*/ + priv->phandle->band = IEEE80211_BAND_2GHZ; + } + + if (fw_info.fw_bands & BAND_A) { + /** reduce scan time from 110ms to 80ms */ + woal_set_scan_time(priv, INIT_ACTIVE_SCAN_CHAN_TIME, + INIT_PASSIVE_SCAN_CHAN_TIME, + INIT_SPECIFIC_SCAN_CHAN_TIME); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + cfg80211_iface_comb_ap_sta.radar_detect_widths |= + MBIT(NL80211_CHAN_WIDTH_40); + if (fw_info.fw_bands & BAND_AAC) + cfg80211_iface_comb_ap_sta.radar_detect_widths |= + MBIT(NL80211_CHAN_WIDTH_80); +#endif + } else + woal_set_scan_time(priv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + SPECIFIC_SCAN_CHAN_TIME); + + /* Initialize cipher suits */ + wiphy->cipher_suites = cfg80211_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(cfg80211_cipher_suites); +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (!moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) +#endif + wiphy->max_acl_mac_addrs = MAX_MAC_FILTER_NUM; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + if (fw_info.max_ap_assoc_sta) { + wiphy->max_ap_assoc_sta = fw_info.max_ap_assoc_sta; + PRINTM(MCMND, "Set wiphy max_ap_assoc_sta=%d\n", + wiphy->max_ap_assoc_sta); + } +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + /* Initialize interface combinations */ + wiphy->iface_combinations = &cfg80211_iface_comb_ap_sta; + wiphy->n_iface_combinations = 1; +#endif + + moal_memcpy_ext(priv->phandle, wiphy->perm_addr, priv->current_addr, + ETH_ALEN, sizeof(wiphy->perm_addr)); + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + + wiphy->flags = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) + wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; + wiphy->flags |= WIPHY_FLAG_NETNS_OK; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + wiphy->flags |= + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_OFFCHAN_TX; + wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) + wiphy->flags |= WIPHY_FLAG_REPORTS_OBSS; + else +#endif + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; +#endif +#ifdef ANDROID_KERNEL +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (!moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) +#endif + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +#else + wiphy->max_sched_scan_reqs = 1; +#endif + wiphy->max_sched_scan_ssids = MRVDRV_MAX_SSID_LIST_LENGTH; + wiphy->max_sched_scan_ie_len = MAX_IE_SIZE; + wiphy->max_match_sets = MRVDRV_MAX_SSID_LIST_LENGTH; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) + wiphy->max_sched_scan_plans = 3; + wiphy->max_sched_scan_plan_iterations = 100; +#endif +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + wiphy->features |= NL80211_FEATURE_INACTIVITY_TIMER; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) + wiphy->features |= NL80211_FEATURE_SAE; +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + wiphy->features |= NL80211_FEATURE_NEED_OBSS_SCAN; +#endif + + wiphy->reg_notifier = woal_cfg80211_reg_notifier; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + wiphy->flags |= WIPHY_FLAG_HAS_CHANNEL_SWITCH; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + /* Indicate to cfg80211 that the driver can support + * CSA and ESCA,i.e., both types of channel switch + * Applications like hostapd 2.6 will append CSA IE + * and ECSA IE and expect the driver to advertise 2 + * in max_num_csa_counters to successfully issue a + * channel switch + */ + wiphy->max_num_csa_counters = MAX_CSA_COUNTERS_NUM; +#endif + wiphy->flags |= WIPHY_FLAG_CONTROL_PORT_PROTOCOL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; + wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SET_SCAN_DWELL); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) + wiphy->features |= NL80211_FEATURE_SK_TX_STATUS; +#endif + /* Set struct moal_handle pointer in wiphy_priv */ + wdev_priv = wiphy_priv(wiphy); + *(unsigned long *)wdev_priv = (unsigned long)priv->phandle; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + set_wiphy_dev(wiphy, (struct device *)priv->phandle->hotplug_device); +#endif + /* Set phy name*/ + for (index = 0; index < MAX_MLAN_ADAPTER; index++) { + if (m_handle[index] == priv->phandle) { + dev_set_name(&wiphy->dev, mwiphy_name, index); + break; + } + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (moal_extflg_isset(priv->phandle, EXT_BEACON_HINTS)) { + /* REGULATORY_DISABLE_BEACON_HINTS: NO-IR flag won't be removed + * on chn where an AP is visible! */ + wiphy->regulatory_flags |= REGULATORY_DISABLE_BEACON_HINTS; + } + if (moal_extflg_isset(priv->phandle, EXT_COUNTRY_IE_IGNORE)) { + PRINTM(MIOCTL, "Don't follow countryIE provided by AP.\n"); + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE; + } else { + PRINTM(MIOCTL, "Follow countryIE provided by AP.\n"); + } +#endif + + priv->phandle->country_code[0] = '0'; + priv->phandle->country_code[1] = '0'; + priv->phandle->country_code[2] = ' '; + + if (reg_alpha2 && !strncmp(reg_alpha2, "99", strlen("99"))) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; +#else + wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY; +#endif + wiphy_apply_custom_regulatory(wiphy, &mrvl_regdom); + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) + if (woal_request_extcap(priv, (t_u8 *)&priv->extended_capabilities, + sizeof(priv->extended_capabilities)) < 0) + PRINTM(MERROR, + "Failed to get driver extended capability, use default\n"); + DBG_HEXDUMP(MCMD_D, "wiphy ext cap", + (t_u8 *)&priv->extended_capabilities, + sizeof(priv->extended_capabilities)); + wiphy->extended_capabilities = (t_u8 *)&priv->extended_capabilities; + wiphy->extended_capabilities_mask = + (t_u8 *)&priv->extended_capabilities; + wiphy->extended_capabilities_len = sizeof(priv->extended_capabilities); +#endif + if (wiphy_register(wiphy) < 0) { + PRINTM(MERROR, "Wiphy device registration failed!\n"); + ret = MLAN_STATUS_FAILURE; + goto err_wiphy; + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + if (fw_info.force_reg || + (fw_region && priv->phandle->params.txpwrlimit_cfg)) { + PRINTM(MCMND, "FW region_code=%d force_reg=%d\n", + fw_info.region_code, fw_info.force_reg); + country = region_code_2_string(fw_info.region_code); + if (country) { + moal_memcpy_ext(priv->phandle, + priv->phandle->country_code, country, 2, + 2); + woal_update_custom_regdomain(priv, wiphy); + } + } +#endif + if ((!reg_alpha2 || strncmp(reg_alpha2, "99", strlen("99"))) + + ) { + /** we will try driver parameter first */ + if (reg_alpha2 && woal_is_valid_alpha2(reg_alpha2)) { + PRINTM(MIOCTL, "Notify reg_alpha2 %c%c\n", + reg_alpha2[0], reg_alpha2[1]); + if (!moal_extflg_isset(priv->phandle, + EXT_DISABLE_REGD_BY_DRIVER)) + regulatory_hint(wiphy, reg_alpha2); + } else { + country = region_code_2_string(fw_info.region_code); + if (country) { + if (fw_info.region_code != 0) { + PRINTM(MIOCTL, + "Notify hw region code=%d %c%c\n", + fw_info.region_code, country[0], + country[1]); + if (!moal_extflg_isset( + priv->phandle, + EXT_DISABLE_REGD_BY_DRIVER)) + regulatory_hint(wiphy, country); + } + } else + PRINTM(MCMND, + "hw region code=%d not supported\n", + fw_info.region_code); + } + } + priv->phandle->wiphy = wiphy; + woal_cfg80211_init_wiphy(priv, MOAL_IOCTL_WAIT); + + return ret; +err_wiphy: + if (wiphy) + wiphy_free(wiphy); + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.h b/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.h new file mode 100644 index 0000000..8080a48 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_sta_cfg80211.h @@ -0,0 +1,41 @@ +/** @file moal_sta_cfg80211.h + * + * @brief This file contains the STA CFG80211 specific defines. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _MOAL_STA_CFG80211_H_ +#define _MOAL_STA_CFG80211_H_ + +/** Convert RSSI signal strength from dBm to mBm (100*dBm) */ +#define RSSI_DBM_TO_MDM(x) ((x)*100) + +mlan_status woal_register_sta_cfg80211(struct net_device *dev, t_u8 bss_type); + +mlan_status woal_cfg80211_set_key(moal_private *priv, t_u8 is_enable_wep, + t_u32 cipher, const t_u8 *key, int key_len, + const t_u8 *seq, int seq_len, t_u8 key_index, + const t_u8 *addr, int disable, + t_u8 wait_option); + +mlan_status woal_cfg80211_set_wep_keys(moal_private *priv, const t_u8 *key, + int key_len, t_u8 index, + t_u8 wait_option); + +#endif /* _MOAL_STA_CFG80211_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_uap.c b/mxm_wifiex/wlan_src/mlinux/moal_uap.c new file mode 100644 index 0000000..34e66cf --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_uap.c @@ -0,0 +1,4526 @@ +/** @file moal_uap.c + * + * @brief This file contains the major functions in UAP + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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" +#ifdef SDIO +#include "moal_sdio.h" +#endif /* SDIO */ +#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; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_addba_param() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, + "addba param: action=%d, timeout=%d, txwinsize=%d, rxwinsize=%d txamsdu=%d rxamsdu=%d\n", + (int)param.action, (int)param.timeout, (int)param.txwinsize, + (int)param.rxwinsize, (int)param.txamsdu, (int)param.rxamsdu); + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_PARAM; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!param.action) + /* Get addba param from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + else { + /* Set addba param in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + cfg_11n->param.addba_param.timeout = param.timeout; + cfg_11n->param.addba_param.txwinsize = param.txwinsize; + cfg_11n->param.addba_param.rxwinsize = param.rxwinsize; + cfg_11n->param.addba_param.txamsdu = param.txamsdu; + cfg_11n->param.addba_param.rxamsdu = param.rxamsdu; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + param.timeout = cfg_11n->param.addba_param.timeout; + param.txwinsize = cfg_11n->param.addba_param.txwinsize; + param.rxwinsize = cfg_11n->param.addba_param.rxwinsize; + param.txamsdu = cfg_11n->param.addba_param.txamsdu; + param.rxamsdu = cfg_11n->param.addba_param.rxamsdu; + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap aggr priority tbl + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_aggr_priotbl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + aggr_prio_tbl param; + int ret = 0; + int i = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_aggr_priotbl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "aggr_prio_tbl", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_AGGR_PRIO_TBL; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!param.action) { + /* Get aggr_prio_tbl from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set aggr_prio_tbl in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + for (i = 0; i < MAX_NUM_TID; i++) { + cfg_11n->param.aggr_prio_tbl.ampdu[i] = param.ampdu[i]; + cfg_11n->param.aggr_prio_tbl.amsdu[i] = param.amsdu[i]; + } + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + for (i = 0; i < MAX_NUM_TID; i++) { + param.ampdu[i] = cfg_11n->param.aggr_prio_tbl.ampdu[i]; + param.amsdu[i] = cfg_11n->param.aggr_prio_tbl.amsdu[i]; + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap addba reject tbl + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_addba_reject(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11n_cfg *cfg_11n = NULL; + addba_reject_para param; + int ret = 0; + int i = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_addba_reject() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "addba_reject tbl", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_ADDBA_REJECT; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!param.action) { + /* Get addba_reject tbl from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set addba_reject tbl in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + for (i = 0; i < MAX_NUM_TID; i++) + cfg_11n->param.addba_reject[i] = param.addba_reject[i]; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + for (i = 0; i < MAX_NUM_TID; i++) + param.addba_reject[i] = cfg_11n->param.addba_reject[i]; + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap get_fw_info handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_get_fw_info(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + fw_info fw; + mlan_fw_info fw_info; + int ret = 0; + + ENTER(); + memset(&fw, 0, sizeof(fw)); + memset(&fw_info, 0, sizeof(fw_info)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_get_fw_info() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&fw, req->ifr_data, sizeof(fw))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info)) { + ret = -EFAULT; + goto done; + } + fw.fw_release_number = fw_info.fw_ver; + fw.hw_dev_mcs_support = fw_info.hw_dev_mcs_support; + fw.fw_bands = fw_info.fw_bands; + fw.region_code = fw_info.region_code; + fw.hw_dot_11n_dev_cap = fw_info.hw_dot_11n_dev_cap; + /* Copy to user */ + if (copy_to_user(req->ifr_data, &fw, sizeof(fw))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief configure deep sleep + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_deep_sleep(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm = NULL; + deep_sleep_para param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_deep_sleep() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "deep_sleep_para", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + pm = (mlan_ds_pm_cfg *)ioctl_req->pbuf; + pm->sub_command = MLAN_OID_PM_CFG_DEEP_SLEEP; + ioctl_req->req_id = MLAN_IOCTL_PM_CFG; + + if (!param.action) { + /* Get deep_sleep status from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set deep_sleep in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + if (param.deep_sleep == MTRUE) { + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_ON; + pm->param.auto_deep_sleep.idletime = param.idle_time; + } else { + pm->param.auto_deep_sleep.auto_ds = DEEP_SLEEP_OFF; + } + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (pm->param.auto_deep_sleep.auto_ds == DEEP_SLEEP_ON) + param.deep_sleep = MTRUE; + else + param.deep_sleep = MFALSE; + param.idle_time = pm->param.auto_deep_sleep.idletime; + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief configure band steering + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_band_steer(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 *pm = NULL; + band_steer_para param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_band_steer() 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, "band_steer_para", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_band_steer_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + pm = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + pm->sub_command = MLAN_OID_MISC_BAND_STEERING; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + pm->param.band_steer_cfg.action = param.action; + pm->param.band_steer_cfg.block_2g_prb_req = param.block_2g_prb_req; + pm->param.band_steer_cfg.state = param.state; + pm->param.band_steer_cfg.max_btm_req_allowed = + param.max_btm_req_allowed; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + param.action = pm->param.band_steer_cfg.action; + param.block_2g_prb_req = pm->param.band_steer_cfg.block_2g_prb_req; + param.state = pm->param.band_steer_cfg.state; + param.max_btm_req_allowed = + pm->param.band_steer_cfg.max_btm_req_allowed; + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief configure beacon stuck detect mechanism + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_beacon_stuck(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 *pm = NULL; + beacon_stuck_detect_para param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_beacon_stuck() 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, "beacon_stuck_detect_para", (t_u8 *)¶m, + sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_beacon_stuck_param_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + + pm = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + pm->sub_command = MLAN_OID_MISC_BEACON_STUCK; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + pm->param.beacon_stuck_cfg.action = param.action; + pm->param.beacon_stuck_cfg.beacon_stuck_detect_count = + param.beacon_stuck_detect_count; + pm->param.beacon_stuck_cfg.recovery_confirm_count = + param.recovery_confirm_count; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + param.action = pm->param.beacon_stuck_cfg.action; + param.beacon_stuck_detect_count = + pm->param.beacon_stuck_cfg.beacon_stuck_detect_count; + param.recovery_confirm_count = + pm->param.beacon_stuck_cfg.recovery_confirm_count; + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + + LEAVE(); + + return ret; +} + +/** + * @brief configure tx_pause settings + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_txdatapause(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + tx_data_pause_para param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_txdatapause corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "tx_data_pause_para", (t_u8 *)¶m, + sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_TX_DATAPAUSE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + + if (!param.action) { + /* Get Tx data pause status from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set Tx data pause in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + misc->param.tx_datapause.tx_pause = param.txpause; + misc->param.tx_datapause.tx_buf_cnt = param.txbufcnt; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + param.txpause = misc->param.tx_datapause.tx_pause; + param.txbufcnt = misc->param.tx_datapause.tx_buf_cnt; + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#ifdef SDIO +/** + * @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); +#ifdef SDIO_MMC + 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 + if (sdio_read_ioreg(priv->phandle->card, func, reg, &data) < + 0) { + PRINTM(MERROR, + "sdio_read_ioreg: reading register 0x%X failed\n", + reg); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif + 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); +#ifdef SDIO_MMC + 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; + } +#else + if (sdio_write_ioreg(priv->phandle->card, func, reg, data) < + 0) { + PRINTM(MERROR, + "sdio_write_ioreg: writing register 0x%X failed\n", + reg); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#endif + } + /* 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; +} +#endif + +/** + * @brief configure snmp mib + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_snmp_mib(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_snmp_mib *snmp = NULL; + snmp_mib_para param; + t_u8 value[MAX_SNMP_VALUE_SIZE]; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + memset(value, 0, MAX_SNMP_VALUE_SIZE); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_snmp_mib() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "snmp_mib_para", (t_u8 *)¶m, sizeof(param)); + if (param.action) { + if (copy_from_user(value, req->ifr_data + sizeof(param), + MIN(param.oid_val_len, + MAX_SNMP_VALUE_SIZE))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "snmp_mib_para value", value, + MIN(param.oid_val_len, sizeof(t_u32))); + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_snmp_mib)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + snmp = (mlan_ds_snmp_mib *)ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_SNMP_MIB; + switch (param.oid) { + case OID_80211D_ENABLE: + snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11D; + break; + case OID_80211H_ENABLE: + snmp->sub_command = MLAN_OID_SNMP_MIB_DOT11H; + break; + default: + PRINTM(MERROR, "%s: Unsupported SNMP_MIB OID (%d).\n", __func__, + param.oid); + goto done; + } + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + snmp->param.oid_value = *(t_u32 *)value; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { /* GET */ + if (copy_to_user(req->ifr_data + sizeof(param), + &snmp->param.oid_value, + MIN(param.oid_val_len, sizeof(t_u32)))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief configure domain info + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_domain_info(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11d_cfg *cfg11d = NULL; + domain_info_para param; + t_u8 tlv[MAX_DOMAIN_TLV_LEN]; + t_u16 tlv_data_len = 0; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + memset(tlv, 0, MAX_DOMAIN_TLV_LEN); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_domain_info() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "domain_info_para", (t_u8 *)¶m, sizeof(param)); + if (param.action) { + /* get tlv header */ + if (copy_from_user(tlv, req->ifr_data + sizeof(param), + TLV_HEADER_LEN)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + tlv_data_len = ((t_u16 *)(tlv))[1]; + if ((TLV_HEADER_LEN + tlv_data_len) > sizeof(tlv)) { + PRINTM(MERROR, "TLV buffer is overflowed"); + ret = -EINVAL; + goto done; + } + /* get full tlv */ + if (copy_from_user(tlv, req->ifr_data + sizeof(param), + TLV_HEADER_LEN + tlv_data_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "domain_info_para tlv", tlv, + TLV_HEADER_LEN + tlv_data_len); + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11d_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg11d = (mlan_ds_11d_cfg *)ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_11D_CFG; + cfg11d->sub_command = MLAN_OID_11D_DOMAIN_INFO; + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + moal_memcpy_ext(priv->phandle, cfg11d->param.domain_tlv, tlv, + TLV_HEADER_LEN + tlv_data_len, + sizeof(cfg11d->param.domain_tlv)); + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { /* GET */ + tlv_data_len = ((t_u16 *)(cfg11d->param.domain_tlv))[1]; + if (copy_to_user(req->ifr_data + sizeof(param), + &cfg11d->param.domain_tlv, + TLV_HEADER_LEN + tlv_data_len)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief configure dfs testing settings + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_dfs_testing(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11h_cfg *cfg11h = NULL; + dfs_testing_para param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_dfs_testing() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "dfs_testing_para", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg11h = (mlan_ds_11h_cfg *)ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_11H_CFG; + cfg11h->sub_command = MLAN_OID_11H_DFS_TESTING; + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + cfg11h->param.dfs_testing.usr_cac_period_msec = + param.usr_cac_period; + cfg11h->param.dfs_testing.usr_nop_period_sec = + param.usr_nop_period; + cfg11h->param.dfs_testing.usr_no_chan_change = + param.no_chan_change; + cfg11h->param.dfs_testing.usr_fixed_new_chan = + param.fixed_new_chan; + cfg11h->param.dfs_testing.usr_cac_restart = param.cac_restart; + priv->phandle->cac_restart = param.cac_restart; + priv->phandle->cac_period_jiffies = + param.usr_cac_period * HZ / 1000; + priv->user_cac_period_msec = + cfg11h->param.dfs_testing.usr_cac_period_msec; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!param.action) { /* GET */ + param.usr_cac_period = + cfg11h->param.dfs_testing.usr_cac_period_msec; + param.usr_nop_period = + cfg11h->param.dfs_testing.usr_nop_period_sec; + param.no_chan_change = + cfg11h->param.dfs_testing.usr_no_chan_change; + param.fixed_new_chan = + cfg11h->param.dfs_testing.usr_fixed_new_chan; + param.cac_restart = cfg11h->param.dfs_testing.usr_cac_restart; + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +/** + * @brief uap channel NOP status check ioctl handler + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param data BSS control type + * @return 0 --success, otherwise fail + */ +int woal_uap_get_channel_nop_info(moal_private *priv, t_u8 wait_option, + mlan_ds_11h_chan_nop_info *ch_info) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11h_cfg *ds_11hcfg = NULL; + + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!ch_info) { + PRINTM(MERROR, "Invalid chan_info\n"); + LEAVE(); + return -EFAULT; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_GET; + + ds_11hcfg = (mlan_ds_11h_cfg *)req->pbuf; + ds_11hcfg->sub_command = MLAN_OID_11H_CHAN_NOP_INFO; + moal_memcpy_ext(priv->phandle, &ds_11hcfg->param.ch_nop_info, ch_info, + sizeof(mlan_ds_11h_chan_nop_info), + sizeof(ds_11hcfg->param.ch_nop_info)); + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_FAILURE) { + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, ch_info, &ds_11hcfg->param.ch_nop_info, + sizeof(mlan_ds_11h_chan_nop_info), + sizeof(mlan_ds_11h_chan_nop_info)); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif +#endif + +/** + * @brief configure channel switch count + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_chan_switch_count_cfg(struct net_device *dev, + struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11h_cfg *cfg11h = NULL; + cscount_cfg_t param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + memset(¶m, 0, sizeof(param)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "%s corrupt data\n", __func__); + ret = -EFAULT; + goto done; + } + + /* Copy from user */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "cscount_cfg_t", (t_u8 *)¶m, sizeof(param)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return -ENOMEM; + } + cfg11h = (mlan_ds_11h_cfg *)ioctl_req->pbuf; + ioctl_req->req_id = MLAN_IOCTL_11H_CFG; + cfg11h->sub_command = MLAN_OID_11H_CHAN_SWITCH_COUNT; + + if (!param.action) { + /* Get mib value from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set mib value to MLAN */ + ioctl_req->action = MLAN_ACT_SET; + cfg11h->param.cs_count = param.cs_count; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (!param.action) { /* GET */ + param.cs_count = cfg11h->param.cs_count; + } + /* Copy to user */ + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Configure TX beamforming support + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_tx_bf_cfg(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_11n_tx_bf_cfg bf_cfg; + tx_bf_cfg_para_hdr param; + t_u16 action = 0; + + ENTER(); + + memset(¶m, 0, sizeof(param)); + memset(&bf_cfg, 0, sizeof(bf_cfg)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_tx_bf_cfg corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) + /* Get BF configurations */ + action = MLAN_ACT_GET; + else + /* Set BF configurations */ + action = MLAN_ACT_SET; + if (copy_from_user(&bf_cfg, req->ifr_data + sizeof(tx_bf_cfg_para_hdr), + sizeof(bf_cfg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + DBG_HEXDUMP(MCMD_D, "bf_cfg", (t_u8 *)&bf_cfg, sizeof(bf_cfg)); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_bf_cfg(priv, action, &bf_cfg)) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data + sizeof(tx_bf_cfg_para_hdr), &bf_cfg, + sizeof(bf_cfg))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set/Get 11n configurations + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_ht_tx_cfg(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ds_11n_tx_cfg httx_cfg; + mlan_ioctl_req *ioctl_req = NULL; + ht_tx_cfg_para_hdr param; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(¶m, 0, sizeof(ht_tx_cfg_para_hdr)); + memset(&httx_cfg, 0, sizeof(mlan_ds_11n_tx_cfg)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_ht_tx_cfg corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(ht_tx_cfg_para_hdr))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + if (copy_from_user(&httx_cfg, + req->ifr_data + sizeof(ht_tx_cfg_para_hdr), + sizeof(mlan_ds_11n_tx_cfg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { + /* Get 11n tx parameters from MLAN */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set HT Tx configurations */ + cfg_11n->param.tx_cfg.httxcap = httx_cfg.httxcap; + PRINTM(MINFO, "SET: httxcap:0x%x\n", httx_cfg.httxcap); + /* Update 11n tx parameters in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (ioctl_req->action == MLAN_ACT_GET) { + httx_cfg.httxcap = cfg_11n->param.tx_cfg.httxcap; + PRINTM(MINFO, "GET: httxcap:0x%x\n", httx_cfg.httxcap); + } + /* Copy to user */ + if (copy_to_user(req->ifr_data + sizeof(ht_tx_cfg_para_hdr), &httx_cfg, + sizeof(mlan_ds_11n_tx_cfg))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get Set/Get 11AC configurations + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_vht_cfg(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0, resbuf_len = 0; + mlan_ds_11ac_cfg *cfg_11ac = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_11ac_vht_cfg *vhtcfg = NULL, vht_cfg; + t_u8 *respbuf = NULL; + vht_cfg_para_hdr param; + mlan_status status = MLAN_STATUS_SUCCESS; +#define CMD_RESPBUF_LEN 2048 + gfp_t flag; + + ENTER(); + + memset(¶m, 0, sizeof(vht_cfg_para_hdr)); + + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + respbuf = kzalloc(CMD_RESPBUF_LEN, flag); + if (!respbuf) { + ret = -ENOMEM; + goto done; + } + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_ht_tx_cfg corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(vht_cfg_para_hdr))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ac_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11ac = (mlan_ds_11ac_cfg *)ioctl_req->pbuf; + cfg_11ac->sub_command = MLAN_OID_11AC_VHT_CFG; + ioctl_req->req_id = MLAN_IOCTL_11AC_CFG; + if (copy_from_user(&vht_cfg, req->ifr_data + sizeof(vht_cfg_para_hdr), + sizeof(mlan_ds_11ac_vht_cfg))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (vht_cfg.band == BAND_SELECT_BOTH) { + cfg_11ac->param.vht_cfg.band = (BAND_SELECT_BG | BAND_SELECT_A); + } else { + cfg_11ac->param.vht_cfg.band = vht_cfg.band; + } + if (!param.action) { + /* GET operation */ + if (vht_cfg.band == BAND_SELECT_BOTH) { + /* if get both bands, get BG first */ + cfg_11ac->param.vht_cfg.band = BAND_SELECT_BG; + } + PRINTM(MINFO, "GET: vhtcfg band: 0x%x\n", + cfg_11ac->param.vht_cfg.band); + if (priv->bss_role == MLAN_BSS_ROLE_UAP) + cfg_11ac->param.vht_cfg.txrx = MLAN_RADIO_RX; + else + cfg_11ac->param.vht_cfg.txrx = vht_cfg.txrx; + PRINTM(MINFO, "GET: vhtcfg txrx: 0x%x\n", + cfg_11ac->param.vht_cfg.txrx); + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Band */ + if (vht_cfg.band == BAND_SELECT_BOTH) + cfg_11ac->param.vht_cfg.band = + (BAND_SELECT_BG | BAND_SELECT_A); + else + cfg_11ac->param.vht_cfg.band = vht_cfg.band; + PRINTM(MINFO, "SET: vhtcfg band: 0x%x\n", + cfg_11ac->param.vht_cfg.band); + /* Tx/Rx */ + cfg_11ac->param.vht_cfg.txrx = vht_cfg.txrx; + PRINTM(MINFO, "SET: vhtcfg txrx: 0x%x\n", + cfg_11ac->param.vht_cfg.txrx); + /* BW cfg */ + cfg_11ac->param.vht_cfg.bwcfg = vht_cfg.bwcfg; + PRINTM(MINFO, "SET: vhtcfg bw cfg:0x%x\n", + cfg_11ac->param.vht_cfg.bwcfg); + + cfg_11ac->param.vht_cfg.vht_cap_info = vht_cfg.vht_cap_info; + PRINTM(MINFO, "SET: vhtcfg vht_cap_info:0x%x\n", + cfg_11ac->param.vht_cfg.vht_cap_info); + cfg_11ac->param.vht_cfg.vht_tx_mcs = vht_cfg.vht_tx_mcs; + cfg_11ac->param.vht_cfg.vht_rx_mcs = vht_cfg.vht_rx_mcs; + /* Update 11AC parameters in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* number of vhtcfg entries */ + *respbuf = 1; + vhtcfg = (mlan_ds_11ac_vht_cfg *)(respbuf + 1); + moal_memcpy_ext(priv->phandle, vhtcfg, &cfg_11ac->param.vht_cfg, + sizeof(mlan_ds_11ac_vht_cfg), + sizeof(mlan_ds_11ac_vht_cfg)); + resbuf_len = 1 + sizeof(mlan_ds_11ac_vht_cfg); + + if ((ioctl_req->action == MLAN_ACT_GET) && + (vht_cfg.band == BAND_SELECT_BOTH)) { + cfg_11ac->param.vht_cfg.band = BAND_SELECT_A; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + /* number of vhtcfg entries */ + *respbuf = 2; + vhtcfg++; + moal_memcpy_ext(priv->phandle, vhtcfg, &cfg_11ac->param.vht_cfg, + sizeof(mlan_ds_11ac_vht_cfg), + sizeof(mlan_ds_11ac_vht_cfg)); + resbuf_len += sizeof(mlan_ds_11ac_vht_cfg); + } + if (ioctl_req->action == MLAN_ACT_GET) { + if (copy_to_user(req->ifr_data, respbuf, resbuf_len)) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + if (respbuf) + kfree(respbuf); + 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 = NULL; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_mgmt_frame_ctrl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Get user data */ + if (copy_from_user(¶m, req->ifr_data, sizeof(param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + if (param.action) + action = MLAN_ACT_SET; + else + action = MLAN_ACT_GET; + if (action == MLAN_ACT_SET) { + /* Initialize the invalid values so that the correct + values below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_config); + sys_config->mgmt_ie_passthru_mask = param.mask; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, action, + MOAL_IOCTL_WAIT, + sys_config)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_GET) { + param.mask = sys_config->mgmt_ie_passthru_mask; + if (copy_to_user(req->ifr_data, ¶m, sizeof(param))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + kfree(sys_config); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get tx rate + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_uap_tx_rate_cfg(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0, i = 0; + mlan_ds_rate *rate = NULL; + mlan_ioctl_req *mreq = NULL; + tx_rate_cfg_t tx_rate_config; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_tx_rate_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(&tx_rate_config, 0, sizeof(tx_rate_cfg_t)); + /* Get user data */ + if (copy_from_user(&tx_rate_config, req->ifr_data, + sizeof(tx_rate_cfg_t))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (mreq == NULL) { + ret = -ENOMEM; + goto done; + } + rate = (mlan_ds_rate *)mreq->pbuf; + rate->param.rate_cfg.rate_type = MLAN_RATE_INDEX; + rate->sub_command = MLAN_OID_RATE_CFG; + mreq->req_id = MLAN_IOCTL_RATE; + if (!(tx_rate_config.action)) + mreq->action = MLAN_ACT_GET; + else { + if ((tx_rate_config.user_data_cnt <= 0) || + (tx_rate_config.user_data_cnt > 4)) { + PRINTM(MERROR, "Invalid user_data_cnt\n"); + ret = -EINVAL; + goto done; + } + + mreq->action = MLAN_ACT_SET; + if (tx_rate_config.rate_format == AUTO_RATE) + rate->param.rate_cfg.is_rate_auto = 1; + else { + if ((tx_rate_config.rate_format < 0) || + (tx_rate_config.rate < 0)) { + PRINTM(MERROR, + "Invalid format or rate selection\n"); + ret = -EINVAL; + goto done; + } + /* rate_format sanity check */ + if ((tx_rate_config.rate_format > + MLAN_RATE_FORMAT_HE)) { + PRINTM(MERROR, "Invalid format selection\n"); + ret = -EINVAL; + goto done; + } + rate->param.rate_cfg.rate_format = + tx_rate_config.rate_format; + + /* rate sanity check */ + if (tx_rate_config.user_data_cnt >= 2) { + if (((tx_rate_config.rate_format == + MLAN_RATE_FORMAT_LG) && + (tx_rate_config.rate > + MLAN_RATE_INDEX_OFDM7)) || + ((tx_rate_config.rate_format == + MLAN_RATE_FORMAT_HT) && + (tx_rate_config.rate != 32) && + (tx_rate_config.rate > 15)) || + ((tx_rate_config.rate_format == + MLAN_RATE_FORMAT_VHT) && + (tx_rate_config.rate > + MLAN_RATE_INDEX_MCS9)) || + ((tx_rate_config.rate_format == + MLAN_RATE_FORMAT_HE) && + (tx_rate_config.rate > + MLAN_RATE_INDEX_MCS11))) { + PRINTM(MERROR, + "Invalid rate selection\n"); + ret = -EINVAL; + goto done; + } + rate->param.rate_cfg.rate = tx_rate_config.rate; + } + + /* nss sanity check */ + if ((tx_rate_config.rate_format == 2) || + (tx_rate_config.rate_format == 3)) { + if ((tx_rate_config.nss < 1) || + (tx_rate_config.nss > 2)) { + PRINTM(MERROR, + "Invalid nss selection %d\n", + tx_rate_config.nss); + ret = -EINVAL; + goto done; + } + rate->param.rate_cfg.nss = tx_rate_config.nss; + } + if (tx_rate_config.user_data_cnt <= 3) + rate->param.rate_cfg.rate_setting = 0xffff; + else + rate->param.rate_cfg.rate_setting = + tx_rate_config.rate_setting; + } + } + + status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (tx_rate_config.action) { + priv->rate_index = tx_rate_config.action; + } else { + if (rate->param.rate_cfg.is_rate_auto) + tx_rate_config.rate_format = AUTO_RATE; + else { + /* fixed rate */ + tx_rate_config.rate_format = + rate->param.rate_cfg.rate_format; + tx_rate_config.rate = rate->param.rate_cfg.rate; + if (rate->param.rate_cfg.rate_format == + MLAN_RATE_FORMAT_VHT || + rate->param.rate_cfg.rate_format == + MLAN_RATE_FORMAT_HE) + tx_rate_config.nss = rate->param.rate_cfg.nss; + tx_rate_config.rate_setting = + rate->param.rate_cfg.rate_setting; + } + for (i = 0; i < MAX_BITMAP_RATES_SIZE; i++) { + tx_rate_config.bitmap_rates[i] = + rate->param.rate_cfg.bitmap_rates[i]; + } + + if (copy_to_user(req->ifr_data, &tx_rate_config, + sizeof(tx_rate_cfg_t))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(mreq); + LEAVE(); + return ret; +} + +/** + * @brief 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; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_antenna_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(&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; + } + + status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + 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 (status != MLAN_STATUS_PENDING) + kfree(mreq); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get HT stream configurations + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_uap_htstream_cfg(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + mlan_ds_11n_cfg *cfg = NULL; + mlan_ioctl_req *ioctl_req = NULL; + htstream_cfg_t htstream_cfg; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(&htstream_cfg, 0, sizeof(htstream_cfg_t)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_htstream_cfg corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&htstream_cfg, req->ifr_data, + sizeof(htstream_cfg_t))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg->sub_command = MLAN_OID_11N_CFG_STREAM_CFG; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + if (!htstream_cfg.action) { + /* Get operation */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Update HT stream parameter in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + /* Set HT Stream configuration */ + cfg->param.stream_cfg = htstream_cfg.stream_cfg; + PRINTM(MINFO, "SET: htstream_cfg:0x%x\n", + cfg->param.stream_cfg); + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + /* Copy to user */ + if (ioctl_req->action == MLAN_ACT_GET) { + PRINTM(MINFO, "GET: htstream_cfg:0x%x\n", + htstream_cfg.stream_cfg); + htstream_cfg.stream_cfg = cfg->param.stream_cfg; + if (copy_to_user(req->ifr_data, &htstream_cfg, + sizeof(htstream_cfg_t))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Get DFS_REPEATER mode + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_uap_dfs_repeater(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + dfs_repeater_mode param; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *mreq = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_antenna_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(¶m, 0, sizeof(dfs_repeater_mode)); + /* Get user data */ + if (copy_from_user(¶m, req->ifr_data, sizeof(dfs_repeater_mode))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (mreq == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)mreq->pbuf; + misc->sub_command = MLAN_OID_MISC_DFS_REAPTER_MODE; + mreq->req_id = MLAN_IOCTL_MISC_CFG; + mreq->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + param.mode = misc->param.dfs_repeater.mode; + + if (copy_to_user(req->ifr_data, ¶m, sizeof(dfs_repeater_mode))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(mreq); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get skip CAC mode + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_uap_skip_cac(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + skip_cac_para param; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "skip_cac() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(¶m, 0, sizeof(skip_cac_para)); + + /* Get user data */ + if (copy_from_user(¶m, req->ifr_data, sizeof(skip_cac_para))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + /* Currently default action is get */ + if (param.action == 0) { + param.skip_cac = (t_u16)priv->skip_cac; + } else { + priv->skip_cac = param.skip_cac; + } + + if (copy_to_user(req->ifr_data, ¶m, sizeof(skip_cac_para))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } +done: + + LEAVE(); + return ret; +} + +/** + * @brief Get DFS_REPEATER mode + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_uap_cac_timer_status(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + cac_timer_status param; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_antenna_cfg() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + memset(¶m, 0, sizeof(cac_timer_status)); + + /* Get user data */ + if (copy_from_user(¶m, req->ifr_data, sizeof(cac_timer_status))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + /* Currently default action is get */ + param.mode = 0; + + if (priv->phandle->cac_period == MTRUE) { + long cac_left_jiffies; + + cac_left_jiffies = + MEAS_REPORT_TIME - + (jiffies - priv->phandle->meas_start_jiffies); + + /* cac_left_jiffies would be negative if timer has already + * elapsed. positive if timer is still yet to lapsed + */ + if (cac_left_jiffies > 0) + param.mode = (t_u32)cac_left_jiffies / HZ; + } + + if (copy_to_user(req->ifr_data, ¶m, sizeof(cac_timer_status))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + LEAVE(); + return ret; +} + +/** + * @brief set/get uap operation control + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_uap_operation_ctrl(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_oper_ctrl uap_oper; + uap_oper_para_hdr param; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(¶m, 0, sizeof(uap_oper_para_hdr)); + memset(&uap_oper, 0, sizeof(mlan_uap_oper_ctrl)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "woal_uap_operation_ctrl corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(¶m, req->ifr_data, sizeof(uap_oper_para_hdr))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)ioctl_req->pbuf; + bss->sub_command = MLAN_OID_UAP_OPER_CTRL; + ioctl_req->req_id = MLAN_IOCTL_BSS; + if (copy_from_user(&uap_oper, req->ifr_data + sizeof(uap_oper_para_hdr), + sizeof(mlan_uap_oper_ctrl))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (!param.action) { + /* Get uap operation control parameters from FW */ + ioctl_req->action = MLAN_ACT_GET; + } else { + /* Set uap operation control configurations */ + ioctl_req->action = MLAN_ACT_SET; + bss->param.ap_oper_ctrl.ctrl_value = uap_oper.ctrl_value; + if (uap_oper.ctrl_value == 2) + bss->param.ap_oper_ctrl.chan_opt = uap_oper.chan_opt; + if (uap_oper.chan_opt == 3) { + bss->param.ap_oper_ctrl.band_cfg = uap_oper.band_cfg; + bss->param.ap_oper_ctrl.channel = uap_oper.channel; + } + } + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + /* Copy to user */ + if (copy_to_user(req->ifr_data + sizeof(uap_oper_para_hdr), + &bss->param.ap_oper_ctrl, + sizeof(mlan_uap_oper_ctrl))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_ioctl(struct net_device *dev, struct ifreq *req) +{ + int ret = 0; + t_u32 subcmd = 0; + ENTER(); + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&subcmd, req->ifr_data, sizeof(subcmd))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "ioctl subcmd=%d\n", (int)subcmd); + switch (subcmd) { + case UAP_ADDBA_PARA: + ret = woal_uap_addba_param(dev, req); + break; + case UAP_AGGR_PRIOTBL: + ret = woal_uap_aggr_priotbl(dev, req); + break; + case UAP_ADDBA_REJECT: + ret = woal_uap_addba_reject(dev, req); + break; + case UAP_FW_INFO: + ret = woal_uap_get_fw_info(dev, req); + break; + case UAP_DEEP_SLEEP: + ret = woal_uap_deep_sleep(dev, req); + break; + case UAP_TX_DATA_PAUSE: + ret = woal_uap_txdatapause(dev, req); + break; +#ifdef SDIO + case UAP_SDCMD52_RW: + ret = woal_uap_sdcmd52_rw(dev, req); + break; +#endif + case UAP_SNMP_MIB: + ret = woal_uap_snmp_mib(dev, req); + break; + case UAP_DFS_TESTING: + ret = woal_uap_dfs_testing(dev, req); + break; + case UAP_CHAN_SWITCH_COUNT_CFG: + ret = woal_uap_chan_switch_count_cfg(dev, req); + break; + case UAP_DOMAIN_INFO: + ret = woal_uap_domain_info(dev, req); + break; + case UAP_TX_BF_CFG: + ret = woal_uap_tx_bf_cfg(dev, req); + break; + case UAP_HT_TX_CFG: + ret = woal_uap_ht_tx_cfg(dev, req); + break; + case UAP_VHT_CFG: + ret = woal_uap_vht_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; + case UAP_HT_STREAM_CFG: + ret = woal_uap_htstream_cfg(dev, req); + break; + case UAP_DFS_REPEATER_MODE: + ret = woal_uap_dfs_repeater(dev, req); + break; + case UAP_CAC_TIMER_STATUS: + ret = woal_uap_cac_timer_status(dev, req); + break; + case UAP_SKIP_CAC: + ret = woal_uap_skip_cac(dev, req); + break; + case UAP_OPERATION_CTRL: + ret = woal_uap_operation_ctrl(dev, req); + break; + case UAP_BAND_STEER: + ret = woal_uap_band_steer(dev, req); + break; + case UAP_BEACON_STUCK_DETECT: + ret = woal_uap_beacon_stuck(dev, req); + break; + default: + break; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief uap station deauth ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_sta_deauth_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_bss *bss = NULL; + mlan_deauth_param deauth_param; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(&deauth_param, 0, sizeof(mlan_deauth_param)); + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_sta_deauth_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&deauth_param, req->ifr_data, + sizeof(mlan_deauth_param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, "ioctl deauth station: " MACSTR ", reason=%d\n", + MAC2STR(deauth_param.mac_addr), deauth_param.reason_code); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)ioctl_req->pbuf; + + bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; + ioctl_req->req_id = MLAN_IOCTL_BSS; + ioctl_req->action = MLAN_ACT_SET; + + moal_memcpy_ext(priv->phandle, &bss->param.deauth_param, &deauth_param, + sizeof(mlan_deauth_param), + sizeof(bss->param.deauth_param)); + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + if (copy_to_user(req->ifr_data, &ioctl_req->status_code, + sizeof(t_u32))) + PRINTM(MERROR, "Copy to user failed!\n"); + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get radio + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * + * @return 0 --success, otherwise fail + */ +static int woal_uap_radio_ctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *mreq = NULL; + int data[2] = {0, 0}; + mlan_bss_info bss_info; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_radio_ctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Get user data */ + if (copy_from_user(&data, req->ifr_data, sizeof(data))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + if (data[0]) { + mreq = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (mreq == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)mreq->pbuf; + radio->sub_command = MLAN_OID_RADIO_CTRL; + mreq->req_id = MLAN_IOCTL_RADIO_CFG; + mreq->action = MLAN_ACT_SET; + radio->param.radio_on_off = (t_u32)data[1]; + status = woal_request_ioctl(priv, mreq, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(mreq); + } else { + /* Get radio status */ + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + data[1] = bss_info.radio_on; + if (copy_to_user(req->ifr_data, data, sizeof(data))) { + PRINTM(MERROR, "Copy to user failed\n"); + ret = -EFAULT; + } + } +done: + LEAVE(); + return ret; +} + +/** + * @brief uap bss control ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_bss_ctrl_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0, data = 0; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_bss_ctrl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&data, req->ifr_data, sizeof(data))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, data); + +done: + LEAVE(); + return ret; +} + +/** + * @brief uap report mic error 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_report_mic_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_sec_cfg *sec = NULL; + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(mac_addr, 0, MLAN_MAC_ADDR_LENGTH); + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_report_mic_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(mac_addr, req->ifr_data, MLAN_MAC_ADDR_LENGTH)) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MINFO, "ioctl report mic err station: " MACSTR "\n", + MAC2STR(mac_addr)); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + sec = (mlan_ds_sec_cfg *)ioctl_req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_REPORT_MIC_ERR; + ioctl_req->req_id = MLAN_IOCTL_SEC_CFG; + ioctl_req->action = MLAN_ACT_SET; + moal_memcpy_ext(priv->phandle, sec->param.sta_mac, mac_addr, + MLAN_MAC_ADDR_LENGTH, sizeof(sec->param.sta_mac)); + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap set key 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_set_key_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_sec_cfg *sec = NULL; + encrypt_key key; + int ret = 0; + t_u8 bcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(&key, 0, sizeof(encrypt_key)); + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_set_key_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&key, req->ifr_data, sizeof(encrypt_key))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + + PRINTM(MIOCTL, + "ioctl report set key: " MACSTR " key_index=%d, key_len=%d \n", + MAC2STR(key.mac_addr), (int)key.key_index, (int)key.key_len); + + if ((key.key_len > MLAN_MAX_KEY_LENGTH) || (key.key_index > 3)) { + ret = -EINVAL; + goto done; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + sec = (mlan_ds_sec_cfg *)ioctl_req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY, + ioctl_req->req_id = MLAN_IOCTL_SEC_CFG; + ioctl_req->action = MLAN_ACT_SET; + + moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.mac_addr, + key.mac_addr, MLAN_MAC_ADDR_LENGTH, + sizeof(sec->param.encrypt_key.mac_addr)); + sec->param.encrypt_key.key_index = key.key_index; + sec->param.encrypt_key.key_len = key.key_len; + moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.key_material, + key.key_material, key.key_len, + sizeof(sec->param.encrypt_key.key_material)); + 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; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get uap power mode + * + * @param priv A pointer to moal_private structure + * @param action Action set or get + * @param ps_mgmt A pointer to mlan_ds_ps_mgmt structure + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +int woal_set_get_uap_power_mode(moal_private *priv, t_u32 action, + mlan_ds_ps_mgmt *ps_mgmt) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (!ps_mgmt) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_pm_cfg)); + if (ioctl_req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + pm_cfg = (mlan_ds_pm_cfg *)ioctl_req->pbuf; + pm_cfg->sub_command = MLAN_OID_PM_CFG_PS_MODE; + ioctl_req->req_id = MLAN_IOCTL_PM_CFG; + ioctl_req->action = action; + if (action == MLAN_ACT_SET) + moal_memcpy_ext(priv->phandle, &pm_cfg->param.ps_mgmt, ps_mgmt, + sizeof(mlan_ds_ps_mgmt), + sizeof(pm_cfg->param.ps_mgmt)); + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, ps_mgmt, + &pm_cfg->param.ps_mgmt, + sizeof(mlan_ds_ps_mgmt), + sizeof(mlan_ds_ps_mgmt)); + } + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return status; +} + +/** + * @brief uap power mode ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_power_mode_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_pm_cfg *pm_cfg = NULL; + mlan_ds_ps_mgmt ps_mgmt; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(&ps_mgmt, 0, sizeof(mlan_ds_ps_mgmt)); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_power_mode_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + if (copy_from_user(&ps_mgmt, req->ifr_data, sizeof(mlan_ds_ps_mgmt))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + if (priv->bss_type != MLAN_BSS_TYPE_UAP) { + PRINTM(MERROR, "Invlaid BSS_TYPE for UAP power mode command\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; + moal_memcpy_ext(priv->phandle, &pm_cfg->param.ps_mgmt, &ps_mgmt, + sizeof(mlan_ds_ps_mgmt), + sizeof(pm_cfg->param.ps_mgmt)); + } else { + ioctl_req->action = MLAN_ACT_GET; + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + if (copy_to_user(req->ifr_data, &ioctl_req->status_code, + sizeof(t_u32))) + PRINTM(MERROR, "Copy to user failed!\n"); + goto done; + } + if (!ps_mgmt.flags) { + /* Copy to user */ + if (copy_to_user(req->ifr_data, &pm_cfg->param.ps_mgmt, + sizeof(mlan_ds_ps_mgmt))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap BSS config ioctl handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_bss_cfg_ioctl(struct net_device *dev, struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *ioctl_req = NULL; + int offset = 0; + t_u32 action = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_bss_cfg_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Get action */ + if (copy_from_user(&action, req->ifr_data + offset, sizeof(action))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + offset += sizeof(action); + + /* Allocate an IOCTL request buffer */ + ioctl_req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *)ioctl_req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + ioctl_req->req_id = MLAN_IOCTL_BSS; + if (action == 1) + ioctl_req->action = MLAN_ACT_SET; + else + ioctl_req->action = MLAN_ACT_GET; + + if (ioctl_req->action == MLAN_ACT_SET) { + /* Get the BSS config from user */ + if (copy_from_user(&bss->param.bss_config, + req->ifr_data + offset, + sizeof(mlan_uap_bss_param))) { + PRINTM(MERROR, "Copy from user failed\n"); + ret = -EFAULT; + goto done; + } + } + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + offset = sizeof(action); + + /* Copy to user : BSS config */ + if (copy_to_user(req->ifr_data + offset, &bss->param.bss_config, + sizeof(mlan_uap_bss_param))) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uap get station list handler + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int woal_uap_get_sta_list_ioctl(struct net_device *dev, + struct ifreq *req) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* Sanity check */ + if (req->ifr_data == NULL) { + PRINTM(MERROR, "uap_get_sta_list_ioctl() corrupt data\n"); + ret = -EFAULT; + goto done; + } + + /* Allocate an IOCTL request buffer */ + ioctl_req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + + if (ioctl_req->action == MLAN_ACT_GET) { + /* Copy to user : sta_list */ + if (copy_to_user(req->ifr_data, &info->param.sta_list, + ioctl_req->data_read_written)) { + PRINTM(MERROR, "Copy to user failed!\n"); + ret = -EFAULT; + goto done; + } + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief uAP set WAPI key ioctl + * + * @param priv A pointer to moal_private structure + * @param msg A pointer to wapi_msg structure + * + * @return 0 --success, otherwise fail + */ +static int woal_uap_set_wapi_key_ioctl(moal_private *priv, wapi_msg *msg) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0; + wapi_key_msg *key_msg = NULL; + t_u8 bcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (msg->msg_len != sizeof(wapi_key_msg)) { + ret = -EINVAL; + goto done; + } + key_msg = (wapi_key_msg *)msg->msg; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + + sec->param.encrypt_key.is_wapi_key = MTRUE; + sec->param.encrypt_key.key_len = MLAN_MAX_KEY_LENGTH; + moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.mac_addr, + key_msg->mac_addr, ETH_ALEN, + sizeof(sec->param.encrypt_key.mac_addr)); + 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; + moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.key_material, + key_msg->key, sec->param.encrypt_key.key_len, + sizeof(sec->param.encrypt_key.key_material)); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Enable/Disable wapi in firmware + * + * @param priv A pointer to moal_private structure + * @param enable MTRUE/MFALSE + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +static mlan_status woal_enable_wapi(moal_private *priv, t_u8 enable) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Get AP setting failed! status=%d, error_code=0x%x\n", + status, req->status_code); + } + + /* Change AP default setting */ + req->action = MLAN_ACT_SET; + if (enable == MFALSE) { + bss->param.bss_config.auth_mode = MLAN_AUTH_MODE_OPEN; + bss->param.bss_config.protocol = PROTOCOL_NO_SECURITY; + } else { + bss->param.bss_config.auth_mode = MLAN_AUTH_MODE_OPEN; + bss->param.bss_config.protocol = PROTOCOL_WAPI; + } + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Set AP setting failed! status=%d, error_code=0x%x\n", + status, req->status_code); + } + if (enable) + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief uAP set WAPI flag ioctl + * + * @param priv A pointer to moal_private structure + * @param msg A pointer to wapi_msg structure + * + * @return 0 --success, otherwise fail + */ +static int woal_uap_set_wapi_flag_ioctl(moal_private *priv, wapi_msg *msg) +{ + t_u8 wapi_psk_ie[] = {0x44, 0x14, 0x01, 0x00, 0x01, 0x00, 0x00, 0x14, + 0x72, 0x02, 0x01, 0x00, 0x00, 0x14, 0x72, 0x01, + 0x00, 0x14, 0x72, 0x01, 0x00, 0x00}; + t_u8 wapi_cert_ie[] = {0x44, 0x14, 0x01, 0x00, 0x01, 0x00, 0x00, 0x14, + 0x72, 0x01, 0x01, 0x00, 0x00, 0x14, 0x72, 0x01, + 0x00, 0x14, 0x72, 0x01, 0x00, 0x00}; + mlan_ds_misc_cfg *misc = NULL; + mlan_ioctl_req *req = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + misc->sub_command = MLAN_OID_MISC_GEN_IE; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_SET; + + misc->param.gen_ie.type = MLAN_IE_TYPE_GEN_IE; + misc->param.gen_ie.len = sizeof(wapi_psk_ie); + if (msg->msg[0] & WAPI_MODE_PSK) { + moal_memcpy_ext(priv->phandle, misc->param.gen_ie.ie_data, + wapi_psk_ie, misc->param.gen_ie.len, + sizeof(misc->param.gen_ie.ie_data)); + } else if (msg->msg[0] & WAPI_MODE_CERT) { + moal_memcpy_ext(priv->phandle, misc->param.gen_ie.ie_data, + wapi_cert_ie, misc->param.gen_ie.len, + sizeof(misc->param.gen_ie.ie_data)); + } else if (msg->msg[0] == 0) { + /* disable WAPI in driver */ + if (MLAN_STATUS_SUCCESS != + woal_set_wapi_enable(priv, MOAL_IOCTL_WAIT, 0)) + ret = -EFAULT; + woal_enable_wapi(priv, MFALSE); + goto done; + } else { + ret = -EINVAL; + goto done; + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + woal_enable_wapi(priv, MTRUE); +done: + if ((status != MLAN_STATUS_PENDING) && 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; + } + moal_memcpy_ext(priv->phandle, sys_cfg, &info->param.bss_config, + sizeof(mlan_uap_bss_param), sizeof(mlan_uap_bss_param)); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set uap httxcfg + * + * @param priv A pointer to moal_private structure + * @param band_cfg Band cfg + * @param en Enabled/Disabled + * + * @return 0 --success, otherwise fail + */ +int woal_set_uap_ht_tx_cfg(moal_private *priv, Band_Config_t bandcfg, + t_u16 ht_cap, t_u8 en) +{ + int ret = 0; + mlan_ds_11n_cfg *cfg_11n = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11n_cfg)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11n = (mlan_ds_11n_cfg *)ioctl_req->pbuf; + cfg_11n->sub_command = MLAN_OID_11N_CFG_TX; + ioctl_req->req_id = MLAN_IOCTL_11N_CFG; + + /* Set HT Tx configurations */ + if (bandcfg.chanBand == BAND_2GHZ) { + if (en) + cfg_11n->param.tx_cfg.httxcap = ht_cap; + else + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_BG; + } else if (bandcfg.chanBand == BAND_5GHZ) { + if (en) + cfg_11n->param.tx_cfg.httxcap = ht_cap; + else + cfg_11n->param.tx_cfg.httxcap = 0; + cfg_11n->param.tx_cfg.misc_cfg = BAND_SELECT_A; + } + PRINTM(MCMND, "SET: httxcap=0x%x band:0x%x\n", + cfg_11n->param.tx_cfg.httxcap, cfg_11n->param.tx_cfg.misc_cfg); + /* Update 11n tx parameters in MLAN */ + ioctl_req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Set 11n status based on the configured security mode + * + * @param priv A pointer to moal_private structure + * @param sys_cfg A pointer to mlan_uap_bss_param structure + * @param action MLAN_ACT_DISABLE or MLAN_ACT_ENABLE + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_uap_set_11n_status(moal_private *priv, + mlan_uap_bss_param *sys_cfg, t_u8 action) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_fw_info fw_info; + + ENTER(); + memset(&fw_info, 0, sizeof(mlan_fw_info)); + 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) { + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + sys_cfg->supported_mcs_set[0] = 0xFF; + if (sys_cfg->bandcfg.chan2Offset) + sys_cfg->supported_mcs_set[4] = 0x01; + else + sys_cfg->supported_mcs_set[4] = 0x0; + if (fw_info.usr_dev_mcs_support == HT_STREAM_MODE_2X2) + sys_cfg->supported_mcs_set[1] = 0xFF; + else + sys_cfg->supported_mcs_set[1] = 0; + } + +done: + LEAVE(); + return status; +} + +#define VHT_CAP_11AC_MASK 0x007fffff + +/** + * @brief enable/disable 11AC + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_DISABLE or MLAN_ACT_ENABLE + * @param vht20_40 Enable VHT 20 MHz or 40 MHz band + * @param vhtcap_ie A pointer to vht capability IE + * + * @return 0--success, otherwise failure + */ +int woal_uap_set_11ac_status(moal_private *priv, t_u8 action, t_u8 vht20_40, + IEEEtypes_VHTCap_t *vhtcap_ie) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11ac_cfg *cfg_11ac = NULL; + mlan_fw_info fw_info; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ac_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + cfg_11ac = (mlan_ds_11ac_cfg *)req->pbuf; + cfg_11ac->sub_command = MLAN_OID_11AC_VHT_CFG; + req->req_id = MLAN_IOCTL_11AC_CFG; + req->action = MLAN_ACT_SET; + + cfg_11ac->param.vht_cfg.band = BAND_SELECT_A; + cfg_11ac->param.vht_cfg.txrx = MLAN_RADIO_TXRX; + + /* + * p2p GO (negotiation or auto GO) cases, wpa_supplicant will download + * invalid vht capability with value 0 in beacon parameters, so for p2p + * GO case (vht_cap_info = 0), driver will use hardware 11ac vht + * capability value instead of up layer value. + */ + if (vhtcap_ie && vhtcap_ie->vht_cap.vht_cap_info != 0) { + cfg_11ac->param.vht_cfg.vht_cap_info = + woal_le32_to_cpu(vhtcap_ie->vht_cap.vht_cap_info); + /** todo mcs configuration */ + } else { + cfg_11ac->param.vht_cfg.vht_cap_info = + fw_info.usr_dot_11ac_dev_cap_a; + } + if (action == MLAN_ACT_DISABLE) { + cfg_11ac->param.vht_cfg.bwcfg = MFALSE; + cfg_11ac->param.vht_cfg.vht_cap_info &= ~VHT_CAP_11AC_MASK; + cfg_11ac->param.vht_cfg.vht_rx_mcs = + cfg_11ac->param.vht_cfg.vht_tx_mcs = 0xffff; + cfg_11ac->param.vht_cfg.skip_usr_11ac_mcs_cfg = MTRUE; + } else { + if (vht20_40) + cfg_11ac->param.vht_cfg.bwcfg = MFALSE; + else + cfg_11ac->param.vht_cfg.bwcfg = MTRUE; + cfg_11ac->param.vht_cfg.vht_cap_info &= + ~DEFALUT_11AC_CAP_BEAMFORMING_RESET_MASK; + cfg_11ac->param.vht_cfg.vht_tx_mcs = + fw_info.usr_dot_11ac_mcs_support >> 16; + cfg_11ac->param.vht_cfg.vht_rx_mcs = + fw_info.usr_dot_11ac_mcs_support & 0xffff; + cfg_11ac->param.vht_cfg.skip_usr_11ac_mcs_cfg = MTRUE; + } + PRINTM(MCMND, + "Uap:11ac=%d vht_cap_info=0x%x, vht_tx_mcs=0x%x, vht_rx_mcs=0x%x\n", + action, cfg_11ac->param.vht_cfg.vht_cap_info, + cfg_11ac->param.vht_cfg.vht_tx_mcs, + cfg_11ac->param.vht_cfg.vht_rx_mcs); + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get/Set 11ax cfg + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param he_cfg a pointer to mlan_ds_11ax_he_cfg + * + * @return 0--success, otherwise failure + */ +int woal_11ax_cfg(moal_private *priv, t_u8 action, mlan_ds_11ax_he_cfg *he_cfg) +{ + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ioctl_req *req = NULL; + mlan_ds_11ax_cfg *cfg_11ax = NULL; + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ax_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + cfg_11ax = (mlan_ds_11ax_cfg *)req->pbuf; + cfg_11ax->sub_command = MLAN_OID_11AX_HE_CFG; + req->req_id = MLAN_IOCTL_11AX_CFG; + req->action = action; + moal_memcpy_ext(priv->phandle, &cfg_11ax->param.he_cfg, he_cfg, + sizeof(mlan_ds_11ax_he_cfg), + sizeof(mlan_ds_11ax_he_cfg)); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, he_cfg, &cfg_11ax->param.he_cfg, + sizeof(mlan_ds_11ax_he_cfg), + sizeof(mlan_ds_11ax_he_cfg)); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief enable/disable 11AX + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_DISABLE or MLAN_ACT_ENABLE + * @param band band config + * @param hecap_ie + * + * @return 0--success, otherwise failure + */ +int woal_uap_set_11ax_status(moal_private *priv, t_u8 action, t_u8 band, + IEEEtypes_HECap_t *hecap_ie) +{ + mlan_fw_info fw_info; + int ret = 0; + mlan_ds_11ax_he_cfg he_cfg; + ENTER(); + + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if ((band == BAND_5GHZ && !(fw_info.fw_bands & BAND_AAX)) || + (band == BAND_2GHZ && !(fw_info.fw_bands & BAND_GAX))) { + PRINTM(MERROR, "fw doesn't support 11ax\n"); + ret = -EFAULT; + goto done; + } + memset(&he_cfg, 0, sizeof(he_cfg)); + if (band == BAND_5GHZ) + he_cfg.band = MBIT(1); + else if (band == BAND_2GHZ) + he_cfg.band = MBIT(0); + else { + PRINTM(MERROR, "Invalid band!\n"); + ret = -EFAULT; + goto done; + } + if (woal_11ax_cfg(priv, MLAN_ACT_GET, &he_cfg)) { + PRINTM(MERROR, "Fail to get 11ax cfg!\n"); + ret = -EFAULT; + goto done; + } + if (hecap_ie) { + DBG_HEXDUMP(MCMD_D, "hecap_ie", (t_u8 *)hecap_ie, + hecap_ie->ieee_hdr.len + + sizeof(IEEEtypes_Header_t)); + he_cfg.he_cap.id = hecap_ie->ieee_hdr.element_id; + he_cfg.he_cap.len = hecap_ie->ieee_hdr.len; + moal_memcpy_ext(priv->phandle, &he_cfg.he_cap.ext_id, + &hecap_ie->ext_id, he_cfg.he_cap.len, + he_cfg.he_cap.len); + } + if (action == MLAN_ACT_DISABLE) { + if (he_cfg.he_cap.len && + (he_cfg.he_cap.ext_id == HE_CAPABILITY)) { + memset(he_cfg.he_cap.he_txrx_mcs_support, 0xff, + sizeof(he_cfg.he_cap.he_txrx_mcs_support)); + } else { + PRINTM(MCMND, "11ax already disabled\n"); + goto done; + } + } + DBG_HEXDUMP(MCMD_D, "HE_CFG ", (t_u8 *)&he_cfg, sizeof(he_cfg)); + ret = woal_11ax_cfg(priv, MLAN_ACT_SET, &he_cfg); +done: + LEAVE(); + return ret; +} + +/** + * @brief Parse AP configuration from ASCII string + * + * @param priv A pointer to moal_private structure + * @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(moal_private *priv, mlan_uap_bss_param *ap_cfg, + char *buf) +{ + int ret = 0, atoi_ret; + int set_sec = 0, set_key = 0, set_chan = 0; + int set_preamble = 0, set_scb = 0, set_ssid = 0; + char *begin = buf, *value = NULL, *opt = NULL; + + ENTER(); + + while (begin) { + value = woal_strsep(&begin, ',', '/'); + opt = woal_strsep(&value, '=', '/'); + if (opt && !strncmp(opt, "END", strlen("END"))) { + if (!ap_cfg->ssid.ssid_len) { + PRINTM(MERROR, + "Minimum option required is SSID\n"); + ret = -EINVAL; + goto done; + } + PRINTM(MINFO, "Parsing terminated by string END\n"); + break; + } + if (!opt || !value || !value[0]) { + PRINTM(MERROR, "Invalid option\n"); + ret = -EINVAL; + goto done; + } else if (!strncmp(opt, "ASCII_CMD", strlen("ASCII_CMD"))) { + if (strncmp(value, "AP_CFG", strlen("AP_CFG"))) { + PRINTM(MERROR, + "ASCII_CMD: %s not matched with AP_CFG\n", + value); + ret = -EFAULT; + goto done; + } + value = woal_strsep(&begin, ',', '/'); + opt = woal_strsep(&value, '=', '/'); + if (!opt || !value || !value[0]) { + PRINTM(MERROR, + "Minimum option required is SSID\n"); + ret = -EINVAL; + goto done; + } else if (!strncmp(opt, "SSID", strlen("SSID"))) { + if (set_ssid) { + PRINTM(MWARN, + "Skipping SSID, found again!\n"); + continue; + } + if (strlen(value) >= MLAN_MAX_SSID_LENGTH) { + PRINTM(MERROR, + "SSID length exceeds max length\n"); + ret = -EFAULT; + goto done; + } + ap_cfg->ssid.ssid_len = strlen(value); + strncpy((char *)ap_cfg->ssid.ssid, value, + MIN(MLAN_MAX_SSID_LENGTH - 1, + strlen(value))); + PRINTM(MINFO, "ssid=%s, len=%d\n", + ap_cfg->ssid.ssid, + (int)ap_cfg->ssid.ssid_len); + set_ssid = 1; + } else { + PRINTM(MERROR, + "AP_CFG: Invalid option %s, expect SSID\n", + opt); + ret = -EINVAL; + goto done; + } + } else if (!strncmp(opt, "SEC", strlen("SEC"))) { + if (set_sec) { + PRINTM(MWARN, "Skipping SEC, found again!\n"); + continue; + } + if (!strnicmp(value, "open", strlen("open"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + if (set_key) + ap_cfg->wpa_cfg.length = 0; + ap_cfg->key_mgmt = KEY_MGMT_NONE; + ap_cfg->protocol = PROTOCOL_NO_SECURITY; + } else if (!strnicmp(value, "wpa2-psk", + strlen("wpa2-psk"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + ap_cfg->protocol = PROTOCOL_WPA2; + ap_cfg->key_mgmt = KEY_MGMT_PSK; + ap_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_AES_CCMP; + ap_cfg->wpa_cfg.pairwise_cipher_wpa2 = + CIPHER_AES_CCMP; + ap_cfg->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + } else if (!strnicmp(value, "wpa-psk", + strlen("wpa-psk"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + ap_cfg->protocol = PROTOCOL_WPA; + ap_cfg->key_mgmt = KEY_MGMT_PSK; + ap_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_TKIP; + ap_cfg->wpa_cfg.group_cipher = CIPHER_TKIP; + } else if (!strnicmp(value, "wep128", + strlen("wep128"))) { + ap_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + if (set_key) + ap_cfg->wpa_cfg.length = 0; + ap_cfg->key_mgmt = KEY_MGMT_NONE; + ap_cfg->protocol = PROTOCOL_STATIC_WEP; + } else { + PRINTM(MERROR, + "AP_CFG: Invalid value=%s for %s\n", + value, opt); + ret = -EFAULT; + goto done; + } + set_sec = 1; + } else if (!strncmp(opt, "KEY", strlen("KEY"))) { + if (set_key) { + PRINTM(MWARN, "Skipping KEY, found again!\n"); + continue; + } + if (set_sec && + ap_cfg->protocol == PROTOCOL_STATIC_WEP) { + if (strlen(value) != MAX_WEP_KEY_SIZE) { + PRINTM(MERROR, + "Invalid WEP KEY length\n"); + ret = -EFAULT; + goto done; + } + ap_cfg->wep_cfg.key0.key_index = 0; + ap_cfg->wep_cfg.key0.is_default = 1; + ap_cfg->wep_cfg.key0.length = strlen(value); + moal_memcpy_ext( + priv->phandle, ap_cfg->wep_cfg.key0.key, + value, strlen(value), + sizeof(ap_cfg->wep_cfg.key0.key)); + 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); + moal_memcpy_ext(priv->phandle, + ap_cfg->wpa_cfg.passphrase, value, + strlen(value), + sizeof(ap_cfg->wpa_cfg.passphrase)); + set_key = 1; + } else if (!strncmp(opt, "CHANNEL", strlen("CHANNEL"))) { + if (set_chan) { + PRINTM(MWARN, + "Skipping CHANNEL, found again!\n"); + continue; + } + if (woal_atoi(&atoi_ret, value)) { + ret = -EINVAL; + goto done; + } + if (atoi_ret < 1 || atoi_ret > MLAN_MAX_CHANNEL) { + PRINTM(MERROR, + "AP_CFG: Channel must be between 1 and %d" + "(both included)\n", + MLAN_MAX_CHANNEL); + ret = -EINVAL; + goto done; + } + ap_cfg->channel = atoi_ret; + set_chan = 1; + } else if (!strncmp(opt, "PREAMBLE", strlen("PREAMBLE"))) { + if (set_preamble) { + PRINTM(MWARN, + "Skipping PREAMBLE, found again!\n"); + continue; + } + if (woal_atoi(&atoi_ret, value)) { + ret = -EINVAL; + goto done; + } + /* This is a READ only value from FW, so we + * can not set this and pass it successfully */ + set_preamble = 1; + } else if (!strncmp(opt, "MAX_SCB", strlen("MAX_SCB"))) { + if (set_scb) { + PRINTM(MWARN, + "Skipping MAX_SCB, found again!\n"); + continue; + } + if (woal_atoi(&atoi_ret, value)) { + ret = -EINVAL; + goto done; + } + if (atoi_ret < 1 || atoi_ret > MAX_STA_COUNT) { + PRINTM(MERROR, + "AP_CFG: MAX_SCB must be between 1 to %d " + "(both included)\n", + MAX_STA_COUNT); + ret = -EINVAL; + goto done; + } + ap_cfg->max_sta_count = (t_u16)atoi_ret; + set_scb = 1; + } else { + PRINTM(MERROR, "Invalid option %s\n", opt); + ret = -EINVAL; + goto done; + } + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set AP configuration + * + * @param priv A pointer to moal_private structure + * @param data A pointer to user data + * @param len Length of buf + * + * @return 0 --success, otherwise fail + */ +int woal_uap_set_ap_cfg(moal_private *priv, t_u8 *data, int len) +{ + int ret = 0; + static char buf[MAX_BUF_LEN]; + mlan_uap_bss_param *sys_config = NULL; + int restart = 0; + + ENTER(); + +#define MIN_AP_CFG_CMD_LEN 16 /* strlen("ASCII_CMD=AP_CFG") */ + if ((len - 1) <= MIN_AP_CFG_CMD_LEN) { + PRINTM(MERROR, "Invalid length of command\n"); + ret = -EINVAL; + goto done; + } + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + memset(buf, 0, MAX_BUF_LEN); + moal_memcpy_ext(priv->phandle, buf, data, len, sizeof(buf) - 1); + + /* Initialize the uap bss values which are uploaded from firmware */ + woal_uap_get_bss_param(priv, sys_config, MOAL_IOCTL_WAIT); + + /* Setting the default values */ + sys_config->channel = 6; + sys_config->preamble_type = 0; + + ret = woal_uap_ap_cfg_parse_data(priv, sys_config, buf); + if (ret) + goto done; + + /* If BSS already started stop it first and restart + * after changing the setting */ + if (priv->bss_started == MTRUE) { + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + if (ret) + goto done; + restart = 1; + } + + /* If the security mode is configured as WEP or WPA-PSK, + * it will disable 11n automatically, and if configured as + * open(off) or wpa2-psk, it will automatically enable 11n */ + if ((sys_config->protocol == PROTOCOL_STATIC_WEP) || + (sys_config->protocol == PROTOCOL_WPA)) { + if (MLAN_STATUS_SUCCESS != + woal_uap_set_11n_status(priv, sys_config, + MLAN_ACT_DISABLE)) { + ret = -EFAULT; + goto done; + } + } else { + if (MLAN_STATUS_SUCCESS != + woal_uap_set_11n_status(priv, sys_config, + MLAN_ACT_ENABLE)) { + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_config)) { + ret = -EFAULT; + goto done; + } + + /* Start the BSS after successful configuration */ + if (restart) + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_START); + +done: + kfree(sys_config); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get ap scan channel list + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param scan_channels A pointer to mlan_uap_scan_channels structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_set_get_ap_scan_channels(moal_private *priv, t_u16 action, + mlan_uap_scan_channels *scan_channels) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_SCAN_CHANNELS; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + + moal_memcpy_ext(priv->phandle, &bss->param.ap_scan_channels, + scan_channels, sizeof(mlan_uap_scan_channels), + sizeof(bss->param.ap_scan_channels)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, scan_channels, + &bss->param.ap_scan_channels, + sizeof(mlan_uap_scan_channels), + sizeof(mlan_uap_scan_channels)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get uap channel + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param wait_option wait option + * @param uap_channel A pointer to mlan_uap_channel structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_set_get_ap_channel(moal_private *priv, t_u16 action, + t_u8 wait_option, + chan_band_info *uap_channel) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + + moal_memcpy_ext(priv->phandle, &bss->param.ap_channel, uap_channel, + sizeof(chan_band_info), sizeof(bss->param.ap_channel)); + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, uap_channel, + &bss->param.ap_channel, sizeof(chan_band_info), + sizeof(chan_band_info)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief start ACS scan + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_start_acs_scan(moal_private *priv) +{ + 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_ACS_SCAN; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + PRINTM(MIOCTL, + "ACS scan done: bandcfg:[chanBand=0x%x chanWidth=0x%x chan2Offset=0x%x scanMode=0x%x], channel=%d\n", + bss->param.ap_acs_scan.bandcfg.chanBand, + bss->param.ap_acs_scan.bandcfg.chanWidth, + bss->param.ap_acs_scan.bandcfg.chan2Offset, + bss->param.ap_acs_scan.bandcfg.scanMode, + bss->param.ap_acs_scan.chan); +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brie check if we need do ACS scan + * + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_do_acs_check(moal_private *priv) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_uap_bss_param *sys_config = NULL; + mlan_uap_scan_channels *scan_channels = NULL; + chan_band_info uap_channel; + ENTER(); + + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + sys_config)) { + PRINTM(MERROR, "Fail to get sys config data\n"); + kfree(sys_config); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + if (!(sys_config->bandcfg.scanMode == SCAN_MODE_ACS)) { + kfree(sys_config); + LEAVE(); + return ret; + } + scan_channels = kzalloc(sizeof(mlan_uap_scan_channels), GFP_ATOMIC); + if (scan_channels == NULL) { + PRINTM(MERROR, "Fail to alloc scan channels buffer\n"); + kfree(sys_config); + LEAVE(); + return MLAN_STATUS_FAILURE; + } + scan_channels->remove_nop_channel = MTRUE; + if (MLAN_STATUS_SUCCESS != + woal_set_get_ap_scan_channels(priv, MLAN_ACT_GET, scan_channels)) { + PRINTM(MERROR, "Fail to get scan channels\n"); + goto done; + } + + if (scan_channels->num_remvoed_channel && scan_channels->num_of_chan) { + scan_channels->remove_nop_channel = 0; + /** set back new channel list after remove nop channels */ + if (MLAN_STATUS_SUCCESS != + woal_set_get_ap_scan_channels(priv, MLAN_ACT_SET, + scan_channels)) { + PRINTM(MERROR, "Fail to get scan channels\n"); + goto done; + } + } + if (scan_channels->num_of_chan) + ret = woal_start_acs_scan(priv); + else + ret = MLAN_STATUS_FAILURE; + /** set to default channel 6 when 5G ACS is configured */ + if ((ret != MLAN_STATUS_SUCCESS) && + (sys_config->bandcfg.chanBand == BAND_5GHZ)) { + memset(&uap_channel, 0, sizeof(uap_channel)); + uap_channel.bandcfg.chanBand = DEFAULT_UAP_BAND; + uap_channel.channel = DEFAULT_UAP_CHANNEL; + ret = woal_set_get_ap_channel(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, &uap_channel); + } +done: + kfree(scan_channels); + kfree(sys_config); + LEAVE(); + return ret; +} + +/** + * @brief uap BSS control ioctl handler + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param data BSS control type + * @return 0 --success, otherwise fail + */ +int woal_uap_bss_ctrl(moal_private *priv, t_u8 wait_option, int data) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + PRINTM(MIOCTL, "ioctl bss ctrl=%d\n", data); + if ((data != UAP_BSS_START) && (data != UAP_BSS_STOP) && + (data != UAP_BSS_RESET)) { + PRINTM(MERROR, "Invalid parameter: %d\n", data); + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + switch (data) { + case UAP_BSS_START: + if (priv->bss_started == MTRUE) { + PRINTM(MWARN, "Warning: BSS already started!\n"); + /* goto done; */ + } else if (!priv->uap_host_based +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + || moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD) +#endif + ) { + woal_do_acs_check(priv); + /* about to start bss: issue channel check */ + status = woal_11h_channel_check_ioctl(priv, + MOAL_IOCTL_WAIT); + if (status) { + PRINTM(MMSG, "11h channel check fails\n"); + status = MLAN_STATUS_FAILURE; + ret = -1; + goto done; + } + } + bss->sub_command = MLAN_OID_BSS_START; + if (priv->uap_host_based) { + bss->param.host_based |= UAP_FLAG_HOST_BASED; +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) + bss->param.host_based |= UAP_FLAG_HOST_MLME; +#endif +#endif + } + break; + case UAP_BSS_STOP: + if (priv->bss_started == MFALSE) { + PRINTM(MWARN, "Warning: BSS already stopped!\n"); + /* This is a situation where CAC it started and BSS + * start is dealyed and before CAC timer expires BSS + * stop is triggered. + * + * Do not skip sending the BSS_STOP command since there + * are many routines triggered on BSS_STOP command + * response. + */ + woal_cancel_cac_block(priv); + } + bss->sub_command = MLAN_OID_BSS_STOP; + break; + case UAP_BSS_RESET: + bss->sub_command = MLAN_OID_UAP_BSS_RESET; + woal_cancel_cac_block(priv); + break; + } + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_FAILURE) { + ret = -EFAULT; + goto done; + } + if (data == UAP_BSS_STOP || data == UAP_BSS_RESET) { + priv->bss_started = MFALSE; + woal_stop_queue(priv->netdev); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + if (data == UAP_BSS_RESET) + woal_request_set_mac_address(priv, wait_option); + woal_flush_tcp_sess_queue(priv); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief This function sets multicast addresses to firmware + * + * @param dev A pointer to net_device structure + * @return N/A + */ +void woal_uap_set_multicast_list(struct net_device *dev) +{ + ENTER(); + + LEAVE(); +} + +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd Command + * + * @return 0 --success, otherwise fail + */ +int woal_uap_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int ret = 0; + ENTER(); + switch (cmd) { + case WOAL_ANDROID_DEF_CMD: + /** android default ioctl ID is SIOCDEVPRIVATE + 1 */ + ret = woal_android_priv_cmd(dev, req); + break; + case UAP_IOCTL_CMD: + ret = woal_uap_ioctl(dev, req); + break; + case UAP_POWER_MODE: + ret = woal_uap_power_mode_ioctl(dev, req); + break; + case UAP_BSS_CTRL: + ret = woal_uap_bss_ctrl_ioctl(dev, req); + break; + case UAP_WAPI_MSG: + ret = woal_uap_set_wapi(dev, req); + break; + case UAP_BSS_CONFIG: + ret = woal_uap_bss_cfg_ioctl(dev, req); + break; + case UAP_STA_DEAUTH: + ret = woal_uap_sta_deauth_ioctl(dev, req); + break; + case UAP_RADIO_CTL: + ret = woal_uap_radio_ctl(dev, req); + break; + case UAP_REPORT_MIC_ERR: + ret = woal_uap_report_mic_ioctl(dev, req); + break; + case UAP_SET_KEY: + ret = woal_uap_set_key_ioctl(dev, req); + break; + case UAPHOSTPKTINJECT: + ret = woal_send_host_packet(dev, req); + break; + case UAP_GET_STA_LIST: + ret = woal_uap_get_sta_list_ioctl(dev, req); + break; + case UAP_CUSTOM_IE: + ret = woal_custom_ie_ioctl(dev, req); + break; + case UAP_GET_BSS_TYPE: + ret = woal_get_bss_type(dev, req); + break; + case WOAL_ANDROID_PRIV_CMD: + ret = woal_android_priv_cmd(dev, req); + break; + default: +#ifdef UAP_WEXT + ret = woal_uap_do_priv_ioctl(dev, req, cmd); +#else + ret = -EOPNOTSUPP; +#endif + break; + } + + LEAVE(); + return ret; +} + +#ifdef CONFIG_PROC_FS +/** + * @brief Get version + * + * @param priv A pointer to moal_private structure + * @param version A pointer to version buffer + * @param max_len max length of version buffer + * + * @return N/A + */ +void woal_uap_get_version(moal_private *priv, char *version, int max_len) +{ + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return; + } + + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_VER_EXT; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status == MLAN_STATUS_SUCCESS) { + PRINTM(MINFO, "MOAL UAP VERSION: %s\n", + info->param.ver_ext.version_str); + snprintf(version, max_len, priv->phandle->driver_version, + info->param.ver_ext.version_str); + } + + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return; +} +#endif + +/** + * @brief Get uap statistics + * + * @param priv A pointer to moal_private structure + * @param wait_option Wait option + * @param ustats A pointer to mlan_ds_uap_stats structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- + * success, otherwise fail + */ +mlan_status woal_uap_get_stats(moal_private *priv, t_u8 wait_option, + mlan_ds_uap_stats *ustats) +{ + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + info = (mlan_ds_get_info *)req->pbuf; + info->sub_command = MLAN_OID_GET_STATS; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, wait_option); + if (status == MLAN_STATUS_SUCCESS) { + if (ustats) + moal_memcpy_ext(priv->phandle, ustats, + &info->param.ustats, + sizeof(mlan_ds_uap_stats), + sizeof(mlan_ds_uap_stats)); +#ifdef UAP_WEXT + priv->w_stats.discard.fragment = + info->param.ustats.fcs_error_count; + priv->w_stats.discard.retries = info->param.ustats.retry_count; + priv->w_stats.discard.misc = + info->param.ustats.ack_failure_count; +#endif + } + + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief Set/Get system configuration parameters + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param ap_wmm_para A pointer to wmm_parameter_t structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_set_get_ap_wmm_para(moal_private *priv, t_u16 action, + wmm_parameter_t *ap_wmm_para) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_CFG_WMM_PARAM; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + + if (action == MLAN_ACT_SET) + moal_memcpy_ext(priv->phandle, &bss->param.ap_wmm_para, + ap_wmm_para, sizeof(wmm_parameter_t), + sizeof(bss->param.ap_wmm_para)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + if (bss->param.ap_wmm_para.reserved != MLAN_STATUS_COMPLETE) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, ap_wmm_para, + &bss->param.ap_wmm_para, + sizeof(wmm_parameter_t), + sizeof(wmm_parameter_t)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Get system configuration parameters + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param wait_option Wait option + * @param sys_cfg A pointer to mlan_uap_bss_param structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_set_get_sys_config(moal_private *priv, t_u16 action, + t_u8 wait_option, + mlan_uap_bss_param *sys_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_BSS_CONFIG; + req->req_id = MLAN_IOCTL_BSS; + req->action = action; + + if (action == MLAN_ACT_SET) + moal_memcpy_ext(priv->phandle, &bss->param.bss_config, sys_cfg, + sizeof(mlan_uap_bss_param), + sizeof(bss->param.bss_config)); + + ret = woal_request_ioctl(priv, req, wait_option); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + + if (action == MLAN_ACT_GET) + moal_memcpy_ext(priv->phandle, sys_cfg, &bss->param.bss_config, + sizeof(mlan_uap_bss_param), + sizeof(mlan_uap_bss_param)); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set invalid data for each member of mlan_uap_bss_param + * structure + * + * @param config A pointer to mlan_uap_bss_param structure + * + * @return N/A + */ +void woal_set_sys_config_invalid_data(mlan_uap_bss_param *config) +{ + ENTER(); + + memset(config, 0, sizeof(mlan_uap_bss_param)); + config->bcast_ssid_ctl = 0x7F; + config->radio_ctl = 0x7F; + config->dtim_period = 0x7F; + config->beacon_period = 0x7FFF; + config->tx_data_rate = 0x7FFF; + config->mcbc_data_rate = 0x7FFF; + config->tx_power_level = 0x7F; + config->tx_antenna = 0x7F; + config->rx_antenna = 0x7F; + config->pkt_forward_ctl = 0x7F; + config->max_sta_count = 0x7FFF; + config->auth_mode = 0x7F; + config->sta_ageout_timer = 0x7FFFFFFF; + config->pairwise_update_timeout = 0x7FFFFFFF; + config->pwk_retries = 0x7FFFFFFF; + config->groupwise_update_timeout = 0x7FFFFFFF; + config->gwk_retries = 0x7FFFFFFF; + config->mgmt_ie_passthru_mask = 0x7FFFFFFF; + config->ps_sta_ageout_timer = 0x7FFFFFFF; + config->rts_threshold = 0x7FFF; + config->frag_threshold = 0x7FFF; + config->retry_limit = 0x7FFF; + config->filter.filter_mode = 0x7FFF; + config->filter.mac_count = 0x7FFF; + config->wpa_cfg.rsn_protection = 0x7F; + config->wpa_cfg.gk_rekey_time = 0x7FFFFFFF; + config->enable_2040coex = 0x7F; + config->wmm_para.qos_info = 0x7F; + + LEAVE(); +} diff --git a/mxm_wifiex/wlan_src/mlinux/moal_uap.h b/mxm_wifiex/wlan_src/mlinux/moal_uap.h new file mode 100644 index 0000000..b60dfef --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_uap.h @@ -0,0 +1,581 @@ +/** @file moal_uap.h + * + * @brief This file contains uap driver specific defines etc. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 02/02/2009: initial version +********************************************************/ + +#ifndef _MOAL_UAP_H +#define _MOAL_UAP_H + +/** Maximum buffer length for WOAL_UAP_SET_GET_256_CHAR */ +#define MAX_BUF_LEN 256 + +/** Private command ID to send ioctl */ +#define UAP_IOCTL_CMD (SIOCDEVPRIVATE + 2) +/** Updating ADDBA variables */ +#define UAP_ADDBA_PARA 0 +/** Updating priority table for AMPDU/AMSDU */ +#define UAP_AGGR_PRIOTBL 1 +/** Updating addbareject table */ + +#define UAP_ADDBA_REJECT 2 +/** Get FW INFO */ +#define UAP_FW_INFO 4 +/** Updating Deep sleep variables */ +#define UAP_DEEP_SLEEP 3 +/** Tx data pause subcommand */ +#define UAP_TX_DATA_PAUSE 5 +#ifdef SDIO +/** sdcmd52 read write subcommand */ +#define UAP_SDCMD52_RW 6 +#endif +/** 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 +/** dfs testing subcommand */ +#define UAP_DFS_TESTING 10 +/** 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 + +#define UAP_DFS_REPEATER_MODE 16 + +#define UAP_CAC_TIMER_STATUS 17 + +/** Skip CAC */ +#define UAP_SKIP_CAC 18 + +#define UAP_HT_TX_CFG 19 + +#define UAP_VHT_CFG 20 + +#define UAP_HT_STREAM_CFG 21 + +#define UAP_OPERATION_CTRL 22 + +#define UAP_CHAN_SWITCH_COUNT_CFG 23 +#define UAP_BAND_STEER 24 + +#define UAP_BEACON_STUCK_DETECT 25 + +/** Private command ID to Power Mode */ +#define UAP_POWER_MODE (SIOCDEVPRIVATE + 3) + +/** Private command id to start/stop/reset bss */ +#define UAP_BSS_CTRL (SIOCDEVPRIVATE + 4) +/** BSS START */ +#define UAP_BSS_START 0 +/** BSS STOP */ +#define UAP_BSS_STOP 1 +/** BSS RESET */ +#define UAP_BSS_RESET 2 + +/** wapi_msg */ +typedef struct _wapi_msg { + /** message type */ + t_u16 msg_type; + /** message len */ + t_u16 msg_len; + /** message */ + t_u8 msg[96]; +} wapi_msg; + +/* wapi key msg */ +typedef struct _wapi_key_msg { + /** mac address */ + t_u8 mac_addr[MLAN_MAC_ADDR_LENGTH]; + /** pad */ + t_u8 pad; + /** key id */ + t_u8 key_id; + /** key */ + t_u8 key[32]; +} wapi_key_msg; + +/** Private command ID to set wapi info */ +#define UAP_WAPI_MSG (SIOCDEVPRIVATE + 10) +/** set wapi flag */ +#define P80211_PACKET_WAPIFLAG 0x0001 +/** set wapi key */ +#define P80211_PACKET_SETKEY 0x0003 +/** wapi mode psk */ +#define WAPI_MODE_PSK 0x04 +/** wapi mode certificate */ +#define WAPI_MODE_CERT 0x08 + +typedef struct _tx_rate_cfg_t { + /** sub command */ + int subcmd; + /** Action */ + int action; + /** Rate format */ + int rate_format; + /** Rate configured */ + int rate; + /** nss */ + int nss; + /** user_data_cnt */ + int user_data_cnt; + /** Rate bitmap */ + t_u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + /** Rate Setting */ + t_u16 rate_setting; +} 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; + +/** htstream_cfg structure */ +typedef struct _htstream_cfg_t { + /** Subcommand */ + int subcmd; + /** Action */ + int action; + /** HT stream configuration */ + t_u32 stream_cfg; +} htstream_cfg_t; + +/* dfs repeater mode */ +typedef struct _dfs_repeater_mode { + /** subcmd */ + t_u32 subcmd; + /** set/get */ + t_u32 action; + /** mode */ + t_u32 mode; +} dfs_repeater_mode; + +/* */ +typedef struct _cac_timer_status { + /** subcmd */ + t_u32 subcmd; + /** set/get */ + t_u32 action; + /** mode */ + t_u32 mode; +} cac_timer_status; + +/** skip_cac parameters */ +typedef struct _skip_cac_para { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** enable/disable deepsleep*/ + t_u16 skip_cac; +} skip_cac_para; + +/** radio control command */ +#define UAP_RADIO_CTL (SIOCDEVPRIVATE + 5) + +/** Private command ID to BSS config */ +#define UAP_BSS_CONFIG (SIOCDEVPRIVATE + 6) + +/** deauth station */ +#define UAP_STA_DEAUTH (SIOCDEVPRIVATE + 7) + +/** enable UAP report mic error */ +#define UAP_REPORT_MIC_ERR (SIOCDEVPRIVATE + 8) +/** uap set key */ +#define UAP_SET_KEY (SIOCDEVPRIVATE + 9) +/** encrypt key */ +typedef struct _encrypt_key { + /** Key index */ + t_u32 key_index; + /** 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]; +} encrypt_key; + +/** pkt_header */ +typedef struct _pkt_header { + /** pkt_len */ + u32 pkt_len; + /** pkt_type */ + u32 TxPktType; + /** tx control */ + u32 TxControl; +} pkt_header; +/** uap get station list */ +#define UAP_GET_STA_LIST (SIOCDEVPRIVATE + 11) +/** Packet inject command ioctl number */ +#define UAPHOSTPKTINJECT WOAL_MGMT_FRAME_TX_IOCTL + +/** Private command ID to set/get custom IE buffer */ +#define UAP_CUSTOM_IE (SIOCDEVPRIVATE + 13) + +/** HS WAKE UP event id */ +#define UAP_EVENT_ID_HS_WAKEUP 0x80000001 +/** HS_ACTIVATED event id */ +#define UAP_EVENT_ID_DRV_HS_ACTIVATED 0x80000002 +/** HS DEACTIVATED event id */ +#define UAP_EVENT_ID_DRV_HS_DEACTIVATED 0x80000003 + +/** Host sleep flag set */ +#define HS_CFG_FLAG_GET 0 +/** Host sleep flag get */ +#define HS_CFG_FLAG_SET 1 +/** Host sleep flag for condition */ +#define HS_CFG_FLAG_CONDITION 2 +/** Host sleep flag for GPIO */ +#define HS_CFG_FLAG_GPIO 4 +/** Host sleep flag for Gap */ +#define HS_CFG_FLAG_GAP 8 +/** Host sleep flag for all */ +#define HS_CFG_FLAG_ALL 0x0f +/** Host sleep mask to get condition */ +#define HS_CFG_CONDITION_MASK 0x0f + +/** ds_hs_cfg */ +typedef struct _ds_hs_cfg { + /** subcmd */ + t_u32 subcmd; + /** Bit0: 0 - Get, 1 Set + * Bit1: 1 - conditions is valid + * Bit2: 2 - gpio is valid + * Bit3: 3 - gap is valid + */ + t_u32 flags; + /** Host sleep config condition */ + /** Bit0: non-unicast data + * Bit1: unicast data + * Bit2: mac events + * Bit3: magic packet + */ + t_u32 conditions; + /** GPIO */ + t_u32 gpio; + /** Gap in milliseconds */ + t_u32 gap; +} ds_hs_cfg; + +/** Private command ID to get BSS type */ +#define UAP_GET_BSS_TYPE (SIOCDEVPRIVATE + 15) + +/** addba_param */ +typedef struct _addba_param { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** block ack timeout for ADDBA request */ + t_u32 timeout; + /** Buffer size for ADDBA request */ + t_u32 txwinsize; + /** Buffer size for ADDBA response */ + t_u32 rxwinsize; + /** amsdu for ADDBA request */ + t_u8 txamsdu; + /** amsdu for ADDBA response */ + t_u8 rxamsdu; +} addba_param; + +/** aggr_prio_tbl */ +typedef struct _aggr_prio_tbl { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** ampdu priority table */ + t_u8 ampdu[MAX_NUM_TID]; + /** amsdu priority table */ + t_u8 amsdu[MAX_NUM_TID]; +} aggr_prio_tbl; + +/** addba_reject parameters */ +typedef struct _addba_reject_para { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** BA Reject paramters */ + t_u8 addba_reject[MAX_NUM_TID]; +} addba_reject_para; + +/** fw_info */ +typedef struct _fw_info { + /** subcmd */ + t_u32 subcmd; + /** Get */ + t_u32 action; + /** Firmware release number */ + t_u32 fw_release_number; + /** Device support for MIMO abstraction of MCSs */ + t_u8 hw_dev_mcs_support; + /** fw_bands*/ + t_u8 fw_bands; + /** Region Code */ + t_u16 region_code; + /** 802.11n device capabilities */ + t_u32 hw_dot_11n_dev_cap; +} fw_info; + +typedef struct _ht_tx_cfg_para_hdr { + /** Sub command */ + t_u32 subcmd; + /** Action: Set/Get */ + t_u32 action; +} ht_tx_cfg_para_hdr; + +typedef struct _tx_bf_cfg_para_hdr { + /** Sub command */ + t_u32 subcmd; + /** Action: Set/Get */ + t_u32 action; +} tx_bf_cfg_para_hdr; + +typedef struct _vht_cfg_para_hdr { + /** Sub command */ + t_u32 subcmd; + /** Action: Set/Get */ + t_u32 action; +} vht_cfg_para_hdr; + +typedef struct _uap_oper_para_hdr { + /** Sub command */ + t_u32 subcmd; + /** Action: Set/Get */ + t_u32 action; +} uap_oper_para_hdr; + +#ifdef SDIO +/** 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; +#endif + +/** 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; + +/** band_steering parameters */ +typedef struct _band_steer_para { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u8 action; + /** enable/disable band steering*/ + t_u8 state; + /** Probe Response will be blocked to 2G channel for first + * block_2g_prb_req probe requests*/ + t_u8 block_2g_prb_req; + /** When band steering is enabled, limit the btm request sent to STA at + * */ + t_u8 max_btm_req_allowed; + +} band_steer_para; + +/** beacon stuck detect mechanism parameters */ +typedef struct _beacon_stuck_detect_para { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u8 action; + /** No of beacon interval after which firmware will check if beacon Tx + * is going fine */ + t_u8 beacon_stuck_detect_count; + /** Upon performing MAC reset, no of beacon interval after which + * firmware will check if recovery was successful */ + t_u8 recovery_confirm_count; +} beacon_stuck_detect_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[]; +} 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 + +/** dfs_testing parameters */ +typedef struct _dfs_testing_param { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** user CAC period (msec) */ + t_u32 usr_cac_period; + /** user NOP period (sec) */ + t_u16 usr_nop_period; + /** don't change channel on radar */ + t_u8 no_chan_change; + /** fixed channel to change to on radar */ + t_u8 fixed_new_chan; + /** CAC restart */ + t_u8 cac_restart; +} dfs_testing_para; + +/** Channel switch count config */ +typedef struct _cscount_cfg_t { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** user channel switch count */ + t_u8 cs_count; +} cscount_cfg_t; + +/** domain_info parameters */ +typedef struct _domain_info_param { + /** subcmd */ + t_u32 subcmd; + /** Set/Get */ + t_u32 action; + /** domain_param TLV (incl. header) */ + t_u8 tlv[]; +} domain_info_para; + +/** DOMAIN_INFO param sizes */ +#define TLV_HEADER_LEN (2 + 2) +#define SUB_BAND_LEN 3 +#define MAX_SUB_BANDS 40 + +/** MAX domain TLV length */ +#define MAX_DOMAIN_TLV_LEN \ + (TLV_HEADER_LEN + COUNTRY_CODE_LEN + (SUB_BAND_LEN * MAX_SUB_BANDS)) + +int woal_set_get_uap_power_mode(moal_private *priv, t_u32 action, + mlan_ds_ps_mgmt *ps_mgmt); +void woal_uap_set_multicast_list(struct net_device *dev); +int woal_uap_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd); +int woal_uap_bss_ctrl(moal_private *priv, t_u8 wait_option, int data); +#ifdef UAP_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +int woal_uap_get_channel_nop_info(moal_private *priv, t_u8 wait_option, + pmlan_ds_11h_chan_nop_info ch_info); +#endif +#endif +mlan_status woal_set_get_ap_channel(moal_private *priv, t_u16 action, + t_u8 wait_option, + pchan_band_info uap_channel); +#ifdef CONFIG_PROC_FS +void woal_uap_get_version(moal_private *priv, char *version, int max_len); +#endif +mlan_status woal_uap_get_stats(moal_private *priv, t_u8 wait_option, + pmlan_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(pmlan_uap_bss_param config); +/** Set/Get system configuration parameters */ +mlan_status woal_set_get_sys_config(moal_private *priv, t_u16 action, + t_u8 wait_option, + mlan_uap_bss_param *sys_cfg); +/** Set get AP wmm parameter */ +mlan_status woal_set_get_ap_wmm_para(moal_private *priv, t_u16 action, + wmm_parameter_t *ap_wmm_para); +int woal_uap_set_ap_cfg(moal_private *priv, t_u8 *data, int len); +int woal_uap_set_11ac_status(moal_private *priv, t_u8 action, t_u8 vht20_40, + IEEEtypes_VHTCap_t *vhtcap_ie); +int woal_11ax_cfg(moal_private *priv, t_u8 action, mlan_ds_11ax_he_cfg *he_cfg); +int woal_uap_set_11ax_status(moal_private *priv, t_u8 action, t_u8 band, + IEEEtypes_HECap_t *hecap_ie); +int woal_set_uap_ht_tx_cfg(moal_private *priv, Band_Config_t bandcfg, + t_u16 ht_cap, t_u8 en); +mlan_status woal_uap_set_11n_status(moal_private *priv, + mlan_uap_bss_param *sys_cfg, t_u8 action); +#ifdef UAP_WEXT +void woal_ioctl_get_uap_info_resp(moal_private *priv, pmlan_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/mxm_wifiex/wlan_src/mlinux/moal_uap_cfg80211.c b/mxm_wifiex/wlan_src/mlinux/moal_uap_cfg80211.c new file mode 100644 index 0000000..f065de4 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_uap_cfg80211.c @@ -0,0 +1,3259 @@ +/** @file moal_uap_cfg80211.c + * + * @brief This file contains the functions for uAP CFG80211. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include "moal_cfg80211.h" +#include "moal_uap_cfg80211.h" +/** deauth reason code */ +#define REASON_CODE_DEAUTH_LEAVING 3 +/******************************************************** + Local Variables +********************************************************/ + +/******************************************************** + Global Variables +********************************************************/ +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ +/** + * @brief send deauth to station + * + * @param A pointer to moal_private + * @param mac A pointer to station mac address + * @param reason_code ieee deauth reason code + * @return 0 -- success, otherwise fail + */ +static int woal_deauth_station(moal_private *priv, u8 *mac_addr, + u16 reason_code) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_bss *bss = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)ioctl_req->pbuf; + bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; + ioctl_req->req_id = MLAN_IOCTL_BSS; + ioctl_req->action = MLAN_ACT_SET; + + moal_memcpy_ext(priv->phandle, bss->param.deauth_param.mac_addr, + mac_addr, MLAN_MAC_ADDR_LENGTH, + sizeof(bss->param.deauth_param.mac_addr)); + bss->param.deauth_param.reason_code = reason_code; + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief send deauth to all station + * + * @param A pointer to moal_private + * @param mac A pointer to station mac address + * + * @return 0 -- success, otherwise fail + */ +static int woal_deauth_all_station(moal_private *priv) +{ + int ret = -EFAULT; + int i = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return 0; + } + PRINTM(MIOCTL, "del all station\n"); + /* Allocate an IOCTL request buffer */ + ioctl_req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + goto done; + if (!info->param.sta_list.sta_count) + goto done; + for (i = 0; i < info->param.sta_list.sta_count; i++) { + PRINTM(MIOCTL, "deauth station " MACSTR "\n", + MAC2STR(info->param.sta_list.info[i].mac_address)); + ret = woal_deauth_station( + priv, info->param.sta_list.info[i].mac_address, + REASON_CODE_DEAUTH_LEAVING); + } + woal_sched_timeout(200); +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + return ret; +} + +/** + * @brief Verify RSN IE + * + * @param rsn_ie Pointer 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; + sys_config->key_mgmt = 0; + /* check the group cipher */ + switch (rsn_ie->group_cipher.type) { + case WPA_CIPHER_TKIP: + sys_config->wpa_cfg.group_cipher = CIPHER_TKIP; + break; + case WPA_CIPHER_AES_CCM: + sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + break; + default: + break; + } + count = le16_to_cpu(rsn_ie->pairwise_cipher.count); + for (i = 0; i < count; i++) { + switch (rsn_ie->pairwise_cipher.list[i].type) { + case WPA_CIPHER_TKIP: + sys_config->wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_TKIP; + break; + case WPA_CIPHER_AES_CCM: + sys_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_AES_CCMP; + break; + default: + break; + } + } + left -= sizeof(IEEEtypes_Rsn_t) + (count - 1) * sizeof(wpa_suite); + if (left < sizeof(wpa_suite_auth_key_mgmt_t)) + return MFALSE; + key_mgmt = + (wpa_suite_auth_key_mgmt_t *)((u8 *)rsn_ie + + sizeof(IEEEtypes_Rsn_t) + + (count - 1) * sizeof(wpa_suite)); + count = le16_to_cpu(key_mgmt->count); + if (left < (sizeof(wpa_suite_auth_key_mgmt_t) + + (count - 1) * sizeof(wpa_suite))) + return MFALSE; + for (i = 0; i < count; i++) { + switch (key_mgmt->list[i].type) { + case RSN_AKM_8021X: + sys_config->key_mgmt |= KEY_MGMT_EAP; + break; + case RSN_AKM_PSK: + sys_config->key_mgmt |= KEY_MGMT_PSK; + break; + case RSN_AKM_PSK_SHA256: + sys_config->key_mgmt |= KEY_MGMT_PSK_SHA256; + break; + case RSN_AKM_SAE: + sys_config->key_mgmt |= KEY_MGMT_SAE; + break; + case RSN_AKM_OWE: + sys_config->key_mgmt |= KEY_MGMT_OWE; + break; + } + } + return MTRUE; +} + +/** + * @brief Verify WPA IE + * + * @param wpa_ie Pointer WPA IE + * @param sys_config Pointer to mlan_uap_bss_param structure + * + * @return MTRUE/MFALSE + */ +static t_u8 woal_check_wpa_ie(IEEEtypes_Wpa_t *wpa_ie, + mlan_uap_bss_param *sys_config) +{ + int left = 0; + int count = 0; + int i = 0; + wpa_suite_auth_key_mgmt_t *key_mgmt = NULL; + left = wpa_ie->len + 2; + if (left < sizeof(IEEEtypes_Wpa_t)) + return MFALSE; + sys_config->wpa_cfg.group_cipher = 0; + sys_config->wpa_cfg.pairwise_cipher_wpa = 0; + switch (wpa_ie->group_cipher.type) { + case WPA_CIPHER_TKIP: + sys_config->wpa_cfg.group_cipher = CIPHER_TKIP; + break; + case WPA_CIPHER_AES_CCM: + sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + break; + default: + break; + } + count = le16_to_cpu(wpa_ie->pairwise_cipher.count); + for (i = 0; i < count; i++) { + switch (wpa_ie->pairwise_cipher.list[i].type) { + case WPA_CIPHER_TKIP: + sys_config->wpa_cfg.pairwise_cipher_wpa |= CIPHER_TKIP; + break; + case WPA_CIPHER_AES_CCM: + sys_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_AES_CCMP; + break; + default: + break; + } + } + left -= sizeof(IEEEtypes_Wpa_t) + (count - 1) * sizeof(wpa_suite); + if (left < sizeof(wpa_suite_auth_key_mgmt_t)) + return MFALSE; + key_mgmt = + (wpa_suite_auth_key_mgmt_t *)((u8 *)wpa_ie + + sizeof(IEEEtypes_Wpa_t) + + (count - 1) * sizeof(wpa_suite)); + count = le16_to_cpu(key_mgmt->count); + if (left < (sizeof(wpa_suite_auth_key_mgmt_t) + + (count - 1) * sizeof(wpa_suite))) + return MFALSE; + for (i = 0; i < count; i++) { + switch (key_mgmt->list[i].type) { + case RSN_AKM_8021X: + sys_config->key_mgmt = KEY_MGMT_EAP; + break; + case RSN_AKM_PSK: + sys_config->key_mgmt = KEY_MGMT_PSK; + break; + } + } + return MTRUE; +} + +/** + * @brief Find RSN/WPA IES + * + * @param ie Pointer IE buffer + * @param sys_config Pointer to mlan_uap_bss_param structure + * + * @return MTRUE/MFALSE + */ +static t_u8 woal_find_wpa_ies(const t_u8 *ie, int len, + mlan_uap_bss_param *sys_config) +{ + int bytes_left = len; + const t_u8 *pcurrent_ptr = ie; + t_u16 total_ie_len; + t_u8 element_len; + t_u8 wpa2 = 0; + t_u8 wpa = 0; + t_u8 ret = MFALSE; + IEEEtypes_ElementId_e element_id; + IEEEtypes_VendorSpecific_t *pvendor_ie; + const t_u8 wpa_oui[4] = {0x00, 0x50, 0xf2, 0x01}; + + while (bytes_left >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + if (bytes_left < total_ie_len) { + PRINTM(MERROR, + "InterpretIE: Error in processing IE, bytes left < IE length\n"); + bytes_left = 0; + continue; + } + switch (element_id) { + case RSN_IE: + wpa2 = woal_check_rsn_ie( + (IEEEtypes_Rsn_t *)pcurrent_ptr, sys_config); + break; + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; + if (!memcmp(pvendor_ie->vend_hdr.oui, wpa_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + (pvendor_ie->vend_hdr.oui_type == wpa_oui[3])) { + wpa = woal_check_wpa_ie( + (IEEEtypes_Wpa_t *)pcurrent_ptr, + sys_config); + } + break; + default: + break; + } + pcurrent_ptr += element_len + 2; + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + } + if (wpa && wpa2) { + sys_config->protocol = PROTOCOL_WPA | PROTOCOL_WPA2; + ret = MTRUE; + } else if (wpa2) { + sys_config->protocol = PROTOCOL_WPA2; + ret = MTRUE; + } else if (wpa) { + sys_config->protocol = PROTOCOL_WPA; + ret = MTRUE; + } + return ret; +} + +/** + * @brief Find and set WMM IES + * + * @param priv Pointer to moal_private + * @param ie Pointer IE buffer + * @param sys_config Pointer to mlan_uap_bss_param structure + * + * @return N/A + */ +static t_void woal_set_wmm_ies(moal_private *priv, const t_u8 *ie, int len, + mlan_uap_bss_param *sys_config) +{ + int bytes_left = len; + const t_u8 *pcurrent_ptr = ie; + t_u16 total_ie_len; + t_u8 element_len; + IEEEtypes_VendorSpecific_t *pvendor_ie; + IEEEtypes_ElementId_e element_id; + const t_u8 wmm_oui[4] = {0x00, 0x50, 0xf2, 0x02}; + + while (bytes_left >= 2) { + element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); + element_len = *((t_u8 *)pcurrent_ptr + 1); + total_ie_len = element_len + sizeof(IEEEtypes_Header_t); + if (bytes_left < total_ie_len) { + PRINTM(MERROR, + "InterpretIE: Error in processing IE, bytes left < IE length\n"); + bytes_left = 0; + continue; + } + switch (element_id) { + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; + if (!memcmp(pvendor_ie->vend_hdr.oui, wmm_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wmm_oui[3]) { + if (total_ie_len == + sizeof(IEEEtypes_WmmParameter_t)) { + /* + * Only accept and copy the WMM IE if + * it matches the size expected for the + * WMM Parameter IE. + */ + moal_memcpy_ext( + priv->phandle, + &sys_config->wmm_para, + pcurrent_ptr + + sizeof(IEEEtypes_Header_t), + element_len, + sizeof(sys_config->wmm_para)); + /** set uap_host_based_config to true */ + sys_config->uap_host_based_config = + MTRUE; + } + } + + break; + default: + break; + } + pcurrent_ptr += element_len + 2; + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + } +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief initialize AP or GO bss config + * @param priv A pointer to moal private structure + * @param band BAND_2GHZ/BAND_5GHZ + * @param params A pointer to cfg80211_ap_settings structure + * @return 0 -- success, otherwise fail + */ +t_u8 woal_check_11ac_capability(moal_private *priv, t_u8 band, + struct cfg80211_ap_settings *params) +#else +/** + * @brief initialize AP or GO bss config + * @param band BAND_2GHZ/BAND_5GHZ + * @param priv A pointer to moal private structure + * @return 0 -- success, otherwise fail + */ +t_u8 woal_check_11ac_capability(moal_private *priv, t_u8 band) +#endif +{ + mlan_fw_info fw_info; + t_u8 enable_11ac = MFALSE; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + const u8 *vht_ie = NULL; +#endif + ENTER(); + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if ((band == BAND_5GHZ) && !(fw_info.fw_bands & BAND_AAC)) { + PRINTM(MCMND, "FW don't support 5G AC"); + LEAVE(); + return enable_11ac; + } + if ((band == BAND_2GHZ) && !(fw_info.fw_bands & BAND_GAC)) { + PRINTM(MCMND, "FW don't support 2G AC"); + LEAVE(); + return enable_11ac; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail, + params->beacon.tail_len); + if (vht_ie) + enable_11ac = MTRUE; + else + enable_11ac = MFALSE; +#else + enable_11ac = MTRUE; +#endif + LEAVE(); + return enable_11ac; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief initialize AP or GO bss config + * @param priv A pointer to moal private structure + * @param band BAND_5G/BAND_2GHZ + * @param params A pointer to cfg80211_ap_settings structure + * @return 0 -- success, otherwise fail + */ +t_u8 woal_check_11ax_capability(moal_private *priv, t_u8 band, + struct cfg80211_ap_settings *params) +{ + mlan_fw_info fw_info; + t_u8 enable_11ax = MFALSE; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 20, 0) + mlan_ds_11ax_he_cfg he_cfg; + t_u8 he_txrx_mcs_support[4] = {0xff, 0xff, 0xff, 0xff}; +#endif + ENTER(); + memset(&fw_info, 0, sizeof(mlan_fw_info)); + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if ((band == BAND_5GHZ) && !(fw_info.fw_bands & BAND_AAX)) { + PRINTM(MCMND, "FW don't support 5G AX\n"); + LEAVE(); + return enable_11ax; + } + if ((band == BAND_2GHZ) && !(fw_info.fw_bands & BAND_GAX)) { + PRINTM(MCMND, "FW don't support 2G AX"); + LEAVE(); + return enable_11ax; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + if (params->he_cap) + enable_11ax = MTRUE; + else + enable_11ax = MFALSE; +#else + memset(&he_cfg, 0, sizeof(he_cfg)); + if (band == BAND_5GHZ) + he_cfg.band = MBIT(1); + else if (band == BAND_2GHZ) + he_cfg.band = MBIT(0); + if (0 == woal_11ax_cfg(priv, MLAN_ACT_GET, &he_cfg)) { + if (he_cfg.he_cap.len && + (he_cfg.he_cap.ext_id == HE_CAPABILITY)) { + if (memcmp(he_cfg.he_cap.he_txrx_mcs_support, + he_txrx_mcs_support, + sizeof(he_txrx_mcs_support))) + enable_11ax = MTRUE; + } + } +#endif + PRINTM(MCMND, "enable_11ax=%d\n", enable_11ax); + LEAVE(); + return enable_11ax; +} +#endif + +/** + * @brief get ht_cap from beacon ie + * + * @param ie Pointer to IEs + * @param len Total length of ie + * + * @return ht_cap + */ +static t_u16 woal_get_htcap_info(const t_u8 *ie, int len) +{ + t_u16 ht_cap_info = 0; + IEEEtypes_HTCap_t *htcap_ie = NULL; + htcap_ie = + (IEEEtypes_HTCap_t *)woal_parse_ie_tlv(ie, len, HT_CAPABILITY); + if (htcap_ie) { + /* hostap has converted ht_cap_info to little endian, here + * conver to host endian */ + ht_cap_info = woal_le16_to_cpu(htcap_ie->ht_cap.ht_cap_info); + PRINTM(MMSG, "Get ht_cap from beacon ies: 0x%x\n", ht_cap_info); + } + return ht_cap_info; +} + +/** + * @brief get vht_cap from beacon ie + * + * @param ie Pointer to IEs + * @param len Total length of ie + * + * @return Pointer to vht_cap ie + */ +static IEEEtypes_VHTCap_t *woal_get_vhtcap_info(const t_u8 *ie, int len) +{ + IEEEtypes_VHTCap_t *vhtcap_ie = NULL; + vhtcap_ie = (IEEEtypes_VHTCap_t *)woal_parse_ie_tlv(ie, len, + VHT_CAPABILITY); + if (vhtcap_ie) + PRINTM(MMSG, "Get vht_cap from beacon ies: 0x%x\n", + vhtcap_ie->vht_cap.vht_cap_info); + return vhtcap_ie; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +/** Starting Frequency for 11A band */ +#define START_FREQ_11A_BAND 5000 /* in MHz */ +/** + * @brief convert cfg80211_chan_def to Band_Config + * + * @param bandcfg A pointer to (Band_Config_t structure + * @param chandef A pointer to cfg80211_chan_def structure + * + * @return N/A + */ +static void woal_convert_chan_to_bandconfig(Band_Config_t *bandcfg, + struct cfg80211_chan_def *chandef) +{ + ENTER(); + + if (chandef->chan->hw_value <= MAX_BG_CHANNEL) + bandcfg->chanBand = BAND_2GHZ; + else + bandcfg->chanBand = BAND_5GHZ; + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + bandcfg->chanWidth = CHAN_BW_20MHZ; + break; + case NL80211_CHAN_WIDTH_40: + bandcfg->chanWidth = CHAN_BW_40MHZ; + if (chandef->center_freq1 > chandef->chan->center_freq) + bandcfg->chan2Offset = SEC_CHAN_ABOVE; + else + bandcfg->chan2Offset = SEC_CHAN_BELOW; + break; + case NL80211_CHAN_WIDTH_80: + bandcfg->chan2Offset = + woal_get_second_channel_offset(chandef->chan->hw_value); + bandcfg->chanWidth = CHAN_BW_80MHZ; + break; + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + default: + break; + } + LEAVE(); + return; +} + +/** + * @brief Enable radar detect for DFS channel + * + * @param priv A pointer to moal private structure + * @param chandef A pointer to cfg80211_chan_def structure + * @return N/A + */ +static void woal_enable_dfs_support(moal_private *priv, + struct cfg80211_chan_def *chandef) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11h_chan_rep_req *pchan_rpt_req = NULL; + mlan_ds_11h_cfg *p11h_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + ENTER(); + if (!(chandef->chan->flags & IEEE80211_CHAN_RADAR)) { + PRINTM(MIOCTL, "No radar channel\n"); + LEAVE(); + return; + } + PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d \n", + chandef->chan->hw_value, chandef->width); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (NULL == req) { + PRINTM(MIOCTL, "No Memory to allocate ioctl buffer\n"); + LEAVE(); + return; + } + p11h_cfg = (mlan_ds_11h_cfg *)req->pbuf; + pchan_rpt_req = &p11h_cfg->param.chan_rpt_req; + pchan_rpt_req->startFreq = 5000; + pchan_rpt_req->chanNum = (t_u8)chandef->chan->hw_value; + woal_convert_chan_to_bandconfig(&pchan_rpt_req->bandcfg, chandef); + pchan_rpt_req->host_based = MTRUE; + pchan_rpt_req->millisec_dwell_time = 0; + + p11h_cfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK; + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_SET; + /* Send Channel Check command and wait until the report is ready */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return; +} +#endif + +/** + * @brief Prase supported rates from beacon data, set bss cfg accordingly + * + * @param priv A pointer to moal_private + * @param bss_cfg A pointer to bss configuration structure + * @param head_ie A pointer to beacon head IE buffer + * @param head_len head IE buffer length + * @param tail_ie A pointer to beacon tail IE buffer + * @param tail_len tail IE buffer length * + * @return N/A + */ +static void woal_set_uap_rates(moal_private *priv, mlan_uap_bss_param *bss_cfg, + const t_u8 *head_ie, int head_len, + const t_u8 *tail_ie, int tail_len) +{ + pIEEEtypes_Header_t rate_ie; + pIEEEtypes_Header_t ext_rate_ie; + int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + const u8 *var_pos = head_ie + var_offset; + int len = head_len - var_offset; + int rate_len = 0; + + rate_ie = (void *)woal_parse_ie_tlv(var_pos, len, WLAN_EID_SUPP_RATES); + if (rate_ie) { + memset(bss_cfg->rates, 0, sizeof(bss_cfg->rates)); + moal_memcpy_ext(priv->phandle, bss_cfg->rates, rate_ie + 1, + rate_ie->len, sizeof(bss_cfg->rates)); + rate_len = MIN(rate_ie->len, sizeof(bss_cfg->rates)); + } + ext_rate_ie = (void *)woal_parse_ie_tlv(tail_ie, tail_len, + WLAN_EID_EXT_SUPP_RATES); + if (ext_rate_ie) { + moal_memcpy_ext(priv->phandle, &bss_cfg->rates[rate_len], + ext_rate_ie + 1, ext_rate_ie->len, + sizeof(bss_cfg->rates) - rate_len); + rate_len += MIN(ext_rate_ie->len, + (sizeof(bss_cfg->rates) - rate_len)); + } + DBG_HEXDUMP(MCMD_D, "rates", bss_cfg->rates, sizeof(bss_cfg->rates)); +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +/** + * @brief initialize AP or GO bss config + * + * @param priv A pointer to moal private structure + * @param params A pointer to cfg80211_ap_settings structure + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_beacon_config(moal_private *priv, + struct cfg80211_ap_settings *params) +#else +/** + * @brief initialize AP or GO bss config + * + * @param priv A pointer to moal private structure + * @param params A pointer to beacon_parameters structure + * @return 0 -- success, otherwise fail + */ +static int woal_cfg80211_beacon_config(moal_private *priv, + struct beacon_parameters *params) +#endif +{ + struct wiphy *wiphy = NULL; + const t_u8 *ie = NULL; + int ret = 0, ie_len; + mlan_uap_bss_param *sys_config = NULL; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + int i = 0; +#else + t_u8 wpa_ies; + const t_u8 *ssid_ie = NULL; + struct ieee80211_mgmt *head = NULL; + t_u16 capab_info = 0; +#endif + t_u8 rates_bg[13] = {0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, + 0x24, 0x30, 0x48, 0x60, 0x6c, 0x00}; + t_u8 rates_a[9] = {0x8c, 0x12, 0x98, 0x24, 0xb0, + 0x48, 0x60, 0x6c, 0x00}; +#ifdef WIFI_DIRECT_SUPPORT + t_u8 rates_wfd[9] = {0x8c, 0x12, 0x18, 0x24, 0x30, + 0x48, 0x60, 0x6c, 0x00}; +#endif + t_u8 chan2Offset = SEC_CHAN_NONE; + t_u8 enable_11n = MTRUE; + t_u16 ht_cap = 0; + t_u8 enable_11ac = MFALSE; + t_u8 vht20_40 = MFALSE; + IEEEtypes_VHTCap_t *vhtcap_ie = NULL; + IEEEtypes_HECap_t *hecap_ie = NULL; + t_u8 enable_11ax = MFALSE; + t_u8 *wapi_ie = NULL; + int wapi_ie_len = 0; +#ifdef WIFI_DIRECT_SUPPORT + int GoAgeoutTime = priv->phandle->params.GoAgeoutTime; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + mlan_ds_11h_chan_nop_info chan_nop_info; + Band_Config_t bandcfg; +#endif + ENTER(); + + if (!params) { + ret = -EFAULT; + goto done; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + ie = ((struct cfg80211_ap_settings *)params)->beacon.tail; + ie_len = ((struct cfg80211_ap_settings *)params)->beacon.tail_len; +#else + ie = ((struct beacon_parameters *)params)->tail; + ie_len = ((struct beacon_parameters *)params)->tail_len; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + wapi_ie = (t_u8 *)woal_parse_ie_tlv(params->beacon.tail, + params->beacon.tail_len, WAPI_IE); +#else + wapi_ie = (t_u8 *)woal_parse_ie_tlv(params->tail, params->tail_len, + WAPI_IE); +#endif + if (wapi_ie) { + wapi_ie_len = *(wapi_ie + 1) + 2; + if (MLAN_STATUS_FAILURE == + woal_set_get_gen_ie(priv, MLAN_ACT_SET, wapi_ie, + &wapi_ie_len, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Failed to set wapi ie\n"); + ret = -EFAULT; + goto done; + } + } + + wiphy = priv->phandle->wiphy; + if (priv->bss_type != MLAN_BSS_TYPE_UAP +#ifdef WIFI_DIRECT_SUPPORT + && priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT +#endif + ) { + ret = -EFAULT; + goto done; + } + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + /* Initialize the uap bss values which are uploaded from firmware */ + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + sys_config)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + + if (priv->phandle->params.uap_max_sta) + sys_config->max_sta_count = priv->phandle->params.uap_max_sta; + + /* Setting the default values */ + sys_config->channel = 6; + sys_config->preamble_type = 0; + sys_config->mgmt_ie_passthru_mask = priv->mgmt_subtype_mask; + moal_memcpy_ext(priv->phandle, sys_config->mac_addr, priv->current_addr, + ETH_ALEN, sizeof(sys_config->mac_addr)); + +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && GoAgeoutTime) { + sys_config->sta_ageout_timer = GoAgeoutTime; + sys_config->ps_sta_ageout_timer = GoAgeoutTime; + } +#endif + /* Set frag_threshold, rts_threshold, and retry limit */ + sys_config->frag_threshold = wiphy->frag_threshold; + sys_config->rts_threshold = wiphy->rts_threshold; + sys_config->retry_limit = wiphy->retry_long; + if (sys_config->frag_threshold == (t_u16)MLAN_FRAG_RTS_DISABLED) { + sys_config->frag_threshold = MLAN_FRAG_MAX_VALUE; + } + if (sys_config->rts_threshold == (t_u16)MLAN_FRAG_RTS_DISABLED) { + sys_config->rts_threshold = MLAN_RTS_MAX_VALUE; + } + + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + if (params->beacon_interval) + sys_config->beacon_period = params->beacon_interval; +#else + if (params->interval) + sys_config->beacon_period = params->interval; +#endif + if (params->dtim_period) + sys_config->dtim_period = params->dtim_period; + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /** back up ap's channel */ + moal_memcpy_ext(priv->phandle, &priv->chan, ¶ms->chandef, + sizeof(struct cfg80211_chan_def), sizeof(priv->chan)); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->phandle->usr_nop_period_sec) { + PRINTM(MCMND, "Checking if AP's channel %d is under NOP\n", + priv->channel); + woal_convert_chan_to_bandconfig(&bandcfg, ¶ms->chandef); + memset(&chan_nop_info, 0, sizeof(chan_nop_info)); + chan_nop_info.curr_chan = priv->channel; + chan_nop_info.chan_width = bandcfg.chanWidth; + if (params->chandef.width >= NL80211_CHAN_WIDTH_20) + chan_nop_info.new_chan.is_11n_enabled = MTRUE; + chan_nop_info.new_chan.bandcfg = bandcfg; + woal_uap_get_channel_nop_info(priv, MOAL_IOCTL_WAIT, + &chan_nop_info); + if (chan_nop_info.chan_under_nop) { + PRINTM(MCMND, + "cfg80211: Channel %d is under NOP, New channel=%d\n", + priv->channel, chan_nop_info.new_chan.channel); + priv->chan_under_nop = chan_nop_info.chan_under_nop; + priv->channel = chan_nop_info.new_chan.channel; + woal_chandef_create(priv, &priv->chan, + &chan_nop_info.new_chan); + } + } +#endif + + if (priv->channel) { + memset(sys_config->rates, 0, sizeof(sys_config->rates)); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + switch (priv->chan.width) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: +#endif + case NL80211_CHAN_WIDTH_20_NOHT: + enable_11n = MFALSE; + break; + case NL80211_CHAN_WIDTH_20: + break; + case NL80211_CHAN_WIDTH_40: + if (priv->chan.center_freq1 < + priv->chan.chan->center_freq) + chan2Offset = SEC_CHAN_BELOW; + else + chan2Offset = SEC_CHAN_ABOVE; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + chan2Offset = + woal_get_second_channel_offset(priv->channel); + break; + default: + PRINTM(MWARN, "Unknown channel width: %d\n", + priv->chan.width); + break; + } +#else + switch (params->channel_type) { + case NL80211_CHAN_NO_HT: + enable_11n = MFALSE; + break; + case NL80211_CHAN_HT20: + break; + case NL80211_CHAN_HT40PLUS: + chan2Offset = SEC_CHAN_ABOVE; + break; + case NL80211_CHAN_HT40MINUS: + chan2Offset = SEC_CHAN_BELOW; + break; + default: + PRINTM(MWARN, "Unknown channel type: %d\n", + params->channel_type); + break; + } +#endif +#endif /* CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) */ + + sys_config->channel = priv->channel; + if (priv->channel <= MAX_BG_CHANNEL) { + sys_config->bandcfg.chanBand = BAND_2GHZ; +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + moal_memcpy_ext(priv->phandle, + sys_config->rates, rates_wfd, + sizeof(rates_wfd), + sizeof(sys_config->rates)); + else +#endif + moal_memcpy_ext(priv->phandle, + sys_config->rates, rates_bg, + sizeof(rates_bg), + sizeof(sys_config->rates)); + } else { + sys_config->bandcfg.chanBand = BAND_5GHZ; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + chan2Offset = + woal_get_second_channel_offset(priv->channel); +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + /* Force enable 40MHZ on WFD interface */ + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + chan2Offset = woal_get_second_channel_offset( + priv->channel); +#endif +#endif +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + moal_memcpy_ext(priv->phandle, + sys_config->rates, rates_wfd, + sizeof(rates_wfd), + sizeof(sys_config->rates)); + else +#endif + moal_memcpy_ext(priv->phandle, + sys_config->rates, rates_a, + sizeof(rates_a), + sizeof(sys_config->rates)); + } + + /* Replaced with rate from userspace, if exist */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + woal_set_uap_rates(priv, sys_config, params->beacon.head, + params->beacon.head_len, params->beacon.tail, + params->beacon.tail_len); +#else + woal_set_uap_rates(priv, sys_config, params->head, + params->head_len, params->tail, + params->tail_len); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + enable_11ac = woal_check_11ac_capability( + priv, sys_config->bandcfg.chanBand, params); + if (enable_11ac && + ((priv->chan.width == NL80211_CHAN_WIDTH_20) || + (priv->chan.width == NL80211_CHAN_WIDTH_40))) + vht20_40 = MTRUE; +#else + enable_11ac = woal_check_11ac_capability( + priv, sys_config->bandcfg.chanBand); +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + enable_11ax = woal_check_11ax_capability( + priv, sys_config->bandcfg.chanBand, params); +#endif + + /* Disable GreenField by default */ + sys_config->ht_cap_info = 0x10c; + if (enable_11n) + sys_config->ht_cap_info |= 0x20; + if (chan2Offset) { + sys_config->bandcfg.chan2Offset = chan2Offset; + sys_config->ht_cap_info |= 0x1042; + sys_config->ampdu_param = 3; + } else { + sys_config->bandcfg.chan2Offset = 0; + } + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + ht_cap = woal_get_htcap_info(ie, ie_len); + if (ht_cap) { + if (sys_config->bandcfg.chanBand == BAND_2GHZ) + sys_config->ht_cap_info = + (ht_cap & + (wiphy->bands[IEEE80211_BAND_2GHZ] + ->ht_cap.cap & + 0x13ff)) | + 0x0c; + else + sys_config->ht_cap_info = + (ht_cap & + (wiphy->bands[IEEE80211_BAND_5GHZ] + ->ht_cap.cap & + 0x13ff)) | + 0x0c; + } + PRINTM(MCMND, + "11n=%d, ht_cap=0x%x, channel=%d, bandcfg:chanBand=0x%x chanWidth=0x%x chan2Offset=0x%x scanMode=0x%x\n", + enable_11n, sys_config->ht_cap_info, + priv->channel, sys_config->bandcfg.chanBand, + sys_config->bandcfg.chanWidth, + sys_config->bandcfg.chan2Offset, + sys_config->bandcfg.scanMode); + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + if (!params->ssid || !params->ssid_len) { + ret = -EINVAL; + goto done; + } + moal_memcpy_ext(priv->phandle, sys_config->ssid.ssid, params->ssid, + MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len), + sizeof(sys_config->ssid.ssid)); + sys_config->ssid.ssid_len = MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len); + /** + * hidden_ssid=0: broadcast SSID in beacons. + * hidden_ssid=1: send empty SSID (length=0) in beacon. + * hidden_ssid=2: clear SSID (ACSII 0), but keep the original length + */ + if (!params->hidden_ssid) + sys_config->bcast_ssid_ctl = 1; + else if (params->hidden_ssid == 1) + sys_config->bcast_ssid_ctl = 0; + else if (params->hidden_ssid == 2) + sys_config->bcast_ssid_ctl = 2; + switch (params->auth_type) { + case NL80211_AUTHTYPE_SHARED_KEY: + sys_config->auth_mode = MLAN_AUTH_MODE_SHARED; + break; + case NL80211_AUTHTYPE_AUTOMATIC: + sys_config->auth_mode = MLAN_AUTH_MODE_AUTO; + break; + case NL80211_AUTHTYPE_OPEN_SYSTEM: + default: + sys_config->auth_mode = MLAN_AUTH_MODE_OPEN; + break; + } + + sys_config->protocol = PROTOCOL_NO_SECURITY; + if ((params->crypto.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; + if (params->crypto.n_akm_suites || + (params->privacy && params->crypto.wpa_versions)) + woal_find_wpa_ies(ie, ie_len, sys_config); + for (i = 0; i < params->crypto.n_akm_suites; i++) { + switch (params->crypto.akm_suites[i]) { + case WLAN_AKM_SUITE_8021X: + sys_config->key_mgmt |= KEY_MGMT_EAP; + break; + case WLAN_AKM_SUITE_PSK: + sys_config->key_mgmt |= KEY_MGMT_PSK; + break; + } + } + sys_config->wpa_cfg.pairwise_cipher_wpa = 0; + sys_config->wpa_cfg.pairwise_cipher_wpa2 = 0; + for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) { + switch (params->crypto.ciphers_pairwise[i]) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + break; + case WLAN_CIPHER_SUITE_TKIP: + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + sys_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_TKIP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + sys_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + sys_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_AES_CCMP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + sys_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_AES_CCMP; + break; + case WLAN_CIPHER_SUITE_SMS4: + sys_config->protocol = PROTOCOL_WAPI; + break; + } + } + switch (params->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if ((priv->cipher == WLAN_CIPHER_SUITE_WEP40) || + (priv->cipher == WLAN_CIPHER_SUITE_WEP104)) { + sys_config->protocol = PROTOCOL_STATIC_WEP; + sys_config->key_mgmt = KEY_MGMT_NONE; + sys_config->wpa_cfg.length = 0; + moal_memcpy_ext(priv->phandle, + &sys_config->wep_cfg.key0, + &priv->uap_wep_key[0], sizeof(wep_key), + sizeof(sys_config->wep_cfg.key0)); + moal_memcpy_ext(priv->phandle, + &sys_config->wep_cfg.key1, + &priv->uap_wep_key[1], sizeof(wep_key), + sizeof(sys_config->wep_cfg.key1)); + moal_memcpy_ext(priv->phandle, + &sys_config->wep_cfg.key2, + &priv->uap_wep_key[2], sizeof(wep_key), + sizeof(sys_config->wep_cfg.key2)); + moal_memcpy_ext(priv->phandle, + &sys_config->wep_cfg.key3, + &priv->uap_wep_key[3], sizeof(wep_key), + sizeof(sys_config->wep_cfg.key3)); + } + break; + case WLAN_CIPHER_SUITE_TKIP: + sys_config->wpa_cfg.group_cipher = CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + break; + case WLAN_CIPHER_SUITE_SMS4: + sys_config->protocol = PROTOCOL_WAPI; + break; + } +#else +/* Since in Android ICS 4.0.1's wpa_supplicant, there is no way to set ssid + * when GO (AP) starts up, so get it from beacon head parameter + * TODO: right now use hard code + * 24 -- ieee80211 header lenth, 12 -- fixed element length for beacon + */ +#define BEACON_IE_OFFSET 36 + /* Find SSID in head + * SSID IE id: 0, right now use hard code + */ + ssid_ie = woal_parse_ie_tlv(params->head + BEACON_IE_OFFSET, + params->head_len - BEACON_IE_OFFSET, 0); + + if (!ssid_ie) { + PRINTM(MERROR, "No ssid IE found.\n"); + ret = -EFAULT; + goto done; + } + if (*(ssid_ie + 1) > 32) { + PRINTM(MERROR, "ssid len error: %d\n", *(ssid_ie + 1)); + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, sys_config->ssid.ssid, ssid_ie + 2, + *(ssid_ie + 1), sizeof(sys_config->ssid.ssid)); + 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; + moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key0, + &priv->uap_wep_key[0], sizeof(wep_key), + sizeof(sys_config->wep_cfg.key0)); + moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key1, + &priv->uap_wep_key[1], sizeof(wep_key), + sizeof(sys_config->wep_cfg.key1)); + moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key2, + &priv->uap_wep_key[2], sizeof(wep_key), + sizeof(sys_config->wep_cfg.key2)); + moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key3, + &priv->uap_wep_key[3], sizeof(wep_key), + sizeof(sys_config->wep_cfg.key3)); + } else { + /** Get cipher and key_mgmt from RSN/WPA IE */ + if (capab_info & WLAN_CAPABILITY_PRIVACY) { + wpa_ies = woal_find_wpa_ies( + params->tail, params->tail_len, sys_config); + if (wpa_ies == MFALSE) { + /* hard code setting to wpa2-psk */ + sys_config->protocol = PROTOCOL_WPA2; + sys_config->key_mgmt = KEY_MGMT_PSK; + sys_config->wpa_cfg.pairwise_cipher_wpa2 = + CIPHER_AES_CCMP; + sys_config->wpa_cfg.group_cipher = + CIPHER_AES_CCMP; + } + } + } +#endif + + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + /*find and set wmm ie*/ + woal_set_wmm_ies(priv, ie, ie_len, sys_config); + } + /* If the security mode is configured as WEP or WPA-PSK, + * it will disable 11n automatically, and if configured as + * open(off) or wpa2-psk, it will automatically enable 11n */ + if ((sys_config->protocol == PROTOCOL_STATIC_WEP) || + (sys_config->protocol == PROTOCOL_WPA)) + enable_11n = MFALSE; + if (!enable_11n) { + woal_set_uap_ht_tx_cfg(priv, sys_config->bandcfg, ht_cap, + MFALSE); + woal_uap_set_11n_status(priv, sys_config, MLAN_ACT_DISABLE); + } else { + woal_set_uap_ht_tx_cfg(priv, sys_config->bandcfg, ht_cap, + MTRUE); + woal_uap_set_11n_status(priv, sys_config, MLAN_ACT_ENABLE); + woal_set_get_tx_bf_cap(priv, MLAN_ACT_GET, + &sys_config->tx_bf_cap); + } + if (enable_11ac && enable_11n) { + vhtcap_ie = woal_get_vhtcap_info(ie, ie_len); + woal_uap_set_11ac_status(priv, MLAN_ACT_ENABLE, vht20_40, + vhtcap_ie); + } else { + woal_uap_set_11ac_status(priv, MLAN_ACT_DISABLE, vht20_40, + NULL); + } + if (enable_11ax && enable_11n) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + hecap_ie = (IEEEtypes_HECap_t *)woal_parse_ext_ie_tlv( + ie, ie_len, HE_CAPABILITY); +#endif + woal_uap_set_11ax_status(priv, MLAN_ACT_ENABLE, + sys_config->bandcfg.chanBand, + hecap_ie); + } else + woal_uap_set_11ax_status(priv, MLAN_ACT_DISABLE, + sys_config->bandcfg.chanBand, NULL); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + if (params->inactivity_timeout) { + sys_config->sta_ageout_timer = params->inactivity_timeout * 10; + sys_config->ps_sta_ageout_timer = + params->inactivity_timeout * 10; + } + PRINTM(MIOCTL, "inactivity_timeout=%d\n", params->inactivity_timeout); + PRINTM(MIOCTL, "sta_ageout_timer=%d ps_sta_ageout_timer=%d\n", + sys_config->sta_ageout_timer, sys_config->ps_sta_ageout_timer); +#endif + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_config)) { + ret = -EFAULT; + goto done; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + woal_enable_dfs_support(priv, &priv->chan); +#endif +done: + kfree(sys_config); + LEAVE(); + return ret; +} + +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** + * @brief Callback function for virtual interface + * setup + * + * @param dev A pointer to structure net_device + * + * @return N/A + */ +static void woal_virt_if_setup(struct net_device *dev) +{ + ENTER(); + ether_setup(dev); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 9) + dev->needs_free_netdev = true; +#else + dev->destructor = free_netdev; +#endif + LEAVE(); +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +/** + * @brief This function adds a new interface. It will + * allocate, initialize and register the device. + * + * @param handle A pointer to moal_handle structure + * @param bss_index BSS index number + * @param name_assign_type Interface name assignment type + * @param bss_type BSS type + * + * @return A pointer to the new priv structure + */ +moal_private *woal_alloc_virt_interface(moal_handle *handle, t_u8 bss_index, + unsigned char name_assign_type, + t_u8 bss_type, const char *name) +#else +/** + * @brief This function adds a new interface. It will + * allocate, initialize and register the device. + * + * @param handle A pointer to moal_handle structure + * @param bss_index BSS index number + * @param bss_type BSS type + * + * @return A pointer to the new priv structure + */ +moal_private *woal_alloc_virt_interface(moal_handle *handle, t_u8 bss_index, + t_u8 bss_type, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) + const +#endif + char *name) +#endif +{ + struct net_device *dev = NULL; + moal_private *priv = NULL; + ENTER(); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#ifndef MAX_WMM_QUEUE +#define MAX_WMM_QUEUE 4 +#endif + /* Allocate an Ethernet device */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + dev = alloc_netdev_mq(sizeof(moal_private), name, name_assign_type, + woal_virt_if_setup, MAX_WMM_QUEUE); +#else + dev = alloc_netdev_mq(sizeof(moal_private), name, NET_NAME_UNKNOWN, + woal_virt_if_setup, MAX_WMM_QUEUE); +#endif +#else + dev = alloc_netdev_mq(sizeof(moal_private), name, woal_virt_if_setup, + MAX_WMM_QUEUE); +#endif +#else + dev = alloc_netdev(sizeof(moal_private), name, woal_virt_if_setup); +#endif + if (!dev) { + PRINTM(MFATAL, "Init virtual ethernet device failed\n"); + goto error; + } + /* Allocate device name */ + if ((dev_alloc_name(dev, name) < 0)) { + PRINTM(MERROR, "Could not allocate device name\n"); + goto error; + } + + priv = (moal_private *)netdev_priv(dev); + /* Save the priv to handle */ + handle->priv[bss_index] = priv; + + /* Use the same handle structure */ + priv->phandle = handle; + priv->netdev = dev; + priv->bss_index = bss_index; + priv->bss_type = bss_type; + priv->bss_role = MLAN_BSS_ROLE_STA; + + INIT_LIST_HEAD(&priv->tcp_sess_queue); + spin_lock_init(&priv->tcp_sess_lock); + + INIT_LIST_HEAD(&priv->tx_stat_queue); + spin_lock_init(&priv->tx_stat_lock); + spin_lock_init(&priv->connect_lock); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) + SET_MODULE_OWNER(dev); +#endif + + PRINTM(MCMND, "Alloc virtual interface%s\n", dev->name); + + LEAVE(); + return priv; +error: + if (dev) + free_netdev(dev); + LEAVE(); + return NULL; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param name_assign_type Interface name assignment type + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * @param new_dev new net_device to return + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_add_virt_if(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params, + struct net_device **new_dev) +#else +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * @param new_dev new net_device to return + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_add_virt_if(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) + const +#endif + char *name, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params, + struct net_device **new_dev) +#endif +{ + int ret = 0; + struct net_device *ndev = NULL; + moal_private *priv = NULL, *new_priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + struct wireless_dev *wdev = NULL; + moal_private *vir_priv; + int i = 0; + + ENTER(); + ASSERT_RTNL(); + priv = woal_get_vir_priv_bss_type(handle, MLAN_BSS_TYPE_WIFIDIRECT); + if (priv && priv->bss_role == MLAN_BSS_ROLE_UAP && + priv->bss_started == MTRUE) { + if (handle->pref_mac) + handle = (moal_handle *)handle->pref_mac; + } + priv = (moal_private *)woal_get_priv_bss_type(handle, + MLAN_BSS_TYPE_WIFIDIRECT); + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is NULL\n"); + LEAVE(); + return -EFAULT; + } + if (priv->phandle->drv_mode.intf_num == priv->phandle->priv_num) { + PRINTM(MERROR, "max virtual interface limit reached\n"); + for (i = 0; i < priv->phandle->priv_num; i++) { + vir_priv = priv->phandle->priv[i]; + if (vir_priv->bss_virtual) { + woal_cfg80211_del_virt_if(wiphy, + vir_priv->netdev); + break; + } + } + if (priv->phandle->drv_mode.intf_num == + priv->phandle->priv_num) { + LEAVE(); + return -ENOMEM; + } + } + PRINTM(MMSG, "Add virtual interface %s\n", name); + if ((type != NL80211_IFTYPE_P2P_CLIENT) && + (type != NL80211_IFTYPE_P2P_GO)) { + PRINTM(MERROR, "Invalid iftype: %d\n", type); + LEAVE(); + return -EINVAL; + } + + handle = priv->phandle; + /* Cancel previous scan req */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + new_priv = woal_alloc_virt_interface(handle, handle->priv_num, + name_assign_type, + MLAN_BSS_TYPE_WIFIDIRECT, name); +#else + new_priv = woal_alloc_virt_interface(handle, handle->priv_num, + MLAN_BSS_TYPE_WIFIDIRECT, name); +#endif + if (!new_priv) { + PRINTM(MERROR, "Add virtual interface fail."); + LEAVE(); + return -EFAULT; + } + handle->priv_num++; + + wdev = (struct wireless_dev *)&new_priv->w_dev; + memset(wdev, 0, sizeof(struct wireless_dev)); + ndev = new_priv->netdev; + SET_NETDEV_DEV(ndev, wiphy_dev(wiphy)); + ndev->ieee80211_ptr = wdev; + wdev->iftype = type; + wdev->wiphy = wiphy; + new_priv->wdev = wdev; + new_priv->bss_virtual = MTRUE; + new_priv->pa_netdev = priv->netdev; + + woal_init_sta_dev(ndev, new_priv); + + /* Initialize priv structure */ + woal_init_priv(new_priv, MOAL_IOCTL_WAIT); + /** Init to GO/CLIENT mode */ + if (type == NL80211_IFTYPE_P2P_CLIENT) + woal_cfg80211_init_p2p_client(new_priv); + else if (type == NL80211_IFTYPE_P2P_GO) + woal_cfg80211_init_p2p_go(new_priv); + ret = register_netdevice(ndev); + if (ret) { + handle->priv[new_priv->bss_index] = NULL; + handle->priv_num--; + if (ndev->reg_state == NETREG_REGISTERED) { + unregister_netdevice(ndev); + free_netdev(ndev); + ndev = NULL; + } + PRINTM(MFATAL, "register net_device failed, ret=%d\n", ret); + goto done; + } + netif_carrier_off(ndev); + woal_stop_queue(ndev); + if (new_dev) + *new_dev = ndev; +#ifdef CONFIG_PROC_FS + woal_create_proc_entry(new_priv); + woal_debug_entry(new_priv); +#endif /* CONFIG_PROC_FS */ +done: + LEAVE(); + return ret; +} + +/** + * @brief Notify mlan BSS will be removed. + * + * @param priv A pointer to moal_private structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, + * otherwise fail + */ +mlan_status woal_bss_remove(moal_private *priv) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_bss *bss = NULL; + mlan_status status; + + ENTER(); + + /* Allocate an IOCTL request buffer */ + req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_REMOVE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return status; +} + +/** + * @brief This function removes an virtual interface. + * + * @param wiphy A pointer to the wiphy structure + * @param dev A pointer to the net_device structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_del_virt_if(struct wiphy *wiphy, struct net_device *dev) +{ + int ret = 0; + int i = 0; + moal_private *priv = NULL; + moal_private *vir_priv = NULL; + moal_private *remain_priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + t_u8 find_bss = MFALSE; + + for (i = 0; i < handle->priv_num; i++) { + vir_priv = handle->priv[i]; + if (vir_priv) { + if (vir_priv->netdev == dev) { + find_bss = MTRUE; + PRINTM(MMSG, + "Del virtual interface %s, index=%d\n", + dev->name, i); + break; + } + } + } + if (!find_bss) { + /* Switch to the other MAC */ + if (handle->pref_mac) + handle = (moal_handle *)handle->pref_mac; + for (i = 0; i < handle->priv_num; i++) { + vir_priv = handle->priv[i]; + if (vir_priv) { + if (vir_priv->netdev == dev) { + find_bss = MTRUE; + PRINTM(MMSG, + "Del virtual interface %s, index=%d\n", + dev->name, i); + break; + } + } + } + } + + priv = (moal_private *)woal_get_priv_bss_type(handle, + MLAN_BSS_TYPE_WIFIDIRECT); + if (!priv) + return ret; + if (vir_priv && vir_priv->netdev == dev) { + woal_stop_queue(dev); + netif_carrier_off(dev); + netif_device_detach(dev); + if (handle->is_remain_timer_set) { + woal_cancel_timer(&handle->remain_timer); + woal_remain_timer_func(handle); + } + + /*** cancel pending scan */ + woal_cancel_scan(vir_priv, MOAL_IOCTL_WAIT); + + woal_flush_tx_stat_queue(vir_priv); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + /* cancel previous remain on channel to avoid firmware hang */ + if (priv->phandle->remain_on_channel) { + t_u8 channel_status; + remain_priv = + priv->phandle + ->priv[priv->phandle->remain_bss_index]; + if (remain_priv) { + if (woal_cfg80211_remain_on_channel_cfg( + remain_priv, MOAL_IOCTL_WAIT, MTRUE, + &channel_status, NULL, 0, 0)) + PRINTM(MERROR, + "del_virt_if: Fail to cancel remain on channel\n"); + + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv->netdev, +#else + remain_priv->wdev, +#endif + priv->phandle->cookie, + &priv->phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv->phandle->channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + } +#endif + + woal_clear_all_mgmt_ies(vir_priv, MOAL_IOCTL_WAIT); + woal_cfg80211_deinit_p2p(vir_priv); + woal_bss_remove(vir_priv); +#ifdef CONFIG_PROC_FS + /* Remove proc debug */ + woal_debug_remove(vir_priv); + woal_proc_remove(vir_priv); +#endif /* CONFIG_PROC_FS */ + /* Last reference is our one */ +#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 37) + PRINTM(MINFO, "refcnt = %d\n", atomic_read(&dev->refcnt)); +#else + PRINTM(MINFO, "refcnt = %d\n", netdev_refcnt_read(dev)); +#endif + PRINTM(MINFO, "netdev_finish_unregister: %s\n", dev->name); + /* Clear the priv in handle */ + vir_priv->phandle->priv[vir_priv->bss_index] = NULL; + priv->phandle->priv_num--; + if (dev->reg_state == NETREG_REGISTERED) + unregister_netdevice(dev); + } + return ret; +} +#endif +#endif + +#if defined(WIFI_DIRECT_SUPPORT) +/** + * @brief This function removes an virtual interface. + * + * @param handle A pointer to the moal_handle structure + * + * @return N/A + */ +void woal_remove_virtual_interface(moal_handle *handle) +{ +#ifdef WIFI_DIRECT_SUPPORT + moal_private *priv = NULL; + int vir_intf = 0; + int i = 0; +#endif + ENTER(); + rtnl_lock(); +#ifdef WIFI_DIRECT_SUPPORT + for (i = 0; i < handle->priv_num; i++) { + priv = handle->priv[i]; + if (priv) { + if (priv->bss_virtual) { + PRINTM(MCMND, "Remove virtual interface %s\n", + priv->netdev->name); +#ifdef CONFIG_PROC_FS + /* Remove proc debug */ + woal_debug_remove(priv); + woal_proc_remove(priv); +#endif /* CONFIG_PROC_FS */ + netif_device_detach(priv->netdev); + if (priv->netdev->reg_state == + NETREG_REGISTERED) + unregister_netdevice(priv->netdev); + handle->priv[i] = NULL; + vir_intf++; + } + } + } +#endif + rtnl_unlock(); +#ifdef WIFI_DIRECT_SUPPORT + handle->priv_num -= vir_intf; +#endif + LEAVE(); +} +#endif + +/** + * @brief This function check if uap interface is ready + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * + * @return MTRUE/MFALSE; + */ +static t_u8 woal_uap_interface_ready(struct wiphy *wiphy, char *name, + struct net_device **new_dev) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + int i; + + for (i = 0; i < handle->priv_num; i++) { + priv = handle->priv[i]; + if (priv && (priv->bss_type == MLAN_BSS_TYPE_UAP) && + !strcmp(priv->netdev->name, name)) { + priv->wdev->iftype = NL80211_IFTYPE_AP; + *new_dev = priv->netdev; + break; + } + } + if (priv && *new_dev) + return MTRUE; + else + return MFALSE; +} + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * + * @return A pointer to net_device -- success, otherwise null + */ +struct net_device *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +#else +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, char *name, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +#endif +#else +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * + * @return A pointer to wireless_dev -- success, otherwise null + */ +struct wireless_dev *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) + const +#endif + char *name, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +#else +/** + * @brief Request the driver to add a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param name Virtual interface name + * @param name_assign_type Interface name assignment type + * @param type Virtual interface type + * @param flags Flags for the virtual interface + * @param params A pointer to vif_params structure + * + * @return A pointer to wireless_dev -- success, otherwise null + */ +struct wireless_dev * +woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + u32 *flags, +#endif + struct vif_params *params) +#endif +#endif +{ + struct net_device *ndev = NULL; + int ret = 0; +#if defined(WIFI_DIRECT_SUPPORT) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + u32 *flags = ¶ms->flags; +#endif +#endif + + ENTER(); + PRINTM(MIOCTL, "add virtual intf: %d name: %s\n", type, name); + switch (type) { +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + ret = woal_cfg80211_add_virt_if(wiphy, name, name_assign_type, + type, flags, params, &ndev); +#else + ret = woal_cfg80211_add_virt_if(wiphy, name, type, flags, + params, &ndev); +#endif + break; +#endif +#endif + case NL80211_IFTYPE_AP: + if (!woal_uap_interface_ready(wiphy, (char *)name, &ndev)) { + PRINTM(MMSG, + "Not support dynamically create %s UAP interface\n", + name); + ret = -EFAULT; + } + break; + default: + PRINTM(MWARN, "Not supported if type: %d\n", type); + ret = -EFAULT; + break; + } + LEAVE(); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) + if (ret) + return ERR_PTR(ret); + else + return ndev; +#else + return ret; +#endif +#else + if (ret) + return ERR_PTR(ret); + else + return ndev->ieee80211_ptr; +#endif +} + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) +/** + * @brief Request the driver to del a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param dev The pointer to net_device + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev) +#else +/** + * @brief Request the driver to del a virtual interface + * + * @param wiphy A pointer to wiphy structure + * @param wdev The pointer to wireless_dev + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, + struct wireless_dev *wdev) +#endif +{ + int ret = 0; + + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + int i; + moal_private *vir_priv = NULL; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + ENTER(); + + PRINTM(MIOCTL, "del virtual intf %s\n", dev->name); + ASSERT_RTNL(); + + if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) { + for (i = 0; i < handle->priv_num; i++) { + vir_priv = handle->priv[i]; + if (vir_priv) { + if (vir_priv->netdev == dev) { + PRINTM(MMSG, + "Del virtual interface %s, index=%d\n", + dev->name, i); + break; + } + } + } + if (vir_priv && vir_priv->bss_type == MLAN_BSS_TYPE_UAP) { + woal_cfg80211_del_beacon(wiphy, dev); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) + vir_priv->wdev->beacon_interval = 0; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + memset(&vir_priv->wdev->chandef, 0, + sizeof(vir_priv->wdev->chandef)); +#endif +#endif + vir_priv->wdev->ssid_len = 0; + PRINTM(MMSG, "Skip del UAP virtual interface %s", + dev->name); + } + LEAVE(); + return ret; + } + +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + ret = woal_cfg80211_del_virt_if(wiphy, dev); +#endif +#endif + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +/** + * @brief initialize AP or GO parameters + + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to cfg80211_ap_settings structure + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ap_settings *params) +#else +/** + * @brief initialize AP or GO parameters + + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to beacon_parameters structure + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *params) +#endif +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + + t_u8 wait_option = MOAL_IOCTL_WAIT_TIMEOUT; + + ENTER(); + + PRINTM(MMSG, "wlan: Starting AP\n"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + /* cancel previous remain on channel to avoid firmware hang */ + if (priv->phandle->remain_on_channel) { + t_u8 channel_status; + moal_private *remain_priv; + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (remain_priv) { + PRINTM(MCMND, + "Cancel Remain on Channel before Starting AP\n"); + if (woal_cfg80211_remain_on_channel_cfg( + remain_priv, MOAL_IOCTL_WAIT, MTRUE, + &channel_status, NULL, 0, 0)) + PRINTM(MERROR, + "add beacon: Fail to cancel remain on channel\n"); + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv->netdev, +#else + remain_priv->wdev, +#endif + priv->phandle->cookie, + &priv->phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv->phandle->channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + } +#endif + +#ifdef STA_CFG80211 + /*** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#endif + + if (!params) { + LEAVE(); + return -EFAULT; + } + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + priv->channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); +#else + priv->channel = + ieee80211_frequency_to_channel(params->channel->center_freq); +#endif +#endif + /* bss config */ + if (MLAN_STATUS_SUCCESS != woal_cfg80211_beacon_config(priv, params)) { + ret = -EFAULT; + goto done; + } + + /* set mgmt frame ies */ + ret = woal_cfg80211_mgmt_frame_ie(priv, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 2, 0) + params->tail, params->tail_len, NULL, + 0, NULL, 0, NULL, 0, MGMT_MASK_BEACON +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + params->beacon.tail, + params->beacon.tail_len, + params->beacon.proberesp_ies, + params->beacon.proberesp_ies_len, + params->beacon.assocresp_ies, + params->beacon.assocresp_ies_len, +#else + params->tail, params->tail_len, + params->proberesp_ies, + params->proberesp_ies_len, + params->assocresp_ies, + params->assocresp_ies_len, +#endif + NULL, 0, + MGMT_MASK_BEACON | + MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP +#endif + , + MOAL_IOCTL_WAIT); + if (ret) + goto done; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + if (params->beacon.beacon_ies && params->beacon.beacon_ies_len) { + ret = woal_cfg80211_mgmt_frame_ie( + priv, params->beacon.beacon_ies, + params->beacon.beacon_ies_len, NULL, 0, NULL, 0, NULL, + 0, MGMT_MASK_BEACON_WPS_P2P, MOAL_IOCTL_WAIT); + if (ret) { + PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); + goto done; + } + } +#else + if (params->beacon_ies && params->beacon_ies_len) { + ret = woal_cfg80211_mgmt_frame_ie(priv, params->beacon_ies, + params->beacon_ies_len, NULL, + 0, NULL, 0, NULL, 0, + MGMT_MASK_BEACON_WPS_P2P, + MOAL_IOCTL_WAIT); + if (ret) { + PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); + goto done; + } + } +#endif +#endif + priv->uap_host_based = MTRUE; + + /* if the bss is stopped, then start it */ + if (priv->bss_started == MFALSE) { +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) + wait_option = MOAL_NO_WAIT; +#endif + if (MLAN_STATUS_SUCCESS != + woal_uap_bss_ctrl(priv, wait_option, UAP_BSS_START)) { + priv->uap_host_based = MFALSE; + ret = -EFAULT; + goto done; + } + } + PRINTM(MMSG, "wlan: AP started\n"); +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) +/** + * @brief set AP or GO parameter + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to cfg80211_beacon_data structure + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_beacon_data *params) +#else +/** + * @brief set AP or GO parameter + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to beacon_parameters structure + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, + struct beacon_parameters *params) +#endif +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + + ENTER(); + + PRINTM(MIOCTL, "set beacon\n"); + if (params != NULL) { +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 2, 0) + if (params->tail && params->tail_len) { + ret = woal_cfg80211_mgmt_frame_ie( + priv, params->tail, params->tail_len, NULL, 0, + NULL, 0, NULL, 0, MGMT_MASK_BEACON, + MOAL_IOCTL_WAIT); + if (ret) + goto done; + } +#else + t_u16 mask = 0; + if (params->tail && params->tail_len) + mask |= MGMT_MASK_BEACON; + if (params->proberesp_ies && params->proberesp_ies_len) + mask |= MGMT_MASK_PROBE_RESP; + if (params->assocresp_ies && params->assocresp_ies_len) + mask |= MGMT_MASK_ASSOC_RESP; + PRINTM(MIOCTL, "Set beacon: mask=0x%x\n", mask); + if (mask) { + ret = woal_cfg80211_mgmt_frame_ie( + priv, params->tail, params->tail_len, + params->proberesp_ies, + params->proberesp_ies_len, + params->assocresp_ies, + params->assocresp_ies_len, NULL, 0, mask, + MOAL_IOCTL_WAIT); + if (ret) + goto done; + } + if (params->beacon_ies && params->beacon_ies_len) { + ret = woal_cfg80211_mgmt_frame_ie( + priv, params->beacon_ies, + params->beacon_ies_len, NULL, 0, NULL, 0, NULL, + 0, MGMT_MASK_BEACON_WPS_P2P, MOAL_IOCTL_WAIT); + if (ret) { + PRINTM(MERROR, + "Failed to set beacon wps/p2p ie\n"); + goto done; + } + } +#endif + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief reset AP or GO parameters + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; +#ifdef STA_SUPPORT + moal_private *pmpriv = NULL; +#endif + + ENTER(); + + if (priv->phandle->driver_status) { + PRINTM(MERROR, + "Block woal_cfg80211_del_beacon in abnormal driver state\n"); + LEAVE(); + return ret; + } + priv->uap_host_based = MFALSE; + PRINTM(MMSG, "wlan: Stoping AP\n"); +#ifdef STA_SUPPORT + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#endif + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + woal_deauth_all_station(priv); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) + woal_cancel_cac_block(priv); +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + memset(&priv->chan, 0, sizeof(struct cfg80211_chan_def)); + if (priv->phandle->is_cac_timer_set && + priv->bss_index == priv->phandle->cac_bss_index) { + woal_cancel_timer(&priv->phandle->cac_timer); + priv->phandle->is_cac_timer_set = MFALSE; + /* Make sure Chan Report is cancelled */ + woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#endif + memset(&priv->phandle->dfs_channel, 0, + sizeof(struct cfg80211_chan_def)); + priv->phandle->cac_bss_index = 0xff; + } + if (priv->csa_workqueue) + flush_workqueue(priv->csa_workqueue); +#endif + /* if the bss is still running, then stop it */ + if (priv->bss_started == MTRUE) { + if (MLAN_STATUS_FAILURE == + woal_uap_bss_ctrl(priv, MOAL_NO_WAIT, UAP_BSS_STOP)) { + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_FAILURE == + woal_uap_bss_ctrl(priv, MOAL_NO_WAIT, UAP_BSS_RESET)) { + ret = -EFAULT; + goto done; + } + /* Set WLAN MAC addresses */ + if (MLAN_STATUS_FAILURE == + woal_request_set_mac_address(priv, MOAL_NO_WAIT)) { + PRINTM(MERROR, "Set MAC address failed\n"); + ret = -EFAULT; + goto done; + } + } + woal_clear_all_mgmt_ies(priv, MOAL_NO_WAIT); +#ifdef STA_SUPPORT + if (!woal_is_any_interface_active(priv->phandle)) { + pmpriv = woal_get_priv((moal_handle *)priv->phandle, + MLAN_BSS_ROLE_STA); + if (pmpriv) + woal_set_scan_time(pmpriv, ACTIVE_SCAN_CHAN_TIME, + PASSIVE_SCAN_CHAN_TIME, + SPECIFIC_SCAN_CHAN_TIME); + } +#endif + + priv->cipher = 0; + memset(priv->uap_wep_key, 0, sizeof(priv->uap_wep_key)); + priv->channel = 0; + PRINTM(MMSG, "wlan: AP stopped\n"); +done: + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief change BSS + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to bss_parameters structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev, + struct bss_parameters *params) +{ + int ret = 0; + t_u8 change = MFALSE; + mlan_uap_bss_param *sys_config = NULL; + u8 bss_started = MFALSE; + t_u8 pkt_forward_ctl; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + PRINTM(MIOCTL, "isolate=%d\n", params->ap_isolate); + + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + sys_config)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + + pkt_forward_ctl = sys_config->pkt_forward_ctl; + if (params->ap_isolate) { + /** disable packet forwarding */ + sys_config->pkt_forward_ctl |= PKT_FWD_INTRA_BCAST; + sys_config->pkt_forward_ctl |= PKT_FWD_INTRA_UCAST; + } else { + sys_config->pkt_forward_ctl &= ~PKT_FWD_INTRA_BCAST; + sys_config->pkt_forward_ctl &= ~PKT_FWD_INTRA_UCAST; + } + if (pkt_forward_ctl != sys_config->pkt_forward_ctl) { + change = MTRUE; + PRINTM(MIOCTL, "ap_isolate=%xd\n", params->ap_isolate); + } + if (change) { + if (priv->bss_started == MTRUE) { + bss_started = MTRUE; + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + } + if (params->use_short_preamble == 1) + sys_config->preamble_type = 1; + else if (params->use_short_preamble == 0) + sys_config->preamble_type = 2; + else + sys_config->preamble_type = 0; + if (MLAN_STATUS_SUCCESS == + woal_set_get_sys_config(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + sys_config)) + ret = 0; + if (bss_started) + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT_TIMEOUT, + UAP_BSS_START); + } +done: + kfree(sys_config); + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) +/** + * @brief del station + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param param A pointer tostation_del_parameters structure + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief del station + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac_addr A pointer to station mac address + * + * @return 0 -- success, otherwise fail + */ +#endif +int woal_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + struct station_del_parameters *param) +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac_addr) +#else + u8 *mac_addr) +#endif +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + const u8 *mac_addr = NULL; +#endif + u16 reason_code = REASON_CODE_DEAUTH_LEAVING; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + ENTER(); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) + if (priv->phandle->is_cac_timer_set && + priv->bss_index == priv->phandle->cac_bss_index) { + woal_cancel_timer(&priv->phandle->cac_timer); + priv->phandle->is_cac_timer_set = MFALSE; + /* Make sure Chan Report is cancelled */ + woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, + GFP_KERNEL); +#endif + memset(&priv->phandle->dfs_channel, 0, + sizeof(struct cfg80211_chan_def)); + priv->phandle->cac_bss_index = 0xff; + } +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + if (moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) + woal_cancel_cac_block(priv); +#endif + + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return 0; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + if (param) { + mac_addr = param->mac; + reason_code = param->reason_code; + } +#endif + /** we will not send deauth to p2p interface, it might cause WPS failure + */ + if (mac_addr) { + PRINTM(MMSG, "wlan: deauth station " MACSTR "\n", + MAC2STR(mac_addr)); +#ifdef WIFI_DIRECT_SUPPORT + if (!priv->phandle->is_go_timer_set) +#endif + woal_deauth_station(priv, (u8 *)mac_addr, reason_code); + } else { + PRINTM(MIOCTL, "del all station\n"); + } + LEAVE(); + return 0; +} + +/** + * @brief Get station info + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param mac A pointer to station mac address + * @param stainfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +int woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) + const u8 *mac, +#else + u8 *mac, +#endif + struct station_info *stainfo) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = -EFAULT; + int i = 0; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + mlan_ds_get_stats stats; + + ENTER(); + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return -ENOENT; + } + + /* Allocate an IOCTL request buffer */ + ioctl_req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + goto done; + for (i = 0; i < info->param.sta_list.sta_count; i++) { + if (!memcmp(info->param.sta_list.info[i].mac_address, mac, + ETH_ALEN)) { + PRINTM(MIOCTL, "Get station: " MACSTR " RSSI=%d\n", + MAC2STR(mac), + (int)info->param.sta_list.info[i].rssi); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + stainfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME) | + BIT(NL80211_STA_INFO_RX_BYTES) | + BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS) | + BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_SIGNAL); + + stainfo->rx_bytes = priv->stats.rx_bytes; + stainfo->tx_bytes = priv->stats.tx_bytes; + stainfo->rx_packets = priv->stats.rx_packets; + stainfo->tx_packets = priv->stats.tx_packets; +#else + stainfo->filled = STATION_INFO_INACTIVE_TIME | + STATION_INFO_SIGNAL; +#endif + stainfo->inactive_time = 0; + stainfo->signal = info->param.sta_list.info[i].rssi; + ret = 0; + break; + } + } + memset(&stats, 0, sizeof(mlan_ds_get_stats)); + if (MLAN_STATUS_SUCCESS != + woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { + PRINTM(MERROR, "Error getting stats information\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + stainfo->filled |= BIT(NL80211_STA_INFO_TX_RETRIES) | + BIT(NL80211_STA_INFO_TX_FAILED) | + BIT(NL80211_STA_INFO_RX_DROP_MISC); + stainfo->tx_failed = stats.failed; + stainfo->tx_retries = stats.retry; + stainfo->rx_dropped_misc = stats.fcs_error; +#endif +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to dump the station information + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param idx Station index + * @param mac MAC address of the station + * @param sinfo A pointer to station_info structure + * + * @return 0 -- success, otherwise fail + */ +int woal_uap_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, t_u8 *mac, + struct station_info *sinfo) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = -EFAULT; + mlan_ds_get_info *info = NULL; + mlan_ioctl_req *ioctl_req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 sec = 0, usec = 0; + t_u64 cur_msec = 0; + + ENTER(); + if (priv->media_connected == MFALSE) { + PRINTM(MINFO, "cfg80211: Media not connected!\n"); + LEAVE(); + return -ENOENT; + } + + /* Allocate an IOCTL request buffer */ + ioctl_req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_get_info)); + if (ioctl_req == NULL) { + ret = -ENOMEM; + goto done; + } + + info = (mlan_ds_get_info *)ioctl_req->pbuf; + info->sub_command = MLAN_OID_UAP_STA_LIST; + ioctl_req->req_id = MLAN_IOCTL_GET_INFO; + ioctl_req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + goto done; + if (idx >= info->param.sta_list.sta_count) { + ret = -EFAULT; + goto done; + } + ret = 0; + moal_memcpy_ext(priv->phandle, mac, + info->param.sta_list.info[idx].mac_address, ETH_ALEN, + ETH_ALEN); + PRINTM(MIOCTL, "Dump station: " MACSTR " RSSI=%d\n", MAC2STR(mac), + (int)info->param.sta_list.info[idx].rssi); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) + sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME) | + BIT(NL80211_STA_INFO_SIGNAL); +#else + sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_SIGNAL; +#endif + if (info->param.sta_list.info[idx].stats.last_rx_in_msec) { + moal_get_system_time(priv->phandle, &sec, &usec); + cur_msec = (t_u64)sec * 1000 + (t_u64)usec / 1000; + sinfo->inactive_time = (t_u32)( + cur_msec - + info->param.sta_list.info[idx].stats.last_rx_in_msec); + PRINTM(MIOCTL, + "cur:%llu - [%d].last_rx:%llu = inactive_time:%d\n", + cur_msec, idx, + info->param.sta_list.info[idx].stats.last_rx_in_msec, + sinfo->inactive_time); + } else + sinfo->inactive_time = 0; + sinfo->signal = info->param.sta_list.info[idx].rssi; +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) +/** + * @brief set mac filter + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to cfg80211_acl_data structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_mac_acl(struct wiphy *wiphy, struct net_device *dev, + const struct cfg80211_acl_data *params) +{ + int ret = -EFAULT; + mlan_uap_bss_param *sys_config = NULL; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + u8 bss_started = MFALSE; + ENTER(); + + PRINTM(MIOCTL, "Set mac acl, entries=%d, policy=%d\n", + params->n_acl_entries, params->acl_policy); + sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_config) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + + /* Initialize the uap bss values which are uploaded from firmware */ + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + sys_config)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + memset(&sys_config->filter, 0, sizeof(mac_filter)); + if (params->n_acl_entries <= MAX_MAC_FILTER_NUM) + sys_config->filter.mac_count = params->n_acl_entries; + else + sys_config->filter.mac_count = MAX_MAC_FILTER_NUM; + + if (params->acl_policy == NL80211_ACL_POLICY_DENY_UNLESS_LISTED) + sys_config->filter.filter_mode = MAC_FILTER_MODE_ALLOW_MAC; + else if (params->acl_policy == NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) + sys_config->filter.filter_mode = MAC_FILTER_MODE_BLOCK_MAC; + moal_memcpy_ext( + priv->phandle, sys_config->filter.mac_list, params->mac_addrs, + sys_config->filter.mac_count * sizeof(mlan_802_11_mac_addr), + sizeof(sys_config->filter.mac_list)); + if (priv->bss_started == MTRUE) { + bss_started = MTRUE; + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); + } + if (MLAN_STATUS_SUCCESS == woal_set_get_sys_config(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_config)) + ret = 0; +done: + kfree(sys_config); + if (bss_started) + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT_TIMEOUT, UAP_BSS_START); + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +/** + * @brief Set txq parameters + + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to ieee80211_txq_params structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev, + struct ieee80211_txq_params *params) +{ + int ret = 0; + u8 ac = 0; + wmm_parameter_t ap_wmm_para; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + + /* AC_BE: 0, AC_BK:1, AC_VI: 2, AC_VO:3 */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + switch (params->ac) { + case NL80211_AC_VO: + ac = 3; + break; + case NL80211_AC_VI: + ac = 2; + break; + case NL80211_AC_BK: + ac = 1; + break; + case NL80211_AC_BE: + ac = 0; + break; + default: + break; + } +#else + switch (params->queue) { + case NL80211_TXQ_Q_VO: + ac = 3; + break; + case NL80211_TXQ_Q_VI: + ac = 2; + break; + case NL80211_TXQ_Q_BK: + ac = 1; + break; + case NL80211_TXQ_Q_BE: + ac = 0; + break; + default: + break; + } +#endif + + PRINTM(MMSG, "Set AC=%d, txop=%d cwmin=%d, cwmax=%d aifs=%d\n", ac, + params->txop, params->cwmin, params->cwmax, params->aifs); + + memset(&ap_wmm_para, 0, sizeof(wmm_parameter_t)); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_ap_wmm_para(priv, MLAN_ACT_GET, &ap_wmm_para)) { + PRINTM(MERROR, "wlan: We don't support AP WMM parameter\n"); + LEAVE(); + return ret; + } + ap_wmm_para.ac_params[ac].aci_aifsn.aifsn = params->aifs; + ap_wmm_para.ac_params[ac].ecw.ecw_max = ilog2(params->cwmax + 1); + ap_wmm_para.ac_params[ac].ecw.ecw_min = ilog2(params->cwmin + 1); + ap_wmm_para.ac_params[ac].tx_op_limit = params->txop; + if (MLAN_STATUS_SUCCESS != + woal_set_get_ap_wmm_para(priv, MLAN_ACT_SET, &ap_wmm_para)) { + PRINTM(MERROR, "wlan: Fail to set AP WMM parameter\n"); + ret = -EFAULT; + } + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +/** + * @brief cac timer call back function. + * + * @param context a pointer to moal_handle + * + * @return N/A + */ +void woal_cac_timer_func(void *context) +{ + moal_handle *handle = (moal_handle *)context; + moal_private *priv = handle->priv[handle->cac_bss_index]; + + PRINTM(MEVENT, "cac_timer fired.\n"); +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + cfg80211_cac_event(priv->netdev, &handle->dfs_channel, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); +#else + cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); +#endif + handle->is_cac_timer_set = MFALSE; + memset(&handle->dfs_channel, 0, sizeof(struct cfg80211_chan_def)); + handle->cac_bss_index = 0xff; +} + +/** + * @brief This function switch AP's channel + * 1. clear mgmt IEs 2. stop uAP + * 3. set beacon after 4. set new channel + * 5. start uAP 6. notify cfg80211 + * + * @param priv a pointer to moal_private + * @param wait_option wait option + * + * @return N/A + */ +void woal_switch_uap_channel(moal_private *priv, t_u8 wait_option) +{ + chan_band_info uap_channel; + t_u8 chan2Offset = SEC_CHAN_NONE; + ENTER(); + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != + woal_uap_bss_ctrl(priv, wait_option, UAP_BSS_STOP)) { + PRINTM(MERROR, "%s: stop uap failed \n", __func__); + goto done; + } + if (woal_cfg80211_set_beacon(priv->wdev->wiphy, priv->netdev, + &priv->beacon_after)) { + PRINTM(MERROR, "%s: set mgmt ies failed \n", __func__); + goto done; + } + + uap_channel.channel = ieee80211_frequency_to_channel( + priv->csa_chan.chan->center_freq); + switch (priv->csa_chan.width) { + case NL80211_CHAN_WIDTH_5: + case NL80211_CHAN_WIDTH_10: + case NL80211_CHAN_WIDTH_20_NOHT: + uap_channel.bandcfg.chanWidth = CHAN_BW_20MHZ; + break; + case NL80211_CHAN_WIDTH_20: + uap_channel.bandcfg.chanWidth = CHAN_BW_20MHZ; + break; + case NL80211_CHAN_WIDTH_40: + if (priv->csa_chan.center_freq1 < + priv->csa_chan.chan->center_freq) + chan2Offset = SEC_CHAN_BELOW; + else + chan2Offset = SEC_CHAN_ABOVE; + uap_channel.bandcfg.chanWidth = CHAN_BW_40MHZ; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_160: + uap_channel.bandcfg.chanWidth = CHAN_BW_80MHZ; + chan2Offset = + woal_get_second_channel_offset(uap_channel.channel); + break; + default: + PRINTM(MWARN, "Unknown channel width: %d\n", + priv->csa_chan.width); + break; + } + if (priv->csa_chan.chan->band == IEEE80211_BAND_2GHZ) + uap_channel.bandcfg.chanBand = BAND_2GHZ; + else if (priv->csa_chan.chan->band == IEEE80211_BAND_5GHZ) + uap_channel.bandcfg.chanBand = BAND_5GHZ; + uap_channel.bandcfg.chan2Offset = chan2Offset; + if (MLAN_STATUS_SUCCESS != woal_set_get_ap_channel(priv, MLAN_ACT_SET, + wait_option, + &uap_channel)) { + PRINTM(MERROR, "Fail to set ap channel \n"); + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT_TIMEOUT, UAP_BSS_START)) { + PRINTM(MERROR, "%s: start uap failed \n", __func__); + goto done; + } + PRINTM(MMSG, "CSA: old chan %d => new chan %d \n", priv->channel, + uap_channel.channel); + priv->channel = uap_channel.channel; + moal_memcpy_ext(priv->phandle, &priv->chan, &priv->csa_chan, + sizeof(struct cfg80211_chan_def), sizeof(priv->chan)); + cfg80211_ch_switch_notify(priv->netdev, &priv->chan); + if (priv->uap_tx_blocked) { + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + woal_start_queue(priv->netdev); + priv->uap_tx_blocked = MFALSE; + } +done: + LEAVE(); + return; +} + +/** + * @brief csa work handler + * + * @param work a pointer to work_struct + * + * @return 0 -- success, otherwise fail + */ +void woal_csa_work_queue(struct work_struct *work) +{ + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + moal_private *priv = container_of(delayed_work, moal_private, csa_work); + ENTER(); + if (priv->bss_started == MTRUE) + woal_switch_uap_channel(priv, MOAL_IOCTL_WAIT); + LEAVE(); +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) +/** + * @brief start radar detection + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chandef A pointer to cfg80211_chan_def structure + * @param cac_time_ms A cac dwell time + * @return 0 -- success, otherwise fail + */ + +int woal_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms) +#else +/** + * @brief start radar detection + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chandef A pointer to cfg80211_chan_def structure + * @return 0 -- success, otherwise fail + */ + +int woal_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef) +#endif +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + moal_handle *handle = priv->phandle; + mlan_ioctl_req *req = NULL; + mlan_ds_11h_chan_rep_req *pchan_rpt_req = NULL; + mlan_ds_11h_cfg *p11h_cfg = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d , Time %d \n", + chandef->chan->hw_value, chandef->width, cac_time_ms); +#else + PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d \n", + chandef->chan->hw_value, chandef->width); +#endif + + if (priv->bss_started == MTRUE) { + PRINTM(MERROR, "recv CAC request when bss already started \n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cac_period || handle->is_cac_timer_set) { + PRINTM(MERROR, + "Maybe other interface is doing CAC, please defer your oper\n"); + ret = -EBUSY; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); + if (NULL == req) { + ret = -ENOMEM; + goto done; + } + + p11h_cfg = (mlan_ds_11h_cfg *)req->pbuf; + pchan_rpt_req = &p11h_cfg->param.chan_rpt_req; + pchan_rpt_req->startFreq = START_FREQ_11A_BAND; + pchan_rpt_req->chanNum = (t_u8)chandef->chan->hw_value; + woal_convert_chan_to_bandconfig(&pchan_rpt_req->bandcfg, chandef); + pchan_rpt_req->host_based = MTRUE; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) + pchan_rpt_req->millisec_dwell_time = cac_time_ms; +#else + pchan_rpt_req->millisec_dwell_time = IEEE80211_DFS_MIN_CAC_TIME_MS; + + if ((woal_is_etsi_country(priv->phandle->country_code) == MTRUE)) { + if (chandef->chan->hw_value == 120 || + chandef->chan->hw_value == 124 || + chandef->chan->hw_value == 128) { + pchan_rpt_req->millisec_dwell_time = + IEEE80211_DFS_MIN_CAC_TIME_MS * 10; + } + if (chandef->chan->hw_value == 116 && + ((chandef->width == NL80211_CHAN_WIDTH_40) || + (chandef->width == NL80211_CHAN_WIDTH_80))) { + pchan_rpt_req->millisec_dwell_time = + IEEE80211_DFS_MIN_CAC_TIME_MS * 10; + } + } +#endif + if (priv->user_cac_period_msec) { + pchan_rpt_req->millisec_dwell_time = priv->user_cac_period_msec; + PRINTM(MCMD_D, + "cfg80211 dfstesting: User CAC Period=%d (msec) \n", + pchan_rpt_req->millisec_dwell_time); + } + + p11h_cfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK; + req->req_id = MLAN_IOCTL_11H_CFG; + req->action = MLAN_ACT_SET; + + /* Send Channel Check command and wait until the report is ready */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, "Fail to start radar detection\n"); + ret = -EFAULT; + } else { + moal_memcpy_ext(priv->phandle, &handle->dfs_channel, chandef, + sizeof(struct cfg80211_chan_def), + sizeof(handle->dfs_channel)); + handle->cac_bss_index = priv->bss_index; + handle->is_cac_timer_set = MTRUE; + /* avoid EVENT_CHANNEL_RAPORT_READY missing, add 1s gap */ + woal_mod_timer(&handle->cac_timer, + pchan_rpt_req->millisec_dwell_time + 1000); + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief channel switch + + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param params A pointer to cfg80211_csa_settings structure + * + * @return 0 -- success, otherwise fail + */ +int woal_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + t_u32 chsw_msec; + mlan_uap_bss_param *bss_cfg = NULL; + + ENTER(); + + if (!params) { + ret = -EINVAL; + goto done; + } + + /* TODO: support this case in next version */ + if (params->radar_required) { + PRINTM(MMSG, + " hostapd handle this case by disable and re-enable interface\n"); + ret = -ENOTSUPP; + goto done; + } + + /* actually hostapd would always choose one diff channel*/ + if (cfg80211_chandef_identical(¶ms->chandef, &priv->chan)) { + PRINTM(MMSG, + "csa channel is same with current channel, invaild\n"); + ret = -EINVAL; + goto done; + } + bss_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!bss_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + if (params->block_tx) { + if (netif_carrier_ok(dev)) + netif_carrier_off(dev); + woal_stop_queue(dev); + priv->uap_tx_blocked = MTRUE; + } + + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); + if (woal_cfg80211_set_beacon(wiphy, dev, ¶ms->beacon_csa)) { + PRINTM(MERROR, "%s: setting csa mgmt ies failed\n", __func__); + goto done; + } + + moal_memcpy_ext(priv->phandle, &priv->csa_chan, ¶ms->chandef, + sizeof(struct cfg80211_chan_def), + sizeof(priv->csa_chan)); + moal_memcpy_ext(priv->phandle, &priv->beacon_after, + ¶ms->beacon_after, + sizeof(struct cfg80211_beacon_data), + sizeof(priv->beacon_after)); + + if (!priv->phandle->fw_ecsa_enable) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_sys_config(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, + bss_cfg)) { + PRINTM(MERROR, "%s: get uap config failed\n", __func__); + ret = -EFAULT; + goto done; + } + chsw_msec = params->count * bss_cfg->beacon_period; + queue_delayed_work(priv->csa_workqueue, &priv->csa_work, + msecs_to_jiffies(chsw_msec)); + } +done: + kfree(bss_cfg); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Register the device with cfg80211 + * + * @param dev A pointer to net_device structure + * @param bss_type BSS type + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_register_uap_cfg80211(struct net_device *dev, t_u8 bss_type) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct wireless_dev *wdev = NULL; + + ENTER(); + + wdev = (struct wireless_dev *)&priv->w_dev; + memset(wdev, 0, sizeof(struct wireless_dev)); + + wdev->wiphy = priv->phandle->wiphy; + if (!wdev->wiphy) { + LEAVE(); + return MLAN_STATUS_FAILURE; + } + + if (bss_type == MLAN_BSS_TYPE_UAP) + wdev->iftype = NL80211_IFTYPE_AP; + + dev_net_set(dev, wiphy_net(wdev->wiphy)); + dev->ieee80211_ptr = wdev; + SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); + priv->wdev = wdev; + + LEAVE(); + return ret; +} diff --git a/mxm_wifiex/wlan_src/mlinux/moal_uap_cfg80211.h b/mxm_wifiex/wlan_src/mlinux/moal_uap_cfg80211.h new file mode 100644 index 0000000..9f3c470 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_uap_cfg80211.h @@ -0,0 +1,30 @@ +/** @file moal_uap_cfg80211.h + * + * @brief This file contains the uAP CFG80211 specific defines. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#ifndef _MOAL_UAP_CFG80211_H_ +#define _MOAL_UAP_CFG80211_H_ + +#include "moal_uap.h" + +mlan_status woal_register_uap_cfg80211(struct net_device *dev, t_u8 bss_type); + +#endif /* _MOAL_UAP_CFG80211_H_ */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_uap_priv.c b/mxm_wifiex/wlan_src/mlinux/moal_uap_priv.c new file mode 100644 index 0000000..02e3cc2 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_uap_priv.c @@ -0,0 +1,179 @@ +/** @file moal_uap_priv.c + * + * @brief This file contains standard ioctl functions + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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; +#ifdef 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/mxm_wifiex/wlan_src/mlinux/moal_uap_priv.h b/mxm_wifiex/wlan_src/mlinux/moal_uap_priv.h new file mode 100644 index 0000000..d050bac --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_uap_priv.h @@ -0,0 +1,128 @@ +/** @file moal_uap_priv.h + * + * @brief This file contains definition for extended private IOCTL call. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 + +#ifdef 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"}, +#ifdef 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/mxm_wifiex/wlan_src/mlinux/moal_uap_wext.c b/mxm_wifiex/wlan_src/mlinux/moal_uap_wext.c new file mode 100644 index 0000000..5196724 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_uap_wext.c @@ -0,0 +1,1865 @@ +/** @file moal_uap_wext.c + * + * @brief This file contains wireless extension standard ioctl functions + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/************************************************************************ +Change log: + 08/06/2010: initial version +************************************************************************/ + +#include "moal_main.h" +#include "moal_uap.h" +#include "moal_wext.h" +#include "moal_uap_priv.h" + +/******************************************************** + Global Variables +********************************************************/ +typedef struct _chan_to_freq_t { + /** Channel */ + t_u16 channel; + /** Frequency */ + t_u32 freq; + /** Band */ + t_u8 band; +} chan_to_freq_t; + +const chan_to_freq_t chan_to_freq[] = { + {1, 2412, 0}, {2, 2417, 0}, {3, 2422, 0}, {4, 2427, 0}, + {5, 2432, 0}, {6, 2437, 0}, {7, 2442, 0}, {8, 2447, 0}, + {9, 2452, 0}, {10, 2457, 0}, {11, 2462, 0}, {12, 2467, 0}, + {13, 2472, 0}, {14, 2484, 0}, {183, 4915, 1}, {184, 4920, 1}, + {185, 4925, 1}, {187, 4935, 1}, {188, 4940, 1}, {189, 4945, 1}, + {192, 4960, 1}, {196, 4980, 1}, {7, 5035, 1}, {8, 5040, 1}, + {9, 5045, 1}, {11, 5055, 1}, {12, 5060, 1}, {16, 5080, 1}, + {34, 5170, 1}, {36, 5180, 1}, {38, 5190, 1}, {40, 5200, 1}, + {42, 5210, 1}, {44, 5220, 1}, {46, 5230, 1}, {48, 5240, 1}, + {52, 5260, 1}, {56, 5280, 1}, {60, 5300, 1}, {64, 5320, 1}, + {100, 5500, 1}, {104, 5520, 1}, {108, 5540, 1}, {112, 5560, 1}, + {116, 5580, 1}, {120, 5600, 1}, {124, 5620, 1}, {128, 5640, 1}, + {132, 5660, 1}, {136, 5680, 1}, {140, 5700, 1}, {144, 5720, 1}, + {149, 5745, 1}, {153, 5765, 1}, {157, 5785, 1}, {161, 5805, 1}, + {165, 5825, 1}, +}; + +/** Convertion from frequency to channel */ +#define freq_to_chan(x) ((((x)-2412) / 5) + 1) + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Sort Channels + * + * @param freq A pointer to iw_freq structure + * @param num Number of Channels + * + * @return N/A + */ +static inline void woal_sort_channels(struct iw_freq *freq, int num) +{ + int i, j; + struct iw_freq temp; + + for (i = 0; i < num; i++) + for (j = i + 1; j < num; j++) + if (freq[i].i > freq[j].i) { + temp.i = freq[i].i; + temp.m = freq[i].m; + + freq[i].i = freq[j].i; + freq[i].m = freq[j].m; + + freq[j].i = temp.i; + freq[j].m = temp.m; + } +} + +/** + * @brief Get frequency for channel in given band + * + * @param channel channel + * @param band band + * + * @return freq + */ +static int channel_to_frequency(t_u16 channel, t_u8 band) +{ + int i = 0; + + ENTER(); + for (i = 0; i < ARRAY_SIZE(chan_to_freq); i++) { + if (channel == chan_to_freq[i].channel && + band == chan_to_freq[i].band) { + LEAVE(); + return chan_to_freq[i].freq; + } + } + LEAVE(); + return 0; +} + +/** + * @brief Commit handler: called after a bunch of SET operations + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param cwrq A pointer to char buffer + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int woal_config_commit(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *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, + union iwreq_data *wrqu, char *extra) +{ + char *cwrq = wrqu->name; + 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, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct sockaddr *awrq = &wrqu->addr; + int ret = 0; + + ENTER(); + + if (priv->bss_started) + moal_memcpy_ext(priv->phandle, awrq->sa_data, + priv->current_addr, MLAN_MAC_ADDR_LENGTH, + sizeof(awrq->sa_data)); + 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, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct sockaddr *awrq = &wrqu->addr; + const t_u8 zero_mac[MLAN_MAC_ADDR_LENGTH] = {0, 0, 0, 0, 0, 0}; + + ENTER(); + + if (awrq->sa_family != ARPHRD_ETHER) { + ret = -EINVAL; + goto done; + } + + PRINTM(MINFO, "ASSOC: WAP: uAP bss : " MACSTR "\n", + MAC2STR((t_u8 *)awrq->sa_data)); + + /* + * Using this ioctl to start/stop the BSS, return if bss + * is already started/stopped. + */ + if (memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + if (priv->bss_started == MFALSE) + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, + UAP_BSS_START); + else + PRINTM(MINFO, "BSS is already started.\n"); + } else { + /* zero_mac means bss_stop */ + if (priv->bss_started == MTRUE) + ret = woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, + UAP_BSS_STOP); + else + PRINTM(MINFO, "BSS is already stopped.\n"); + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set frequency/channel + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param fwrq A pointer to iw_freq structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_freq *fwrq = &wrqu->freq; + mlan_uap_bss_param *sys_cfg = NULL, *ap_cfg = NULL; + int ret = 0, chan = 0, i = 0; + + ENTER(); + + ap_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL); + if (ap_cfg == NULL) { + ret = -ENOMEM; + goto done; + } + + sys_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL); + if (sys_cfg == NULL) { + ret = -ENOMEM; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + i = ap_cfg->num_of_chan; + + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + /* If setting by frequency, convert to a channel */ + if (fwrq->e == 1) + chan = freq_to_chan(fwrq->m / 100000); + else + chan = fwrq->m; + if (chan > 0 && chan < MLAN_MAX_CHANNEL) + sys_cfg->channel = chan; + else { + ret = -EINVAL; + goto done; + } + for (i = 0; i < ap_cfg->num_of_chan; i++) + if (ap_cfg->chan_list[i].chan_number == chan) + break; + if (i == ap_cfg->num_of_chan) { + PRINTM(MERROR, "Channel %d is not supported\n", chan); + ret = -EINVAL; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + kfree(ap_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Get frequency and channel + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param fwrq A pointer to iw_freq structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_freq *fwrq = &wrqu->freq; + mlan_uap_bss_param *ap_cfg = NULL; + t_u8 band = 0; + int ret = 0; + + ENTER(); + + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + kfree(ap_cfg); + LEAVE(); + return -EFAULT; + } + + band = (ap_cfg->bandcfg.chanBand == BAND_5GHZ); + fwrq->m = (long)channel_to_frequency(ap_cfg->channel, band); + fwrq->i = (long)ap_cfg->channel; + fwrq->e = 6; + + kfree(ap_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Set wlan mode + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param uwrq A pointer to t_u32 string + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_bss_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + t_u32 *uwrq = &wrqu->mode; + 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, + union iwreq_data *wrqu, char *extra) +{ + t_u32 *uwrq = &wrqu->mode; + ENTER(); + + *uwrq = IW_MODE_MASTER; + + LEAVE(); + return 0; +} + +/** + * @brief Set encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_encode(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *sys_cfg = NULL, *ap_cfg = NULL; + wep_key *pkey = NULL; + int key_index = 0; + + ENTER(); + + /* Check index */ + key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key_index > 3) { + PRINTM(MERROR, "Key index #%d out of range\n", key_index); + ret = -EINVAL; + goto done; + } + + ap_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL); + if (ap_cfg == NULL) { + ret = -ENOMEM; + goto done; + } + + sys_cfg = kmalloc(sizeof(mlan_uap_bss_param), GFP_KERNEL); + if (sys_cfg == NULL) { + ret = -ENOMEM; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + sys_cfg->wep_cfg.key0.key_index = 0; + sys_cfg->wep_cfg.key1.key_index = 1; + sys_cfg->wep_cfg.key2.key_index = 2; + sys_cfg->wep_cfg.key3.key_index = 3; + + if (key_index >= 0 && key_index <= 3) { + if (key_index == 0) + pkey = &sys_cfg->wep_cfg.key0; + else if (key_index == 1) + pkey = &sys_cfg->wep_cfg.key1; + else if (key_index == 2) + pkey = &sys_cfg->wep_cfg.key2; + else if (key_index == 3) + pkey = &sys_cfg->wep_cfg.key3; + } + + if (!(dwrq->flags & IW_ENCODE_NOKEY) && dwrq->length) { + if (dwrq->length > MAX_WEP_KEY_SIZE) { + PRINTM(MERROR, "Key length (%d) out of range\n", + dwrq->length); + ret = -E2BIG; + goto done; + } + if (key_index < 0) { + /* Get current default key index */ + if (ap_cfg->wep_cfg.key0.is_default) + pkey = &sys_cfg->wep_cfg.key0; + if (ap_cfg->wep_cfg.key1.is_default) + pkey = &sys_cfg->wep_cfg.key1; + if (ap_cfg->wep_cfg.key2.is_default) + pkey = &sys_cfg->wep_cfg.key2; + if (ap_cfg->wep_cfg.key3.is_default) + pkey = &sys_cfg->wep_cfg.key3; + else { /* Something wrong, select first key as default + */ + PRINTM(MERROR, + "No default key set! Selecting first key.\n"); + pkey = &sys_cfg->wep_cfg.key0; + } + } + + sys_cfg->protocol = PROTOCOL_STATIC_WEP; + if (extra) + moal_memcpy_ext(priv->phandle, pkey->key, extra, + dwrq->length, sizeof(pkey->key)); + /* 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) + moal_memcpy_ext(priv->phandle, pkey, + &ap_cfg->wep_cfg.key0, + sizeof(wep_key), + sizeof(wep_key)); + if (key_index == 1) + moal_memcpy_ext(priv->phandle, pkey, + &ap_cfg->wep_cfg.key1, + sizeof(wep_key), + sizeof(wep_key)); + if (key_index == 2) + moal_memcpy_ext(priv->phandle, pkey, + &ap_cfg->wep_cfg.key2, + sizeof(wep_key), + sizeof(wep_key)); + if (key_index == 3) + moal_memcpy_ext(priv->phandle, pkey, + &ap_cfg->wep_cfg.key3, + sizeof(wep_key), + sizeof(wep_key)); + /* Set current key index as default */ + pkey->is_default = MTRUE; + } + } + } + if (dwrq->flags & (IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)) { + switch (dwrq->flags & 0xf000) { + case IW_ENCODE_RESTRICTED: + /* iwconfig mlanX restricted key [1] */ + sys_cfg->auth_mode = MLAN_AUTH_MODE_SHARED; + PRINTM(MINFO, "Auth mode restricted!\n"); + break; + case IW_ENCODE_OPEN: + /* iwconfig mlanX key [2] open */ + sys_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + PRINTM(MINFO, "Auth mode open!\n"); + break; + case IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN: + default: + /* iwconfig mlanX key [2] open restricted */ + PRINTM(MINFO, "Auth mode auto!\n"); + break; + } + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + kfree(ap_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Get encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_encode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + int index = (dwrq->flags & IW_ENCODE_INDEX); + wep_key *pkey = NULL; + mlan_uap_bss_param *ap_cfg = NULL; + int ret = 0; + + ENTER(); + if (index < 0 || index > 4) { + PRINTM(MERROR, "Key index #%d out of range\n", index); + ret = -EINVAL; + goto done; + } + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + + dwrq->flags = 0; + /* + * Check encryption mode + */ + switch (ap_cfg->auth_mode) { + case MLAN_AUTH_MODE_OPEN: + dwrq->flags = IW_ENCODE_OPEN; + break; + case MLAN_AUTH_MODE_SHARED: + case MLAN_AUTH_MODE_NETWORKEAP: + dwrq->flags = IW_ENCODE_RESTRICTED; + break; + default: + dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; + break; + } + + switch (ap_cfg->protocol) { + case PROTOCOL_NO_SECURITY: + dwrq->flags |= IW_ENCODE_DISABLED; + break; + case PROTOCOL_STATIC_WEP: + if (ap_cfg->wep_cfg.key0.is_default) + pkey = &ap_cfg->wep_cfg.key0; + else if (ap_cfg->wep_cfg.key1.is_default) + pkey = &ap_cfg->wep_cfg.key1; + else if (ap_cfg->wep_cfg.key2.is_default) + pkey = &ap_cfg->wep_cfg.key2; + else if (ap_cfg->wep_cfg.key3.is_default) + pkey = &ap_cfg->wep_cfg.key3; + if (pkey) { + dwrq->flags |= (pkey->key_index + 1); + dwrq->length = pkey->length; + moal_memcpy_ext(priv->phandle, extra, pkey->key, + pkey->length, pkey->length); + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + ret = -EFAULT; + } + break; + case PROTOCOL_WPA: + case PROTOCOL_WPA2: + case PROTOCOL_WPA2_MIXED: + moal_memcpy_ext(priv->phandle, extra, + ap_cfg->wpa_cfg.passphrase, + ap_cfg->wpa_cfg.length, ap_cfg->wpa_cfg.length); + dwrq->length = ap_cfg->wpa_cfg.length; + dwrq->flags |= 1; + dwrq->flags &= ~IW_ENCODE_DISABLED; + break; + default: + dwrq->flags &= ~IW_ENCODE_DISABLED; + break; + } + dwrq->flags |= IW_ENCODE_NOKEY; + +done: + kfree(ap_cfg); + LEAVE(); + return ret; +} + +#if (WIRELESS_EXT >= 18) +/** + * @brief Get IE + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return -EOPNOTSUPP + */ +static int woal_get_gen_ie(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, 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, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + mlan_uap_bss_param *sys_cfg = NULL; + IEEEtypes_Header_t *tlv = NULL; + int tlv_hdr_len = sizeof(IEEEtypes_Header_t), tlv_buf_left = 0; + int ret = 0; + + ENTER(); + + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + tlv_buf_left = dwrq->length; + tlv = (IEEEtypes_Header_t *)extra; + while (tlv_buf_left >= tlv_hdr_len) { + if (tlv->element_id == WPA_IE) { + sys_cfg->protocol |= PROTOCOL_WPA; + if (priv->pairwise_cipher == CIPHER_TKIP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_TKIP; + PRINTM(MINFO, "Set IE Cipher TKIP\n"); + } + if (priv->pairwise_cipher == CIPHER_AES_CCMP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_AES_CCMP; + PRINTM(MINFO, "Set IE Cipher CCMP\n"); + } + if (priv->pairwise_cipher == + (CIPHER_TKIP | CIPHER_AES_CCMP)) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = + CIPHER_TKIP | CIPHER_AES_CCMP; + PRINTM(MINFO, "Set IE Cipher TKIP + CCMP\n"); + } + moal_memcpy_ext(priv->phandle, + priv->bcn_ie_buf + priv->bcn_ie_len, + ((t_u8 *)tlv), + sizeof(IEEEtypes_Header_t) + tlv->len, + sizeof(priv->bcn_ie_buf) - + priv->bcn_ie_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); + } + moal_memcpy_ext(priv->phandle, + priv->bcn_ie_buf + priv->bcn_ie_len, + ((t_u8 *)tlv), + sizeof(IEEEtypes_Header_t) + tlv->len, + sizeof(priv->bcn_ie_buf) - + priv->bcn_ie_len); + priv->bcn_ie_len += + sizeof(IEEEtypes_Header_t) + tlv->len; + } + if (priv->group_cipher == CIPHER_TKIP) + sys_cfg->wpa_cfg.group_cipher = CIPHER_TKIP; + if (priv->group_cipher == CIPHER_AES_CCMP) + sys_cfg->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + tlv_buf_left -= (tlv_hdr_len + tlv->len); + tlv = (IEEEtypes_Header_t *)((t_u8 *)tlv + tlv_hdr_len + + tlv->len); + } + sys_cfg->key_mgmt = priv->uap_key_mgmt; + if (sys_cfg->key_mgmt & KEY_MGMT_PSK) + sys_cfg->key_mgmt_operation |= 0x01; + if (sys_cfg->key_mgmt & KEY_MGMT_EAP) + sys_cfg->key_mgmt_operation |= 0x03; + + if (sys_cfg->protocol) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_sys_config(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP configuration\n"); + ret = -EFAULT; + goto done; + } + priv->pairwise_cipher = 0; + priv->group_cipher = 0; + + /* custom IE command to set priv->bcn_ie_buf */ + if (MLAN_STATUS_SUCCESS != +#define UAP_RSN_MASK (BIT(8) | BIT(5) | BIT(1) | BIT(3)) + woal_set_get_custom_ie(priv, UAP_RSN_MASK, priv->bcn_ie_buf, + priv->bcn_ie_len)) { + PRINTM(MERROR, "Error setting wpa-rsn IE\n"); + ret = -EFAULT; + } + } else if (dwrq->length == 0) { + /* custom IE command to re-set priv->bcn_ie_buf */ + if (MLAN_STATUS_SUCCESS != + woal_set_get_custom_ie(priv, 0, priv->bcn_ie_buf, + priv->bcn_ie_len)) { + PRINTM(MERROR, "Error resetting wpa-rsn IE\n"); + ret = -EFAULT; + } + priv->bcn_ie_len = 0; + } + +done: + kfree(sys_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Extended version of encoding configuration + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_encode_ext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + int key_index; + t_u8 *pkey_material = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_uap_bss_param *sys_cfg = NULL; + wep_key *pwep_key = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key_index < 0 || key_index > 5) { + ret = -EINVAL; + goto done; + } + if (ext->key_len > (dwrq->length - sizeof(struct iw_encode_ext))) { + ret = -EINVAL; + goto done; + } + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + pkey_material = (t_u8 *)(ext + 1); + /* Disable Key */ + if ((dwrq->flags & IW_ENCODE_DISABLED) && !ext->key_len) { + sys_cfg->protocol = PROTOCOL_NO_SECURITY; + } else if (ext->alg == IW_ENCODE_ALG_WEP) { + sys_cfg->protocol = PROTOCOL_STATIC_WEP; + /* Set WEP key */ + switch (key_index) { + case 0: + pwep_key = &sys_cfg->wep_cfg.key0; + break; + case 1: + pwep_key = &sys_cfg->wep_cfg.key1; + break; + case 2: + pwep_key = &sys_cfg->wep_cfg.key2; + break; + case 3: + pwep_key = &sys_cfg->wep_cfg.key3; + break; + } + if (pwep_key) { + pwep_key->key_index = key_index; + pwep_key->is_default = MTRUE; + pwep_key->length = ext->key_len; + moal_memcpy_ext(priv->phandle, pwep_key->key, + pkey_material, ext->key_len, + sizeof(pwep_key->key)); + } + } 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; + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.key_material, + pkey_material, ext->key_len, + sizeof(sec->param.encrypt_key.key_material)); + moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.mac_addr, + ext->addr.sa_data, ETH_ALEN, + sizeof(sec->param.encrypt_key.mac_addr)); + sec->param.encrypt_key.key_flags = ext->ext_flags; + if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.pn, + (t_u8 *)ext->rx_seq, SEQ_MAX_SIZE, + sizeof(sec->param.encrypt_key.pn)); + 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) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.pn, + (t_u8 *)ext->tx_seq, SEQ_MAX_SIZE, + sizeof(sec->param.encrypt_key.pn)); + DBG_HEXDUMP(MCMD_D, "Uap Tx PN", + sec->param.encrypt_key.pn, SEQ_MAX_SIZE); + } + PRINTM(MIOCTL, + "set uap wpa key key_index=%d, key_len=%d key_flags=0x%x " MACSTR + "\n", + key_index, ext->key_len, + sec->param.encrypt_key.key_flags, + MAC2STR(sec->param.encrypt_key.mac_addr)); + DBG_HEXDUMP(MCMD_D, "uap wpa key", pkey_material, ext->key_len); +#define IW_ENCODE_ALG_AES_CMAC 5 + + if (ext->alg == IW_ENCODE_ALG_AES_CMAC) + sec->param.encrypt_key.key_flags |= + KEY_FLAG_AES_MCAST_IGTK; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; + /* Cipher set will be done in set generic IE */ + priv->pairwise_cipher = ext->alg; + priv->group_cipher = ext->alg; + goto done; /* No AP configuration */ + } + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Extended version of encoding configuration + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return -EOPNOTSUPP + */ +static int woal_get_encode_ext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, 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, + union iwreq_data *wrqu, char *extra) +{ + struct iw_mlme *mlme = (struct iw_mlme *)extra; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ds_get_info *pinfo = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_sta_list *sta_list = NULL; + const t_u8 bc_addr[] = {0XFF, 0XFF, 0XFF, 0XFF, 0XFF, 0XFF}; + t_u8 sta_addr[ETH_ALEN]; + int ret = 0, i; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + memset(sta_addr, 0, ETH_ALEN); + if ((mlme->cmd == IW_MLME_DEAUTH) || (mlme->cmd == IW_MLME_DISASSOC)) { + moal_memcpy_ext(priv->phandle, sta_addr, + (t_u8 *)mlme->addr.sa_data, ETH_ALEN, + sizeof(sta_addr)); + PRINTM(MIOCTL, "Deauth station: " MACSTR ", reason=%d\n", + MAC2STR(sta_addr), mlme->reason_code); + + /* FIXME: For flushing all stations we need to use zero MAC, + * but right now the FW does not support this. So, manually + * delete each one individually. + */ + /* If deauth all station, get the connected STA list first */ + if (!memcmp(bc_addr, sta_addr, ETH_ALEN)) { + PRINTM(MIOCTL, "Deauth all stations\n"); + req = woal_alloc_mlan_ioctl_req( + sizeof(mlan_ds_get_info)); + if (req == NULL) { + LEAVE(); + return -ENOMEM; + } + pinfo = (mlan_ds_get_info *)req->pbuf; + pinfo->sub_command = MLAN_OID_UAP_STA_LIST; + req->req_id = MLAN_IOCTL_GET_INFO; + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + sta_list = + kmalloc(sizeof(mlan_ds_sta_list), GFP_KERNEL); + if (sta_list == NULL) { + PRINTM(MERROR, "Memory allocation failed!\n"); + ret = -ENOMEM; + goto done; + } + moal_memcpy_ext(priv->phandle, sta_list, + &pinfo->param.sta_list, + sizeof(mlan_ds_sta_list), + sizeof(mlan_ds_sta_list)); + kfree(req); + req = NULL; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + if (sta_list && !memcmp(bc_addr, sta_addr, ETH_ALEN)) { + for (i = 0; i < sta_list->sta_count; i++) { + moal_memcpy_ext( + priv->phandle, + bss->param.deauth_param.mac_addr, + sta_list->info[i].mac_address, ETH_ALEN, + sizeof(bss->param.deauth_param + .mac_addr)); + bss->param.deauth_param.reason_code = + mlme->reason_code; + + status = woal_request_ioctl(priv, req, + MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } + } else { + moal_memcpy_ext( + priv->phandle, bss->param.deauth_param.mac_addr, + sta_addr, ETH_ALEN, + sizeof(bss->param.deauth_param.mac_addr)); + bss->param.deauth_param.reason_code = mlme->reason_code; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + kfree(sta_list); + LEAVE(); + return ret; +} + +/** @brief Set authentication mode parameters + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_auth(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_param *vwrq = &wrqu->param; + mlan_uap_bss_param *sys_cfg = NULL; + + ENTER(); + + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + switch (vwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + /* Rest are not supported now */ + if (vwrq->value & IW_AUTH_CIPHER_NONE) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_WEP40) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_WEP104) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value == IW_AUTH_CIPHER_TKIP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = CIPHER_TKIP; + sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = CIPHER_TKIP; + priv->pairwise_cipher = CIPHER_TKIP; + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher TKIP\n"); + } else if (vwrq->value == IW_AUTH_CIPHER_CCMP) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = CIPHER_AES_CCMP; + sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = CIPHER_AES_CCMP; + priv->pairwise_cipher = CIPHER_AES_CCMP; + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher CCMP\n"); + } else if (vwrq->value == + (IW_AUTH_CIPHER_TKIP | IW_AUTH_CIPHER_CCMP)) { + sys_cfg->wpa_cfg.pairwise_cipher_wpa = + (CIPHER_TKIP | CIPHER_AES_CCMP); + sys_cfg->wpa_cfg.pairwise_cipher_wpa2 = + (CIPHER_TKIP | CIPHER_AES_CCMP); + priv->pairwise_cipher = (CIPHER_TKIP | CIPHER_AES_CCMP); + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher TKIP + CCMP\n"); + } + break; + case IW_AUTH_CIPHER_GROUP: + /* Rest are not supported now */ + if (vwrq->value & IW_AUTH_CIPHER_NONE) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_WEP40) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_WEP104) + /* XXX Do not delete no-operation line */ + ; + else if (vwrq->value & IW_AUTH_CIPHER_TKIP) { + sys_cfg->wpa_cfg.group_cipher = CIPHER_TKIP; + priv->group_cipher = CIPHER_TKIP; + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher TKIP\n"); + } else if (vwrq->value & IW_AUTH_CIPHER_CCMP) { + sys_cfg->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + priv->group_cipher = CIPHER_AES_CCMP; + if (!priv->uap_key_mgmt) + priv->uap_key_mgmt = KEY_MGMT_PSK; + PRINTM(MINFO, "Set Auth Cipher CCMP\n"); + } + break; + case IW_AUTH_80211_AUTH_ALG: + switch (vwrq->value) { + case IW_AUTH_ALG_SHARED_KEY: + PRINTM(MINFO, "Auth mode shared key!\n"); + sys_cfg->auth_mode = MLAN_AUTH_MODE_SHARED; + break; + case IW_AUTH_ALG_LEAP: + break; + case IW_AUTH_ALG_OPEN_SYSTEM: + PRINTM(MINFO, "Auth mode open!\n"); + sys_cfg->auth_mode = MLAN_AUTH_MODE_OPEN; + break; + default: + PRINTM(MINFO, "Auth mode auto!\n"); + break; + } + break; + case IW_AUTH_WPA_VERSION: + switch (vwrq->value) { + case IW_AUTH_WPA_VERSION_DISABLED: + sys_cfg->protocol = PROTOCOL_NO_SECURITY; + break; + case IW_AUTH_WPA_VERSION_WPA: + sys_cfg->protocol = PROTOCOL_WPA; + break; + case IW_AUTH_WPA_VERSION_WPA2: + sys_cfg->protocol = PROTOCOL_WPA2; + break; + case IW_AUTH_WPA_VERSION_WPA | IW_AUTH_WPA_VERSION_WPA2: + sys_cfg->protocol = PROTOCOL_WPA2_MIXED; + break; + default: + break; + } + priv->uap_protocol = sys_cfg->protocol; + break; + case IW_AUTH_KEY_MGMT: + switch (vwrq->value) { + case IW_AUTH_KEY_MGMT_802_1X: + sys_cfg->key_mgmt |= KEY_MGMT_EAP; + priv->uap_key_mgmt |= KEY_MGMT_EAP; + break; + case IW_AUTH_KEY_MGMT_PSK: + sys_cfg->key_mgmt |= KEY_MGMT_PSK; + priv->uap_key_mgmt |= KEY_MGMT_PSK; + break; + default: + break; + } + break; + case IW_AUTH_WPA_ENABLED: + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + default: + kfree(sys_cfg); + LEAVE(); + return -EOPNOTSUPP; /* No AP configuration */ + } + if (!sys_cfg->key_mgmt) + sys_cfg->key_mgmt = priv->uap_key_mgmt; + if (sys_cfg->key_mgmt & KEY_MGMT_PSK) + sys_cfg->key_mgmt_operation |= 0x01; + if (sys_cfg->key_mgmt & KEY_MGMT_EAP) + sys_cfg->key_mgmt_operation |= 0x03; + if (!sys_cfg->protocol) + sys_cfg->protocol = priv->uap_protocol; + + /* Set AP configuration */ + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + LEAVE(); + return 0; +} + +/** + * @brief Get authentication mode parameters + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_auth(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_param *vwrq = &wrqu->param; + mlan_uap_bss_param *ap_cfg = NULL; + + ENTER(); + + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + kfree(ap_cfg); + LEAVE(); + return -EFAULT; + } + switch (vwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_TKIP || + ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == CIPHER_TKIP) + vwrq->value = IW_AUTH_CIPHER_TKIP; + else if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == + CIPHER_AES_CCMP || + ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == + CIPHER_AES_CCMP) + vwrq->value = IW_AUTH_CIPHER_CCMP; + else + vwrq->value = IW_AUTH_CIPHER_NONE; + break; + case IW_AUTH_CIPHER_GROUP: + if (ap_cfg->wpa_cfg.group_cipher == CIPHER_TKIP) + vwrq->value = IW_AUTH_CIPHER_TKIP; + else if (ap_cfg->wpa_cfg.group_cipher == CIPHER_AES_CCMP) + vwrq->value = IW_AUTH_CIPHER_CCMP; + else + vwrq->value = IW_AUTH_CIPHER_NONE; + break; + case IW_AUTH_80211_AUTH_ALG: + if (ap_cfg->auth_mode == MLAN_AUTH_MODE_SHARED) + vwrq->value = IW_AUTH_ALG_SHARED_KEY; + else if (ap_cfg->auth_mode == MLAN_AUTH_MODE_NETWORKEAP) + vwrq->value = IW_AUTH_ALG_LEAP; + else + vwrq->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + case IW_AUTH_WPA_ENABLED: + if (ap_cfg->protocol == PROTOCOL_WPA || + ap_cfg->protocol == PROTOCOL_WPA2 || + ap_cfg->protocol == PROTOCOL_WPA2_MIXED) + vwrq->value = 1; + else + vwrq->value = 0; + break; + case IW_AUTH_KEY_MGMT: + if (ap_cfg->key_mgmt & KEY_MGMT_EAP) + vwrq->value |= IW_AUTH_KEY_MGMT_802_1X; + if (ap_cfg->key_mgmt & KEY_MGMT_PSK) + vwrq->value |= IW_AUTH_KEY_MGMT_PSK; + break; + case IW_AUTH_WPA_VERSION: + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: + default: + kfree(ap_cfg); + LEAVE(); + return -EOPNOTSUPP; + } + kfree(ap_cfg); + LEAVE(); + return 0; +} +#endif /* WE >= 18 */ + +/* Data rate listing + * MULTI_BANDS: + * abg a b b/g + * Infra G(12) A(8) B(4) G(12) + * Adhoc A+B(12) A(8) B(4) B(4) + * non-MULTI_BANDS: + b b/g + * Infra B(4) G(12) + * Adhoc B(4) B(4) + */ +/** + * @brief Get Range Info + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_range(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + mlan_uap_bss_param *ap_cfg = NULL; + struct iw_range *range = (struct iw_range *)extra; + t_u8 band = 0; + int i; + + ENTER(); + + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + kfree(ap_cfg); + LEAVE(); + return -EFAULT; + } + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->min_nwid = 0; + range->max_nwid = 0; + + range->num_bitrates = MAX_DATA_RATES; + for (i = 0; + i < MIN(MAX_DATA_RATES, IW_MAX_BITRATES) && ap_cfg->rates[i]; + i++) { + range->bitrate[i] = (ap_cfg->rates[i] & 0x7f) * 500000; + } + range->num_bitrates = i; + PRINTM(MINFO, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES, + range->num_bitrates); + + range->num_frequency = MIN(ap_cfg->num_of_chan, IW_MAX_FREQUENCIES); + + for (i = 0; i < range->num_frequency; i++) { + range->freq[i].i = (long)ap_cfg->chan_list[i].chan_number; + band = (ap_cfg->chan_list[i].bandcfg.chanBand == BAND_5GHZ); + range->freq[i].m = + (long)channel_to_frequency( + ap_cfg->chan_list[i].chan_number, band) * + 100000; + range->freq[i].e = 1; + } + + PRINTM(MINFO, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n", + IW_MAX_FREQUENCIES, range->num_frequency); + + range->num_channels = range->num_frequency; + + woal_sort_channels(&range->freq[0], range->num_frequency); + + /* + * Set an indication of the max TCP throughput in bit/s that we can + * expect using this interface + */ + if (i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = MLAN_RTS_MIN_VALUE; + range->max_rts = MLAN_RTS_MAX_VALUE; + range->min_frag = MLAN_FRAG_MIN_VALUE; + range->max_frag = MLAN_FRAG_MAX_VALUE; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + +/** Minimum power period */ +#define IW_POWER_PERIOD_MIN 1000000 /* 1 sec */ +/** Maximum power period */ +#define IW_POWER_PERIOD_MAX 120000000 /* 2 min */ +/** Minimum power timeout value */ +#define IW_POWER_TIMEOUT_MIN 1000 /* 1 ms */ +/** Maximim power timeout value */ +#define IW_POWER_TIMEOUT_MAX 1000000 /* 1 sec */ + + /* Power Management duration & timeout */ + range->min_pmp = IW_POWER_PERIOD_MIN; + range->max_pmp = IW_POWER_PERIOD_MAX; + range->min_pmt = IW_POWER_TIMEOUT_MIN; + range->max_pmt = IW_POWER_TIMEOUT_MAX; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* + * Minimum version we recommend + */ + range->we_version_source = 15; + + /* + * Version we are compiled with + */ + range->we_version_compiled = WIRELESS_EXT; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + + range->min_retry = MLAN_TX_RETRY_MIN; + range->max_retry = MLAN_TX_RETRY_MAX; + +#if (WIRELESS_EXT >= 18) + if (ap_cfg->protocol & PROTOCOL_WPA) + range->enc_capa |= IW_ENC_CAPA_WPA; + if (ap_cfg->protocol & PROTOCOL_WPA2) + range->enc_capa |= IW_ENC_CAPA_WPA2; + if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_TKIP || + ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == CIPHER_TKIP || + ap_cfg->wpa_cfg.group_cipher == CIPHER_TKIP) + range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; + if (ap_cfg->wpa_cfg.pairwise_cipher_wpa == CIPHER_AES_CCMP || + ap_cfg->wpa_cfg.pairwise_cipher_wpa2 == CIPHER_AES_CCMP || + ap_cfg->wpa_cfg.group_cipher == CIPHER_AES_CCMP) + range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; +#endif + kfree(ap_cfg); + LEAVE(); + return 0; +} + +/** + * @brief Set priv command + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_priv(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, 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, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + mlan_uap_bss_param *sys_cfg = NULL; + int ret = 0; + + ENTER(); + + /* Check the size of the string */ + if (dwrq->length > IW_ESSID_MAX_SIZE + 1) { + ret = -E2BIG; + goto done; + } + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + ret = -EFAULT; + goto done; + } + + /* Initialize the invalid values so that the correct values + * below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + + /* Set the SSID */ +#if WIRELESS_EXT > 20 + sys_cfg->ssid.ssid_len = dwrq->length; +#else + sys_cfg->ssid.ssid_len = dwrq->length - 1; +#endif + + moal_memcpy_ext(priv->phandle, sys_cfg->ssid.ssid, extra, + sys_cfg->ssid.ssid_len, sizeof(sys_cfg->ssid.ssid)); + if (!sys_cfg->ssid.ssid_len || sys_cfg->ssid.ssid[0] < 0x20) { + PRINTM(MERROR, "Invalid SSID - aborting set_essid\n"); + ret = -EINVAL; + goto done; + } + PRINTM(MINFO, "Requested new SSID = %s\n", + (sys_cfg->ssid.ssid_len > 0) ? (char *)sys_cfg->ssid.ssid : + "NULL"); + + /* Set AP configuration */ + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET, + MOAL_IOCTL_WAIT, + sys_cfg)) { + PRINTM(MERROR, "Error setting AP confiruration\n"); + ret = -EFAULT; + goto done; + } + +done: + kfree(sys_cfg); + LEAVE(); + return ret; +} + +/** + * @brief Get current essid + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int woal_get_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_uap_bss_param *ap_cfg = NULL; + + ENTER(); + + ap_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!ap_cfg) { + PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); + return -EFAULT; + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + ap_cfg)) { + PRINTM(MERROR, "Error getting AP confiruration\n"); + kfree(ap_cfg); + LEAVE(); + return -EFAULT; + } + + if (priv->bss_started) { + dwrq->length = MIN(dwrq->length, ap_cfg->ssid.ssid_len); + moal_memcpy_ext(priv->phandle, extra, ap_cfg->ssid.ssid, + dwrq->length, dwrq->length); + } else + dwrq->length = 0; + + dwrq->flags = 1; + kfree(ap_cfg); + LEAVE(); + return 0; +} + +/** + * iwconfig settable callbacks + */ +static const iw_handler woal_handler[] = { + (iw_handler)woal_config_commit, /* SIOCSIWCOMMIT */ + (iw_handler)woal_get_name, /* SIOCGIWNAME */ + (iw_handler)NULL, /* SIOCSIWNWID */ + (iw_handler)NULL, /* SIOCGIWNWID */ + (iw_handler)woal_set_freq, /* SIOCSIWFREQ */ + (iw_handler)woal_get_freq, /* SIOCGIWFREQ */ + (iw_handler)woal_set_bss_mode, /* SIOCSIWMODE */ + (iw_handler)woal_get_bss_mode, /* SIOCGIWMODE */ + (iw_handler)NULL, /* SIOCSIWSENS */ + (iw_handler)NULL, /* SIOCGIWSENS */ + (iw_handler)NULL, /* SIOCSIWRANGE */ + (iw_handler)woal_get_range, /* SIOCGIWRANGE */ + (iw_handler)woal_set_priv, /* SIOCSIWPRIV */ + (iw_handler)NULL, /* SIOCGIWPRIV */ + (iw_handler)NULL, /* SIOCSIWSTATS */ + (iw_handler)NULL, /* SIOCGIWSTATS */ +#if WIRELESS_EXT > 15 +#ifdef CONFIG_WEXT_SPY + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ +#endif +#else /* WIRELESS_EXT > 15 */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ +#endif /* WIRELESS_EXT > 15 */ + (iw_handler)woal_set_wap, /* SIOCSIWAP */ + (iw_handler)woal_get_wap, /* SIOCGIWAP */ +#if WIRELESS_EXT >= 18 + (iw_handler)woal_set_mlme, /* SIOCSIWMLME */ +#else + (iw_handler)NULL, /* -- hole -- */ +#endif + /* (iw_handler) wlan_get_aplist, */ /* SIOCGIWAPLIST */ + NULL, /* SIOCGIWAPLIST */ +#if WIRELESS_EXT > 13 + (iw_handler)NULL, /* SIOCSIWSCAN */ + (iw_handler)NULL, /* SIOCGIWSCAN */ +#else /* WIRELESS_EXT > 13 */ + (iw_handler)NULL, /* SIOCSIWSCAN */ + (iw_handler)NULL, /* SIOCGIWSCAN */ +#endif /* WIRELESS_EXT > 13 */ + (iw_handler)woal_set_essid, /* SIOCSIWESSID */ + (iw_handler)woal_get_essid, /* SIOCGIWESSID */ + (iw_handler)NULL, /* SIOCSIWNICKN */ + (iw_handler)NULL, /* SIOCGIWNICKN */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* SIOCSIWRATE */ + (iw_handler)NULL, /* SIOCGIWRATE */ + (iw_handler)NULL, /* SIOCSIWRTS */ + (iw_handler)NULL, /* SIOCGIWRTS */ + (iw_handler)NULL, /* SIOCSIWFRAG */ + (iw_handler)NULL, /* SIOCGIWFRAG */ + (iw_handler)NULL, /* SIOCSIWTXPOW */ + (iw_handler)NULL, /* SIOCGIWTXPOW */ + (iw_handler)NULL, /* SIOCSIWRETRY */ + (iw_handler)NULL, /* SIOCGIWRETRY */ + (iw_handler)woal_set_encode, /* SIOCSIWENCODE */ + (iw_handler)woal_get_encode, /* SIOCGIWENCODE */ + (iw_handler)NULL, /* SIOCSIWPOWER */ + (iw_handler)NULL, /* SIOCGIWPOWER */ +#if (WIRELESS_EXT >= 18) + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)woal_set_gen_ie, /* SIOCSIWGENIE */ + (iw_handler)woal_get_gen_ie, /* SIOCGIWGENIE */ + (iw_handler)woal_set_auth, /* SIOCSIWAUTH */ + (iw_handler)woal_get_auth, /* SIOCGIWAUTH */ + (iw_handler)woal_set_encode_ext, /* SIOCSIWENCODEEXT */ + (iw_handler)woal_get_encode_ext, /* SIOCGIWENCODEEXT */ +#endif /* WIRELESSS_EXT >= 18 */ +}; + +/** + * iwpriv settable callbacks + */ +static const iw_handler woal_private_handler[] = { + NULL, /* SIOCIWFIRSTPRIV */ +}; + +/******************************************************** + Global Functions +********************************************************/ + +// clang-format off +/** wlan_handler_def */ +struct iw_handler_def woal_uap_handler_def = { + num_standard: ARRAY_SIZE(woal_handler), + num_private : ARRAY_SIZE(woal_private_handler), + num_private_args : ARRAY_SIZE(woal_uap_priv_args), + standard : (iw_handler *)woal_handler, + private : (iw_handler *)woal_private_handler, + private_args : (struct iw_priv_args *)woal_uap_priv_args, +#if WIRELESS_EXT > 20 + get_wireless_stats : woal_get_uap_wireless_stats, +#endif +}; +// clang-format on + +/** + * @brief Get wireless statistics + * + * @param dev A pointer to net_device structure + * + * @return A pointer to iw_statistics buf + */ +struct iw_statistics *woal_get_uap_wireless_stats(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u16 wait_option = MOAL_IOCTL_WAIT; + + ENTER(); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + /* + * Since schedule() is not allowed from an atomic context + * such as when dev_base_lock for netdevices is acquired + * for reading/writing in kernel before this call, HostCmd + * is issued in non-blocking way in such contexts and + * blocking in other cases. + */ + if (in_atomic() || !write_can_lock(&dev_base_lock)) + wait_option = MOAL_NO_WAIT; +#endif + + priv->w_stats.qual.qual = 0; + priv->w_stats.qual.level = 0; + priv->w_stats.discard.code = 0; + priv->w_stats.status = IW_MODE_MASTER; + woal_uap_get_stats(priv, wait_option, NULL); + + LEAVE(); + return &priv->w_stats; +} diff --git a/mxm_wifiex/wlan_src/mlinux/moal_usb.c b/mxm_wifiex/wlan_src/mlinux/moal_usb.c new file mode 100644 index 0000000..155003f --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_usb.c @@ -0,0 +1,2028 @@ +/** @file moal_usb.c + * + * @brief This file contains the interfaceing to USB bus + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_usb.h" +extern struct semaphore AddRemoveCardSem; + +/******************************************************** + Local Variables +********************************************************/ + +#if defined(USB8997) || defined(USB9098) || defined(USB9097) || defined(USB8978) +/** Card-type detection frame response */ +typedef struct { + /** 32-bit ACK+WINNER field */ + t_u32 ack_winner; + /** 32-bit Sequence number */ + t_u32 seq; + /** 32-bit extend */ + t_u32 extend; + /** 32-bit chip-revision code */ + t_u32 chip_rev; + /** 32-bit strap setting */ + t_u32 strap; +} usb_ack_pkt; +#endif + +/** NXP USB device */ +#define NXP_USB_DEVICE(vid, pid, name) \ + USB_DEVICE(vid, pid), .driver_info = (t_ptr)name + +/** Name of the USB driver */ +const char usbdriver_name[] = "usbxxx"; + +/** This structure contains the device signature */ +struct usb_device_id woal_usb_table[] = { +/* Enter the device signature inside */ +#ifdef USB8897 + {NXP_USB_DEVICE(USB8897_VID_1, USB8897_PID_1, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8897_VID_1, USB8897_PID_2, "NXP WLAN USB Adapter")}, +#endif +#ifdef USB8997 + {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_1, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8997_VID_1, USB8997V2_PID_1, + "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_2, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_3, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_4, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_5, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_6, "NXP WLAN USB Adapter")}, +#endif +#ifdef USB8978 + {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_1, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_1_BT, + "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2_BT, + "NXP WLAN USB Adapter")}, +#endif +#ifdef USB9098 + {NXP_USB_DEVICE(USB9098_VID_1, USB9098_PID_1, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB9098_VID_1, USB9098_PID_2, "NXP WLAN USB Adapter")}, +#endif +#ifdef USB9097 + {NXP_USB_DEVICE(USB9097_VID_1, USB9097_PID_1, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB9097_VID_1, USB9097_PID_2, "NXP WLAN USB Adapter")}, +#endif + /* Terminating entry */ + {}, +}; + +/** This structure contains the device signature */ +struct usb_device_id woal_usb_table_skip_fwdnld[] = { +/* Enter the device signature inside */ +#ifdef USB8897 + {NXP_USB_DEVICE(USB8897_VID_1, USB8897_PID_2, "NXP WLAN USB Adapter")}, +#endif +#ifdef USB8997 + {NXP_USB_DEVICE(USB8997_VID_1, USB8997_PID_2, "NXP WLAN USB Adapter")}, +#endif +#ifdef USB8978 + {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2, "NXP WLAN USB Adapter")}, + {NXP_USB_DEVICE(USB8978_VID_1, USB8978_PID_2_BT, + "NXP WLAN USB Adapter")}, +#endif +#ifdef USB9098 + {NXP_USB_DEVICE(USB9098_VID_1, USB9098_PID_2, "NXP WLAN USB Adapter")}, +#endif +#ifdef USB9097 + {NXP_USB_DEVICE(USB9097_VID_1, USB9097_PID_2, "NXP WLAN USB Adapter")}, +#endif + /* Terminating entry */ + {}, +}; + +static mlan_status woal_usb_submit_rx_urb(urb_context *ctx, int size); +static int woal_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id); +static void woal_usb_disconnect(struct usb_interface *intf); +static mlan_status woal_usb_write_data_sync(moal_handle *handle, + mlan_buffer *pmbuf, t_u32 endpoint, + t_u32 timeout); +static mlan_status woal_usb_read_data_sync(moal_handle *handle, + mlan_buffer *pmbuf, t_u32 endpoint, + t_u32 timeout); +#ifdef CONFIG_PM +static int woal_usb_suspend(struct usb_interface *intf, pm_message_t message); +static int woal_usb_resume(struct usb_interface *intf); +#endif /* CONFIG_PM */ + +/** woal_usb_driver */ +static struct usb_driver REFDATA woal_usb_driver = { + /* Driver name */ + .name = usbdriver_name, + + /* Probe function name */ + .probe = woal_usb_probe, + + /* Disconnect function name */ + .disconnect = woal_usb_disconnect, + + /* Device signature table */ + .id_table = woal_usb_table, +#ifdef CONFIG_PM + /* Suspend function name */ + .suspend = woal_usb_suspend, + + /* Resume function name */ + .resume = woal_usb_resume, + + /* Reset resume function name */ + .reset_resume = woal_usb_resume, + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) + /* Driver supports autosuspend */ + .supports_autosuspend = 1, +#endif +#endif /* CONFIG_PM */ +}; + +MODULE_DEVICE_TABLE(usb, woal_usb_table); +MODULE_DEVICE_TABLE(usb, woal_usb_table_skip_fwdnld); + +/* moal interface ops */ +static moal_if_ops usb_ops; + +/******************************************************** + Global Variables +********************************************************/ + +extern int skip_fwdnld; +extern int max_tx_buf; + +/******************************************************** + Local Functions +********************************************************/ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +/** + * @brief This function receive packet of the data/cmd/event packet + * and pass to MLAN + * + * @param urb Pointer to struct urb + * @param regs Registers + * + * @return N/A + */ +static void woal_usb_receive(struct urb *urb, struct pt_regs *regs) +#else +/** + * @brief This function receive packet of the data/cmd/event packet + * and pass to MLAN + * + * @param urb Pointer to struct urb + * + * @return N/A + */ +static void woal_usb_receive(struct urb *urb) +#endif +{ + urb_context *context = NULL; + moal_handle *handle = NULL; + mlan_buffer *pmbuf = NULL; + struct usb_card_rec *cardp = NULL; + int recv_length; + int size; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (!urb || !urb->context) { + PRINTM(MERROR, "URB or URB context is not valid in USB Rx\n"); + LEAVE(); + return; + } + context = (urb_context *)urb->context; + handle = context->handle; + pmbuf = context->pmbuf; + recv_length = urb->actual_length; + + if (!handle || !handle->card || !pmbuf) { + PRINTM(MERROR, + "moal handle, card structure or mlan_buffer is not valid in USB Rx\n"); + LEAVE(); + return; + } + cardp = (struct usb_card_rec *)handle->card; + if (cardp->rx_cmd_ep == context->ep) + atomic_dec(&cardp->rx_cmd_urb_pending); + else + atomic_dec(&cardp->rx_data_urb_pending); + + if (recv_length) { + if (urb->status || (handle->surprise_removed == MTRUE)) { + if (handle->surprise_removed || handle->is_suspended) { + woal_free_mlan_buffer(handle, pmbuf); + context->pmbuf = NULL; + goto rx_exit; + } else { + PRINTM(MERROR, + "EP %d Rx URB status failure: %d\n", + context->ep, urb->status); + /* Do not free mlan_buffer in case of command ep + */ + if (cardp->rx_cmd_ep != context->ep) + woal_free_mlan_buffer(handle, pmbuf); + goto setup_for_next; + } + } + pmbuf->data_len = recv_length; + pmbuf->flags |= MLAN_BUF_FLAG_RX_DEAGGR; + /* Send packet to MLAN */ + atomic_inc(&handle->rx_pending); + status = mlan_recv(handle->pmlan_adapter, pmbuf, context->ep); + PRINTM(MINFO, "Receive length = 0x%x, status=%d\n", recv_length, + status); + if (status == MLAN_STATUS_PENDING) { + queue_work(handle->workqueue, &handle->main_work); + /* urb for data_ep is re-submitted now, unless we reach + * HIGH_RX_PENDING */ + /* urb for cmd_ep will be re-submitted in callback + * moal_recv_complete */ + if (cardp->rx_cmd_ep == context->ep) + goto rx_exit; + else if (atomic_read(&handle->rx_pending) >= + HIGH_RX_PENDING) { + context->pmbuf = NULL; + goto rx_exit; + } + } else { + atomic_dec(&handle->rx_pending); + if (status == MLAN_STATUS_FAILURE) { + PRINTM(MERROR, + "MLAN fail to process the receive data\n"); + } else if ((status == MLAN_STATUS_SUCCESS) && + (pmbuf->flags & + MLAN_BUF_FLAG_SLEEPCFM_RESP)) { + pmbuf->flags &= ~MLAN_BUF_FLAG_SLEEPCFM_RESP; + queue_work(handle->workqueue, + &handle->main_work); + } + /* Do not free mlan_buffer in case of command ep */ + if (cardp->rx_cmd_ep != context->ep) + woal_free_mlan_buffer(handle, pmbuf); + } + } else if (urb->status) { + if (!((cardp->rx_data_ep == context->ep) && + (cardp->resubmit_urbs == 1))) { + if (!handle->is_suspended) { + PRINTM(MMSG, "Card is removed: %d\n", + urb->status); + handle->surprise_removed = MTRUE; + } + } + woal_free_mlan_buffer(handle, pmbuf); + context->pmbuf = NULL; + goto rx_exit; + } else { + /* Do not free mlan_buffer in case of command ep */ + if (cardp->rx_cmd_ep != context->ep) + woal_free_mlan_buffer(handle, pmbuf); + goto setup_for_next; + } + +setup_for_next: + if (cardp->rx_cmd_ep == context->ep) { + size = MLAN_RX_CMD_BUF_SIZE; + } else { + if (cardp->rx_deaggr_ctrl.enable) { + size = cardp->rx_deaggr_ctrl.aggr_max; + if (cardp->rx_deaggr_ctrl.aggr_mode == + MLAN_USB_AGGR_MODE_NUM) { + size *= MAX(MLAN_USB_MAX_PKT_SIZE, + cardp->rx_deaggr_ctrl.aggr_align); + size = MAX(size, MLAN_RX_DATA_BUF_SIZE); + } + } else + size = MLAN_RX_DATA_BUF_SIZE; + } + woal_usb_submit_rx_urb(context, size); + +rx_exit: + LEAVE(); + return; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +/** + * @brief Call back function to handle the status of the Tx data URB + * + * @param urb Pointer to urb structure + * @param regs Registers + * + * @return N/A + */ +static void woal_usb_tx_complete(struct urb *urb, struct pt_regs *regs) +#else +/** + * @brief Call back function to handle the status of the Tx data URB + * + * @param urb Pointer to urb structure + * + * @return N/A + */ +static void woal_usb_tx_complete(struct urb *urb) +#endif +{ + urb_context *context = NULL; + moal_handle *handle = NULL; + struct usb_card_rec *cardp = NULL; + + ENTER(); + + if (!urb || !urb->context) { + PRINTM(MERROR, + "URB or URB context is not valid in USB Tx complete\n"); + LEAVE(); + return; + } + context = (urb_context *)urb->context; + handle = context->handle; + + if (!handle || !handle->card || !context->pmbuf) { + PRINTM(MERROR, + "moal handle, card structure or mlan_buffer is not valid in USB Tx complete\n"); + LEAVE(); + return; + } + cardp = handle->card; + + /* Handle the transmission complete validations */ + if (urb->status) { + PRINTM(MERROR, "EP %d Tx URB status failure: %d\n", context->ep, + urb->status); + mlan_write_data_async_complete(handle->pmlan_adapter, + context->pmbuf, context->ep, + MLAN_STATUS_FAILURE); + } else { + mlan_write_data_async_complete(handle->pmlan_adapter, + context->pmbuf, context->ep, + MLAN_STATUS_SUCCESS); + } + + /* Decrease pending URB counter */ + if (context->ep == cardp->tx_cmd_ep) + atomic_dec(&cardp->tx_cmd_urb_pending); + else if (context->ep == cardp->tx_data_ep) + atomic_dec(&cardp->tx_data_urb_pending); + + queue_work(handle->workqueue, &handle->main_work); + + LEAVE(); + return; +} + +/** + * @brief This function sets up the data to receive + * + * @param ctx Pointer to urb_context structure + * @param size Skb size + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_usb_submit_rx_urb(urb_context *ctx, int size) +{ + moal_handle *handle = ctx->handle; + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; + mlan_status ret = MLAN_STATUS_FAILURE; + t_u8 *data = NULL; + + ENTER(); + + if (handle->surprise_removed || handle->is_suspended) { + if ((cardp->rx_cmd_ep == ctx->ep) && ctx->pmbuf) { + woal_free_mlan_buffer(handle, ctx->pmbuf); + ctx->pmbuf = NULL; + } + PRINTM(MERROR, + "Card removed/suspended, EP %d Rx URB submit skipped\n", + ctx->ep); + goto rx_ret; + } + + if (cardp->rx_cmd_ep != ctx->ep) { + ctx->pmbuf = woal_alloc_mlan_buffer(handle, size); + if (!ctx->pmbuf) { + PRINTM(MERROR, + "Fail to submit Rx URB due to no memory/skb\n"); + goto rx_ret; + } + ctx->pmbuf->data_offset = MLAN_RX_HEADER_LEN; + data = ctx->pmbuf->pbuf + ctx->pmbuf->data_offset; + } else { + ctx->pmbuf->data_offset = 0; + data = ctx->pmbuf->pbuf + ctx->pmbuf->data_offset; + } + + if (cardp->rx_cmd_ep == ctx->ep && + cardp->rx_cmd_ep_type == USB_ENDPOINT_XFER_INT) + usb_fill_int_urb(ctx->urb, cardp->udev, + usb_rcvintpipe(cardp->udev, ctx->ep), data, + size - ctx->pmbuf->data_offset, + woal_usb_receive, (void *)ctx, + cardp->rx_cmd_interval); + else + usb_fill_bulk_urb(ctx->urb, cardp->udev, + usb_rcvbulkpipe(cardp->udev, ctx->ep), data, + size - ctx->pmbuf->data_offset, + woal_usb_receive, (void *)ctx); + if (cardp->rx_cmd_ep == ctx->ep) + atomic_inc(&cardp->rx_cmd_urb_pending); + else + atomic_inc(&cardp->rx_data_urb_pending); + if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) { + /* Submit URB failure */ + PRINTM(MERROR, "Submit EP %d Rx URB failed: %d\n", ctx->ep, + ret); + woal_free_mlan_buffer(handle, ctx->pmbuf); + if (cardp->rx_cmd_ep == ctx->ep) + atomic_dec(&cardp->rx_cmd_urb_pending); + else + atomic_dec(&cardp->rx_data_urb_pending); + ctx->pmbuf = NULL; + ret = MLAN_STATUS_FAILURE; + } else { + ret = MLAN_STATUS_SUCCESS; + } +rx_ret: + LEAVE(); + return ret; +} + +/******************************************************** + Global Functions +********************************************************/ + +#if defined(USB8997) || defined(USB9098) || defined(USB9097) || defined(USB8978) +/** + * @brief Check chip revision + * + * @param handle A pointer to moal_handle structure + * @param usb_chip_rev A pointer to usb_chip_rev variable + * @param usb_strap A pointer to usb_strap + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_check_chip_revision(moal_handle *handle, t_u32 *usb_chip_rev, + t_u32 *usb_strap) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_buffer mbuf; + t_u8 *tx_buff = 0; + t_u8 *recv_buff = 0; + usb_ack_pkt ack_pkt; + t_u32 extend_ver; + t_u8 tx_size = CHIP_REV_TX_BUF_SIZE; + struct usb_card_rec *cardp = handle->card; + + ENTER(); + + /* Allocate memory for transmit */ + tx_buff = kzalloc(tx_size, GFP_ATOMIC | GFP_DMA); + if (tx_buff == NULL) { + PRINTM(MERROR, + "Could not allocate buffer for chip revision check frame transmission\n"); + ret = MLAN_STATUS_FAILURE; + goto cleanup; + } + + /* Allocate memory for receive */ + recv_buff = kzalloc(CHIP_REV_RX_BUF_SIZE, GFP_ATOMIC | GFP_DMA); + if (recv_buff == NULL) { + PRINTM(MERROR, + "Could not allocate buffer for chip revision check frame response\n"); + ret = MLAN_STATUS_FAILURE; + goto cleanup; + } + + /* The struct is initialised to all zero */ + memset(&ack_pkt, 0, sizeof(usb_ack_pkt)); + + /* Send pseudo data to check winner status first */ + memset(&mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = (t_u8 *)tx_buff; + mbuf.data_len = tx_size; + + /* Send the chip revision check frame */ + ret = woal_usb_write_data_sync(handle, &mbuf, cardp->tx_cmd_ep, + MLAN_USB_BULK_MSG_TIMEOUT); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Chip revision check frame dnld: write_data failed, ret %d\n", + ret); + ret = MLAN_STATUS_FAILURE; + goto cleanup; + } + + memset(&mbuf, 0, sizeof(mlan_buffer)); + mbuf.pbuf = (t_u8 *)recv_buff; + mbuf.data_len = CHIP_REV_RX_BUF_SIZE; + + /* Receive the chip revision check frame response */ + ret = woal_usb_read_data_sync(handle, &mbuf, cardp->rx_cmd_ep, + MLAN_USB_BULK_MSG_TIMEOUT); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MERROR, + "Chip revision check frame response: read_data failed, ret %d\n", + ret); + ret = MLAN_STATUS_FAILURE; + goto cleanup; + } + moal_memcpy_ext(handle, &ack_pkt, recv_buff, sizeof(usb_ack_pkt), + sizeof(ack_pkt)); + ack_pkt.ack_winner = woal_le32_to_cpu(ack_pkt.ack_winner); + ack_pkt.seq = woal_le32_to_cpu(ack_pkt.seq); + ack_pkt.extend = woal_le32_to_cpu(ack_pkt.extend); + ack_pkt.chip_rev = woal_le32_to_cpu(ack_pkt.chip_rev); + ack_pkt.strap = woal_le32_to_cpu(ack_pkt.strap); + + if ((ack_pkt.extend & 0xffff0000) == EXTEND_HDR) { + extend_ver = ack_pkt.extend & 0x0000ffff; + *usb_chip_rev = ack_pkt.chip_rev & 0x000000ff; + if (extend_ver >= EXTEND_V2) { + PRINTM(MINFO, "chip_rev=0x%x, strap=0x%x\n", + *usb_chip_rev, ack_pkt.strap); + *usb_strap = ack_pkt.strap & 0x7; + } else + PRINTM(MINFO, "chip_rev=0x%x\n", *usb_chip_rev); + } +cleanup: + kfree(recv_buff); + kfree(tx_buff); + + LEAVE(); + return ret; +} +#endif + +/** + * @brief This function unlink urb + * + * @param handle A pointer to moal_handle structure + * @return N/A + */ +static void woal_usb_unlink_urb(void *card_desc) +{ + struct usb_card_rec *cardp = (struct usb_card_rec *)card_desc; + int i; + ENTER(); + if (cardp) { + /* Unlink Rx cmd URB */ + if (atomic_read(&cardp->rx_cmd_urb_pending) && + cardp->rx_cmd.urb) { + usb_kill_urb(cardp->rx_cmd.urb); + } + /* Unlink Rx data URBs */ + if (atomic_read(&cardp->rx_data_urb_pending)) { + for (i = 0; i < MVUSB_RX_DATA_URB; i++) { + if (cardp->rx_data_list[i].urb) + usb_kill_urb( + cardp->rx_data_list[i].urb); + } + } + /* Unlink Tx cmd URB */ + if (atomic_read(&cardp->tx_cmd_urb_pending) && + cardp->tx_cmd.urb) { + usb_kill_urb(cardp->tx_cmd.urb); + } + /* Unlink Tx data URBs */ + if (atomic_read(&cardp->tx_data_urb_pending)) { + for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) { + if (cardp->tx_data_list[i].urb) { + usb_kill_urb( + cardp->tx_data_list[i].urb); + } + } + } + } + LEAVE(); +} + +/** + * @brief Free Tx/Rx urb, skb and Rx buffer + * + * @param cardp Pointer usb_card_rec + * + * @return N/A + */ +void woal_usb_free(struct usb_card_rec *cardp) +{ + int i; + + ENTER(); + + woal_usb_unlink_urb(cardp); + /* Free Rx data URBs */ + for (i = 0; i < MVUSB_RX_DATA_URB; i++) { + if (cardp->rx_data_list[i].urb) { + usb_free_urb(cardp->rx_data_list[i].urb); + cardp->rx_data_list[i].urb = NULL; + } + } + /* Free Rx cmd URB */ + if (cardp->rx_cmd.urb) { + usb_free_urb(cardp->rx_cmd.urb); + cardp->rx_cmd.urb = NULL; + } + + /* Free Tx data URBs */ + for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) { + if (cardp->tx_data_list[i].urb) { + usb_free_urb(cardp->tx_data_list[i].urb); + cardp->tx_data_list[i].urb = NULL; + } + } + /* Free Tx cmd URB */ + if (cardp->tx_cmd.urb) { + usb_free_urb(cardp->tx_cmd.urb); + cardp->tx_cmd.urb = NULL; + } + + LEAVE(); + return; +} + +static t_u16 woal_update_card_type(t_void *card) +{ + struct usb_card_rec *cardp_usb = (struct usb_card_rec *)card; + t_u16 card_type = 0; + + /* Update card type */ +#ifdef USB8897 + if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8897_PID_1 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8897_PID_2) { + card_type = CARD_TYPE_USB8897; + moal_memcpy_ext(NULL, driver_version, CARD_USB8897, + strlen(CARD_USB8897), strlen(driver_version)); + moal_memcpy_ext(NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V15, strlen(V15), + strlen(driver_version) - strlen(INTF_CARDTYPE) - + strlen(KERN_VERSION)); + } +#endif +#ifdef USB8997 + if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8997_PID_1 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8997_PID_2 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8997_PID_3 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8997_PID_4 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8997_PID_5 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8997_PID_6 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8997V2_PID_1) { + card_type = CARD_TYPE_USB8997; + moal_memcpy_ext(NULL, driver_version, CARD_USB8997, + strlen(CARD_USB8997), strlen(driver_version)); + moal_memcpy_ext(NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V16, strlen(V16), + strlen(driver_version) - strlen(INTF_CARDTYPE) - + strlen(KERN_VERSION)); + } +#endif +#ifdef USB8978 + if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8978_PID_1 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB8978_PID_2) { + card_type = CARD_TYPE_USB8978; + moal_memcpy_ext(NULL, driver_version, CARD_USB8978, + strlen(CARD_USB8978), strlen(driver_version)); + moal_memcpy_ext(NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V16, strlen(V16), + strlen(driver_version) - strlen(INTF_CARDTYPE) - + strlen(KERN_VERSION)); + } +#endif +#ifdef USB9098 + if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB9098_PID_1 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB9098_PID_2) { + card_type = CARD_TYPE_USB9098; + moal_memcpy_ext(NULL, driver_version, CARD_USB9098, + strlen(CARD_USB9098), strlen(driver_version)); + moal_memcpy_ext(NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V17, strlen(V17), + strlen(driver_version) - strlen(INTF_CARDTYPE) - + strlen(KERN_VERSION)); + } +#endif +#ifdef USB9097 + if (woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB9097_PID_1 || + woal_cpu_to_le16(cardp_usb->udev->descriptor.idProduct) == + USB9097_PID_2) { + card_type = CARD_TYPE_USB9097; + moal_memcpy_ext(NULL, driver_version, CARD_USB9097, + strlen(CARD_USB9097), strlen(driver_version)); + moal_memcpy_ext(NULL, + driver_version + strlen(INTF_CARDTYPE) + + strlen(KERN_VERSION), + V17, strlen(V17), + strlen(driver_version) - strlen(INTF_CARDTYPE) - + strlen(KERN_VERSION)); + } +#endif + return card_type; +} + +/** + * @brief Sets the configuration values + * + * @param intf Pointer to usb_interface + * @param id Pointer to usb_device_id + * + * @return Address of variable usb_cardp, error code otherwise + */ +static int woal_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + struct usb_card_rec *usb_cardp = NULL; + t_u16 card_type = 0; + + ENTER(); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + PRINTM(MMSG, + "USB probe: idVendor=%x idProduct=%x bInterfaceNumber=%d\n", + id->idVendor, id->idProduct, id->bInterfaceNumber); +#endif + + udev = interface_to_usbdev(intf); + usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); + if (!usb_cardp) { + LEAVE(); + return -ENOMEM; + } + + /* Check probe is for our device */ + for (i = 0; woal_usb_table[i].idVendor; i++) { + if (woal_cpu_to_le16(udev->descriptor.idVendor) == + woal_usb_table[i].idVendor && + woal_cpu_to_le16(udev->descriptor.idProduct) == + woal_usb_table[i].idProduct) { + PRINTM(MMSG, "VID/PID = %X/%X, Boot2 version = %X\n", + woal_cpu_to_le16(udev->descriptor.idVendor), + woal_cpu_to_le16(udev->descriptor.idProduct), + woal_cpu_to_le16(udev->descriptor.bcdDevice)); + switch (woal_cpu_to_le16(udev->descriptor.idProduct)) { +#ifdef USB8897 + case USB8897_PID_1: +#endif /* USB8897 */ +#ifdef USB8997 + case USB8997_PID_1: + case USB8997V2_PID_1: +#endif /* USB8997 */ +#ifdef USB8978 + case USB8978_PID_1: + case USB8978_PID_1_BT: +#endif /* USB8978 */ +#ifdef USB9098 + case USB9098_PID_1: +#endif /* USB9098 */ +#ifdef USB9097 + case USB9097_PID_1: +#endif /* USB9097 */ + /* If skip FW is set, we must return error so + * the next driver can download the FW */ + if (skip_fwdnld) + goto error; + else + usb_cardp->boot_state = USB_FW_DNLD; + break; +#ifdef USB8897 + case USB8897_PID_2: +#endif /* USB8897 */ +#ifdef USB8997 + case USB8997_PID_2: +#endif /* USB8997 */ +#ifdef USB8978 + case USB8978_PID_2: + case USB8978_PID_2_BT: +#endif /* USB8978 */ +#ifdef USB9098 + case USB9098_PID_2: +#endif /* USB9098 */ +#ifdef USB9097 + case USB9097_PID_2: +#endif /* USB9097 */ + usb_cardp->boot_state = USB_FW_READY; + break; + } + /*To do, get card type*/ + /* if + (woal_cpu_to_le16(udev->descriptor.idProduct) == + USB8897_PID_2) usb_cardp->card_type = + CARD_TYPE_USB8897; else if + (woal_cpu_to_le16(udev->descriptor.idProduct) == + USB8997_PID_2) usb_cardp->card_type = + CARD_TYPE_USB997; + */ + break; + } + } + + if (woal_usb_table[i].idVendor) { + usb_cardp->udev = udev; + iface_desc = intf->cur_altsetting; + usb_cardp->intf = intf; + + PRINTM(MINFO, + "bcdUSB = 0x%X bDeviceClass = 0x%X" + " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", + woal_cpu_to_le16(udev->descriptor.bcdUSB), + udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if ((usb_endpoint_is_bulk_in(endpoint) || + usb_endpoint_is_int_in(endpoint)) && + (usb_endpoint_num(endpoint) == + MLAN_USB_EP_CMD_EVENT || + usb_endpoint_num(endpoint) == + MLAN_USB_EP_CMD_EVENT_IF2)) { + usb_cardp->rx_cmd_ep_type = + usb_endpoint_type(endpoint); + usb_cardp->rx_cmd_interval = + endpoint->bInterval; + /* We found a bulk in command/event endpoint */ + PRINTM(MCMND, + "Rx CMD/EVT: max packet size = %d, address = %d ep_type=%d\n", + woal_le16_to_cpu( + endpoint->wMaxPacketSize), + endpoint->bEndpointAddress, + usb_cardp->rx_cmd_ep_type); + usb_cardp->rx_cmd_ep = + (endpoint->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + + atomic_set(&usb_cardp->rx_cmd_urb_pending, 0); + if (usb_endpoint_num(endpoint) == + MLAN_USB_EP_CMD_EVENT_IF2) + usb_cardp->second_mac = MTRUE; + } + if (usb_endpoint_is_bulk_in(endpoint) && + (usb_endpoint_num(endpoint) == MLAN_USB_EP_DATA || + usb_endpoint_num(endpoint) == + MLAN_USB_EP_DATA_IF2)) { + /* We found a bulk in data endpoint */ + PRINTM(MINFO, + "Bulk IN: max packet size = %d, address = %d\n", + woal_le16_to_cpu( + endpoint->wMaxPacketSize), + endpoint->bEndpointAddress); + usb_cardp->rx_data_ep = + (endpoint->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + atomic_set(&usb_cardp->rx_data_urb_pending, 0); + } + if (usb_endpoint_is_bulk_out(endpoint) && + (usb_endpoint_num(endpoint) == MLAN_USB_EP_DATA || + usb_endpoint_num(endpoint) == + MLAN_USB_EP_DATA_IF2)) { + /* We found a bulk out data endpoint */ + PRINTM(MCMND, + "Bulk OUT: max packet size = %d, address = %d\n", + woal_le16_to_cpu( + endpoint->wMaxPacketSize), + endpoint->bEndpointAddress); + usb_cardp->tx_data_ep = + endpoint->bEndpointAddress; + atomic_set(&usb_cardp->tx_data_urb_pending, 0); + usb_cardp->tx_data_maxpktsize = + woal_le16_to_cpu( + endpoint->wMaxPacketSize); + } + + if ((usb_endpoint_is_bulk_out(endpoint) || + usb_endpoint_is_int_out(endpoint)) && + (usb_endpoint_num(endpoint) == + MLAN_USB_EP_CMD_EVENT || + usb_endpoint_num(endpoint) == + MLAN_USB_EP_CMD_EVENT_IF2)) { + usb_cardp->tx_cmd_ep_type = + usb_endpoint_type(endpoint); + usb_cardp->tx_cmd_interval = + endpoint->bInterval; + /* We found a bulk out command/event endpoint */ + PRINTM(MCMND, + "Tx CMD: max packet size = %d, address = %d ep_type=%d\n", + woal_le16_to_cpu( + endpoint->wMaxPacketSize), + endpoint->bEndpointAddress, + usb_cardp->tx_cmd_ep_type); + usb_cardp->tx_cmd_ep = + endpoint->bEndpointAddress; + atomic_set(&usb_cardp->tx_cmd_urb_pending, 0); + usb_cardp->tx_cmd_maxpktsize = woal_le16_to_cpu( + endpoint->wMaxPacketSize); + } + } + + if (usb_cardp->boot_state == USB_FW_DNLD) { + if (!usb_cardp->tx_cmd_ep || !usb_cardp->rx_cmd_ep) + goto error; + } else if (usb_cardp->boot_state == USB_FW_READY) { + if (!usb_cardp->tx_cmd_ep || !usb_cardp->tx_data_ep || + !usb_cardp->rx_cmd_ep || !usb_cardp->rx_data_ep) { + PRINTM(MERROR, + "%s: invalid endpoint assignment\n", + __FUNCTION__); + goto error; + } + } + + usb_cardp->tx_aggr_ctrl.enable = MFALSE; + usb_cardp->tx_aggr_ctrl.aggr_mode = MLAN_USB_AGGR_MODE_NUM; + usb_cardp->tx_aggr_ctrl.aggr_align = + MAX(max_tx_buf, MLAN_USB_TX_AGGR_ALIGN); + usb_cardp->tx_aggr_ctrl.aggr_max = MLAN_USB_TX_MAX_AGGR_NUM; + usb_cardp->tx_aggr_ctrl.aggr_tmo = + MLAN_USB_TX_AGGR_TIMEOUT_MSEC * 1000; + usb_cardp->rx_deaggr_ctrl.enable = MFALSE; + usb_cardp->rx_deaggr_ctrl.aggr_mode = MLAN_USB_AGGR_MODE_NUM; + usb_cardp->rx_deaggr_ctrl.aggr_align = MLAN_USB_RX_ALIGN_SIZE; + usb_cardp->rx_deaggr_ctrl.aggr_max = MLAN_USB_RX_MAX_AGGR_NUM; + usb_cardp->rx_deaggr_ctrl.aggr_tmo = + MLAN_USB_RX_DEAGGR_TIMEOUT_USEC; + usb_set_intfdata(intf, usb_cardp); + + card_type = woal_update_card_type(usb_cardp); + if (!card_type) { + PRINTM(MERROR, + "usb probe: woal_update_card_type() failed\n"); + goto error; + } + + /* At this point wlan_add_card() will be called */ + if (!(woal_add_card(usb_cardp, &usb_cardp->udev->dev, &usb_ops, + card_type))) { + PRINTM(MERROR, "%s: woal_add_card failed\n", + __FUNCTION__); + goto error; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) + /* Note: From 2.6.34.2 onwards, remote wakeup is NOT enabled by + * default. So drivers wanting remote wakeup will have to enable + * this using - + * device_set_wakeup_enable(&udev->dev, 1); + * It has been observed that some cards having device attr = + * 0xa0 do not support remote wakeup. These cards come + * immediately out of the suspend when power/wakeup file is set + * to 'enabled'. To support all types of cards i.e. with/without + * remote wakeup, we are NOT setting the 'power/wakeup' file + * from here. Also in principle, we are not supposed to change + * the wakeup policy, which is purely a userspace decision. + */ + /* if (udev->actconfig->desc.bmAttributes & + USB_CONFIG_ATT_WAKEUP) intf->needs_remote_wakeup = 1; */ +#endif + usb_get_dev(udev); + LEAVE(); + return 0; + } else { + PRINTM(MINFO, "Discard the Probe request\n"); + PRINTM(MINFO, "VID = 0x%X PID = 0x%X\n", + woal_cpu_to_le16(udev->descriptor.idVendor), + woal_cpu_to_le16(udev->descriptor.idProduct)); + } +error: + kfree(usb_cardp); + usb_cardp = NULL; + LEAVE(); + return -ENXIO; +} + +/** + * @brief Free resource and cleanup + * + * @param intf Pointer to usb_interface + * + * @return N/A + */ +static void woal_usb_disconnect(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + moal_handle *phandle = NULL; + ENTER(); + if (!cardp || !cardp->phandle) { + PRINTM(MERROR, "Card or phandle is not valid\n"); + LEAVE(); + return; + } + phandle = (moal_handle *)cardp->phandle; + + /* + * Update Surprise removed to TRUE + * Free all the URB's allocated + */ + phandle->surprise_removed = MTRUE; + + /* Card is removed and we can call wlan_remove_card */ + PRINTM(MINFO, "Call remove card\n"); + woal_remove_card(cardp); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + kfree(cardp); + LEAVE(); + return; +} + +/** + * @brief killall pending urbs + * + * @param handle Pointer to moal_handle + * + * + * @return N/A + */ +void woal_kill_urbs(moal_handle *handle) +{ + ENTER(); + handle->is_suspended = MTRUE; + woal_usb_unlink_urb(handle->card); + LEAVE(); +} + +/** + * @brief resubmit urbs + * + * @param handle Pointer to moal_handle + * + * + * @return N/A + */ +void woal_resubmit_urbs(moal_handle *handle) +{ + struct usb_card_rec *cardp = handle->card; + + ENTER(); + handle->is_suspended = MFALSE; + + if (!atomic_read(&cardp->rx_data_urb_pending)) { + /* Submit multiple Rx data URBs */ + woal_usb_submit_rx_data_urbs(handle); + } + if (!atomic_read(&cardp->rx_cmd_urb_pending)) { + cardp->rx_cmd.pmbuf = + woal_alloc_mlan_buffer(handle, MLAN_RX_CMD_BUF_SIZE); + if (cardp->rx_cmd.pmbuf) + woal_usb_submit_rx_urb(&cardp->rx_cmd, + MLAN_RX_CMD_BUF_SIZE); + } + LEAVE(); +} + +#ifdef CONFIG_PM +/** + * @brief Handle suspend + * + * @param intf Pointer to usb_interface + * @param message Pointer to pm_message_t structure + * + * @return MLAN_STATUS_SUCCESS + */ +static int woal_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + moal_handle *handle = NULL; + int i; + int ret = 0; + + ENTER(); + + PRINTM(MCMND, "<--- Enter woal_usb_suspend --->\n"); + if (!cardp || !cardp->phandle) { + PRINTM(MERROR, "Card or moal_handle structure is not valid\n"); + ret = 0; + goto done; + } + handle = cardp->phandle; + if (handle->is_suspended == MTRUE) { + PRINTM(MWARN, "Device already suspended\n"); + ret = 0; + goto done; + } +#ifdef STA_SUPPORT + for (i = 0; i < MIN(handle->priv_num, MLAN_MAX_BSS_NUM); i++) { + if (handle->priv[i] && + (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA)) + woal_cancel_scan(handle->priv[i], MOAL_IOCTL_WAIT); + } +#endif + /* Enable Host Sleep */ + woal_enable_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY)); + + /* Indicate device suspended */ + /* The flag must be set here before the usb_kill_urb() calls. + * Reason: In the complete handlers, urb->status(= -ENOENT) and + * 'is_suspended' flag is used in combination to distinguish + * between a suspended state and a 'disconnect' one. + */ + handle->is_suspended = MTRUE; + for (i = 0; i < handle->priv_num; i++) + netif_carrier_off(handle->priv[i]->netdev); + + /* Unlink Rx cmd URB */ + if (atomic_read(&cardp->rx_cmd_urb_pending) && cardp->rx_cmd.urb) { + usb_kill_urb(cardp->rx_cmd.urb); + } + /* Unlink Rx data URBs */ + if (atomic_read(&cardp->rx_data_urb_pending)) { + for (i = 0; i < MVUSB_RX_DATA_URB; i++) { + if (cardp->rx_data_list[i].urb) { + usb_kill_urb(cardp->rx_data_list[i].urb); + usb_init_urb(cardp->rx_data_list[i].urb); + } + } + } + + /* Unlink Tx data URBs */ + for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) { + if (cardp->tx_data_list[i].urb) { + usb_kill_urb(cardp->tx_data_list[i].urb); + } + } + /* Unlink Tx cmd URB */ + if (cardp->tx_cmd.urb) { + usb_kill_urb(cardp->tx_cmd.urb); + } + + handle->suspend_wait_q_woken = MTRUE; + wake_up_interruptible(&handle->suspend_wait_q); + +done: + PRINTM(MCMND, "<--- Leave woal_usb_suspend --->\n"); + LEAVE(); + return ret; +} + +/** + * @brief Handle resume + * + * @param intf Pointer to usb_interface + * + * @return MLAN_STATUS_SUCCESS + */ +static int woal_usb_resume(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + moal_handle *handle = NULL; + int i; + int ret = 0; + + ENTER(); + + PRINTM(MCMND, "<--- Enter woal_usb_resume --->\n"); + if (!cardp || !cardp->phandle) { + PRINTM(MERROR, "Card or adapter structure is not valid\n"); + ret = 0; + goto done; + } + handle = cardp->phandle; + + if (handle->is_suspended == MFALSE) { + PRINTM(MWARN, "Device already resumed\n"); + ret = 0; + goto done; + } + + /* Indicate device resumed. + * The netdev queue will be resumed only after the urbs + * have been resubmitted */ + handle->is_suspended = MFALSE; + + if (!atomic_read(&cardp->rx_data_urb_pending)) { + /* Submit multiple Rx data URBs */ + woal_usb_submit_rx_data_urbs(handle); + } + if (!atomic_read(&cardp->rx_cmd_urb_pending)) { + cardp->rx_cmd.pmbuf = + woal_alloc_mlan_buffer(handle, MLAN_RX_CMD_BUF_SIZE); + if (cardp->rx_cmd.pmbuf) + woal_usb_submit_rx_urb(&cardp->rx_cmd, + MLAN_RX_CMD_BUF_SIZE); + } + + for (i = 0; i < handle->priv_num; i++) + if (handle->priv[i]->media_connected == MTRUE) + netif_carrier_on(handle->priv[i]->netdev); + + /* Disable Host Sleep */ + if (handle->hs_activated) + woal_cancel_hs(woal_get_priv(handle, MLAN_BSS_ROLE_ANY), + MOAL_NO_WAIT); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) + /* Resume handler may be called due to remote wakeup, + force to exit suspend anyway */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) + cardp->udev->autosuspend_disabled = 1; +#else +#ifdef CONFIG_PM_RUNTIME + cardp->udev->dev.power.runtime_auto = 0; +#endif +#endif /* < 2.6.35 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + cardp->udev->autoresume_disabled = 0; +#endif /* < 2.6.33 */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34) +#ifdef CONFIG_PM_RUNTIME + atomic_inc(&(cardp->udev)->dev.power.usage_count); +#endif +#endif /* >= 2.6.34 */ +#endif /* >= 2.6.24 */ + +done: + PRINTM(MCMND, "<--- Leave woal_usb_resume --->\n"); + LEAVE(); + return 0; +} +#endif /* CONFIG_PM */ + +/** + * @brief This function initialize the tx URBs + * + * @param handle Pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_usb_tx_init(moal_handle *handle) +{ + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; + int i; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + cardp->tx_cmd.handle = handle; + cardp->tx_cmd.ep = cardp->tx_cmd_ep; + /* Allocate URB for command */ + cardp->tx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->tx_cmd.urb) { + PRINTM(MERROR, "Tx command URB allocation failed\n"); + ret = MLAN_STATUS_FAILURE; + goto init_exit; + } + + cardp->tx_data_ix = 0; + for (i = 0; i < MVUSB_TX_HIGH_WMARK; i++) { + cardp->tx_data_list[i].handle = handle; + cardp->tx_data_list[i].ep = cardp->tx_data_ep; + /* Allocate URB for data */ + cardp->tx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->tx_data_list[i].urb) { + PRINTM(MERROR, "Tx data URB allocation failed\n"); + ret = MLAN_STATUS_FAILURE; + goto init_exit; + } + } + +init_exit: + LEAVE(); + return ret; +} + +/** + * @brief This function submits the rx data URBs + * + * @param handle Pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_usb_submit_rx_data_urbs(moal_handle *handle) +{ + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; + int i; + mlan_status ret = MLAN_STATUS_FAILURE; + t_u32 buffer_len = MLAN_RX_DATA_BUF_SIZE; + + ENTER(); + + if (cardp->rx_deaggr_ctrl.enable) { + buffer_len = cardp->rx_deaggr_ctrl.aggr_max; + if (cardp->rx_deaggr_ctrl.aggr_mode == MLAN_USB_AGGR_MODE_NUM) { + buffer_len *= MAX(MLAN_USB_MAX_PKT_SIZE, + cardp->rx_deaggr_ctrl.aggr_align); + buffer_len = MAX(buffer_len, MLAN_RX_DATA_BUF_SIZE); + } + } + + for (i = 0; i < MVUSB_RX_DATA_URB; i++) { + /* Submit Rx data URB */ + if (!cardp->rx_data_list[i].pmbuf) { + if (woal_usb_submit_rx_urb(&cardp->rx_data_list[i], + buffer_len)) + continue; + } + ret = MLAN_STATUS_SUCCESS; + } + LEAVE(); + return ret; +} + +/** + * @brief This function initialize the rx URBs and submit them + * + * @param handle Pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_usb_rx_init(moal_handle *handle) +{ + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; + int i; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + cardp->rx_cmd.handle = handle; + cardp->rx_cmd.ep = cardp->rx_cmd_ep; + /* Allocate URB for command/event */ + cardp->rx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->rx_cmd.urb) { + PRINTM(MERROR, "Rx command URB allocation failed\n"); + ret = MLAN_STATUS_FAILURE; + goto init_exit; + } + + cardp->rx_cmd.pmbuf = + woal_alloc_mlan_buffer(handle, MLAN_RX_CMD_BUF_SIZE); + if (cardp->rx_cmd.pmbuf) { + /* Submit Rx command URB */ + if (woal_usb_submit_rx_urb(&cardp->rx_cmd, + MLAN_RX_CMD_BUF_SIZE)) { + ret = MLAN_STATUS_FAILURE; + goto init_exit; + } + } + for (i = 0; i < MVUSB_RX_DATA_URB; i++) { + cardp->rx_data_list[i].handle = handle; + cardp->rx_data_list[i].ep = cardp->rx_data_ep; + /* Allocate URB for data */ + cardp->rx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); + if (!cardp->rx_data_list[i].urb) { + PRINTM(MERROR, "Rx data URB allocation failed\n"); + ret = MLAN_STATUS_FAILURE; + goto init_exit; + } + } + ret = woal_usb_submit_rx_data_urbs(handle); + if (ret) { + PRINTM(MERROR, "Rx data URB submission failed\n"); + goto init_exit; + } + +init_exit: + LEAVE(); + return ret; +} + +/** + * @brief This function downloads data blocks to device + * + * @param handle Pointer to moal_handle structure + * @param pmbuf Pointer to mlan_buffer structure + * @param ep Endpoint to send + * @param timeout Timeout value in milliseconds (if 0 the wait is forever) + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_usb_write_data_sync(moal_handle *handle, + mlan_buffer *pmbuf, t_u32 endpoint, + t_u32 timeout) +{ + struct usb_card_rec *cardp = handle->card; + t_u8 *data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + t_u8 ep = endpoint; + t_u32 length = pmbuf->data_len; + int actual_length; + mlan_status ret = MLAN_STATUS_SUCCESS; + int bulk_out_maxpktsize = 512; + + if (ep == cardp->tx_cmd_ep) + bulk_out_maxpktsize = cardp->tx_cmd_maxpktsize; + else if (ep == cardp->tx_data_ep) + bulk_out_maxpktsize = cardp->tx_data_maxpktsize; + + if (length % bulk_out_maxpktsize == 0) + length++; + + /* Send the data block */ + ret = usb_bulk_msg(cardp->udev, usb_sndbulkpipe(cardp->udev, ep), + (t_u8 *)data, length, &actual_length, timeout); + if (ret) { + PRINTM(MERROR, "usb_blk_msg for send failed, ret %d\n", ret); + ret = MLAN_STATUS_FAILURE; + } + + pmbuf->data_len = actual_length; + DBG_HEXDUMP(MIF_D, "write sync", data, actual_length); + + return ret; +} + +/** + * @brief This function read data blocks to device + * + * @param handle Pointer to moal_handle structure + * @param pmbuf Pointer to mlan_buffer structure + * @param ep Endpoint to receive + * @param timeout Timeout value in milliseconds (if 0 the wait is forever) + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_usb_read_data_sync(moal_handle *handle, + mlan_buffer *pmbuf, t_u32 endpoint, + t_u32 timeout) +{ + struct usb_card_rec *cardp = handle->card; + t_u8 *data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + t_u8 ep = endpoint; + t_u32 buf_len = pmbuf->data_len; + int actual_length; + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + /* Receive the data response */ + ret = usb_bulk_msg(cardp->udev, usb_rcvbulkpipe(cardp->udev, ep), data, + buf_len, &actual_length, timeout); + if (ret) { + PRINTM(MERROR, "usb_bulk_msg failed: %d\n", ret); + ret = MLAN_STATUS_FAILURE; + } + pmbuf->data_len = actual_length; + DBG_HEXDUMP(MIF_D, "read sync", data, actual_length); + LEAVE(); + return ret; +} + +/** + * @brief This function downloads data/command packet to device + * + * @param handle Pointer to moal_handle structure + * @param pmbuf Pointer to mlan_buffer structure + * @param ep Endpoint to send + * + * @return MLAN_STATUS_PENDING or MLAN_STATUS_FAILURE or + * MLAN_STATUS_RESOURCE + */ +mlan_status woal_write_data_async(moal_handle *handle, mlan_buffer *pmbuf, + t_u8 ep) +{ + struct usb_card_rec *cardp = handle->card; + urb_context *context = NULL; + t_u8 *data = (t_u8 *)(pmbuf->pbuf + pmbuf->data_offset); + struct urb *tx_urb = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 data_len = pmbuf->data_len; + int bulk_out_maxpktsize = 512; + + ENTER(); + + /* Check if device is removed */ + if (handle->surprise_removed) { + PRINTM(MERROR, "Device removed\n"); + ret = MLAN_STATUS_FAILURE; + goto tx_ret; + } + + if ((ep == cardp->tx_data_ep) && + (atomic_read(&cardp->tx_data_urb_pending) >= MVUSB_TX_HIGH_WMARK)) { + ret = MLAN_STATUS_RESOURCE; + goto tx_ret; + } + PRINTM(MINFO, "woal_write_data_async: ep=%d\n", ep); + + if (ep == cardp->tx_cmd_ep) { + context = &cardp->tx_cmd; + bulk_out_maxpktsize = cardp->tx_cmd_maxpktsize; + } else { + if (ep == cardp->tx_data_ep) { + bulk_out_maxpktsize = cardp->tx_data_maxpktsize; + if (cardp->tx_data_ix >= MVUSB_TX_HIGH_WMARK) + cardp->tx_data_ix = 0; + context = &cardp->tx_data_list[cardp->tx_data_ix++]; + } + } + + if (!context) { + PRINTM(MERROR, "Cannot allocate Tx urb_context\n"); + ret = MLAN_STATUS_FAILURE; + goto tx_ret; + } + context->handle = handle; + context->ep = ep; + context->pmbuf = pmbuf; + + tx_urb = context->urb; + + if (data_len % bulk_out_maxpktsize == 0) + data_len++; + + /* + * Use USB API usb_fill_bulk_urb() to set the + * configuration information of the Tx bulk URB + * and initialize the Tx callback + */ + + if (ep == cardp->tx_cmd_ep && + cardp->tx_cmd_ep_type == USB_ENDPOINT_XFER_INT) { + usb_fill_int_urb(tx_urb, cardp->udev, + usb_sndintpipe(cardp->udev, ep), data, + data_len, woal_usb_tx_complete, + (void *)context, cardp->tx_cmd_interval); + } else + usb_fill_bulk_urb(tx_urb, cardp->udev, + usb_sndbulkpipe(cardp->udev, ep), data, + data_len, woal_usb_tx_complete, + (void *)context); + /* We find on Ubuntu 12.10 this flag does not work */ + // tx_urb->transfer_flags |= URB_ZERO_PACKET; + + if (ep == cardp->tx_cmd_ep) + atomic_inc(&cardp->tx_cmd_urb_pending); + else if (ep == cardp->tx_data_ep) + atomic_inc(&cardp->tx_data_urb_pending); + if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { + /* Submit URB failure */ + PRINTM(MERROR, "Submit EP %d Tx URB failed: %d\n", ep, ret); + if (ep == cardp->tx_cmd_ep) + atomic_dec(&cardp->tx_cmd_urb_pending); + else { + if (ep == cardp->tx_data_ep) { + atomic_dec(&cardp->tx_data_urb_pending); + if (cardp->tx_data_ix) + cardp->tx_data_ix--; + else + cardp->tx_data_ix = MVUSB_TX_HIGH_WMARK; + } + } + ret = MLAN_STATUS_FAILURE; + } else { + if (ep == cardp->tx_data_ep && + (atomic_read(&cardp->tx_data_urb_pending) == + MVUSB_TX_HIGH_WMARK)) + ret = MLAN_STATUS_PRESOURCE; + else + ret = MLAN_STATUS_SUCCESS; + } + +tx_ret: + + if (!ret) + ret = MLAN_STATUS_PENDING; + + LEAVE(); + return ret; +} + +/** + * @brief This function register usb device and initialize parameter + * + * @param handle Pointer to moal_handle structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +static mlan_status woal_usb_register_dev(moal_handle *handle) +{ + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; + mlan_status ret = MLAN_STATUS_SUCCESS; + + ENTER(); + + cardp->phandle = handle; + LEAVE(); + return ret; +} + +static void woal_usb_unregister_dev(moal_handle *handle) +{ + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; + PRINTM(MMSG, "USB: unregister device\n"); + woal_usb_free(cardp); + cardp->phandle = NULL; + return; +} + +/** + * @brief This function registers driver. + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status woal_usb_bus_register(void) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + ENTER(); + + if (skip_fwdnld) { + woal_usb_driver.id_table = woal_usb_table_skip_fwdnld; + } + /* + * API registers the NXP USB driver + * to the USB system + */ + if (usb_register(&woal_usb_driver)) { + PRINTM(MFATAL, "USB Driver Registration Failed \n"); + ret = MLAN_STATUS_FAILURE; + } + LEAVE(); + return ret; +} + +/** + * @brief This function removes usb driver. + * + * @return N/A + */ +void woal_usb_bus_unregister(void) +{ + ENTER(); + /* API unregisters the driver from USB subsystem */ + usb_deregister(&woal_usb_driver); + LEAVE(); + return; +} + +/** + * @brief This function check if this is second mac + * + * @param handle A pointer to moal_handle structure + * @return MTRUE/MFALSE + * + */ +static t_u8 woal_usb_is_second_mac(moal_handle *handle) +{ + return ((struct usb_card_rec *)(handle->card))->second_mac; +} + +#ifdef CONFIG_USB_SUSPEND +/** + * @brief This function makes USB device to suspend. + * + * @param handle A pointer to moal_handle structure + * + * @return 0 --success, otherwise fail + */ +int woal_enter_usb_suspend(moal_handle *handle) +{ + struct usb_device *udev = ((struct usb_card_rec *)(handle->card))->udev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + struct usb_interface *intf = + ((struct usb_card_rec *)(handle->card))->intf; +#endif /* < 2.6.34 */ +#endif /* >= 2.6.24 */ + + ENTER(); + + PRINTM(MIOCTL, "USB suspend ioctl (state %d)\n", udev->state); + + if (handle->is_suspended == MTRUE) { + PRINTM(MERROR, "Device already suspended\n"); + LEAVE(); + return -EFAULT; + } + + handle->suspend_wait_q_woken = MFALSE; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) + /* Enter into USB suspend */ + usb_lock_device(udev); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) + udev->autosuspend_delay = 0; /* Autosuspend delay in jiffies */ +#else + pm_runtime_set_autosuspend_delay(&udev->dev, 0); /* Autosuspend delay in + jiffies */ +#endif /* < 2.6.38 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + udev->autosuspend_disabled = 0; /* /sys/bus/usb/devices/.../power/level + < auto */ +#endif /* < 2.6.34 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + udev->autoresume_disabled = 0; +#endif /* < 2.6.33 */ + usb_unlock_device(udev); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + intf->pm_usage_cnt = 1; +#else + atomic_set(&intf->pm_usage_cnt, 1); +#endif /* < 2.6.32 */ + usb_autopm_put_interface(intf); +#else + usb_lock_device(udev); + atomic_set(&udev->dev.power.usage_count, 1); + usb_enable_autosuspend(udev); + usb_unlock_device(udev); +#endif /* < 2.6.34 */ +#endif /* >= 2.6.24 */ + + /* Wait for suspend to complete */ + wait_event_interruptible(handle->suspend_wait_q, + handle->suspend_wait_q_woken); + + LEAVE(); + return 0; +} + +/** + * @brief This function makes USB device to resume. + * + * @param handle A pointer to moal_handle structure + * + * @return 0 --success, otherwise fail + */ +int woal_exit_usb_suspend(moal_handle *handle) +{ + struct usb_device *udev = ((struct usb_card_rec *)(handle->card))->udev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + struct usb_interface *intf = + ((struct usb_card_rec *)(handle->card))->intf; +#endif /* < 2.6.34 */ +#endif /* >= 2.6.24 */ + + ENTER(); + + PRINTM(MIOCTL, "USB resume ioctl (state %d)\n", udev->state); + + if (handle->is_suspended == MFALSE || + udev->state != USB_STATE_SUSPENDED) { + PRINTM(MERROR, "Device already resumed\n"); + LEAVE(); + return -EFAULT; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) + /* Exit from USB suspend */ + usb_lock_device(udev); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) + udev->autosuspend_disabled = 1; /* /sys/bus/usb/devices/.../power/level + < on */ +#endif /* < 2.6.34 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) + udev->autoresume_disabled = 0; +#endif /* < 2.6.33 */ + usb_unlock_device(udev); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32) + intf->pm_usage_cnt = 0; +#else + atomic_set(&intf->pm_usage_cnt, 0); +#endif /* < 2.6.32 */ + usb_autopm_get_interface(intf); +#else + usb_lock_device(udev); + atomic_set(&udev->dev.power.usage_count, 0); + usb_disable_autosuspend(udev); + usb_unlock_device(udev); +#endif /* < 2.6.34 */ +#endif /* >= 2.6.24 */ + + LEAVE(); + return 0; +} +#endif /* CONFIG_USB_SUSPEND */ + +/** + * @brief This function will submit rx urb. + * + * @param handle Pointer to moal_handle + * @param ep Endpoint to re-submit urb + * + * @return N/A + */ +void woal_submit_rx_urb(moal_handle *handle, t_u8 ep) +{ + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; + + ENTER(); + + if ((ep == cardp->rx_cmd_ep) && + (!atomic_read(&cardp->rx_cmd_urb_pending))) { + woal_usb_submit_rx_urb(&cardp->rx_cmd, MLAN_RX_CMD_BUF_SIZE); + } + + LEAVE(); + return; +} + +/** + * @brief This function dump firmware memory to file + * + * @param phandle A pointer to moal_handle + * + * @return N/A + */ +void woal_usb_dump_fw_info(moal_handle *phandle) +{ + moal_private *priv = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *pcfg_misc = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + priv = woal_get_priv(phandle, MLAN_BSS_ROLE_ANY); + if (!priv) { + PRINTM(MERROR, "woal_dump_firmware_info get priv is NULL!\n"); + goto done; + } + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + PRINTM(MERROR, "woal_dump_firmware_info alloc req fail!\n"); + goto done; + } + + /* Fill request buffer */ + pcfg_misc = (mlan_ds_misc_cfg *)req->pbuf; + pcfg_misc->sub_command = MLAN_OID_MISC_FW_DUMP_EVENT; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = MLAN_ACT_GET; + + /* Send IOCTL request to MLAN */ + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + + if (status != MLAN_STATUS_PENDING) + kfree(req); + +done: + LEAVE(); + return; +} + +static mlan_status woal_usb_get_fw_name(moal_handle *handle) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; +#if defined(USB8997) || defined(USB9098) || defined(USB9097) || defined(USB8978) + t_u32 revision_id = 0; + t_u32 strap = 0; +#endif + struct usb_card_rec *cardp = (struct usb_card_rec *)handle->card; +#if defined(USB9098) + moal_handle *ref_handle = NULL; +#endif + + ENTER(); + if (handle->params.fw_name) + goto done; + if (cardp->boot_state == USB_FW_READY) + goto done; +#if defined(USB8997) || defined(USB9098) || defined(USB9097) || defined(USB8978) + ret = woal_check_chip_revision(handle, &revision_id, &strap); + if (ret != MLAN_STATUS_SUCCESS) { + PRINTM(MFATAL, "Chip revision check failure!\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + PRINTM(MCMND, "revision=0x%x, strap=0x%x\n", revision_id, strap); +#endif + +#ifdef USB8997 + if (IS_USB8997(handle->card_type)) { + if (strap == CARD_TYPE_USB_UART) + strcpy(handle->card_info->fw_name, + USBUART8997_DEFAULT_COMBO_FW_NAME); + else if (strap != 0) + strcpy(handle->card_info->fw_name, + USBUSB8997_DEFAULT_COMBO_FW_NAME); + } +#endif + +#ifdef USB8978 + if (IS_USB8978(handle->card_type)) { + if (strap == CARD_TYPE_USB_UART) + strcpy(handle->card_info->fw_name, + USBUART8978_DEFAULT_COMBO_FW_NAME); + else if (strap != 0) + strcpy(handle->card_info->fw_name, + USBUSB8978_DEFAULT_COMBO_FW_NAME); + } +#endif + +#ifdef USB9098 + if (IS_USB9098(handle->card_type)) { + if (cardp->second_mac) { + ref_handle = (moal_handle *)handle->pref_mac; + if (ref_handle) { + strcpy(handle->card_info->fw_name, + ref_handle->card_info->fw_name); + strcpy(handle->card_info->fw_name_wlan, + ref_handle->card_info->fw_name_wlan); + } + goto done; + } + switch (revision_id) { + case USB9098_Z1Z2: + if (strap != 0) { + if (strap == CARD_TYPE_USB_UART) + strcpy(handle->card_info->fw_name, + USBUART9098_DEFAULT_COMBO_FW_NAME); + else + strcpy(handle->card_info->fw_name, + USBUSB9098_DEFAULT_COMBO_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + USB9098_DEFAULT_WLAN_FW_NAME); + break; + case USB9098_A0: + case USB9098_A1: + case USB9098_A2: + if (strap != 0) { + if (strap == CARD_TYPE_USB_UART) + strcpy(handle->card_info->fw_name, + USBUART9098_COMBO_V1_FW_NAME); + else + strcpy(handle->card_info->fw_name, + USBUSB9098_COMBO_V1_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + USB9098_WLAN_V1_FW_NAME); + break; + } + } +#endif +#ifdef USB9097 + if (IS_USB9097(handle->card_type)) { + switch (revision_id) { + case USB9097_B0: + case USB9097_B1: + if (strap != 0) { + if (strap == CARD_TYPE_USB_UART) + strcpy(handle->card_info->fw_name, + USBUART9097_COMBO_V1_FW_NAME); + else + strcpy(handle->card_info->fw_name, + USBUSB9097_COMBO_V1_FW_NAME); + } + strcpy(handle->card_info->fw_name_wlan, + USB9097_WLAN_V1_FW_NAME); + break; + } + } +#endif +done: + PRINTM(MCMND, "combo fw:%s wlan fw:%s \n", handle->card_info->fw_name, + handle->card_info->fw_name_wlan); + LEAVE(); + return ret; +} + +static moal_if_ops usb_ops = { + .register_dev = woal_usb_register_dev, + .unregister_dev = woal_usb_unregister_dev, + .read_data_sync = woal_usb_read_data_sync, + .write_data_sync = woal_usb_write_data_sync, + .get_fw_name = woal_usb_get_fw_name, + .dump_fw_info = woal_usb_dump_fw_info, + .is_second_mac = woal_usb_is_second_mac, +}; diff --git a/mxm_wifiex/wlan_src/mlinux/moal_usb.h b/mxm_wifiex/wlan_src/mlinux/moal_usb.h new file mode 100644 index 0000000..7c95c28 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_usb.h @@ -0,0 +1,234 @@ +/** @file moal_usb.h + * + * @brief This file contains definitions for USB interface. + * driver. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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_USB_H +#define _MOAL_USB_H + +#ifdef USB8997 +/** USB VID 1 */ +#define USB8997_VID_1 0x1286 +/** USB PID 1 */ +#define USB8997_PID_1 0x204D +/** USB PID 2 */ +#define USB8997_PID_2 0x204E +#define USB8997_PID_3 0x2047 +#define USB8997_PID_4 0x2048 +#define USB8997_PID_5 0x2050 +#define USB8997_PID_6 0x2051 +#define USB8997V2_PID_1 0x2052 +#endif /* USB8997 */ + +#ifdef USB8978 +/** USB VID 1 */ +#define USB8978_VID_1 0x1286 +/** USB PID 1 */ +#define USB8978_PID_1 0x2062 +/** USB PID 2 */ +#define USB8978_PID_2 0x2063 +/* BT downloaded combo firmware; register for WLAN enumeration */ +#define USB8978_PID_1_BT 0x2064 +#define USB8978_PID_2_BT 0x2065 +#endif + +#ifdef USB8897 +/** USB VID 1 */ +#define USB8897_VID_1 0x1286 +/** USB PID 1 */ +#define USB8897_PID_1 0x2045 +/** USB PID 2 */ +#define USB8897_PID_2 0x2046 +#endif /* USB8897 */ + +#ifdef USB9098 +/** USB VID 1 */ +#define USB9098_VID_1 0x1286 +/** USB PID 1 */ +#define USB9098_PID_1 0x2056 +/** USB PID 2 */ +#define USB9098_PID_2 0x2057 +#endif /* USB9098 */ + +#ifdef USB9097 +/** USB VID 1 */ +#define USB9097_VID_1 0x1286 +/** USB PID 1 */ +#define USB9097_PID_1 0x2060 +/** USB PID 2 */ +#define USB9097_PID_2 0x2061 +#endif /* USB9097 */ + +/** Boot state: FW download */ +#define USB_FW_DNLD 1 +/** Boot state: FW ready */ +#define USB_FW_READY 2 + +/** High watermark for Tx data */ +#define MVUSB_TX_HIGH_WMARK 12 + +/** Number of Rx data URB */ +#define MVUSB_RX_DATA_URB 6 + +#if defined(USB8997) || defined(USB9098) || defined(USB9097) || defined(USB8978) +/* Transmit buffer size for chip revision check */ +#define CHIP_REV_TX_BUF_SIZE 16 +/* Receive buffer size for chip revision check */ +#define CHIP_REV_RX_BUF_SIZE 2048 + +/* Extensions */ +#define EXTEND_HDR (0xAB950000) +#define EXTEND_V1 (0x00000001) +#define EXTEND_V2 (0x00000002) + +#endif + +/** Default firmaware name */ +#ifdef USB8997 +#define USB8997_DEFAULT_COMBO_FW_NAME "nxp/usbusb8997_combo_v4.bin" +#define USB8997_DEFAULT_WLAN_FW_NAME "nxp/usb8997_wlan_v4.bin" +#define USBUART8997_DEFAULT_COMBO_FW_NAME "nxp/usbuart8997_combo_v4.bin" +#define USBUSB8997_DEFAULT_COMBO_FW_NAME "nxp/usbusb8997_combo_v4.bin" + +#endif /* USB8997 */ + +#ifdef USB8978 +#define USB8978_DEFAULT_COMBO_FW_NAME "nxp/usbusb8978_combo.bin" +#define USB8978_DEFAULT_WLAN_FW_NAME "nxp/usb8978_wlan.bin" +#define USBUART8978_DEFAULT_COMBO_FW_NAME "nxp/usbuart8978_combo.bin" +#define USBUSB8978_DEFAULT_COMBO_FW_NAME "nxp/usbusb8978_combo.bin" +#endif /* USB8978 */ + +#ifdef USB8897 +#define USB8897_DEFAULT_COMBO_FW_NAME "nxp/usb8897_uapsta.bin" +#define USB8897_DEFAULT_WLAN_FW_NAME "nxp/usb8897_wlan.bin" +#endif /* USB8897 */ + +#ifdef USB9098 +#define USB9098_Z1Z2 0x00 +#define USB9098_A0 0x01 +#define USB9098_A1 0x02 +#define USB9098_A2 0x03 +#define USB9098_DEFAULT_COMBO_FW_NAME "nxp/usbusb9098_combo.bin" +#define USB9098_DEFAULT_WLAN_FW_NAME "nxp/usb9098_wlan.bin" +#define USBUART9098_DEFAULT_COMBO_FW_NAME "nxp/usbuart9098_combo.bin" +#define USBUSB9098_DEFAULT_COMBO_FW_NAME "nxp/usbusb9098_combo.bin" +#define USB9098_WLAN_V1_FW_NAME "nxp/usb9098_wlan_v1.bin" +#define USBUART9098_COMBO_V1_FW_NAME "nxp/usbuart9098_combo_v1.bin" +#define USBUSB9098_COMBO_V1_FW_NAME "nxp/usbusb9098_combo_v1.bin" +#endif /* USB9098 */ + +#ifdef USB9097 +#define USB9097_B0 0x01 +#define USB9097_B1 0x02 +#define USB9097_DEFAULT_COMBO_FW_NAME "nxp/usbusb9097_combo_v1.bin" +#define USB9097_DEFAULT_WLAN_FW_NAME "nxp/usb9097_wlan_v1.bin" +#define USB9097_WLAN_V1_FW_NAME "nxp/usb9097_wlan_v1.bin" +#define USBUART9097_COMBO_V1_FW_NAME "nxp/usbuart9097_combo_v1.bin" +#define USBUSB9097_COMBO_V1_FW_NAME "nxp/usbusb9097_combo_v1.bin" +#endif /* USB9097 */ + +/** urb context */ +typedef struct _urb_context { + /** Pointer to moal_handle structure */ + moal_handle *handle; + /** Pointer to mlan buffer */ + mlan_buffer *pmbuf; + /** URB */ + struct urb *urb; + /** EP */ + t_u8 ep; +} urb_context; + +/** USB card description structure*/ +struct usb_card_rec { + /** USB device */ + struct usb_device *udev; + /** MOAL handle */ + moal_handle *phandle; + /** USB interface */ + struct usb_interface *intf; + /** Rx command endpoint type */ + int rx_cmd_ep_type; + /** Rx command interval for INTR type */ + t_u8 rx_cmd_interval; + /** Rx data endpoint address */ + t_u8 rx_cmd_ep; + /** Rx cmd contxt */ + urb_context rx_cmd; + /** Rx command URB pending count */ + atomic_t rx_cmd_urb_pending; + /** Rx data context list */ + urb_context rx_data_list[MVUSB_RX_DATA_URB]; + /** Flag to indicate boot state */ + t_u8 boot_state; + /** Rx data endpoint address */ + t_u8 rx_data_ep; + /** Rx data URB pending count */ + atomic_t rx_data_urb_pending; + /** Tx data endpoint address */ + t_u8 tx_data_ep; + /** Tx command endpoint type */ + int tx_cmd_ep_type; + /** Tx command interval for INTR type */ + t_u8 tx_cmd_interval; + /** Tx command endpoint address */ + t_u8 tx_cmd_ep; + /** Tx data URB pending count */ + atomic_t tx_data_urb_pending; + /** Tx command URB pending count */ + atomic_t tx_cmd_urb_pending; + /** Tx data endpoint max pkt size */ + int tx_data_maxpktsize; + /** Tx cmd endpoint max pkt size */ + int tx_cmd_maxpktsize; + /** Pre-allocated urb for command */ + urb_context tx_cmd; + /** Index to point to next data urb to use */ + int tx_data_ix; + /** Pre-allocated urb for data */ + urb_context tx_data_list[MVUSB_TX_HIGH_WMARK]; + usb_aggr_ctrl tx_aggr_ctrl; + usb_aggr_ctrl rx_deaggr_ctrl; + t_u8 resubmit_urbs; + /** USB card type */ + t_u16 card_type; + t_u8 second_mac; +}; + +void woal_kill_urbs(moal_handle *handle); +void woal_resubmit_urbs(moal_handle *handle); + +mlan_status woal_write_data_async(moal_handle *handle, mlan_buffer *pmbuf, + t_u8 ep); +mlan_status woal_usb_submit_rx_data_urbs(moal_handle *handle); +mlan_status woal_usb_rx_init(moal_handle *handle); +mlan_status woal_usb_tx_init(moal_handle *handle); +mlan_status woal_usb_aggr_init(moal_handle *handle); +void woal_submit_rx_urb(moal_handle *handle, t_u8 ep); +void woal_usb_bus_unregister(void); +mlan_status woal_usb_bus_register(void); +void woal_usb_free(struct usb_card_rec *cardp); +#endif /*_MOAL_USB_H */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_wext.c b/mxm_wifiex/wlan_src/mlinux/moal_wext.c new file mode 100644 index 0000000..0072b18 --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_wext.c @@ -0,0 +1,3175 @@ +/** @file moal_wext.c + * + * @brief This file contains wireless extension standard ioctl functions + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the 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 + +/******************************************************** + Local Functions +********************************************************/ + +/** + * @brief Compare two SSIDs + * + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare + * + * @return 0--ssid is same, otherwise is different + */ +static t_s32 woal_ssid_cmp(mlan_802_11_ssid *ssid1, mlan_802_11_ssid *ssid2) +{ + ENTER(); + + if (!ssid1 || !ssid2) { + LEAVE(); + return -1; + } + if (ssid1->ssid_len != ssid2->ssid_len) { + LEAVE(); + return -1; + } + + LEAVE(); + return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); +} + +/** + * @brief Sort Channels + * + * @param freq A pointer to iw_freq structure + * @param num Number of Channels + * + * @return N/A + */ +static inline void woal_sort_channels(struct iw_freq *freq, int num) +{ + int i, j; + struct iw_freq temp; + + for (i = 0; i < num; i++) + for (j = i + 1; j < num; j++) + if (freq[i].i > freq[j].i) { + temp.i = freq[i].i; + temp.m = freq[i].m; + + freq[i].i = freq[j].i; + freq[i].m = freq[j].m; + + freq[j].i = temp.i; + freq[j].m = temp.m; + } +} + +/** + * @brief Convert RSSI to quality + * + * @param rssi RSSI in dBm + * + * @return Quality of the link (0-5) + */ +static t_u8 woal_rssi_to_quality(t_s16 rssi) +{ +/** Macro for RSSI range */ +#define MOAL_RSSI_NO_SIGNAL -90 +#define MOAL_RSSI_VERY_LOW -80 +#define MOAL_RSSI_LOW -70 +#define MOAL_RSSI_GOOD -60 +#define MOAL_RSSI_VERY_GOOD -50 +#define MOAL_RSSI_INVALID 0 + if (rssi <= MOAL_RSSI_NO_SIGNAL || rssi == MOAL_RSSI_INVALID) + return 0; + else if (rssi <= MOAL_RSSI_VERY_LOW) + return 1; + else if (rssi <= MOAL_RSSI_LOW) + return 2; + else if (rssi <= MOAL_RSSI_GOOD) + return 3; + else if (rssi <= MOAL_RSSI_VERY_GOOD) + return 4; + else + return 5; +} + +/** + * @brief Set Adapter Node Name + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + ENTER(); + /* + * Check the size of the string + */ + if (dwrq->length > 16) { + LEAVE(); + return -E2BIG; + } + memset(priv->nick_name, 0, sizeof(priv->nick_name)); + moal_memcpy_ext(priv->phandle, priv->nick_name, extra, dwrq->length, + sizeof(priv->nick_name)); + 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, + union iwreq_data *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, + union iwreq_data *wrqu, char *extra) +{ + char *cwrq = wrqu->name; + 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, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_freq *fwrq = &wrqu->freq; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + /* + * If setting by frequency, convert to a channel + */ + if (fwrq->e == 1) { + long f = fwrq->m / 100000; + bss->param.bss_chan.freq = f; + } else + bss->param.bss_chan.channel = fwrq->m; + + bss->sub_command = MLAN_OID_BSS_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_change_adhoc_chan(priv, bss->param.bss_chan.channel, + MOAL_IOCTL_WAIT)) + ret = -EFAULT; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get frequency + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param fwrq A pointer to iw_freq structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_freq(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_freq *fwrq = &wrqu->freq; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_CHANNEL; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_GET; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + fwrq->m = (long)bss->param.bss_chan.freq; + fwrq->i = (long)bss->param.bss_chan.channel; + fwrq->e = 6; + fwrq->flags = IW_FREQ_FIXED; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set wlan mode + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param uwrq Wireless mode to set + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_bss_mode(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u32 *uwrq = &wrqu->mode; + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + switch (*uwrq) { + case IW_MODE_INFRA: + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + break; + case IW_MODE_ADHOC: + bss->param.bss_mode = MLAN_BSS_MODE_IBSS; + break; + case IW_MODE_AUTO: + bss->param.bss_mode = MLAN_BSS_MODE_AUTO; + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get current BSSID + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param awrq A pointer to sockaddr structure + * @param extra A pointer to extra data buf + * + * @return 0 --success + */ +static int woal_get_wap(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct sockaddr *awrq = &wrqu->addr; + 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) + moal_memcpy_ext(priv->phandle, awrq->sa_data, &bss_info.bssid, + MLAN_MAC_ADDR_LENGTH, sizeof(awrq->sa_data)); + 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, + union iwreq_data *wrqu, 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); + struct sockaddr *awrq = &wrqu->addr; + mlan_ssid_bssid ssid_bssid; + mlan_bss_info bss_info; + + ENTER(); + + if (awrq->sa_family != ARPHRD_ETHER) { + ret = -EINVAL; + goto done; + } + + PRINTM(MINFO, "ASSOC: WAP: sa_data: " MACSTR "\n", + MAC2STR((t_u8 *)awrq->sa_data)); + + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; +#endif + + /* zero_mac means disconnect */ + if (!memcmp(zero_mac, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE); + goto done; + } + + /* Broadcast MAC means search for best network */ + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + + if (memcmp(bcast, awrq->sa_data, MLAN_MAC_ADDR_LENGTH)) { + /* Check if we are already assoicated to the AP */ + if (bss_info.media_connected == MTRUE) { + if (!memcmp(awrq->sa_data, &bss_info.bssid, ETH_ALEN)) + goto done; + } + moal_memcpy_ext(priv->phandle, &ssid_bssid.bssid, awrq->sa_data, + ETH_ALEN, sizeof(ssid_bssid.bssid)); + } + + 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; + } + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.ssid, + &bss_info.ssid, sizeof(mlan_802_11_ssid), + sizeof(priv->prev_ssid_bssid.ssid)); + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.bssid, + &bss_info.bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(priv->prev_ssid_bssid.bssid)); +#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, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u32 *uwrq = &wrqu->mode; + 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, + union iwreq_data *wrqu, 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, + union iwreq_data *wrqu, char *extra) +{ + int ret = -1; + + ENTER(); + + LEAVE(); + return ret; +} + +/** + * @brief Set Tx power + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_txpow(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_power_cfg_t power_cfg; + + ENTER(); + if (vwrq->disabled) { + woal_set_radio(priv, 0); + goto done; + } + woal_set_radio(priv, 1); + + if (!vwrq->fixed) + power_cfg.is_power_auto = 1; + else { + power_cfg.is_power_auto = 0; + power_cfg.power_level = vwrq->value; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_SET, &power_cfg)) { + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get Tx power + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_txpow(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_power_cfg_t power_cfg; + mlan_bss_info bss_info; + + ENTER(); + + memset(&power_cfg, 0, sizeof(mlan_power_cfg_t)); + memset(&bss_info, 0, sizeof(bss_info)); + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_tx_power(priv, MLAN_ACT_GET, &power_cfg)) { + ret = -EFAULT; + goto done; + } + + vwrq->value = power_cfg.power_level; + if (power_cfg.is_power_auto) + vwrq->fixed = 0; + else + vwrq->fixed = 1; + if (bss_info.radio_on) { + vwrq->disabled = 0; + vwrq->flags = IW_TXPOW_DBM; + } else { + vwrq->disabled = 1; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set power management + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int woal_set_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, disabled; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (moal_extflg_isset(priv->phandle, EXT_HW_TEST)) { + PRINTM(MIOCTL, "block set power in hw_test mode\n"); + LEAVE(); + return ret; + } + disabled = vwrq->disabled; + + if (MLAN_STATUS_SUCCESS != + woal_set_get_power_mgmt(priv, MLAN_ACT_SET, &disabled, vwrq->flags, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get power management + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return MLAN_STATUS_SUCCESS --success, otherwise fail + */ +static int woal_get_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, ps_mode = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_set_get_power_mgmt(priv, MLAN_ACT_GET, + &ps_mode, 0, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + + if (ps_mode) + vwrq->disabled = 0; + else + vwrq->disabled = 1; + + vwrq->value = 0; + + LEAVE(); + return ret; +} + +/** + * @brief Set Tx retry count + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, retry_val = vwrq->value; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (vwrq->flags == IW_RETRY_LIMIT) { + /* + * The MAC has a 4-bit Total_Tx_Count register + * Total_Tx_Count = 1 + Tx_Retry_Count + */ + + if (MLAN_STATUS_SUCCESS != + woal_set_get_retry(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, + &retry_val)) { + ret = -EFAULT; + goto done; + } + } else { + ret = -EOPNOTSUPP; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get Tx retry count + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int retry_val, ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_set_get_retry(priv, MLAN_ACT_GET, + MOAL_IOCTL_WAIT, + &retry_val)) { + ret = -EFAULT; + goto done; + } + + vwrq->disabled = 0; + if (!vwrq->flags) { + vwrq->flags = IW_RETRY_LIMIT; + /* Get Tx retry count */ + vwrq->value = retry_val; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_encode(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + int index = 0; + t_u32 auth_mode = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + + /* Check index */ + index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (index > 3) { + PRINTM(MERROR, "Key index #%d out of range\n", index); + ret = -EINVAL; + goto done; + } + + sec->param.encrypt_key.key_len = 0; + if (!(dwrq->flags & IW_ENCODE_NOKEY) && dwrq->length) { + if (dwrq->length > MAX_WEP_KEY_SIZE) { + PRINTM(MERROR, "Key length (%d) out of range\n", + dwrq->length); + ret = -EINVAL; + goto done; + } + if (index < 0) + sec->param.encrypt_key.key_index = + MLAN_KEY_INDEX_DEFAULT; + else + sec->param.encrypt_key.key_index = index; + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.key_material, extra, + dwrq->length, + sizeof(sec->param.encrypt_key.key_material)); + /* Set the length */ + if (dwrq->length > MIN_WEP_KEY_SIZE) + sec->param.encrypt_key.key_len = MAX_WEP_KEY_SIZE; + else + sec->param.encrypt_key.key_len = MIN_WEP_KEY_SIZE; + } else { + /* + * No key provided so it is either enable key, + * on or off + */ + if (dwrq->flags & IW_ENCODE_DISABLED) { + PRINTM(MINFO, "*** iwconfig mlanX key off ***\n"); + sec->param.encrypt_key.key_disable = MTRUE; + } else { + /* + * iwconfig mlanX key [n] + * iwconfig mlanX key on + * iwconfig mlanX key open + * iwconfig mlanX key restricted + * Do we want to just set the transmit key index ? + */ + if (index < 0) { + PRINTM(MINFO, + "*** iwconfig mlanX key on ***\n"); + sec->param.encrypt_key.key_index = + MLAN_KEY_INDEX_DEFAULT; + } else + sec->param.encrypt_key.key_index = index; + sec->param.encrypt_key.is_current_wep_key = MTRUE; + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + if (dwrq->flags & (IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN)) { + switch (dwrq->flags & 0xf000) { + case IW_ENCODE_RESTRICTED: + /* iwconfig mlanX restricted key [1] */ + auth_mode = MLAN_AUTH_MODE_SHARED; + PRINTM(MINFO, "Auth mode restricted!\n"); + break; + case IW_ENCODE_OPEN: + /* iwconfig mlanX key [2] open */ + auth_mode = MLAN_AUTH_MODE_OPEN; + PRINTM(MINFO, "Auth mode open!\n"); + break; + case IW_ENCODE_RESTRICTED | IW_ENCODE_OPEN: + default: + /* iwconfig mlanX key [2] open restricted */ + auth_mode = MLAN_AUTH_MODE_AUTO; + PRINTM(MINFO, "Auth mode auto!\n"); + break; + } + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) + ret = -EFAULT; + } +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Get encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_encode(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + mlan_ds_sec_cfg *sec = NULL; + mlan_ioctl_req *req = NULL; + t_u32 auth_mode; + int index = (dwrq->flags & IW_ENCODE_INDEX); + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + if (index < 0 || index > 4) { + PRINTM(MERROR, "Key index #%d out of range\n", index); + ret = -EINVAL; + goto done; + } + if (MLAN_STATUS_SUCCESS != + woal_get_auth_mode(priv, MOAL_IOCTL_WAIT, &auth_mode)) { + ret = -EFAULT; + goto done; + } + dwrq->flags = 0; + /* + * Check encryption mode + */ + switch (auth_mode) { + case MLAN_AUTH_MODE_OPEN: + dwrq->flags = IW_ENCODE_OPEN; + break; + + case MLAN_AUTH_MODE_SHARED: + case MLAN_AUTH_MODE_NETWORKEAP: + dwrq->flags = IW_ENCODE_RESTRICTED; + break; + + case MLAN_AUTH_MODE_AUTO: + dwrq->flags = IW_ENCODE_OPEN | IW_ENCODE_RESTRICTED; + break; + + default: + dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; + break; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_GET; + + if (!index) + sec->param.encrypt_key.key_index = MLAN_KEY_INDEX_DEFAULT; + else + sec->param.encrypt_key.key_index = index - 1; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + memset(extra, 0, 16); + if (sec->param.encrypt_key.key_len) { + moal_memcpy_ext(priv->phandle, extra, + sec->param.encrypt_key.key_material, + sec->param.encrypt_key.key_len, + sec->param.encrypt_key.key_len); + dwrq->length = sec->param.encrypt_key.key_len; + dwrq->flags |= (sec->param.encrypt_key.key_index + 1); + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else if (sec->param.encrypt_key.key_disable) + dwrq->flags |= IW_ENCODE_DISABLED; + else + dwrq->flags &= ~IW_ENCODE_DISABLED; + + dwrq->flags |= IW_ENCODE_NOKEY; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set data rate + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_rate_cfg_t rate_cfg; + + ENTER(); + + if (vwrq->value == -1) { + rate_cfg.is_rate_auto = 1; + } else { + rate_cfg.is_rate_auto = 0; + rate_cfg.rate_type = MLAN_RATE_VALUE; + rate_cfg.rate = vwrq->value / 500000; + } + if (MLAN_STATUS_SUCCESS != + woal_set_get_data_rate(priv, MLAN_ACT_SET, &rate_cfg)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get data rate + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + mlan_rate_cfg_t rate_cfg; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_data_rate(priv, MLAN_ACT_GET, &rate_cfg)) { + ret = -EFAULT; + goto done; + } + + if (rate_cfg.is_rate_auto) + vwrq->fixed = 0; + else + vwrq->fixed = 1; + vwrq->value = rate_cfg.rate * 500000; +done: + LEAVE(); + return ret; +} + +/** + * @brief Set RTS threshold + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + int rthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + rthr = MLAN_RTS_MAX_VALUE; + } else { + if (rthr < MLAN_RTS_MIN_VALUE || rthr > MLAN_RTS_MAX_VALUE) { + ret = -EINVAL; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_rts(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &rthr)) { + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get RTS threshold + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int rthr, ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_rts(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, &rthr)) { + ret = -EFAULT; + goto done; + } + + vwrq->value = rthr; + vwrq->disabled = ((vwrq->value < MLAN_RTS_MIN_VALUE) || + (vwrq->value > MLAN_RTS_MAX_VALUE)); + vwrq->fixed = 1; + +done: + LEAVE(); + return ret; +} + +/** + * @brief Set Fragment threshold + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + int fthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + fthr = MLAN_FRAG_MAX_VALUE; + } else { + if (fthr < MLAN_FRAG_MIN_VALUE || fthr > MLAN_FRAG_MAX_VALUE) { + ret = -EINVAL; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_get_frag(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &fthr)) { + ret = -EFAULT; + goto done; + } + +done: + LEAVE(); + return ret; +} + +/** + * @brief Get Fragment threshold + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0, fthr; + moal_private *priv = (moal_private *)netdev_priv(dev); + + ENTER(); + + if (MLAN_STATUS_SUCCESS != + woal_set_get_frag(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, &fthr)) { + ret = -EFAULT; + goto done; + } + + vwrq->value = fthr; + vwrq->disabled = ((vwrq->value < MLAN_FRAG_MIN_VALUE) || + (vwrq->value > MLAN_FRAG_MAX_VALUE)); + vwrq->fixed = 1; + +done: + LEAVE(); + return ret; +} + +#if (WIRELESS_EXT >= 18) +/** + * @brief Get IE + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_gen_ie(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + int copy_size = 0, ie_len; + t_u8 ie[MAX_IE_SIZE]; + + ENTER(); + + if (MLAN_STATUS_SUCCESS != woal_set_get_gen_ie(priv, MLAN_ACT_GET, ie, + &ie_len, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + + copy_size = MIN(ie_len, dwrq->length); + moal_memcpy_ext(priv->phandle, extra, ie, copy_size, 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, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + int ie_len = dwrq->length; + const t_u8 wps_oui[] = {0x00, 0x50, 0xf2, 0x04}; + mlan_ds_wps_cfg *pwps = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + /* extra + 2 to skip element id and length */ + if (!memcmp((t_u8 *)(extra + 2), wps_oui, sizeof(wps_oui))) { + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_wps_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + pwps = (mlan_ds_wps_cfg *)req->pbuf; + req->req_id = MLAN_IOCTL_WPS_CFG; + req->action = MLAN_ACT_SET; + pwps->sub_command = MLAN_OID_WPS_CFG_SESSION; + pwps->param.wps_session = MLAN_WPS_CFG_SESSION_START; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + } + + if (MLAN_STATUS_SUCCESS != woal_set_get_gen_ie(priv, MLAN_ACT_SET, + (t_u8 *)extra, &ie_len, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + + LEAVE(); + return ret; +} + +/** + * @brief Extended version of encoding configuration + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_encode_ext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + int key_index; + t_u8 *pkey_material = NULL; + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + key_index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + if (key_index < 0 || key_index > 5) { + ret = -EINVAL; + goto done; + } + if (ext->key_len > (dwrq->length - sizeof(struct iw_encode_ext))) { + ret = -EINVAL; + goto done; + } + if (ext->key_len > (MLAN_MAX_KEY_LENGTH)) { + ret = -EINVAL; + goto done; + } + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + pkey_material = (t_u8 *)(ext + 1); + sec->param.encrypt_key.key_len = ext->key_len; + moal_memcpy_ext(priv->phandle, sec->param.encrypt_key.mac_addr, + (u8 *)ext->addr.sa_data, ETH_ALEN, + sizeof(sec->param.encrypt_key.mac_addr)); + /* Disable and Remove Key */ + if ((dwrq->flags & IW_ENCODE_DISABLED) && !ext->key_len) { + sec->param.encrypt_key.key_remove = MTRUE; + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY; + PRINTM(MIOCTL, + "Remove key key_index=%d, dwrq->flags=0x%x " MACSTR "\n", + key_index, dwrq->flags, + MAC2STR(sec->param.encrypt_key.mac_addr)); + } else if (ext->key_len <= MAX_WEP_KEY_SIZE) { + /* Set WEP key */ + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.key_flags = ext->ext_flags; + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.key_material, + pkey_material, ext->key_len, + sizeof(sec->param.encrypt_key.key_material)); + 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) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.pn, + (t_u8 *)ext->rx_seq, SEQ_MAX_SIZE, + sizeof(sec->param.encrypt_key.pn)); + DBG_HEXDUMP(MCMD_D, "Rx PN", sec->param.encrypt_key.pn, + SEQ_MAX_SIZE); + } + if (ext->ext_flags & IW_ENCODE_EXT_TX_SEQ_VALID) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.pn, + (t_u8 *)ext->tx_seq, SEQ_MAX_SIZE, + sizeof(sec->param.encrypt_key.pn)); + DBG_HEXDUMP(MCMD_D, "Tx PN", sec->param.encrypt_key.pn, + SEQ_MAX_SIZE); + } + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.key_material, + pkey_material, ext->key_len, + sizeof(sec->param.encrypt_key.key_material)); + PRINTM(MIOCTL, + "set wpa key key_index=%d, key_len=%d key_flags=0x%x " MACSTR + "\n", + key_index, ext->key_len, + sec->param.encrypt_key.key_flags, + MAC2STR(sec->param.encrypt_key.mac_addr)); + DBG_HEXDUMP(MCMD_D, "wpa key", pkey_material, ext->key_len); +#define IW_ENCODE_ALG_AES_CMAC 5 + if (ext->alg == IW_ENCODE_ALG_AES_CMAC) + sec->param.encrypt_key.key_flags |= + KEY_FLAG_AES_MCAST_IGTK; +#define IW_ENCODE_ALG_SMS4 0x20 + /* Set WAPI key */ + if (ext->alg == IW_ENCODE_ALG_SMS4) { + sec->param.encrypt_key.is_wapi_key = MTRUE; + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.pn, + (t_u8 *)ext->tx_seq, SEQ_MAX_SIZE, + sizeof(sec->param.encrypt_key.pn)); + moal_memcpy_ext( + priv->phandle, + &sec->param.encrypt_key.pn[SEQ_MAX_SIZE], + (t_u8 *)ext->rx_seq, SEQ_MAX_SIZE, + sizeof(sec->param.encrypt_key.pn) - + SEQ_MAX_SIZE); + DBG_HEXDUMP(MCMD_D, "WAPI PN", + sec->param.encrypt_key.pn, PN_SIZE); + } + } + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) + ret = -EFAULT; +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Extended version of encoding configuration + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return -EOPNOTSUPP + */ +static int woal_get_encode_ext(struct net_device *dev, + struct iw_request_info *info, + union iwreq_data *wrqu, 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, + union iwreq_data *wrqu, char *extra) +{ + struct iw_mlme *mlme = (struct iw_mlme *)extra; + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + + ENTER(); + if ((mlme->cmd == IW_MLME_DEAUTH) || (mlme->cmd == IW_MLME_DISASSOC)) { + if (MLAN_STATUS_SUCCESS != + woal_disconnect(priv, MOAL_IOCTL_WAIT, + (t_u8 *)mlme->addr.sa_data, + DEF_DEAUTH_REASON_CODE)) + ret = -EFAULT; + } + LEAVE(); + return ret; +} + +/** @brief Set authentication mode parameters + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_auth(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_param *vwrq = &wrqu->param; + t_u32 auth_mode = 0; + t_u32 encrypt_mode = 0; + ENTER(); + + switch (vwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + if (vwrq->value & IW_AUTH_CIPHER_NONE) + encrypt_mode = MLAN_ENCRYPTION_MODE_NONE; + else if (vwrq->value & IW_AUTH_CIPHER_WEP40) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP40; + else if (vwrq->value & IW_AUTH_CIPHER_WEP104) + encrypt_mode = MLAN_ENCRYPTION_MODE_WEP104; + else if (vwrq->value & IW_AUTH_CIPHER_TKIP) + encrypt_mode = MLAN_ENCRYPTION_MODE_TKIP; + else if (vwrq->value & IW_AUTH_CIPHER_CCMP) + encrypt_mode = MLAN_ENCRYPTION_MODE_CCMP; + if (MLAN_STATUS_SUCCESS != + woal_set_encrypt_mode(priv, MOAL_IOCTL_WAIT, encrypt_mode)) + ret = -EFAULT; + break; + case IW_AUTH_80211_AUTH_ALG: + switch (vwrq->value) { + case IW_AUTH_ALG_SHARED_KEY: + PRINTM(MINFO, "Auth mode shared key!\n"); + auth_mode = MLAN_AUTH_MODE_SHARED; + break; + case IW_AUTH_ALG_LEAP: + PRINTM(MINFO, "Auth mode LEAP!\n"); + auth_mode = MLAN_AUTH_MODE_NETWORKEAP; + break; + case IW_AUTH_ALG_OPEN_SYSTEM: + PRINTM(MINFO, "Auth mode open!\n"); + auth_mode = MLAN_AUTH_MODE_OPEN; + break; + case IW_AUTH_ALG_SHARED_KEY | IW_AUTH_ALG_OPEN_SYSTEM: + default: + PRINTM(MINFO, "Auth mode auto!\n"); + auth_mode = MLAN_AUTH_MODE_AUTO; + break; + } + if (MLAN_STATUS_SUCCESS != + woal_set_auth_mode(priv, MOAL_IOCTL_WAIT, auth_mode)) + ret = -EFAULT; + break; + case IW_AUTH_WPA_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wpa_enable(priv, MOAL_IOCTL_WAIT, vwrq->value)) + ret = -EFAULT; + break; +#define IW_AUTH_WAPI_ENABLED 0x20 + case IW_AUTH_WAPI_ENABLED: + if (MLAN_STATUS_SUCCESS != + woal_set_wapi_enable(priv, MOAL_IOCTL_WAIT, vwrq->value)) + ret = -EFAULT; + break; + case IW_AUTH_WPA_VERSION: + /* set WPA_VERSION_DISABLED/VERSION_WPA/VERSION_WP2 */ + priv->wpa_version = vwrq->value; + break; + case IW_AUTH_KEY_MGMT: + /* set KEY_MGMT_802_1X/KEY_MGMT_PSK */ + priv->key_mgmt = vwrq->value; + break; + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_DROP_UNENCRYPTED: + case IW_AUTH_RX_UNENCRYPTED_EAPOL: + case IW_AUTH_ROAMING_CONTROL: + case IW_AUTH_PRIVACY_INVOKED: +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 29) + case IW_AUTH_MFP: +#endif + break; + default: + ret = -EOPNOTSUPP; + break; + } + LEAVE(); + return ret; +} + +/** + * @brief Get authentication mode parameters + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_get_auth(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_param *vwrq = &wrqu->param; + 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, + union iwreq_data *wrqu, char *extra) +{ + int i; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + struct iw_range *range = (struct iw_range *)extra; + moal_802_11_rates rates; + mlan_chan_list *pchan_list = NULL; + mlan_bss_info bss_info; + gfp_t flag; + + ENTER(); + + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + pchan_list = kzalloc(sizeof(mlan_chan_list), flag); + if (!pchan_list) { + LEAVE(); + return -ENOMEM; + } + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->min_nwid = 0; + range->max_nwid = 0; + + memset(&rates, 0, sizeof(rates)); + woal_get_data_rates(priv, MOAL_IOCTL_WAIT, &rates); + range->num_bitrates = rates.num_of_rates; + + for (i = 0; + i < MIN(range->num_bitrates, IW_MAX_BITRATES) && rates.rates[i]; + i++) { + range->bitrate[i] = (rates.rates[i] & 0x7f) * 500000; + } + range->num_bitrates = i; + PRINTM(MINFO, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES, + range->num_bitrates); + + range->num_frequency = 0; + + woal_get_channel_list(priv, MOAL_IOCTL_WAIT, pchan_list); + + range->num_frequency = MIN(pchan_list->num_of_chan, IW_MAX_FREQUENCIES); + + for (i = 0; i < range->num_frequency; i++) { + range->freq[i].i = (long)pchan_list->cf[i].channel; + range->freq[i].m = (long)pchan_list->cf[i].freq * 100000; + range->freq[i].e = 1; + } + kfree(pchan_list); + + PRINTM(MINFO, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n", + IW_MAX_FREQUENCIES, range->num_frequency); + + range->num_channels = range->num_frequency; + + woal_sort_channels(&range->freq[0], range->num_frequency); + + /* + * Set an indication of the max TCP throughput in bit/s that we can + * expect using this interface + */ + if (i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = MLAN_RTS_MIN_VALUE; + range->max_rts = MLAN_RTS_MAX_VALUE; + range->min_frag = MLAN_FRAG_MIN_VALUE; + range->max_frag = MLAN_FRAG_MAX_VALUE; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + +/** Minimum power period */ +#define IW_POWER_PERIOD_MIN 1000000 /* 1 sec */ +/** Maximum power period */ +#define IW_POWER_PERIOD_MAX 120000000 /* 2 min */ +/** Minimum power timeout value */ +#define IW_POWER_TIMEOUT_MIN 1000 /* 1 ms */ +/** Maximim power timeout value */ +#define IW_POWER_TIMEOUT_MAX 1000000 /* 1 sec */ + + /* Power Management duration & timeout */ + range->min_pmp = IW_POWER_PERIOD_MIN; + range->max_pmp = IW_POWER_PERIOD_MAX; + range->min_pmt = IW_POWER_TIMEOUT_MIN; + range->max_pmt = IW_POWER_TIMEOUT_MAX; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* + * Minimum version we recommend + */ + range->we_version_source = 15; + + /* + * Version we are compiled with + */ + range->we_version_compiled = WIRELESS_EXT; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + + range->min_retry = MLAN_TX_RETRY_MIN; + range->max_retry = MLAN_TX_RETRY_MAX; + + /* + * Set the qual, level and noise range values + */ + /* + * need to put the right values here + */ +/** Maximum quality percentage */ +#define IW_MAX_QUAL_PERCENT 5 +/** Average quality percentage */ +#define IW_AVG_QUAL_PERCENT 3 + range->max_qual.qual = IW_MAX_QUAL_PERCENT; + range->max_qual.level = 0; + range->max_qual.noise = 0; + + range->avg_qual.qual = IW_AVG_QUAL_PERCENT; + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + + range->sensitivity = 0; + + /* + * Setup the supported power level ranges + */ + memset(range->txpower, 0, sizeof(range->txpower)); + + memset(&bss_info, 0, sizeof(bss_info)); + + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + + range->txpower[0] = bss_info.min_power_level; + range->txpower[1] = bss_info.max_power_level; + range->num_txpower = 2; + range->txpower_capa = IW_TXPOW_DBM | IW_TXPOW_RANGE; + +#if (WIRELESS_EXT >= 18) + range->enc_capa = IW_ENC_CAPA_WPA | IW_ENC_CAPA_WPA2 | + IW_ENC_CAPA_CIPHER_CCMP | IW_ENC_CAPA_CIPHER_TKIP; +#endif + LEAVE(); + return 0; +} + +#ifdef MEF_CFG_RX_FILTER +/** + * @brief Enable/disable Rx broadcast/multicast filter in non-HS mode + * + * @param priv A pointer to moal_private structure + * @param enable MTRUE/MFALSE: enable/disable + * + * @return 0 -- success, otherwise fail + */ +static int woal_set_rxfilter(moal_private *priv, BOOLEAN enable) +{ + int ret = 0; + mlan_ioctl_req *req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_mef_cfg *mef_cfg = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + misc = (mlan_ds_misc_cfg *)req->pbuf; + mef_cfg = &misc->param.mef_cfg; + req->req_id = MLAN_IOCTL_MISC_CFG; + misc->sub_command = MLAN_OID_MISC_MEF_CFG; + req->action = MLAN_ACT_SET; + + mef_cfg->sub_id = (enable ? MEF_CFG_RX_FILTER_ENABLE : MEF_CFG_DISABLE); + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (status != MLAN_STATUS_SUCCESS) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} +#endif + +/** + * @brief Set priv command + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +static int woal_set_priv(struct net_device *dev, struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + int ret = 0; + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + char *buf = NULL; + int power_mode = 0; + int band = 0; + char *pband = NULL; + mlan_bss_info bss_info; + mlan_ds_get_signal signal; + mlan_rate_cfg_t rate; + char *pdata = NULL; + t_u8 country_code[COUNTRY_CODE_LEN]; + int len = 0; + gfp_t flag; + ENTER(); + if (!priv || !priv->phandle) { + PRINTM(MERROR, "priv or handle is NULL\n"); + ret = -EFAULT; + goto done; + } + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc(dwrq->length + 1, flag); + if (!buf) { + ret = -ENOMEM; + goto done; + } + if (copy_from_user(buf, dwrq->pointer, dwrq->length)) { + ret = -EFAULT; + goto done; + } + buf[dwrq->length] = '\0'; + PRINTM(MIOCTL, "SIOCSIWPRIV request = %s\n", buf); + if (strncmp(buf, "RSSILOW-THRESHOLD", strlen("RSSILOW-THRESHOLD")) == + 0) { + if (dwrq->length > strlen("RSSILOW-THRESHOLD") + 1) { + pdata = buf + strlen("RSSILOW-THRESHOLD") + 1; + if (MLAN_STATUS_SUCCESS != + woal_set_rssi_low_threshold(priv, pdata, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "RSSI", strlen("RSSI")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + if (bss_info.media_connected) { + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, + &signal)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "%s rssi %d\n", bss_info.ssid.ssid, + signal.bcn_rssi_avg) + + 1; + } else { + len = sprintf(buf, "OK\n") + 1; + } + } else if (strncmp(buf, "LINKSPEED", strlen("LINKSPEED")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_get_data_rate(priv, MLAN_ACT_GET, &rate)) { + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "tx rate=%d\n", (int)rate.rate); + len = sprintf(buf, "LinkSpeed %d\n", + (int)(rate.rate * 500000 / 1000000)) + + 1; + } else if (strncmp(buf, "MACADDR", strlen("MACADDR")) == 0) { + len = sprintf(buf, "Macaddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + priv->current_addr[0], priv->current_addr[1], + priv->current_addr[2], priv->current_addr[3], + priv->current_addr[4], priv->current_addr[5]) + + 1; + } else if (strncmp(buf, "GETPOWER", strlen("GETPOWER")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_get_powermode(priv, &power_mode)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "powermode = %d\n", power_mode) + 1; + } else if (strncmp(buf, "SCAN-ACTIVE", strlen("SCAN-ACTIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE)) { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_ACTIVE; + PRINTM(MIOCTL, "Set Active Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SCAN-PASSIVE", strlen("SCAN-PASSIVE")) == 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_scan_type(priv, MLAN_SCAN_TYPE_PASSIVE)) { + ret = -EFAULT; + goto done; + } + priv->scan_type = MLAN_SCAN_TYPE_PASSIVE; + PRINTM(MIOCTL, "Set Passive Scan\n"); + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "POWERMODE", strlen("POWERMODE")) == 0) { + if (dwrq->length > strlen("POWERMODE") + 1) { + pdata = buf + strlen("POWERMODE") + 1; + if (!moal_extflg_isset(priv->phandle, EXT_HW_TEST)) { + if (MLAN_STATUS_SUCCESS != + woal_set_powermode(priv, pdata)) { + ret = -EFAULT; + goto done; + } + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "COUNTRY", strlen("COUNTRY")) == 0) { + memset(country_code, 0, sizeof(country_code)); + if ((strlen(buf) - strlen("COUNTRY") - 1) > COUNTRY_CODE_LEN) { + ret = -EFAULT; + goto done; + } + moal_memcpy_ext(priv->phandle, country_code, + buf + strlen("COUNTRY") + 1, + COUNTRY_CODE_LEN - 1, sizeof(country_code) - 1); + PRINTM(MIOCTL, "Set COUNTRY %s\n", country_code); + if (MLAN_STATUS_SUCCESS != + woal_set_region_code(priv, country_code)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (memcmp(buf, WEXT_CSCAN_HEADER, strlen(WEXT_CSCAN_HEADER)) == + 0) { + PRINTM(MIOCTL, "Set Combo Scan\n"); + if (MLAN_STATUS_SUCCESS != + woal_set_combo_scan(priv, buf, dwrq->length)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "GETBAND", strlen("GETBAND")) == 0) { + if (MLAN_STATUS_SUCCESS != woal_get_band(priv, &band)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "Band %d\n", band) + 1; + } else if (strncmp(buf, "SETBAND", strlen("SETBAND")) == 0) { + pband = buf + strlen("SETBAND") + 1; + if (MLAN_STATUS_SUCCESS != woal_set_band(priv, pband)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "START", strlen("START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "STOP", strlen("STOP")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "SETSUSPENDOPT", strlen("SETSUSPENDOPT")) == + 0) { + /* it will be done by GUI */ + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXMODE", strlen("BTCOEXMODE")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-START", + strlen("BTCOEXSCAN-START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BTCOEXSCAN-STOP", strlen("BTCOEXSCAN-STOP")) == + 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-START", strlen("BGSCAN-START")) == 0) { + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-CONFIG", strlen("BGSCAN-CONFIG")) == + 0) { + if (MLAN_STATUS_SUCCESS != + woal_set_bg_scan(priv, buf, dwrq->length)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MTRUE; + priv->bg_scan_reported = MFALSE; + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "BGSCAN-STOP", strlen("BGSCAN-STOP")) == 0) { + if (priv->bg_scan_start && !priv->scan_cfg.rssi_threshold) { + if (MLAN_STATUS_SUCCESS != + woal_stop_bg_scan(priv, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + goto done; + } + priv->bg_scan_start = MFALSE; + priv->bg_scan_reported = MFALSE; + } + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-START", strlen("RXFILTER-START")) == + 0) { +#ifdef MEF_CFG_RX_FILTER + ret = woal_set_rxfilter(priv, MTRUE); + if (ret) + goto done; +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-STOP", strlen("RXFILTER-STOP")) == + 0) { +#ifdef MEF_CFG_RX_FILTER + ret = woal_set_rxfilter(priv, MFALSE); + if (ret) + goto done; +#endif + len = sprintf(buf, "OK\n") + 1; + } else if (strncmp(buf, "RXFILTER-ADD", strlen("RXFILTER-ADD")) == 0) { + if (dwrq->length > strlen("RXFILTER-ADD") + 1) { + pdata = buf + strlen("RXFILTER-ADD") + 1; + if (MLAN_STATUS_SUCCESS != + woal_add_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "RXFILTER-REMOVE", strlen("RXFILTER-REMOVE")) == + 0) { + if (dwrq->length > strlen("RXFILTER-REMOVE") + 1) { + pdata = buf + strlen("RXFILTER-REMOVE") + 1; + if (MLAN_STATUS_SUCCESS != + woal_remove_rxfilter(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "QOSINFO", strlen("QOSINFO")) == 0) { + if (dwrq->length > strlen("QOSINFO") + 1) { + pdata = buf + strlen("QOSINFO") + 1; + if (MLAN_STATUS_SUCCESS != + woal_priv_qos_cfg(priv, MLAN_ACT_SET, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else if (strncmp(buf, "SLEEPPD", strlen("SLEEPPD")) == 0) { + if (dwrq->length > strlen("SLEEPPD") + 1) { + pdata = buf + strlen("SLEEPPD") + 1; + if (MLAN_STATUS_SUCCESS != + woal_set_sleeppd(priv, pdata)) { + ret = -EFAULT; + goto done; + } + len = sprintf(buf, "OK\n") + 1; + } else { + ret = -EFAULT; + goto done; + } + } else { + PRINTM(MIOCTL, "Unknow PRIVATE command: %s, ignored\n", buf); + ret = -EFAULT; + goto done; + } + PRINTM(MIOCTL, "PRIV Command return: %s, length=%d\n", buf, len); + dwrq->length = (t_u16)len; + if (copy_to_user(dwrq->pointer, buf, dwrq->length)) + ret = -EFAULT; +done: + kfree(buf); + LEAVE(); + return ret; +} + +/** + * @brief 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_wext_request_scan(moal_private *priv, t_u8 wait_option, + mlan_802_11_ssid *req_ssid) +{ + wlan_user_scan_cfg scan_req; + mlan_scan_cfg scan_cfg; + ENTER(); + if (!woal_is_any_interface_active(priv->phandle)) { + LEAVE(); + return woal_request_scan(priv, wait_option, req_ssid); + } + memset(&scan_cfg, 0, sizeof(scan_cfg)); + memset(&scan_req, 0, sizeof(scan_req)); + if (req_ssid && req_ssid->ssid_len != 0) { + moal_memcpy_ext(priv->phandle, scan_req.ssid_list[0].ssid, + req_ssid->ssid, req_ssid->ssid_len, + MLAN_MAX_SSID_LENGTH); + scan_req.ssid_list[0].max_len = 0; + } + woal_get_scan_config(priv, &scan_cfg); + if (scan_cfg.scan_chan_gap) + scan_req.scan_chan_gap = scan_cfg.scan_chan_gap; + else + scan_req.scan_chan_gap = priv->phandle->scan_chan_gap; + /** indicate FW, gap is optional */ + if (scan_req.scan_chan_gap && priv->phandle->pref_mac) + scan_req.scan_chan_gap |= GAP_FLAG_OPTIONAL; + LEAVE(); + return woal_request_userscan(priv, wait_option, &scan_req); +} + +/** + * @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; + moal_memcpy_ext(priv->phandle, req_ssid.ssid, + (t_u8 *)req->essid, req->essid_len, + sizeof(req_ssid.ssid)); + if (MLAN_STATUS_SUCCESS != + woal_wext_request_scan(priv, MOAL_NO_WAIT, + &req_ssid)) { + ret = -EFAULT; + goto done; + } + } + } else { +#endif + if (MLAN_STATUS_SUCCESS != + woal_wext_request_scan(priv, MOAL_NO_WAIT, &req_ssid)) { + 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, + union iwreq_data *wrqu, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + struct iw_point *dwrq = &wrqu->data; + mlan_802_11_ssid req_ssid; + mlan_ssid_bssid ssid_bssid; +#ifdef REASSOCIATION + moal_handle *handle = priv->phandle; + mlan_bss_info bss_info; +#endif + int ret = 0; + t_u32 mode = 0; + + ENTER(); + +#ifdef REASSOCIATION + /* Cancel re-association */ + priv->reassoc_required = MFALSE; + + if (MOAL_ACQ_SEMAPHORE_BLOCK(&handle->reassoc_sem)) { + PRINTM(MERROR, "Acquire semaphore error, woal_set_essid\n"); + LEAVE(); + return -EBUSY; + } +#endif /* REASSOCIATION */ + + /* Check the size of the string */ + if (dwrq->length > IW_ESSID_MAX_SIZE + 1) { + ret = -E2BIG; + goto setessid_ret; + } + if (priv->scan_type == MLAN_SCAN_TYPE_PASSIVE) + woal_set_scan_type(priv, MLAN_SCAN_TYPE_ACTIVE); + memset(&req_ssid, 0, sizeof(mlan_802_11_ssid)); + memset(&ssid_bssid, 0, sizeof(mlan_ssid_bssid)); + +#if WIRELESS_EXT > 20 + req_ssid.ssid_len = dwrq->length; +#else + req_ssid.ssid_len = dwrq->length - 1; +#endif + + /* + * Check if we asked for `any' or 'particular' + */ + if (!dwrq->flags) { +#ifdef REASSOCIATION + if (!req_ssid.ssid_len) { + memset(&priv->prev_ssid_bssid.ssid, 0x00, + sizeof(mlan_802_11_ssid)); + memset(&priv->prev_ssid_bssid.bssid, 0x00, + MLAN_MAC_ADDR_LENGTH); + goto setessid_ret; + } +#endif + /* Do normal SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, NULL)) { + ret = -EFAULT; + goto setessid_ret; + } + } else { + /* Set the SSID */ + moal_memcpy_ext(priv->phandle, req_ssid.ssid, extra, + MIN(req_ssid.ssid_len, MLAN_MAX_SSID_LENGTH), + sizeof(req_ssid.ssid)); + 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); + moal_memcpy_ext(priv->phandle, &ssid_bssid.ssid, &req_ssid, + sizeof(mlan_802_11_ssid), + sizeof(ssid_bssid.ssid)); + if (MTRUE == woal_is_connected(priv, &ssid_bssid)) { + PRINTM(MIOCTL, "Already connect to the network\n"); + goto setessid_ret; + } + + if (dwrq->flags != 0xFFFF) { + if (MLAN_STATUS_SUCCESS != + woal_find_essid(priv, &ssid_bssid, + MOAL_IOCTL_WAIT)) { + /* Do specific SSID scanning */ + if (MLAN_STATUS_SUCCESS != + woal_request_scan(priv, MOAL_IOCTL_WAIT, + &req_ssid)) { + ret = -EFAULT; + goto setessid_ret; + } + } + } + } + + mode = woal_get_mode(priv, MOAL_IOCTL_WAIT); + if (mode == IW_MODE_ADHOC) + /* disconnect before try to associate */ + woal_disconnect(priv, MOAL_IOCTL_WAIT, NULL, + DEF_DEAUTH_REASON_CODE); + + if (mode != IW_MODE_ADHOC) { + if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, + &ssid_bssid)) { + ret = -EFAULT; + goto setessid_ret; + } + if (MLAN_STATUS_SUCCESS != + woal_11d_check_ap_channel(priv, MOAL_IOCTL_WAIT, + &ssid_bssid)) { + PRINTM(MERROR, + "The AP's channel is invalid for current region\n"); + ret = -EFAULT; + goto setessid_ret; + } + } else if (MLAN_STATUS_SUCCESS != + woal_find_best_network(priv, MOAL_IOCTL_WAIT, &ssid_bssid)) + /* Adhoc start, Check the channel command */ + woal_11h_channel_check_ioctl(priv, MOAL_IOCTL_WAIT); + +#ifdef REASSOCIATION + if (priv->reassoc_on == MTRUE && req_ssid.ssid_len) { + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.ssid, + &req_ssid, sizeof(mlan_802_11_ssid), + sizeof(priv->prev_ssid_bssid.ssid)); + memset(&priv->prev_ssid_bssid.bssid, 0x00, + MLAN_MAC_ADDR_LENGTH); + } +#endif /* REASSOCIATION */ + + /* 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; + } + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.ssid, + &bss_info.ssid, sizeof(mlan_802_11_ssid), + sizeof(priv->prev_ssid_bssid.ssid)); + moal_memcpy_ext(priv->phandle, &priv->prev_ssid_bssid.bssid, + &bss_info.bssid, MLAN_MAC_ADDR_LENGTH, + sizeof(priv->prev_ssid_bssid.bssid)); +#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); + moal_memcpy_ext(priv->phandle, extra, bss_info.ssid.ssid, + dwrq->length, dwrq->length); + } else + dwrq->length = 0; + + if (bss_info.scan_table_idx) + dwrq->flags = (bss_info.scan_table_idx + 1) & IW_ENCODE_INDEX; + else + dwrq->flags = 1; + +done: + LEAVE(); + return ret; +} + +/** + * @brief Retrieve the scan table entries via wireless tools IOCTL call + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0--success, otherwise fail + */ +static int woal_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + int ret = 0; + char *current_ev = extra; + char *end_buf = extra + IW_SCAN_MAX_DATA; + char *current_val; /* For rates */ + struct iw_event iwe; /* Temporary buffer */ + unsigned int i; + unsigned int j; + mlan_scan_resp scan_resp; + mlan_bss_info bss_info; + BSSDescriptor_t *scan_table; + mlan_ds_get_signal rssi; + t_u16 buf_size = 16 + 256 * 2; + char *buf = NULL; + char *ptr; +#if WIRELESS_EXT >= 18 + t_u8 *praw_data; +#endif + int beacon_size; + t_u8 *pbeacon; + IEEEtypes_ElementId_e element_id; + t_u8 element_len; + gfp_t flag; + + ENTER(); + + if (priv->phandle->scan_pending_on_block == MTRUE) { + LEAVE(); + return -EAGAIN; + } + flag = (in_atomic() || irqs_disabled()) ? GFP_ATOMIC : GFP_KERNEL; + buf = kzalloc((buf_size), flag); + if (!buf) { + PRINTM(MERROR, "Cannot allocate buffer!\n"); + ret = -EFAULT; + goto done; + } + + memset(&bss_info, 0, sizeof(bss_info)); + if (MLAN_STATUS_SUCCESS != + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info)) { + ret = -EFAULT; + goto done; + } + memset(&scan_resp, 0, sizeof(scan_resp)); + if (MLAN_STATUS_SUCCESS != + woal_get_scan_table(priv, MOAL_IOCTL_WAIT, &scan_resp)) { + ret = -EFAULT; + goto done; + } + scan_table = (BSSDescriptor_t *)scan_resp.pscan_table; + if (dwrq->length) + end_buf = extra + dwrq->length; + if (priv->media_connected == MTRUE) + PRINTM(MINFO, "Current Ssid: %-32s\n", bss_info.ssid.ssid); + PRINTM(MINFO, "Scan: Get: NumInScanTable = %d\n", + (int)scan_resp.num_in_scan_table); + +#if WIRELESS_EXT > 13 + /* The old API using SIOCGIWAPLIST had a hard limit of + * IW_MAX_AP. The new API using SIOCGIWSCAN is only + * limited by buffer size WE-14 -> WE-16 the buffer is + * limited to IW_SCAN_MAX_DATA bytes which is 4096. + */ + for (i = 0; i < MIN(scan_resp.num_in_scan_table, 64); i++) { + if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { + PRINTM(MINFO, + "i=%d break out: current_ev=%p end_buf=%p " + "MAX_SCAN_CELL_SIZE=%d\n", + i, current_ev, end_buf, + (t_u32)MAX_SCAN_CELL_SIZE); + ret = -E2BIG; + break; + } + if (!scan_table[i].freq) { + PRINTM(MWARN, "Invalid channel number %d\n", + (int)scan_table[i].channel); + continue; + } + PRINTM(MINFO, "i=%d Ssid: %-32s\n", i, + scan_table[i].ssid.ssid); + + /* check ssid is valid or not, ex. hidden ssid will be filter + * out */ + if (woal_ssid_valid(&scan_table[i].ssid) == MFALSE) + continue; + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + moal_memcpy_ext(priv->phandle, iwe.u.ap_addr.sa_data, + &scan_table[i].mac_address, ETH_ALEN, + sizeof(iwe.u.ap_addr.sa_data)); + + iwe.len = IW_EV_ADDR_LEN; + current_ev = IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, + &iwe, iwe.len); + + /* Add the ESSID */ + iwe.u.data.length = scan_table[i].ssid.ssid_len; + + if (iwe.u.data.length > 32) + iwe.u.data.length = 32; + + iwe.cmd = SIOCGIWESSID; + iwe.u.essid.flags = (i + 1) & IW_ENCODE_INDEX; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + IWE_STREAM_ADD_POINT(info, current_ev, end_buf, &iwe, + (char *)scan_table[i].ssid.ssid); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + if (scan_table[i].bss_mode == MLAN_BSS_MODE_IBSS) + iwe.u.mode = IW_MODE_ADHOC; + else if (scan_table[i].bss_mode == MLAN_BSS_MODE_INFRA) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_AUTO; + + iwe.len = IW_EV_UINT_LEN; + current_ev = IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, + &iwe, iwe.len); + + /* Frequency */ + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = (long)scan_table[i].freq; + iwe.u.freq.e = 6; + iwe.u.freq.flags = IW_FREQ_FIXED; + iwe.len = IW_EV_FREQ_LEN; + current_ev = IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, + &iwe, iwe.len); + + memset(&iwe, 0, sizeof(iwe)); + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.level = SCAN_RSSI(scan_table[i].rssi); + if (!bss_info.bcn_nf_last) + iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + else + iwe.u.qual.noise = bss_info.bcn_nf_last; + + if ((bss_info.bss_mode == MLAN_BSS_MODE_IBSS) && + !woal_ssid_cmp(&bss_info.ssid, &scan_table[i].ssid) && + bss_info.adhoc_state == ADHOC_STARTED) { + memset(&rssi, 0, sizeof(mlan_ds_get_signal)); + if (MLAN_STATUS_SUCCESS != + woal_get_signal_info(priv, MOAL_IOCTL_WAIT, + &rssi)) { + ret = -EFAULT; + break; + } + iwe.u.qual.level = rssi.data_rssi_avg; + } + iwe.u.qual.qual = + woal_rssi_to_quality((t_s16)(iwe.u.qual.level - 0x100)); + iwe.len = IW_EV_QUAL_LEN; + current_ev = IWE_STREAM_ADD_EVENT(info, current_ev, end_buf, + &iwe, iwe.len); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (scan_table[i].privacy) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + + iwe.u.data.length = 0; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = IWE_STREAM_ADD_POINT(info, current_ev, end_buf, + &iwe, NULL); + + current_val = current_ev + IW_EV_LCP_LEN; + + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = 0; + + /* Bit rate given in 500 kb/s units (+ 0x80) */ + for (j = 0; j < sizeof(scan_table[i].supported_rates); j++) { + if (!scan_table[i].supported_rates[j]) + break; + + iwe.u.bitrate.value = + (scan_table[i].supported_rates[j] & 0x7f) * + 500000; + iwe.len = IW_EV_PARAM_LEN; + current_val = IWE_STREAM_ADD_VALUE(info, current_ev, + current_val, end_buf, + &iwe, iwe.len); + } + if ((bss_info.bss_mode == MLAN_BSS_MODE_IBSS) && + !woal_ssid_cmp(&bss_info.ssid, &scan_table[i].ssid) && + bss_info.adhoc_state == ADHOC_STARTED) { + iwe.u.bitrate.value = 22 * 500000; + iwe.len = IW_EV_PARAM_LEN; + current_val = IWE_STREAM_ADD_VALUE(info, current_ev, + current_val, end_buf, + &iwe, iwe.len); + } + + /* Check if an event is added */ + if ((unsigned int)(current_val - current_ev) >= IW_EV_PARAM_LEN) + current_ev = current_val; + + /* Beacon Interval */ + memset(&iwe, 0, sizeof(iwe)); + ptr = buf; + ptr += sprintf(ptr, "Beacon interval=%d", + scan_table[i].beacon_period); + + iwe.u.data.length = strlen(buf); + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = IWE_STREAM_ADD_POINT(info, current_ev, end_buf, + &iwe, buf); + current_val = current_ev + IW_EV_LCP_LEN + strlen(buf); + + /* Parse and send the IEs */ + pbeacon = scan_table[i].pbeacon_buf; + beacon_size = scan_table[i].beacon_buf_size; + + /* Skip time stamp, beacon interval and capability */ + if (pbeacon) { + pbeacon += sizeof(scan_table[i].beacon_period) + + sizeof(scan_table[i].time_stamp) + + sizeof(scan_table[i].cap_info); + beacon_size -= sizeof(scan_table[i].beacon_period) + + sizeof(scan_table[i].time_stamp) + + sizeof(scan_table[i].cap_info); + + while ((unsigned int)beacon_size >= + sizeof(IEEEtypes_Header_t)) { + element_id = (IEEEtypes_ElementId_e)( + *(t_u8 *)pbeacon); + element_len = *((t_u8 *)pbeacon + 1); + if ((unsigned int)beacon_size < + (unsigned int)element_len + + sizeof(IEEEtypes_Header_t)) { + PRINTM(MERROR, + "Get scan: Error in processing IE, " + "bytes left < IE length\n"); + break; + } + + switch (element_id) { +#if WIRELESS_EXT >= 18 + case VENDOR_SPECIFIC_221: + case RSN_IE: + case WAPI_IE: + praw_data = (t_u8 *)pbeacon; + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, buf_size); + ptr = buf; + moal_memcpy_ext( + priv->phandle, buf, praw_data, + element_len + + sizeof(IEEEtypes_Header_t), + buf_size); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = + element_len + + sizeof(IEEEtypes_Header_t); + iwe.len = IW_EV_POINT_LEN + + iwe.u.data.length; + current_ev = IWE_STREAM_ADD_POINT( + info, current_ev, end_buf, &iwe, + buf); + current_val = current_ev + + IW_EV_LCP_LEN + + strlen(buf); + break; +#endif + default: + break; + } + pbeacon += element_len + + sizeof(IEEEtypes_Header_t); + beacon_size -= element_len + + sizeof(IEEEtypes_Header_t); + } + } + +#if WIRELESS_EXT > 14 + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, buf_size); + ptr = buf; + ptr += sprintf(ptr, "band="); + memset(&iwe, 0, sizeof(iwe)); + if (scan_table[i].bss_band == BAND_A) + ptr += sprintf(ptr, "a"); + else + ptr += sprintf(ptr, "bg"); + iwe.u.data.length = strlen(buf); + PRINTM(MINFO, "iwe.u.data.length %d\n", iwe.u.data.length); + PRINTM(MINFO, "BUF: %s\n", buf); + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = IWE_STREAM_ADD_POINT(info, current_ev, end_buf, + &iwe, buf); + current_val = current_ev + IW_EV_LCP_LEN + strlen(buf); +#endif + current_val = current_ev + IW_EV_LCP_LEN; + + /* + * Check if we added any event + */ + if ((unsigned int)(current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + } + + dwrq->length = (current_ev - extra); + dwrq->flags = 0; +#endif + +done: + kfree(buf); + LEAVE(); + return ret; +} + +/** + * iwconfig settable callbacks + */ +static const iw_handler woal_handler[] = { + (iw_handler)woal_config_commit, /* SIOCSIWCOMMIT */ + (iw_handler)woal_get_name, /* SIOCGIWNAME */ + (iw_handler)NULL, /* SIOCSIWNWID */ + (iw_handler)NULL, /* SIOCGIWNWID */ + (iw_handler)woal_set_freq, /* SIOCSIWFREQ */ + (iw_handler)woal_get_freq, /* SIOCGIWFREQ */ + (iw_handler)woal_set_bss_mode, /* SIOCSIWMODE */ + (iw_handler)woal_get_bss_mode, /* SIOCGIWMODE */ + (iw_handler)woal_set_sens, /* SIOCSIWSENS */ + (iw_handler)woal_get_sens, /* SIOCGIWSENS */ + (iw_handler)NULL, /* SIOCSIWRANGE */ + (iw_handler)woal_get_range, /* SIOCGIWRANGE */ + (iw_handler)woal_set_priv, /* SIOCSIWPRIV */ + (iw_handler)NULL, /* SIOCGIWPRIV */ + (iw_handler)NULL, /* SIOCSIWSTATS */ + (iw_handler)NULL, /* SIOCGIWSTATS */ +#if WIRELESS_EXT > 15 +#ifdef CONFIG_WEXT_SPY + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ +#else + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ +#endif +#else /* WIRELESS_EXT > 15 */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ +#endif /* WIRELESS_EXT > 15 */ + (iw_handler)woal_set_wap, /* SIOCSIWAP */ + (iw_handler)woal_get_wap, /* SIOCGIWAP */ +#if WIRELESS_EXT >= 18 + (iw_handler)woal_set_mlme, /* SIOCSIWMLME */ +#else + (iw_handler)NULL, /* -- hole -- */ +#endif + /* (iw_handler) wlan_get_aplist, */ /* SIOCGIWAPLIST */ + NULL, /* SIOCGIWAPLIST */ +#if WIRELESS_EXT > 13 + (iw_handler)woal_set_scan, /* SIOCSIWSCAN */ + (iw_handler)woal_get_scan, /* SIOCGIWSCAN */ +#else /* WIRELESS_EXT > 13 */ + (iw_handler)NULL, /* SIOCSIWSCAN */ + (iw_handler)NULL, /* SIOCGIWSCAN */ +#endif /* WIRELESS_EXT > 13 */ + (iw_handler)woal_set_essid, /* SIOCSIWESSID */ + (iw_handler)woal_get_essid, /* SIOCGIWESSID */ + (iw_handler)woal_set_nick, /* SIOCSIWNICKN */ + (iw_handler)woal_get_nick, /* SIOCGIWNICKN */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)woal_set_rate, /* SIOCSIWRATE */ + (iw_handler)woal_get_rate, /* SIOCGIWRATE */ + (iw_handler)woal_set_rts, /* SIOCSIWRTS */ + (iw_handler)woal_get_rts, /* SIOCGIWRTS */ + (iw_handler)woal_set_frag, /* SIOCSIWFRAG */ + (iw_handler)woal_get_frag, /* SIOCGIWFRAG */ + (iw_handler)woal_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler)woal_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler)woal_set_retry, /* SIOCSIWRETRY */ + (iw_handler)woal_get_retry, /* SIOCGIWRETRY */ + (iw_handler)woal_set_encode, /* SIOCSIWENCODE */ + (iw_handler)woal_get_encode, /* SIOCGIWENCODE */ + (iw_handler)woal_set_power, /* SIOCSIWPOWER */ + (iw_handler)woal_get_power, /* SIOCGIWPOWER */ +#if (WIRELESS_EXT >= 18) + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)NULL, /* -- hole -- */ + (iw_handler)woal_set_gen_ie, /* SIOCSIWGENIE */ + (iw_handler)woal_get_gen_ie, /* SIOCGIWGENIE */ + (iw_handler)woal_set_auth, /* SIOCSIWAUTH */ + (iw_handler)woal_get_auth, /* SIOCGIWAUTH */ + (iw_handler)woal_set_encode_ext, /* SIOCSIWENCODEEXT */ + (iw_handler)woal_get_encode_ext, /* SIOCGIWENCODEEXT */ + (iw_handler)woal_set_pmksa, /* SIOCSIWPMKSA */ +#endif /* WIRELESSS_EXT >= 18 */ +}; + +/** + * iwpriv settable callbacks + */ +static const iw_handler woal_private_handler[] = { + NULL, /* SIOCIWFIRSTPRIV */ +}; +#endif /* STA_SUPPORT */ + +/******************************************************** + Global Functions +********************************************************/ + +#if WIRELESS_EXT > 14 + +/** + * @brief This function sends customized event to application. + * + * @param priv A pointer to moal_private structure + * @param str A pointer to event string + * + * @return N/A + */ +void woal_send_iwevcustom_event(moal_private *priv, char *str) +{ + union iwreq_data iwrq; + char buf[IW_CUSTOM_MAX]; + + ENTER(); + + memset(&iwrq, 0, sizeof(union iwreq_data)); + memset(buf, 0, sizeof(buf)); + + snprintf(buf, sizeof(buf) - 1, "%s", str); + + iwrq.data.pointer = buf; + iwrq.data.length = strlen(buf) + 1; + + /* Send Event to upper layer */ + wireless_send_event(priv->netdev, IWEVCUSTOM, &iwrq, buf); + PRINTM(MINFO, "Wireless event %s is sent to application\n", str); + + LEAVE(); + return; +} +#endif + +#if WIRELESS_EXT >= 18 +/** + * @brief This function sends mic error event to application. + * + * @param priv A pointer to moal_private structure + * @param event MIC MERROR EVENT. + * + * @return N/A + */ +void woal_send_mic_error_event(moal_private *priv, t_u32 event) +{ + union iwreq_data iwrq; + struct iw_michaelmicfailure mic; + + ENTER(); + + memset(&iwrq, 0, sizeof(iwrq)); + memset(&mic, 0, sizeof(mic)); + if (event == MLAN_EVENT_ID_FW_MIC_ERR_UNI) + mic.flags = IW_MICFAILURE_PAIRWISE; + else + mic.flags = IW_MICFAILURE_GROUP; + iwrq.data.pointer = &mic; + iwrq.data.length = sizeof(mic); + + wireless_send_event(priv->netdev, IWEVMICHAELMICFAILURE, &iwrq, + (char *)&mic); + + LEAVE(); + return; +} +#endif + +// clang-format off +#ifdef STA_SUPPORT +/** wlan_handler_def */ +struct iw_handler_def woal_handler_def = { + num_standard: ARRAY_SIZE(woal_handler), + num_private : ARRAY_SIZE(woal_private_handler), + num_private_args : ARRAY_SIZE(woal_private_args), + standard : (iw_handler *)woal_handler, + private : (iw_handler *)woal_private_handler, + private_args : (struct iw_priv_args *)woal_private_args, +#if WIRELESS_EXT > 20 + get_wireless_stats : woal_get_wireless_stats, +#endif +}; +// clang-format on + +/** + * @brief Get wireless statistics + * + * @param dev A pointer to net_device structure + * + * @return A pointer to iw_statistics buf + */ +struct iw_statistics *woal_get_wireless_stats(struct net_device *dev) +{ + moal_private *priv = (moal_private *)netdev_priv(dev); + t_u16 wait_option = MOAL_IOCTL_WAIT; + + ENTER(); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) + /* + * Since schedule() is not allowed from an atomic context + * such as when dev_base_lock for netdevices is acquired + * for reading/writing in kernel before this call, HostCmd + * is issued in non-blocking way in such contexts and + * blocking in other cases. + */ + if (in_atomic() || !write_can_lock(&dev_base_lock)) + wait_option = MOAL_NO_WAIT; +#endif + + priv->w_stats.status = woal_get_mode(priv, wait_option); + priv->w_stats.discard.retries = priv->stats.tx_errors; + priv->w_stats.qual.qual = 0; + + /* Send RSSI command to get beacon RSSI/NF, valid only if associated */ + if (priv->media_connected == MTRUE) { + if (MLAN_STATUS_SUCCESS == + woal_get_signal_info(priv, wait_option, NULL)) + priv->w_stats.qual.qual = woal_rssi_to_quality( + (t_s16)(priv->w_stats.qual.level - 0x100)); + } +#if WIRELESS_EXT > 18 + priv->w_stats.qual.updated |= (IW_QUAL_ALL_UPDATED | IW_QUAL_DBM); +#else + priv->w_stats.qual.updated |= 7; +#endif + if (!priv->w_stats.qual.noise && priv->media_connected == MTRUE) + priv->w_stats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + + PRINTM(MINFO, "Link Quality = %#x\n", priv->w_stats.qual.qual); + PRINTM(MINFO, "Signal Level = %#x\n", priv->w_stats.qual.level); + PRINTM(MINFO, "Noise = %#x\n", priv->w_stats.qual.noise); + priv->w_stats.discard.code = 0; + if (MLAN_STATUS_SUCCESS != woal_get_stats_info(priv, wait_option, NULL)) + PRINTM(MERROR, "Error getting stats information\n"); + + LEAVE(); + return &priv->w_stats; +} +#endif /* STA_SUPPORT */ diff --git a/mxm_wifiex/wlan_src/mlinux/moal_wext.h b/mxm_wifiex/wlan_src/mlinux/moal_wext.h new file mode 100644 index 0000000..f363f2f --- /dev/null +++ b/mxm_wifiex/wlan_src/mlinux/moal_wext.h @@ -0,0 +1,58 @@ +/** @file moal_wext.h + * + * @brief This file contains definition for wireless extension IOCTL call. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +/******************************************************** +Change log: + 10/21/2008: initial version +********************************************************/ + +#ifndef _WOAL_WEXT_H_ +#define _WOAL_WEXT_H_ + +/** NF value for default scan */ +#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +/** Add event */ +#define IWE_STREAM_ADD_EVENT(i, c, e, w, l) \ + iwe_stream_add_event((i), (c), (e), (w), (l)) +/** Add point */ +#define IWE_STREAM_ADD_POINT(i, c, e, w, p) \ + iwe_stream_add_point((i), (c), (e), (w), (p)) +/** Add value */ +#define IWE_STREAM_ADD_VALUE(i, c, v, e, w, l) \ + iwe_stream_add_value((i), (c), (v), (e), (w), (l)) +#else +/** Add event */ +#define IWE_STREAM_ADD_EVENT(i, c, e, w, l) \ + iwe_stream_add_event((c), (e), (w), (l)) +/** Add point */ +#define IWE_STREAM_ADD_POINT(i, c, e, w, p) \ + iwe_stream_add_point((c), (e), (w), (p)) +/** Add value */ +#define IWE_STREAM_ADD_VALUE(i, c, v, e, w, l) \ + iwe_stream_add_value((c), (v), (e), (w), (l)) +#endif + +extern struct iw_handler_def woal_handler_def; +struct iw_statistics *woal_get_wireless_stats(struct net_device *dev); +#endif /* _WOAL_WEXT_H_ */