From: Kalle Valo Date: Tue, 17 Nov 2015 19:14:51 +0000 (+0200) Subject: mwifiex: move under marvell vendor directory X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=277b024e5e3d4af4c219c0b9bd541ca4398e0b69;p=GitHub%2Fmoto-9609%2Fandroid_kernel_motorola_exynos9610.git mwifiex: move under marvell vendor directory Part of reorganising wireless drivers directory and Kconfig. Signed-off-by: Kalle Valo --- diff --git a/MAINTAINERS b/MAINTAINERS index 75ff7434db0e..5b889f6972c0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6728,7 +6728,7 @@ M: Amitkumar Karwar M: Nishant Sarmukadam L: linux-wireless@vger.kernel.org S: Maintained -F: drivers/net/wireless/mwifiex/ +F: drivers/net/wireless/marvell/mwifiex/ MARVELL MWL8K WIRELESS DRIVER M: Lennert Buytenhek diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index 25f52b32d725..465665237308 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -152,7 +152,6 @@ source "drivers/net/wireless/realtek/rtlwifi/Kconfig" source "drivers/net/wireless/realtek/rtl8xxxu/Kconfig" source "drivers/net/wireless/ti/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" -source "drivers/net/wireless/mwifiex/Kconfig" source "drivers/net/wireless/rsi/Kconfig" endif # WLAN diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 72b167742ac7..7a95b58a5c76 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -40,6 +40,4 @@ obj-$(CONFIG_MAC80211_HWSIM) += mac80211_hwsim.o obj-$(CONFIG_WL_TI) += ti/ -obj-$(CONFIG_MWIFIEX) += mwifiex/ - obj-$(CONFIG_RSI_91X) += rsi/ diff --git a/drivers/net/wireless/marvell/Kconfig b/drivers/net/wireless/marvell/Kconfig index 97ec8f35745e..bbdbac9a2a45 100644 --- a/drivers/net/wireless/marvell/Kconfig +++ b/drivers/net/wireless/marvell/Kconfig @@ -13,5 +13,6 @@ if WLAN_VENDOR_MARVELL source "drivers/net/wireless/marvell/libertas/Kconfig" source "drivers/net/wireless/marvell/libertas_tf/Kconfig" +source "drivers/net/wireless/marvell/mwifiex/Kconfig" endif # WLAN_VENDOR_MARVELL diff --git a/drivers/net/wireless/marvell/Makefile b/drivers/net/wireless/marvell/Makefile index 8f5eb423b71a..f4ab48aaff3c 100644 --- a/drivers/net/wireless/marvell/Makefile +++ b/drivers/net/wireless/marvell/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_LIBERTAS) += libertas/ obj-$(CONFIG_LIBERTAS_THINFIRM) += libertas_tf/ +obj-$(CONFIG_MWIFIEX) += mwifiex/ diff --git a/drivers/net/wireless/marvell/mwifiex/11ac.c b/drivers/net/wireless/marvell/mwifiex/11ac.c new file mode 100644 index 000000000000..59d23fb2365f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11ac.c @@ -0,0 +1,382 @@ +/* + * Marvell Wireless LAN device driver: 802.11ac + * + * Copyright (C) 2013-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "fw.h" +#include "main.h" +#include "11ac.h" + +/* Tables of the MCS map to the highest data rate (in Mbps) supported + * for long GI. + */ +static const 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 */ +}; + +static const 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 */ +}; + +/* This function converts the 2-bit MCS map to the highest long GI + * VHT data rate. + */ +static u16 +mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, + u8 bands, u16 mcs_map) +{ + u8 i, nss, mcs; + u16 max_rate = 0; + u32 usr_vht_cap_info = 0; + struct mwifiex_adapter *adapter = priv->adapter; + + if (bands & BAND_AAC) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* find the max NSS supported */ + nss = 1; + for (i = 1; i <= 8; i++) { + mcs = GET_VHTNSSMCS(mcs_map, i); + if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED) + nss = i; + } + mcs = GET_VHTNSSMCS(mcs_map, nss); + + /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */ + if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) + mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; + + if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) { + /* support 160 MHz */ + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs]; + if (!max_rate) + /* MCS9 is not supported in NSS6 */ + max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1]; + } else { + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs]; + if (!max_rate) + /* MCS9 is not supported in NSS3 */ + max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1]; + } + + return max_rate; +} + +static void +mwifiex_fill_vht_cap_info(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (bands & BAND_A) + vht_cap->vht_cap_info = + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a); + else + vht_cap->vht_cap_info = + cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); +} + +void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u16 mcs_map_user, mcs_map_resp, mcs_map_result; + u16 mcs_user, mcs_resp, nss, tmp; + + /* Fill VHT cap info */ + mwifiex_fill_vht_cap_info(priv, vht_cap, bands); + + /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */ + mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_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 ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min(mcs_user, mcs_resp)); + } + + vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); + + tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp); + + /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */ + mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.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 ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min(mcs_user, mcs_resp)); + } + + vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); + + tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); + vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp); + + return; +} + +int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 **buffer) +{ + struct mwifiex_ie_types_vhtcap *vht_cap; + struct mwifiex_ie_types_oper_mode_ntf *oper_ntf; + struct ieee_types_oper_mode_ntf *ieee_oper_ntf; + struct mwifiex_ie_types_vht_oper *vht_op; + struct mwifiex_adapter *adapter = priv->adapter; + u8 supp_chwd_set; + u32 usr_vht_cap_info; + int ret_len = 0; + + if (bss_desc->bss_band & BAND_A) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* VHT Capabilities IE */ + if (bss_desc->bcn_vht_cap) { + vht_cap = (struct mwifiex_ie_types_vhtcap *)*buffer; + memset(vht_cap, 0, sizeof(*vht_cap)); + vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + vht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_vht_cap)); + memcpy((u8 *)vht_cap + sizeof(struct mwifiex_ie_types_header), + (u8 *)bss_desc->bcn_vht_cap, + le16_to_cpu(vht_cap->header.len)); + + mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap, + bss_desc->bss_band); + *buffer += sizeof(*vht_cap); + ret_len += sizeof(*vht_cap); + } + + /* VHT Operation IE */ + if (bss_desc->bcn_vht_oper) { + if (priv->bss_mode == NL80211_IFTYPE_STATION) { + vht_op = (struct mwifiex_ie_types_vht_oper *)*buffer; + memset(vht_op, 0, sizeof(*vht_op)); + vht_op->header.type = + cpu_to_le16(WLAN_EID_VHT_OPERATION); + vht_op->header.len = cpu_to_le16(sizeof(*vht_op) - + sizeof(struct mwifiex_ie_types_header)); + memcpy((u8 *)vht_op + + sizeof(struct mwifiex_ie_types_header), + (u8 *)bss_desc->bcn_vht_oper, + le16_to_cpu(vht_op->header.len)); + + /* negotiate the channel width and central freq + * and keep the central freq as the peer suggests + */ + supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); + + switch (supp_chwd_set) { + case 0: + vht_op->chan_width = + min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + case 1: + vht_op->chan_width = + min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + case 2: + vht_op->chan_width = + min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ, + bss_desc->bcn_vht_oper->chan_width); + break; + default: + vht_op->chan_width = + IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + *buffer += sizeof(*vht_op); + ret_len += sizeof(*vht_op); + } + } + + /* Operating Mode Notification IE */ + if (bss_desc->oper_mode) { + ieee_oper_ntf = bss_desc->oper_mode; + oper_ntf = (void *)*buffer; + memset(oper_ntf, 0, sizeof(*oper_ntf)); + oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF); + oper_ntf->header.len = cpu_to_le16(sizeof(u8)); + oper_ntf->oper_mode = ieee_oper_ntf->oper_mode; + *buffer += sizeof(*oper_ntf); + ret_len += sizeof(*oper_ntf); + } + + return ret_len; +} + +int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct mwifiex_11ac_vht_cfg *cfg) +{ + struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_11AC_CFG); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) + + S_DS_GEN); + vhtcfg->action = cpu_to_le16(cmd_action); + vhtcfg->band_config = cfg->band_config; + vhtcfg->misc_config = cfg->misc_config; + vhtcfg->cap_info = cpu_to_le32(cfg->cap_info); + vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set); + vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set); + + return 0; +} + +/* This function initializes the BlockACK setup information for given + * mwifiex_private structure for 11ac enabled networks. + */ +void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv) +{ + priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + priv->add_ba_param.tx_win_size = + MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE; + } else { + priv->add_ba_param.tx_win_size = + MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; + } + + return; +} + +bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv) +{ + struct mwifiex_bssdescriptor *bss_desc; + struct ieee80211_vht_operation *vht_oper; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + vht_oper = bss_desc->bcn_vht_oper; + + if (!bss_desc->bcn_vht_cap || !vht_oper) + return false; + + if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT) + return false; + + return true; +} + +u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, + u32 pri_chan, u8 chan_bw) +{ + u8 center_freq_idx = 0; + + if (band & BAND_AAC) { + switch (pri_chan) { + case 36: + case 40: + case 44: + case 48: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 42; + break; + case 52: + case 56: + case 60: + case 64: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 58; + else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) + center_freq_idx = 50; + break; + case 100: + case 104: + case 108: + case 112: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 106; + break; + case 116: + case 120: + case 124: + case 128: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 122; + else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) + center_freq_idx = 114; + break; + case 132: + case 136: + case 140: + case 144: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 138; + break; + case 149: + case 153: + case 157: + case 161: + if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) + center_freq_idx = 155; + break; + default: + center_freq_idx = 42; + } + } + + return center_freq_idx; +} diff --git a/drivers/net/wireless/marvell/mwifiex/11ac.h b/drivers/net/wireless/marvell/mwifiex/11ac.h new file mode 100644 index 000000000000..1ca92c7a8a4a --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11ac.h @@ -0,0 +1,45 @@ +/* + * Marvell Wireless LAN device driver: 802.11ac + * + * Copyright (C) 2013-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_11AC_H_ +#define _MWIFIEX_11AC_H_ + +#define VHT_CFG_2GHZ BIT(0) +#define VHT_CFG_5GHZ BIT(1) + +enum vht_cfg_misc_config { + VHT_CAP_TX_OPERATION = 1, + VHT_CAP_ASSOCIATION, + VHT_CAP_UAP_ONLY +}; + +#define DEFAULT_VHT_MCS_SET 0xfffa +#define DISABLE_VHT_MCS_SET 0xffff + +#define VHT_BW_80_160_80P80 BIT(2) + +int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 **buffer); +int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct mwifiex_11ac_vht_cfg *cfg); +void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, + struct ieee80211_vht_cap *vht_cap, u8 bands); +#endif /* _MWIFIEX_11AC_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c new file mode 100644 index 000000000000..71a1b580796f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11h.c @@ -0,0 +1,319 @@ +/* + * Marvell Wireless LAN device driver: 802.11h + * + * Copyright (C) 2013-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "main.h" +#include "fw.h" + + +void mwifiex_init_11h_params(struct mwifiex_private *priv) +{ + priv->state_11h.is_11h_enabled = true; + priv->state_11h.is_11h_active = false; +} + +inline int mwifiex_is_11h_active(struct mwifiex_private *priv) +{ + return priv->state_11h.is_11h_active; +} +/* This function appends 11h info to a buffer while joining an + * infrastructure BSS + */ +static void +mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc) +{ + struct mwifiex_ie_types_header *ie_header; + struct mwifiex_ie_types_pwr_capability *cap; + struct mwifiex_ie_types_local_pwr_constraint *constraint; + struct ieee80211_supported_band *sband; + u8 radio_type; + int i; + + if (!buffer || !(*buffer)) + return; + + radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + sband = priv->wdev.wiphy->bands[radio_type]; + + cap = (struct mwifiex_ie_types_pwr_capability *)*buffer; + cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY); + cap->header.len = cpu_to_le16(2); + cap->min_pwr = 0; + cap->max_pwr = 0; + *buffer += sizeof(*cap); + + constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer; + constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT); + constraint->header.len = cpu_to_le16(2); + constraint->chan = bss_desc->channel; + constraint->constraint = bss_desc->local_constraint; + *buffer += sizeof(*constraint); + + ie_header = (struct mwifiex_ie_types_header *)*buffer; + ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header->len = cpu_to_le16(2 * sband->n_channels + 2); + *buffer += sizeof(*ie_header); + *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS; + *(*buffer)++ = 2 * sband->n_channels; + for (i = 0; i < sband->n_channels; i++) { + *(*buffer)++ = ieee80211_frequency_to_channel( + sband->channels[i].center_freq); + *(*buffer)++ = 1; /* one channel in the subband */ + } +} + +/* Enable or disable the 11h extensions in the firmware */ +int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag) +{ + u32 enable = flag; + + /* enable master mode radar detection on AP interface */ + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && enable) + enable |= MWIFIEX_MASTER_RADAR_DET_MASK; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true); +} + +/* This functions processes TLV buffer for a pending BSS Join command. + * + * Activate 11h functionality in the firmware if the spectrum management + * capability bit is found in the network we are joining. Also, necessary + * TLVs are set based on requested network's 11h capability. + */ +void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (bss_desc->sensed_11h) { + /* Activate 11h functions in firmware, turns on capability + * bit + */ + mwifiex_11h_activate(priv, true); + priv->state_11h.is_11h_active = true; + bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT; + mwifiex_11h_process_infra_join(priv, buffer, bss_desc); + } else { + /* Deactivate 11h functions in the firmware */ + mwifiex_11h_activate(priv, false); + priv->state_11h.is_11h_active = false; + bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT; + } +} + +/* This is DFS CAC work queue function. + * This delayed work emits CAC finished event for cfg80211 if + * CAC was started earlier. + */ +void mwifiex_dfs_cac_work_queue(struct work_struct *work) +{ + struct cfg80211_chan_def chandef; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct mwifiex_private *priv = + container_of(delayed_work, struct mwifiex_private, + dfs_cac_work); + + if (WARN_ON(!priv)) + return; + + chandef = priv->dfs_chandef; + if (priv->wdev.cac_started) { + mwifiex_dbg(priv->adapter, MSG, + "CAC timer finished; No radar detected\n"); + cfg80211_cac_event(priv->netdev, &chandef, + NL80211_RADAR_CAC_FINISHED, + GFP_KERNEL); + } +} + +/* This function prepares channel report request command to FW for + * starting radar detection. + */ +int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req; + struct mwifiex_radar_params *radar_params = (void *)data_buf; + + cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST); + cmd->size = cpu_to_le16(S_DS_GEN); + le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_chan_rpt_req)); + + cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ); + cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value; + cr_req->chan_desc.chan_width = radar_params->chandef->width; + cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms); + + if (radar_params->cac_time_ms) + mwifiex_dbg(priv->adapter, MSG, + "11h: issuing DFS Radar check for channel=%d\n", + radar_params->chandef->chan->hw_value); + else + mwifiex_dbg(priv->adapter, MSG, "cancelling CAC\n"); + + return 0; +} + +int mwifiex_stop_radar_detection(struct mwifiex_private *priv, + struct cfg80211_chan_def *chandef) +{ + struct mwifiex_radar_params radar_params; + + memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); + radar_params.chandef = chandef; + radar_params.cac_time_ms = 0; + + return mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, &radar_params, true); +} + +/* This function is to abort ongoing CAC upon stopping AP operations + * or during unload. + */ +void mwifiex_abort_cac(struct mwifiex_private *priv) +{ + if (priv->wdev.cac_started) { + if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) + mwifiex_dbg(priv->adapter, ERROR, + "failed to stop CAC in FW\n"); + mwifiex_dbg(priv->adapter, MSG, + "Aborting delayed work for CAC.\n"); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, + NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); + } +} + +/* This function handles channel report event from FW during CAC period. + * If radar is detected during CAC, driver indicates the same to cfg80211 + * and also cancels ongoing delayed work. + */ +int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct host_cmd_ds_chan_rpt_event *rpt_event; + struct mwifiex_ie_types_chan_rpt_data *rpt; + u8 *evt_buf; + u16 event_len, tlv_len; + + rpt_event = (void *)(skb->data + sizeof(u32)); + event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event)+ + sizeof(u32)); + + if (le32_to_cpu(rpt_event->result) != HostCmd_RESULT_OK) { + mwifiex_dbg(priv->adapter, ERROR, + "Error in channel report event\n"); + return -1; + } + + evt_buf = (void *)&rpt_event->tlvbuf; + + while (event_len >= sizeof(struct mwifiex_ie_types_header)) { + rpt = (void *)&rpt_event->tlvbuf; + tlv_len = le16_to_cpu(rpt->header.len); + + switch (le16_to_cpu(rpt->header.type)) { + case TLV_TYPE_CHANRPT_11H_BASIC: + if (rpt->map.radar) { + mwifiex_dbg(priv->adapter, MSG, + "RADAR Detected on channel %d!\n", + priv->dfs_chandef.chan->hw_value); + cancel_delayed_work_sync(&priv->dfs_cac_work); + cfg80211_cac_event(priv->netdev, + &priv->dfs_chandef, + NL80211_RADAR_DETECTED, + GFP_KERNEL); + } + break; + default: + break; + } + + evt_buf += (tlv_len + sizeof(rpt->header)); + event_len -= (tlv_len + sizeof(rpt->header)); + } + + return 0; +} + +/* Handler for radar detected event from FW.*/ +int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_radar_det_event *rdr_event; + + rdr_event = (void *)(skb->data + sizeof(u32)); + + if (le32_to_cpu(rdr_event->passed)) { + mwifiex_dbg(priv->adapter, MSG, + "radar detected; indicating kernel\n"); + if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) + mwifiex_dbg(priv->adapter, ERROR, + "Failed to stop CAC in FW\n"); + cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef, + GFP_KERNEL); + mwifiex_dbg(priv->adapter, MSG, "regdomain: %d\n", + rdr_event->reg_domain); + mwifiex_dbg(priv->adapter, MSG, "radar detection type: %d\n", + rdr_event->det_type); + } else { + mwifiex_dbg(priv->adapter, MSG, + "false radar detection event!\n"); + } + + return 0; +} + +/* This is work queue function for channel switch handling. + * This function takes care of updating new channel definitin to + * bss config structure, restart AP and indicate channel switch success + * to cfg80211. + */ +void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) +{ + struct mwifiex_uap_bss_param *bss_cfg; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct mwifiex_private *priv = + container_of(delayed_work, struct mwifiex_private, + dfs_chan_sw_work); + + if (WARN_ON(!priv)) + return; + + bss_cfg = &priv->bss_cfg; + if (!bss_cfg->beacon_period) { + mwifiex_dbg(priv->adapter, ERROR, + "channel switch: AP already stopped\n"); + return; + } + + mwifiex_uap_set_channel(priv, bss_cfg, priv->dfs_chandef); + + if (mwifiex_config_start_uap(priv, bss_cfg)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to start AP after channel switch\n"); + return; + } + + mwifiex_dbg(priv->adapter, MSG, + "indicating channel switch completion to kernel\n"); + cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef); +} diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c new file mode 100644 index 000000000000..c174e79e6df2 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n.c @@ -0,0 +1,914 @@ +/* + * Marvell Wireless LAN device driver: 802.11n + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + +/* + * Fills HT capability information field, AMPDU Parameters field, HT extended + * capability field, and supported MCS set fields. + * + * HT capability information field, AMPDU Parameters field, supported MCS set + * fields are retrieved from cfg80211 stack + * + * RD responder bit to set to clear in the extended capability header. + */ +int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, + struct ieee80211_ht_cap *ht_cap) +{ + uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info); + struct ieee80211_supported_band *sband = + priv->wdev.wiphy->bands[radio_type]; + + if (WARN_ON_ONCE(!sband)) { + mwifiex_dbg(priv->adapter, ERROR, "Invalid radio type!\n"); + return -EINVAL; + } + + ht_cap->ampdu_params_info = + (sband->ht_cap.ampdu_factor & + IEEE80211_HT_AMPDU_PARM_FACTOR) | + ((sband->ht_cap.ampdu_density << + IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) & + IEEE80211_HT_AMPDU_PARM_DENSITY); + + memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs, + sizeof(sband->ht_cap.mcs)); + + if (priv->bss_mode == NL80211_IFTYPE_STATION || + (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + (priv->adapter->sec_chan_offset != + IEEE80211_HT_PARAM_CHA_SEC_NONE))) + /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ + SETHT_MCS32(ht_cap->mcs.rx_mask); + + /* Clear RD responder bit */ + ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; + + ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap); + ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap); + + if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) + ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP); + + return 0; +} + +/* + * This function returns the pointer to an entry in BA Stream + * table which matches the requested BA status. + */ +static struct mwifiex_tx_ba_stream_tbl * +mwifiex_get_ba_status(struct mwifiex_private *priv, + enum mwifiex_ba_status ba_status) +{ + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (tx_ba_tsr_tbl->ba_status == ba_status) { + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, + flags); + return tx_ba_tsr_tbl; + } + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + return NULL; +} + +/* + * This function handles the command response of delete a block + * ack request. + * + * The function checks the response success status and takes action + * accordingly (send an add BA request in case of success, or recreate + * the deleted stream in case of failure, if the add BA was also + * initiated by us). + */ +int mwifiex_ret_11n_delba(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + int tid; + struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; + struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba; + uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set); + + tid = del_ba_param_set >> DELBA_TID_POS; + if (del_ba->del_result == BA_RESULT_SUCCESS) { + mwifiex_del_ba_tbl(priv, tid, del_ba->peer_mac_addr, + TYPE_DELBA_SENT, + INITIATOR_BIT(del_ba_param_set)); + + tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); + if (tx_ba_tbl) + mwifiex_send_addba(priv, tx_ba_tbl->tid, + tx_ba_tbl->ra); + } else { /* + * In case of failure, recreate the deleted stream in case + * we initiated the ADDBA + */ + if (!INITIATOR_BIT(del_ba_param_set)) + return 0; + + mwifiex_create_ba_tbl(priv, del_ba->peer_mac_addr, tid, + BA_SETUP_INPROGRESS); + + tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); + + if (tx_ba_tbl) + mwifiex_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra, + TYPE_DELBA_SENT, true); + } + + return 0; +} + +/* + * This function handles the command response of add a block + * ack request. + * + * Handling includes changing the header fields to CPU formats, checking + * the response success status and taking actions accordingly (delete the + * BA stream table in case of failure). + */ +int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + int tid, tid_down; + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; + struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; + struct mwifiex_ra_list_tbl *ra_list; + u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); + + add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) + & SSN_MASK); + + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, add_ba_rsp-> + peer_mac_addr); + if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { + if (ra_list) { + ra_list->ba_status = BA_SETUP_NONE; + ra_list->amsdu_in_ampdu = false; + } + mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr, + TYPE_DELBA_SENT, true); + if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) + priv->aggr_prio_tbl[tid].ampdu_ap = + BA_STREAM_NOT_ALLOWED; + return 0; + } + + tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr); + if (tx_ba_tbl) { + mwifiex_dbg(priv->adapter, EVENT, "info: BA stream complete\n"); + tx_ba_tbl->ba_status = BA_SETUP_COMPLETE; + if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.tx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + tx_ba_tbl->amsdu = true; + else + tx_ba_tbl->amsdu = false; + if (ra_list) { + ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu; + ra_list->ba_status = BA_SETUP_COMPLETE; + } + } else { + mwifiex_dbg(priv->adapter, ERROR, "BA stream not created\n"); + } + + return 0; +} + +/* + * This function prepares command of reconfigure Tx buffer. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting Tx buffer size (for SET only) + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, int cmd_action, + u16 *buf_size) +{ + struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf; + u16 action = (u16) cmd_action; + + cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN); + tx_buf->action = cpu_to_le16(action); + switch (action) { + case HostCmd_ACT_GEN_SET: + mwifiex_dbg(priv->adapter, CMD, + "cmd: set tx_buf=%d\n", *buf_size); + tx_buf->buff_size = cpu_to_le16(*buf_size); + break; + case HostCmd_ACT_GEN_GET: + default: + tx_buf->buff_size = 0; + break; + } + return 0; +} + +/* + * This function prepares command of AMSDU aggregation control. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting AMSDU control parameters (for SET only) + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, + int cmd_action, + struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl) +{ + struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = + &cmd->params.amsdu_aggr_ctrl; + u16 action = (u16) cmd_action; + + cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl) + + S_DS_GEN); + amsdu_ctrl->action = cpu_to_le16(action); + switch (action) { + case HostCmd_ACT_GEN_SET: + amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable); + amsdu_ctrl->curr_buf_size = 0; + break; + case HostCmd_ACT_GEN_GET: + default: + amsdu_ctrl->curr_buf_size = 0; + break; + } + return 0; +} + +/* + * This function prepares 11n configuration command. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting HT Tx capability and HT Tx information fields + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct mwifiex_ds_11n_tx_cfg *txcfg) +{ + struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN); + htcfg->action = cpu_to_le16(cmd_action); + htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap); + htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo); + + if (priv->adapter->is_hw_11ac_capable) + htcfg->misc_config = cpu_to_le16(txcfg->misc_config); + + return 0; +} + +/* + * This function appends an 11n TLV to a buffer. + * + * Buffer allocation is responsibility of the calling + * function. No size validation is made here. + * + * The function fills up the following sections, if applicable - + * - HT capability IE + * - HT information IE (with channel list) + * - 20/40 BSS Coexistence IE + * - HT Extended Capabilities IE + */ +int +mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 **buffer) +{ + struct mwifiex_ie_types_htcap *ht_cap; + struct mwifiex_ie_types_htinfo *ht_info; + struct mwifiex_ie_types_chan_list_param_set *chan_list; + struct mwifiex_ie_types_2040bssco *bss_co_2040; + struct mwifiex_ie_types_extcap *ext_cap; + int ret_len = 0; + struct ieee80211_supported_band *sband; + struct ieee_types_header *hdr; + u8 radio_type; + + if (!buffer || !*buffer) + return ret_len; + + radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + sband = priv->wdev.wiphy->bands[radio_type]; + + if (bss_desc->bcn_ht_cap) { + ht_cap = (struct mwifiex_ie_types_htcap *) *buffer; + memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); + ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header), + (u8 *)bss_desc->bcn_ht_cap, + le16_to_cpu(ht_cap->header.len)); + + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); + + *buffer += sizeof(struct mwifiex_ie_types_htcap); + ret_len += sizeof(struct mwifiex_ie_types_htcap); + } + + if (bss_desc->bcn_ht_oper) { + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + ht_info = (struct mwifiex_ie_types_htinfo *) *buffer; + memset(ht_info, 0, + sizeof(struct mwifiex_ie_types_htinfo)); + ht_info->header.type = + cpu_to_le16(WLAN_EID_HT_OPERATION); + ht_info->header.len = + cpu_to_le16( + sizeof(struct ieee80211_ht_operation)); + + memcpy((u8 *) ht_info + + sizeof(struct mwifiex_ie_types_header), + (u8 *)bss_desc->bcn_ht_oper, + le16_to_cpu(ht_info->header.len)); + + if (!(sband->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40)) + ht_info->ht_oper.ht_param &= + ~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY | + IEEE80211_HT_PARAM_CHA_SEC_OFFSET); + + *buffer += sizeof(struct mwifiex_ie_types_htinfo); + ret_len += sizeof(struct mwifiex_ie_types_htinfo); + } + + chan_list = + (struct mwifiex_ie_types_chan_list_param_set *) *buffer; + memset(chan_list, 0, + sizeof(struct mwifiex_ie_types_chan_list_param_set)); + chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_list->header.len = cpu_to_le16( + sizeof(struct mwifiex_ie_types_chan_list_param_set) - + sizeof(struct mwifiex_ie_types_header)); + chan_list->chan_scan_param[0].chan_number = + bss_desc->bcn_ht_oper->primary_chan; + chan_list->chan_scan_param[0].radio_type = + mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + + if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && + bss_desc->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) + SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. + radio_type, + (bss_desc->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); + + *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set); + ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set); + } + + if (bss_desc->bcn_bss_co_2040) { + bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer; + memset(bss_co_2040, 0, + sizeof(struct mwifiex_ie_types_2040bssco)); + bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040); + bss_co_2040->header.len = + cpu_to_le16(sizeof(bss_co_2040->bss_co_2040)); + + memcpy((u8 *) bss_co_2040 + + sizeof(struct mwifiex_ie_types_header), + bss_desc->bcn_bss_co_2040 + + sizeof(struct ieee_types_header), + le16_to_cpu(bss_co_2040->header.len)); + + *buffer += sizeof(struct mwifiex_ie_types_2040bssco); + ret_len += sizeof(struct mwifiex_ie_types_2040bssco); + } + + if (bss_desc->bcn_ext_cap) { + hdr = (void *)bss_desc->bcn_ext_cap; + ext_cap = (struct mwifiex_ie_types_extcap *) *buffer; + memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap)); + ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); + ext_cap->header.len = cpu_to_le16(hdr->len); + + memcpy((u8 *)ext_cap->ext_capab, + bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header), + le16_to_cpu(ext_cap->header.len)); + + if (hdr->len > 3 && + ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED) + priv->hs2_enabled = true; + else + priv->hs2_enabled = false; + + *buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; + ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; + } + + return ret_len; +} + +/* + * This function checks if the given pointer is valid entry of + * Tx BA Stream table. + */ +static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv, + struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr) +{ + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; + + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (tx_ba_tsr_tbl == tx_tbl_ptr) + return true; + } + + return false; +} + +/* + * This function deletes the given entry in Tx BA Stream table. + * + * The function also performs a validity check on the supplied + * pointer before trying to delete. + */ +void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl) +{ + if (!tx_ba_tsr_tbl && + mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl)) + return; + + mwifiex_dbg(priv->adapter, INFO, + "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl); + + list_del(&tx_ba_tsr_tbl->list); + + kfree(tx_ba_tsr_tbl); +} + +/* + * This function deletes all the entries in Tx BA Stream table. + */ +void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv) +{ + int i; + struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry_safe(del_tbl_ptr, tmp_node, + &priv->tx_ba_stream_tbl_ptr, list) + mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + + INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); + + for (i = 0; i < MAX_NUM_TID; ++i) + priv->aggr_prio_tbl[i].ampdu_ap = + priv->aggr_prio_tbl[i].ampdu_user; +} + +/* + * This function returns the pointer to an entry in BA Stream + * table which matches the given RA/TID pair. + */ +struct mwifiex_tx_ba_stream_tbl * +mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra) +{ + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) && + tx_ba_tsr_tbl->tid == tid) { + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, + flags); + return tx_ba_tsr_tbl; + } + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + return NULL; +} + +/* + * This function creates an entry in Tx BA stream table for the + * given RA/TID pair. + */ +void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, + enum mwifiex_ba_status ba_status) +{ + struct mwifiex_tx_ba_stream_tbl *new_node; + struct mwifiex_ra_list_tbl *ra_list; + unsigned long flags; + int tid_down; + + if (!mwifiex_get_ba_tbl(priv, tid, ra)) { + new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl), + GFP_ATOMIC); + if (!new_node) + return; + + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, ra); + if (ra_list) { + ra_list->ba_status = ba_status; + ra_list->amsdu_in_ampdu = false; + } + INIT_LIST_HEAD(&new_node->list); + + new_node->tid = tid; + new_node->ba_status = ba_status; + memcpy(new_node->ra, ra, ETH_ALEN); + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr); + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + } +} + +/* + * This function sends an add BA request to the given TID/RA pair. + */ +int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) +{ + struct host_cmd_ds_11n_addba_req add_ba_req; + u32 tx_win_size = priv->add_ba_param.tx_win_size; + static u8 dialog_tok; + int ret; + unsigned long flags; + u16 block_ack_param_set; + + mwifiex_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid); + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->is_hw_11ac_capable && + memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) { + struct mwifiex_sta_node *sta_ptr; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, peer_mac); + if (!sta_ptr) { + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + mwifiex_dbg(priv->adapter, ERROR, + "BA setup with unknown TDLS peer %pM!\n", + peer_mac); + return -1; + } + if (sta_ptr->is_11ac_enabled) + tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + } + + block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) | + 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)) + block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK; + + add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set); + add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); + + ++dialog_tok; + + if (dialog_tok == 0) + dialog_tok = 1; + + add_ba_req.dialog_token = dialog_tok; + memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); + + /* We don't wait for the response of this command */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, + 0, 0, &add_ba_req, false); + + return ret; +} + +/* + * This function sends a delete BA request to the given TID/RA pair. + */ +int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, + int initiator) +{ + struct host_cmd_ds_11n_delba delba; + int ret; + uint16_t del_ba_param_set; + + memset(&delba, 0, sizeof(delba)); + delba.del_ba_param_set = cpu_to_le16(tid << DELBA_TID_POS); + + del_ba_param_set = le16_to_cpu(delba.del_ba_param_set); + if (initiator) + del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK; + else + del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK; + + memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN); + + /* We don't wait for the response of this command */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, + HostCmd_ACT_GEN_SET, 0, &delba, false); + + return ret; +} + +/* + * This function sends delba to specific tid + */ +void mwifiex_11n_delba(struct mwifiex_private *priv, int tid) +{ + struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; + + if (list_empty(&priv->rx_reorder_tbl_ptr)) { + dev_dbg(priv->adapter->dev, + "mwifiex_11n_delba: rx_reorder_tbl_ptr empty\n"); + return; + } + + list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { + if (rx_reor_tbl_ptr->tid == tid) { + dev_dbg(priv->adapter->dev, + "Send delba to tid=%d, %pM\n", + tid, rx_reor_tbl_ptr->ta); + mwifiex_send_delba(priv, tid, rx_reor_tbl_ptr->ta, 0); + return; + } + } +} + +/* + * This function handles the command response of a delete BA request. + */ +void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba) +{ + struct host_cmd_ds_11n_delba *cmd_del_ba = + (struct host_cmd_ds_11n_delba *) del_ba; + uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set); + int tid; + + tid = del_ba_param_set >> DELBA_TID_POS; + + mwifiex_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr, + TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set)); +} + +/* + * This function retrieves the Rx reordering table. + */ +int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, + struct mwifiex_ds_rx_reorder_tbl *buf) +{ + int i; + struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf; + struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr; + int count = 0; + unsigned long flags; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr, + list) { + rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid; + memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN); + rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win; + rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size; + for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) + rx_reo_tbl->buffer[i] = true; + else + rx_reo_tbl->buffer[i] = false; + } + rx_reo_tbl++; + count++; + + if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED) + break; + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + return count; +} + +/* + * This function retrieves the Tx BA stream table. + */ +int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, + struct mwifiex_ds_tx_ba_stream_tbl *buf) +{ + struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; + struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf; + int count = 0; + unsigned long flags; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid; + mwifiex_dbg(priv->adapter, DATA, "data: %s tid=%d\n", + __func__, rx_reo_tbl->tid); + memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); + rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu; + rx_reo_tbl++; + count++; + if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) + break; + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + + return count; +} + +/* + * This function retrieves the entry for specific tx BA stream table by RA and + * deletes it. + */ +void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra) +{ + struct mwifiex_tx_ba_stream_tbl *tbl, *tmp; + unsigned long flags; + + if (!ra) + return; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) { + if (!memcmp(tbl->ra, ra, ETH_ALEN)) { + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, + flags); + mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl); + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + } + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + + return; +} + +/* This function initializes the BlockACK setup information for given + * mwifiex_private structure. + */ +void mwifiex_set_ba_params(struct mwifiex_private *priv) +{ + priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + priv->add_ba_param.tx_win_size = + MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; + } else { + priv->add_ba_param.tx_win_size = + MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; + } + + priv->add_ba_param.tx_amsdu = true; + priv->add_ba_param.rx_amsdu = true; + + return; +} + +u8 mwifiex_get_sec_chan_offset(int chan) +{ + u8 sec_offset; + + 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: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_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: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; + break; + case 165: + default: + sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; + break; + } + + return sec_offset; +} + +/* This function will send DELBA to entries in the priv's + * Tx BA stream table + */ +static void +mwifiex_send_delba_txbastream_tbl(struct mwifiex_private *priv, u8 tid) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr; + + if (list_empty(&priv->tx_ba_stream_tbl_ptr)) + return; + + list_for_each_entry(tx_ba_stream_tbl_ptr, + &priv->tx_ba_stream_tbl_ptr, list) { + if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) { + if (tid == tx_ba_stream_tbl_ptr->tid) { + dev_dbg(adapter->dev, + "Tx:Send delba to tid=%d, %pM\n", tid, + tx_ba_stream_tbl_ptr->ra); + mwifiex_send_delba(priv, + tx_ba_stream_tbl_ptr->tid, + tx_ba_stream_tbl_ptr->ra, 1); + return; + } + } + } +} + +/* This function updates all the tx_win_size + */ +void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *adapter) +{ + u8 i; + u32 tx_win_size; + struct mwifiex_private *priv; + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]) + continue; + priv = adapter->priv[i]; + tx_win_size = priv->add_ba_param.tx_win_size; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) + priv->add_ba_param.tx_win_size = + MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) + priv->add_ba_param.tx_win_size = + MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) + priv->add_ba_param.tx_win_size = + MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE; + + if (adapter->coex_win_size) { + if (adapter->coex_tx_win_size) + priv->add_ba_param.tx_win_size = + adapter->coex_tx_win_size; + } + + if (tx_win_size != priv->add_ba_param.tx_win_size) { + if (!priv->media_connected) + continue; + for (i = 0; i < MAX_NUM_TID; i++) + mwifiex_send_delba_txbastream_tbl(priv, i); + } + } +} diff --git a/drivers/net/wireless/marvell/mwifiex/11n.h b/drivers/net/wireless/marvell/mwifiex/11n.h new file mode 100644 index 000000000000..afdd58aa90de --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n.h @@ -0,0 +1,191 @@ +/* + * Marvell Wireless LAN device driver: 802.11n + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_11N_H_ +#define _MWIFIEX_11N_H_ + +#include "11n_aggr.h" +#include "11n_rxreorder.h" +#include "wmm.h" + +int mwifiex_ret_11n_delba(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action, + struct mwifiex_ds_11n_tx_cfg *txcfg); +int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 **buffer); +int mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, + struct ieee80211_ht_cap *); +int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv, + u16 action, int *htcap_cfg); +void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, + struct mwifiex_tx_ba_stream_tbl + *tx_tbl); +void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv); +struct mwifiex_tx_ba_stream_tbl *mwifiex_get_ba_tbl(struct + mwifiex_private + *priv, int tid, + u8 *ra); +void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, + enum mwifiex_ba_status ba_status); +int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac); +int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, + int initiator); +void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba); +int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, + struct mwifiex_ds_rx_reorder_tbl *buf); +int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, + struct mwifiex_ds_tx_ba_stream_tbl *buf); +int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + int cmd_action, u16 *buf_size); +int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, + int cmd_action, + struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl); +void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra); +u8 mwifiex_get_sec_chan_offset(int chan); + +static inline u8 +mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int tid) +{ + struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ptr->ra); + + if (unlikely(!node)) + return false; + + return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false; +} + +/* This function checks whether AMPDU is allowed or not for a particular TID. */ +static inline u8 +mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int tid) +{ + if (is_broadcast_ether_addr(ptr->ra)) + return false; + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); + } else { + if (ptr->tdls_link) + return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); + + return (priv->aggr_prio_tbl[tid].ampdu_ap != + BA_STREAM_NOT_ALLOWED) ? true : false; + } +} + +/* + * This function checks whether AMSDU is allowed or not for a particular TID. + */ +static inline u8 +mwifiex_is_amsdu_allowed(struct mwifiex_private *priv, int tid) +{ + return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) && + (priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x03))) + ? true : false); +} + +/* + * This function checks whether a space is available for new BA stream or not. + */ +static inline u8 mwifiex_space_avail_for_new_ba_stream( + struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + u8 i; + u32 ba_stream_num = 0, ba_stream_max; + + ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + ba_stream_num += mwifiex_wmm_list_len( + &priv->tx_ba_stream_tbl_ptr); + } + + if (adapter->fw_api_ver == MWIFIEX_FW_V15) { + ba_stream_max = + GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap); + if (!ba_stream_max) + ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; + } + + return ((ba_stream_num < ba_stream_max) ? true : false); +} + +/* + * This function finds the correct Tx BA stream to delete. + * + * Upon successfully locating, both the TID and the RA are returned. + */ +static inline u8 +mwifiex_find_stream_to_delete(struct mwifiex_private *priv, int ptr_tid, + int *ptid, u8 *ra) +{ + int tid; + u8 ret = false; + struct mwifiex_tx_ba_stream_tbl *tx_tbl; + unsigned long flags; + + tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; + + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) { + if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) { + tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; + *ptid = tx_tbl->tid; + memcpy(ra, tx_tbl->ra, ETH_ALEN); + ret = true; + } + } + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + + return ret; +} + +/* + * This function checks whether associated station is 11n enabled + */ +static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, + struct mwifiex_sta_node *node) +{ + + if (!node || (priv->bss_role != MWIFIEX_BSS_ROLE_UAP) || + !priv->ap_11n_enabled) + return 0; + + return node->is_11n_enabled; +} + +static inline u8 +mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra) +{ + struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra); + if (node) + return node->is_11n_enabled; + + return false; +} +#endif /* !_MWIFIEX_11N_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c new file mode 100644 index 000000000000..aa498e0d2204 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c @@ -0,0 +1,318 @@ +/* + * Marvell Wireless LAN device driver: 802.11n Aggregation + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11n_aggr.h" + +/* + * Creates an AMSDU subframe for aggregation into one AMSDU packet. + * + * The resultant AMSDU subframe format is - + * + * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ + * | DA | SA | Length | SNAP header | MSDU | + * | data[0..5] | data[6..11] | | | data[14..] | + * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ + * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes--> + * + * This function also computes the amount of padding required to make the + * buffer length multiple of 4 bytes. + * + * Data => |DA|SA|SNAP-TYPE|........ .| + * MSDU => |DA|SA|Length|SNAP|...... ..| + */ +static int +mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr, + struct sk_buff *skb_src, int *pad) + +{ + int dt_offset; + struct rfc_1042_hdr 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 + */ + }; + struct tx_packet_hdr *tx_header; + + tx_header = (void *)skb_put(skb_aggr, sizeof(*tx_header)); + + /* Copy DA and SA */ + dt_offset = 2 * ETH_ALEN; + memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); + + /* Copy SNAP header */ + snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto; + + dt_offset += sizeof(__be16); + + memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); + + skb_pull(skb_src, dt_offset); + + /* Update Length field */ + tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN); + + /* Add payload */ + memcpy(skb_put(skb_aggr, skb_src->len), skb_src->data, skb_src->len); + + /* Add padding for new MSDU to start from 4 byte boundary */ + *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4; + + return skb_aggr->len + *pad; +} + +/* + * Adds TxPD to AMSDU header. + * + * Each AMSDU packet will contain one TxPD at the beginning, + * followed by multiple AMSDU subframes. + */ +static void +mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct txpd *local_tx_pd; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + unsigned int pad; + int headroom = (priv->adapter->iface_type == + MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; + + pad = ((void *)skb->data - sizeof(*local_tx_pd) - + headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); + skb_push(skb, pad); + + skb_push(skb, sizeof(*local_tx_pd)); + + local_tx_pd = (struct txpd *) skb->data; + memset(local_tx_pd, 0, sizeof(struct txpd)); + + /* Original priority has been overwritten */ + local_tx_pd->priority = (u8) skb->priority; + local_tx_pd->pkt_delay_2ms = + mwifiex_wmm_compute_drv_pkt_delay(priv, skb); + local_tx_pd->bss_num = priv->bss_num; + local_tx_pd->bss_type = priv->bss_type; + /* Always zero as the data is followed by struct txpd */ + local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) + + pad); + local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); + local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - + sizeof(*local_tx_pd) - + pad); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; + + if (local_tx_pd->tx_control == 0) + /* TxCtrl set by user or default */ + local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + priv->adapter->pps_uapsd_mode) { + if (true == mwifiex_check_last_packet_indication(priv)) { + priv->adapter->tx_lock_flag = true; + local_tx_pd->flags = + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; + } + } +} + +/* + * Create aggregated packet. + * + * This function creates an aggregated MSDU packet, by combining buffers + * from the RA list. Each individual buffer is encapsulated as an AMSDU + * subframe and all such subframes are concatenated together to form the + * AMSDU packet. + * + * A TxPD is also added to the front of the resultant AMSDU packets for + * transmission. The resultant packets format is - + * + * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+ + * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame| + * | | 1 | 2 | .. | n | + * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+ + */ +int +mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *pra_list, + int ptrindex, unsigned long ra_list_flags) + __releases(&priv->wmm.ra_list_spinlock) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct sk_buff *skb_aggr, *skb_src; + struct mwifiex_txinfo *tx_info_aggr, *tx_info_src; + int pad = 0, aggr_num = 0, ret; + struct mwifiex_tx_param tx_param; + struct txpd *ptx_pd = NULL; + int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN; + + skb_src = skb_peek(&pra_list->skb_head); + if (!skb_src) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + return 0; + } + + tx_info_src = MWIFIEX_SKB_TXCB(skb_src); + skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size, + GFP_ATOMIC | GFP_DMA); + if (!skb_aggr) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + return -1; + } + skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN); + tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); + + memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); + tx_info_aggr->bss_type = tx_info_src->bss_type; + tx_info_aggr->bss_num = tx_info_src->bss_num; + + if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; + skb_aggr->priority = skb_src->priority; + skb_aggr->tstamp = skb_src->tstamp; + + skb_aggr->tstamp = ktime_get_real(); + + do { + /* Check if AMSDU can accommodate this MSDU */ + if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) + break; + + skb_src = skb_dequeue(&pra_list->skb_head); + pra_list->total_pkt_count--; + atomic_dec(&priv->wmm.tx_pkts_queued); + aggr_num++; + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); + + mwifiex_write_data_complete(adapter, skb_src, 0, 0); + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + + if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + return -1; + } + + if (skb_tailroom(skb_aggr) < pad) { + pad = 0; + break; + } + skb_put(skb_aggr, pad); + + skb_src = skb_peek(&pra_list->skb_head); + + } while (skb_src); + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); + + /* Last AMSDU packet does not need padding */ + skb_trim(skb_aggr, skb_aggr->len - pad); + + /* Form AMSDU */ + mwifiex_11n_form_amsdu_txpd(priv, skb_aggr); + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) + ptx_pd = (struct txpd *)skb_aggr->data; + + skb_push(skb_aggr, headroom); + tx_info_aggr->aggr_num = aggr_num * 2; + if (adapter->data_sent || adapter->tx_lock_flag) { + atomic_add(aggr_num * 2, &adapter->tx_queued); + skb_queue_tail(&adapter->tx_data_q, skb_aggr); + return 0; + } + + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, + skb_aggr, NULL); + } else { + if (skb_src) + tx_param.next_pkt_len = + skb_src->len + sizeof(struct txpd); + else + tx_param.next_pkt_len = 0; + + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, + skb_aggr, &tx_param); + } + switch (ret) { + case -EBUSY: + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_write_data_complete(adapter, skb_aggr, 1, -1); + return -1; + } + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + adapter->pps_uapsd_mode && adapter->tx_lock_flag) { + priv->adapter->tx_lock_flag = false; + if (ptx_pd) + ptx_pd->flags = 0; + } + + skb_queue_tail(&pra_list->skb_head, skb_aggr); + + pra_list->total_pkt_count++; + + atomic_inc(&priv->wmm.tx_pkts_queued); + + tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + break; + case -1: + mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); + return 0; + case -EINPROGRESS: + break; + case 0: + mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); + break; + default: + break; + } + if (ret != -EBUSY) { + mwifiex_rotate_priolists(priv, pra_list, ptrindex); + } + + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.h b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h new file mode 100644 index 000000000000..0cd2a3eb6c17 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h @@ -0,0 +1,33 @@ +/* + * Marvell Wireless LAN device driver: 802.11n Aggregation + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_11N_AGGR_H_ +#define _MWIFIEX_11N_AGGR_H_ + +#define PKT_TYPE_AMSDU 0xE6 +#define MIN_NUM_AMSDU 2 + +int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, + struct sk_buff *skb); +int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, + int ptr_index, unsigned long flags) + __releases(&priv->wmm.ra_list_spinlock); + +#endif /* !_MWIFIEX_11N_AGGR_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c new file mode 100644 index 000000000000..b3970a8c9e48 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -0,0 +1,910 @@ +/* + * Marvell Wireless LAN device driver: 802.11n RX Re-ordering + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11n_rxreorder.h" + +/* This function will dispatch amsdu packet and forward it to kernel/upper + * layer. + */ +static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct rxpd *local_rx_pd = (struct rxpd *)(skb->data); + int ret; + + if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { + struct sk_buff_head list; + struct sk_buff *rx_skb; + + __skb_queue_head_init(&list); + + skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset)); + skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); + + ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, + priv->wdev.iftype, 0, false); + + while (!skb_queue_empty(&list)) { + rx_skb = __skb_dequeue(&list); + ret = mwifiex_recv_packet(priv, rx_skb); + if (ret == -1) + mwifiex_dbg(priv->adapter, ERROR, + "Rx of A-MSDU failed"); + } + return 0; + } + + return -1; +} + +/* This function will process the rx packet and forward it to kernel/upper + * layer. + */ +static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload) +{ + int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload); + + if (!ret) + return 0; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) + return mwifiex_handle_uap_rx_forward(priv, payload); + + return mwifiex_process_rx_packet(priv, payload); +} + +/* + * This function dispatches all packets in the Rx reorder table until the + * start window. + * + * There could be holes in the buffer, which are skipped by the function. + * Since the buffer is linear, the function uses rotation to simulate + * circular buffer. + */ +static void +mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, + struct mwifiex_rx_reorder_tbl *tbl, + int start_win) +{ + int pkt_to_send, i; + void *rx_tmp_ptr; + unsigned long flags; + + pkt_to_send = (start_win > tbl->start_win) ? + min((start_win - tbl->start_win), tbl->win_size) : + tbl->win_size; + + for (i = 0; i < pkt_to_send; ++i) { + spin_lock_irqsave(&priv->rx_pkt_lock, flags); + rx_tmp_ptr = NULL; + if (tbl->rx_reorder_ptr[i]) { + rx_tmp_ptr = tbl->rx_reorder_ptr[i]; + tbl->rx_reorder_ptr[i] = NULL; + } + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); + if (rx_tmp_ptr) + mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); + } + + spin_lock_irqsave(&priv->rx_pkt_lock, flags); + /* + * We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + for (i = 0; i < tbl->win_size - pkt_to_send; ++i) { + tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i]; + tbl->rx_reorder_ptr[pkt_to_send + i] = NULL; + } + + tbl->start_win = start_win; + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); +} + +/* + * This function dispatches all packets in the Rx reorder table until + * a hole is found. + * + * The start window is adjusted automatically when a hole is located. + * Since the buffer is linear, the function uses rotation to simulate + * circular buffer. + */ +static void +mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, + struct mwifiex_rx_reorder_tbl *tbl) +{ + int i, j, xchg; + void *rx_tmp_ptr; + unsigned long flags; + + for (i = 0; i < tbl->win_size; ++i) { + spin_lock_irqsave(&priv->rx_pkt_lock, flags); + if (!tbl->rx_reorder_ptr[i]) { + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); + break; + } + rx_tmp_ptr = tbl->rx_reorder_ptr[i]; + tbl->rx_reorder_ptr[i] = NULL; + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); + mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); + } + + spin_lock_irqsave(&priv->rx_pkt_lock, flags); + /* + * We don't have a circular buffer, hence use rotation to simulate + * circular buffer + */ + if (i > 0) { + xchg = tbl->win_size - i; + for (j = 0; j < xchg; ++j) { + tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j]; + tbl->rx_reorder_ptr[i + j] = NULL; + } + } + tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1); + spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); +} + +/* + * This function deletes the Rx reorder table and frees the memory. + * + * The function stops the associated timer and dispatches all the + * pending packets in the Rx reorder table before deletion. + */ +static void +mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, + struct mwifiex_rx_reorder_tbl *tbl) +{ + unsigned long flags; + int start_win; + + if (!tbl) + return; + + spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); + priv->adapter->rx_locked = true; + if (priv->adapter->rx_processing) { + spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); + flush_workqueue(priv->adapter->rx_workqueue); + } else { + spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); + } + + start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); + + del_timer_sync(&tbl->timer_context.timer); + tbl->timer_context.timer_is_set = false; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_del(&tbl->list); + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + kfree(tbl->rx_reorder_ptr); + kfree(tbl); + + spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); + priv->adapter->rx_locked = false; + spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); + +} + +/* + * This function returns the pointer to an entry in Rx reordering + * table which matches the given TA/TID pair. + */ +struct mwifiex_rx_reorder_tbl * +mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) +{ + struct mwifiex_rx_reorder_tbl *tbl; + unsigned long flags; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) { + if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + flags); + return tbl; + } + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + return NULL; +} + +/* This function retrieves the pointer to an entry in Rx reordering + * table which matches the given TA and deletes it. + */ +void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) +{ + struct mwifiex_rx_reorder_tbl *tbl, *tmp; + unsigned long flags; + + if (!ta) + return; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { + if (!memcmp(tbl->ta, ta, ETH_ALEN)) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + flags); + mwifiex_del_rx_reorder_entry(priv, tbl); + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + } + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + return; +} + +/* + * This function finds the last sequence number used in the packets + * buffered in Rx reordering table. + */ +static int +mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) +{ + struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr; + struct mwifiex_private *priv = ctx->priv; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + flags); + return i; + } + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + return -1; +} + +/* + * This function flushes all the packets in Rx reordering table. + * + * The function checks if any packets are currently buffered in the + * table or not. In case there are packets available, it dispatches + * them and then dumps the Rx reordering table. + */ +static void +mwifiex_flush_data(unsigned long context) +{ + struct reorder_tmr_cnxt *ctx = + (struct reorder_tmr_cnxt *) context; + int start_win, seq_num; + + ctx->timer_is_set = false; + seq_num = mwifiex_11n_find_last_seq_num(ctx); + + if (seq_num < 0) + return; + + mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num); + start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); + mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, + start_win); +} + +/* + * This function creates an entry in Rx reordering table for the + * given TA/TID. + * + * The function also initializes the entry with sequence number, window + * size as well as initializes the timer. + * + * If the received TA/TID pair is already present, all the packets are + * dispatched and the window size is moved until the SSN. + */ +static void +mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, + int tid, int win_size, int seq_num) +{ + int i; + struct mwifiex_rx_reorder_tbl *tbl, *new_node; + u16 last_seq = 0; + unsigned long flags; + struct mwifiex_sta_node *node; + + /* + * If we get a TID, ta pair which is already present dispatch all the + * the packets and move the window size until the ssn + */ + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); + if (tbl) { + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); + return; + } + /* if !tbl then create one */ + new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); + if (!new_node) + return; + + INIT_LIST_HEAD(&new_node->list); + new_node->tid = tid; + memcpy(new_node->ta, ta, ETH_ALEN); + new_node->start_win = seq_num; + new_node->init_win = seq_num; + new_node->flags = 0; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + if (mwifiex_queuing_ra_based(priv)) { + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { + node = mwifiex_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + } + } else { + node = mwifiex_get_sta_entry(priv, ta); + if (node) + last_seq = node->rx_seq[tid]; + else + last_seq = priv->rx_seq[tid]; + } + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + mwifiex_dbg(priv->adapter, INFO, + "info: last_seq=%d start_win=%d\n", + last_seq, new_node->start_win); + + if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && + last_seq >= new_node->start_win) { + new_node->start_win = last_seq + 1; + new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; + } + + new_node->win_size = win_size; + + new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, + GFP_KERNEL); + if (!new_node->rx_reorder_ptr) { + kfree((u8 *) new_node); + mwifiex_dbg(priv->adapter, ERROR, + "%s: failed to alloc reorder_ptr\n", __func__); + return; + } + + new_node->timer_context.ptr = new_node; + new_node->timer_context.priv = priv; + new_node->timer_context.timer_is_set = false; + + setup_timer(&new_node->timer_context.timer, mwifiex_flush_data, + (unsigned long)&new_node->timer_context); + + for (i = 0; i < win_size; ++i) + new_node->rx_reorder_ptr[i] = NULL; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); +} + +static void +mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl) +{ + u32 min_flush_time; + + if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32) + min_flush_time = MIN_FLUSH_TIMER_15_MS; + else + min_flush_time = MIN_FLUSH_TIMER_MS; + + mod_timer(&tbl->timer_context.timer, + jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size)); + + tbl->timer_context.timer_is_set = true; +} + +/* + * This function prepares command for adding a BA request. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting add BA request buffer + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) +{ + struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; + + cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); + cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); + memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); + + return 0; +} + +/* + * This function prepares command for adding a BA response. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting add BA response buffer + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct host_cmd_ds_11n_addba_req + *cmd_addba_req) +{ + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; + struct mwifiex_sta_node *sta_ptr; + u32 rx_win_size = priv->add_ba_param.rx_win_size; + u8 tid; + int win_size; + unsigned long flags; + uint16_t block_ack_param_set; + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->is_hw_11ac_capable && + memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, + cmd_addba_req->peer_mac_addr); + if (!sta_ptr) { + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + mwifiex_dbg(priv->adapter, ERROR, + "BA setup with unknown TDLS peer %pM!\n", + cmd_addba_req->peer_mac_addr); + return -1; + } + if (sta_ptr->is_11ac_enabled) + rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + } + + cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); + cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); + + memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, + ETH_ALEN); + add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; + add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; + add_ba_rsp->ssn = cmd_addba_req->ssn; + + block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); + block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; + + /* If we don't support AMSDU inside AMPDU, reset the bit */ + if (!priv->add_ba_param.rx_amsdu || + (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)) + block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; + block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; + add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); + win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) + & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); + + mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, + tid, win_size, + le16_to_cpu(cmd_addba_req->ssn)); + return 0; +} + +/* + * This function prepares command for deleting a BA request. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting del BA request buffer + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) +{ + struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; + + cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); + cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); + memcpy(del_ba, data_buf, sizeof(*del_ba)); + + return 0; +} + +/* + * This function identifies if Rx reordering is needed for a received packet. + * + * In case reordering is required, the function will do the reordering + * before sending it to kernel. + * + * The Rx reorder table is checked first with the received TID/TA pair. If + * not found, the received packet is dispatched immediately. But if found, + * the packet is reordered and all the packets in the updated Rx reordering + * table is dispatched until a hole is found. + * + * For sequence number less than the starting window, the packet is dropped. + */ +int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, + u16 seq_num, u16 tid, + u8 *ta, u8 pkt_type, void *payload) +{ + struct mwifiex_rx_reorder_tbl *tbl; + int prev_start_win, start_win, end_win, win_size; + u16 pkt_index; + bool init_window_shift = false; + int ret = 0; + + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); + if (!tbl) { + if (pkt_type != PKT_TYPE_BAR) + mwifiex_11n_dispatch_pkt(priv, payload); + return ret; + } + + if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) { + mwifiex_11n_dispatch_pkt(priv, payload); + return ret; + } + + start_win = tbl->start_win; + prev_start_win = start_win; + win_size = tbl->win_size; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { + init_window_shift = true; + tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT; + } + + if (tbl->flags & RXREOR_FORCE_NO_DROP) { + mwifiex_dbg(priv->adapter, INFO, + "RXREOR_FORCE_NO_DROP when HS is activated\n"); + tbl->flags &= ~RXREOR_FORCE_NO_DROP; + } else if (init_window_shift && seq_num < start_win && + seq_num >= tbl->init_win) { + mwifiex_dbg(priv->adapter, INFO, + "Sender TID sequence number reset %d->%d for SSN %d\n", + start_win, seq_num, tbl->init_win); + tbl->start_win = start_win = seq_num; + end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); + } else { + /* + * If seq_num is less then starting win then ignore and drop + * the packet + */ + if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { + if (seq_num >= ((start_win + TWOPOW11) & + (MAX_TID_VALUE - 1)) && + seq_num < start_win) { + ret = -1; + goto done; + } + } else if ((seq_num < start_win) || + (seq_num >= (start_win + TWOPOW11))) { + ret = -1; + 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); + + 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 (((end_win - win_size) + 1) >= 0) + start_win = (end_win - win_size) + 1; + else + start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1; + mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); + } + + if (pkt_type != PKT_TYPE_BAR) { + if (seq_num >= start_win) + pkt_index = seq_num - start_win; + else + pkt_index = (seq_num+MAX_TID_VALUE) - start_win; + + if (tbl->rx_reorder_ptr[pkt_index]) { + ret = -1; + goto done; + } + + tbl->rx_reorder_ptr[pkt_index] = payload; + } + + /* + * Dispatch all packets sequentially from start_win until a + * hole is found and adjust the start_win appropriately + */ + mwifiex_11n_scan_and_dispatch(priv, tbl); + +done: + if (!tbl->timer_context.timer_is_set || + prev_start_win != tbl->start_win) + mwifiex_11n_rxreorder_timer_restart(tbl); + return ret; +} + +/* + * This function deletes an entry for a given TID/TA pair. + * + * The TID/TA are taken from del BA event body. + */ +void +mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, + u8 type, int initiator) +{ + struct mwifiex_rx_reorder_tbl *tbl; + struct mwifiex_tx_ba_stream_tbl *ptx_tbl; + struct mwifiex_ra_list_tbl *ra_list; + u8 cleanup_rx_reorder_tbl; + unsigned long flags; + int tid_down; + + if (type == TYPE_DELBA_RECEIVE) + cleanup_rx_reorder_tbl = (initiator) ? true : false; + else + cleanup_rx_reorder_tbl = (initiator) ? false : true; + + mwifiex_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n", + peer_mac, tid, initiator); + + if (cleanup_rx_reorder_tbl) { + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, + peer_mac); + if (!tbl) { + mwifiex_dbg(priv->adapter, EVENT, + "event: TID, TA not found in table\n"); + return; + } + mwifiex_del_rx_reorder_entry(priv, tbl); + } else { + ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac); + if (!ptx_tbl) { + mwifiex_dbg(priv->adapter, EVENT, + "event: TID, RA not found in table\n"); + return; + } + + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, peer_mac); + if (ra_list) { + ra_list->amsdu_in_ampdu = false; + ra_list->ba_status = BA_SETUP_NONE; + } + spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); + mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); + spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); + } +} + +/* + * This function handles the command response of an add BA response. + * + * Handling includes changing the header fields into CPU format and + * creating the stream, provided the add BA is accepted. + */ +int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; + int tid, win_size; + struct mwifiex_rx_reorder_tbl *tbl; + uint16_t block_ack_param_set; + + block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); + + tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) + >> BLOCKACKPARAM_TID_POS; + /* + * Check if we had rejected the ADDBA, if yes then do not create + * the stream + */ + if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { + mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n", + add_ba_rsp->peer_mac_addr, tid); + + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, + add_ba_rsp->peer_mac_addr); + if (tbl) + mwifiex_del_rx_reorder_entry(priv, tbl); + + return 0; + } + + win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) + >> BLOCKACKPARAM_WINSIZE_POS; + + tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, + add_ba_rsp->peer_mac_addr); + if (tbl) { + if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && + priv->add_ba_param.rx_amsdu && + (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) + tbl->amsdu = true; + else + tbl->amsdu = false; + } + + mwifiex_dbg(priv->adapter, CMD, + "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", + add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); + + return 0; +} + +/* + * This function handles BA stream timeout event by preparing and sending + * a command to the firmware. + */ +void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, + struct host_cmd_ds_11n_batimeout *event) +{ + struct host_cmd_ds_11n_delba delba; + + memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); + memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); + + delba.del_ba_param_set |= + cpu_to_le16((u16) event->tid << DELBA_TID_POS); + delba.del_ba_param_set |= cpu_to_le16( + (u16) event->origninator << DELBA_INITIATOR_POS); + delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); + mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false); +} + +/* + * This function cleans up the Rx reorder table by deleting all the entries + * and re-initializing. + */ +void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) +{ + struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; + unsigned long flags; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + list_for_each_entry_safe(del_tbl_ptr, tmp_node, + &priv->rx_reorder_tbl_ptr, list) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr); + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + } + INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); + + mwifiex_reset_11n_rx_seq_num(priv); +} + +/* + * This function updates all rx_reorder_tbl's flags. + */ +void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) +{ + struct mwifiex_private *priv; + struct mwifiex_rx_reorder_tbl *tbl; + unsigned long lock_flags; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags); + if (list_empty(&priv->rx_reorder_tbl_ptr)) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + lock_flags); + continue; + } + + list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) + tbl->flags = flags; + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags); + } + + return; +} + +/* This function update all the rx_win_size based on coex flag + */ +static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter, + bool coex_flag) +{ + u8 i; + u32 rx_win_size; + struct mwifiex_private *priv; + + dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag); + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]) + continue; + priv = adapter->priv[i]; + rx_win_size = priv->add_ba_param.rx_win_size; + if (coex_flag) { + if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size = + MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE; + } else { + if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) + priv->add_ba_param.rx_win_size = + MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) + priv->add_ba_param.rx_win_size = + MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; + } + + if (adapter->coex_win_size && adapter->coex_rx_win_size) + priv->add_ba_param.rx_win_size = + adapter->coex_rx_win_size; + + if (rx_win_size != priv->add_ba_param.rx_win_size) { + if (!priv->media_connected) + continue; + for (i = 0; i < MAX_NUM_TID; i++) + mwifiex_11n_delba(priv, i); + } + } +} + +/* This function check coex for RX BA + */ +void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter) +{ + u8 i; + struct mwifiex_private *priv; + u8 count = 0; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { + if (priv->media_connected) + count++; + } + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + if (priv->bss_started) + count++; + } + } + if (count >= MWIFIEX_BSS_COEX_COUNT) + break; + } + if (count >= MWIFIEX_BSS_COEX_COUNT) + mwifiex_update_ampdu_rxwinsize(adapter, true); + else + mwifiex_update_ampdu_rxwinsize(adapter, false); +} diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h new file mode 100644 index 000000000000..63ecea89b4ab --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h @@ -0,0 +1,85 @@ +/* + * Marvell Wireless LAN device driver: 802.11n RX Re-ordering + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_11N_RXREORDER_H_ +#define _MWIFIEX_11N_RXREORDER_H_ + +#define MIN_FLUSH_TIMER_MS 50 +#define MIN_FLUSH_TIMER_15_MS 15 +#define MWIFIEX_BA_WIN_SIZE_32 32 + +#define PKT_TYPE_BAR 0xE7 +#define MAX_TID_VALUE (2 << 11) +#define TWOPOW11 (2 << 10) + +#define BLOCKACKPARAM_TID_POS 2 +#define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1 +#define BLOCKACKPARAM_WINSIZE_POS 6 +#define DELBA_TID_POS 12 +#define DELBA_INITIATOR_POS 11 +#define TYPE_DELBA_SENT 1 +#define TYPE_DELBA_RECEIVE 2 +#define IMMEDIATE_BLOCK_ACK 0x2 + +#define ADDBA_RSP_STATUS_ACCEPT 0 + +#define MWIFIEX_DEF_11N_RX_SEQ_NUM 0xffff +#define BA_SETUP_MAX_PACKET_THRESHOLD 16 +#define BA_SETUP_PACKET_OFFSET 16 + +enum mwifiex_rxreor_flags { + RXREOR_FORCE_NO_DROP = 1<<0, + RXREOR_INIT_WINDOW_SHIFT = 1<<1, +}; + +static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv) +{ + memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq)); +} + +int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *, + u16 seqNum, + u16 tid, u8 *ta, + u8 pkttype, void *payload); +void mwifiex_del_ba_tbl(struct mwifiex_private *priv, int Tid, + u8 *PeerMACAddr, u8 type, int initiator); +void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, + struct host_cmd_ds_11n_batimeout *event); +int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, + struct host_cmd_ds_command + *resp); +int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct host_cmd_ds_11n_addba_req + *cmd_addba_req); +int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, + void *data_buf); +void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv); +struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct + mwifiex_private + *priv, int tid, + u8 *ta); +struct mwifiex_rx_reorder_tbl * +mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta); +void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta); +void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags); + +#endif /* _MWIFIEX_11N_RXREORDER_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/Kconfig b/drivers/net/wireless/marvell/mwifiex/Kconfig new file mode 100644 index 000000000000..279167ddd293 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/Kconfig @@ -0,0 +1,44 @@ +config MWIFIEX + tristate "Marvell WiFi-Ex Driver" + depends on CFG80211 + ---help--- + This adds support for wireless adapters based on Marvell + 802.11n/ac chipsets. + + If you choose to build it as a module, it will be called + mwifiex. + +config MWIFIEX_SDIO + tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8997" + depends on MWIFIEX && MMC + select FW_LOADER + select WANT_DEV_COREDUMP + ---help--- + This adds support for wireless adapters based on Marvell + 8786/8787/8797/8887/8897/8997 chipsets with SDIO interface. + + If you choose to build it as a module, it will be called + mwifiex_sdio. + +config MWIFIEX_PCIE + tristate "Marvell WiFi-Ex Driver for PCIE 8766/8897/8997" + depends on MWIFIEX && PCI + select FW_LOADER + select WANT_DEV_COREDUMP + ---help--- + This adds support for wireless adapters based on Marvell + 8766/8897/8997 chipsets with PCIe interface. + + If you choose to build it as a module, it will be called + mwifiex_pcie. + +config MWIFIEX_USB + tristate "Marvell WiFi-Ex Driver for USB8766/8797/8997" + depends on MWIFIEX && USB + select FW_LOADER + ---help--- + This adds support for wireless adapters based on Marvell + 8797/8997 chipset with USB interface. + + If you choose to build it as a module, it will be called + mwifiex_usb. diff --git a/drivers/net/wireless/marvell/mwifiex/Makefile b/drivers/net/wireless/marvell/mwifiex/Makefile new file mode 100644 index 000000000000..fdfd9bf15ed4 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/Makefile @@ -0,0 +1,57 @@ +# +# Copyright (C) 2011-2014, Marvell International Ltd. +# +# This software file (the "File") is distributed by Marvell International +# Ltd. under the terms of the GNU General Public License Version 2, June 1991 +# (the "License"). You may use, redistribute and/or modify this File in +# accordance with the terms and conditions of the License, a copy of which +# is available by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the +# worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. +# +# THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE +# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE +# ARE EXPRESSLY DISCLAIMED. The License provides additional details about +# this warranty disclaimer. + + +mwifiex-y += main.o +mwifiex-y += init.o +mwifiex-y += cfp.o +mwifiex-y += cmdevt.o +mwifiex-y += util.o +mwifiex-y += txrx.o +mwifiex-y += wmm.o +mwifiex-y += 11n.o +mwifiex-y += 11ac.o +mwifiex-y += 11n_aggr.o +mwifiex-y += 11n_rxreorder.o +mwifiex-y += scan.o +mwifiex-y += join.o +mwifiex-y += sta_ioctl.o +mwifiex-y += sta_cmd.o +mwifiex-y += uap_cmd.o +mwifiex-y += ie.o +mwifiex-y += sta_cmdresp.o +mwifiex-y += sta_event.o +mwifiex-y += uap_event.o +mwifiex-y += sta_tx.o +mwifiex-y += sta_rx.o +mwifiex-y += uap_txrx.o +mwifiex-y += cfg80211.o +mwifiex-y += ethtool.o +mwifiex-y += 11h.o +mwifiex-y += tdls.o +mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o +obj-$(CONFIG_MWIFIEX) += mwifiex.o + +mwifiex_sdio-y += sdio.o +obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o + +mwifiex_pcie-y += pcie.o +obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o + +mwifiex_usb-y += usb.o +obj-$(CONFIG_MWIFIEX_USB) += mwifiex_usb.o + +ccflags-y += -D__CHECK_ENDIAN diff --git a/drivers/net/wireless/marvell/mwifiex/README b/drivers/net/wireless/marvell/mwifiex/README new file mode 100644 index 000000000000..2f0f9b5609d0 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/README @@ -0,0 +1,240 @@ +# Copyright (C) 2011-2014, Marvell International Ltd. +# +# This software file (the "File") is distributed by Marvell International +# Ltd. under the terms of the GNU General Public License Version 2, June 1991 +# (the "License"). You may use, redistribute and/or modify this File in +# accordance with the terms and conditions of the License, a copy of which +# is available by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the +# worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. +# +# THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE +# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE +# ARE EXPRESSLY DISCLAIMED. The License provides additional details about +# this warranty disclaimer. + + +=============================================================================== + U S E R M A N U A L + +1) FOR DRIVER INSTALL + + a) Copy sd8787.bin to /lib/firmware/mrvl/ directory, + create the directory if it doesn't exist. + b) Install WLAN driver, + insmod mwifiex.ko + c) Uninstall WLAN driver, + ifconfig mlanX down + rmmod mwifiex + + +2) FOR DRIVER CONFIGURATION AND INFO + The configurations can be done either using the 'iw' user space + utility or debugfs. + + a) 'iw' utility commands + + Following are some useful iw commands:- + +iw dev mlan0 scan + + This command will trigger a scan. + The command will then display the scan table entries + +iw dev mlan0 connect -w [] [] [key 0:abcde d:1123456789a] + The above command can be used to connect to an AP with a particular SSID. + Ap's operating frequency can be specified or even the bssid. If the AP is using + WEP encryption, wep keys can be specified in the command. + Note: Every time before connecting to an AP scan command (iw dev mlan0 scan) should be used by user. + +iw dev mlan0 disconnect + This command will be used to disconnect from an AP. + + +iw dev mlan0 ibss join [fixed-freq] [fixed-bssid] [key 0:abcde] + The command will be used to join or create an ibss. Optionally, operating frequency, + bssid and the security related parameters can be specified while joining/creating + and ibss. + +iw dev mlan0 ibss leave + The command will be used to leave an ibss network. + +iw dev mlan0 link + The command will be used to get the connection status. The command will return parameters + such as SSID, operating frequency, rx/tx packets, signal strength, tx bitrate. + + Apart from the iw utility all standard configurations using the 'iwconfig' utility are also supported. + + b) Debugfs interface + + The debugfs interface can be used for configurations and for getting + some useful information from the driver. + The section below explains the configurations that can be + done. + + Mount debugfs to /debugfs mount point: + + mkdir /debugfs + mount -t debugfs debugfs /debugfs + + The information is provided in /debugfs/mwifiex/mlanX/: + +iw reg set + The command will be used to change the regulatory domain. + +iw reg get + The command will be used to get current regulatory domain. + +info + This command is used to get driver info. + + Usage: + cat info + + driver_name = "mwifiex" + driver_version = + interface_name = "mlanX" + bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown" + media_state = "Disconnected" | "Connected" + mac_address = <6-byte adapter MAC address> + multicase_count = + essid = + bssid = + channel = + region_code = + multicasr_address[n] = + num_tx_bytes = + num_rx_bytes = + num_tx_pkts = + num_rx_pkts = + num_tx_pkts_dropped = + num_rx_pkts_dropped = + num_tx_pkts_err = + num_rx_pkts_err = + carrier "on" | "off" + tx queue "stopped" | "started" + + The following debug info are provided in /debugfs/mwifiex/mlanX/debug: + + int_counter = + wmm_ac_vo = + wmm_ac_vi = + wmm_ac_be = + wmm_ac_bk = + tx_buf_size = + curr_tx_buf_size = + ps_mode = <0/1, CAM mode/PS mode> + ps_state = <0/1/2/3, full power state/awake state/pre-sleep state/sleep state> + is_deep_sleep = <0/1, not deep sleep state/deep sleep state> + wakeup_dev_req = <0/1, wakeup device not required/required> + wakeup_tries = + hs_configured = <0/1, host sleep not configured/configured> + hs_activated = <0/1, extended host sleep not activated/activated> + num_tx_timeout = + is_cmd_timedout = <0/1 command timeout not occurred/occurred> + timeout_cmd_id = + timeout_cmd_act = + last_cmd_id = + last_cmd_act = + last_cmd_index = <0 based last command index> + last_cmd_resp_id = + last_cmd_resp_index = <0 based last command response index> + last_event = + last_event_index = <0 based last event index> + num_cmd_h2c_fail = + num_cmd_sleep_cfm_fail = + num_tx_h2c_fail = + num_evt_deauth = + num_evt_disassoc = + num_evt_link_lost = + num_cmd_deauth = + num_cmd_assoc_ok = + num_cmd_assoc_fail = + cmd_sent = <0/1, send command resources available/sending command to device> + data_sent = <0/1, send data resources available/sending data to device> + mp_rd_bitmap = + mp_wr_bitmap = + cmd_resp_received = <0/1, no cmd response to process/response received and yet to process> + event_received = <0/1, no event to process/event received and yet to process> + cmd_pending = + tx_pending = + rx_pending = + + +3) FOR DRIVER CONFIGURATION + +regrdwr + This command is used to read/write the adapter register. + + Usage: + echo " [value]" > regrdwr + cat regrdwr + + where the parameters are, + : 1:MAC/SOC, 2:BBP, 3:RF, 4:PMIC, 5:CAU + : offset of register + [value]: value to be written + + Examples: + echo "1 0xa060" > regrdwr : Read the MAC register + echo "1 0xa060 0x12" > regrdwr : Write the MAC register + echo "1 0xa794 0x80000000" > regrdwr + : Write 0x80000000 to MAC register +rdeeprom + This command is used to read the EEPROM contents of the card. + + Usage: + echo " " > rdeeprom + cat rdeeprom + + where the parameters are, + : multiples of 4 + : 4-20, multiples of 4 + + Example: + echo "0 20" > rdeeprom : Read 20 bytes of EEPROM data from offset 0 + +hscfg + This command is used to debug/simulate host sleep feature using + different configuration parameters. + + Usage: + echo " [GPIO# [gap]]]" > hscfg + cat hscfg + + where the parameters are, + : bit 0 = 1 -- broadcast data + bit 1 = 1 -- unicast data + bit 2 = 1 -- mac event + bit 3 = 1 -- multicast data + [GPIO#]: pin number of GPIO used to wakeup the host. + GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO + will be used instead). + [gap]: the gap in milliseconds between wakeup signal and + wakeup event or 0xff for special setting (host + acknowledge required) when GPIO is used to wakeup host. + + Examples: + echo "-1" > hscfg : Cancel host sleep mode + echo "3" > hscfg : Broadcast and unicast data; + Use GPIO and gap set previously + echo "2 3" > hscfg : Unicast data and GPIO 3; + Use gap set previously + echo "2 1 160" > hscfg : Unicast data, GPIO 1 and gap 160 ms + echo "2 1 0xff" > hscfg : Unicast data, GPIO 1; Wait for host + to ack before sending wakeup event + +getlog + This command is used to get the statistics available in the station. + Usage: + + cat getlog + +device_dump + This command is used to dump driver information and firmware memory + segments. + Usage: + + cat fw_dump + +=============================================================================== diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c new file mode 100644 index 000000000000..4073116e6e9f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -0,0 +1,3887 @@ +/* + * Marvell Wireless LAN device driver: CFG80211 + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "cfg80211.h" +#include "main.h" +#include "11n.h" + +static char *reg_alpha2; +module_param(reg_alpha2, charp, 0); + +static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { + { + .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_CLIENT), + }, + { + .max = 1, .types = BIT(NL80211_IFTYPE_AP), + }, +}; + +static const struct ieee80211_iface_combination +mwifiex_iface_comb_ap_sta = { + .limits = mwifiex_ap_sta_limits, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), + .max_interfaces = MWIFIEX_MAX_BSS_NUM, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40), +}; + +static const struct ieee80211_iface_combination +mwifiex_iface_comb_ap_sta_vht = { + .limits = mwifiex_ap_sta_limits, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), + .max_interfaces = MWIFIEX_MAX_BSS_NUM, + .beacon_int_infra_match = true, + .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80), +}; + +static const struct +ieee80211_iface_combination mwifiex_iface_comb_ap_sta_drcs = { + .limits = mwifiex_ap_sta_limits, + .num_different_channels = 2, + .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), + .max_interfaces = MWIFIEX_MAX_BSS_NUM, + .beacon_int_infra_match = true, +}; + +/* + * This function maps the nl802.11 channel type into driver channel type. + * + * The mapping is as follows - + * NL80211_CHAN_NO_HT -> IEEE80211_HT_PARAM_CHA_SEC_NONE + * NL80211_CHAN_HT20 -> IEEE80211_HT_PARAM_CHA_SEC_NONE + * NL80211_CHAN_HT40PLUS -> IEEE80211_HT_PARAM_CHA_SEC_ABOVE + * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW + * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE + */ +u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type) +{ + switch (chan_type) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + return IEEE80211_HT_PARAM_CHA_SEC_NONE; + case NL80211_CHAN_HT40PLUS: + return IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + case NL80211_CHAN_HT40MINUS: + return IEEE80211_HT_PARAM_CHA_SEC_BELOW; + default: + return IEEE80211_HT_PARAM_CHA_SEC_NONE; + } +} + +/* This function maps IEEE HT secondary channel type to NL80211 channel type + */ +u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset) +{ + switch (second_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + return NL80211_CHAN_HT20; + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + return NL80211_CHAN_HT40PLUS; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + return NL80211_CHAN_HT40MINUS; + default: + return NL80211_CHAN_HT20; + } +} + +/* + * This function checks whether WEP is set. + */ +static int +mwifiex_is_alg_wep(u32 cipher) +{ + switch (cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + return 1; + default: + break; + } + + return 0; +} + +/* + * This function retrieves the private structure from kernel wiphy structure. + */ +static void *mwifiex_cfg80211_get_adapter(struct wiphy *wiphy) +{ + return (void *) (*(unsigned long *) wiphy_priv(wiphy)); +} + +/* + * CFG802.11 operation handler to delete a network key. + */ +static int +mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 *peer_mac = pairwise ? mac_addr : bc_mac; + + if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) { + mwifiex_dbg(priv->adapter, ERROR, "deleting the crypto keys\n"); + return -EFAULT; + } + + mwifiex_dbg(priv->adapter, INFO, "info: crypto keys deleted\n"); + return 0; +} + +/* + * This function forms an skb for management frame. + */ +static int +mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) +{ + u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + u16 pkt_len; + u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; + + pkt_len = len + ETH_ALEN; + + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len)); + + memcpy(skb_push(skb, sizeof(tx_control)), + &tx_control, sizeof(tx_control)); + + memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type)); + + /* Add packet data and address4 */ + memcpy(skb_put(skb, sizeof(struct ieee80211_hdr_3addr)), buf, + sizeof(struct ieee80211_hdr_3addr)); + memcpy(skb_put(skb, ETH_ALEN), addr, ETH_ALEN); + memcpy(skb_put(skb, len - sizeof(struct ieee80211_hdr_3addr)), + buf + sizeof(struct ieee80211_hdr_3addr), + len - sizeof(struct ieee80211_hdr_3addr)); + + skb->priority = LOW_PRIO_TID; + __net_timestamp(skb); + + return 0; +} + +/* + * CFG802.11 operation handler to transmit a management frame. + */ +static int +mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie) +{ + const u8 *buf = params->buf; + size_t len = params->len; + struct sk_buff *skb; + u16 pkt_len; + const struct ieee80211_mgmt *mgmt; + struct mwifiex_txinfo *tx_info; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + + if (!buf || !len) { + mwifiex_dbg(priv->adapter, ERROR, "invalid buffer and length\n"); + return -EFAULT; + } + + mgmt = (const struct ieee80211_mgmt *)buf; + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA && + ieee80211_is_probe_resp(mgmt->frame_control)) { + /* Since we support offload probe resp, we need to skip probe + * resp in AP or GO mode */ + mwifiex_dbg(priv->adapter, INFO, + "info: skip to send probe resp in AP or GO mode\n"); + return 0; + } + + pkt_len = len + ETH_ALEN; + skb = dev_alloc_skb(MWIFIEX_MIN_DATA_HEADER_LEN + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + pkt_len + sizeof(pkt_len)); + + if (!skb) { + mwifiex_dbg(priv->adapter, ERROR, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = pkt_len; + + mwifiex_form_mgmt_frame(skb, buf, len); + *cookie = prandom_u32() | 1; + + if (ieee80211_is_action(mgmt->frame_control)) + skb = mwifiex_clone_skb_for_tx_status(priv, + skb, + MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie); + else + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, + GFP_ATOMIC); + + mwifiex_queue_tx_pkt(priv, skb); + + mwifiex_dbg(priv->adapter, INFO, "info: management frame transmitted\n"); + return 0; +} + +/* + * CFG802.11 operation handler to register a mgmt frame. + */ +static void +mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, + u16 frame_type, bool reg) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + u32 mask; + + if (reg) + mask = priv->mgmt_frame_mask | BIT(frame_type >> 4); + else + mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4); + + if (mask != priv->mgmt_frame_mask) { + priv->mgmt_frame_mask = mask; + mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, + HostCmd_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false); + mwifiex_dbg(priv->adapter, INFO, "info: mgmt frame registered\n"); + } +} + +/* + * CFG802.11 operation handler to remain on channel. + */ +static int +mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct ieee80211_channel *chan, + unsigned int duration, u64 *cookie) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + int ret; + + if (!chan || !cookie) { + mwifiex_dbg(priv->adapter, ERROR, "Invalid parameter for ROC\n"); + return -EINVAL; + } + + if (priv->roc_cfg.cookie) { + mwifiex_dbg(priv->adapter, INFO, + "info: ongoing ROC, cookie = 0x%llx\n", + priv->roc_cfg.cookie); + return -EBUSY; + } + + ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, chan, + duration); + + if (!ret) { + *cookie = prandom_u32() | 1; + priv->roc_cfg.cookie = *cookie; + priv->roc_cfg.chan = *chan; + + cfg80211_ready_on_channel(wdev, *cookie, chan, + duration, GFP_ATOMIC); + + mwifiex_dbg(priv->adapter, INFO, + "info: ROC, cookie = 0x%llx\n", *cookie); + } + + return ret; +} + +/* + * CFG802.11 operation handler to cancel remain on channel. + */ +static int +mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, u64 cookie) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + int ret; + + if (cookie != priv->roc_cfg.cookie) + return -ENOENT; + + ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE, + &priv->roc_cfg.chan, 0); + + if (!ret) { + cfg80211_remain_on_channel_expired(wdev, cookie, + &priv->roc_cfg.chan, + GFP_ATOMIC); + + memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg)); + + mwifiex_dbg(priv->adapter, INFO, + "info: cancel ROC, cookie = 0x%llx\n", cookie); + } + + return ret; +} + +/* + * CFG802.11 operation handler to set Tx power. + */ +static int +mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, + int mbm) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct mwifiex_power_cfg power_cfg; + int dbm = MBM_TO_DBM(mbm); + + if (type == NL80211_TX_POWER_FIXED) { + power_cfg.is_power_auto = 0; + power_cfg.power_level = dbm; + } else { + power_cfg.is_power_auto = 1; + } + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + return mwifiex_set_tx_power(priv, &power_cfg); +} + +/* + * CFG802.11 operation handler to set Power Save option. + * + * The timeout value, if provided, is currently ignored. + */ +static int +mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, + struct net_device *dev, + bool enabled, int timeout) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + u32 ps_mode; + + if (timeout) + mwifiex_dbg(priv->adapter, INFO, + "info: ignore timeout value for IEEE Power Save\n"); + + ps_mode = enabled; + + return mwifiex_drv_set_power(priv, &ps_mode); +} + +/* + * CFG802.11 operation handler to set the default network key. + */ +static int +mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool unicast, + bool multicast) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); + + /* Return if WEP key not configured */ + if (!priv->sec_info.wep_enabled) + return 0; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) { + priv->wep_key_curr_index = key_index; + } else if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, + NULL, 0)) { + mwifiex_dbg(priv->adapter, ERROR, "set default Tx key index\n"); + return -EFAULT; + } + + return 0; +} + +/* + * CFG802.11 operation handler to add a network key. + */ +static int +mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, + u8 key_index, bool pairwise, const u8 *mac_addr, + struct key_params *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); + struct mwifiex_wep_key *wep_key; + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const u8 *peer_mac = pairwise ? mac_addr : bc_mac; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && + (params->cipher == WLAN_CIPHER_SUITE_WEP40 || + params->cipher == WLAN_CIPHER_SUITE_WEP104)) { + if (params->key && params->key_len) { + wep_key = &priv->wep_key[key_index]; + memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); + memcpy(wep_key->key_material, params->key, + params->key_len); + wep_key->key_index = key_index; + wep_key->key_length = params->key_len; + priv->sec_info.wep_enabled = 1; + } + return 0; + } + + if (mwifiex_set_encode(priv, params, params->key, params->key_len, + key_index, peer_mac, 0)) { + mwifiex_dbg(priv->adapter, ERROR, "crypto keys added\n"); + return -EFAULT; + } + + return 0; +} + +/* + * This function sends domain information to the firmware. + * + * The following information are passed to the firmware - + * - Country codes + * - Sub bands (first channel, number of channels, maximum Tx power) + */ +int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) +{ + u8 no_of_triplet = 0; + struct ieee80211_country_ie_triplet *t; + u8 no_of_parsed_chan = 0; + u8 first_chan = 0, next_chan = 0, max_pwr = 0; + u8 i, flag = 0; + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg; + + /* Set country code */ + domain_info->country_code[0] = adapter->country_code[0]; + domain_info->country_code[1] = adapter->country_code[1]; + domain_info->country_code[2] = ' '; + + band = mwifiex_band_to_radio_type(adapter->config_bands); + if (!wiphy->bands[band]) { + mwifiex_dbg(adapter, ERROR, + "11D: setting domain info in FW\n"); + return -1; + } + + sband = wiphy->bands[band]; + + for (i = 0; i < sband->n_channels ; i++) { + ch = &sband->channels[i]; + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (!flag) { + flag = 1; + first_chan = (u32) ch->hw_value; + next_chan = first_chan; + max_pwr = ch->max_power; + no_of_parsed_chan = 1; + continue; + } + + if (ch->hw_value == next_chan + 1 && + ch->max_power == max_pwr) { + next_chan++; + no_of_parsed_chan++; + } else { + t = &domain_info->triplet[no_of_triplet]; + t->chans.first_channel = first_chan; + t->chans.num_channels = no_of_parsed_chan; + t->chans.max_power = max_pwr; + no_of_triplet++; + first_chan = (u32) ch->hw_value; + next_chan = first_chan; + max_pwr = ch->max_power; + no_of_parsed_chan = 1; + } + } + + if (flag) { + t = &domain_info->triplet[no_of_triplet]; + t->chans.first_channel = first_chan; + t->chans.num_channels = no_of_parsed_chan; + t->chans.max_power = max_pwr; + no_of_triplet++; + } + + domain_info->no_of_triplet = no_of_triplet; + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { + mwifiex_dbg(adapter, INFO, + "11D: setting domain info in FW\n"); + return -1; + } + + return 0; +} + +/* + * CFG802.11 regulatory domain callback function. + * + * This function is called when the regulatory domain is changed due to the + * following reasons - + * - Set by driver + * - Set by system core + * - Set by user + * - Set bt Country IE + */ +static void mwifiex_reg_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv = mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY); + mwifiex_dbg(adapter, INFO, + "info: cfg80211 regulatory domain callback for %c%c\n", + request->alpha2[0], request->alpha2[1]); + + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_DRIVER: + case NL80211_REGDOM_SET_BY_CORE: + case NL80211_REGDOM_SET_BY_USER: + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + break; + default: + mwifiex_dbg(adapter, ERROR, + "unknown regdom initiator: %d\n", + request->initiator); + return; + } + + /* Don't send world or same regdom info to firmware */ + if (strncmp(request->alpha2, "00", 2) && + strncmp(request->alpha2, adapter->country_code, + sizeof(request->alpha2))) { + memcpy(adapter->country_code, request->alpha2, + sizeof(request->alpha2)); + mwifiex_send_domain_info_cmd_fw(wiphy); + mwifiex_dnld_txpwr_table(priv); + } +} + +/* + * This function sets the fragmentation threshold. + * + * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE + * and MWIFIEX_FRAG_MAX_VALUE. + */ +static int +mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) +{ + if (frag_thr < MWIFIEX_FRAG_MIN_VALUE || + frag_thr > MWIFIEX_FRAG_MAX_VALUE) + frag_thr = MWIFIEX_FRAG_MAX_VALUE; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, FRAG_THRESH_I, + &frag_thr, true); +} + +/* + * This function sets the RTS threshold. + + * The rts value must lie between MWIFIEX_RTS_MIN_VALUE + * and MWIFIEX_RTS_MAX_VALUE. + */ +static int +mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) +{ + if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) + rts_thr = MWIFIEX_RTS_MAX_VALUE; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, RTS_THRESH_I, + &rts_thr, true); +} + +/* + * CFG802.11 operation handler to set wiphy parameters. + * + * This function can be used to set the RTS threshold and the + * Fragmentation threshold of the driver. + */ +static int +mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct mwifiex_uap_bss_param *bss_cfg; + int ret; + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + switch (priv->bss_role) { + case MWIFIEX_BSS_ROLE_UAP: + if (priv->bss_started) { + mwifiex_dbg(adapter, ERROR, + "cannot change wiphy params when bss started"); + return -EINVAL; + } + + bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL); + if (!bss_cfg) + return -ENOMEM; + + mwifiex_set_sys_config_invalid_data(bss_cfg); + + if (changed & WIPHY_PARAM_RTS_THRESHOLD) + bss_cfg->rts_threshold = wiphy->rts_threshold; + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) + bss_cfg->frag_threshold = wiphy->frag_threshold; + if (changed & WIPHY_PARAM_RETRY_LONG) + bss_cfg->retry_limit = wiphy->retry_long; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, + false); + + kfree(bss_cfg); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "Failed to set wiphy phy params\n"); + return ret; + } + break; + + case MWIFIEX_BSS_ROLE_STA: + if (priv->media_connected) { + mwifiex_dbg(adapter, ERROR, + "cannot change wiphy params when connected"); + return -EINVAL; + } + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + ret = mwifiex_set_rts(priv, + wiphy->rts_threshold); + if (ret) + return ret; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + ret = mwifiex_set_frag(priv, + wiphy->frag_threshold); + if (ret) + return ret; + } + break; + } + + return 0; +} + +static int +mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv) +{ + u16 mode = P2P_MODE_DISABLE; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + return 0; +} + +/* + * This function initializes the functionalities for P2P client. + * The P2P client initialization sequence is: + * disable -> device -> client + */ +static int +mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv) +{ + u16 mode; + + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -1; + + mode = P2P_MODE_DEVICE; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + mode = P2P_MODE_CLIENT; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + return 0; +} + +/* + * This function initializes the functionalities for P2P GO. + * The P2P GO initialization sequence is: + * disable -> device -> GO + */ +static int +mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv) +{ + u16 mode; + + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -1; + + mode = P2P_MODE_DEVICE; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + mode = P2P_MODE_GO; + if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, + HostCmd_ACT_GEN_SET, 0, &mode, true)) + return -1; + + return 0; +} + +static int mwifiex_deinit_priv_params(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + unsigned long flags; + + priv->mgmt_frame_mask = 0; + if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, + HostCmd_ACT_GEN_SET, 0, + &priv->mgmt_frame_mask, false)) { + mwifiex_dbg(adapter, ERROR, + "could not unregister mgmt frame rx\n"); + return -1; + } + + mwifiex_deauthenticate(priv, NULL); + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + adapter->main_locked = true; + if (adapter->mwifiex_processing) { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + flush_workqueue(adapter->workqueue); + } else { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + adapter->rx_locked = true; + if (adapter->rx_processing) { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + flush_workqueue(adapter->rx_workqueue); + } else { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } + + mwifiex_free_priv(priv); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; + + return 0; +} + +static int +mwifiex_init_new_priv_params(struct mwifiex_private *priv, + struct net_device *dev, + enum nl80211_iftype type) +{ + struct mwifiex_adapter *adapter = priv->adapter; + unsigned long flags; + + mwifiex_init_priv(priv); + + priv->bss_mode = type; + priv->wdev.iftype = type; + + mwifiex_init_priv_params(priv, priv->netdev); + priv->bss_started = 0; + + switch (type) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_type = MWIFIEX_BSS_TYPE_STA; + break; + case NL80211_IFTYPE_P2P_CLIENT: + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_type = MWIFIEX_BSS_TYPE_P2P; + break; + case NL80211_IFTYPE_P2P_GO: + priv->bss_role = MWIFIEX_BSS_ROLE_UAP; + priv->bss_type = MWIFIEX_BSS_TYPE_P2P; + break; + case NL80211_IFTYPE_AP: + priv->bss_type = MWIFIEX_BSS_TYPE_UAP; + priv->bss_role = MWIFIEX_BSS_ROLE_UAP; + break; + default: + mwifiex_dbg(adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + adapter->main_locked = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + adapter->rx_locked = false; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + + return 0; +} + +static int +mwifiex_change_vif_to_p2p(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter; + + priv = mwifiex_netdev_get_priv(dev); + + if (!priv) + return -1; + + adapter = priv->adapter; + + if (adapter->curr_iface_comb.p2p_intf == + adapter->iface_limit.p2p_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple P2P ifaces\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "%s: changing role to p2p\n", dev->name); + + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + + switch (type) { + case NL80211_IFTYPE_P2P_CLIENT: + if (mwifiex_cfg80211_init_p2p_client(priv)) + return -EFAULT; + break; + case NL80211_IFTYPE_P2P_GO: + if (mwifiex_cfg80211_init_p2p_go(priv)) + return -EFAULT; + break; + default: + mwifiex_dbg(adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true)) + return -1; + + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; + + switch (curr_iftype) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf--; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf--; + break; + default: + break; + } + + adapter->curr_iface_comb.p2p_intf++; + dev->ieee80211_ptr->iftype = type; + + return 0; +} + +static int +mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter; + + priv = mwifiex_netdev_get_priv(dev); + + if (!priv) + return -1; + + adapter = priv->adapter; + + if ((curr_iftype != NL80211_IFTYPE_P2P_CLIENT && + curr_iftype != NL80211_IFTYPE_P2P_GO) && + (adapter->curr_iface_comb.sta_intf == + adapter->iface_limit.sta_intf)) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple station/adhoc ifaces\n"); + return -1; + } + + if (type == NL80211_IFTYPE_STATION) + mwifiex_dbg(adapter, INFO, + "%s: changing role to station\n", dev->name); + else + mwifiex_dbg(adapter, INFO, + "%s: changing role to adhoc\n", dev->name); + + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true)) + return -1; + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; + + switch (curr_iftype) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + adapter->curr_iface_comb.p2p_intf--; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf--; + break; + default: + break; + } + + adapter->curr_iface_comb.sta_intf++; + dev->ieee80211_ptr->iftype = type; + return 0; +} + +static int +mwifiex_change_vif_to_ap(struct net_device *dev, + enum nl80211_iftype curr_iftype, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter; + + priv = mwifiex_netdev_get_priv(dev); + + if (!priv) + return -1; + + adapter = priv->adapter; + + if (adapter->curr_iface_comb.uap_intf == + adapter->iface_limit.uap_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple AP ifaces\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "%s: changing role to AP\n", dev->name); + + if (mwifiex_deinit_priv_params(priv)) + return -1; + if (mwifiex_init_new_priv_params(priv, dev, type)) + return -1; + if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, true)) + return -1; + if (mwifiex_sta_init_cmd(priv, false, false)) + return -1; + + switch (curr_iftype) { + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + adapter->curr_iface_comb.p2p_intf--; + break; + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf--; + break; + default: + break; + } + + adapter->curr_iface_comb.uap_intf++; + dev->ieee80211_ptr->iftype = type; + return 0; +} +/* + * CFG802.11 operation handler to change interface type. + */ +static int +mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, u32 *flags, + struct vif_params *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype; + + switch (curr_iftype) { + case NL80211_IFTYPE_ADHOC: + switch (type) { + case NL80211_IFTYPE_STATION: + priv->bss_mode = type; + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_OPEN_SYSTEM; + dev->ieee80211_ptr->iftype = type; + mwifiex_deauthenticate(priv, NULL); + return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return mwifiex_change_vif_to_p2p(dev, curr_iftype, + type, flags, params); + case NL80211_IFTYPE_AP: + return mwifiex_change_vif_to_ap(dev, curr_iftype, type, + flags, params); + case NL80211_IFTYPE_UNSPECIFIED: + mwifiex_dbg(priv->adapter, INFO, + "%s: kept type as IBSS\n", dev->name); + case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */ + return 0; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + case NL80211_IFTYPE_STATION: + switch (type) { + case NL80211_IFTYPE_ADHOC: + priv->bss_mode = type; + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_OPEN_SYSTEM; + dev->ieee80211_ptr->iftype = type; + mwifiex_deauthenticate(priv, NULL); + return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return mwifiex_change_vif_to_p2p(dev, curr_iftype, + type, flags, params); + case NL80211_IFTYPE_AP: + return mwifiex_change_vif_to_ap(dev, curr_iftype, type, + flags, params); + case NL80211_IFTYPE_UNSPECIFIED: + mwifiex_dbg(priv->adapter, INFO, + "%s: kept type as STA\n", dev->name); + case NL80211_IFTYPE_STATION: /* This shouldn't happen */ + return 0; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + case NL80211_IFTYPE_AP: + switch (type) { + case NL80211_IFTYPE_ADHOC: + case NL80211_IFTYPE_STATION: + return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, + type, flags, + params); + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return mwifiex_change_vif_to_p2p(dev, curr_iftype, + type, flags, params); + case NL80211_IFTYPE_UNSPECIFIED: + mwifiex_dbg(priv->adapter, INFO, + "%s: kept type as AP\n", dev->name); + case NL80211_IFTYPE_AP: /* This shouldn't happen */ + return 0; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + switch (type) { + case NL80211_IFTYPE_STATION: + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -EFAULT; + priv->adapter->curr_iface_comb.p2p_intf--; + priv->adapter->curr_iface_comb.sta_intf++; + dev->ieee80211_ptr->iftype = type; + break; + case NL80211_IFTYPE_ADHOC: + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -EFAULT; + return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, + type, flags, + params); + break; + case NL80211_IFTYPE_AP: + if (mwifiex_cfg80211_deinit_p2p(priv)) + return -EFAULT; + return mwifiex_change_vif_to_ap(dev, curr_iftype, type, + flags, params); + case NL80211_IFTYPE_UNSPECIFIED: + mwifiex_dbg(priv->adapter, INFO, + "%s: kept type as P2P\n", dev->name); + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + return 0; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: changing to %d not supported\n", + dev->name, type); + return -EOPNOTSUPP; + } + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "%s: unknown iftype: %d\n", + dev->name, dev->ieee80211_ptr->iftype); + return -EOPNOTSUPP; + } + + + return 0; +} + +static void +mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo, + struct rate_info *rate) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (adapter->is_hw_11ac_capable) { + /* bit[1-0]: 00=LG 01=HT 10=VHT */ + if (tx_htinfo & BIT(0)) { + /* HT */ + rate->mcs = priv->tx_rate; + rate->flags |= RATE_INFO_FLAGS_MCS; + } + if (tx_htinfo & BIT(1)) { + /* VHT */ + rate->mcs = priv->tx_rate & 0x0F; + rate->flags |= RATE_INFO_FLAGS_VHT_MCS; + } + + if (tx_htinfo & (BIT(1) | BIT(0))) { + /* HT or VHT */ + switch (tx_htinfo & (BIT(3) | BIT(2))) { + case 0: + rate->bw = RATE_INFO_BW_20; + break; + case (BIT(2)): + rate->bw = RATE_INFO_BW_40; + break; + case (BIT(3)): + rate->bw = RATE_INFO_BW_80; + break; + case (BIT(3) | BIT(2)): + rate->bw = RATE_INFO_BW_160; + break; + } + + if (tx_htinfo & BIT(4)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + + if ((priv->tx_rate >> 4) == 1) + rate->nss = 2; + else + rate->nss = 1; + } + } else { + /* + * Bit 0 in tx_htinfo indicates that current Tx rate + * is 11n rate. Valid MCS index values for us are 0 to 15. + */ + if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) { + rate->mcs = priv->tx_rate; + rate->flags |= RATE_INFO_FLAGS_MCS; + rate->bw = RATE_INFO_BW_20; + if (tx_htinfo & BIT(1)) + rate->bw = RATE_INFO_BW_40; + if (tx_htinfo & BIT(2)) + rate->flags |= RATE_INFO_FLAGS_SHORT_GI; + } + } +} + +/* + * This function dumps the station information on a buffer. + * + * The following information are shown - + * - Total bytes transmitted + * - Total bytes received + * - Total packets transmitted + * - Total packets received + * - Signal quality level + * - Transmission rate + */ +static int +mwifiex_dump_station_info(struct mwifiex_private *priv, + struct mwifiex_sta_node *node, + struct station_info *sinfo) +{ + u32 rate; + + sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) | + BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) | + BIT(NL80211_STA_INFO_TX_BITRATE) | + BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + if (!node) + return -ENOENT; + + sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) | + BIT(NL80211_STA_INFO_TX_FAILED); + sinfo->inactive_time = + jiffies_to_msecs(jiffies - node->stats.last_rx); + + sinfo->signal = node->stats.rssi; + sinfo->signal_avg = node->stats.rssi; + sinfo->rx_bytes = node->stats.rx_bytes; + sinfo->tx_bytes = node->stats.tx_bytes; + sinfo->rx_packets = node->stats.rx_packets; + sinfo->tx_packets = node->stats.tx_packets; + sinfo->tx_failed = node->stats.tx_failed; + + mwifiex_parse_htinfo(priv, node->stats.last_tx_htinfo, + &sinfo->txrate); + sinfo->txrate.legacy = node->stats.last_tx_rate * 5; + + return 0; + } + + /* Get signal information from the firmware */ + if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "failed to get signal information\n"); + return -EFAULT; + } + + if (mwifiex_drv_get_data_rate(priv, &rate)) { + mwifiex_dbg(priv->adapter, ERROR, + "getting data rate error\n"); + return -EFAULT; + } + + /* Get DTIM period information from firmware */ + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, DTIM_PERIOD_I, + &priv->dtim_period, true); + + mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate); + + sinfo->signal_avg = priv->bcn_rssi_avg; + 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 = priv->bcn_rssi_avg; + /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */ + sinfo->txrate.legacy = rate * 5; + + if (priv->bss_mode == NL80211_IFTYPE_STATION) { + sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); + sinfo->bss_param.flags = 0; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_PREAMBLE) + sinfo->bss_param.flags |= + BSS_PARAM_FLAGS_SHORT_PREAMBLE; + if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & + WLAN_CAPABILITY_SHORT_SLOT_TIME) + sinfo->bss_param.flags |= + BSS_PARAM_FLAGS_SHORT_SLOT_TIME; + sinfo->bss_param.dtim_period = priv->dtim_period; + sinfo->bss_param.beacon_interval = + priv->curr_bss_params.bss_descriptor.beacon_period; + } + + return 0; +} + +/* + * CFG802.11 operation handler to get station information. + * + * This function only works in connected mode, and dumps the + * requested station information, if available. + */ +static int +mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_info *sinfo) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!priv->media_connected) + return -ENOENT; + if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) + return -ENOENT; + + return mwifiex_dump_station_info(priv, NULL, sinfo); +} + +/* + * CFG802.11 operation handler to dump station information. + */ +static int +mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, + int idx, u8 *mac, struct station_info *sinfo) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + static struct mwifiex_sta_node *node; + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + priv->media_connected && idx == 0) { + ether_addr_copy(mac, priv->cfg_bssid); + return mwifiex_dump_station_info(priv, NULL, sinfo); + } else if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + mwifiex_send_cmd(priv, HOST_CMD_APCMD_STA_LIST, + HostCmd_ACT_GEN_GET, 0, NULL, true); + + if (node && (&node->list == &priv->sta_list)) { + node = NULL; + return -ENOENT; + } + + node = list_prepare_entry(node, &priv->sta_list, list); + list_for_each_entry_continue(node, &priv->sta_list, list) { + ether_addr_copy(mac, node->mac_addr); + return mwifiex_dump_station_info(priv, node, sinfo); + } + } + + return -ENOENT; +} + +static int +mwifiex_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, + int idx, struct survey_info *survey) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_chan_stats *pchan_stats = priv->adapter->chan_stats; + enum ieee80211_band band; + + mwifiex_dbg(priv->adapter, DUMP, "dump_survey idx=%d\n", idx); + + memset(survey, 0, sizeof(struct survey_info)); + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + priv->media_connected && idx == 0) { + u8 curr_bss_band = priv->curr_bss_params.band; + u32 chan = priv->curr_bss_params.bss_descriptor.channel; + + band = mwifiex_band_to_radio_type(curr_bss_band); + survey->channel = ieee80211_get_channel(wiphy, + ieee80211_channel_to_frequency(chan, band)); + + if (priv->bcn_nf_last) { + survey->filled = SURVEY_INFO_NOISE_DBM; + survey->noise = priv->bcn_nf_last; + } + return 0; + } + + if (idx >= priv->adapter->num_in_chan_stats) + return -ENOENT; + + if (!pchan_stats[idx].cca_scan_dur) + return 0; + + band = pchan_stats[idx].bandcfg; + survey->channel = ieee80211_get_channel(wiphy, + ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band)); + survey->filled = SURVEY_INFO_NOISE_DBM | + SURVEY_INFO_TIME | + SURVEY_INFO_TIME_BUSY; + survey->noise = pchan_stats[idx].noise; + survey->time = pchan_stats[idx].cca_scan_dur; + survey->time_busy = pchan_stats[idx].cca_busy_dur; + + return 0; +} + +/* Supported rates to be advertised to the cfg80211 */ +static struct ieee80211_rate mwifiex_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 to be advertised to cfg80211 */ +static struct ieee80211_channel mwifiex_channels_2ghz[] = { + {.center_freq = 2412, .hw_value = 1, }, + {.center_freq = 2417, .hw_value = 2, }, + {.center_freq = 2422, .hw_value = 3, }, + {.center_freq = 2427, .hw_value = 4, }, + {.center_freq = 2432, .hw_value = 5, }, + {.center_freq = 2437, .hw_value = 6, }, + {.center_freq = 2442, .hw_value = 7, }, + {.center_freq = 2447, .hw_value = 8, }, + {.center_freq = 2452, .hw_value = 9, }, + {.center_freq = 2457, .hw_value = 10, }, + {.center_freq = 2462, .hw_value = 11, }, + {.center_freq = 2467, .hw_value = 12, }, + {.center_freq = 2472, .hw_value = 13, }, + {.center_freq = 2484, .hw_value = 14, }, +}; + +static struct ieee80211_supported_band mwifiex_band_2ghz = { + .channels = mwifiex_channels_2ghz, + .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz), + .bitrates = mwifiex_rates, + .n_bitrates = ARRAY_SIZE(mwifiex_rates), +}; + +static struct ieee80211_channel mwifiex_channels_5ghz[] = { + {.center_freq = 5040, .hw_value = 8, }, + {.center_freq = 5060, .hw_value = 12, }, + {.center_freq = 5080, .hw_value = 16, }, + {.center_freq = 5170, .hw_value = 34, }, + {.center_freq = 5190, .hw_value = 38, }, + {.center_freq = 5210, .hw_value = 42, }, + {.center_freq = 5230, .hw_value = 46, }, + {.center_freq = 5180, .hw_value = 36, }, + {.center_freq = 5200, .hw_value = 40, }, + {.center_freq = 5220, .hw_value = 44, }, + {.center_freq = 5240, .hw_value = 48, }, + {.center_freq = 5260, .hw_value = 52, }, + {.center_freq = 5280, .hw_value = 56, }, + {.center_freq = 5300, .hw_value = 60, }, + {.center_freq = 5320, .hw_value = 64, }, + {.center_freq = 5500, .hw_value = 100, }, + {.center_freq = 5520, .hw_value = 104, }, + {.center_freq = 5540, .hw_value = 108, }, + {.center_freq = 5560, .hw_value = 112, }, + {.center_freq = 5580, .hw_value = 116, }, + {.center_freq = 5600, .hw_value = 120, }, + {.center_freq = 5620, .hw_value = 124, }, + {.center_freq = 5640, .hw_value = 128, }, + {.center_freq = 5660, .hw_value = 132, }, + {.center_freq = 5680, .hw_value = 136, }, + {.center_freq = 5700, .hw_value = 140, }, + {.center_freq = 5745, .hw_value = 149, }, + {.center_freq = 5765, .hw_value = 153, }, + {.center_freq = 5785, .hw_value = 157, }, + {.center_freq = 5805, .hw_value = 161, }, + {.center_freq = 5825, .hw_value = 165, }, +}; + +static struct ieee80211_supported_band mwifiex_band_5ghz = { + .channels = mwifiex_channels_5ghz, + .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz), + .bitrates = mwifiex_rates + 4, + .n_bitrates = ARRAY_SIZE(mwifiex_rates) - 4, +}; + + +/* Supported crypto cipher suits to be advertised to cfg80211 */ +static const u32 mwifiex_cipher_suites[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_AES_CMAC, +}; + +/* Supported mgmt frame types to be advertised to cfg80211 */ +static const struct ieee80211_txrx_stypes +mwifiex_mgmt_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_AP] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_RESP >> 4), + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4), + }, +}; + +/* + * CFG802.11 operation handler for setting bit rates. + * + * Function configures data rates to firmware using bitrate mask + * provided by cfg80211. + */ +static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + enum ieee80211_band band; + struct mwifiex_adapter *adapter = priv->adapter; + + if (!priv->media_connected) { + mwifiex_dbg(adapter, ERROR, + "Can not set Tx data rate in disconnected state\n"); + return -EINVAL; + } + + band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + + memset(bitmap_rates, 0, sizeof(bitmap_rates)); + + /* Fill HR/DSSS rates. */ + if (band == IEEE80211_BAND_2GHZ) + bitmap_rates[0] = mask->control[band].legacy & 0x000f; + + /* Fill OFDM rates */ + if (band == IEEE80211_BAND_2GHZ) + bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4; + else + bitmap_rates[1] = mask->control[band].legacy; + + /* Fill HT MCS rates */ + bitmap_rates[2] = mask->control[band].ht_mcs[0]; + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; + + /* Fill VHT MCS rates */ + if (adapter->fw_api_ver == MWIFIEX_FW_V15) { + bitmap_rates[10] = mask->control[band].vht_mcs[0]; + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + bitmap_rates[11] = mask->control[band].vht_mcs[1]; + } + + return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_SET, 0, bitmap_rates, true); +} + +/* + * CFG802.11 operation handler for connection quality monitoring. + * + * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI + * events to FW. + */ +static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, + struct net_device *dev, + s32 rssi_thold, u32 rssi_hyst) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_ds_misc_subsc_evt subsc_evt; + + priv->cqm_rssi_thold = rssi_thold; + priv->cqm_rssi_hyst = rssi_hyst; + + memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); + subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + + /* Subscribe/unsubscribe low and high rssi events */ + if (rssi_thold && rssi_hyst) { + subsc_evt.action = HostCmd_ACT_BITWISE_SET; + subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold); + subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold); + subsc_evt.bcn_l_rssi_cfg.evt_freq = 1; + subsc_evt.bcn_h_rssi_cfg.evt_freq = 1; + return mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); + } else { + subsc_evt.action = HostCmd_ACT_BITWISE_CLR; + return mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, &subsc_evt, true); + } + + return 0; +} + +/* cfg80211 operation handler for change_beacon. + * Function retrieves and sets modified management IEs to FW. + */ +static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_beacon_data *data) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: bss_type mismatched\n", __func__); + return -EINVAL; + } + + if (!priv->bss_started) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: bss not started\n", __func__); + return -EINVAL; + } + + if (mwifiex_set_mgmt_ies(priv, data)) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: setting mgmt ies failed\n", __func__); + return -EFAULT; + } + + return 0; +} + +/* cfg80211 operation handler for del_station. + * Function deauthenticates station which value is provided in mac parameter. + * If mac is NULL/broadcast, all stations in associated station list are + * deauthenticated. If bss is not started or there are no stations in + * associated stations list, no action is taken. + */ +static int +mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, + struct station_del_parameters *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_sta_node *sta_node; + u8 deauth_mac[ETH_ALEN]; + unsigned long flags; + + if (list_empty(&priv->sta_list) || !priv->bss_started) + return 0; + + if (!params->mac || is_broadcast_ether_addr(params->mac)) + return 0; + + mwifiex_dbg(priv->adapter, INFO, "%s: mac address %pM\n", + __func__, params->mac); + + eth_zero_addr(deauth_mac); + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_node = mwifiex_get_sta_entry(priv, params->mac); + if (sta_node) + ether_addr_copy(deauth_mac, params->mac); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (is_valid_ether_addr(deauth_mac)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH, + HostCmd_ACT_GEN_SET, 0, + deauth_mac, true)) + return -1; + } + + return 0; +} + +static int +mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv = mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY); + struct mwifiex_ds_ant_cfg ant_cfg; + + if (!tx_ant || !rx_ant) + return -EOPNOTSUPP; + + if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) { + /* Not a MIMO chip. User should provide specific antenna number + * for Tx/Rx path or enable all antennas for diversity + */ + if (tx_ant != rx_ant) + return -EOPNOTSUPP; + + if ((tx_ant & (tx_ant - 1)) && + (tx_ant != BIT(adapter->number_of_antenna) - 1)) + return -EOPNOTSUPP; + + if ((tx_ant == BIT(adapter->number_of_antenna) - 1) && + (priv->adapter->number_of_antenna > 1)) { + tx_ant = RF_ANTENNA_AUTO; + rx_ant = RF_ANTENNA_AUTO; + } + } else { + struct ieee80211_sta_ht_cap *ht_info; + int rx_mcs_supp; + enum ieee80211_band band; + + if ((tx_ant == 0x1 && rx_ant == 0x1)) { + adapter->user_dev_mcs_support = HT_STREAM_1X1; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + MWIFIEX_11AC_MCS_MAP_1X1; + } else { + adapter->user_dev_mcs_support = HT_STREAM_2X2; + if (adapter->is_hw_11ac_capable) + adapter->usr_dot_11ac_mcs_support = + MWIFIEX_11AC_MCS_MAP_2X2; + } + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + if (!adapter->wiphy->bands[band]) + continue; + + ht_info = &adapter->wiphy->bands[band]->ht_cap; + rx_mcs_supp = + GET_RXMCSSUPP(adapter->user_dev_mcs_support); + memset(&ht_info->mcs, 0, adapter->number_of_antenna); + memset(&ht_info->mcs, 0xff, rx_mcs_supp); + } + } + + ant_cfg.tx_ant = tx_ant; + ant_cfg.rx_ant = rx_ant; + + return mwifiex_send_cmd(priv, HostCmd_CMD_RF_ANTENNA, + HostCmd_ACT_GEN_SET, 0, &ant_cfg, true); +} + +/* cfg80211 operation handler for stop ap. + * Function stops BSS running at uAP interface. + */ +static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + mwifiex_abort_cac(priv); + + if (mwifiex_del_mgmt_ies(priv)) + mwifiex_dbg(priv->adapter, ERROR, + "Failed to delete mgmt IEs!\n"); + + priv->ap_11n_enabled = 0; + memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg)); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to stop the BSS\n"); + return -1; + } + + if (mwifiex_send_cmd(priv, HOST_CMD_APCMD_SYS_RESET, + HostCmd_ACT_GEN_SET, 0, NULL, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to reset BSS\n"); + return -1; + } + + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); + + return 0; +} + +/* cfg80211 operation handler for start_ap. + * Function sets beacon period, DTIM period, SSID and security into + * AP config structure. + * AP is configured with these settings and BSS is started. + */ +static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_ap_settings *params) +{ + struct mwifiex_uap_bss_param *bss_cfg; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) + return -1; + + bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL); + if (!bss_cfg) + return -ENOMEM; + + mwifiex_set_sys_config_invalid_data(bss_cfg); + + if (params->beacon_interval) + bss_cfg->beacon_period = params->beacon_interval; + if (params->dtim_period) + bss_cfg->dtim_period = params->dtim_period; + + if (params->ssid && params->ssid_len) { + memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len); + bss_cfg->ssid.ssid_len = params->ssid_len; + } + if (params->inactivity_timeout > 0) { + /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */ + bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout; + bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout; + } + + switch (params->hidden_ssid) { + case NL80211_HIDDEN_SSID_NOT_IN_USE: + bss_cfg->bcast_ssid_ctl = 1; + break; + case NL80211_HIDDEN_SSID_ZERO_LEN: + bss_cfg->bcast_ssid_ctl = 0; + break; + case NL80211_HIDDEN_SSID_ZERO_CONTENTS: + /* firmware doesn't support this type of hidden SSID */ + default: + kfree(bss_cfg); + return -EINVAL; + } + + mwifiex_uap_set_channel(priv, bss_cfg, params->chandef); + mwifiex_set_uap_rates(bss_cfg, params); + + if (mwifiex_set_secure_params(priv, bss_cfg, params)) { + kfree(bss_cfg); + mwifiex_dbg(priv->adapter, ERROR, + "Failed to parse secuirty parameters!\n"); + return -1; + } + + mwifiex_set_ht_params(priv, bss_cfg, params); + + if (priv->adapter->is_hw_11ac_capable) { + mwifiex_set_vht_params(priv, bss_cfg, params); + mwifiex_set_vht_width(priv, params->chandef.width, + priv->ap_11ac_enabled); + } + + if (priv->ap_11ac_enabled) + mwifiex_set_11ac_ba_params(priv); + else + mwifiex_set_ba_params(priv); + + mwifiex_set_wmm_params(priv, bss_cfg, params); + + if (mwifiex_is_11h_active(priv)) + mwifiex_set_tpc_params(priv, bss_cfg, params); + + if (mwifiex_is_11h_active(priv) && + !cfg80211_chandef_dfs_required(wiphy, ¶ms->chandef, + priv->bss_mode)) { + mwifiex_dbg(priv->adapter, INFO, + "Disable 11h extensions in FW\n"); + if (mwifiex_11h_activate(priv, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to disable 11h extensions!!"); + return -1; + } + priv->state_11h.is_11h_active = false; + } + + if (mwifiex_config_start_uap(priv, bss_cfg)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to start AP\n"); + kfree(bss_cfg); + return -1; + } + + if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon)) + return -1; + + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, priv->adapter); + + memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg)); + kfree(bss_cfg); + return 0; +} + +/* + * CFG802.11 operation handler for disconnection request. + * + * This function does not work when there is already a disconnection + * procedure going on. + */ +static int +mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, + u16 reason_code) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (mwifiex_deauthenticate(priv, NULL)) + return -EFAULT; + + mwifiex_dbg(priv->adapter, MSG, + "info: successfully disconnected from %pM:\t" + "reason code %d\n", priv->cfg_bssid, reason_code); + + eth_zero_addr(priv->cfg_bssid); + priv->hs2_enabled = false; + + return 0; +} + +/* + * This function informs the CFG802.11 subsystem of a new IBSS. + * + * The following information are sent to the CFG802.11 subsystem + * to register the new IBSS. If we do not register the new IBSS, + * a kernel panic will result. + * - SSID + * - SSID length + * - BSSID + * - Channel + */ +static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) +{ + struct ieee80211_channel *chan; + struct mwifiex_bss_info bss_info; + struct cfg80211_bss *bss; + int ie_len; + u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)]; + enum ieee80211_band band; + + if (mwifiex_get_bss_info(priv, &bss_info)) + return -1; + + ie_buf[0] = WLAN_EID_SSID; + ie_buf[1] = bss_info.ssid.ssid_len; + + memcpy(&ie_buf[sizeof(struct ieee_types_header)], + &bss_info.ssid.ssid, bss_info.ssid.ssid_len); + ie_len = ie_buf[1] + sizeof(struct ieee_types_header); + + band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + chan = __ieee80211_get_channel(priv->wdev.wiphy, + ieee80211_channel_to_frequency(bss_info.bss_chan, + band)); + + bss = cfg80211_inform_bss(priv->wdev.wiphy, chan, + CFG80211_BSS_FTYPE_UNKNOWN, + bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, + 0, ie_buf, ie_len, 0, GFP_KERNEL); + if (bss) { + cfg80211_put_bss(priv->wdev.wiphy, bss); + ether_addr_copy(priv->cfg_bssid, bss_info.bssid); + } + + return 0; +} + +/* + * This function connects with a BSS. + * + * This function handles both Infra and Ad-Hoc modes. It also performs + * validity checking on the provided parameters, disconnects from the + * current BSS (if any), sets up the association/scan parameters, + * including security settings, and performs specific SSID scan before + * trying to connect. + * + * For Infra mode, the function returns failure if the specified SSID + * is not found in scan table. However, for Ad-Hoc mode, it can create + * the IBSS if it does not exist. On successful completion in either case, + * the function notifies the CFG802.11 subsystem of the new BSS connection. + */ +static int +mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, + const u8 *ssid, const u8 *bssid, int mode, + struct ieee80211_channel *channel, + struct cfg80211_connect_params *sme, bool privacy) +{ + struct cfg80211_ssid req_ssid; + int ret, auth_type = 0; + struct cfg80211_bss *bss = NULL; + u8 is_scanning_required = 0; + + memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); + + req_ssid.ssid_len = ssid_len; + if (ssid_len > IEEE80211_MAX_SSID_LEN) { + mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n"); + return -EINVAL; + } + + memcpy(req_ssid.ssid, ssid, ssid_len); + if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { + mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n"); + return -EINVAL; + } + + /* As this is new association, clear locally stored + * keys and security related flags */ + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + priv->wep_key_curr_index = 0; + priv->sec_info.encryption_mode = 0; + priv->sec_info.is_authtype_auto = 0; + ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1); + + if (mode == NL80211_IFTYPE_ADHOC) { + /* "privacy" is set only for ad-hoc mode */ + if (privacy) { + /* + * Keep WLAN_CIPHER_SUITE_WEP104 for now so that + * the firmware can find a matching network from the + * scan. The cfg80211 does not give us the encryption + * mode at this stage so just setting it to WEP here. + */ + priv->sec_info.encryption_mode = + WLAN_CIPHER_SUITE_WEP104; + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_OPEN_SYSTEM; + } + + goto done; + } + + /* Now handle infra mode. "sme" is valid for infra mode only */ + if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { + auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; + priv->sec_info.is_authtype_auto = 1; + } else { + auth_type = sme->auth_type; + } + + if (sme->crypto.n_ciphers_pairwise) { + priv->sec_info.encryption_mode = + sme->crypto.ciphers_pairwise[0]; + priv->sec_info.authentication_mode = auth_type; + } + + if (sme->crypto.cipher_group) { + priv->sec_info.encryption_mode = sme->crypto.cipher_group; + priv->sec_info.authentication_mode = auth_type; + } + if (sme->ie) + ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len); + + if (sme->key) { + if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) { + mwifiex_dbg(priv->adapter, INFO, + "info: setting wep encryption\t" + "with key len %d\n", sme->key_len); + priv->wep_key_curr_index = sme->key_idx; + ret = mwifiex_set_encode(priv, NULL, sme->key, + sme->key_len, sme->key_idx, + NULL, 0); + } + } +done: + /* + * Scan entries are valid for some time (15 sec). So we can save one + * active scan time if we just try cfg80211_get_bss first. If it fails + * then request scan and cfg80211_get_bss() again for final output. + */ + while (1) { + if (is_scanning_required) { + /* Do specific SSID scanning */ + if (mwifiex_request_scan(priv, &req_ssid)) { + mwifiex_dbg(priv->adapter, ERROR, "scan error\n"); + return -EFAULT; + } + } + + /* Find the BSS we want using available scan results */ + if (mode == NL80211_IFTYPE_ADHOC) + bss = cfg80211_get_bss(priv->wdev.wiphy, channel, + bssid, ssid, ssid_len, + IEEE80211_BSS_TYPE_IBSS, + IEEE80211_PRIVACY_ANY); + else + bss = cfg80211_get_bss(priv->wdev.wiphy, channel, + bssid, ssid, ssid_len, + IEEE80211_BSS_TYPE_ESS, + IEEE80211_PRIVACY_ANY); + + if (!bss) { + if (is_scanning_required) { + mwifiex_dbg(priv->adapter, WARN, + "assoc: requested bss not found in scan results\n"); + break; + } + is_scanning_required = 1; + } else { + mwifiex_dbg(priv->adapter, MSG, + "info: trying to associate to '%s' bssid %pM\n", + (char *)req_ssid.ssid, bss->bssid); + memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN); + break; + } + } + + ret = mwifiex_bss_start(priv, bss, &req_ssid); + if (ret) + return ret; + + if (mode == NL80211_IFTYPE_ADHOC) { + /* Inform the BSS information to kernel, otherwise + * kernel will give a panic after successful assoc */ + if (mwifiex_cfg80211_inform_ibss_bss(priv)) + return -EFAULT; + } + + return ret; +} + +/* + * CFG802.11 operation handler for association request. + * + * This function does not work when the current mode is set to Ad-Hoc, or + * when there is already an association procedure going on. The given BSS + * information is used to associate. + */ +static int +mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_connect_params *sme) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + + if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) { + mwifiex_dbg(adapter, ERROR, + "%s: reject infra assoc request in non-STA role\n", + dev->name); + return -EINVAL; + } + + if (priv->wdev.current_bss) { + mwifiex_dbg(adapter, ERROR, + "%s: already connected\n", dev->name); + return -EALREADY; + } + + if (adapter->surprise_removed || adapter->is_cmd_timedout) { + mwifiex_dbg(adapter, ERROR, + "%s: Ignore connection.\t" + "Card removed or FW in bad state\n", + dev->name); + return -EFAULT; + } + + mwifiex_dbg(adapter, INFO, + "info: Trying to associate to %s and bssid %pM\n", + (char *)sme->ssid, sme->bssid); + + ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, + priv->bss_mode, sme->channel, sme, 0); + if (!ret) { + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, + NULL, 0, WLAN_STATUS_SUCCESS, + GFP_KERNEL); + mwifiex_dbg(priv->adapter, MSG, + "info: associated to bssid %pM successfully\n", + priv->cfg_bssid); + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->auto_tdls && + priv->bss_type == MWIFIEX_BSS_TYPE_STA) + mwifiex_setup_auto_tdls_timer(priv); + } else { + mwifiex_dbg(priv->adapter, ERROR, + "info: association to bssid %pM failed\n", + priv->cfg_bssid); + eth_zero_addr(priv->cfg_bssid); + + if (ret > 0) + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, ret, + GFP_KERNEL); + else + cfg80211_connect_result(priv->netdev, priv->cfg_bssid, + NULL, 0, NULL, 0, + WLAN_STATUS_UNSPECIFIED_FAILURE, + GFP_KERNEL); + } + + return 0; +} + +/* + * This function sets following parameters for ibss network. + * - channel + * - start band + * - 11n flag + * - secondary channel offset + */ +static int mwifiex_set_ibss_params(struct mwifiex_private *priv, + struct cfg80211_ibss_params *params) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int index = 0, i; + u8 config_bands = 0; + + if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { + if (!params->basic_rates) { + config_bands = BAND_B | BAND_G; + } else { + for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) { + /* + * Rates below 6 Mbps in the table are CCK + * rates; 802.11b and from 6 they are OFDM; + * 802.11G + */ + if (mwifiex_rates[i].bitrate == 60) { + index = 1 << i; + break; + } + } + + if (params->basic_rates < index) { + config_bands = BAND_B; + } else { + config_bands = BAND_G; + if (params->basic_rates % index) + config_bands |= BAND_B; + } + } + + if (cfg80211_get_chandef_type(¶ms->chandef) != + NL80211_CHAN_NO_HT) + config_bands |= BAND_G | BAND_GN; + } else { + if (cfg80211_get_chandef_type(¶ms->chandef) == + NL80211_CHAN_NO_HT) + config_bands = BAND_A; + else + config_bands = BAND_AN | BAND_A; + } + + if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) { + adapter->config_bands = config_bands; + adapter->adhoc_start_band = config_bands; + + if ((config_bands & BAND_GN) || (config_bands & BAND_AN)) + adapter->adhoc_11n_enabled = true; + else + adapter->adhoc_11n_enabled = false; + } + + adapter->sec_chan_offset = + mwifiex_chan_type_to_sec_chan_offset( + cfg80211_get_chandef_type(¶ms->chandef)); + priv->adhoc_channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); + + mwifiex_dbg(adapter, INFO, + "info: set ibss band %d, chan %d, chan offset %d\n", + config_bands, priv->adhoc_channel, + adapter->sec_chan_offset); + + return 0; +} + +/* + * CFG802.11 operation handler to join an IBSS. + * + * This function does not work in any mode other than Ad-Hoc, or if + * a join operation is already in progress. + */ +static int +mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_ibss_params *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int ret = 0; + + if (priv->bss_mode != NL80211_IFTYPE_ADHOC) { + mwifiex_dbg(priv->adapter, ERROR, + "request to join ibss received\t" + "when station is not in ibss mode\n"); + goto done; + } + + mwifiex_dbg(priv->adapter, MSG, + "info: trying to join to %s and bssid %pM\n", + (char *)params->ssid, params->bssid); + + mwifiex_set_ibss_params(priv, params); + + ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, + params->bssid, priv->bss_mode, + params->chandef.chan, NULL, + params->privacy); +done: + if (!ret) { + cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, + params->chandef.chan, GFP_KERNEL); + mwifiex_dbg(priv->adapter, MSG, + "info: joined/created adhoc network with bssid\t" + "%pM successfully\n", priv->cfg_bssid); + } else { + mwifiex_dbg(priv->adapter, ERROR, + "info: failed creating/joining adhoc network\n"); + } + + return ret; +} + +/* + * CFG802.11 operation handler to leave an IBSS. + * + * This function does not work if a leave operation is + * already in progress. + */ +static int +mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + mwifiex_dbg(priv->adapter, MSG, "info: disconnecting from essid %pM\n", + priv->cfg_bssid); + if (mwifiex_deauthenticate(priv, NULL)) + return -EFAULT; + + eth_zero_addr(priv->cfg_bssid); + + return 0; +} + +/* + * CFG802.11 operation handler for scan request. + * + * This function issues a scan request to the firmware based upon + * the user specified scan configuration. On successful completion, + * it also informs the results. + */ +static int +mwifiex_cfg80211_scan(struct wiphy *wiphy, + struct cfg80211_scan_request *request) +{ + struct net_device *dev = request->wdev->netdev; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int i, offset, ret; + struct ieee80211_channel *chan; + struct ieee_types_header *ie; + struct mwifiex_user_scan_cfg *user_scan_cfg; + + mwifiex_dbg(priv->adapter, CMD, + "info: received scan request on %s\n", dev->name); + + /* Block scan request if scan operation or scan cleanup when interface + * is disabled is in process + */ + if (priv->scan_request || priv->scan_aborting) { + mwifiex_dbg(priv->adapter, WARN, + "cmd: Scan already in process..\n"); + return -EBUSY; + } + + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + if (!user_scan_cfg) + return -ENOMEM; + + priv->scan_request = request; + + user_scan_cfg->num_ssids = request->n_ssids; + user_scan_cfg->ssid_list = request->ssids; + + if (request->ie && request->ie_len) { + offset = 0; + for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR) + continue; + priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN; + ie = (struct ieee_types_header *)(request->ie + offset); + memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len); + offset += sizeof(*ie) + ie->len; + + if (offset >= request->ie_len) + break; + } + } + + for (i = 0; i < min_t(u32, request->n_channels, + MWIFIEX_USER_SCAN_CHAN_MAX); i++) { + chan = request->channels[i]; + user_scan_cfg->chan_list[i].chan_number = chan->hw_value; + user_scan_cfg->chan_list[i].radio_type = chan->band; + + if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids) + user_scan_cfg->chan_list[i].scan_type = + MWIFIEX_SCAN_TYPE_PASSIVE; + else + user_scan_cfg->chan_list[i].scan_type = + MWIFIEX_SCAN_TYPE_ACTIVE; + + user_scan_cfg->chan_list[i].scan_time = 0; + } + + if (priv->adapter->scan_chan_gap_enabled && + mwifiex_is_any_intf_active(priv)) + user_scan_cfg->scan_chan_gap = + priv->adapter->scan_chan_gap_time; + + ret = mwifiex_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); + if (ret) { + mwifiex_dbg(priv->adapter, ERROR, + "scan failed: %d\n", ret); + priv->scan_aborting = false; + priv->scan_request = NULL; + return ret; + } + + if (request->ie && request->ie_len) { + for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { + if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) { + priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR; + memset(&priv->vs_ie[i].ie, 0, + MWIFIEX_MAX_VSIE_LEN); + } + } + } + return 0; +} + +static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info, + struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + vht_info->vht_supported = true; + + vht_info->cap = adapter->hw_dot_11ac_dev_cap; + /* Update MCS support for VHT */ + vht_info->vht_mcs.rx_mcs_map = cpu_to_le16( + adapter->hw_dot_11ac_mcs_support & 0xFFFF); + vht_info->vht_mcs.rx_highest = 0; + vht_info->vht_mcs.tx_mcs_map = cpu_to_le16( + adapter->hw_dot_11ac_mcs_support >> 16); + vht_info->vht_mcs.tx_highest = 0; +} + +/* + * This function sets up the CFG802.11 specific HT capability fields + * with default values. + * + * The following default values are set - + * - HT Supported = True + * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K + * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE + * - HT Capabilities supported by firmware + * - MCS information, Rx mask = 0xff + * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01) + */ +static void +mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, + struct mwifiex_private *priv) +{ + int rx_mcs_supp; + struct ieee80211_mcs_info mcs_set; + u8 *mcs = (u8 *)&mcs_set; + struct mwifiex_adapter *adapter = priv->adapter; + + ht_info->ht_supported = true; + ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; + ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + + /* Fill HT capability information */ + if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + else + ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + else + ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20; + + if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + else + ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40; + + if (adapter->user_dev_mcs_support == HT_STREAM_2X2) + ht_info->cap |= 3 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + else + ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; + + if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + else + ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC; + + if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + else + ht_info->cap &= ~IEEE80211_HT_CAP_GRN_FLD; + + if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; + else + ht_info->cap &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT; + + if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap)) + ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; + else + ht_info->cap &= ~IEEE80211_HT_CAP_LDPC_CODING; + + ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; + ht_info->cap |= IEEE80211_HT_CAP_SM_PS; + + rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support); + /* Set MCS for 1x1/2x2 */ + memset(mcs, 0xff, rx_mcs_supp); + /* Clear all the other values */ + memset(&mcs[rx_mcs_supp], 0, + sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); + if (priv->bss_mode == NL80211_IFTYPE_STATION || + ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) + /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ + SETHT_MCS32(mcs_set.rx_mask); + + memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info)); + + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; +} + +/* + * create a new virtual interface with the given name and name assign type + */ +struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_private *priv; + struct net_device *dev; + void *mdev_priv; + + if (!adapter) + return ERR_PTR(-EFAULT); + + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + if (adapter->curr_iface_comb.sta_intf == + adapter->iface_limit.sta_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple sta/adhoc ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv = mwifiex_get_unused_priv(adapter); + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } + + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_STATION; + + if (type == NL80211_IFTYPE_UNSPECIFIED) + priv->bss_mode = NL80211_IFTYPE_STATION; + else + priv->bss_mode = type; + + priv->bss_type = MWIFIEX_BSS_TYPE_STA; + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = 0; + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_num = adapter->curr_iface_comb.sta_intf; + + break; + case NL80211_IFTYPE_AP: + if (adapter->curr_iface_comb.uap_intf == + adapter->iface_limit.uap_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple AP ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv = mwifiex_get_unused_priv(adapter); + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } + + priv->wdev.wiphy = wiphy; + priv->wdev.iftype = NL80211_IFTYPE_AP; + + priv->bss_type = MWIFIEX_BSS_TYPE_UAP; + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = 0; + priv->bss_role = MWIFIEX_BSS_ROLE_UAP; + priv->bss_started = 0; + priv->bss_num = adapter->curr_iface_comb.uap_intf; + priv->bss_mode = type; + + break; + case NL80211_IFTYPE_P2P_CLIENT: + if (adapter->curr_iface_comb.p2p_intf == + adapter->iface_limit.p2p_intf) { + mwifiex_dbg(adapter, ERROR, + "cannot create multiple P2P ifaces\n"); + return ERR_PTR(-EINVAL); + } + + priv = mwifiex_get_unused_priv(adapter); + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "could not get free private struct\n"); + return ERR_PTR(-EFAULT); + } + + priv->wdev.wiphy = wiphy; + /* At start-up, wpa_supplicant tries to change the interface + * to NL80211_IFTYPE_STATION if it is not managed mode. + */ + priv->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT; + priv->bss_mode = NL80211_IFTYPE_P2P_CLIENT; + + /* Setting bss_type to P2P tells firmware that this interface + * is receiving P2P peers found during find phase and doing + * action frame handshake. + */ + priv->bss_type = MWIFIEX_BSS_TYPE_P2P; + + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + priv->bss_priority = MWIFIEX_BSS_ROLE_STA; + priv->bss_role = MWIFIEX_BSS_ROLE_STA; + priv->bss_started = 0; + priv->bss_num = adapter->curr_iface_comb.p2p_intf; + + if (mwifiex_cfg80211_init_p2p_client(priv)) { + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-EFAULT); + } + + break; + default: + mwifiex_dbg(adapter, ERROR, "type not supported\n"); + return ERR_PTR(-EINVAL); + } + + dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, + name_assign_type, ether_setup, + IEEE80211_NUM_ACS, 1); + if (!dev) { + mwifiex_dbg(adapter, ERROR, + "no memory available for netdevice\n"); + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-ENOMEM); + } + + mwifiex_init_priv_params(priv, dev); + priv->netdev = dev; + + mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv); + if (adapter->is_hw_11ac_capable) + mwifiex_setup_vht_caps( + &wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap, priv); + + if (adapter->config_bands & BAND_A) + mwifiex_setup_ht_caps( + &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv); + + if ((adapter->config_bands & BAND_A) && adapter->is_hw_11ac_capable) + mwifiex_setup_vht_caps( + &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv); + + dev_net_set(dev, wiphy_net(wiphy)); + dev->ieee80211_ptr = &priv->wdev; + dev->ieee80211_ptr->iftype = priv->bss_mode; + memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); + SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); + + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; + dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; + dev->ethtool_ops = &mwifiex_ethtool_ops; + + mdev_priv = netdev_priv(dev); + *((unsigned long *) mdev_priv) = (unsigned long) priv; + + SET_NETDEV_DEV(dev, adapter->dev); + + /* Register network device */ + if (register_netdevice(dev)) { + mwifiex_dbg(adapter, ERROR, + "cannot register virtual network device\n"); + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->netdev = NULL; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-EFAULT); + } + + priv->dfs_cac_workqueue = alloc_workqueue("MWIFIEX_DFS_CAC%s", + WQ_HIGHPRI | + WQ_MEM_RECLAIM | + WQ_UNBOUND, 1, name); + if (!priv->dfs_cac_workqueue) { + mwifiex_dbg(adapter, ERROR, + "cannot register virtual network device\n"); + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->netdev = NULL; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-ENOMEM); + } + + INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue); + + priv->dfs_chan_sw_workqueue = alloc_workqueue("MWIFIEX_DFS_CHSW%s", + WQ_HIGHPRI | WQ_UNBOUND | + WQ_MEM_RECLAIM, 1, name); + if (!priv->dfs_chan_sw_workqueue) { + mwifiex_dbg(adapter, ERROR, + "cannot register virtual network device\n"); + free_netdev(dev); + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->netdev = NULL; + memset(&priv->wdev, 0, sizeof(priv->wdev)); + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + return ERR_PTR(-ENOMEM); + } + + INIT_DELAYED_WORK(&priv->dfs_chan_sw_work, + mwifiex_dfs_chan_sw_work_queue); + + sema_init(&priv->async_sem, 1); + + mwifiex_dbg(adapter, INFO, + "info: %s: Marvell 802.11 Adapter\n", dev->name); + +#ifdef CONFIG_DEBUG_FS + mwifiex_dev_debugfs_init(priv); +#endif + + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf++; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf++; + break; + case NL80211_IFTYPE_P2P_CLIENT: + adapter->curr_iface_comb.p2p_intf++; + break; + default: + mwifiex_dbg(adapter, ERROR, "type not supported\n"); + return ERR_PTR(-EINVAL); + } + + return &priv->wdev; +} +EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); + +/* + * del_virtual_intf: remove the virtual interface determined by dev + */ +int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + struct mwifiex_adapter *adapter = priv->adapter; + struct sk_buff *skb, *tmp; + +#ifdef CONFIG_DEBUG_FS + mwifiex_dev_debugfs_remove(priv); +#endif + + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + + skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + + if (wdev->netdev->reg_state == NETREG_REGISTERED) + unregister_netdevice(wdev->netdev); + + if (priv->dfs_cac_workqueue) { + flush_workqueue(priv->dfs_cac_workqueue); + destroy_workqueue(priv->dfs_cac_workqueue); + priv->dfs_cac_workqueue = NULL; + } + + if (priv->dfs_chan_sw_workqueue) { + flush_workqueue(priv->dfs_chan_sw_workqueue); + destroy_workqueue(priv->dfs_chan_sw_workqueue); + priv->dfs_chan_sw_workqueue = NULL; + } + /* Clear the priv in adapter */ + priv->netdev->ieee80211_ptr = NULL; + priv->netdev = NULL; + priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; + + priv->media_connected = false; + + switch (priv->bss_mode) { + case NL80211_IFTYPE_UNSPECIFIED: + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + adapter->curr_iface_comb.sta_intf--; + break; + case NL80211_IFTYPE_AP: + adapter->curr_iface_comb.uap_intf--; + break; + case NL80211_IFTYPE_P2P_CLIENT: + case NL80211_IFTYPE_P2P_GO: + adapter->curr_iface_comb.p2p_intf--; + break; + default: + mwifiex_dbg(adapter, ERROR, + "del_virtual_intf: type not supported\n"); + break; + } + + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + kfree(priv->hist_data); + + return 0; +} +EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); + +static bool +mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, + u8 max_byte_seq) +{ + int j, k, valid_byte_cnt = 0; + bool dont_care_byte = false; + + for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { + for (k = 0; k < 8; k++) { + if (pat->mask[j] & 1 << k) { + memcpy(byte_seq + valid_byte_cnt, + &pat->pattern[j * 8 + k], 1); + valid_byte_cnt++; + if (dont_care_byte) + return false; + } else { + if (valid_byte_cnt) + dont_care_byte = true; + } + + if (valid_byte_cnt > max_byte_seq) + return false; + } + } + + byte_seq[max_byte_seq] = valid_byte_cnt; + + return true; +} + +#ifdef CONFIG_PM +static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv, + struct mwifiex_mef_entry *mef_entry) +{ + int i, filt_num = 0, num_ipv4 = 0; + struct in_device *in_dev; + struct in_ifaddr *ifa; + __be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR]; + struct mwifiex_adapter *adapter = priv->adapter; + + mef_entry->mode = MEF_MODE_HOST_SLEEP; + mef_entry->action = MEF_ACTION_AUTO_ARP; + + /* Enable ARP offload feature */ + memset(ips, 0, sizeof(ips)); + for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { + if (adapter->priv[i]->netdev) { + in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev); + if (!in_dev) + continue; + ifa = in_dev->ifa_list; + if (!ifa || !ifa->ifa_local) + continue; + ips[i] = ifa->ifa_local; + num_ipv4++; + } + } + + for (i = 0; i < num_ipv4; i++) { + if (!ips[i]) + continue; + mef_entry->filter[filt_num].repeat = 1; + memcpy(mef_entry->filter[filt_num].byte_seq, + (u8 *)&ips[i], sizeof(ips[i])); + mef_entry->filter[filt_num]. + byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + sizeof(ips[i]); + mef_entry->filter[filt_num].offset = 46; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + if (filt_num) { + mef_entry->filter[filt_num].filt_action = + TYPE_OR; + } + filt_num++; + } + + mef_entry->filter[filt_num].repeat = 1; + mef_entry->filter[filt_num].byte_seq[0] = 0x08; + mef_entry->filter[filt_num].byte_seq[1] = 0x06; + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2; + mef_entry->filter[filt_num].offset = 20; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + mef_entry->filter[filt_num].filt_action = TYPE_AND; +} + +static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv, + struct mwifiex_ds_mef_cfg *mef_cfg, + struct mwifiex_mef_entry *mef_entry, + struct cfg80211_wowlan *wowlan) +{ + int i, filt_num = 0, ret = 0; + bool first_pat = true; + u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; + const u8 ipv4_mc_mac[] = {0x33, 0x33}; + const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + + mef_entry->mode = MEF_MODE_HOST_SLEEP; + mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST; + + for (i = 0; i < wowlan->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!mwifiex_is_pattern_supported(&wowlan->patterns[i], + byte_seq, + MWIFIEX_MEF_MAX_BYTESEQ)) { + mwifiex_dbg(priv->adapter, ERROR, + "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!wowlan->patterns[i].pkt_offset) { + if (!(byte_seq[0] & 0x01) && + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) { + mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; + continue; + } else if (is_broadcast_ether_addr(byte_seq)) { + mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST; + continue; + } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) { + mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST; + continue; + } + } + mef_entry->filter[filt_num].repeat = 1; + mef_entry->filter[filt_num].offset = + wowlan->patterns[i].pkt_offset; + memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq, + sizeof(byte_seq)); + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + + if (first_pat) + first_pat = false; + else + mef_entry->filter[filt_num].filt_action = TYPE_AND; + + filt_num++; + } + + if (wowlan->magic_pkt) { + mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; + mef_entry->filter[filt_num].repeat = 16; + memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, + ETH_ALEN); + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + ETH_ALEN; + mef_entry->filter[filt_num].offset = 28; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + if (filt_num) + mef_entry->filter[filt_num].filt_action = TYPE_OR; + + filt_num++; + mef_entry->filter[filt_num].repeat = 16; + memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, + ETH_ALEN); + mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = + ETH_ALEN; + mef_entry->filter[filt_num].offset = 56; + mef_entry->filter[filt_num].filt_type = TYPE_EQ; + mef_entry->filter[filt_num].filt_action = TYPE_OR; + } + return ret; +} + +static int mwifiex_set_mef_filter(struct mwifiex_private *priv, + struct cfg80211_wowlan *wowlan) +{ + int ret = 0, num_entries = 1; + struct mwifiex_ds_mef_cfg mef_cfg; + struct mwifiex_mef_entry *mef_entry; + + if (wowlan->n_patterns || wowlan->magic_pkt) + num_entries++; + + mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL); + if (!mef_entry) + return -ENOMEM; + + memset(&mef_cfg, 0, sizeof(mef_cfg)); + mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST | + MWIFIEX_CRITERIA_UNICAST; + mef_cfg.num_entries = num_entries; + mef_cfg.mef_entry = mef_entry; + + mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]); + + if (wowlan->n_patterns || wowlan->magic_pkt) { + ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg, + &mef_entry[1], wowlan); + if (ret) + goto err; + } + + if (!mef_cfg.criteria) + mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST | + MWIFIEX_CRITERIA_UNICAST | + MWIFIEX_CRITERIA_MULTICAST; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG, + HostCmd_ACT_GEN_SET, 0, + &mef_cfg, true); + +err: + kfree(mef_entry); + return ret; +} + +static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, + struct cfg80211_wowlan *wowlan) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + struct mwifiex_ds_hs_cfg hs_cfg; + int i, ret = 0; + struct mwifiex_private *priv; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + mwifiex_abort_cac(priv); + } + + mwifiex_cancel_all_pending_cmd(adapter); + + if (!wowlan) { + mwifiex_dbg(adapter, ERROR, + "None of the WOWLAN triggers enabled\n"); + return 0; + } + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + if (!priv->media_connected) { + mwifiex_dbg(adapter, ERROR, + "Can not configure WOWLAN in disconnected state\n"); + return 0; + } + + ret = mwifiex_set_mef_filter(priv, wowlan); + if (ret) { + mwifiex_dbg(adapter, ERROR, "Failed to set MEF filter\n"); + return ret; + } + + if (wowlan->disconnect) { + memset(&hs_cfg, 0, sizeof(hs_cfg)); + hs_cfg.is_invoke_hostcmd = false; + hs_cfg.conditions = HS_CFG_COND_MAC_EVENT; + hs_cfg.gpio = HS_CFG_GPIO_DEF; + hs_cfg.gap = HS_CFG_GAP_DEF; + ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + MWIFIEX_SYNC_CMD, &hs_cfg); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "Failed to set HS params\n"); + return ret; + } + } + + return ret; +} + +static int mwifiex_cfg80211_resume(struct wiphy *wiphy) +{ + return 0; +} + +static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy, + bool enabled) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + + device_set_wakeup_enable(adapter->dev, enabled); +} +#endif + +static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq) +{ + const u8 ipv4_mc_mac[] = {0x33, 0x33}; + const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; + const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff}; + + if ((byte_seq[0] & 0x01) && + (byte_seq[MWIFIEX_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[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3)) + return PACKET_TYPE_MULTICAST; + + return 0; +} + +static int +mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv, + struct cfg80211_coalesce_rules *crule, + struct mwifiex_coalesce_rule *mrule) +{ + u8 byte_seq[MWIFIEX_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 (!mwifiex_is_pattern_supported(&crule->patterns[i], + byte_seq, + MWIFIEX_COALESCE_MAX_BYTESEQ)) { + mwifiex_dbg(priv->adapter, ERROR, + "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!crule->patterns[i].pkt_offset) { + u8 pkt_type; + + pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq); + if (pkt_type && mrule->pkt_type) { + mwifiex_dbg(priv->adapter, ERROR, + "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[MWIFIEX_COALESCE_MAX_BYTESEQ]; + memcpy(param->operand_byte_stream, byte_seq, + param->operand_len); + param->offset = crule->patterns[i].pkt_offset; + param++; + + mrule->num_of_fields++; + } + + if (!mrule->pkt_type) { + mwifiex_dbg(priv->adapter, ERROR, + "Packet type can not be determined\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce) +{ + struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); + int i, ret; + struct mwifiex_ds_coalesce_cfg coalesce_cfg; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); + + memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); + if (!coalesce) { + mwifiex_dbg(adapter, WARN, + "Disable coalesce and reset all previous rules\n"); + return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, + &coalesce_cfg, true); + } + + coalesce_cfg.num_of_rules = coalesce->n_rules; + for (i = 0; i < coalesce->n_rules; i++) { + ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i], + &coalesce_cfg.rule[i]); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "Recheck the patterns provided for rule %d\n", + i + 1); + return ret; + } + } + + return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, + HostCmd_ACT_GEN_SET, 0, &coalesce_cfg, true); +} + +/* cfg80211 ops handler for tdls_mgmt. + * Function prepares TDLS action frame packets and forwards them to FW + */ +static int +mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capability, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + int ret; + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Setup Request to %pM status_code=%d\n", + peer, status_code); + mwifiex_add_auto_tdls_peer(priv, peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_RESPONSE: + mwifiex_add_auto_tdls_peer(priv, peer); + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Setup Response to %pM status_code=%d\n", + peer, status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_SETUP_CONFIRM: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Confirm to %pM status_code=%d\n", peer, + status_code); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_TEARDOWN: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Tear down to %pM\n", peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_TDLS_DISCOVERY_REQUEST: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Discovery Request to %pM\n", peer); + ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + mwifiex_dbg(priv->adapter, MSG, + "Send TDLS Discovery Response to %pM\n", peer); + ret = mwifiex_send_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, + extra_ies, extra_ies_len); + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "Unknown TDLS mgmt/action frame %pM\n", peer); + ret = -EINVAL; + break; + } + + return ret; +} + +static int +mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, + const u8 *peer, enum nl80211_tdls_operation action) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) || + !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return -ENOTSUPP; + + mwifiex_dbg(priv->adapter, MSG, + "TDLS peer=%pM, oper=%d\n", peer, action); + + switch (action) { + case NL80211_TDLS_ENABLE_LINK: + action = MWIFIEX_TDLS_ENABLE_LINK; + break; + case NL80211_TDLS_DISABLE_LINK: + action = MWIFIEX_TDLS_DISABLE_LINK; + break; + case NL80211_TDLS_TEARDOWN: + /* shouldn't happen!*/ + mwifiex_dbg(priv->adapter, ERROR, + "tdls_oper: teardown from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_SETUP: + /* shouldn't happen!*/ + mwifiex_dbg(priv->adapter, ERROR, + "tdls_oper: setup from driver not supported\n"); + return -EINVAL; + case NL80211_TDLS_DISCOVERY_REQ: + /* shouldn't happen!*/ + mwifiex_dbg(priv->adapter, ERROR, + "tdls_oper: discovery from driver not supported\n"); + return -EINVAL; + default: + mwifiex_dbg(priv->adapter, ERROR, + "tdls_oper: operation not supported\n"); + return -ENOTSUPP; + } + + return mwifiex_tdls_oper(priv, peer, action); +} + +static int +mwifiex_cfg80211_tdls_chan_switch(struct wiphy *wiphy, struct net_device *dev, + const u8 *addr, u8 oper_class, + struct cfg80211_chan_def *chandef) +{ + struct mwifiex_sta_node *sta_ptr; + unsigned long flags; + u16 chan; + u8 second_chan_offset, band; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, addr); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (!sta_ptr) { + wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", + __func__, addr); + return -ENOENT; + } + + if (!(sta_ptr->tdls_cap.extcap.ext_capab[3] & + WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)) { + wiphy_err(wiphy, "%pM do not support tdls cs\n", addr); + return -ENOENT; + } + + if (sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || + sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) { + wiphy_err(wiphy, "channel switch is running, abort request\n"); + return -EALREADY; + } + + chan = chandef->chan->hw_value; + second_chan_offset = mwifiex_get_sec_chan_offset(chan); + band = chandef->chan->band; + mwifiex_start_tdls_cs(priv, addr, chan, second_chan_offset, band); + + return 0; +} + +static void +mwifiex_cfg80211_tdls_cancel_chan_switch(struct wiphy *wiphy, + struct net_device *dev, + const u8 *addr) +{ + struct mwifiex_sta_node *sta_ptr; + unsigned long flags; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, addr); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (!sta_ptr) { + wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", + __func__, addr); + } else if (!(sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || + sta_ptr->tdls_status == TDLS_IN_BASE_CHAN || + sta_ptr->tdls_status == TDLS_IN_OFF_CHAN)) { + wiphy_err(wiphy, "tdls chan switch not initialize by %pM\n", + addr); + } else + mwifiex_stop_tdls_cs(priv, addr); +} + +static int +mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, struct station_parameters *params) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); +} + +static int +mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_csa_settings *params) +{ + struct ieee_types_header *chsw_ie; + struct ieee80211_channel_sw_ie *channel_sw; + int chsw_msec; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (priv->adapter->scan_processing) { + mwifiex_dbg(priv->adapter, ERROR, + "radar detection: scan in process...\n"); + return -EBUSY; + } + + if (priv->wdev.cac_started) + return -EBUSY; + + if (cfg80211_chandef_identical(¶ms->chandef, + &priv->dfs_chandef)) + return -EINVAL; + + chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, + params->beacon_csa.tail, + params->beacon_csa.tail_len); + if (!chsw_ie) { + mwifiex_dbg(priv->adapter, ERROR, + "Could not parse channel switch announcement IE\n"); + return -EINVAL; + } + + channel_sw = (void *)(chsw_ie + 1); + if (channel_sw->mode) { + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); + } + + if (mwifiex_del_mgmt_ies(priv)) + mwifiex_dbg(priv->adapter, ERROR, + "Failed to delete mgmt IEs!\n"); + + if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon_csa)) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: setting mgmt ies failed\n", __func__); + return -EFAULT; + } + + memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef)); + memcpy(&priv->beacon_after, ¶ms->beacon_after, + sizeof(priv->beacon_after)); + + chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100); + queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work, + msecs_to_jiffies(chsw_msec)); + return 0; +} + +static int mwifiex_cfg80211_get_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); + struct mwifiex_bssdescriptor *curr_bss; + struct ieee80211_channel *chan; + u8 second_chan_offset; + enum nl80211_channel_type chan_type; + enum ieee80211_band band; + int freq; + int ret = -ENODATA; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && + cfg80211_chandef_valid(&priv->bss_chandef)) { + *chandef = priv->bss_chandef; + ret = 0; + } else if (priv->media_connected) { + curr_bss = &priv->curr_bss_params.bss_descriptor; + band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + freq = ieee80211_channel_to_frequency(curr_bss->channel, band); + chan = ieee80211_get_channel(wiphy, freq); + + if (curr_bss->bcn_ht_oper) { + second_chan_offset = curr_bss->bcn_ht_oper->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET; + chan_type = mwifiex_sec_chan_offset_to_chan_type + (second_chan_offset); + cfg80211_chandef_create(chandef, chan, chan_type); + } else { + cfg80211_chandef_create(chandef, chan, + NL80211_CHAN_NO_HT); + } + ret = 0; + } + + return ret; +} + +static int +mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_chan_def *chandef, + u32 cac_time_ms) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_radar_params radar_params; + + if (priv->adapter->scan_processing) { + mwifiex_dbg(priv->adapter, ERROR, + "radar detection: scan already in process...\n"); + return -EBUSY; + } + + if (!mwifiex_is_11h_active(priv)) { + mwifiex_dbg(priv->adapter, INFO, + "Enable 11h extensions in FW\n"); + if (mwifiex_11h_activate(priv, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to activate 11h extensions!!"); + return -1; + } + priv->state_11h.is_11h_active = true; + } + + memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); + radar_params.chandef = chandef; + radar_params.cac_time_ms = cac_time_ms; + + memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef)); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, + HostCmd_ACT_GEN_SET, 0, &radar_params, true)) + return -1; + + queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work, + msecs_to_jiffies(cac_time_ms)); + return 0; +} + +static int +mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, + const u8 *mac, + struct station_parameters *params) +{ + int ret; + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + /* we support change_station handler only for TDLS peers*/ + if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) + return -ENOTSUPP; + + /* make sure we are in station mode and connected */ + if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) + return -ENOTSUPP; + + priv->sta_params = params; + + ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK); + priv->sta_params = NULL; + + return ret; +} + +/* station cfg80211 operations */ +static struct cfg80211_ops mwifiex_cfg80211_ops = { + .add_virtual_intf = mwifiex_add_virtual_intf, + .del_virtual_intf = mwifiex_del_virtual_intf, + .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf, + .scan = mwifiex_cfg80211_scan, + .connect = mwifiex_cfg80211_connect, + .disconnect = mwifiex_cfg80211_disconnect, + .get_station = mwifiex_cfg80211_get_station, + .dump_station = mwifiex_cfg80211_dump_station, + .dump_survey = mwifiex_cfg80211_dump_survey, + .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params, + .join_ibss = mwifiex_cfg80211_join_ibss, + .leave_ibss = mwifiex_cfg80211_leave_ibss, + .add_key = mwifiex_cfg80211_add_key, + .del_key = mwifiex_cfg80211_del_key, + .mgmt_tx = mwifiex_cfg80211_mgmt_tx, + .mgmt_frame_register = mwifiex_cfg80211_mgmt_frame_register, + .remain_on_channel = mwifiex_cfg80211_remain_on_channel, + .cancel_remain_on_channel = mwifiex_cfg80211_cancel_remain_on_channel, + .set_default_key = mwifiex_cfg80211_set_default_key, + .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt, + .set_tx_power = mwifiex_cfg80211_set_tx_power, + .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask, + .start_ap = mwifiex_cfg80211_start_ap, + .stop_ap = mwifiex_cfg80211_stop_ap, + .change_beacon = mwifiex_cfg80211_change_beacon, + .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, + .set_antenna = mwifiex_cfg80211_set_antenna, + .del_station = mwifiex_cfg80211_del_station, +#ifdef CONFIG_PM + .suspend = mwifiex_cfg80211_suspend, + .resume = mwifiex_cfg80211_resume, + .set_wakeup = mwifiex_cfg80211_set_wakeup, +#endif + .set_coalesce = mwifiex_cfg80211_set_coalesce, + .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, + .tdls_oper = mwifiex_cfg80211_tdls_oper, + .tdls_channel_switch = mwifiex_cfg80211_tdls_chan_switch, + .tdls_cancel_channel_switch = mwifiex_cfg80211_tdls_cancel_chan_switch, + .add_station = mwifiex_cfg80211_add_station, + .change_station = mwifiex_cfg80211_change_station, + .get_channel = mwifiex_cfg80211_get_channel, + .start_radar_detection = mwifiex_cfg80211_start_radar_detection, + .channel_switch = mwifiex_cfg80211_channel_switch, +}; + +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support mwifiex_wowlan_support = { + .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, + .n_patterns = MWIFIEX_MEF_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, + .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, +}; +#endif + +static bool mwifiex_is_valid_alpha2(const char *alpha2) +{ + if (!alpha2 || strlen(alpha2) != 2) + return false; + + if (isalpha(alpha2[0]) && isalpha(alpha2[1])) + return true; + + return false; +} + +static const struct wiphy_coalesce_support mwifiex_coalesce_support = { + .n_rules = MWIFIEX_COALESCE_MAX_RULES, + .max_delay = MWIFIEX_MAX_COALESCING_DELAY, + .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS, + .pattern_min_len = 1, + .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, + .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, +}; + +int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter) +{ + u32 n_channels_bg, n_channels_a = 0; + + n_channels_bg = mwifiex_band_2ghz.n_channels; + + if (adapter->config_bands & BAND_A) + n_channels_a = mwifiex_band_5ghz.n_channels; + + adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a); + adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) * + adapter->num_in_chan_stats); + + if (!adapter->chan_stats) + return -ENOMEM; + + return 0; +} + +/* + * This function registers the device with CFG802.11 subsystem. + * + * The function creates the wireless device/wiphy, populates it with + * default parameters and handler function pointers, and finally + * registers the device. + */ + +int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) +{ + int ret; + void *wdev_priv; + struct wiphy *wiphy; + struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; + u8 *country_code; + u32 thr, retry; + + /* create a new wiphy for use with cfg80211 */ + wiphy = wiphy_new(&mwifiex_cfg80211_ops, + sizeof(struct mwifiex_adapter *)); + if (!wiphy) { + mwifiex_dbg(adapter, ERROR, + "%s: creating new wiphy\n", __func__); + return -ENOMEM; + } + wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; + wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; + wiphy->mgmt_stypes = mwifiex_mgmt_stypes; + wiphy->max_remain_on_channel_duration = 5000; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_AP); + + wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz; + if (adapter->config_bands & BAND_A) + wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz; + else + wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; + + if (adapter->drcs_enabled && ISSUPP_DRCS_ENABLED(adapter->fw_cap_info)) + wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_drcs; + else if (adapter->is_hw_11ac_capable) + wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_vht; + else + wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta; + wiphy->n_iface_combinations = 1; + + /* Initialize cipher suits */ + wiphy->cipher_suites = mwifiex_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); + + ether_addr_copy(wiphy->perm_addr, adapter->perm_addr); + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | + WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_HAS_CHANNEL_SWITCH | + WIPHY_FLAG_PS_ON_BY_DEFAULT; + + if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_TDLS_EXTERNAL_SETUP; + +#ifdef CONFIG_PM + wiphy->wowlan = &mwifiex_wowlan_support; +#endif + + wiphy->coalesce = &mwifiex_coalesce_support; + + wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | + NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + + wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1; + wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1; + + wiphy->features |= NL80211_FEATURE_HT_IBSS | + NL80211_FEATURE_INACTIVITY_TIMER | + NL80211_FEATURE_NEED_OBSS_SCAN; + + if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) + wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; + + if (adapter->fw_api_ver == MWIFIEX_FW_V15) + wiphy->features |= NL80211_FEATURE_SK_TX_STATUS; + + /* Reserve space for mwifiex specific private data for BSS */ + wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv); + + wiphy->reg_notifier = mwifiex_reg_notifier; + + /* Set struct mwifiex_adapter pointer in wiphy_priv */ + wdev_priv = wiphy_priv(wiphy); + *(unsigned long *)wdev_priv = (unsigned long)adapter; + + set_wiphy_dev(wiphy, priv->adapter->dev); + + ret = wiphy_register(wiphy); + if (ret < 0) { + mwifiex_dbg(adapter, ERROR, + "%s: wiphy_register failed: %d\n", __func__, ret); + wiphy_free(wiphy); + return ret; + } + + if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) { + mwifiex_dbg(adapter, INFO, + "driver hint alpha2: %2.2s\n", reg_alpha2); + regulatory_hint(wiphy, reg_alpha2); + } else { + country_code = mwifiex_11d_code_2_region(adapter->region_code); + if (country_code) + mwifiex_dbg(adapter, WARN, + "ignoring F/W country code %2.2s\n", + country_code); + } + + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr, true); + wiphy->frag_threshold = thr; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr, true); + wiphy->rts_threshold = thr; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true); + wiphy->retry_short = (u8) retry; + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true); + wiphy->retry_long = (u8) retry; + + adapter->wiphy = wiphy; + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.h b/drivers/net/wireless/marvell/mwifiex/cfg80211.h new file mode 100644 index 000000000000..908367857d58 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.h @@ -0,0 +1,29 @@ +/* + * Marvell Wireless LAN device driver: CFG80211 + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef __MWIFIEX_CFG80211__ +#define __MWIFIEX_CFG80211__ + +#include + +#include "main.h" + +int mwifiex_register_cfg80211(struct mwifiex_adapter *); + +#endif diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c new file mode 100644 index 000000000000..3ddb8ec676ed --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/cfp.c @@ -0,0 +1,537 @@ +/* + * Marvell Wireless LAN device driver: Channel, Frequence and Power + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "cfg80211.h" + +/* 100mW */ +#define MWIFIEX_TX_PWR_DEFAULT 20 +/* 100mW */ +#define MWIFIEX_TX_PWR_US_DEFAULT 20 +/* 50mW */ +#define MWIFIEX_TX_PWR_JP_DEFAULT 16 +/* 100mW */ +#define MWIFIEX_TX_PWR_FR_100MW 20 +/* 10mW */ +#define MWIFIEX_TX_PWR_FR_10MW 10 +/* 100mW */ +#define MWIFIEX_TX_PWR_EMEA_DEFAULT 20 + +static u8 adhoc_rates_b[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 }; + +static u8 adhoc_rates_g[G_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, + 0xb0, 0x48, 0x60, 0x6c, 0 }; + +static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, + 0x0c, 0x12, 0x18, 0x24, + 0x30, 0x48, 0x60, 0x6c, 0 }; + +static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, + 0xb0, 0x48, 0x60, 0x6c, 0 }; +static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, + 0xb0, 0x48, 0x60, 0x6c, 0 }; +static u16 mwifiex_data_rates[MWIFIEX_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 }; + +static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 }; + +static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, + 0x30, 0x48, 0x60, 0x6c, 0 }; + +static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c, + 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, + 0x60, 0x6c, 0 }; + +u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, + 0x32, 0x40, 0x41, 0xff }; + +static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; + +/* For every mcs_rate line, the first 8 bytes are for stream 1x1, + * and all 16 bytes are for stream 2x2. + */ +static const u16 mcs_rate[4][16] = { + /* LGI 40M */ + { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, + 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, + + /* SGI 40M */ + { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, + 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, + + /* LGI 20M */ + { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, + 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, + + /* SGI 20M */ + { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, + 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } +}; + +/* AC rates */ +static const u16 ac_mcs_rate_nss1[8][10] = { + /* LG 160M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 160M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 80M */ + { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, + 0x249, 0x2BE, 0x30C }, + + /* SG 80M */ + { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, + 0x28A, 0x30C, 0x363 }, + + /* LG 40M */ + { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, + 0x10E, 0x144, 0x168 }, + + /* SG 40M */ + { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, + 0x12C, 0x168, 0x190 }, + + /* LG 20M */ + { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, + + /* SG 20M */ + { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, +}; + +/* NSS2 note: the value in the table is 2 multiplier of the actual rate */ +static const u16 ac_mcs_rate_nss2[8][10] = { + /* LG 160M */ + { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, + 0x924, 0xAF8, 0xC30 }, + + /* SG 160M */ + { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, + 0xA28, 0xC30, 0xD8B }, + + /* LG 80M */ + { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, + 0x492, 0x57C, 0x618 }, + + /* SG 80M */ + { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, + 0x514, 0x618, 0x6C6 }, + + /* LG 40M */ + { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, + 0x21C, 0x288, 0x2D0 }, + + /* SG 40M */ + { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, + 0x258, 0x2D0, 0x320 }, + + /* LG 20M */ + { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, + 0x138, 0x00 }, + + /* SG 20M */ + { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, + 0x15B, 0x00 }, +}; + +struct region_code_mapping { + u8 code; + u8 region[IEEE80211_COUNTRY_STRING_LEN]; +}; + +static struct region_code_mapping region_code_mapping_t[] = { + { 0x10, "US " }, /* US FCC */ + { 0x20, "CA " }, /* IC Canada */ + { 0x30, "EU " }, /* ETSI */ + { 0x31, "ES " }, /* Spain */ + { 0x32, "FR " }, /* France */ + { 0x40, "JP " }, /* Japan */ + { 0x41, "JP " }, /* Japan */ + { 0x50, "CN " }, /* China */ +}; + +/* This function converts integer code to region string */ +u8 *mwifiex_11d_code_2_region(u8 code) +{ + u8 i; + u8 size = sizeof(region_code_mapping_t)/ + sizeof(struct region_code_mapping); + + /* Look for code in mapping table */ + for (i = 0; i < size; i++) + if (region_code_mapping_t[i].code == code) + return region_code_mapping_t[i].region; + + return NULL; +} + +/* + * This function maps an index in supported rates table into + * the corresponding data rate. + */ +u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, + u8 index, u8 ht_info) +{ + u32 rate = 0; + u8 mcs_index = 0; + u8 bw = 0; + u8 gi = 0; + + if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_VHT) { + mcs_index = min(index & 0xF, 9); + + /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ + bw = (ht_info & 0xC) >> 2; + + /* LGI: gi =0, SGI: gi = 1 */ + gi = (ht_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 ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_HT) { + /* 20M: bw=0, 40M: bw=1 */ + bw = (ht_info & 0xC) >> 2; + + /* LGI: gi =0, SGI: gi = 1 */ + gi = (ht_info & 0x10) >> 4; + + if (index == MWIFIEX_RATE_BITMAP_MCS0) { + if (gi == 1) + rate = 0x0D; /* MCS 32 SGI rate */ + else + rate = 0x0C; /* MCS 32 LGI rate */ + } else if (index < 16) { + if ((bw == 1) || (bw == 0)) + rate = mcs_rate[2 * (1 - bw) + gi][index]; + else + rate = mwifiex_data_rates[0]; + } else { + rate = mwifiex_data_rates[0]; + } + } else { + /* 11n non-HT rates */ + if (index >= MWIFIEX_SUPPORTED_RATES_EXT) + index = 0; + rate = mwifiex_data_rates[index]; + } + + return rate; +} + +/* This function maps an index in supported rates table into + * the corresponding data rate. + */ +u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, + u8 index, u8 ht_info) +{ + u32 mcs_num_supp = + (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; + u32 rate; + + if (priv->adapter->is_hw_11ac_capable) + return mwifiex_index_to_acs_data_rate(priv, index, ht_info); + + if (ht_info & BIT(0)) { + if (index == MWIFIEX_RATE_BITMAP_MCS0) { + if (ht_info & BIT(2)) + rate = 0x0D; /* MCS 32 SGI rate */ + else + rate = 0x0C; /* MCS 32 LGI rate */ + } else if (index < mcs_num_supp) { + if (ht_info & BIT(1)) { + if (ht_info & BIT(2)) + /* SGI, 40M */ + rate = mcs_rate[1][index]; + else + /* LGI, 40M */ + rate = mcs_rate[0][index]; + } else { + if (ht_info & BIT(2)) + /* SGI, 20M */ + rate = mcs_rate[3][index]; + else + /* LGI, 20M */ + rate = mcs_rate[2][index]; + } + } else + rate = mwifiex_data_rates[0]; + } else { + if (index >= MWIFIEX_SUPPORTED_RATES_EXT) + index = 0; + rate = mwifiex_data_rates[index]; + } + return rate; +} + +/* + * This function returns the current active data rates. + * + * The result may vary depending upon connection status. + */ +u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates) +{ + if (!priv->media_connected) + return mwifiex_get_supported_rates(priv, rates); + else + return mwifiex_copy_rates(rates, 0, + priv->curr_bss_params.data_rates, + priv->curr_bss_params.num_of_rates); +} + +/* + * This function locates the Channel-Frequency-Power triplet based upon + * band and channel/frequency parameters. + */ +struct mwifiex_chan_freq_power * +mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq) +{ + struct mwifiex_chan_freq_power *cfp = NULL; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch = NULL; + int i; + + if (!channel && !freq) + return cfp; + + if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) + sband = priv->wdev.wiphy->bands[IEEE80211_BAND_2GHZ]; + else + sband = priv->wdev.wiphy->bands[IEEE80211_BAND_5GHZ]; + + if (!sband) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: cannot find cfp by band %d\n", + __func__, band); + return cfp; + } + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + if (ch->flags & IEEE80211_CHAN_DISABLED) + continue; + + if (freq) { + if (ch->center_freq == freq) + break; + } else { + /* find by valid channel*/ + if (ch->hw_value == channel || + channel == FIRST_VALID_CHANNEL) + break; + } + } + if (i == sband->n_channels) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: cannot find cfp by band %d\t" + "& channel=%d freq=%d\n", + __func__, band, channel, freq); + } else { + if (!ch) + return cfp; + + priv->cfp.channel = ch->hw_value; + priv->cfp.freq = ch->center_freq; + priv->cfp.max_tx_power = ch->max_power; + cfp = &priv->cfp; + } + + return cfp; +} + +/* + * This function checks if the data rate is set to auto. + */ +u8 +mwifiex_is_rate_auto(struct mwifiex_private *priv) +{ + u32 i; + int rate_num = 0; + + for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) + if (priv->bitmap_rates[i]) + rate_num++; + + if (rate_num > 1) + return true; + else + return false; +} + +/* This function gets the supported data rates from bitmask inside + * cfg80211_scan_request. + */ +u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, + u8 *rates, u8 radio_type) +{ + struct wiphy *wiphy = priv->adapter->wiphy; + struct cfg80211_scan_request *request = priv->scan_request; + u32 num_rates, rate_mask; + struct ieee80211_supported_band *sband; + int i; + + if (radio_type) { + sband = wiphy->bands[IEEE80211_BAND_5GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask = request->rates[IEEE80211_BAND_5GHZ]; + } else { + sband = wiphy->bands[IEEE80211_BAND_2GHZ]; + if (WARN_ON_ONCE(!sband)) + return 0; + rate_mask = request->rates[IEEE80211_BAND_2GHZ]; + } + + num_rates = 0; + for (i = 0; i < sband->n_bitrates; i++) { + if ((BIT(i) & rate_mask) == 0) + continue; /* skip rate */ + rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5); + } + + return num_rates; +} + +/* This function gets the supported data rates. The function works in + * both Ad-Hoc and infra mode by printing the band and returning the + * data rates. + */ +u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) +{ + u32 k = 0; + struct mwifiex_adapter *adapter = priv->adapter; + + if (priv->bss_mode == NL80211_IFTYPE_STATION || + priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { + switch (adapter->config_bands) { + case BAND_B: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_b\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_b, + sizeof(supported_rates_b)); + break; + case BAND_G: + case BAND_G | BAND_GN: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_g\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_g, + sizeof(supported_rates_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_B | BAND_G | BAND_GN: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_bg\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_bg, + sizeof(supported_rates_bg)); + break; + case BAND_A: + case BAND_A | BAND_G: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_a\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_a, + sizeof(supported_rates_a)); + break; + case BAND_AN: + case BAND_A | BAND_AN: + case BAND_A | BAND_AN | BAND_AAC: + case BAND_A | BAND_G | BAND_AN | BAND_GN: + case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_a\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_a, + sizeof(supported_rates_a)); + break; + case BAND_GN: + mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" + "supported_rates_n\n", + adapter->config_bands); + k = mwifiex_copy_rates(rates, k, supported_rates_n, + sizeof(supported_rates_n)); + break; + } + } else { + /* Ad-hoc mode */ + switch (adapter->adhoc_start_band) { + case BAND_B: + mwifiex_dbg(adapter, INFO, "info: adhoc B\n"); + k = mwifiex_copy_rates(rates, k, adhoc_rates_b, + sizeof(adhoc_rates_b)); + break; + case BAND_G: + case BAND_G | BAND_GN: + mwifiex_dbg(adapter, INFO, "info: adhoc G only\n"); + k = mwifiex_copy_rates(rates, k, adhoc_rates_g, + sizeof(adhoc_rates_g)); + break; + case BAND_B | BAND_G: + case BAND_B | BAND_G | BAND_GN: + mwifiex_dbg(adapter, INFO, "info: adhoc BG\n"); + k = mwifiex_copy_rates(rates, k, adhoc_rates_bg, + sizeof(adhoc_rates_bg)); + break; + case BAND_A: + case BAND_A | BAND_AN: + mwifiex_dbg(adapter, INFO, "info: adhoc A\n"); + k = mwifiex_copy_rates(rates, k, adhoc_rates_a, + sizeof(adhoc_rates_a)); + break; + } + } + + return k; +} + +u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, + u8 rx_rate, u8 rate_info) +{ + u8 rate_index = 0; + + /* HT40 */ + if ((rate_info & BIT(0)) && (rate_info & BIT(1))) + rate_index = MWIFIEX_RATE_INDEX_MCS0 + + MWIFIEX_BW20_MCS_NUM + rx_rate; + else if (rate_info & BIT(0)) /* HT20 */ + rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate; + else + rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ? + rx_rate - 1 : rx_rate; + + return rate_index; +} diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c new file mode 100644 index 000000000000..45ae38e32621 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -0,0 +1,1659 @@ +/* + * Marvell Wireless LAN device driver: commands and events + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" + +/* + * This function initializes a command node. + * + * The actual allocation of the node is not done by this function. It only + * initiates a node by filling it with default parameters. Similarly, + * allocation of the different buffers used (IOCTL buffer, data buffer) are + * not done by this function either. + */ +static void +mwifiex_init_cmd_node(struct mwifiex_private *priv, + struct cmd_ctrl_node *cmd_node, + u32 cmd_oid, void *data_buf, bool sync) +{ + cmd_node->priv = priv; + cmd_node->cmd_oid = cmd_oid; + if (sync) { + cmd_node->wait_q_enabled = true; + cmd_node->cmd_wait_q_woken = false; + cmd_node->condition = &cmd_node->cmd_wait_q_woken; + } + cmd_node->data_buf = data_buf; + cmd_node->cmd_skb = cmd_node->skb; +} + +/* + * This function returns a command node from the free queue depending upon + * availability. + */ +static struct cmd_ctrl_node * +mwifiex_get_cmd_node(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node; + unsigned long flags; + + spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); + if (list_empty(&adapter->cmd_free_q)) { + mwifiex_dbg(adapter, ERROR, + "GET_CMD_NODE: cmd node not available\n"); + spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); + return NULL; + } + cmd_node = list_first_entry(&adapter->cmd_free_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); + + return cmd_node; +} + +/* + * This function cleans up a command node. + * + * The function resets the fields including the buffer pointers. + * This function does not try to free the buffers. They must be + * freed before calling this function. + * + * This function will however call the receive completion callback + * in case a response buffer is still available before resetting + * the pointer. + */ +static void +mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + cmd_node->cmd_oid = 0; + cmd_node->cmd_flag = 0; + cmd_node->data_buf = NULL; + cmd_node->wait_q_enabled = false; + + if (cmd_node->cmd_skb) + skb_trim(cmd_node->cmd_skb, 0); + + if (cmd_node->resp_skb) { + adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb); + cmd_node->resp_skb = NULL; + } +} + +/* + * This function sends a host command to the firmware. + * + * The function copies the host command into the driver command + * buffer, which will be transferred to the firmware later by the + * main thread. + */ +static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_misc_cmd *pcmd_ptr) +{ + /* Copy the HOST command to command buffer */ + memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len); + mwifiex_dbg(priv->adapter, CMD, + "cmd: host cmd size = %d\n", pcmd_ptr->len); + return 0; +} + +/* + * This function downloads a command to the firmware. + * + * The function performs sanity tests, sets the command sequence + * number and size, converts the header fields to CPU format before + * sending. Afterwards, it logs the command ID and action for debugging + * and sets up the command timeout timer. + */ +static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, + struct cmd_ctrl_node *cmd_node) +{ + + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + struct host_cmd_ds_command *host_cmd; + uint16_t cmd_code; + uint16_t cmd_size; + unsigned long flags; + __le32 tmp; + + if (!adapter || !cmd_node) + return -1; + + host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); + + /* Sanity test */ + if (host_cmd == NULL || host_cmd->size == 0) { + mwifiex_dbg(adapter, ERROR, + "DNLD_CMD: host_cmd is null\t" + "or cmd size is 0, not sending\n"); + if (cmd_node->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + mwifiex_recycle_cmd_node(adapter, cmd_node); + return -1; + } + + cmd_code = le16_to_cpu(host_cmd->command); + cmd_size = le16_to_cpu(host_cmd->size); + + if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET && + cmd_code != HostCmd_CMD_FUNC_SHUTDOWN && + cmd_code != HostCmd_CMD_FUNC_INIT) { + mwifiex_dbg(adapter, ERROR, + "DNLD_CMD: FW in reset state, ignore cmd %#x\n", + cmd_code); + mwifiex_recycle_cmd_node(adapter, cmd_node); + queue_work(adapter->workqueue, &adapter->main_work); + return -1; + } + + /* Set command sequence number */ + adapter->seq_num++; + host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO + (adapter->seq_num, + cmd_node->priv->bss_num, + cmd_node->priv->bss_type)); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = cmd_node; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + /* Adjust skb length */ + if (cmd_node->cmd_skb->len > cmd_size) + /* + * cmd_size is less than sizeof(struct host_cmd_ds_command). + * Trim off the unused portion. + */ + skb_trim(cmd_node->cmd_skb, cmd_size); + else if (cmd_node->cmd_skb->len < cmd_size) + /* + * cmd_size is larger than sizeof(struct host_cmd_ds_command) + * because we have appended custom IE TLV. Increase skb length + * accordingly. + */ + skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len); + + mwifiex_dbg(adapter, CMD, + "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", + cmd_code, + le16_to_cpu(*(__le16 *)((u8 *)host_cmd + S_DS_GEN)), + cmd_size, le16_to_cpu(host_cmd->seq_num)); + mwifiex_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size); + + if (adapter->iface_type == MWIFIEX_USB) { + tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); + skb_push(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); + memcpy(cmd_node->cmd_skb->data, &tmp, MWIFIEX_TYPE_LEN); + adapter->cmd_sent = true; + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_USB_EP_CMD_EVENT, + cmd_node->cmd_skb, NULL); + skb_pull(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); + if (ret == -EBUSY) + cmd_node->cmd_skb = NULL; + } else { + skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN); + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, + cmd_node->cmd_skb, NULL); + skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN); + } + + if (ret == -1) { + mwifiex_dbg(adapter, ERROR, + "DNLD_CMD: host to card failed\n"); + if (adapter->iface_type == MWIFIEX_USB) + adapter->cmd_sent = false; + if (cmd_node->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + adapter->dbg.num_cmd_host_to_card_failure++; + return -1; + } + + /* Save the last command id and action to debug log */ + adapter->dbg.last_cmd_index = + (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code; + adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] = + le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)); + + /* Clear BSS_NO_BITS from HostCmd */ + cmd_code &= HostCmd_CMD_ID_MASK; + + /* Setup the timer after transmit command */ + mod_timer(&adapter->cmd_timer, + jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); + + return 0; +} + +/* + * This function downloads a sleep confirm command to the firmware. + * + * The function performs sanity tests, sets the command sequence + * number and size, converts the header fields to CPU format before + * sending. + * + * No responses are needed for sleep confirm command. + */ +static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) +{ + int ret; + struct mwifiex_private *priv; + struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = + (struct mwifiex_opt_sleep_confirm *) + adapter->sleep_cfm->data; + struct sk_buff *sleep_cfm_tmp; + __le32 tmp; + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + adapter->seq_num++; + sleep_cfm_buf->seq_num = + cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO + (adapter->seq_num, priv->bss_num, + priv->bss_type))); + + mwifiex_dbg(adapter, CMD, + "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", + le16_to_cpu(sleep_cfm_buf->command), + le16_to_cpu(sleep_cfm_buf->action), + le16_to_cpu(sleep_cfm_buf->size), + le16_to_cpu(sleep_cfm_buf->seq_num)); + mwifiex_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf, + le16_to_cpu(sleep_cfm_buf->size)); + + if (adapter->iface_type == MWIFIEX_USB) { + sleep_cfm_tmp = + dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) + + MWIFIEX_TYPE_LEN); + skb_put(sleep_cfm_tmp, sizeof(struct mwifiex_opt_sleep_confirm) + + MWIFIEX_TYPE_LEN); + tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); + memcpy(sleep_cfm_tmp->data, &tmp, MWIFIEX_TYPE_LEN); + memcpy(sleep_cfm_tmp->data + MWIFIEX_TYPE_LEN, + adapter->sleep_cfm->data, + sizeof(struct mwifiex_opt_sleep_confirm)); + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_USB_EP_CMD_EVENT, + sleep_cfm_tmp, NULL); + if (ret != -EBUSY) + dev_kfree_skb_any(sleep_cfm_tmp); + } else { + skb_push(adapter->sleep_cfm, INTF_HEADER_LEN); + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, + adapter->sleep_cfm, NULL); + skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN); + } + + if (ret == -1) { + mwifiex_dbg(adapter, ERROR, "SLEEP_CFM: failed\n"); + adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; + return -1; + } + + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl)) + /* Response is not needed for sleep confirm command */ + adapter->ps_state = PS_STATE_SLEEP; + else + adapter->ps_state = PS_STATE_SLEEP_CFM; + + if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) && + (adapter->is_hs_configured && + !adapter->sleep_period.period)) { + adapter->pm_wakeup_card_req = true; + mwifiex_hs_activated_event(mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), true); + } + + return ret; +} + +/* + * This function allocates the command buffers and links them to + * the command free queue. + * + * The driver uses a pre allocated number of command buffers, which + * are created at driver initializations and freed at driver cleanup. + * Every command needs to obtain a command buffer from this pool before + * it can be issued. The command free queue lists the command buffers + * currently free to use, while the command pending queue lists the + * command buffers already in use and awaiting handling. Command buffers + * are returned to the free queue after use. + */ +int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_array; + u32 i; + + /* Allocate and initialize struct cmd_ctrl_node */ + cmd_array = kcalloc(MWIFIEX_NUM_OF_CMD_BUFFER, + sizeof(struct cmd_ctrl_node), GFP_KERNEL); + if (!cmd_array) + return -ENOMEM; + + adapter->cmd_pool = cmd_array; + + /* Allocate and initialize command buffers */ + for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { + cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER); + if (!cmd_array[i].skb) { + mwifiex_dbg(adapter, ERROR, + "unable to allocate command buffer\n"); + return -ENOMEM; + } + } + + for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) + mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]); + + return 0; +} + +/* + * This function frees the command buffers. + * + * The function calls the completion callback for all the command + * buffers that still have response buffers associated with them. + */ +int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_array; + u32 i; + + /* Need to check if cmd pool is allocated or not */ + if (!adapter->cmd_pool) { + mwifiex_dbg(adapter, FATAL, + "info: FREE_CMD_BUF: cmd_pool is null\n"); + return 0; + } + + cmd_array = adapter->cmd_pool; + + /* Release shared memory buffers */ + for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { + if (cmd_array[i].skb) { + mwifiex_dbg(adapter, CMD, + "cmd: free cmd buffer %d\n", i); + dev_kfree_skb_any(cmd_array[i].skb); + } + if (!cmd_array[i].resp_skb) + continue; + + if (adapter->iface_type == MWIFIEX_USB) + adapter->if_ops.cmdrsp_complete(adapter, + cmd_array[i].resp_skb); + else + dev_kfree_skb_any(cmd_array[i].resp_skb); + } + /* Release struct cmd_ctrl_node */ + if (adapter->cmd_pool) { + mwifiex_dbg(adapter, CMD, + "cmd: free cmd pool\n"); + kfree(adapter->cmd_pool); + adapter->cmd_pool = NULL; + } + + return 0; +} + +/* + * This function handles events generated by firmware. + * + * Event body of events received from firmware are not used (though they are + * saved), only the event ID is used. Some events are re-invoked by + * the driver, with a new event body. + * + * After processing, the function calls the completion callback + * for cleanup. + */ +int mwifiex_process_event(struct mwifiex_adapter *adapter) +{ + int ret; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + struct sk_buff *skb = adapter->event_skb; + u32 eventcause = adapter->event_cause; + struct mwifiex_rxinfo *rx_info; + + /* Save the last event to debug log */ + adapter->dbg.last_event_index = + (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_event[adapter->dbg.last_event_index] = + (u16) eventcause; + + /* Get BSS number and corresponding priv */ + priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause), + EVENT_GET_BSS_TYPE(eventcause)); + if (!priv) + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + /* Clear BSS_NO_BITS from event */ + eventcause &= EVENT_ID_MASK; + adapter->event_cause = eventcause; + + if (skb) { + rx_info = MWIFIEX_SKB_RXCB(skb); + memset(rx_info, 0, sizeof(*rx_info)); + rx_info->bss_num = priv->bss_num; + rx_info->bss_type = priv->bss_type; + mwifiex_dbg_dump(adapter, EVT_D, "Event Buf:", + skb->data, skb->len); + } + + mwifiex_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause); + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) + ret = mwifiex_process_uap_event(priv); + else + ret = mwifiex_process_sta_event(priv); + + adapter->event_cause = 0; + adapter->event_skb = NULL; + adapter->if_ops.event_complete(adapter, skb); + + return ret; +} + +/* + * This function prepares a command and send it to the firmware. + * + * Preparation includes - + * - Sanity tests to make sure the card is still present or the FW + * is not reset + * - Getting a new command node from the command free queue + * - Initializing the command node for default parameters + * - Fill up the non-default parameters and buffer pointers + * - Add the command to pending queue + */ +int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, + u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node; + struct host_cmd_ds_command *cmd_ptr; + + if (!adapter) { + pr_err("PREP_CMD: adapter is NULL\n"); + return -1; + } + + if (adapter->is_suspended) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: device in suspended state\n"); + return -1; + } + + if (adapter->hs_enabling && cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: host entering sleep state\n"); + return -1; + } + + if (adapter->surprise_removed) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: card is removed\n"); + return -1; + } + + if (adapter->is_cmd_timedout) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: FW is in bad state\n"); + return -1; + } + + if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) { + if (cmd_no != HostCmd_CMD_FUNC_INIT) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: FW in reset state\n"); + return -1; + } + } + + /* Get a new command node */ + cmd_node = mwifiex_get_cmd_node(adapter); + + if (!cmd_node) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: no free cmd node\n"); + return -1; + } + + /* Initialize the command node */ + mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf, sync); + + if (!cmd_node->cmd_skb) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: no free cmd buf\n"); + return -1; + } + + memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)), + 0, sizeof(struct host_cmd_ds_command)); + + cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->result = 0; + + /* Prepare command */ + if (cmd_no) { + switch (cmd_no) { + case HostCmd_CMD_UAP_SYS_CONFIG: + case HostCmd_CMD_UAP_BSS_START: + case HostCmd_CMD_UAP_BSS_STOP: + case HostCmd_CMD_UAP_STA_DEAUTH: + case HOST_CMD_APCMD_SYS_RESET: + case HOST_CMD_APCMD_STA_LIST: + ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action, + cmd_oid, data_buf, + cmd_ptr); + break; + default: + ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action, + cmd_oid, data_buf, + cmd_ptr); + break; + } + } else { + ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf); + cmd_node->cmd_flag |= CMD_F_HOSTCMD; + } + + /* Return error, since the command preparation failed */ + if (ret) { + mwifiex_dbg(adapter, ERROR, + "PREP_CMD: cmd %#x preparation failed\n", + cmd_no); + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + return -1; + } + + /* Send command */ + if (cmd_no == HostCmd_CMD_802_11_SCAN || + cmd_no == HostCmd_CMD_802_11_SCAN_EXT) { + mwifiex_queue_scan_cmd(priv, cmd_node); + } else { + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + queue_work(adapter->workqueue, &adapter->main_work); + if (cmd_node->wait_q_enabled) + ret = mwifiex_wait_queue_complete(adapter, cmd_node); + } + + return ret; +} + +/* + * This function returns a command to the command free queue. + * + * The function also calls the completion callback if required, before + * cleaning the command node and re-inserting it into the free queue. + */ +void +mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + unsigned long flags; + + if (!cmd_node) + return; + + if (cmd_node->wait_q_enabled) + mwifiex_complete_cmd(adapter, cmd_node); + /* Clean the node */ + mwifiex_clean_cmd_node(adapter, cmd_node); + + /* Insert node into cmd_free_q */ + spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); + list_add_tail(&cmd_node->list, &adapter->cmd_free_q); + spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); +} + +/* This function reuses a command node. */ +void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data; + + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + + atomic_dec(&adapter->cmd_pending); + mwifiex_dbg(adapter, CMD, + "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n", + le16_to_cpu(host_cmd->command), + atomic_read(&adapter->cmd_pending)); +} + +/* + * This function queues a command to the command pending queue. + * + * This in effect adds the command to the command list to be executed. + * Exit PS command is handled specially, by placing it always to the + * front of the command queue. + */ +void +mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node, u32 add_tail) +{ + struct host_cmd_ds_command *host_cmd = NULL; + u16 command; + unsigned long flags; + + host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); + if (!host_cmd) { + mwifiex_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n"); + return; + } + + command = le16_to_cpu(host_cmd->command); + + /* Exit_PS command needs to be queued in the header always. */ + if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { + struct host_cmd_ds_802_11_ps_mode_enh *pm = + &host_cmd->params.psmode_enh; + if ((le16_to_cpu(pm->action) == DIS_PS) || + (le16_to_cpu(pm->action) == DIS_AUTO_PS)) { + if (adapter->ps_state != PS_STATE_AWAKE) + add_tail = false; + } + } + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + if (add_tail) + list_add_tail(&cmd_node->list, &adapter->cmd_pending_q); + else + list_add(&cmd_node->list, &adapter->cmd_pending_q); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + + atomic_inc(&adapter->cmd_pending); + mwifiex_dbg(adapter, CMD, + "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n", + command, atomic_read(&adapter->cmd_pending)); +} + +/* + * This function executes the next command in command pending queue. + * + * This function will fail if a command is already in processing stage, + * otherwise it will dequeue the first command from the command pending + * queue and send to the firmware. + * + * If the device is currently in host sleep mode, any commands, except the + * host sleep configuration command will de-activate the host sleep. For PS + * mode, the function will put the firmware back to sleep if applicable. + */ +int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + struct cmd_ctrl_node *cmd_node; + int ret = 0; + struct host_cmd_ds_command *host_cmd; + unsigned long cmd_flags; + unsigned long cmd_pending_q_flags; + + /* Check if already in processing */ + if (adapter->curr_cmd) { + mwifiex_dbg(adapter, FATAL, + "EXEC_NEXT_CMD: cmd in processing\n"); + return -1; + } + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + /* Check if any command is pending */ + spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); + if (list_empty(&adapter->cmd_pending_q)) { + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, + cmd_pending_q_flags); + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + return 0; + } + cmd_node = list_first_entry(&adapter->cmd_pending_q, + struct cmd_ctrl_node, list); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, + cmd_pending_q_flags); + + host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); + priv = cmd_node->priv; + + if (adapter->ps_state != PS_STATE_AWAKE) { + mwifiex_dbg(adapter, ERROR, + "%s: cannot send cmd in sleep state,\t" + "this should not happen\n", __func__); + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + return ret; + } + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, + cmd_pending_q_flags); + + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node); + priv = mwifiex_get_priv(adapter, MWIFIEX_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 && (host_cmd->command != + cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { + if (adapter->hs_activated) { + adapter->is_hs_configured = false; + mwifiex_hs_activated_event(priv, false); + } + } + + return ret; +} + +/* + * This function handles the command response. + * + * After processing, the function cleans the command node and puts + * it back to the command free queue. + */ +int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) +{ + struct host_cmd_ds_command *resp; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + int ret = 0; + uint16_t orig_cmdresp_no; + uint16_t cmdresp_no; + uint16_t cmdresp_result; + unsigned long flags; + + /* Now we got response from FW, cancel the command timer */ + del_timer_sync(&adapter->cmd_timer); + + if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { + resp = (struct host_cmd_ds_command *) adapter->upld_buf; + mwifiex_dbg(adapter, ERROR, + "CMD_RESP: NULL curr_cmd, %#x\n", + le16_to_cpu(resp->command)); + return -1; + } + + adapter->is_cmd_timedout = 0; + + resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data; + if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + /* Copy original response back to response buffer */ + struct mwifiex_ds_misc_cmd *hostcmd; + uint16_t size = le16_to_cpu(resp->size); + mwifiex_dbg(adapter, INFO, + "info: host cmd resp size = %d\n", size); + size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER); + if (adapter->curr_cmd->data_buf) { + hostcmd = adapter->curr_cmd->data_buf; + hostcmd->len = size; + memcpy(hostcmd->cmd, resp, size); + } + } + orig_cmdresp_no = le16_to_cpu(resp->command); + + /* Get BSS number and corresponding priv */ + priv = mwifiex_get_priv_by_id(adapter, + HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)), + HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num))); + if (!priv) + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + /* Clear RET_BIT from HostCmd */ + resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK); + + cmdresp_no = le16_to_cpu(resp->command); + cmdresp_result = le16_to_cpu(resp->result); + + /* Save the last command response to debug log */ + adapter->dbg.last_cmd_resp_index = + (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; + adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] = + orig_cmdresp_no; + + mwifiex_dbg(adapter, CMD, + "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", + orig_cmdresp_no, cmdresp_result, + le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); + mwifiex_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp, + le16_to_cpu(resp->size)); + + if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { + mwifiex_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n"); + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + return -1; + } + + if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { + adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; + if ((cmdresp_result == HostCmd_RESULT_OK) && + (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) + ret = mwifiex_ret_802_11_hs_cfg(priv, resp); + } else { + /* handle response */ + ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp); + } + + /* Check init command response */ + if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { + if (ret) { + mwifiex_dbg(adapter, ERROR, + "%s: cmd %#x failed during\t" + "initialization\n", __func__, cmdresp_no); + mwifiex_init_fw_complete(adapter); + return -1; + } else if (adapter->last_init_cmd == cmdresp_no) + adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE; + } + + if (adapter->curr_cmd) { + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = ret; + + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + } + + return ret; +} + +/* + * This function handles the timeout of command sending. + * + * It will re-send the same command again. + */ +void +mwifiex_cmd_timeout_func(unsigned long function_context) +{ + struct mwifiex_adapter *adapter = + (struct mwifiex_adapter *) function_context; + struct cmd_ctrl_node *cmd_node; + + adapter->is_cmd_timedout = 1; + if (!adapter->curr_cmd) { + mwifiex_dbg(adapter, ERROR, + "cmd: empty curr_cmd\n"); + return; + } + cmd_node = adapter->curr_cmd; + if (cmd_node) { + adapter->dbg.timeout_cmd_id = + adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; + adapter->dbg.timeout_cmd_act = + adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; + mwifiex_dbg(adapter, MSG, + "%s: Timeout cmd id = %#x, act = %#x\n", __func__, + adapter->dbg.timeout_cmd_id, + adapter->dbg.timeout_cmd_act); + + mwifiex_dbg(adapter, MSG, + "num_data_h2c_failure = %d\n", + adapter->dbg.num_tx_host_to_card_failure); + mwifiex_dbg(adapter, MSG, + "num_cmd_h2c_failure = %d\n", + adapter->dbg.num_cmd_host_to_card_failure); + + mwifiex_dbg(adapter, MSG, + "is_cmd_timedout = %d\n", + adapter->is_cmd_timedout); + mwifiex_dbg(adapter, MSG, + "num_tx_timeout = %d\n", + adapter->dbg.num_tx_timeout); + + mwifiex_dbg(adapter, MSG, + "last_cmd_index = %d\n", + adapter->dbg.last_cmd_index); + mwifiex_dbg(adapter, MSG, + "last_cmd_id: %*ph\n", + (int)sizeof(adapter->dbg.last_cmd_id), + adapter->dbg.last_cmd_id); + mwifiex_dbg(adapter, MSG, + "last_cmd_act: %*ph\n", + (int)sizeof(adapter->dbg.last_cmd_act), + adapter->dbg.last_cmd_act); + + mwifiex_dbg(adapter, MSG, + "last_cmd_resp_index = %d\n", + adapter->dbg.last_cmd_resp_index); + mwifiex_dbg(adapter, MSG, + "last_cmd_resp_id: %*ph\n", + (int)sizeof(adapter->dbg.last_cmd_resp_id), + adapter->dbg.last_cmd_resp_id); + + mwifiex_dbg(adapter, MSG, + "last_event_index = %d\n", + adapter->dbg.last_event_index); + mwifiex_dbg(adapter, MSG, + "last_event: %*ph\n", + (int)sizeof(adapter->dbg.last_event), + adapter->dbg.last_event); + + mwifiex_dbg(adapter, MSG, + "data_sent=%d cmd_sent=%d\n", + adapter->data_sent, adapter->cmd_sent); + + mwifiex_dbg(adapter, MSG, + "ps_mode=%d ps_state=%d\n", + adapter->ps_mode, adapter->ps_state); + + if (cmd_node->wait_q_enabled) { + adapter->cmd_wait_q.status = -ETIMEDOUT; + mwifiex_cancel_pending_ioctl(adapter); + } + } + if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { + mwifiex_init_fw_complete(adapter); + return; + } + + if (adapter->if_ops.device_dump) + adapter->if_ops.device_dump(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); +} + +/* + * This function cancels all the pending commands. + * + * The current command, all commands in command pending queue and all scan + * commands in scan pending queue are cancelled. All the completion callbacks + * are called with failure status to ensure cleanup. + */ +void +mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; + unsigned long flags, cmd_flags; + struct mwifiex_private *priv; + int i; + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + /* Cancel current cmd */ + if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { + adapter->curr_cmd->wait_q_enabled = false; + adapter->cmd_wait_q.status = -1; + mwifiex_complete_cmd(adapter, adapter->curr_cmd); + /* no recycle probably wait for response */ + } + /* Cancel all pending command */ + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->cmd_pending_q, list) { + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + + if (cmd_node->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + mwifiex_recycle_cmd_node(adapter, cmd_node); + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + } + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + + /* Cancel all pending scan command */ + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + + cmd_node->wait_q_enabled = false; + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + + if (adapter->scan_processing) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (priv->scan_request) { + mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } + } + } +} + +/* + * This function cancels all pending commands that matches with + * the given IOCTL request. + * + * Both the current command buffer and the pending command queue are + * searched for matching IOCTL request. The completion callback of + * the matched command is called with failure status to ensure cleanup. + * In case of scan commands, all pending commands in scan pending queue + * are cancelled. + */ +void +mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) +{ + struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; + unsigned long cmd_flags; + unsigned long scan_pending_q_flags; + struct mwifiex_private *priv; + int i; + + if ((adapter->curr_cmd) && + (adapter->curr_cmd->wait_q_enabled)) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + cmd_node = adapter->curr_cmd; + /* setting curr_cmd to NULL is quite dangerous, because + * mwifiex_process_cmdresp checks curr_cmd to be != NULL + * at the beginning then relies on it and dereferences + * it at will + * this probably works since mwifiex_cmd_timeout_func + * is the only caller of this function and responses + * at that point + */ + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + + mwifiex_recycle_cmd_node(adapter, cmd_node); + } + + /* Cancel all pending scan command */ + spin_lock_irqsave(&adapter->scan_pending_q_lock, + scan_pending_q_flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + cmd_node->wait_q_enabled = false; + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + scan_pending_q_flags); + + if (adapter->scan_processing) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (priv->scan_request) { + mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } + } + } +} + +/* + * This function sends the sleep confirm command to firmware, if + * possible. + * + * The sleep confirm command cannot be issued if command response, + * data response or event response is awaiting handling, or if we + * are in the middle of sending a command, or expecting a command + * response. + */ +void +mwifiex_check_ps_cond(struct mwifiex_adapter *adapter) +{ + if (!adapter->cmd_sent && + !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter)) + mwifiex_dnld_sleep_confirm_cmd(adapter); + else + mwifiex_dbg(adapter, CMD, + "cmd: Delay Sleep Confirm (%s%s%s)\n", + (adapter->cmd_sent) ? "D" : "", + (adapter->curr_cmd) ? "C" : "", + (IS_CARD_RX_RCVD(adapter)) ? "R" : ""); +} + +/* + * This function sends a Host Sleep activated event to applications. + * + * This event is generated by the driver, with a blank event body. + */ +void +mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated) +{ + if (activated) { + if (priv->adapter->is_hs_configured) { + priv->adapter->hs_activated = true; + mwifiex_update_rxreor_flags(priv->adapter, + RXREOR_FORCE_NO_DROP); + mwifiex_dbg(priv->adapter, EVENT, + "event: hs_activated\n"); + priv->adapter->hs_activate_wait_q_woken = true; + wake_up_interruptible( + &priv->adapter->hs_activate_wait_q); + } else { + mwifiex_dbg(priv->adapter, EVENT, + "event: HS not configured\n"); + } + } else { + mwifiex_dbg(priv->adapter, EVENT, + "event: hs_deactivated\n"); + priv->adapter->hs_activated = false; + } +} + +/* + * This function handles the command response of a Host Sleep configuration + * command. + * + * Handling includes changing the header fields into CPU format + * and setting the current host sleep activation status in driver. + * + * In case host sleep status change, the function generates an event to + * notify the applications. + */ +int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg = + &resp->params.opt_hs_cfg; + uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); + + if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) && + adapter->iface_type != MWIFIEX_USB) { + mwifiex_hs_activated_event(priv, true); + return 0; + } else { + mwifiex_dbg(adapter, CMD, + "cmd: CMD_RESP: HS_CFG cmd reply\t" + " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n", + resp->result, conditions, + phs_cfg->params.hs_config.gpio, + phs_cfg->params.hs_config.gap); + } + if (conditions != HS_CFG_CANCEL) { + adapter->is_hs_configured = true; + if (adapter->iface_type == MWIFIEX_USB) + mwifiex_hs_activated_event(priv, true); + } else { + adapter->is_hs_configured = false; + if (adapter->hs_activated) + mwifiex_hs_activated_event(priv, false); + } + + return 0; +} + +/* + * This function wakes up the adapter and generates a Host Sleep + * cancel event on receiving the power up interrupt. + */ +void +mwifiex_process_hs_config(struct mwifiex_adapter *adapter) +{ + mwifiex_dbg(adapter, INFO, + "info: %s: auto cancelling host sleep\t" + "since there is interrupt from the firmware\n", + __func__); + + adapter->if_ops.wakeup(adapter); + adapter->hs_activated = false; + adapter->is_hs_configured = false; + adapter->is_suspended = false; + mwifiex_hs_activated_event(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), + false); +} +EXPORT_SYMBOL_GPL(mwifiex_process_hs_config); + +/* + * This function handles the command response of a sleep confirm command. + * + * The function sets the card state to SLEEP if the response indicates success. + */ +void +mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, + u8 *pbuf, u32 upld_len) +{ + struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf; + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + uint16_t result = le16_to_cpu(cmd->result); + uint16_t command = le16_to_cpu(cmd->command); + uint16_t seq_num = le16_to_cpu(cmd->seq_num); + + if (!upld_len) { + mwifiex_dbg(adapter, ERROR, + "%s: cmd size is 0\n", __func__); + return; + } + + mwifiex_dbg(adapter, CMD, + "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", + command, result, le16_to_cpu(cmd->size), seq_num); + + /* Get BSS number and corresponding priv */ + priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num), + HostCmd_GET_BSS_TYPE(seq_num)); + if (!priv) + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + /* Update sequence number */ + seq_num = HostCmd_GET_SEQ_NO(seq_num); + /* Clear RET_BIT from HostCmd */ + command &= HostCmd_CMD_ID_MASK; + + if (command != HostCmd_CMD_802_11_PS_MODE_ENH) { + mwifiex_dbg(adapter, ERROR, + "%s: rcvd unexpected resp for cmd %#x, result = %x\n", + __func__, command, result); + return; + } + + if (result) { + mwifiex_dbg(adapter, ERROR, + "%s: sleep confirm cmd failed\n", + __func__); + adapter->pm_wakeup_card_req = false; + adapter->ps_state = PS_STATE_AWAKE; + return; + } + adapter->pm_wakeup_card_req = true; + if (adapter->is_hs_configured) + mwifiex_hs_activated_event(mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), + true); + adapter->ps_state = PS_STATE_SLEEP; + cmd->command = cpu_to_le16(command); + cmd->seq_num = cpu_to_le16(seq_num); +} +EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp); + +/* + * This function prepares an enhanced power mode command. + * + * This function can be used to disable power save or to configure + * power save with auto PS or STA PS or auto deep sleep. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV, + * auto deep sleep TLV (as required) + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, uint16_t ps_bitmap, + struct mwifiex_ds_auto_ds *auto_ds) +{ + struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh = + &cmd->params.psmode_enh; + u8 *tlv; + u16 cmd_size = 0; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); + if (cmd_action == DIS_AUTO_PS) { + psmode_enh->action = cpu_to_le16(DIS_AUTO_PS); + psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap)); + } else if (cmd_action == GET_PS) { + psmode_enh->action = cpu_to_le16(GET_PS); + psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap)); + } else if (cmd_action == EN_AUTO_PS) { + psmode_enh->action = cpu_to_le16(EN_AUTO_PS); + psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); + cmd_size = S_DS_GEN + sizeof(psmode_enh->action) + + sizeof(psmode_enh->params.ps_bitmap); + tlv = (u8 *) cmd + cmd_size; + if (ps_bitmap & BITMAP_STA_PS) { + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_ps_param *ps_tlv = + (struct mwifiex_ie_types_ps_param *) tlv; + struct mwifiex_ps_param *ps_mode = &ps_tlv->param; + ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM); + ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) - + sizeof(struct mwifiex_ie_types_header)); + cmd_size += sizeof(*ps_tlv); + tlv += sizeof(*ps_tlv); + mwifiex_dbg(priv->adapter, CMD, + "cmd: PS Command: Enter PS\n"); + ps_mode->null_pkt_interval = + cpu_to_le16(adapter->null_pkt_interval); + ps_mode->multiple_dtims = + cpu_to_le16(adapter->multiple_dtim); + ps_mode->bcn_miss_timeout = + cpu_to_le16(adapter->bcn_miss_time_out); + ps_mode->local_listen_interval = + cpu_to_le16(adapter->local_listen_interval); + ps_mode->adhoc_wake_period = + cpu_to_le16(adapter->adhoc_awake_period); + ps_mode->delay_to_ps = + cpu_to_le16(adapter->delay_to_ps); + ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode); + + } + if (ps_bitmap & BITMAP_AUTO_DS) { + struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv = + (struct mwifiex_ie_types_auto_ds_param *) tlv; + u16 idletime = 0; + + auto_ds_tlv->header.type = + cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); + auto_ds_tlv->header.len = + cpu_to_le16(sizeof(*auto_ds_tlv) - + sizeof(struct mwifiex_ie_types_header)); + cmd_size += sizeof(*auto_ds_tlv); + tlv += sizeof(*auto_ds_tlv); + if (auto_ds) + idletime = auto_ds->idle_time; + mwifiex_dbg(priv->adapter, CMD, + "cmd: PS Command: Enter Auto Deep Sleep\n"); + auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime); + } + cmd->size = cpu_to_le16(cmd_size); + } + return 0; +} + +/* + * This function handles the command response of an enhanced power mode + * command. + * + * Handling includes changing the header fields into CPU format + * and setting the current enhanced power mode in driver. + */ +int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct mwifiex_ds_pm_cfg *pm_cfg) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ps_mode_enh *ps_mode = + &resp->params.psmode_enh; + uint16_t action = le16_to_cpu(ps_mode->action); + uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); + uint16_t auto_ps_bitmap = + le16_to_cpu(ps_mode->params.ps_bitmap); + + mwifiex_dbg(adapter, INFO, + "info: %s: PS_MODE cmd reply result=%#x action=%#X\n", + __func__, resp->result, action); + if (action == EN_AUTO_PS) { + if (auto_ps_bitmap & BITMAP_AUTO_DS) { + mwifiex_dbg(adapter, CMD, + "cmd: Enabled auto deep sleep\n"); + priv->adapter->is_deep_sleep = true; + } + if (auto_ps_bitmap & BITMAP_STA_PS) { + mwifiex_dbg(adapter, CMD, + "cmd: Enabled STA power save\n"); + if (adapter->sleep_period.period) + mwifiex_dbg(adapter, CMD, + "cmd: set to uapsd/pps mode\n"); + } + } else if (action == DIS_AUTO_PS) { + if (ps_bitmap & BITMAP_AUTO_DS) { + priv->adapter->is_deep_sleep = false; + mwifiex_dbg(adapter, CMD, + "cmd: Disabled auto deep sleep\n"); + } + if (ps_bitmap & BITMAP_STA_PS) { + mwifiex_dbg(adapter, CMD, + "cmd: Disabled STA power save\n"); + if (adapter->sleep_period.period) { + adapter->delay_null_pkt = false; + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + } + } + } else if (action == GET_PS) { + if (ps_bitmap & BITMAP_STA_PS) + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; + else + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; + + mwifiex_dbg(adapter, CMD, + "cmd: ps_bitmap=%#x\n", ps_bitmap); + + if (pm_cfg) { + /* This section is for get power save mode */ + if (ps_bitmap & BITMAP_STA_PS) + pm_cfg->param.ps_mode = 1; + else + pm_cfg->param.ps_mode = 0; + } + } + return 0; +} + +/* + * This function prepares command to get hardware specifications. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting permanent address parameter + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd) +{ + struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec; + + cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN); + memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN); + + return 0; +} + +/* + * This function handles the command response of get hardware + * specifications. + * + * Handling includes changing the header fields into CPU format + * and saving/updating the following parameters in driver - + * - Firmware capability information + * - Firmware band settings + * - Ad-hoc start band and channel + * - Ad-hoc 11n activation status + * - Firmware release number + * - Number of antennas + * - Hardware address + * - Hardware interface version + * - Firmware version + * - Region code + * - 11n capabilities + * - MCS support fields + * - MP end port + */ +int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_header *tlv; + struct hw_spec_api_rev *api_rev; + u16 resp_size, api_id; + int i, left_len, parsed_len = 0; + + adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); + + if (IS_SUPPORT_MULTI_BANDS(adapter)) + adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter); + else + adapter->fw_bands = BAND_B; + + adapter->config_bands = adapter->fw_bands; + + if (adapter->fw_bands & BAND_A) { + if (adapter->fw_bands & BAND_GN) { + adapter->config_bands |= BAND_AN; + adapter->fw_bands |= BAND_AN; + } + if (adapter->fw_bands & BAND_AN) { + adapter->adhoc_start_band = BAND_A | BAND_AN; + adapter->adhoc_11n_enabled = true; + } else { + adapter->adhoc_start_band = BAND_A; + } + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A; + } else if (adapter->fw_bands & BAND_GN) { + adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + adapter->adhoc_11n_enabled = true; + } else if (adapter->fw_bands & BAND_G) { + adapter->adhoc_start_band = BAND_G | BAND_B; + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + } else if (adapter->fw_bands & BAND_B) { + adapter->adhoc_start_band = BAND_B; + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + } + + adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); + adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff; + adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna); + + if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) { + adapter->is_hw_11ac_capable = true; + + /* Copy 11AC cap */ + adapter->hw_dot_11ac_dev_cap = + le32_to_cpu(hw_spec->dot_11ac_dev_cap); + adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap + & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; + adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap + & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; + + /* Copy 11AC mcs */ + adapter->hw_dot_11ac_mcs_support = + le32_to_cpu(hw_spec->dot_11ac_mcs_support); + adapter->usr_dot_11ac_mcs_support = + adapter->hw_dot_11ac_mcs_support; + } else { + adapter->is_hw_11ac_capable = false; + } + + resp_size = le16_to_cpu(resp->size) - S_DS_GEN; + if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) { + /* we have variable HW SPEC information */ + left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec); + while (left_len > sizeof(struct mwifiex_ie_types_header)) { + tlv = (void *)&hw_spec->tlvs + parsed_len; + switch (le16_to_cpu(tlv->type)) { + case TLV_TYPE_API_REV: + api_rev = (struct hw_spec_api_rev *)tlv; + api_id = le16_to_cpu(api_rev->api_id); + switch (api_id) { + case KEY_API_VER_ID: + adapter->key_api_major_ver = + api_rev->major_ver; + adapter->key_api_minor_ver = + api_rev->minor_ver; + mwifiex_dbg(adapter, INFO, + "key_api v%d.%d\n", + adapter->key_api_major_ver, + adapter->key_api_minor_ver); + break; + case FW_API_VER_ID: + adapter->fw_api_ver = + api_rev->major_ver; + mwifiex_dbg(adapter, INFO, + "Firmware api version %d\n", + adapter->fw_api_ver); + break; + default: + mwifiex_dbg(adapter, FATAL, + "Unknown api_id: %d\n", + api_id); + break; + } + break; + default: + mwifiex_dbg(adapter, FATAL, + "Unknown GET_HW_SPEC TLV type: %#x\n", + le16_to_cpu(tlv->type)); + break; + } + parsed_len += le16_to_cpu(tlv->len) + + sizeof(struct mwifiex_ie_types_header); + left_len -= le16_to_cpu(tlv->len) + + sizeof(struct mwifiex_ie_types_header); + } + } + + mwifiex_dbg(adapter, INFO, + "info: GET_HW_SPEC: fw_release_number- %#x\n", + adapter->fw_release_number); + mwifiex_dbg(adapter, INFO, + "info: GET_HW_SPEC: permanent addr: %pM\n", + hw_spec->permanent_addr); + mwifiex_dbg(adapter, INFO, + "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n", + le16_to_cpu(hw_spec->hw_if_version), + le16_to_cpu(hw_spec->version)); + + ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr); + adapter->region_code = le16_to_cpu(hw_spec->region_code); + + for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++) + /* Use the region code to search for the index */ + if (adapter->region_code == region_code_index[i]) + break; + + /* If it's unidentified region code, use the default (USA) */ + if (i >= MWIFIEX_MAX_REGION_CODE) { + adapter->region_code = 0x10; + mwifiex_dbg(adapter, WARN, + "cmd: unknown region code, use default (USA)\n"); + } + + adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); + adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; + adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support; + + if (adapter->if_ops.update_mp_end_port) + adapter->if_ops.update_mp_end_port(adapter, + le16_to_cpu(hw_spec->mp_end_port)); + + if (adapter->fw_api_ver == MWIFIEX_FW_V15) + adapter->scan_chan_gap_enabled = true; + + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c new file mode 100644 index 000000000000..9824d8dd2b44 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c @@ -0,0 +1,1005 @@ +/* + * Marvell Wireless LAN device driver: debugfs + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include + +#include "main.h" +#include "11n.h" + + +static struct dentry *mwifiex_dfs_dir; + +static char *bss_modes[] = { + "UNSPECIFIED", + "ADHOC", + "STATION", + "AP", + "AP_VLAN", + "WDS", + "MONITOR", + "MESH_POINT", + "P2P_CLIENT", + "P2P_GO", + "P2P_DEVICE", +}; + +/* + * Proc info file read handler. + * + * This function is called when the 'info' file is opened for reading. + * It prints the following driver related information - + * - Driver name + * - Driver version + * - Driver extended version + * - Interface name + * - BSS mode + * - Media state (connected or disconnected) + * - MAC address + * - Total number of Tx bytes + * - Total number of Rx bytes + * - Total number of Tx packets + * - Total number of Rx packets + * - Total number of dropped Tx packets + * - Total number of dropped Rx packets + * - Total number of corrupted Tx packets + * - Total number of corrupted Rx packets + * - Carrier status (on or off) + * - Tx queue status (started or stopped) + * + * For STA mode drivers, it also prints the following extra - + * - ESSID + * - BSSID + * - Channel + * - Region code + * - Multicast count + * - Multicast addresses + */ +static ssize_t +mwifiex_info_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + struct net_device *netdev = priv->netdev; + struct netdev_hw_addr *ha; + struct netdev_queue *txq; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *) page, fmt[64]; + struct mwifiex_bss_info info; + ssize_t ret; + int i = 0; + + if (!p) + return -ENOMEM; + + memset(&info, 0, sizeof(info)); + ret = mwifiex_get_bss_info(priv, &info); + if (ret) + goto free_and_exit; + + mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); + + if (!priv->version_str[0]) + mwifiex_get_ver_ext(priv); + + p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); + p += sprintf(p, "driver_version = %s", fmt); + p += sprintf(p, "\nverext = %s", priv->version_str); + p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); + + if (info.bss_mode >= ARRAY_SIZE(bss_modes)) + p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode); + else + p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]); + + p += sprintf(p, "media_state=\"%s\"\n", + (!priv->media_connected ? "Disconnected" : "Connected")); + p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { + p += sprintf(p, "multicast_count=\"%d\"\n", + netdev_mc_count(netdev)); + p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid); + p += sprintf(p, "bssid=\"%pM\"\n", info.bssid); + p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan); + p += sprintf(p, "country_code = \"%s\"\n", info.country_code); + + netdev_for_each_mc_addr(ha, netdev) + p += sprintf(p, "multicast_address[%d]=\"%pM\"\n", + i++, ha->addr); + } + + p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); + p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); + p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); + p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); + p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); + p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); + p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); + p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); + p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) + ? "on" : "off")); + p += sprintf(p, "tx queue"); + for (i = 0; i < netdev->num_tx_queues; i++) { + txq = netdev_get_tx_queue(netdev, i); + p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p += sprintf(p, "\n"); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + +free_and_exit: + free_page(page); + return ret; +} + +/* + * Proc device dump read handler. + * + * This function is called when the 'device_dump' file is opened for + * reading. + * This function dumps driver information and firmware memory segments + * (ex. DTCM, ITCM, SQRAM etc.) for + * debugging. + */ +static ssize_t +mwifiex_device_dump_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = file->private_data; + + if (!priv->adapter->if_ops.device_dump) + return -EIO; + + priv->adapter->if_ops.device_dump(priv->adapter); + + return 0; +} + +/* + * Proc getlog file read handler. + * + * This function is called when the 'getlog' file is opened for reading + * It prints the following log information - + * - Number of multicast Tx frames + * - Number of failed packets + * - Number of Tx retries + * - Number of multicast Tx retries + * - Number of duplicate frames + * - Number of RTS successes + * - Number of RTS failures + * - Number of ACK failures + * - Number of fragmented Rx frames + * - Number of multicast Rx frames + * - Number of FCS errors + * - Number of Tx frames + * - WEP ICV error counts + * - Number of received beacons + * - Number of missed beacons + */ +static ssize_t +mwifiex_getlog_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *) page; + ssize_t ret; + struct mwifiex_ds_get_stats stats; + + if (!p) + return -ENOMEM; + + memset(&stats, 0, sizeof(stats)); + ret = mwifiex_get_stats_info(priv, &stats); + if (ret) + goto free_and_exit; + + p += sprintf(p, "\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" + "bcn_rcv_cnt %u\n" + "bcn_miss_cnt %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); + + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + +free_and_exit: + free_page(page); + return ret; +} + +/* Sysfs histogram file read handler. + * + * This function is called when the 'histogram' file is opened for reading + * It prints the following histogram information - + * - Number of histogram samples + * - Receive packet number of each rx_rate + * - Receive packet number of each snr + * - Receive packet number of each nosie_flr + * - Receive packet number of each signal streath + */ +static ssize_t +mwifiex_histogram_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *)file->private_data; + ssize_t ret; + struct mwifiex_histogram_data *phist_data; + int i, value; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *)page; + + if (!p) + return -ENOMEM; + + if (!priv || !priv->hist_data) + return -EFAULT; + phist_data = priv->hist_data; + + p += sprintf(p, "\n" + "total samples = %d\n", + atomic_read(&phist_data->num_samples)); + + p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M"); + p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n"); + p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M"); + p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + p += sprintf(p, "44-53=MCS0-9(VHT:BW20)"); + p += sprintf(p, "54-63=MCS0-9(VHT:BW40)"); + p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n"); + } else { + p += sprintf(p, "\n"); + } + + for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", i, value); + } + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { + for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES; + i++) { + value = atomic_read(&phist_data->rx_rate[i]); + if (value) + p += sprintf(p, "rx_rate[%02d] = %d\n", + i, value); + } + } + + for (i = 0; i < MWIFIEX_MAX_SNR; i++) { + value = atomic_read(&phist_data->snr[i]); + if (value) + p += sprintf(p, "snr[%02ddB] = %d\n", i, value); + } + for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) { + value = atomic_read(&phist_data->noise_flr[i]); + if (value) + p += sprintf(p, "noise_flr[-%02ddBm] = %d\n", + (int)(i-128), value); + } + for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) { + value = atomic_read(&phist_data->sig_str[i]); + if (value) + p += sprintf(p, "sig_strength[-%02ddBm] = %d\n", + i, value); + } + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, + (unsigned long)p - page); + + return ret; +} + +static ssize_t +mwifiex_histogram_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + + if (priv && priv->hist_data) + mwifiex_hist_data_reset(priv); + return 0; +} + +static struct mwifiex_debug_info info; + +/* + * Proc debug file read handler. + * + * This function is called when the 'debug' file is opened for reading + * It prints the following log information - + * - Interrupt count + * - WMM AC VO packets count + * - WMM AC VI packets count + * - WMM AC BE packets count + * - WMM AC BK packets count + * - Maximum Tx buffer size + * - Tx buffer size + * - Current Tx buffer size + * - Power Save mode + * - Power Save state + * - Deep Sleep status + * - Device wakeup required status + * - Number of wakeup tries + * - Host Sleep configured status + * - Host Sleep activated status + * - Number of Tx timeouts + * - Number of command timeouts + * - Last timed out command ID + * - Last timed out command action + * - Last command ID + * - Last command action + * - Last command index + * - Last command response ID + * - Last command response index + * - Last event + * - Last event index + * - Number of host to card command failures + * - Number of sleep confirm command failures + * - Number of host to card data failure + * - Number of deauthentication events + * - Number of disassociation events + * - Number of link lost events + * - Number of deauthentication commands + * - Number of association success commands + * - Number of association failure commands + * - Number of commands sent + * - Number of data packets sent + * - Number of command responses received + * - Number of events received + * - Tx BA stream table (TID, RA) + * - Rx reorder table (TID, TA, Start window, Window size, Buffer) + */ +static ssize_t +mwifiex_debug_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *p = (char *) page; + ssize_t ret; + + if (!p) + return -ENOMEM; + + ret = mwifiex_get_debug_info(priv, &info); + if (ret) + goto free_and_exit; + + p += mwifiex_debug_info_to_buffer(priv, p, &info); + + ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, + (unsigned long) p - page); + +free_and_exit: + free_page(page); + return ret; +} + +static u32 saved_reg_type, saved_reg_offset, saved_reg_value; + +/* + * Proc regrdwr file write handler. + * + * This function is called when the 'regrdwr' file is opened for writing + * + * This function can be used to write to a register. + */ +static ssize_t +mwifiex_regrdwr_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); + int ret; + u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; + + if (!buf) + return -ENOMEM; + + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); + + if (reg_type == 0 || reg_offset == 0) { + ret = -EINVAL; + goto done; + } else { + saved_reg_type = reg_type; + saved_reg_offset = reg_offset; + saved_reg_value = reg_value; + ret = count; + } +done: + free_page(addr); + return ret; +} + +/* + * Proc regrdwr file read handler. + * + * This function is called when the 'regrdwr' file is opened for reading + * + * This function can be used to read from a register. + */ +static ssize_t +mwifiex_regrdwr_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + int pos = 0, ret = 0; + u32 reg_value; + + if (!buf) + return -ENOMEM; + + if (!saved_reg_type) { + /* No command has been given */ + pos += snprintf(buf, PAGE_SIZE, "0"); + goto done; + } + /* Set command has been given */ + if (saved_reg_value != UINT_MAX) { + ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset, + saved_reg_value); + + pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", + saved_reg_type, saved_reg_offset, + saved_reg_value); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + goto done; + } + /* Get command has been given */ + ret = mwifiex_reg_read(priv, saved_reg_type, + saved_reg_offset, ®_value); + if (ret) { + ret = -EINVAL; + goto done; + } + + pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, + saved_reg_offset, reg_value); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + +done: + free_page(addr); + return ret; +} + +/* Proc debug_mask file read handler. + * This function is called when the 'debug_mask' file is opened for reading + * This function can be used read driver debugging mask value. + */ +static ssize_t +mwifiex_debug_mask_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *)file->private_data; + unsigned long page = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)page; + size_t ret = 0; + int pos = 0; + + if (!buf) + return -ENOMEM; + + pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n", + priv->adapter->debug_mask); + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(page); + return ret; +} + +/* Proc debug_mask file read handler. + * This function is called when the 'debug_mask' file is opened for reading + * This function can be used read driver debugging mask value. + */ +static ssize_t +mwifiex_debug_mask_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret; + unsigned long debug_mask; + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (void *)addr; + size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1)); + + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + if (kstrtoul(buf, 0, &debug_mask)) { + ret = -EINVAL; + goto done; + } + + priv->adapter->debug_mask = debug_mask; + ret = count; +done: + free_page(addr); + return ret; +} + +/* Proc memrw file write handler. + * This function is called when the 'memrw' file is opened for writing + * This function can be used to write to a memory location. + */ +static ssize_t +mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos) +{ + int ret; + char cmd; + struct mwifiex_ds_mem_rw mem_rw; + u16 cmd_action; + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (void *)addr; + size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1)); + + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value); + if (ret != 3) { + ret = -EINVAL; + goto done; + } + + if ((cmd == 'r') || (cmd == 'R')) { + cmd_action = HostCmd_ACT_GEN_GET; + mem_rw.value = 0; + } else if ((cmd == 'w') || (cmd == 'W')) { + cmd_action = HostCmd_ACT_GEN_SET; + } else { + ret = -EINVAL; + goto done; + } + + memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw)); + if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0, + &mem_rw, true)) + ret = -1; + else + ret = count; + +done: + free_page(addr); + return ret; +} + +/* Proc memrw file read handler. + * This function is called when the 'memrw' file is opened for reading + * This function can be used to read from a memory location. + */ +static ssize_t +mwifiex_memrw_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + int ret, pos = 0; + + if (!buf) + return -ENOMEM; + + pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr, + priv->mem_rw.value); + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} + +static u32 saved_offset = -1, saved_bytes = -1; + +/* + * Proc rdeeprom file write handler. + * + * This function is called when the 'rdeeprom' file is opened for writing + * + * This function can be used to write to a RDEEPROM location. + */ +static ssize_t +mwifiex_rdeeprom_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); + int ret = 0; + int offset = -1, bytes = -1; + + if (!buf) + return -ENOMEM; + + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + sscanf(buf, "%d %d", &offset, &bytes); + + if (offset == -1 || bytes == -1) { + ret = -EINVAL; + goto done; + } else { + saved_offset = offset; + saved_bytes = bytes; + ret = count; + } +done: + free_page(addr); + return ret; +} + +/* + * Proc rdeeprom read write handler. + * + * This function is called when the 'rdeeprom' file is opened for reading + * + * This function can be used to read from a RDEEPROM location. + */ +static ssize_t +mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = + (struct mwifiex_private *) file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *) addr; + int pos, ret, i; + u8 value[MAX_EEPROM_DATA]; + + if (!buf) + return -ENOMEM; + + if (saved_offset == -1) { + /* No command has been given */ + pos = snprintf(buf, PAGE_SIZE, "0"); + goto done; + } + + /* Get command has been given */ + ret = mwifiex_eeprom_read(priv, (u16) saved_offset, + (u16) saved_bytes, value); + if (ret) { + ret = -EINVAL; + goto out_free; + } + + pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); + + for (i = 0; i < saved_bytes; i++) + pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); + +done: + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); +out_free: + free_page(addr); + return ret; +} + +/* Proc hscfg file write handler + * This function can be used to configure the host sleep parameters. + */ +static ssize_t +mwifiex_hscfg_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); + int ret, arg_num; + struct mwifiex_ds_hs_cfg hscfg; + int conditions = HS_CFG_COND_DEF; + u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; + + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, ubuf, buf_size)) { + ret = -EFAULT; + goto done; + } + + arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); + + memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); + + if (arg_num > 3) { + mwifiex_dbg(priv->adapter, ERROR, + "Too many arguments\n"); + ret = -EINVAL; + goto done; + } + + if (arg_num >= 1 && arg_num < 3) + mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, + MWIFIEX_SYNC_CMD, &hscfg); + + if (arg_num) { + if (conditions == HS_CFG_CANCEL) { + mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD); + ret = count; + goto done; + } + hscfg.conditions = conditions; + } + if (arg_num >= 2) + hscfg.gpio = gpio; + if (arg_num == 3) + hscfg.gap = gap; + + hscfg.is_invoke_hostcmd = false; + mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + MWIFIEX_SYNC_CMD, &hscfg); + + mwifiex_enable_hs(priv->adapter); + priv->adapter->hs_enabling = false; + ret = count; +done: + free_page(addr); + return ret; +} + +/* Proc hscfg file read handler + * This function can be used to read host sleep configuration + * parameters from driver. + */ +static ssize_t +mwifiex_hscfg_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = (void *)file->private_data; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + int pos, ret; + struct mwifiex_ds_hs_cfg hscfg; + + if (!buf) + return -ENOMEM; + + mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, + MWIFIEX_SYNC_CMD, &hscfg); + + pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, + hscfg.gpio, hscfg.gap); + + ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); + + free_page(addr); + return ret; +} + +static ssize_t +mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct mwifiex_private *priv = file->private_data; + char buf[3]; + bool timeshare_coex; + int ret; + unsigned int len; + + if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) + return -EOPNOTSUPP; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, + HostCmd_ACT_GEN_GET, 0, ×hare_coex, true); + if (ret) + return ret; + + len = sprintf(buf, "%d\n", timeshare_coex); + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static ssize_t +mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + bool timeshare_coex; + struct mwifiex_private *priv = file->private_data; + char kbuf[16]; + int ret; + + if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) + return -EOPNOTSUPP; + + memset(kbuf, 0, sizeof(kbuf)); + + if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count))) + return -EFAULT; + + if (strtobool(kbuf, ×hare_coex)) + return -EINVAL; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, + HostCmd_ACT_GEN_SET, 0, ×hare_coex, true); + if (ret) + return ret; + else + return count; +} + +#define MWIFIEX_DFS_ADD_FILE(name) do { \ + if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ + priv, &mwifiex_dfs_##name##_fops)) \ + return; \ +} while (0); + +#define MWIFIEX_DFS_FILE_OPS(name) \ +static const struct file_operations mwifiex_dfs_##name##_fops = { \ + .read = mwifiex_##name##_read, \ + .write = mwifiex_##name##_write, \ + .open = simple_open, \ +}; + +#define MWIFIEX_DFS_FILE_READ_OPS(name) \ +static const struct file_operations mwifiex_dfs_##name##_fops = { \ + .read = mwifiex_##name##_read, \ + .open = simple_open, \ +}; + +#define MWIFIEX_DFS_FILE_WRITE_OPS(name) \ +static const struct file_operations mwifiex_dfs_##name##_fops = { \ + .write = mwifiex_##name##_write, \ + .open = simple_open, \ +}; + + +MWIFIEX_DFS_FILE_READ_OPS(info); +MWIFIEX_DFS_FILE_READ_OPS(debug); +MWIFIEX_DFS_FILE_READ_OPS(getlog); +MWIFIEX_DFS_FILE_READ_OPS(device_dump); +MWIFIEX_DFS_FILE_OPS(regrdwr); +MWIFIEX_DFS_FILE_OPS(rdeeprom); +MWIFIEX_DFS_FILE_OPS(memrw); +MWIFIEX_DFS_FILE_OPS(hscfg); +MWIFIEX_DFS_FILE_OPS(histogram); +MWIFIEX_DFS_FILE_OPS(debug_mask); +MWIFIEX_DFS_FILE_OPS(timeshare_coex); + +/* + * This function creates the debug FS directory structure and the files. + */ +void +mwifiex_dev_debugfs_init(struct mwifiex_private *priv) +{ + if (!mwifiex_dfs_dir || !priv) + return; + + priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, + mwifiex_dfs_dir); + + if (!priv->dfs_dev_dir) + return; + + MWIFIEX_DFS_ADD_FILE(info); + MWIFIEX_DFS_ADD_FILE(debug); + MWIFIEX_DFS_ADD_FILE(getlog); + MWIFIEX_DFS_ADD_FILE(regrdwr); + MWIFIEX_DFS_ADD_FILE(rdeeprom); + MWIFIEX_DFS_ADD_FILE(device_dump); + MWIFIEX_DFS_ADD_FILE(memrw); + MWIFIEX_DFS_ADD_FILE(hscfg); + MWIFIEX_DFS_ADD_FILE(histogram); + MWIFIEX_DFS_ADD_FILE(debug_mask); + MWIFIEX_DFS_ADD_FILE(timeshare_coex); +} + +/* + * This function removes the debug FS directory structure and the files. + */ +void +mwifiex_dev_debugfs_remove(struct mwifiex_private *priv) +{ + if (!priv) + return; + + debugfs_remove_recursive(priv->dfs_dev_dir); +} + +/* + * This function creates the top level proc directory. + */ +void +mwifiex_debugfs_init(void) +{ + if (!mwifiex_dfs_dir) + mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL); +} + +/* + * This function removes the top level proc directory. + */ +void +mwifiex_debugfs_remove(void) +{ + if (mwifiex_dfs_dir) + debugfs_remove(mwifiex_dfs_dir); +} diff --git a/drivers/net/wireless/marvell/mwifiex/decl.h b/drivers/net/wireless/marvell/mwifiex/decl.h new file mode 100644 index 000000000000..098e1f14dc9a --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/decl.h @@ -0,0 +1,273 @@ +/* + * Marvell Wireless LAN device driver: generic data structures and APIs + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_DECL_H_ +#define _MWIFIEX_DECL_H_ + +#undef pr_fmt +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include + +#define MWIFIEX_BSS_COEX_COUNT 2 +#define MWIFIEX_MAX_BSS_NUM (3) + +#define MWIFIEX_DMA_ALIGN_SZ 64 +#define MWIFIEX_RX_HEADROOM 64 +#define MAX_TXPD_SZ 32 +#define INTF_HDR_ALIGN 4 + +#define MWIFIEX_MIN_DATA_HEADER_LEN (MWIFIEX_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \ + MAX_TXPD_SZ) +#define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type) + * + sizeof(tx_control) + */ + +#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 +#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 +#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8 + +#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64 +#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64 +#define MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE 16 + +#define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE 32 + +#define MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE 16 + +#define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE 16 +#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 64 +#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 64 +#define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE 64 +#define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE 64 + +#define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff + +#define MWIFIEX_RATE_BITMAP_MCS0 32 + +#define MWIFIEX_RX_DATA_BUF_SIZE (4 * 1024) +#define MWIFIEX_RX_CMD_BUF_SIZE (2 * 1024) + +#define MAX_BEACON_PERIOD (4000) +#define MIN_BEACON_PERIOD (50) +#define MAX_DTIM_PERIOD (100) +#define MIN_DTIM_PERIOD (1) + +#define MWIFIEX_RTS_MIN_VALUE (0) +#define MWIFIEX_RTS_MAX_VALUE (2347) +#define MWIFIEX_FRAG_MIN_VALUE (256) +#define MWIFIEX_FRAG_MAX_VALUE (2346) +#define MWIFIEX_WMM_VERSION 0x01 +#define MWIFIEX_WMM_SUBTYPE 0x01 + +#define MWIFIEX_RETRY_LIMIT 14 +#define MWIFIEX_SDIO_BLOCK_SIZE 256 + +#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) +#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1) +#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2) +#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3) +#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4) +#define MWIFIEX_BUF_FLAG_AGGR_PKT BIT(5) + +#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 +#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 + +#define MWIFIEX_TDLS_DISABLE_LINK 0x00 +#define MWIFIEX_TDLS_ENABLE_LINK 0x01 +#define MWIFIEX_TDLS_CREATE_LINK 0x02 +#define MWIFIEX_TDLS_CONFIG_LINK 0x03 + +#define MWIFIEX_TDLS_RSSI_HIGH 50 +#define MWIFIEX_TDLS_RSSI_LOW 55 +#define MWIFIEX_TDLS_MAX_FAIL_COUNT 4 +#define MWIFIEX_AUTO_TDLS_IDLE_TIME 10 + +/* 54M rates, index from 0 to 11 */ +#define MWIFIEX_RATE_INDEX_MCS0 12 +/* 12-27=MCS0-15(BW20) */ +#define MWIFIEX_BW20_MCS_NUM 15 + +/* Rate index for OFDM 0 */ +#define MWIFIEX_RATE_INDEX_OFDM0 4 + +#define MWIFIEX_MAX_STA_NUM 1 +#define MWIFIEX_MAX_UAP_NUM 1 +#define MWIFIEX_MAX_P2P_NUM 1 + +#define MWIFIEX_A_BAND_START_FREQ 5000 + +/* SDIO Aggr data packet special info */ +#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255) +#define BLOCK_NUMBER_OFFSET 15 +#define SDIO_HEADER_OFFSET 28 + +enum mwifiex_bss_type { + MWIFIEX_BSS_TYPE_STA = 0, + MWIFIEX_BSS_TYPE_UAP = 1, + MWIFIEX_BSS_TYPE_P2P = 2, + MWIFIEX_BSS_TYPE_ANY = 0xff, +}; + +enum mwifiex_bss_role { + MWIFIEX_BSS_ROLE_STA = 0, + MWIFIEX_BSS_ROLE_UAP = 1, + MWIFIEX_BSS_ROLE_ANY = 0xff, +}; + +enum mwifiex_tdls_status { + TDLS_NOT_SETUP = 0, + TDLS_SETUP_INPROGRESS, + TDLS_SETUP_COMPLETE, + TDLS_SETUP_FAILURE, + TDLS_LINK_TEARDOWN, + TDLS_CHAN_SWITCHING, + TDLS_IN_BASE_CHAN, + TDLS_IN_OFF_CHAN, +}; + +enum mwifiex_tdls_error_code { + TDLS_ERR_NO_ERROR = 0, + TDLS_ERR_INTERNAL_ERROR, + TDLS_ERR_MAX_LINKS_EST, + TDLS_ERR_LINK_EXISTS, + TDLS_ERR_LINK_NONEXISTENT, + TDLS_ERR_PEER_STA_UNREACHABLE = 25, +}; + +#define BSS_ROLE_BIT_MASK BIT(0) + +#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) + +enum mwifiex_data_frame_type { + MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0, + MWIFIEX_DATA_FRAME_TYPE_802_11, +}; + +struct mwifiex_fw_image { + u8 *helper_buf; + u32 helper_len; + u8 *fw_buf; + u32 fw_len; +}; + +struct mwifiex_802_11_ssid { + u32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; +}; + +struct mwifiex_wait_queue { + wait_queue_head_t wait; + int status; +}; + +struct mwifiex_rxinfo { + struct sk_buff *parent; + u8 bss_num; + u8 bss_type; + u8 use_count; + u8 buf_type; +}; + +struct mwifiex_txinfo { + u32 status_code; + u8 flags; + u8 bss_num; + u8 bss_type; + u8 aggr_num; + u32 pkt_len; + u8 ack_frame_id; + u64 cookie; +}; + +enum mwifiex_wmm_ac_e { + WMM_AC_BK, + WMM_AC_BE, + WMM_AC_VI, + WMM_AC_VO +} __packed; + +struct ieee_types_wmm_ac_parameters { + u8 aci_aifsn_bitmap; + u8 ecw_bitmap; + __le16 tx_op_limit; +} __packed; + +struct mwifiex_types_wmm_info { + u8 oui[4]; + u8 subtype; + u8 version; + u8 qos_info; + u8 reserved; + struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; +} __packed; + +struct mwifiex_arp_eth_header { + struct arphdr hdr; + u8 ar_sha[ETH_ALEN]; + u8 ar_sip[4]; + u8 ar_tha[ETH_ALEN]; + u8 ar_tip[4]; +} __packed; + +struct mwifiex_chan_stats { + u8 chan_num; + u8 bandcfg; + u8 flags; + s8 noise; + u16 total_bss; + u16 cca_scan_dur; + u16 cca_busy_dur; +} __packed; + +#define MWIFIEX_HIST_MAX_SAMPLES 1048576 +#define MWIFIEX_MAX_RX_RATES 44 +#define MWIFIEX_MAX_AC_RX_RATES 74 +#define MWIFIEX_MAX_SNR 256 +#define MWIFIEX_MAX_NOISE_FLR 256 +#define MWIFIEX_MAX_SIG_STRENGTH 256 + +struct mwifiex_histogram_data { + atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES]; + atomic_t snr[MWIFIEX_MAX_SNR]; + atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR]; + atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH]; + atomic_t num_samples; +}; + +struct mwifiex_iface_comb { + u8 sta_intf; + u8 uap_intf; + u8 p2p_intf; +}; + +struct mwifiex_radar_params { + struct cfg80211_chan_def *chandef; + u32 cac_time_ms; +} __packed; + +struct mwifiex_11h_intf_state { + bool is_11h_enabled; + bool is_11h_active; +} __packed; +#endif /* !_MWIFIEX_DECL_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/ethtool.c b/drivers/net/wireless/marvell/mwifiex/ethtool.c new file mode 100644 index 000000000000..58400c69ab26 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/ethtool.c @@ -0,0 +1,70 @@ +/* + * Marvell Wireless LAN device driver: ethtool + * + * Copyright (C) 2013-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "main.h" + +static void mwifiex_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + u32 conditions = le32_to_cpu(priv->adapter->hs_cfg.conditions); + + wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY; + + if (conditions == HS_CFG_COND_DEF) + return; + + if (conditions & HS_CFG_COND_UNICAST_DATA) + wol->wolopts |= WAKE_UCAST; + if (conditions & HS_CFG_COND_MULTICAST_DATA) + wol->wolopts |= WAKE_MCAST; + if (conditions & HS_CFG_COND_BROADCAST_DATA) + wol->wolopts |= WAKE_BCAST; + if (conditions & HS_CFG_COND_MAC_EVENT) + wol->wolopts |= WAKE_PHY; +} + +static int mwifiex_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wol) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + u32 conditions = 0; + + if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) + return -EOPNOTSUPP; + + if (wol->wolopts & WAKE_UCAST) + conditions |= HS_CFG_COND_UNICAST_DATA; + if (wol->wolopts & WAKE_MCAST) + conditions |= HS_CFG_COND_MULTICAST_DATA; + if (wol->wolopts & WAKE_BCAST) + conditions |= HS_CFG_COND_BROADCAST_DATA; + if (wol->wolopts & WAKE_PHY) + conditions |= HS_CFG_COND_MAC_EVENT; + if (wol->wolopts == 0) + conditions |= HS_CFG_COND_DEF; + priv->adapter->hs_cfg.conditions = cpu_to_le32(conditions); + + return 0; +} + +const struct ethtool_ops mwifiex_ethtool_ops = { + .get_wol = mwifiex_ethtool_get_wol, + .set_wol = mwifiex_ethtool_set_wol, +}; diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h new file mode 100644 index 000000000000..1e1e81a0a8d4 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -0,0 +1,2177 @@ +/* + * Marvell Wireless LAN device driver: Firmware specific macros & structures + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_FW_H_ +#define _MWIFIEX_FW_H_ + +#include + + +#define INTF_HEADER_LEN 4 + +struct rfc_1042_hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + __be16 snap_type; +}; + +struct rx_packet_hdr { + struct ethhdr eth803_hdr; + struct rfc_1042_hdr rfc1042_hdr; +}; + +struct tx_packet_hdr { + struct ethhdr eth803_hdr; + struct rfc_1042_hdr rfc1042_hdr; +}; + +#define B_SUPPORTED_RATES 5 +#define G_SUPPORTED_RATES 9 +#define BG_SUPPORTED_RATES 13 +#define A_SUPPORTED_RATES 9 +#define HOSTCMD_SUPPORTED_RATES 14 +#define N_SUPPORTED_RATES 3 +#define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \ + BAND_AN | BAND_AAC) + +#define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \ + BIT(13)) +#define IS_SUPPORT_MULTI_BANDS(adapter) \ + (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT) + +/* bit 13: 11ac BAND_AAC + * bit 12: reserved for lab testing, will be reused for BAND_AN + * bit 11: 11n BAND_GN + * bit 10: 11a BAND_A + * bit 9: 11g BAND_G + * bit 8: 11b BAND_B + * Map these bits to band capability by right shifting 8 bits. + */ +#define GET_FW_DEFAULT_BANDS(adapter) \ + (((adapter->fw_cap_info & 0x2f00) >> 8) & \ + ALL_802_11_BANDS) + +#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff + +#define KEY_INFO_ENABLED 0x01 +enum KEY_TYPE_ID { + KEY_TYPE_ID_WEP = 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES, + KEY_TYPE_ID_WAPI, + KEY_TYPE_ID_AES_CMAC, +}; + +#define WPA_PN_SIZE 8 +#define KEY_PARAMS_FIXED_LEN 10 +#define KEY_INDEX_MASK 0xf +#define KEY_API_VER_MAJOR_V2 2 + +#define KEY_MCAST BIT(0) +#define KEY_UNICAST BIT(1) +#define KEY_ENABLED BIT(2) +#define KEY_DEFAULT BIT(3) +#define KEY_TX_KEY BIT(4) +#define KEY_RX_KEY BIT(5) +#define KEY_IGTK BIT(10) + +#define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2) + +#define MAX_POLL_TRIES 100 +#define MAX_FIRMWARE_POLL_TRIES 100 + +#define FIRMWARE_READY_SDIO 0xfedc +#define FIRMWARE_READY_PCIE 0xfedcba00 + +#define MWIFIEX_COEX_MODE_TIMESHARE 0x01 +#define MWIFIEX_COEX_MODE_SPATIAL 0x82 + +enum mwifiex_usb_ep { + MWIFIEX_USB_EP_CMD_EVENT = 1, + MWIFIEX_USB_EP_DATA = 2, + MWIFIEX_USB_EP_DATA_CH2 = 3, +}; + +enum MWIFIEX_802_11_PRIVACY_FILTER { + MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL, + MWIFIEX_802_11_PRIV_FILTER_8021X_WEP +}; + +#define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF))) +#define CAL_RSSI(SNR, NF) ((s16)((s16)(SNR)+(s16)(NF))) + +#define UAP_BSS_PARAMS_I 0 +#define UAP_CUSTOM_IE_I 1 +#define MWIFIEX_AUTO_IDX_MASK 0xffff +#define MWIFIEX_DELETE_MASK 0x0000 +#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 TLV_TYPE_UAP_SSID 0x0000 +#define TLV_TYPE_UAP_RATES 0x0001 +#define TLV_TYPE_PWR_CONSTRAINT 0x0020 + +#define PROPRIETARY_TLV_BASE_ID 0x0100 +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) +#define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32) +#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35) +#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) +#define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44) +#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) +#define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48) +#define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51) +#define TLV_TYPE_UAP_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 57) +#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59) +#define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60) +#define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64) +#define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65) +#define TLV_TYPE_UAP_FRAG_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 70) +#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) +#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) +#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) +#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86) +#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87) +#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 91) +#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93) +#define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) +#define TLV_TYPE_ROBUST_COEX (PROPRIETARY_TLV_BASE_ID + 96) +#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104) +#define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105) +#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113) +#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114) +#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123) +#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) +#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) +#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148) +#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) +#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) +#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183) +#define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184) +#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) +#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197) +#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199) +#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198) +#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 202) +#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 203) +#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 206) + +#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 + +#define SSN_MASK 0xfff0 + +#define BA_RESULT_SUCCESS 0x0 +#define BA_RESULT_TIMEOUT 0x2 + +#define IS_BASTREAM_SETUP(ptr) (ptr->ba_status) + +#define BA_STREAM_NOT_ALLOWED 0xff + +#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \ + priv->adapter->config_bands & BAND_AN) && \ + priv->curr_bss_params.bss_descriptor.bcn_ht_cap) +#define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\ + BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS) + +#define MWIFIEX_TX_DATA_BUF_SIZE_4K 4096 +#define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 + +#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) +#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14)) +#define ISSUPP_DRCS_ENABLED(FwCapInfo) (FwCapInfo & BIT(15)) +#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16)) + +#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ + (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ + IEEE80211_HT_CAP_SM_PS) + +#define MWIFIEX_DEF_11N_TX_BF_CAP 0x09E1E008 + +#define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR + +#define GET_RXSTBC(x) (x & IEEE80211_HT_CAP_RX_STBC) +#define MWIFIEX_RX_STBC1 0x0100 +#define MWIFIEX_RX_STBC12 0x0200 +#define MWIFIEX_RX_STBC123 0x0300 + +/* dev_cap bitmap + * BIT + * 0-16 reserved + * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40 + * 18-22 reserved + * 23 IEEE80211_HT_CAP_SGI_20 + * 24 IEEE80211_HT_CAP_SGI_40 + * 25 IEEE80211_HT_CAP_TX_STBC + * 26 IEEE80211_HT_CAP_RX_STBC + * 27-28 reserved + * 29 IEEE80211_HT_CAP_GRN_FLD + * 30-31 reserved + */ +#define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17)) +#define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23)) +#define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24)) +#define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25)) +#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26)) +#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29)) +#define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8)) +#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) +#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) +#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2)) +#define GETSUPP_TXBASTREAMS(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF) + +/* httxcfg bitmap + * 0 reserved + * 1 20/40 Mhz enable(1)/disable(0) + * 2-3 reserved + * 4 green field enable(1)/disable(0) + * 5 short GI in 20 Mhz enable(1)/disable(0) + * 6 short GI in 40 Mhz enable(1)/disable(0) + * 7-15 reserved + */ +#define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6)) + +/* 11AC Tx and Rx MCS map for 1x1 mode: + * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 + * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams + */ +#define MWIFIEX_11AC_MCS_MAP_1X1 0xfffefffe + +/* 11AC Tx and Rx MCS map for 2x2 mode: + * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2 + * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams + */ +#define MWIFIEX_11AC_MCS_MAP_2X2 0xfffafffa + +#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) +#define SETHT_MCS32(x) (x[4] |= 1) +#define HT_STREAM_1X1 0x11 +#define HT_STREAM_2X2 0x22 + +#define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4)) + +#define LLC_SNAP_LEN 8 + +/* HW_SPEC fw_cap_info */ + +#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & BIT(13)) + +#define GET_VHTCAP_CHWDSET(vht_cap_info) ((vht_cap_info >> 2) & 0x3) +#define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3) +#define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \ + (2 * (nss - 1))) +#define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16) +#define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF) + +/* Clear SU Beanformer, MU beanformer, MU beanformee and + * sounding dimensions bits + */ +#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \ + (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \ + IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \ + IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK) + +#define MOD_CLASS_HR_DSSS 0x03 +#define MOD_CLASS_OFDM 0x07 +#define MOD_CLASS_HT 0x08 +#define HT_BW_20 0 +#define HT_BW_40 1 + +#define DFS_CHAN_MOVE_TIME 10000 + +#define HostCmd_CMD_GET_HW_SPEC 0x0003 +#define HostCmd_CMD_802_11_SCAN 0x0006 +#define HostCmd_CMD_802_11_GET_LOG 0x000b +#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 +#define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059 +#define HostCmd_CMD_802_11_ASSOCIATE 0x0012 +#define HostCmd_CMD_802_11_SNMP_MIB 0x0016 +#define HostCmd_CMD_MAC_REG_ACCESS 0x0019 +#define HostCmd_CMD_BBP_REG_ACCESS 0x001a +#define HostCmd_CMD_RF_REG_ACCESS 0x001b +#define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad +#define HostCmd_CMD_RF_TX_PWR 0x001e +#define HostCmd_CMD_RF_ANTENNA 0x0020 +#define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 +#define HostCmd_CMD_MAC_CONTROL 0x0028 +#define HostCmd_CMD_802_11_AD_HOC_START 0x002b +#define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c +#define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040 +#define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D +#define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b +#define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e +#define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c +#define HostCmd_CMD_WMM_GET_STATUS 0x0071 +#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 +#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f +#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 +#define HostCmd_CMD_MEM_ACCESS 0x0086 +#define HostCmd_CMD_CFG_DATA 0x008f +#define HostCmd_CMD_VERSION_EXT 0x0097 +#define HostCmd_CMD_MEF_CFG 0x009a +#define HostCmd_CMD_RSSI_INFO 0x00a4 +#define HostCmd_CMD_FUNC_INIT 0x00a9 +#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa +#define HOST_CMD_APCMD_SYS_RESET 0x00af +#define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0 +#define HostCmd_CMD_UAP_BSS_START 0x00b1 +#define HostCmd_CMD_UAP_BSS_STOP 0x00b2 +#define HOST_CMD_APCMD_STA_LIST 0x00b3 +#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5 +#define HostCmd_CMD_11N_CFG 0x00cd +#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce +#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf +#define HostCmd_CMD_11N_DELBA 0x00d0 +#define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 +#define HostCmd_CMD_CHAN_REPORT_REQUEST 0x00dd +#define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df +#define HostCmd_CMD_TXPWR_CFG 0x00d1 +#define HostCmd_CMD_TX_RATE_CFG 0x00d6 +#define HostCmd_CMD_ROBUST_COEX 0x00e0 +#define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4 +#define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 +#define HostCmd_CMD_P2P_MODE_CFG 0x00eb +#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed +#define HostCmd_CMD_SET_BSS_MODE 0x00f7 +#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa +#define HostCmd_CMD_802_11_SCAN_EXT 0x0107 +#define HostCmd_CMD_COALESCE_CFG 0x010a +#define HostCmd_CMD_MGMT_FRAME_REG 0x010c +#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d +#define HostCmd_CMD_11AC_CFG 0x0112 +#define HostCmd_CMD_TDLS_CONFIG 0x0100 +#define HostCmd_CMD_MC_POLICY 0x0121 +#define HostCmd_CMD_TDLS_OPER 0x0122 +#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223 + +#define PROTOCOL_NO_SECURITY 0x01 +#define PROTOCOL_STATIC_WEP 0x02 +#define PROTOCOL_WPA 0x08 +#define PROTOCOL_WPA2 0x20 +#define PROTOCOL_WPA2_MIXED 0x28 +#define PROTOCOL_EAP 0x40 +#define KEY_MGMT_NONE 0x04 +#define KEY_MGMT_PSK 0x02 +#define KEY_MGMT_EAP 0x01 +#define CIPHER_TKIP 0x04 +#define CIPHER_AES_CCMP 0x08 +#define VALID_CIPHER_BITMAP 0x0c + +enum ENH_PS_MODES { + EN_PS = 1, + DIS_PS = 2, + EN_AUTO_DS = 3, + DIS_AUTO_DS = 4, + SLEEP_CONFIRM = 5, + GET_PS = 0, + EN_AUTO_PS = 0xff, + DIS_AUTO_PS = 0xfe, +}; + +enum P2P_MODES { + P2P_MODE_DISABLE = 0, + P2P_MODE_DEVICE = 1, + P2P_MODE_GO = 2, + P2P_MODE_CLIENT = 3, +}; + +#define HostCmd_RET_BIT 0x8000 +#define HostCmd_ACT_GEN_GET 0x0000 +#define HostCmd_ACT_GEN_SET 0x0001 +#define HostCmd_ACT_GEN_REMOVE 0x0004 +#define HostCmd_ACT_BITWISE_SET 0x0002 +#define HostCmd_ACT_BITWISE_CLR 0x0003 +#define HostCmd_RESULT_OK 0x0000 + +#define HostCmd_ACT_MAC_RX_ON 0x0001 +#define HostCmd_ACT_MAC_TX_ON 0x0002 +#define HostCmd_ACT_MAC_WEP_ENABLE 0x0008 +#define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010 +#define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 +#define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 +#define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000 + +#define HostCmd_BSS_MODE_IBSS 0x0002 +#define HostCmd_BSS_MODE_ANY 0x0003 + +#define HostCmd_SCAN_RADIO_TYPE_BG 0 +#define HostCmd_SCAN_RADIO_TYPE_A 1 + +#define HS_CFG_CANCEL 0xffffffff +#define HS_CFG_COND_DEF 0x00000000 +#define HS_CFG_GPIO_DEF 0xff +#define HS_CFG_GAP_DEF 0xff +#define HS_CFG_COND_BROADCAST_DATA 0x00000001 +#define HS_CFG_COND_UNICAST_DATA 0x00000002 +#define HS_CFG_COND_MAC_EVENT 0x00000004 +#define HS_CFG_COND_MULTICAST_DATA 0x00000008 + +#define CONNECT_ERR_AUTH_ERR_STA_FAILURE 0xFFFB +#define CONNECT_ERR_ASSOC_ERR_TIMEOUT 0xFFFC +#define CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED 0xFFFD +#define CONNECT_ERR_AUTH_MSG_UNHANDLED 0xFFFE +#define CONNECT_ERR_STA_FAILURE 0xFFFF + + +#define CMD_F_HOSTCMD (1 << 0) + +#define HostCmd_CMD_ID_MASK 0x0fff + +#define HostCmd_SEQ_NUM_MASK 0x00ff + +#define HostCmd_BSS_NUM_MASK 0x0f00 + +#define HostCmd_BSS_TYPE_MASK 0xf000 + +#define HostCmd_ACT_SET_RX 0x0001 +#define HostCmd_ACT_SET_TX 0x0002 +#define HostCmd_ACT_SET_BOTH 0x0003 + +#define RF_ANTENNA_AUTO 0xFFFF + +#define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) { \ + (((seq) & 0x00ff) | \ + (((num) & 0x000f) << 8)) | \ + (((type) & 0x000f) << 12); } + +#define HostCmd_GET_SEQ_NO(seq) \ + ((seq) & HostCmd_SEQ_NUM_MASK) + +#define HostCmd_GET_BSS_NO(seq) \ + (((seq) & HostCmd_BSS_NUM_MASK) >> 8) + +#define HostCmd_GET_BSS_TYPE(seq) \ + (((seq) & HostCmd_BSS_TYPE_MASK) >> 12) + +#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 +#define EVENT_LINK_LOST 0x00000003 +#define EVENT_LINK_SENSED 0x00000004 +#define EVENT_MIB_CHANGED 0x00000006 +#define EVENT_INIT_DONE 0x00000007 +#define EVENT_DEAUTHENTICATED 0x00000008 +#define EVENT_DISASSOCIATED 0x00000009 +#define EVENT_PS_AWAKE 0x0000000a +#define EVENT_PS_SLEEP 0x0000000b +#define EVENT_MIC_ERR_MULTICAST 0x0000000d +#define EVENT_MIC_ERR_UNICAST 0x0000000e +#define EVENT_DEEP_SLEEP_AWAKE 0x00000010 +#define EVENT_ADHOC_BCN_LOST 0x00000011 + +#define EVENT_WMM_STATUS_CHANGE 0x00000017 +#define EVENT_BG_SCAN_REPORT 0x00000018 +#define EVENT_RSSI_LOW 0x00000019 +#define EVENT_SNR_LOW 0x0000001a +#define EVENT_MAX_FAIL 0x0000001b +#define EVENT_RSSI_HIGH 0x0000001c +#define EVENT_SNR_HIGH 0x0000001d +#define EVENT_IBSS_COALESCED 0x0000001e +#define EVENT_DATA_RSSI_LOW 0x00000024 +#define EVENT_DATA_SNR_LOW 0x00000025 +#define EVENT_DATA_RSSI_HIGH 0x00000026 +#define EVENT_DATA_SNR_HIGH 0x00000027 +#define EVENT_LINK_QUALITY 0x00000028 +#define EVENT_PORT_RELEASE 0x0000002b +#define EVENT_UAP_STA_DEAUTH 0x0000002c +#define EVENT_UAP_STA_ASSOC 0x0000002d +#define EVENT_UAP_BSS_START 0x0000002e +#define EVENT_PRE_BEACON_LOST 0x00000031 +#define EVENT_ADDBA 0x00000033 +#define EVENT_DELBA 0x00000034 +#define EVENT_BA_STREAM_TIEMOUT 0x00000037 +#define EVENT_AMSDU_AGGR_CTRL 0x00000042 +#define EVENT_UAP_BSS_IDLE 0x00000043 +#define EVENT_UAP_BSS_ACTIVE 0x00000044 +#define EVENT_WEP_ICV_ERR 0x00000046 +#define EVENT_HS_ACT_REQ 0x00000047 +#define EVENT_BW_CHANGE 0x00000048 +#define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c +#define EVENT_HOSTWAKE_STAIE 0x0000004d +#define EVENT_CHANNEL_SWITCH_ANN 0x00000050 +#define EVENT_TDLS_GENERIC_EVENT 0x00000052 +#define EVENT_RADAR_DETECTED 0x00000053 +#define EVENT_CHANNEL_REPORT_RDY 0x00000054 +#define EVENT_TX_DATA_PAUSE 0x00000055 +#define EVENT_EXT_SCAN_REPORT 0x00000058 +#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f +#define EVENT_MULTI_CHAN_INFO 0x0000006a +#define EVENT_TX_STATUS_REPORT 0x00000074 +#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0X00000076 + +#define EVENT_ID_MASK 0xffff +#define BSS_NUM_MASK 0xf + +#define EVENT_GET_BSS_NUM(event_cause) \ + (((event_cause) >> 16) & BSS_NUM_MASK) + +#define EVENT_GET_BSS_TYPE(event_cause) \ + (((event_cause) >> 24) & 0x00ff) + +#define MWIFIEX_MAX_PATTERN_LEN 20 +#define MWIFIEX_MAX_OFFSET_LEN 100 +#define STACK_NBYTES 100 +#define TYPE_DNUM 1 +#define TYPE_BYTESEQ 2 +#define MAX_OPERAND 0x40 +#define TYPE_EQ (MAX_OPERAND+1) +#define TYPE_EQ_DNUM (MAX_OPERAND+2) +#define TYPE_EQ_BIT (MAX_OPERAND+3) +#define TYPE_AND (MAX_OPERAND+4) +#define TYPE_OR (MAX_OPERAND+5) +#define MEF_MODE_HOST_SLEEP 1 +#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3 +#define MEF_ACTION_AUTO_ARP 0x10 +#define MWIFIEX_CRITERIA_BROADCAST BIT(0) +#define MWIFIEX_CRITERIA_UNICAST BIT(1) +#define MWIFIEX_CRITERIA_MULTICAST BIT(3) +#define MWIFIEX_MAX_SUPPORTED_IPADDR 4 + +#define ACT_TDLS_DELETE 0x00 +#define ACT_TDLS_CREATE 0x01 +#define ACT_TDLS_CONFIG 0x02 + +#define TDLS_EVENT_LINK_TEAR_DOWN 3 +#define TDLS_EVENT_CHAN_SWITCH_RESULT 7 +#define TDLS_EVENT_START_CHAN_SWITCH 8 +#define TDLS_EVENT_CHAN_SWITCH_STOPPED 9 + +#define TDLS_BASE_CHANNEL 0 +#define TDLS_OFF_CHANNEL 1 + +#define ACT_TDLS_CS_ENABLE_CONFIG 0x00 +#define ACT_TDLS_CS_INIT 0x06 +#define ACT_TDLS_CS_STOP 0x07 +#define ACT_TDLS_CS_PARAMS 0x08 + +#define MWIFIEX_DEF_CS_UNIT_TIME 2 +#define MWIFIEX_DEF_CS_THR_OTHERLINK 10 +#define MWIFIEX_DEF_THR_DIRECTLINK 0 +#define MWIFIEX_DEF_CS_TIME 10 +#define MWIFIEX_DEF_CS_TIMEOUT 16 +#define MWIFIEX_DEF_CS_REG_CLASS 12 +#define MWIFIEX_DEF_CS_PERIODICITY 1 + +#define MWIFIEX_FW_V15 15 + +#define MWIFIEX_MASTER_RADAR_DET_MASK BIT(1) + +struct mwifiex_ie_types_header { + __le16 type; + __le16 len; +} __packed; + +struct mwifiex_ie_types_data { + struct mwifiex_ie_types_header header; + u8 data[1]; +} __packed; + +#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 +#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 +#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10 +#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01 +#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS 0x20 + +struct txpd { + u8 bss_type; + u8 bss_num; + __le16 tx_pkt_length; + __le16 tx_pkt_offset; + __le16 tx_pkt_type; + __le32 tx_control; + u8 priority; + u8 flags; + u8 pkt_delay_2ms; + u8 reserved1[2]; + u8 tx_token_id; + u8 reserved[2]; +} __packed; + +struct rxpd { + u8 bss_type; + u8 bss_num; + __le16 rx_pkt_length; + __le16 rx_pkt_offset; + __le16 rx_pkt_type; + __le16 seq_num; + u8 priority; + u8 rx_rate; + s8 snr; + s8 nf; + + /* For: Non-802.11 AC cards + * + * Ht Info [Bit 0] RxRate format: LG=0, HT=1 + * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 + * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 + * + * For: 802.11 AC cards + * [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10 + * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01 + * BW80 = 10 BW160 = 11 + * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1 + * [Bit 5] STBC support Enabled = 1 + * [Bit 6] LDPC support Enabled = 1 + * [Bit 7] Reserved + */ + u8 ht_info; + u8 reserved[3]; + u8 flags; +} __packed; + +struct uap_txpd { + u8 bss_type; + u8 bss_num; + __le16 tx_pkt_length; + __le16 tx_pkt_offset; + __le16 tx_pkt_type; + __le32 tx_control; + u8 priority; + u8 flags; + u8 pkt_delay_2ms; + u8 reserved1[2]; + u8 tx_token_id; + u8 reserved[2]; +}; + +struct uap_rxpd { + u8 bss_type; + u8 bss_num; + __le16 rx_pkt_length; + __le16 rx_pkt_offset; + __le16 rx_pkt_type; + __le16 seq_num; + u8 priority; + u8 rx_rate; + s8 snr; + s8 nf; + u8 ht_info; + u8 reserved[3]; + u8 flags; +}; + +struct mwifiex_fw_chan_stats { + u8 chan_num; + u8 bandcfg; + u8 flags; + s8 noise; + __le16 total_bss; + __le16 cca_scan_dur; + __le16 cca_busy_dur; +} __packed; + +enum mwifiex_chan_scan_mode_bitmasks { + MWIFIEX_PASSIVE_SCAN = BIT(0), + MWIFIEX_DISABLE_CHAN_FILT = BIT(1), + MWIFIEX_HIDDEN_SSID_REPORT = BIT(4), +}; + +struct mwifiex_chan_scan_param_set { + u8 radio_type; + u8 chan_number; + u8 chan_scan_mode_bitmap; + __le16 min_scan_time; + __le16 max_scan_time; +} __packed; + +struct mwifiex_ie_types_chan_list_param_set { + struct mwifiex_ie_types_header header; + struct mwifiex_chan_scan_param_set chan_scan_param[1]; +} __packed; + +struct chan_band_param_set { + u8 radio_type; + u8 chan_number; +}; + +struct mwifiex_ie_types_chan_band_list_param_set { + struct mwifiex_ie_types_header header; + struct chan_band_param_set chan_band_param[1]; +} __packed; + +struct mwifiex_ie_types_rates_param_set { + struct mwifiex_ie_types_header header; + u8 rates[1]; +} __packed; + +struct mwifiex_ie_types_ssid_param_set { + struct mwifiex_ie_types_header header; + u8 ssid[1]; +} __packed; + +struct mwifiex_ie_types_num_probes { + struct mwifiex_ie_types_header header; + __le16 num_probes; +} __packed; + +struct mwifiex_ie_types_scan_chan_gap { + struct mwifiex_ie_types_header header; + /* time gap in TUs to be used between two consecutive channels scan */ + __le16 chan_gap; +} __packed; + +struct mwifiex_ietypes_chanstats { + struct mwifiex_ie_types_header header; + struct mwifiex_fw_chan_stats chanstats[0]; +} __packed; + +struct mwifiex_ie_types_wildcard_ssid_params { + struct mwifiex_ie_types_header header; + u8 max_ssid_length; + u8 ssid[1]; +} __packed; + +#define TSF_DATA_SIZE 8 +struct mwifiex_ie_types_tsf_timestamp { + struct mwifiex_ie_types_header header; + u8 tsf_data[1]; +} __packed; + +struct mwifiex_cf_param_set { + u8 cfp_cnt; + u8 cfp_period; + __le16 cfp_max_duration; + __le16 cfp_duration_remaining; +} __packed; + +struct mwifiex_ibss_param_set { + __le16 atim_window; +} __packed; + +struct mwifiex_ie_types_ss_param_set { + struct mwifiex_ie_types_header header; + union { + struct mwifiex_cf_param_set cf_param_set[1]; + struct mwifiex_ibss_param_set ibss_param_set[1]; + } cf_ibss; +} __packed; + +struct mwifiex_fh_param_set { + __le16 dwell_time; + u8 hop_set; + u8 hop_pattern; + u8 hop_index; +} __packed; + +struct mwifiex_ds_param_set { + u8 current_chan; +} __packed; + +struct mwifiex_ie_types_phy_param_set { + struct mwifiex_ie_types_header header; + union { + struct mwifiex_fh_param_set fh_param_set[1]; + struct mwifiex_ds_param_set ds_param_set[1]; + } fh_ds; +} __packed; + +struct mwifiex_ie_types_auth_type { + struct mwifiex_ie_types_header header; + __le16 auth_type; +} __packed; + +struct mwifiex_ie_types_vendor_param_set { + struct mwifiex_ie_types_header header; + u8 ie[MWIFIEX_MAX_VSIE_LEN]; +}; + +#define MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC 60 + +struct mwifiex_ie_types_tdls_idle_timeout { + struct mwifiex_ie_types_header header; + __le16 value; +} __packed; + +struct mwifiex_ie_types_rsn_param_set { + struct mwifiex_ie_types_header header; + u8 rsn_ie[1]; +} __packed; + +#define KEYPARAMSET_FIXED_LEN 6 + +struct mwifiex_ie_type_key_param_set { + __le16 type; + __le16 length; + __le16 key_type_id; + __le16 key_info; + __le16 key_len; + u8 key[50]; +} __packed; + +#define IGTK_PN_LEN 8 + +struct mwifiex_cmac_param { + u8 ipn[IGTK_PN_LEN]; + u8 key[WLAN_KEY_LEN_AES_CMAC]; +} __packed; + +struct mwifiex_wep_param { + __le16 key_len; + u8 key[WLAN_KEY_LEN_WEP104]; +} __packed; + +struct mwifiex_tkip_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_TKIP]; +} __packed; + +struct mwifiex_aes_param { + u8 pn[WPA_PN_SIZE]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_CCMP]; +} __packed; + +struct mwifiex_wapi_param { + u8 pn[PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_SMS4]; +} __packed; + +struct mwifiex_cmac_aes_param { + u8 ipn[IGTK_PN_LEN]; + __le16 key_len; + u8 key[WLAN_KEY_LEN_AES_CMAC]; +} __packed; + +struct mwifiex_ie_type_key_param_set_v2 { + __le16 type; + __le16 len; + u8 mac_addr[ETH_ALEN]; + u8 key_idx; + u8 key_type; + __le16 key_info; + union { + struct mwifiex_wep_param wep; + struct mwifiex_tkip_param tkip; + struct mwifiex_aes_param aes; + struct mwifiex_wapi_param wapi; + struct mwifiex_cmac_aes_param cmac_aes; + } key_params; +} __packed; + +struct host_cmd_ds_802_11_key_material_v2 { + __le16 action; + struct mwifiex_ie_type_key_param_set_v2 key_param_set; +} __packed; + +struct host_cmd_ds_802_11_key_material { + __le16 action; + struct mwifiex_ie_type_key_param_set key_param_set; +} __packed; + +struct host_cmd_ds_gen { + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; +}; + +#define S_DS_GEN sizeof(struct host_cmd_ds_gen) + +enum sleep_resp_ctrl { + RESP_NOT_NEEDED = 0, + RESP_NEEDED, +}; + +struct mwifiex_ps_param { + __le16 null_pkt_interval; + __le16 multiple_dtims; + __le16 bcn_miss_timeout; + __le16 local_listen_interval; + __le16 adhoc_wake_period; + __le16 mode; + __le16 delay_to_ps; +}; + +#define BITMAP_AUTO_DS 0x01 +#define BITMAP_STA_PS 0x10 + +struct mwifiex_ie_types_auto_ds_param { + struct mwifiex_ie_types_header header; + __le16 deep_sleep_timeout; +} __packed; + +struct mwifiex_ie_types_ps_param { + struct mwifiex_ie_types_header header; + struct mwifiex_ps_param param; +} __packed; + +struct host_cmd_ds_802_11_ps_mode_enh { + __le16 action; + + union { + struct mwifiex_ps_param opt_ps; + __le16 ps_bitmap; + } params; +} __packed; + +enum API_VER_ID { + KEY_API_VER_ID = 1, + FW_API_VER_ID = 2, +}; + +struct hw_spec_api_rev { + struct mwifiex_ie_types_header header; + __le16 api_id; + u8 major_ver; + u8 minor_ver; +} __packed; + +struct host_cmd_ds_get_hw_spec { + __le16 hw_if_version; + __le16 version; + __le16 reserved; + __le16 num_of_mcast_adr; + u8 permanent_addr[ETH_ALEN]; + __le16 region_code; + __le16 number_of_antenna; + __le32 fw_release_number; + __le32 reserved_1; + __le32 reserved_2; + __le32 reserved_3; + __le32 fw_cap_info; + __le32 dot_11n_dev_cap; + u8 dev_mcs_support; + __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ + __le16 mgmt_buf_count; /* mgmt IE buffer count */ + __le32 reserved_5; + __le32 reserved_6; + __le32 dot_11ac_dev_cap; + __le32 dot_11ac_mcs_support; + u8 tlvs[0]; +} __packed; + +struct host_cmd_ds_802_11_rssi_info { + __le16 action; + __le16 ndata; + __le16 nbcn; + __le16 reserved[9]; + long long reserved_1; +}; + +struct host_cmd_ds_802_11_rssi_info_rsp { + __le16 action; + __le16 ndata; + __le16 nbcn; + __le16 data_rssi_last; + __le16 data_nf_last; + __le16 data_rssi_avg; + __le16 data_nf_avg; + __le16 bcn_rssi_last; + __le16 bcn_nf_last; + __le16 bcn_rssi_avg; + __le16 bcn_nf_avg; + long long tsf_bcn; +}; + +struct host_cmd_ds_802_11_mac_address { + __le16 action; + u8 mac_addr[ETH_ALEN]; +}; + +struct host_cmd_ds_mac_control { + __le16 action; + __le16 reserved; +}; + +struct host_cmd_ds_mac_multicast_adr { + __le16 action; + __le16 num_of_adrs; + u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; +} __packed; + +struct host_cmd_ds_802_11_deauthenticate { + u8 mac_addr[ETH_ALEN]; + __le16 reason_code; +} __packed; + +struct host_cmd_ds_802_11_associate { + u8 peer_sta_addr[ETH_ALEN]; + __le16 cap_info_bitmap; + __le16 listen_interval; + __le16 beacon_period; + u8 dtim_period; +} __packed; + +struct ieee_types_assoc_rsp { + __le16 cap_info_bitmap; + __le16 status_code; + __le16 a_id; + u8 ie_buffer[1]; +} __packed; + +struct host_cmd_ds_802_11_associate_rsp { + struct ieee_types_assoc_rsp assoc_rsp; +} __packed; + +struct ieee_types_cf_param_set { + u8 element_id; + u8 len; + u8 cfp_cnt; + u8 cfp_period; + __le16 cfp_max_duration; + __le16 cfp_duration_remaining; +} __packed; + +struct ieee_types_ibss_param_set { + u8 element_id; + u8 len; + __le16 atim_window; +} __packed; + +union ieee_types_ss_param_set { + struct ieee_types_cf_param_set cf_param_set; + struct ieee_types_ibss_param_set ibss_param_set; +} __packed; + +struct ieee_types_fh_param_set { + u8 element_id; + u8 len; + __le16 dwell_time; + u8 hop_set; + u8 hop_pattern; + u8 hop_index; +} __packed; + +struct ieee_types_ds_param_set { + u8 element_id; + u8 len; + u8 current_chan; +} __packed; + +union ieee_types_phy_param_set { + struct ieee_types_fh_param_set fh_param_set; + struct ieee_types_ds_param_set ds_param_set; +} __packed; + +struct ieee_types_oper_mode_ntf { + u8 element_id; + u8 len; + u8 oper_mode; +} __packed; + +struct host_cmd_ds_802_11_ad_hoc_start { + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 bss_mode; + __le16 beacon_period; + u8 dtim_period; + union ieee_types_ss_param_set ss_param_set; + union ieee_types_phy_param_set phy_param_set; + u16 reserved1; + __le16 cap_info_bitmap; + u8 data_rate[HOSTCMD_SUPPORTED_RATES]; +} __packed; + +struct host_cmd_ds_802_11_ad_hoc_result { + u8 pad[3]; + u8 bssid[ETH_ALEN]; +} __packed; + +struct adhoc_bss_desc { + u8 bssid[ETH_ALEN]; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 bss_mode; + __le16 beacon_period; + u8 dtim_period; + u8 time_stamp[8]; + u8 local_time[8]; + union ieee_types_phy_param_set phy_param_set; + union ieee_types_ss_param_set ss_param_set; + __le16 cap_info_bitmap; + 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 + */ +} __packed; + +struct host_cmd_ds_802_11_ad_hoc_join { + struct adhoc_bss_desc bss_descriptor; + u16 reserved1; + u16 reserved2; +} __packed; + +struct host_cmd_ds_802_11_get_log { + __le32 mcast_tx_frame; + __le32 failed; + __le32 retry; + __le32 multi_retry; + __le32 frame_dup; + __le32 rts_success; + __le32 rts_failure; + __le32 ack_failure; + __le32 rx_frag; + __le32 mcast_rx_frame; + __le32 fcs_error; + __le32 tx_frame; + __le32 reserved; + __le32 wep_icv_err_cnt[4]; + __le32 bcn_rcv_cnt; + __le32 bcn_miss_cnt; +}; + +/* Enumeration for rate format */ +enum _mwifiex_rate_format { + MWIFIEX_RATE_FORMAT_LG = 0, + MWIFIEX_RATE_FORMAT_HT, + MWIFIEX_RATE_FORMAT_VHT, + MWIFIEX_RATE_FORMAT_AUTO = 0xFF, +}; + +struct host_cmd_ds_tx_rate_query { + u8 tx_rate; + /* Tx Rate Info: For 802.11 AC cards + * + * [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 + * + * For non-802.11 AC cards + * Ht Info [Bit 0] RxRate format: LG=0, HT=1 + * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 + * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 + */ + u8 ht_info; +} __packed; + +struct mwifiex_tx_pause_tlv { + struct mwifiex_ie_types_header header; + u8 peermac[ETH_ALEN]; + u8 tx_pause; + u8 pkt_cnt; +} __packed; + +enum Host_Sleep_Action { + HS_CONFIGURE = 0x0001, + HS_ACTIVATE = 0x0002, +}; + +struct mwifiex_hs_config_param { + __le32 conditions; + u8 gpio; + u8 gap; +} __packed; + +struct hs_activate_param { + __le16 resp_ctrl; +} __packed; + +struct host_cmd_ds_802_11_hs_cfg_enh { + __le16 action; + + union { + struct mwifiex_hs_config_param hs_config; + struct hs_activate_param hs_activate; + } params; +} __packed; + +enum SNMP_MIB_INDEX { + OP_RATE_SET_I = 1, + DTIM_PERIOD_I = 3, + RTS_THRESH_I = 5, + SHORT_RETRY_LIM_I = 6, + LONG_RETRY_LIM_I = 7, + FRAG_THRESH_I = 8, + DOT11D_I = 9, + DOT11H_I = 10, +}; + +enum mwifiex_assocmd_failurepoint { + MWIFIEX_ASSOC_CMD_SUCCESS = 0, + MWIFIEX_ASSOC_CMD_FAILURE_ASSOC, + MWIFIEX_ASSOC_CMD_FAILURE_AUTH, + MWIFIEX_ASSOC_CMD_FAILURE_JOIN +}; + +#define MAX_SNMP_BUF_SIZE 128 + +struct host_cmd_ds_802_11_snmp_mib { + __le16 query_type; + __le16 oid; + __le16 buf_size; + u8 value[1]; +} __packed; + +struct mwifiex_rate_scope { + __le16 type; + __le16 length; + __le16 hr_dsss_rate_bitmap; + __le16 ofdm_rate_bitmap; + __le16 ht_mcs_rate_bitmap[8]; + __le16 vht_mcs_rate_bitmap[8]; +} __packed; + +struct mwifiex_rate_drop_pattern { + __le16 type; + __le16 length; + __le32 rate_drop_mode; +} __packed; + +struct host_cmd_ds_tx_rate_cfg { + __le16 action; + __le16 cfg_index; +} __packed; + +struct mwifiex_power_group { + u8 modulation_class; + u8 first_rate_code; + u8 last_rate_code; + s8 power_step; + s8 power_min; + s8 power_max; + u8 ht_bandwidth; + u8 reserved; +} __packed; + +struct mwifiex_types_power_group { + __le16 type; + __le16 length; +} __packed; + +struct host_cmd_ds_txpwr_cfg { + __le16 action; + __le16 cfg_index; + __le32 mode; +} __packed; + +struct host_cmd_ds_rf_tx_pwr { + __le16 action; + __le16 cur_level; + u8 max_power; + u8 min_power; +} __packed; + +struct host_cmd_ds_rf_ant_mimo { + __le16 action_tx; + __le16 tx_ant_mode; + __le16 action_rx; + __le16 rx_ant_mode; +}; + +struct host_cmd_ds_rf_ant_siso { + __le16 action; + __le16 ant_mode; +}; + +struct host_cmd_ds_tdls_oper { + __le16 tdls_action; + __le16 reason; + u8 peer_mac[ETH_ALEN]; +} __packed; + +struct mwifiex_tdls_config { + __le16 enable; +}; + +struct mwifiex_tdls_config_cs_params { + u8 unit_time; + u8 thr_otherlink; + u8 thr_directlink; +}; + +struct mwifiex_tdls_init_cs_params { + u8 peer_mac[ETH_ALEN]; + u8 primary_chan; + u8 second_chan_offset; + u8 band; + __le16 switch_time; + __le16 switch_timeout; + u8 reg_class; + u8 periodicity; +} __packed; + +struct mwifiex_tdls_stop_cs_params { + u8 peer_mac[ETH_ALEN]; +}; + +struct host_cmd_ds_tdls_config { + __le16 tdls_action; + u8 tdls_data[1]; +} __packed; + +struct mwifiex_chan_desc { + __le16 start_freq; + u8 chan_width; + u8 chan_num; +} __packed; + +struct host_cmd_ds_chan_rpt_req { + struct mwifiex_chan_desc chan_desc; + __le32 msec_dwell_time; +} __packed; + +struct host_cmd_ds_chan_rpt_event { + __le32 result; + __le64 start_tsf; + __le32 duration; + u8 tlvbuf[0]; +} __packed; + +struct host_cmd_sdio_sp_rx_aggr_cfg { + u8 action; + u8 enable; + __le16 block_size; +} __packed; + +struct mwifiex_fixed_bcn_param { + __le64 timestamp; + __le16 beacon_period; + __le16 cap_info_bitmap; +} __packed; + +struct mwifiex_event_scan_result { + __le16 event_id; + u8 bss_index; + u8 bss_type; + u8 more_event; + u8 reserved[3]; + __le16 buf_size; + u8 num_of_set; +} __packed; + +struct tx_status_event { + u8 packet_type; + u8 tx_token_id; + u8 status; +} __packed; + +#define MWIFIEX_USER_SCAN_CHAN_MAX 50 + +#define MWIFIEX_MAX_SSID_LIST_LENGTH 10 + +struct mwifiex_scan_cmd_config { + /* + * BSS mode to be sent in the firmware command + */ + u8 bss_mode; + + /* Specific BSSID used to filter scan results in the firmware */ + u8 specific_bssid[ETH_ALEN]; + + /* Length of TLVs sent in command starting at tlvBuffer */ + u32 tlv_buf_len; + + /* + * SSID TLV(s) and ChanList TLVs to be sent in the firmware command + * + * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set + * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set + */ + u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored + here */ +} __packed; + +struct mwifiex_user_scan_chan { + u8 chan_number; + u8 radio_type; + u8 scan_type; + u8 reserved; + u32 scan_time; +} __packed; + +struct mwifiex_user_scan_cfg { + /* + * BSS mode to be sent in the firmware command + */ + u8 bss_mode; + /* Configure the number of probe requests for active chan scans */ + u8 num_probes; + u8 reserved; + /* BSSID filter sent in the firmware command to limit the results */ + u8 specific_bssid[ETH_ALEN]; + /* SSID filter list used in the firmware to limit the scan results */ + struct cfg80211_ssid *ssid_list; + u8 num_ssids; + /* Variable number (fixed maximum) of channels to scan up */ + struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX]; + u16 scan_chan_gap; +} __packed; + +struct ie_body { + u8 grp_key_oui[4]; + u8 ptk_cnt[2]; + u8 ptk_body[4]; +} __packed; + +struct host_cmd_ds_802_11_scan { + u8 bss_mode; + u8 bssid[ETH_ALEN]; + u8 tlv_buffer[1]; +} __packed; + +struct host_cmd_ds_802_11_scan_rsp { + __le16 bss_descript_size; + u8 number_of_sets; + u8 bss_desc_and_tlv_buffer[1]; +} __packed; + +struct host_cmd_ds_802_11_scan_ext { + u32 reserved; + u8 tlv_buffer[1]; +} __packed; + +struct mwifiex_ie_types_bss_mode { + struct mwifiex_ie_types_header header; + u8 bss_mode; +} __packed; + +struct mwifiex_ie_types_bss_scan_rsp { + struct mwifiex_ie_types_header header; + u8 bssid[ETH_ALEN]; + u8 frame_body[1]; +} __packed; + +struct mwifiex_ie_types_bss_scan_info { + struct mwifiex_ie_types_header header; + __le16 rssi; + __le16 anpi; + u8 cca_busy_fraction; + u8 radio_type; + u8 channel; + u8 reserved; + __le64 tsf; +} __packed; + +struct host_cmd_ds_802_11_bg_scan_query { + u8 flush; +} __packed; + +struct host_cmd_ds_802_11_bg_scan_query_rsp { + __le32 report_condition; + struct host_cmd_ds_802_11_scan_rsp scan_resp; +} __packed; + +struct mwifiex_ietypes_domain_param_set { + struct mwifiex_ie_types_header header; + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + struct ieee80211_country_ie_triplet triplet[1]; +} __packed; + +struct host_cmd_ds_802_11d_domain_info { + __le16 action; + struct mwifiex_ietypes_domain_param_set domain; +} __packed; + +struct host_cmd_ds_802_11d_domain_info_rsp { + __le16 action; + struct mwifiex_ietypes_domain_param_set domain; +} __packed; + +struct host_cmd_ds_11n_addba_req { + u8 add_req_result; + u8 peer_mac_addr[ETH_ALEN]; + u8 dialog_token; + __le16 block_ack_param_set; + __le16 block_ack_tmo; + __le16 ssn; +} __packed; + +struct host_cmd_ds_11n_addba_rsp { + u8 add_rsp_result; + u8 peer_mac_addr[ETH_ALEN]; + u8 dialog_token; + __le16 status_code; + __le16 block_ack_param_set; + __le16 block_ack_tmo; + __le16 ssn; +} __packed; + +struct host_cmd_ds_11n_delba { + u8 del_result; + u8 peer_mac_addr[ETH_ALEN]; + __le16 del_ba_param_set; + __le16 reason_code; + u8 reserved; +} __packed; + +struct host_cmd_ds_11n_batimeout { + u8 tid; + u8 peer_mac_addr[ETH_ALEN]; + u8 origninator; +} __packed; + +struct host_cmd_ds_11n_cfg { + __le16 action; + __le16 ht_tx_cap; + __le16 ht_tx_info; + __le16 misc_config; /* Needed for 802.11AC cards only */ +} __packed; + +struct host_cmd_ds_txbuf_cfg { + __le16 action; + __le16 buff_size; + __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ + __le16 reserved3; +} __packed; + +struct host_cmd_ds_amsdu_aggr_ctrl { + __le16 action; + __le16 enable; + __le16 curr_buf_size; +} __packed; + +struct host_cmd_ds_sta_deauth { + u8 mac[ETH_ALEN]; + __le16 reason; +} __packed; + +struct mwifiex_ie_types_sta_info { + struct mwifiex_ie_types_header header; + u8 mac[ETH_ALEN]; + u8 power_mfg_status; + s8 rssi; +}; + +struct host_cmd_ds_sta_list { + u16 sta_count; + u8 tlv[0]; +} __packed; + +struct mwifiex_ie_types_pwr_capability { + struct mwifiex_ie_types_header header; + s8 min_pwr; + s8 max_pwr; +}; + +struct mwifiex_ie_types_local_pwr_constraint { + struct mwifiex_ie_types_header header; + u8 chan; + u8 constraint; +}; + +struct mwifiex_ie_types_wmm_param_set { + struct mwifiex_ie_types_header header; + u8 wmm_ie[1]; +}; + +struct mwifiex_ie_types_wmm_queue_status { + struct mwifiex_ie_types_header header; + u8 queue_index; + u8 disabled; + __le16 medium_time; + u8 flow_required; + u8 flow_created; + u32 reserved; +}; + +struct ieee_types_vendor_header { + u8 element_id; + u8 len; + u8 oui[4]; /* 0~2: oui, 3: oui_type */ + u8 oui_subtype; + u8 version; +} __packed; + +struct ieee_types_wmm_parameter { + /* + * WMM Parameter IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [24] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [1] + * Version [1] + */ + struct ieee_types_vendor_header vend_hdr; + u8 qos_info_bitmap; + u8 reserved; + struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; +} __packed; + +struct ieee_types_wmm_info { + + /* + * WMM Info IE - Vendor Specific Header: + * element_id [221/0xdd] + * Len [7] + * Oui [00:50:f2] + * OuiType [2] + * OuiSubType [0] + * Version [1] + */ + struct ieee_types_vendor_header vend_hdr; + + u8 qos_info_bitmap; +} __packed; + +struct host_cmd_ds_wmm_get_status { + u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) * + IEEE80211_NUM_ACS]; + u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2]; +} __packed; + +struct mwifiex_wmm_ac_status { + u8 disabled; + u8 flow_required; + u8 flow_created; +}; + +struct mwifiex_ie_types_htcap { + struct mwifiex_ie_types_header header; + struct ieee80211_ht_cap ht_cap; +} __packed; + +struct mwifiex_ie_types_vhtcap { + struct mwifiex_ie_types_header header; + struct ieee80211_vht_cap vht_cap; +} __packed; + +struct mwifiex_ie_types_aid { + struct mwifiex_ie_types_header header; + __le16 aid; +} __packed; + +struct mwifiex_ie_types_oper_mode_ntf { + struct mwifiex_ie_types_header header; + u8 oper_mode; +} __packed; + +/* VHT Operations IE */ +struct mwifiex_ie_types_vht_oper { + struct mwifiex_ie_types_header header; + u8 chan_width; + u8 chan_center_freq_1; + u8 chan_center_freq_2; + /* Basic MCS set map, each 2 bits stands for a NSS */ + __le16 basic_mcs_map; +} __packed; + +struct mwifiex_ie_types_wmmcap { + struct mwifiex_ie_types_header header; + struct mwifiex_types_wmm_info wmm_info; +} __packed; + +struct mwifiex_ie_types_htinfo { + struct mwifiex_ie_types_header header; + struct ieee80211_ht_operation ht_oper; +} __packed; + +struct mwifiex_ie_types_2040bssco { + struct mwifiex_ie_types_header header; + u8 bss_co_2040; +} __packed; + +struct mwifiex_ie_types_extcap { + struct mwifiex_ie_types_header header; + u8 ext_capab[0]; +} __packed; + +struct host_cmd_ds_mem_access { + __le16 action; + __le16 reserved; + __le32 addr; + __le32 value; +}; + +struct mwifiex_ie_types_qos_info { + struct mwifiex_ie_types_header header; + u8 qos_info; +} __packed; + +struct host_cmd_ds_mac_reg_access { + __le16 action; + __le16 offset; + __le32 value; +} __packed; + +struct host_cmd_ds_bbp_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __packed; + +struct host_cmd_ds_rf_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __packed; + +struct host_cmd_ds_pmic_reg_access { + __le16 action; + __le16 offset; + u8 value; + u8 reserved[3]; +} __packed; + +struct host_cmd_ds_802_11_eeprom_access { + __le16 action; + + __le16 offset; + __le16 byte_count; + u8 value; +} __packed; + +struct mwifiex_assoc_event { + u8 sta_addr[ETH_ALEN]; + __le16 type; + __le16 len; + __le16 frame_control; + __le16 cap_info; + __le16 listen_interval; + u8 data[0]; +} __packed; + +struct host_cmd_ds_sys_config { + __le16 action; + u8 tlv[0]; +}; + +struct host_cmd_11ac_vht_cfg { + __le16 action; + u8 band_config; + u8 misc_config; + __le32 cap_info; + __le32 mcs_tx_set; + __le32 mcs_rx_set; +} __packed; + +struct host_cmd_tlv_akmp { + struct mwifiex_ie_types_header header; + __le16 key_mgmt; + __le16 key_mgmt_operation; +} __packed; + +struct host_cmd_tlv_pwk_cipher { + struct mwifiex_ie_types_header header; + __le16 proto; + u8 cipher; + u8 reserved; +} __packed; + +struct host_cmd_tlv_gwk_cipher { + struct mwifiex_ie_types_header header; + u8 cipher; + u8 reserved; +} __packed; + +struct host_cmd_tlv_passphrase { + struct mwifiex_ie_types_header header; + u8 passphrase[0]; +} __packed; + +struct host_cmd_tlv_wep_key { + struct mwifiex_ie_types_header header; + u8 key_index; + u8 is_default; + u8 key[1]; +}; + +struct host_cmd_tlv_auth_type { + struct mwifiex_ie_types_header header; + u8 auth_type; +} __packed; + +struct host_cmd_tlv_encrypt_protocol { + struct mwifiex_ie_types_header header; + __le16 proto; +} __packed; + +struct host_cmd_tlv_ssid { + struct mwifiex_ie_types_header header; + u8 ssid[0]; +} __packed; + +struct host_cmd_tlv_rates { + struct mwifiex_ie_types_header header; + u8 rates[0]; +} __packed; + +struct mwifiex_ie_types_bssid_list { + struct mwifiex_ie_types_header header; + u8 bssid[ETH_ALEN]; +} __packed; + +struct host_cmd_tlv_bcast_ssid { + struct mwifiex_ie_types_header header; + u8 bcast_ctl; +} __packed; + +struct host_cmd_tlv_beacon_period { + struct mwifiex_ie_types_header header; + __le16 period; +} __packed; + +struct host_cmd_tlv_dtim_period { + struct mwifiex_ie_types_header header; + u8 period; +} __packed; + +struct host_cmd_tlv_frag_threshold { + struct mwifiex_ie_types_header header; + __le16 frag_thr; +} __packed; + +struct host_cmd_tlv_rts_threshold { + struct mwifiex_ie_types_header header; + __le16 rts_thr; +} __packed; + +struct host_cmd_tlv_retry_limit { + struct mwifiex_ie_types_header header; + u8 limit; +} __packed; + +struct host_cmd_tlv_mac_addr { + struct mwifiex_ie_types_header header; + u8 mac_addr[ETH_ALEN]; +} __packed; + +struct host_cmd_tlv_channel_band { + struct mwifiex_ie_types_header header; + u8 band_config; + u8 channel; +} __packed; + +struct host_cmd_tlv_ageout_timer { + struct mwifiex_ie_types_header header; + __le32 sta_ao_timer; +} __packed; + +struct host_cmd_tlv_power_constraint { + struct mwifiex_ie_types_header header; + u8 constraint; +} __packed; + +struct mwifiex_ie_types_btcoex_scan_time { + struct mwifiex_ie_types_header header; + u8 coex_scan; + u8 reserved; + u16 min_scan_time; + u16 max_scan_time; +} __packed; + +struct mwifiex_ie_types_btcoex_aggr_win_size { + struct mwifiex_ie_types_header header; + u8 coex_win_size; + u8 tx_win_size; + u8 rx_win_size; + u8 reserved; +} __packed; + +struct mwifiex_ie_types_robust_coex { + struct mwifiex_ie_types_header header; + __le32 mode; +} __packed; + +struct host_cmd_ds_version_ext { + u8 version_str_sel; + char version_str[128]; +} __packed; + +struct host_cmd_ds_mgmt_frame_reg { + __le16 action; + __le32 mask; +} __packed; + +struct host_cmd_ds_p2p_mode_cfg { + __le16 action; + __le16 mode; +} __packed; + +struct host_cmd_ds_remain_on_chan { + __le16 action; + u8 status; + u8 reserved; + u8 band_cfg; + u8 channel; + __le32 duration; +} __packed; + +struct host_cmd_ds_802_11_ibss_status { + __le16 action; + __le16 enable; + u8 bssid[ETH_ALEN]; + __le16 beacon_interval; + __le16 atim_window; + __le16 use_g_rate_protect; +} __packed; + +struct mwifiex_fw_mef_entry { + u8 mode; + u8 action; + __le16 exprsize; + u8 expr[0]; +} __packed; + +struct host_cmd_ds_mef_cfg { + __le32 criteria; + __le16 num_entries; + struct mwifiex_fw_mef_entry mef_entry[0]; +} __packed; + +#define CONNECTION_TYPE_INFRA 0 +#define CONNECTION_TYPE_ADHOC 1 +#define CONNECTION_TYPE_AP 2 + +struct host_cmd_ds_set_bss_mode { + u8 con_type; +} __packed; + +struct host_cmd_ds_pcie_details { + /* TX buffer descriptor ring address */ + u32 txbd_addr_lo; + u32 txbd_addr_hi; + /* TX buffer descriptor ring count */ + u32 txbd_count; + + /* RX buffer descriptor ring address */ + u32 rxbd_addr_lo; + u32 rxbd_addr_hi; + /* RX buffer descriptor ring count */ + u32 rxbd_count; + + /* Event buffer descriptor ring address */ + u32 evtbd_addr_lo; + u32 evtbd_addr_hi; + /* Event buffer descriptor ring count */ + u32 evtbd_count; + + /* Sleep cookie buffer physical address */ + u32 sleep_cookie_addr_lo; + u32 sleep_cookie_addr_hi; +} __packed; + +struct mwifiex_ie_types_rssi_threshold { + struct mwifiex_ie_types_header header; + u8 abs_value; + u8 evt_freq; +} __packed; + +#define MWIFIEX_DFS_REC_HDR_LEN 8 +#define MWIFIEX_DFS_REC_HDR_NUM 10 +#define MWIFIEX_BIN_COUNTER_LEN 7 + +struct mwifiex_radar_det_event { + __le32 detect_count; + u8 reg_domain; /*1=fcc, 2=etsi, 3=mic*/ + u8 det_type; /*0=none, 1=pw(chirp), 2=pri(radar)*/ + __le16 pw_chirp_type; + u8 pw_chirp_idx; + u8 pw_value; + u8 pri_radar_type; + u8 pri_bincnt; + u8 bin_counter[MWIFIEX_BIN_COUNTER_LEN]; + u8 num_dfs_records; + u8 dfs_record_hdr[MWIFIEX_DFS_REC_HDR_NUM][MWIFIEX_DFS_REC_HDR_LEN]; + __le32 passed; +} __packed; + +struct mwifiex_ie_types_multi_chan_info { + struct mwifiex_ie_types_header header; + __le16 status; + u8 tlv_buffer[0]; +} __packed; + +struct mwifiex_ie_types_mc_group_info { + struct mwifiex_ie_types_header header; + u8 chan_group_id; + u8 chan_buf_weight; + u8 band_config; + u8 chan_num; + u32 chan_time; + u32 reserved; + union { + u8 sdio_func_num; + u8 usb_ep_num; + } hid_num; + u8 intf_num; + u8 bss_type_numlist[0]; +} __packed; + +struct meas_rpt_map { + u8 rssi:3; + u8 unmeasured:1; + u8 radar:1; + u8 unidentified_sig:1; + u8 ofdm_preamble:1; + u8 bss:1; +} __packed; + +struct mwifiex_ie_types_chan_rpt_data { + struct mwifiex_ie_types_header header; + struct meas_rpt_map map; +} __packed; + +struct host_cmd_ds_802_11_subsc_evt { + __le16 action; + __le16 events; +} __packed; + +struct chan_switch_result { + u8 cur_chan; + u8 status; + u8 reason; +} __packed; + +struct mwifiex_tdls_generic_event { + __le16 type; + u8 peer_mac[ETH_ALEN]; + union { + struct chan_switch_result switch_result; + u8 cs_stop_reason; + __le16 reason_code; + __le16 reserved; + } u; +} __packed; + +struct mwifiex_ie { + __le16 ie_index; + __le16 mgmt_subtype_mask; + __le16 ie_length; + u8 ie_buffer[IEEE_MAX_IE_SIZE]; +} __packed; + +#define MAX_MGMT_IE_INDEX 16 +struct mwifiex_ie_list { + __le16 type; + __le16 len; + struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX]; +} __packed; + +struct coalesce_filt_field_param { + u8 operation; + u8 operand_len; + __le16 offset; + u8 operand_byte_stream[4]; +}; + +struct coalesce_receive_filt_rule { + struct mwifiex_ie_types_header header; + u8 num_of_fields; + u8 pkt_type; + __le16 max_coalescing_delay; + struct coalesce_filt_field_param params[0]; +} __packed; + +struct host_cmd_ds_coalesce_cfg { + __le16 action; + __le16 num_of_rules; + struct coalesce_receive_filt_rule rule[0]; +} __packed; + +struct host_cmd_ds_multi_chan_policy { + __le16 action; + __le16 policy; +} __packed; + +struct host_cmd_ds_robust_coex { + __le16 action; + __le16 reserved; +} __packed; + +struct host_cmd_ds_command { + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; + union { + struct host_cmd_ds_get_hw_spec hw_spec; + struct host_cmd_ds_mac_control mac_ctrl; + struct host_cmd_ds_802_11_mac_address mac_addr; + struct host_cmd_ds_mac_multicast_adr mc_addr; + struct host_cmd_ds_802_11_get_log get_log; + struct host_cmd_ds_802_11_rssi_info rssi_info; + struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp; + struct host_cmd_ds_802_11_snmp_mib smib; + struct host_cmd_ds_tx_rate_query tx_rate; + struct host_cmd_ds_tx_rate_cfg tx_rate_cfg; + struct host_cmd_ds_txpwr_cfg txp_cfg; + struct host_cmd_ds_rf_tx_pwr txp; + struct host_cmd_ds_rf_ant_mimo ant_mimo; + struct host_cmd_ds_rf_ant_siso ant_siso; + struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; + struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; + struct host_cmd_ds_802_11_scan scan; + struct host_cmd_ds_802_11_scan_ext ext_scan; + struct host_cmd_ds_802_11_scan_rsp scan_resp; + struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; + struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; + struct host_cmd_ds_802_11_associate associate; + struct host_cmd_ds_802_11_associate_rsp associate_rsp; + struct host_cmd_ds_802_11_deauthenticate deauth; + struct host_cmd_ds_802_11_ad_hoc_start adhoc_start; + struct host_cmd_ds_802_11_ad_hoc_result adhoc_result; + struct host_cmd_ds_802_11_ad_hoc_join adhoc_join; + struct host_cmd_ds_802_11d_domain_info domain_info; + struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp; + struct host_cmd_ds_11n_addba_req add_ba_req; + struct host_cmd_ds_11n_addba_rsp add_ba_rsp; + struct host_cmd_ds_11n_delba del_ba; + struct host_cmd_ds_txbuf_cfg tx_buf; + struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl; + struct host_cmd_ds_11n_cfg htcfg; + struct host_cmd_ds_wmm_get_status get_wmm_status; + struct host_cmd_ds_802_11_key_material key_material; + struct host_cmd_ds_802_11_key_material_v2 key_material_v2; + struct host_cmd_ds_version_ext verext; + struct host_cmd_ds_mgmt_frame_reg reg_mask; + struct host_cmd_ds_remain_on_chan roc_cfg; + struct host_cmd_ds_p2p_mode_cfg mode_cfg; + struct host_cmd_ds_802_11_ibss_status ibss_coalescing; + struct host_cmd_ds_mef_cfg mef_cfg; + struct host_cmd_ds_mem_access mem; + struct host_cmd_ds_mac_reg_access mac_reg; + struct host_cmd_ds_bbp_reg_access bbp_reg; + struct host_cmd_ds_rf_reg_access rf_reg; + struct host_cmd_ds_pmic_reg_access pmic_reg; + struct host_cmd_ds_set_bss_mode bss_mode; + struct host_cmd_ds_pcie_details pcie_host_spec; + struct host_cmd_ds_802_11_eeprom_access eeprom; + struct host_cmd_ds_802_11_subsc_evt subsc_evt; + struct host_cmd_ds_sys_config uap_sys_config; + struct host_cmd_ds_sta_deauth sta_deauth; + struct host_cmd_ds_sta_list sta_list; + struct host_cmd_11ac_vht_cfg vht_cfg; + struct host_cmd_ds_coalesce_cfg coalesce_cfg; + struct host_cmd_ds_tdls_config tdls_config; + struct host_cmd_ds_tdls_oper tdls_oper; + struct host_cmd_ds_chan_rpt_req chan_rpt_req; + struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg; + struct host_cmd_ds_multi_chan_policy mc_policy; + struct host_cmd_ds_robust_coex coex; + } params; +} __packed; + +struct mwifiex_opt_sleep_confirm { + __le16 command; + __le16 size; + __le16 seq_num; + __le16 result; + __le16 action; + __le16 resp_ctrl; +} __packed; +#endif /* !_MWIFIEX_FW_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/ie.c b/drivers/net/wireless/marvell/mwifiex/ie.c new file mode 100644 index 000000000000..abf52d25b981 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/ie.c @@ -0,0 +1,488 @@ +/* + * Marvell Wireless LAN device driver: management IE handling- setting and + * deleting IE. + * + * Copyright (C) 2012-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "main.h" + +/* This function checks if current IE index is used by any on other interface. + * Return: -1: yes, current IE index is used by someone else. + * 0: no, current IE index is NOT used by other interface. + */ +static int +mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx) +{ + int i; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie *ie; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i] != priv) { + ie = &adapter->priv[i]->mgmt_ie[idx]; + if (ie->mgmt_subtype_mask && ie->ie_length) + return -1; + } + } + + return 0; +} + +/* Get unused IE index. This index will be used for setting new IE */ +static int +mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask, + struct mwifiex_ie *ie, u16 *index) +{ + u16 mask, len, i; + + for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) { + mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask); + len = le16_to_cpu(ie->ie_length); + + if (mask == MWIFIEX_AUTO_IDX_MASK) + continue; + + if (mask == subtype_mask) { + if (len > IEEE_MAX_IE_SIZE) + continue; + + *index = i; + return 0; + } + + if (!priv->mgmt_ie[i].ie_length) { + if (mwifiex_ie_index_used_by_other_intf(priv, i)) + continue; + + *index = i; + return 0; + } + } + + return -1; +} + +/* This function prepares IE data buffer for command to be sent to FW */ +static int +mwifiex_update_autoindex_ies(struct mwifiex_private *priv, + struct mwifiex_ie_list *ie_list) +{ + u16 travel_len, index, mask; + s16 input_len, tlv_len; + struct mwifiex_ie *ie; + u8 *tmp; + + input_len = le16_to_cpu(ie_list->len); + travel_len = sizeof(struct mwifiex_ie_types_header); + + ie_list->len = 0; + + while (input_len >= sizeof(struct mwifiex_ie_types_header)) { + ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len); + tlv_len = le16_to_cpu(ie->ie_length); + travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE; + + if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE) + return -1; + index = le16_to_cpu(ie->ie_index); + mask = le16_to_cpu(ie->mgmt_subtype_mask); + + if (index == MWIFIEX_AUTO_IDX_MASK) { + /* automatic addition */ + if (mwifiex_ie_get_autoidx(priv, mask, ie, &index)) + return -1; + if (index == MWIFIEX_AUTO_IDX_MASK) + return -1; + + tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer; + memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length)); + priv->mgmt_ie[index].ie_length = ie->ie_length; + priv->mgmt_ie[index].ie_index = cpu_to_le16(index); + priv->mgmt_ie[index].mgmt_subtype_mask = + cpu_to_le16(mask); + + ie->ie_index = cpu_to_le16(index); + } else { + if (mask != MWIFIEX_DELETE_MASK) + return -1; + /* + * Check if this index is being used on any + * other interface. + */ + if (mwifiex_ie_index_used_by_other_intf(priv, index)) + return -1; + + ie->ie_length = 0; + memcpy(&priv->mgmt_ie[index], ie, + sizeof(struct mwifiex_ie)); + } + + le16_add_cpu(&ie_list->len, + le16_to_cpu(priv->mgmt_ie[index].ie_length) + + MWIFIEX_IE_HDR_SIZE); + input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE; + } + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_CUSTOM_IE_I, ie_list, false); + + return 0; +} + +/* Copy individual custom IEs for beacon, probe response and assoc response + * and prepare single structure for IE setting. + * This function also updates allocated IE indices from driver. + */ +static int +mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, + struct mwifiex_ie *beacon_ie, u16 *beacon_idx, + struct mwifiex_ie *pr_ie, u16 *probe_idx, + struct mwifiex_ie *ar_ie, u16 *assoc_idx) +{ + struct mwifiex_ie_list *ap_custom_ie; + u8 *pos; + u16 len; + int ret; + + ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL); + if (!ap_custom_ie) + return -ENOMEM; + + ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); + pos = (u8 *)ap_custom_ie->ie_list; + + if (beacon_ie) { + len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(beacon_ie->ie_length); + memcpy(pos, beacon_ie, len); + pos += len; + le16_add_cpu(&ap_custom_ie->len, len); + } + if (pr_ie) { + len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(pr_ie->ie_length); + memcpy(pos, pr_ie, len); + pos += len; + le16_add_cpu(&ap_custom_ie->len, len); + } + if (ar_ie) { + len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(ar_ie->ie_length); + memcpy(pos, ar_ie, len); + pos += len; + le16_add_cpu(&ap_custom_ie->len, len); + } + + ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie); + + pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index); + if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) { + /* save beacon ie index after auto-indexing */ + *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index); + len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(beacon_ie->ie_length); + pos += len; + } + if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) { + /* save probe resp ie index after auto-indexing */ + *probe_idx = *((u16 *)pos); + len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE + + le16_to_cpu(pr_ie->ie_length); + pos += len; + } + if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) + /* save assoc resp ie index after auto-indexing */ + *assoc_idx = *((u16 *)pos); + + kfree(ap_custom_ie); + return ret; +} + +/* This function checks if the vendor specified IE is present in passed buffer + * and copies it to mwifiex_ie structure. + * Function takes pointer to struct mwifiex_ie pointer as argument. + * If the vendor specified IE is present then memory is allocated for + * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing + * this memory. + */ +static int mwifiex_update_vs_ie(const u8 *ies, int ies_len, + struct mwifiex_ie **ie_ptr, u16 mask, + unsigned int oui, u8 oui_type) +{ + struct ieee_types_header *vs_ie; + struct mwifiex_ie *ie = *ie_ptr; + const u8 *vendor_ie; + + vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len); + if (vendor_ie) { + if (!*ie_ptr) { + *ie_ptr = kzalloc(sizeof(struct mwifiex_ie), + GFP_KERNEL); + if (!*ie_ptr) + return -ENOMEM; + ie = *ie_ptr; + } + + vs_ie = (struct ieee_types_header *)vendor_ie; + memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length), + vs_ie, vs_ie->len + 2); + le16_add_cpu(&ie->ie_length, vs_ie->len + 2); + ie->mgmt_subtype_mask = cpu_to_le16(mask); + ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK); + } + + *ie_ptr = ie; + return 0; +} + +/* This function parses beacon IEs, probe response IEs, association response IEs + * from cfg80211_ap_settings->beacon and sets these IE to FW. + */ +static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *data) +{ + struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL; + u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK; + u16 ar_idx = MWIFIEX_AUTO_IDX_MASK; + int ret = 0; + + if (data->beacon_ies && data->beacon_ies_len) { + mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, + &beacon_ie, MGMT_MASK_BEACON, + WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, + &beacon_ie, MGMT_MASK_BEACON, + WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); + } + + if (data->proberesp_ies && data->proberesp_ies_len) { + mwifiex_update_vs_ie(data->proberesp_ies, + data->proberesp_ies_len, &pr_ie, + MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + mwifiex_update_vs_ie(data->proberesp_ies, + data->proberesp_ies_len, &pr_ie, + MGMT_MASK_PROBE_RESP, + WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); + } + + if (data->assocresp_ies && data->assocresp_ies_len) { + mwifiex_update_vs_ie(data->assocresp_ies, + data->assocresp_ies_len, &ar_ie, + MGMT_MASK_ASSOC_RESP | + MGMT_MASK_REASSOC_RESP, + WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPS); + mwifiex_update_vs_ie(data->assocresp_ies, + data->assocresp_ies_len, &ar_ie, + MGMT_MASK_ASSOC_RESP | + MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA, + WLAN_OUI_TYPE_WFA_P2P); + } + + if (beacon_ie || pr_ie || ar_ie) { + ret = mwifiex_update_uap_custom_ie(priv, beacon_ie, + &beacon_idx, pr_ie, + &pr_idx, ar_ie, &ar_idx); + if (ret) + goto done; + } + + priv->beacon_idx = beacon_idx; + priv->proberesp_idx = pr_idx; + priv->assocresp_idx = ar_idx; + +done: + kfree(beacon_ie); + kfree(pr_ie); + kfree(ar_ie); + + return ret; +} + +/* This function parses head and tail IEs, from cfg80211_beacon_data and sets + * these IE to FW. + */ +static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *info) +{ + struct mwifiex_ie *gen_ie; + struct ieee_types_header *hdr; + struct ieee80211_vendor_ie *vendorhdr; + u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; + int left_len, parsed_len = 0; + + if (!info->tail || !info->tail_len) + return 0; + + gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + + left_len = info->tail_len; + + /* Many IEs are generated in FW by parsing bss configuration. + * Let's not add them here; else we may end up duplicating these IEs + */ + while (left_len > sizeof(struct ieee_types_header)) { + hdr = (void *)(info->tail + parsed_len); + switch (hdr->element_id) { + case WLAN_EID_SSID: + case WLAN_EID_SUPP_RATES: + case WLAN_EID_COUNTRY: + case WLAN_EID_PWR_CONSTRAINT: + case WLAN_EID_EXT_SUPP_RATES: + case WLAN_EID_HT_CAPABILITY: + case WLAN_EID_HT_OPERATION: + case WLAN_EID_VHT_CAPABILITY: + case WLAN_EID_VHT_OPERATION: + case WLAN_EID_VENDOR_SPECIFIC: + break; + default: + memcpy(gen_ie->ie_buffer + ie_len, hdr, + hdr->len + sizeof(struct ieee_types_header)); + ie_len += hdr->len + sizeof(struct ieee_types_header); + break; + } + left_len -= hdr->len + sizeof(struct ieee_types_header); + parsed_len += hdr->len + sizeof(struct ieee_types_header); + } + + /* parse only WPA vendor IE from tail, WMM IE is configured by + * bss_config command + */ + vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WPA, + info->tail, info->tail_len); + if (vendorhdr) { + memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, + vendorhdr->len + sizeof(struct ieee_types_header)); + ie_len += vendorhdr->len + sizeof(struct ieee_types_header); + } + + if (!ie_len) { + kfree(gen_ie); + return 0; + } + + gen_ie->ie_index = cpu_to_le16(gen_idx); + gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON | + MGMT_MASK_PROBE_RESP | + MGMT_MASK_ASSOC_RESP); + gen_ie->ie_length = cpu_to_le16(ie_len); + + if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL, + NULL, NULL)) { + kfree(gen_ie); + return -1; + } + + priv->gen_idx = gen_idx; + kfree(gen_ie); + return 0; +} + +/* This function parses different IEs-head & tail IEs, beacon IEs, + * probe response IEs, association response IEs from cfg80211_ap_settings + * function and sets these IE to FW. + */ +int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *info) +{ + int ret; + + ret = mwifiex_uap_parse_tail_ies(priv, info); + + if (ret) + return ret; + + return mwifiex_set_mgmt_beacon_data_ies(priv, info); +} + +/* This function removes management IE set */ +int mwifiex_del_mgmt_ies(struct mwifiex_private *priv) +{ + struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL; + struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL; + int ret = 0; + + if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) { + gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL); + if (!gen_ie) + return -ENOMEM; + + gen_ie->ie_index = cpu_to_le16(priv->gen_idx); + gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); + gen_ie->ie_length = 0; + if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx, + NULL, &priv->proberesp_idx, + NULL, &priv->assocresp_idx)) { + ret = -1; + goto done; + } + + priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; + } + + if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) { + beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); + if (!beacon_ie) { + ret = -ENOMEM; + goto done; + } + beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx); + beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); + beacon_ie->ie_length = 0; + } + if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) { + pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); + if (!pr_ie) { + ret = -ENOMEM; + goto done; + } + pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx); + pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); + pr_ie->ie_length = 0; + } + if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) { + ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); + if (!ar_ie) { + ret = -ENOMEM; + goto done; + } + ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx); + ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); + ar_ie->ie_length = 0; + } + + if (beacon_ie || pr_ie || ar_ie) + ret = mwifiex_update_uap_custom_ie(priv, + beacon_ie, &priv->beacon_idx, + pr_ie, &priv->proberesp_idx, + ar_ie, &priv->assocresp_idx); + +done: + kfree(gen_ie); + kfree(beacon_ie); + kfree(pr_ie); + kfree(ar_ie); + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c new file mode 100644 index 000000000000..de74a7773fb6 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -0,0 +1,782 @@ +/* + * Marvell Wireless LAN device driver: HW/FW Initialization + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + +/* + * This function adds a BSS priority table to the table list. + * + * The function allocates a new BSS priority table node and adds it to + * the end of BSS priority table list, kept in driver memory. + */ +static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bss_prio_node *bss_prio; + struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; + unsigned long flags; + + bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL); + if (!bss_prio) + return -ENOMEM; + + bss_prio->priv = priv; + INIT_LIST_HEAD(&bss_prio->list); + + spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); + list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head); + spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); + + return 0; +} + +static void wakeup_timer_fn(unsigned long data) +{ + struct mwifiex_adapter *adapter = (struct mwifiex_adapter *)data; + + mwifiex_dbg(adapter, ERROR, "Firmware wakeup failed\n"); + adapter->hw_status = MWIFIEX_HW_STATUS_RESET; + mwifiex_cancel_all_pending_cmd(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); +} + +/* + * This function initializes the private structure and sets default + * values to the members. + * + * Additionally, it also initializes all the locks and sets up all the + * lists. + */ +int mwifiex_init_priv(struct mwifiex_private *priv) +{ + u32 i; + + priv->media_connected = false; + eth_broadcast_addr(priv->curr_addr); + priv->port_open = false; + priv->usb_port = MWIFIEX_USB_EP_DATA; + priv->pkt_tx_ctrl = 0; + priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; + priv->data_rate = 0; /* Initially indicate the rate as auto */ + priv->is_data_rate_auto = true; + priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; + priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; + + priv->sec_info.wep_enabled = 0; + priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; + priv->sec_info.encryption_mode = 0; + for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++) + memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key)); + priv->wep_key_curr_index = 0; + priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | + HostCmd_ACT_MAC_ETHERNETII_ENABLE; + + priv->beacon_period = 100; /* beacon interval */ ; + priv->attempted_bss_desc = NULL; + memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); + priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL; + + memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid)); + memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid)); + memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); + priv->assoc_rsp_size = 0; + priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; + priv->atim_window = 0; + priv->adhoc_state = ADHOC_IDLE; + priv->tx_power_level = 0; + priv->max_tx_power_level = 0; + priv->min_tx_power_level = 0; + priv->tx_rate = 0; + priv->rxpd_htinfo = 0; + priv->rxpd_rate = 0; + priv->rate_bitmap = 0; + priv->data_rssi_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->data_nf_last = 0; + priv->bcn_rssi_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->bcn_nf_last = 0; + memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + memset(&priv->aes_key, 0, sizeof(priv->aes_key)); + priv->wpa_ie_len = 0; + priv->wpa_is_gtk_set = false; + + memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf)); + priv->assoc_tlv_buf_len = 0; + memset(&priv->wps, 0, sizeof(priv->wps)); + memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); + priv->gen_ie_buf_len = 0; + memset(priv->vs_ie, 0, sizeof(priv->vs_ie)); + + priv->wmm_required = true; + priv->wmm_enabled = false; + priv->wmm_qosinfo = 0; + priv->curr_bcn_buf = NULL; + priv->curr_bcn_size = 0; + priv->wps_ie = NULL; + priv->wps_ie_len = 0; + priv->ap_11n_enabled = 0; + memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg)); + + priv->scan_block = false; + + priv->csa_chan = 0; + priv->csa_expire_time = 0; + priv->del_list_idx = 0; + priv->hs2_enabled = false; + priv->check_tdls_tx = false; + memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID); + + mwifiex_init_11h_params(priv); + + return mwifiex_add_bss_prio_tbl(priv); +} + +/* + * This function allocates buffers for members of the adapter + * structure. + * + * The memory allocated includes scan table, command buffers, and + * sleep confirm command buffer. In addition, the queues are + * also initialized. + */ +static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter) +{ + int ret; + + /* Allocate command buffer */ + ret = mwifiex_alloc_cmd_buffer(adapter); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to alloc cmd buffer\n", + __func__); + return -1; + } + + adapter->sleep_cfm = + dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) + + INTF_HEADER_LEN); + + if (!adapter->sleep_cfm) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to alloc sleep cfm\t" + " cmd buffer\n", __func__); + return -1; + } + skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN); + + return 0; +} + +/* + * This function initializes the adapter structure and sets default + * values to the members of adapter. + * + * This also initializes the WMM related parameters in the driver private + * structures. + */ +static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) +{ + struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = NULL; + + skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm)); + + adapter->cmd_sent = false; + + if (adapter->iface_type == MWIFIEX_SDIO) + adapter->data_sent = true; + else + adapter->data_sent = false; + + adapter->cmd_resp_received = false; + adapter->event_received = false; + adapter->data_received = false; + + adapter->surprise_removed = false; + + adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; + + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; + adapter->ps_state = PS_STATE_AWAKE; + adapter->need_to_wakeup = false; + + adapter->scan_mode = HostCmd_BSS_MODE_ANY; + adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME; + adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME; + adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME; + adapter->scan_chan_gap_time = MWIFIEX_DEF_SCAN_CHAN_GAP_TIME; + + adapter->scan_probes = 1; + + adapter->multiple_dtim = 1; + + adapter->local_listen_interval = 0; /* default value in firmware + will be used */ + + adapter->is_deep_sleep = false; + + adapter->delay_null_pkt = false; + adapter->delay_to_ps = 1000; + adapter->enhanced_ps_mode = PS_MODE_AUTO; + + adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by + default */ + adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by + default */ + adapter->pm_wakeup_card_req = false; + + adapter->pm_wakeup_fw_try = false; + + adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + + adapter->is_hs_configured = false; + adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF); + adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF; + adapter->hs_cfg.gap = HS_CFG_GAP_DEF; + adapter->hs_activated = false; + + memset(adapter->event_body, 0, sizeof(adapter->event_body)); + adapter->hw_dot_11n_dev_cap = 0; + adapter->hw_dev_mcs_support = 0; + adapter->sec_chan_offset = 0; + adapter->adhoc_11n_enabled = false; + + mwifiex_wmm_init(adapter); + + sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) + adapter->sleep_cfm->data; + memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); + sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); + sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len); + sleep_cfm_buf->result = 0; + sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM); + sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED); + + memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params)); + memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); + adapter->tx_lock_flag = false; + adapter->null_pkt_interval = 0; + adapter->fw_bands = 0; + adapter->config_bands = 0; + adapter->adhoc_start_band = 0; + adapter->scan_channels = NULL; + adapter->fw_release_number = 0; + adapter->fw_cap_info = 0; + memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf)); + adapter->event_cause = 0; + adapter->region_code = 0; + adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; + adapter->adhoc_awake_period = 0; + memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); + adapter->arp_filter_size = 0; + adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; + adapter->key_api_major_ver = 0; + adapter->key_api_minor_ver = 0; + eth_broadcast_addr(adapter->perm_addr); + adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM; + adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM; + adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM; + adapter->active_scan_triggered = false; + setup_timer(&adapter->wakeup_timer, wakeup_timer_fn, + (unsigned long)adapter); +} + +/* + * This function sets trans_start per tx_queue + */ +void mwifiex_set_trans_start(struct net_device *dev) +{ + int i; + + for (i = 0; i < dev->num_tx_queues; i++) + netdev_get_tx_queue(dev, i)->trans_start = jiffies; + + dev->trans_start = jiffies; +} + +/* + * This function wakes up all queues in net_device + */ +void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, + struct mwifiex_adapter *adapter) +{ + unsigned long dev_queue_flags; + unsigned int i; + + spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); + + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); + + if (netif_tx_queue_stopped(txq)) + netif_tx_wake_queue(txq); + } + + spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); +} + +/* + * This function stops all queues in net_device + */ +void mwifiex_stop_net_dev_queue(struct net_device *netdev, + struct mwifiex_adapter *adapter) +{ + unsigned long dev_queue_flags; + unsigned int i; + + spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); + + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); + + if (!netif_tx_queue_stopped(txq)) + netif_tx_stop_queue(txq); + } + + spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); +} + +/* + * This function releases the lock variables and frees the locks and + * associated locks. + */ +static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + s32 i, j; + + /* Free lists */ + list_del(&adapter->cmd_free_q); + list_del(&adapter->cmd_pending_q); + list_del(&adapter->scan_pending_q); + + for (i = 0; i < adapter->priv_num; i++) + list_del(&adapter->bss_prio_tbl[i].bss_prio_head); + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + for (j = 0; j < MAX_NUM_TID; ++j) + list_del(&priv->wmm.tid_tbl_ptr[j].ra_list); + list_del(&priv->tx_ba_stream_tbl_ptr); + list_del(&priv->rx_reorder_tbl_ptr); + list_del(&priv->sta_list); + list_del(&priv->auto_tdls_list); + } + } +} + +/* + * This function performs cleanup for adapter structure. + * + * The cleanup is done recursively, by canceling all pending + * commands, freeing the member buffers previously allocated + * (command buffers, scan table buffer, sleep confirm command + * buffer), stopping the timers and calling the cleanup routines + * for every interface. + */ +static void +mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) +{ + int idx; + + if (!adapter) { + pr_err("%s: adapter is NULL\n", __func__); + return; + } + + del_timer(&adapter->wakeup_timer); + mwifiex_cancel_all_pending_cmd(adapter); + wake_up_interruptible(&adapter->cmd_wait_q.wait); + wake_up_interruptible(&adapter->hs_activate_wait_q); + + /* Free lock variables */ + mwifiex_free_lock_list(adapter); + + /* Free command buffer */ + mwifiex_dbg(adapter, INFO, "info: free cmd buffer\n"); + mwifiex_free_cmd_buffer(adapter); + + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_dump = NULL; + adapter->drv_info_size = 0; + } + + if (adapter->sleep_cfm) + dev_kfree_skb_any(adapter->sleep_cfm); +} + +/* + * This function intializes the lock variables and + * the list heads. + */ +int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + s32 i, j; + + spin_lock_init(&adapter->mwifiex_lock); + spin_lock_init(&adapter->int_lock); + spin_lock_init(&adapter->main_proc_lock); + spin_lock_init(&adapter->mwifiex_cmd_lock); + spin_lock_init(&adapter->queue_lock); + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + spin_lock_init(&priv->rx_pkt_lock); + spin_lock_init(&priv->wmm.ra_list_spinlock); + spin_lock_init(&priv->curr_bcn_buf_lock); + spin_lock_init(&priv->sta_list_spinlock); + spin_lock_init(&priv->auto_tdls_lock); + } + } + + /* Initialize cmd_free_q */ + INIT_LIST_HEAD(&adapter->cmd_free_q); + /* Initialize cmd_pending_q */ + INIT_LIST_HEAD(&adapter->cmd_pending_q); + /* Initialize scan_pending_q */ + INIT_LIST_HEAD(&adapter->scan_pending_q); + + spin_lock_init(&adapter->cmd_free_q_lock); + spin_lock_init(&adapter->cmd_pending_q_lock); + spin_lock_init(&adapter->scan_pending_q_lock); + spin_lock_init(&adapter->rx_proc_lock); + + skb_queue_head_init(&adapter->rx_data_q); + skb_queue_head_init(&adapter->tx_data_q); + + for (i = 0; i < adapter->priv_num; ++i) { + INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); + spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock); + } + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i]) + continue; + priv = adapter->priv[i]; + for (j = 0; j < MAX_NUM_TID; ++j) + INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list); + INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); + INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); + INIT_LIST_HEAD(&priv->sta_list); + INIT_LIST_HEAD(&priv->auto_tdls_list); + skb_queue_head_init(&priv->tdls_txq); + skb_queue_head_init(&priv->bypass_txq); + + spin_lock_init(&priv->tx_ba_stream_tbl_lock); + spin_lock_init(&priv->rx_reorder_tbl_lock); + + spin_lock_init(&priv->ack_status_lock); + idr_init(&priv->ack_status_frames); + } + + return 0; +} + +/* + * This function initializes the firmware. + * + * The following operations are performed sequentially - + * - Allocate adapter structure + * - Initialize the adapter structure + * - Initialize the private structure + * - Add BSS priority tables to the adapter structure + * - For each interface, send the init commands to firmware + * - Send the first command in command pending queue, if available + */ +int mwifiex_init_fw(struct mwifiex_adapter *adapter) +{ + int ret; + struct mwifiex_private *priv; + u8 i, first_sta = true; + int is_cmd_pend_q_empty; + unsigned long flags; + + adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; + + /* Allocate memory for member of adapter structure */ + ret = mwifiex_allocate_adapter(adapter); + if (ret) + return -1; + + /* Initialize adapter structure */ + mwifiex_init_adapter(adapter); + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + + /* Initialize private structure */ + ret = mwifiex_init_priv(priv); + if (ret) + return -1; + } + } + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta, + true); + if (ret == -1) + return -1; + + first_sta = false; + } + } + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + if (!is_cmd_pend_q_empty) { + /* Send the first command in queue and return */ + if (mwifiex_main_process(adapter) != -1) + ret = -EINPROGRESS; + } else { + adapter->hw_status = MWIFIEX_HW_STATUS_READY; + } + + return ret; +} + +/* + * This function deletes the BSS priority tables. + * + * The function traverses through all the allocated BSS priority nodes + * in every BSS priority table and frees them. + */ +static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) +{ + int i; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bss_prio_node *bssprio_node, *tmp_node; + struct list_head *head; + spinlock_t *lock; /* bss priority lock */ + unsigned long flags; + + for (i = 0; i < adapter->priv_num; ++i) { + head = &adapter->bss_prio_tbl[i].bss_prio_head; + lock = &adapter->bss_prio_tbl[i].bss_prio_lock; + mwifiex_dbg(adapter, INFO, + "info: delete BSS priority table,\t" + "bss_type = %d, bss_num = %d, i = %d,\t" + "head = %p\n", + priv->bss_type, priv->bss_num, i, head); + + { + spin_lock_irqsave(lock, flags); + if (list_empty(head)) { + spin_unlock_irqrestore(lock, flags); + continue; + } + list_for_each_entry_safe(bssprio_node, tmp_node, head, + list) { + if (bssprio_node->priv == priv) { + mwifiex_dbg(adapter, INFO, + "info: Delete\t" + "node %p, next = %p\n", + bssprio_node, tmp_node); + list_del(&bssprio_node->list); + kfree(bssprio_node); + } + } + spin_unlock_irqrestore(lock, flags); + } + } +} + +/* + * This function frees the private structure, including cleans + * up the TX and RX queues and frees the BSS priority tables. + */ +void mwifiex_free_priv(struct mwifiex_private *priv) +{ + mwifiex_clean_txrx(priv); + mwifiex_delete_bss_prio_tbl(priv); + mwifiex_free_curr_bcn(priv); +} + +/* + * This function is used to shutdown the driver. + * + * The following operations are performed sequentially - + * - Check if already shut down + * - Make sure the main process has stopped + * - Clean up the Tx and Rx queues + * - Delete BSS priority tables + * - Free the adapter + * - Notify completion + */ +int +mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) +{ + int ret = -EINPROGRESS; + struct mwifiex_private *priv; + s32 i; + unsigned long flags; + struct sk_buff *skb; + + /* mwifiex already shutdown */ + if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY) + return 0; + + adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING; + /* wait for mwifiex_process to complete */ + if (adapter->mwifiex_processing) { + mwifiex_dbg(adapter, WARN, + "main process is still running\n"); + return ret; + } + + /* cancel current command */ + if (adapter->curr_cmd) { + mwifiex_dbg(adapter, WARN, + "curr_cmd is still in processing\n"); + del_timer_sync(&adapter->cmd_timer); + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + adapter->curr_cmd = NULL; + } + + /* shut down mwifiex */ + mwifiex_dbg(adapter, MSG, + "info: shutdown mwifiex...\n"); + + /* Clean up Tx/Rx queues and delete BSS priority table */ + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + priv = adapter->priv[i]; + + mwifiex_clean_auto_tdls(priv); + mwifiex_abort_cac(priv); + mwifiex_clean_txrx(priv); + mwifiex_delete_bss_prio_tbl(priv); + } + } + + atomic_set(&adapter->tx_queued, 0); + while ((skb = skb_dequeue(&adapter->tx_data_q))) + mwifiex_write_data_complete(adapter, skb, 0, 0); + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + + while ((skb = skb_dequeue(&adapter->rx_data_q))) { + struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); + + atomic_dec(&adapter->rx_pending); + priv = adapter->priv[rx_info->bss_num]; + if (priv) + priv->stats.rx_dropped++; + + dev_kfree_skb_any(skb); + } + + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + + spin_lock(&adapter->mwifiex_lock); + + mwifiex_adapter_cleanup(adapter); + + spin_unlock(&adapter->mwifiex_lock); + + /* Notify completion */ + ret = mwifiex_shutdown_fw_complete(adapter); + + return ret; +} + +/* + * This function downloads the firmware to the card. + * + * The actual download is preceded by two sanity checks - + * - Check if firmware is already running + * - Check if the interface is the winner to download the firmware + * + * ...and followed by another - + * - Check if the firmware is downloaded successfully + * + * After download is successfully completed, the host interrupts are enabled. + */ +int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *pmfw) +{ + int ret; + u32 poll_num = 1; + + if (adapter->if_ops.check_fw_status) { + adapter->winner = 0; + + /* check if firmware is already running */ + ret = adapter->if_ops.check_fw_status(adapter, poll_num); + if (!ret) { + mwifiex_dbg(adapter, MSG, + "WLAN FW already running! Skip FW dnld\n"); + return 0; + } + + poll_num = MAX_FIRMWARE_POLL_TRIES; + + /* check if we are the winner for downloading FW */ + if (!adapter->winner) { + mwifiex_dbg(adapter, MSG, + "FW already running! Skip FW dnld\n"); + goto poll_fw; + } + } + + if (pmfw) { + /* Download firmware with helper */ + ret = adapter->if_ops.prog_fw(adapter, pmfw); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "prog_fw failed ret=%#x\n", ret); + return ret; + } + } + +poll_fw: + /* Check if the firmware is downloaded successfully or not */ + ret = adapter->if_ops.check_fw_status(adapter, poll_num); + if (ret) + mwifiex_dbg(adapter, ERROR, + "FW failed to be active in time\n"); + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/ioctl.h b/drivers/net/wireless/marvell/mwifiex/ioctl.h new file mode 100644 index 000000000000..4f0174c64946 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/ioctl.h @@ -0,0 +1,470 @@ +/* + * Marvell Wireless LAN device driver: ioctl data structures & APIs + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_IOCTL_H_ +#define _MWIFIEX_IOCTL_H_ + +#include + +enum { + MWIFIEX_SCAN_TYPE_UNCHANGED = 0, + MWIFIEX_SCAN_TYPE_ACTIVE, + MWIFIEX_SCAN_TYPE_PASSIVE +}; + +struct mwifiex_user_scan { + u32 scan_cfg_len; + u8 scan_cfg_buf[1]; +}; + +#define MWIFIEX_PROMISC_MODE 1 +#define MWIFIEX_MULTICAST_MODE 2 +#define MWIFIEX_ALL_MULTI_MODE 4 +#define MWIFIEX_MAX_MULTICAST_LIST_SIZE 32 + +struct mwifiex_multicast_list { + u32 mode; + u32 num_multicast_addr; + u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; +}; + +struct mwifiex_chan_freq { + u32 channel; + u32 freq; +}; + +struct mwifiex_ssid_bssid { + struct cfg80211_ssid ssid; + u8 bssid[ETH_ALEN]; +}; + +enum { + BAND_B = 1, + BAND_G = 2, + BAND_A = 4, + BAND_GN = 8, + BAND_AN = 16, + BAND_AAC = 32, +}; + +#define MWIFIEX_WPA_PASSHPHRASE_LEN 64 +struct wpa_param { + u8 pairwise_cipher_wpa; + u8 pairwise_cipher_wpa2; + u8 group_cipher; + u32 length; + u8 passphrase[MWIFIEX_WPA_PASSHPHRASE_LEN]; +}; + +struct wep_key { + u8 key_index; + u8 is_default; + u16 length; + u8 key[WLAN_KEY_LEN_WEP104]; +}; + +#define KEY_MGMT_ON_HOST 0x03 +#define MWIFIEX_AUTH_MODE_AUTO 0xFF +#define BAND_CONFIG_BG 0x00 +#define BAND_CONFIG_A 0x01 +#define MWIFIEX_SUPPORTED_RATES 14 +#define MWIFIEX_SUPPORTED_RATES_EXT 32 +#define MWIFIEX_TDLS_SUPPORTED_RATES 8 +#define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf +#define MWIFIEX_PRIO_BK 2 +#define MWIFIEX_PRIO_VI 5 + +struct mwifiex_uap_bss_param { + u8 channel; + u8 band_cfg; + u16 rts_threshold; + u16 frag_threshold; + u8 retry_limit; + struct mwifiex_802_11_ssid ssid; + u8 bcast_ssid_ctl; + u8 radio_ctl; + u8 dtim_period; + u16 beacon_period; + u16 auth_mode; + u16 protocol; + u16 key_mgmt; + u16 key_mgmt_operation; + struct wpa_param wpa_cfg; + struct wep_key wep_cfg[NUM_WEP_KEYS]; + struct ieee80211_ht_cap ht_cap; + struct ieee80211_vht_cap vht_cap; + u8 rates[MWIFIEX_SUPPORTED_RATES]; + u32 sta_ao_timer; + u32 ps_sta_ao_timer; + u8 qos_info; + u8 power_constraint; + struct mwifiex_types_wmm_info wmm_info; +}; + +enum { + ADHOC_IDLE, + ADHOC_STARTED, + ADHOC_JOINED, + ADHOC_COALESCED +}; + +struct mwifiex_ds_get_stats { + u32 mcast_tx_frame; + u32 failed; + u32 retry; + u32 multi_retry; + u32 frame_dup; + u32 rts_success; + u32 rts_failure; + u32 ack_failure; + u32 rx_frag; + u32 mcast_rx_frame; + u32 fcs_error; + u32 tx_frame; + u32 wep_icv_error[4]; + u32 bcn_rcv_cnt; + u32 bcn_miss_cnt; +}; + +#define MWIFIEX_MAX_VER_STR_LEN 128 + +struct mwifiex_ver_ext { + u32 version_str_sel; + char version_str[MWIFIEX_MAX_VER_STR_LEN]; +}; + +struct mwifiex_bss_info { + u32 bss_mode; + struct cfg80211_ssid ssid; + u32 bss_chan; + u8 country_code[3]; + u32 media_connected; + u32 max_power_level; + u32 min_power_level; + u32 adhoc_state; + signed int bcn_nf_last; + u32 wep_status; + u32 is_hs_configured; + u32 is_deep_sleep; + u8 bssid[ETH_ALEN]; +}; + +#define MAX_NUM_TID 8 + +#define MAX_RX_WINSIZE 64 + +struct mwifiex_ds_rx_reorder_tbl { + u16 tid; + u8 ta[ETH_ALEN]; + u32 start_win; + u32 win_size; + u32 buffer[MAX_RX_WINSIZE]; +}; + +struct mwifiex_ds_tx_ba_stream_tbl { + u16 tid; + u8 ra[ETH_ALEN]; + u8 amsdu; +}; + +#define DBG_CMD_NUM 5 + +struct tdls_peer_info { + u8 peer_addr[ETH_ALEN]; +}; + +struct mwifiex_debug_info { + unsigned int debug_mask; + u32 int_counter; + u32 packets_out[MAX_NUM_TID]; + u32 tx_buf_size; + u32 curr_tx_buf_size; + u32 tx_tbl_num; + struct mwifiex_ds_tx_ba_stream_tbl + tx_tbl[MWIFIEX_MAX_TX_BASTREAM_SUPPORTED]; + u32 rx_tbl_num; + struct mwifiex_ds_rx_reorder_tbl rx_tbl + [MWIFIEX_MAX_RX_BASTREAM_SUPPORTED]; + u32 tdls_peer_num; + struct tdls_peer_info tdls_list + [MWIFIEX_MAX_TDLS_PEER_SUPPORTED]; + u16 ps_mode; + u32 ps_state; + u8 is_deep_sleep; + u8 pm_wakeup_card_req; + u32 pm_wakeup_fw_try; + u8 is_hs_configured; + u8 hs_activated; + u32 num_cmd_host_to_card_failure; + u32 num_cmd_sleep_cfm_host_to_card_failure; + u32 num_tx_host_to_card_failure; + u32 num_event_deauth; + u32 num_event_disassoc; + u32 num_event_link_lost; + u32 num_cmd_deauth; + u32 num_cmd_assoc_success; + u32 num_cmd_assoc_failure; + u32 num_tx_timeout; + u8 is_cmd_timedout; + u16 timeout_cmd_id; + u16 timeout_cmd_act; + u16 last_cmd_id[DBG_CMD_NUM]; + u16 last_cmd_act[DBG_CMD_NUM]; + u16 last_cmd_index; + u16 last_cmd_resp_id[DBG_CMD_NUM]; + u16 last_cmd_resp_index; + u16 last_event[DBG_CMD_NUM]; + u16 last_event_index; + u8 data_sent; + u8 cmd_sent; + u8 cmd_resp_received; + u8 event_received; +}; + +#define MWIFIEX_KEY_INDEX_UNICAST 0x40000000 +#define PN_LEN 16 + +struct mwifiex_ds_encrypt_key { + u32 key_disable; + u32 key_index; + u32 key_len; + u8 key_material[WLAN_MAX_KEY_LEN]; + u8 mac_addr[ETH_ALEN]; + u32 is_wapi_key; + u8 pn[PN_LEN]; /* packet number */ + u8 pn_len; + u8 is_igtk_key; + u8 is_current_wep_key; + u8 is_rx_seq_valid; +}; + +struct mwifiex_power_cfg { + u32 is_power_auto; + u32 power_level; +}; + +struct mwifiex_ds_hs_cfg { + u32 is_invoke_hostcmd; + /* Bit0: non-unicast data + * Bit1: unicast data + * Bit2: mac events + * Bit3: magic packet + */ + u32 conditions; + u32 gpio; + u32 gap; +}; + +#define DEEP_SLEEP_ON 1 +#define DEEP_SLEEP_OFF 0 +#define DEEP_SLEEP_IDLE_TIME 100 +#define PS_MODE_AUTO 1 + +struct mwifiex_ds_auto_ds { + u16 auto_ds; + u16 idle_time; +}; + +struct mwifiex_ds_pm_cfg { + union { + u32 ps_mode; + struct mwifiex_ds_hs_cfg hs_cfg; + struct mwifiex_ds_auto_ds auto_deep_sleep; + u32 sleep_period; + } param; +}; + +struct mwifiex_11ac_vht_cfg { + u8 band_config; + u8 misc_config; + u32 cap_info; + u32 mcs_tx_set; + u32 mcs_rx_set; +}; + +struct mwifiex_ds_11n_tx_cfg { + u16 tx_htcap; + u16 tx_htinfo; + u16 misc_config; /* Needed for 802.11AC cards only */ +}; + +struct mwifiex_ds_11n_amsdu_aggr_ctrl { + u16 enable; + u16 curr_buf_size; +}; + +struct mwifiex_ds_ant_cfg { + u32 tx_ant; + u32 rx_ant; +}; + +#define MWIFIEX_NUM_OF_CMD_BUFFER 50 +#define MWIFIEX_SIZE_OF_CMD_BUFFER 2048 + +enum { + MWIFIEX_IE_TYPE_GEN_IE = 0, + MWIFIEX_IE_TYPE_ARP_FILTER, +}; + +enum { + MWIFIEX_REG_MAC = 1, + MWIFIEX_REG_BBP, + MWIFIEX_REG_RF, + MWIFIEX_REG_PMIC, + MWIFIEX_REG_CAU, +}; + +struct mwifiex_ds_reg_rw { + __le32 type; + __le32 offset; + __le32 value; +}; + +#define MAX_EEPROM_DATA 256 + +struct mwifiex_ds_read_eeprom { + __le16 offset; + __le16 byte_count; + u8 value[MAX_EEPROM_DATA]; +}; + +struct mwifiex_ds_mem_rw { + u32 addr; + u32 value; +}; + +#define IEEE_MAX_IE_SIZE 256 + +#define MWIFIEX_IE_HDR_SIZE (sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE) + +struct mwifiex_ds_misc_gen_ie { + u32 type; + u32 len; + u8 ie_data[IEEE_MAX_IE_SIZE]; +}; + +struct mwifiex_ds_misc_cmd { + u32 len; + u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER]; +}; + +#define BITMASK_BCN_RSSI_LOW BIT(0) +#define BITMASK_BCN_RSSI_HIGH BIT(4) + +enum subsc_evt_rssi_state { + EVENT_HANDLED, + RSSI_LOW_RECVD, + RSSI_HIGH_RECVD +}; + +struct subsc_evt_cfg { + u8 abs_value; + u8 evt_freq; +}; + +struct mwifiex_ds_misc_subsc_evt { + u16 action; + u16 events; + struct subsc_evt_cfg bcn_l_rssi_cfg; + struct subsc_evt_cfg bcn_h_rssi_cfg; +}; + +#define MWIFIEX_MEF_MAX_BYTESEQ 6 /* non-adjustable */ +#define MWIFIEX_MEF_MAX_FILTERS 10 + +struct mwifiex_mef_filter { + u16 repeat; + u16 offset; + s8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; + u8 filt_type; + u8 filt_action; +}; + +struct mwifiex_mef_entry { + u8 mode; + u8 action; + struct mwifiex_mef_filter filter[MWIFIEX_MEF_MAX_FILTERS]; +}; + +struct mwifiex_ds_mef_cfg { + u32 criteria; + u16 num_entries; + struct mwifiex_mef_entry *mef_entry; +}; + +#define MWIFIEX_MAX_VSIE_LEN (256) +#define MWIFIEX_MAX_VSIE_NUM (8) +#define MWIFIEX_VSIE_MASK_CLEAR 0x00 +#define MWIFIEX_VSIE_MASK_SCAN 0x01 +#define MWIFIEX_VSIE_MASK_ASSOC 0x02 +#define MWIFIEX_VSIE_MASK_ADHOC 0x04 + +enum { + MWIFIEX_FUNC_INIT = 1, + MWIFIEX_FUNC_SHUTDOWN, +}; + +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 MWIFIEX_COALESCE_MAX_RULES 8 +#define MWIFIEX_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ +#define MWIFIEX_COALESCE_MAX_FILTERS 4 +#define MWIFIEX_MAX_COALESCING_DELAY 100 /* in msecs */ + +struct filt_field_param { + u8 operation; + u8 operand_len; + u16 offset; + u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ]; +}; + +struct mwifiex_coalesce_rule { + u16 max_coalescing_delay; + u8 num_of_fields; + u8 pkt_type; + struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS]; +}; + +struct mwifiex_ds_coalesce_cfg { + u16 num_of_rules; + struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES]; +}; + +struct mwifiex_ds_tdls_oper { + u16 tdls_action; + u8 peer_mac[ETH_ALEN]; + u16 capability; + u8 qos_info; + u8 *ext_capab; + u8 ext_capab_len; + u8 *supp_rates; + u8 supp_rates_len; + u8 *ht_capab; +}; + +#endif /* !_MWIFIEX_IOCTL_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c new file mode 100644 index 000000000000..3cda1f956f0b --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -0,0 +1,1525 @@ +/* + * Marvell Wireless LAN device driver: association and ad-hoc start/join + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" + +#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9))) + +/* + * Append a generic IE as a pass through TLV to a TLV buffer. + * + * This function is called from the network join command preparation 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. + */ +static int +mwifiex_cmd_append_generic_ie(struct mwifiex_private *priv, u8 **buffer) +{ + int ret_len = 0; + struct mwifiex_ie_types_header ie_header; + + /* Null Checks */ + if (!buffer) + return 0; + if (!(*buffer)) + return 0; + + /* + * If there is a generic ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->gen_ie_buf_len) { + mwifiex_dbg(priv->adapter, INFO, + "info: %s: append generic ie len %d to %p\n", + __func__, priv->gen_ie_buf_len, *buffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); + ie_header.len = cpu_to_le16(priv->gen_ie_buf_len); + memcpy(*buffer, &ie_header, sizeof(ie_header)); + + /* Increment the return size and the return buffer pointer + param */ + *buffer += sizeof(ie_header); + ret_len += sizeof(ie_header); + + /* Copy the generic IE buffer to the output buffer, advance + pointer */ + memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len); + + /* Increment the return size and the return buffer pointer + param */ + *buffer += 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 */ + return ret_len; +} + +/* + * Append TSF tracking info from the scan table for the target AP. + * + * This function is called from the network join command preparation 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. + */ +static int +mwifiex_cmd_append_tsf_tlv(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc) +{ + struct mwifiex_ie_types_tsf_timestamp tsf_tlv; + __le64 tsf_val; + + /* Null Checks */ + if (buffer == NULL) + return 0; + if (*buffer == NULL) + return 0; + + memset(&tsf_tlv, 0x00, sizeof(struct mwifiex_ie_types_tsf_timestamp)); + + tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); + tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val)); + + memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header)); + *buffer += sizeof(tsf_tlv.header); + + /* TSF at the time when beacon/probe_response was received */ + tsf_val = cpu_to_le64(bss_desc->fw_tsf); + memcpy(*buffer, &tsf_val, sizeof(tsf_val)); + *buffer += sizeof(tsf_val); + + tsf_val = cpu_to_le64(bss_desc->timestamp); + + mwifiex_dbg(priv->adapter, INFO, + "info: %s: TSF offset calc: %016llx - %016llx\n", + __func__, bss_desc->timestamp, bss_desc->fw_tsf); + + memcpy(*buffer, &tsf_val, sizeof(tsf_val)); + *buffer += sizeof(tsf_val); + + return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); +} + +/* + * 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. + */ +static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1, + u32 rate1_size, u8 *rate2, u32 rate2_size) +{ + int ret; + u8 *ptr = rate1, *tmp; + u32 i, j; + + tmp = kmemdup(rate1, rate1_size, GFP_KERNEL); + if (!tmp) { + mwifiex_dbg(priv->adapter, ERROR, "failed to alloc tmp buf\n"); + return -ENOMEM; + } + + memset(rate1, 0, rate1_size); + + for (i = 0; i < rate2_size && rate2[i]; i++) { + for (j = 0; j < rate1_size && tmp[j]; j++) { + /* Check common rate, excluding the bit for + basic rate */ + if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { + *rate1++ = tmp[j]; + break; + } + } + } + + mwifiex_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n", + priv->data_rate); + + if (!priv->is_data_rate_auto) { + while (*ptr) { + if ((*ptr & 0x7f) == priv->data_rate) { + ret = 0; + goto done; + } + ptr++; + } + mwifiex_dbg(priv->adapter, ERROR, + "previously set fixed data rate %#x\t" + "is not compatible with the network\n", + priv->data_rate); + + ret = -1; + goto done; + } + + ret = 0; +done: + kfree(tmp); + return ret; +} + +/* + * This function creates the intersection of the rates supported by a + * target BSS and our adapter settings for use in an assoc/join command. + */ +static int +mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, + u8 *out_rates, u32 *out_rates_size) +{ + u8 card_rates[MWIFIEX_SUPPORTED_RATES]; + u32 card_rates_size; + + /* Copy AP supported rates */ + memcpy(out_rates, bss_desc->supported_rates, MWIFIEX_SUPPORTED_RATES); + /* Get the STA supported rates */ + card_rates_size = mwifiex_get_active_data_rates(priv, card_rates); + /* Get the common rates between AP and STA supported rates */ + if (mwifiex_get_common_rates(priv, out_rates, MWIFIEX_SUPPORTED_RATES, + card_rates, card_rates_size)) { + *out_rates_size = 0; + mwifiex_dbg(priv->adapter, ERROR, + "%s: cannot get common rates\n", + __func__); + return -1; + } + + *out_rates_size = + min_t(size_t, strlen(out_rates), MWIFIEX_SUPPORTED_RATES); + + return 0; +} + +/* + * This function appends a WPS IE. It is called from the network join command + * preparation routine. + * + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a WPS TLV type to the request. + */ +static int +mwifiex_cmd_append_wps_ie(struct mwifiex_private *priv, u8 **buffer) +{ + int retLen = 0; + struct mwifiex_ie_types_header ie_header; + + if (!buffer || !*buffer) + return 0; + + /* + * If there is a wps ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->wps_ie_len) { + mwifiex_dbg(priv->adapter, CMD, + "cmd: append wps ie %d to %p\n", + priv->wps_ie_len, *buffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = cpu_to_le16(TLV_TYPE_MGMT_IE); + ie_header.len = cpu_to_le16(priv->wps_ie_len); + memcpy(*buffer, &ie_header, sizeof(ie_header)); + *buffer += sizeof(ie_header); + retLen += sizeof(ie_header); + + memcpy(*buffer, priv->wps_ie, priv->wps_ie_len); + *buffer += priv->wps_ie_len; + retLen += priv->wps_ie_len; + + } + + kfree(priv->wps_ie); + priv->wps_ie_len = 0; + return retLen; +} + +/* + * This function appends a WAPI IE. + * + * This function is called from the network join command preparation routine. + * + * If the IE buffer has been setup by the application, this routine appends + * the buffer as a WAPI TLV type to the request. + */ +static int +mwifiex_cmd_append_wapi_ie(struct mwifiex_private *priv, u8 **buffer) +{ + int retLen = 0; + struct mwifiex_ie_types_header ie_header; + + /* Null Checks */ + if (buffer == NULL) + return 0; + if (*buffer == NULL) + return 0; + + /* + * If there is a wapi ie buffer setup, append it to the return + * parameter buffer pointer. + */ + if (priv->wapi_ie_len) { + mwifiex_dbg(priv->adapter, CMD, + "cmd: append wapi ie %d to %p\n", + priv->wapi_ie_len, *buffer); + + /* Wrap the generic IE buffer with a pass through TLV type */ + ie_header.type = cpu_to_le16(TLV_TYPE_WAPI_IE); + ie_header.len = cpu_to_le16(priv->wapi_ie_len); + memcpy(*buffer, &ie_header, sizeof(ie_header)); + + /* Increment the return size and the return buffer pointer + param */ + *buffer += sizeof(ie_header); + retLen += sizeof(ie_header); + + /* Copy the wapi IE buffer to the output buffer, advance + pointer */ + memcpy(*buffer, priv->wapi_ie, priv->wapi_ie_len); + + /* Increment the return size and the return buffer pointer + param */ + *buffer += priv->wapi_ie_len; + retLen += priv->wapi_ie_len; + + } + /* return the length appended to the buffer */ + return retLen; +} + +/* + * This function appends rsn ie tlv for wpa/wpa2 security modes. + * It is called from the network join command preparation routine. + */ +static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv, + u8 **buffer) +{ + struct mwifiex_ie_types_rsn_param_set *rsn_ie_tlv; + int rsn_ie_len; + + if (!buffer || !(*buffer)) + return 0; + + rsn_ie_tlv = (struct mwifiex_ie_types_rsn_param_set *) (*buffer); + rsn_ie_tlv->header.type = cpu_to_le16((u16) priv->wpa_ie[0]); + rsn_ie_tlv->header.type = cpu_to_le16( + le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF); + rsn_ie_tlv->header.len = cpu_to_le16((u16) priv->wpa_ie[1]); + rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len) + & 0x00FF); + if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2)) + memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2], + le16_to_cpu(rsn_ie_tlv->header.len)); + else + return -1; + + rsn_ie_len = sizeof(rsn_ie_tlv->header) + + le16_to_cpu(rsn_ie_tlv->header.len); + *buffer += rsn_ie_len; + + return rsn_ie_len; +} + +/* + * This function prepares command for association. + * + * This sets the following parameters - + * - Peer MAC address + * - Listen interval + * - Beacon interval + * - Capability information + * + * ...and the following TLVs, as required - + * - SSID TLV + * - PHY TLV + * - SS TLV + * - Rates TLV + * - Authentication TLV + * - Channel TLV + * - WPA/WPA2 IE + * - 11n TLV + * - Vendor specific TLV + * - WMM TLV + * - WAPI IE + * - Generic IE + * - TSF TLV + * + * Preparation also includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_bssdescriptor *bss_desc) +{ + struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate; + struct mwifiex_ie_types_ssid_param_set *ssid_tlv; + struct mwifiex_ie_types_phy_param_set *phy_tlv; + struct mwifiex_ie_types_ss_param_set *ss_tlv; + struct mwifiex_ie_types_rates_param_set *rates_tlv; + struct mwifiex_ie_types_auth_type *auth_tlv; + struct mwifiex_ie_types_chan_list_param_set *chan_tlv; + u8 rates[MWIFIEX_SUPPORTED_RATES]; + u32 rates_size; + u16 tmp_cap; + u8 *pos; + int rsn_ie_len = 0; + + pos = (u8 *) assoc; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE); + + /* Save so we know which BSS Desc to use in the response handler */ + priv->attempted_bss_desc = bss_desc; + + memcpy(assoc->peer_sta_addr, + bss_desc->mac_address, sizeof(assoc->peer_sta_addr)); + pos += sizeof(assoc->peer_sta_addr); + + /* Set the listen interval */ + assoc->listen_interval = cpu_to_le16(priv->listen_interval); + /* Set the beacon period */ + assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period); + + pos += sizeof(assoc->cap_info_bitmap); + pos += sizeof(assoc->listen_interval); + pos += sizeof(assoc->beacon_period); + pos += sizeof(assoc->dtim_period); + + ssid_tlv = (struct mwifiex_ie_types_ssid_param_set *) pos; + ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID); + ssid_tlv->header.len = cpu_to_le16((u16) bss_desc->ssid.ssid_len); + memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid, + le16_to_cpu(ssid_tlv->header.len)); + pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len); + + phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos; + phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); + phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); + memcpy(&phy_tlv->fh_ds.ds_param_set, + &bss_desc->phy_param_set.ds_param_set.current_chan, + sizeof(phy_tlv->fh_ds.ds_param_set)); + pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); + + ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos; + ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); + ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); + pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); + + /* Get the common rates supported between the driver and the BSS Desc */ + if (mwifiex_setup_rates_from_bssdesc + (priv, bss_desc, rates, &rates_size)) + return -1; + + /* Save the data rates into Current BSS state structure */ + priv->curr_bss_params.num_of_rates = rates_size; + memcpy(&priv->curr_bss_params.data_rates, rates, rates_size); + + /* Setup the Rates TLV in the association command */ + rates_tlv = (struct mwifiex_ie_types_rates_param_set *) pos; + rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); + rates_tlv->header.len = cpu_to_le16((u16) rates_size); + memcpy(rates_tlv->rates, rates, rates_size); + pos += sizeof(rates_tlv->header) + rates_size; + mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_CMD: rates size = %d\n", + rates_size); + + /* Add the Authentication type to be used for Auth frames */ + auth_tlv = (struct mwifiex_ie_types_auth_type *) pos; + auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type)); + if (priv->sec_info.wep_enabled) + auth_tlv->auth_type = cpu_to_le16( + (u16) priv->sec_info.authentication_mode); + else + auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM); + + pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len); + + if (IS_SUPPORT_MULTI_BANDS(priv->adapter) && + !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && + (!bss_desc->disable_11n) && + (priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN) && + (bss_desc->bcn_ht_cap) + ) + ) { + /* Append a channel TLV for the channel the attempted AP was + found on */ + chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; + chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); + + memset(chan_tlv->chan_scan_param, 0x00, + sizeof(struct mwifiex_chan_scan_param_set)); + chan_tlv->chan_scan_param[0].chan_number = + (bss_desc->phy_param_set.ds_param_set.current_chan); + mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Chan = %d\n", + chan_tlv->chan_scan_param[0].chan_number); + + chan_tlv->chan_scan_param[0].radio_type = + mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + + mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Band = %d\n", + chan_tlv->chan_scan_param[0].radio_type); + pos += sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + } + + if (!priv->wps.session_enable) { + if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) + rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); + + if (rsn_ie_len == -1) + return -1; + } + + if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && + (!bss_desc->disable_11n) && + (priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN)) + mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + !bss_desc->disable_11n && !bss_desc->disable_11ac && + priv->adapter->config_bands & BAND_AAC) + mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos); + + /* Append vendor specific IE TLV */ + mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ASSOC, &pos); + + mwifiex_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie, + bss_desc->bcn_ht_cap); + if (priv->sec_info.wapi_enabled && priv->wapi_ie_len) + mwifiex_cmd_append_wapi_ie(priv, &pos); + + if (priv->wps.session_enable && priv->wps_ie_len) + mwifiex_cmd_append_wps_ie(priv, &pos); + + mwifiex_cmd_append_generic_ie(priv, &pos); + + mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc); + + mwifiex_11h_process_join(priv, &pos, bss_desc); + + cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN); + + /* Set the Capability info at last */ + tmp_cap = bss_desc->cap_info_bitmap; + + if (priv->adapter->config_bands == BAND_B) + tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; + + tmp_cap &= CAPINFO_MASK; + mwifiex_dbg(priv->adapter, INFO, + "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", + tmp_cap, CAPINFO_MASK); + assoc->cap_info_bitmap = cpu_to_le16(tmp_cap); + + return 0; +} + +static const char *assoc_failure_reason_to_str(u16 cap_info) +{ + switch (cap_info) { + case CONNECT_ERR_AUTH_ERR_STA_FAILURE: + return "CONNECT_ERR_AUTH_ERR_STA_FAILURE"; + case CONNECT_ERR_AUTH_MSG_UNHANDLED: + return "CONNECT_ERR_AUTH_MSG_UNHANDLED"; + case CONNECT_ERR_ASSOC_ERR_TIMEOUT: + return "CONNECT_ERR_ASSOC_ERR_TIMEOUT"; + case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED: + return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED"; + case CONNECT_ERR_STA_FAILURE: + return "CONNECT_ERR_STA_FAILURE"; + } + + return "Unknown connect failure"; +} +/* + * 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 | + * | 0xFFFE(-2): Authentication unhandled message | + * | 0xFFFD(-3): Authentication refused | + * | 0xFFFC(-4): Timeout waiting for AP response | + * .------------------------------------------------------------. + * | status_code(t_u16): | + * | If cap_info is -1: | + * | An internal firmware failure prevented the | + * | command from being processed. The status_code | + * | will be set to 1. | + * | | + * | 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 | + * .------------------------------------------------------------. + * | 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). + */ +int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret = 0; + struct ieee_types_assoc_rsp *assoc_rsp; + struct mwifiex_bssdescriptor *bss_desc; + bool enable_data = true; + u16 cap_info, status_code, aid; + + assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; + + cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap); + status_code = le16_to_cpu(assoc_rsp->status_code); + aid = le16_to_cpu(assoc_rsp->a_id); + + if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) + dev_err(priv->adapter->dev, + "invalid AID value 0x%x; bits 15:14 not set\n", + aid); + + aid &= ~(BIT(15) | BIT(14)); + + priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, + sizeof(priv->assoc_rsp_buf)); + + memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); + + assoc_rsp->a_id = cpu_to_le16(aid); + + if (status_code) { + priv->adapter->dbg.num_cmd_assoc_failure++; + mwifiex_dbg(priv->adapter, ERROR, + "ASSOC_RESP: failed,\t" + "status code=%d err=%#x a_id=%#x\n", + status_code, cap_info, + le16_to_cpu(assoc_rsp->a_id)); + + mwifiex_dbg(priv->adapter, ERROR, "assoc failure: reason %s\n", + assoc_failure_reason_to_str(cap_info)); + if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) { + if (status_code == MWIFIEX_ASSOC_CMD_FAILURE_AUTH) { + ret = WLAN_STATUS_AUTH_TIMEOUT; + mwifiex_dbg(priv->adapter, ERROR, + "ASSOC_RESP: AUTH timeout\n"); + } else { + ret = WLAN_STATUS_UNSPECIFIED_FAILURE; + mwifiex_dbg(priv->adapter, ERROR, + "ASSOC_RESP: UNSPECIFIED failure\n"); + } + } else { + ret = status_code; + } + + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + priv->media_connected = true; + + priv->adapter->ps_state = PS_STATE_AWAKE; + priv->adapter->pps_uapsd_mode = false; + priv->adapter->tx_lock_flag = false; + + /* Set the attempted BSSID Index to current */ + bss_desc = priv->attempted_bss_desc; + + mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: %s\n", + bss_desc->ssid.ssid); + + /* Make a copy of current BSSID descriptor */ + memcpy(&priv->curr_bss_params.bss_descriptor, + bss_desc, sizeof(struct mwifiex_bssdescriptor)); + + /* Update curr_bss_params */ + priv->curr_bss_params.bss_descriptor.channel + = bss_desc->phy_param_set.ds_param_set.current_chan; + + priv->curr_bss_params.band = (u8) bss_desc->bss_band; + + if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) + priv->curr_bss_params.wmm_enabled = true; + else + priv->curr_bss_params.wmm_enabled = false; + + if ((priv->wmm_required || bss_desc->bcn_ht_cap) && + priv->curr_bss_params.wmm_enabled) + priv->wmm_enabled = true; + else + priv->wmm_enabled = false; + + priv->curr_bss_params.wmm_uapsd_enabled = false; + + if (priv->wmm_enabled) + priv->curr_bss_params.wmm_uapsd_enabled + = ((bss_desc->wmm_ie.qos_info_bitmap & + IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); + + mwifiex_dbg(priv->adapter, INFO, + "info: ASSOC_RESP: curr_pkt_filter is %#x\n", + priv->curr_pkt_filter); + if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) + priv->wpa_is_gtk_set = false; + + if (priv->wmm_enabled) { + /* Don't re-enable carrier until we get the WMM_GET_STATUS + event */ + enable_data = false; + } else { + /* Since WMM is not enabled, setup the queues with the + defaults */ + mwifiex_wmm_setup_queue_priorities(priv, NULL); + mwifiex_wmm_setup_ac_downgrade(priv); + } + + if (enable_data) + mwifiex_dbg(priv->adapter, INFO, + "info: post association, re-enabling data flow\n"); + + /* Reset SNR/NF/RSSI values */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->rxpd_rate = 0; + priv->rxpd_htinfo = 0; + + mwifiex_save_curr_bcn(priv); + + priv->adapter->dbg.num_cmd_assoc_success++; + + mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: associated\n"); + + /* Add the ra_list here for infra mode as there will be only 1 ra + always */ + mwifiex_ralist_add(priv, + priv->curr_bss_params.bss_descriptor.mac_address); + + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); + + if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) + priv->scan_block = true; + else + priv->port_open = true; + +done: + /* Need to indicate IOCTL complete */ + if (adapter->curr_cmd->wait_q_enabled) { + if (ret) + adapter->cmd_wait_q.status = -1; + else + adapter->cmd_wait_q.status = 0; + } + + return ret; +} + +/* + * This function prepares command for ad-hoc start. + * + * Driver will fill up SSID, BSS mode, IBSS parameters, physical + * parameters, probe delay, and capability information. Firmware + * will fill up beacon period, basic rates and operational rates. + * + * In addition, the following TLVs are added - + * - Channel TLV + * - Vendor specific IE + * - WPA/WPA2 IE + * - HT Capabilities IE + * - HT Information IE + * + * Preparation also includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +int +mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct cfg80211_ssid *req_ssid) +{ + int rsn_ie_len = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ad_hoc_start *adhoc_start = + &cmd->params.adhoc_start; + struct mwifiex_bssdescriptor *bss_desc; + u32 cmd_append_size = 0; + u32 i; + u16 tmp_cap; + struct mwifiex_ie_types_chan_list_param_set *chan_tlv; + u8 radio_type; + + struct mwifiex_ie_types_htcap *ht_cap; + struct mwifiex_ie_types_htinfo *ht_info; + u8 *pos = (u8 *) adhoc_start + + sizeof(struct host_cmd_ds_802_11_ad_hoc_start); + + if (!adapter) + return -1; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START); + + bss_desc = &priv->curr_bss_params.bss_descriptor; + priv->attempted_bss_desc = bss_desc; + + /* + * Fill in the parameters for 2 data structures: + * 1. struct host_cmd_ds_802_11_ad_hoc_start command + * 2. bss_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(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN); + + memcpy(adhoc_start->ssid, req_ssid->ssid, req_ssid->ssid_len); + + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: SSID = %s\n", + adhoc_start->ssid); + + memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN); + memcpy(bss_desc->ssid.ssid, req_ssid->ssid, req_ssid->ssid_len); + + bss_desc->ssid.ssid_len = req_ssid->ssid_len; + + /* Set the BSS mode */ + adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS; + bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; + adhoc_start->beacon_period = cpu_to_le16(priv->beacon_period); + bss_desc->beacon_period = priv->beacon_period; + + /* Set Physical param set */ +/* Parameter IE Id */ +#define DS_PARA_IE_ID 3 +/* Parameter IE length */ +#define DS_PARA_IE_LEN 1 + + adhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID; + adhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN; + + if (!mwifiex_get_cfp(priv, adapter->adhoc_start_band, + (u16) priv->adhoc_channel, 0)) { + struct mwifiex_chan_freq_power *cfp; + cfp = mwifiex_get_cfp(priv, adapter->adhoc_start_band, + FIRST_VALID_CHANNEL, 0); + if (cfp) + priv->adhoc_channel = (u8) cfp->channel; + } + + if (!priv->adhoc_channel) { + mwifiex_dbg(adapter, ERROR, + "ADHOC_S_CMD: adhoc_channel cannot be 0\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "info: ADHOC_S_CMD: creating ADHOC on channel %d\n", + priv->adhoc_channel); + + priv->curr_bss_params.bss_descriptor.channel = priv->adhoc_channel; + priv->curr_bss_params.band = adapter->adhoc_start_band; + + bss_desc->channel = priv->adhoc_channel; + adhoc_start->phy_param_set.ds_param_set.current_chan = + priv->adhoc_channel; + + memcpy(&bss_desc->phy_param_set, &adhoc_start->phy_param_set, + sizeof(union ieee_types_phy_param_set)); + + /* Set IBSS param set */ +/* IBSS parameter IE Id */ +#define IBSS_PARA_IE_ID 6 +/* IBSS parameter IE length */ +#define IBSS_PARA_IE_LEN 2 + + adhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID; + adhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN; + adhoc_start->ss_param_set.ibss_param_set.atim_window + = cpu_to_le16(priv->atim_window); + memcpy(&bss_desc->ss_param_set, &adhoc_start->ss_param_set, + sizeof(union ieee_types_ss_param_set)); + + /* Set Capability info */ + bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS; + tmp_cap = WLAN_CAPABILITY_IBSS; + + /* Set up privacy in bss_desc */ + if (priv->sec_info.encryption_mode) { + /* Ad-Hoc capability privacy on */ + mwifiex_dbg(adapter, INFO, + "info: ADHOC_S_CMD: wep_status set privacy to WEP\n"); + bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; + tmp_cap |= WLAN_CAPABILITY_PRIVACY; + } else { + mwifiex_dbg(adapter, INFO, + "info: ADHOC_S_CMD: wep_status NOT set,\t" + "setting privacy to ACCEPT ALL\n"); + bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; + } + + memset(adhoc_start->data_rate, 0, sizeof(adhoc_start->data_rate)); + mwifiex_get_active_data_rates(priv, adhoc_start->data_rate); + if ((adapter->adhoc_start_band & BAND_G) && + (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, false)) { + mwifiex_dbg(adapter, ERROR, + "ADHOC_S_CMD: G Protection config failed\n"); + return -1; + } + } + /* Find the last non zero */ + for (i = 0; i < sizeof(adhoc_start->data_rate); i++) + if (!adhoc_start->data_rate[i]) + break; + + priv->curr_bss_params.num_of_rates = i; + + /* Copy the ad-hoc creating rates into Current BSS rate structure */ + memcpy(&priv->curr_bss_params.data_rates, + &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates); + + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: rates=%4ph\n", + adhoc_start->data_rate); + + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n"); + + if (IS_SUPPORT_MULTI_BANDS(adapter)) { + /* Append a channel TLV */ + chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; + chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); + + memset(chan_tlv->chan_scan_param, 0x00, + sizeof(struct mwifiex_chan_scan_param_set)); + chan_tlv->chan_scan_param[0].chan_number = + (u8) priv->curr_bss_params.bss_descriptor.channel; + + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Chan = %d\n", + chan_tlv->chan_scan_param[0].chan_number); + + chan_tlv->chan_scan_param[0].radio_type + = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + if (adapter->adhoc_start_band & BAND_GN || + adapter->adhoc_start_band & BAND_AN) { + if (adapter->sec_chan_offset == + IEEE80211_HT_PARAM_CHA_SEC_ABOVE) + chan_tlv->chan_scan_param[0].radio_type |= + (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4); + else if (adapter->sec_chan_offset == + IEEE80211_HT_PARAM_CHA_SEC_BELOW) + chan_tlv->chan_scan_param[0].radio_type |= + (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4); + } + mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Band = %d\n", + chan_tlv->chan_scan_param[0].radio_type); + pos += sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + cmd_append_size += + sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + } + + /* Append vendor specific IE TLV */ + cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, + MWIFIEX_VSIE_MASK_ADHOC, &pos); + + if (priv->sec_info.wpa_enabled) { + rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); + if (rsn_ie_len == -1) + return -1; + cmd_append_size += rsn_ie_len; + } + + if (adapter->adhoc_11n_enabled) { + /* Fill HT CAPABILITY */ + ht_cap = (struct mwifiex_ie_types_htcap *) pos; + memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); + ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + radio_type = mwifiex_band_to_radio_type( + priv->adapter->config_bands); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); + + if (adapter->sec_chan_offset == + IEEE80211_HT_PARAM_CHA_SEC_NONE) { + u16 tmp_ht_cap; + + tmp_ht_cap = le16_to_cpu(ht_cap->ht_cap.cap_info); + tmp_ht_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + tmp_ht_cap &= ~IEEE80211_HT_CAP_SGI_40; + ht_cap->ht_cap.cap_info = cpu_to_le16(tmp_ht_cap); + } + + pos += sizeof(struct mwifiex_ie_types_htcap); + cmd_append_size += sizeof(struct mwifiex_ie_types_htcap); + + /* Fill HT INFORMATION */ + ht_info = (struct mwifiex_ie_types_htinfo *) pos; + memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo)); + ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION); + ht_info->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_operation)); + + ht_info->ht_oper.primary_chan = + (u8) priv->curr_bss_params.bss_descriptor.channel; + if (adapter->sec_chan_offset) { + ht_info->ht_oper.ht_param = adapter->sec_chan_offset; + ht_info->ht_oper.ht_param |= + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; + } + ht_info->ht_oper.operation_mode = + cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + ht_info->ht_oper.basic_set[0] = 0xff; + pos += sizeof(struct mwifiex_ie_types_htinfo); + cmd_append_size += + sizeof(struct mwifiex_ie_types_htinfo); + } + + cmd->size = + cpu_to_le16((u16)(sizeof(struct host_cmd_ds_802_11_ad_hoc_start) + + S_DS_GEN + cmd_append_size)); + + if (adapter->adhoc_start_band == BAND_B) + tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; + else + tmp_cap |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + + adhoc_start->cap_info_bitmap = cpu_to_le16(tmp_cap); + + return 0; +} + +/* + * This function prepares command for ad-hoc join. + * + * Most of the parameters are set up by copying from the target BSS descriptor + * from the scan response. + * + * In addition, the following TLVs are added - + * - Channel TLV + * - Vendor specific IE + * - WPA/WPA2 IE + * - 11n IE + * + * Preparation also includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +int +mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_bssdescriptor *bss_desc) +{ + int rsn_ie_len = 0; + struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join = + &cmd->params.adhoc_join; + struct mwifiex_ie_types_chan_list_param_set *chan_tlv; + u32 cmd_append_size = 0; + u16 tmp_cap; + u32 i, rates_size = 0; + u16 curr_pkt_filter; + u8 *pos = + (u8 *) adhoc_join + + sizeof(struct host_cmd_ds_802_11_ad_hoc_join); + +/* Use G protection */ +#define USE_G_PROTECTION 0x02 + if (bss_desc->erp_flags & USE_G_PROTECTION) { + curr_pkt_filter = + priv-> + curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &curr_pkt_filter, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "ADHOC_J_CMD: G Protection config failed\n"); + return -1; + } + } + + priv->attempted_bss_desc = bss_desc; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN); + + adhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS; + + adhoc_join->bss_descriptor.beacon_period + = cpu_to_le16(bss_desc->beacon_period); + + memcpy(&adhoc_join->bss_descriptor.bssid, + &bss_desc->mac_address, ETH_ALEN); + + memcpy(&adhoc_join->bss_descriptor.ssid, + &bss_desc->ssid.ssid, bss_desc->ssid.ssid_len); + + memcpy(&adhoc_join->bss_descriptor.phy_param_set, + &bss_desc->phy_param_set, + sizeof(union ieee_types_phy_param_set)); + + memcpy(&adhoc_join->bss_descriptor.ss_param_set, + &bss_desc->ss_param_set, sizeof(union ieee_types_ss_param_set)); + + tmp_cap = bss_desc->cap_info_bitmap; + + tmp_cap &= CAPINFO_MASK; + + mwifiex_dbg(priv->adapter, INFO, + "info: ADHOC_J_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", + tmp_cap, CAPINFO_MASK); + + /* Information on BSSID descriptor passed to FW */ + mwifiex_dbg(priv->adapter, INFO, + "info: ADHOC_J_CMD: BSSID=%pM, SSID='%s'\n", + adhoc_join->bss_descriptor.bssid, + adhoc_join->bss_descriptor.ssid); + + for (i = 0; i < MWIFIEX_SUPPORTED_RATES && + bss_desc->supported_rates[i]; i++) + ; + rates_size = i; + + /* Copy Data Rates from the Rates recorded in scan response */ + memset(adhoc_join->bss_descriptor.data_rates, 0, + sizeof(adhoc_join->bss_descriptor.data_rates)); + memcpy(adhoc_join->bss_descriptor.data_rates, + bss_desc->supported_rates, rates_size); + + /* Copy the adhoc join rates into Current BSS state structure */ + priv->curr_bss_params.num_of_rates = rates_size; + memcpy(&priv->curr_bss_params.data_rates, bss_desc->supported_rates, + rates_size); + + /* Copy the channel information */ + priv->curr_bss_params.bss_descriptor.channel = bss_desc->channel; + priv->curr_bss_params.band = (u8) bss_desc->bss_band; + + if (priv->sec_info.wep_enabled || priv->sec_info.wpa_enabled) + tmp_cap |= WLAN_CAPABILITY_PRIVACY; + + if (IS_SUPPORT_MULTI_BANDS(priv->adapter)) { + /* Append a channel TLV */ + chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; + chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + chan_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); + + memset(chan_tlv->chan_scan_param, 0x00, + sizeof(struct mwifiex_chan_scan_param_set)); + chan_tlv->chan_scan_param[0].chan_number = + (bss_desc->phy_param_set.ds_param_set.current_chan); + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Chan=%d\n", + chan_tlv->chan_scan_param[0].chan_number); + + chan_tlv->chan_scan_param[0].radio_type = + mwifiex_band_to_radio_type((u8) bss_desc->bss_band); + + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Band=%d\n", + chan_tlv->chan_scan_param[0].radio_type); + pos += sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + cmd_append_size += sizeof(chan_tlv->header) + + sizeof(struct mwifiex_chan_scan_param_set); + } + + if (priv->sec_info.wpa_enabled) + rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); + if (rsn_ie_len == -1) + return -1; + cmd_append_size += rsn_ie_len; + + if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) + cmd_append_size += mwifiex_cmd_append_11n_tlv(priv, + bss_desc, &pos); + + /* Append vendor specific IE TLV */ + cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, + MWIFIEX_VSIE_MASK_ADHOC, &pos); + + cmd->size = cpu_to_le16 + ((u16) (sizeof(struct host_cmd_ds_802_11_ad_hoc_join) + + S_DS_GEN + cmd_append_size)); + + adhoc_join->bss_descriptor.cap_info_bitmap = cpu_to_le16(tmp_cap); + + return 0; +} + +/* + * This function handles the command response of ad-hoc start and + * ad-hoc join. + * + * The function generates a device-connected event to notify + * the applications, in case of successful ad-hoc start/join, and + * saves the beacon buffer. + */ +int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result; + struct mwifiex_bssdescriptor *bss_desc; + u16 reason_code; + + adhoc_result = &resp->params.adhoc_result; + + bss_desc = priv->attempted_bss_desc; + + /* Join result code 0 --> SUCCESS */ + reason_code = le16_to_cpu(resp->result); + if (reason_code) { + mwifiex_dbg(priv->adapter, ERROR, "ADHOC_RESP: failed\n"); + if (priv->media_connected) + mwifiex_reset_connect_state(priv, reason_code); + + memset(&priv->curr_bss_params.bss_descriptor, + 0x00, sizeof(struct mwifiex_bssdescriptor)); + + ret = -1; + goto done; + } + + /* Send a Media Connected event, according to the Spec */ + priv->media_connected = true; + + if (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_AD_HOC_START) { + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_S_RESP %s\n", + bss_desc->ssid.ssid); + + /* Update the created network descriptor with the new BSSID */ + memcpy(bss_desc->mac_address, + adhoc_result->bssid, ETH_ALEN); + + priv->adhoc_state = ADHOC_STARTED; + } else { + /* + * Now the join cmd should be successful. + * If BSSID has changed use SSID to compare instead of BSSID + */ + mwifiex_dbg(priv->adapter, INFO, + "info: ADHOC_J_RESP %s\n", + bss_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(&priv->curr_bss_params.bss_descriptor, + bss_desc, sizeof(struct mwifiex_bssdescriptor)); + + priv->adhoc_state = ADHOC_JOINED; + } + + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: channel = %d\n", + priv->adhoc_channel); + mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: BSSID = %pM\n", + priv->curr_bss_params.bss_descriptor.mac_address); + + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); + + mwifiex_save_curr_bcn(priv); + +done: + /* Need to indicate IOCTL complete */ + if (adapter->curr_cmd->wait_q_enabled) { + if (ret) + adapter->cmd_wait_q.status = -1; + else + adapter->cmd_wait_q.status = 0; + + } + + return ret; +} + +/* + * This function associates to a specific BSS discovered in a scan. + * + * It clears any past association response stored for application + * retrieval and calls the command preparation routine to send the + * command to firmware. + */ +int mwifiex_associate(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + /* Return error if the adapter is not STA role or table entry + * is not marked as infra. + */ + if ((GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) || + (bss_desc->bss_mode != NL80211_IFTYPE_STATION)) + return -1; + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + !bss_desc->disable_11n && !bss_desc->disable_11ac && + priv->adapter->config_bands & BAND_AAC) + mwifiex_set_11ac_ba_params(priv); + else + mwifiex_set_ba_params(priv); + + /* Clear any past association response stored for application + retrieval */ + priv->assoc_rsp_size = 0; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_ASSOCIATE, + HostCmd_ACT_GEN_SET, 0, bss_desc, true); +} + +/* + * This function starts an ad-hoc network. + * + * It calls the command preparation routine to send the command to firmware. + */ +int +mwifiex_adhoc_start(struct mwifiex_private *priv, + struct cfg80211_ssid *adhoc_ssid) +{ + mwifiex_dbg(priv->adapter, INFO, "info: Adhoc Channel = %d\n", + priv->adhoc_channel); + mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.channel = %d\n", + priv->curr_bss_params.bss_descriptor.channel); + mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.band = %d\n", + priv->curr_bss_params.band); + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + priv->adapter->config_bands & BAND_AAC) + mwifiex_set_11ac_ba_params(priv); + else + mwifiex_set_ba_params(priv); + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_START, + HostCmd_ACT_GEN_SET, 0, adhoc_ssid, true); +} + +/* + * This function joins an ad-hoc network found in a previous scan. + * + * It calls the command preparation routine to send the command to firmware, + * if already not connected to the requested SSID. + */ +int mwifiex_adhoc_join(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + mwifiex_dbg(priv->adapter, INFO, + "info: adhoc join: curr_bss ssid =%s\n", + priv->curr_bss_params.bss_descriptor.ssid.ssid); + mwifiex_dbg(priv->adapter, INFO, + "info: adhoc join: curr_bss ssid_len =%u\n", + priv->curr_bss_params.bss_descriptor.ssid.ssid_len); + mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid =%s\n", + bss_desc->ssid.ssid); + mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid_len =%u\n", + bss_desc->ssid.ssid_len); + + /* Check if the requested SSID is already joined */ + if (priv->curr_bss_params.bss_descriptor.ssid.ssid_len && + !mwifiex_ssid_cmp(&bss_desc->ssid, + &priv->curr_bss_params.bss_descriptor.ssid) && + (priv->curr_bss_params.bss_descriptor.bss_mode == + NL80211_IFTYPE_ADHOC)) { + mwifiex_dbg(priv->adapter, INFO, + "info: ADHOC_J_CMD: new ad-hoc SSID\t" + "is the same as current; not attempting to re-join\n"); + return -1; + } + + if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && + !bss_desc->disable_11n && !bss_desc->disable_11ac && + priv->adapter->config_bands & BAND_AAC) + mwifiex_set_11ac_ba_params(priv); + else + mwifiex_set_ba_params(priv); + + mwifiex_dbg(priv->adapter, INFO, + "info: curr_bss_params.channel = %d\n", + priv->curr_bss_params.bss_descriptor.channel); + mwifiex_dbg(priv->adapter, INFO, + "info: curr_bss_params.band = %c\n", + priv->curr_bss_params.band); + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_JOIN, + HostCmd_ACT_GEN_SET, 0, bss_desc, true); +} + +/* + * This function deauthenticates/disconnects from infra network by sending + * deauthentication request. + */ +static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac) +{ + u8 mac_address[ETH_ALEN]; + int ret; + + if (!mac || is_zero_ether_addr(mac)) + memcpy(mac_address, + priv->curr_bss_params.bss_descriptor.mac_address, + ETH_ALEN); + else + memcpy(mac_address, mac, ETH_ALEN); + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, 0, mac_address, true); + + return ret; +} + +/* + * This function deauthenticates/disconnects from a BSS. + * + * In case of infra made, it sends deauthentication request, and + * in case of ad-hoc mode, a stop network request is sent to the firmware. + * In AP mode, a command to stop bss is sent to firmware. + */ +int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac) +{ + int ret = 0; + + if (!priv->media_connected) + return 0; + + switch (priv->bss_mode) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: + ret = mwifiex_deauthenticate_infra(priv, mac); + if (ret) + cfg80211_disconnected(priv->netdev, 0, NULL, 0, + true, GFP_KERNEL); + break; + case NL80211_IFTYPE_ADHOC: + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true); + case NL80211_IFTYPE_AP: + return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, + HostCmd_ACT_GEN_SET, 0, NULL, true); + default: + break; + } + + return ret; +} + +/* This function deauthenticates/disconnects from all BSS. */ +void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + mwifiex_deauthenticate(priv, NULL); + } +} +EXPORT_SYMBOL_GPL(mwifiex_deauthenticate_all); + +/* + * This function converts band to radio type used in channel TLV. + */ +u8 +mwifiex_band_to_radio_type(u8 band) +{ + switch (band) { + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + case BAND_A | BAND_AN | BAND_AAC: + return HostCmd_SCAN_RADIO_TYPE_A; + case BAND_B: + case BAND_G: + case BAND_B | BAND_G: + default: + return HostCmd_SCAN_RADIO_TYPE_BG; + } +} diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c new file mode 100644 index 000000000000..969ca1e1f3e9 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -0,0 +1,1552 @@ +/* + * Marvell Wireless LAN device driver: major functions + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "main.h" +#include "wmm.h" +#include "cfg80211.h" +#include "11n.h" + +#define VERSION "1.0" + +static unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK; +module_param(debug_mask, uint, 0); +MODULE_PARM_DESC(debug_mask, "bitmap for debug flags"); + +const char driver_version[] = "mwifiex " VERSION " (%s) "; +static char *cal_data_cfg; +module_param(cal_data_cfg, charp, 0); + +static unsigned short driver_mode; +module_param(driver_mode, ushort, 0); +MODULE_PARM_DESC(driver_mode, + "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7"); + +/* + * This function registers the device and performs all the necessary + * initializations. + * + * The following initialization operations are performed - + * - Allocate adapter structure + * - Save interface specific operations table in adapter + * - Call interface specific initialization routine + * - Allocate private structures + * - Set default adapter structure parameters + * - Initialize locks + * + * In case of any errors during inittialization, this function also ensures + * proper cleanup before exiting. + */ +static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, + void **padapter) +{ + struct mwifiex_adapter *adapter; + int i; + + adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL); + if (!adapter) + return -ENOMEM; + + *padapter = adapter; + adapter->card = card; + + /* Save interface specific operations in adapter */ + memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops)); + adapter->debug_mask = debug_mask; + + /* card specific initialization has been deferred until now .. */ + if (adapter->if_ops.init_if) + if (adapter->if_ops.init_if(adapter)) + goto error; + + adapter->priv_num = 0; + + for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { + /* Allocate memory for private structure */ + adapter->priv[i] = + kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL); + if (!adapter->priv[i]) + goto error; + + adapter->priv[i]->adapter = adapter; + adapter->priv_num++; + } + mwifiex_init_lock_list(adapter); + + setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func, + (unsigned long)adapter); + + return 0; + +error: + mwifiex_dbg(adapter, ERROR, + "info: leave mwifiex_register with error\n"); + + for (i = 0; i < adapter->priv_num; i++) + kfree(adapter->priv[i]); + + kfree(adapter); + + return -1; +} + +/* + * This function unregisters the device and performs all the necessary + * cleanups. + * + * The following cleanup operations are performed - + * - Free the timers + * - Free beacon buffers + * - Free private structures + * - Free adapter structure + */ +static int mwifiex_unregister(struct mwifiex_adapter *adapter) +{ + s32 i; + + if (adapter->if_ops.cleanup_if) + adapter->if_ops.cleanup_if(adapter); + + del_timer_sync(&adapter->cmd_timer); + + /* Free private structures */ + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + mwifiex_free_curr_bcn(adapter->priv[i]); + kfree(adapter->priv[i]); + } + } + + vfree(adapter->chan_stats); + kfree(adapter); + return 0; +} + +void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + if (adapter->mwifiex_processing) { + adapter->more_task_flag = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } else { + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + queue_work(adapter->workqueue, &adapter->main_work); + } +} +EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); + +static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + if (adapter->rx_processing) { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } else { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + queue_work(adapter->rx_workqueue, &adapter->rx_work); + } +} + +static int mwifiex_process_rx(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + struct sk_buff *skb; + struct mwifiex_rxinfo *rx_info; + + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + if (adapter->rx_processing || adapter->rx_locked) { + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + goto exit_rx_proc; + } else { + adapter->rx_processing = true; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + } + + /* Check for Rx data */ + while ((skb = skb_dequeue(&adapter->rx_data_q))) { + atomic_dec(&adapter->rx_pending); + if ((adapter->delay_main_work || + adapter->iface_type == MWIFIEX_USB) && + (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) { + if (adapter->if_ops.submit_rem_rx_urbs) + adapter->if_ops.submit_rem_rx_urbs(adapter); + adapter->delay_main_work = false; + mwifiex_queue_main_work(adapter); + } + rx_info = MWIFIEX_SKB_RXCB(skb); + if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) { + if (adapter->if_ops.deaggr_pkt) + adapter->if_ops.deaggr_pkt(adapter, skb); + dev_kfree_skb_any(skb); + } else { + mwifiex_handle_rx_packet(adapter, skb); + } + } + spin_lock_irqsave(&adapter->rx_proc_lock, flags); + adapter->rx_processing = false; + spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); + +exit_rx_proc: + return 0; +} + +/* + * The main process. + * + * This function is the main procedure of the driver and handles various driver + * operations. It runs in a loop and provides the core functionalities. + * + * The main responsibilities of this function are - + * - Ensure concurrency control + * - Handle pending interrupts and call interrupt handlers + * - Wake up the card if required + * - Handle command responses and call response handlers + * - Handle events and call event handlers + * - Execute pending commands + * - Transmit pending data packets + */ +int mwifiex_main_process(struct mwifiex_adapter *adapter) +{ + int ret = 0; + unsigned long flags; + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + + /* Check if already processing */ + if (adapter->mwifiex_processing || adapter->main_locked) { + adapter->more_task_flag = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + goto exit_main_proc; + } else { + adapter->mwifiex_processing = true; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + } +process_start: + do { + if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) || + (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)) + break; + + /* For non-USB interfaces, If we process interrupts first, it + * would increase RX pending even further. Avoid this by + * checking if rx_pending has crossed high threshold and + * schedule rx work queue and then process interrupts. + * For USB interface, there are no interrupts. We already have + * HIGH_RX_PENDING check in usb.c + */ + if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING && + adapter->iface_type != MWIFIEX_USB) { + adapter->delay_main_work = true; + mwifiex_queue_rx_work(adapter); + break; + } + + /* Handle pending interrupt if any */ + if (adapter->int_status) { + if (adapter->hs_activated) + mwifiex_process_hs_config(adapter); + if (adapter->if_ops.process_int_status) + adapter->if_ops.process_int_status(adapter); + } + + if (adapter->rx_work_enabled && adapter->data_received) + mwifiex_queue_rx_work(adapter); + + /* Need to wake up the card ? */ + if ((adapter->ps_state == PS_STATE_SLEEP) && + (adapter->pm_wakeup_card_req && + !adapter->pm_wakeup_fw_try) && + (is_command_pending(adapter) || + !skb_queue_empty(&adapter->tx_data_q) || + !mwifiex_bypass_txlist_empty(adapter) || + !mwifiex_wmm_lists_empty(adapter))) { + adapter->pm_wakeup_fw_try = true; + mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3)); + adapter->if_ops.wakeup(adapter); + continue; + } + + if (IS_CARD_RX_RCVD(adapter)) { + adapter->data_received = false; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + if (adapter->ps_state == PS_STATE_SLEEP) + adapter->ps_state = PS_STATE_AWAKE; + } else { + /* We have tried to wakeup the card already */ + if (adapter->pm_wakeup_fw_try) + break; + if (adapter->ps_state != PS_STATE_AWAKE) + break; + if (adapter->tx_lock_flag) { + if (adapter->iface_type == MWIFIEX_USB) { + if (!adapter->usb_mc_setup) + break; + } else + break; + } + + if ((!adapter->scan_chan_gap_enabled && + adapter->scan_processing) || adapter->data_sent || + mwifiex_is_tdls_chan_switching + (mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_STA)) || + (mwifiex_wmm_lists_empty(adapter) && + mwifiex_bypass_txlist_empty(adapter) && + skb_queue_empty(&adapter->tx_data_q))) { + if (adapter->cmd_sent || adapter->curr_cmd || + !mwifiex_is_send_cmd_allowed + (mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_STA)) || + (!is_command_pending(adapter))) + break; + } + } + + /* Check for event */ + if (adapter->event_received) { + adapter->event_received = false; + mwifiex_process_event(adapter); + } + + /* Check for Cmd Resp */ + if (adapter->cmd_resp_received) { + adapter->cmd_resp_received = false; + mwifiex_process_cmdresp(adapter); + + /* call mwifiex back when init_fw is done */ + if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) { + adapter->hw_status = MWIFIEX_HW_STATUS_READY; + mwifiex_init_fw_complete(adapter); + } + } + + /* Check if we need to confirm Sleep Request + received previously */ + if (adapter->ps_state == PS_STATE_PRE_SLEEP) { + if (!adapter->cmd_sent && !adapter->curr_cmd) + mwifiex_check_ps_cond(adapter); + } + + /* * The ps_state may have been changed during processing of + * Sleep Request event. + */ + if ((adapter->ps_state == PS_STATE_SLEEP) || + (adapter->ps_state == PS_STATE_PRE_SLEEP) || + (adapter->ps_state == PS_STATE_SLEEP_CFM)) { + continue; + } + + if (adapter->tx_lock_flag) { + if (adapter->iface_type == MWIFIEX_USB) { + if (!adapter->usb_mc_setup) + continue; + } else + continue; + } + + if (!adapter->cmd_sent && !adapter->curr_cmd && + mwifiex_is_send_cmd_allowed + (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { + if (mwifiex_exec_next_cmd(adapter) == -1) { + ret = -1; + break; + } + } + + /** If USB Multi channel setup ongoing, + * wait for ready to tx data. + */ + if (adapter->iface_type == MWIFIEX_USB && + adapter->usb_mc_setup) + continue; + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && + !adapter->data_sent && + !skb_queue_empty(&adapter->tx_data_q)) { + mwifiex_process_tx_queue(adapter); + if (adapter->hs_activated) { + adapter->is_hs_configured = false; + mwifiex_hs_activated_event + (mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), + false); + } + } + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && + !adapter->data_sent && + !mwifiex_bypass_txlist_empty(adapter) && + !mwifiex_is_tdls_chan_switching + (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { + mwifiex_process_bypass_tx(adapter); + if (adapter->hs_activated) { + adapter->is_hs_configured = false; + mwifiex_hs_activated_event + (mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), + false); + } + } + + if ((adapter->scan_chan_gap_enabled || + !adapter->scan_processing) && + !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) && + !mwifiex_is_tdls_chan_switching + (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { + mwifiex_wmm_process_tx(adapter); + if (adapter->hs_activated) { + adapter->is_hs_configured = false; + mwifiex_hs_activated_event + (mwifiex_get_priv + (adapter, MWIFIEX_BSS_ROLE_ANY), + false); + } + } + + if (adapter->delay_null_pkt && !adapter->cmd_sent && + !adapter->curr_cmd && !is_command_pending(adapter) && + (mwifiex_wmm_lists_empty(adapter) && + mwifiex_bypass_txlist_empty(adapter) && + skb_queue_empty(&adapter->tx_data_q))) { + if (!mwifiex_send_null_packet + (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), + MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) { + adapter->delay_null_pkt = false; + adapter->ps_state = PS_STATE_SLEEP; + } + break; + } + } while (true); + + spin_lock_irqsave(&adapter->main_proc_lock, flags); + if (adapter->more_task_flag) { + adapter->more_task_flag = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + goto process_start; + } + adapter->mwifiex_processing = false; + spin_unlock_irqrestore(&adapter->main_proc_lock, flags); + +exit_main_proc: + if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) + mwifiex_shutdown_drv(adapter); + return ret; +} +EXPORT_SYMBOL_GPL(mwifiex_main_process); + +/* + * This function frees the adapter structure. + * + * Additionally, this closes the netlink socket, frees the timers + * and private structures. + */ +static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) +{ + if (!adapter) { + pr_err("%s: adapter is NULL\n", __func__); + return; + } + + mwifiex_unregister(adapter); + pr_debug("info: %s: free adapter\n", __func__); +} + +/* + * This function cancels all works in the queue and destroys + * the main workqueue. + */ +static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) +{ + flush_workqueue(adapter->workqueue); + destroy_workqueue(adapter->workqueue); + adapter->workqueue = NULL; + + if (adapter->rx_workqueue) { + flush_workqueue(adapter->rx_workqueue); + destroy_workqueue(adapter->rx_workqueue); + adapter->rx_workqueue = NULL; + } +} + +/* + * This function gets firmware and initializes it. + * + * The main initialization steps followed are - + * - Download the correct firmware to card + * - Issue the init commands to firmware + */ +static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) +{ + int ret; + char fmt[64]; + struct mwifiex_private *priv; + struct mwifiex_adapter *adapter = context; + struct mwifiex_fw_image fw; + struct semaphore *sem = adapter->card_sem; + bool init_failed = false; + struct wireless_dev *wdev; + + if (!firmware) { + mwifiex_dbg(adapter, ERROR, + "Failed to get firmware %s\n", adapter->fw_name); + goto err_dnld_fw; + } + + memset(&fw, 0, sizeof(struct mwifiex_fw_image)); + adapter->firmware = firmware; + fw.fw_buf = (u8 *) adapter->firmware->data; + fw.fw_len = adapter->firmware->size; + + if (adapter->if_ops.dnld_fw) + ret = adapter->if_ops.dnld_fw(adapter, &fw); + else + ret = mwifiex_dnld_fw(adapter, &fw); + if (ret == -1) + goto err_dnld_fw; + + mwifiex_dbg(adapter, MSG, "WLAN FW is active\n"); + + if (cal_data_cfg) { + if ((request_firmware(&adapter->cal_data, cal_data_cfg, + adapter->dev)) < 0) + mwifiex_dbg(adapter, ERROR, + "Cal data request_firmware() failed\n"); + } + + /* enable host interrupt after fw dnld is successful */ + if (adapter->if_ops.enable_int) { + if (adapter->if_ops.enable_int(adapter)) + goto err_dnld_fw; + } + + adapter->init_wait_q_woken = false; + ret = mwifiex_init_fw(adapter); + if (ret == -1) { + goto err_init_fw; + } else if (!ret) { + adapter->hw_status = MWIFIEX_HW_STATUS_READY; + goto done; + } + /* Wait for mwifiex_init to complete */ + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) + goto err_init_fw; + + priv = adapter->priv[MWIFIEX_BSS_ROLE_STA]; + if (mwifiex_register_cfg80211(adapter)) { + mwifiex_dbg(adapter, ERROR, + "cannot register with cfg80211\n"); + goto err_init_fw; + } + + if (mwifiex_init_channel_scan_gap(adapter)) { + mwifiex_dbg(adapter, ERROR, + "could not init channel stats table\n"); + goto err_init_fw; + } + + if (driver_mode) { + driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK; + driver_mode |= MWIFIEX_DRIVER_MODE_STA; + } + + rtnl_lock(); + /* Create station interface by default */ + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM, + NL80211_IFTYPE_STATION, NULL, NULL); + if (IS_ERR(wdev)) { + mwifiex_dbg(adapter, ERROR, + "cannot create default STA interface\n"); + rtnl_unlock(); + goto err_add_intf; + } + + if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) { + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM, + NL80211_IFTYPE_AP, NULL, NULL); + if (IS_ERR(wdev)) { + mwifiex_dbg(adapter, ERROR, + "cannot create AP interface\n"); + rtnl_unlock(); + goto err_add_intf; + } + } + + if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) { + wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM, + NL80211_IFTYPE_P2P_CLIENT, NULL, + NULL); + if (IS_ERR(wdev)) { + mwifiex_dbg(adapter, ERROR, + "cannot create p2p client interface\n"); + rtnl_unlock(); + goto err_add_intf; + } + } + rtnl_unlock(); + + mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); + mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt); + goto done; + +err_add_intf: + wiphy_unregister(adapter->wiphy); + wiphy_free(adapter->wiphy); +err_init_fw: + if (adapter->if_ops.disable_int) + adapter->if_ops.disable_int(adapter); +err_dnld_fw: + mwifiex_dbg(adapter, ERROR, + "info: %s: unregister device\n", __func__); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { + pr_debug("info: %s: shutdown mwifiex\n", __func__); + adapter->init_wait_q_woken = false; + + if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + } + adapter->surprise_removed = true; + mwifiex_terminate_workqueue(adapter); + init_failed = true; +done: + if (adapter->cal_data) { + release_firmware(adapter->cal_data); + adapter->cal_data = NULL; + } + if (adapter->firmware) { + release_firmware(adapter->firmware); + adapter->firmware = NULL; + } + if (init_failed) + mwifiex_free_adapter(adapter); + up(sem); + return; +} + +/* + * This function initializes the hardware and gets firmware. + */ +static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) +{ + int ret; + + ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name, + adapter->dev, GFP_KERNEL, adapter, + mwifiex_fw_dpc); + if (ret < 0) + mwifiex_dbg(adapter, ERROR, + "request_firmware_nowait error %d\n", ret); + return ret; +} + +/* + * CFG802.11 network device handler for open. + * + * Starts the data queue. + */ +static int +mwifiex_open(struct net_device *dev) +{ + netif_carrier_off(dev); + + return 0; +} + +/* + * CFG802.11 network device handler for close. + */ +static int +mwifiex_close(struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + if (priv->scan_request) { + mwifiex_dbg(priv->adapter, INFO, + "aborting scan on ndo_stop\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + priv->scan_aborting = true; + } + + return 0; +} + +static bool +mwifiex_bypass_tx_queue(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; + + if (ntohs(eth_hdr->h_proto) == ETH_P_PAE || + mwifiex_is_skb_mgmt_frame(skb) || + (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) { + mwifiex_dbg(priv->adapter, DATA, + "bypass txqueue; eth type %#x, mgmt %d\n", + ntohs(eth_hdr->h_proto), + mwifiex_is_skb_mgmt_frame(skb)); + return true; + } + + return false; +} +/* + * Add buffer into wmm tx queue and queue work to transmit it. + */ +int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb) +{ + struct netdev_queue *txq; + int index = mwifiex_1d_to_wmm_queue[skb->priority]; + + if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) { + txq = netdev_get_tx_queue(priv->netdev, index); + if (!netif_tx_queue_stopped(txq)) { + netif_tx_stop_queue(txq); + mwifiex_dbg(priv->adapter, DATA, + "stop queue: %d\n", index); + } + } + + if (mwifiex_bypass_tx_queue(priv, skb)) { + atomic_inc(&priv->adapter->tx_pending); + atomic_inc(&priv->adapter->bypass_tx_pending); + mwifiex_wmm_add_buf_bypass_txqueue(priv, skb); + } else { + atomic_inc(&priv->adapter->tx_pending); + mwifiex_wmm_add_buf_txqueue(priv, skb); + } + + mwifiex_queue_main_work(priv->adapter); + + return 0; +} + +struct sk_buff * +mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, + struct sk_buff *skb, u8 flag, u64 *cookie) +{ + struct sk_buff *orig_skb = skb; + struct mwifiex_txinfo *tx_info, *orig_tx_info; + + skb = skb_clone(skb, GFP_ATOMIC); + if (skb) { + unsigned long flags; + int id; + + spin_lock_irqsave(&priv->ack_status_lock, flags); + id = idr_alloc(&priv->ack_status_frames, orig_skb, + 1, 0xff, GFP_ATOMIC); + spin_unlock_irqrestore(&priv->ack_status_lock, flags); + + if (id >= 0) { + tx_info = MWIFIEX_SKB_TXCB(skb); + tx_info->ack_frame_id = id; + tx_info->flags |= flag; + orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb); + orig_tx_info->ack_frame_id = id; + orig_tx_info->flags |= flag; + + if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie) + orig_tx_info->cookie = *cookie; + + } else if (skb_shared(skb)) { + kfree_skb(orig_skb); + } else { + kfree_skb(skb); + skb = orig_skb; + } + } else { + /* couldn't clone -- lose tx status ... */ + skb = orig_skb; + } + + return skb; +} + +/* + * CFG802.11 network device handler for data transmission. + */ +static int +mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct sk_buff *new_skb; + struct mwifiex_txinfo *tx_info; + bool multicast; + + mwifiex_dbg(priv->adapter, DATA, + "data: %lu BSS(%d-%d): Data <= kernel\n", + jiffies, priv->bss_type, priv->bss_num); + + if (priv->adapter->surprise_removed) { + kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + if (!skb->len || (skb->len > ETH_FRAME_LEN)) { + mwifiex_dbg(priv->adapter, ERROR, + "Tx: bad skb len %d\n", skb->len); + kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { + mwifiex_dbg(priv->adapter, DATA, + "data: Tx: insufficient skb headroom %d\n", + skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + new_skb = + skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + if (unlikely(!new_skb)) { + mwifiex_dbg(priv->adapter, ERROR, + "Tx: cannot alloca new_skb\n"); + kfree_skb(skb); + priv->stats.tx_dropped++; + return 0; + } + kfree_skb(skb); + skb = new_skb; + mwifiex_dbg(priv->adapter, INFO, + "info: new skb headroomd %d\n", + skb_headroom(skb)); + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = skb->len; + + multicast = is_multicast_ether_addr(skb->data); + + if (unlikely(!multicast && skb->sk && + skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS && + priv->adapter->fw_api_ver == MWIFIEX_FW_V15)) + skb = mwifiex_clone_skb_for_tx_status(priv, + skb, + MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL); + + /* 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. + */ + __net_timestamp(skb); + + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->bss_type == MWIFIEX_BSS_TYPE_STA && + !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) { + if (priv->adapter->auto_tdls && priv->check_tdls_tx) + mwifiex_tdls_check_tx(priv, skb); + } + + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} + +/* + * CFG802.11 network device handler for setting MAC address. + */ +static int +mwifiex_set_mac_address(struct net_device *dev, void *addr) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct sockaddr *hw_addr = addr; + int ret; + + memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); + + /* Send request to firmware */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS, + HostCmd_ACT_GEN_SET, 0, NULL, true); + + if (!ret) + memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); + else + mwifiex_dbg(priv->adapter, ERROR, + "set mac address failed: ret=%d\n", ret); + + memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); + + return ret; +} + +/* + * CFG802.11 network device handler for setting multicast list. + */ +static void mwifiex_set_multicast_list(struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + struct mwifiex_multicast_list mcast_list; + + if (dev->flags & IFF_PROMISC) { + mcast_list.mode = MWIFIEX_PROMISC_MODE; + } else if (dev->flags & IFF_ALLMULTI || + netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) { + mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; + } else { + mcast_list.mode = MWIFIEX_MULTICAST_MODE; + mcast_list.num_multicast_addr = + mwifiex_copy_mcast_addr(&mcast_list, dev); + } + mwifiex_request_set_multicast_list(priv, &mcast_list); +} + +/* + * CFG802.11 network device handler for transmission timeout. + */ +static void +mwifiex_tx_timeout(struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + priv->num_tx_timeout++; + priv->tx_timeout_cnt++; + mwifiex_dbg(priv->adapter, ERROR, + "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n", + jiffies, priv->tx_timeout_cnt, priv->bss_type, + priv->bss_num); + mwifiex_set_trans_start(dev); + + if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD && + priv->adapter->if_ops.card_reset) { + mwifiex_dbg(priv->adapter, ERROR, + "tx_timeout_cnt exceeds threshold.\t" + "Triggering card reset!\n"); + priv->adapter->if_ops.card_reset(priv->adapter); + } +} + +void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + struct mwifiex_private *priv; + u16 tx_buf_size; + int i, ret; + + card->mc_resync_flag = true; + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + if (atomic_read(&card->port[i].tx_data_urb_pending)) { + mwifiex_dbg(adapter, WARN, "pending data urb in sys\n"); + return; + } + } + + card->mc_resync_flag = false; + tx_buf_size = 0xffff; + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false); + if (ret) + mwifiex_dbg(adapter, ERROR, + "send reconfig tx buf size cmd err\n"); +} +EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync); + +void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter) +{ + void *p; + char drv_version[64]; + struct usb_card_rec *cardp; + struct sdio_mmc_card *sdio_card; + struct mwifiex_private *priv; + int i, idx; + struct netdev_queue *txq; + struct mwifiex_debug_info *debug_info; + + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_dump = NULL; + adapter->drv_info_size = 0; + } + + mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump start===\n"); + + adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX); + + if (!adapter->drv_info_dump) + return; + + p = (char *)(adapter->drv_info_dump); + p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); + + mwifiex_drv_get_driver_version(adapter, drv_version, + sizeof(drv_version) - 1); + p += sprintf(p, "driver_version = %s\n", drv_version); + + if (adapter->iface_type == MWIFIEX_USB) { + cardp = (struct usb_card_rec *)adapter->card; + p += sprintf(p, "tx_cmd_urb_pending = %d\n", + atomic_read(&cardp->tx_cmd_urb_pending)); + p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n", + atomic_read(&cardp->port[0].tx_data_urb_pending)); + p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n", + atomic_read(&cardp->port[1].tx_data_urb_pending)); + p += sprintf(p, "rx_cmd_urb_pending = %d\n", + atomic_read(&cardp->rx_cmd_urb_pending)); + p += sprintf(p, "rx_data_urb_pending = %d\n", + atomic_read(&cardp->rx_data_urb_pending)); + } + + p += sprintf(p, "tx_pending = %d\n", + atomic_read(&adapter->tx_pending)); + p += sprintf(p, "rx_pending = %d\n", + atomic_read(&adapter->rx_pending)); + + if (adapter->iface_type == MWIFIEX_SDIO) { + sdio_card = (struct sdio_mmc_card *)adapter->card; + p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n", + sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port); + p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", + sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port); + } + + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i] || !adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + p += sprintf(p, "\n[interface : \"%s\"]\n", + priv->netdev->name); + p += sprintf(p, "wmm_tx_pending[0] = %d\n", + atomic_read(&priv->wmm_tx_pending[0])); + p += sprintf(p, "wmm_tx_pending[1] = %d\n", + atomic_read(&priv->wmm_tx_pending[1])); + p += sprintf(p, "wmm_tx_pending[2] = %d\n", + atomic_read(&priv->wmm_tx_pending[2])); + p += sprintf(p, "wmm_tx_pending[3] = %d\n", + atomic_read(&priv->wmm_tx_pending[3])); + p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ? + "Disconnected" : "Connected"); + p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev) + ? "on" : "off")); + for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) { + txq = netdev_get_tx_queue(priv->netdev, idx); + p += sprintf(p, "tx queue %d:%s ", idx, + netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p += sprintf(p, "\n%s: num_tx_timeout = %d\n", + priv->netdev->name, priv->num_tx_timeout); + } + + if (adapter->iface_type == MWIFIEX_SDIO) { + p += sprintf(p, "\n=== SDIO register dump===\n"); + if (adapter->if_ops.reg_dump) + p += adapter->if_ops.reg_dump(adapter, p); + } + + p += sprintf(p, "\n=== more debug information\n"); + debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL); + if (debug_info) { + for (i = 0; i < adapter->priv_num; i++) { + if (!adapter->priv[i] || !adapter->priv[i]->netdev) + continue; + priv = adapter->priv[i]; + mwifiex_get_debug_info(priv, debug_info); + p += mwifiex_debug_info_to_buffer(priv, p, debug_info); + break; + } + kfree(debug_info); + } + + adapter->drv_info_size = p - adapter->drv_info_dump; + mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump end===\n"); +} +EXPORT_SYMBOL_GPL(mwifiex_drv_info_dump); + +void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter) +{ + u8 idx, *dump_data, *fw_dump_ptr; + u32 dump_len; + + dump_len = (strlen("========Start dump driverinfo========\n") + + adapter->drv_info_size + + strlen("\n========End dump========\n")); + + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + dump_len += (strlen("========Start dump ") + + strlen(entry->mem_name) + + strlen("========\n") + + (entry->mem_size + 1) + + strlen("\n========End dump========\n")); + } + } + + dump_data = vzalloc(dump_len + 1); + if (!dump_data) + goto done; + + fw_dump_ptr = dump_data; + + /* Dump all the memory data into single file, a userspace script will + * be used to split all the memory data to multiple files + */ + mwifiex_dbg(adapter, MSG, + "== mwifiex dump information to /sys/class/devcoredump start"); + + strcpy(fw_dump_ptr, "========Start dump driverinfo========\n"); + fw_dump_ptr += strlen("========Start dump driverinfo========\n"); + memcpy(fw_dump_ptr, adapter->drv_info_dump, adapter->drv_info_size); + fw_dump_ptr += adapter->drv_info_size; + strcpy(fw_dump_ptr, "\n========End dump========\n"); + fw_dump_ptr += strlen("\n========End dump========\n"); + + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + strcpy(fw_dump_ptr, "========Start dump "); + fw_dump_ptr += strlen("========Start dump "); + + strcpy(fw_dump_ptr, entry->mem_name); + fw_dump_ptr += strlen(entry->mem_name); + + strcpy(fw_dump_ptr, "========\n"); + fw_dump_ptr += strlen("========\n"); + + memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); + fw_dump_ptr += entry->mem_size; + + strcpy(fw_dump_ptr, "\n========End dump========\n"); + fw_dump_ptr += strlen("\n========End dump========\n"); + } + } + + /* device dump data will be free in device coredump release function + * after 5 min + */ + dev_coredumpv(adapter->dev, dump_data, dump_len, GFP_KERNEL); + mwifiex_dbg(adapter, MSG, + "== mwifiex dump information to /sys/class/devcoredump end"); + +done: + for (idx = 0; idx < adapter->num_mem_types; idx++) { + struct memory_type_mapping *entry = + &adapter->mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + if (adapter->drv_info_dump) { + vfree(adapter->drv_info_dump); + adapter->drv_info_dump = NULL; + adapter->drv_info_size = 0; + } +} +EXPORT_SYMBOL_GPL(mwifiex_upload_device_dump); + +/* + * CFG802.11 network device handler for statistics retrieval. + */ +static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) +{ + struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); + + return &priv->stats; +} + +static u16 +mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback) +{ + skb->priority = cfg80211_classify8021d(skb, NULL); + return mwifiex_1d_to_wmm_queue[skb->priority]; +} + +/* Network device handlers */ +static const struct net_device_ops mwifiex_netdev_ops = { + .ndo_open = mwifiex_open, + .ndo_stop = mwifiex_close, + .ndo_start_xmit = mwifiex_hard_start_xmit, + .ndo_set_mac_address = mwifiex_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = mwifiex_tx_timeout, + .ndo_get_stats = mwifiex_get_stats, + .ndo_set_rx_mode = mwifiex_set_multicast_list, + .ndo_select_queue = mwifiex_netdev_select_wmm_queue, +}; + +/* + * This function initializes the private structure parameters. + * + * The following wait queues are initialized - + * - IOCTL wait queue + * - Command wait queue + * - Statistics wait queue + * + * ...and the following default parameters are set - + * - Current key index : Set to 0 + * - Rate index : Set to auto + * - Media connected : Set to disconnected + * - Adhoc link sensed : Set to false + * - Nick name : Set to null + * - Number of Tx timeout : Set to 0 + * - Device address : Set to current address + * - Rx histogram statistc : Set to 0 + * + * In addition, the CFG80211 work queue is also created. + */ +void mwifiex_init_priv_params(struct mwifiex_private *priv, + struct net_device *dev) +{ + dev->netdev_ops = &mwifiex_netdev_ops; + dev->destructor = free_netdev; + /* Initialize private structure */ + priv->current_key_index = 0; + priv->media_connected = false; + memset(priv->mgmt_ie, 0, + sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX); + priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK; + priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK; + priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK; + priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; + priv->num_tx_timeout = 0; + ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr); + memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL); + if (priv->hist_data) + mwifiex_hist_data_reset(priv); + } +} + +/* + * This function check if command is pending. + */ +int is_command_pending(struct mwifiex_adapter *adapter) +{ + unsigned long flags; + int is_cmd_pend_q_empty; + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); + is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); + + return !is_cmd_pend_q_empty; +} + +/* + * This is the RX work queue function. + * + * It handles the RX operations. + */ +static void mwifiex_rx_work_queue(struct work_struct *work) +{ + struct mwifiex_adapter *adapter = + container_of(work, struct mwifiex_adapter, rx_work); + + if (adapter->surprise_removed) + return; + mwifiex_process_rx(adapter); +} + +/* + * This is the main work queue function. + * + * It handles the main process, which in turn handles the complete + * driver operations. + */ +static void mwifiex_main_work_queue(struct work_struct *work) +{ + struct mwifiex_adapter *adapter = + container_of(work, struct mwifiex_adapter, main_work); + + if (adapter->surprise_removed) + return; + mwifiex_main_process(adapter); +} + +/* + * This function adds the card. + * + * This function follows the following major steps to set up the device - + * - Initialize software. This includes probing the card, registering + * the interface operations table, and allocating/initializing the + * adapter structure + * - Set up the netlink socket + * - Create and start the main work queue + * - Register the device + * - Initialize firmware and hardware + * - Add logical interfaces + */ +int +mwifiex_add_card(void *card, struct semaphore *sem, + struct mwifiex_if_ops *if_ops, u8 iface_type) +{ + struct mwifiex_adapter *adapter; + + if (down_interruptible(sem)) + goto exit_sem_err; + + if (mwifiex_register(card, if_ops, (void **)&adapter)) { + pr_err("%s: software init failed\n", __func__); + goto err_init_sw; + } + + adapter->iface_type = iface_type; + adapter->card_sem = sem; + + adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; + adapter->surprise_removed = false; + init_waitqueue_head(&adapter->init_wait_q); + adapter->is_suspended = false; + adapter->hs_activated = false; + init_waitqueue_head(&adapter->hs_activate_wait_q); + init_waitqueue_head(&adapter->cmd_wait_q.wait); + adapter->cmd_wait_q.status = 0; + adapter->scan_wait_q_woken = false; + + if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) { + adapter->rx_work_enabled = true; + pr_notice("rx work enabled, cpus %d\n", num_possible_cpus()); + } + + adapter->workqueue = + alloc_workqueue("MWIFIEX_WORK_QUEUE", + WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); + if (!adapter->workqueue) + goto err_kmalloc; + + INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); + + if (adapter->rx_work_enabled) { + adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE", + WQ_HIGHPRI | + WQ_MEM_RECLAIM | + WQ_UNBOUND, 1); + if (!adapter->rx_workqueue) + goto err_kmalloc; + + INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue); + } + + /* Register the device. Fill up the private data structure with relevant + information from the card. */ + if (adapter->if_ops.register_dev(adapter)) { + pr_err("%s: failed to register mwifiex device\n", __func__); + goto err_registerdev; + } + + if (mwifiex_init_hw_fw(adapter)) { + pr_err("%s: firmware init failed\n", __func__); + goto err_init_fw; + } + + return 0; + +err_init_fw: + pr_debug("info: %s: unregister device\n", __func__); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { + pr_debug("info: %s: shutdown mwifiex\n", __func__); + adapter->init_wait_q_woken = false; + + if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + } +err_registerdev: + adapter->surprise_removed = true; + mwifiex_terminate_workqueue(adapter); +err_kmalloc: + mwifiex_free_adapter(adapter); + +err_init_sw: + up(sem); + +exit_sem_err: + return -1; +} +EXPORT_SYMBOL_GPL(mwifiex_add_card); + +/* + * This function removes the card. + * + * This function follows the following major steps to remove the device - + * - Stop data traffic + * - Shutdown firmware + * - Remove the logical interfaces + * - Terminate the work queue + * - Unregister the device + * - Free the adapter structure + */ +int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) +{ + struct mwifiex_private *priv = NULL; + int i; + + if (down_interruptible(sem)) + goto exit_sem_err; + + if (!adapter) + goto exit_remove; + + /* We can no longer handle interrupts once we start doing the teardown + * below. */ + if (adapter->if_ops.disable_int) + adapter->if_ops.disable_int(adapter); + + adapter->surprise_removed = true; + + mwifiex_terminate_workqueue(adapter); + + /* Stop data */ + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv && priv->netdev) { + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + } + } + + mwifiex_dbg(adapter, CMD, + "cmd: calling mwifiex_shutdown_drv...\n"); + adapter->init_wait_q_woken = false; + + if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) + wait_event_interruptible(adapter->init_wait_q, + adapter->init_wait_q_woken); + mwifiex_dbg(adapter, CMD, + "cmd: mwifiex_shutdown_drv done\n"); + if (atomic_read(&adapter->rx_pending) || + atomic_read(&adapter->tx_pending) || + atomic_read(&adapter->cmd_pending)) { + mwifiex_dbg(adapter, ERROR, + "rx_pending=%d, tx_pending=%d,\t" + "cmd_pending=%d\n", + atomic_read(&adapter->rx_pending), + atomic_read(&adapter->tx_pending), + atomic_read(&adapter->cmd_pending)); + } + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + + if (!priv) + continue; + + rtnl_lock(); + if (priv->netdev && + priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) + mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); + rtnl_unlock(); + } + + wiphy_unregister(adapter->wiphy); + wiphy_free(adapter->wiphy); + + /* Unregister device */ + mwifiex_dbg(adapter, INFO, + "info: unregister device\n"); + if (adapter->if_ops.unregister_dev) + adapter->if_ops.unregister_dev(adapter); + /* Free adapter structure */ + mwifiex_dbg(adapter, INFO, + "info: free adapter\n"); + mwifiex_free_adapter(adapter); + +exit_remove: + up(sem); +exit_sem_err: + return 0; +} +EXPORT_SYMBOL_GPL(mwifiex_remove_card); + +void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, + const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (!adapter->dev || !(adapter->debug_mask & mask)) + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + dev_info(adapter->dev, "%pV", &vaf); + + va_end(args); +} +EXPORT_SYMBOL_GPL(_mwifiex_dbg); + +/* + * This function initializes the module. + * + * The debug FS is also initialized if configured. + */ +static int +mwifiex_init_module(void) +{ +#ifdef CONFIG_DEBUG_FS + mwifiex_debugfs_init(); +#endif + return 0; +} + +/* + * This function cleans up the module. + * + * The debug FS is removed if available. + */ +static void +mwifiex_cleanup_module(void) +{ +#ifdef CONFIG_DEBUG_FS + mwifiex_debugfs_remove(); +#endif +} + +module_init(mwifiex_init_module); +module_exit(mwifiex_cleanup_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h new file mode 100644 index 000000000000..3959f1c97f4e --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -0,0 +1,1579 @@ +/* + * Marvell Wireless LAN device driver: major data structures and prototypes + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_MAIN_H_ +#define _MWIFIEX_MAIN_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "pcie.h" +#include "usb.h" +#include "sdio.h" + +extern const char driver_version[]; + +struct mwifiex_adapter; +struct mwifiex_private; + +enum { + MWIFIEX_ASYNC_CMD, + MWIFIEX_SYNC_CMD +}; + +#define MWIFIEX_DRIVER_MODE_STA BIT(0) +#define MWIFIEX_DRIVER_MODE_UAP BIT(1) +#define MWIFIEX_DRIVER_MODE_P2P BIT(2) +#define MWIFIEX_DRIVER_MODE_BITMASK (BIT(0) | BIT(1) | BIT(2)) + +#define MWIFIEX_MAX_AP 64 + +#define MWIFIEX_MAX_PKTS_TXQ 16 + +#define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) + +#define MWIFIEX_TIMER_10S 10000 +#define MWIFIEX_TIMER_1S 1000 + +#define MAX_TX_PENDING 100 +#define LOW_TX_PENDING 80 + +#define HIGH_RX_PENDING 50 +#define LOW_RX_PENDING 20 + +#define MWIFIEX_UPLD_SIZE (2312) + +#define MAX_EVENT_SIZE 2048 + +#define ARP_FILTER_MAX_BUF_SIZE 68 + +#define MWIFIEX_KEY_BUFFER_SIZE 16 +#define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10 +#define MWIFIEX_MAX_REGION_CODE 7 + +#define DEFAULT_BCN_AVG_FACTOR 8 +#define DEFAULT_DATA_AVG_FACTOR 8 + +#define FIRST_VALID_CHANNEL 0xff +#define DEFAULT_AD_HOC_CHANNEL 6 +#define DEFAULT_AD_HOC_CHANNEL_A 36 + +#define DEFAULT_BCN_MISS_TIMEOUT 5 + +#define MAX_SCAN_BEACON_BUFFER 8000 + +#define SCAN_BEACON_ENTRY_PAD 6 + +#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 110 +#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 30 +#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 30 +#define MWIFIEX_DEF_SCAN_CHAN_GAP_TIME 50 + +#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) + +#define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S) + +#define RSN_GTK_OUI_OFFSET 2 + +#define MWIFIEX_OUI_NOT_PRESENT 0 +#define MWIFIEX_OUI_PRESENT 1 + +#define PKT_TYPE_MGMT 0xE5 + +/* + * Do not check for data_received for USB, as data_received + * is handled in mwifiex_usb_recv for USB + */ +#define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \ + adapter->event_received || \ + adapter->data_received) + +#define MWIFIEX_TYPE_CMD 1 +#define MWIFIEX_TYPE_DATA 0 +#define MWIFIEX_TYPE_AGGR_DATA 10 +#define MWIFIEX_TYPE_EVENT 3 + +#define MAX_BITMAP_RATES_SIZE 18 + +#define MAX_CHANNEL_BAND_BG 14 +#define MAX_CHANNEL_BAND_A 165 + +#define MAX_FREQUENCY_BAND_BG 2484 + +#define MWIFIEX_EVENT_HEADER_LEN 4 +#define MWIFIEX_UAP_EVENT_EXTRA_HEADER 2 + +#define MWIFIEX_TYPE_LEN 4 +#define MWIFIEX_USB_TYPE_CMD 0xF00DFACE +#define MWIFIEX_USB_TYPE_DATA 0xBEADC0DE +#define MWIFIEX_USB_TYPE_EVENT 0xBEEFFACE + +/* Threshold for tx_timeout_cnt before we trigger a card reset */ +#define TX_TIMEOUT_THRESHOLD 6 + +#define MWIFIEX_DRV_INFO_SIZE_MAX 0x40000 + +/* Address alignment */ +#define MWIFIEX_ALIGN_ADDR(p, a) (((long)(p) + (a) - 1) & ~((a) - 1)) + +/** + *enum mwifiex_debug_level - marvell wifi debug level + */ +enum MWIFIEX_DEBUG_LEVEL { + MWIFIEX_DBG_MSG = 0x00000001, + MWIFIEX_DBG_FATAL = 0x00000002, + MWIFIEX_DBG_ERROR = 0x00000004, + MWIFIEX_DBG_DATA = 0x00000008, + MWIFIEX_DBG_CMD = 0x00000010, + MWIFIEX_DBG_EVENT = 0x00000020, + MWIFIEX_DBG_INTR = 0x00000040, + MWIFIEX_DBG_IOCTL = 0x00000080, + + MWIFIEX_DBG_MPA_D = 0x00008000, + MWIFIEX_DBG_DAT_D = 0x00010000, + MWIFIEX_DBG_CMD_D = 0x00020000, + MWIFIEX_DBG_EVT_D = 0x00040000, + MWIFIEX_DBG_FW_D = 0x00080000, + MWIFIEX_DBG_IF_D = 0x00100000, + + MWIFIEX_DBG_ENTRY = 0x10000000, + MWIFIEX_DBG_WARN = 0x20000000, + MWIFIEX_DBG_INFO = 0x40000000, + MWIFIEX_DBG_DUMP = 0x80000000, + + MWIFIEX_DBG_ANY = 0xffffffff +}; + +#define MWIFIEX_DEFAULT_DEBUG_MASK (MWIFIEX_DBG_MSG | \ + MWIFIEX_DBG_FATAL | \ + MWIFIEX_DBG_ERROR) + +__printf(3, 4) +void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, + const char *fmt, ...); +#define mwifiex_dbg(adapter, mask, fmt, ...) \ + _mwifiex_dbg(adapter, MWIFIEX_DBG_##mask, fmt, ##__VA_ARGS__) + +#define DEBUG_DUMP_DATA_MAX_LEN 128 +#define mwifiex_dbg_dump(adapter, dbg_mask, str, buf, len) \ +do { \ + if ((adapter)->debug_mask & MWIFIEX_DBG_##dbg_mask) \ + print_hex_dump(KERN_DEBUG, str, \ + DUMP_PREFIX_OFFSET, 16, 1, \ + buf, len, false); \ +} while (0) + +struct mwifiex_dbg { + u32 num_cmd_host_to_card_failure; + u32 num_cmd_sleep_cfm_host_to_card_failure; + u32 num_tx_host_to_card_failure; + u32 num_event_deauth; + u32 num_event_disassoc; + u32 num_event_link_lost; + u32 num_cmd_deauth; + u32 num_cmd_assoc_success; + u32 num_cmd_assoc_failure; + u32 num_tx_timeout; + u16 timeout_cmd_id; + u16 timeout_cmd_act; + u16 last_cmd_id[DBG_CMD_NUM]; + u16 last_cmd_act[DBG_CMD_NUM]; + u16 last_cmd_index; + u16 last_cmd_resp_id[DBG_CMD_NUM]; + u16 last_cmd_resp_index; + u16 last_event[DBG_CMD_NUM]; + u16 last_event_index; +}; + +enum MWIFIEX_HARDWARE_STATUS { + MWIFIEX_HW_STATUS_READY, + MWIFIEX_HW_STATUS_INITIALIZING, + MWIFIEX_HW_STATUS_INIT_DONE, + MWIFIEX_HW_STATUS_RESET, + MWIFIEX_HW_STATUS_CLOSING, + MWIFIEX_HW_STATUS_NOT_READY +}; + +enum MWIFIEX_802_11_POWER_MODE { + MWIFIEX_802_11_POWER_MODE_CAM, + MWIFIEX_802_11_POWER_MODE_PSP +}; + +struct mwifiex_tx_param { + u32 next_pkt_len; +}; + +enum MWIFIEX_PS_STATE { + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP_CFM, + PS_STATE_SLEEP +}; + +enum mwifiex_iface_type { + MWIFIEX_SDIO, + MWIFIEX_PCIE, + MWIFIEX_USB +}; + +struct mwifiex_add_ba_param { + u32 tx_win_size; + u32 rx_win_size; + u32 timeout; + u8 tx_amsdu; + u8 rx_amsdu; +}; + +struct mwifiex_tx_aggr { + u8 ampdu_user; + u8 ampdu_ap; + u8 amsdu; +}; + +enum mwifiex_ba_status { + BA_SETUP_NONE = 0, + BA_SETUP_INPROGRESS, + BA_SETUP_COMPLETE +}; + +struct mwifiex_ra_list_tbl { + struct list_head list; + struct sk_buff_head skb_head; + u8 ra[ETH_ALEN]; + u32 is_11n_enabled; + u16 max_amsdu; + u16 ba_pkt_count; + u8 ba_packet_thr; + enum mwifiex_ba_status ba_status; + u8 amsdu_in_ampdu; + u16 total_pkt_count; + bool tdls_link; + bool tx_paused; +}; + +struct mwifiex_tid_tbl { + struct list_head ra_list; +}; + +#define WMM_HIGHEST_PRIORITY 7 +#define HIGH_PRIO_TID 7 +#define LOW_PRIO_TID 0 + +struct mwifiex_wmm_desc { + struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID]; + u32 packets_out[MAX_NUM_TID]; + u32 pkts_paused[MAX_NUM_TID]; + /* spin lock to protect ra_list */ + spinlock_t ra_list_spinlock; + struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS]; + enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS]; + u32 drv_pkt_delay_max; + u8 queue_priority[IEEE80211_NUM_ACS]; + u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ + /* Number of transmit packets queued */ + atomic_t tx_pkts_queued; + /* Tracks highest priority with a packet queued */ + atomic_t highest_queued_prio; +}; + +struct mwifiex_802_11_security { + u8 wpa_enabled; + u8 wpa2_enabled; + u8 wapi_enabled; + u8 wapi_key_on; + u8 wep_enabled; + u32 authentication_mode; + u8 is_authtype_auto; + u32 encryption_mode; +}; + +struct ieee_types_header { + u8 element_id; + u8 len; +} __packed; + +struct ieee_types_vendor_specific { + struct ieee_types_vendor_header vend_hdr; + u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)]; +} __packed; + +struct ieee_types_generic { + struct ieee_types_header ieee_hdr; + u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; +} __packed; + +struct ieee_types_bss_co_2040 { + struct ieee_types_header ieee_hdr; + u8 bss_2040co; +} __packed; + +struct ieee_types_extcap { + struct ieee_types_header ieee_hdr; + u8 ext_capab[8]; +} __packed; + +struct ieee_types_vht_cap { + struct ieee_types_header ieee_hdr; + struct ieee80211_vht_cap vhtcap; +} __packed; + +struct ieee_types_vht_oper { + struct ieee_types_header ieee_hdr; + struct ieee80211_vht_operation vhtoper; +} __packed; + +struct ieee_types_aid { + struct ieee_types_header ieee_hdr; + u16 aid; +} __packed; + +struct mwifiex_bssdescriptor { + u8 mac_address[ETH_ALEN]; + struct cfg80211_ssid ssid; + u32 privacy; + s32 rssi; + u32 channel; + u32 freq; + u16 beacon_period; + u8 erp_flags; + u32 bss_mode; + u8 supported_rates[MWIFIEX_SUPPORTED_RATES]; + u8 data_rates[MWIFIEX_SUPPORTED_RATES]; + /* Network band. + * BAND_B(0x01): 'b' band + * BAND_G(0x02): 'g' band + * BAND_A(0X04): 'a' band + */ + u16 bss_band; + u64 fw_tsf; + u64 timestamp; + union ieee_types_phy_param_set phy_param_set; + union ieee_types_ss_param_set ss_param_set; + u16 cap_info_bitmap; + struct ieee_types_wmm_parameter wmm_ie; + u8 disable_11n; + struct ieee80211_ht_cap *bcn_ht_cap; + u16 ht_cap_offset; + struct ieee80211_ht_operation *bcn_ht_oper; + u16 ht_info_offset; + u8 *bcn_bss_co_2040; + u16 bss_co_2040_offset; + u8 *bcn_ext_cap; + u16 ext_cap_offset; + struct ieee80211_vht_cap *bcn_vht_cap; + u16 vht_cap_offset; + struct ieee80211_vht_operation *bcn_vht_oper; + u16 vht_info_offset; + struct ieee_types_oper_mode_ntf *oper_mode; + u16 oper_mode_offset; + u8 disable_11ac; + struct ieee_types_vendor_specific *bcn_wpa_ie; + u16 wpa_offset; + struct ieee_types_generic *bcn_rsn_ie; + u16 rsn_offset; + struct ieee_types_generic *bcn_wapi_ie; + u16 wapi_offset; + u8 *beacon_buf; + u32 beacon_buf_size; + u8 sensed_11h; + u8 local_constraint; + u8 chan_sw_ie_present; +}; + +struct mwifiex_current_bss_params { + struct mwifiex_bssdescriptor bss_descriptor; + u8 wmm_enabled; + u8 wmm_uapsd_enabled; + u8 band; + u32 num_of_rates; + u8 data_rates[MWIFIEX_SUPPORTED_RATES]; +}; + +struct mwifiex_sleep_params { + u16 sp_error; + u16 sp_offset; + u16 sp_stable_time; + u8 sp_cal_control; + u8 sp_ext_sleep_clk; + u16 sp_reserved; +}; + +struct mwifiex_sleep_period { + u16 period; + u16 reserved; +}; + +struct mwifiex_wep_key { + u32 length; + u32 key_index; + u32 key_length; + u8 key_material[MWIFIEX_KEY_BUFFER_SIZE]; +}; + +#define MAX_REGION_CHANNEL_NUM 2 + +struct mwifiex_chan_freq_power { + u16 channel; + u32 freq; + u16 max_tx_power; + u8 unsupported; +}; + +enum state_11d_t { + DISABLE_11D = 0, + ENABLE_11D = 1, +}; + +#define MWIFIEX_MAX_TRIPLET_802_11D 83 + +struct mwifiex_802_11d_domain_reg { + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u8 no_of_triplet; + struct ieee80211_country_ie_triplet + triplet[MWIFIEX_MAX_TRIPLET_802_11D]; +}; + +struct mwifiex_vendor_spec_cfg_ie { + u16 mask; + u16 flag; + u8 ie[MWIFIEX_MAX_VSIE_LEN]; +}; + +struct wps { + u8 session_enable; +}; + +struct mwifiex_roc_cfg { + u64 cookie; + struct ieee80211_channel chan; +}; + +#define MWIFIEX_FW_DUMP_IDX 0xff +#define MWIFIEX_DRV_INFO_IDX 20 +#define FW_DUMP_MAX_NAME_LEN 8 +#define FW_DUMP_HOST_READY 0xEE +#define FW_DUMP_DONE 0xFF +#define FW_DUMP_READ_DONE 0xFE + +struct memory_type_mapping { + u8 mem_name[FW_DUMP_MAX_NAME_LEN]; + u8 *mem_ptr; + u32 mem_size; + u8 done_flag; +}; + +enum rdwr_status { + RDWR_STATUS_SUCCESS = 0, + RDWR_STATUS_FAILURE = 1, + RDWR_STATUS_DONE = 2 +}; + +enum mwifiex_iface_work_flags { + MWIFIEX_IFACE_WORK_DEVICE_DUMP, + MWIFIEX_IFACE_WORK_CARD_RESET, +}; + +struct mwifiex_private { + struct mwifiex_adapter *adapter; + u8 bss_type; + u8 bss_role; + u8 bss_priority; + u8 bss_num; + u8 bss_started; + u8 frame_type; + u8 curr_addr[ETH_ALEN]; + u8 media_connected; + u8 port_open; + u8 usb_port; + u32 num_tx_timeout; + /* track consecutive timeout */ + u8 tx_timeout_cnt; + struct net_device *netdev; + struct net_device_stats stats; + u16 curr_pkt_filter; + u32 bss_mode; + u32 pkt_tx_ctrl; + u16 tx_power_level; + u8 max_tx_power_level; + u8 min_tx_power_level; + u8 tx_rate; + u8 tx_htinfo; + u8 rxpd_htinfo; + u8 rxpd_rate; + u16 rate_bitmap; + u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; + u32 data_rate; + u8 is_data_rate_auto; + u16 bcn_avg_factor; + u16 data_avg_factor; + s16 data_rssi_last; + s16 data_nf_last; + s16 data_rssi_avg; + s16 data_nf_avg; + s16 bcn_rssi_last; + s16 bcn_nf_last; + s16 bcn_rssi_avg; + s16 bcn_nf_avg; + struct mwifiex_bssdescriptor *attempted_bss_desc; + struct cfg80211_ssid prev_ssid; + u8 prev_bssid[ETH_ALEN]; + struct mwifiex_current_bss_params curr_bss_params; + u16 beacon_period; + u8 dtim_period; + u16 listen_interval; + u16 atim_window; + u8 adhoc_channel; + u8 adhoc_is_link_sensed; + u8 adhoc_state; + struct mwifiex_802_11_security sec_info; + struct mwifiex_wep_key wep_key[NUM_WEP_KEYS]; + u16 wep_key_curr_index; + u8 wpa_ie[256]; + u8 wpa_ie_len; + u8 wpa_is_gtk_set; + struct host_cmd_ds_802_11_key_material aes_key; + struct host_cmd_ds_802_11_key_material_v2 aes_key_v2; + u8 wapi_ie[256]; + u8 wapi_ie_len; + u8 *wps_ie; + u8 wps_ie_len; + u8 wmm_required; + u8 wmm_enabled; + u8 wmm_qosinfo; + struct mwifiex_wmm_desc wmm; + atomic_t wmm_tx_pending[IEEE80211_NUM_ACS]; + struct list_head sta_list; + /* spin lock for associated station/TDLS peers list */ + spinlock_t sta_list_spinlock; + struct list_head auto_tdls_list; + /* spin lock for auto TDLS peer list */ + spinlock_t auto_tdls_lock; + struct list_head tx_ba_stream_tbl_ptr; + /* spin lock for tx_ba_stream_tbl_ptr queue */ + spinlock_t tx_ba_stream_tbl_lock; + struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; + struct mwifiex_add_ba_param add_ba_param; + u16 rx_seq[MAX_NUM_TID]; + u8 tos_to_tid_inv[MAX_NUM_TID]; + struct list_head rx_reorder_tbl_ptr; + /* spin lock for rx_reorder_tbl_ptr queue */ + spinlock_t rx_reorder_tbl_lock; + /* spin lock for Rx packets */ + spinlock_t rx_pkt_lock; + +#define MWIFIEX_ASSOC_RSP_BUF_SIZE 500 + u8 assoc_rsp_buf[MWIFIEX_ASSOC_RSP_BUF_SIZE]; + u32 assoc_rsp_size; + +#define MWIFIEX_GENIE_BUF_SIZE 256 + u8 gen_ie_buf[MWIFIEX_GENIE_BUF_SIZE]; + u8 gen_ie_buf_len; + + struct mwifiex_vendor_spec_cfg_ie vs_ie[MWIFIEX_MAX_VSIE_NUM]; + +#define MWIFIEX_ASSOC_TLV_BUF_SIZE 256 + u8 assoc_tlv_buf[MWIFIEX_ASSOC_TLV_BUF_SIZE]; + u8 assoc_tlv_buf_len; + + u8 *curr_bcn_buf; + u32 curr_bcn_size; + /* spin lock for beacon buffer */ + spinlock_t curr_bcn_buf_lock; + struct wireless_dev wdev; + struct mwifiex_chan_freq_power cfp; + char version_str[128]; +#ifdef CONFIG_DEBUG_FS + struct dentry *dfs_dev_dir; +#endif + u16 current_key_index; + struct semaphore async_sem; + struct cfg80211_scan_request *scan_request; + u8 cfg_bssid[6]; + struct wps wps; + u8 scan_block; + s32 cqm_rssi_thold; + u32 cqm_rssi_hyst; + u8 subsc_evt_rssi_state; + struct mwifiex_ds_misc_subsc_evt async_subsc_evt_storage; + struct mwifiex_ie mgmt_ie[MAX_MGMT_IE_INDEX]; + u16 beacon_idx; + u16 proberesp_idx; + u16 assocresp_idx; + u16 gen_idx; + u8 ap_11n_enabled; + u8 ap_11ac_enabled; + u32 mgmt_frame_mask; + struct mwifiex_roc_cfg roc_cfg; + bool scan_aborting; + u8 csa_chan; + unsigned long csa_expire_time; + u8 del_list_idx; + bool hs2_enabled; + struct mwifiex_uap_bss_param bss_cfg; + struct cfg80211_chan_def bss_chandef; + struct station_parameters *sta_params; + struct sk_buff_head tdls_txq; + u8 check_tdls_tx; + struct timer_list auto_tdls_timer; + bool auto_tdls_timer_active; + struct idr ack_status_frames; + /* spin lock for ack status */ + spinlock_t ack_status_lock; + /** rx histogram data */ + struct mwifiex_histogram_data *hist_data; + struct cfg80211_chan_def dfs_chandef; + struct workqueue_struct *dfs_cac_workqueue; + struct delayed_work dfs_cac_work; + struct timer_list dfs_chan_switch_timer; + struct workqueue_struct *dfs_chan_sw_workqueue; + struct delayed_work dfs_chan_sw_work; + struct cfg80211_beacon_data beacon_after; + struct mwifiex_11h_intf_state state_11h; + struct mwifiex_ds_mem_rw mem_rw; + struct sk_buff_head bypass_txq; + struct mwifiex_user_scan_chan hidden_chan[MWIFIEX_USER_SCAN_CHAN_MAX]; +}; + + +struct mwifiex_tx_ba_stream_tbl { + struct list_head list; + int tid; + u8 ra[ETH_ALEN]; + enum mwifiex_ba_status ba_status; + u8 amsdu; +}; + +struct mwifiex_rx_reorder_tbl; + +struct reorder_tmr_cnxt { + struct timer_list timer; + struct mwifiex_rx_reorder_tbl *ptr; + struct mwifiex_private *priv; + u8 timer_is_set; +}; + +struct mwifiex_rx_reorder_tbl { + struct list_head list; + int tid; + u8 ta[ETH_ALEN]; + int init_win; + int start_win; + int win_size; + void **rx_reorder_ptr; + struct reorder_tmr_cnxt timer_context; + u8 amsdu; + u8 flags; +}; + +struct mwifiex_bss_prio_node { + struct list_head list; + struct mwifiex_private *priv; +}; + +struct mwifiex_bss_prio_tbl { + struct list_head bss_prio_head; + /* spin lock for bss priority */ + spinlock_t bss_prio_lock; + struct mwifiex_bss_prio_node *bss_prio_cur; +}; + +struct cmd_ctrl_node { + struct list_head list; + struct mwifiex_private *priv; + u32 cmd_oid; + u32 cmd_flag; + struct sk_buff *cmd_skb; + struct sk_buff *resp_skb; + void *data_buf; + u32 wait_q_enabled; + struct sk_buff *skb; + u8 *condition; + u8 cmd_wait_q_woken; +}; + +struct mwifiex_bss_priv { + u8 band; + u64 fw_tsf; +}; + +struct mwifiex_tdls_capab { + __le16 capab; + u8 rates[32]; + u8 rates_len; + u8 qos_info; + u8 coex_2040; + u16 aid; + struct ieee80211_ht_cap ht_capb; + struct ieee80211_ht_operation ht_oper; + struct ieee_types_extcap extcap; + struct ieee_types_generic rsn_ie; + struct ieee80211_vht_cap vhtcap; + struct ieee80211_vht_operation vhtoper; +}; + +struct mwifiex_station_stats { + u64 last_rx; + s8 rssi; + u64 rx_bytes; + u64 tx_bytes; + u32 rx_packets; + u32 tx_packets; + u32 tx_failed; + u8 last_tx_rate; + u8 last_tx_htinfo; +}; + +/* This is AP/TDLS specific structure which stores information + * about associated/peer STA + */ +struct mwifiex_sta_node { + struct list_head list; + u8 mac_addr[ETH_ALEN]; + u8 is_wmm_enabled; + u8 is_11n_enabled; + u8 is_11ac_enabled; + u8 ampdu_sta[MAX_NUM_TID]; + u16 rx_seq[MAX_NUM_TID]; + u16 max_amsdu; + u8 tdls_status; + struct mwifiex_tdls_capab tdls_cap; + struct mwifiex_station_stats stats; + u8 tx_pause; +}; + +struct mwifiex_auto_tdls_peer { + struct list_head list; + u8 mac_addr[ETH_ALEN]; + u8 tdls_status; + int rssi; + long rssi_jiffies; + u8 failure_count; + u8 do_discover; + u8 do_setup; +}; + +struct mwifiex_if_ops { + int (*init_if) (struct mwifiex_adapter *); + void (*cleanup_if) (struct mwifiex_adapter *); + int (*check_fw_status) (struct mwifiex_adapter *, u32); + int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); + int (*register_dev) (struct mwifiex_adapter *); + void (*unregister_dev) (struct mwifiex_adapter *); + int (*enable_int) (struct mwifiex_adapter *); + void (*disable_int) (struct mwifiex_adapter *); + int (*process_int_status) (struct mwifiex_adapter *); + int (*host_to_card) (struct mwifiex_adapter *, u8, struct sk_buff *, + struct mwifiex_tx_param *); + int (*wakeup) (struct mwifiex_adapter *); + int (*wakeup_complete) (struct mwifiex_adapter *); + + /* Interface specific functions */ + void (*update_mp_end_port) (struct mwifiex_adapter *, u16); + void (*cleanup_mpa_buf) (struct mwifiex_adapter *); + int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *); + int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); + int (*init_fw_port) (struct mwifiex_adapter *); + int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); + void (*card_reset) (struct mwifiex_adapter *); + int (*reg_dump)(struct mwifiex_adapter *, char *); + void (*device_dump)(struct mwifiex_adapter *); + int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); + void (*iface_work)(struct work_struct *work); + void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter); + void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *); + void (*multi_port_resync)(struct mwifiex_adapter *); + bool (*is_port_ready)(struct mwifiex_private *); +}; + +struct mwifiex_adapter { + u8 iface_type; + unsigned int debug_mask; + struct mwifiex_iface_comb iface_limit; + struct mwifiex_iface_comb curr_iface_comb; + struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; + u8 priv_num; + const struct firmware *firmware; + char fw_name[32]; + int winner; + struct device *dev; + struct wiphy *wiphy; + u8 perm_addr[ETH_ALEN]; + bool surprise_removed; + u32 fw_release_number; + u16 init_wait_q_woken; + wait_queue_head_t init_wait_q; + void *card; + struct mwifiex_if_ops if_ops; + atomic_t bypass_tx_pending; + atomic_t rx_pending; + atomic_t tx_pending; + atomic_t cmd_pending; + struct workqueue_struct *workqueue; + struct work_struct main_work; + struct workqueue_struct *rx_workqueue; + struct work_struct rx_work; + struct workqueue_struct *dfs_workqueue; + struct work_struct dfs_work; + bool rx_work_enabled; + bool rx_processing; + bool delay_main_work; + bool rx_locked; + bool main_locked; + struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; + /* spin lock for init/shutdown */ + spinlock_t mwifiex_lock; + /* spin lock for main process */ + spinlock_t main_proc_lock; + u32 mwifiex_processing; + u8 more_task_flag; + u16 tx_buf_size; + u16 curr_tx_buf_size; + /* sdio single port rx aggregation capability */ + bool host_disable_sdio_rx_aggr; + bool sdio_rx_aggr_enable; + u16 sdio_rx_block_size; + u32 ioport; + enum MWIFIEX_HARDWARE_STATUS hw_status; + u16 number_of_antenna; + u32 fw_cap_info; + /* spin lock for interrupt handling */ + spinlock_t int_lock; + u8 int_status; + u32 event_cause; + struct sk_buff *event_skb; + u8 upld_buf[MWIFIEX_UPLD_SIZE]; + u8 data_sent; + u8 cmd_sent; + u8 cmd_resp_received; + u8 event_received; + u8 data_received; + u16 seq_num; + struct cmd_ctrl_node *cmd_pool; + struct cmd_ctrl_node *curr_cmd; + /* spin lock for command */ + spinlock_t mwifiex_cmd_lock; + u8 is_cmd_timedout; + u16 last_init_cmd; + struct timer_list cmd_timer; + struct list_head cmd_free_q; + /* spin lock for cmd_free_q */ + spinlock_t cmd_free_q_lock; + struct list_head cmd_pending_q; + /* spin lock for cmd_pending_q */ + spinlock_t cmd_pending_q_lock; + struct list_head scan_pending_q; + /* spin lock for scan_pending_q */ + spinlock_t scan_pending_q_lock; + /* spin lock for RX processing routine */ + spinlock_t rx_proc_lock; + struct sk_buff_head tx_data_q; + atomic_t tx_queued; + u32 scan_processing; + u16 region_code; + struct mwifiex_802_11d_domain_reg domain_reg; + u16 scan_probes; + u32 scan_mode; + u16 specific_scan_time; + u16 active_scan_time; + u16 passive_scan_time; + u16 scan_chan_gap_time; + u8 fw_bands; + u8 adhoc_start_band; + u8 config_bands; + struct mwifiex_chan_scan_param_set *scan_channels; + u8 tx_lock_flag; + struct mwifiex_sleep_params sleep_params; + struct mwifiex_sleep_period sleep_period; + u16 ps_mode; + u32 ps_state; + u8 need_to_wakeup; + u16 multiple_dtim; + u16 local_listen_interval; + u16 null_pkt_interval; + struct sk_buff *sleep_cfm; + u16 bcn_miss_time_out; + u16 adhoc_awake_period; + u8 is_deep_sleep; + u8 delay_null_pkt; + u16 delay_to_ps; + u16 enhanced_ps_mode; + u8 pm_wakeup_card_req; + u16 gen_null_pkt; + u16 pps_uapsd_mode; + u32 pm_wakeup_fw_try; + struct timer_list wakeup_timer; + u8 is_hs_configured; + struct mwifiex_hs_config_param hs_cfg; + u8 hs_activated; + u16 hs_activate_wait_q_woken; + wait_queue_head_t hs_activate_wait_q; + bool is_suspended; + bool hs_enabling; + u8 event_body[MAX_EVENT_SIZE]; + u32 hw_dot_11n_dev_cap; + u8 hw_dev_mcs_support; + u8 user_dev_mcs_support; + u8 adhoc_11n_enabled; + u8 sec_chan_offset; + struct mwifiex_dbg dbg; + u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; + u32 arp_filter_size; + struct mwifiex_wait_queue cmd_wait_q; + u8 scan_wait_q_woken; + spinlock_t queue_lock; /* lock for tx queues */ + u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; + u16 max_mgmt_ie_index; + const struct firmware *cal_data; + struct device_node *dt_node; + + /* 11AC */ + u32 is_hw_11ac_capable; + u32 hw_dot_11ac_dev_cap; + u32 hw_dot_11ac_mcs_support; + u32 usr_dot_11ac_dev_cap_bg; + u32 usr_dot_11ac_dev_cap_a; + u32 usr_dot_11ac_mcs_support; + + atomic_t pending_bridged_pkts; + struct semaphore *card_sem; + bool ext_scan; + u8 fw_api_ver; + u8 key_api_major_ver, key_api_minor_ver; + struct memory_type_mapping *mem_type_mapping_tbl; + u8 num_mem_types; + void *drv_info_dump; + u32 drv_info_size; + bool scan_chan_gap_enabled; + struct sk_buff_head rx_data_q; + struct mwifiex_chan_stats *chan_stats; + u32 num_in_chan_stats; + int survey_idx; + bool auto_tdls; + u8 coex_scan; + u8 coex_min_scan_time; + u8 coex_max_scan_time; + u8 coex_win_size; + u8 coex_tx_win_size; + u8 coex_rx_win_size; + bool drcs_enabled; + u8 active_scan_triggered; + bool usb_mc_status; + bool usb_mc_setup; +}; + +void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); + +int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); + +void mwifiex_set_trans_start(struct net_device *dev); + +void mwifiex_stop_net_dev_queue(struct net_device *netdev, + struct mwifiex_adapter *adapter); + +void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, + struct mwifiex_adapter *adapter); + +int mwifiex_init_priv(struct mwifiex_private *priv); +void mwifiex_free_priv(struct mwifiex_private *priv); + +int mwifiex_init_fw(struct mwifiex_adapter *adapter); + +int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter); + +int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter); + +int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter); + +int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *); + +int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb); + +int mwifiex_process_mgmt_packet(struct mwifiex_private *priv, + struct sk_buff *skb); + +int mwifiex_process_event(struct mwifiex_adapter *adapter); + +int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node); + +int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, + u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync); + +void mwifiex_cmd_timeout_func(unsigned long function_context); + +int mwifiex_get_debug_info(struct mwifiex_private *, + struct mwifiex_debug_info *); + +int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); +int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); +void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); +void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); + +void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node); +void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node); + +void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node, + u32 addtail); + +int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter); +int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter); +int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, + struct sk_buff *skb); +int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, + struct mwifiex_tx_param *tx_param); +int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags); +int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb, int aggr, int status); +void mwifiex_clean_txrx(struct mwifiex_private *priv); +u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv); +void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter); +void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *, + u32); +int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, uint16_t ps_bitmap, + struct mwifiex_ds_auto_ds *auto_ds); +int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct mwifiex_ds_pm_cfg *pm_cfg); +void mwifiex_process_hs_config(struct mwifiex_adapter *adapter); +void mwifiex_hs_activated_event(struct mwifiex_private *priv, + u8 activated); +int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, + int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg); +int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_process_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb); +int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no, + u16 cmd_action, u32 cmd_oid, + void *data_buf, void *cmd_buf); +int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, + u16 cmd_action, u32 cmd_oid, + void *data_buf, void *cmd_buf); +int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no, + struct host_cmd_ds_command *resp); +int mwifiex_process_sta_rx_packet(struct mwifiex_private *, + struct sk_buff *skb); +int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb); +int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, + struct sk_buff *skb); +int mwifiex_process_sta_event(struct mwifiex_private *); +int mwifiex_process_uap_event(struct mwifiex_private *); +void mwifiex_delete_all_station_list(struct mwifiex_private *priv); +void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, + const u8 *ra_addr); +void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); +void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); +int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init); +int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, + struct mwifiex_scan_cmd_config *scan_cfg); +void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, + struct cmd_ctrl_node *cmd_node); +int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +s32 mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2); +int mwifiex_associate(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason); +u8 mwifiex_band_to_radio_type(u8 band); +int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac); +void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter); +int mwifiex_adhoc_start(struct mwifiex_private *priv, + struct cfg80211_ssid *adhoc_ssid); +int mwifiex_adhoc_join(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct cfg80211_ssid *req_ssid); +int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd); +struct mwifiex_chan_freq_power *mwifiex_get_cfp(struct mwifiex_private *priv, + u8 band, u16 channel, u32 freq); +u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, + u8 index, u8 ht_info); +u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, + u8 index, u8 ht_info); +u32 mwifiex_find_freq_from_band_chan(u8, u8); +int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask, + u8 **buffer); +u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, + u8 *rates); +u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates); +u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, + u8 *rates, u8 radio_type); +u8 mwifiex_is_rate_auto(struct mwifiex_private *priv); +extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE]; +void mwifiex_save_curr_bcn(struct mwifiex_private *priv); +void mwifiex_free_curr_bcn(struct mwifiex_private *priv); +int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd); +int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int is_command_pending(struct mwifiex_adapter *adapter); +void mwifiex_init_priv_params(struct mwifiex_private *priv, + struct net_device *dev); +int mwifiex_set_secure_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_config, + struct cfg80211_ap_settings *params); +void mwifiex_set_ht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_vht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_tpc_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_vht_width(struct mwifiex_private *priv, + enum nl80211_chan_width width, + bool ap_11ac_disable); +void +mwifiex_set_wmm_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params); +void mwifiex_set_ba_params(struct mwifiex_private *priv); + +void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *pmadapter); +void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv, + struct sk_buff *event_skb); + +void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv); +int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp); +int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, + void *buf); + +/* + * This function checks if the queuing is RA based or not. + */ +static inline u8 +mwifiex_queuing_ra_based(struct mwifiex_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 == NL80211_IFTYPE_STATION) && + (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)) + return false; + + return true; +} + +/* + * This function copies rates. + */ +static inline u32 +mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= MWIFIEX_SUPPORTED_RATES) + break; + dest[pos] = src[i]; + } + + return pos; +} + +/* + * This function returns the correct private structure pointer based + * upon the BSS type and BSS number. + */ +static inline struct mwifiex_private * +mwifiex_get_priv_by_id(struct mwifiex_adapter *adapter, + u8 bss_num, u8 bss_type) +{ + int i; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + if ((adapter->priv[i]->bss_num == bss_num) && + (adapter->priv[i]->bss_type == bss_type)) + break; + } + } + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* + * This function returns the first available private structure pointer + * based upon the BSS role. + */ +static inline struct mwifiex_private * +mwifiex_get_priv(struct mwifiex_adapter *adapter, + enum mwifiex_bss_role bss_role) +{ + int i; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + if (bss_role == MWIFIEX_BSS_ROLE_ANY || + GET_BSS_ROLE(adapter->priv[i]) == bss_role) + break; + } + } + + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* + * This function returns the first available unused private structure pointer. + */ +static inline struct mwifiex_private * +mwifiex_get_unused_priv(struct mwifiex_adapter *adapter) +{ + int i; + + for (i = 0; i < adapter->priv_num; i++) { + if (adapter->priv[i]) { + if (adapter->priv[i]->bss_mode == + NL80211_IFTYPE_UNSPECIFIED) + break; + } + } + + return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); +} + +/* + * This function returns the driver private structure of a network device. + */ +static inline struct mwifiex_private * +mwifiex_netdev_get_priv(struct net_device *dev) +{ + return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev)); +} + +/* + * This function checks if a skb holds a management frame. + */ +static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb) +{ + return (le32_to_cpu(*(__le32 *)skb->data) == PKT_TYPE_MGMT); +} + +/* This function retrieves channel closed for operation by Channel + * Switch Announcement. + */ +static inline u8 +mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv) +{ + if (!priv->csa_chan) + return 0; + + /* Clear csa channel, if DFS channel move time has passed */ + if (time_after(jiffies, priv->csa_expire_time)) { + priv->csa_chan = 0; + priv->csa_expire_time = 0; + } + + return priv->csa_chan; +} + +static inline u8 mwifiex_is_any_intf_active(struct mwifiex_private *priv) +{ + struct mwifiex_private *priv_num; + int i; + + for (i = 0; i < priv->adapter->priv_num; i++) { + priv_num = priv->adapter->priv[i]; + if (priv_num) { + if ((GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_UAP && + priv_num->bss_started) || + (GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_STA && + priv_num->media_connected)) + return 1; + } + } + + return 0; +} + +static inline u8 mwifiex_is_tdls_link_setup(u8 status) +{ + switch (status) { + case TDLS_SETUP_COMPLETE: + case TDLS_CHAN_SWITCHING: + case TDLS_IN_BASE_CHAN: + case TDLS_IN_OFF_CHAN: + return true; + default: + break; + } + + return false; +} + +int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, + u32 func_init_shutdown); +int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8); +int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); + +void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, + int maxlen); +int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, + struct mwifiex_multicast_list *mcast_list); +int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, + struct net_device *dev); +int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_queued); +int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, + struct cfg80211_ssid *req_ssid); +int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type); +int mwifiex_enable_hs(struct mwifiex_adapter *adapter); +int mwifiex_disable_auto_ds(struct mwifiex_private *priv); +int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate); +int mwifiex_request_scan(struct mwifiex_private *priv, + struct cfg80211_ssid *req_ssid); +int mwifiex_scan_networks(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg *user_scan_in); +int mwifiex_set_radio(struct mwifiex_private *priv, u8 option); + +int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, + const u8 *key, int key_len, u8 key_index, + const u8 *mac_addr, int disable); + +int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len); + +int mwifiex_get_ver_ext(struct mwifiex_private *priv); + +int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, + struct ieee80211_channel *chan, + unsigned int duration); + +int mwifiex_get_stats_info(struct mwifiex_private *priv, + struct mwifiex_ds_get_stats *log); + +int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, + u32 reg_offset, u32 reg_value); + +int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, + u32 reg_offset, u32 *value); + +int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, + u8 *value); + +int mwifiex_set_11n_httx_cfg(struct mwifiex_private *priv, int data); + +int mwifiex_get_11n_httx_cfg(struct mwifiex_private *priv, int *data); + +int mwifiex_set_tx_rate_cfg(struct mwifiex_private *priv, int tx_rate_index); + +int mwifiex_get_tx_rate_cfg(struct mwifiex_private *priv, int *tx_rate_index); + +int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode); + +int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, + char *version, int max_len); + +int mwifiex_set_tx_power(struct mwifiex_private *priv, + struct mwifiex_power_cfg *power_cfg); + +int mwifiex_main_process(struct mwifiex_adapter *); + +int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb); + +int mwifiex_get_bss_info(struct mwifiex_private *, + struct mwifiex_bss_info *); +int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, + struct cfg80211_bss *bss, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, + struct mwifiex_bssdescriptor *bss_entry); +int mwifiex_check_network_compatibility(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc); + +u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type); +u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset); + +struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, + const char *name, + unsigned char name_assign_type, + enum nl80211_iftype type, + u32 *flags, + struct vif_params *params); +int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); + +void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config); + +int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter); + +int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, + struct cfg80211_beacon_data *data); +int mwifiex_del_mgmt_ies(struct mwifiex_private *priv); +u8 *mwifiex_11d_code_2_region(u8 code); +void mwifiex_uap_set_channel(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_chan_def chandef); +int mwifiex_config_start_uap(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg); +void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, + struct mwifiex_sta_node *node); + +void mwifiex_init_11h_params(struct mwifiex_private *priv); +int mwifiex_is_11h_active(struct mwifiex_private *priv); +int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag); + +void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, + struct mwifiex_bssdescriptor *bss_desc); +int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv); +int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, + struct device_node *node, const char *prefix); +void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv); + +extern const struct ethtool_ops mwifiex_ethtool_ops; + +void mwifiex_del_all_sta_list(struct mwifiex_private *priv); +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac); +void +mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, + int ies_len, struct mwifiex_sta_node *node); +struct mwifiex_sta_node * +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac); +struct mwifiex_sta_node * +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac); +u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv); +u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv); +u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv); +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len); +void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, + u8 *buf, int len); +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action); +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac); +int mwifiex_get_tdls_list(struct mwifiex_private *priv, + struct tdls_peer_info *buf); +void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); +bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); +u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, + u32 pri_chan, u8 chan_bw); +int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter); + +int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb); +void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv); +void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, + const u8 *mac, u8 link_status); +void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, + u8 *mac, s8 snr, s8 nflr); +void mwifiex_check_auto_tdls(unsigned long context); +void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac); +void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv); +void mwifiex_clean_auto_tdls(struct mwifiex_private *priv); +int mwifiex_config_tdls_enable(struct mwifiex_private *priv); +int mwifiex_config_tdls_disable(struct mwifiex_private *priv); +int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv); +int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac); +int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac, + u8 primary_chan, u8 second_chan_offset, u8 band); + +int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf); +int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, + struct sk_buff *skb); + +void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, + void *event_body); + +struct sk_buff * +mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, + struct sk_buff *skb, u8 flag, u64 *cookie); +void mwifiex_dfs_cac_work_queue(struct work_struct *work); +void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work); +void mwifiex_abort_cac(struct mwifiex_private *priv); +int mwifiex_stop_radar_detection(struct mwifiex_private *priv, + struct cfg80211_chan_def *chandef); +int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, + struct sk_buff *skb); + +void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, + s8 nflr); +void mwifiex_hist_data_reset(struct mwifiex_private *priv); +void mwifiex_hist_data_add(struct mwifiex_private *priv, + u8 rx_rate, s8 snr, s8 nflr); +u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, + u8 rx_rate, u8 ht_info); + +void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter); +void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter); +void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); +void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); +void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter); +void mwifiex_11n_delba(struct mwifiex_private *priv, int tid); +int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy); +void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, + struct sk_buff *event); +void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, + struct sk_buff *event_skb); +void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter); + +#ifdef CONFIG_DEBUG_FS +void mwifiex_debugfs_init(void); +void mwifiex_debugfs_remove(void); + +void mwifiex_dev_debugfs_init(struct mwifiex_private *priv); +void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv); +#endif +#endif /* !_MWIFIEX_MAIN_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c new file mode 100644 index 000000000000..21192b6f9c64 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -0,0 +1,2742 @@ +/* + * Marvell Wireless LAN device driver: PCIE specific handling + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "pcie.h" + +#define PCIE_VERSION "1.0" +#define DRV_NAME "Marvell mwifiex PCIe" + +static u8 user_rmmod; + +static struct mwifiex_if_ops pcie_ops; + +static struct semaphore add_remove_card_sem; + +static struct memory_type_mapping mem_type_mapping_tbl[] = { + {"ITCM", NULL, 0, 0xF0}, + {"DTCM", NULL, 0, 0xF1}, + {"SQRAM", NULL, 0, 0xF2}, + {"IRAM", NULL, 0, 0xF3}, + {"APU", NULL, 0, 0xF4}, + {"CIU", NULL, 0, 0xF5}, + {"ICU", NULL, 0, 0xF6}, + {"MAC", NULL, 0, 0xF7}, +}; + +static int +mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, + size_t size, int flags) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_dma_mapping mapping; + + mapping.addr = pci_map_single(card->dev, skb->data, size, flags); + if (pci_dma_mapping_error(card->dev, mapping.addr)) { + mwifiex_dbg(adapter, ERROR, "failed to map pci memory!\n"); + return -1; + } + mapping.len = size; + mwifiex_store_mapping(skb, &mapping); + return 0; +} + +static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter, + struct sk_buff *skb, int flags) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_dma_mapping mapping; + + mwifiex_get_mapping(skb, &mapping); + pci_unmap_single(card->dev, mapping.addr, mapping.len, flags); +} + +/* + * This function reads sleep cookie and checks if FW is ready + */ +static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) +{ + u32 *cookie_addr; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (!reg->sleep_cookie) + return true; + + if (card->sleep_cookie_vbase) { + cookie_addr = (u32 *)card->sleep_cookie_vbase; + mwifiex_dbg(adapter, INFO, + "info: ACCESS_HW: sleep cookie=0x%x\n", + *cookie_addr); + if (*cookie_addr == FW_AWAKE_COOKIE) + return true; + } + + return false; +} + +#ifdef CONFIG_PM_SLEEP +/* + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not suspended, this function allocates and sends a host + * sleep activate request to the firmware and turns off the traffic. + */ +static int mwifiex_pcie_suspend(struct device *dev) +{ + struct mwifiex_adapter *adapter; + struct pcie_service_card *card; + int hs_actived; + struct pci_dev *pdev = to_pci_dev(dev); + + if (pdev) { + card = pci_get_drvdata(pdev); + if (!card || !card->adapter) { + pr_err("Card or adapter structure is not valid\n"); + return 0; + } + } else { + pr_err("PCIE device is not specified\n"); + return 0; + } + + adapter = card->adapter; + + hs_actived = mwifiex_enable_hs(adapter); + + /* Indicate device suspended */ + adapter->is_suspended = true; + adapter->hs_enabling = false; + + return 0; +} + +/* + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not resumed, this function turns on the traffic and + * sends a host sleep cancel request to the firmware. + */ +static int mwifiex_pcie_resume(struct device *dev) +{ + struct mwifiex_adapter *adapter; + struct pcie_service_card *card; + struct pci_dev *pdev = to_pci_dev(dev); + + if (pdev) { + card = pci_get_drvdata(pdev); + if (!card || !card->adapter) { + pr_err("Card or adapter structure is not valid\n"); + return 0; + } + } else { + pr_err("PCIE device is not specified\n"); + return 0; + } + + adapter = card->adapter; + + if (!adapter->is_suspended) { + mwifiex_dbg(adapter, WARN, + "Device already resumed\n"); + return 0; + } + + adapter->is_suspended = false; + + mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), + MWIFIEX_ASYNC_CMD); + + return 0; +} +#endif + +/* + * This function probes an mwifiex device and registers it. It allocates + * the card structure, enables PCIE function number and initiates the + * device registration and initialization procedure by adding a logical + * interface. + */ +static int mwifiex_pcie_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct pcie_service_card *card; + + pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", + pdev->vendor, pdev->device, pdev->revision); + + card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->dev = pdev; + + if (ent->driver_data) { + struct mwifiex_pcie_device *data = (void *)ent->driver_data; + card->pcie.firmware = data->firmware; + card->pcie.reg = data->reg; + card->pcie.blksz_fw_dl = data->blksz_fw_dl; + card->pcie.tx_buf_size = data->tx_buf_size; + card->pcie.can_dump_fw = data->can_dump_fw; + card->pcie.can_ext_scan = data->can_ext_scan; + } + + if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, + MWIFIEX_PCIE)) { + pr_err("%s failed\n", __func__); + kfree(card); + return -1; + } + + return 0; +} + +/* + * This function removes the interface and frees up the card structure. + */ +static void mwifiex_pcie_remove(struct pci_dev *pdev) +{ + struct pcie_service_card *card; + struct mwifiex_adapter *adapter; + struct mwifiex_private *priv; + + card = pci_get_drvdata(pdev); + if (!card) + return; + + adapter = card->adapter; + if (!adapter || !adapter->priv_num) + return; + + if (user_rmmod) { +#ifdef CONFIG_PM_SLEEP + if (adapter->is_suspended) + mwifiex_pcie_resume(&pdev->dev); +#endif + + mwifiex_deauthenticate_all(adapter); + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + mwifiex_disable_auto_ds(priv); + + mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); + } + + mwifiex_remove_card(card->adapter, &add_remove_card_sem); +} + +static void mwifiex_pcie_shutdown(struct pci_dev *pdev) +{ + user_rmmod = 1; + mwifiex_pcie_remove(pdev); + + return; +} + +static const struct pci_device_id mwifiex_ids[] = { + { + PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + .driver_data = (unsigned long)&mwifiex_pcie8766, + }, + { + PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8897, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + .driver_data = (unsigned long)&mwifiex_pcie8897, + }, + { + PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8997, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, + .driver_data = (unsigned long)&mwifiex_pcie8997, + }, + {}, +}; + +MODULE_DEVICE_TABLE(pci, mwifiex_ids); + +#ifdef CONFIG_PM_SLEEP +/* Power Management Hooks */ +static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend, + mwifiex_pcie_resume); +#endif + +/* PCI Device Driver */ +static struct pci_driver __refdata mwifiex_pcie = { + .name = "mwifiex_pcie", + .id_table = mwifiex_ids, + .probe = mwifiex_pcie_probe, + .remove = mwifiex_pcie_remove, +#ifdef CONFIG_PM_SLEEP + .driver = { + .pm = &mwifiex_pcie_pm_ops, + }, +#endif + .shutdown = mwifiex_pcie_shutdown, +}; + +/* + * This function writes data into PCIE card register. + */ +static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data) +{ + struct pcie_service_card *card = adapter->card; + + iowrite32(data, card->pci_mmap1 + reg); + + return 0; +} + +/* + * This function reads data from PCIE card register. + */ +static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) +{ + struct pcie_service_card *card = adapter->card; + + *data = ioread32(card->pci_mmap1 + reg); + + return 0; +} + +/* This function reads u8 data from PCIE card register. */ +static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, + int reg, u8 *data) +{ + struct pcie_service_card *card = adapter->card; + + *data = ioread8(card->pci_mmap1 + reg); + + return 0; +} + +/* + * This function adds delay loop to ensure FW is awake before proceeding. + */ +static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter) +{ + int i = 0; + + while (mwifiex_pcie_ok_to_access_hw(adapter)) { + i++; + usleep_range(10, 20); + /* 50ms max wait */ + if (i == 5000) + break; + } + + return; +} + +static void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter, + u32 max_delay_loop_cnt) +{ + struct pcie_service_card *card = adapter->card; + u8 *buffer; + u32 sleep_cookie, count; + + for (count = 0; count < max_delay_loop_cnt; count++) { + buffer = card->cmdrsp_buf->data - INTF_HEADER_LEN; + sleep_cookie = *(u32 *)buffer; + + if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) { + mwifiex_dbg(adapter, INFO, + "sleep cookie found at count %d\n", count); + break; + } + usleep_range(20, 30); + } + + if (count >= max_delay_loop_cnt) + mwifiex_dbg(adapter, INFO, + "max count reached while accessing sleep cookie\n"); +} + +/* This function wakes up the card by reading fw_status register. */ +static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) +{ + u32 fw_status; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + mwifiex_dbg(adapter, EVENT, + "event: Wakeup device...\n"); + + if (reg->sleep_cookie) + mwifiex_pcie_dev_wakeup_delay(adapter); + + /* Reading fw_status register will wakeup device */ + if (mwifiex_read_reg(adapter, reg->fw_status, &fw_status)) { + mwifiex_dbg(adapter, ERROR, + "Reading fw_status register failed\n"); + return -1; + } + + if (reg->sleep_cookie) { + mwifiex_pcie_dev_wakeup_delay(adapter); + mwifiex_dbg(adapter, INFO, + "PCIE wakeup: Setting PS_STATE_AWAKE\n"); + adapter->ps_state = PS_STATE_AWAKE; + } + + return 0; +} + +/* + * This function is called after the card has woken up. + * + * The card configuration register is reset. + */ +static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) +{ + mwifiex_dbg(adapter, CMD, + "cmd: Wakeup device completed\n"); + + return 0; +} + +/* + * This function disables the host interrupt. + * + * The host interrupt mask is read, the disable bit is reset and + * written back to the card host interrupt mask register. + */ +static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter) +{ + if (mwifiex_pcie_ok_to_access_hw(adapter)) { + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, + 0x00000000)) { + mwifiex_dbg(adapter, ERROR, + "Disable host interrupt failed\n"); + return -1; + } + } + + return 0; +} + +/* + * This function enables the host interrupt. + * + * The host interrupt enable mask is written to the card + * host interrupt mask register. + */ +static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter) +{ + if (mwifiex_pcie_ok_to_access_hw(adapter)) { + /* Simply write the mask to the register */ + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, + HOST_INTR_MASK)) { + mwifiex_dbg(adapter, ERROR, + "Enable host interrupt failed\n"); + return -1; + } + } + + return 0; +} + +/* + * This function initializes TX buffer ring descriptors + */ +static int mwifiex_init_txq_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + card->tx_buf_list[i] = NULL; + if (reg->pfu_enabled) { + card->txbd_ring[i] = (void *)card->txbd_ring_vbase + + (sizeof(*desc2) * i); + desc2 = card->txbd_ring[i]; + memset(desc2, 0, sizeof(*desc2)); + } else { + card->txbd_ring[i] = (void *)card->txbd_ring_vbase + + (sizeof(*desc) * i); + desc = card->txbd_ring[i]; + memset(desc, 0, sizeof(*desc)); + } + } + + return 0; +} + +/* This function initializes RX buffer ring descriptors. Each SKB is allocated + * here and after mapping PCI memory, its physical address is assigned to + * PCIE Rx buffer descriptor's physical address. + */ +static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct sk_buff *skb; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + dma_addr_t buf_pa; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + /* Allocate skb here so that firmware can DMA data from it */ + skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "Unable to allocate skb for RX ring.\n"); + kfree(card->rxbd_ring_vbase); + return -ENOMEM; + } + + if (mwifiex_map_pci_memory(adapter, skb, + MWIFIEX_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); + + mwifiex_dbg(adapter, INFO, + "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", + skb, skb->len, skb->data, (u32)buf_pa, + (u32)((u64)buf_pa >> 32)); + + card->rx_buf_list[i] = skb; + if (reg->pfu_enabled) { + card->rxbd_ring[i] = (void *)card->rxbd_ring_vbase + + (sizeof(*desc2) * i); + desc2 = card->rxbd_ring[i]; + desc2->paddr = buf_pa; + desc2->len = (u16)skb->len; + desc2->frag_len = (u16)skb->len; + desc2->flags = reg->ring_flag_eop | reg->ring_flag_sop; + desc2->offset = 0; + } else { + card->rxbd_ring[i] = (void *)(card->rxbd_ring_vbase + + (sizeof(*desc) * i)); + desc = card->rxbd_ring[i]; + desc->paddr = buf_pa; + desc->len = (u16)skb->len; + desc->flags = 0; + } + } + + return 0; +} + +/* This function initializes event buffer ring descriptors. Each SKB is + * allocated here and after mapping PCI memory, its physical address is assigned + * to PCIE Rx buffer descriptor's physical address + */ +static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_evt_buf_desc *desc; + struct sk_buff *skb; + dma_addr_t buf_pa; + int i; + + for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { + /* Allocate skb here so that firmware can DMA data from it */ + skb = dev_alloc_skb(MAX_EVENT_SIZE); + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "Unable to allocate skb for EVENT buf.\n"); + kfree(card->evtbd_ring_vbase); + return -ENOMEM; + } + skb_put(skb, MAX_EVENT_SIZE); + + if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); + + mwifiex_dbg(adapter, EVENT, + "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", + skb, skb->len, skb->data, (u32)buf_pa, + (u32)((u64)buf_pa >> 32)); + + card->evt_buf_list[i] = skb; + card->evtbd_ring[i] = (void *)(card->evtbd_ring_vbase + + (sizeof(*desc) * i)); + desc = card->evtbd_ring[i]; + desc->paddr = buf_pa; + desc->len = (u16)skb->len; + desc->flags = 0; + } + + return 0; +} + +/* This function cleans up TX buffer rings. If any of the buffer list has valid + * SKB address, associated SKB is freed. + */ +static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct sk_buff *skb; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + if (reg->pfu_enabled) { + desc2 = card->txbd_ring[i]; + if (card->tx_buf_list[i]) { + skb = card->tx_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); + dev_kfree_skb_any(skb); + } + memset(desc2, 0, sizeof(*desc2)); + } else { + desc = card->txbd_ring[i]; + if (card->tx_buf_list[i]) { + skb = card->tx_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); + dev_kfree_skb_any(skb); + } + memset(desc, 0, sizeof(*desc)); + } + card->tx_buf_list[i] = NULL; + } + + return; +} + +/* This function cleans up RX buffer rings. If any of the buffer list has valid + * SKB address, associated SKB is freed. + */ +static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + struct sk_buff *skb; + int i; + + for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { + if (reg->pfu_enabled) { + desc2 = card->rxbd_ring[i]; + if (card->rx_buf_list[i]) { + skb = card->rx_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + } + memset(desc2, 0, sizeof(*desc2)); + } else { + desc = card->rxbd_ring[i]; + if (card->rx_buf_list[i]) { + skb = card->rx_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + } + memset(desc, 0, sizeof(*desc)); + } + card->rx_buf_list[i] = NULL; + } + + return; +} + +/* This function cleans up event buffer rings. If any of the buffer list has + * valid SKB address, associated SKB is freed. + */ +static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct mwifiex_evt_buf_desc *desc; + struct sk_buff *skb; + int i; + + for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { + desc = card->evtbd_ring[i]; + if (card->evt_buf_list[i]) { + skb = card->evt_buf_list[i]; + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + } + card->evt_buf_list[i] = NULL; + memset(desc, 0, sizeof(*desc)); + } + + return; +} + +/* This function creates buffer descriptor ring for TX + */ +static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + /* + * driver maintaines the write pointer and firmware maintaines the read + * pointer. The write pointer starts at 0 (zero) while the read pointer + * starts at zero with rollover bit set + */ + card->txbd_wrptr = 0; + + if (reg->pfu_enabled) + card->txbd_rdptr = 0; + else + card->txbd_rdptr |= reg->tx_rollover_ind; + + /* allocate shared memory for the BD ring and divide the same in to + several descriptors */ + if (reg->pfu_enabled) + card->txbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + else + card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + + mwifiex_dbg(adapter, INFO, + "info: txbd_ring: Allocating %d bytes\n", + card->txbd_ring_size); + card->txbd_ring_vbase = pci_alloc_consistent(card->dev, + card->txbd_ring_size, + &card->txbd_ring_pbase); + if (!card->txbd_ring_vbase) { + mwifiex_dbg(adapter, ERROR, + "allocate consistent memory (%d bytes) failed!\n", + card->txbd_ring_size); + return -ENOMEM; + } + mwifiex_dbg(adapter, DATA, + "info: txbd_ring - base: %p, pbase: %#x:%x, len: %x\n", + card->txbd_ring_vbase, (unsigned int)card->txbd_ring_pbase, + (u32)((u64)card->txbd_ring_pbase >> 32), + card->txbd_ring_size); + + return mwifiex_init_txq_ring(adapter); +} + +static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + mwifiex_cleanup_txq_ring(adapter); + + if (card->txbd_ring_vbase) + pci_free_consistent(card->dev, card->txbd_ring_size, + card->txbd_ring_vbase, + card->txbd_ring_pbase); + card->txbd_ring_size = 0; + card->txbd_wrptr = 0; + card->txbd_rdptr = 0 | reg->tx_rollover_ind; + card->txbd_ring_vbase = NULL; + card->txbd_ring_pbase = 0; + + return 0; +} + +/* + * This function creates buffer descriptor ring for RX + */ +static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + /* + * driver maintaines the read pointer and firmware maintaines the write + * pointer. The write pointer starts at 0 (zero) while the read pointer + * starts at zero with rollover bit set + */ + card->rxbd_wrptr = 0; + card->rxbd_rdptr = reg->rx_rollover_ind; + + if (reg->pfu_enabled) + card->rxbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + else + card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * + MWIFIEX_MAX_TXRX_BD; + + mwifiex_dbg(adapter, INFO, + "info: rxbd_ring: Allocating %d bytes\n", + card->rxbd_ring_size); + card->rxbd_ring_vbase = pci_alloc_consistent(card->dev, + card->rxbd_ring_size, + &card->rxbd_ring_pbase); + if (!card->rxbd_ring_vbase) { + mwifiex_dbg(adapter, ERROR, + "allocate consistent memory (%d bytes) failed!\n", + card->rxbd_ring_size); + return -ENOMEM; + } + + mwifiex_dbg(adapter, DATA, + "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n", + card->rxbd_ring_vbase, (u32)card->rxbd_ring_pbase, + (u32)((u64)card->rxbd_ring_pbase >> 32), + card->rxbd_ring_size); + + return mwifiex_init_rxq_ring(adapter); +} + +/* + * This function deletes Buffer descriptor ring for RX + */ +static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + mwifiex_cleanup_rxq_ring(adapter); + + if (card->rxbd_ring_vbase) + pci_free_consistent(card->dev, card->rxbd_ring_size, + card->rxbd_ring_vbase, + card->rxbd_ring_pbase); + card->rxbd_ring_size = 0; + card->rxbd_wrptr = 0; + card->rxbd_rdptr = 0 | reg->rx_rollover_ind; + card->rxbd_ring_vbase = NULL; + card->rxbd_ring_pbase = 0; + + return 0; +} + +/* + * This function creates buffer descriptor ring for Events + */ +static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + /* + * driver maintaines the read pointer and firmware maintaines the write + * pointer. The write pointer starts at 0 (zero) while the read pointer + * starts at zero with rollover bit set + */ + card->evtbd_wrptr = 0; + card->evtbd_rdptr = reg->evt_rollover_ind; + + card->evtbd_ring_size = sizeof(struct mwifiex_evt_buf_desc) * + MWIFIEX_MAX_EVT_BD; + + mwifiex_dbg(adapter, INFO, + "info: evtbd_ring: Allocating %d bytes\n", + card->evtbd_ring_size); + card->evtbd_ring_vbase = pci_alloc_consistent(card->dev, + card->evtbd_ring_size, + &card->evtbd_ring_pbase); + if (!card->evtbd_ring_vbase) { + mwifiex_dbg(adapter, ERROR, + "allocate consistent memory (%d bytes) failed!\n", + card->evtbd_ring_size); + return -ENOMEM; + } + + mwifiex_dbg(adapter, EVENT, + "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n", + card->evtbd_ring_vbase, (u32)card->evtbd_ring_pbase, + (u32)((u64)card->evtbd_ring_pbase >> 32), + card->evtbd_ring_size); + + return mwifiex_pcie_init_evt_ring(adapter); +} + +/* + * This function deletes Buffer descriptor ring for Events + */ +static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + mwifiex_cleanup_evt_ring(adapter); + + if (card->evtbd_ring_vbase) + pci_free_consistent(card->dev, card->evtbd_ring_size, + card->evtbd_ring_vbase, + card->evtbd_ring_pbase); + card->evtbd_wrptr = 0; + card->evtbd_rdptr = 0 | reg->evt_rollover_ind; + card->evtbd_ring_size = 0; + card->evtbd_ring_vbase = NULL; + card->evtbd_ring_pbase = 0; + + return 0; +} + +/* + * This function allocates a buffer for CMDRSP + */ +static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct sk_buff *skb; + + /* Allocate memory for receiving command response data */ + skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "Unable to allocate skb for command response data.\n"); + return -ENOMEM; + } + skb_put(skb, MWIFIEX_UPLD_SIZE); + if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + card->cmdrsp_buf = skb; + + return 0; +} + +/* + * This function deletes a buffer for CMDRSP + */ +static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card; + + if (!adapter) + return 0; + + card = adapter->card; + + if (card && card->cmdrsp_buf) { + mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(card->cmdrsp_buf); + } + + if (card && card->cmd_buf) { + mwifiex_unmap_pci_memory(adapter, card->cmd_buf, + PCI_DMA_TODEVICE); + } + return 0; +} + +/* + * This function allocates a buffer for sleep cookie + */ +static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + + card->sleep_cookie_vbase = pci_alloc_consistent(card->dev, sizeof(u32), + &card->sleep_cookie_pbase); + if (!card->sleep_cookie_vbase) { + mwifiex_dbg(adapter, ERROR, + "pci_alloc_consistent failed!\n"); + return -ENOMEM; + } + /* Init val of Sleep Cookie */ + *(u32 *)card->sleep_cookie_vbase = FW_AWAKE_COOKIE; + + mwifiex_dbg(adapter, INFO, + "alloc_scook: sleep cookie=0x%x\n", + *((u32 *)card->sleep_cookie_vbase)); + + return 0; +} + +/* + * This function deletes buffer for sleep cookie + */ +static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card; + + if (!adapter) + return 0; + + card = adapter->card; + + if (card && card->sleep_cookie_vbase) { + pci_free_consistent(card->dev, sizeof(u32), + card->sleep_cookie_vbase, + card->sleep_cookie_pbase); + card->sleep_cookie_vbase = NULL; + } + + return 0; +} + +/* This function flushes the TX buffer descriptor ring + * This function defined as handler is also called while cleaning TXRX + * during disconnect/ bss stop. + */ +static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + + if (!mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) { + card->txbd_flush = 1; + /* write pointer already set at last send + * send dnld-rdy intr again, wait for completion. + */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DNLD_RDY)) { + mwifiex_dbg(adapter, ERROR, + "failed to assert dnld-rdy interrupt.\n"); + return -1; + } + } + return 0; +} + +/* + * This function unmaps and frees downloaded data buffer + */ +static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) +{ + struct sk_buff *skb; + u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + /* Read the TX ring read pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->tx_rdptr, &rdptr)) { + mwifiex_dbg(adapter, ERROR, + "SEND COMP: failed to read reg->tx_rdptr\n"); + return -1; + } + + mwifiex_dbg(adapter, DATA, + "SEND COMP: rdptr_prev=0x%x, rdptr=0x%x\n", + card->txbd_rdptr, rdptr); + + num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; + /* free from previous txbd_rdptr to current txbd_rdptr */ + while (((card->txbd_rdptr & reg->tx_mask) != + (rdptr & reg->tx_mask)) || + ((card->txbd_rdptr & reg->tx_rollover_ind) != + (rdptr & reg->tx_rollover_ind))) { + wrdoneidx = (card->txbd_rdptr & reg->tx_mask) >> + reg->tx_start_ptr; + + skb = card->tx_buf_list[wrdoneidx]; + + if (skb) { + mwifiex_dbg(adapter, DATA, + "SEND COMP: Detach skb %p at txbd_rdidx=%d\n", + skb, wrdoneidx); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); + + unmap_count++; + + if (card->txbd_flush) + mwifiex_write_data_complete(adapter, skb, 0, + -1); + else + mwifiex_write_data_complete(adapter, skb, 0, 0); + } + + card->tx_buf_list[wrdoneidx] = NULL; + + if (reg->pfu_enabled) { + desc2 = card->txbd_ring[wrdoneidx]; + memset(desc2, 0, sizeof(*desc2)); + } else { + desc = card->txbd_ring[wrdoneidx]; + memset(desc, 0, sizeof(*desc)); + } + switch (card->dev->device) { + case PCIE_DEVICE_ID_MARVELL_88W8766P: + card->txbd_rdptr++; + break; + case PCIE_DEVICE_ID_MARVELL_88W8897: + case PCIE_DEVICE_ID_MARVELL_88W8997: + card->txbd_rdptr += reg->ring_tx_start_ptr; + break; + } + + + if ((card->txbd_rdptr & reg->tx_mask) == num_tx_buffs) + card->txbd_rdptr = ((card->txbd_rdptr & + reg->tx_rollover_ind) ^ + reg->tx_rollover_ind); + } + + if (unmap_count) + adapter->data_sent = false; + + if (card->txbd_flush) { + if (mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) + card->txbd_flush = 0; + else + mwifiex_clean_pcie_ring_buf(adapter); + } + + return 0; +} + +/* This function sends data buffer to device. First 4 bytes of payload + * are filled with payload length and payload type. Then this payload + * is mapped to PCI device memory. Tx ring pointers are advanced accordingly. + * Download ready interrupt to FW is deffered if Tx ring is not full and + * additional payload can be accomodated. + * Caller must ensure tx_param parameter to this function is not NULL. + */ +static int +mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + u32 wrindx, num_tx_buffs, rx_val; + int ret; + dma_addr_t buf_pa; + struct mwifiex_pcie_buf_desc *desc = NULL; + struct mwifiex_pfu_buf_desc *desc2 = NULL; + __le16 *tmp; + + if (!(skb->data && skb->len)) { + mwifiex_dbg(adapter, ERROR, + "%s(): invalid parameter <%p, %#x>\n", + __func__, skb->data, skb->len); + return -1; + } + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; + mwifiex_dbg(adapter, DATA, + "info: SEND DATA: \n", + card->txbd_rdptr, card->txbd_wrptr); + if (mwifiex_pcie_txbd_not_full(card)) { + u8 *payload; + + adapter->data_sent = true; + payload = skb->data; + tmp = (__le16 *)&payload[0]; + *tmp = cpu_to_le16((u16)skb->len); + tmp = (__le16 *)&payload[2]; + *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); + + if (mwifiex_map_pci_memory(adapter, skb, skb->len, + PCI_DMA_TODEVICE)) + return -1; + + wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr; + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); + card->tx_buf_list[wrindx] = skb; + + if (reg->pfu_enabled) { + desc2 = card->txbd_ring[wrindx]; + desc2->paddr = buf_pa; + desc2->len = (u16)skb->len; + desc2->frag_len = (u16)skb->len; + desc2->offset = 0; + desc2->flags = MWIFIEX_BD_FLAG_FIRST_DESC | + MWIFIEX_BD_FLAG_LAST_DESC; + } else { + desc = card->txbd_ring[wrindx]; + desc->paddr = buf_pa; + desc->len = (u16)skb->len; + desc->flags = MWIFIEX_BD_FLAG_FIRST_DESC | + MWIFIEX_BD_FLAG_LAST_DESC; + } + + switch (card->dev->device) { + case PCIE_DEVICE_ID_MARVELL_88W8766P: + card->txbd_wrptr++; + break; + case PCIE_DEVICE_ID_MARVELL_88W8897: + case PCIE_DEVICE_ID_MARVELL_88W8997: + card->txbd_wrptr += reg->ring_tx_start_ptr; + break; + } + + if ((card->txbd_wrptr & reg->tx_mask) == num_tx_buffs) + card->txbd_wrptr = ((card->txbd_wrptr & + reg->tx_rollover_ind) ^ + reg->tx_rollover_ind); + + rx_val = card->rxbd_rdptr & reg->rx_wrap_mask; + /* Write the TX ring write pointer in to reg->tx_wrptr */ + if (mwifiex_write_reg(adapter, reg->tx_wrptr, + card->txbd_wrptr | rx_val)) { + mwifiex_dbg(adapter, ERROR, + "SEND DATA: failed to write reg->tx_wrptr\n"); + ret = -1; + goto done_unmap; + } + if ((mwifiex_pcie_txbd_not_full(card)) && + tx_param->next_pkt_len) { + /* have more packets and TxBD still can hold more */ + mwifiex_dbg(adapter, DATA, + "SEND DATA: delay dnld-rdy interrupt.\n"); + adapter->data_sent = false; + } else { + /* Send the TX ready interrupt */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DNLD_RDY)) { + mwifiex_dbg(adapter, ERROR, + "SEND DATA: failed to assert dnld-rdy interrupt.\n"); + ret = -1; + goto done_unmap; + } + } + mwifiex_dbg(adapter, DATA, + "info: SEND DATA: Updated and sent packet to firmware successfully\n", + card->txbd_rdptr, card->txbd_wrptr); + } else { + mwifiex_dbg(adapter, DATA, + "info: TX Ring full, can't send packets to fw\n"); + adapter->data_sent = true; + /* Send the TX ready interrupt */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DNLD_RDY)) + mwifiex_dbg(adapter, ERROR, + "SEND DATA: failed to assert door-bell intr\n"); + return -EBUSY; + } + + return -EINPROGRESS; +done_unmap: + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + card->tx_buf_list[wrindx] = NULL; + if (reg->pfu_enabled) + memset(desc2, 0, sizeof(*desc2)); + else + memset(desc, 0, sizeof(*desc)); + + return ret; +} + +/* + * This function handles received buffer ring and + * dispatches packets to upper + */ +static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + u32 wrptr, rd_index, tx_val; + dma_addr_t buf_pa; + int ret = 0; + struct sk_buff *skb_tmp = NULL; + struct mwifiex_pcie_buf_desc *desc; + struct mwifiex_pfu_buf_desc *desc2; + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + /* Read the RX ring Write pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { + mwifiex_dbg(adapter, ERROR, + "RECV DATA: failed to read reg->rx_wrptr\n"); + ret = -1; + goto done; + } + card->rxbd_wrptr = wrptr; + + while (((wrptr & reg->rx_mask) != + (card->rxbd_rdptr & reg->rx_mask)) || + ((wrptr & reg->rx_rollover_ind) == + (card->rxbd_rdptr & reg->rx_rollover_ind))) { + struct sk_buff *skb_data; + u16 rx_len; + __le16 pkt_len; + + rd_index = card->rxbd_rdptr & reg->rx_mask; + skb_data = card->rx_buf_list[rd_index]; + + /* If skb allocation was failed earlier for Rx packet, + * rx_buf_list[rd_index] would have been left with a NULL. + */ + if (!skb_data) + return -ENOMEM; + + mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE); + card->rx_buf_list[rd_index] = NULL; + + /* Get data length from interface header - + * first 2 bytes for len, next 2 bytes is for type + */ + pkt_len = *((__le16 *)skb_data->data); + rx_len = le16_to_cpu(pkt_len); + if (WARN_ON(rx_len <= INTF_HEADER_LEN || + rx_len > MWIFIEX_RX_DATA_BUF_SIZE)) { + mwifiex_dbg(adapter, ERROR, + "Invalid RX len %d, Rd=%#x, Wr=%#x\n", + rx_len, card->rxbd_rdptr, wrptr); + dev_kfree_skb_any(skb_data); + } else { + skb_put(skb_data, rx_len); + mwifiex_dbg(adapter, DATA, + "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n", + card->rxbd_rdptr, wrptr, rx_len); + skb_pull(skb_data, INTF_HEADER_LEN); + if (adapter->rx_work_enabled) { + skb_queue_tail(&adapter->rx_data_q, skb_data); + adapter->data_received = true; + atomic_inc(&adapter->rx_pending); + } else { + mwifiex_handle_rx_packet(adapter, skb_data); + } + } + + skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, + GFP_KERNEL | GFP_DMA); + if (!skb_tmp) { + mwifiex_dbg(adapter, ERROR, + "Unable to allocate skb.\n"); + return -ENOMEM; + } + + if (mwifiex_map_pci_memory(adapter, skb_tmp, + MWIFIEX_RX_DATA_BUF_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp); + + mwifiex_dbg(adapter, INFO, + "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n", + skb_tmp, rd_index); + card->rx_buf_list[rd_index] = skb_tmp; + + if (reg->pfu_enabled) { + desc2 = card->rxbd_ring[rd_index]; + desc2->paddr = buf_pa; + desc2->len = skb_tmp->len; + desc2->frag_len = skb_tmp->len; + desc2->offset = 0; + desc2->flags = reg->ring_flag_sop | reg->ring_flag_eop; + } else { + desc = card->rxbd_ring[rd_index]; + desc->paddr = buf_pa; + desc->len = skb_tmp->len; + desc->flags = 0; + } + + if ((++card->rxbd_rdptr & reg->rx_mask) == + MWIFIEX_MAX_TXRX_BD) { + card->rxbd_rdptr = ((card->rxbd_rdptr & + reg->rx_rollover_ind) ^ + reg->rx_rollover_ind); + } + mwifiex_dbg(adapter, DATA, + "info: RECV DATA: \n", + card->rxbd_rdptr, wrptr); + + tx_val = card->txbd_wrptr & reg->tx_wrap_mask; + /* Write the RX ring read pointer in to reg->rx_rdptr */ + if (mwifiex_write_reg(adapter, reg->rx_rdptr, + card->rxbd_rdptr | tx_val)) { + mwifiex_dbg(adapter, DATA, + "RECV DATA: failed to write reg->rx_rdptr\n"); + ret = -1; + goto done; + } + + /* Read the RX ring Write pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { + mwifiex_dbg(adapter, ERROR, + "RECV DATA: failed to read reg->rx_wrptr\n"); + ret = -1; + goto done; + } + mwifiex_dbg(adapter, DATA, + "info: RECV DATA: Rcvd packet from fw successfully\n"); + card->rxbd_wrptr = wrptr; + } + +done: + return ret; +} + +/* + * This function downloads the boot command to device + */ +static int +mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) +{ + dma_addr_t buf_pa; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (!(skb->data && skb->len)) { + mwifiex_dbg(adapter, ERROR, + "Invalid parameter in %s <%p. len %d>\n", + __func__, skb->data, skb->len); + return -1; + } + + if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE)) + return -1; + + buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); + + /* Write the lower 32bits of the physical address to low command + * address scratch register + */ + if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)buf_pa)) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to write download command to boot code.\n", + __func__); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + return -1; + } + + /* Write the upper 32bits of the physical address to high command + * address scratch register + */ + if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, + (u32)((u64)buf_pa >> 32))) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to write download command to boot code.\n", + __func__); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + return -1; + } + + /* Write the command length to cmd_size scratch register */ + if (mwifiex_write_reg(adapter, reg->cmd_size, skb->len)) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to write command len to cmd_size scratch reg\n", + __func__); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + return -1; + } + + /* Ring the door bell */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DOOR_BELL)) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to assert door-bell intr\n", __func__); + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + return -1; + } + + return 0; +} + +/* This function init rx port in firmware which in turn enables to receive data + * from device before transmitting any packet. + */ +static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask; + + /* Write the RX ring read pointer in to reg->rx_rdptr */ + if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | + tx_wrap)) { + mwifiex_dbg(adapter, ERROR, + "RECV DATA: failed to write reg->rx_rdptr\n"); + return -1; + } + return 0; +} + +/* This function downloads commands to the device + */ +static int +mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + int ret = 0; + dma_addr_t cmd_buf_pa, cmdrsp_buf_pa; + u8 *payload = (u8 *)skb->data; + + if (!(skb->data && skb->len)) { + mwifiex_dbg(adapter, ERROR, + "Invalid parameter in %s <%p, %#x>\n", + __func__, skb->data, skb->len); + return -1; + } + + /* Make sure a command response buffer is available */ + if (!card->cmdrsp_buf) { + mwifiex_dbg(adapter, ERROR, + "No response buffer available, send command failed\n"); + return -EBUSY; + } + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + adapter->cmd_sent = true; + + *(__le16 *)&payload[0] = cpu_to_le16((u16)skb->len); + *(__le16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_CMD); + + if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) + return -1; + + card->cmd_buf = skb; + + /* To send a command, the driver will: + 1. Write the 64bit physical address of the data buffer to + cmd response address low + cmd response address high + 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 (card->cmdrsp_buf) { + cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf); + /* Write the lower 32bits of the cmdrsp buffer physical + address */ + if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, + (u32)cmdrsp_buf_pa)) { + mwifiex_dbg(adapter, ERROR, + "Failed to write download cmd to boot code.\n"); + ret = -1; + goto done; + } + /* Write the upper 32bits of the cmdrsp buffer physical + address */ + if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, + (u32)((u64)cmdrsp_buf_pa >> 32))) { + mwifiex_dbg(adapter, ERROR, + "Failed to write download cmd to boot code.\n"); + ret = -1; + goto done; + } + } + + cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf); + /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */ + if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, + (u32)cmd_buf_pa)) { + mwifiex_dbg(adapter, ERROR, + "Failed to write download cmd to boot code.\n"); + ret = -1; + goto done; + } + /* Write the upper 32bits of the physical address to reg->cmd_addr_hi */ + if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, + (u32)((u64)cmd_buf_pa >> 32))) { + mwifiex_dbg(adapter, ERROR, + "Failed to write download cmd to boot code.\n"); + ret = -1; + goto done; + } + + /* Write the command length to reg->cmd_size */ + if (mwifiex_write_reg(adapter, reg->cmd_size, + card->cmd_buf->len)) { + mwifiex_dbg(adapter, ERROR, + "Failed to write cmd len to reg->cmd_size\n"); + ret = -1; + goto done; + } + + /* Ring the door bell */ + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_DOOR_BELL)) { + mwifiex_dbg(adapter, ERROR, + "Failed to assert door-bell intr\n"); + ret = -1; + goto done; + } + +done: + if (ret) + adapter->cmd_sent = false; + + return 0; +} + +/* + * This function handles command complete interrupt + */ +static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + struct sk_buff *skb = card->cmdrsp_buf; + int count = 0; + u16 rx_len; + __le16 pkt_len; + + mwifiex_dbg(adapter, CMD, + "info: Rx CMD Response\n"); + + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE); + + /* Unmap the command as a response has been received. */ + if (card->cmd_buf) { + mwifiex_unmap_pci_memory(adapter, card->cmd_buf, + PCI_DMA_TODEVICE); + card->cmd_buf = NULL; + } + + pkt_len = *((__le16 *)skb->data); + rx_len = le16_to_cpu(pkt_len); + skb_trim(skb, rx_len); + skb_pull(skb, INTF_HEADER_LEN); + + if (!adapter->curr_cmd) { + if (adapter->ps_state == PS_STATE_SLEEP_CFM) { + mwifiex_process_sleep_confirm_resp(adapter, skb->data, + skb->len); + mwifiex_pcie_enable_host_int(adapter); + if (mwifiex_write_reg(adapter, + PCIE_CPU_INT_EVENT, + CPU_INTR_SLEEP_CFM_DONE)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return -1; + } + mwifiex_delay_for_sleep_cookie(adapter, + MWIFIEX_MAX_DELAY_COUNT); + while (reg->sleep_cookie && (count++ < 10) && + mwifiex_pcie_ok_to_access_hw(adapter)) + usleep_range(50, 60); + } else { + mwifiex_dbg(adapter, ERROR, + "There is no command but got cmdrsp\n"); + } + memcpy(adapter->upld_buf, skb->data, + min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); + skb_push(skb, INTF_HEADER_LEN); + if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + } else if (mwifiex_pcie_ok_to_access_hw(adapter)) { + adapter->curr_cmd->resp_skb = skb; + adapter->cmd_resp_received = true; + /* Take the pointer and set it to CMD node and will + return in the response complete callback */ + card->cmdrsp_buf = NULL; + + /* Clear the cmd-rsp buffer address in scratch registers. This + will prevent firmware from writing to the same response + buffer again. */ + if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, 0)) { + mwifiex_dbg(adapter, ERROR, + "cmd_done: failed to clear cmd_rsp_addr_lo\n"); + return -1; + } + /* Write the upper 32bits of the cmdrsp buffer physical + address */ + if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, 0)) { + mwifiex_dbg(adapter, ERROR, + "cmd_done: failed to clear cmd_rsp_addr_hi\n"); + return -1; + } + } + + return 0; +} + +/* + * Command Response processing complete handler + */ +static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + struct pcie_service_card *card = adapter->card; + + if (skb) { + card->cmdrsp_buf = skb; + skb_push(card->cmdrsp_buf, INTF_HEADER_LEN); + if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + } + + return 0; +} + +/* + * This function handles firmware event ready interrupt + */ +static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; + u32 wrptr, event; + struct mwifiex_evt_buf_desc *desc; + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + mwifiex_pm_wakeup_card(adapter); + + if (adapter->event_received) { + mwifiex_dbg(adapter, EVENT, + "info: Event being processed,\t" + "do not process this interrupt just yet\n"); + return 0; + } + + if (rdptr >= MWIFIEX_MAX_EVT_BD) { + mwifiex_dbg(adapter, ERROR, + "info: Invalid read pointer...\n"); + return -1; + } + + /* Read the event ring write pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { + mwifiex_dbg(adapter, ERROR, + "EventReady: failed to read reg->evt_wrptr\n"); + return -1; + } + + mwifiex_dbg(adapter, EVENT, + "info: EventReady: Initial ", + card->evtbd_rdptr, wrptr); + if (((wrptr & MWIFIEX_EVTBD_MASK) != (card->evtbd_rdptr + & MWIFIEX_EVTBD_MASK)) || + ((wrptr & reg->evt_rollover_ind) == + (card->evtbd_rdptr & reg->evt_rollover_ind))) { + struct sk_buff *skb_cmd; + __le16 data_len = 0; + u16 evt_len; + + mwifiex_dbg(adapter, INFO, + "info: Read Index: %d\n", rdptr); + skb_cmd = card->evt_buf_list[rdptr]; + mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE); + + /* Take the pointer and set it to event pointer in adapter + and will return back after event handling callback */ + card->evt_buf_list[rdptr] = NULL; + desc = card->evtbd_ring[rdptr]; + memset(desc, 0, sizeof(*desc)); + + event = *(u32 *) &skb_cmd->data[INTF_HEADER_LEN]; + adapter->event_cause = event; + /* The first 4bytes will be the event transfer header + len is 2 bytes followed by type which is 2 bytes */ + memcpy(&data_len, skb_cmd->data, sizeof(__le16)); + evt_len = le16_to_cpu(data_len); + skb_trim(skb_cmd, evt_len); + skb_pull(skb_cmd, INTF_HEADER_LEN); + mwifiex_dbg(adapter, EVENT, + "info: Event length: %d\n", evt_len); + + if ((evt_len > 0) && (evt_len < MAX_EVENT_SIZE)) + memcpy(adapter->event_body, skb_cmd->data + + MWIFIEX_EVENT_HEADER_LEN, evt_len - + MWIFIEX_EVENT_HEADER_LEN); + + adapter->event_received = true; + adapter->event_skb = skb_cmd; + + /* Do not update the event read 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 { + if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, + CPU_INTR_EVENT_DONE)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return -1; + } + } + + return 0; +} + +/* + * Event processing complete handler + */ +static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + int ret = 0; + u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; + u32 wrptr; + struct mwifiex_evt_buf_desc *desc; + + if (!skb) + return 0; + + if (rdptr >= MWIFIEX_MAX_EVT_BD) { + mwifiex_dbg(adapter, ERROR, + "event_complete: Invalid rdptr 0x%x\n", + rdptr); + return -EINVAL; + } + + /* Read the event ring write pointer set by firmware */ + if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { + mwifiex_dbg(adapter, ERROR, + "event_complete: failed to read reg->evt_wrptr\n"); + return -1; + } + + if (!card->evt_buf_list[rdptr]) { + skb_push(skb, INTF_HEADER_LEN); + skb_put(skb, MAX_EVENT_SIZE - skb->len); + if (mwifiex_map_pci_memory(adapter, skb, + MAX_EVENT_SIZE, + PCI_DMA_FROMDEVICE)) + return -1; + card->evt_buf_list[rdptr] = skb; + desc = card->evtbd_ring[rdptr]; + desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb); + desc->len = (u16)skb->len; + desc->flags = 0; + skb = NULL; + } else { + mwifiex_dbg(adapter, ERROR, + "info: ERROR: buf still valid at index %d, <%p, %p>\n", + rdptr, card->evt_buf_list[rdptr], skb); + } + + if ((++card->evtbd_rdptr & MWIFIEX_EVTBD_MASK) == MWIFIEX_MAX_EVT_BD) { + card->evtbd_rdptr = ((card->evtbd_rdptr & + reg->evt_rollover_ind) ^ + reg->evt_rollover_ind); + } + + mwifiex_dbg(adapter, EVENT, + "info: Updated ", + card->evtbd_rdptr, wrptr); + + /* Write the event ring read pointer in to reg->evt_rdptr */ + if (mwifiex_write_reg(adapter, reg->evt_rdptr, + card->evtbd_rdptr)) { + mwifiex_dbg(adapter, ERROR, + "event_complete: failed to read reg->evt_rdptr\n"); + return -1; + } + + mwifiex_dbg(adapter, EVENT, + "info: Check Events Again\n"); + ret = mwifiex_pcie_process_event_ready(adapter); + + return ret; +} + +/* + * This function downloads the firmware to the card. + * + * Firmware is downloaded to the card in blocks. Every block download + * is tested for CRC errors, and retried a number of times before + * returning failure. + */ +static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *fw) +{ + int ret; + u8 *firmware = fw->fw_buf; + u32 firmware_len = fw->fw_len; + u32 offset = 0; + struct sk_buff *skb; + u32 txlen, tx_blocks = 0, tries, len; + u32 block_retry_cnt = 0; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (!firmware || !firmware_len) { + mwifiex_dbg(adapter, ERROR, + "No firmware image found! Terminating download\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "info: Downloading FW image (%d bytes)\n", + firmware_len); + + if (mwifiex_pcie_disable_host_int(adapter)) { + mwifiex_dbg(adapter, ERROR, + "%s: Disabling interrupts failed.\n", __func__); + return -1; + } + + skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); + if (!skb) { + ret = -ENOMEM; + goto done; + } + + /* Perform firmware data transfer */ + do { + u32 ireg_intr = 0; + + /* More data? */ + if (offset >= firmware_len) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = mwifiex_read_reg(adapter, reg->cmd_size, + &len); + if (ret) { + mwifiex_dbg(adapter, FATAL, + "Failed reading len from boot code\n"); + goto done; + } + if (len) + break; + usleep_range(10, 20); + } + + if (!len) { + break; + } else if (len > MWIFIEX_UPLD_SIZE) { + mwifiex_dbg(adapter, ERROR, + "FW download failure @ %d, invalid length %d\n", + offset, len); + ret = -1; + goto done; + } + + txlen = len; + + if (len & BIT(0)) { + block_retry_cnt++; + if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) { + mwifiex_dbg(adapter, ERROR, + "FW download failure @ %d, over max\t" + "retry count\n", offset); + ret = -1; + goto done; + } + mwifiex_dbg(adapter, ERROR, + "FW CRC error indicated by the\t" + "helper: len = 0x%04X, txlen = %d\n", + len, txlen); + len &= ~BIT(0); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + block_retry_cnt = 0; + /* Set blocksize to transfer - checking for + last block */ + if (firmware_len - offset < txlen) + txlen = firmware_len - offset; + + mwifiex_dbg(adapter, INFO, "."); + + tx_blocks = (txlen + card->pcie.blksz_fw_dl - 1) / + card->pcie.blksz_fw_dl; + + /* Copy payload to buffer */ + memmove(skb->data, &firmware[offset], txlen); + } + + skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); + skb_trim(skb, tx_blocks * card->pcie.blksz_fw_dl); + + /* Send the boot command to device */ + if (mwifiex_pcie_send_boot_cmd(adapter, skb)) { + mwifiex_dbg(adapter, ERROR, + "Failed to send firmware download command\n"); + ret = -1; + goto done; + } + + /* Wait for the command done interrupt */ + do { + if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, + &ireg_intr)) { + mwifiex_dbg(adapter, ERROR, + "%s: Failed to read\t" + "interrupt status during fw dnld.\n", + __func__); + mwifiex_unmap_pci_memory(adapter, skb, + PCI_DMA_TODEVICE); + ret = -1; + goto done; + } + } while ((ireg_intr & CPU_INTR_DOOR_BELL) == + CPU_INTR_DOOR_BELL); + + mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); + + offset += txlen; + } while (true); + + mwifiex_dbg(adapter, MSG, + "info: FW download over, size %d bytes\n", offset); + + ret = 0; + +done: + dev_kfree_skb_any(skb); + return ret; +} + +/* + * This function checks the firmware status in card. + * + * The winner interface is also determined by this function. + */ +static int +mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) +{ + int ret = 0; + u32 firmware_stat, winner_status; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + u32 tries; + + /* Mask spurios interrupts */ + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS_MASK, + HOST_INTR_MASK)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "Setting driver ready signature\n"); + if (mwifiex_write_reg(adapter, reg->drv_rdy, + FIRMWARE_READY_PCIE)) { + mwifiex_dbg(adapter, ERROR, + "Failed to write driver ready signature\n"); + return -1; + } + + /* Wait for firmware initialization event */ + for (tries = 0; tries < poll_num; tries++) { + if (mwifiex_read_reg(adapter, reg->fw_status, + &firmware_stat)) + ret = -1; + else + ret = 0; + if (ret) + continue; + if (firmware_stat == FIRMWARE_READY_PCIE) { + ret = 0; + break; + } else { + msleep(100); + ret = -1; + } + } + + if (ret) { + if (mwifiex_read_reg(adapter, reg->fw_status, + &winner_status)) + ret = -1; + else if (!winner_status) { + mwifiex_dbg(adapter, INFO, + "PCI-E is the winner\n"); + adapter->winner = 1; + } else { + mwifiex_dbg(adapter, ERROR, + "PCI-E is not the winner <%#x,%d>, exit dnld\n", + ret, adapter->winner); + } + } + + return ret; +} + +/* + * This function reads the interrupt status from card. + */ +static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) +{ + u32 pcie_ireg; + unsigned long flags; + + if (!mwifiex_pcie_ok_to_access_hw(adapter)) + return; + + if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) { + mwifiex_dbg(adapter, ERROR, "Read register failed\n"); + return; + } + + if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { + + mwifiex_pcie_disable_host_int(adapter); + + /* Clear the pending interrupts */ + if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS, + ~pcie_ireg)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return; + } + spin_lock_irqsave(&adapter->int_lock, flags); + adapter->int_status |= pcie_ireg; + spin_unlock_irqrestore(&adapter->int_lock, flags); + + if (!adapter->pps_uapsd_mode && + adapter->ps_state == PS_STATE_SLEEP && + mwifiex_pcie_ok_to_access_hw(adapter)) { + /* Potentially for PCIe we could get other + * interrupts like shared. Don't change power + * state until cookie is set */ + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + } + } +} + +/* + * Interrupt handler for PCIe root port + * + * This function reads the interrupt status from firmware and assigns + * the main process in workqueue which will handle the interrupt. + */ +static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) +{ + struct pci_dev *pdev = (struct pci_dev *)context; + struct pcie_service_card *card; + struct mwifiex_adapter *adapter; + + if (!pdev) { + pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev); + goto exit; + } + + card = pci_get_drvdata(pdev); + if (!card || !card->adapter) { + pr_debug("info: %s: card=%p adapter=%p\n", __func__, card, + card ? card->adapter : NULL); + goto exit; + } + adapter = card->adapter; + + if (adapter->surprise_removed) + goto exit; + + mwifiex_interrupt_status(adapter); + mwifiex_queue_main_work(adapter); + +exit: + return IRQ_HANDLED; +} + +/* + * This function checks the current interrupt status. + * + * The following interrupts are checked and handled by this function - + * - Data sent + * - Command sent + * - Command received + * - Packets received + * - Events received + * + * In case of Rx packets received, the packets are uploaded from card to + * host and processed accordingly. + */ +static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) +{ + int ret; + u32 pcie_ireg; + unsigned long flags; + + spin_lock_irqsave(&adapter->int_lock, flags); + /* Clear out unused interrupts */ + pcie_ireg = adapter->int_status; + adapter->int_status = 0; + spin_unlock_irqrestore(&adapter->int_lock, flags); + + while (pcie_ireg & HOST_INTR_MASK) { + if (pcie_ireg & HOST_INTR_DNLD_DONE) { + pcie_ireg &= ~HOST_INTR_DNLD_DONE; + mwifiex_dbg(adapter, INTR, + "info: TX DNLD Done\n"); + ret = mwifiex_pcie_send_data_complete(adapter); + if (ret) + return ret; + } + if (pcie_ireg & HOST_INTR_UPLD_RDY) { + pcie_ireg &= ~HOST_INTR_UPLD_RDY; + mwifiex_dbg(adapter, INTR, + "info: Rx DATA\n"); + ret = mwifiex_pcie_process_recv_data(adapter); + if (ret) + return ret; + } + if (pcie_ireg & HOST_INTR_EVENT_RDY) { + pcie_ireg &= ~HOST_INTR_EVENT_RDY; + mwifiex_dbg(adapter, INTR, + "info: Rx EVENT\n"); + ret = mwifiex_pcie_process_event_ready(adapter); + if (ret) + return ret; + } + + if (pcie_ireg & HOST_INTR_CMD_DONE) { + pcie_ireg &= ~HOST_INTR_CMD_DONE; + if (adapter->cmd_sent) { + mwifiex_dbg(adapter, INTR, + "info: CMD sent Interrupt\n"); + adapter->cmd_sent = false; + } + /* Handle command response */ + ret = mwifiex_pcie_process_cmd_complete(adapter); + if (ret) + return ret; + } + + if (mwifiex_pcie_ok_to_access_hw(adapter)) { + if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, + &pcie_ireg)) { + mwifiex_dbg(adapter, ERROR, + "Read register failed\n"); + return -1; + } + + if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { + if (mwifiex_write_reg(adapter, + PCIE_HOST_INT_STATUS, + ~pcie_ireg)) { + mwifiex_dbg(adapter, ERROR, + "Write register failed\n"); + return -1; + } + } + + } + } + mwifiex_dbg(adapter, INTR, + "info: cmd_sent=%d data_sent=%d\n", + adapter->cmd_sent, adapter->data_sent); + if (adapter->ps_state != PS_STATE_SLEEP) + mwifiex_pcie_enable_host_int(adapter); + + return 0; +} + +/* + * 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. + */ +static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, + struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "Passed NULL skb to %s\n", __func__); + return -1; + } + + if (type == MWIFIEX_TYPE_DATA) + return mwifiex_pcie_send_data(adapter, skb, tx_param); + else if (type == MWIFIEX_TYPE_CMD) + return mwifiex_pcie_send_cmd(adapter, skb); + + return 0; +} + +/* This function read/write firmware */ +static enum rdwr_status +mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) +{ + int ret, tries; + u8 ctrl_data; + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "PCIE write err\n"); + return RDWR_STATUS_FAILURE; + } + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data); + if (ctrl_data == FW_DUMP_DONE) + return RDWR_STATUS_SUCCESS; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != FW_DUMP_HOST_READY) { + mwifiex_dbg(adapter, WARN, + "The ctrl reg was changed, re-try again!\n"); + ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, + FW_DUMP_HOST_READY); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "PCIE write err\n"); + return RDWR_STATUS_FAILURE; + } + } + usleep_range(100, 200); + } + + mwifiex_dbg(adapter, ERROR, "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; +} + +/* This function dump firmware memory to file */ +static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *creg = card->pcie.reg; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; + enum rdwr_status stat; + u32 memory_size; + int ret; + + if (!card->pcie.can_dump_fw) + return; + + for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump start ==\n"); + + /* Read the number of the memories which will dump */ + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + return; + + reg = creg->fw_dump_start; + mwifiex_read_reg_byte(adapter, reg, &dump_num); + + /* Read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + return; + + memory_size = 0; + reg = creg->fw_dump_start; + for (i = 0; i < 4; i++) { + mwifiex_read_reg_byte(adapter, reg, &read_reg); + memory_size |= (read_reg << (i * 8)); + reg++; + } + + if (memory_size == 0) { + mwifiex_dbg(adapter, MSG, "Firmware dump Finished!\n"); + ret = mwifiex_write_reg(adapter, creg->fw_dump_ctrl, + FW_DUMP_READ_DONE); + if (ret) { + mwifiex_dbg(adapter, ERROR, "PCIE write err\n"); + return; + } + break; + } + + mwifiex_dbg(adapter, DUMP, + "%s_SIZE=0x%x\n", entry->mem_name, memory_size); + entry->mem_ptr = vmalloc(memory_size + 1); + entry->mem_size = memory_size; + if (!entry->mem_ptr) { + mwifiex_dbg(adapter, ERROR, + "Vmalloc %s failed\n", entry->mem_name); + return; + } + dbg_ptr = entry->mem_ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = entry->done_flag; + mwifiex_dbg(adapter, DUMP, "Start %s output, please wait...\n", + entry->mem_name); + + do { + stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); + if (RDWR_STATUS_FAILURE == stat) + return; + + reg_start = creg->fw_dump_start; + reg_end = creg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + mwifiex_read_reg_byte(adapter, reg, dbg_ptr); + if (dbg_ptr < end_ptr) { + dbg_ptr++; + } else { + mwifiex_dbg(adapter, ERROR, + "Allocated buf not enough\n"); + return; + } + } + + if (stat != RDWR_STATUS_DONE) + continue; + + mwifiex_dbg(adapter, DUMP, + "%s done: size=0x%tx\n", + entry->mem_name, dbg_ptr - entry->mem_ptr); + break; + } while (true); + } + mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump end ==\n"); +} + +static void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter) +{ + mwifiex_drv_info_dump(adapter); + mwifiex_pcie_fw_dump(adapter); + mwifiex_upload_device_dump(adapter); +} + +static unsigned long iface_work_flags; +static struct mwifiex_adapter *save_adapter; +static void mwifiex_pcie_work(struct work_struct *work) +{ + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, + &iface_work_flags)) + mwifiex_pcie_device_dump_work(save_adapter); +} + +static DECLARE_WORK(pcie_work, mwifiex_pcie_work); +/* This function dumps FW information */ +static void mwifiex_pcie_device_dump(struct mwifiex_adapter *adapter) +{ + save_adapter = adapter; + if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags)) + return; + + set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags); + + schedule_work(&pcie_work); +} + +/* + * This function initializes the PCI-E host memory space, WCB rings, etc. + * + * The following initializations steps are followed - + * - Allocate TXBD ring buffers + * - Allocate RXBD ring buffers + * - Allocate event BD ring buffers + * - Allocate command response ring buffer + * - Allocate sleep cookie buffer + */ +static int mwifiex_pcie_init(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + int ret; + struct pci_dev *pdev = card->dev; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + pci_set_drvdata(pdev, card); + + ret = pci_enable_device(pdev); + if (ret) + goto err_enable_dev; + + pci_set_master(pdev); + + mwifiex_dbg(adapter, INFO, + "try set_consistent_dma_mask(32)\n"); + ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "set_dma_mask(32) failed\n"); + goto err_set_dma_mask; + } + + ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "set_consistent_dma_mask(64) failed\n"); + goto err_set_dma_mask; + } + + ret = pci_request_region(pdev, 0, DRV_NAME); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "req_reg(0) error\n"); + goto err_req_region0; + } + card->pci_mmap = pci_iomap(pdev, 0, 0); + if (!card->pci_mmap) { + mwifiex_dbg(adapter, ERROR, "iomap(0) error\n"); + ret = -EIO; + goto err_iomap0; + } + ret = pci_request_region(pdev, 2, DRV_NAME); + if (ret) { + mwifiex_dbg(adapter, ERROR, "req_reg(2) error\n"); + goto err_req_region2; + } + card->pci_mmap1 = pci_iomap(pdev, 2, 0); + if (!card->pci_mmap1) { + mwifiex_dbg(adapter, ERROR, + "iomap(2) error\n"); + ret = -EIO; + goto err_iomap2; + } + + mwifiex_dbg(adapter, INFO, + "PCI memory map Virt0: %p PCI memory map Virt2: %p\n", + card->pci_mmap, card->pci_mmap1); + + card->cmdrsp_buf = NULL; + ret = mwifiex_pcie_create_txbd_ring(adapter); + if (ret) + goto err_cre_txbd; + ret = mwifiex_pcie_create_rxbd_ring(adapter); + if (ret) + goto err_cre_rxbd; + ret = mwifiex_pcie_create_evtbd_ring(adapter); + if (ret) + goto err_cre_evtbd; + ret = mwifiex_pcie_alloc_cmdrsp_buf(adapter); + if (ret) + goto err_alloc_cmdbuf; + if (reg->sleep_cookie) { + ret = mwifiex_pcie_alloc_sleep_cookie_buf(adapter); + if (ret) + goto err_alloc_cookie; + } else { + card->sleep_cookie_vbase = NULL; + } + return ret; + +err_alloc_cookie: + mwifiex_pcie_delete_cmdrsp_buf(adapter); +err_alloc_cmdbuf: + mwifiex_pcie_delete_evtbd_ring(adapter); +err_cre_evtbd: + mwifiex_pcie_delete_rxbd_ring(adapter); +err_cre_rxbd: + mwifiex_pcie_delete_txbd_ring(adapter); +err_cre_txbd: + pci_iounmap(pdev, card->pci_mmap1); +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: +err_set_dma_mask: + pci_disable_device(pdev); +err_enable_dev: + pci_set_drvdata(pdev, NULL); + return ret; +} + +/* + * This function cleans up the allocated card buffers. + * + * The following are freed by this function - + * - TXBD ring buffers + * - RXBD ring buffers + * - Event BD ring buffers + * - Command response ring buffer + * - Sleep cookie buffer + */ +static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + struct pci_dev *pdev = card->dev; + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + if (user_rmmod) { + mwifiex_dbg(adapter, INFO, + "Clearing driver ready signature\n"); + if (mwifiex_write_reg(adapter, reg->drv_rdy, 0x00000000)) + mwifiex_dbg(adapter, ERROR, + "Failed to write driver not-ready signature\n"); + } + + if (pdev) { + pci_iounmap(pdev, card->pci_mmap); + pci_iounmap(pdev, card->pci_mmap1); + pci_disable_device(pdev); + pci_release_region(pdev, 2); + pci_release_region(pdev, 0); + pci_set_drvdata(pdev, NULL); + } + kfree(card); +} + +/* + * This function registers the PCIE device. + * + * PCIE IRQ is claimed, block size is set and driver data is initialized. + */ +static int mwifiex_register_dev(struct mwifiex_adapter *adapter) +{ + int ret; + struct pcie_service_card *card = adapter->card; + struct pci_dev *pdev = card->dev; + + /* save adapter pointer in card */ + card->adapter = adapter; + + ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED, + "MRVL_PCIE", pdev); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "request_irq failed: ret=%d\n", ret); + adapter->card = NULL; + return -1; + } + + adapter->dev = &pdev->dev; + adapter->tx_buf_size = card->pcie.tx_buf_size; + adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; + adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); + strcpy(adapter->fw_name, card->pcie.firmware); + adapter->ext_scan = card->pcie.can_ext_scan; + + return 0; +} + +/* + * This function unregisters the PCIE device. + * + * The PCIE IRQ is released, the function is disabled and driver + * data is set to null. + */ +static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) +{ + struct pcie_service_card *card = adapter->card; + const struct mwifiex_pcie_card_reg *reg; + + if (card) { + mwifiex_dbg(adapter, INFO, + "%s(): calling free_irq()\n", __func__); + free_irq(card->dev->irq, card->dev); + + reg = card->pcie.reg; + if (reg->sleep_cookie) + mwifiex_pcie_delete_sleep_cookie_buf(adapter); + + mwifiex_pcie_delete_cmdrsp_buf(adapter); + mwifiex_pcie_delete_evtbd_ring(adapter); + mwifiex_pcie_delete_rxbd_ring(adapter); + mwifiex_pcie_delete_txbd_ring(adapter); + card->cmdrsp_buf = NULL; + } +} + +static struct mwifiex_if_ops pcie_ops = { + .init_if = mwifiex_pcie_init, + .cleanup_if = mwifiex_pcie_cleanup, + .check_fw_status = mwifiex_check_fw_status, + .prog_fw = mwifiex_prog_fw_w_helper, + .register_dev = mwifiex_register_dev, + .unregister_dev = mwifiex_unregister_dev, + .enable_int = mwifiex_pcie_enable_host_int, + .process_int_status = mwifiex_process_int_status, + .host_to_card = mwifiex_pcie_host_to_card, + .wakeup = mwifiex_pm_wakeup_card, + .wakeup_complete = mwifiex_pm_wakeup_card_complete, + + /* PCIE specific */ + .cmdrsp_complete = mwifiex_pcie_cmdrsp_complete, + .event_complete = mwifiex_pcie_event_complete, + .update_mp_end_port = NULL, + .cleanup_mpa_buf = NULL, + .init_fw_port = mwifiex_pcie_init_fw_port, + .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, + .device_dump = mwifiex_pcie_device_dump, +}; + +/* + * This function initializes the PCIE driver module. + * + * This initiates the semaphore and registers the device with + * PCIE bus. + */ +static int mwifiex_pcie_init_module(void) +{ + int ret; + + pr_debug("Marvell PCIe Driver\n"); + + sema_init(&add_remove_card_sem, 1); + + /* Clear the flag in case user removes the card. */ + user_rmmod = 0; + + ret = pci_register_driver(&mwifiex_pcie); + if (ret) + pr_err("Driver register failed!\n"); + else + pr_debug("info: Driver registered successfully!\n"); + + return ret; +} + +/* + * This function cleans up the PCIE driver. + * + * The following major steps are followed for cleanup - + * - Resume the device if its suspended + * - Disconnect the device if connected + * - Shutdown the firmware + * - Unregister the device from PCIE bus. + */ +static void mwifiex_pcie_cleanup_module(void) +{ + if (!down_interruptible(&add_remove_card_sem)) + up(&add_remove_card_sem); + + /* Set the flag as user is removing this module. */ + user_rmmod = 1; + + cancel_work_sync(&pcie_work); + pci_unregister_driver(&mwifiex_pcie); +} + +module_init(mwifiex_pcie_init_module); +module_exit(mwifiex_pcie_cleanup_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell WiFi-Ex PCI-Express Driver version " PCIE_VERSION); +MODULE_VERSION(PCIE_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(PCIE8766_DEFAULT_FW_NAME); +MODULE_FIRMWARE(PCIE8897_DEFAULT_FW_NAME); +MODULE_FIRMWARE(PCIE8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h new file mode 100644 index 000000000000..48e549c3b285 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -0,0 +1,382 @@ +/* @file mwifiex_pcie.h + * + * @brief This file contains definitions for PCI-E interface. + * driver. + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_PCIE_H +#define _MWIFIEX_PCIE_H + +#include +#include +#include + +#include "main.h" + +#define PCIE8766_DEFAULT_FW_NAME "mrvl/pcie8766_uapsta.bin" +#define PCIE8897_DEFAULT_FW_NAME "mrvl/pcie8897_uapsta.bin" +#define PCIE8997_DEFAULT_FW_NAME "mrvl/pcie8997_uapsta.bin" + +#define PCIE_VENDOR_ID_MARVELL (0x11ab) +#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30) +#define PCIE_DEVICE_ID_MARVELL_88W8897 (0x2b38) +#define PCIE_DEVICE_ID_MARVELL_88W8997 (0x2b42) + +/* Constants for Buffer Descriptor (BD) rings */ +#define MWIFIEX_MAX_TXRX_BD 0x20 +#define MWIFIEX_TXBD_MASK 0x3F +#define MWIFIEX_RXBD_MASK 0x3F + +#define MWIFIEX_MAX_EVT_BD 0x08 +#define MWIFIEX_EVTBD_MASK 0x0f + +/* PCIE INTERNAL REGISTERS */ +#define PCIE_SCRATCH_0_REG 0xC10 +#define PCIE_SCRATCH_1_REG 0xC14 +#define PCIE_CPU_INT_EVENT 0xC18 +#define PCIE_CPU_INT_STATUS 0xC1C +#define PCIE_HOST_INT_STATUS 0xC30 +#define PCIE_HOST_INT_MASK 0xC34 +#define PCIE_HOST_INT_STATUS_MASK 0xC3C +#define PCIE_SCRATCH_2_REG 0xC40 +#define PCIE_SCRATCH_3_REG 0xC44 +#define PCIE_SCRATCH_4_REG 0xCD0 +#define PCIE_SCRATCH_5_REG 0xCD4 +#define PCIE_SCRATCH_6_REG 0xCD8 +#define PCIE_SCRATCH_7_REG 0xCDC +#define PCIE_SCRATCH_8_REG 0xCE0 +#define PCIE_SCRATCH_9_REG 0xCE4 +#define PCIE_SCRATCH_10_REG 0xCE8 +#define PCIE_SCRATCH_11_REG 0xCEC +#define PCIE_SCRATCH_12_REG 0xCF0 +#define PCIE_RD_DATA_PTR_Q0_Q1 0xC08C +#define PCIE_WR_DATA_PTR_Q0_Q1 0xC05C + +#define CPU_INTR_DNLD_RDY BIT(0) +#define CPU_INTR_DOOR_BELL BIT(1) +#define CPU_INTR_SLEEP_CFM_DONE BIT(2) +#define CPU_INTR_RESET BIT(3) +#define CPU_INTR_EVENT_DONE BIT(5) + +#define HOST_INTR_DNLD_DONE BIT(0) +#define HOST_INTR_UPLD_RDY BIT(1) +#define HOST_INTR_CMD_DONE BIT(2) +#define HOST_INTR_EVENT_RDY BIT(3) +#define HOST_INTR_MASK (HOST_INTR_DNLD_DONE | \ + HOST_INTR_UPLD_RDY | \ + HOST_INTR_CMD_DONE | \ + HOST_INTR_EVENT_RDY) + +#define MWIFIEX_BD_FLAG_ROLLOVER_IND BIT(7) +#define MWIFIEX_BD_FLAG_FIRST_DESC BIT(0) +#define MWIFIEX_BD_FLAG_LAST_DESC BIT(1) +#define MWIFIEX_BD_FLAG_SOP BIT(0) +#define MWIFIEX_BD_FLAG_EOP BIT(1) +#define MWIFIEX_BD_FLAG_XS_SOP BIT(2) +#define MWIFIEX_BD_FLAG_XS_EOP BIT(3) +#define MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND BIT(7) +#define MWIFIEX_BD_FLAG_RX_ROLLOVER_IND BIT(10) +#define MWIFIEX_BD_FLAG_TX_START_PTR BIT(16) +#define MWIFIEX_BD_FLAG_TX_ROLLOVER_IND BIT(26) + +/* Max retry number of command write */ +#define MAX_WRITE_IOMEM_RETRY 2 +/* Define PCIE block size for firmware download */ +#define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256 +/* FW awake cookie after FW ready */ +#define FW_AWAKE_COOKIE (0xAA55AA55) +#define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF +#define MWIFIEX_MAX_DELAY_COUNT 5 + +struct mwifiex_pcie_card_reg { + u16 cmd_addr_lo; + u16 cmd_addr_hi; + u16 fw_status; + u16 cmd_size; + u16 cmdrsp_addr_lo; + u16 cmdrsp_addr_hi; + u16 tx_rdptr; + u16 tx_wrptr; + u16 rx_rdptr; + u16 rx_wrptr; + u16 evt_rdptr; + u16 evt_wrptr; + u16 drv_rdy; + u16 tx_start_ptr; + u32 tx_mask; + u32 tx_wrap_mask; + u32 rx_mask; + u32 rx_wrap_mask; + u32 tx_rollover_ind; + u32 rx_rollover_ind; + u32 evt_rollover_ind; + u8 ring_flag_sop; + u8 ring_flag_eop; + u8 ring_flag_xs_sop; + u8 ring_flag_xs_eop; + u32 ring_tx_start_ptr; + u8 pfu_enabled; + u8 sleep_cookie; + u16 fw_dump_ctrl; + u16 fw_dump_start; + u16 fw_dump_end; +}; + +static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { + .cmd_addr_lo = PCIE_SCRATCH_0_REG, + .cmd_addr_hi = PCIE_SCRATCH_1_REG, + .cmd_size = PCIE_SCRATCH_2_REG, + .fw_status = PCIE_SCRATCH_3_REG, + .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, + .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, + .tx_rdptr = PCIE_SCRATCH_6_REG, + .tx_wrptr = PCIE_SCRATCH_7_REG, + .rx_rdptr = PCIE_SCRATCH_8_REG, + .rx_wrptr = PCIE_SCRATCH_9_REG, + .evt_rdptr = PCIE_SCRATCH_10_REG, + .evt_wrptr = PCIE_SCRATCH_11_REG, + .drv_rdy = PCIE_SCRATCH_12_REG, + .tx_start_ptr = 0, + .tx_mask = MWIFIEX_TXBD_MASK, + .tx_wrap_mask = 0, + .rx_mask = MWIFIEX_RXBD_MASK, + .rx_wrap_mask = 0, + .tx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, + .rx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, + .evt_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, + .ring_flag_sop = 0, + .ring_flag_eop = 0, + .ring_flag_xs_sop = 0, + .ring_flag_xs_eop = 0, + .ring_tx_start_ptr = 0, + .pfu_enabled = 0, + .sleep_cookie = 1, +}; + +static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { + .cmd_addr_lo = PCIE_SCRATCH_0_REG, + .cmd_addr_hi = PCIE_SCRATCH_1_REG, + .cmd_size = PCIE_SCRATCH_2_REG, + .fw_status = PCIE_SCRATCH_3_REG, + .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, + .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, + .tx_rdptr = PCIE_RD_DATA_PTR_Q0_Q1, + .tx_wrptr = PCIE_WR_DATA_PTR_Q0_Q1, + .rx_rdptr = PCIE_WR_DATA_PTR_Q0_Q1, + .rx_wrptr = PCIE_RD_DATA_PTR_Q0_Q1, + .evt_rdptr = PCIE_SCRATCH_10_REG, + .evt_wrptr = PCIE_SCRATCH_11_REG, + .drv_rdy = PCIE_SCRATCH_12_REG, + .tx_start_ptr = 16, + .tx_mask = 0x03FF0000, + .tx_wrap_mask = 0x07FF0000, + .rx_mask = 0x000003FF, + .rx_wrap_mask = 0x000007FF, + .tx_rollover_ind = MWIFIEX_BD_FLAG_TX_ROLLOVER_IND, + .rx_rollover_ind = MWIFIEX_BD_FLAG_RX_ROLLOVER_IND, + .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, + .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, + .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, + .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, + .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, + .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, + .pfu_enabled = 1, + .sleep_cookie = 0, + .fw_dump_ctrl = 0xcf4, + .fw_dump_start = 0xcf8, + .fw_dump_end = 0xcff, +}; + +static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = { + .cmd_addr_lo = PCIE_SCRATCH_0_REG, + .cmd_addr_hi = PCIE_SCRATCH_1_REG, + .cmd_size = PCIE_SCRATCH_2_REG, + .fw_status = PCIE_SCRATCH_3_REG, + .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, + .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, + .tx_rdptr = 0xC1A4, + .tx_wrptr = 0xC1A8, + .rx_rdptr = 0xC1A8, + .rx_wrptr = 0xC1A4, + .evt_rdptr = PCIE_SCRATCH_10_REG, + .evt_wrptr = PCIE_SCRATCH_11_REG, + .drv_rdy = PCIE_SCRATCH_12_REG, + .tx_start_ptr = 16, + .tx_mask = 0x0FFF0000, + .tx_wrap_mask = 0x01FF0000, + .rx_mask = 0x00000FFF, + .rx_wrap_mask = 0x000001FF, + .tx_rollover_ind = BIT(28), + .rx_rollover_ind = BIT(12), + .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, + .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, + .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, + .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, + .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, + .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, + .pfu_enabled = 1, + .sleep_cookie = 0, +}; + +struct mwifiex_pcie_device { + const char *firmware; + const struct mwifiex_pcie_card_reg *reg; + u16 blksz_fw_dl; + u16 tx_buf_size; + bool can_dump_fw; + bool can_ext_scan; +}; + +static const struct mwifiex_pcie_device mwifiex_pcie8766 = { + .firmware = PCIE8766_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_8766, + .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .can_dump_fw = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_pcie_device mwifiex_pcie8897 = { + .firmware = PCIE8897_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_8897, + .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .can_dump_fw = true, + .can_ext_scan = true, +}; + +static const struct mwifiex_pcie_device mwifiex_pcie8997 = { + .firmware = PCIE8997_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_8997, + .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .can_dump_fw = false, + .can_ext_scan = true, +}; + +struct mwifiex_evt_buf_desc { + u64 paddr; + u16 len; + u16 flags; +} __packed; + +struct mwifiex_pcie_buf_desc { + u64 paddr; + u16 len; + u16 flags; +} __packed; + +struct mwifiex_pfu_buf_desc { + u16 flags; + u16 offset; + u16 frag_len; + u16 len; + u64 paddr; + u32 reserved; +} __packed; + +struct pcie_service_card { + struct pci_dev *dev; + struct mwifiex_adapter *adapter; + struct mwifiex_pcie_device pcie; + + u8 txbd_flush; + u32 txbd_wrptr; + u32 txbd_rdptr; + u32 txbd_ring_size; + u8 *txbd_ring_vbase; + dma_addr_t txbd_ring_pbase; + void *txbd_ring[MWIFIEX_MAX_TXRX_BD]; + struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD]; + + u32 rxbd_wrptr; + u32 rxbd_rdptr; + u32 rxbd_ring_size; + u8 *rxbd_ring_vbase; + dma_addr_t rxbd_ring_pbase; + void *rxbd_ring[MWIFIEX_MAX_TXRX_BD]; + struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD]; + + u32 evtbd_wrptr; + u32 evtbd_rdptr; + u32 evtbd_ring_size; + u8 *evtbd_ring_vbase; + dma_addr_t evtbd_ring_pbase; + void *evtbd_ring[MWIFIEX_MAX_EVT_BD]; + struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD]; + + struct sk_buff *cmd_buf; + struct sk_buff *cmdrsp_buf; + u8 *sleep_cookie_vbase; + dma_addr_t sleep_cookie_pbase; + void __iomem *pci_mmap; + void __iomem *pci_mmap1; +}; + +static inline int +mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr) +{ + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + switch (card->dev->device) { + case PCIE_DEVICE_ID_MARVELL_88W8766P: + if (((card->txbd_wrptr & reg->tx_mask) == + (rdptr & reg->tx_mask)) && + ((card->txbd_wrptr & reg->tx_rollover_ind) != + (rdptr & reg->tx_rollover_ind))) + return 1; + break; + case PCIE_DEVICE_ID_MARVELL_88W8897: + if (((card->txbd_wrptr & reg->tx_mask) == + (rdptr & reg->tx_mask)) && + ((card->txbd_wrptr & reg->tx_rollover_ind) == + (rdptr & reg->tx_rollover_ind))) + return 1; + break; + } + + return 0; +} + +static inline int +mwifiex_pcie_txbd_not_full(struct pcie_service_card *card) +{ + const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; + + switch (card->dev->device) { + case PCIE_DEVICE_ID_MARVELL_88W8766P: + if (((card->txbd_wrptr & reg->tx_mask) != + (card->txbd_rdptr & reg->tx_mask)) || + ((card->txbd_wrptr & reg->tx_rollover_ind) != + (card->txbd_rdptr & reg->tx_rollover_ind))) + return 1; + break; + case PCIE_DEVICE_ID_MARVELL_88W8897: + case PCIE_DEVICE_ID_MARVELL_88W8997: + if (((card->txbd_wrptr & reg->tx_mask) != + (card->txbd_rdptr & reg->tx_mask)) || + ((card->txbd_wrptr & reg->tx_rollover_ind) == + (card->txbd_rdptr & reg->tx_rollover_ind))) + return 1; + break; + } + + return 0; +} + +#endif /* _MWIFIEX_PCIE_H */ diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c new file mode 100644 index 000000000000..c20017ced566 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -0,0 +1,2639 @@ +/* + * Marvell Wireless LAN device driver: scan ioctl and command handling + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "11n.h" +#include "cfg80211.h" + +/* The maximum number of channels the firmware can scan per command */ +#define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 + +#define MWIFIEX_DEF_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(struct mwifiex_ie_types_header) \ + + (MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN \ + *sizeof(struct mwifiex_chan_scan_param_set))) + +/* Memory needed to store supported rate */ +#define RATE_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_rates_param_set) \ + + HOSTCMD_SUPPORTED_RATES) + +/* Memory needed to store a max number/size WildCard SSID TLV for a firmware + scan */ +#define WILDCARD_SSID_TLV_MAX_SIZE \ + (MWIFIEX_MAX_SSID_LIST_LENGTH * \ + (sizeof(struct mwifiex_ie_types_wildcard_ssid_params) \ + + IEEE80211_MAX_SSID_LEN)) + +/* Maximum memory needed for a mwifiex_scan_cmd_config with all TLVs at max */ +#define MAX_SCAN_CFG_ALLOC (sizeof(struct mwifiex_scan_cmd_config) \ + + sizeof(struct mwifiex_ie_types_num_probes) \ + + sizeof(struct mwifiex_ie_types_htcap) \ + + CHAN_TLV_MAX_SIZE \ + + RATE_TLV_MAX_SIZE \ + + WILDCARD_SSID_TLV_MAX_SIZE) + + +union mwifiex_scan_cmd_config_tlv { + /* Scan configuration (variable length) */ + struct mwifiex_scan_cmd_config config; + /* Max allocated block */ + u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; +}; + +enum cipher_suite { + CIPHER_SUITE_TKIP, + CIPHER_SUITE_CCMP, + CIPHER_SUITE_MAX +}; +static u8 mwifiex_wpa_oui[CIPHER_SUITE_MAX][4] = { + { 0x00, 0x50, 0xf2, 0x02 }, /* TKIP */ + { 0x00, 0x50, 0xf2, 0x04 }, /* AES */ +}; +static u8 mwifiex_rsn_oui[CIPHER_SUITE_MAX][4] = { + { 0x00, 0x0f, 0xac, 0x02 }, /* TKIP */ + { 0x00, 0x0f, 0xac, 0x04 }, /* AES */ +}; + +/* + * This function parses a given IE for a given OUI. + * + * This is used to parse a WPA/RSN IE to find if it has + * a given oui in PTK. + */ +static u8 +mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui) +{ + u8 count; + + count = iebody->ptk_cnt[0]; + + /* 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(iebody->ptk_body, oui, sizeof(iebody->ptk_body))) + return MWIFIEX_OUI_PRESENT; + + --count; + if (count) + iebody = (struct ie_body *) ((u8 *) iebody + + sizeof(iebody->ptk_body)); + } + + pr_debug("info: %s: OUI is not found in PTK\n", __func__); + return MWIFIEX_OUI_NOT_PRESENT; +} + +/* + * This function checks if a given OUI is present in a RSN IE. + * + * The function first checks if a RSN IE is present or not in the + * BSS descriptor. It tries to locate the OUI only if such an IE is + * present. + */ +static u8 +mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) +{ + u8 *oui; + struct ie_body *iebody; + u8 ret = MWIFIEX_OUI_NOT_PRESENT; + + if (((bss_desc->bcn_rsn_ie) && ((*(bss_desc->bcn_rsn_ie)). + ieee_hdr.element_id == WLAN_EID_RSN))) { + iebody = (struct ie_body *) + (((u8 *) bss_desc->bcn_rsn_ie->data) + + RSN_GTK_OUI_OFFSET); + oui = &mwifiex_rsn_oui[cipher][0]; + ret = mwifiex_search_oui_in_ie(iebody, oui); + if (ret) + return ret; + } + return ret; +} + +/* + * This function checks if a given OUI is present in a WPA IE. + * + * The function first checks if a WPA IE is present or not in the + * BSS descriptor. It tries to locate the OUI only if such an IE is + * present. + */ +static u8 +mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) +{ + u8 *oui; + struct ie_body *iebody; + u8 ret = MWIFIEX_OUI_NOT_PRESENT; + + if (((bss_desc->bcn_wpa_ie) && + ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id == + WLAN_EID_VENDOR_SPECIFIC))) { + iebody = (struct ie_body *) bss_desc->bcn_wpa_ie->data; + oui = &mwifiex_wpa_oui[cipher][0]; + ret = mwifiex_search_oui_in_ie(iebody, oui); + if (ret) + return ret; + } + return ret; +} + +/* + * This function compares two SSIDs and checks if they match. + */ +s32 +mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2) +{ + if (!ssid1 || !ssid2 || (ssid1->ssid_len != ssid2->ssid_len)) + return -1; + return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); +} + +/* + * This function checks if wapi is enabled in driver and scanned network is + * compatible with it. + */ +static bool +mwifiex_is_bss_wapi(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (priv->sec_info.wapi_enabled && + (bss_desc->bcn_wapi_ie && + ((*(bss_desc->bcn_wapi_ie)).ieee_hdr.element_id == + WLAN_EID_BSS_AC_ACCESS_DELAY))) { + return true; + } + return false; +} + +/* + * This function checks if driver is configured with no security mode and + * scanned network is compatible with it. + */ +static bool +mwifiex_is_bss_no_sec(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || + ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != + WLAN_EID_VENDOR_SPECIFIC)) && + ((!bss_desc->bcn_rsn_ie) || + ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != + WLAN_EID_RSN)) && + !priv->sec_info.encryption_mode && !bss_desc->privacy) { + return true; + } + return false; +} + +/* + * This function checks if static WEP is enabled in driver and scanned network + * is compatible with it. + */ +static bool +mwifiex_is_bss_static_wep(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && bss_desc->privacy) { + return true; + } + return false; +} + +/* + * This function checks if wpa is enabled in driver and scanned network is + * compatible with it. + */ +static bool +mwifiex_is_bss_wpa(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && ((bss_desc->bcn_wpa_ie) && + ((*(bss_desc->bcn_wpa_ie)). + vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC)) + /* + * Privacy bit may NOT be set in some APs like + * LinkSys WRT54G && bss_desc->privacy + */ + ) { + mwifiex_dbg(priv->adapter, INFO, + "info: %s: WPA:\t" + "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\t" + "EncMode=%#x privacy=%#x\n", __func__, + (bss_desc->bcn_wpa_ie) ? + (*bss_desc->bcn_wpa_ie). + vend_hdr.element_id : 0, + (bss_desc->bcn_rsn_ie) ? + (*bss_desc->bcn_rsn_ie). + ieee_hdr.element_id : 0, + (priv->sec_info.wep_enabled) ? "e" : "d", + (priv->sec_info.wpa_enabled) ? "e" : "d", + (priv->sec_info.wpa2_enabled) ? "e" : "d", + priv->sec_info.encryption_mode, + bss_desc->privacy); + return true; + } + return false; +} + +/* + * This function checks if wpa2 is enabled in driver and scanned network is + * compatible with it. + */ +static bool +mwifiex_is_bss_wpa2(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && + !priv->sec_info.wpa_enabled && + priv->sec_info.wpa2_enabled && + ((bss_desc->bcn_rsn_ie) && + ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id == WLAN_EID_RSN))) { + /* + * Privacy bit may NOT be set in some APs like + * LinkSys WRT54G && bss_desc->privacy + */ + mwifiex_dbg(priv->adapter, INFO, + "info: %s: WPA2:\t" + "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\t" + "EncMode=%#x privacy=%#x\n", __func__, + (bss_desc->bcn_wpa_ie) ? + (*bss_desc->bcn_wpa_ie). + vend_hdr.element_id : 0, + (bss_desc->bcn_rsn_ie) ? + (*bss_desc->bcn_rsn_ie). + ieee_hdr.element_id : 0, + (priv->sec_info.wep_enabled) ? "e" : "d", + (priv->sec_info.wpa_enabled) ? "e" : "d", + (priv->sec_info.wpa2_enabled) ? "e" : "d", + priv->sec_info.encryption_mode, + bss_desc->privacy); + return true; + } + return false; +} + +/* + * This function checks if adhoc AES is enabled in driver and scanned network is + * compatible with it. + */ +static bool +mwifiex_is_bss_adhoc_aes(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && + ((!bss_desc->bcn_wpa_ie) || + ((*(bss_desc->bcn_wpa_ie)). + vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && + ((!bss_desc->bcn_rsn_ie) || + ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && + !priv->sec_info.encryption_mode && bss_desc->privacy) { + return true; + } + return false; +} + +/* + * This function checks if dynamic WEP is enabled in driver and scanned network + * is compatible with it. + */ +static bool +mwifiex_is_bss_dynamic_wep(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && + !priv->sec_info.wpa2_enabled && + ((!bss_desc->bcn_wpa_ie) || + ((*(bss_desc->bcn_wpa_ie)). + vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && + ((!bss_desc->bcn_rsn_ie) || + ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && + priv->sec_info.encryption_mode && bss_desc->privacy) { + mwifiex_dbg(priv->adapter, INFO, + "info: %s: dynamic\t" + "WEP: wpa_ie=%#x wpa2_ie=%#x\t" + "EncMode=%#x privacy=%#x\n", + __func__, + (bss_desc->bcn_wpa_ie) ? + (*bss_desc->bcn_wpa_ie). + vend_hdr.element_id : 0, + (bss_desc->bcn_rsn_ie) ? + (*bss_desc->bcn_rsn_ie). + ieee_hdr.element_id : 0, + priv->sec_info.encryption_mode, + bss_desc->privacy); + return true; + } + return false; +} + +/* + * This function checks if a scanned network is 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 + * + * Compatibility is not matched while roaming, except for mode. + */ +static s32 +mwifiex_is_network_compatible(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc, u32 mode) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + bss_desc->disable_11n = false; + + /* Don't check for compatibility if roaming */ + if (priv->media_connected && + (priv->bss_mode == NL80211_IFTYPE_STATION) && + (bss_desc->bss_mode == NL80211_IFTYPE_STATION)) + return 0; + + if (priv->wps.session_enable) { + mwifiex_dbg(adapter, IOCTL, + "info: return success directly in WPS period\n"); + return 0; + } + + if (bss_desc->chan_sw_ie_present) { + mwifiex_dbg(adapter, INFO, + "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n"); + return -1; + } + + if (mwifiex_is_bss_wapi(priv, bss_desc)) { + mwifiex_dbg(adapter, INFO, + "info: return success for WAPI AP\n"); + return 0; + } + + if (bss_desc->bss_mode == mode) { + if (mwifiex_is_bss_no_sec(priv, bss_desc)) { + /* No security */ + return 0; + } else if (mwifiex_is_bss_static_wep(priv, bss_desc)) { + /* Static WEP enabled */ + mwifiex_dbg(adapter, INFO, + "info: Disable 11n in WEP mode.\n"); + bss_desc->disable_11n = true; + return 0; + } else if (mwifiex_is_bss_wpa(priv, bss_desc)) { + /* WPA enabled */ + if (((priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN) && + bss_desc->bcn_ht_cap) && + !mwifiex_is_wpa_oui_present(bss_desc, + CIPHER_SUITE_CCMP)) { + + if (mwifiex_is_wpa_oui_present + (bss_desc, CIPHER_SUITE_TKIP)) { + mwifiex_dbg(adapter, INFO, + "info: Disable 11n if AES\t" + "is not supported by AP\n"); + bss_desc->disable_11n = true; + } else { + return -1; + } + } + return 0; + } else if (mwifiex_is_bss_wpa2(priv, bss_desc)) { + /* WPA2 enabled */ + if (((priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN) && + bss_desc->bcn_ht_cap) && + !mwifiex_is_rsn_oui_present(bss_desc, + CIPHER_SUITE_CCMP)) { + + if (mwifiex_is_rsn_oui_present + (bss_desc, CIPHER_SUITE_TKIP)) { + mwifiex_dbg(adapter, INFO, + "info: Disable 11n if AES\t" + "is not supported by AP\n"); + bss_desc->disable_11n = true; + } else { + return -1; + } + } + return 0; + } else if (mwifiex_is_bss_adhoc_aes(priv, bss_desc)) { + /* Ad-hoc AES enabled */ + return 0; + } else if (mwifiex_is_bss_dynamic_wep(priv, bss_desc)) { + /* Dynamic WEP enabled */ + return 0; + } + + /* Security doesn't match */ + mwifiex_dbg(adapter, ERROR, + "info: %s: failed: wpa_ie=%#x wpa2_ie=%#x WEP=%s\t" + "WPA=%s WPA2=%s EncMode=%#x privacy=%#x\n", + __func__, + (bss_desc->bcn_wpa_ie) ? + (*bss_desc->bcn_wpa_ie).vend_hdr.element_id : 0, + (bss_desc->bcn_rsn_ie) ? + (*bss_desc->bcn_rsn_ie).ieee_hdr.element_id : 0, + (priv->sec_info.wep_enabled) ? "e" : "d", + (priv->sec_info.wpa_enabled) ? "e" : "d", + (priv->sec_info.wpa2_enabled) ? "e" : "d", + priv->sec_info.encryption_mode, bss_desc->privacy); + return -1; + } + + /* Mode doesn't match */ + return -1; +} + +/* + * This function creates a channel list for the driver to scan, based + * on region/band information. + * + * This routine is used for any scan that is not provided with a + * specific channel list to scan. + */ +static int +mwifiex_scan_create_channel_list(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg + *user_scan_in, + struct mwifiex_chan_scan_param_set + *scan_chan_list, + u8 filtered_scan) +{ + enum ieee80211_band band; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *ch; + struct mwifiex_adapter *adapter = priv->adapter; + int chan_idx = 0, i; + + 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_chan_list[chan_idx].radio_type = band; + + if (user_scan_in && + user_scan_in->chan_list[0].scan_time) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16((u16) user_scan_in-> + chan_list[0].scan_time); + else if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->passive_scan_time); + else + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->active_scan_time); + + if (ch->flags & IEEE80211_CHAN_NO_IR) + scan_chan_list[chan_idx].chan_scan_mode_bitmap + |= (MWIFIEX_PASSIVE_SCAN | + MWIFIEX_HIDDEN_SSID_REPORT); + else + scan_chan_list[chan_idx].chan_scan_mode_bitmap + &= ~MWIFIEX_PASSIVE_SCAN; + scan_chan_list[chan_idx].chan_number = + (u32) ch->hw_value; + if (filtered_scan) { + scan_chan_list[chan_idx].max_scan_time = + cpu_to_le16(adapter->specific_scan_time); + scan_chan_list[chan_idx].chan_scan_mode_bitmap + |= MWIFIEX_DISABLE_CHAN_FILT; + } + chan_idx++; + } + + } + return chan_idx; +} + +/* This function appends rate TLV to scan config command. */ +static int +mwifiex_append_rate_tlv(struct mwifiex_private *priv, + struct mwifiex_scan_cmd_config *scan_cfg_out, + u8 radio) +{ + struct mwifiex_ie_types_rates_param_set *rates_tlv; + u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos; + u32 rates_size; + + memset(rates, 0, sizeof(rates)); + + tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len; + + if (priv->scan_request) + rates_size = mwifiex_get_rates_from_cfg80211(priv, rates, + radio); + else + rates_size = mwifiex_get_supported_rates(priv, rates); + + mwifiex_dbg(priv->adapter, CMD, + "info: SCAN_CMD: Rates size = %d\n", + rates_size); + rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos; + rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); + rates_tlv->header.len = cpu_to_le16((u16) rates_size); + memcpy(rates_tlv->rates, rates, rates_size); + scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size; + + return rates_size; +} + +/* + * This function constructs and sends multiple scan config commands to + * the firmware. + * + * Previous routines in the code flow have created a scan command configuration + * with any requested TLVs. This function splits the channel TLV into maximum + * channels supported per scan lists and sends the portion of the channel TLV, + * along with the other TLVs, to the firmware. + */ +static int +mwifiex_scan_channel_list(struct mwifiex_private *priv, + u32 max_chan_per_scan, u8 filtered_scan, + struct mwifiex_scan_cmd_config *scan_cfg_out, + struct mwifiex_ie_types_chan_list_param_set + *chan_tlv_out, + struct mwifiex_chan_scan_param_set *scan_chan_list) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret = 0; + struct mwifiex_chan_scan_param_set *tmp_chan_list; + struct mwifiex_chan_scan_param_set *start_chan; + struct cmd_ctrl_node *cmd_node, *tmp_node; + unsigned long flags; + u32 tlv_idx, rates_size, cmd_no; + u32 total_scan_time; + u32 done_early; + u8 radio_type; + + if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) { + mwifiex_dbg(priv->adapter, ERROR, + "info: Scan: Null detect: %p, %p, %p\n", + scan_cfg_out, chan_tlv_out, scan_chan_list); + return -1; + } + + /* Check csa channel expiry before preparing scan list */ + mwifiex_11h_get_csa_closed_channel(priv); + + chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + + /* Set the temp channel struct pointer to the start of the desired + list */ + tmp_chan_list = scan_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 (tmp_chan_list->chan_number) { + + tlv_idx = 0; + total_scan_time = 0; + radio_type = 0; + chan_tlv_out->header.len = 0; + start_chan = tmp_chan_list; + done_early = false; + + /* + * 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 && + tmp_chan_list->chan_number && !done_early) { + + if (tmp_chan_list->chan_number == priv->csa_chan) { + tmp_chan_list++; + continue; + } + + radio_type = tmp_chan_list->radio_type; + mwifiex_dbg(priv->adapter, INFO, + "info: Scan: Chan(%3d), Radio(%d),\t" + "Mode(%d, %d), Dur(%d)\n", + tmp_chan_list->chan_number, + tmp_chan_list->radio_type, + tmp_chan_list->chan_scan_mode_bitmap + & MWIFIEX_PASSIVE_SCAN, + (tmp_chan_list->chan_scan_mode_bitmap + & MWIFIEX_DISABLE_CHAN_FILT) >> 1, + le16_to_cpu(tmp_chan_list->max_scan_time)); + + /* Copy the current channel TLV to the command being + prepared */ + memcpy(chan_tlv_out->chan_scan_param + tlv_idx, + tmp_chan_list, + sizeof(chan_tlv_out->chan_scan_param)); + + /* Increment the TLV header length by the size + appended */ + le16_add_cpu(&chan_tlv_out->header.len, + sizeof(chan_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. + */ + scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out - + scan_cfg_out->tlv_buf); + + /* Add the size of the channel tlv header and the data + length */ + scan_cfg_out->tlv_buf_len += + (sizeof(chan_tlv_out->header) + + le16_to_cpu(chan_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 += + le16_to_cpu(tmp_chan_list->max_scan_time); + + done_early = false; + + /* 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 && + (tmp_chan_list->chan_number == 1 || + tmp_chan_list->chan_number == 6 || + tmp_chan_list->chan_number == 11)) + done_early = true; + + /* Increment the tmp pointer to the next channel to + be scanned */ + tmp_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 && + (tmp_chan_list->chan_number == 1 || + tmp_chan_list->chan_number == 6 || + tmp_chan_list->chan_number == 11)) + done_early = true; + } + + /* The total scan time should be less than scan command timeout + value */ + if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) { + mwifiex_dbg(priv->adapter, ERROR, + "total scan time %dms\t" + "is over limit (%dms), scan skipped\n", + total_scan_time, + MWIFIEX_MAX_TOTAL_SCAN_TIME); + ret = -1; + break; + } + + rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out, + radio_type); + + priv->adapter->scan_channels = start_chan; + + /* Send the scan command to the firmware with the specified + cfg */ + if (priv->adapter->ext_scan) + cmd_no = HostCmd_CMD_802_11_SCAN_EXT; + else + cmd_no = HostCmd_CMD_802_11_SCAN; + + ret = mwifiex_send_cmd(priv, cmd_no, HostCmd_ACT_GEN_SET, + 0, scan_cfg_out, false); + + /* rate IE is updated per scan command but same starting + * pointer is used each time so that rate IE from earlier + * scan_cfg_out->buf is overwritten with new one. + */ + scan_cfg_out->tlv_buf_len -= + sizeof(struct mwifiex_ie_types_header) + rates_size; + + if (ret) { + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, + list) { + list_del(&cmd_node->list); + cmd_node->wait_q_enabled = false; + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + break; + } + } + + if (ret) + return -1; + + return 0; +} + +/* + * This function constructs a scan command configuration structure to use + * in scan commands. + * + * Application layer or other functions can invoke network scanning + * with a scan configuration supplied in a user scan configuration structure. + * This structure is used as the basis of one or many scan command configuration + * commands that are sent to the command processing module and eventually to the + * firmware. + * + * This function creates a scan command configuration structure 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, the filter is disabled/cleared. + * If the number of probes is not set, adapter default setting is used. + */ +static void +mwifiex_config_scan(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg *user_scan_in, + struct mwifiex_scan_cmd_config *scan_cfg_out, + struct mwifiex_ie_types_chan_list_param_set **chan_list_out, + struct mwifiex_chan_scan_param_set *scan_chan_list, + u8 *max_chan_per_scan, u8 *filtered_scan, + u8 *scan_current_only) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_num_probes *num_probes_tlv; + struct mwifiex_ie_types_scan_chan_gap *chan_gap_tlv; + struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; + struct mwifiex_ie_types_bssid_list *bssid_tlv; + u8 *tlv_pos; + u32 num_probes; + u32 ssid_len; + u32 chan_idx; + u32 chan_num; + u32 scan_type; + u16 scan_dur; + u8 channel; + u8 radio_type; + int i; + u8 ssid_filter; + struct mwifiex_ie_types_htcap *ht_cap; + struct mwifiex_ie_types_bss_mode *bss_mode; + + /* 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 *chan_list_out. The difference + between the *chan_list_out and the tlv_buf start will be used to + calculate the size of anything we add in this routine. */ + scan_cfg_out->tlv_buf_len = 0; + + /* Running tlv pointer. Assigned to chan_list_out at end of function + so later routines know where channels can be added to the command + buf */ + tlv_pos = scan_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 */ + *filtered_scan = false; + + /* 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. */ + *scan_current_only = false; + + if (user_scan_in) { + + /* Default the ssid_filter flag to TRUE, set false under + certain wildcard conditions and qualified by the existence + of an SSID list before marking the scan as filtered */ + ssid_filter = true; + + /* Set the BSS type scan filter, use Adapter setting if + unset */ + scan_cfg_out->bss_mode = + (user_scan_in->bss_mode ? (u8) user_scan_in-> + bss_mode : (u8) adapter->scan_mode); + + /* Set the number of probes to send, use Adapter setting + if unset */ + num_probes = + (user_scan_in->num_probes ? user_scan_in-> + num_probes : adapter->scan_probes); + + /* + * Set the BSSID filter to the incoming configuration, + * if non-zero. If not set, it will remain disabled + * (all zeros). + */ + memcpy(scan_cfg_out->specific_bssid, + user_scan_in->specific_bssid, + sizeof(scan_cfg_out->specific_bssid)); + + if (adapter->ext_scan && + !is_zero_ether_addr(scan_cfg_out->specific_bssid)) { + bssid_tlv = + (struct mwifiex_ie_types_bssid_list *)tlv_pos; + bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID); + bssid_tlv->header.len = cpu_to_le16(ETH_ALEN); + memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid, + ETH_ALEN); + tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list); + } + + for (i = 0; i < user_scan_in->num_ssids; i++) { + ssid_len = user_scan_in->ssid_list[i].ssid_len; + + wildcard_ssid_tlv = + (struct mwifiex_ie_types_wildcard_ssid_params *) + tlv_pos; + wildcard_ssid_tlv->header.type = + cpu_to_le16(TLV_TYPE_WILDCARDSSID); + wildcard_ssid_tlv->header.len = cpu_to_le16( + (u16) (ssid_len + sizeof(wildcard_ssid_tlv-> + max_ssid_length))); + + /* + * max_ssid_length = 0 tells firmware to perform + * specific scan for the SSID filled, whereas + * max_ssid_length = IEEE80211_MAX_SSID_LEN is for + * wildcard scan. + */ + if (ssid_len) + wildcard_ssid_tlv->max_ssid_length = 0; + else + wildcard_ssid_tlv->max_ssid_length = + IEEE80211_MAX_SSID_LEN; + + if (!memcmp(user_scan_in->ssid_list[i].ssid, + "DIRECT-", 7)) + wildcard_ssid_tlv->max_ssid_length = 0xfe; + + memcpy(wildcard_ssid_tlv->ssid, + user_scan_in->ssid_list[i].ssid, ssid_len); + + tlv_pos += (sizeof(wildcard_ssid_tlv->header) + + le16_to_cpu(wildcard_ssid_tlv->header.len)); + + mwifiex_dbg(adapter, INFO, + "info: scan: ssid[%d]: %s, %d\n", + i, wildcard_ssid_tlv->ssid, + wildcard_ssid_tlv->max_ssid_length); + + /* Empty wildcard ssid with a maxlen will match many or + potentially all SSIDs (maxlen == 32), therefore do + not treat the scan as + filtered. */ + if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) + ssid_filter = false; + } + + /* + * 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 ((i && ssid_filter) || + !is_zero_ether_addr(scan_cfg_out->specific_bssid)) + *filtered_scan = true; + + if (user_scan_in->scan_chan_gap) { + mwifiex_dbg(adapter, INFO, + "info: scan: channel gap = %d\n", + user_scan_in->scan_chan_gap); + *max_chan_per_scan = + MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; + + chan_gap_tlv = (void *)tlv_pos; + chan_gap_tlv->header.type = + cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); + chan_gap_tlv->header.len = + cpu_to_le16(sizeof(chan_gap_tlv->chan_gap)); + chan_gap_tlv->chan_gap = + cpu_to_le16((user_scan_in->scan_chan_gap)); + tlv_pos += + sizeof(struct mwifiex_ie_types_scan_chan_gap); + } + } else { + scan_cfg_out->bss_mode = (u8) adapter->scan_mode; + num_probes = adapter->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 (*filtered_scan) + *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; + else + *max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD; + + if (adapter->ext_scan) { + bss_mode = (struct mwifiex_ie_types_bss_mode *)tlv_pos; + bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE); + bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode)); + bss_mode->bss_mode = scan_cfg_out->bss_mode; + tlv_pos += sizeof(bss_mode->header) + + le16_to_cpu(bss_mode->header.len); + } + + /* If the input config or adapter has the number of Probes set, + add tlv */ + if (num_probes) { + + mwifiex_dbg(adapter, INFO, + "info: scan: num_probes = %d\n", + num_probes); + + num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos; + num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); + num_probes_tlv->header.len = + cpu_to_le16(sizeof(num_probes_tlv->num_probes)); + num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes); + + tlv_pos += sizeof(num_probes_tlv->header) + + le16_to_cpu(num_probes_tlv->header.len); + + } + + if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && + (priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN)) { + ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos; + memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); + ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_cap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + radio_type = + mwifiex_band_to_radio_type(priv->adapter->config_bands); + mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); + tlv_pos += sizeof(struct mwifiex_ie_types_htcap); + } + + /* Append vendor specific IE TLV */ + mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos); + + /* + * Set the output for the channel TLV to the address in the tlv buffer + * past any TLVs that were added in this function (SSID, num_probes). + * Channel TLVs will be added past this for each scan command, + * preserving the TLVs that were previously added. + */ + *chan_list_out = + (struct mwifiex_ie_types_chan_list_param_set *) tlv_pos; + + if (user_scan_in && user_scan_in->chan_list[0].chan_number) { + + mwifiex_dbg(adapter, INFO, + "info: Scan: Using supplied channel list\n"); + + for (chan_idx = 0; + chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX && + user_scan_in->chan_list[chan_idx].chan_number; + chan_idx++) { + + channel = user_scan_in->chan_list[chan_idx].chan_number; + (scan_chan_list + chan_idx)->chan_number = channel; + + radio_type = + user_scan_in->chan_list[chan_idx].radio_type; + (scan_chan_list + chan_idx)->radio_type = radio_type; + + scan_type = user_scan_in->chan_list[chan_idx].scan_type; + + if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) + (scan_chan_list + + chan_idx)->chan_scan_mode_bitmap + |= (MWIFIEX_PASSIVE_SCAN | + MWIFIEX_HIDDEN_SSID_REPORT); + else + (scan_chan_list + + chan_idx)->chan_scan_mode_bitmap + &= ~MWIFIEX_PASSIVE_SCAN; + + if (*filtered_scan) + (scan_chan_list + + chan_idx)->chan_scan_mode_bitmap + |= MWIFIEX_DISABLE_CHAN_FILT; + + if (user_scan_in->chan_list[chan_idx].scan_time) { + scan_dur = (u16) user_scan_in-> + chan_list[chan_idx].scan_time; + } else { + if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) + scan_dur = adapter->passive_scan_time; + else if (*filtered_scan) + scan_dur = adapter->specific_scan_time; + else + scan_dur = adapter->active_scan_time; + } + + (scan_chan_list + chan_idx)->min_scan_time = + cpu_to_le16(scan_dur); + (scan_chan_list + chan_idx)->max_scan_time = + cpu_to_le16(scan_dur); + } + + /* Check if we are only scanning the current channel */ + if ((chan_idx == 1) && + (user_scan_in->chan_list[0].chan_number == + priv->curr_bss_params.bss_descriptor.channel)) { + *scan_current_only = true; + mwifiex_dbg(adapter, INFO, + "info: Scan: Scanning current channel only\n"); + } + chan_num = chan_idx; + } else { + mwifiex_dbg(adapter, INFO, + "info: Scan: Creating full region channel list\n"); + chan_num = mwifiex_scan_create_channel_list(priv, user_scan_in, + scan_chan_list, + *filtered_scan); + } + +} + +/* + * This function inspects the scan response buffer for pointers to + * expected TLVs. + * + * TLVs can be included at the end of the scan response BSS information. + * + * Data in the buffer is parsed pointers to TLVs that can potentially + * be passed back in the response. + */ +static void +mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter, + struct mwifiex_ie_types_data *tlv, + u32 tlv_buf_size, u32 req_tlv_type, + struct mwifiex_ie_types_data **tlv_data) +{ + struct mwifiex_ie_types_data *current_tlv; + u32 tlv_buf_left; + u32 tlv_type; + u32 tlv_len; + + current_tlv = tlv; + tlv_buf_left = tlv_buf_size; + *tlv_data = NULL; + + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: tlv_buf_size = %d\n", + tlv_buf_size); + + while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) { + + tlv_type = le16_to_cpu(current_tlv->header.type); + tlv_len = le16_to_cpu(current_tlv->header.len); + + if (sizeof(tlv->header) + tlv_len > tlv_buf_left) { + mwifiex_dbg(adapter, ERROR, + "SCAN_RESP: TLV buffer corrupt\n"); + break; + } + + if (req_tlv_type == tlv_type) { + switch (tlv_type) { + case TLV_TYPE_TSFTIMESTAMP: + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: TSF\t" + "timestamp TLV, len = %d\n", + tlv_len); + *tlv_data = current_tlv; + break; + case TLV_TYPE_CHANNELBANDLIST: + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: channel\t" + "band list TLV, len = %d\n", + tlv_len); + *tlv_data = current_tlv; + break; + default: + mwifiex_dbg(adapter, ERROR, + "SCAN_RESP: unhandled TLV = %d\n", + tlv_type); + /* Give up, this seems corrupted */ + return; + } + } + + if (*tlv_data) + break; + + + tlv_buf_left -= (sizeof(tlv->header) + tlv_len); + current_tlv = + (struct mwifiex_ie_types_data *) (current_tlv->data + + tlv_len); + + } /* while */ +} + +/* + * This function parses provided beacon buffer and updates + * respective fields in bss descriptor structure. + */ +int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, + struct mwifiex_bssdescriptor *bss_entry) +{ + int ret = 0; + u8 element_id; + struct ieee_types_fh_param_set *fh_param_set; + struct ieee_types_ds_param_set *ds_param_set; + struct ieee_types_cf_param_set *cf_param_set; + struct ieee_types_ibss_param_set *ibss_param_set; + u8 *current_ptr; + u8 *rate; + u8 element_len; + u16 total_ie_len; + u8 bytes_to_copy; + u8 rate_size; + u8 found_data_rate_ie; + u32 bytes_left; + struct ieee_types_vendor_specific *vendor_ie; + const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; + const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + + found_data_rate_ie = false; + rate_size = 0; + current_ptr = bss_entry->beacon_buf; + bytes_left = bss_entry->beacon_buf_size; + + /* Process variable IE */ + while (bytes_left >= 2) { + element_id = *current_ptr; + element_len = *(current_ptr + 1); + total_ie_len = element_len + sizeof(struct ieee_types_header); + + if (bytes_left < total_ie_len) { + mwifiex_dbg(adapter, ERROR, + "err: InterpretIE: in processing\t" + "IE, bytes left < IE length\n"); + return -1; + } + switch (element_id) { + case WLAN_EID_SSID: + bss_entry->ssid.ssid_len = element_len; + memcpy(bss_entry->ssid.ssid, (current_ptr + 2), + element_len); + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: ssid: %-32s\n", + bss_entry->ssid.ssid); + break; + + case WLAN_EID_SUPP_RATES: + memcpy(bss_entry->data_rates, current_ptr + 2, + element_len); + memcpy(bss_entry->supported_rates, current_ptr + 2, + element_len); + rate_size = element_len; + found_data_rate_ie = true; + break; + + case WLAN_EID_FH_PARAMS: + fh_param_set = + (struct ieee_types_fh_param_set *) current_ptr; + memcpy(&bss_entry->phy_param_set.fh_param_set, + fh_param_set, + sizeof(struct ieee_types_fh_param_set)); + break; + + case WLAN_EID_DS_PARAMS: + ds_param_set = + (struct ieee_types_ds_param_set *) current_ptr; + + bss_entry->channel = ds_param_set->current_chan; + + memcpy(&bss_entry->phy_param_set.ds_param_set, + ds_param_set, + sizeof(struct ieee_types_ds_param_set)); + break; + + case WLAN_EID_CF_PARAMS: + cf_param_set = + (struct ieee_types_cf_param_set *) current_ptr; + memcpy(&bss_entry->ss_param_set.cf_param_set, + cf_param_set, + sizeof(struct ieee_types_cf_param_set)); + break; + + case WLAN_EID_IBSS_PARAMS: + ibss_param_set = + (struct ieee_types_ibss_param_set *) + current_ptr; + memcpy(&bss_entry->ss_param_set.ibss_param_set, + ibss_param_set, + sizeof(struct ieee_types_ibss_param_set)); + break; + + case WLAN_EID_ERP_INFO: + bss_entry->erp_flags = *(current_ptr + 2); + break; + + case WLAN_EID_PWR_CONSTRAINT: + bss_entry->local_constraint = *(current_ptr + 2); + bss_entry->sensed_11h = true; + break; + + case WLAN_EID_CHANNEL_SWITCH: + bss_entry->chan_sw_ie_present = true; + case WLAN_EID_PWR_CAPABILITY: + case WLAN_EID_TPC_REPORT: + case WLAN_EID_QUIET: + bss_entry->sensed_11h = true; + break; + + case WLAN_EID_EXT_SUPP_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) > + MWIFIEX_SUPPORTED_RATES) + bytes_to_copy = + (MWIFIEX_SUPPORTED_RATES - + rate_size); + else + bytes_to_copy = element_len; + + rate = (u8 *) bss_entry->data_rates; + rate += rate_size; + memcpy(rate, current_ptr + 2, bytes_to_copy); + + rate = (u8 *) bss_entry->supported_rates; + rate += rate_size; + memcpy(rate, current_ptr + 2, bytes_to_copy); + } + break; + + case WLAN_EID_VENDOR_SPECIFIC: + vendor_ie = (struct ieee_types_vendor_specific *) + current_ptr; + + if (!memcmp + (vendor_ie->vend_hdr.oui, wpa_oui, + sizeof(wpa_oui))) { + bss_entry->bcn_wpa_ie = + (struct ieee_types_vendor_specific *) + current_ptr; + bss_entry->wpa_offset = (u16) + (current_ptr - bss_entry->beacon_buf); + } else if (!memcmp(vendor_ie->vend_hdr.oui, wmm_oui, + sizeof(wmm_oui))) { + if (total_ie_len == + sizeof(struct ieee_types_wmm_parameter) || + total_ie_len == + sizeof(struct ieee_types_wmm_info)) + /* + * Only accept and copy the WMM IE if + * it matches the size expected for the + * WMM Info IE or the WMM Parameter IE. + */ + memcpy((u8 *) &bss_entry->wmm_ie, + current_ptr, total_ie_len); + } + break; + case WLAN_EID_RSN: + bss_entry->bcn_rsn_ie = + (struct ieee_types_generic *) current_ptr; + bss_entry->rsn_offset = (u16) (current_ptr - + bss_entry->beacon_buf); + break; + case WLAN_EID_BSS_AC_ACCESS_DELAY: + bss_entry->bcn_wapi_ie = + (struct ieee_types_generic *) current_ptr; + bss_entry->wapi_offset = (u16) (current_ptr - + bss_entry->beacon_buf); + break; + case WLAN_EID_HT_CAPABILITY: + bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *) + (current_ptr + + sizeof(struct ieee_types_header)); + bss_entry->ht_cap_offset = (u16) (current_ptr + + sizeof(struct ieee_types_header) - + bss_entry->beacon_buf); + break; + case WLAN_EID_HT_OPERATION: + bss_entry->bcn_ht_oper = + (struct ieee80211_ht_operation *)(current_ptr + + sizeof(struct ieee_types_header)); + bss_entry->ht_info_offset = (u16) (current_ptr + + sizeof(struct ieee_types_header) - + bss_entry->beacon_buf); + break; + case WLAN_EID_VHT_CAPABILITY: + bss_entry->disable_11ac = false; + bss_entry->bcn_vht_cap = + (void *)(current_ptr + + sizeof(struct ieee_types_header)); + bss_entry->vht_cap_offset = + (u16)((u8 *)bss_entry->bcn_vht_cap - + bss_entry->beacon_buf); + break; + case WLAN_EID_VHT_OPERATION: + bss_entry->bcn_vht_oper = + (void *)(current_ptr + + sizeof(struct ieee_types_header)); + bss_entry->vht_info_offset = + (u16)((u8 *)bss_entry->bcn_vht_oper - + bss_entry->beacon_buf); + break; + case WLAN_EID_BSS_COEX_2040: + bss_entry->bcn_bss_co_2040 = current_ptr; + bss_entry->bss_co_2040_offset = + (u16) (current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_EXT_CAPABILITY: + bss_entry->bcn_ext_cap = current_ptr; + bss_entry->ext_cap_offset = + (u16) (current_ptr - bss_entry->beacon_buf); + break; + case WLAN_EID_OPMODE_NOTIF: + bss_entry->oper_mode = (void *)current_ptr; + bss_entry->oper_mode_offset = + (u16)((u8 *)bss_entry->oper_mode - + bss_entry->beacon_buf); + break; + default: + break; + } + + current_ptr += element_len + 2; + + /* Need to account for IE ID and IE Len */ + bytes_left -= (element_len + 2); + + } /* while (bytes_left > 2) */ + return ret; +} + +/* + * This function converts radio type scan parameter to a band configuration + * to be used in join command. + */ +static u8 +mwifiex_radio_type_to_band(u8 radio_type) +{ + switch (radio_type) { + case HostCmd_SCAN_RADIO_TYPE_A: + return BAND_A; + case HostCmd_SCAN_RADIO_TYPE_BG: + default: + return BAND_G; + } +} + +/* + * This is an internal function used to start a scan based on an input + * configuration. + * + * This uses 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. + */ +int mwifiex_scan_networks(struct mwifiex_private *priv, + const struct mwifiex_user_scan_cfg *user_scan_in) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node; + union mwifiex_scan_cmd_config_tlv *scan_cfg_out; + struct mwifiex_ie_types_chan_list_param_set *chan_list_out; + struct mwifiex_chan_scan_param_set *scan_chan_list; + u8 filtered_scan; + u8 scan_current_chan_only; + u8 max_chan_per_scan; + unsigned long flags; + + if (adapter->scan_processing) { + mwifiex_dbg(adapter, WARN, + "cmd: Scan already in process...\n"); + return -EBUSY; + } + + if (priv->scan_block) { + mwifiex_dbg(adapter, WARN, + "cmd: Scan is blocked during association...\n"); + return -EBUSY; + } + + if (adapter->surprise_removed || adapter->is_cmd_timedout) { + mwifiex_dbg(adapter, ERROR, + "Ignore scan. Card removed or firmware in bad state\n"); + return -EFAULT; + } + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = true; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), + GFP_KERNEL); + if (!scan_cfg_out) { + ret = -ENOMEM; + goto done; + } + + scan_chan_list = kcalloc(MWIFIEX_USER_SCAN_CHAN_MAX, + sizeof(struct mwifiex_chan_scan_param_set), + GFP_KERNEL); + if (!scan_chan_list) { + kfree(scan_cfg_out); + ret = -ENOMEM; + goto done; + } + + mwifiex_config_scan(priv, user_scan_in, &scan_cfg_out->config, + &chan_list_out, scan_chan_list, &max_chan_per_scan, + &filtered_scan, &scan_current_chan_only); + + ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan, + &scan_cfg_out->config, chan_list_out, + scan_chan_list); + + /* Get scan command from scan_pending_q and put to cmd_pending_q */ + if (!ret) { + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + if (!list_empty(&adapter->scan_pending_q)) { + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, + true); + queue_work(adapter->workqueue, &adapter->main_work); + + /* Perform internal scan synchronously */ + if (!priv->scan_request) { + mwifiex_dbg(adapter, INFO, + "wait internal scan\n"); + mwifiex_wait_queue_complete(adapter, cmd_node); + } + } else { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + } + } + + kfree(scan_cfg_out); + kfree(scan_chan_list); +done: + if (ret) { + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + } + return ret; +} + +/* + * This function prepares a scan command to be sent to the firmware. + * + * This uses the scan command configuration sent to the command processing + * module in command preparation stage to configure a scan command structure + * 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. + * + * Preparation also includes - + * - Setting command ID, and proper size + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, + struct mwifiex_scan_cmd_config *scan_cfg) +{ + struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan; + + /* Set fixed field variables in scan command */ + scan_cmd->bss_mode = scan_cfg->bss_mode; + memcpy(scan_cmd->bssid, scan_cfg->specific_bssid, + sizeof(scan_cmd->bssid)); + memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode) + + sizeof(scan_cmd->bssid) + + scan_cfg->tlv_buf_len + S_DS_GEN)); + + return 0; +} + +/* + * This function checks compatibility of requested network with current + * driver settings. + */ +int mwifiex_check_network_compatibility(struct mwifiex_private *priv, + struct mwifiex_bssdescriptor *bss_desc) +{ + int ret = -1; + + if (!bss_desc) + return -1; + + if ((mwifiex_get_cfp(priv, (u8) bss_desc->bss_band, + (u16) bss_desc->channel, 0))) { + switch (priv->bss_mode) { + case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_ADHOC: + ret = mwifiex_is_network_compatible(priv, bss_desc, + priv->bss_mode); + if (ret) + mwifiex_dbg(priv->adapter, ERROR, + "Incompatible network settings\n"); + break; + default: + ret = 0; + } + } + + return ret; +} + +/* This function checks if SSID string contains all zeroes or length is zero */ +static bool mwifiex_is_hidden_ssid(struct cfg80211_ssid *ssid) +{ + int idx; + + for (idx = 0; idx < ssid->ssid_len; idx++) { + if (ssid->ssid[idx]) + return false; + } + + return true; +} + +/* This function checks if any hidden SSID found in passive scan channels + * and save those channels for specific SSID active scan + */ +static int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv, + struct cfg80211_bss *bss) +{ + struct mwifiex_bssdescriptor *bss_desc; + int ret; + int chid; + + /* Allocate and fill new bss descriptor */ + bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + + if (mwifiex_is_hidden_ssid(&bss_desc->ssid)) { + mwifiex_dbg(priv->adapter, INFO, "found hidden SSID\n"); + for (chid = 0 ; chid < MWIFIEX_USER_SCAN_CHAN_MAX; chid++) { + if (priv->hidden_chan[chid].chan_number == + bss->channel->hw_value) + break; + + if (!priv->hidden_chan[chid].chan_number) { + priv->hidden_chan[chid].chan_number = + bss->channel->hw_value; + priv->hidden_chan[chid].radio_type = + bss->channel->band; + priv->hidden_chan[chid].scan_type = + MWIFIEX_SCAN_TYPE_ACTIVE; + break; + } + } + } + +done: + kfree(bss_desc); + return 0; +} + +static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv, + struct cfg80211_bss *bss) +{ + struct mwifiex_bssdescriptor *bss_desc; + int ret; + unsigned long flags; + + /* Allocate and fill new bss descriptor */ + bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + + ret = mwifiex_check_network_compatibility(priv, bss_desc); + if (ret) + goto done; + + spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); + /* Make a copy of current BSSID descriptor */ + memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, + sizeof(priv->curr_bss_params.bss_descriptor)); + + /* The contents of beacon_ie will be copied to its own buffer + * in mwifiex_save_curr_bcn() + */ + mwifiex_save_curr_bcn(priv); + spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); + +done: + /* beacon_ie buffer was allocated in function + * mwifiex_fill_new_bss_desc(). Free it now. + */ + kfree(bss_desc->beacon_buf); + kfree(bss_desc); + return 0; +} + +static int +mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, + u32 *bytes_left, u64 fw_tsf, u8 *radio_type, + bool ext_scan, s32 rssi_val) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_chan_freq_power *cfp; + struct cfg80211_bss *bss; + u8 bssid[ETH_ALEN]; + s32 rssi; + const u8 *ie_buf; + size_t ie_len; + u16 channel = 0; + u16 beacon_size = 0; + u32 curr_bcn_bytes; + u32 freq; + u16 beacon_period; + u16 cap_info_bitmap; + u8 *current_ptr; + u64 timestamp; + struct mwifiex_fixed_bcn_param *bcn_param; + struct mwifiex_bss_priv *bss_priv; + + if (*bytes_left >= sizeof(beacon_size)) { + /* Extract & convert beacon size from command buffer */ + beacon_size = le16_to_cpu(*(__le16 *)(*bss_info)); + *bytes_left -= sizeof(beacon_size); + *bss_info += sizeof(beacon_size); + } + + if (!beacon_size || beacon_size > *bytes_left) { + *bss_info += *bytes_left; + *bytes_left = 0; + return -EFAULT; + } + + /* Initialize the current working beacon pointer for this BSS + * iteration + */ + current_ptr = *bss_info; + + /* Advance the return beacon pointer past the current beacon */ + *bss_info += beacon_size; + *bytes_left -= beacon_size; + + curr_bcn_bytes = beacon_size; + + /* First 5 fields are bssid, RSSI(for legacy scan only), + * time stamp, beacon interval, and capability information + */ + if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + + sizeof(struct mwifiex_fixed_bcn_param)) { + mwifiex_dbg(adapter, ERROR, + "InterpretIE: not enough bytes left\n"); + return -EFAULT; + } + + memcpy(bssid, current_ptr, ETH_ALEN); + current_ptr += ETH_ALEN; + curr_bcn_bytes -= ETH_ALEN; + + if (!ext_scan) { + rssi = (s32) *current_ptr; + rssi = (-rssi) * 100; /* Convert dBm to mBm */ + current_ptr += sizeof(u8); + curr_bcn_bytes -= sizeof(u8); + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: RSSI=%d\n", rssi); + } else { + rssi = rssi_val; + } + + bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; + current_ptr += sizeof(*bcn_param); + curr_bcn_bytes -= sizeof(*bcn_param); + + timestamp = le64_to_cpu(bcn_param->timestamp); + beacon_period = le16_to_cpu(bcn_param->beacon_period); + + cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: capabilities=0x%X\n", + cap_info_bitmap); + + /* Rest of the current buffer are IE's */ + ie_buf = current_ptr; + ie_len = curr_bcn_bytes; + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: IELength for this AP = %d\n", + curr_bcn_bytes); + + while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { + u8 element_id, element_len; + + element_id = *current_ptr; + element_len = *(current_ptr + 1); + if (curr_bcn_bytes < element_len + + sizeof(struct ieee_types_header)) { + mwifiex_dbg(adapter, ERROR, + "%s: bytes left < IE length\n", __func__); + return -EFAULT; + } + if (element_id == WLAN_EID_DS_PARAMS) { + channel = *(current_ptr + + sizeof(struct ieee_types_header)); + break; + } + + current_ptr += element_len + sizeof(struct ieee_types_header); + curr_bcn_bytes -= element_len + + sizeof(struct ieee_types_header); + } + + if (channel) { + struct ieee80211_channel *chan; + u8 band; + + /* Skip entry if on csa closed channel */ + if (channel == priv->csa_chan) { + mwifiex_dbg(adapter, WARN, + "Dropping entry on csa closed channel\n"); + return 0; + } + + band = BAND_G; + if (radio_type) + band = mwifiex_radio_type_to_band(*radio_type & + (BIT(0) | BIT(1))); + + cfp = mwifiex_get_cfp(priv, band, channel, 0); + + freq = cfp ? cfp->freq : 0; + + chan = ieee80211_get_channel(priv->wdev.wiphy, freq); + + if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { + bss = cfg80211_inform_bss(priv->wdev.wiphy, + chan, CFG80211_BSS_FTYPE_UNKNOWN, + bssid, timestamp, + cap_info_bitmap, beacon_period, + ie_buf, ie_len, rssi, GFP_KERNEL); + if (bss) { + bss_priv = (struct mwifiex_bss_priv *)bss->priv; + bss_priv->band = band; + bss_priv->fw_tsf = fw_tsf; + if (priv->media_connected && + !memcmp(bssid, priv->curr_bss_params. + bss_descriptor.mac_address, + ETH_ALEN)) + mwifiex_update_curr_bss_params(priv, + bss); + cfg80211_put_bss(priv->wdev.wiphy, bss); + } + + if ((chan->flags & IEEE80211_CHAN_RADAR) || + (chan->flags & IEEE80211_CHAN_NO_IR)) { + mwifiex_dbg(adapter, INFO, + "radar or passive channel %d\n", + channel); + mwifiex_save_hidden_ssid_channels(priv, bss); + } + } + } else { + mwifiex_dbg(adapter, WARN, "missing BSS channel IE\n"); + } + + return 0; +} + +static void mwifiex_complete_scan(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + adapter->survey_idx = 0; + if (adapter->curr_cmd->wait_q_enabled) { + adapter->cmd_wait_q.status = 0; + if (!priv->scan_request) { + mwifiex_dbg(adapter, INFO, + "complete internal scan\n"); + mwifiex_complete_cmd(adapter, adapter->curr_cmd); + } + } +} + +/* This function checks if any hidden SSID found in passive scan channels + * and do specific SSID active scan for those channels + */ +static int +mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + u8 id = 0; + struct mwifiex_user_scan_cfg *user_scan_cfg; + + if (adapter->active_scan_triggered || !priv->scan_request) { + adapter->active_scan_triggered = false; + return 0; + } + + if (!priv->hidden_chan[0].chan_number) { + mwifiex_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n"); + return 0; + } + user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); + + if (!user_scan_cfg) + return -ENOMEM; + + memset(user_scan_cfg, 0, sizeof(*user_scan_cfg)); + + for (id = 0; id < MWIFIEX_USER_SCAN_CHAN_MAX; id++) { + if (!priv->hidden_chan[id].chan_number) + break; + memcpy(&user_scan_cfg->chan_list[id], + &priv->hidden_chan[id], + sizeof(struct mwifiex_user_scan_chan)); + } + + adapter->active_scan_triggered = true; + user_scan_cfg->num_ssids = priv->scan_request->n_ssids; + user_scan_cfg->ssid_list = priv->scan_request->ssids; + + ret = mwifiex_scan_networks(priv, user_scan_cfg); + kfree(user_scan_cfg); + + memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan)); + + if (ret) { + dev_err(priv->adapter->dev, "scan failed: %d\n", ret); + return ret; + } + + return 0; +} +static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmd_node, *tmp_node; + unsigned long flags; + + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + if (list_empty(&adapter->scan_pending_q)) { + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + mwifiex_active_scan_req_for_passive_chan(priv); + + if (!adapter->ext_scan) + mwifiex_complete_scan(priv); + + if (priv->scan_request) { + mwifiex_dbg(adapter, INFO, + "info: notifying scan done\n"); + cfg80211_scan_done(priv->scan_request, 0); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + mwifiex_dbg(adapter, INFO, + "info: scan already aborted\n"); + } + } else if ((priv->scan_aborting && !priv->scan_request) || + priv->scan_block) { + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + + if (!adapter->active_scan_triggered) { + if (priv->scan_request) { + mwifiex_dbg(adapter, INFO, + "info: aborting scan\n"); + cfg80211_scan_done(priv->scan_request, 1); + priv->scan_request = NULL; + } else { + priv->scan_aborting = false; + mwifiex_dbg(adapter, INFO, + "info: scan already aborted\n"); + } + } + } else { + /* Get scan command from scan_pending_q and put to + * cmd_pending_q + */ + cmd_node = list_first_entry(&adapter->scan_pending_q, + struct cmd_ctrl_node, list); + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); + } + + return; +} + +/* + * 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) | + * .-------------------------------------------------------------. + */ +int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_scan_rsp *scan_rsp; + struct mwifiex_ie_types_data *tlv_data; + struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; + u8 *bss_info; + u32 scan_resp_size; + u32 bytes_left; + u32 idx; + u32 tlv_buf_size; + struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; + struct chan_band_param_set *chan_band; + u8 is_bgscan_resp; + __le64 fw_tsf = 0; + u8 *radio_type; + + is_bgscan_resp = (le16_to_cpu(resp->command) + == HostCmd_CMD_802_11_BG_SCAN_QUERY); + if (is_bgscan_resp) + scan_rsp = &resp->params.bg_scan_query_resp.scan_resp; + else + scan_rsp = &resp->params.scan_resp; + + + if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) { + mwifiex_dbg(adapter, ERROR, + "SCAN_RESP: too many AP returned (%d)\n", + scan_rsp->number_of_sets); + ret = -1; + goto check_next_scan; + } + + /* Check csa channel expiry before parsing scan response */ + mwifiex_11h_get_csa_closed_channel(priv); + + bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: bss_descript_size %d\n", + bytes_left); + + scan_resp_size = le16_to_cpu(resp->size); + + mwifiex_dbg(adapter, INFO, + "info: SCAN_RESP: returned %d APs before parsing\n", + scan_rsp->number_of_sets); + + bss_info = scan_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(scan_rsp->bss_descript_size) + + sizeof(scan_rsp->number_of_sets) + + S_DS_GEN); + + tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp-> + bss_desc_and_tlv_buffer + + bytes_left); + + /* Search the TLV buffer space in the scan response for any valid + TLVs */ + mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, + TLV_TYPE_TSFTIMESTAMP, + (struct mwifiex_ie_types_data **) + &tsf_tlv); + + /* Search the TLV buffer space in the scan response for any valid + TLVs */ + mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, + TLV_TYPE_CHANNELBANDLIST, + (struct mwifiex_ie_types_data **) + &chan_band_tlv); + + for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { + /* + * If the TSF TLV was appended to the scan results, save this + * entry's TSF value in the fw_tsf field. It is the firmware's + * TSF value at the time the beacon or probe response was + * received. + */ + if (tsf_tlv) + memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], + sizeof(fw_tsf)); + + if (chan_band_tlv) { + chan_band = &chan_band_tlv->chan_band_param[idx]; + radio_type = &chan_band->radio_type; + } else { + radio_type = NULL; + } + + ret = mwifiex_parse_single_response_buf(priv, &bss_info, + &bytes_left, + le64_to_cpu(fw_tsf), + radio_type, false, 0); + if (ret) + goto check_next_scan; + } + +check_next_scan: + mwifiex_check_next_scan_command(priv); + return ret; +} + +/* + * This function prepares an extended scan command to be sent to the firmware + * + * This uses the scan command configuration sent to the command processing + * module in command preparation stage to configure a extended scan command + * structure to send to firmware. + */ +int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan; + struct mwifiex_scan_cmd_config *scan_cfg = data_buf; + + memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); + + /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved) + + scan_cfg->tlv_buf_len + S_DS_GEN)); + + return 0; +} + +static void +mwifiex_update_chan_statistics(struct mwifiex_private *priv, + struct mwifiex_ietypes_chanstats *tlv_stat) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u8 i, num_chan; + struct mwifiex_fw_chan_stats *fw_chan_stats; + struct mwifiex_chan_stats chan_stats; + + fw_chan_stats = (void *)((u8 *)tlv_stat + + sizeof(struct mwifiex_ie_types_header)); + num_chan = le16_to_cpu(tlv_stat->header.len) / + sizeof(struct mwifiex_chan_stats); + + for (i = 0 ; i < num_chan; i++) { + chan_stats.chan_num = fw_chan_stats->chan_num; + chan_stats.bandcfg = fw_chan_stats->bandcfg; + chan_stats.flags = fw_chan_stats->flags; + chan_stats.noise = fw_chan_stats->noise; + chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss); + chan_stats.cca_scan_dur = + le16_to_cpu(fw_chan_stats->cca_scan_dur); + chan_stats.cca_busy_dur = + le16_to_cpu(fw_chan_stats->cca_busy_dur); + mwifiex_dbg(adapter, INFO, + "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n", + chan_stats.chan_num, + chan_stats.noise, + chan_stats.total_bss, + chan_stats.cca_scan_dur, + chan_stats.cca_busy_dur); + memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats, + sizeof(struct mwifiex_chan_stats)); + fw_chan_stats++; + } +} + +/* This function handles the command response of extended scan */ +int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_scan_ext *ext_scan_resp; + struct mwifiex_ie_types_header *tlv; + struct mwifiex_ietypes_chanstats *tlv_stat; + u16 buf_left, type, len; + + struct host_cmd_ds_command *cmd_ptr; + struct cmd_ctrl_node *cmd_node; + unsigned long cmd_flags, scan_flags; + bool complete_scan = false; + + mwifiex_dbg(adapter, INFO, "info: EXT scan returns successfully\n"); + + ext_scan_resp = &resp->params.ext_scan; + + tlv = (void *)ext_scan_resp->tlv_buffer; + buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN + - 1); + + while (buf_left >= sizeof(struct mwifiex_ie_types_header)) { + type = le16_to_cpu(tlv->type); + len = le16_to_cpu(tlv->len); + + if (buf_left < (sizeof(struct mwifiex_ie_types_header) + len)) { + mwifiex_dbg(adapter, ERROR, + "error processing scan response TLVs"); + break; + } + + switch (type) { + case TLV_TYPE_CHANNEL_STATS: + tlv_stat = (void *)tlv; + mwifiex_update_chan_statistics(priv, tlv_stat); + break; + default: + break; + } + + buf_left -= len + sizeof(struct mwifiex_ie_types_header); + tlv = (void *)((u8 *)tlv + len + + sizeof(struct mwifiex_ie_types_header)); + } + + spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_flags); + spin_lock_irqsave(&adapter->scan_pending_q_lock, scan_flags); + if (list_empty(&adapter->scan_pending_q)) { + complete_scan = true; + list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) { + cmd_ptr = (void *)cmd_node->cmd_skb->data; + if (le16_to_cpu(cmd_ptr->command) == + HostCmd_CMD_802_11_SCAN_EXT) { + mwifiex_dbg(adapter, INFO, + "Scan pending in command pending list"); + complete_scan = false; + break; + } + } + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_flags); + spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, cmd_flags); + + if (complete_scan) + mwifiex_complete_scan(priv); + + return 0; +} + +/* This function This function handles the event extended scan report. It + * parses extended scan results and informs to cfg80211 stack. + */ +int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, + void *buf) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + u8 *bss_info; + u32 bytes_left, bytes_left_for_tlv, idx; + u16 type, len; + struct mwifiex_ie_types_data *tlv; + struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv; + struct mwifiex_ie_types_bss_scan_info *scan_info_tlv; + u8 *radio_type; + u64 fw_tsf = 0; + s32 rssi = 0; + struct mwifiex_event_scan_result *event_scan = buf; + u8 num_of_set = event_scan->num_of_set; + u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result); + u16 scan_resp_size = le16_to_cpu(event_scan->buf_size); + + if (num_of_set > MWIFIEX_MAX_AP) { + mwifiex_dbg(adapter, ERROR, + "EXT_SCAN: Invalid number of AP returned (%d)!!\n", + num_of_set); + ret = -1; + goto check_next_scan; + } + + bytes_left = scan_resp_size; + mwifiex_dbg(adapter, INFO, + "EXT_SCAN: size %d, returned %d APs...", + scan_resp_size, num_of_set); + mwifiex_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf, + scan_resp_size + + sizeof(struct mwifiex_event_scan_result)); + + tlv = (struct mwifiex_ie_types_data *)scan_resp; + + for (idx = 0; idx < num_of_set && bytes_left; idx++) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) { + mwifiex_dbg(adapter, ERROR, + "EXT_SCAN: Error bytes left < TLV length\n"); + break; + } + scan_rsp_tlv = NULL; + scan_info_tlv = NULL; + bytes_left_for_tlv = bytes_left; + + /* BSS response TLV with beacon or probe response buffer + * at the initial position of each descriptor + */ + if (type != TLV_TYPE_BSS_SCAN_RSP) + break; + + bss_info = (u8 *)tlv; + scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv; + tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); + bytes_left_for_tlv -= + (len + sizeof(struct mwifiex_ie_types_header)); + + while (bytes_left_for_tlv >= + sizeof(struct mwifiex_ie_types_header) && + le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) { + type = le16_to_cpu(tlv->header.type); + len = le16_to_cpu(tlv->header.len); + if (bytes_left_for_tlv < + sizeof(struct mwifiex_ie_types_header) + len) { + mwifiex_dbg(adapter, ERROR, + "EXT_SCAN: Error in processing TLV,\t" + "bytes left < TLV length\n"); + scan_rsp_tlv = NULL; + bytes_left_for_tlv = 0; + continue; + } + switch (type) { + case TLV_TYPE_BSS_SCAN_INFO: + scan_info_tlv = + (struct mwifiex_ie_types_bss_scan_info *)tlv; + if (len != + sizeof(struct mwifiex_ie_types_bss_scan_info) - + sizeof(struct mwifiex_ie_types_header)) { + bytes_left_for_tlv = 0; + continue; + } + break; + default: + break; + } + tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); + bytes_left -= + (len + sizeof(struct mwifiex_ie_types_header)); + bytes_left_for_tlv -= + (len + sizeof(struct mwifiex_ie_types_header)); + } + + if (!scan_rsp_tlv) + 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 + */ + bss_info += sizeof(u16); + bytes_left -= sizeof(u16); + + if (scan_info_tlv) { + rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi)); + rssi *= 100; /* Convert dBm to mBm */ + mwifiex_dbg(adapter, INFO, + "info: InterpretIE: RSSI=%d\n", rssi); + fw_tsf = le64_to_cpu(scan_info_tlv->tsf); + radio_type = &scan_info_tlv->radio_type; + } else { + radio_type = NULL; + } + ret = mwifiex_parse_single_response_buf(priv, &bss_info, + &bytes_left, fw_tsf, + radio_type, true, rssi); + if (ret) + goto check_next_scan; + } + +check_next_scan: + if (!event_scan->more_event) + mwifiex_check_next_scan_command(priv); + + return ret; +} + +/* + * This function prepares command for background scan query. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting background scan flush parameter + * - Ensuring correct endian-ness + */ +int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd) +{ + struct host_cmd_ds_802_11_bg_scan_query *bg_query = + &cmd->params.bg_scan_query; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) + + S_DS_GEN); + + bg_query->flush = 1; + + return 0; +} + +/* + * This function inserts scan command node to the scan pending queue. + */ +void +mwifiex_queue_scan_cmd(struct mwifiex_private *priv, + struct cmd_ctrl_node *cmd_node) +{ + struct mwifiex_adapter *adapter = priv->adapter; + unsigned long flags; + + cmd_node->wait_q_enabled = true; + cmd_node->condition = &adapter->scan_wait_q_woken; + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_add_tail(&cmd_node->list, &adapter->scan_pending_q); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); +} + +/* + * This function sends a scan command for all available channels to the + * firmware, filtered on a specific SSID. + */ +static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv, + struct cfg80211_ssid *req_ssid) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + struct mwifiex_user_scan_cfg *scan_cfg; + + if (adapter->scan_processing) { + mwifiex_dbg(adapter, WARN, + "cmd: Scan already in process...\n"); + return -EBUSY; + } + + if (priv->scan_block) { + mwifiex_dbg(adapter, WARN, + "cmd: Scan is blocked during association...\n"); + return -EBUSY; + } + + scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); + if (!scan_cfg) + return -ENOMEM; + + scan_cfg->ssid_list = req_ssid; + scan_cfg->num_ssids = 1; + + ret = mwifiex_scan_networks(priv, scan_cfg); + + kfree(scan_cfg); + return ret; +} + +/* + * Sends IOCTL request to start a scan. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + * + * Scan command can be issued for both normal scan and specific SSID + * scan, depending upon whether an SSID is provided or not. + */ +int mwifiex_request_scan(struct mwifiex_private *priv, + struct cfg80211_ssid *req_ssid) +{ + int ret; + + if (down_interruptible(&priv->async_sem)) { + mwifiex_dbg(priv->adapter, ERROR, + "%s: acquire semaphore fail\n", + __func__); + return -1; + } + + priv->adapter->scan_wait_q_woken = false; + + if (req_ssid && req_ssid->ssid_len != 0) + /* Specific SSID scan */ + ret = mwifiex_scan_specific_ssid(priv, req_ssid); + else + /* Normal scan */ + ret = mwifiex_scan_networks(priv, NULL); + + up(&priv->async_sem); + + return ret; +} + +/* + * This function appends the vendor specific IE TLV to a buffer. + */ +int +mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, + u16 vsie_mask, u8 **buffer) +{ + int id, ret_len = 0; + struct mwifiex_ie_types_vendor_param_set *vs_param_set; + + if (!buffer) + return 0; + if (!(*buffer)) + return 0; + + /* + * Traverse through the saved vendor specific IE array and append + * the selected(scan/assoc/adhoc) IE as TLV to the command + */ + for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) { + if (priv->vs_ie[id].mask & vsie_mask) { + vs_param_set = + (struct mwifiex_ie_types_vendor_param_set *) + *buffer; + vs_param_set->header.type = + cpu_to_le16(TLV_TYPE_PASSTHROUGH); + vs_param_set->header.len = + cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) + & 0x00FF) + 2); + memcpy(vs_param_set->ie, priv->vs_ie[id].ie, + le16_to_cpu(vs_param_set->header.len)); + *buffer += le16_to_cpu(vs_param_set->header.len) + + sizeof(struct mwifiex_ie_types_header); + ret_len += le16_to_cpu(vs_param_set->header.len) + + sizeof(struct mwifiex_ie_types_header); + } + } + return ret_len; +} + +/* + * This function saves a beacon buffer of the current BSS descriptor. + * + * The current beacon buffer is saved so that it can be restored in the + * following cases that makes the beacon buffer 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. + */ +void +mwifiex_save_curr_bcn(struct mwifiex_private *priv) +{ + struct mwifiex_bssdescriptor *curr_bss = + &priv->curr_bss_params.bss_descriptor; + + if (!curr_bss->beacon_buf_size) + return; + + /* allocate beacon buffer at 1st time; or if it's size has changed */ + if (!priv->curr_bcn_buf || + priv->curr_bcn_size != curr_bss->beacon_buf_size) { + priv->curr_bcn_size = curr_bss->beacon_buf_size; + + kfree(priv->curr_bcn_buf); + priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size, + GFP_ATOMIC); + if (!priv->curr_bcn_buf) + return; + } + + memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf, + curr_bss->beacon_buf_size); + mwifiex_dbg(priv->adapter, INFO, + "info: current beacon saved %d\n", + priv->curr_bcn_size); + + curr_bss->beacon_buf = priv->curr_bcn_buf; + + /* adjust the pointers in the current BSS descriptor */ + if (curr_bss->bcn_wpa_ie) + curr_bss->bcn_wpa_ie = + (struct ieee_types_vendor_specific *) + (curr_bss->beacon_buf + + curr_bss->wpa_offset); + + if (curr_bss->bcn_rsn_ie) + curr_bss->bcn_rsn_ie = (struct ieee_types_generic *) + (curr_bss->beacon_buf + + curr_bss->rsn_offset); + + if (curr_bss->bcn_ht_cap) + curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *) + (curr_bss->beacon_buf + + curr_bss->ht_cap_offset); + + if (curr_bss->bcn_ht_oper) + curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *) + (curr_bss->beacon_buf + + curr_bss->ht_info_offset); + + if (curr_bss->bcn_vht_cap) + curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf + + curr_bss->vht_cap_offset); + + if (curr_bss->bcn_vht_oper) + curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf + + curr_bss->vht_info_offset); + + if (curr_bss->bcn_bss_co_2040) + curr_bss->bcn_bss_co_2040 = + (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset); + + if (curr_bss->bcn_ext_cap) + curr_bss->bcn_ext_cap = curr_bss->beacon_buf + + curr_bss->ext_cap_offset; + + if (curr_bss->oper_mode) + curr_bss->oper_mode = (void *)(curr_bss->beacon_buf + + curr_bss->oper_mode_offset); +} + +/* + * This function frees the current BSS descriptor beacon buffer. + */ +void +mwifiex_free_curr_bcn(struct mwifiex_private *priv) +{ + kfree(priv->curr_bcn_buf); + priv->curr_bcn_buf = NULL; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c new file mode 100644 index 000000000000..78a8474e1a3d --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -0,0 +1,2684 @@ +/* + * Marvell Wireless LAN device driver: SDIO specific handling + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "sdio.h" + + +#define SDIO_VERSION "1.0" + +/* The mwifiex_sdio_remove() callback function is called when + * user removes this module from kernel space or ejects + * the card from the slot. The driver handles these 2 cases + * differently. + * If the user is removing the module, the few commands (FUNC_SHUTDOWN, + * HS_CANCEL etc.) are sent to the firmware. + * If the card is removed, there is no need to send these command. + * + * The variable 'user_rmmod' is used to distinguish these two + * scenarios. This flag is initialized as FALSE in case the card + * is removed, and will be set to TRUE for module removal when + * module_exit function is called. + */ +static u8 user_rmmod; + +static struct mwifiex_if_ops sdio_ops; +static unsigned long iface_work_flags; + +static struct semaphore add_remove_card_sem; + +static struct memory_type_mapping generic_mem_type_map[] = { + {"DUMP", NULL, 0, 0xDD}, +}; + +static struct memory_type_mapping mem_type_mapping_tbl[] = { + {"ITCM", NULL, 0, 0xF0}, + {"DTCM", NULL, 0, 0xF1}, + {"SQRAM", NULL, 0, 0xF2}, + {"APU", NULL, 0, 0xF3}, + {"CIU", NULL, 0, 0xF4}, + {"ICU", NULL, 0, 0xF5}, + {"MAC", NULL, 0, 0xF6}, + {"EXT7", NULL, 0, 0xF7}, + {"EXT8", NULL, 0, 0xF8}, + {"EXT9", NULL, 0, 0xF9}, + {"EXT10", NULL, 0, 0xFA}, + {"EXT11", NULL, 0, 0xFB}, + {"EXT12", NULL, 0, 0xFC}, + {"EXT13", NULL, 0, 0xFD}, + {"EXTLAST", NULL, 0, 0xFE}, +}; + +/* + * SDIO probe. + * + * This function probes an mwifiex device and registers it. It allocates + * the card structure, enables SDIO function number and initiates the + * device registration and initialization procedure by adding a logical + * interface. + */ +static int +mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret; + struct sdio_mmc_card *card = NULL; + + pr_debug("info: 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) + return -ENOMEM; + + card->func = func; + card->device_id = id; + + func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; + + if (id->driver_data) { + struct mwifiex_sdio_device *data = (void *)id->driver_data; + + card->firmware = data->firmware; + card->reg = data->reg; + card->max_ports = data->max_ports; + card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; + card->supports_sdio_new_mode = data->supports_sdio_new_mode; + card->has_control_mask = data->has_control_mask; + card->tx_buf_size = data->tx_buf_size; + card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size; + card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; + card->can_dump_fw = data->can_dump_fw; + card->fw_dump_enh = data->fw_dump_enh; + card->can_auto_tdls = data->can_auto_tdls; + card->can_ext_scan = data->can_ext_scan; + } + + sdio_claim_host(func); + ret = sdio_enable_func(func); + sdio_release_host(func); + + if (ret) { + pr_err("%s: failed to enable function\n", __func__); + kfree(card); + return -EIO; + } + + if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops, + MWIFIEX_SDIO)) { + pr_err("%s: add card failed\n", __func__); + kfree(card); + sdio_claim_host(func); + ret = sdio_disable_func(func); + sdio_release_host(func); + ret = -1; + } + + return ret; +} + +/* + * SDIO resume. + * + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not resumed, this function turns on the traffic and + * sends a host sleep cancel request to the firmware. + */ +static int mwifiex_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + struct mwifiex_adapter *adapter; + mmc_pm_flag_t pm_flag = 0; + + if (func) { + pm_flag = sdio_get_host_pm_caps(func); + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + pr_err("resume: invalid card or adapter\n"); + return 0; + } + } else { + pr_err("resume: sdio_func is not specified\n"); + return 0; + } + + adapter = card->adapter; + + if (!adapter->is_suspended) { + mwifiex_dbg(adapter, WARN, + "device already resumed\n"); + return 0; + } + + adapter->is_suspended = false; + + /* Disable Host Sleep */ + mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), + MWIFIEX_ASYNC_CMD); + + return 0; +} + +/* + * SDIO remove. + * + * This function removes the interface and frees up the card structure. + */ +static void +mwifiex_sdio_remove(struct sdio_func *func) +{ + struct sdio_mmc_card *card; + struct mwifiex_adapter *adapter; + struct mwifiex_private *priv; + + card = sdio_get_drvdata(func); + if (!card) + return; + + adapter = card->adapter; + if (!adapter || !adapter->priv_num) + return; + + mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); + + if (user_rmmod) { + if (adapter->is_suspended) + mwifiex_sdio_resume(adapter->dev); + + mwifiex_deauthenticate_all(adapter); + + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + mwifiex_disable_auto_ds(priv); + mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); + } + + mwifiex_remove_card(card->adapter, &add_remove_card_sem); +} + +/* + * SDIO suspend. + * + * Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not suspended, this function allocates and sends a host + * sleep activate request to the firmware and turns off the traffic. + */ +static int mwifiex_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct sdio_mmc_card *card; + struct mwifiex_adapter *adapter; + mmc_pm_flag_t pm_flag = 0; + int ret = 0; + + if (func) { + pm_flag = sdio_get_host_pm_caps(func); + pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", + sdio_func_id(func), pm_flag); + if (!(pm_flag & MMC_PM_KEEP_POWER)) { + pr_err("%s: cannot remain alive while host is" + " suspended\n", sdio_func_id(func)); + return -ENOSYS; + } + + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + pr_err("suspend: invalid card or adapter\n"); + return 0; + } + } else { + pr_err("suspend: sdio_func is not specified\n"); + return 0; + } + + adapter = card->adapter; + + /* Enable the Host Sleep */ + if (!mwifiex_enable_hs(adapter)) { + mwifiex_dbg(adapter, ERROR, + "cmd: failed to suspend\n"); + adapter->hs_enabling = false; + return -EFAULT; + } + + mwifiex_dbg(adapter, INFO, + "cmd: suspend with MMC_PM_KEEP_POWER\n"); + ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); + + /* Indicate device suspended */ + adapter->is_suspended = true; + adapter->hs_enabling = false; + + return ret; +} + +/* Device ID for SD8786 */ +#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116) +/* Device ID for SD8787 */ +#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) +/* Device ID for SD8797 */ +#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129) +/* Device ID for SD8897 */ +#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d) +/* Device ID for SD8887 */ +#define SDIO_DEVICE_ID_MARVELL_8887 (0x9135) +/* Device ID for SD8801 */ +#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139) +/* Device ID for SD8997 */ +#define SDIO_DEVICE_ID_MARVELL_8997 (0x9141) + + +/* WLAN IDs */ +static const struct sdio_device_id mwifiex_ids[] = { + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786), + .driver_data = (unsigned long) &mwifiex_sdio_sd8786}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787), + .driver_data = (unsigned long) &mwifiex_sdio_sd8787}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797), + .driver_data = (unsigned long) &mwifiex_sdio_sd8797}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897), + .driver_data = (unsigned long) &mwifiex_sdio_sd8897}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887), + .driver_data = (unsigned long)&mwifiex_sdio_sd8887}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801), + .driver_data = (unsigned long)&mwifiex_sdio_sd8801}, + {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997), + .driver_data = (unsigned long)&mwifiex_sdio_sd8997}, + {}, +}; + +MODULE_DEVICE_TABLE(sdio, mwifiex_ids); + +static const struct dev_pm_ops mwifiex_sdio_pm_ops = { + .suspend = mwifiex_sdio_suspend, + .resume = mwifiex_sdio_resume, +}; + +static struct sdio_driver mwifiex_sdio = { + .name = "mwifiex_sdio", + .id_table = mwifiex_ids, + .probe = mwifiex_sdio_probe, + .remove = mwifiex_sdio_remove, + .drv = { + .owner = THIS_MODULE, + .pm = &mwifiex_sdio_pm_ops, + } +}; + +/* Write data into SDIO card register. Caller claims SDIO device. */ +static int +mwifiex_write_reg_locked(struct sdio_func *func, u32 reg, u8 data) +{ + int ret = -1; + sdio_writeb(func, data, reg, &ret); + return ret; +} + +/* + * This function writes data into SDIO card register. + */ +static int +mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + + sdio_claim_host(card->func); + ret = mwifiex_write_reg_locked(card->func, reg, data); + sdio_release_host(card->func); + + return ret; +} + +/* + * This function reads data from SDIO card register. + */ +static int +mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = -1; + u8 val; + + sdio_claim_host(card->func); + val = sdio_readb(card->func, reg, &ret); + sdio_release_host(card->func); + + *data = val; + + return ret; +} + +/* + * This function writes multiple data into SDIO card memory. + * + * This does not work in suspended mode. + */ +static int +mwifiex_write_data_sync(struct mwifiex_adapter *adapter, + u8 *buffer, u32 pkt_len, u32 port) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u8 blk_mode = + (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; + u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; + u32 blk_cnt = + (blk_mode == + BLOCK_MODE) ? (pkt_len / + MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len; + u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); + + if (adapter->is_suspended) { + mwifiex_dbg(adapter, ERROR, + "%s: not allowed while suspended\n", __func__); + return -1; + } + + sdio_claim_host(card->func); + + ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size); + + sdio_release_host(card->func); + + return ret; +} + +/* + * This function reads multiple data from SDIO card memory. + */ +static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, + u32 len, u32 port, u8 claim) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE + : BLOCK_MODE; + u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; + u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE) + : len; + u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); + + if (claim) + sdio_claim_host(card->func); + + ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size); + + if (claim) + sdio_release_host(card->func); + + return ret; +} + +/* + * This function wakes up the card. + * + * A host power up command is written to the card configuration + * register to wake up the card. + */ +static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) +{ + mwifiex_dbg(adapter, EVENT, + "event: wakeup device...\n"); + + return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); +} + +/* + * This function is called after the card has woken up. + * + * The card configuration register is reset. + */ +static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) +{ + mwifiex_dbg(adapter, EVENT, + "cmd: wakeup device completed\n"); + + return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0); +} + +/* + * This function is used to initialize IO ports for the + * chipsets supporting SDIO new mode eg SD8897. + */ +static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter) +{ + u8 reg; + struct sdio_mmc_card *card = adapter->card; + + adapter->ioport = MEM_PORT; + + /* enable sdio new mode */ + if (mwifiex_read_reg(adapter, card->reg->card_cfg_2_1_reg, ®)) + return -1; + if (mwifiex_write_reg(adapter, card->reg->card_cfg_2_1_reg, + reg | CMD53_NEW_MODE)) + return -1; + + /* Configure cmd port and enable reading rx length from the register */ + if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_0, ®)) + return -1; + if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_0, + reg | CMD_PORT_RD_LEN_EN)) + return -1; + + /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is + * completed + */ + if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_1, ®)) + return -1; + if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_1, + reg | CMD_PORT_AUTO_EN)) + return -1; + + return 0; +} + +/* This function initializes the IO ports. + * + * The following operations are performed - + * - Read the IO ports (0, 1 and 2) + * - Set host interrupt Reset-To-Read to clear + * - Set auto re-enable interrupt + */ +static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) +{ + u8 reg; + struct sdio_mmc_card *card = adapter->card; + + adapter->ioport = 0; + + if (card->supports_sdio_new_mode) { + if (mwifiex_init_sdio_new_mode(adapter)) + return -1; + goto cont; + } + + /* Read the IO port */ + if (!mwifiex_read_reg(adapter, card->reg->io_port_0_reg, ®)) + adapter->ioport |= (reg & 0xff); + else + return -1; + + if (!mwifiex_read_reg(adapter, card->reg->io_port_1_reg, ®)) + adapter->ioport |= ((reg & 0xff) << 8); + else + return -1; + + if (!mwifiex_read_reg(adapter, card->reg->io_port_2_reg, ®)) + adapter->ioport |= ((reg & 0xff) << 16); + else + return -1; +cont: + mwifiex_dbg(adapter, INFO, + "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); + + /* Set Host interrupt reset to read to clear */ + if (!mwifiex_read_reg(adapter, card->reg->host_int_rsr_reg, ®)) + mwifiex_write_reg(adapter, card->reg->host_int_rsr_reg, + reg | card->reg->sdio_int_mask); + else + return -1; + + /* Dnld/Upld ready set to auto reset */ + if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, ®)) + mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg, + reg | AUTO_RE_ENABLE_INT); + else + return -1; + + return 0; +} + +/* + * This function sends data to the card. + */ +static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, + u8 *payload, u32 pkt_len, u32 port) +{ + u32 i = 0; + int ret; + + do { + ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port); + if (ret) { + i++; + mwifiex_dbg(adapter, ERROR, + "host_to_card, write iomem\t" + "(%d) failed: %d\n", i, ret); + if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) + mwifiex_dbg(adapter, ERROR, + "write CFG reg failed\n"); + + ret = -1; + if (i > MAX_WRITE_IOMEM_RETRY) + return ret; + } + } while (ret == -1); + + return ret; +} + +/* + * This function gets the read port. + * + * If control port bit is set in MP read bitmap, the control port + * is returned, otherwise the current read port is returned and + * the value is increased (provided it does not reach the maximum + * limit, in which case it is reset to 1) + */ +static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + u32 rd_bitmap = card->mp_rd_bitmap; + + mwifiex_dbg(adapter, DATA, + "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); + + if (card->supports_sdio_new_mode) { + if (!(rd_bitmap & reg->data_port_mask)) + return -1; + } else { + if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) + return -1; + } + + if ((card->has_control_mask) && + (card->mp_rd_bitmap & CTRL_PORT_MASK)) { + card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK); + *port = CTRL_PORT; + mwifiex_dbg(adapter, DATA, + "data: port=%d mp_rd_bitmap=0x%08x\n", + *port, card->mp_rd_bitmap); + return 0; + } + + if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port))) + return -1; + + /* We are now handling the SDIO data ports */ + card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port)); + *port = card->curr_rd_port; + + if (++card->curr_rd_port == card->max_ports) + card->curr_rd_port = reg->start_rd_port; + + mwifiex_dbg(adapter, DATA, + "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", + *port, rd_bitmap, card->mp_rd_bitmap); + + return 0; +} + +/* + * This function gets the write port for data. + * + * The current write port is returned if available and the value is + * increased (provided it does not reach the maximum limit, in which + * case it is reset to 1) + */ +static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + u32 wr_bitmap = card->mp_wr_bitmap; + + mwifiex_dbg(adapter, DATA, + "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); + + if (!(wr_bitmap & card->mp_data_port_mask)) { + adapter->data_sent = true; + return -EBUSY; + } + + if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { + card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); + *port = card->curr_wr_port; + if (++card->curr_wr_port == card->mp_end_port) + card->curr_wr_port = reg->start_wr_port; + } else { + adapter->data_sent = true; + return -EBUSY; + } + + if ((card->has_control_mask) && (*port == CTRL_PORT)) { + mwifiex_dbg(adapter, ERROR, + "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", + *port, card->curr_wr_port, wr_bitmap, + card->mp_wr_bitmap); + return -1; + } + + mwifiex_dbg(adapter, DATA, + "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", + *port, wr_bitmap, card->mp_wr_bitmap); + + return 0; +} + +/* + * This function polls the card status. + */ +static int +mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) +{ + struct sdio_mmc_card *card = adapter->card; + u32 tries; + u8 cs; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs)) + break; + else if ((cs & bits) == bits) + return 0; + + usleep_range(10, 20); + } + + mwifiex_dbg(adapter, ERROR, + "poll card status failed, tries = %d\n", tries); + + return -1; +} + +/* + * This function reads the firmware status. + */ +static int +mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + u8 fws0, fws1; + + if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0)) + return -1; + + if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1)) + return -1; + + *dat = (u16) ((fws1 << 8) | fws0); + + return 0; +} + +/* + * This function disables the host interrupt. + * + * The host interrupt mask is read, the disable bit is reset and + * written back to the card host interrupt mask register. + */ +static void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + + sdio_claim_host(func); + mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 0); + sdio_release_irq(func); + sdio_release_host(func); +} + +/* + * This function reads the interrupt status from card. + */ +static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + u8 sdio_ireg; + unsigned long flags; + + if (mwifiex_read_data_sync(adapter, card->mp_regs, + card->reg->max_mp_regs, + REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { + mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n"); + return; + } + + sdio_ireg = card->mp_regs[card->reg->host_int_status_reg]; + if (sdio_ireg) { + /* + * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS + * For SDIO new mode CMD port interrupts + * DN_LD_CMD_PORT_HOST_INT_STATUS and/or + * UP_LD_CMD_PORT_HOST_INT_STATUS + * Clear the interrupt status register + */ + mwifiex_dbg(adapter, INTR, + "int: sdio_ireg = %#x\n", sdio_ireg); + spin_lock_irqsave(&adapter->int_lock, flags); + adapter->int_status |= sdio_ireg; + spin_unlock_irqrestore(&adapter->int_lock, flags); + } +} + +/* + * SDIO interrupt handler. + * + * This function reads the interrupt status from firmware and handles + * the interrupt in current thread (ksdioirqd) right away. + */ +static void +mwifiex_sdio_interrupt(struct sdio_func *func) +{ + struct mwifiex_adapter *adapter; + struct sdio_mmc_card *card; + + card = sdio_get_drvdata(func); + if (!card || !card->adapter) { + pr_debug("int: func=%p card=%p adapter=%p\n", + func, card, card ? card->adapter : NULL); + return; + } + adapter = card->adapter; + + if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) + adapter->ps_state = PS_STATE_AWAKE; + + mwifiex_interrupt_status(adapter); + mwifiex_main_process(adapter); +} + +/* + * This function enables the host interrupt. + * + * The host interrupt enable mask is written to the card + * host interrupt mask register. + */ +static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + int ret; + + sdio_claim_host(func); + + /* Request the SDIO IRQ */ + ret = sdio_claim_irq(func, mwifiex_sdio_interrupt); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "claim irq failed: ret=%d\n", ret); + goto out; + } + + /* Simply write the mask to the register */ + ret = mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, + card->reg->host_int_enable); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "enable host interrupt failed\n"); + sdio_release_irq(func); + } + +out: + sdio_release_host(func); + return ret; +} + +/* + * This function sends a data buffer to the card. + */ +static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, + u32 *type, u8 *buffer, + u32 npayload, u32 ioport) +{ + int ret; + u32 nb; + + if (!buffer) { + mwifiex_dbg(adapter, ERROR, + "%s: buffer is NULL\n", __func__); + return -1; + } + + ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1); + + if (ret) { + mwifiex_dbg(adapter, ERROR, + "%s: read iomem failed: %d\n", __func__, + ret); + return -1; + } + + nb = le16_to_cpu(*(__le16 *) (buffer)); + if (nb > npayload) { + mwifiex_dbg(adapter, ERROR, + "%s: invalid packet, nb=%d npayload=%d\n", + __func__, nb, npayload); + return -1; + } + + *type = le16_to_cpu(*(__le16 *) (buffer + 2)); + + return ret; +} + +/* + * This function downloads the firmware to the card. + * + * Firmware is downloaded to the card in blocks. Every block download + * is tested for CRC errors, and retried a number of times before + * returning failure. + */ +static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *fw) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + int ret; + u8 *firmware = fw->fw_buf; + u32 firmware_len = fw->fw_len; + u32 offset = 0; + u8 base0, base1; + u8 *fwbuf; + u16 len = 0; + u32 txlen, tx_blocks = 0, tries; + u32 i = 0; + + if (!firmware_len) { + mwifiex_dbg(adapter, ERROR, + "firmware image not found! Terminating download\n"); + return -1; + } + + mwifiex_dbg(adapter, INFO, + "info: downloading FW image (%d bytes)\n", + firmware_len); + + /* Assume that the allocated buffer is 8-byte aligned */ + fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL); + if (!fwbuf) + return -ENOMEM; + + sdio_claim_host(card->func); + + /* Perform firmware data transfer */ + do { + /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY + bits */ + ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY | + DN_LD_CARD_RDY); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "FW download with helper:\t" + "poll status timeout @ %d\n", offset); + goto done; + } + + /* More data? */ + if (offset >= firmware_len) + break; + + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ret = mwifiex_read_reg(adapter, reg->base_0_reg, + &base0); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "dev BASE0 register read failed:\t" + "base0=%#04X(%d). Terminating dnld\n", + base0, base0); + goto done; + } + ret = mwifiex_read_reg(adapter, reg->base_1_reg, + &base1); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "dev BASE1 register read failed:\t" + "base1=%#04X(%d). Terminating dnld\n", + base1, base1); + goto done; + } + len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); + + if (len) + break; + + usleep_range(10, 20); + } + + if (!len) { + break; + } else if (len > MWIFIEX_UPLD_SIZE) { + mwifiex_dbg(adapter, ERROR, + "FW dnld failed @ %d, invalid length %d\n", + offset, len); + ret = -1; + goto done; + } + + txlen = len; + + if (len & BIT(0)) { + i++; + if (i > MAX_WRITE_IOMEM_RETRY) { + mwifiex_dbg(adapter, ERROR, + "FW dnld failed @ %d, over max retry\n", + offset); + ret = -1; + goto done; + } + mwifiex_dbg(adapter, ERROR, + "CRC indicated by the helper:\t" + "len = 0x%04X, txlen = %d\n", len, txlen); + len &= ~BIT(0); + /* Setting this to 0 to resend from same offset */ + txlen = 0; + } else { + i = 0; + + /* Set blocksize to transfer - checking for last + block */ + if (firmware_len - offset < txlen) + txlen = firmware_len - offset; + + tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - 1) + / MWIFIEX_SDIO_BLOCK_SIZE; + + /* Copy payload to buffer */ + memmove(fwbuf, &firmware[offset], txlen); + } + + ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks * + MWIFIEX_SDIO_BLOCK_SIZE, + adapter->ioport); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "FW download, write iomem (%d) failed @ %d\n", + i, offset); + if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) + mwifiex_dbg(adapter, ERROR, + "write CFG reg failed\n"); + + ret = -1; + goto done; + } + + offset += txlen; + } while (true); + + sdio_release_host(card->func); + + mwifiex_dbg(adapter, MSG, + "info: FW download over, size %d bytes\n", offset); + + ret = 0; +done: + kfree(fwbuf); + return ret; +} + +/* + * This function checks the firmware status in card. + * + * The winner interface is also determined by this function. + */ +static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, + u32 poll_num) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = 0; + u16 firmware_stat; + u32 tries; + u8 winner_status; + + /* Wait for firmware initialization event */ + for (tries = 0; tries < poll_num; tries++) { + ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); + if (ret) + continue; + if (firmware_stat == FIRMWARE_READY_SDIO) { + ret = 0; + break; + } else { + msleep(100); + ret = -1; + } + } + + if (ret) { + if (mwifiex_read_reg + (adapter, card->reg->status_reg_0, &winner_status)) + winner_status = 0; + + if (winner_status) + adapter->winner = 0; + else + adapter->winner = 1; + } + return ret; +} + +/* + * This function decode sdio aggreation pkt. + * + * Based on the the data block size and pkt_len, + * skb data will be decoded to few packets. + */ +static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + u32 total_pkt_len, pkt_len; + struct sk_buff *skb_deaggr; + u32 pkt_type; + u16 blk_size; + u8 blk_num; + u8 *data; + + data = skb->data; + total_pkt_len = skb->len; + + while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) { + if (total_pkt_len < adapter->sdio_rx_block_size) + break; + blk_num = *(data + BLOCK_NUMBER_OFFSET); + blk_size = adapter->sdio_rx_block_size * blk_num; + if (blk_size > total_pkt_len) { + mwifiex_dbg(adapter, ERROR, + "%s: error in blk_size,\t" + "blk_num=%d, blk_size=%d, total_pkt_len=%d\n", + __func__, blk_num, blk_size, total_pkt_len); + break; + } + pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET)); + pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET + + 2)); + if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { + mwifiex_dbg(adapter, ERROR, + "%s: error in pkt_len,\t" + "pkt_len=%d, blk_size=%d\n", + __func__, pkt_len, blk_size); + break; + } + skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len, + GFP_KERNEL | GFP_DMA); + if (!skb_deaggr) + break; + skb_put(skb_deaggr, pkt_len); + memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len); + skb_pull(skb_deaggr, INTF_HEADER_LEN); + + mwifiex_handle_rx_packet(adapter, skb_deaggr); + data += blk_size; + total_pkt_len -= blk_size; + } +} + +/* + * This function decodes a received packet. + * + * Based on the type, the packet is treated as either a data, or + * a command response, or an event, and the correct handler + * function is invoked. + */ +static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, + struct sk_buff *skb, u32 upld_typ) +{ + u8 *cmd_buf; + __le16 *curr_ptr = (__le16 *)skb->data; + u16 pkt_len = le16_to_cpu(*curr_ptr); + struct mwifiex_rxinfo *rx_info; + + if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) { + skb_trim(skb, pkt_len); + skb_pull(skb, INTF_HEADER_LEN); + } + + switch (upld_typ) { + case MWIFIEX_TYPE_AGGR_DATA: + mwifiex_dbg(adapter, INFO, + "info: --- Rx: Aggr Data packet ---\n"); + rx_info = MWIFIEX_SKB_RXCB(skb); + rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA; + if (adapter->rx_work_enabled) { + skb_queue_tail(&adapter->rx_data_q, skb); + atomic_inc(&adapter->rx_pending); + adapter->data_received = true; + } else { + mwifiex_deaggr_sdio_pkt(adapter, skb); + dev_kfree_skb_any(skb); + } + break; + + case MWIFIEX_TYPE_DATA: + mwifiex_dbg(adapter, DATA, + "info: --- Rx: Data packet ---\n"); + if (adapter->rx_work_enabled) { + skb_queue_tail(&adapter->rx_data_q, skb); + adapter->data_received = true; + atomic_inc(&adapter->rx_pending); + } else { + mwifiex_handle_rx_packet(adapter, skb); + } + break; + + case MWIFIEX_TYPE_CMD: + mwifiex_dbg(adapter, CMD, + "info: --- Rx: Cmd Response ---\n"); + /* take care of curr_cmd = NULL case */ + if (!adapter->curr_cmd) { + cmd_buf = adapter->upld_buf; + + if (adapter->ps_state == PS_STATE_SLEEP_CFM) + mwifiex_process_sleep_confirm_resp(adapter, + skb->data, + skb->len); + + memcpy(cmd_buf, skb->data, + min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, + skb->len)); + + dev_kfree_skb_any(skb); + } else { + adapter->cmd_resp_received = true; + adapter->curr_cmd->resp_skb = skb; + } + break; + + case MWIFIEX_TYPE_EVENT: + mwifiex_dbg(adapter, EVENT, + "info: --- Rx: Event ---\n"); + adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data); + + if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) + memcpy(adapter->event_body, + skb->data + MWIFIEX_EVENT_HEADER_LEN, + skb->len); + + /* event cause has been saved to adapter->event_cause */ + adapter->event_received = true; + adapter->event_skb = skb; + + break; + + default: + mwifiex_dbg(adapter, ERROR, + "unknown upload type %#x\n", upld_typ); + dev_kfree_skb_any(skb); + break; + } + + return 0; +} + +/* + * This function transfers received packets from card to driver, performing + * aggregation if required. + * + * For data received on control port, or if aggregation is disabled, the + * received buffers are uploaded as separate packets. However, if aggregation + * is enabled and required, the buffers are copied onto an aggregation buffer, + * provided there is space left, processed and finally uploaded. + */ +static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, + u16 rx_len, u8 port) +{ + struct sdio_mmc_card *card = adapter->card; + s32 f_do_rx_aggr = 0; + s32 f_do_rx_cur = 0; + s32 f_aggr_cur = 0; + s32 f_post_aggr_cur = 0; + struct sk_buff *skb_deaggr; + struct sk_buff *skb = NULL; + u32 pkt_len, pkt_type, mport, pind; + u8 *curr_ptr; + + if ((card->has_control_mask) && (port == CTRL_PORT)) { + /* Read the command Resp without aggr */ + mwifiex_dbg(adapter, CMD, + "info: %s: no aggregation for cmd\t" + "response\n", __func__); + + f_do_rx_cur = 1; + goto rx_curr_single; + } + + if (!card->mpa_rx.enabled) { + mwifiex_dbg(adapter, WARN, + "info: %s: rx aggregation disabled\n", + __func__); + + f_do_rx_cur = 1; + goto rx_curr_single; + } + + if ((!card->has_control_mask && (card->mp_rd_bitmap & + card->reg->data_port_mask)) || + (card->has_control_mask && (card->mp_rd_bitmap & + (~((u32) CTRL_PORT_MASK))))) { + /* Some more data RX pending */ + mwifiex_dbg(adapter, INFO, + "info: %s: not last packet\n", __func__); + + if (MP_RX_AGGR_IN_PROGRESS(card)) { + if (MP_RX_AGGR_BUF_HAS_ROOM(card, 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 */ + mwifiex_dbg(adapter, INFO, + "info: %s: last packet\n", __func__); + + if (MP_RX_AGGR_IN_PROGRESS(card)) { + f_do_rx_aggr = 1; + if (MP_RX_AGGR_BUF_HAS_ROOM(card, 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) { + mwifiex_dbg(adapter, INFO, + "info: current packet aggregation\n"); + /* Curr pkt can be aggregated */ + mp_rx_aggr_setup(card, rx_len, port); + + if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || + mp_rx_aggr_port_limit_reached(card)) { + mwifiex_dbg(adapter, INFO, + "info: %s: aggregated packet\t" + "limit reached\n", __func__); + /* No more pkts allowed in Aggr buf, rx it */ + f_do_rx_aggr = 1; + } + } + + if (f_do_rx_aggr) { + /* do aggr RX now */ + mwifiex_dbg(adapter, DATA, + "info: do_rx_aggr: num of packets: %d\n", + card->mpa_rx.pkt_cnt); + + if (card->supports_sdio_new_mode) { + int i; + u32 port_count; + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_rx.ports & BIT(i)) + port_count++; + + /* Reading data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_rx.start_port; + } else { + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (card->mpa_rx.ports << 4)) + + card->mpa_rx.start_port; + } + + if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, + card->mpa_rx.buf_len, mport, 1)) + goto error; + + curr_ptr = card->mpa_rx.buf; + + for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { + u32 *len_arr = card->mpa_rx.len_arr; + + /* get curr PKT len & type */ + pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]); + pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]); + + /* copy pkt to deaggr buf */ + skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind], + GFP_KERNEL | + GFP_DMA); + if (!skb_deaggr) { + mwifiex_dbg(adapter, ERROR, "skb allocation failure\t" + "drop pkt len=%d type=%d\n", + pkt_len, pkt_type); + curr_ptr += len_arr[pind]; + continue; + } + + skb_put(skb_deaggr, len_arr[pind]); + + if ((pkt_type == MWIFIEX_TYPE_DATA || + (pkt_type == MWIFIEX_TYPE_AGGR_DATA && + adapter->sdio_rx_aggr_enable)) && + (pkt_len <= len_arr[pind])) { + + memcpy(skb_deaggr->data, curr_ptr, pkt_len); + + skb_trim(skb_deaggr, pkt_len); + + /* Process de-aggr packet */ + mwifiex_decode_rx_packet(adapter, skb_deaggr, + pkt_type); + } else { + mwifiex_dbg(adapter, ERROR, + "drop wrong aggr pkt:\t" + "sdio_single_port_rx_aggr=%d\t" + "type=%d len=%d max_len=%d\n", + adapter->sdio_rx_aggr_enable, + pkt_type, pkt_len, len_arr[pind]); + dev_kfree_skb_any(skb_deaggr); + } + curr_ptr += len_arr[pind]; + } + MP_RX_AGGR_BUF_RESET(card); + } + +rx_curr_single: + if (f_do_rx_cur) { + mwifiex_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n", + port, rx_len); + + skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); + if (!skb) { + mwifiex_dbg(adapter, ERROR, + "single skb allocated fail,\t" + "drop pkt port=%d len=%d\n", port, rx_len); + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, + card->mpa_rx.buf, rx_len, + adapter->ioport + port)) + goto error; + return 0; + } + + skb_put(skb, rx_len); + + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, + skb->data, skb->len, + adapter->ioport + port)) + goto error; + if (!adapter->sdio_rx_aggr_enable && + pkt_type == MWIFIEX_TYPE_AGGR_DATA) { + mwifiex_dbg(adapter, ERROR, "drop wrong pkt type %d\t" + "current SDIO RX Aggr not enabled\n", + pkt_type); + dev_kfree_skb_any(skb); + return 0; + } + + mwifiex_decode_rx_packet(adapter, skb, pkt_type); + } + if (f_post_aggr_cur) { + mwifiex_dbg(adapter, INFO, + "info: current packet aggregation\n"); + /* Curr pkt can be aggregated */ + mp_rx_aggr_setup(card, rx_len, port); + } + + return 0; +error: + if (MP_RX_AGGR_IN_PROGRESS(card)) + MP_RX_AGGR_BUF_RESET(card); + + if (f_do_rx_cur && skb) + /* Single transfer pending. Free curr buff also */ + dev_kfree_skb_any(skb); + + return -1; +} + +/* + * This function checks the current interrupt status. + * + * The following interrupts are checked and handled by this function - + * - Data sent + * - Command sent + * - Packets received + * + * Since the firmware does not generate download ready interrupt if the + * port updated is command port only, command sent interrupt checking + * should be done manually, and for every SDIO interrupt. + * + * In case of Rx packets received, the packets are uploaded from card to + * host and processed accordingly. + */ +static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + int ret = 0; + u8 sdio_ireg; + struct sk_buff *skb; + u8 port = CTRL_PORT; + u32 len_reg_l, len_reg_u; + u32 rx_blocks; + u16 rx_len; + unsigned long flags; + u32 bitmap; + u8 cr; + + spin_lock_irqsave(&adapter->int_lock, flags); + sdio_ireg = adapter->int_status; + adapter->int_status = 0; + spin_unlock_irqrestore(&adapter->int_lock, flags); + + if (!sdio_ireg) + return ret; + + /* Following interrupt is only for SDIO new mode */ + if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) + adapter->cmd_sent = false; + + /* Following interrupt is only for SDIO new mode */ + if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { + u32 pkt_type; + + /* read the len of control packet */ + rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8; + rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0]; + rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE); + if (rx_len <= INTF_HEADER_LEN || + (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > + MWIFIEX_RX_DATA_BUF_SIZE) + return -1; + rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); + mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len); + + skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); + if (!skb) + return -1; + + skb_put(skb, rx_len); + + if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, + skb->len, adapter->ioport | + CMD_PORT_SLCT)) { + mwifiex_dbg(adapter, ERROR, + "%s: failed to card_to_host", __func__); + dev_kfree_skb_any(skb); + goto term_cmd; + } + + if ((pkt_type != MWIFIEX_TYPE_CMD) && + (pkt_type != MWIFIEX_TYPE_EVENT)) + mwifiex_dbg(adapter, ERROR, + "%s:Received wrong packet on cmd port", + __func__); + + mwifiex_decode_rx_packet(adapter, skb, pkt_type); + } + + if (sdio_ireg & DN_LD_HOST_INT_STATUS) { + bitmap = (u32) card->mp_regs[reg->wr_bitmap_l]; + bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; + if (card->supports_sdio_new_mode) { + bitmap |= + ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16; + bitmap |= + ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24; + } + card->mp_wr_bitmap = bitmap; + + mwifiex_dbg(adapter, INTR, + "int: DNLD: wr_bitmap=0x%x\n", + card->mp_wr_bitmap); + if (adapter->data_sent && + (card->mp_wr_bitmap & card->mp_data_port_mask)) { + mwifiex_dbg(adapter, INTR, + "info: <--- Tx DONE Interrupt --->\n"); + adapter->data_sent = false; + } + } + + /* As firmware will not generate download ready interrupt if the port + updated is command port only, cmd_sent should be done for any SDIO + interrupt. */ + if (card->has_control_mask && adapter->cmd_sent) { + /* Check if firmware has attach buffer at command port and + update just that in wr_bit_map. */ + card->mp_wr_bitmap |= + (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK; + if (card->mp_wr_bitmap & CTRL_PORT_MASK) + adapter->cmd_sent = false; + } + + mwifiex_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n", + adapter->cmd_sent, adapter->data_sent); + if (sdio_ireg & UP_LD_HOST_INT_STATUS) { + bitmap = (u32) card->mp_regs[reg->rd_bitmap_l]; + bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; + if (card->supports_sdio_new_mode) { + bitmap |= + ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16; + bitmap |= + ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24; + } + card->mp_rd_bitmap = bitmap; + mwifiex_dbg(adapter, INTR, + "int: UPLD: rd_bitmap=0x%x\n", + card->mp_rd_bitmap); + + while (true) { + ret = mwifiex_get_rd_port(adapter, &port); + if (ret) { + mwifiex_dbg(adapter, INFO, + "info: no more rd_port available\n"); + break; + } + len_reg_l = reg->rd_len_p0_l + (port << 1); + len_reg_u = reg->rd_len_p0_u + (port << 1); + rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; + rx_len |= (u16) card->mp_regs[len_reg_l]; + mwifiex_dbg(adapter, INFO, + "info: RX: port=%d rx_len=%u\n", + port, rx_len); + rx_blocks = + (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - + 1) / MWIFIEX_SDIO_BLOCK_SIZE; + if (rx_len <= INTF_HEADER_LEN || + (card->mpa_rx.enabled && + ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > + card->mpa_rx.buf_size))) { + mwifiex_dbg(adapter, ERROR, + "invalid rx_len=%d\n", + rx_len); + return -1; + } + + rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); + mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", + rx_len); + + if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len, + port)) { + mwifiex_dbg(adapter, ERROR, + "card_to_host_mpa failed: int status=%#x\n", + sdio_ireg); + goto term_cmd; + } + } + } + + return 0; + +term_cmd: + /* terminate cmd */ + if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) + mwifiex_dbg(adapter, ERROR, "read CFG reg failed\n"); + else + mwifiex_dbg(adapter, INFO, + "info: CFG reg val = %d\n", cr); + + if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) + mwifiex_dbg(adapter, ERROR, + "write CFG reg failed\n"); + else + mwifiex_dbg(adapter, INFO, "info: write success\n"); + + if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) + mwifiex_dbg(adapter, ERROR, + "read CFG reg failed\n"); + else + mwifiex_dbg(adapter, INFO, + "info: CFG reg val =%x\n", cr); + + return -1; +} + +/* + * This function aggregates transmission buffers in driver and downloads + * the aggregated packet to card. + * + * The individual packets are aggregated by copying into an aggregation + * buffer and then downloaded to the card. Previous unsent packets in the + * aggregation buffer are pre-copied first before new packets are added. + * Aggregation is done till there is space left in the aggregation buffer, + * or till new packets are available. + * + * The function will only download the packet to the card when aggregation + * stops, otherwise it will just aggregate the packet in aggregation buffer + * and return. + */ +static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, + u8 *payload, u32 pkt_len, u32 port, + u32 next_pkt_len) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = 0; + s32 f_send_aggr_buf = 0; + s32 f_send_cur_buf = 0; + s32 f_precopy_cur_buf = 0; + s32 f_postcopy_cur_buf = 0; + u32 mport; + + if (!card->mpa_tx.enabled || + (card->has_control_mask && (port == CTRL_PORT)) || + (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) { + mwifiex_dbg(adapter, WARN, + "info: %s: tx aggregation disabled\n", + __func__); + + f_send_cur_buf = 1; + goto tx_curr_single; + } + + if (next_pkt_len) { + /* More pkt in TX queue */ + mwifiex_dbg(adapter, INFO, + "info: %s: more packets in queue.\n", + __func__); + + if (MP_TX_AGGR_IN_PROGRESS(card)) { + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { + f_precopy_cur_buf = 1; + + if (!(card->mp_wr_bitmap & + (1 << card->curr_wr_port)) || + !MP_TX_AGGR_BUF_HAS_ROOM( + card, pkt_len + next_pkt_len)) + f_send_aggr_buf = 1; + } else { + /* No room in Aggr buf, send it */ + f_send_aggr_buf = 1; + + if (!(card->mp_wr_bitmap & + (1 << card->curr_wr_port))) + f_send_cur_buf = 1; + else + f_postcopy_cur_buf = 1; + } + } else { + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) && + (card->mp_wr_bitmap & (1 << card->curr_wr_port))) + f_precopy_cur_buf = 1; + else + f_send_cur_buf = 1; + } + } else { + /* Last pkt in TX queue */ + mwifiex_dbg(adapter, INFO, + "info: %s: Last packet in Tx Queue.\n", + __func__); + + if (MP_TX_AGGR_IN_PROGRESS(card)) { + /* some packs in Aggr buf already */ + f_send_aggr_buf = 1; + + if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) + f_precopy_cur_buf = 1; + else + /* No room in Aggr buf, send it */ + f_send_cur_buf = 1; + } else { + f_send_cur_buf = 1; + } + } + + if (f_precopy_cur_buf) { + mwifiex_dbg(adapter, DATA, + "data: %s: precopy current buffer\n", + __func__); + MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); + + if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || + mp_tx_aggr_port_limit_reached(card)) + /* No more pkts allowed in Aggr buf, send it */ + f_send_aggr_buf = 1; + } + + if (f_send_aggr_buf) { + mwifiex_dbg(adapter, DATA, + "data: %s: send aggr buffer: %d %d\n", + __func__, card->mpa_tx.start_port, + card->mpa_tx.ports); + if (card->supports_sdio_new_mode) { + u32 port_count; + int i; + + for (i = 0, port_count = 0; i < card->max_ports; i++) + if (card->mpa_tx.ports & BIT(i)) + port_count++; + + /* Writing data from "start_port + 0" to "start_port + + * port_count -1", so decrease the count by 1 + */ + port_count--; + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (port_count << 8)) + card->mpa_tx.start_port; + } else { + mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | + (card->mpa_tx.ports << 4)) + + card->mpa_tx.start_port; + } + + ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, + card->mpa_tx.buf_len, mport); + + MP_TX_AGGR_BUF_RESET(card); + } + +tx_curr_single: + if (f_send_cur_buf) { + mwifiex_dbg(adapter, DATA, + "data: %s: send current buffer %d\n", + __func__, port); + ret = mwifiex_write_data_to_card(adapter, payload, pkt_len, + adapter->ioport + port); + } + + if (f_postcopy_cur_buf) { + mwifiex_dbg(adapter, DATA, + "data: %s: postcopy current buffer\n", + __func__); + MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); + } + + return ret; +} + +/* + * 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 SDIO 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. + */ +static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, + u8 type, struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct sdio_mmc_card *card = adapter->card; + int ret; + u32 buf_block_len; + u32 blk_size; + u32 port = CTRL_PORT; + u8 *payload = (u8 *)skb->data; + u32 pkt_len = skb->len; + + /* Allocate buffer and copy payload */ + blk_size = MWIFIEX_SDIO_BLOCK_SIZE; + buf_block_len = (pkt_len + blk_size - 1) / blk_size; + *(__le16 *)&payload[0] = cpu_to_le16((u16)pkt_len); + *(__le16 *)&payload[2] = cpu_to_le16(type); + + /* + * This is SDIO specific header + * u16 length, + * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1, + * MWIFIEX_TYPE_EVENT = 3) + */ + if (type == MWIFIEX_TYPE_DATA) { + ret = mwifiex_get_wr_port_data(adapter, &port); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "%s: no wr_port available\n", + __func__); + return ret; + } + } else { + adapter->cmd_sent = true; + /* Type must be MWIFIEX_TYPE_CMD */ + + if (pkt_len <= INTF_HEADER_LEN || + pkt_len > MWIFIEX_UPLD_SIZE) + mwifiex_dbg(adapter, ERROR, + "%s: payload=%p, nb=%d\n", + __func__, payload, pkt_len); + + if (card->supports_sdio_new_mode) + port = CMD_PORT_SLCT; + } + + /* Transfer data to card */ + pkt_len = buf_block_len * blk_size; + + if (tx_param) + ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, + port, tx_param->next_pkt_len + ); + else + ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, + port, 0); + + if (ret) { + if (type == MWIFIEX_TYPE_CMD) + adapter->cmd_sent = false; + if (type == MWIFIEX_TYPE_DATA) { + adapter->data_sent = false; + /* restore curr_wr_port in error cases */ + card->curr_wr_port = port; + card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port); + } + } else { + if (type == MWIFIEX_TYPE_DATA) { + if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) + adapter->data_sent = true; + else + adapter->data_sent = false; + } + } + + return ret; +} + +/* + * This function allocates the MPA Tx and Rx buffers. + */ +static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, + u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) +{ + struct sdio_mmc_card *card = adapter->card; + u32 rx_buf_size; + int ret = 0; + + card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); + if (!card->mpa_tx.buf) { + ret = -1; + goto error; + } + + card->mpa_tx.buf_size = mpa_tx_buf_size; + + rx_buf_size = max_t(u32, mpa_rx_buf_size, + (u32)SDIO_MAX_AGGR_BUF_SIZE); + card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL); + if (!card->mpa_rx.buf) { + ret = -1; + goto error; + } + + card->mpa_rx.buf_size = rx_buf_size; + +error: + if (ret) { + kfree(card->mpa_tx.buf); + kfree(card->mpa_rx.buf); + card->mpa_tx.buf_size = 0; + card->mpa_rx.buf_size = 0; + } + + return ret; +} + +/* + * This function unregisters the SDIO device. + * + * The SDIO IRQ is released, the function is disabled and driver + * data is set to null. + */ +static void +mwifiex_unregister_dev(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + if (adapter->card) { + sdio_claim_host(card->func); + sdio_disable_func(card->func); + sdio_release_host(card->func); + } +} + +/* + * This function registers the SDIO device. + * + * SDIO IRQ is claimed, block size is set and driver data is initialized. + */ +static int mwifiex_register_dev(struct mwifiex_adapter *adapter) +{ + int ret; + struct sdio_mmc_card *card = adapter->card; + struct sdio_func *func = card->func; + + /* save adapter pointer in card */ + card->adapter = adapter; + adapter->tx_buf_size = card->tx_buf_size; + + sdio_claim_host(func); + + /* Set block size */ + ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); + sdio_release_host(func); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "cannot set SDIO block size\n"); + return ret; + } + + + adapter->dev = &func->dev; + + strcpy(adapter->fw_name, card->firmware); + if (card->fw_dump_enh) { + adapter->mem_type_mapping_tbl = generic_mem_type_map; + adapter->num_mem_types = 1; + } else { + adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; + adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); + } + + return 0; +} + +/* + * This function initializes the SDIO driver. + * + * The following initializations steps are followed - + * - Read the Host interrupt status register to acknowledge + * the first interrupt got from bootloader + * - Disable host interrupt mask register + * - Get SDIO port + * - Initialize SDIO variables in card + * - Allocate MP registers + * - Allocate MPA Tx and Rx buffers + */ +static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + int ret; + u8 sdio_ireg; + + sdio_set_drvdata(card->func, card); + + /* + * 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. + */ + mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); + + /* Get SDIO ioport */ + mwifiex_init_sdio_ioport(adapter); + + /* Initialize SDIO variables in card */ + card->mp_rd_bitmap = 0; + card->mp_wr_bitmap = 0; + card->curr_rd_port = reg->start_rd_port; + card->curr_wr_port = reg->start_wr_port; + + card->mp_data_port_mask = reg->data_port_mask; + + card->mpa_tx.buf_len = 0; + card->mpa_tx.pkt_cnt = 0; + card->mpa_tx.start_port = 0; + + card->mpa_tx.enabled = 1; + card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit; + + card->mpa_rx.buf_len = 0; + card->mpa_rx.pkt_cnt = 0; + card->mpa_rx.start_port = 0; + + card->mpa_rx.enabled = 1; + card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit; + + /* Allocate buffers for SDIO MP-A */ + card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL); + if (!card->mp_regs) + return -ENOMEM; + + /* Allocate skb pointer buffers */ + card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) * + card->mp_agg_pkt_limit, GFP_KERNEL); + card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * + card->mp_agg_pkt_limit, GFP_KERNEL); + ret = mwifiex_alloc_sdio_mpa_buffers(adapter, + card->mp_tx_agg_buf_size, + card->mp_rx_agg_buf_size); + + /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */ + if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX || + card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) { + /* Disable rx single port aggregation */ + adapter->host_disable_sdio_rx_aggr = true; + + ret = mwifiex_alloc_sdio_mpa_buffers + (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K, + MWIFIEX_MP_AGGR_BUF_SIZE_32K); + if (ret) { + /* Disable multi port aggregation */ + card->mpa_tx.enabled = 0; + card->mpa_rx.enabled = 0; + } + } + + adapter->auto_tdls = card->can_auto_tdls; + adapter->ext_scan = card->can_ext_scan; + return 0; +} + +/* + * This function resets the MPA Tx and Rx buffers. + */ +static void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + MP_TX_AGGR_BUF_RESET(card); + MP_RX_AGGR_BUF_RESET(card); +} + +/* + * This function cleans up the allocated card buffers. + * + * The following are freed by this function - + * - MP registers + * - MPA Tx buffer + * - MPA Rx buffer + */ +static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + kfree(card->mp_regs); + kfree(card->mpa_rx.skb_arr); + kfree(card->mpa_rx.len_arr); + kfree(card->mpa_tx.buf); + kfree(card->mpa_rx.buf); + sdio_set_drvdata(card->func, NULL); + kfree(card); +} + +/* + * This function updates the MP end port in card. + */ +static void +mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) +{ + struct sdio_mmc_card *card = adapter->card; + const struct mwifiex_sdio_card_reg *reg = card->reg; + int i; + + card->mp_end_port = port; + + card->mp_data_port_mask = reg->data_port_mask; + + if (reg->start_wr_port) { + for (i = 1; i <= card->max_ports - card->mp_end_port; i++) + card->mp_data_port_mask &= + ~(1 << (card->max_ports - i)); + } + + card->curr_wr_port = reg->start_wr_port; + + mwifiex_dbg(adapter, CMD, + "cmd: mp_end_port %d, data port mask 0x%x\n", + port, card->mp_data_port_mask); +} + +static void mwifiex_recreate_adapter(struct sdio_mmc_card *card) +{ + struct sdio_func *func = card->func; + const struct sdio_device_id *device_id = card->device_id; + + /* TODO mmc_hw_reset does not require destroying and re-probing the + * whole adapter. Hence there was no need to for this rube-goldberg + * design to reload the fw from an external workqueue. If we don't + * destroy the adapter we could reload the fw from + * mwifiex_main_work_queue directly. + * The real difficulty with fw reset is to restore all the user + * settings applied through ioctl. By destroying and recreating the + * adapter, we take the easy way out, since we rely on user space to + * restore them. We assume that user space will treat the new + * incarnation of the adapter(interfaces) as if they had been just + * discovered and initializes them from scratch. + */ + + mwifiex_sdio_remove(func); + + /* power cycle the adapter */ + sdio_claim_host(func); + mmc_hw_reset(func->card->host); + sdio_release_host(func); + + mwifiex_sdio_probe(func, device_id); +} + +static struct mwifiex_adapter *save_adapter; +static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + /* TODO card pointer is unprotected. If the adapter is removed + * physically, sdio core might trigger mwifiex_sdio_remove, before this + * workqueue is run, which will destroy the adapter struct. When this + * workqueue eventually exceutes it will dereference an invalid adapter + * pointer + */ + mwifiex_recreate_adapter(card); +} + +/* This function read/write firmware */ +static enum +rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter, + u8 doneflag) +{ + struct sdio_mmc_card *card = adapter->card; + int ret, tries; + u8 ctrl_data = 0; + + sdio_writeb(card->func, card->reg->fw_dump_host_ready, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO Write ERR\n"); + return RDWR_STATUS_FAILURE; + } + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, + &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); + return RDWR_STATUS_FAILURE; + } + if (ctrl_data == FW_DUMP_DONE) + break; + if (doneflag && ctrl_data == doneflag) + return RDWR_STATUS_DONE; + if (ctrl_data != card->reg->fw_dump_host_ready) { + mwifiex_dbg(adapter, WARN, + "The ctrl reg was changed, re-try again\n"); + sdio_writeb(card->func, card->reg->fw_dump_host_ready, + card->reg->fw_dump_ctrl, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); + return RDWR_STATUS_FAILURE; + } + } + usleep_range(100, 200); + } + if (ctrl_data == card->reg->fw_dump_host_ready) { + mwifiex_dbg(adapter, ERROR, + "Fail to pull ctrl_data\n"); + return RDWR_STATUS_FAILURE; + } + + return RDWR_STATUS_SUCCESS; +} + +/* This function dump firmware memory to file */ +static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + int ret = 0; + unsigned int reg, reg_start, reg_end; + u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; + enum rdwr_status stat; + u32 memory_size; + + if (!card->can_dump_fw) + return; + + for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + + mwifiex_pm_wakeup_card(adapter); + sdio_claim_host(card->func); + + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); + + stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg = card->reg->fw_dump_start; + /* Read the number of the memories which will dump */ + dump_num = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO read memory length err\n"); + goto done; + } + + /* Read the length of every memory which will dump */ + for (idx = 0; idx < dump_num; idx++) { + struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; + + stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + memory_size = 0; + reg = card->reg->fw_dump_start; + for (i = 0; i < 4; i++) { + read_reg = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); + goto done; + } + memory_size |= (read_reg << i*8); + reg++; + } + + if (memory_size == 0) { + mwifiex_dbg(adapter, DUMP, "Firmware dump Finished!\n"); + ret = mwifiex_write_reg(adapter, + card->reg->fw_dump_ctrl, + FW_DUMP_READ_DONE); + if (ret) { + mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); + return; + } + break; + } + + mwifiex_dbg(adapter, DUMP, + "%s_SIZE=0x%x\n", entry->mem_name, memory_size); + entry->mem_ptr = vmalloc(memory_size + 1); + entry->mem_size = memory_size; + if (!entry->mem_ptr) { + mwifiex_dbg(adapter, ERROR, "Vmalloc %s failed\n", + entry->mem_name); + goto done; + } + dbg_ptr = entry->mem_ptr; + end_ptr = dbg_ptr + memory_size; + + doneflag = entry->done_flag; + mwifiex_dbg(adapter, DUMP, + "Start %s output, please wait...\n", + entry->mem_name); + + do { + stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg_start = card->reg->fw_dump_start; + reg_end = card->reg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "SDIO read err\n"); + goto done; + } + if (dbg_ptr < end_ptr) + dbg_ptr++; + else + mwifiex_dbg(adapter, ERROR, + "Allocated buf not enough\n"); + } + + if (stat != RDWR_STATUS_DONE) + continue; + + mwifiex_dbg(adapter, DUMP, "%s done: size=0x%tx\n", + entry->mem_name, dbg_ptr - entry->mem_ptr); + break; + } while (1); + } + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); + +done: + sdio_release_host(card->func); +} + +static void mwifiex_sdio_generic_fw_dump(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + struct memory_type_mapping *entry = &generic_mem_type_map[0]; + unsigned int reg, reg_start, reg_end; + u8 start_flag = 0, done_flag = 0; + u8 *dbg_ptr, *end_ptr; + enum rdwr_status stat; + int ret = -1, tries; + + if (!card->fw_dump_enh) + return; + + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + + mwifiex_pm_wakeup_card(adapter); + sdio_claim_host(card->func); + + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); + + stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + + reg_start = card->reg->fw_dump_start; + reg_end = card->reg->fw_dump_end; + for (reg = reg_start; reg <= reg_end; reg++) { + for (tries = 0; tries < MAX_POLL_TRIES; tries++) { + start_flag = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "SDIO read err\n"); + goto done; + } + if (start_flag == 0) + break; + if (tries == MAX_POLL_TRIES) { + mwifiex_dbg(adapter, ERROR, + "FW not ready to dump\n"); + ret = -1; + goto done; + } + } + usleep_range(100, 200); + } + + entry->mem_ptr = vmalloc(0xf0000 + 1); + if (!entry->mem_ptr) { + ret = -1; + goto done; + } + dbg_ptr = entry->mem_ptr; + entry->mem_size = 0xf0000; + end_ptr = dbg_ptr + entry->mem_size; + + done_flag = entry->done_flag; + mwifiex_dbg(adapter, DUMP, + "Start %s output, please wait...\n", entry->mem_name); + + while (true) { + stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); + if (stat == RDWR_STATUS_FAILURE) + goto done; + for (reg = reg_start; reg <= reg_end; reg++) { + *dbg_ptr = sdio_readb(card->func, reg, &ret); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "SDIO read err\n"); + goto done; + } + dbg_ptr++; + if (dbg_ptr >= end_ptr) { + u8 *tmp_ptr; + + tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1); + if (!tmp_ptr) + goto done; + + memcpy(tmp_ptr, entry->mem_ptr, + entry->mem_size); + vfree(entry->mem_ptr); + entry->mem_ptr = tmp_ptr; + tmp_ptr = NULL; + dbg_ptr = entry->mem_ptr + entry->mem_size; + entry->mem_size += 0x4000; + end_ptr = entry->mem_ptr + entry->mem_size; + } + } + if (stat == RDWR_STATUS_DONE) { + entry->mem_size = dbg_ptr - entry->mem_ptr; + mwifiex_dbg(adapter, DUMP, "dump %s done size=0x%x\n", + entry->mem_name, entry->mem_size); + ret = 0; + break; + } + } + mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); + +done: + if (ret) { + mwifiex_dbg(adapter, ERROR, "firmware dump failed\n"); + if (entry->mem_ptr) { + vfree(entry->mem_ptr); + entry->mem_ptr = NULL; + } + entry->mem_size = 0; + } + sdio_release_host(card->func); +} + +static void mwifiex_sdio_device_dump_work(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + mwifiex_drv_info_dump(adapter); + if (card->fw_dump_enh) + mwifiex_sdio_generic_fw_dump(adapter); + else + mwifiex_sdio_fw_dump(adapter); + mwifiex_upload_device_dump(adapter); +} + +static void mwifiex_sdio_work(struct work_struct *work) +{ + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, + &iface_work_flags)) + mwifiex_sdio_device_dump_work(save_adapter); + if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, + &iface_work_flags)) + mwifiex_sdio_card_reset_work(save_adapter); +} + +static DECLARE_WORK(sdio_work, mwifiex_sdio_work); +/* This function resets the card */ +static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) +{ + save_adapter = adapter; + if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags)) + return; + + set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags); + + schedule_work(&sdio_work); +} + +/* This function dumps FW information */ +static void mwifiex_sdio_device_dump(struct mwifiex_adapter *adapter) +{ + save_adapter = adapter; + if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags)) + return; + + set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags); + schedule_work(&sdio_work); +} + +/* Function to dump SDIO function registers and SDIO scratch registers in case + * of FW crash + */ +static int +mwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) +{ + char *p = drv_buf; + struct sdio_mmc_card *cardp = adapter->card; + int ret = 0; + u8 count, func, data, index = 0, size = 0; + u8 reg, reg_start, reg_end; + char buf[256], *ptr; + + if (!p) + return 0; + + mwifiex_dbg(adapter, MSG, "SDIO register dump start\n"); + + mwifiex_pm_wakeup_card(adapter); + + sdio_claim_host(cardp->func); + + for (count = 0; count < 5; count++) { + memset(buf, 0, sizeof(buf)); + ptr = buf; + + switch (count) { + case 0: + /* Read the registers of SDIO function0 */ + func = count; + reg_start = 0; + reg_end = 9; + break; + case 1: + /* Read the registers of SDIO function1 */ + func = count; + reg_start = cardp->reg->func1_dump_reg_start; + reg_end = cardp->reg->func1_dump_reg_end; + break; + case 2: + index = 0; + func = 1; + reg_start = cardp->reg->func1_spec_reg_table[index++]; + size = cardp->reg->func1_spec_reg_num; + reg_end = cardp->reg->func1_spec_reg_table[size-1]; + break; + default: + /* Read the scratch registers of SDIO function1 */ + if (count == 4) + mdelay(100); + func = 1; + reg_start = cardp->reg->func1_scratch_reg; + reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE; + } + + if (count != 2) + ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", + func, reg_start, reg_end); + else + ptr += sprintf(ptr, "SDIO Func%d: ", func); + + for (reg = reg_start; reg <= reg_end;) { + if (func == 0) + data = sdio_f0_readb(cardp->func, reg, &ret); + else + data = sdio_readb(cardp->func, reg, &ret); + + if (count == 2) + ptr += sprintf(ptr, "(%#x) ", reg); + if (!ret) { + ptr += sprintf(ptr, "%02x ", data); + } else { + ptr += sprintf(ptr, "ERR"); + break; + } + + if (count == 2 && reg < reg_end) + reg = cardp->reg->func1_spec_reg_table[index++]; + else + reg++; + } + + mwifiex_dbg(adapter, MSG, "%s\n", buf); + p += sprintf(p, "%s\n", buf); + } + + sdio_release_host(cardp->func); + + mwifiex_dbg(adapter, MSG, "SDIO register dump end\n"); + + return p - drv_buf; +} + +static struct mwifiex_if_ops sdio_ops = { + .init_if = mwifiex_init_sdio, + .cleanup_if = mwifiex_cleanup_sdio, + .check_fw_status = mwifiex_check_fw_status, + .prog_fw = mwifiex_prog_fw_w_helper, + .register_dev = mwifiex_register_dev, + .unregister_dev = mwifiex_unregister_dev, + .enable_int = mwifiex_sdio_enable_host_int, + .disable_int = mwifiex_sdio_disable_host_int, + .process_int_status = mwifiex_process_int_status, + .host_to_card = mwifiex_sdio_host_to_card, + .wakeup = mwifiex_pm_wakeup_card, + .wakeup_complete = mwifiex_pm_wakeup_card_complete, + + /* SDIO specific */ + .update_mp_end_port = mwifiex_update_mp_end_port, + .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, + .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, + .event_complete = mwifiex_sdio_event_complete, + .card_reset = mwifiex_sdio_card_reset, + .reg_dump = mwifiex_sdio_reg_dump, + .device_dump = mwifiex_sdio_device_dump, + .deaggr_pkt = mwifiex_deaggr_sdio_pkt, +}; + +/* + * This function initializes the SDIO driver. + * + * This initiates the semaphore and registers the device with + * SDIO bus. + */ +static int +mwifiex_sdio_init_module(void) +{ + sema_init(&add_remove_card_sem, 1); + + /* Clear the flag in case user removes the card. */ + user_rmmod = 0; + + return sdio_register_driver(&mwifiex_sdio); +} + +/* + * This function cleans up the SDIO driver. + * + * The following major steps are followed for cleanup - + * - Resume the device if its suspended + * - Disconnect the device if connected + * - Shutdown the firmware + * - Unregister the device from SDIO bus. + */ +static void +mwifiex_sdio_cleanup_module(void) +{ + if (!down_interruptible(&add_remove_card_sem)) + up(&add_remove_card_sem); + + /* Set the flag as user is removing this module. */ + user_rmmod = 1; + cancel_work_sync(&sdio_work); + + sdio_unregister_driver(&mwifiex_sdio); +} + +module_init(mwifiex_sdio_init_module); +module_exit(mwifiex_sdio_cleanup_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); +MODULE_VERSION(SDIO_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME); +MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h new file mode 100644 index 000000000000..b9fbc5cf6262 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sdio.h @@ -0,0 +1,672 @@ +/* + * Marvell Wireless LAN device driver: SDIO specific definitions + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_SDIO_H +#define _MWIFIEX_SDIO_H + + +#include +#include +#include +#include +#include + +#include "main.h" + +#define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin" +#define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" +#define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin" +#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin" +#define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin" +#define SD8801_DEFAULT_FW_NAME "mrvl/sd8801_uapsta.bin" +#define SD8997_DEFAULT_FW_NAME "mrvl/sd8997_uapsta.bin" + +#define BLOCK_MODE 1 +#define BYTE_MODE 0 + +#define REG_PORT 0 + +#define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff + +#define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 + +#define MWIFIEX_MAX_FUNC2_REG_NUM 13 +#define MWIFIEX_SDIO_SCRATCH_SIZE 10 + +#define SDIO_MPA_ADDR_BASE 0x1000 +#define CTRL_PORT 0 +#define CTRL_PORT_MASK 0x0001 + +#define CMD_PORT_UPLD_INT_MASK (0x1U<<6) +#define CMD_PORT_DNLD_INT_MASK (0x1U<<7) +#define HOST_TERM_CMD53 (0x1U << 2) +#define REG_PORT 0 +#define MEM_PORT 0x10000 + +#define CMD53_NEW_MODE (0x1U << 0) +#define CMD_PORT_RD_LEN_EN (0x1U << 2) +#define CMD_PORT_AUTO_EN (0x1U << 0) +#define CMD_PORT_SLCT 0x8000 +#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) +#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) + +#define MWIFIEX_MP_AGGR_BUF_SIZE_16K (16384) +#define MWIFIEX_MP_AGGR_BUF_SIZE_32K (32768) +/* we leave one block of 256 bytes for DMA alignment*/ +#define MWIFIEX_MP_AGGR_BUF_SIZE_MAX (65280) + +/* Misc. Config Register : Auto Re-enable interrupts */ +#define AUTO_RE_ENABLE_INT BIT(4) + +/* Host Control Registers : Configuration */ +#define CONFIGURATION_REG 0x00 +/* Host Control Registers : Host power up */ +#define HOST_POWER_UP (0x1U << 1) + +/* 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 : 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 : Host interrupt status */ +#define CARD_INT_STATUS_REG 0x28 + +/* Card Control Registers : Card I/O ready */ +#define CARD_IO_READY (0x1U << 3) +/* Card Control Registers : Download card ready */ +#define DN_LD_CARD_RDY (0x1U << 0) + +/* Max retry number of CMD53 write */ +#define MAX_WRITE_IOMEM_RETRY 2 + +/* SDIO Tx aggregation in progress ? */ +#define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0) + +/* SDIO Tx aggregation buffer room for next packet ? */ +#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ((a->mpa_tx.buf_len+len) \ + <= a->mpa_tx.buf_size) + +/* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */ +#define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \ + memmove(&a->mpa_tx.buf[a->mpa_tx.buf_len], \ + payload, pkt_len); \ + a->mpa_tx.buf_len += pkt_len; \ + if (!a->mpa_tx.pkt_cnt) \ + a->mpa_tx.start_port = port; \ + if (a->mpa_tx.start_port <= port) \ + a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \ + else \ + a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+ \ + (a->max_ports - \ + a->mp_end_port))); \ + a->mpa_tx.pkt_cnt++; \ +} while (0) + +/* SDIO Tx aggregation limit ? */ +#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \ + (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit) + +/* Reset SDIO Tx aggregation buffer parameters */ +#define MP_TX_AGGR_BUF_RESET(a) do { \ + a->mpa_tx.pkt_cnt = 0; \ + a->mpa_tx.buf_len = 0; \ + a->mpa_tx.ports = 0; \ + a->mpa_tx.start_port = 0; \ +} while (0) + +/* SDIO Rx aggregation limit ? */ +#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \ + (a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit) + +/* SDIO Rx aggregation in progress ? */ +#define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) + +/* SDIO Rx aggregation buffer room for next packet ? */ +#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ + ((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size) + +/* Reset SDIO Rx aggregation buffer parameters */ +#define MP_RX_AGGR_BUF_RESET(a) do { \ + a->mpa_rx.pkt_cnt = 0; \ + a->mpa_rx.buf_len = 0; \ + a->mpa_rx.ports = 0; \ + a->mpa_rx.start_port = 0; \ +} while (0) + +/* data structure for SDIO MPA TX */ +struct mwifiex_sdio_mpa_tx { + /* multiport tx aggregation buffer pointer */ + u8 *buf; + u32 buf_len; + u32 pkt_cnt; + u32 ports; + u16 start_port; + u8 enabled; + u32 buf_size; + u32 pkt_aggr_limit; +}; + +struct mwifiex_sdio_mpa_rx { + u8 *buf; + u32 buf_len; + u32 pkt_cnt; + u32 ports; + u16 start_port; + + struct sk_buff **skb_arr; + u32 *len_arr; + + u8 enabled; + u32 buf_size; + u32 pkt_aggr_limit; +}; + +int mwifiex_bus_register(void); +void mwifiex_bus_unregister(void); + +struct mwifiex_sdio_card_reg { + u8 start_rd_port; + u8 start_wr_port; + u8 base_0_reg; + u8 base_1_reg; + u8 poll_reg; + u8 host_int_enable; + u8 host_int_rsr_reg; + u8 host_int_status_reg; + u8 host_int_mask_reg; + u8 status_reg_0; + u8 status_reg_1; + u8 sdio_int_mask; + u32 data_port_mask; + u8 io_port_0_reg; + u8 io_port_1_reg; + u8 io_port_2_reg; + u8 max_mp_regs; + u8 rd_bitmap_l; + u8 rd_bitmap_u; + u8 rd_bitmap_1l; + u8 rd_bitmap_1u; + u8 wr_bitmap_l; + u8 wr_bitmap_u; + u8 wr_bitmap_1l; + u8 wr_bitmap_1u; + u8 rd_len_p0_l; + u8 rd_len_p0_u; + u8 card_misc_cfg_reg; + u8 card_cfg_2_1_reg; + u8 cmd_rd_len_0; + u8 cmd_rd_len_1; + u8 cmd_rd_len_2; + u8 cmd_rd_len_3; + u8 cmd_cfg_0; + u8 cmd_cfg_1; + u8 cmd_cfg_2; + u8 cmd_cfg_3; + u8 fw_dump_host_ready; + u8 fw_dump_ctrl; + u8 fw_dump_start; + u8 fw_dump_end; + u8 func1_dump_reg_start; + u8 func1_dump_reg_end; + u8 func1_scratch_reg; + u8 func1_spec_reg_num; + u8 func1_spec_reg_table[MWIFIEX_MAX_FUNC2_REG_NUM]; +}; + +struct sdio_mmc_card { + struct sdio_func *func; + struct mwifiex_adapter *adapter; + + const char *firmware; + const struct mwifiex_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; + u16 tx_buf_size; + u32 mp_tx_agg_buf_size; + u32 mp_rx_agg_buf_size; + + u32 mp_rd_bitmap; + u32 mp_wr_bitmap; + + u16 mp_end_port; + u32 mp_data_port_mask; + + u8 curr_rd_port; + u8 curr_wr_port; + + u8 *mp_regs; + bool supports_sdio_new_mode; + bool has_control_mask; + bool can_dump_fw; + bool fw_dump_enh; + bool can_auto_tdls; + bool can_ext_scan; + + struct mwifiex_sdio_mpa_tx mpa_tx; + struct mwifiex_sdio_mpa_rx mpa_rx; + + /* needed for card reset */ + const struct sdio_device_id *device_id; +}; + +struct mwifiex_sdio_device { + const char *firmware; + const struct mwifiex_sdio_card_reg *reg; + u8 max_ports; + u8 mp_agg_pkt_limit; + u16 tx_buf_size; + u32 mp_tx_agg_buf_size; + u32 mp_rx_agg_buf_size; + bool supports_sdio_new_mode; + bool has_control_mask; + bool can_dump_fw; + bool fw_dump_enh; + bool can_auto_tdls; + bool can_ext_scan; +}; + +static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { + .start_rd_port = 1, + .start_wr_port = 1, + .base_0_reg = 0x0040, + .base_1_reg = 0x0041, + .poll_reg = 0x30, + .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, + .host_int_rsr_reg = 0x1, + .host_int_mask_reg = 0x02, + .host_int_status_reg = 0x03, + .status_reg_0 = 0x60, + .status_reg_1 = 0x61, + .sdio_int_mask = 0x3f, + .data_port_mask = 0x0000fffe, + .io_port_0_reg = 0x78, + .io_port_1_reg = 0x79, + .io_port_2_reg = 0x7A, + .max_mp_regs = 64, + .rd_bitmap_l = 0x04, + .rd_bitmap_u = 0x05, + .wr_bitmap_l = 0x06, + .wr_bitmap_u = 0x07, + .rd_len_p0_l = 0x08, + .rd_len_p0_u = 0x09, + .card_misc_cfg_reg = 0x6c, + .func1_dump_reg_start = 0x0, + .func1_dump_reg_end = 0x9, + .func1_scratch_reg = 0x60, + .func1_spec_reg_num = 5, + .func1_spec_reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c}, +}; + +static const struct mwifiex_sdio_card_reg mwifiex_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_rsr_reg = 0x1, + .host_int_status_reg = 0x03, + .host_int_mask_reg = 0x02, + .status_reg_0 = 0xc0, + .status_reg_1 = 0xc1, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .io_port_0_reg = 0xD8, + .io_port_1_reg = 0xD9, + .io_port_2_reg = 0xDA, + .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_misc_cfg_reg = 0xcc, + .card_cfg_2_1_reg = 0xcd, + .cmd_rd_len_0 = 0xb4, + .cmd_rd_len_1 = 0xb5, + .cmd_rd_len_2 = 0xb6, + .cmd_rd_len_3 = 0xb7, + .cmd_cfg_0 = 0xb8, + .cmd_cfg_1 = 0xb9, + .cmd_cfg_2 = 0xba, + .cmd_cfg_3 = 0xbb, + .fw_dump_host_ready = 0xee, + .fw_dump_ctrl = 0xe2, + .fw_dump_start = 0xe3, + .fw_dump_end = 0xea, + .func1_dump_reg_start = 0x0, + .func1_dump_reg_end = 0xb, + .func1_scratch_reg = 0xc0, + .func1_spec_reg_num = 8, + .func1_spec_reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, + 0x59, 0x5c, 0x5d}, +}; + +static const struct mwifiex_sdio_card_reg mwifiex_reg_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_rsr_reg = 0x4, + .host_int_status_reg = 0x0C, + .host_int_mask_reg = 0x08, + .status_reg_0 = 0xE8, + .status_reg_1 = 0xE9, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .io_port_0_reg = 0xE4, + .io_port_1_reg = 0xE5, + .io_port_2_reg = 0xE6, + .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_misc_cfg_reg = 0xd8, + .card_cfg_2_1_reg = 0xd9, + .cmd_rd_len_0 = 0xc0, + .cmd_rd_len_1 = 0xc1, + .cmd_rd_len_2 = 0xc2, + .cmd_rd_len_3 = 0xc3, + .cmd_cfg_0 = 0xc4, + .cmd_cfg_1 = 0xc5, + .cmd_cfg_2 = 0xc6, + .cmd_cfg_3 = 0xc7, + .fw_dump_host_ready = 0xcc, + .fw_dump_ctrl = 0xf0, + .fw_dump_start = 0xf1, + .fw_dump_end = 0xf8, + .func1_dump_reg_start = 0x10, + .func1_dump_reg_end = 0x17, + .func1_scratch_reg = 0xe8, + .func1_spec_reg_num = 13, + .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, + 0x60, 0x61, 0x62, 0x64, + 0x65, 0x66, 0x68, 0x69, + 0x6a}, +}; + +static const struct mwifiex_sdio_card_reg mwifiex_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_rsr_reg = 0x4, + .host_int_status_reg = 0x0C, + .host_int_mask_reg = 0x08, + .status_reg_0 = 0x90, + .status_reg_1 = 0x91, + .sdio_int_mask = 0xff, + .data_port_mask = 0xffffffff, + .io_port_0_reg = 0xE4, + .io_port_1_reg = 0xE5, + .io_port_2_reg = 0xE6, + .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_misc_cfg_reg = 0xd8, + .card_cfg_2_1_reg = 0xd9, + .cmd_rd_len_0 = 0xc0, + .cmd_rd_len_1 = 0xc1, + .cmd_rd_len_2 = 0xc2, + .cmd_rd_len_3 = 0xc3, + .cmd_cfg_0 = 0xc4, + .cmd_cfg_1 = 0xc5, + .cmd_cfg_2 = 0xc6, + .cmd_cfg_3 = 0xc7, + .func1_dump_reg_start = 0x10, + .func1_dump_reg_end = 0x17, + .func1_scratch_reg = 0x90, + .func1_spec_reg_num = 13, + .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, + 0x61, 0x62, 0x64, 0x65, 0x66, + 0x68, 0x69, 0x6a}, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { + .firmware = SD8786_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_sdio_new_mode = false, + .has_control_mask = true, + .can_dump_fw = false, + .can_auto_tdls = false, + .can_ext_scan = false, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { + .firmware = SD8787_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_sdio_new_mode = false, + .has_control_mask = true, + .can_dump_fw = false, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { + .firmware = SD8797_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .supports_sdio_new_mode = false, + .has_control_mask = true, + .can_dump_fw = false, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { + .firmware = SD8897_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd8897, + .max_ports = 32, + .mp_agg_pkt_limit = 16, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, + .supports_sdio_new_mode = true, + .has_control_mask = false, + .can_dump_fw = true, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = { + .firmware = SD8997_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd8997, + .max_ports = 32, + .mp_agg_pkt_limit = 16, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, + .supports_sdio_new_mode = true, + .has_control_mask = false, + .can_dump_fw = true, + .fw_dump_enh = true, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = { + .firmware = SD8887_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd8887, + .max_ports = 32, + .mp_agg_pkt_limit = 16, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, + .supports_sdio_new_mode = true, + .has_control_mask = false, + .can_dump_fw = false, + .can_auto_tdls = true, + .can_ext_scan = true, +}; + +static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = { + .firmware = SD8801_DEFAULT_FW_NAME, + .reg = &mwifiex_reg_sd87xx, + .max_ports = 16, + .mp_agg_pkt_limit = 8, + .supports_sdio_new_mode = false, + .has_control_mask = true, + .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, + .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, + .can_dump_fw = false, + .can_auto_tdls = false, + .can_ext_scan = true, +}; + +/* + * .cmdrsp_complete handler + */ +static inline int mwifiex_sdio_cmdrsp_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} + +/* + * .event_complete handler + */ +static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + dev_kfree_skb_any(skb); + return 0; +} + +static inline bool +mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u8 tmp; + + if (card->curr_rd_port < card->mpa_rx.start_port) { + if (card->supports_sdio_new_mode) + tmp = card->mp_end_port >> 1; + else + tmp = card->mp_agg_pkt_limit; + + if (((card->max_ports - card->mpa_rx.start_port) + + card->curr_rd_port) >= tmp) + return true; + } + + if (!card->supports_sdio_new_mode) + return false; + + if ((card->curr_rd_port - card->mpa_rx.start_port) >= + (card->mp_end_port >> 1)) + return true; + + return false; +} + +static inline bool +mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card) +{ + u16 tmp; + + if (card->curr_wr_port < card->mpa_tx.start_port) { + if (card->supports_sdio_new_mode) + tmp = card->mp_end_port >> 1; + else + tmp = card->mp_agg_pkt_limit; + + if (((card->max_ports - card->mpa_tx.start_port) + + card->curr_wr_port) >= tmp) + return true; + } + + if (!card->supports_sdio_new_mode) + return false; + + if ((card->curr_wr_port - card->mpa_tx.start_port) >= + (card->mp_end_port >> 1)) + return true; + + return false; +} + +/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ +static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, + u16 rx_len, u8 port) +{ + card->mpa_rx.buf_len += rx_len; + + if (!card->mpa_rx.pkt_cnt) + card->mpa_rx.start_port = port; + + if (card->supports_sdio_new_mode) { + card->mpa_rx.ports |= (1 << port); + } else { + if (card->mpa_rx.start_port <= port) + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt); + else + card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1); + } + card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = NULL; + card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len; + card->mpa_rx.pkt_cnt++; +} +#endif /* _MWIFIEX_SDIO_H */ diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c new file mode 100644 index 000000000000..e486867a4c67 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -0,0 +1,2282 @@ +/* + * Marvell Wireless LAN device driver: station command handling + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" + +static bool drcs; +module_param(drcs, bool, 0644); +MODULE_PARM_DESC(drcs, "multi-channel operation:1, single-channel operation:0"); + +static bool disable_auto_ds; +module_param(disable_auto_ds, bool, 0); +MODULE_PARM_DESC(disable_auto_ds, + "deepsleep enabled=0(default), deepsleep disabled=1"); +/* + * This function prepares command to set/get RSSI information. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting data/beacon average factors + * - Resetting SNR/NF/RSSI values in private structure + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_rssi_info(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 cmd_action) +{ + cmd->command = cpu_to_le16(HostCmd_CMD_RSSI_INFO); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) + + S_DS_GEN); + cmd->params.rssi_info.action = cpu_to_le16(cmd_action); + cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor); + cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor); + + /* Reset SNR/NF/RSSI values in private structure */ + 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; + + return 0; +} + +/* + * This function prepares command to set MAC control. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_mac_control(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u16 *action) +{ + struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl; + + if (cmd_action != HostCmd_ACT_GEN_SET) { + mwifiex_dbg(priv->adapter, ERROR, + "mac_control: only support set cmd\n"); + return -1; + } + + cmd->command = cpu_to_le16(HostCmd_CMD_MAC_CONTROL); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN); + mac_ctrl->action = cpu_to_le16(*action); + + return 0; +} + +/* + * This function prepares command to set/get SNMP MIB. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting SNMP MIB OID number and value + * (as required) + * - Ensuring correct endian-ness + * + * The following SNMP MIB OIDs are supported - + * - FRAG_THRESH_I : Fragmentation threshold + * - RTS_THRESH_I : RTS threshold + * - SHORT_RETRY_LIM_I : Short retry limit + * - DOT11D_I : 11d support + */ +static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + u16 *ul_temp) +{ + struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib; + + mwifiex_dbg(priv->adapter, CMD, + "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) + - 1 + S_DS_GEN); + + snmp_mib->oid = cpu_to_le16((u16)cmd_oid); + if (cmd_action == HostCmd_ACT_GEN_GET) { + snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_GET); + snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE); + le16_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); + } else if (cmd_action == HostCmd_ACT_GEN_SET) { + snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); + snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); + *((__le16 *) (snmp_mib->value)) = cpu_to_le16(*ul_temp); + le16_add_cpu(&cmd->size, sizeof(u16)); + } + + mwifiex_dbg(priv->adapter, CMD, + "cmd: SNMP_CMD: Action=0x%x, OID=0x%x,\t" + "OIDSize=0x%x, Value=0x%x\n", + cmd_action, cmd_oid, le16_to_cpu(snmp_mib->buf_size), + le16_to_cpu(*(__le16 *)snmp_mib->value)); + return 0; +} + +/* + * This function prepares command to get log. + * + * Preparation includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_get_log(struct host_cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_GET_LOG); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) + + S_DS_GEN); + return 0; +} + +/* + * This function prepares command to set/get Tx data rate configuration. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting configuration index, rate scope and rate drop pattern + * parameters (as required) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u16 *pbitmap_rates) +{ + struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; + struct mwifiex_rate_scope *rate_scope; + struct mwifiex_rate_drop_pattern *rate_drop; + u32 i; + + cmd->command = cpu_to_le16(HostCmd_CMD_TX_RATE_CFG); + + rate_cfg->action = cpu_to_le16(cmd_action); + rate_cfg->cfg_index = 0; + + rate_scope = (struct mwifiex_rate_scope *) ((u8 *) rate_cfg + + sizeof(struct host_cmd_ds_tx_rate_cfg)); + rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); + rate_scope->length = cpu_to_le16 + (sizeof(*rate_scope) - sizeof(struct mwifiex_ie_types_header)); + if (pbitmap_rates != NULL) { + rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); + for (i = 0; + i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); + i++) + rate_scope->ht_mcs_rate_bitmap[i] = + cpu_to_le16(pbitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] = + cpu_to_le16(pbitmap_rates[10 + i]); + } + } else { + rate_scope->hr_dsss_rate_bitmap = + cpu_to_le16(priv->bitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = + cpu_to_le16(priv->bitmap_rates[1]); + for (i = 0; + i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); + i++) + rate_scope->ht_mcs_rate_bitmap[i] = + cpu_to_le16(priv->bitmap_rates[2 + i]); + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; + i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); + i++) + rate_scope->vht_mcs_rate_bitmap[i] = + cpu_to_le16(priv->bitmap_rates[10 + i]); + } + } + + rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope + + sizeof(struct mwifiex_rate_scope)); + rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); + rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); + rate_drop->rate_drop_mode = 0; + + cmd->size = + cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) + + sizeof(struct mwifiex_rate_scope) + + sizeof(struct mwifiex_rate_drop_pattern)); + + return 0; +} + +/* + * This function prepares command to set/get Tx power configuration. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting Tx power mode, power group TLV + * (as required) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd, + u16 cmd_action, + struct host_cmd_ds_txpwr_cfg *txp) +{ + struct mwifiex_types_power_group *pg_tlv; + struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_TXPWR_CFG); + cmd->size = + cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg)); + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + if (txp->mode) { + pg_tlv = (struct mwifiex_types_power_group + *) ((unsigned long) txp + + sizeof(struct host_cmd_ds_txpwr_cfg)); + memmove(cmd_txp_cfg, txp, + sizeof(struct host_cmd_ds_txpwr_cfg) + + sizeof(struct mwifiex_types_power_group) + + le16_to_cpu(pg_tlv->length)); + + pg_tlv = (struct mwifiex_types_power_group *) ((u8 *) + cmd_txp_cfg + + sizeof(struct host_cmd_ds_txpwr_cfg)); + cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + + sizeof(struct mwifiex_types_power_group) + + le16_to_cpu(pg_tlv->length)); + } else { + memmove(cmd_txp_cfg, txp, sizeof(*txp)); + } + cmd_txp_cfg->action = cpu_to_le16(cmd_action); + break; + case HostCmd_ACT_GEN_GET: + cmd_txp_cfg->action = cpu_to_le16(cmd_action); + break; + } + + return 0; +} + +/* + * This function prepares command to get RF Tx power. + */ +static int mwifiex_cmd_rf_tx_power(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp; + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr) + + S_DS_GEN); + cmd->command = cpu_to_le16(HostCmd_CMD_RF_TX_PWR); + txp->action = cpu_to_le16(cmd_action); + + return 0; +} + +/* + * This function prepares command to set rf antenna. + */ +static int mwifiex_cmd_rf_antenna(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, + struct mwifiex_ds_ant_cfg *ant_cfg) +{ + struct host_cmd_ds_rf_ant_mimo *ant_mimo = &cmd->params.ant_mimo; + struct host_cmd_ds_rf_ant_siso *ant_siso = &cmd->params.ant_siso; + + cmd->command = cpu_to_le16(HostCmd_CMD_RF_ANTENNA); + + if (cmd_action != HostCmd_ACT_GEN_SET) + return 0; + + if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) { + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_mimo) + + S_DS_GEN); + ant_mimo->action_tx = cpu_to_le16(HostCmd_ACT_SET_TX); + ant_mimo->tx_ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); + ant_mimo->action_rx = cpu_to_le16(HostCmd_ACT_SET_RX); + ant_mimo->rx_ant_mode = cpu_to_le16((u16)ant_cfg->rx_ant); + } else { + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_siso) + + S_DS_GEN); + ant_siso->action = cpu_to_le16(HostCmd_ACT_SET_BOTH); + ant_siso->ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); + } + + return 0; +} + +/* + * This function prepares command to set Host Sleep configuration. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting Host Sleep action, conditions, ARP filters + * (as required) + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, + struct mwifiex_hs_config_param *hscfg_param) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; + bool hs_activate = false; + + if (!hscfg_param) + /* New Activate command */ + hs_activate = true; + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); + + if (!hs_activate && + (hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL)) && + ((adapter->arp_filter_size > 0) && + (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) { + mwifiex_dbg(adapter, CMD, + "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n", + adapter->arp_filter_size); + memcpy(((u8 *) hs_cfg) + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), + adapter->arp_filter, adapter->arp_filter_size); + cmd->size = cpu_to_le16 + (adapter->arp_filter_size + + sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) + + S_DS_GEN); + } else { + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct + host_cmd_ds_802_11_hs_cfg_enh)); + } + if (hs_activate) { + hs_cfg->action = cpu_to_le16(HS_ACTIVATE); + hs_cfg->params.hs_activate.resp_ctrl = cpu_to_le16(RESP_NEEDED); + } else { + hs_cfg->action = cpu_to_le16(HS_CONFIGURE); + hs_cfg->params.hs_config.conditions = hscfg_param->conditions; + hs_cfg->params.hs_config.gpio = hscfg_param->gpio; + hs_cfg->params.hs_config.gap = hscfg_param->gap; + mwifiex_dbg(adapter, CMD, + "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", + hs_cfg->params.hs_config.conditions, + hs_cfg->params.hs_config.gpio, + hs_cfg->params.hs_config.gap); + } + + return 0; +} + +/* + * This function prepares command to set/get MAC address. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting MAC address (for SET only) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_802_11_mac_address(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action) +{ + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_MAC_ADDRESS); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.mac_addr.action = cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_SET) + memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr, + ETH_ALEN); + return 0; +} + +/* + * This function prepares command to set MAC multicast address. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting MAC multicast address + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd, + u16 cmd_action, + struct mwifiex_multicast_list *mcast_list) +{ + struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr; + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + + S_DS_GEN); + cmd->command = cpu_to_le16(HostCmd_CMD_MAC_MULTICAST_ADR); + + mcast_addr->action = cpu_to_le16(cmd_action); + mcast_addr->num_of_adrs = + cpu_to_le16((u16) mcast_list->num_multicast_addr); + memcpy(mcast_addr->mac_list, mcast_list->mac_list, + mcast_list->num_multicast_addr * ETH_ALEN); + + return 0; +} + +/* + * This function prepares command to deauthenticate. + * + * Preparation includes - + * - Setting command ID and proper size + * - Setting AP MAC address and reason code + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u8 *mac) +{ + struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_DEAUTHENTICATE); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate) + + S_DS_GEN); + + /* Set AP MAC address */ + memcpy(deauth->mac_addr, mac, ETH_ALEN); + + mwifiex_dbg(priv->adapter, CMD, "cmd: Deauth: %pM\n", deauth->mac_addr); + + deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); + + return 0; +} + +/* + * This function prepares command to stop Ad-Hoc network. + * + * Preparation includes - + * - Setting command ID and proper size + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_802_11_ad_hoc_stop(struct host_cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP); + cmd->size = cpu_to_le16(S_DS_GEN); + return 0; +} + +/* + * This function sets WEP key(s) to key parameter TLV(s). + * + * Multi-key parameter TLVs are supported, so we can send multiple + * WEP keys in a single buffer. + */ +static int +mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, + struct mwifiex_ie_type_key_param_set *key_param_set, + u16 *key_param_len) +{ + int cur_key_param_len; + u8 i; + + /* Multi-key_param_set TLV is supported */ + for (i = 0; i < NUM_WEP_KEYS; i++) { + if ((priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP40) || + (priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP104)) { + key_param_set->type = + cpu_to_le16(TLV_TYPE_KEY_MATERIAL); +/* Key_param_set WEP fixed length */ +#define KEYPARAMSET_WEP_FIXED_LEN 8 + key_param_set->length = cpu_to_le16((u16) + (priv->wep_key[i]. + key_length + + KEYPARAMSET_WEP_FIXED_LEN)); + key_param_set->key_type_id = + cpu_to_le16(KEY_TYPE_ID_WEP); + key_param_set->key_info = + cpu_to_le16(KEY_ENABLED | KEY_UNICAST | + KEY_MCAST); + key_param_set->key_len = + cpu_to_le16(priv->wep_key[i].key_length); + /* Set WEP key index */ + key_param_set->key[0] = i; + /* Set default Tx key flag */ + if (i == + (priv-> + wep_key_curr_index & HostCmd_WEP_KEY_INDEX_MASK)) + key_param_set->key[1] = 1; + else + key_param_set->key[1] = 0; + memmove(&key_param_set->key[2], + priv->wep_key[i].key_material, + priv->wep_key[i].key_length); + + cur_key_param_len = priv->wep_key[i].key_length + + KEYPARAMSET_WEP_FIXED_LEN + + sizeof(struct mwifiex_ie_types_header); + *key_param_len += (u16) cur_key_param_len; + key_param_set = + (struct mwifiex_ie_type_key_param_set *) + ((u8 *)key_param_set + + cur_key_param_len); + } else if (!priv->wep_key[i].key_length) { + continue; + } else { + mwifiex_dbg(priv->adapter, ERROR, + "key%d Length = %d is incorrect\n", + (i + 1), priv->wep_key[i].key_length); + return -1; + } + } + + return 0; +} + +/* This function populates key material v2 command + * to set network key for AES & CMAC AES. + */ +static int mwifiex_set_aes_key_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_encrypt_key *enc_key, + struct host_cmd_ds_802_11_key_material_v2 *km) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u16 size, len = KEY_PARAMS_FIXED_LEN; + + if (enc_key->is_igtk_key) { + mwifiex_dbg(adapter, INFO, + "%s: Set CMAC AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.cmac_aes.ipn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST); + km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK); + km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; + km->key_param_set.key_params.cmac_aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.cmac_aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct mwifiex_cmac_aes_param); + } else { + mwifiex_dbg(adapter, INFO, + "%s: Set AES Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.aes.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_AES; + km->key_param_set.key_params.aes.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.aes.key, + enc_key->key_material, enc_key->key_len); + len += sizeof(struct mwifiex_aes_param); + } + + km->key_param_set.len = cpu_to_le16(len); + size = len + sizeof(struct mwifiex_ie_types_header) + + sizeof(km->action) + S_DS_GEN; + cmd->size = cpu_to_le16(size); + + return 0; +} + +/* This function prepares command to set/get/reset network key(s). + * This function prepares key material command for V2 format. + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting WEP keys, WAPI keys or WPA keys along with required + * encryption (TKIP, AES) (as required) + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_key_material_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u8 *mac = enc_key->mac_addr; + u16 key_info, len = KEY_PARAMS_FIXED_LEN; + struct host_cmd_ds_802_11_key_material_v2 *km = + &cmd->params.key_material_v2; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + km->action = cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_GET) { + mwifiex_dbg(adapter, INFO, "%s: Get key\n", __func__); + km->key_param_set.key_idx = + enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) + key_info = KEY_UNICAST; + else + key_info = KEY_MCAST; + + if (enc_key->is_igtk_key) + key_info |= KEY_IGTK; + + km->key_param_set.key_info = cpu_to_le16(key_info); + + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + memset(&km->key_param_set, 0, + sizeof(struct mwifiex_ie_type_key_param_set_v2)); + + if (enc_key->key_disable) { + mwifiex_dbg(adapter, INFO, "%s: Remove key\n", __func__); + km->action = cpu_to_le16(HostCmd_ACT_GEN_REMOVE); + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + key_info = KEY_MCAST | KEY_UNICAST; + km->key_param_set.key_info = cpu_to_le16(key_info); + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN + KEY_PARAMS_FIXED_LEN + + sizeof(km->action)); + return 0; + } + + km->action = cpu_to_le16(HostCmd_ACT_GEN_SET); + km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; + km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); + key_info = KEY_ENABLED; + memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); + + if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) { + mwifiex_dbg(adapter, INFO, "%s: Set WEP Key\n", __func__); + len += sizeof(struct mwifiex_wep_param); + km->key_param_set.len = cpu_to_le16(len); + km->key_param_set.key_type = KEY_TYPE_ID_WEP; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + key_info |= KEY_MCAST | KEY_UNICAST; + } else { + if (enc_key->is_current_wep_key) { + key_info |= KEY_MCAST | KEY_UNICAST; + if (km->key_param_set.key_idx == + (priv->wep_key_curr_index & KEY_INDEX_MASK)) + key_info |= KEY_DEFAULT; + } else { + if (mac) { + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST; + else + key_info |= KEY_UNICAST | + KEY_DEFAULT; + } else { + key_info |= KEY_MCAST; + } + } + } + km->key_param_set.key_info = cpu_to_le16(key_info); + + km->key_param_set.key_params.wep.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.wep.key, + enc_key->key_material, enc_key->key_len); + + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + return 0; + } + + if (is_broadcast_ether_addr(mac)) + key_info |= KEY_MCAST | KEY_RX_KEY; + else + key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY; + + if (enc_key->is_wapi_key) { + mwifiex_dbg(adapter, INFO, "%s: Set WAPI Key\n", __func__); + km->key_param_set.key_type = KEY_TYPE_ID_WAPI; + memcpy(km->key_param_set.key_params.wapi.pn, enc_key->pn, + PN_LEN); + km->key_param_set.key_params.wapi.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.wapi.key, + enc_key->key_material, enc_key->key_len); + if (is_broadcast_ether_addr(mac)) + priv->sec_info.wapi_key_on = true; + + if (!priv->sec_info.wapi_key_on) + key_info |= KEY_DEFAULT; + km->key_param_set.key_info = cpu_to_le16(key_info); + + len += sizeof(struct mwifiex_wapi_param); + km->key_param_set.len = cpu_to_le16(len); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + return 0; + } + + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + key_info |= KEY_DEFAULT; + /* Enable unicast bit for WPA-NONE/ADHOC_AES */ + if (!priv->sec_info.wpa2_enabled && + !is_broadcast_ether_addr(mac)) + key_info |= KEY_UNICAST; + } else { + /* Enable default key for WPA/WPA2 */ + if (!priv->wpa_is_gtk_set) + key_info |= KEY_DEFAULT; + } + + km->key_param_set.key_info = cpu_to_le16(key_info); + + if (enc_key->key_len == WLAN_KEY_LEN_CCMP) + return mwifiex_set_aes_key_v2(priv, cmd, enc_key, km); + + if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { + mwifiex_dbg(adapter, INFO, + "%s: Set TKIP Key\n", __func__); + if (enc_key->is_rx_seq_valid) + memcpy(km->key_param_set.key_params.tkip.pn, + enc_key->pn, enc_key->pn_len); + km->key_param_set.key_type = KEY_TYPE_ID_TKIP; + km->key_param_set.key_params.tkip.key_len = + cpu_to_le16(enc_key->key_len); + memcpy(km->key_param_set.key_params.tkip.key, + enc_key->key_material, enc_key->key_len); + + len += sizeof(struct mwifiex_tkip_param); + km->key_param_set.len = cpu_to_le16(len); + cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + + len + sizeof(km->action) + S_DS_GEN); + } + + return 0; +} + +/* + * This function prepares command to set/get/reset network key(s). + * This function prepares key material command for V1 format. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting WEP keys, WAPI keys or WPA keys along with required + * encryption (TKIP, AES) (as required) + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_802_11_key_material_v1(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + struct host_cmd_ds_802_11_key_material *key_material = + &cmd->params.key_material; + struct host_cmd_tlv_mac_addr *tlv_mac; + u16 key_param_len = 0, cmd_size; + int ret = 0; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); + key_material->action = cpu_to_le16(cmd_action); + + if (cmd_action == HostCmd_ACT_GEN_GET) { + cmd->size = + cpu_to_le16(sizeof(key_material->action) + S_DS_GEN); + return ret; + } + + if (!enc_key) { + memset(&key_material->key_param_set, 0, + (NUM_WEP_KEYS * + sizeof(struct mwifiex_ie_type_key_param_set))); + ret = mwifiex_set_keyparamset_wep(priv, + &key_material->key_param_set, + &key_param_len); + cmd->size = cpu_to_le16(key_param_len + + sizeof(key_material->action) + S_DS_GEN); + return ret; + } else + memset(&key_material->key_param_set, 0, + sizeof(struct mwifiex_ie_type_key_param_set)); + if (enc_key->is_wapi_key) { + mwifiex_dbg(priv->adapter, INFO, "info: Set WAPI Key\n"); + key_material->key_param_set.key_type_id = + cpu_to_le16(KEY_TYPE_ID_WAPI); + if (cmd_oid == KEY_INFO_ENABLED) + key_material->key_param_set.key_info = + cpu_to_le16(KEY_ENABLED); + else + key_material->key_param_set.key_info = + cpu_to_le16(!KEY_ENABLED); + + key_material->key_param_set.key[0] = enc_key->key_index; + if (!priv->sec_info.wapi_key_on) + key_material->key_param_set.key[1] = 1; + else + /* set 0 when re-key */ + key_material->key_param_set.key[1] = 0; + + if (!is_broadcast_ether_addr(enc_key->mac_addr)) { + /* WAPI pairwise key: unicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_UNICAST); + } else { /* WAPI group key: multicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_MCAST); + priv->sec_info.wapi_key_on = true; + } + + key_material->key_param_set.type = + cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + key_material->key_param_set.key_len = + cpu_to_le16(WAPI_KEY_LEN); + memcpy(&key_material->key_param_set.key[2], + enc_key->key_material, enc_key->key_len); + memcpy(&key_material->key_param_set.key[2 + enc_key->key_len], + enc_key->pn, PN_LEN); + key_material->key_param_set.length = + cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN); + + key_param_len = (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) + + sizeof(struct mwifiex_ie_types_header); + cmd->size = cpu_to_le16(sizeof(key_material->action) + + S_DS_GEN + key_param_len); + return ret; + } + if (enc_key->key_len == WLAN_KEY_LEN_CCMP) { + if (enc_key->is_igtk_key) { + mwifiex_dbg(priv->adapter, CMD, "cmd: CMAC_AES\n"); + key_material->key_param_set.key_type_id = + cpu_to_le16(KEY_TYPE_ID_AES_CMAC); + if (cmd_oid == KEY_INFO_ENABLED) + key_material->key_param_set.key_info = + cpu_to_le16(KEY_ENABLED); + else + key_material->key_param_set.key_info = + cpu_to_le16(!KEY_ENABLED); + + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_IGTK); + } else { + mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_AES\n"); + key_material->key_param_set.key_type_id = + cpu_to_le16(KEY_TYPE_ID_AES); + if (cmd_oid == KEY_INFO_ENABLED) + key_material->key_param_set.key_info = + cpu_to_le16(KEY_ENABLED); + else + key_material->key_param_set.key_info = + cpu_to_le16(!KEY_ENABLED); + + if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) + /* AES pairwise key: unicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_UNICAST); + else /* AES group key: multicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_MCAST); + } + } else if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { + mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_TKIP\n"); + key_material->key_param_set.key_type_id = + cpu_to_le16(KEY_TYPE_ID_TKIP); + key_material->key_param_set.key_info = + cpu_to_le16(KEY_ENABLED); + + if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) + /* TKIP pairwise key: unicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_UNICAST); + else /* TKIP group key: multicast */ + key_material->key_param_set.key_info |= + cpu_to_le16(KEY_MCAST); + } + + if (key_material->key_param_set.key_type_id) { + key_material->key_param_set.type = + cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + key_material->key_param_set.key_len = + cpu_to_le16((u16) enc_key->key_len); + memcpy(key_material->key_param_set.key, enc_key->key_material, + enc_key->key_len); + key_material->key_param_set.length = + cpu_to_le16((u16) enc_key->key_len + + KEYPARAMSET_FIXED_LEN); + + key_param_len = (u16)(enc_key->key_len + KEYPARAMSET_FIXED_LEN) + + sizeof(struct mwifiex_ie_types_header); + + if (le16_to_cpu(key_material->key_param_set.key_type_id) == + KEY_TYPE_ID_AES_CMAC) { + struct mwifiex_cmac_param *param = + (void *)key_material->key_param_set.key; + + memcpy(param->ipn, enc_key->pn, IGTK_PN_LEN); + memcpy(param->key, enc_key->key_material, + WLAN_KEY_LEN_AES_CMAC); + + key_param_len = sizeof(struct mwifiex_cmac_param); + key_material->key_param_set.key_len = + cpu_to_le16(key_param_len); + key_param_len += KEYPARAMSET_FIXED_LEN; + key_material->key_param_set.length = + cpu_to_le16(key_param_len); + key_param_len += sizeof(struct mwifiex_ie_types_header); + } + + cmd->size = cpu_to_le16(sizeof(key_material->action) + S_DS_GEN + + key_param_len); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + tlv_mac = (void *)((u8 *)&key_material->key_param_set + + key_param_len); + tlv_mac->header.type = + cpu_to_le16(TLV_TYPE_STA_MAC_ADDR); + tlv_mac->header.len = cpu_to_le16(ETH_ALEN); + memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN); + cmd_size = key_param_len + S_DS_GEN + + sizeof(key_material->action) + + sizeof(struct host_cmd_tlv_mac_addr); + } else { + cmd_size = key_param_len + S_DS_GEN + + sizeof(key_material->action); + } + cmd->size = cpu_to_le16(cmd_size); + } + + return ret; +} + +/* Wrapper function for setting network key depending upon FW KEY API version */ +static int +mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, u32 cmd_oid, + struct mwifiex_ds_encrypt_key *enc_key) +{ + if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) + return mwifiex_cmd_802_11_key_material_v2(priv, cmd, + cmd_action, cmd_oid, + enc_key); + + else + return mwifiex_cmd_802_11_key_material_v1(priv, cmd, + cmd_action, cmd_oid, + enc_key); +} + +/* + * This function prepares command to set/get 11d domain information. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting domain information fields (for SET only) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_802_11d_domain_info(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11d_domain_info *domain_info = + &cmd->params.domain_info; + struct mwifiex_ietypes_domain_param_set *domain = + &domain_info->domain; + u8 no_of_triplet = adapter->domain_reg.no_of_triplet; + + mwifiex_dbg(adapter, INFO, + "info: 11D: no_of_triplet=0x%x\n", no_of_triplet); + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO); + domain_info->action = cpu_to_le16(cmd_action); + if (cmd_action == HostCmd_ACT_GEN_GET) { + cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); + return 0; + } + + /* Set domain info fields */ + domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY); + memcpy(domain->country_code, adapter->domain_reg.country_code, + sizeof(domain->country_code)); + + domain->header.len = + cpu_to_le16((no_of_triplet * + sizeof(struct ieee80211_country_ie_triplet)) + + sizeof(domain->country_code)); + + if (no_of_triplet) { + memcpy(domain->triplet, adapter->domain_reg.triplet, + no_of_triplet * sizeof(struct + ieee80211_country_ie_triplet)); + + cmd->size = cpu_to_le16(sizeof(domain_info->action) + + le16_to_cpu(domain->header.len) + + sizeof(struct mwifiex_ie_types_header) + + S_DS_GEN); + } else { + cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); + } + + return 0; +} + +/* + * This function prepares command to set/get IBSS coalescing status. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting status to enable or disable (for SET only) + * - Ensuring correct endian-ness + */ +static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd, + u16 cmd_action, u16 *enable) +{ + struct host_cmd_ds_802_11_ibss_status *ibss_coal = + &(cmd->params.ibss_coalescing); + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_ibss_status) + + S_DS_GEN); + cmd->result = 0; + ibss_coal->action = cpu_to_le16(cmd_action); + + switch (cmd_action) { + case HostCmd_ACT_GEN_SET: + if (enable) + ibss_coal->enable = cpu_to_le16(*enable); + else + ibss_coal->enable = 0; + break; + + /* In other case.. Nothing to do */ + case HostCmd_ACT_GEN_GET: + default: + break; + } + + return 0; +} + +/* This function prepares command buffer to get/set memory location value. + */ +static int +mwifiex_cmd_mem_access(struct host_cmd_ds_command *cmd, u16 cmd_action, + void *pdata_buf) +{ + struct mwifiex_ds_mem_rw *mem_rw = (void *)pdata_buf; + struct host_cmd_ds_mem_access *mem_access = (void *)&cmd->params.mem; + + cmd->command = cpu_to_le16(HostCmd_CMD_MEM_ACCESS); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mem_access) + + S_DS_GEN); + + mem_access->action = cpu_to_le16(cmd_action); + mem_access->addr = cpu_to_le32(mem_rw->addr); + mem_access->value = cpu_to_le32(mem_rw->value); + + return 0; +} + +/* + * This function prepares command to set/get register value. + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting register offset (for both GET and SET) and + * register value (for SET only) + * - Ensuring correct endian-ness + * + * The following type of registers can be accessed with this function - + * - MAC register + * - BBP register + * - RF register + * - PMIC register + * - CAU register + * - EEPROM + */ +static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct mwifiex_ds_reg_rw *reg_rw = data_buf; + + switch (le16_to_cpu(cmd->command)) { + case HostCmd_CMD_MAC_REG_ACCESS: + { + struct host_cmd_ds_mac_reg_access *mac_reg; + + cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); + mac_reg = &cmd->params.mac_reg; + mac_reg->action = cpu_to_le16(cmd_action); + mac_reg->offset = + cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + mac_reg->value = reg_rw->value; + break; + } + case HostCmd_CMD_BBP_REG_ACCESS: + { + struct host_cmd_ds_bbp_reg_access *bbp_reg; + + cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); + bbp_reg = &cmd->params.bbp_reg; + bbp_reg->action = cpu_to_le16(cmd_action); + bbp_reg->offset = + cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + bbp_reg->value = (u8) le32_to_cpu(reg_rw->value); + break; + } + case HostCmd_CMD_RF_REG_ACCESS: + { + struct host_cmd_ds_rf_reg_access *rf_reg; + + cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); + rf_reg = &cmd->params.rf_reg; + rf_reg->action = cpu_to_le16(cmd_action); + rf_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + rf_reg->value = (u8) le32_to_cpu(reg_rw->value); + break; + } + case HostCmd_CMD_PMIC_REG_ACCESS: + { + struct host_cmd_ds_pmic_reg_access *pmic_reg; + + cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); + pmic_reg = &cmd->params.pmic_reg; + pmic_reg->action = cpu_to_le16(cmd_action); + pmic_reg->offset = + cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + pmic_reg->value = (u8) le32_to_cpu(reg_rw->value); + break; + } + case HostCmd_CMD_CAU_REG_ACCESS: + { + struct host_cmd_ds_rf_reg_access *cau_reg; + + cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); + cau_reg = &cmd->params.rf_reg; + cau_reg->action = cpu_to_le16(cmd_action); + cau_reg->offset = + cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); + cau_reg->value = (u8) le32_to_cpu(reg_rw->value); + break; + } + case HostCmd_CMD_802_11_EEPROM_ACCESS: + { + struct mwifiex_ds_read_eeprom *rd_eeprom = data_buf; + struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = + &cmd->params.eeprom; + + cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); + cmd_eeprom->action = cpu_to_le16(cmd_action); + cmd_eeprom->offset = rd_eeprom->offset; + cmd_eeprom->byte_count = rd_eeprom->byte_count; + cmd_eeprom->value = 0; + break; + } + default: + return -1; + } + + return 0; +} + +/* + * This function prepares command to set PCI-Express + * host buffer configuration + * + * Preparation includes - + * - Setting command ID, action and proper size + * - Setting host buffer configuration + * - Ensuring correct endian-ness + */ +static int +mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u16 action) +{ + struct host_cmd_ds_pcie_details *host_spec = + &cmd->params.pcie_host_spec; + struct pcie_service_card *card = priv->adapter->card; + + cmd->command = cpu_to_le16(HostCmd_CMD_PCIE_DESC_DETAILS); + cmd->size = cpu_to_le16(sizeof(struct + host_cmd_ds_pcie_details) + S_DS_GEN); + cmd->result = 0; + + memset(host_spec, 0, sizeof(struct host_cmd_ds_pcie_details)); + + if (action != HostCmd_ACT_GEN_SET) + return 0; + + /* Send the ring base addresses and count to firmware */ + host_spec->txbd_addr_lo = (u32)(card->txbd_ring_pbase); + host_spec->txbd_addr_hi = (u32)(((u64)card->txbd_ring_pbase)>>32); + host_spec->txbd_count = MWIFIEX_MAX_TXRX_BD; + host_spec->rxbd_addr_lo = (u32)(card->rxbd_ring_pbase); + host_spec->rxbd_addr_hi = (u32)(((u64)card->rxbd_ring_pbase)>>32); + host_spec->rxbd_count = MWIFIEX_MAX_TXRX_BD; + host_spec->evtbd_addr_lo = (u32)(card->evtbd_ring_pbase); + host_spec->evtbd_addr_hi = (u32)(((u64)card->evtbd_ring_pbase)>>32); + host_spec->evtbd_count = MWIFIEX_MAX_EVT_BD; + if (card->sleep_cookie_vbase) { + host_spec->sleep_cookie_addr_lo = + (u32)(card->sleep_cookie_pbase); + host_spec->sleep_cookie_addr_hi = + (u32)(((u64)(card->sleep_cookie_pbase)) >> 32); + mwifiex_dbg(priv->adapter, INFO, + "sleep_cook_lo phy addr: 0x%x\n", + host_spec->sleep_cookie_addr_lo); + } + + return 0; +} + +/* + * This function prepares command for event subscription, configuration + * and query. Events can be subscribed or unsubscribed. Current subscribed + * events can be queried. Also, current subscribed events are reported in + * every FW response. + */ +static int +mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_misc_subsc_evt *subsc_evt_cfg) +{ + struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt; + struct mwifiex_ie_types_rssi_threshold *rssi_tlv; + u16 event_bitmap; + u8 *pos; + + cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) + + S_DS_GEN); + + subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action); + mwifiex_dbg(priv->adapter, CMD, + "cmd: action: %d\n", subsc_evt_cfg->action); + + /*For query requests, no configuration TLV structures are to be added.*/ + if (subsc_evt_cfg->action == HostCmd_ACT_GEN_GET) + return 0; + + subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events); + + event_bitmap = subsc_evt_cfg->events; + mwifiex_dbg(priv->adapter, CMD, "cmd: event bitmap : %16x\n", + event_bitmap); + + if (((subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) || + (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_SET)) && + (event_bitmap == 0)) { + mwifiex_dbg(priv->adapter, ERROR, + "Error: No event specified\t" + "for bitwise action type\n"); + return -EINVAL; + } + + /* + * Append TLV structures for each of the specified events for + * subscribing or re-configuring. This is not required for + * bitwise unsubscribing request. + */ + if (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) + return 0; + + pos = ((u8 *)subsc_evt) + + sizeof(struct host_cmd_ds_802_11_subsc_evt); + + if (event_bitmap & BITMASK_BCN_RSSI_LOW) { + rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; + + rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW); + rssi_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - + sizeof(struct mwifiex_ie_types_header)); + rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value; + rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq; + + mwifiex_dbg(priv->adapter, EVENT, + "Cfg Beacon Low Rssi event,\t" + "RSSI:-%d dBm, Freq:%d\n", + subsc_evt_cfg->bcn_l_rssi_cfg.abs_value, + subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq); + + pos += sizeof(struct mwifiex_ie_types_rssi_threshold); + le16_add_cpu(&cmd->size, + sizeof(struct mwifiex_ie_types_rssi_threshold)); + } + + if (event_bitmap & BITMASK_BCN_RSSI_HIGH) { + rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; + + rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_tlv->header.len = + cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - + sizeof(struct mwifiex_ie_types_header)); + rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value; + rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq; + + mwifiex_dbg(priv->adapter, EVENT, + "Cfg Beacon High Rssi event,\t" + "RSSI:-%d dBm, Freq:%d\n", + subsc_evt_cfg->bcn_h_rssi_cfg.abs_value, + subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq); + + pos += sizeof(struct mwifiex_ie_types_rssi_threshold); + le16_add_cpu(&cmd->size, + sizeof(struct mwifiex_ie_types_rssi_threshold)); + } + + return 0; +} + +static int +mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv, + struct mwifiex_mef_entry *mef_entry, + u8 **buffer) +{ + struct mwifiex_mef_filter *filter = mef_entry->filter; + int i, byte_len; + u8 *stack_ptr = *buffer; + + for (i = 0; i < MWIFIEX_MEF_MAX_FILTERS; i++) { + filter = &mef_entry->filter[i]; + if (!filter->filt_type) + break; + *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->repeat); + stack_ptr += 4; + *stack_ptr = TYPE_DNUM; + stack_ptr += 1; + + byte_len = filter->byte_seq[MWIFIEX_MEF_MAX_BYTESEQ]; + memcpy(stack_ptr, filter->byte_seq, byte_len); + stack_ptr += byte_len; + *stack_ptr = byte_len; + stack_ptr += 1; + *stack_ptr = TYPE_BYTESEQ; + stack_ptr += 1; + + *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->offset); + stack_ptr += 4; + *stack_ptr = TYPE_DNUM; + stack_ptr += 1; + + *stack_ptr = filter->filt_type; + stack_ptr += 1; + + if (filter->filt_action) { + *stack_ptr = filter->filt_action; + stack_ptr += 1; + } + + if (stack_ptr - *buffer > STACK_NBYTES) + return -1; + } + + *buffer = stack_ptr; + return 0; +} + +static int +mwifiex_cmd_mef_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + struct mwifiex_ds_mef_cfg *mef) +{ + struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg; + struct mwifiex_fw_mef_entry *mef_entry = NULL; + u8 *pos = (u8 *)mef_cfg; + u16 i; + + cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG); + + mef_cfg->criteria = cpu_to_le32(mef->criteria); + mef_cfg->num_entries = cpu_to_le16(mef->num_entries); + pos += sizeof(*mef_cfg); + + for (i = 0; i < mef->num_entries; i++) { + mef_entry = (struct mwifiex_fw_mef_entry *)pos; + mef_entry->mode = mef->mef_entry[i].mode; + mef_entry->action = mef->mef_entry[i].action; + pos += sizeof(*mef_cfg->mef_entry); + + if (mwifiex_cmd_append_rpn_expression(priv, + &mef->mef_entry[i], &pos)) + return -1; + + mef_entry->exprsize = + cpu_to_le16(pos - mef_entry->expr); + } + cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN); + + return 0; +} + +/* This function parse cal data from ASCII to hex */ +static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst) +{ + u8 *s = src, *d = dst; + + while (s - src < len) { + if (*s && (isspace(*s) || *s == '\t')) { + s++; + continue; + } + if (isxdigit(*s)) { + *d++ = simple_strtol(s, NULL, 16); + s += 2; + } else { + s++; + } + } + + return d - dst; +} + +int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, + struct device_node *node, const char *prefix) +{ +#ifdef CONFIG_OF + struct property *prop; + size_t len = strlen(prefix); + int ret; + + /* look for all matching property names */ + for_each_property_of_node(node, prop) { + if (len > strlen(prop->name) || + strncmp(prop->name, prefix, len)) + continue; + + /* property header is 6 bytes, data must fit in cmd buffer */ + if (prop && prop->value && prop->length > 6 && + prop->length <= MWIFIEX_SIZE_OF_CMD_BUFFER - S_DS_GEN) { + ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, + prop, true); + if (ret) + return ret; + } + } +#endif + return 0; +} + +/* This function prepares command of set_cfg_data. */ +static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, void *data_buf) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct property *prop = data_buf; + u32 len; + u8 *data = (u8 *)cmd + S_DS_GEN; + int ret; + + if (prop) { + len = prop->length; + ret = of_property_read_u8_array(adapter->dt_node, prop->name, + data, len); + if (ret) + return ret; + mwifiex_dbg(adapter, INFO, + "download cfg_data from device tree: %s\n", + prop->name); + } else if (adapter->cal_data->data && adapter->cal_data->size > 0) { + len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, + adapter->cal_data->size, data); + mwifiex_dbg(adapter, INFO, + "download cfg_data from config file\n"); + } else { + return -1; + } + + cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); + cmd->size = cpu_to_le16(S_DS_GEN + len); + + return 0; +} + +static int +mwifiex_cmd_set_mc_policy(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_multi_chan_policy *mc_pol = &cmd->params.mc_policy; + const u16 *drcs_info = data_buf; + + mc_pol->action = cpu_to_le16(cmd_action); + mc_pol->policy = cpu_to_le16(*drcs_info); + cmd->command = cpu_to_le16(HostCmd_CMD_MC_POLICY); + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_multi_chan_policy) + + S_DS_GEN); + return 0; +} + +static int mwifiex_cmd_robust_coex(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, bool *is_timeshare) +{ + struct host_cmd_ds_robust_coex *coex = &cmd->params.coex; + struct mwifiex_ie_types_robust_coex *coex_tlv; + + cmd->command = cpu_to_le16(HostCmd_CMD_ROBUST_COEX); + cmd->size = cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN); + + coex->action = cpu_to_le16(cmd_action); + coex_tlv = (struct mwifiex_ie_types_robust_coex *) + ((u8 *)coex + sizeof(*coex)); + coex_tlv->header.type = cpu_to_le16(TLV_TYPE_ROBUST_COEX); + coex_tlv->header.len = cpu_to_le16(sizeof(coex_tlv->mode)); + + if (coex->action == HostCmd_ACT_GEN_GET) + return 0; + + if (*is_timeshare) + coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_TIMESHARE); + else + coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_SPATIAL); + + return 0; +} + +static int +mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_coalesce_cfg *coalesce_cfg = + &cmd->params.coalesce_cfg; + struct mwifiex_ds_coalesce_cfg *cfg = data_buf; + struct coalesce_filt_field_param *param; + u16 cnt, idx, length; + struct coalesce_receive_filt_rule *rule; + + cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG); + cmd->size = cpu_to_le16(S_DS_GEN); + + coalesce_cfg->action = cpu_to_le16(cmd_action); + coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules); + rule = coalesce_cfg->rule; + + for (cnt = 0; cnt < cfg->num_of_rules; cnt++) { + rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE); + rule->max_coalescing_delay = + 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 = + cpu_to_le16(cfg->rule[cnt].params[idx].offset); + memcpy(param->operand_byte_stream, + cfg->rule[cnt].params[idx].operand_byte_stream, + param->operand_len); + + length += sizeof(struct coalesce_filt_field_param); + + param++; + } + + /* Total rule length is sizeof max_coalescing_delay(u16), + * num_of_fields(u8), pkt_type(u8) and total length of the all + * params + */ + rule->header.len = cpu_to_le16(length + sizeof(u16) + + sizeof(u8) + sizeof(u8)); + + /* Add the rule length to the command size*/ + le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) + + sizeof(struct mwifiex_ie_types_header)); + + rule = (void *)((u8 *)rule->params + length); + } + + /* Add sizeof action, num_of_rules to total command length */ + le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16)); + + return 0; +} + +static int +mwifiex_cmd_tdls_config(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_ds_tdls_config *tdls_config = &cmd->params.tdls_config; + struct mwifiex_tdls_init_cs_params *config; + struct mwifiex_tdls_config *init_config; + u16 len; + + cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_CONFIG); + cmd->size = cpu_to_le16(S_DS_GEN); + tdls_config->tdls_action = cpu_to_le16(cmd_action); + le16_add_cpu(&cmd->size, sizeof(tdls_config->tdls_action)); + + switch (cmd_action) { + case ACT_TDLS_CS_ENABLE_CONFIG: + init_config = data_buf; + len = sizeof(*init_config); + memcpy(tdls_config->tdls_data, init_config, len); + break; + case ACT_TDLS_CS_INIT: + config = data_buf; + len = sizeof(*config); + memcpy(tdls_config->tdls_data, config, len); + break; + case ACT_TDLS_CS_STOP: + len = sizeof(struct mwifiex_tdls_stop_cs_params); + memcpy(tdls_config->tdls_data, data_buf, len); + break; + case ACT_TDLS_CS_PARAMS: + len = sizeof(struct mwifiex_tdls_config_cs_params); + memcpy(tdls_config->tdls_data, data_buf, len); + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "Unknown TDLS configuration\n"); + return -ENOTSUPP; + } + + le16_add_cpu(&cmd->size, len); + return 0; +} + +static int +mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, + void *data_buf) +{ + struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper; + struct mwifiex_ds_tdls_oper *oper = data_buf; + struct mwifiex_sta_node *sta_ptr; + struct host_cmd_tlv_rates *tlv_rates; + struct mwifiex_ie_types_htcap *ht_capab; + struct mwifiex_ie_types_qos_info *wmm_qos_info; + struct mwifiex_ie_types_extcap *extcap; + struct mwifiex_ie_types_vhtcap *vht_capab; + struct mwifiex_ie_types_aid *aid; + struct mwifiex_ie_types_tdls_idle_timeout *timeout; + u8 *pos, qos_info; + u16 config_len = 0; + struct station_parameters *params = priv->sta_params; + + cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER); + cmd->size = cpu_to_le16(S_DS_GEN); + le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper)); + + tdls_oper->reason = 0; + memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN); + sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac); + + pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper); + + switch (oper->tdls_action) { + case MWIFIEX_TDLS_DISABLE_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE); + break; + case MWIFIEX_TDLS_CREATE_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE); + break; + case MWIFIEX_TDLS_CONFIG_LINK: + tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CONFIG); + + if (!params) { + mwifiex_dbg(priv->adapter, ERROR, + "TDLS config params not available for %pM\n", + oper->peer_mac); + return -ENODATA; + } + + *(__le16 *)pos = cpu_to_le16(params->capability); + config_len += sizeof(params->capability); + + qos_info = params->uapsd_queues | (params->max_sp << 5); + wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos + + config_len); + wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA); + wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info)); + wmm_qos_info->qos_info = qos_info; + config_len += sizeof(struct mwifiex_ie_types_qos_info); + + if (params->ht_capa) { + ht_capab = (struct mwifiex_ie_types_htcap *)(pos + + config_len); + ht_capab->header.type = + cpu_to_le16(WLAN_EID_HT_CAPABILITY); + ht_capab->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + memcpy(&ht_capab->ht_cap, params->ht_capa, + sizeof(struct ieee80211_ht_cap)); + config_len += sizeof(struct mwifiex_ie_types_htcap); + } + + if (params->supported_rates && params->supported_rates_len) { + tlv_rates = (struct host_cmd_tlv_rates *)(pos + + config_len); + tlv_rates->header.type = + cpu_to_le16(WLAN_EID_SUPP_RATES); + tlv_rates->header.len = + cpu_to_le16(params->supported_rates_len); + memcpy(tlv_rates->rates, params->supported_rates, + params->supported_rates_len); + config_len += sizeof(struct host_cmd_tlv_rates) + + params->supported_rates_len; + } + + if (params->ext_capab && params->ext_capab_len) { + extcap = (struct mwifiex_ie_types_extcap *)(pos + + config_len); + extcap->header.type = + cpu_to_le16(WLAN_EID_EXT_CAPABILITY); + extcap->header.len = cpu_to_le16(params->ext_capab_len); + memcpy(extcap->ext_capab, params->ext_capab, + params->ext_capab_len); + config_len += sizeof(struct mwifiex_ie_types_extcap) + + params->ext_capab_len; + } + if (params->vht_capa) { + vht_capab = (struct mwifiex_ie_types_vhtcap *)(pos + + config_len); + vht_capab->header.type = + cpu_to_le16(WLAN_EID_VHT_CAPABILITY); + vht_capab->header.len = + cpu_to_le16(sizeof(struct ieee80211_vht_cap)); + memcpy(&vht_capab->vht_cap, params->vht_capa, + sizeof(struct ieee80211_vht_cap)); + config_len += sizeof(struct mwifiex_ie_types_vhtcap); + } + if (params->aid) { + aid = (struct mwifiex_ie_types_aid *)(pos + config_len); + aid->header.type = cpu_to_le16(WLAN_EID_AID); + aid->header.len = cpu_to_le16(sizeof(params->aid)); + aid->aid = cpu_to_le16(params->aid); + config_len += sizeof(struct mwifiex_ie_types_aid); + } + + timeout = (void *)(pos + config_len); + timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT); + timeout->header.len = cpu_to_le16(sizeof(timeout->value)); + timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC); + config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout); + + break; + default: + mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS operation\n"); + return -ENOTSUPP; + } + + le16_add_cpu(&cmd->size, config_len); + + return 0; +} + +/* This function prepares command of sdio rx aggr info. */ +static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd, + u16 cmd_action, void *data_buf) +{ + struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = + &cmd->params.sdio_rx_aggr_cfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG); + cmd->size = + cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) + + S_DS_GEN); + cfg->action = cmd_action; + if (cmd_action == HostCmd_ACT_GEN_SET) + cfg->enable = *(u8 *)data_buf; + + return 0; +} + +/* + * This function prepares the commands before sending them to the firmware. + * + * This is a generic function which calls specific command preparation + * routines based upon the command number. + */ +int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, + u16 cmd_action, u32 cmd_oid, + void *data_buf, void *cmd_buf) +{ + struct host_cmd_ds_command *cmd_ptr = cmd_buf; + int ret = 0; + + /* Prepare command */ + switch (cmd_no) { + case HostCmd_CMD_GET_HW_SPEC: + ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); + break; + case HostCmd_CMD_CFG_DATA: + ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_MAC_CONTROL: + ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_802_11_MAC_ADDRESS: + ret = mwifiex_cmd_802_11_mac_address(priv, cmd_ptr, + cmd_action); + break; + case HostCmd_CMD_MAC_MULTICAST_ADR: + ret = mwifiex_cmd_mac_multicast_adr(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = mwifiex_cmd_tx_rate_cfg(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_TXPWR_CFG: + ret = mwifiex_cmd_tx_power_cfg(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_RF_TX_PWR: + ret = mwifiex_cmd_rf_tx_power(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_RF_ANTENNA: + ret = mwifiex_cmd_rf_antenna(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = mwifiex_cmd_enh_power_mode(priv, cmd_ptr, cmd_action, + (uint16_t)cmd_oid, data_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = mwifiex_cmd_802_11_hs_cfg(priv, cmd_ptr, cmd_action, + (struct mwifiex_hs_config_param *) data_buf); + break; + case HostCmd_CMD_802_11_SCAN: + ret = mwifiex_cmd_802_11_scan(cmd_ptr, data_buf); + break; + case HostCmd_CMD_802_11_BG_SCAN_QUERY: + ret = mwifiex_cmd_802_11_bg_scan_query(cmd_ptr); + break; + case HostCmd_CMD_802_11_ASSOCIATE: + ret = mwifiex_cmd_802_11_associate(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_802_11_DEAUTHENTICATE: + ret = mwifiex_cmd_802_11_deauthenticate(priv, cmd_ptr, + data_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_START: + ret = mwifiex_cmd_802_11_ad_hoc_start(priv, cmd_ptr, + data_buf); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = mwifiex_cmd_802_11_get_log(cmd_ptr); + break; + case HostCmd_CMD_802_11_AD_HOC_JOIN: + ret = mwifiex_cmd_802_11_ad_hoc_join(priv, cmd_ptr, + data_buf); + break; + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = mwifiex_cmd_802_11_ad_hoc_stop(cmd_ptr); + break; + case HostCmd_CMD_RSSI_INFO: + ret = mwifiex_cmd_802_11_rssi_info(priv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = mwifiex_cmd_802_11_snmp_mib(priv, cmd_ptr, cmd_action, + cmd_oid, data_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + cmd_ptr->command = + cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + + S_DS_GEN); + priv->tx_rate = 0; + ret = 0; + break; + case HostCmd_CMD_VERSION_EXT: + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->params.verext.version_str_sel = + (u8) (*((u32 *) data_buf)); + memcpy(&cmd_ptr->params, data_buf, + sizeof(struct host_cmd_ds_version_ext)); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) + + S_DS_GEN); + ret = 0; + break; + case HostCmd_CMD_MGMT_FRAME_REG: + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->params.reg_mask.action = cpu_to_le16(cmd_action); + cmd_ptr->params.reg_mask.mask = cpu_to_le32(*(u32 *)data_buf); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) + + S_DS_GEN); + ret = 0; + break; + case HostCmd_CMD_REMAIN_ON_CHAN: + cmd_ptr->command = cpu_to_le16(cmd_no); + memcpy(&cmd_ptr->params, data_buf, + sizeof(struct host_cmd_ds_remain_on_chan)); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) + + S_DS_GEN); + break; + case HostCmd_CMD_11AC_CFG: + ret = mwifiex_cmd_11ac_cfg(priv, cmd_ptr, cmd_action, data_buf); + break; + case HostCmd_CMD_P2P_MODE_CFG: + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action); + cmd_ptr->params.mode_cfg.mode = cpu_to_le16(*(u16 *)data_buf); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_p2p_mode_cfg) + + S_DS_GEN); + break; + case HostCmd_CMD_FUNC_INIT: + if (priv->adapter->hw_status == MWIFIEX_HW_STATUS_RESET) + priv->adapter->hw_status = MWIFIEX_HW_STATUS_READY; + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->size = cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_FUNC_SHUTDOWN: + priv->adapter->hw_status = MWIFIEX_HW_STATUS_RESET; + cmd_ptr->command = cpu_to_le16(cmd_no); + cmd_ptr->size = cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = mwifiex_cmd_11n_addba_req(cmd_ptr, data_buf); + break; + case HostCmd_CMD_11N_DELBA: + ret = mwifiex_cmd_11n_delba(cmd_ptr, data_buf); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = mwifiex_cmd_11n_addba_rsp_gen(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + ret = mwifiex_cmd_802_11_key_material(priv, cmd_ptr, + cmd_action, cmd_oid, + data_buf); + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = mwifiex_cmd_802_11d_domain_info(priv, cmd_ptr, + cmd_action); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + ret = mwifiex_cmd_recfg_tx_buf(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + ret = mwifiex_cmd_amsdu_aggr_ctrl(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_11N_CFG: + ret = mwifiex_cmd_11n_cfg(priv, cmd_ptr, cmd_action, data_buf); + break; + case HostCmd_CMD_WMM_GET_STATUS: + mwifiex_dbg(priv->adapter, CMD, + "cmd: WMM: WMM_GET_STATUS cmd sent\n"); + cmd_ptr->command = cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS); + cmd_ptr->size = + cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) + + S_DS_GEN); + ret = 0; + break; + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = mwifiex_cmd_802_11_scan_ext(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = mwifiex_cmd_mem_access(cmd_ptr, cmd_action, data_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_PMIC_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + ret = mwifiex_cmd_reg_access(cmd_ptr, cmd_action, data_buf); + break; + case HostCmd_CMD_SET_BSS_MODE: + cmd_ptr->command = cpu_to_le16(cmd_no); + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) + cmd_ptr->params.bss_mode.con_type = + CONNECTION_TYPE_ADHOC; + else if (priv->bss_mode == NL80211_IFTYPE_STATION || + priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) + cmd_ptr->params.bss_mode.con_type = + CONNECTION_TYPE_INFRA; + else if (priv->bss_mode == NL80211_IFTYPE_AP || + priv->bss_mode == NL80211_IFTYPE_P2P_GO) + cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_AP; + cmd_ptr->size = cpu_to_le16(sizeof(struct + host_cmd_ds_set_bss_mode) + S_DS_GEN); + ret = 0; + break; + case HostCmd_CMD_PCIE_DESC_DETAILS: + ret = mwifiex_cmd_pcie_host_spec(priv, cmd_ptr, cmd_action); + break; + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_MEF_CFG: + ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_COALESCE_CFG: + ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_TDLS_OPER: + ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf); + break; + case HostCmd_CMD_TDLS_CONFIG: + ret = mwifiex_cmd_tdls_config(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr, + data_buf); + break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_MC_POLICY: + ret = mwifiex_cmd_set_mc_policy(priv, cmd_ptr, cmd_action, + data_buf); + break; + case HostCmd_CMD_ROBUST_COEX: + ret = mwifiex_cmd_robust_coex(priv, cmd_ptr, cmd_action, + data_buf); + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "PREP_CMD: unknown cmd- %#x\n", cmd_no); + ret = -1; + break; + } + return ret; +} + +/* + * This function issues commands to initialize firmware. + * + * This is called after firmware download to bring the card to + * working state. + * Function is also called during reinitialization of virtual + * interfaces. + * + * The following commands are issued sequentially - + * - Set PCI-Express host buffer configuration (PCIE only) + * - Function init (for first interface only) + * - Read MAC address (for first interface only) + * - Reconfigure Tx buffer size (for first interface only) + * - Enable auto deep sleep (for first interface only) + * - Get Tx rate + * - Get Tx power + * - Set IBSS coalescing status + * - Set AMSDU aggregation control + * - Set 11d control + * - Set MAC control (this must be the last command to initialize firmware) + */ +int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + u16 enable = true; + struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; + struct mwifiex_ds_auto_ds auto_ds; + enum state_11d_t state_11d; + struct mwifiex_ds_11n_tx_cfg tx_cfg; + u8 sdio_sp_rx_aggr_enable; + + if (first_sta) { + if (priv->adapter->iface_type == MWIFIEX_PCIE) { + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_PCIE_DESC_DETAILS, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + if (ret) + return -1; + } + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_FUNC_INIT, + HostCmd_ACT_GEN_SET, 0, NULL, true); + if (ret) + return -1; + + /* Download calibration data to firmware. + * The cal-data can be read from device tree and/or + * a configuration file and downloaded to firmware. + */ + adapter->dt_node = + of_find_node_by_name(NULL, "marvell_cfgdata"); + if (adapter->dt_node) { + ret = mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node, + "marvell,caldata"); + if (ret) + return -1; + } + + if (adapter->cal_data) { + ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, + HostCmd_ACT_GEN_SET, 0, NULL, + true); + if (ret) + return -1; + } + + /* Read MAC address from HW */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC, + HostCmd_ACT_GEN_GET, 0, NULL, true); + if (ret) + return -1; + + /** Set SDIO Single Port RX Aggr Info */ + if (priv->adapter->iface_type == MWIFIEX_SDIO && + ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) && + !priv->adapter->host_disable_sdio_rx_aggr) { + sdio_sp_rx_aggr_enable = true; + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_SDIO_SP_RX_AGGR_CFG, + HostCmd_ACT_GEN_SET, 0, + &sdio_sp_rx_aggr_enable, + true); + if (ret) { + mwifiex_dbg(priv->adapter, ERROR, + "error while enabling SP aggregation..disable it"); + adapter->sdio_rx_aggr_enable = false; + } + } + + /* Reconfigure tx buf size */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, + HostCmd_ACT_GEN_SET, 0, + &priv->adapter->tx_buf_size, true); + if (ret) + return -1; + + if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Enable IEEE PS by default */ + priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_STA_PS, NULL, + true); + if (ret) + return -1; + } + + if (drcs) { + adapter->drcs_enabled = true; + if (ISSUPP_DRCS_ENABLED(adapter->fw_cap_info)) + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_MC_POLICY, + HostCmd_ACT_GEN_SET, 0, + &adapter->drcs_enabled, + true); + if (ret) + return -1; + } + } + + /* get tx rate */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, + HostCmd_ACT_GEN_GET, 0, NULL, true); + if (ret) + return -1; + priv->data_rate = 0; + + /* get tx power */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_RF_TX_PWR, + HostCmd_ACT_GEN_GET, 0, NULL, true); + if (ret) + return -1; + + if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) { + /* set ibss coalescing_status */ + ret = mwifiex_send_cmd( + priv, + HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + HostCmd_ACT_GEN_SET, 0, &enable, true); + if (ret) + return -1; + } + + memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); + amsdu_aggr_ctrl.enable = true; + /* Send request to firmware */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, + HostCmd_ACT_GEN_SET, 0, + &amsdu_aggr_ctrl, true); + if (ret) + return -1; + /* MAC Control must be the last command in init_fw */ + /* set MAC Control */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true); + if (ret) + return -1; + + if (!disable_auto_ds && + first_sta && priv->adapter->iface_type != MWIFIEX_USB && + priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Enable auto deep sleep */ + auto_ds.auto_ds = DEEP_SLEEP_ON; + auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + EN_AUTO_PS, BITMAP_AUTO_DS, + &auto_ds, true); + if (ret) + return -1; + } + + if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { + /* Send cmd to FW to enable/disable 11D function */ + state_11d = ENABLE_11D; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11D_I, + &state_11d, true); + if (ret) + mwifiex_dbg(priv->adapter, ERROR, + "11D: failed to enable 11D\n"); + } + + /* Send cmd to FW to configure 11n specific configuration + * (Short GI, Channel BW, Green field support etc.) for transmit + */ + tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG, + HostCmd_ACT_GEN_SET, 0, &tx_cfg, true); + + if (init) { + /* set last_init_cmd before sending the command */ + priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; + ret = -EINPROGRESS; + } + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c new file mode 100644 index 000000000000..9ac7aa2431b4 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c @@ -0,0 +1,1249 @@ +/* + * Marvell Wireless LAN device driver: station command response handling + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "11ac.h" + + +/* + * This function handles the command response error case. + * + * For scan response error, the function cancels all the pending + * scan commands and generates an event to inform the applications + * of the scan completion. + * + * For Power Save command failure, we do not retry enter PS + * command in case of Ad-hoc mode. + * + * For all other response errors, the current command buffer is freed + * and returned to the free command queue. + */ +static void +mwifiex_process_cmdresp_error(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_802_11_ps_mode_enh *pm; + unsigned long flags; + + mwifiex_dbg(adapter, ERROR, + "CMD_RESP: cmd %#x error, result=%#x\n", + resp->command, resp->result); + + if (adapter->curr_cmd->wait_q_enabled) + adapter->cmd_wait_q.status = -1; + + switch (le16_to_cpu(resp->command)) { + case HostCmd_CMD_802_11_PS_MODE_ENH: + pm = &resp->params.psmode_enh; + mwifiex_dbg(adapter, ERROR, + "PS_MODE_ENH cmd failed: result=0x%x action=0x%X\n", + resp->result, le16_to_cpu(pm->action)); + /* We do not re-try enter-ps command in ad-hoc mode. */ + if (le16_to_cpu(pm->action) == EN_AUTO_PS && + (le16_to_cpu(pm->params.ps_bitmap) & BITMAP_STA_PS) && + priv->bss_mode == NL80211_IFTYPE_ADHOC) + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; + + break; + case HostCmd_CMD_802_11_SCAN: + case HostCmd_CMD_802_11_SCAN_EXT: + /* Cancel all pending scan command */ + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + list_for_each_entry_safe(cmd_node, tmp_node, + &adapter->scan_pending_q, list) { + list_del(&cmd_node->list); + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, + flags); + mwifiex_insert_cmd_to_free_q(adapter, cmd_node); + spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + } + spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->scan_processing = false; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); + break; + + case HostCmd_CMD_MAC_CONTROL: + break; + + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + mwifiex_dbg(adapter, MSG, + "SDIO RX single-port aggregation Not support\n"); + break; + + default: + break; + } + /* Handling errors here */ + mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); + + spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); + adapter->curr_cmd = NULL; + spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); +} + +/* + * This function handles the command response of get RSSI info. + * + * Handling includes changing the header fields into CPU format + * and saving the following parameters in driver - + * - Last data and beacon RSSI value + * - Average data and beacon RSSI value + * - Last data and beacon NF value + * - Average data and beacon NF value + * + * The parameters are send to the application as well, along with + * calculated SNR values. + */ +static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = + &resp->params.rssi_info_rsp; + struct mwifiex_ds_misc_subsc_evt *subsc_evt = + &priv->async_subsc_evt_storage; + + priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); + priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); + + priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg); + priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg); + + priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last); + priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last); + + priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); + priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg); + + if (priv->subsc_evt_rssi_state == EVENT_HANDLED) + return 0; + + memset(subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); + + /* Resubscribe low and high rssi events with new thresholds */ + subsc_evt->events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; + subsc_evt->action = HostCmd_ACT_BITWISE_SET; + if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) { + subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg - + priv->cqm_rssi_hyst); + subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); + } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) { + subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); + subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg + + priv->cqm_rssi_hyst); + } + subsc_evt->bcn_l_rssi_cfg.evt_freq = 1; + subsc_evt->bcn_h_rssi_cfg.evt_freq = 1; + + priv->subsc_evt_rssi_state = EVENT_HANDLED; + + mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, + 0, 0, subsc_evt, false); + + return 0; +} + +/* + * This function handles the command response of set/get SNMP + * MIB parameters. + * + * Handling includes changing the header fields into CPU format + * and saving the parameter in driver. + * + * The following parameters are supported - + * - Fragmentation threshold + * - RTS threshold + * - Short retry limit + */ +static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + u32 *data_buf) +{ + struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; + u16 oid = le16_to_cpu(smib->oid); + u16 query_type = le16_to_cpu(smib->query_type); + u32 ul_temp; + + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: oid value = %#x,\t" + "query_type = %#x, buf size = %#x\n", + oid, query_type, le16_to_cpu(smib->buf_size)); + if (query_type == HostCmd_ACT_GEN_GET) { + ul_temp = le16_to_cpu(*((__le16 *) (smib->value))); + if (data_buf) + *data_buf = ul_temp; + switch (oid) { + case FRAG_THRESH_I: + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: FragThsd =%u\n", + ul_temp); + break; + case RTS_THRESH_I: + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: RTSThsd =%u\n", + ul_temp); + break; + case SHORT_RETRY_LIM_I: + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: TxRetryCount=%u\n", + ul_temp); + break; + case DTIM_PERIOD_I: + mwifiex_dbg(priv->adapter, INFO, + "info: SNMP_RESP: DTIM period=%u\n", + ul_temp); + default: + break; + } + } + + return 0; +} + +/* + * This function handles the command response of get log request + * + * Handling includes changing the header fields into CPU format + * and sending the received parameters to application. + */ +static int mwifiex_ret_get_log(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct mwifiex_ds_get_stats *stats) +{ + struct host_cmd_ds_802_11_get_log *get_log = + &resp->params.get_log; + + if (stats) { + stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); + stats->failed = le32_to_cpu(get_log->failed); + stats->retry = le32_to_cpu(get_log->retry); + stats->multi_retry = le32_to_cpu(get_log->multi_retry); + stats->frame_dup = le32_to_cpu(get_log->frame_dup); + stats->rts_success = le32_to_cpu(get_log->rts_success); + stats->rts_failure = le32_to_cpu(get_log->rts_failure); + stats->ack_failure = le32_to_cpu(get_log->ack_failure); + stats->rx_frag = le32_to_cpu(get_log->rx_frag); + stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame); + stats->fcs_error = le32_to_cpu(get_log->fcs_error); + stats->tx_frame = le32_to_cpu(get_log->tx_frame); + stats->wep_icv_error[0] = + le32_to_cpu(get_log->wep_icv_err_cnt[0]); + stats->wep_icv_error[1] = + le32_to_cpu(get_log->wep_icv_err_cnt[1]); + stats->wep_icv_error[2] = + le32_to_cpu(get_log->wep_icv_err_cnt[2]); + stats->wep_icv_error[3] = + le32_to_cpu(get_log->wep_icv_err_cnt[3]); + stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt); + stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt); + } + + return 0; +} + +/* + * This function handles the command response of set/get Tx rate + * configurations. + * + * Handling includes changing the header fields into CPU format + * and saving the following parameters in driver - + * - DSSS rate bitmap + * - OFDM rate bitmap + * - HT MCS rate bitmaps + * + * Based on the new rate bitmaps, the function re-evaluates if + * auto data rate has been activated. If not, it sends another + * query to the firmware to get the current Tx data rate. + */ +static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; + struct mwifiex_rate_scope *rate_scope; + struct mwifiex_ie_types_header *head; + u16 tlv, tlv_buf_len, tlv_buf_left; + u8 *tlv_buf; + u32 i; + + tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); + tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg); + + while (tlv_buf_left >= sizeof(*head)) { + head = (struct mwifiex_ie_types_header *)tlv_buf; + tlv = le16_to_cpu(head->type); + tlv_buf_len = le16_to_cpu(head->len); + + if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) + break; + + switch (tlv) { + case TLV_TYPE_RATE_SCOPE: + rate_scope = (struct mwifiex_rate_scope *) tlv_buf; + priv->bitmap_rates[0] = + le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); + priv->bitmap_rates[1] = + le16_to_cpu(rate_scope->ofdm_rate_bitmap); + for (i = 0; + i < + sizeof(rate_scope->ht_mcs_rate_bitmap) / + sizeof(u16); i++) + priv->bitmap_rates[2 + i] = + le16_to_cpu(rate_scope-> + ht_mcs_rate_bitmap[i]); + + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; i < ARRAY_SIZE(rate_scope-> + vht_mcs_rate_bitmap); + i++) + priv->bitmap_rates[10 + i] = + le16_to_cpu(rate_scope-> + vht_mcs_rate_bitmap[i]); + } + break; + /* Add RATE_DROP tlv here */ + } + + tlv_buf += (sizeof(*head) + tlv_buf_len); + tlv_buf_left -= (sizeof(*head) + tlv_buf_len); + } + + priv->is_data_rate_auto = mwifiex_is_rate_auto(priv); + + if (priv->is_data_rate_auto) + priv->data_rate = 0; + else + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL, false); + + return 0; +} + +/* + * This function handles the command response of get Tx power level. + * + * Handling includes saving the maximum and minimum Tx power levels + * in driver, as well as sending the values to user. + */ +static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf) +{ + int length, max_power = -1, min_power = -1; + struct mwifiex_types_power_group *pg_tlv_hdr; + struct mwifiex_power_group *pg; + + if (!data_buf) + return -1; + + pg_tlv_hdr = (struct mwifiex_types_power_group *)((u8 *)data_buf); + pg = (struct mwifiex_power_group *) + ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group)); + length = le16_to_cpu(pg_tlv_hdr->length); + + /* At least one structure required to update power */ + if (length < sizeof(struct mwifiex_power_group)) + return 0; + + max_power = pg->power_max; + min_power = pg->power_min; + length -= sizeof(struct mwifiex_power_group); + + while (length >= sizeof(struct mwifiex_power_group)) { + 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(struct mwifiex_power_group); + } + priv->min_tx_power_level = (u8) min_power; + priv->max_tx_power_level = (u8) max_power; + + return 0; +} + +/* + * This function handles the command response of set/get Tx power + * configurations. + * + * Handling includes changing the header fields into CPU format + * and saving the current Tx power level in driver. + */ +static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; + struct mwifiex_types_power_group *pg_tlv_hdr; + struct mwifiex_power_group *pg; + u16 action = le16_to_cpu(txp_cfg->action); + u16 tlv_buf_left; + + pg_tlv_hdr = (struct mwifiex_types_power_group *) + ((u8 *)txp_cfg + + sizeof(struct host_cmd_ds_txpwr_cfg)); + + pg = (struct mwifiex_power_group *) + ((u8 *)pg_tlv_hdr + + sizeof(struct mwifiex_types_power_group)); + + tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*txp_cfg); + if (tlv_buf_left < + le16_to_cpu(pg_tlv_hdr->length) + sizeof(*pg_tlv_hdr)) + return 0; + + switch (action) { + case HostCmd_ACT_GEN_GET: + if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) + mwifiex_get_power_level(priv, pg_tlv_hdr); + + priv->tx_power_level = (u16) pg->power_min; + break; + + case HostCmd_ACT_GEN_SET: + if (!le32_to_cpu(txp_cfg->mode)) + break; + + if (pg->power_max == pg->power_min) + priv->tx_power_level = (u16) pg->power_min; + break; + default: + mwifiex_dbg(adapter, ERROR, + "CMD_RESP: unknown cmd action %d\n", + action); + return 0; + } + mwifiex_dbg(adapter, INFO, + "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", + priv->tx_power_level, priv->max_tx_power_level, + priv->min_tx_power_level); + + return 0; +} + +/* + * This function handles the command response of get RF Tx power. + */ +static int mwifiex_ret_rf_tx_power(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_rf_tx_pwr *txp = &resp->params.txp; + u16 action = le16_to_cpu(txp->action); + + priv->tx_power_level = le16_to_cpu(txp->cur_level); + + if (action == HostCmd_ACT_GEN_GET) { + priv->max_tx_power_level = txp->max_power; + priv->min_tx_power_level = txp->min_power; + } + + mwifiex_dbg(priv->adapter, INFO, + "Current TxPower Level=%d, Max Power=%d, Min Power=%d\n", + priv->tx_power_level, priv->max_tx_power_level, + priv->min_tx_power_level); + + return 0; +} + +/* + * This function handles the command response of set rf antenna + */ +static int mwifiex_ret_rf_antenna(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_rf_ant_mimo *ant_mimo = &resp->params.ant_mimo; + struct host_cmd_ds_rf_ant_siso *ant_siso = &resp->params.ant_siso; + struct mwifiex_adapter *adapter = priv->adapter; + + if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) + mwifiex_dbg(adapter, INFO, + "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x\t" + "Rx action = 0x%x, Rx Mode = 0x%04x\n", + le16_to_cpu(ant_mimo->action_tx), + le16_to_cpu(ant_mimo->tx_ant_mode), + le16_to_cpu(ant_mimo->action_rx), + le16_to_cpu(ant_mimo->rx_ant_mode)); + else + mwifiex_dbg(adapter, INFO, + "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x\n", + le16_to_cpu(ant_siso->action), + le16_to_cpu(ant_siso->ant_mode)); + + return 0; +} + +/* + * This function handles the command response of set/get MAC address. + * + * Handling includes saving the MAC address in driver. + */ +static int mwifiex_ret_802_11_mac_address(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_mac_address *cmd_mac_addr = + &resp->params.mac_addr; + + memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN); + + mwifiex_dbg(priv->adapter, INFO, + "info: set mac address: %pM\n", priv->curr_addr); + + return 0; +} + +/* + * This function handles the command response of set/get MAC multicast + * address. + */ +static int mwifiex_ret_mac_multicast_adr(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + return 0; +} + +/* + * This function handles the command response of get Tx rate query. + * + * Handling includes changing the header fields into CPU format + * and saving the Tx rate and HT information parameters in driver. + * + * Both rate configuration and current data rate can be retrieved + * with this request. + */ +static int mwifiex_ret_802_11_tx_rate_query(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + priv->tx_rate = resp->params.tx_rate.tx_rate; + priv->tx_htinfo = resp->params.tx_rate.ht_info; + if (!priv->is_data_rate_auto) + priv->data_rate = + mwifiex_index_to_data_rate(priv, priv->tx_rate, + priv->tx_htinfo); + + return 0; +} + +/* + * This function handles the command response of a deauthenticate + * command. + * + * If the deauthenticated MAC matches the current BSS MAC, the connection + * state is reset. + */ +static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + adapter->dbg.num_cmd_deauth++; + if (!memcmp(resp->params.deauth.mac_addr, + &priv->curr_bss_params.bss_descriptor.mac_address, + sizeof(resp->params.deauth.mac_addr))) + mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); + + return 0; +} + +/* + * This function handles the command response of ad-hoc stop. + * + * The function resets the connection state in driver. + */ +static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); + return 0; +} + +/* + * This function handles the command response of set/get v1 key material. + * + * Handling includes updating the driver parameters to reflect the + * changes. + */ +static int mwifiex_ret_802_11_key_material_v1(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_key_material *key = + &resp->params.key_material; + + if (le16_to_cpu(key->action) == HostCmd_ACT_GEN_SET) { + if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) { + mwifiex_dbg(priv->adapter, INFO, + "info: key: GTK is set\n"); + priv->wpa_is_gtk_set = true; + priv->scan_block = false; + priv->port_open = true; + } + } + + memset(priv->aes_key.key_param_set.key, 0, + sizeof(key->key_param_set.key)); + priv->aes_key.key_param_set.key_len = key->key_param_set.key_len; + memcpy(priv->aes_key.key_param_set.key, key->key_param_set.key, + le16_to_cpu(priv->aes_key.key_param_set.key_len)); + + return 0; +} + +/* + * This function handles the command response of set/get v2 key material. + * + * Handling includes updating the driver parameters to reflect the + * changes. + */ +static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_key_material_v2 *key_v2; + __le16 len; + + key_v2 = &resp->params.key_material_v2; + if (le16_to_cpu(key_v2->action) == HostCmd_ACT_GEN_SET) { + if ((le16_to_cpu(key_v2->key_param_set.key_info) & KEY_MCAST)) { + mwifiex_dbg(priv->adapter, INFO, "info: key: GTK is set\n"); + priv->wpa_is_gtk_set = true; + priv->scan_block = false; + priv->port_open = true; + } + } + + if (key_v2->key_param_set.key_type != KEY_TYPE_ID_AES) + return 0; + + memset(priv->aes_key_v2.key_param_set.key_params.aes.key, 0, + WLAN_KEY_LEN_CCMP); + priv->aes_key_v2.key_param_set.key_params.aes.key_len = + key_v2->key_param_set.key_params.aes.key_len; + len = priv->aes_key_v2.key_param_set.key_params.aes.key_len; + memcpy(priv->aes_key_v2.key_param_set.key_params.aes.key, + key_v2->key_param_set.key_params.aes.key, le16_to_cpu(len)); + + return 0; +} + +/* Wrapper function for processing response of key material command */ +static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) + return mwifiex_ret_802_11_key_material_v2(priv, resp); + else + return mwifiex_ret_802_11_key_material_v1(priv, resp); +} + +/* + * This function handles the command response of get 11d domain information. + */ +static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11d_domain_info_rsp *domain_info = + &resp->params.domain_info_resp; + struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain; + u16 action = le16_to_cpu(domain_info->action); + u8 no_of_triplet; + + no_of_triplet = (u8) ((le16_to_cpu(domain->header.len) + - IEEE80211_COUNTRY_STRING_LEN) + / sizeof(struct ieee80211_country_ie_triplet)); + + mwifiex_dbg(priv->adapter, INFO, + "info: 11D Domain Info Resp: no_of_triplet=%d\n", + no_of_triplet); + + if (no_of_triplet > MWIFIEX_MAX_TRIPLET_802_11D) { + mwifiex_dbg(priv->adapter, FATAL, + "11D: invalid number of triplets %d returned\n", + no_of_triplet); + return -1; + } + + switch (action) { + case HostCmd_ACT_GEN_SET: /* Proc Set Action */ + break; + case HostCmd_ACT_GEN_GET: + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "11D: invalid action:%d\n", domain_info->action); + return -1; + } + + return 0; +} + +/* + * This function handles the command response of get extended version. + * + * Handling includes forming the extended version string and sending it + * to application. + */ +static int mwifiex_ret_ver_ext(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct host_cmd_ds_version_ext *version_ext) +{ + struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; + + if (version_ext) { + version_ext->version_str_sel = ver_ext->version_str_sel; + memcpy(version_ext->version_str, ver_ext->version_str, + sizeof(char) * 128); + memcpy(priv->version_str, ver_ext->version_str, 128); + } + return 0; +} + +/* + * This function handles the command response of remain on channel. + */ +static int +mwifiex_ret_remain_on_chan(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + struct host_cmd_ds_remain_on_chan *roc_cfg) +{ + struct host_cmd_ds_remain_on_chan *resp_cfg = &resp->params.roc_cfg; + + if (roc_cfg) + memcpy(roc_cfg, resp_cfg, sizeof(*roc_cfg)); + + return 0; +} + +/* + * This function handles the command response of P2P mode cfg. + */ +static int +mwifiex_ret_p2p_mode_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + void *data_buf) +{ + struct host_cmd_ds_p2p_mode_cfg *mode_cfg = &resp->params.mode_cfg; + + if (data_buf) + *((u16 *)data_buf) = le16_to_cpu(mode_cfg->mode); + + return 0; +} + +/* This function handles the command response of mem_access command + */ +static int +mwifiex_ret_mem_access(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, void *pioctl_buf) +{ + struct host_cmd_ds_mem_access *mem = (void *)&resp->params.mem; + + priv->mem_rw.addr = le32_to_cpu(mem->addr); + priv->mem_rw.value = le32_to_cpu(mem->value); + + return 0; +} +/* + * This function handles the command response of register access. + * + * The register value and offset are returned to the user. For EEPROM + * access, the byte count is also returned. + */ +static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp, + void *data_buf) +{ + struct mwifiex_ds_reg_rw *reg_rw; + struct mwifiex_ds_read_eeprom *eeprom; + union reg { + struct host_cmd_ds_mac_reg_access *mac; + struct host_cmd_ds_bbp_reg_access *bbp; + struct host_cmd_ds_rf_reg_access *rf; + struct host_cmd_ds_pmic_reg_access *pmic; + struct host_cmd_ds_802_11_eeprom_access *eeprom; + } r; + + if (!data_buf) + return 0; + + reg_rw = data_buf; + eeprom = data_buf; + switch (type) { + case HostCmd_CMD_MAC_REG_ACCESS: + r.mac = &resp->params.mac_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.mac->offset)); + reg_rw->value = r.mac->value; + break; + case HostCmd_CMD_BBP_REG_ACCESS: + r.bbp = &resp->params.bbp_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.bbp->offset)); + reg_rw->value = cpu_to_le32((u32) r.bbp->value); + break; + + case HostCmd_CMD_RF_REG_ACCESS: + r.rf = &resp->params.rf_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); + reg_rw->value = cpu_to_le32((u32) r.bbp->value); + break; + case HostCmd_CMD_PMIC_REG_ACCESS: + r.pmic = &resp->params.pmic_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.pmic->offset)); + reg_rw->value = cpu_to_le32((u32) r.pmic->value); + break; + case HostCmd_CMD_CAU_REG_ACCESS: + r.rf = &resp->params.rf_reg; + reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); + reg_rw->value = cpu_to_le32((u32) r.rf->value); + break; + case HostCmd_CMD_802_11_EEPROM_ACCESS: + r.eeprom = &resp->params.eeprom; + pr_debug("info: EEPROM read len=%x\n", r.eeprom->byte_count); + if (le16_to_cpu(eeprom->byte_count) < + le16_to_cpu(r.eeprom->byte_count)) { + eeprom->byte_count = cpu_to_le16(0); + pr_debug("info: EEPROM read length is too big\n"); + return -1; + } + eeprom->offset = r.eeprom->offset; + eeprom->byte_count = r.eeprom->byte_count; + if (le16_to_cpu(eeprom->byte_count) > 0) + memcpy(&eeprom->value, &r.eeprom->value, + le16_to_cpu(r.eeprom->byte_count)); + + break; + default: + return -1; + } + return 0; +} + +/* + * This function handles the command response of get IBSS coalescing status. + * + * If the received BSSID is different than the current one, the current BSSID, + * beacon interval, ATIM window and ERP information are updated, along with + * changing the ad-hoc state accordingly. + */ +static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp = + &(resp->params.ibss_coalescing); + + if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET) + return 0; + + mwifiex_dbg(priv->adapter, INFO, + "info: new BSSID %pM\n", ibss_coal_resp->bssid); + + /* If rsp has NULL BSSID, Just return..... No Action */ + if (is_zero_ether_addr(ibss_coal_resp->bssid)) { + mwifiex_dbg(priv->adapter, FATAL, "new BSSID is NULL\n"); + return 0; + } + + /* If BSSID is diff, modify current BSS parameters */ + if (!ether_addr_equal(priv->curr_bss_params.bss_descriptor.mac_address, ibss_coal_resp->bssid)) { + /* BSSID */ + memcpy(priv->curr_bss_params.bss_descriptor.mac_address, + ibss_coal_resp->bssid, ETH_ALEN); + + /* Beacon Interval */ + priv->curr_bss_params.bss_descriptor.beacon_period + = le16_to_cpu(ibss_coal_resp->beacon_interval); + + /* ERP Information */ + priv->curr_bss_params.bss_descriptor.erp_flags = + (u8) le16_to_cpu(ibss_coal_resp->use_g_rate_protect); + + priv->adhoc_state = ADHOC_COALESCED; + } + + return 0; +} +static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper; + u16 reason = le16_to_cpu(cmd_tdls_oper->reason); + u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action); + struct mwifiex_sta_node *node = + mwifiex_get_sta_entry(priv, cmd_tdls_oper->peer_mac); + + switch (action) { + case ACT_TDLS_DELETE: + if (reason) { + if (!node || reason == TDLS_ERR_LINK_NONEXISTENT) + mwifiex_dbg(priv->adapter, MSG, + "TDLS link delete for %pM failed: reason %d\n", + cmd_tdls_oper->peer_mac, reason); + else + mwifiex_dbg(priv->adapter, ERROR, + "TDLS link delete for %pM failed: reason %d\n", + cmd_tdls_oper->peer_mac, reason); + } else { + mwifiex_dbg(priv->adapter, MSG, + "TDLS link delete for %pM successful\n", + cmd_tdls_oper->peer_mac); + } + break; + case ACT_TDLS_CREATE: + if (reason) { + mwifiex_dbg(priv->adapter, ERROR, + "TDLS link creation for %pM failed: reason %d", + cmd_tdls_oper->peer_mac, reason); + if (node && reason != TDLS_ERR_LINK_EXISTS) + node->tdls_status = TDLS_SETUP_FAILURE; + } else { + mwifiex_dbg(priv->adapter, MSG, + "TDLS link creation for %pM successful", + cmd_tdls_oper->peer_mac); + } + break; + case ACT_TDLS_CONFIG: + if (reason) { + mwifiex_dbg(priv->adapter, ERROR, + "TDLS link config for %pM failed, reason %d\n", + cmd_tdls_oper->peer_mac, reason); + if (node) + node->tdls_status = TDLS_SETUP_FAILURE; + } else { + mwifiex_dbg(priv->adapter, MSG, + "TDLS link config for %pM successful\n", + cmd_tdls_oper->peer_mac); + } + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "Unknown TDLS command action response %d", action); + return -1; + } + + return 0; +} +/* + * This function handles the command response for subscribe event command. + */ +static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event = + &resp->params.subsc_evt; + + /* For every subscribe event command (Get/Set/Clear), FW reports the + * current set of subscribed events*/ + mwifiex_dbg(priv->adapter, EVENT, + "Bitmap of currently subscribed events: %16x\n", + le16_to_cpu(cmd_sub_event->events)); + + return 0; +} + +static int mwifiex_ret_uap_sta_list(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct host_cmd_ds_sta_list *sta_list = + &resp->params.sta_list; + struct mwifiex_ie_types_sta_info *sta_info = (void *)&sta_list->tlv; + int i; + struct mwifiex_sta_node *sta_node; + + for (i = 0; i < sta_list->sta_count; i++) { + sta_node = mwifiex_get_sta_entry(priv, sta_info->mac); + if (unlikely(!sta_node)) + continue; + + sta_node->stats.rssi = sta_info->rssi; + sta_info++; + } + + return 0; +} + +/* This function handles the command response of set_cfg_data */ +static int mwifiex_ret_cfg_data(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + if (resp->result != HostCmd_RESULT_OK) { + mwifiex_dbg(priv->adapter, ERROR, "Cal data cmd resp failed\n"); + return -1; + } + + return 0; +} + +/** This Function handles the command response of sdio rx aggr */ +static int mwifiex_ret_sdio_rx_aggr_cfg(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = + &resp->params.sdio_rx_aggr_cfg; + + adapter->sdio_rx_aggr_enable = cfg->enable; + adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size); + + return 0; +} + +static int mwifiex_ret_robust_coex(struct mwifiex_private *priv, + struct host_cmd_ds_command *resp, + bool *is_timeshare) +{ + struct host_cmd_ds_robust_coex *coex = &resp->params.coex; + struct mwifiex_ie_types_robust_coex *coex_tlv; + u16 action = le16_to_cpu(coex->action); + u32 mode; + + coex_tlv = (struct mwifiex_ie_types_robust_coex + *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex)); + if (action == HostCmd_ACT_GEN_GET) { + mode = le32_to_cpu(coex_tlv->mode); + if (mode == MWIFIEX_COEX_MODE_TIMESHARE) + *is_timeshare = true; + else + *is_timeshare = false; + } + + return 0; +} + +/* + * This function handles the command responses. + * + * This is a generic function, which calls command specific + * response handlers based on the command ID. + */ +int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, + struct host_cmd_ds_command *resp) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + void *data_buf = adapter->curr_cmd->data_buf; + + /* If the command is not successful, cleanup and return failure */ + if (resp->result != HostCmd_RESULT_OK) { + mwifiex_process_cmdresp_error(priv, resp); + return -1; + } + /* Command successful, handle response */ + switch (cmdresp_no) { + case HostCmd_CMD_GET_HW_SPEC: + ret = mwifiex_ret_get_hw_spec(priv, resp); + break; + case HostCmd_CMD_CFG_DATA: + ret = mwifiex_ret_cfg_data(priv, resp); + break; + case HostCmd_CMD_MAC_CONTROL: + break; + case HostCmd_CMD_802_11_MAC_ADDRESS: + ret = mwifiex_ret_802_11_mac_address(priv, resp); + break; + case HostCmd_CMD_MAC_MULTICAST_ADR: + ret = mwifiex_ret_mac_multicast_adr(priv, resp); + break; + case HostCmd_CMD_TX_RATE_CFG: + ret = mwifiex_ret_tx_rate_cfg(priv, resp); + break; + case HostCmd_CMD_802_11_SCAN: + ret = mwifiex_ret_802_11_scan(priv, resp); + adapter->curr_cmd->wait_q_enabled = false; + break; + case HostCmd_CMD_802_11_SCAN_EXT: + ret = mwifiex_ret_802_11_scan_ext(priv, resp); + adapter->curr_cmd->wait_q_enabled = false; + break; + case HostCmd_CMD_802_11_BG_SCAN_QUERY: + ret = mwifiex_ret_802_11_scan(priv, resp); + mwifiex_dbg(adapter, CMD, + "info: CMD_RESP: BG_SCAN result is ready!\n"); + break; + case HostCmd_CMD_TXPWR_CFG: + ret = mwifiex_ret_tx_power_cfg(priv, resp); + break; + case HostCmd_CMD_RF_TX_PWR: + ret = mwifiex_ret_rf_tx_power(priv, resp); + break; + case HostCmd_CMD_RF_ANTENNA: + ret = mwifiex_ret_rf_antenna(priv, resp); + break; + case HostCmd_CMD_802_11_PS_MODE_ENH: + ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf); + break; + case HostCmd_CMD_802_11_HS_CFG_ENH: + ret = mwifiex_ret_802_11_hs_cfg(priv, resp); + break; + case HostCmd_CMD_802_11_ASSOCIATE: + ret = mwifiex_ret_802_11_associate(priv, resp); + break; + case HostCmd_CMD_802_11_DEAUTHENTICATE: + ret = mwifiex_ret_802_11_deauthenticate(priv, resp); + break; + case HostCmd_CMD_802_11_AD_HOC_START: + case HostCmd_CMD_802_11_AD_HOC_JOIN: + ret = mwifiex_ret_802_11_ad_hoc(priv, resp); + break; + case HostCmd_CMD_802_11_AD_HOC_STOP: + ret = mwifiex_ret_802_11_ad_hoc_stop(priv, resp); + break; + case HostCmd_CMD_802_11_GET_LOG: + ret = mwifiex_ret_get_log(priv, resp, data_buf); + break; + case HostCmd_CMD_RSSI_INFO: + ret = mwifiex_ret_802_11_rssi_info(priv, resp); + break; + case HostCmd_CMD_802_11_SNMP_MIB: + ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf); + break; + case HostCmd_CMD_802_11_TX_RATE_QUERY: + ret = mwifiex_ret_802_11_tx_rate_query(priv, resp); + break; + case HostCmd_CMD_VERSION_EXT: + ret = mwifiex_ret_ver_ext(priv, resp, data_buf); + break; + case HostCmd_CMD_REMAIN_ON_CHAN: + ret = mwifiex_ret_remain_on_chan(priv, resp, data_buf); + break; + case HostCmd_CMD_11AC_CFG: + break; + case HostCmd_CMD_P2P_MODE_CFG: + ret = mwifiex_ret_p2p_mode_cfg(priv, resp, data_buf); + break; + case HostCmd_CMD_MGMT_FRAME_REG: + case HostCmd_CMD_FUNC_INIT: + case HostCmd_CMD_FUNC_SHUTDOWN: + break; + case HostCmd_CMD_802_11_KEY_MATERIAL: + ret = mwifiex_ret_802_11_key_material(priv, resp); + break; + case HostCmd_CMD_802_11D_DOMAIN_INFO: + ret = mwifiex_ret_802_11d_domain_info(priv, resp); + break; + case HostCmd_CMD_11N_ADDBA_REQ: + ret = mwifiex_ret_11n_addba_req(priv, resp); + break; + case HostCmd_CMD_11N_DELBA: + ret = mwifiex_ret_11n_delba(priv, resp); + break; + case HostCmd_CMD_11N_ADDBA_RSP: + ret = mwifiex_ret_11n_addba_resp(priv, resp); + break; + case HostCmd_CMD_RECONFIGURE_TX_BUFF: + if (0xffff == (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) { + if (adapter->iface_type == MWIFIEX_USB && + adapter->usb_mc_setup) { + if (adapter->if_ops.multi_port_resync) + adapter->if_ops. + multi_port_resync(adapter); + adapter->usb_mc_setup = false; + adapter->tx_lock_flag = false; + } + break; + } + adapter->tx_buf_size = (u16) le16_to_cpu(resp->params. + tx_buf.buff_size); + adapter->tx_buf_size = (adapter->tx_buf_size + / MWIFIEX_SDIO_BLOCK_SIZE) + * MWIFIEX_SDIO_BLOCK_SIZE; + adapter->curr_tx_buf_size = adapter->tx_buf_size; + mwifiex_dbg(adapter, CMD, "cmd: curr_tx_buf_size=%d\n", + adapter->curr_tx_buf_size); + + if (adapter->if_ops.update_mp_end_port) + adapter->if_ops.update_mp_end_port(adapter, + le16_to_cpu(resp->params.tx_buf.mp_end_port)); + break; + case HostCmd_CMD_AMSDU_AGGR_CTRL: + break; + case HostCmd_CMD_WMM_GET_STATUS: + ret = mwifiex_ret_wmm_get_status(priv, resp); + break; + case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: + ret = mwifiex_ret_ibss_coalescing_status(priv, resp); + break; + case HostCmd_CMD_MEM_ACCESS: + ret = mwifiex_ret_mem_access(priv, resp, data_buf); + break; + case HostCmd_CMD_MAC_REG_ACCESS: + case HostCmd_CMD_BBP_REG_ACCESS: + case HostCmd_CMD_RF_REG_ACCESS: + case HostCmd_CMD_PMIC_REG_ACCESS: + case HostCmd_CMD_CAU_REG_ACCESS: + case HostCmd_CMD_802_11_EEPROM_ACCESS: + ret = mwifiex_ret_reg_access(cmdresp_no, resp, data_buf); + break; + case HostCmd_CMD_SET_BSS_MODE: + break; + case HostCmd_CMD_11N_CFG: + break; + case HostCmd_CMD_PCIE_DESC_DETAILS: + break; + case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: + ret = mwifiex_ret_subsc_evt(priv, resp); + break; + case HostCmd_CMD_UAP_SYS_CONFIG: + break; + case HOST_CMD_APCMD_STA_LIST: + ret = mwifiex_ret_uap_sta_list(priv, resp); + break; + case HostCmd_CMD_UAP_BSS_START: + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + adapter->delay_null_pkt = false; + priv->bss_started = 1; + break; + case HostCmd_CMD_UAP_BSS_STOP: + priv->bss_started = 0; + break; + case HostCmd_CMD_UAP_STA_DEAUTH: + break; + case HOST_CMD_APCMD_SYS_RESET: + break; + case HostCmd_CMD_MEF_CFG: + break; + case HostCmd_CMD_COALESCE_CFG: + break; + case HostCmd_CMD_TDLS_OPER: + ret = mwifiex_ret_tdls_oper(priv, resp); + case HostCmd_CMD_MC_POLICY: + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + break; + case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: + ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp); + break; + case HostCmd_CMD_TDLS_CONFIG: + break; + case HostCmd_CMD_ROBUST_COEX: + ret = mwifiex_ret_robust_coex(priv, resp, data_buf); + break; + default: + mwifiex_dbg(adapter, ERROR, + "CMD_RESP: unknown cmd response %#x\n", + resp->command); + break; + } + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c new file mode 100644 index 000000000000..ff3ee9dfbbd5 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c @@ -0,0 +1,864 @@ +/* + * Marvell Wireless LAN device driver: station event handling + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + +/* + * This function resets the connection state. + * + * The function is invoked after receiving a disconnect event from firmware, + * and performs the following actions - + * - Set media status to disconnected + * - Clean up Tx and Rx packets + * - Resets SNR/NF/RSSI value in driver + * - Resets security configurations in driver + * - Enables auto data rate + * - Saves the previous SSID and BSSID so that they can + * be used for re-association, if required + * - Erases current SSID and BSSID information + * - Sends a disconnect event to upper layers/applications. + */ +void +mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (!priv->media_connected) + return; + + mwifiex_dbg(adapter, INFO, + "info: handles disconnect event\n"); + + priv->media_connected = false; + + priv->scan_block = false; + priv->port_open = false; + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) { + mwifiex_disable_all_tdls_links(priv); + + if (priv->adapter->auto_tdls) + mwifiex_clean_auto_tdls(priv); + } + + /* Free Tx and Rx packets, report disconnect to upper layer */ + mwifiex_clean_txrx(priv); + + /* Reset SNR/NF/RSSI values */ + priv->data_rssi_last = 0; + priv->data_nf_last = 0; + priv->data_rssi_avg = 0; + priv->data_nf_avg = 0; + priv->bcn_rssi_last = 0; + priv->bcn_nf_last = 0; + priv->bcn_rssi_avg = 0; + priv->bcn_nf_avg = 0; + priv->rxpd_rate = 0; + priv->rxpd_htinfo = 0; + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + priv->wpa_ie_len = 0; + + priv->sec_info.wapi_enabled = false; + priv->wapi_ie_len = 0; + priv->sec_info.wapi_key_on = false; + + priv->sec_info.encryption_mode = 0; + + /* Enable auto data rate */ + priv->is_data_rate_auto = true; + priv->data_rate = 0; + + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data) + mwifiex_hist_data_reset(priv); + + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + priv->adhoc_state = ADHOC_IDLE; + priv->adhoc_is_link_sensed = false; + } + + /* + * Memorize the previous SSID and BSSID so + * it could be used for re-assoc + */ + + mwifiex_dbg(adapter, INFO, + "info: previous SSID=%s, SSID len=%u\n", + priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); + + mwifiex_dbg(adapter, INFO, + "info: current SSID=%s, SSID len=%u\n", + priv->curr_bss_params.bss_descriptor.ssid.ssid, + priv->curr_bss_params.bss_descriptor.ssid.ssid_len); + + memcpy(&priv->prev_ssid, + &priv->curr_bss_params.bss_descriptor.ssid, + sizeof(struct cfg80211_ssid)); + + memcpy(priv->prev_bssid, + priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); + + /* Need to erase the current SSID and BSSID info */ + memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params)); + + adapter->tx_lock_flag = false; + adapter->pps_uapsd_mode = false; + + if (adapter->is_cmd_timedout && adapter->curr_cmd) + return; + priv->media_connected = false; + mwifiex_dbg(adapter, MSG, + "info: successfully disconnected from %pM: reason code %d\n", + priv->cfg_bssid, reason_code); + if (priv->bss_mode == NL80211_IFTYPE_STATION || + priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { + cfg80211_disconnected(priv->netdev, reason_code, NULL, 0, + false, GFP_KERNEL); + } + eth_zero_addr(priv->cfg_bssid); + + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); +} + +static int mwifiex_parse_tdls_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + int ret = 0; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_tdls_generic_event *tdls_evt = + (void *)event_skb->data + sizeof(adapter->event_cause); + u8 *mac = tdls_evt->peer_mac; + + /* reserved 2 bytes are not mandatory in tdls event */ + if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) - + sizeof(u16) - sizeof(adapter->event_cause))) { + mwifiex_dbg(adapter, ERROR, "Invalid event length!\n"); + return -1; + } + + sta_ptr = mwifiex_get_sta_entry(priv, tdls_evt->peer_mac); + if (!sta_ptr) { + mwifiex_dbg(adapter, ERROR, "cannot get sta entry!\n"); + return -1; + } + + switch (le16_to_cpu(tdls_evt->type)) { + case TDLS_EVENT_LINK_TEAR_DOWN: + cfg80211_tdls_oper_request(priv->netdev, + tdls_evt->peer_mac, + NL80211_TDLS_TEARDOWN, + le16_to_cpu(tdls_evt->u.reason_code), + GFP_KERNEL); + break; + case TDLS_EVENT_CHAN_SWITCH_RESULT: + mwifiex_dbg(adapter, EVENT, "tdls channel switch result :\n"); + mwifiex_dbg(adapter, EVENT, + "status=0x%x, reason=0x%x cur_chan=%d\n", + tdls_evt->u.switch_result.status, + tdls_evt->u.switch_result.reason, + tdls_evt->u.switch_result.cur_chan); + + /* tdls channel switch failed */ + if (tdls_evt->u.switch_result.status != 0) { + switch (tdls_evt->u.switch_result.cur_chan) { + case TDLS_BASE_CHANNEL: + sta_ptr->tdls_status = TDLS_IN_BASE_CHAN; + break; + case TDLS_OFF_CHANNEL: + sta_ptr->tdls_status = TDLS_IN_OFF_CHAN; + break; + default: + break; + } + return ret; + } + + /* tdls channel switch success */ + switch (tdls_evt->u.switch_result.cur_chan) { + case TDLS_BASE_CHANNEL: + if (sta_ptr->tdls_status == TDLS_IN_BASE_CHAN) + break; + mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac, + false); + sta_ptr->tdls_status = TDLS_IN_BASE_CHAN; + break; + case TDLS_OFF_CHANNEL: + if (sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) + break; + mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac, + true); + sta_ptr->tdls_status = TDLS_IN_OFF_CHAN; + break; + default: + break; + } + + break; + case TDLS_EVENT_START_CHAN_SWITCH: + mwifiex_dbg(adapter, EVENT, "tdls start channel switch...\n"); + sta_ptr->tdls_status = TDLS_CHAN_SWITCHING; + break; + case TDLS_EVENT_CHAN_SWITCH_STOPPED: + mwifiex_dbg(adapter, EVENT, + "tdls chan switch stopped, reason=%d\n", + tdls_evt->u.cs_stop_reason); + break; + default: + break; + } + + return ret; +} + +static void mwifiex_process_uap_tx_pause(struct mwifiex_private *priv, + struct mwifiex_ie_types_header *tlv) +{ + struct mwifiex_tx_pause_tlv *tp; + struct mwifiex_sta_node *sta_ptr; + unsigned long flags; + + tp = (void *)tlv; + mwifiex_dbg(priv->adapter, EVENT, + "uap tx_pause: %pM pause=%d, pkts=%d\n", + tp->peermac, tp->tx_pause, + tp->pkt_cnt); + + if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) { + if (tp->tx_pause) + priv->port_open = false; + else + priv->port_open = true; + } else if (is_multicast_ether_addr(tp->peermac)) { + mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); + } else { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { + sta_ptr->tx_pause = tp->tx_pause; + mwifiex_update_ralist_tx_pause(priv, tp->peermac, + tp->tx_pause); + } + } +} + +static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv, + struct mwifiex_ie_types_header *tlv) +{ + struct mwifiex_tx_pause_tlv *tp; + struct mwifiex_sta_node *sta_ptr; + int status; + unsigned long flags; + + tp = (void *)tlv; + mwifiex_dbg(priv->adapter, EVENT, + "sta tx_pause: %pM pause=%d, pkts=%d\n", + tp->peermac, tp->tx_pause, + tp->pkt_cnt); + + if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) { + if (tp->tx_pause) + priv->port_open = false; + else + priv->port_open = true; + } else { + if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return; + + status = mwifiex_get_tdls_link_status(priv, tp->peermac); + if (mwifiex_is_tdls_link_setup(status)) { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { + sta_ptr->tx_pause = tp->tx_pause; + mwifiex_update_ralist_tx_pause(priv, + tp->peermac, + tp->tx_pause); + } + } + } +} + +void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + struct mwifiex_ie_types_multi_chan_info *chan_info; + struct mwifiex_ie_types_mc_group_info *grp_info; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_header *tlv; + u16 tlv_buf_left, tlv_type, tlv_len; + int intf_num, bss_type, bss_num, i; + struct mwifiex_private *intf_priv; + + tlv_buf_left = event_skb->len - sizeof(u32); + chan_info = (void *)event_skb->data + sizeof(u32); + + if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO || + tlv_buf_left < sizeof(struct mwifiex_ie_types_multi_chan_info)) { + mwifiex_dbg(adapter, ERROR, + "unknown TLV in chan_info event\n"); + return; + } + + adapter->usb_mc_status = le16_to_cpu(chan_info->status); + mwifiex_dbg(adapter, EVENT, "multi chan operation %s\n", + adapter->usb_mc_status ? "started" : "over"); + + tlv_buf_left -= sizeof(struct mwifiex_ie_types_multi_chan_info); + tlv = (struct mwifiex_ie_types_header *)chan_info->tlv_buffer; + + while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_len = le16_to_cpu(tlv->len); + if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > + tlv_buf_left) { + mwifiex_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t" + "tlvBufLeft=%d\n", tlv_len, tlv_buf_left); + break; + } + if (tlv_type != TLV_TYPE_MC_GROUP_INFO) { + mwifiex_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n", + tlv_type); + break; + } + + grp_info = (struct mwifiex_ie_types_mc_group_info *)tlv; + intf_num = grp_info->intf_num; + for (i = 0; i < intf_num; i++) { + bss_type = grp_info->bss_type_numlist[i] >> 4; + bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK; + intf_priv = mwifiex_get_priv_by_id(adapter, bss_num, + bss_type); + if (!intf_priv) { + mwifiex_dbg(adapter, ERROR, + "Invalid bss_type bss_num\t" + "in multi channel event\n"); + continue; + } + if (adapter->iface_type == MWIFIEX_USB) { + u8 ep; + + ep = grp_info->hid_num.usb_ep_num; + if (ep == MWIFIEX_USB_EP_DATA || + ep == MWIFIEX_USB_EP_DATA_CH2) + intf_priv->usb_port = ep; + } + } + + tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + + tlv_len; + tlv = (void *)((u8 *)tlv + tlv_len + + sizeof(struct mwifiex_ie_types_header)); + } + + if (adapter->iface_type == MWIFIEX_USB) { + adapter->tx_lock_flag = true; + adapter->usb_mc_setup = true; + mwifiex_multi_chan_resync(adapter); + } +} + +void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + struct mwifiex_ie_types_header *tlv; + u16 tlv_type, tlv_len; + int tlv_buf_left; + + if (!priv->media_connected) { + mwifiex_dbg(priv->adapter, ERROR, + "tx_pause event while disconnected; bss_role=%d\n", + priv->bss_role); + return; + } + + tlv_buf_left = event_skb->len - sizeof(u32); + tlv = (void *)event_skb->data + sizeof(u32); + + while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { + tlv_type = le16_to_cpu(tlv->type); + tlv_len = le16_to_cpu(tlv->len); + if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > + tlv_buf_left) { + mwifiex_dbg(priv->adapter, ERROR, + "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", + tlv_len, tlv_buf_left); + break; + } + if (tlv_type == TLV_TYPE_TX_PAUSE) { + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) + mwifiex_process_sta_tx_pause(priv, tlv); + else + mwifiex_process_uap_tx_pause(priv, tlv); + } + + tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + + tlv_len; + tlv = (void *)((u8 *)tlv + tlv_len + + sizeof(struct mwifiex_ie_types_header)); + } + +} + +/* +* This function handles coex events generated by firmware +*/ +void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv, + struct sk_buff *event_skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_ie_types_header *tlv; + struct mwifiex_ie_types_btcoex_aggr_win_size *winsizetlv; + struct mwifiex_ie_types_btcoex_scan_time *scantlv; + s32 len = event_skb->len - sizeof(u32); + u8 *cur_ptr = event_skb->data + sizeof(u32); + u16 tlv_type, tlv_len; + + while (len >= sizeof(struct mwifiex_ie_types_header)) { + tlv = (struct mwifiex_ie_types_header *)cur_ptr; + tlv_len = le16_to_cpu(tlv->len); + tlv_type = le16_to_cpu(tlv->type); + + if ((tlv_len + sizeof(struct mwifiex_ie_types_header)) > len) + break; + switch (tlv_type) { + case TLV_BTCOEX_WL_AGGR_WINSIZE: + winsizetlv = + (struct mwifiex_ie_types_btcoex_aggr_win_size *)tlv; + adapter->coex_win_size = winsizetlv->coex_win_size; + adapter->coex_tx_win_size = + winsizetlv->tx_win_size; + adapter->coex_rx_win_size = + winsizetlv->rx_win_size; + mwifiex_coex_ampdu_rxwinsize(adapter); + mwifiex_update_ampdu_txwinsize(adapter); + break; + + case TLV_BTCOEX_WL_SCANTIME: + scantlv = + (struct mwifiex_ie_types_btcoex_scan_time *)tlv; + adapter->coex_scan = scantlv->coex_scan; + adapter->coex_min_scan_time = scantlv->min_scan_time; + adapter->coex_max_scan_time = scantlv->max_scan_time; + break; + + default: + break; + } + + len -= tlv_len + sizeof(struct mwifiex_ie_types_header); + cur_ptr += tlv_len + + sizeof(struct mwifiex_ie_types_header); + } + + dev_dbg(adapter->dev, "coex_scan=%d min_scan=%d coex_win=%d, tx_win=%d rx_win=%d\n", + adapter->coex_scan, adapter->coex_min_scan_time, + adapter->coex_win_size, adapter->coex_tx_win_size, + adapter->coex_rx_win_size); +} + +/* + * This function handles events generated by firmware. + * + * This is a generic function and handles all events. + * + * Event specific routines are called by this function based + * upon the generated event cause. + * + * For the following events, the function just forwards them to upper + * layers, optionally recording the change - + * - EVENT_LINK_SENSED + * - EVENT_MIC_ERR_UNICAST + * - EVENT_MIC_ERR_MULTICAST + * - EVENT_PORT_RELEASE + * - EVENT_RSSI_LOW + * - EVENT_SNR_LOW + * - EVENT_MAX_FAIL + * - EVENT_RSSI_HIGH + * - EVENT_SNR_HIGH + * - EVENT_DATA_RSSI_LOW + * - EVENT_DATA_SNR_LOW + * - EVENT_DATA_RSSI_HIGH + * - EVENT_DATA_SNR_HIGH + * - EVENT_LINK_QUALITY + * - EVENT_PRE_BEACON_LOST + * - EVENT_IBSS_COALESCED + * - EVENT_WEP_ICV_ERR + * - EVENT_BW_CHANGE + * - EVENT_HOSTWAKE_STAIE + * + * For the following events, no action is taken - + * - EVENT_MIB_CHANGED + * - EVENT_INIT_DONE + * - EVENT_DUMMY_HOST_WAKEUP_SIGNAL + * + * Rest of the supported events requires driver handling - + * - EVENT_DEAUTHENTICATED + * - EVENT_DISASSOCIATED + * - EVENT_LINK_LOST + * - EVENT_PS_SLEEP + * - EVENT_PS_AWAKE + * - EVENT_DEEP_SLEEP_AWAKE + * - EVENT_HS_ACT_REQ + * - EVENT_ADHOC_BCN_LOST + * - EVENT_BG_SCAN_REPORT + * - EVENT_WMM_STATUS_CHANGE + * - EVENT_ADDBA + * - EVENT_DELBA + * - EVENT_BA_STREAM_TIEMOUT + * - EVENT_AMSDU_AGGR_CTRL + */ +int mwifiex_process_sta_event(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret = 0; + u32 eventcause = adapter->event_cause; + u16 ctrl, reason_code; + + switch (eventcause) { + case EVENT_DUMMY_HOST_WAKEUP_SIGNAL: + mwifiex_dbg(adapter, ERROR, + "invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL, ignore it\n"); + break; + case EVENT_LINK_SENSED: + mwifiex_dbg(adapter, EVENT, "event: LINK_SENSED\n"); + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); + break; + + case EVENT_DEAUTHENTICATED: + mwifiex_dbg(adapter, EVENT, "event: Deauthenticated\n"); + if (priv->wps.session_enable) { + mwifiex_dbg(adapter, INFO, + "info: receive deauth event in wps session\n"); + break; + } + adapter->dbg.num_event_deauth++; + if (priv->media_connected) { + reason_code = + le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_reset_connect_state(priv, reason_code); + } + break; + + case EVENT_DISASSOCIATED: + mwifiex_dbg(adapter, EVENT, "event: Disassociated\n"); + if (priv->wps.session_enable) { + mwifiex_dbg(adapter, INFO, + "info: receive disassoc event in wps session\n"); + break; + } + adapter->dbg.num_event_disassoc++; + if (priv->media_connected) { + reason_code = + le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_reset_connect_state(priv, reason_code); + } + break; + + case EVENT_LINK_LOST: + mwifiex_dbg(adapter, EVENT, "event: Link lost\n"); + adapter->dbg.num_event_link_lost++; + if (priv->media_connected) { + reason_code = + le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_reset_connect_state(priv, reason_code); + } + break; + + case EVENT_PS_SLEEP: + mwifiex_dbg(adapter, EVENT, "info: EVENT: SLEEP\n"); + + adapter->ps_state = PS_STATE_PRE_SLEEP; + + mwifiex_check_ps_cond(adapter); + break; + + case EVENT_PS_AWAKE: + mwifiex_dbg(adapter, EVENT, "info: EVENT: AWAKE\n"); + if (!adapter->pps_uapsd_mode && priv->port_open && + priv->media_connected && adapter->sleep_period.period) { + adapter->pps_uapsd_mode = true; + mwifiex_dbg(adapter, EVENT, + "event: PPS/UAPSD mode activated\n"); + } + adapter->tx_lock_flag = false; + if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { + if (mwifiex_check_last_packet_indication(priv)) { + if (adapter->data_sent || + (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv))) { + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + break; + } + if (!mwifiex_send_null_packet + (priv, + MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) + adapter->ps_state = + PS_STATE_SLEEP; + return 0; + } + } + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + + break; + + case EVENT_DEEP_SLEEP_AWAKE: + adapter->if_ops.wakeup_complete(adapter); + mwifiex_dbg(adapter, EVENT, "event: DS_AWAKE\n"); + if (adapter->is_deep_sleep) + adapter->is_deep_sleep = false; + break; + + case EVENT_HS_ACT_REQ: + mwifiex_dbg(adapter, EVENT, "event: HS_ACT_REQ\n"); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH, + 0, 0, NULL, false); + break; + + case EVENT_MIC_ERR_UNICAST: + mwifiex_dbg(adapter, EVENT, "event: UNICAST MIC ERROR\n"); + cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, + NL80211_KEYTYPE_PAIRWISE, + -1, NULL, GFP_KERNEL); + break; + + case EVENT_MIC_ERR_MULTICAST: + mwifiex_dbg(adapter, EVENT, "event: MULTICAST MIC ERROR\n"); + cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, + NL80211_KEYTYPE_GROUP, + -1, NULL, GFP_KERNEL); + break; + case EVENT_MIB_CHANGED: + case EVENT_INIT_DONE: + break; + + case EVENT_ADHOC_BCN_LOST: + mwifiex_dbg(adapter, EVENT, "event: ADHOC_BCN_LOST\n"); + priv->adhoc_is_link_sensed = false; + mwifiex_clean_txrx(priv); + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + break; + + case EVENT_BG_SCAN_REPORT: + mwifiex_dbg(adapter, EVENT, "event: BGS_REPORT\n"); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL, false); + break; + + case EVENT_PORT_RELEASE: + mwifiex_dbg(adapter, EVENT, "event: PORT RELEASE\n"); + priv->port_open = true; + break; + + case EVENT_EXT_SCAN_REPORT: + mwifiex_dbg(adapter, EVENT, "event: EXT_SCAN Report\n"); + if (adapter->ext_scan) + ret = mwifiex_handle_event_ext_scan_report(priv, + adapter->event_skb->data); + + break; + + case EVENT_WMM_STATUS_CHANGE: + mwifiex_dbg(adapter, EVENT, "event: WMM status changed\n"); + ret = mwifiex_send_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, + 0, 0, NULL, false); + break; + + case EVENT_RSSI_LOW: + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + GFP_KERNEL); + mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, false); + priv->subsc_evt_rssi_state = RSSI_LOW_RECVD; + mwifiex_dbg(adapter, EVENT, "event: Beacon RSSI_LOW\n"); + break; + case EVENT_SNR_LOW: + mwifiex_dbg(adapter, EVENT, "event: Beacon SNR_LOW\n"); + break; + case EVENT_MAX_FAIL: + mwifiex_dbg(adapter, EVENT, "event: MAX_FAIL\n"); + break; + case EVENT_RSSI_HIGH: + cfg80211_cqm_rssi_notify(priv->netdev, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, + GFP_KERNEL); + mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, + HostCmd_ACT_GEN_GET, 0, NULL, false); + priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD; + mwifiex_dbg(adapter, EVENT, "event: Beacon RSSI_HIGH\n"); + break; + case EVENT_SNR_HIGH: + mwifiex_dbg(adapter, EVENT, "event: Beacon SNR_HIGH\n"); + break; + case EVENT_DATA_RSSI_LOW: + mwifiex_dbg(adapter, EVENT, "event: Data RSSI_LOW\n"); + break; + case EVENT_DATA_SNR_LOW: + mwifiex_dbg(adapter, EVENT, "event: Data SNR_LOW\n"); + break; + case EVENT_DATA_RSSI_HIGH: + mwifiex_dbg(adapter, EVENT, "event: Data RSSI_HIGH\n"); + break; + case EVENT_DATA_SNR_HIGH: + mwifiex_dbg(adapter, EVENT, "event: Data SNR_HIGH\n"); + break; + case EVENT_LINK_QUALITY: + mwifiex_dbg(adapter, EVENT, "event: Link Quality\n"); + break; + case EVENT_PRE_BEACON_LOST: + mwifiex_dbg(adapter, EVENT, "event: Pre-Beacon Lost\n"); + break; + case EVENT_IBSS_COALESCED: + mwifiex_dbg(adapter, EVENT, "event: IBSS_COALESCED\n"); + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, + HostCmd_ACT_GEN_GET, 0, NULL, false); + break; + case EVENT_ADDBA: + mwifiex_dbg(adapter, EVENT, "event: ADDBA Request\n"); + mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, + adapter->event_body, false); + break; + case EVENT_DELBA: + mwifiex_dbg(adapter, EVENT, "event: DELBA Request\n"); + mwifiex_11n_delete_ba_stream(priv, adapter->event_body); + break; + case EVENT_BA_STREAM_TIEMOUT: + mwifiex_dbg(adapter, EVENT, "event: BA Stream timeout\n"); + mwifiex_11n_ba_stream_timeout(priv, + (struct host_cmd_ds_11n_batimeout + *) + adapter->event_body); + break; + case EVENT_AMSDU_AGGR_CTRL: + ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_dbg(adapter, EVENT, + "event: AMSDU_AGGR_CTRL %d\n", ctrl); + + adapter->tx_buf_size = + min_t(u16, adapter->curr_tx_buf_size, ctrl); + mwifiex_dbg(adapter, EVENT, "event: tx_buf_size %d\n", + adapter->tx_buf_size); + break; + + case EVENT_WEP_ICV_ERR: + mwifiex_dbg(adapter, EVENT, "event: WEP ICV error\n"); + break; + + case EVENT_BW_CHANGE: + mwifiex_dbg(adapter, EVENT, "event: BW Change\n"); + break; + + case EVENT_HOSTWAKE_STAIE: + mwifiex_dbg(adapter, EVENT, + "event: HOSTWAKE_STAIE %d\n", eventcause); + break; + + case EVENT_REMAIN_ON_CHAN_EXPIRED: + mwifiex_dbg(adapter, EVENT, + "event: Remain on channel expired\n"); + cfg80211_remain_on_channel_expired(&priv->wdev, + priv->roc_cfg.cookie, + &priv->roc_cfg.chan, + GFP_ATOMIC); + + memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg)); + + break; + + case EVENT_CHANNEL_SWITCH_ANN: + mwifiex_dbg(adapter, EVENT, "event: Channel Switch Announcement\n"); + priv->csa_expire_time = + jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME); + priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, + HostCmd_ACT_GEN_SET, 0, + priv->curr_bss_params.bss_descriptor.mac_address, + false); + break; + + case EVENT_TDLS_GENERIC_EVENT: + ret = mwifiex_parse_tdls_event(priv, adapter->event_skb); + break; + + case EVENT_TX_DATA_PAUSE: + mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n"); + mwifiex_process_tx_pause_event(priv, adapter->event_skb); + break; + + case EVENT_MULTI_CHAN_INFO: + mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n"); + mwifiex_process_multi_chan_event(priv, adapter->event_skb); + break; + + case EVENT_TX_STATUS_REPORT: + mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n"); + mwifiex_parse_tx_status_event(priv, adapter->event_body); + break; + + case EVENT_CHANNEL_REPORT_RDY: + mwifiex_dbg(adapter, EVENT, "event: Channel Report\n"); + ret = mwifiex_11h_handle_chanrpt_ready(priv, + adapter->event_skb); + break; + case EVENT_RADAR_DETECTED: + mwifiex_dbg(adapter, EVENT, "event: Radar detected\n"); + ret = mwifiex_11h_handle_radar_detected(priv, + adapter->event_skb); + break; + case EVENT_BT_COEX_WLAN_PARA_CHANGE: + dev_dbg(adapter->dev, "EVENT: BT coex wlan param update\n"); + mwifiex_bt_coex_wlan_param_update_event(priv, + adapter->event_skb); + break; + default: + mwifiex_dbg(adapter, ERROR, "event: unknown event id: %#x\n", + eventcause); + break; + } + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c new file mode 100644 index 000000000000..a6c8a4f7bfe9 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -0,0 +1,1421 @@ +/* + * Marvell Wireless LAN device driver: functions for station ioctl + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" +#include "cfg80211.h" + +static int disconnect_on_suspend; +module_param(disconnect_on_suspend, int, 0644); + +/* + * Copies the multicast address list from device to driver. + * + * This function does not validate the destination memory for + * size, and the calling function must ensure enough memory is + * available. + */ +int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, + struct net_device *dev) +{ + int i = 0; + struct netdev_hw_addr *ha; + + netdev_for_each_mc_addr(ha, dev) + memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); + + return i; +} + +/* + * Wait queue completion handler. + * + * This function waits on a cmd wait queue. It also cancels the pending + * request after waking up, in case of errors. + */ +int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_queued) +{ + int status; + + /* Wait for completion */ + status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait, + *(cmd_queued->condition), + (12 * HZ)); + if (status <= 0) { + if (status == 0) + status = -ETIMEDOUT; + mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n", + status); + mwifiex_cancel_all_pending_cmd(adapter); + return status; + } + + status = adapter->cmd_wait_q.status; + adapter->cmd_wait_q.status = 0; + + return status; +} + +/* + * This function prepares the correct firmware command and + * issues it to set the multicast list. + * + * This function can be used to enable promiscuous mode, or enable all + * multicast packets, or to enable selective multicast. + */ +int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, + struct mwifiex_multicast_list *mcast_list) +{ + int ret = 0; + u16 old_pkt_filter; + + old_pkt_filter = priv->curr_pkt_filter; + + if (mcast_list->mode == MWIFIEX_PROMISC_MODE) { + mwifiex_dbg(priv->adapter, INFO, + "info: Enable Promiscuous mode\n"); + priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; + priv->curr_pkt_filter &= + ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + /* Multicast */ + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; + if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) { + mwifiex_dbg(priv->adapter, INFO, + "info: Enabling All Multicast!\n"); + priv->curr_pkt_filter |= + HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + } else { + priv->curr_pkt_filter &= + ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; + mwifiex_dbg(priv->adapter, INFO, + "info: Set multicast list=%d\n", + mcast_list->num_multicast_addr); + /* Send multicast addresses to firmware */ + ret = mwifiex_send_cmd(priv, + HostCmd_CMD_MAC_MULTICAST_ADR, + HostCmd_ACT_GEN_SET, 0, + mcast_list, false); + } + } + mwifiex_dbg(priv->adapter, INFO, + "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", + old_pkt_filter, priv->curr_pkt_filter); + if (old_pkt_filter != priv->curr_pkt_filter) { + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, + 0, &priv->curr_pkt_filter, false); + } + + return ret; +} + +/* + * This function fills bss descriptor structure using provided + * information. + * beacon_ie buffer is allocated in this function. It is caller's + * responsibility to free the memory. + */ +int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, + struct cfg80211_bss *bss, + struct mwifiex_bssdescriptor *bss_desc) +{ + u8 *beacon_ie; + size_t beacon_ie_len; + struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; + const struct cfg80211_bss_ies *ies; + + rcu_read_lock(); + ies = rcu_dereference(bss->ies); + beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); + beacon_ie_len = ies->len; + bss_desc->timestamp = ies->tsf; + rcu_read_unlock(); + + if (!beacon_ie) { + mwifiex_dbg(priv->adapter, ERROR, + " failed to alloc beacon_ie\n"); + return -ENOMEM; + } + + memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN); + bss_desc->rssi = bss->signal; + /* The caller of this function will free beacon_ie */ + bss_desc->beacon_buf = beacon_ie; + bss_desc->beacon_buf_size = beacon_ie_len; + bss_desc->beacon_period = bss->beacon_interval; + bss_desc->cap_info_bitmap = bss->capability; + bss_desc->bss_band = bss_priv->band; + bss_desc->fw_tsf = bss_priv->fw_tsf; + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { + mwifiex_dbg(priv->adapter, INFO, + "info: InterpretIE: AP WEP enabled\n"); + bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; + } else { + bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; + } + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS) + bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; + else + bss_desc->bss_mode = NL80211_IFTYPE_STATION; + + /* Disable 11ac by default. Enable it only where there + * exist VHT_CAP IE in AP beacon + */ + bss_desc->disable_11ac = true; + + if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT) + bss_desc->sensed_11h = true; + + return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc); +} + +void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv) +{ + if (priv->adapter->dt_node) { + char txpwr[] = {"marvell,00_txpwrlimit"}; + + memcpy(&txpwr[8], priv->adapter->country_code, 2); + mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr); + } +} + +static int mwifiex_process_country_ie(struct mwifiex_private *priv, + struct cfg80211_bss *bss) +{ + const u8 *country_ie; + u8 country_ie_len; + struct mwifiex_802_11d_domain_reg *domain_info = + &priv->adapter->domain_reg; + + rcu_read_lock(); + country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); + if (!country_ie) { + rcu_read_unlock(); + return 0; + } + + country_ie_len = country_ie[1]; + if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { + rcu_read_unlock(); + return 0; + } + + if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { + rcu_read_unlock(); + mwifiex_dbg(priv->adapter, INFO, + "11D: skip setting domain info in FW\n"); + return 0; + } + memcpy(priv->adapter->country_code, &country_ie[2], 2); + + domain_info->country_code[0] = country_ie[2]; + domain_info->country_code[1] = country_ie[3]; + domain_info->country_code[2] = ' '; + + country_ie_len -= IEEE80211_COUNTRY_STRING_LEN; + + domain_info->no_of_triplet = + country_ie_len / sizeof(struct ieee80211_country_ie_triplet); + + memcpy((u8 *)domain_info->triplet, + &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); + + rcu_read_unlock(); + + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "11D: setting domain info in FW fail\n"); + return -1; + } + + mwifiex_dnld_txpwr_table(priv); + + return 0; +} + +/* + * In Ad-Hoc mode, the IBSS is created if not found in scan list. + * In both Ad-Hoc and infra mode, an deauthentication is performed + * first. + */ +int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, + struct cfg80211_ssid *req_ssid) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bssdescriptor *bss_desc = NULL; + + priv->scan_block = false; + + if (bss) { + mwifiex_process_country_ie(priv, bss); + + /* Allocate and fill new bss descriptor */ + bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), + GFP_KERNEL); + if (!bss_desc) + return -ENOMEM; + + ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); + if (ret) + goto done; + } + + if (priv->bss_mode == NL80211_IFTYPE_STATION || + priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { + u8 config_bands; + + if (!bss_desc) + return -1; + + if (mwifiex_band_to_radio_type(bss_desc->bss_band) == + HostCmd_SCAN_RADIO_TYPE_BG) { + config_bands = BAND_B | BAND_G | BAND_GN; + } else { + config_bands = BAND_A | BAND_AN; + if (adapter->fw_bands & BAND_AAC) + config_bands |= BAND_AAC; + } + + if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) + adapter->config_bands = config_bands; + + ret = mwifiex_check_network_compatibility(priv, bss_desc); + if (ret) + goto done; + + if (mwifiex_11h_get_csa_closed_channel(priv) == + (u8)bss_desc->channel) { + mwifiex_dbg(adapter, ERROR, + "Attempt to reconnect on csa closed chan(%d)\n", + bss_desc->channel); + goto done; + } + + mwifiex_dbg(adapter, INFO, + "info: SSID found in scan list ...\t" + "associating...\n"); + + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + + /* Clear any past association response stored for + * application retrieval */ + priv->assoc_rsp_size = 0; + ret = mwifiex_associate(priv, bss_desc); + + /* If auth type is auto and association fails using open mode, + * try to connect using shared mode */ + if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && + priv->sec_info.is_authtype_auto && + priv->sec_info.wep_enabled) { + priv->sec_info.authentication_mode = + NL80211_AUTHTYPE_SHARED_KEY; + ret = mwifiex_associate(priv, bss_desc); + } + + if (bss) + cfg80211_put_bss(priv->adapter->wiphy, bss); + } else { + /* Adhoc mode */ + /* If the requested SSID matches current SSID, return */ + if (bss_desc && bss_desc->ssid.ssid_len && + (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor. + ssid, &bss_desc->ssid))) { + ret = 0; + goto done; + } + + priv->adhoc_is_link_sensed = false; + + ret = mwifiex_check_network_compatibility(priv, bss_desc); + + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + + if (!ret) { + mwifiex_dbg(adapter, INFO, + "info: network found in scan\t" + " list. Joining...\n"); + ret = mwifiex_adhoc_join(priv, bss_desc); + if (bss) + cfg80211_put_bss(priv->adapter->wiphy, bss); + } else { + mwifiex_dbg(adapter, INFO, + "info: Network not found in\t" + "the list, creating adhoc with ssid = %s\n", + req_ssid->ssid); + ret = mwifiex_adhoc_start(priv, req_ssid); + } + } + +done: + /* beacon_ie buffer was allocated in function + * mwifiex_fill_new_bss_desc(). Free it now. + */ + if (bss_desc) + kfree(bss_desc->beacon_buf); + kfree(bss_desc); + return ret; +} + +/* + * IOCTL request handler to set host sleep configuration. + * + * This function prepares the correct firmware command and + * issues it. + */ +int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, + int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg) + +{ + struct mwifiex_adapter *adapter = priv->adapter; + int status = 0; + u32 prev_cond = 0; + + if (!hs_cfg) + return -ENOMEM; + + switch (action) { + case HostCmd_ACT_GEN_SET: + if (adapter->pps_uapsd_mode) { + mwifiex_dbg(adapter, INFO, + "info: Host Sleep IOCTL\t" + "is blocked in UAPSD/PPS mode\n"); + status = -1; + break; + } + if (hs_cfg->is_invoke_hostcmd) { + if (hs_cfg->conditions == HS_CFG_CANCEL) { + if (!adapter->is_hs_configured) + /* Already cancelled */ + break; + /* Save previous condition */ + prev_cond = le32_to_cpu(adapter->hs_cfg + .conditions); + adapter->hs_cfg.conditions = + cpu_to_le32(hs_cfg->conditions); + } else if (hs_cfg->conditions) { + adapter->hs_cfg.conditions = + cpu_to_le32(hs_cfg->conditions); + adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; + if (hs_cfg->gap) + adapter->hs_cfg.gap = (u8)hs_cfg->gap; + } else if (adapter->hs_cfg.conditions == + cpu_to_le32(HS_CFG_CANCEL)) { + /* Return failure if no parameters for HS + enable */ + status = -1; + break; + } + + status = mwifiex_send_cmd(priv, + HostCmd_CMD_802_11_HS_CFG_ENH, + HostCmd_ACT_GEN_SET, 0, + &adapter->hs_cfg, + cmd_type == MWIFIEX_SYNC_CMD); + + if (hs_cfg->conditions == HS_CFG_CANCEL) + /* Restore previous condition */ + adapter->hs_cfg.conditions = + cpu_to_le32(prev_cond); + } else { + adapter->hs_cfg.conditions = + cpu_to_le32(hs_cfg->conditions); + adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; + adapter->hs_cfg.gap = (u8)hs_cfg->gap; + } + break; + case HostCmd_ACT_GEN_GET: + hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions); + hs_cfg->gpio = adapter->hs_cfg.gpio; + hs_cfg->gap = adapter->hs_cfg.gap; + break; + default: + status = -1; + break; + } + + return status; +} + +/* + * Sends IOCTL request to cancel the existing Host Sleep configuration. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type) +{ + struct mwifiex_ds_hs_cfg hscfg; + + hscfg.conditions = HS_CFG_CANCEL; + hscfg.is_invoke_hostcmd = true; + + return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, + cmd_type, &hscfg); +} +EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); + +/* + * Sends IOCTL request to cancel the existing Host Sleep configuration. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int mwifiex_enable_hs(struct mwifiex_adapter *adapter) +{ + struct mwifiex_ds_hs_cfg hscfg; + struct mwifiex_private *priv; + int i; + + if (disconnect_on_suspend) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + mwifiex_deauthenticate(priv, NULL); + } + } + + if (adapter->hs_activated) { + mwifiex_dbg(adapter, CMD, + "cmd: HS Already activated\n"); + return true; + } + + adapter->hs_activate_wait_q_woken = false; + + memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); + hscfg.is_invoke_hostcmd = true; + + adapter->hs_enabling = true; + mwifiex_cancel_all_pending_cmd(adapter); + + if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_STA), + HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, + &hscfg)) { + mwifiex_dbg(adapter, ERROR, + "IOCTL request HS enable failed\n"); + return false; + } + + if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q, + adapter->hs_activate_wait_q_woken, + (10 * HZ)) <= 0) { + mwifiex_dbg(adapter, ERROR, + "hs_activate_wait_q terminated\n"); + return false; + } + + return true; +} +EXPORT_SYMBOL_GPL(mwifiex_enable_hs); + +/* + * IOCTL request handler to get BSS information. + * + * This function collates the information from different driver structures + * to send to the user. + */ +int mwifiex_get_bss_info(struct mwifiex_private *priv, + struct mwifiex_bss_info *info) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bssdescriptor *bss_desc; + + if (!info) + return -1; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + + info->bss_mode = priv->bss_mode; + + memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid)); + + memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN); + + info->bss_chan = bss_desc->channel; + + memcpy(info->country_code, adapter->country_code, + IEEE80211_COUNTRY_STRING_LEN); + + info->media_connected = priv->media_connected; + + info->max_power_level = priv->max_tx_power_level; + info->min_power_level = priv->min_tx_power_level; + + info->adhoc_state = priv->adhoc_state; + + info->bcn_nf_last = priv->bcn_nf_last; + + if (priv->sec_info.wep_enabled) + info->wep_status = true; + else + info->wep_status = false; + + info->is_hs_configured = adapter->is_hs_configured; + info->is_deep_sleep = adapter->is_deep_sleep; + + return 0; +} + +/* + * The function disables auto deep sleep mode. + */ +int mwifiex_disable_auto_ds(struct mwifiex_private *priv) +{ + struct mwifiex_ds_auto_ds auto_ds; + + auto_ds.auto_ds = DEEP_SLEEP_OFF; + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true); +} +EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds); + +/* + * Sends IOCTL request to get the data rate. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate) +{ + int ret; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, + HostCmd_ACT_GEN_GET, 0, NULL, true); + + if (!ret) { + if (priv->is_data_rate_auto) + *rate = mwifiex_index_to_data_rate(priv, priv->tx_rate, + priv->tx_htinfo); + else + *rate = priv->data_rate; + } + + return ret; +} + +/* + * IOCTL request handler to set tx power configuration. + * + * This function prepares the correct firmware command and + * issues it. + * + * For non-auto power mode, all the following power groups are set - + * - Modulation class HR/DSSS + * - Modulation class OFDM + * - Modulation class HTBW20 + * - Modulation class HTBW40 + */ +int mwifiex_set_tx_power(struct mwifiex_private *priv, + struct mwifiex_power_cfg *power_cfg) +{ + int ret; + struct host_cmd_ds_txpwr_cfg *txp_cfg; + struct mwifiex_types_power_group *pg_tlv; + struct mwifiex_power_group *pg; + u8 *buf; + u16 dbm = 0; + + if (!power_cfg->is_power_auto) { + dbm = (u16) power_cfg->power_level; + if ((dbm < priv->min_tx_power_level) || + (dbm > priv->max_tx_power_level)) { + mwifiex_dbg(priv->adapter, ERROR, + "txpower value %d dBm\t" + "is out of range (%d dBm-%d dBm)\n", + dbm, priv->min_tx_power_level, + priv->max_tx_power_level); + return -1; + } + } + buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf; + txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET); + if (!power_cfg->is_power_auto) { + txp_cfg->mode = cpu_to_le32(1); + pg_tlv = (struct mwifiex_types_power_group *) + (buf + sizeof(struct host_cmd_ds_txpwr_cfg)); + pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP); + pg_tlv->length = + cpu_to_le16(4 * sizeof(struct mwifiex_power_group)); + pg = (struct mwifiex_power_group *) + (buf + sizeof(struct host_cmd_ds_txpwr_cfg) + + sizeof(struct mwifiex_types_power_group)); + /* 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 = (s8) dbm; + pg->power_max = (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 = (s8) dbm; + pg->power_max = (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 = (s8) dbm; + pg->power_max = (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 = (s8) dbm; + pg->power_max = (s8) dbm; + pg->ht_bandwidth = HT_BW_40; + } + ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG, + HostCmd_ACT_GEN_SET, 0, buf, true); + + kfree(buf); + return ret; +} + +/* + * IOCTL request handler to get power save mode. + * + * This function prepares the correct firmware command and + * issues it. + */ +int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode) +{ + int ret; + struct mwifiex_adapter *adapter = priv->adapter; + u16 sub_cmd; + + if (*ps_mode) + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; + else + adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; + sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + sub_cmd, BITMAP_STA_PS, NULL, true); + if ((!ret) && (sub_cmd == DIS_AUTO_PS)) + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, + GET_PS, 0, NULL, false); + + return ret; +} + +/* + * IOCTL request handler to set/reset WPA IE. + * + * The supplied WPA IE is treated as a opaque buffer. Only the first field + * is checked to determine WPA version. If buffer length is zero, the existing + * WPA IE is reset. + */ +static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, + u8 *ie_data_ptr, u16 ie_len) +{ + if (ie_len) { + if (ie_len > sizeof(priv->wpa_ie)) { + mwifiex_dbg(priv->adapter, ERROR, + "failed to copy WPA IE, too big\n"); + return -1; + } + memcpy(priv->wpa_ie, ie_data_ptr, ie_len); + priv->wpa_ie_len = (u8) ie_len; + mwifiex_dbg(priv->adapter, CMD, + "cmd: Set Wpa_ie_len=%d IE=%#x\n", + priv->wpa_ie_len, priv->wpa_ie[0]); + + if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) { + priv->sec_info.wpa_enabled = true; + } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { + priv->sec_info.wpa2_enabled = true; + } else { + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + } + } else { + memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); + priv->wpa_ie_len = 0; + mwifiex_dbg(priv->adapter, INFO, + "info: reset wpa_ie_len=%d IE=%#x\n", + priv->wpa_ie_len, priv->wpa_ie[0]); + priv->sec_info.wpa_enabled = false; + priv->sec_info.wpa2_enabled = false; + } + + return 0; +} + +/* + * IOCTL request handler to set/reset WAPI IE. + * + * The supplied WAPI IE is treated as a opaque buffer. Only the first field + * is checked to internally enable WAPI. If buffer length is zero, the existing + * WAPI IE is reset. + */ +static int mwifiex_set_wapi_ie(struct mwifiex_private *priv, + u8 *ie_data_ptr, u16 ie_len) +{ + if (ie_len) { + if (ie_len > sizeof(priv->wapi_ie)) { + mwifiex_dbg(priv->adapter, ERROR, + "info: failed to copy WAPI IE, too big\n"); + return -1; + } + memcpy(priv->wapi_ie, ie_data_ptr, ie_len); + priv->wapi_ie_len = ie_len; + mwifiex_dbg(priv->adapter, CMD, + "cmd: Set wapi_ie_len=%d IE=%#x\n", + priv->wapi_ie_len, priv->wapi_ie[0]); + + if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY) + priv->sec_info.wapi_enabled = true; + } else { + memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie)); + priv->wapi_ie_len = ie_len; + mwifiex_dbg(priv->adapter, INFO, + "info: Reset wapi_ie_len=%d IE=%#x\n", + priv->wapi_ie_len, priv->wapi_ie[0]); + priv->sec_info.wapi_enabled = false; + } + return 0; +} + +/* + * IOCTL request handler to set/reset WPS IE. + * + * The supplied WPS IE is treated as a opaque buffer. Only the first field + * is checked to internally enable WPS. If buffer length is zero, the existing + * WPS IE is reset. + */ +static int mwifiex_set_wps_ie(struct mwifiex_private *priv, + u8 *ie_data_ptr, u16 ie_len) +{ + if (ie_len) { + if (ie_len > MWIFIEX_MAX_VSIE_LEN) { + mwifiex_dbg(priv->adapter, ERROR, + "info: failed to copy WPS IE, too big\n"); + return -1; + } + + priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL); + if (!priv->wps_ie) + return -ENOMEM; + + memcpy(priv->wps_ie, ie_data_ptr, ie_len); + priv->wps_ie_len = ie_len; + mwifiex_dbg(priv->adapter, CMD, + "cmd: Set wps_ie_len=%d IE=%#x\n", + priv->wps_ie_len, priv->wps_ie[0]); + } else { + kfree(priv->wps_ie); + priv->wps_ie_len = ie_len; + mwifiex_dbg(priv->adapter, INFO, + "info: Reset wps_ie_len=%d\n", priv->wps_ie_len); + } + return 0; +} + +/* + * IOCTL request handler to set WAPI key. + * + * This function prepares the correct firmware command and + * issues it. + */ +static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv, + struct mwifiex_ds_encrypt_key *encrypt_key) +{ + + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, + encrypt_key, true); +} + +/* + * IOCTL request handler to set WEP network key. + * + * This function prepares the correct firmware command and + * issues it, after validation checks. + */ +static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, + struct mwifiex_ds_encrypt_key *encrypt_key) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + struct mwifiex_wep_key *wep_key; + int index; + + if (priv->wep_key_curr_index >= NUM_WEP_KEYS) + priv->wep_key_curr_index = 0; + wep_key = &priv->wep_key[priv->wep_key_curr_index]; + index = encrypt_key->key_index; + if (encrypt_key->key_disable) { + priv->sec_info.wep_enabled = 0; + } else if (!encrypt_key->key_len) { + /* Copy the required key as the current key */ + wep_key = &priv->wep_key[index]; + if (!wep_key->key_length) { + mwifiex_dbg(adapter, ERROR, + "key not set, so cannot enable it\n"); + return -1; + } + + if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) { + memcpy(encrypt_key->key_material, + wep_key->key_material, wep_key->key_length); + encrypt_key->key_len = wep_key->key_length; + } + + priv->wep_key_curr_index = (u16) index; + priv->sec_info.wep_enabled = 1; + } else { + wep_key = &priv->wep_key[index]; + memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); + /* Copy the key in the driver */ + memcpy(wep_key->key_material, + encrypt_key->key_material, + encrypt_key->key_len); + wep_key->key_index = index; + wep_key->key_length = encrypt_key->key_len; + priv->sec_info.wep_enabled = 1; + } + if (wep_key->key_length) { + void *enc_key; + + if (encrypt_key->key_disable) { + memset(&priv->wep_key[index], 0, + sizeof(struct mwifiex_wep_key)); + if (wep_key->key_length) + goto done; + } + + if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) + enc_key = encrypt_key; + else + enc_key = NULL; + + /* Send request to firmware */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, 0, enc_key, false); + if (ret) + return ret; + } + +done: + if (priv->sec_info.wep_enabled) + priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; + + ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true); + + return ret; +} + +/* + * IOCTL request handler to set WPA key. + * + * This function prepares the correct firmware command and + * issues it, after validation checks. + * + * Current driver only supports key length of up to 32 bytes. + * + * This function can also be used to disable a currently set key. + */ +static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv, + struct mwifiex_ds_encrypt_key *encrypt_key) +{ + int ret; + u8 remove_key = false; + struct host_cmd_ds_802_11_key_material *ibss_key; + + /* Current driver only supports key length of up to 32 bytes */ + if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) { + mwifiex_dbg(priv->adapter, ERROR, + "key length too long\n"); + return -1; + } + + if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { + /* + * IBSS/WPA-None uses only one key (Group) for both receiving + * and sending unicast and multicast packets. + */ + /* Send the key as PTK to firmware */ + encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, encrypt_key, false); + if (ret) + return ret; + + ibss_key = &priv->aes_key; + memset(ibss_key, 0, + sizeof(struct host_cmd_ds_802_11_key_material)); + /* Copy the key in the driver */ + memcpy(ibss_key->key_param_set.key, encrypt_key->key_material, + encrypt_key->key_len); + memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len, + sizeof(ibss_key->key_param_set.key_len)); + ibss_key->key_param_set.key_type_id + = cpu_to_le16(KEY_TYPE_ID_TKIP); + ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); + + /* Send the key as GTK to firmware */ + encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST; + } + + if (!encrypt_key->key_index) + encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; + + if (remove_key) + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + !KEY_INFO_ENABLED, encrypt_key, true); + else + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, + HostCmd_ACT_GEN_SET, + KEY_INFO_ENABLED, encrypt_key, true); + + return ret; +} + +/* + * IOCTL request handler to set/get network keys. + * + * This is a generic key handling function which supports WEP, WPA + * and WAPI. + */ +static int +mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv, + struct mwifiex_ds_encrypt_key *encrypt_key) +{ + int status; + + if (encrypt_key->is_wapi_key) + status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key); + else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104) + status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key); + else + status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key); + return status; +} + +/* + * This function returns the driver version. + */ +int +mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version, + int max_len) +{ + union { + __le32 l; + u8 c[4]; + } ver; + char fw_ver[32]; + + ver.l = cpu_to_le32(adapter->fw_release_number); + sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]); + + snprintf(version, max_len, driver_version, fw_ver); + + mwifiex_dbg(adapter, MSG, "info: MWIFIEX VERSION: %s\n", version); + + return 0; +} + +/* + * Sends IOCTL request to set encoding parameters. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, + const u8 *key, int key_len, u8 key_index, + const u8 *mac_addr, int disable) +{ + struct mwifiex_ds_encrypt_key encrypt_key; + + memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); + encrypt_key.key_len = key_len; + encrypt_key.key_index = key_index; + + if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC) + encrypt_key.is_igtk_key = true; + + if (!disable) { + if (key_len) + memcpy(encrypt_key.key_material, key, key_len); + else + encrypt_key.is_current_wep_key = true; + + if (mac_addr) + memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); + if (kp && kp->seq && kp->seq_len) { + memcpy(encrypt_key.pn, kp->seq, kp->seq_len); + encrypt_key.pn_len = kp->seq_len; + encrypt_key.is_rx_seq_valid = true; + } + } else { + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) + return 0; + encrypt_key.key_disable = true; + if (mac_addr) + memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); + } + + return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key); +} + +/* + * Sends IOCTL request to get extended version. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_get_ver_ext(struct mwifiex_private *priv) +{ + struct mwifiex_ver_ext ver_ext; + + memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); + if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT, + HostCmd_ACT_GEN_GET, 0, &ver_ext, true)) + return -1; + + return 0; +} + +int +mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, + struct ieee80211_channel *chan, + unsigned int duration) +{ + struct host_cmd_ds_remain_on_chan roc_cfg; + u8 sc; + + memset(&roc_cfg, 0, sizeof(roc_cfg)); + roc_cfg.action = cpu_to_le16(action); + if (action == HostCmd_ACT_GEN_SET) { + roc_cfg.band_cfg = chan->band; + sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT); + roc_cfg.band_cfg |= (sc << 2); + + roc_cfg.channel = + ieee80211_frequency_to_channel(chan->center_freq); + roc_cfg.duration = cpu_to_le32(duration); + } + if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN, + action, 0, &roc_cfg, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "failed to remain on channel\n"); + return -1; + } + + return roc_cfg.status; +} + +/* + * Sends IOCTL request to get statistics information. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_get_stats_info(struct mwifiex_private *priv, + struct mwifiex_ds_get_stats *log) +{ + return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG, + HostCmd_ACT_GEN_GET, 0, log, true); +} + +/* + * IOCTL request handler to read/write register. + * + * This function prepares the correct firmware command and + * issues it. + * + * Access to the following registers are supported - + * - MAC + * - BBP + * - RF + * - PMIC + * - CAU + */ +static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv, + struct mwifiex_ds_reg_rw *reg_rw, + u16 action) +{ + u16 cmd_no; + + switch (le32_to_cpu(reg_rw->type)) { + case MWIFIEX_REG_MAC: + cmd_no = HostCmd_CMD_MAC_REG_ACCESS; + break; + case MWIFIEX_REG_BBP: + cmd_no = HostCmd_CMD_BBP_REG_ACCESS; + break; + case MWIFIEX_REG_RF: + cmd_no = HostCmd_CMD_RF_REG_ACCESS; + break; + case MWIFIEX_REG_PMIC: + cmd_no = HostCmd_CMD_PMIC_REG_ACCESS; + break; + case MWIFIEX_REG_CAU: + cmd_no = HostCmd_CMD_CAU_REG_ACCESS; + break; + default: + return -1; + } + + return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true); +} + +/* + * Sends IOCTL request to write to a register. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, + u32 reg_offset, u32 reg_value) +{ + struct mwifiex_ds_reg_rw reg_rw; + + reg_rw.type = cpu_to_le32(reg_type); + reg_rw.offset = cpu_to_le32(reg_offset); + reg_rw.value = cpu_to_le32(reg_value); + + return mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_SET); +} + +/* + * Sends IOCTL request to read from a register. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, + u32 reg_offset, u32 *value) +{ + int ret; + struct mwifiex_ds_reg_rw reg_rw; + + reg_rw.type = cpu_to_le32(reg_type); + reg_rw.offset = cpu_to_le32(reg_offset); + ret = mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_GET); + + if (ret) + goto done; + + *value = le32_to_cpu(reg_rw.value); + +done: + return ret; +} + +/* + * Sends IOCTL request to read from EEPROM. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, + u8 *value) +{ + int ret; + struct mwifiex_ds_read_eeprom rd_eeprom; + + rd_eeprom.offset = cpu_to_le16((u16) offset); + rd_eeprom.byte_count = cpu_to_le16((u16) bytes); + + /* Send request to firmware */ + ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, + HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true); + + if (!ret) + memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA); + return ret; +} + +/* + * This function sets a generic IE. In addition to generic IE, it can + * also handle WPA, WPA2 and WAPI IEs. + */ +static int +mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, + u16 ie_len) +{ + int ret = 0; + struct ieee_types_vendor_header *pvendor_ie; + const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; + const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; + + /* If the passed length is zero, reset the buffer */ + if (!ie_len) { + priv->gen_ie_buf_len = 0; + priv->wps.session_enable = false; + + return 0; + } else if (!ie_data_ptr) { + return -1; + } + pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; + /* Test to see if it is a WPA IE, if not, then it is a gen IE */ + if (((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && + (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) || + (pvendor_ie->element_id == WLAN_EID_RSN)) { + + /* IE is a WPA/WPA2 IE so call set_wpa function */ + ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); + priv->wps.session_enable = false; + + return ret; + } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { + /* IE is a WAPI IE so call set_wapi function */ + ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len); + + return ret; + } + /* + * 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 = (struct ieee_types_vendor_header *) ie_data_ptr; + if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && + (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui)))) { + priv->wps.session_enable = true; + mwifiex_dbg(priv->adapter, INFO, + "info: WPS Session Enabled.\n"); + ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len); + } + + /* Append the passed data to the end of the + genIeBuffer */ + memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, + ie_len); + /* Increment the stored buffer length by the + size passed */ + priv->gen_ie_buf_len += ie_len; + } else { + /* Passed data does not fit in the remaining + buffer space */ + ret = -1; + } + + /* Return 0, or -1 for error case */ + return ret; +} + +/* + * IOCTL request handler to set/get generic IE. + * + * In addition to various generic IEs, this function can also be + * used to set the ARP filter. + */ +static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, + struct mwifiex_ds_misc_gen_ie *gen_ie, + u16 action) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + switch (gen_ie->type) { + case MWIFIEX_IE_TYPE_GEN_IE: + if (action == HostCmd_ACT_GEN_GET) { + gen_ie->len = priv->wpa_ie_len; + memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); + } else { + mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data, + (u16) gen_ie->len); + } + break; + case MWIFIEX_IE_TYPE_ARP_FILTER: + memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); + if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { + adapter->arp_filter_size = 0; + mwifiex_dbg(adapter, ERROR, + "invalid ARP filter size\n"); + return -1; + } else { + memcpy(adapter->arp_filter, gen_ie->ie_data, + gen_ie->len); + adapter->arp_filter_size = gen_ie->len; + } + break; + default: + mwifiex_dbg(adapter, ERROR, "invalid IE type\n"); + return -1; + } + return 0; +} + +/* + * Sends IOCTL request to set a generic IE. + * + * This function allocates the IOCTL request buffer, fills it + * with requisite parameters and calls the IOCTL handler. + */ +int +mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len) +{ + struct mwifiex_ds_misc_gen_ie gen_ie; + + if (ie_len > IEEE_MAX_IE_SIZE) + return -EFAULT; + + gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE; + gen_ie.len = ie_len; + memcpy(gen_ie.ie_data, ie, ie_len); + if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET)) + return -EFAULT; + + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_rx.c b/drivers/net/wireless/marvell/mwifiex/sta_rx.c new file mode 100644 index 000000000000..d4d4cb1ce95b --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_rx.c @@ -0,0 +1,267 @@ +/* + * Marvell Wireless LAN device driver: station RX data handling + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include +#include +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "11n_aggr.h" +#include "11n_rxreorder.h" + +/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement + * frame. If frame has both source and destination mac address as same, this + * function drops such gratuitous frames. + */ +static bool +mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + const struct mwifiex_arp_eth_header *arp; + struct ethhdr *eth; + struct ipv6hdr *ipv6; + struct icmp6hdr *icmpv6; + + eth = (struct ethhdr *)skb->data; + switch (ntohs(eth->h_proto)) { + case ETH_P_ARP: + arp = (void *)(skb->data + sizeof(struct ethhdr)); + if (arp->hdr.ar_op == htons(ARPOP_REPLY) || + arp->hdr.ar_op == htons(ARPOP_REQUEST)) { + if (!memcmp(arp->ar_sip, arp->ar_tip, 4)) + return true; + } + break; + case ETH_P_IPV6: + ipv6 = (void *)(skb->data + sizeof(struct ethhdr)); + icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) + + sizeof(struct ipv6hdr)); + if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) { + if (!memcmp(&ipv6->saddr, &ipv6->daddr, + sizeof(struct in6_addr))) + return true; + } + break; + default: + break; + } + + return false; +} + +/* + * This function processes the received packet and forwards it + * to kernel/upper layer. + * + * This function parses through the received packet and determines + * if it is a debug packet or normal packet. + * + * For non-debug packets, the function chops off unnecessary leading + * header bytes, reconstructs the packet as an ethernet frame or + * 802.2/llc/snap frame as required, and sends it to kernel/upper layer. + * + * The completion callback is called after processing in complete. + */ +int mwifiex_process_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + int ret; + struct rx_packet_hdr *rx_pkt_hdr; + struct rxpd *local_rx_pd; + int hdr_chop; + struct ethhdr *eth; + u16 rx_pkt_off, rx_pkt_len; + u8 *offset; + u8 adj_rx_rate = 0; + + local_rx_pd = (struct rxpd *) (skb->data); + + rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length); + rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off; + + if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, + sizeof(bridge_tunnel_header))) || + (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, + sizeof(rfc1042_header)) && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { + /* + * 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. + */ + eth = (struct ethhdr *) + ((u8 *) &rx_pkt_hdr->eth803_hdr + + sizeof(rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->rfc1042_hdr) + - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) + - sizeof(rx_pkt_hdr->eth803_hdr.h_source) + - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); + + memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source, + sizeof(eth->h_source)); + memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, + sizeof(eth->h_dest)); + + /* Chop off the rxpd + the excess memory from the 802.2/llc/snap + header that was removed. */ + hdr_chop = (u8 *) eth - (u8 *) local_rx_pd; + } else { + /* Chop off the rxpd */ + hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr - + (u8 *) local_rx_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 */ + skb_pull(skb, hdr_chop); + + if (priv->hs2_enabled && + mwifiex_discard_gratuitous_arp(priv, skb)) { + mwifiex_dbg(priv->adapter, INFO, "Bypassed Gratuitous ARP\n"); + dev_kfree_skb_any(skb); + return 0; + } + + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { + offset = (u8 *)local_rx_pd + rx_pkt_off; + mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len); + } + + priv->rxpd_rate = local_rx_pd->rx_rate; + + priv->rxpd_htinfo = local_rx_pd->ht_info; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || + GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate, + priv->rxpd_htinfo); + mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr, + local_rx_pd->nf); + } + + ret = mwifiex_recv_packet(priv, skb); + if (ret == -1) + mwifiex_dbg(priv->adapter, ERROR, + "recv packet failed\n"); + + return ret; +} + +/* + * This function processes the received buffer. + * + * The function looks into the RxPD and performs sanity tests on the + * received buffer to ensure its a valid packet, before processing it + * further. If the packet is determined to be aggregated, it is + * de-aggregated accordingly. Non-unicast packets are sent directly to + * the kernel/upper layers. Unicast packets are handed over to the + * Rx reordering routine if 11n is enabled. + * + * The completion callback is called after processing in complete. + */ +int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret = 0; + struct rxpd *local_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u8 ta[ETH_ALEN]; + u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num; + struct mwifiex_sta_node *sta_ptr; + + local_rx_pd = (struct rxpd *) (skb->data); + rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type); + rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset); + rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length); + seq_num = le16_to_cpu(local_rx_pd->seq_num); + + rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset; + + if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) { + mwifiex_dbg(adapter, ERROR, + "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n", + skb->len, rx_pkt_offset, rx_pkt_length); + priv->stats.rx_dropped++; + dev_kfree_skb_any(skb); + return ret; + } + + if (rx_pkt_type == PKT_TYPE_MGMT) { + ret = mwifiex_process_mgmt_packet(priv, skb); + if (ret) + mwifiex_dbg(adapter, ERROR, "Rx of mgmt packet failed"); + dev_kfree_skb_any(skb); + return ret; + } + + /* + * 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) && + !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) || + !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) { + mwifiex_process_rx_packet(priv, skb); + return ret; + } + + if (mwifiex_queuing_ra_based(priv) || + (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) { + memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); + if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET && + local_rx_pd->priority < MAX_NUM_TID) { + sta_ptr = mwifiex_get_sta_entry(priv, ta); + if (sta_ptr) + sta_ptr->rx_seq[local_rx_pd->priority] = + le16_to_cpu(local_rx_pd->seq_num); + mwifiex_auto_tdls_update_peer_signal(priv, ta, + local_rx_pd->snr, + local_rx_pd->nf); + } + } else { + if (rx_pkt_type != PKT_TYPE_BAR) + priv->rx_seq[local_rx_pd->priority] = seq_num; + memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, + ETH_ALEN); + } + + /* Reorder and send to OS */ + ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority, + ta, (u8) rx_pkt_type, skb); + + if (ret || (rx_pkt_type == PKT_TYPE_BAR)) + dev_kfree_skb_any(skb); + + if (ret) + priv->stats.rx_dropped++; + + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/sta_tx.c b/drivers/net/wireless/marvell/mwifiex/sta_tx.c new file mode 100644 index 000000000000..f6683ea6bd5d --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/sta_tx.c @@ -0,0 +1,244 @@ +/* + * Marvell Wireless LAN device driver: station TX data handling + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" + +/* + * This function fills the TxPD for tx packets. + * + * The Tx buffer received by this function should already have the + * header space allocated for TxPD. + * + * This function inserts the TxPD in between interface header and actual + * data and adjusts the buffer pointers accordingly. + * + * The following TxPD fields are set by this function, as required - + * - BSS number + * - Tx packet length and offset + * - Priority + * - Packet delay + * - Priority specific Tx control + * - Flags + */ +void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct txpd *local_tx_pd; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + unsigned int pad; + u16 pkt_type, pkt_offset; + int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : + INTF_HEADER_LEN; + + if (!skb->len) { + mwifiex_dbg(adapter, ERROR, + "Tx: bad packet length: %d\n", skb->len); + tx_info->status_code = -1; + return skb->data; + } + + BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); + + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + + pad = ((void *)skb->data - (sizeof(*local_tx_pd) + hroom)- + NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); + skb_push(skb, sizeof(*local_tx_pd) + pad); + + local_tx_pd = (struct txpd *) skb->data; + memset(local_tx_pd, 0, sizeof(struct txpd)); + local_tx_pd->bss_num = priv->bss_num; + local_tx_pd->bss_type = priv->bss_type; + local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len - + (sizeof(struct txpd) + + pad))); + + local_tx_pd->priority = (u8) skb->priority; + local_tx_pd->pkt_delay_2ms = + mwifiex_wmm_compute_drv_pkt_delay(priv, skb); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS || + tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) { + local_tx_pd->tx_token_id = tx_info->ack_frame_id; + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS; + } + + if (local_tx_pd->priority < + ARRAY_SIZE(priv->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 + */ + local_tx_pd->tx_control = + cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd-> + priority]); + + if (adapter->pps_uapsd_mode) { + if (mwifiex_check_last_packet_indication(priv)) { + adapter->tx_lock_flag = true; + local_tx_pd->flags = + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; + } + } + + if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) + local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; + + /* Offset of actual data */ + pkt_offset = sizeof(struct txpd) + pad; + if (pkt_type == PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type); + pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; + } + + local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset); + + /* make space for INTF_HEADER_LEN */ + skb_push(skb, hroom); + + if (!local_tx_pd->tx_control) + /* TxCtrl set by user or default */ + local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + + return skb->data; +} + +/* + * This function tells firmware to send a NULL data packet. + * + * The function creates a NULL data packet with TxPD and sends to the + * firmware for transmission, with highest priority setting. + */ +int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct txpd *local_tx_pd; + struct mwifiex_tx_param tx_param; +/* sizeof(struct txpd) + Interface specific header */ +#define NULL_PACKET_HDR 64 + u32 data_len = NULL_PACKET_HDR; + struct sk_buff *skb; + int ret; + struct mwifiex_txinfo *tx_info = NULL; + + if (adapter->surprise_removed) + return -1; + + if (!priv->media_connected) + return -1; + + if (adapter->data_sent) + return -1; + + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + return -1; + + skb = dev_alloc_skb(data_len); + if (!skb) + return -1; + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->pkt_len = data_len - (sizeof(struct txpd) + INTF_HEADER_LEN); + skb_reserve(skb, sizeof(struct txpd) + INTF_HEADER_LEN); + skb_push(skb, sizeof(struct txpd)); + + local_tx_pd = (struct txpd *) skb->data; + local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + local_tx_pd->flags = flags; + local_tx_pd->priority = WMM_HIGHEST_PRIORITY; + local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); + local_tx_pd->bss_num = priv->bss_num; + local_tx_pd->bss_type = priv->bss_type; + + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, + skb, NULL); + } else { + skb_push(skb, INTF_HEADER_LEN); + tx_param.next_pkt_len = 0; + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, + skb, &tx_param); + } + switch (ret) { + case -EBUSY: + dev_kfree_skb_any(skb); + mwifiex_dbg(adapter, ERROR, + "%s: host_to_card failed: ret=%d\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + break; + case -1: + dev_kfree_skb_any(skb); + mwifiex_dbg(adapter, ERROR, + "%s: host_to_card failed: ret=%d\n", + __func__, ret); + adapter->dbg.num_tx_host_to_card_failure++; + break; + case 0: + dev_kfree_skb_any(skb); + mwifiex_dbg(adapter, DATA, + "data: %s: host_to_card succeeded\n", + __func__); + adapter->tx_lock_flag = true; + break; + case -EINPROGRESS: + adapter->tx_lock_flag = true; + break; + default: + break; + } + + return ret; +} + +/* + * This function checks if we need to send last packet indication. + */ +u8 +mwifiex_check_last_packet_indication(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u8 ret = false; + + if (!adapter->sleep_period.period) + return ret; + if (mwifiex_wmm_lists_empty(adapter)) + ret = true; + + if (ret && !adapter->cmd_sent && !adapter->curr_cmd && + !is_command_pending(adapter)) { + adapter->delay_null_pkt = false; + ret = true; + } else { + ret = false; + adapter->delay_null_pkt = true; + } + return ret; +} diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c new file mode 100644 index 000000000000..9275f9c3f869 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -0,0 +1,1500 @@ +/* Marvell Wireless LAN device driver: TDLS handling + * + * Copyright (C) 2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available on the worldwide web at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, 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 "main.h" +#include "wmm.h" +#include "11n.h" +#include "11n_rxreorder.h" +#include "11ac.h" + +#define TDLS_REQ_FIX_LEN 6 +#define TDLS_RESP_FIX_LEN 8 +#define TDLS_CONFIRM_FIX_LEN 6 +#define MWIFIEX_TDLS_WMM_INFO_SIZE 7 + +static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, + const u8 *mac, u8 status) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct list_head *tid_list; + struct sk_buff *skb, *tmp; + struct mwifiex_txinfo *tx_info; + unsigned long flags; + u32 tid; + u8 tid_down; + + mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { + if (!ether_addr_equal(mac, skb->data)) + continue; + + __skb_unlink(skb, &priv->tdls_txq); + tx_info = MWIFIEX_SKB_TXCB(skb); + tid = skb->priority; + tid_down = mwifiex_wmm_downgrade_tid(priv, tid); + + if (mwifiex_is_tdls_link_setup(status)) { + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac); + ra_list->tdls_link = true; + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + } else { + tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list; + if (!list_empty(tid_list)) + ra_list = list_first_entry(tid_list, + struct mwifiex_ra_list_tbl, list); + else + ra_list = NULL; + tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT; + } + + if (!ra_list) { + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + continue; + } + + skb_queue_tail(&ra_list->skb_head, skb); + + ra_list->ba_pkt_count++; + ra_list->total_pkt_count++; + + if (atomic_read(&priv->wmm.highest_queued_prio) < + tos_to_tid_inv[tid_down]) + atomic_set(&priv->wmm.highest_queued_prio, + tos_to_tid_inv[tid_down]); + + atomic_inc(&priv->wmm.tx_pkts_queued); + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return; +} + +static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, + const u8 *mac) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct list_head *ra_list_head; + struct sk_buff *skb, *tmp; + unsigned long flags; + int i; + + mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; i++) { + if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) { + ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; + list_for_each_entry(ra_list, ra_list_head, list) { + skb_queue_walk_safe(&ra_list->skb_head, skb, + tmp) { + if (!ether_addr_equal(mac, skb->data)) + continue; + __skb_unlink(skb, &ra_list->skb_head); + atomic_dec(&priv->wmm.tx_pkts_queued); + ra_list->total_pkt_count--; + skb_queue_tail(&priv->tdls_txq, skb); + } + } + } + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return; +} + +/* This function appends rate TLV to scan config command. */ +static int +mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + u8 rates[MWIFIEX_SUPPORTED_RATES], *pos; + u16 rates_size, supp_rates_size, ext_rates_size; + + memset(rates, 0, sizeof(rates)); + rates_size = mwifiex_get_supported_rates(priv, rates); + + supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES); + + if (skb_tailroom(skb) < rates_size + 4) { + mwifiex_dbg(priv->adapter, ERROR, + "Insuffient space while adding rates\n"); + return -ENOMEM; + } + + pos = skb_put(skb, supp_rates_size + 2); + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = supp_rates_size; + memcpy(pos, rates, supp_rates_size); + + if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) { + ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES; + pos = skb_put(skb, ext_rates_size + 2); + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = ext_rates_size; + memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES, + ext_rates_size); + } + + return 0; +} + +static void mwifiex_tdls_add_aid(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee_types_assoc_rsp *assoc_rsp; + u8 *pos; + + assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf; + pos = (void *)skb_put(skb, 4); + *pos++ = WLAN_EID_AID; + *pos++ = 2; + memcpy(pos, &assoc_rsp->a_id, sizeof(assoc_rsp->a_id)); + + return; +} + +static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee80211_vht_cap vht_cap; + u8 *pos; + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); + *pos++ = WLAN_EID_VHT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_vht_cap); + + memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap)); + + mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band); + memcpy(pos, &vht_cap, sizeof(vht_cap)); + + return 0; +} + +static int +mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, + u8 vht_enabled, struct sk_buff *skb) +{ + struct ieee80211_ht_operation *ht_oper; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_bssdescriptor *bss_desc = + &priv->curr_bss_params.bss_descriptor; + u8 *pos; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (unlikely(!sta_ptr)) { + mwifiex_dbg(priv->adapter, ERROR, + "TDLS peer station not found in list\n"); + return -1; + } + + if (!(le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info))) { + mwifiex_dbg(priv->adapter, WARN, + "TDLS peer doesn't support ht capabilities\n"); + return 0; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2); + *pos++ = WLAN_EID_HT_OPERATION; + *pos++ = sizeof(struct ieee80211_ht_operation); + ht_oper = (void *)pos; + + ht_oper->primary_chan = bss_desc->channel; + + /* follow AP's channel bandwidth */ + if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && + bss_desc->bcn_ht_cap && + ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) + ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; + + if (vht_enabled) { + ht_oper->ht_param = + mwifiex_get_sec_chan_offset(bss_desc->channel); + ht_oper->ht_param |= BIT(2); + } + + memcpy(&sta_ptr->tdls_cap.ht_oper, ht_oper, + sizeof(struct ieee80211_ht_operation)); + + return 0; +} + +static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, + const u8 *mac, struct sk_buff *skb) +{ + struct mwifiex_bssdescriptor *bss_desc; + struct ieee80211_vht_operation *vht_oper; + struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL; + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_adapter *adapter = priv->adapter; + u8 supp_chwd_set, peer_supp_chwd_set; + u8 *pos, ap_supp_chwd_set, chan_bw; + u16 mcs_map_user, mcs_map_resp, mcs_map_result; + u16 mcs_user, mcs_resp, nss; + u32 usr_vht_cap_info; + + bss_desc = &priv->curr_bss_params.bss_descriptor; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (unlikely(!sta_ptr)) { + mwifiex_dbg(adapter, ERROR, + "TDLS peer station not found in list\n"); + return -1; + } + + if (!(le32_to_cpu(sta_ptr->tdls_cap.vhtcap.vht_cap_info))) { + mwifiex_dbg(adapter, WARN, + "TDLS peer doesn't support vht capabilities\n"); + return 0; + } + + if (!mwifiex_is_bss_in_11ac_mode(priv)) { + if (sta_ptr->tdls_cap.extcap.ext_capab[7] & + WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { + mwifiex_dbg(adapter, WARN, + "TDLS peer doesn't support wider bandwidth\n"); + return 0; + } + } else { + ap_vht_cap = bss_desc->bcn_vht_cap; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2); + *pos++ = WLAN_EID_VHT_OPERATION; + *pos++ = sizeof(struct ieee80211_vht_operation); + vht_oper = (struct ieee80211_vht_operation *)pos; + + if (bss_desc->bss_band & BAND_A) + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; + else + usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; + + /* find the minmum bandwith between AP/TDLS peers */ + vht_cap = &sta_ptr->tdls_cap.vhtcap; + supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); + peer_supp_chwd_set = + GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info)); + supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set); + + /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */ + + if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] & + WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { + ap_supp_chwd_set = + GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info)); + supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set); + } + + switch (supp_chwd_set) { + case IEEE80211_VHT_CHANWIDTH_80MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; + break; + default: + vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + + mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); + mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_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 ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || + (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) + SET_VHTNSSMCS(mcs_map_result, nss, + IEEE80211_VHT_MCS_NOT_SUPPORTED); + else + SET_VHTNSSMCS(mcs_map_result, nss, + min_t(u16, mcs_user, mcs_resp)); + } + + vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result); + + switch (vht_oper->chan_width) { + case IEEE80211_VHT_CHANWIDTH_80MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_160MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ; + break; + case IEEE80211_VHT_CHANWIDTH_80P80MHZ: + chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; + break; + default: + chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; + break; + } + vht_oper->center_freq_seg1_idx = + mwifiex_get_center_freq_index(priv, BAND_AAC, + bss_desc->channel, + chan_bw); + + return 0; +} + +static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct ieee_types_extcap *extcap; + + extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap)); + extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY; + extcap->ieee_hdr.len = 8; + memset(extcap->ext_capab, 0, 8); + extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED; + extcap->ext_capab[3] |= WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH; + + if (priv->adapter->is_hw_11ac_capable) + extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED; +} + +static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) +{ + u8 *pos = (void *)skb_put(skb, 3); + + *pos++ = WLAN_EID_QOS_CAPA; + *pos++ = 1; + *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB; +} + +static void +mwifiex_tdls_add_wmm_param_ie(struct mwifiex_private *priv, struct sk_buff *skb) +{ + struct ieee80211_wmm_param_ie *wmm; + u8 ac_vi[] = {0x42, 0x43, 0x5e, 0x00}; + u8 ac_vo[] = {0x62, 0x32, 0x2f, 0x00}; + u8 ac_be[] = {0x03, 0xa4, 0x00, 0x00}; + u8 ac_bk[] = {0x27, 0xa4, 0x00, 0x00}; + + wmm = (void *)skb_put(skb, sizeof(*wmm)); + memset(wmm, 0, sizeof(*wmm)); + + wmm->element_id = WLAN_EID_VENDOR_SPECIFIC; + wmm->len = sizeof(*wmm) - 2; + wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */ + wmm->oui[1] = 0x50; + wmm->oui[2] = 0xf2; + wmm->oui_type = 2; /* WME */ + wmm->oui_subtype = 1; /* WME param */ + wmm->version = 1; /* WME ver */ + wmm->qos_info = 0; /* U-APSD not in use */ + + /* use default WMM AC parameters for TDLS link*/ + memcpy(&wmm->ac[0], ac_be, sizeof(ac_be)); + memcpy(&wmm->ac[1], ac_bk, sizeof(ac_bk)); + memcpy(&wmm->ac[2], ac_vi, sizeof(ac_vi)); + memcpy(&wmm->ac[3], ac_vo, sizeof(ac_vo)); +} + +static void +mwifiex_add_wmm_info_ie(struct mwifiex_private *priv, struct sk_buff *skb, + u8 qosinfo) +{ + u8 *buf; + + buf = (void *)skb_put(skb, MWIFIEX_TDLS_WMM_INFO_SIZE + + sizeof(struct ieee_types_header)); + + *buf++ = WLAN_EID_VENDOR_SPECIFIC; + *buf++ = 7; /* len */ + *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */ + *buf++ = 0x50; + *buf++ = 0xf2; + *buf++ = 2; /* WME */ + *buf++ = 0; /* WME info */ + *buf++ = 1; /* WME ver */ + *buf++ = qosinfo; /* U-APSD no in use */ +} + +static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, + const u8 *peer, u8 action_code, + u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_tdls_data *tf; + int ret; + u16 capab; + struct ieee80211_ht_cap *ht_cap; + u8 radio, *pos; + + capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; + + tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); + memcpy(tf->da, peer, ETH_ALEN); + memcpy(tf->sa, priv->curr_addr, ETH_ALEN); + tf->ether_type = cpu_to_be16(ETH_P_TDLS); + tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_REQUEST; + skb_put(skb, sizeof(tf->u.setup_req)); + tf->u.setup_req.dialog_token = dialog_token; + tf->u.setup_req.capability = cpu_to_le16(capab); + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_qos_capab(skb); + mwifiex_add_wmm_info_ie(priv, skb, 0); + break; + + case WLAN_TDLS_SETUP_RESPONSE: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_RESPONSE; + skb_put(skb, sizeof(tf->u.setup_resp)); + tf->u.setup_resp.status_code = cpu_to_le16(status_code); + tf->u.setup_resp.dialog_token = dialog_token; + tf->u.setup_resp.capability = cpu_to_le16(capab); + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_qos_capab(skb); + mwifiex_add_wmm_info_ie(priv, skb, 0); + break; + + case WLAN_TDLS_SETUP_CONFIRM: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_SETUP_CONFIRM; + skb_put(skb, sizeof(tf->u.setup_cfm)); + tf->u.setup_cfm.status_code = cpu_to_le16(status_code); + tf->u.setup_cfm.dialog_token = dialog_token; + + mwifiex_tdls_add_wmm_param_ie(priv, skb); + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_oper(priv, peer, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + ret = mwifiex_tdls_add_ht_oper(priv, peer, 1, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + } else { + ret = mwifiex_tdls_add_ht_oper(priv, peer, 0, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + } + break; + + case WLAN_TDLS_TEARDOWN: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_TEARDOWN; + skb_put(skb, sizeof(tf->u.teardown)); + tf->u.teardown.reason_code = cpu_to_le16(status_code); + break; + + case WLAN_TDLS_DISCOVERY_REQUEST: + tf->category = WLAN_CATEGORY_TDLS; + tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; + skb_put(skb, sizeof(tf->u.discover_req)); + tf->u.discover_req.dialog_token = dialog_token; + break; + default: + mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); + return -EINVAL; + } + + return 0; +} + +static void +mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, + const u8 *peer, const u8 *bssid) +{ + struct ieee80211_tdls_lnkie *lnkid; + + lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); + lnkid->ie_type = WLAN_EID_LINK_ID; + lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - + sizeof(struct ieee_types_header); + + memcpy(lnkid->bssid, bssid, ETH_ALEN); + memcpy(lnkid->init_sta, src_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, peer, ETH_ALEN); +} + +int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + int ret; + u16 skb_len; + + skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + MWIFIEX_SUPPORTED_RATES + + 3 + /* Qos Info */ + sizeof(struct ieee_types_extcap) + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee_types_bss_co_2040) + + sizeof(struct ieee80211_ht_operation) + + sizeof(struct ieee80211_tdls_lnkie) + + sizeof(struct ieee80211_wmm_param_ie) + + extra_ies_len; + + if (priv->adapter->is_hw_11ac_capable) + skb_len += sizeof(struct ieee_types_vht_cap) + + sizeof(struct ieee_types_vht_oper) + + sizeof(struct ieee_types_aid); + + skb = dev_alloc_skb(skb_len); + if (!skb) { + mwifiex_dbg(priv->adapter, ERROR, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, + dialog_token, status_code, + skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, + extra_ies_len); + mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer, + priv->cfg_bssid); + break; + case WLAN_TDLS_SETUP_RESPONSE: + ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, + dialog_token, status_code, + skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, + extra_ies_len); + mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, + priv->cfg_bssid); + break; + } + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + skb->priority = MWIFIEX_PRIO_BK; + break; + default: + skb->priority = MWIFIEX_PRIO_VI; + break; + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + + __net_timestamp(skb); + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} + +static int +mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, + const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, struct sk_buff *skb) +{ + struct ieee80211_mgmt *mgmt; + u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + int ret; + u16 capab; + struct ieee80211_ht_cap *ht_cap; + u8 radio, *pos; + + capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; + + mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u)); + + memset(mgmt, 0, 24); + memcpy(mgmt->da, peer, ETH_ALEN); + memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); + memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); + mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | + IEEE80211_STYPE_ACTION); + + /* add address 4 */ + pos = skb_put(skb, ETH_ALEN); + + switch (action_code) { + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1); + mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; + mgmt->u.action.u.tdls_discover_resp.action_code = + WLAN_PUB_ACTION_TDLS_DISCOVER_RES; + mgmt->u.action.u.tdls_discover_resp.dialog_token = + dialog_token; + mgmt->u.action.u.tdls_discover_resp.capability = + cpu_to_le16(capab); + /* move back for addr4 */ + memmove(pos + ETH_ALEN, &mgmt->u.action.category, + sizeof(mgmt->u.action.u.tdls_discover_resp)); + /* init address 4 */ + memcpy(pos, bc_addr, ETH_ALEN); + + ret = mwifiex_tdls_append_rates_ie(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + *pos++ = WLAN_EID_HT_CAPABILITY; + *pos++ = sizeof(struct ieee80211_ht_cap); + ht_cap = (void *)pos; + radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); + ret = mwifiex_fill_cap_info(priv, radio, ht_cap); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + if (priv->adapter->is_hw_11ac_capable) { + ret = mwifiex_tdls_add_vht_capab(priv, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + mwifiex_tdls_add_aid(priv, skb); + } + + mwifiex_tdls_add_ext_capab(priv, skb); + mwifiex_tdls_add_qos_capab(skb); + break; + default: + mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS action frame type\n"); + return -EINVAL; + } + + return 0; +} + +int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, + u8 action_code, u8 dialog_token, + u16 status_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + u8 *pos; + u32 pkt_type, tx_control; + u16 pkt_len, skb_len; + + skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + + max(sizeof(struct ieee80211_mgmt), + sizeof(struct ieee80211_tdls_data)) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + MWIFIEX_SUPPORTED_RATES + + sizeof(struct ieee_types_extcap) + + sizeof(struct ieee80211_ht_cap) + + sizeof(struct ieee_types_bss_co_2040) + + sizeof(struct ieee80211_ht_operation) + + sizeof(struct ieee80211_tdls_lnkie) + + extra_ies_len + + 3 + /* Qos Info */ + ETH_ALEN; /* Address4 */ + + if (priv->adapter->is_hw_11ac_capable) + skb_len += sizeof(struct ieee_types_vht_cap) + + sizeof(struct ieee_types_vht_oper) + + sizeof(struct ieee_types_aid); + + skb = dev_alloc_skb(skb_len); + if (!skb) { + mwifiex_dbg(priv->adapter, ERROR, + "allocate skb failed for management frame\n"); + return -ENOMEM; + } + + skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + + pkt_type = PKT_TYPE_MGMT; + tx_control = 0; + pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); + memcpy(pos, &pkt_type, sizeof(pkt_type)); + memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control)); + + if (mwifiex_construct_tdls_action_frame(priv, peer, action_code, + dialog_token, status_code, + skb)) { + dev_kfree_skb_any(skb); + return -EINVAL; + } + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + /* the TDLS link IE is always added last we are the responder */ + + mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, + priv->cfg_bssid); + + skb->priority = MWIFIEX_PRIO_VI; + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + + pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); + memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, + sizeof(pkt_len)); + __net_timestamp(skb); + mwifiex_queue_tx_pkt(priv, skb); + + return 0; +} + +/* This function process tdls action frame from peer. + * Peer capabilities are stored into station node structure. + */ +void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, + u8 *buf, int len) +{ + struct mwifiex_sta_node *sta_ptr; + u8 *peer, *pos, *end; + u8 i, action, basic; + __le16 cap = 0; + int ie_len = 0; + + if (len < (sizeof(struct ethhdr) + 3)) + return; + if (*(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE) + return; + if (*(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS) + return; + + peer = buf + ETH_ALEN; + action = *(buf + sizeof(struct ethhdr) + 2); + mwifiex_dbg(priv->adapter, DATA, + "rx:tdls action: peer=%pM, action=%d\n", peer, action); + + switch (action) { + case WLAN_TDLS_SETUP_REQUEST: + if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN)) + return; + + pos = buf + sizeof(struct ethhdr) + 4; + /* payload 1+ category 1 + action 1 + dialog 1 */ + cap = cpu_to_le16(*(u16 *)pos); + ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; + pos += 2; + break; + + case WLAN_TDLS_SETUP_RESPONSE: + if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN)) + return; + /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ + pos = buf + sizeof(struct ethhdr) + 6; + cap = cpu_to_le16(*(u16 *)pos); + ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; + pos += 2; + break; + + case WLAN_TDLS_SETUP_CONFIRM: + if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN)) + return; + pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN; + ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; + break; + default: + mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); + return; + } + + sta_ptr = mwifiex_add_sta_entry(priv, peer); + if (!sta_ptr) + return; + + sta_ptr->tdls_cap.capab = cap; + + for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos + 2 + pos[1] > end) + break; + + switch (*pos) { + case WLAN_EID_SUPP_RATES: + sta_ptr->tdls_cap.rates_len = pos[1]; + for (i = 0; i < pos[1]; i++) + sta_ptr->tdls_cap.rates[i] = pos[i + 2]; + break; + + case WLAN_EID_EXT_SUPP_RATES: + basic = sta_ptr->tdls_cap.rates_len; + for (i = 0; i < pos[1]; i++) + sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; + sta_ptr->tdls_cap.rates_len += pos[1]; + break; + case WLAN_EID_HT_CAPABILITY: + memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos, + sizeof(struct ieee80211_ht_cap)); + sta_ptr->is_11n_enabled = 1; + break; + case WLAN_EID_HT_OPERATION: + memcpy(&sta_ptr->tdls_cap.ht_oper, pos, + sizeof(struct ieee80211_ht_operation)); + break; + case WLAN_EID_BSS_COEX_2040: + sta_ptr->tdls_cap.coex_2040 = pos[2]; + break; + case WLAN_EID_EXT_CAPABILITY: + memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, + sizeof(struct ieee_types_header) + + min_t(u8, pos[1], 8)); + break; + case WLAN_EID_RSN: + memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, + sizeof(struct ieee_types_header) + + min_t(u8, pos[1], IEEE_MAX_IE_SIZE - + sizeof(struct ieee_types_header))); + break; + case WLAN_EID_QOS_CAPA: + sta_ptr->tdls_cap.qos_info = pos[2]; + break; + case WLAN_EID_VHT_OPERATION: + if (priv->adapter->is_hw_11ac_capable) + memcpy(&sta_ptr->tdls_cap.vhtoper, pos, + sizeof(struct ieee80211_vht_operation)); + break; + case WLAN_EID_VHT_CAPABILITY: + if (priv->adapter->is_hw_11ac_capable) { + memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos, + sizeof(struct ieee80211_vht_cap)); + sta_ptr->is_11ac_enabled = 1; + } + break; + case WLAN_EID_AID: + if (priv->adapter->is_hw_11ac_capable) + sta_ptr->tdls_cap.aid = + le16_to_cpu(*(__le16 *)(pos + 2)); + default: + break; + } + } + + return; +} + +static int +mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) { + mwifiex_dbg(priv->adapter, ERROR, + "link absent for peer %pM; cannot config\n", peer); + return -EINVAL; + } + + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK; + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); +} + +static int +mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) { + mwifiex_dbg(priv->adapter, WARN, + "Setup already in progress for peer %pM\n", peer); + return 0; + } + + sta_ptr = mwifiex_add_sta_entry(priv, peer); + if (!sta_ptr) + return -ENOMEM; + + sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS; + mwifiex_hold_tdls_packets(priv, peer); + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); +} + +static int +mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + unsigned long flags; + + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr) { + if (sta_ptr->is_11n_enabled) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + } + mwifiex_del_sta_entry(priv, peer); + } + + mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); + mwifiex_auto_tdls_update_peer_status(priv, peer, TDLS_NOT_SETUP); + memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); +} + +static int +mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) +{ + struct mwifiex_sta_node *sta_ptr; + struct ieee80211_mcs_info mcs; + unsigned long flags; + int i; + + sta_ptr = mwifiex_get_sta_entry(priv, peer); + + if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) { + mwifiex_dbg(priv->adapter, MSG, + "tdls: enable link %pM success\n", peer); + + sta_ptr->tdls_status = TDLS_SETUP_COMPLETE; + + mcs = sta_ptr->tdls_cap.ht_capb.mcs; + if (mcs.rx_mask[0] != 0xff) + sta_ptr->is_11n_enabled = true; + if (sta_ptr->is_11n_enabled) { + if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU) + sta_ptr->max_amsdu = + MWIFIEX_TX_DATA_BUF_SIZE_8K; + else + sta_ptr->max_amsdu = + MWIFIEX_TX_DATA_BUF_SIZE_4K; + + for (i = 0; i < MAX_NUM_TID; i++) + sta_ptr->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + } else { + for (i = 0; i < MAX_NUM_TID; i++) + sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; + } + if (sta_ptr->tdls_cap.extcap.ext_capab[3] & + WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) { + mwifiex_config_tdls_enable(priv); + mwifiex_config_tdls_cs_params(priv); + } + + memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); + mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE); + mwifiex_auto_tdls_update_peer_status(priv, peer, + TDLS_SETUP_COMPLETE); + } else { + mwifiex_dbg(priv->adapter, ERROR, + "tdls: enable link %pM failed\n", peer); + if (sta_ptr) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_del_sta_entry(priv, peer); + } + mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); + mwifiex_auto_tdls_update_peer_status(priv, peer, + TDLS_NOT_SETUP); + + return -1; + } + + return 0; +} + +int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action) +{ + switch (action) { + case MWIFIEX_TDLS_ENABLE_LINK: + return mwifiex_tdls_process_enable_link(priv, peer); + case MWIFIEX_TDLS_DISABLE_LINK: + return mwifiex_tdls_process_disable_link(priv, peer); + case MWIFIEX_TDLS_CREATE_LINK: + return mwifiex_tdls_process_create_link(priv, peer); + case MWIFIEX_TDLS_CONFIG_LINK: + return mwifiex_tdls_process_config_link(priv, peer); + } + return 0; +} + +int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *sta_ptr; + + sta_ptr = mwifiex_get_sta_entry(priv, mac); + if (sta_ptr) + return sta_ptr->tdls_status; + + return TDLS_NOT_SETUP; +} + +int mwifiex_get_tdls_list(struct mwifiex_private *priv, + struct tdls_peer_info *buf) +{ + struct mwifiex_sta_node *sta_ptr; + struct tdls_peer_info *peer = buf; + int count = 0; + unsigned long flags; + + if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return 0; + + /* make sure we are in station mode and connected */ + if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) + return 0; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + list_for_each_entry(sta_ptr, &priv->sta_list, list) { + if (mwifiex_is_tdls_link_setup(sta_ptr->tdls_status)) { + ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr); + peer++; + count++; + if (count >= MWIFIEX_MAX_TDLS_PEER_SUPPORTED) + break; + } + } + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + + return count; +} + +void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *sta_ptr; + struct mwifiex_ds_tdls_oper tdls_oper; + unsigned long flags; + + if (list_empty(&priv->sta_list)) + return; + + list_for_each_entry(sta_ptr, &priv->sta_list, list) { + memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); + + if (sta_ptr->is_11n_enabled) { + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, + flags); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + } + + mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr, + TDLS_LINK_TEARDOWN); + memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN); + tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; + if (mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, + HostCmd_ACT_GEN_SET, 0, &tdls_oper, false)) + mwifiex_dbg(priv->adapter, ERROR, + "Disable link failed for TDLS peer %pM", + sta_ptr->mac_addr); + } + + mwifiex_del_all_sta_list(priv); +} + +int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb) +{ + struct mwifiex_auto_tdls_peer *peer; + unsigned long flags; + u8 mac[ETH_ALEN]; + + ether_addr_copy(mac, skb->data); + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(peer, &priv->auto_tdls_list, list) { + if (!memcmp(mac, peer->mac_addr, ETH_ALEN)) { + if (peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && + peer->tdls_status == TDLS_NOT_SETUP && + (peer->failure_count < + MWIFIEX_TDLS_MAX_FAIL_COUNT)) { + peer->tdls_status = TDLS_SETUP_INPROGRESS; + mwifiex_dbg(priv->adapter, INFO, + "setup TDLS link, peer=%pM rssi=%d\n", + peer->mac_addr, peer->rssi); + + cfg80211_tdls_oper_request(priv->netdev, + peer->mac_addr, + NL80211_TDLS_SETUP, + 0, GFP_ATOMIC); + peer->do_setup = false; + priv->check_tdls_tx = false; + } else if (peer->failure_count < + MWIFIEX_TDLS_MAX_FAIL_COUNT && + peer->do_discover) { + mwifiex_send_tdls_data_frame(priv, + peer->mac_addr, + WLAN_TDLS_DISCOVERY_REQUEST, + 1, 0, NULL, 0); + peer->do_discover = false; + } + } + } + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + + return 0; +} + +void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv) +{ + struct mwifiex_auto_tdls_peer *peer, *tmp_node; + unsigned long flags; + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry_safe(peer, tmp_node, &priv->auto_tdls_list, list) { + list_del(&peer->list); + kfree(peer); + } + + INIT_LIST_HEAD(&priv->auto_tdls_list); + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + priv->check_tdls_tx = false; +} + +void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_auto_tdls_peer *tdls_peer; + unsigned long flags; + + if (!priv->adapter->auto_tdls) + return; + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { + if (!memcmp(tdls_peer->mac_addr, mac, ETH_ALEN)) { + tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; + tdls_peer->rssi_jiffies = jiffies; + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + return; + } + } + + /* create new TDLS peer */ + tdls_peer = kzalloc(sizeof(*tdls_peer), GFP_ATOMIC); + if (tdls_peer) { + ether_addr_copy(tdls_peer->mac_addr, mac); + tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; + tdls_peer->rssi_jiffies = jiffies; + INIT_LIST_HEAD(&tdls_peer->list); + list_add_tail(&tdls_peer->list, &priv->auto_tdls_list); + mwifiex_dbg(priv->adapter, INFO, + "Add auto TDLS peer= %pM to list\n", mac); + } + + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); +} + +void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, + const u8 *mac, u8 link_status) +{ + struct mwifiex_auto_tdls_peer *peer; + unsigned long flags; + + if (!priv->adapter->auto_tdls) + return; + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(peer, &priv->auto_tdls_list, list) { + if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { + if ((link_status == TDLS_NOT_SETUP) && + (peer->tdls_status == TDLS_SETUP_INPROGRESS)) + peer->failure_count++; + else if (mwifiex_is_tdls_link_setup(link_status)) + peer->failure_count = 0; + + peer->tdls_status = link_status; + break; + } + } + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); +} + +void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, + u8 *mac, s8 snr, s8 nflr) +{ + struct mwifiex_auto_tdls_peer *peer; + unsigned long flags; + + if (!priv->adapter->auto_tdls) + return; + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(peer, &priv->auto_tdls_list, list) { + if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { + peer->rssi = nflr - snr; + peer->rssi_jiffies = jiffies; + break; + } + } + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); +} + +void mwifiex_check_auto_tdls(unsigned long context) +{ + struct mwifiex_private *priv = (struct mwifiex_private *)context; + struct mwifiex_auto_tdls_peer *tdls_peer; + unsigned long flags; + u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; + + if (WARN_ON_ONCE(!priv || !priv->adapter)) { + pr_err("mwifiex: %s: adapter or private structure is NULL\n", + __func__); + return; + } + + if (unlikely(!priv->adapter->auto_tdls)) + return; + + if (!priv->auto_tdls_timer_active) { + mwifiex_dbg(priv->adapter, INFO, + "auto TDLS timer inactive; return"); + return; + } + + priv->check_tdls_tx = false; + + if (list_empty(&priv->auto_tdls_list)) { + mod_timer(&priv->auto_tdls_timer, + jiffies + + msecs_to_jiffies(MWIFIEX_TIMER_10S)); + return; + } + + spin_lock_irqsave(&priv->auto_tdls_lock, flags); + list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { + if ((jiffies - tdls_peer->rssi_jiffies) > + (MWIFIEX_AUTO_TDLS_IDLE_TIME * HZ)) { + tdls_peer->rssi = 0; + tdls_peer->do_discover = true; + priv->check_tdls_tx = true; + } + + if (((tdls_peer->rssi >= MWIFIEX_TDLS_RSSI_LOW) || + !tdls_peer->rssi) && + mwifiex_is_tdls_link_setup(tdls_peer->tdls_status)) { + tdls_peer->tdls_status = TDLS_LINK_TEARDOWN; + mwifiex_dbg(priv->adapter, MSG, + "teardown TDLS link,peer=%pM rssi=%d\n", + tdls_peer->mac_addr, -tdls_peer->rssi); + tdls_peer->do_discover = true; + priv->check_tdls_tx = true; + cfg80211_tdls_oper_request(priv->netdev, + tdls_peer->mac_addr, + NL80211_TDLS_TEARDOWN, + reason, GFP_ATOMIC); + } else if (tdls_peer->rssi && + tdls_peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && + tdls_peer->tdls_status == TDLS_NOT_SETUP && + tdls_peer->failure_count < + MWIFIEX_TDLS_MAX_FAIL_COUNT) { + priv->check_tdls_tx = true; + tdls_peer->do_setup = true; + mwifiex_dbg(priv->adapter, INFO, + "check TDLS with peer=%pM\t" + "rssi=%d\n", tdls_peer->mac_addr, + tdls_peer->rssi); + } + } + spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); + + mod_timer(&priv->auto_tdls_timer, + jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); +} + +void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv) +{ + setup_timer(&priv->auto_tdls_timer, mwifiex_check_auto_tdls, + (unsigned long)priv); + priv->auto_tdls_timer_active = true; + mod_timer(&priv->auto_tdls_timer, + jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); +} + +void mwifiex_clean_auto_tdls(struct mwifiex_private *priv) +{ + if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && + priv->adapter->auto_tdls && + priv->bss_type == MWIFIEX_BSS_TYPE_STA) { + priv->auto_tdls_timer_active = false; + del_timer(&priv->auto_tdls_timer); + mwifiex_flush_auto_tdls_list(priv); + } +} + +static int mwifiex_config_tdls(struct mwifiex_private *priv, u8 enable) +{ + struct mwifiex_tdls_config config; + + config.enable = cpu_to_le16(enable); + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, + ACT_TDLS_CS_ENABLE_CONFIG, 0, &config, true); +} + +int mwifiex_config_tdls_enable(struct mwifiex_private *priv) +{ + return mwifiex_config_tdls(priv, true); +} + +int mwifiex_config_tdls_disable(struct mwifiex_private *priv) +{ + return mwifiex_config_tdls(priv, false); +} + +int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv) +{ + struct mwifiex_tdls_config_cs_params config_tdls_cs_params; + + config_tdls_cs_params.unit_time = MWIFIEX_DEF_CS_UNIT_TIME; + config_tdls_cs_params.thr_otherlink = MWIFIEX_DEF_CS_THR_OTHERLINK; + config_tdls_cs_params.thr_directlink = MWIFIEX_DEF_THR_DIRECTLINK; + + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, + ACT_TDLS_CS_PARAMS, 0, + &config_tdls_cs_params, true); +} + +int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac) +{ + struct mwifiex_tdls_stop_cs_params stop_tdls_cs_params; + + ether_addr_copy(stop_tdls_cs_params.peer_mac, peer_mac); + + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, + ACT_TDLS_CS_STOP, 0, + &stop_tdls_cs_params, true); +} + +int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac, + u8 primary_chan, u8 second_chan_offset, u8 band) +{ + struct mwifiex_tdls_init_cs_params start_tdls_cs_params; + + ether_addr_copy(start_tdls_cs_params.peer_mac, peer_mac); + start_tdls_cs_params.primary_chan = primary_chan; + start_tdls_cs_params.second_chan_offset = second_chan_offset; + start_tdls_cs_params.band = band; + + start_tdls_cs_params.switch_time = cpu_to_le16(MWIFIEX_DEF_CS_TIME); + start_tdls_cs_params.switch_timeout = + cpu_to_le16(MWIFIEX_DEF_CS_TIMEOUT); + start_tdls_cs_params.reg_class = MWIFIEX_DEF_CS_REG_CLASS; + start_tdls_cs_params.periodicity = MWIFIEX_DEF_CS_PERIODICITY; + + return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, + ACT_TDLS_CS_INIT, 0, + &start_tdls_cs_params, true); +} diff --git a/drivers/net/wireless/marvell/mwifiex/txrx.c b/drivers/net/wireless/marvell/mwifiex/txrx.c new file mode 100644 index 000000000000..bf6182b646a5 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/txrx.c @@ -0,0 +1,386 @@ +/* + * Marvell Wireless LAN device driver: generic TX/RX data handling + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" + +/* + * This function processes the received buffer. + * + * Main responsibility of this function is to parse the RxPD to + * identify the correct interface this packet is headed for and + * forwarding it to the associated handling function, where the + * packet will be further processed and sent to kernel/upper layer + * if required. + */ +int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + struct mwifiex_private *priv = + mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + struct rxpd *local_rx_pd; + struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); + int ret; + + local_rx_pd = (struct rxpd *) (skb->data); + /* Get the BSS number from rxpd, get corresponding priv */ + priv = mwifiex_get_priv_by_id(adapter, local_rx_pd->bss_num & + BSS_NUM_MASK, local_rx_pd->bss_type); + if (!priv) + priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "data: priv not found. Drop RX packet\n"); + dev_kfree_skb_any(skb); + return -1; + } + + mwifiex_dbg_dump(adapter, DAT_D, "rx pkt:", skb->data, + min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); + + memset(rx_info, 0, sizeof(*rx_info)); + rx_info->bss_num = priv->bss_num; + rx_info->bss_type = priv->bss_type; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) + ret = mwifiex_process_uap_rx_packet(priv, skb); + else + ret = mwifiex_process_sta_rx_packet(priv, skb); + + return ret; +} +EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet); + +/* + * This function sends a packet to device. + * + * It processes the packet to add the TxPD, checks condition and + * sends the processed packet to firmware for transmission. + * + * On successful completion, the function calls the completion callback + * and logs the time. + */ +int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + int hroom, ret = -1; + struct mwifiex_adapter *adapter = priv->adapter; + u8 *head_ptr; + struct txpd *local_tx_pd = NULL; + struct mwifiex_sta_node *dest_node; + struct ethhdr *hdr = (void *)skb->data; + + hroom = (adapter->iface_type == MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { + dest_node = mwifiex_get_sta_entry(priv, hdr->h_dest); + if (dest_node) { + dest_node->stats.tx_bytes += skb->len; + dest_node->stats.tx_packets++; + } + + head_ptr = mwifiex_process_uap_txpd(priv, skb); + } else { + head_ptr = mwifiex_process_sta_txpd(priv, skb); + } + + if ((adapter->data_sent || adapter->tx_lock_flag) && head_ptr) { + skb_queue_tail(&adapter->tx_data_q, skb); + atomic_inc(&adapter->tx_queued); + return 0; + } + + if (head_ptr) { + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) + local_tx_pd = (struct txpd *)(head_ptr + hroom); + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, + priv->usb_port, + skb, NULL); + } else { + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_TYPE_DATA, + skb, tx_param); + } + } + mwifiex_dbg_dump(adapter, DAT_D, "tx pkt:", skb->data, + min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); + + switch (ret) { + case -ENOSR: + mwifiex_dbg(adapter, DATA, "data: -ENOSR is returned\n"); + break; + case -EBUSY: + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + (adapter->pps_uapsd_mode) && (adapter->tx_lock_flag)) { + priv->adapter->tx_lock_flag = false; + if (local_tx_pd) + local_tx_pd->flags = 0; + } + mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + break; + case -1: + mwifiex_dbg(adapter, ERROR, + "mwifiex_write_data_async failed: 0x%X\n", + ret); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + case -EINPROGRESS: + break; + case 0: + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + default: + break; + } + + return ret; +} + +static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, + struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct txpd *local_tx_pd = NULL; + u8 *head_ptr = skb->data; + int ret = 0; + struct mwifiex_private *priv; + struct mwifiex_txinfo *tx_info; + + tx_info = MWIFIEX_SKB_TXCB(skb); + priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, + tx_info->bss_type); + if (!priv) { + mwifiex_dbg(adapter, ERROR, + "data: priv not found. Drop TX packet\n"); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, 0); + return ret; + } + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { + if (adapter->iface_type == MWIFIEX_USB) + local_tx_pd = (struct txpd *)head_ptr; + else + local_tx_pd = (struct txpd *) (head_ptr + + INTF_HEADER_LEN); + } + + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, + priv->usb_port, + skb, NULL); + } else { + ret = adapter->if_ops.host_to_card(adapter, + MWIFIEX_TYPE_DATA, + skb, tx_param); + } + switch (ret) { + case -ENOSR: + mwifiex_dbg(adapter, ERROR, "data: -ENOSR is returned\n"); + break; + case -EBUSY: + if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && + (adapter->pps_uapsd_mode) && + (adapter->tx_lock_flag)) { + priv->adapter->tx_lock_flag = false; + if (local_tx_pd) + local_tx_pd->flags = 0; + } + skb_queue_head(&adapter->tx_data_q, skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) + atomic_add(tx_info->aggr_num, &adapter->tx_queued); + else + atomic_inc(&adapter->tx_queued); + mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + break; + case -1: + mwifiex_dbg(adapter, ERROR, + "mwifiex_write_data_async failed: 0x%X\n", ret); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + case -EINPROGRESS: + break; + case 0: + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + default: + break; + } + return ret; +} + +static int +mwifiex_dequeue_tx_queue(struct mwifiex_adapter *adapter) +{ + struct sk_buff *skb, *skb_next; + struct mwifiex_txinfo *tx_info; + struct mwifiex_tx_param tx_param; + + skb = skb_dequeue(&adapter->tx_data_q); + if (!skb) + return -1; + + tx_info = MWIFIEX_SKB_TXCB(skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) + atomic_sub(tx_info->aggr_num, &adapter->tx_queued); + else + atomic_dec(&adapter->tx_queued); + + if (!skb_queue_empty(&adapter->tx_data_q)) + skb_next = skb_peek(&adapter->tx_data_q); + else + skb_next = NULL; + tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0); + if (!tx_param.next_pkt_len) { + if (!mwifiex_wmm_lists_empty(adapter)) + tx_param.next_pkt_len = 1; + } + return mwifiex_host_to_card(adapter, skb, &tx_param); +} + +void +mwifiex_process_tx_queue(struct mwifiex_adapter *adapter) +{ + do { + if (adapter->data_sent || adapter->tx_lock_flag) + break; + if (mwifiex_dequeue_tx_queue(adapter)) + break; + } while (!skb_queue_empty(&adapter->tx_data_q)); +} + +/* + * Packet send completion callback handler. + * + * It either frees the buffer directly or forwards it to another + * completion callback which checks conditions, updates statistics, + * wakes up stalled traffic queue if required, and then frees the buffer. + */ +int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb, int aggr, int status) +{ + struct mwifiex_private *priv; + struct mwifiex_txinfo *tx_info; + struct netdev_queue *txq; + int index; + + if (!skb) + return 0; + + tx_info = MWIFIEX_SKB_TXCB(skb); + priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, + tx_info->bss_type); + if (!priv) + goto done; + + mwifiex_set_trans_start(priv->netdev); + if (!status) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += tx_info->pkt_len; + if (priv->tx_timeout_cnt) + priv->tx_timeout_cnt = 0; + } else { + priv->stats.tx_errors++; + } + + if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) + atomic_dec_return(&adapter->pending_bridged_pkts); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) + goto done; + + if (aggr) + /* For skb_aggr, do not wake up tx queue */ + goto done; + + atomic_dec(&adapter->tx_pending); + + index = mwifiex_1d_to_wmm_queue[skb->priority]; + if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) { + txq = netdev_get_tx_queue(priv->netdev, index); + if (netif_tx_queue_stopped(txq)) { + netif_tx_wake_queue(txq); + mwifiex_dbg(adapter, DATA, "wake queue: %d\n", index); + } + } +done: + dev_kfree_skb_any(skb); + + return 0; +} +EXPORT_SYMBOL_GPL(mwifiex_write_data_complete); + +void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, + void *event_body) +{ + struct tx_status_event *tx_status = (void *)priv->adapter->event_body; + struct sk_buff *ack_skb; + unsigned long flags; + struct mwifiex_txinfo *tx_info; + + if (!tx_status->tx_token_id) + return; + + spin_lock_irqsave(&priv->ack_status_lock, flags); + ack_skb = idr_find(&priv->ack_status_frames, tx_status->tx_token_id); + if (ack_skb) + idr_remove(&priv->ack_status_frames, tx_status->tx_token_id); + spin_unlock_irqrestore(&priv->ack_status_lock, flags); + + if (ack_skb) { + tx_info = MWIFIEX_SKB_TXCB(ack_skb); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS) { + /* consumes ack_skb */ + skb_complete_wifi_ack(ack_skb, !tx_status->status); + } else { + /* Remove broadcast address which was added by driver */ + memmove(ack_skb->data + + sizeof(struct ieee80211_hdr_3addr) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16), + ack_skb->data + + sizeof(struct ieee80211_hdr_3addr) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + + ETH_ALEN, ack_skb->len - + (sizeof(struct ieee80211_hdr_3addr) + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + + ETH_ALEN)); + ack_skb->len = ack_skb->len - ETH_ALEN; + /* Remove driver's proprietary header including 2 bytes + * of packet length and pass actual management frame buffer + * to cfg80211. + */ + cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie, + ack_skb->data + + MWIFIEX_MGMT_FRAME_HEADER_SIZE + + sizeof(u16), ack_skb->len - + (MWIFIEX_MGMT_FRAME_HEADER_SIZE + + sizeof(u16)), + !tx_status->status, GFP_ATOMIC); + dev_kfree_skb_any(ack_skb); + } + } +} diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c new file mode 100644 index 000000000000..759a6ada5b0f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c @@ -0,0 +1,885 @@ +/* + * Marvell Wireless LAN device driver: AP specific command handling + * + * Copyright (C) 2012-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "main.h" +#include "11ac.h" + +/* This function parses security related parameters from cfg80211_ap_settings + * and sets into FW understandable bss_config structure. + */ +int mwifiex_set_secure_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_config, + struct cfg80211_ap_settings *params) { + int i; + struct mwifiex_wep_key wep_key; + + if (!params->privacy) { + bss_config->protocol = PROTOCOL_NO_SECURITY; + bss_config->key_mgmt = KEY_MGMT_NONE; + bss_config->wpa_cfg.length = 0; + priv->sec_info.wep_enabled = 0; + priv->sec_info.wpa_enabled = 0; + priv->sec_info.wpa2_enabled = 0; + + return 0; + } + + switch (params->auth_type) { + case NL80211_AUTHTYPE_OPEN_SYSTEM: + bss_config->auth_mode = WLAN_AUTH_OPEN; + break; + case NL80211_AUTHTYPE_SHARED_KEY: + bss_config->auth_mode = WLAN_AUTH_SHARED_KEY; + break; + case NL80211_AUTHTYPE_NETWORK_EAP: + bss_config->auth_mode = WLAN_AUTH_LEAP; + break; + default: + bss_config->auth_mode = MWIFIEX_AUTH_MODE_AUTO; + break; + } + + bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST; + + for (i = 0; i < params->crypto.n_akm_suites; i++) { + switch (params->crypto.akm_suites[i]) { + case WLAN_AKM_SUITE_8021X: + if (params->crypto.wpa_versions & + NL80211_WPA_VERSION_1) { + bss_config->protocol = PROTOCOL_WPA; + bss_config->key_mgmt = KEY_MGMT_EAP; + } + if (params->crypto.wpa_versions & + NL80211_WPA_VERSION_2) { + bss_config->protocol |= PROTOCOL_WPA2; + bss_config->key_mgmt = KEY_MGMT_EAP; + } + break; + case WLAN_AKM_SUITE_PSK: + if (params->crypto.wpa_versions & + NL80211_WPA_VERSION_1) { + bss_config->protocol = PROTOCOL_WPA; + bss_config->key_mgmt = KEY_MGMT_PSK; + } + if (params->crypto.wpa_versions & + NL80211_WPA_VERSION_2) { + bss_config->protocol |= PROTOCOL_WPA2; + bss_config->key_mgmt = KEY_MGMT_PSK; + } + break; + default: + break; + } + } + 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) + bss_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_TKIP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) + bss_config->wpa_cfg.pairwise_cipher_wpa |= + CIPHER_AES_CCMP; + if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) + bss_config->wpa_cfg.pairwise_cipher_wpa2 |= + CIPHER_AES_CCMP; + default: + break; + } + } + + switch (params->crypto.cipher_group) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + if (priv->sec_info.wep_enabled) { + bss_config->protocol = PROTOCOL_STATIC_WEP; + bss_config->key_mgmt = KEY_MGMT_NONE; + bss_config->wpa_cfg.length = 0; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + wep_key = priv->wep_key[i]; + bss_config->wep_cfg[i].key_index = i; + + if (priv->wep_key_curr_index == i) + bss_config->wep_cfg[i].is_default = 1; + else + bss_config->wep_cfg[i].is_default = 0; + + bss_config->wep_cfg[i].length = + wep_key.key_length; + memcpy(&bss_config->wep_cfg[i].key, + &wep_key.key_material, + wep_key.key_length); + } + } + break; + case WLAN_CIPHER_SUITE_TKIP: + bss_config->wpa_cfg.group_cipher = CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_CCMP: + bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; + break; + default: + break; + } + + return 0; +} + +/* This function updates 11n related parameters from IE and sets them into + * bss_config structure. + */ +void +mwifiex_set_ht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *ht_ie; + u16 cap_info; + + if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) + return; + + ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail, + params->beacon.tail_len); + if (ht_ie) { + memcpy(&bss_cfg->ht_cap, ht_ie + 2, + sizeof(struct ieee80211_ht_cap)); + cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info); + memset(&bss_cfg->ht_cap.mcs, 0, + priv->adapter->number_of_antenna); + switch (GET_RXSTBC(cap_info)) { + case MWIFIEX_RX_STBC1: + /* HT_CAP 1X1 mode */ + bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; + break; + case MWIFIEX_RX_STBC12: /* fall through */ + case MWIFIEX_RX_STBC123: + /* HT_CAP 2X2 mode */ + bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; + bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; + break; + default: + mwifiex_dbg(priv->adapter, WARN, + "Unsupported RX-STBC, default to 2x2\n"); + bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; + bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; + break; + } + priv->ap_11n_enabled = 1; + } else { + memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap)); + bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP); + bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU; + } + + return; +} + +/* This function updates 11ac related parameters from IE + * and sets them into bss_config structure. + */ +void mwifiex_set_vht_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *vht_ie; + + vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail, + params->beacon.tail_len); + if (vht_ie) { + memcpy(&bss_cfg->vht_cap, vht_ie + 2, + sizeof(struct ieee80211_vht_cap)); + priv->ap_11ac_enabled = 1; + } else { + priv->ap_11ac_enabled = 0; + } + + return; +} + +/* This function updates 11ac related parameters from IE + * and sets them into bss_config structure. + */ +void mwifiex_set_tpc_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *tpc_ie; + + tpc_ie = cfg80211_find_ie(WLAN_EID_TPC_REQUEST, params->beacon.tail, + params->beacon.tail_len); + if (tpc_ie) + bss_cfg->power_constraint = *(tpc_ie + 2); + else + bss_cfg->power_constraint = 0; +} + +/* Enable VHT only when cfg80211_ap_settings has VHT IE. + * Otherwise disable VHT. + */ +void mwifiex_set_vht_width(struct mwifiex_private *priv, + enum nl80211_chan_width width, + bool ap_11ac_enable) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_11ac_vht_cfg vht_cfg; + + vht_cfg.band_config = VHT_CFG_5GHZ; + vht_cfg.cap_info = adapter->hw_dot_11ac_dev_cap; + + if (!ap_11ac_enable) { + vht_cfg.mcs_tx_set = DISABLE_VHT_MCS_SET; + vht_cfg.mcs_rx_set = DISABLE_VHT_MCS_SET; + } else { + vht_cfg.mcs_tx_set = DEFAULT_VHT_MCS_SET; + vht_cfg.mcs_rx_set = DEFAULT_VHT_MCS_SET; + } + + vht_cfg.misc_config = VHT_CAP_UAP_ONLY; + + if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80) + vht_cfg.misc_config |= VHT_BW_80_160_80P80; + + mwifiex_send_cmd(priv, HostCmd_CMD_11AC_CFG, + HostCmd_ACT_GEN_SET, 0, &vht_cfg, true); + + return; +} + +/* This function finds supported rates IE from beacon parameter and sets + * these rates into bss_config structure. + */ +void +mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + struct ieee_types_header *rate_ie; + int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); + const u8 *var_pos = params->beacon.head + var_offset; + int len = params->beacon.head_len - var_offset; + u8 rate_len = 0; + + rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len); + if (rate_ie) { + memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len); + rate_len = rate_ie->len; + } + + rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, + params->beacon.tail, + params->beacon.tail_len); + if (rate_ie) + memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len); + + return; +} + +/* This function initializes some of mwifiex_uap_bss_param variables. + * This helps FW in ignoring invalid values. These values may or may not + * be get updated to valid ones at later stage. + */ +void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config) +{ + config->bcast_ssid_ctl = 0x7F; + config->radio_ctl = 0x7F; + config->dtim_period = 0x7F; + config->beacon_period = 0x7FFF; + config->auth_mode = 0x7F; + config->rts_threshold = 0x7FFF; + config->frag_threshold = 0x7FFF; + config->retry_limit = 0x7F; + config->qos_info = 0xFF; +} + +/* This function parses BSS related parameters from structure + * and prepares TLVs specific to WPA/WPA2 security. + * These TLVs are appended to command buffer. + */ +static void +mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_pwk_cipher *pwk_cipher; + struct host_cmd_tlv_gwk_cipher *gwk_cipher; + struct host_cmd_tlv_passphrase *passphrase; + struct host_cmd_tlv_akmp *tlv_akmp; + struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; + u16 cmd_size = *param_size; + u8 *tlv = *tlv_buf; + + tlv_akmp = (struct host_cmd_tlv_akmp *)tlv; + tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); + tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - + sizeof(struct mwifiex_ie_types_header)); + tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation); + tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt); + cmd_size += sizeof(struct host_cmd_tlv_akmp); + tlv += sizeof(struct host_cmd_tlv_akmp); + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { + pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct mwifiex_ie_types_header)); + pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA); + pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa; + cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); + tlv += sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { + pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; + pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); + pwk_cipher->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - + sizeof(struct mwifiex_ie_types_header)); + pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2); + pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2; + cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); + tlv += sizeof(struct host_cmd_tlv_pwk_cipher); + } + + if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { + gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv; + gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); + gwk_cipher->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) - + sizeof(struct mwifiex_ie_types_header)); + gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher; + cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher); + tlv += sizeof(struct host_cmd_tlv_gwk_cipher); + } + + if (bss_cfg->wpa_cfg.length) { + passphrase = (struct host_cmd_tlv_passphrase *)tlv; + passphrase->header.type = + cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); + passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length); + memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase, + bss_cfg->wpa_cfg.length); + cmd_size += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->wpa_cfg.length; + tlv += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->wpa_cfg.length; + } + + *param_size = cmd_size; + *tlv_buf = tlv; + + return; +} + +/* This function parses WMM related parameters from cfg80211_ap_settings + * structure and updates bss_config structure. + */ +void +mwifiex_set_wmm_params(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_ap_settings *params) +{ + const u8 *vendor_ie; + struct ieee_types_header *wmm_ie; + u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02}; + + vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, + WLAN_OUI_TYPE_MICROSOFT_WMM, + params->beacon.tail, + params->beacon.tail_len); + if (vendor_ie) { + wmm_ie = (struct ieee_types_header *)vendor_ie; + memcpy(&bss_cfg->wmm_info, wmm_ie + 1, + sizeof(bss_cfg->wmm_info)); + priv->wmm_enabled = 1; + } else { + memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info)); + memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui)); + bss_cfg->wmm_info.subtype = MWIFIEX_WMM_SUBTYPE; + bss_cfg->wmm_info.version = MWIFIEX_WMM_VERSION; + priv->wmm_enabled = 0; + } + + bss_cfg->qos_info = 0x00; + return; +} +/* This function parses BSS related parameters from structure + * and prepares TLVs specific to WEP encryption. + * These TLVs are appended to command buffer. + */ +static void +mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_wep_key *wep_key; + u16 cmd_size = *param_size; + int i; + u8 *tlv = *tlv_buf; + struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (bss_cfg->wep_cfg[i].length && + (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 || + bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) { + wep_key = (struct host_cmd_tlv_wep_key *)tlv; + wep_key->header.type = + cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); + wep_key->header.len = + cpu_to_le16(bss_cfg->wep_cfg[i].length + 2); + wep_key->key_index = bss_cfg->wep_cfg[i].key_index; + wep_key->is_default = bss_cfg->wep_cfg[i].is_default; + memcpy(wep_key->key, bss_cfg->wep_cfg[i].key, + bss_cfg->wep_cfg[i].length); + cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 + + bss_cfg->wep_cfg[i].length; + tlv += sizeof(struct mwifiex_ie_types_header) + 2 + + bss_cfg->wep_cfg[i].length; + } + } + + *param_size = cmd_size; + *tlv_buf = tlv; + + return; +} + +/* This function parses BSS related parameters from structure + * and prepares TLVs. These TLVs are appended to command buffer. +*/ +static int +mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) +{ + struct host_cmd_tlv_dtim_period *dtim_period; + struct host_cmd_tlv_beacon_period *beacon_period; + struct host_cmd_tlv_ssid *ssid; + struct host_cmd_tlv_bcast_ssid *bcast_ssid; + struct host_cmd_tlv_channel_band *chan_band; + struct host_cmd_tlv_frag_threshold *frag_threshold; + struct host_cmd_tlv_rts_threshold *rts_threshold; + struct host_cmd_tlv_retry_limit *retry_limit; + struct host_cmd_tlv_encrypt_protocol *encrypt_protocol; + struct host_cmd_tlv_auth_type *auth_type; + struct host_cmd_tlv_rates *tlv_rates; + struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer; + struct host_cmd_tlv_power_constraint *pwr_ct; + struct mwifiex_ie_types_htcap *htcap; + struct mwifiex_ie_types_wmmcap *wmm_cap; + struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; + int i; + u16 cmd_size = *param_size; + + if (bss_cfg->ssid.ssid_len) { + ssid = (struct host_cmd_tlv_ssid *)tlv; + ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID); + ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len); + memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len); + cmd_size += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->ssid.ssid_len; + tlv += sizeof(struct mwifiex_ie_types_header) + + bss_cfg->ssid.ssid_len; + + bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv; + bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); + bcast_ssid->header.len = + cpu_to_le16(sizeof(bcast_ssid->bcast_ctl)); + bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl; + cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid); + tlv += sizeof(struct host_cmd_tlv_bcast_ssid); + } + if (bss_cfg->rates[0]) { + tlv_rates = (struct host_cmd_tlv_rates *)tlv; + tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES); + + for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i]; + i++) + tlv_rates->rates[i] = bss_cfg->rates[i]; + + tlv_rates->header.len = cpu_to_le16(i); + cmd_size += sizeof(struct host_cmd_tlv_rates) + i; + tlv += sizeof(struct host_cmd_tlv_rates) + i; + } + if (bss_cfg->channel && + ((bss_cfg->band_cfg == BAND_CONFIG_BG && + bss_cfg->channel <= MAX_CHANNEL_BAND_BG) || + (bss_cfg->band_cfg == BAND_CONFIG_A && + bss_cfg->channel <= MAX_CHANNEL_BAND_A))) { + chan_band = (struct host_cmd_tlv_channel_band *)tlv; + chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); + chan_band->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) - + sizeof(struct mwifiex_ie_types_header)); + chan_band->band_config = bss_cfg->band_cfg; + chan_band->channel = bss_cfg->channel; + cmd_size += sizeof(struct host_cmd_tlv_channel_band); + tlv += sizeof(struct host_cmd_tlv_channel_band); + } + if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD && + bss_cfg->beacon_period <= MAX_BEACON_PERIOD) { + beacon_period = (struct host_cmd_tlv_beacon_period *)tlv; + beacon_period->header.type = + cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); + beacon_period->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) - + sizeof(struct mwifiex_ie_types_header)); + beacon_period->period = cpu_to_le16(bss_cfg->beacon_period); + cmd_size += sizeof(struct host_cmd_tlv_beacon_period); + tlv += sizeof(struct host_cmd_tlv_beacon_period); + } + if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD && + bss_cfg->dtim_period <= MAX_DTIM_PERIOD) { + dtim_period = (struct host_cmd_tlv_dtim_period *)tlv; + dtim_period->header.type = + cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); + dtim_period->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) - + sizeof(struct mwifiex_ie_types_header)); + dtim_period->period = bss_cfg->dtim_period; + cmd_size += sizeof(struct host_cmd_tlv_dtim_period); + tlv += sizeof(struct host_cmd_tlv_dtim_period); + } + if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) { + rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv; + rts_threshold->header.type = + cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); + rts_threshold->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) - + sizeof(struct mwifiex_ie_types_header)); + rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold); + cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); + tlv += sizeof(struct host_cmd_tlv_frag_threshold); + } + if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) && + (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) { + frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv; + frag_threshold->header.type = + cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); + frag_threshold->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) - + sizeof(struct mwifiex_ie_types_header)); + frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold); + cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); + tlv += sizeof(struct host_cmd_tlv_frag_threshold); + } + if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) { + retry_limit = (struct host_cmd_tlv_retry_limit *)tlv; + retry_limit->header.type = + cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); + retry_limit->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) - + sizeof(struct mwifiex_ie_types_header)); + retry_limit->limit = (u8)bss_cfg->retry_limit; + cmd_size += sizeof(struct host_cmd_tlv_retry_limit); + tlv += sizeof(struct host_cmd_tlv_retry_limit); + } + if ((bss_cfg->protocol & PROTOCOL_WPA) || + (bss_cfg->protocol & PROTOCOL_WPA2) || + (bss_cfg->protocol & PROTOCOL_EAP)) + mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size); + else + mwifiex_uap_bss_wep(&tlv, cmd_buf, &cmd_size); + + if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) || + (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) { + auth_type = (struct host_cmd_tlv_auth_type *)tlv; + auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); + auth_type->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) - + sizeof(struct mwifiex_ie_types_header)); + auth_type->auth_type = (u8)bss_cfg->auth_mode; + cmd_size += sizeof(struct host_cmd_tlv_auth_type); + tlv += sizeof(struct host_cmd_tlv_auth_type); + } + if (bss_cfg->protocol) { + encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv; + encrypt_protocol->header.type = + cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL); + encrypt_protocol->header.len = + cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol) + - sizeof(struct mwifiex_ie_types_header)); + encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol); + cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol); + tlv += sizeof(struct host_cmd_tlv_encrypt_protocol); + } + + if (bss_cfg->ht_cap.cap_info) { + htcap = (struct mwifiex_ie_types_htcap *)tlv; + htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); + htcap->header.len = + cpu_to_le16(sizeof(struct ieee80211_ht_cap)); + htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info; + htcap->ht_cap.ampdu_params_info = + bss_cfg->ht_cap.ampdu_params_info; + memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs, + sizeof(struct ieee80211_mcs_info)); + htcap->ht_cap.extended_ht_cap_info = + bss_cfg->ht_cap.extended_ht_cap_info; + htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info; + htcap->ht_cap.antenna_selection_info = + bss_cfg->ht_cap.antenna_selection_info; + cmd_size += sizeof(struct mwifiex_ie_types_htcap); + tlv += sizeof(struct mwifiex_ie_types_htcap); + } + + if (bss_cfg->wmm_info.qos_info != 0xFF) { + wmm_cap = (struct mwifiex_ie_types_wmmcap *)tlv; + wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC); + wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info)); + memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info, + sizeof(wmm_cap->wmm_info)); + cmd_size += sizeof(struct mwifiex_ie_types_wmmcap); + tlv += sizeof(struct mwifiex_ie_types_wmmcap); + } + + if (bss_cfg->sta_ao_timer) { + ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; + ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); + ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) - + sizeof(struct mwifiex_ie_types_header)); + ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer); + cmd_size += sizeof(*ao_timer); + tlv += sizeof(*ao_timer); + } + + if (bss_cfg->power_constraint) { + pwr_ct = (void *)tlv; + pwr_ct->header.type = cpu_to_le16(TLV_TYPE_PWR_CONSTRAINT); + pwr_ct->header.len = cpu_to_le16(sizeof(u8)); + pwr_ct->constraint = bss_cfg->power_constraint; + cmd_size += sizeof(*pwr_ct); + tlv += sizeof(*pwr_ct); + } + + if (bss_cfg->ps_sta_ao_timer) { + ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; + ps_ao_timer->header.type = + cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER); + ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) - + sizeof(struct mwifiex_ie_types_header)); + ps_ao_timer->sta_ao_timer = + cpu_to_le32(bss_cfg->ps_sta_ao_timer); + cmd_size += sizeof(*ps_ao_timer); + tlv += sizeof(*ps_ao_timer); + } + + *param_size = cmd_size; + + return 0; +} + +/* This function parses custom IEs from IE list and prepares command buffer */ +static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size) +{ + struct mwifiex_ie_list *ap_ie = cmd_buf; + struct mwifiex_ie_types_header *tlv_ie = (void *)tlv; + + if (!ap_ie || !ap_ie->len || !ap_ie->ie_list) + return -1; + + *ie_size += le16_to_cpu(ap_ie->len) + + sizeof(struct mwifiex_ie_types_header); + + tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); + tlv_ie->len = ap_ie->len; + tlv += sizeof(struct mwifiex_ie_types_header); + + memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len)); + + return 0; +} + +/* Parse AP config structure and prepare TLV based command structure + * to be sent to FW for uAP configuration + */ +static int +mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action, + u32 type, void *cmd_buf) +{ + u8 *tlv; + u16 cmd_size, param_size, ie_size; + struct host_cmd_ds_sys_config *sys_cfg; + + cmd->command = cpu_to_le16(HostCmd_CMD_UAP_SYS_CONFIG); + cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN); + sys_cfg = (struct host_cmd_ds_sys_config *)&cmd->params.uap_sys_config; + sys_cfg->action = cpu_to_le16(cmd_action); + tlv = sys_cfg->tlv; + + switch (type) { + case UAP_BSS_PARAMS_I: + param_size = cmd_size; + if (mwifiex_uap_bss_param_prepare(tlv, cmd_buf, ¶m_size)) + return -1; + cmd->size = cpu_to_le16(param_size); + break; + case UAP_CUSTOM_IE_I: + ie_size = cmd_size; + if (mwifiex_uap_custom_ie_prepare(tlv, cmd_buf, &ie_size)) + return -1; + cmd->size = cpu_to_le16(ie_size); + break; + default: + return -1; + } + + return 0; +} + +/* This function prepares AP specific deauth command with mac supplied in + * function parameter. + */ +static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv, + struct host_cmd_ds_command *cmd, u8 *mac) +{ + struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth; + + cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH); + memcpy(sta_deauth->mac, mac, ETH_ALEN); + sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); + + cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) + + S_DS_GEN); + return 0; +} + +/* This function prepares the AP specific commands before sending them + * to the firmware. + * This is a generic function which calls specific command preparation + * routines based upon the command number. + */ +int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, + u16 cmd_action, u32 type, + void *data_buf, void *cmd_buf) +{ + struct host_cmd_ds_command *cmd = cmd_buf; + + switch (cmd_no) { + case HostCmd_CMD_UAP_SYS_CONFIG: + if (mwifiex_cmd_uap_sys_config(cmd, cmd_action, type, data_buf)) + return -1; + break; + case HostCmd_CMD_UAP_BSS_START: + case HostCmd_CMD_UAP_BSS_STOP: + case HOST_CMD_APCMD_SYS_RESET: + case HOST_CMD_APCMD_STA_LIST: + cmd->command = cpu_to_le16(cmd_no); + cmd->size = cpu_to_le16(S_DS_GEN); + break; + case HostCmd_CMD_UAP_STA_DEAUTH: + if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf)) + return -1; + break; + case HostCmd_CMD_CHAN_REPORT_REQUEST: + if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf, + data_buf)) + return -1; + break; + default: + mwifiex_dbg(priv->adapter, ERROR, + "PREP_CMD: unknown cmd %#x\n", cmd_no); + return -1; + } + + return 0; +} + +void mwifiex_uap_set_channel(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg, + struct cfg80211_chan_def chandef) +{ + u8 config_bands = 0, old_bands = priv->adapter->config_bands; + + priv->bss_chandef = chandef; + + bss_cfg->channel = ieee80211_frequency_to_channel( + chandef.chan->center_freq); + + /* Set appropriate bands */ + if (chandef.chan->band == IEEE80211_BAND_2GHZ) { + bss_cfg->band_cfg = BAND_CONFIG_BG; + config_bands = BAND_B | BAND_G; + + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_GN; + } else { + bss_cfg->band_cfg = BAND_CONFIG_A; + config_bands = BAND_A; + + if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) + config_bands |= BAND_AN; + + if (chandef.width > NL80211_CHAN_WIDTH_40) + config_bands |= BAND_AAC; + } + + priv->adapter->config_bands = config_bands; + + if (old_bands != config_bands) { + mwifiex_send_domain_info_cmd_fw(priv->adapter->wiphy); + mwifiex_dnld_txpwr_table(priv); + } +} + +int mwifiex_config_start_uap(struct mwifiex_private *priv, + struct mwifiex_uap_bss_param *bss_cfg) +{ + enum state_11d_t state_11d; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, + HostCmd_ACT_GEN_SET, + UAP_BSS_PARAMS_I, bss_cfg, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to set the SSID\n"); + return -1; + } + + /* Send cmd to FW to enable 11D function */ + state_11d = ENABLE_11D; + if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, + HostCmd_ACT_GEN_SET, DOT11D_I, + &state_11d, true)) { + mwifiex_dbg(priv->adapter, ERROR, + "11D: failed to enable 11D\n"); + return -1; + } + + if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, + HostCmd_ACT_GEN_SET, 0, NULL, false)) { + mwifiex_dbg(priv->adapter, ERROR, + "Failed to start the BSS\n"); + return -1; + } + + if (priv->sec_info.wep_enabled) + priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; + else + priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; + + if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, + HostCmd_ACT_GEN_SET, 0, + &priv->curr_pkt_filter, true)) + return -1; + + return 0; +} diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c new file mode 100644 index 000000000000..86ff54296f39 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c @@ -0,0 +1,333 @@ +/* + * Marvell Wireless LAN device driver: AP event handling + * + * Copyright (C) 2012-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "main.h" +#include "11n.h" + +#define MWIFIEX_BSS_START_EVT_FIX_SIZE 12 + +static int mwifiex_check_uap_capabilties(struct mwifiex_private *priv, + struct sk_buff *event) +{ + int evt_len; + u8 *curr; + u16 tlv_len; + struct mwifiex_ie_types_data *tlv_hdr; + struct ieee_types_wmm_parameter *wmm_param_ie = NULL; + int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; + + priv->wmm_enabled = false; + skb_pull(event, MWIFIEX_BSS_START_EVT_FIX_SIZE); + evt_len = event->len; + curr = event->data; + + mwifiex_dbg_dump(priv->adapter, EVT_D, "uap capabilties:", + event->data, event->len); + + skb_push(event, MWIFIEX_BSS_START_EVT_FIX_SIZE); + + while ((evt_len >= sizeof(tlv_hdr->header))) { + tlv_hdr = (struct mwifiex_ie_types_data *)curr; + tlv_len = le16_to_cpu(tlv_hdr->header.len); + + if (evt_len < tlv_len + sizeof(tlv_hdr->header)) + break; + + switch (le16_to_cpu(tlv_hdr->header.type)) { + case WLAN_EID_HT_CAPABILITY: + priv->ap_11n_enabled = true; + break; + + case WLAN_EID_VHT_CAPABILITY: + priv->ap_11ac_enabled = true; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + /* Point the regular IEEE IE 2 bytes into the Marvell IE + * and setup the IEEE IE type and length byte fields + */ + wmm_param_ie = (void *)(curr + 2); + wmm_param_ie->vend_hdr.len = (u8)tlv_len; + wmm_param_ie->vend_hdr.element_id = + WLAN_EID_VENDOR_SPECIFIC; + mwifiex_dbg(priv->adapter, EVENT, + "info: check uap capabilities:\t" + "wmm parameter set count: %d\n", + wmm_param_ie->qos_info_bitmap & mask); + + mwifiex_wmm_setup_ac_downgrade(priv); + priv->wmm_enabled = true; + mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); + break; + + default: + break; + } + + curr += (tlv_len + sizeof(tlv_hdr->header)); + evt_len -= (tlv_len + sizeof(tlv_hdr->header)); + } + + return 0; +} + +/* + * This function handles AP interface specific events generated by firmware. + * + * Event specific routines are called by this function based + * upon the generated event cause. + * + * + * Events supported for AP - + * - EVENT_UAP_STA_ASSOC + * - EVENT_UAP_STA_DEAUTH + * - EVENT_UAP_BSS_ACTIVE + * - EVENT_UAP_BSS_START + * - EVENT_UAP_BSS_IDLE + * - EVENT_UAP_MIC_COUNTERMEASURES: + */ +int mwifiex_process_uap_event(struct mwifiex_private *priv) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int len, i; + u32 eventcause = adapter->event_cause; + struct station_info sinfo; + struct mwifiex_assoc_event *event; + struct mwifiex_sta_node *node; + u8 *deauth_mac; + struct host_cmd_ds_11n_batimeout *ba_timeout; + u16 ctrl; + + switch (eventcause) { + case EVENT_UAP_STA_ASSOC: + memset(&sinfo, 0, sizeof(sinfo)); + event = (struct mwifiex_assoc_event *) + (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER); + if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) { + len = -1; + + if (ieee80211_is_assoc_req(event->frame_control)) + len = 0; + else if (ieee80211_is_reassoc_req(event->frame_control)) + /* There will be ETH_ALEN bytes of + * current_ap_addr before the re-assoc ies. + */ + len = ETH_ALEN; + + if (len != -1) { + sinfo.assoc_req_ies = &event->data[len]; + len = (u8 *)sinfo.assoc_req_ies - + (u8 *)&event->frame_control; + sinfo.assoc_req_ies_len = + le16_to_cpu(event->len) - (u16)len; + } + } + cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo, + GFP_KERNEL); + + node = mwifiex_add_sta_entry(priv, event->sta_addr); + if (!node) { + mwifiex_dbg(adapter, ERROR, + "could not create station entry!\n"); + return -1; + } + + if (!priv->ap_11n_enabled) + break; + + mwifiex_set_sta_ht_cap(priv, sinfo.assoc_req_ies, + sinfo.assoc_req_ies_len, node); + + for (i = 0; i < MAX_NUM_TID; i++) { + if (node->is_11n_enabled) + node->ampdu_sta[i] = + priv->aggr_prio_tbl[i].ampdu_user; + else + node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; + } + memset(node->rx_seq, 0xff, sizeof(node->rx_seq)); + break; + case EVENT_UAP_STA_DEAUTH: + deauth_mac = adapter->event_body + + MWIFIEX_UAP_EVENT_EXTRA_HEADER; + cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL); + + if (priv->ap_11n_enabled) { + mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac); + mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac); + } + mwifiex_wmm_del_peer_ra_list(priv, deauth_mac); + mwifiex_del_sta_entry(priv, deauth_mac); + break; + case EVENT_UAP_BSS_IDLE: + priv->media_connected = false; + priv->port_open = false; + mwifiex_clean_txrx(priv); + mwifiex_del_all_sta_list(priv); + break; + case EVENT_UAP_BSS_ACTIVE: + priv->media_connected = true; + priv->port_open = true; + break; + case EVENT_UAP_BSS_START: + mwifiex_dbg(adapter, EVENT, + "AP EVENT: event id: %#x\n", eventcause); + priv->port_open = false; + memcpy(priv->netdev->dev_addr, adapter->event_body + 2, + ETH_ALEN); + if (priv->hist_data) + mwifiex_hist_data_reset(priv); + mwifiex_check_uap_capabilties(priv, adapter->event_skb); + break; + case EVENT_UAP_MIC_COUNTERMEASURES: + /* For future development */ + mwifiex_dbg(adapter, EVENT, + "AP EVENT: event id: %#x\n", eventcause); + break; + case EVENT_AMSDU_AGGR_CTRL: + ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); + mwifiex_dbg(adapter, EVENT, + "event: AMSDU_AGGR_CTRL %d\n", ctrl); + + if (priv->media_connected) { + adapter->tx_buf_size = + min_t(u16, adapter->curr_tx_buf_size, ctrl); + mwifiex_dbg(adapter, EVENT, + "event: tx_buf_size %d\n", + adapter->tx_buf_size); + } + break; + case EVENT_ADDBA: + mwifiex_dbg(adapter, EVENT, "event: ADDBA Request\n"); + if (priv->media_connected) + mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, + HostCmd_ACT_GEN_SET, 0, + adapter->event_body, false); + break; + case EVENT_DELBA: + mwifiex_dbg(adapter, EVENT, "event: DELBA Request\n"); + if (priv->media_connected) + mwifiex_11n_delete_ba_stream(priv, adapter->event_body); + break; + case EVENT_BA_STREAM_TIEMOUT: + mwifiex_dbg(adapter, EVENT, "event: BA Stream timeout\n"); + if (priv->media_connected) { + ba_timeout = (void *)adapter->event_body; + mwifiex_11n_ba_stream_timeout(priv, ba_timeout); + } + break; + case EVENT_EXT_SCAN_REPORT: + mwifiex_dbg(adapter, EVENT, "event: EXT_SCAN Report\n"); + if (adapter->ext_scan) + return mwifiex_handle_event_ext_scan_report(priv, + adapter->event_skb->data); + break; + case EVENT_TX_STATUS_REPORT: + mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n"); + mwifiex_parse_tx_status_event(priv, adapter->event_body); + break; + case EVENT_PS_SLEEP: + mwifiex_dbg(adapter, EVENT, "info: EVENT: SLEEP\n"); + + adapter->ps_state = PS_STATE_PRE_SLEEP; + + mwifiex_check_ps_cond(adapter); + break; + + case EVENT_PS_AWAKE: + mwifiex_dbg(adapter, EVENT, "info: EVENT: AWAKE\n"); + if (!adapter->pps_uapsd_mode && + priv->media_connected && adapter->sleep_period.period) { + adapter->pps_uapsd_mode = true; + mwifiex_dbg(adapter, EVENT, + "event: PPS/UAPSD mode activated\n"); + } + adapter->tx_lock_flag = false; + if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { + if (mwifiex_check_last_packet_indication(priv)) { + if (adapter->data_sent || + (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv))) { + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + break; + } + if (!mwifiex_send_null_packet + (priv, + MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | + MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) + adapter->ps_state = + PS_STATE_SLEEP; + return 0; + } + } + adapter->ps_state = PS_STATE_AWAKE; + adapter->pm_wakeup_card_req = false; + adapter->pm_wakeup_fw_try = false; + break; + + case EVENT_CHANNEL_REPORT_RDY: + mwifiex_dbg(adapter, EVENT, "event: Channel Report\n"); + mwifiex_11h_handle_chanrpt_ready(priv, adapter->event_skb); + break; + case EVENT_RADAR_DETECTED: + mwifiex_dbg(adapter, EVENT, "event: Radar detected\n"); + mwifiex_11h_handle_radar_detected(priv, adapter->event_skb); + break; + case EVENT_BT_COEX_WLAN_PARA_CHANGE: + dev_err(adapter->dev, "EVENT: BT coex wlan param update\n"); + mwifiex_bt_coex_wlan_param_update_event(priv, + adapter->event_skb); + break; + case EVENT_TX_DATA_PAUSE: + mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n"); + mwifiex_process_tx_pause_event(priv, adapter->event_skb); + break; + + case EVENT_MULTI_CHAN_INFO: + mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n"); + mwifiex_process_multi_chan_event(priv, adapter->event_skb); + break; + + default: + mwifiex_dbg(adapter, EVENT, + "event: unknown event id: %#x\n", eventcause); + break; + } + + return 0; +} + +/* This function deletes station entry from associated station list. + * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream + * tables created for this station are deleted. + */ +void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, + struct mwifiex_sta_node *node) +{ + if (priv->ap_11n_enabled && node->is_11n_enabled) { + mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr); + mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr); + } + mwifiex_del_sta_entry(priv, node->mac_addr); + + return; +} diff --git a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c new file mode 100644 index 000000000000..74d5d7238633 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c @@ -0,0 +1,437 @@ +/* + * Marvell Wireless LAN device driver: AP TX and RX data handling + * + * Copyright (C) 2012-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "main.h" +#include "wmm.h" +#include "11n_aggr.h" +#include "11n_rxreorder.h" + +/* This function checks if particular RA list has packets more than low bridge + * packet threshold and then deletes packet from this RA list. + * Function deletes packets from such RA list and returns true. If no such list + * is found, false is returned. + */ +static bool +mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, + struct list_head *ra_list_head, + int tid) +{ + struct mwifiex_ra_list_tbl *ra_list; + struct sk_buff *skb, *tmp; + bool pkt_deleted = false; + struct mwifiex_txinfo *tx_info; + struct mwifiex_adapter *adapter = priv->adapter; + + list_for_each_entry(ra_list, ra_list_head, list) { + if (skb_queue_empty(&ra_list->skb_head)) + continue; + + skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) { + tx_info = MWIFIEX_SKB_TXCB(skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) { + __skb_unlink(skb, &ra_list->skb_head); + mwifiex_write_data_complete(adapter, skb, 0, + -1); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[tid]--; + else + atomic_dec(&priv->wmm.tx_pkts_queued); + pkt_deleted = true; + } + if ((atomic_read(&adapter->pending_bridged_pkts) <= + MWIFIEX_BRIDGED_PKTS_THR_LOW)) + break; + } + } + + return pkt_deleted; +} + +/* This function deletes packets from particular RA List. RA list index + * from which packets are deleted is preserved so that packets from next RA + * list are deleted upon subsequent call thus maintaining fairness. + */ +static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv) +{ + unsigned long flags; + struct list_head *ra_list; + int i; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) { + if (priv->del_list_idx == MAX_NUM_TID) + priv->del_list_idx = 0; + ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list; + if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) { + priv->del_list_idx++; + break; + } + } + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + + +static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + struct sk_buff *new_skb; + struct mwifiex_txinfo *tx_info; + int hdr_chop; + struct ethhdr *p_ethhdr; + struct mwifiex_sta_node *src_node; + + uap_rx_pd = (struct uap_rxpd *)(skb->data); + rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + if ((atomic_read(&adapter->pending_bridged_pkts) >= + MWIFIEX_BRIDGED_PKTS_THR_HIGH)) { + mwifiex_dbg(priv->adapter, ERROR, + "Tx: Bridge packet limit reached. Drop packet!\n"); + kfree_skb(skb); + mwifiex_uap_cleanup_tx_queues(priv); + return; + } + + if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, + sizeof(bridge_tunnel_header))) || + (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, + sizeof(rfc1042_header)) && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && + ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { + /* Replace the 803 header and rfc1042 header (llc/snap) with + * an Ethernet II 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. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *)(&rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->eth803_hdr) + + sizeof(rx_pkt_hdr->rfc1042_hdr) + - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) + - sizeof(rx_pkt_hdr->eth803_hdr.h_source) + - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); + memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, + sizeof(p_ethhdr->h_dest)); + /* Chop off the rxpd + the excess memory from + * 802.2/llc/snap header that was removed. + */ + hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd; + } else { + /* Chop off the rxpd */ + hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd; + } + + /* Chop off the leading header bytes so that it points + * to the start of either the reconstructed EthII frame + * or the 802.2/llc/snap frame. + */ + skb_pull(skb, hdr_chop); + + if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { + mwifiex_dbg(priv->adapter, ERROR, + "data: Tx: insufficient skb headroom %d\n", + skb_headroom(skb)); + /* Insufficient skb headroom - allocate a new skb */ + new_skb = + skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); + if (unlikely(!new_skb)) { + mwifiex_dbg(priv->adapter, ERROR, + "Tx: cannot allocate new_skb\n"); + kfree_skb(skb); + priv->stats.tx_dropped++; + return; + } + + kfree_skb(skb); + skb = new_skb; + mwifiex_dbg(priv->adapter, INFO, + "info: new skb headroom %d\n", + skb_headroom(skb)); + } + + tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); + tx_info->bss_num = priv->bss_num; + tx_info->bss_type = priv->bss_type; + tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT; + + src_node = mwifiex_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source); + if (src_node) { + src_node->stats.last_rx = jiffies; + src_node->stats.rx_bytes += skb->len; + src_node->stats.rx_packets++; + src_node->stats.last_tx_rate = uap_rx_pd->rx_rate; + src_node->stats.last_tx_htinfo = uap_rx_pd->ht_info; + } + + if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) { + /* Update bridge packet statistics as the + * packet is not going to kernel/upper layer. + */ + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + /* Sending bridge packet to TX queue, so save the packet + * length in TXCB to update statistics in TX complete. + */ + tx_info->pkt_len = skb->len; + } + + __net_timestamp(skb); + mwifiex_wmm_add_buf_txqueue(priv, skb); + atomic_inc(&adapter->tx_pending); + atomic_inc(&adapter->pending_bridged_pkts); + + return; +} + +/* + * This function contains logic for AP packet forwarding. + * + * If a packet is multicast/broadcast, it is sent to kernel/upper layer + * as well as queued back to AP TX queue so that it can be sent to other + * associated stations. + * If a packet is unicast and RA is present in associated station list, + * it is again requeued into AP TX queue. + * If a packet is unicast and RA is not in associated station list, + * packet is forwarded to kernel to handle routing logic. + */ +int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u8 ra[ETH_ALEN]; + struct sk_buff *skb_uap; + + uap_rx_pd = (struct uap_rxpd *)(skb->data); + rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + /* don't do packet forwarding in disconnected state */ + if (!priv->media_connected) { + mwifiex_dbg(adapter, ERROR, + "drop packet in disconnected state.\n"); + dev_kfree_skb_any(skb); + return 0; + } + + memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN); + + if (is_multicast_ether_addr(ra)) { + skb_uap = skb_copy(skb, GFP_ATOMIC); + mwifiex_uap_queue_bridged_pkt(priv, skb_uap); + } else { + if (mwifiex_get_sta_entry(priv, ra)) { + /* Requeue Intra-BSS packet */ + mwifiex_uap_queue_bridged_pkt(priv, skb); + return 0; + } + } + + /* Forward unicat/Inter-BSS packets to kernel. */ + return mwifiex_process_rx_packet(priv, skb); +} + +/* + * This function processes the packet received on AP interface. + * + * The function looks into the RxPD and performs sanity tests on the + * received buffer to ensure its a valid packet before processing it + * further. If the packet is determined to be aggregated, it is + * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic. + * + * The completion callback is called after processing is complete. + */ +int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + int ret; + struct uap_rxpd *uap_rx_pd; + struct rx_packet_hdr *rx_pkt_hdr; + u16 rx_pkt_type; + u8 ta[ETH_ALEN], pkt_type; + unsigned long flags; + struct mwifiex_sta_node *node; + + uap_rx_pd = (struct uap_rxpd *)(skb->data); + rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type); + rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); + + ether_addr_copy(ta, rx_pkt_hdr->eth803_hdr.h_source); + + if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) + + le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16) skb->len) { + mwifiex_dbg(adapter, ERROR, + "wrong rx packet: len=%d, offset=%d, length=%d\n", + skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset), + le16_to_cpu(uap_rx_pd->rx_pkt_length)); + priv->stats.rx_dropped++; + + node = mwifiex_get_sta_entry(priv, ta); + if (node) + node->stats.tx_failed++; + + dev_kfree_skb_any(skb); + return 0; + } + + if (rx_pkt_type == PKT_TYPE_MGMT) { + ret = mwifiex_process_mgmt_packet(priv, skb); + if (ret) + mwifiex_dbg(adapter, ERROR, + "Rx of mgmt packet failed"); + dev_kfree_skb_any(skb); + return ret; + } + + + if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + node = mwifiex_get_sta_entry(priv, ta); + if (node) + node->rx_seq[uap_rx_pd->priority] = + le16_to_cpu(uap_rx_pd->seq_num); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + } + + if (!priv->ap_11n_enabled || + (!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) && + (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) { + ret = mwifiex_handle_uap_rx_forward(priv, skb); + return ret; + } + + /* Reorder and send to kernel */ + pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type); + ret = mwifiex_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num), + uap_rx_pd->priority, ta, pkt_type, + skb); + + if (ret || (rx_pkt_type == PKT_TYPE_BAR)) + dev_kfree_skb_any(skb); + + if (ret) + priv->stats.rx_dropped++; + + return ret; +} + +/* + * This function fills the TxPD for AP tx packets. + * + * The Tx buffer received by this function should already have the + * header space allocated for TxPD. + * + * This function inserts the TxPD in between interface header and actual + * data and adjusts the buffer pointers accordingly. + * + * The following TxPD fields are set by this function, as required - + * - BSS number + * - Tx packet length and offset + * - Priority + * - Packet delay + * - Priority specific Tx control + * - Flags + */ +void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct uap_txpd *txpd; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + int pad; + u16 pkt_type, pkt_offset; + int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : + INTF_HEADER_LEN; + + if (!skb->len) { + mwifiex_dbg(adapter, ERROR, + "Tx: bad packet length: %d\n", skb->len); + tx_info->status_code = -1; + return skb->data; + } + + BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); + + pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; + + pad = ((void *)skb->data - (sizeof(*txpd) + hroom) - NULL) & + (MWIFIEX_DMA_ALIGN_SZ - 1); + + skb_push(skb, sizeof(*txpd) + pad); + + txpd = (struct uap_txpd *)skb->data; + memset(txpd, 0, sizeof(*txpd)); + txpd->bss_num = priv->bss_num; + txpd->bss_type = priv->bss_type; + txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) + + pad))); + txpd->priority = (u8)skb->priority; + + txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); + + if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS || + tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) { + txpd->tx_token_id = tx_info->ack_frame_id; + txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS; + } + + if (txpd->priority < ARRAY_SIZE(priv->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. + */ + txpd->tx_control = + cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); + + /* Offset of actual data */ + pkt_offset = sizeof(*txpd) + pad; + if (pkt_type == PKT_TYPE_MGMT) { + /* Set the packet type and add header for management frame */ + txpd->tx_pkt_type = cpu_to_le16(pkt_type); + pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; + } + + txpd->tx_pkt_offset = cpu_to_le16(pkt_offset); + + /* make space for INTF_HEADER_LEN */ + skb_push(skb, hroom); + + if (!txpd->tx_control) + /* TxCtrl set by user or default */ + txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); + + return skb->data; +} diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c new file mode 100644 index 000000000000..e43aff932360 --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -0,0 +1,1267 @@ +/* + * Marvell Wireless LAN device driver: USB specific handling + * + * Copyright (C) 2012-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "main.h" +#include "usb.h" + +#define USB_VERSION "1.0" + +static u8 user_rmmod; +static struct mwifiex_if_ops usb_ops; +static struct semaphore add_remove_card_sem; + +static struct usb_device_id mwifiex_usb_table[] = { + /* 8766 */ + {USB_DEVICE(USB8XXX_VID, USB8766_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8766_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + /* 8797 */ + {USB_DEVICE(USB8XXX_VID, USB8797_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8797_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + /* 8801 */ + {USB_DEVICE(USB8XXX_VID, USB8801_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8801_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + /* 8997 */ + {USB_DEVICE(USB8XXX_VID, USB8997_PID_1)}, + {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8997_PID_2, + USB_CLASS_VENDOR_SPEC, + USB_SUBCLASS_VENDOR_SPEC, 0xff)}, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, mwifiex_usb_table); + +static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size); + +/* This function handles received packet. Necessary action is taken based on + * cmd/event/data. + */ +static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, + struct sk_buff *skb, u8 ep) +{ + u32 recv_type; + __le32 tmp; + int ret; + + if (adapter->hs_activated) + mwifiex_process_hs_config(adapter); + + if (skb->len < INTF_HEADER_LEN) { + mwifiex_dbg(adapter, ERROR, + "%s: invalid skb->len\n", __func__); + return -1; + } + + switch (ep) { + case MWIFIEX_USB_EP_CMD_EVENT: + mwifiex_dbg(adapter, EVENT, + "%s: EP_CMD_EVENT\n", __func__); + skb_copy_from_linear_data(skb, &tmp, INTF_HEADER_LEN); + recv_type = le32_to_cpu(tmp); + skb_pull(skb, INTF_HEADER_LEN); + + switch (recv_type) { + case MWIFIEX_USB_TYPE_CMD: + if (skb->len > MWIFIEX_SIZE_OF_CMD_BUFFER) { + mwifiex_dbg(adapter, ERROR, + "CMD: skb->len too large\n"); + ret = -1; + goto exit_restore_skb; + } else if (!adapter->curr_cmd) { + mwifiex_dbg(adapter, WARN, "CMD: no curr_cmd\n"); + if (adapter->ps_state == PS_STATE_SLEEP_CFM) { + mwifiex_process_sleep_confirm_resp( + adapter, skb->data, + skb->len); + ret = 0; + goto exit_restore_skb; + } + ret = -1; + goto exit_restore_skb; + } + + adapter->curr_cmd->resp_skb = skb; + adapter->cmd_resp_received = true; + break; + case MWIFIEX_USB_TYPE_EVENT: + if (skb->len < sizeof(u32)) { + mwifiex_dbg(adapter, ERROR, + "EVENT: skb->len too small\n"); + ret = -1; + goto exit_restore_skb; + } + skb_copy_from_linear_data(skb, &tmp, sizeof(u32)); + adapter->event_cause = le32_to_cpu(tmp); + mwifiex_dbg(adapter, EVENT, + "event_cause %#x\n", adapter->event_cause); + + if (skb->len > MAX_EVENT_SIZE) { + mwifiex_dbg(adapter, ERROR, + "EVENT: event body too large\n"); + ret = -1; + goto exit_restore_skb; + } + + memcpy(adapter->event_body, skb->data + + MWIFIEX_EVENT_HEADER_LEN, skb->len); + + adapter->event_received = true; + adapter->event_skb = skb; + break; + default: + mwifiex_dbg(adapter, ERROR, + "unknown recv_type %#x\n", recv_type); + return -1; + } + break; + case MWIFIEX_USB_EP_DATA: + mwifiex_dbg(adapter, DATA, "%s: EP_DATA\n", __func__); + if (skb->len > MWIFIEX_RX_DATA_BUF_SIZE) { + mwifiex_dbg(adapter, ERROR, + "DATA: skb->len too large\n"); + return -1; + } + + skb_queue_tail(&adapter->rx_data_q, skb); + adapter->data_received = true; + atomic_inc(&adapter->rx_pending); + break; + default: + mwifiex_dbg(adapter, ERROR, + "%s: unknown endport %#x\n", __func__, ep); + return -1; + } + + return -EINPROGRESS; + +exit_restore_skb: + /* The buffer will be reused for further cmds/events */ + skb_push(skb, INTF_HEADER_LEN); + + return ret; +} + +static void mwifiex_usb_rx_complete(struct urb *urb) +{ + struct urb_context *context = (struct urb_context *)urb->context; + struct mwifiex_adapter *adapter = context->adapter; + struct sk_buff *skb = context->skb; + struct usb_card_rec *card; + int recv_length = urb->actual_length; + int size, status; + + if (!adapter || !adapter->card) { + pr_err("mwifiex adapter or card structure is not valid\n"); + return; + } + + card = (struct usb_card_rec *)adapter->card; + if (card->rx_cmd_ep == context->ep) + atomic_dec(&card->rx_cmd_urb_pending); + else + atomic_dec(&card->rx_data_urb_pending); + + if (recv_length) { + if (urb->status || (adapter->surprise_removed)) { + mwifiex_dbg(adapter, ERROR, + "URB status is failed: %d\n", urb->status); + /* Do not free skb in case of command ep */ + if (card->rx_cmd_ep != context->ep) + dev_kfree_skb_any(skb); + goto setup_for_next; + } + if (skb->len > recv_length) + skb_trim(skb, recv_length); + else + skb_put(skb, recv_length - skb->len); + + status = mwifiex_usb_recv(adapter, skb, context->ep); + + mwifiex_dbg(adapter, INFO, + "info: recv_length=%d, status=%d\n", + recv_length, status); + if (status == -EINPROGRESS) { + mwifiex_queue_main_work(adapter); + + /* urb for data_ep is re-submitted now; + * urb for cmd_ep will be re-submitted in callback + * mwifiex_usb_recv_complete + */ + if (card->rx_cmd_ep == context->ep) + return; + } else { + if (status == -1) + mwifiex_dbg(adapter, ERROR, + "received data processing failed!\n"); + + /* Do not free skb in case of command ep */ + if (card->rx_cmd_ep != context->ep) + dev_kfree_skb_any(skb); + } + } else if (urb->status) { + if (!adapter->is_suspended) { + mwifiex_dbg(adapter, FATAL, + "Card is removed: %d\n", urb->status); + adapter->surprise_removed = true; + } + dev_kfree_skb_any(skb); + return; + } else { + /* Do not free skb in case of command ep */ + if (card->rx_cmd_ep != context->ep) + dev_kfree_skb_any(skb); + + /* fall through setup_for_next */ + } + +setup_for_next: + if (card->rx_cmd_ep == context->ep) + size = MWIFIEX_RX_CMD_BUF_SIZE; + else + size = MWIFIEX_RX_DATA_BUF_SIZE; + + if (card->rx_cmd_ep == context->ep) { + mwifiex_usb_submit_rx_urb(context, size); + } else { + if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING){ + mwifiex_usb_submit_rx_urb(context, size); + }else{ + context->skb = NULL; + } + } + + return; +} + +static void mwifiex_usb_tx_complete(struct urb *urb) +{ + struct urb_context *context = (struct urb_context *)(urb->context); + struct mwifiex_adapter *adapter = context->adapter; + struct usb_card_rec *card = adapter->card; + struct usb_tx_data_port *port; + int i; + + mwifiex_dbg(adapter, INFO, + "%s: status: %d\n", __func__, urb->status); + + if (context->ep == card->tx_cmd_ep) { + mwifiex_dbg(adapter, CMD, + "%s: CMD\n", __func__); + atomic_dec(&card->tx_cmd_urb_pending); + adapter->cmd_sent = false; + } else { + mwifiex_dbg(adapter, DATA, + "%s: DATA\n", __func__); + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + if (context->ep == port->tx_data_ep) { + atomic_dec(&port->tx_data_urb_pending); + port->block_status = false; + break; + } + } + adapter->data_sent = false; + mwifiex_write_data_complete(adapter, context->skb, 0, + urb->status ? -1 : 0); + } + + if (card->mc_resync_flag) + mwifiex_multi_chan_resync(adapter); + + mwifiex_queue_main_work(adapter); + + return; +} + +static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) +{ + struct mwifiex_adapter *adapter = ctx->adapter; + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + if (card->rx_cmd_ep != ctx->ep) { + ctx->skb = dev_alloc_skb(size); + if (!ctx->skb) { + mwifiex_dbg(adapter, ERROR, + "%s: dev_alloc_skb failed\n", __func__); + return -ENOMEM; + } + } + + usb_fill_bulk_urb(ctx->urb, card->udev, + usb_rcvbulkpipe(card->udev, ctx->ep), ctx->skb->data, + size, mwifiex_usb_rx_complete, (void *)ctx); + + if (card->rx_cmd_ep == ctx->ep) + atomic_inc(&card->rx_cmd_urb_pending); + else + atomic_inc(&card->rx_data_urb_pending); + + if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) { + mwifiex_dbg(adapter, ERROR, "usb_submit_urb failed\n"); + dev_kfree_skb_any(ctx->skb); + ctx->skb = NULL; + + if (card->rx_cmd_ep == ctx->ep) + atomic_dec(&card->rx_cmd_urb_pending); + else + atomic_dec(&card->rx_data_urb_pending); + + return -1; + } + + return 0; +} + +static void mwifiex_usb_free(struct usb_card_rec *card) +{ + struct usb_tx_data_port *port; + int i, j; + + if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) + usb_kill_urb(card->rx_cmd.urb); + + usb_free_urb(card->rx_cmd.urb); + card->rx_cmd.urb = NULL; + + if (atomic_read(&card->rx_data_urb_pending)) + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) + if (card->rx_data_list[i].urb) + usb_kill_urb(card->rx_data_list[i].urb); + + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { + usb_free_urb(card->rx_data_list[i].urb); + card->rx_data_list[i].urb = NULL; + } + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + usb_free_urb(port->tx_data_list[j].urb); + port->tx_data_list[j].urb = NULL; + } + } + + usb_free_urb(card->tx_cmd.urb); + card->tx_cmd.urb = NULL; + + return; +} + +/* This function probes an mwifiex device and registers it. It allocates + * the card structure, initiates the device registration and initialization + * procedure by adding a logical interface. + */ +static int mwifiex_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_host_interface *iface_desc = intf->cur_altsetting; + struct usb_endpoint_descriptor *epd; + int ret, i; + struct usb_card_rec *card; + u16 id_vendor, id_product, bcd_device, bcd_usb; + + card = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); + if (!card) + return -ENOMEM; + + id_vendor = le16_to_cpu(udev->descriptor.idVendor); + id_product = le16_to_cpu(udev->descriptor.idProduct); + bcd_device = le16_to_cpu(udev->descriptor.bcdDevice); + bcd_usb = le16_to_cpu(udev->descriptor.bcdUSB); + pr_debug("info: VID/PID = %X/%X, Boot2 version = %X\n", + id_vendor, id_product, bcd_device); + + /* PID_1 is used for firmware downloading only */ + switch (id_product) { + case USB8766_PID_1: + case USB8797_PID_1: + case USB8801_PID_1: + case USB8997_PID_1: + card->usb_boot_state = USB8XXX_FW_DNLD; + break; + case USB8766_PID_2: + case USB8797_PID_2: + case USB8801_PID_2: + case USB8997_PID_2: + card->usb_boot_state = USB8XXX_FW_READY; + break; + default: + pr_warn("unknown id_product %#x\n", id_product); + card->usb_boot_state = USB8XXX_FW_DNLD; + break; + } + + card->udev = udev; + card->intf = intf; + + pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocol=%#x\n", + udev->descriptor.bcdUSB, udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + epd = &iface_desc->endpoint[i].desc; + if (usb_endpoint_dir_in(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->rx_cmd_ep = usb_endpoint_num(epd); + atomic_set(&card->rx_cmd_urb_pending, 0); + } + if (usb_endpoint_dir_in(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->rx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->rx_data_urb_pending, 0); + } + if (usb_endpoint_dir_out(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->port[0].tx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->port[0].tx_data_urb_pending, 0); + } + if (usb_endpoint_dir_out(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA_CH2 && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk OUT chan2:\t" + "max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->port[1].tx_data_ep = usb_endpoint_num(epd); + atomic_set(&card->port[1].tx_data_urb_pending, 0); + } + if (usb_endpoint_dir_out(epd) && + usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && + usb_endpoint_xfer_bulk(epd)) { + pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", + le16_to_cpu(epd->wMaxPacketSize), + epd->bEndpointAddress); + card->tx_cmd_ep = usb_endpoint_num(epd); + atomic_set(&card->tx_cmd_urb_pending, 0); + card->bulk_out_maxpktsize = + le16_to_cpu(epd->wMaxPacketSize); + } + } + + usb_set_intfdata(intf, card); + + ret = mwifiex_add_card(card, &add_remove_card_sem, &usb_ops, + MWIFIEX_USB); + if (ret) { + pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret); + usb_reset_device(udev); + kfree(card); + return ret; + } + + usb_get_dev(udev); + + return 0; +} + +/* Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not suspended, this function allocates and sends a + * 'host sleep activate' request to the firmware and turns off the traffic. + */ +static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_card_rec *card = usb_get_intfdata(intf); + struct mwifiex_adapter *adapter; + struct usb_tx_data_port *port; + int i, j; + + if (!card || !card->adapter) { + pr_err("%s: card or card->adapter is NULL\n", __func__); + return 0; + } + adapter = card->adapter; + + if (unlikely(adapter->is_suspended)) + mwifiex_dbg(adapter, WARN, + "Device already suspended\n"); + + mwifiex_enable_hs(adapter); + + /* 'is_suspended' flag indicates device is suspended. + * It must be set here before the usb_kill_urb() calls. Reason + * is in the complete handlers, urb->status(= -ENOENT) and + * this flag is used in combination to distinguish between a + * 'suspended' state and a 'disconnect' one. + */ + adapter->is_suspended = true; + adapter->hs_enabling = false; + + if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) + usb_kill_urb(card->rx_cmd.urb); + + if (atomic_read(&card->rx_data_urb_pending)) + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) + if (card->rx_data_list[i].urb) + usb_kill_urb(card->rx_data_list[i].urb); + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + if (port->tx_data_list[j].urb) + usb_kill_urb(port->tx_data_list[j].urb); + } + } + + if (card->tx_cmd.urb) + usb_kill_urb(card->tx_cmd.urb); + + return 0; +} + +/* Kernel needs to suspend all functions separately. Therefore all + * registered functions must have drivers with suspend and resume + * methods. Failing that the kernel simply removes the whole card. + * + * If already not resumed, this function turns on the traffic and + * sends a 'host sleep cancel' request to the firmware. + */ +static int mwifiex_usb_resume(struct usb_interface *intf) +{ + struct usb_card_rec *card = usb_get_intfdata(intf); + struct mwifiex_adapter *adapter; + int i; + + if (!card || !card->adapter) { + pr_err("%s: card or card->adapter is NULL\n", __func__); + return 0; + } + adapter = card->adapter; + + if (unlikely(!adapter->is_suspended)) { + mwifiex_dbg(adapter, WARN, + "Device already resumed\n"); + return 0; + } + + /* Indicate device resumed. The netdev queue will be resumed only + * after the urbs have been re-submitted + */ + adapter->is_suspended = false; + + if (!atomic_read(&card->rx_data_urb_pending)) + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) + mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], + MWIFIEX_RX_DATA_BUF_SIZE); + + if (!atomic_read(&card->rx_cmd_urb_pending)) { + card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); + if (card->rx_cmd.skb) + mwifiex_usb_submit_rx_urb(&card->rx_cmd, + MWIFIEX_RX_CMD_BUF_SIZE); + } + + /* Disable Host Sleep */ + if (adapter->hs_activated) + mwifiex_cancel_hs(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), + MWIFIEX_ASYNC_CMD); + + return 0; +} + +static void mwifiex_usb_disconnect(struct usb_interface *intf) +{ + struct usb_card_rec *card = usb_get_intfdata(intf); + struct mwifiex_adapter *adapter; + + if (!card || !card->adapter) { + pr_err("%s: card or card->adapter is NULL\n", __func__); + return; + } + + adapter = card->adapter; + if (!adapter->priv_num) + return; + + if (user_rmmod) { +#ifdef CONFIG_PM + if (adapter->is_suspended) + mwifiex_usb_resume(intf); +#endif + + mwifiex_deauthenticate_all(adapter); + + mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, + MWIFIEX_BSS_ROLE_ANY), + MWIFIEX_FUNC_SHUTDOWN); + } + + mwifiex_usb_free(card); + + mwifiex_dbg(adapter, FATAL, + "%s: removing card\n", __func__); + mwifiex_remove_card(adapter, &add_remove_card_sem); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + kfree(card); + + return; +} + +static struct usb_driver mwifiex_usb_driver = { + .name = "mwifiex_usb", + .probe = mwifiex_usb_probe, + .disconnect = mwifiex_usb_disconnect, + .id_table = mwifiex_usb_table, + .suspend = mwifiex_usb_suspend, + .resume = mwifiex_usb_resume, + .soft_unbind = 1, +}; + +static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + struct usb_tx_data_port *port; + int i, j; + + card->tx_cmd.adapter = adapter; + card->tx_cmd.ep = card->tx_cmd_ep; + + card->tx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); + if (!card->tx_cmd.urb) { + mwifiex_dbg(adapter, ERROR, + "tx_cmd.urb allocation failed\n"); + return -ENOMEM; + } + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + port = &card->port[i]; + if (!port->tx_data_ep) + continue; + port->tx_data_ix = 0; + if (port->tx_data_ep == MWIFIEX_USB_EP_DATA) + port->block_status = false; + else + port->block_status = true; + for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { + port->tx_data_list[j].adapter = adapter; + port->tx_data_list[j].ep = port->tx_data_ep; + port->tx_data_list[j].urb = + usb_alloc_urb(0, GFP_KERNEL); + if (!port->tx_data_list[j].urb) { + mwifiex_dbg(adapter, ERROR, + "urb allocation failed\n"); + return -ENOMEM; + } + } + } + + return 0; +} + +static int mwifiex_usb_rx_init(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + int i; + + card->rx_cmd.adapter = adapter; + card->rx_cmd.ep = card->rx_cmd_ep; + + card->rx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); + if (!card->rx_cmd.urb) { + mwifiex_dbg(adapter, ERROR, "rx_cmd.urb allocation failed\n"); + return -ENOMEM; + } + + card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); + if (!card->rx_cmd.skb) + return -ENOMEM; + + if (mwifiex_usb_submit_rx_urb(&card->rx_cmd, MWIFIEX_RX_CMD_BUF_SIZE)) + return -1; + + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { + card->rx_data_list[i].adapter = adapter; + card->rx_data_list[i].ep = card->rx_data_ep; + + card->rx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); + if (!card->rx_data_list[i].urb) { + mwifiex_dbg(adapter, ERROR, + "rx_data_list[] urb allocation failed\n"); + return -1; + } + if (mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], + MWIFIEX_RX_DATA_BUF_SIZE)) + return -1; + } + + return 0; +} + +static int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, + u32 *len, u8 ep, u32 timeout) +{ + struct usb_card_rec *card = adapter->card; + int actual_length, ret; + + if (!(*len % card->bulk_out_maxpktsize)) + (*len)++; + + /* Send the data block */ + ret = usb_bulk_msg(card->udev, usb_sndbulkpipe(card->udev, ep), pbuf, + *len, &actual_length, timeout); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "usb_bulk_msg for tx failed: %d\n", ret); + return ret; + } + + *len = actual_length; + + return ret; +} + +static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, + u32 *len, u8 ep, u32 timeout) +{ + struct usb_card_rec *card = adapter->card; + int actual_length, ret; + + /* Receive the data response */ + ret = usb_bulk_msg(card->udev, usb_rcvbulkpipe(card->udev, ep), pbuf, + *len, &actual_length, timeout); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "usb_bulk_msg for rx failed: %d\n", ret); + return ret; + } + + *len = actual_length; + + return ret; +} + +static void mwifiex_usb_port_resync(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + u8 active_port = MWIFIEX_USB_EP_DATA; + struct mwifiex_private *priv = NULL; + int i; + + if (adapter->usb_mc_status) { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && + !priv->bss_started) || + (priv->bss_role == MWIFIEX_BSS_ROLE_STA && + !priv->media_connected)) + priv->usb_port = MWIFIEX_USB_EP_DATA; + } + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) + card->port[i].block_status = false; + } else { + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && + priv->bss_started) || + (priv->bss_role == MWIFIEX_BSS_ROLE_STA && + priv->media_connected)) { + active_port = priv->usb_port; + break; + } + } + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (priv) + priv->usb_port = active_port; + } + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { + if (active_port == card->port[i].tx_data_ep) + card->port[i].block_status = false; + else + card->port[i].block_status = true; + } + } +} + +static bool mwifiex_usb_is_port_ready(struct mwifiex_private *priv) +{ + struct usb_card_rec *card = priv->adapter->card; + int idx; + + for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { + if (priv->usb_port == card->port[idx].tx_data_ep) + return !card->port[idx].block_status; + } + + return false; +} + +static inline u8 mwifiex_usb_data_sent(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = adapter->card; + int i; + + for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) + if (!card->port[i].block_status) + return false; + + return true; +} + +/* This function write a command/data packet to card. */ +static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, + struct sk_buff *skb, + struct mwifiex_tx_param *tx_param) +{ + struct usb_card_rec *card = adapter->card; + struct urb_context *context = NULL; + struct usb_tx_data_port *port = NULL; + u8 *data = (u8 *)skb->data; + struct urb *tx_urb; + int idx, ret; + + if (adapter->is_suspended) { + mwifiex_dbg(adapter, ERROR, + "%s: not allowed while suspended\n", __func__); + return -1; + } + + if (adapter->surprise_removed) { + mwifiex_dbg(adapter, ERROR, "%s: device removed\n", __func__); + return -1; + } + + mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep); + + if (ep == card->tx_cmd_ep) { + context = &card->tx_cmd; + } else { + for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { + if (ep == card->port[idx].tx_data_ep) { + port = &card->port[idx]; + if (atomic_read(&port->tx_data_urb_pending) + >= MWIFIEX_TX_DATA_URB) { + port->block_status = true; + ret = -EBUSY; + goto done; + } + if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) + port->tx_data_ix = 0; + context = + &port->tx_data_list[port->tx_data_ix++]; + break; + } + } + if (!port) { + mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n"); + return -1; + } + } + + context->adapter = adapter; + context->ep = ep; + context->skb = skb; + tx_urb = context->urb; + + usb_fill_bulk_urb(tx_urb, card->udev, usb_sndbulkpipe(card->udev, ep), + data, skb->len, mwifiex_usb_tx_complete, + (void *)context); + + tx_urb->transfer_flags |= URB_ZERO_PACKET; + + if (ep == card->tx_cmd_ep) + atomic_inc(&card->tx_cmd_urb_pending); + else + atomic_inc(&port->tx_data_urb_pending); + + if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { + mwifiex_dbg(adapter, ERROR, + "%s: usb_submit_urb failed\n", __func__); + if (ep == card->tx_cmd_ep) { + atomic_dec(&card->tx_cmd_urb_pending); + } else { + atomic_dec(&port->tx_data_urb_pending); + port->block_status = false; + if (port->tx_data_ix) + port->tx_data_ix--; + else + port->tx_data_ix = MWIFIEX_TX_DATA_URB; + } + + return -1; + } else { + if (ep != card->tx_cmd_ep && + atomic_read(&port->tx_data_urb_pending) == + MWIFIEX_TX_DATA_URB) { + port->block_status = true; + ret = -ENOSR; + goto done; + } + } + + return -EINPROGRESS; + +done: + if (ep != card->tx_cmd_ep) + adapter->data_sent = mwifiex_usb_data_sent(adapter); + + return ret; +} + +/* This function register usb device and initialize parameter. */ +static int mwifiex_register_dev(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + card->adapter = adapter; + adapter->dev = &card->udev->dev; + + switch (le16_to_cpu(card->udev->descriptor.idProduct)) { + case USB8997_PID_1: + case USB8997_PID_2: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; + strcpy(adapter->fw_name, USB8997_DEFAULT_FW_NAME); + adapter->ext_scan = true; + break; + case USB8766_PID_1: + case USB8766_PID_2: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + strcpy(adapter->fw_name, USB8766_DEFAULT_FW_NAME); + adapter->ext_scan = true; + break; + case USB8801_PID_1: + case USB8801_PID_2: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + strcpy(adapter->fw_name, USB8801_DEFAULT_FW_NAME); + adapter->ext_scan = false; + break; + case USB8797_PID_1: + case USB8797_PID_2: + default: + adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; + strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); + break; + } + + adapter->usb_mc_status = false; + adapter->usb_mc_setup = false; + + return 0; +} + +static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + card->adapter = NULL; +} + +static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *fw) +{ + int ret = 0; + u8 *firmware = fw->fw_buf, *recv_buff; + u32 retries = USB8XXX_FW_MAX_RETRY, dlen; + u32 fw_seqnum = 0, tlen = 0, dnld_cmd = 0; + struct fw_data *fwdata; + struct fw_sync_header sync_fw; + u8 check_winner = 1; + + if (!firmware) { + mwifiex_dbg(adapter, ERROR, + "No firmware image found! Terminating download\n"); + ret = -1; + goto fw_exit; + } + + /* Allocate memory for transmit */ + fwdata = kzalloc(FW_DNLD_TX_BUF_SIZE, GFP_KERNEL); + if (!fwdata) { + ret = -ENOMEM; + goto fw_exit; + } + + /* Allocate memory for receive */ + recv_buff = kzalloc(FW_DNLD_RX_BUF_SIZE, GFP_KERNEL); + if (!recv_buff) + goto cleanup; + + do { + /* Send pseudo data to check winner status first */ + if (check_winner) { + memset(&fwdata->fw_hdr, 0, sizeof(struct fw_header)); + dlen = 0; + } else { + /* copy the header of the fw_data to get the length */ + memcpy(&fwdata->fw_hdr, &firmware[tlen], + sizeof(struct fw_header)); + + dlen = le32_to_cpu(fwdata->fw_hdr.data_len); + dnld_cmd = le32_to_cpu(fwdata->fw_hdr.dnld_cmd); + tlen += sizeof(struct fw_header); + + memcpy(fwdata->data, &firmware[tlen], dlen); + + fwdata->seq_num = cpu_to_le32(fw_seqnum); + tlen += dlen; + } + + /* If the send/receive fails or CRC occurs then retry */ + while (retries--) { + u8 *buf = (u8 *)fwdata; + u32 len = FW_DATA_XMIT_SIZE; + + /* send the firmware block */ + ret = mwifiex_write_data_sync(adapter, buf, &len, + MWIFIEX_USB_EP_CMD_EVENT, + MWIFIEX_USB_TIMEOUT); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "write_data_sync: failed: %d\n", + ret); + continue; + } + + buf = recv_buff; + len = FW_DNLD_RX_BUF_SIZE; + + /* Receive the firmware block response */ + ret = mwifiex_read_data_sync(adapter, buf, &len, + MWIFIEX_USB_EP_CMD_EVENT, + MWIFIEX_USB_TIMEOUT); + if (ret) { + mwifiex_dbg(adapter, ERROR, + "read_data_sync: failed: %d\n", + ret); + continue; + } + + memcpy(&sync_fw, recv_buff, + sizeof(struct fw_sync_header)); + + /* check 1st firmware block resp for highest bit set */ + if (check_winner) { + if (le32_to_cpu(sync_fw.cmd) & 0x80000000) { + mwifiex_dbg(adapter, WARN, + "USB is not the winner %#x\n", + sync_fw.cmd); + + /* returning success */ + ret = 0; + goto cleanup; + } + + mwifiex_dbg(adapter, MSG, + "start to download FW...\n"); + + check_winner = 0; + break; + } + + /* check the firmware block response for CRC errors */ + if (sync_fw.cmd) { + mwifiex_dbg(adapter, ERROR, + "FW received block with CRC %#x\n", + sync_fw.cmd); + ret = -1; + continue; + } + + retries = USB8XXX_FW_MAX_RETRY; + break; + } + fw_seqnum++; + } while ((dnld_cmd != FW_HAS_LAST_BLOCK) && retries); + +cleanup: + mwifiex_dbg(adapter, MSG, + "info: FW download over, size %d bytes\n", tlen); + + kfree(recv_buff); + kfree(fwdata); + + if (retries) + ret = 0; +fw_exit: + return ret; +} + +static int mwifiex_usb_dnld_fw(struct mwifiex_adapter *adapter, + struct mwifiex_fw_image *fw) +{ + int ret; + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + if (card->usb_boot_state == USB8XXX_FW_DNLD) { + ret = mwifiex_prog_fw_w_helper(adapter, fw); + if (ret) + return -1; + + /* Boot state changes after successful firmware download */ + if (card->usb_boot_state == USB8XXX_FW_DNLD) + return -1; + } + + ret = mwifiex_usb_rx_init(adapter); + if (!ret) + ret = mwifiex_usb_tx_init(adapter); + + return ret; +} + +static void mwifiex_submit_rx_urb(struct mwifiex_adapter *adapter, u8 ep) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + + skb_push(card->rx_cmd.skb, INTF_HEADER_LEN); + if ((ep == card->rx_cmd_ep) && + (!atomic_read(&card->rx_cmd_urb_pending))) + mwifiex_usb_submit_rx_urb(&card->rx_cmd, + MWIFIEX_RX_CMD_BUF_SIZE); + + return; +} + +static int mwifiex_usb_cmd_event_complete(struct mwifiex_adapter *adapter, + struct sk_buff *skb) +{ + mwifiex_submit_rx_urb(adapter, MWIFIEX_USB_EP_CMD_EVENT); + + return 0; +} + +/* This function wakes up the card. */ +static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) +{ + /* Simulation of HS_AWAKE event */ + adapter->pm_wakeup_fw_try = false; + del_timer(&adapter->wakeup_timer); + adapter->pm_wakeup_card_req = false; + adapter->ps_state = PS_STATE_AWAKE; + + return 0; +} + +static void mwifiex_usb_submit_rem_rx_urbs(struct mwifiex_adapter *adapter) +{ + struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; + int i; + struct urb_context *ctx; + + for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { + if (card->rx_data_list[i].skb) + continue; + ctx = &card->rx_data_list[i]; + mwifiex_usb_submit_rx_urb(ctx, MWIFIEX_RX_DATA_BUF_SIZE); + } +} + +/* This function is called after the card has woken up. */ +static inline int +mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) +{ + return 0; +} + +static struct mwifiex_if_ops usb_ops = { + .register_dev = mwifiex_register_dev, + .unregister_dev = mwifiex_unregister_dev, + .wakeup = mwifiex_pm_wakeup_card, + .wakeup_complete = mwifiex_pm_wakeup_card_complete, + + /* USB specific */ + .dnld_fw = mwifiex_usb_dnld_fw, + .cmdrsp_complete = mwifiex_usb_cmd_event_complete, + .event_complete = mwifiex_usb_cmd_event_complete, + .host_to_card = mwifiex_usb_host_to_card, + .submit_rem_rx_urbs = mwifiex_usb_submit_rem_rx_urbs, + .multi_port_resync = mwifiex_usb_port_resync, + .is_port_ready = mwifiex_usb_is_port_ready, +}; + +/* This function initializes the USB driver module. + * + * This initiates the semaphore and registers the device with + * USB bus. + */ +static int mwifiex_usb_init_module(void) +{ + int ret; + + pr_debug("Marvell USB8797 Driver\n"); + + sema_init(&add_remove_card_sem, 1); + + ret = usb_register(&mwifiex_usb_driver); + if (ret) + pr_err("Driver register failed!\n"); + else + pr_debug("info: Driver registered successfully!\n"); + + return ret; +} + +/* This function cleans up the USB driver. + * + * The following major steps are followed in .disconnect for cleanup: + * - Resume the device if its suspended + * - Disconnect the device if connected + * - Shutdown the firmware + * - Unregister the device from USB bus. + */ +static void mwifiex_usb_cleanup_module(void) +{ + if (!down_interruptible(&add_remove_card_sem)) + up(&add_remove_card_sem); + + /* set the flag as user is removing this module */ + user_rmmod = 1; + + usb_deregister(&mwifiex_usb_driver); +} + +module_init(mwifiex_usb_init_module); +module_exit(mwifiex_usb_cleanup_module); + +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_DESCRIPTION("Marvell WiFi-Ex USB Driver version" USB_VERSION); +MODULE_VERSION(USB_VERSION); +MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(USB8766_DEFAULT_FW_NAME); +MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME); +MODULE_FIRMWARE(USB8801_DEFAULT_FW_NAME); +MODULE_FIRMWARE(USB8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/marvell/mwifiex/usb.h b/drivers/net/wireless/marvell/mwifiex/usb.h new file mode 100644 index 000000000000..b4e9246bbcdc --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/usb.h @@ -0,0 +1,110 @@ +/* + * This file contains definitions for mwifiex USB interface driver. + * + * Copyright (C) 2012-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_USB_H +#define _MWIFIEX_USB_H + +#include + +#define USB8XXX_VID 0x1286 + +#define USB8766_PID_1 0x2041 +#define USB8766_PID_2 0x2042 +#define USB8797_PID_1 0x2043 +#define USB8797_PID_2 0x2044 +#define USB8801_PID_1 0x2049 +#define USB8801_PID_2 0x204a +#define USB8997_PID_1 0x2052 +#define USB8997_PID_2 0x204e + + +#define USB8XXX_FW_DNLD 1 +#define USB8XXX_FW_READY 2 +#define USB8XXX_FW_MAX_RETRY 3 + +#define MWIFIEX_TX_DATA_PORT 2 +#define MWIFIEX_TX_DATA_URB 6 +#define MWIFIEX_RX_DATA_URB 6 +#define MWIFIEX_USB_TIMEOUT 100 + +#define USB8766_DEFAULT_FW_NAME "mrvl/usb8766_uapsta.bin" +#define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin" +#define USB8801_DEFAULT_FW_NAME "mrvl/usb8801_uapsta.bin" +#define USB8997_DEFAULT_FW_NAME "mrvl/usb8997_uapsta.bin" + +#define FW_DNLD_TX_BUF_SIZE 620 +#define FW_DNLD_RX_BUF_SIZE 2048 +#define FW_HAS_LAST_BLOCK 0x00000004 + +#define FW_DATA_XMIT_SIZE \ + (sizeof(struct fw_header) + dlen + sizeof(u32)) + +struct urb_context { + struct mwifiex_adapter *adapter; + struct sk_buff *skb; + struct urb *urb; + u8 ep; +}; + +struct usb_tx_data_port { + u8 tx_data_ep; + u8 block_status; + atomic_t tx_data_urb_pending; + int tx_data_ix; + struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; +}; + +struct usb_card_rec { + struct mwifiex_adapter *adapter; + struct usb_device *udev; + struct usb_interface *intf; + u8 rx_cmd_ep; + struct urb_context rx_cmd; + atomic_t rx_cmd_urb_pending; + struct urb_context rx_data_list[MWIFIEX_RX_DATA_URB]; + u8 usb_boot_state; + u8 rx_data_ep; + atomic_t rx_data_urb_pending; + u8 tx_cmd_ep; + atomic_t tx_cmd_urb_pending; + int bulk_out_maxpktsize; + struct urb_context tx_cmd; + u8 mc_resync_flag; + struct usb_tx_data_port port[MWIFIEX_TX_DATA_PORT]; +}; + +struct fw_header { + __le32 dnld_cmd; + __le32 base_addr; + __le32 data_len; + __le32 crc; +}; + +struct fw_sync_header { + __le32 cmd; + __le32 seq_num; +}; + +struct fw_data { + struct fw_header fw_hdr; + __le32 seq_num; + u8 data[1]; +}; + +#endif /*_MWIFIEX_USB_H */ diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c new file mode 100644 index 000000000000..0cec8a64473e --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/util.c @@ -0,0 +1,751 @@ +/* + * Marvell Wireless LAN device driver: utility functions + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + +static struct mwifiex_debug_data items[] = { + {"debug_mask", item_size(debug_mask), + item_addr(debug_mask), 1}, + {"int_counter", item_size(int_counter), + item_addr(int_counter), 1}, + {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), + item_addr(packets_out[WMM_AC_VO]), 1}, + {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), + item_addr(packets_out[WMM_AC_VI]), 1}, + {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), + item_addr(packets_out[WMM_AC_BE]), 1}, + {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), + item_addr(packets_out[WMM_AC_BK]), 1}, + {"tx_buf_size", item_size(tx_buf_size), + item_addr(tx_buf_size), 1}, + {"curr_tx_buf_size", item_size(curr_tx_buf_size), + item_addr(curr_tx_buf_size), 1}, + {"ps_mode", item_size(ps_mode), + item_addr(ps_mode), 1}, + {"ps_state", item_size(ps_state), + item_addr(ps_state), 1}, + {"is_deep_sleep", item_size(is_deep_sleep), + item_addr(is_deep_sleep), 1}, + {"wakeup_dev_req", item_size(pm_wakeup_card_req), + item_addr(pm_wakeup_card_req), 1}, + {"wakeup_tries", item_size(pm_wakeup_fw_try), + item_addr(pm_wakeup_fw_try), 1}, + {"hs_configured", item_size(is_hs_configured), + item_addr(is_hs_configured), 1}, + {"hs_activated", item_size(hs_activated), + item_addr(hs_activated), 1}, + {"num_tx_timeout", item_size(num_tx_timeout), + item_addr(num_tx_timeout), 1}, + {"is_cmd_timedout", item_size(is_cmd_timedout), + item_addr(is_cmd_timedout), 1}, + {"timeout_cmd_id", item_size(timeout_cmd_id), + item_addr(timeout_cmd_id), 1}, + {"timeout_cmd_act", item_size(timeout_cmd_act), + item_addr(timeout_cmd_act), 1}, + {"last_cmd_id", item_size(last_cmd_id), + item_addr(last_cmd_id), DBG_CMD_NUM}, + {"last_cmd_act", item_size(last_cmd_act), + item_addr(last_cmd_act), DBG_CMD_NUM}, + {"last_cmd_index", item_size(last_cmd_index), + item_addr(last_cmd_index), 1}, + {"last_cmd_resp_id", item_size(last_cmd_resp_id), + item_addr(last_cmd_resp_id), DBG_CMD_NUM}, + {"last_cmd_resp_index", item_size(last_cmd_resp_index), + item_addr(last_cmd_resp_index), 1}, + {"last_event", item_size(last_event), + item_addr(last_event), DBG_CMD_NUM}, + {"last_event_index", item_size(last_event_index), + item_addr(last_event_index), 1}, + {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), + item_addr(num_cmd_host_to_card_failure), 1}, + {"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), 1}, + {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), + item_addr(num_tx_host_to_card_failure), 1}, + {"num_evt_deauth", item_size(num_event_deauth), + item_addr(num_event_deauth), 1}, + {"num_evt_disassoc", item_size(num_event_disassoc), + item_addr(num_event_disassoc), 1}, + {"num_evt_link_lost", item_size(num_event_link_lost), + item_addr(num_event_link_lost), 1}, + {"num_cmd_deauth", item_size(num_cmd_deauth), + item_addr(num_cmd_deauth), 1}, + {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), + item_addr(num_cmd_assoc_success), 1}, + {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), + item_addr(num_cmd_assoc_failure), 1}, + {"cmd_sent", item_size(cmd_sent), + item_addr(cmd_sent), 1}, + {"data_sent", item_size(data_sent), + item_addr(data_sent), 1}, + {"cmd_resp_received", item_size(cmd_resp_received), + item_addr(cmd_resp_received), 1}, + {"event_received", item_size(event_received), + item_addr(event_received), 1}, + + /* variables defined in struct mwifiex_adapter */ + {"cmd_pending", adapter_item_size(cmd_pending), + adapter_item_addr(cmd_pending), 1}, + {"tx_pending", adapter_item_size(tx_pending), + adapter_item_addr(tx_pending), 1}, + {"rx_pending", adapter_item_size(rx_pending), + adapter_item_addr(rx_pending), 1}, +}; + +static int num_of_items = ARRAY_SIZE(items); + +/* + * Firmware initialization complete callback handler. + * + * This function wakes up the function waiting on the init + * wait queue for the firmware initialization to complete. + */ +int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter) +{ + + if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) + if (adapter->if_ops.init_fw_port) + adapter->if_ops.init_fw_port(adapter); + + adapter->init_wait_q_woken = true; + wake_up_interruptible(&adapter->init_wait_q); + return 0; +} + +/* + * Firmware shutdown complete callback handler. + * + * This function sets the hardware status to not ready and wakes up + * the function waiting on the init wait queue for the firmware + * shutdown to complete. + */ +int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter) +{ + adapter->hw_status = MWIFIEX_HW_STATUS_NOT_READY; + adapter->init_wait_q_woken = true; + wake_up_interruptible(&adapter->init_wait_q); + return 0; +} + +/* + * This function sends init/shutdown command + * to firmware. + */ +int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, + u32 func_init_shutdown) +{ + u16 cmd; + + if (func_init_shutdown == MWIFIEX_FUNC_INIT) { + cmd = HostCmd_CMD_FUNC_INIT; + } else if (func_init_shutdown == MWIFIEX_FUNC_SHUTDOWN) { + cmd = HostCmd_CMD_FUNC_SHUTDOWN; + } else { + mwifiex_dbg(priv->adapter, ERROR, + "unsupported parameter\n"); + return -1; + } + + return mwifiex_send_cmd(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL, true); +} +EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw); + +/* + * IOCTL request handler to set/get debug information. + * + * This function collates/sets the information from/to different driver + * structures. + */ +int mwifiex_get_debug_info(struct mwifiex_private *priv, + struct mwifiex_debug_info *info) +{ + struct mwifiex_adapter *adapter = priv->adapter; + + if (info) { + info->debug_mask = adapter->debug_mask; + memcpy(info->packets_out, + priv->wmm.packets_out, + sizeof(priv->wmm.packets_out)); + info->curr_tx_buf_size = (u32) adapter->curr_tx_buf_size; + info->tx_buf_size = (u32) adapter->tx_buf_size; + info->rx_tbl_num = mwifiex_get_rx_reorder_tbl(priv, + info->rx_tbl); + info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(priv, + info->tx_tbl); + info->tdls_peer_num = mwifiex_get_tdls_list(priv, + info->tdls_list); + info->ps_mode = adapter->ps_mode; + info->ps_state = adapter->ps_state; + info->is_deep_sleep = adapter->is_deep_sleep; + info->pm_wakeup_card_req = adapter->pm_wakeup_card_req; + info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; + info->is_hs_configured = adapter->is_hs_configured; + info->hs_activated = adapter->hs_activated; + info->is_cmd_timedout = adapter->is_cmd_timedout; + info->num_cmd_host_to_card_failure + = adapter->dbg.num_cmd_host_to_card_failure; + info->num_cmd_sleep_cfm_host_to_card_failure + = adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; + info->num_tx_host_to_card_failure + = adapter->dbg.num_tx_host_to_card_failure; + info->num_event_deauth = adapter->dbg.num_event_deauth; + info->num_event_disassoc = adapter->dbg.num_event_disassoc; + info->num_event_link_lost = adapter->dbg.num_event_link_lost; + info->num_cmd_deauth = adapter->dbg.num_cmd_deauth; + info->num_cmd_assoc_success = + adapter->dbg.num_cmd_assoc_success; + info->num_cmd_assoc_failure = + adapter->dbg.num_cmd_assoc_failure; + info->num_tx_timeout = adapter->dbg.num_tx_timeout; + info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; + info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; + memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, + sizeof(adapter->dbg.last_cmd_id)); + memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act, + sizeof(adapter->dbg.last_cmd_act)); + info->last_cmd_index = adapter->dbg.last_cmd_index; + memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id, + sizeof(adapter->dbg.last_cmd_resp_id)); + info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index; + memcpy(info->last_event, adapter->dbg.last_event, + sizeof(adapter->dbg.last_event)); + info->last_event_index = adapter->dbg.last_event_index; + info->data_sent = adapter->data_sent; + info->cmd_sent = adapter->cmd_sent; + info->cmd_resp_received = adapter->cmd_resp_received; + } + + return 0; +} + +int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, + struct mwifiex_debug_info *info) +{ + char *p = buf; + struct mwifiex_debug_data *d = &items[0]; + size_t size, addr; + long val; + int i, j; + + if (!info) + return 0; + + for (i = 0; i < num_of_items; i++) { + p += sprintf(p, "%s=", d[i].name); + + size = d[i].size / d[i].num; + + if (i < (num_of_items - 3)) + addr = d[i].addr + (size_t)info; + else /* The last 3 items are struct mwifiex_adapter variables */ + addr = d[i].addr + (size_t)priv->adapter; + + for (j = 0; j < d[i].num; j++) { + switch (size) { + case 1: + val = *((u8 *)addr); + break; + case 2: + val = *((u16 *)addr); + break; + case 4: + val = *((u32 *)addr); + break; + case 8: + val = *((long long *)addr); + break; + default: + val = -1; + break; + } + + p += sprintf(p, "%#lx ", val); + addr += size; + } + + p += sprintf(p, "\n"); + } + + if (info->tx_tbl_num) { + p += sprintf(p, "Tx BA stream table:\n"); + for (i = 0; i < info->tx_tbl_num; i++) + p += sprintf(p, "tid = %d, ra = %pM\n", + info->tx_tbl[i].tid, info->tx_tbl[i].ra); + } + + if (info->rx_tbl_num) { + p += sprintf(p, "Rx reorder table:\n"); + for (i = 0; i < info->rx_tbl_num; i++) { + p += sprintf(p, "tid = %d, ta = %pM, ", + info->rx_tbl[i].tid, + info->rx_tbl[i].ta); + p += sprintf(p, "start_win = %d, ", + info->rx_tbl[i].start_win); + p += sprintf(p, "win_size = %d, buffer: ", + info->rx_tbl[i].win_size); + + for (j = 0; j < info->rx_tbl[i].win_size; j++) + p += sprintf(p, "%c ", + info->rx_tbl[i].buffer[j] ? + '1' : '0'); + + p += sprintf(p, "\n"); + } + } + + if (info->tdls_peer_num) { + p += sprintf(p, "TDLS peer table:\n"); + for (i = 0; i < info->tdls_peer_num; i++) { + p += sprintf(p, "peer = %pM", + info->tdls_list[i].peer_addr); + p += sprintf(p, "\n"); + } + } + + return p - buf; +} + +static int +mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len, + struct rxpd *rx_pd) +{ + u16 stype; + u8 category, action_code, *addr2; + struct ieee80211_hdr *ieee_hdr = (void *)payload; + + stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE); + + switch (stype) { + case IEEE80211_STYPE_ACTION: + category = *(payload + sizeof(struct ieee80211_hdr)); + switch (category) { + case WLAN_CATEGORY_PUBLIC: + action_code = *(payload + sizeof(struct ieee80211_hdr) + + 1); + if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { + addr2 = ieee_hdr->addr2; + mwifiex_dbg(priv->adapter, INFO, + "TDLS discovery response %pM nf=%d, snr=%d\n", + addr2, rx_pd->nf, rx_pd->snr); + mwifiex_auto_tdls_update_peer_signal(priv, + addr2, + rx_pd->snr, + rx_pd->nf); + } + break; + case WLAN_CATEGORY_BACK: + /*we dont indicate BACK action frames to cfg80211*/ + mwifiex_dbg(priv->adapter, INFO, + "drop BACK action frames"); + return -1; + default: + mwifiex_dbg(priv->adapter, INFO, + "unknown public action frame category %d\n", + category); + } + default: + mwifiex_dbg(priv->adapter, INFO, + "unknown mgmt frame subtype %#x\n", stype); + return 0; + } + + return 0; +} +/* + * This function processes the received management packet and send it + * to the kernel. + */ +int +mwifiex_process_mgmt_packet(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct rxpd *rx_pd; + u16 pkt_len; + struct ieee80211_hdr *ieee_hdr; + + if (!skb) + return -1; + + if (!priv->mgmt_frame_mask || + priv->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) { + mwifiex_dbg(priv->adapter, ERROR, + "do not receive mgmt frames on uninitialized intf"); + return -1; + } + + rx_pd = (struct rxpd *)skb->data; + + skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset)); + skb_pull(skb, sizeof(pkt_len)); + + pkt_len = le16_to_cpu(rx_pd->rx_pkt_length); + + ieee_hdr = (void *)skb->data; + if (ieee80211_is_mgmt(ieee_hdr->frame_control)) { + if (mwifiex_parse_mgmt_packet(priv, (u8 *)ieee_hdr, + pkt_len, rx_pd)) + return -1; + } + /* Remove address4 */ + memmove(skb->data + sizeof(struct ieee80211_hdr_3addr), + skb->data + sizeof(struct ieee80211_hdr), + pkt_len - sizeof(struct ieee80211_hdr)); + + pkt_len -= ETH_ALEN + sizeof(pkt_len); + rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); + + cfg80211_rx_mgmt(&priv->wdev, priv->roc_cfg.chan.center_freq, + CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, + 0); + + return 0; +} + +/* + * This function processes the received packet before sending it to the + * kernel. + * + * It extracts the SKB from the received buffer and sends it to kernel. + * In case the received buffer does not contain the data in SKB format, + * the function creates a blank SKB, fills it with the data from the + * received buffer and then sends this new SKB to the kernel. + */ +int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb) +{ + struct mwifiex_sta_node *src_node; + struct ethhdr *p_ethhdr; + + if (!skb) + return -1; + + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { + p_ethhdr = (void *)skb->data; + src_node = mwifiex_get_sta_entry(priv, p_ethhdr->h_source); + if (src_node) { + src_node->stats.last_rx = jiffies; + src_node->stats.rx_bytes += skb->len; + src_node->stats.rx_packets++; + } + } + + skb->dev = priv->netdev; + skb->protocol = eth_type_trans(skb, priv->netdev); + skb->ip_summed = CHECKSUM_NONE; + + /* This is required only in case of 11n and USB/PCIE as we alloc + * 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 ((priv->adapter->iface_type == MWIFIEX_USB || + priv->adapter->iface_type == MWIFIEX_PCIE) && + (skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE)) + skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE); + + if (in_interrupt()) + netif_rx(skb); + else + netif_rx_ni(skb); + + return 0; +} + +/* + * IOCTL completion callback handler. + * + * This function is called when a pending IOCTL is completed. + * + * If work queue support is enabled, the function wakes up the + * corresponding waiting function. Otherwise, it processes the + * IOCTL response and frees the response buffer. + */ +int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, + struct cmd_ctrl_node *cmd_node) +{ + WARN_ON(!cmd_node->wait_q_enabled); + mwifiex_dbg(adapter, CMD, "cmd completed: status=%d\n", + adapter->cmd_wait_q.status); + + *cmd_node->condition = true; + wake_up_interruptible(&adapter->cmd_wait_q.wait); + + return 0; +} + +/* This function will return the pointer to station entry in station list + * table which matches specified mac address. + * This function should be called after acquiring RA list spinlock. + * NULL is returned if station entry is not found in associated STA list. + */ +struct mwifiex_sta_node * +mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *node; + + if (!mac) + return NULL; + + list_for_each_entry(node, &priv->sta_list, list) { + if (!memcmp(node->mac_addr, mac, ETH_ALEN)) + return node; + } + + return NULL; +} + +static struct mwifiex_sta_node * +mwifiex_get_tdls_sta_entry(struct mwifiex_private *priv, u8 status) +{ + struct mwifiex_sta_node *node; + + list_for_each_entry(node, &priv->sta_list, list) { + if (node->tdls_status == status) + return node; + } + + return NULL; +} + +/* If tdls channel switching is on-going, tx data traffic should be + * blocked until the switching stage completed. + */ +u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *sta_ptr; + + if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return false; + + sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_CHAN_SWITCHING); + if (sta_ptr) + return true; + + return false; +} + +u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *sta_ptr; + + if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return false; + + sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_IN_OFF_CHAN); + if (sta_ptr) + return true; + + return false; +} + +/* If tdls channel switching is on-going or tdls operate on off-channel, + * cmd path should be blocked until tdls switched to base-channel. + */ +u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv) +{ + if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) + return true; + + if (mwifiex_is_tdls_chan_switching(priv) || + mwifiex_is_tdls_off_chan(priv)) + return false; + + return true; +} + +/* This function will add a sta_node entry to associated station list + * table with the given mac address. + * If entry exist already, existing entry is returned. + * If received mac address is NULL, NULL is returned. + */ +struct mwifiex_sta_node * +mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *node; + unsigned long flags; + + if (!mac) + return NULL; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + node = mwifiex_get_sta_entry(priv, mac); + if (node) + goto done; + + node = kzalloc(sizeof(*node), GFP_ATOMIC); + if (!node) + goto done; + + memcpy(node->mac_addr, mac, ETH_ALEN); + list_add_tail(&node->list, &priv->sta_list); + +done: + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return node; +} + +/* This function will search for HT IE in association request IEs + * and set station HT parameters accordingly. + */ +void +mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, + int ies_len, struct mwifiex_sta_node *node) +{ + struct ieee_types_header *ht_cap_ie; + const struct ieee80211_ht_cap *ht_cap; + + if (!ies) + return; + + ht_cap_ie = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, + ies_len); + if (ht_cap_ie) { + ht_cap = (void *)(ht_cap_ie + 1); + node->is_11n_enabled = 1; + node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & + IEEE80211_HT_CAP_MAX_AMSDU ? + MWIFIEX_TX_DATA_BUF_SIZE_8K : + MWIFIEX_TX_DATA_BUF_SIZE_4K; + } else { + node->is_11n_enabled = 0; + } + + return; +} + +/* This function will delete a station entry from station list */ +void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) +{ + struct mwifiex_sta_node *node; + unsigned long flags; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + + node = mwifiex_get_sta_entry(priv, mac); + if (node) { + list_del(&node->list); + kfree(node); + } + + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return; +} + +/* This function will delete all stations from associated station list. */ +void mwifiex_del_all_sta_list(struct mwifiex_private *priv) +{ + struct mwifiex_sta_node *node, *tmp; + unsigned long flags; + + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + + list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { + list_del(&node->list); + kfree(node); + } + + INIT_LIST_HEAD(&priv->sta_list); + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + return; +} + +/* This function adds histogram data to histogram array*/ +void mwifiex_hist_data_add(struct mwifiex_private *priv, + u8 rx_rate, s8 snr, s8 nflr) +{ + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + if (atomic_read(&phist_data->num_samples) > MWIFIEX_HIST_MAX_SAMPLES) + mwifiex_hist_data_reset(priv); + mwifiex_hist_data_set(priv, rx_rate, snr, nflr); +} + +/* function to add histogram record */ +void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, + s8 nflr) +{ + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + atomic_inc(&phist_data->num_samples); + atomic_inc(&phist_data->rx_rate[rx_rate]); + atomic_inc(&phist_data->snr[snr]); + atomic_inc(&phist_data->noise_flr[128 + nflr]); + atomic_inc(&phist_data->sig_str[nflr - snr]); +} + +/* function to reset histogram data during init/reset */ +void mwifiex_hist_data_reset(struct mwifiex_private *priv) +{ + int ix; + struct mwifiex_histogram_data *phist_data = priv->hist_data; + + atomic_set(&phist_data->num_samples, 0); + for (ix = 0; ix < MWIFIEX_MAX_AC_RX_RATES; ix++) + atomic_set(&phist_data->rx_rate[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_SNR; ix++) + atomic_set(&phist_data->snr[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_NOISE_FLR; ix++) + atomic_set(&phist_data->noise_flr[ix], 0); + for (ix = 0; ix < MWIFIEX_MAX_SIG_STRENGTH; ix++) + atomic_set(&phist_data->sig_str[ix], 0); +} + +void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags) +{ + struct sk_buff *skb; + int buf_len, pad; + + buf_len = rx_len + MWIFIEX_RX_HEADROOM + MWIFIEX_DMA_ALIGN_SZ; + + skb = __dev_alloc_skb(buf_len, flags); + + if (!skb) + return NULL; + + skb_reserve(skb, MWIFIEX_RX_HEADROOM); + + pad = MWIFIEX_ALIGN_ADDR(skb->data, MWIFIEX_DMA_ALIGN_SZ) - + (long)skb->data; + + skb_reserve(skb, pad); + + return skb; +} +EXPORT_SYMBOL_GPL(mwifiex_alloc_dma_align_buf); diff --git a/drivers/net/wireless/marvell/mwifiex/util.h b/drivers/net/wireless/marvell/mwifiex/util.h new file mode 100644 index 000000000000..b541d66c01eb --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/util.h @@ -0,0 +1,96 @@ +/* + * Marvell Wireless LAN device driver: utility functions + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_UTIL_H_ +#define _MWIFIEX_UTIL_H_ + +struct mwifiex_private; + +struct mwifiex_dma_mapping { + dma_addr_t addr; + size_t len; +}; + +struct mwifiex_cb { + struct mwifiex_dma_mapping dma_mapping; + union { + struct mwifiex_rxinfo rx_info; + struct mwifiex_txinfo tx_info; + }; +}; + +/* size/addr for mwifiex_debug_info */ +#define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) +#define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) + +/* size/addr for struct mwifiex_adapter */ +#define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) +#define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) + +struct mwifiex_debug_data { + char name[32]; /* variable/array name */ + u32 size; /* size of the variable/array */ + size_t addr; /* address of the variable/array */ + int num; /* number of variables in an array */ +}; + +static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + BUILD_BUG_ON(sizeof(struct mwifiex_cb) > sizeof(skb->cb)); + return &cb->rx_info; +} + +static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + return &cb->tx_info; +} + +static inline void mwifiex_store_mapping(struct sk_buff *skb, + struct mwifiex_dma_mapping *mapping) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + memcpy(&cb->dma_mapping, mapping, sizeof(*mapping)); +} + +static inline void mwifiex_get_mapping(struct sk_buff *skb, + struct mwifiex_dma_mapping *mapping) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + memcpy(mapping, &cb->dma_mapping, sizeof(*mapping)); +} + +static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) +{ + struct mwifiex_dma_mapping mapping; + + mwifiex_get_mapping(skb, &mapping); + + return mapping.addr; +} + +int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, + struct mwifiex_debug_info *info); + +#endif /* !_MWIFIEX_UTIL_H_ */ diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c new file mode 100644 index 000000000000..acccd6734e3b --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -0,0 +1,1531 @@ +/* + * Marvell Wireless LAN device driver: WMM + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#include "decl.h" +#include "ioctl.h" +#include "util.h" +#include "fw.h" +#include "main.h" +#include "wmm.h" +#include "11n.h" + + +/* Maximum value FW can accept for driver delay in packet transmission */ +#define DRV_PKT_DELAY_TO_FW_MAX 512 + + +#define WMM_QUEUED_PACKET_LOWER_LIMIT 180 + +#define WMM_QUEUED_PACKET_UPPER_LIMIT 200 + +/* Offset for TOS field in the IP header */ +#define IPTOS_OFFSET 5 + +static bool disable_tx_amsdu; +module_param(disable_tx_amsdu, bool, 0644); + +/* WMM information IE */ +static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, + 0x00, 0x50, 0xf2, 0x02, + 0x00, 0x01, 0x00 +}; + +static const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE, + WMM_AC_BK, + WMM_AC_VI, + WMM_AC_VO +}; + +static 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 */ +}; + +static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; + +/* + * This function debug prints the priority parameters for a WMM AC. + */ +static void +mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) +{ + const char *ac_str[] = { "BK", "BE", "VI", "VO" }; + + pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, " + "EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", + ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn_bitmap + & MWIFIEX_ACI) >> 5]], + (ac_param->aci_aifsn_bitmap & MWIFIEX_ACI) >> 5, + (ac_param->aci_aifsn_bitmap & MWIFIEX_ACM) >> 4, + ac_param->aci_aifsn_bitmap & MWIFIEX_AIFSN, + ac_param->ecw_bitmap & MWIFIEX_ECW_MIN, + (ac_param->ecw_bitmap & MWIFIEX_ECW_MAX) >> 4, + le16_to_cpu(ac_param->tx_op_limit)); +} + +/* + * This function allocates a route address list. + * + * The function also initializes the list with the provided RA. + */ +static struct mwifiex_ra_list_tbl * +mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) +{ + struct mwifiex_ra_list_tbl *ra_list; + + ra_list = kzalloc(sizeof(struct mwifiex_ra_list_tbl), GFP_ATOMIC); + if (!ra_list) + return NULL; + + INIT_LIST_HEAD(&ra_list->list); + skb_queue_head_init(&ra_list->skb_head); + + memcpy(ra_list->ra, ra, ETH_ALEN); + + ra_list->total_pkt_count = 0; + + mwifiex_dbg(adapter, INFO, "info: allocated ra_list %p\n", ra_list); + + return ra_list; +} + +/* This function returns random no between 16 and 32 to be used as threshold + * for no of packets after which BA setup is initiated. + */ +static u8 mwifiex_get_random_ba_threshold(void) +{ + u64 ns; + /* setup ba_packet_threshold here random number between + * [BA_SETUP_PACKET_OFFSET, + * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] + */ + ns = ktime_get_ns(); + ns += (ns >> 32) + (ns >> 16); + + return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; +} + +/* + * This function allocates and adds a RA list for all TIDs + * with the given RA. + */ +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) +{ + int i; + struct mwifiex_ra_list_tbl *ra_list; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_sta_node *node; + unsigned long flags; + + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra); + mwifiex_dbg(adapter, INFO, + "info: created ra_list %p\n", ra_list); + + if (!ra_list) + break; + + ra_list->is_11n_enabled = 0; + ra_list->tdls_link = false; + ra_list->ba_status = BA_SETUP_NONE; + ra_list->amsdu_in_ampdu = false; + if (!mwifiex_queuing_ra_based(priv)) { + if (mwifiex_is_tdls_link_setup + (mwifiex_get_tdls_link_status(priv, ra))) { + ra_list->tdls_link = true; + ra_list->is_11n_enabled = + mwifiex_tdls_peer_11n_enabled(priv, ra); + } else { + ra_list->is_11n_enabled = IS_11N_ENABLED(priv); + } + } else { + spin_lock_irqsave(&priv->sta_list_spinlock, flags); + node = mwifiex_get_sta_entry(priv, ra); + if (node) + ra_list->tx_paused = node->tx_pause; + ra_list->is_11n_enabled = + mwifiex_is_sta_11n_enabled(priv, node); + if (ra_list->is_11n_enabled) + ra_list->max_amsdu = node->max_amsdu; + spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); + } + + mwifiex_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=%d\n", + ra_list, ra_list->is_11n_enabled); + + if (ra_list->is_11n_enabled) { + ra_list->ba_pkt_count = 0; + ra_list->ba_packet_thr = + mwifiex_get_random_ba_threshold(); + } + list_add_tail(&ra_list->list, + &priv->wmm.tid_tbl_ptr[i].ra_list); + } +} + +/* + * This function sets the WMM queue priorities to their default values. + */ +static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv) +{ + /* 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; +} + +/* + * This function map ACs to TIDs. + */ +static void +mwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv) +{ + struct mwifiex_wmm_desc *wmm = &priv->wmm; + u8 *queue_priority = wmm->queue_priority; + int i; + + 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) + priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i; + + atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); +} + +/* + * This function initializes WMM priority queues. + */ +void +mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, + struct ieee_types_wmm_parameter *wmm_ie) +{ + u16 cw_min, avg_back_off, tmp[4]; + u32 i, j, num_ac; + u8 ac_idx; + + if (!wmm_ie || !priv->wmm_enabled) { + /* WMM is not enabled, just set the defaults and return */ + mwifiex_wmm_default_queue_priorities(priv); + return; + } + + mwifiex_dbg(priv->adapter, INFO, + "info: WMM Parameter IE: version=%d,\t" + "qos_info Parameter Set Count=%d, Reserved=%#x\n", + wmm_ie->vend_hdr.version, wmm_ie->qos_info_bitmap & + IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, + wmm_ie->reserved); + + for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac_params); num_ac++) { + u8 ecw = wmm_ie->ac_params[num_ac].ecw_bitmap; + u8 aci_aifsn = wmm_ie->ac_params[num_ac].aci_aifsn_bitmap; + cw_min = (1 << (ecw & MWIFIEX_ECW_MIN)) - 1; + avg_back_off = (cw_min >> 1) + (aci_aifsn & MWIFIEX_AIFSN); + + ac_idx = wmm_aci_to_qidx_map[(aci_aifsn & MWIFIEX_ACI) >> 5]; + priv->wmm.queue_priority[ac_idx] = ac_idx; + tmp[ac_idx] = avg_back_off; + + mwifiex_dbg(priv->adapter, INFO, + "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", + (1 << ((ecw & MWIFIEX_ECW_MAX) >> 4)) - 1, + cw_min, avg_back_off); + mwifiex_wmm_ac_debug_print(&wmm_ie->ac_params[num_ac]); + } + + /* Bubble sort */ + for (i = 0; i < num_ac; i++) { + for (j = 1; j < num_ac - i; j++) { + if (tmp[j - 1] > tmp[j]) { + swap(tmp[j - 1], tmp[j]); + swap(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(priv->wmm.queue_priority[j - 1], + priv->wmm.queue_priority[j]); + } + } + } + + mwifiex_wmm_queue_priorities_tid(priv); +} + +/* + * This function evaluates whether or not an AC is to be downgraded. + * + * In case the AC is not enabled, the highest AC is returned that is + * enabled and does not require admission control. + */ +static enum mwifiex_wmm_ac_e +mwifiex_wmm_eval_downgrade_ac(struct mwifiex_private *priv, + enum mwifiex_wmm_ac_e eval_ac) +{ + int down_ac; + enum mwifiex_wmm_ac_e ret_ac; + struct mwifiex_wmm_ac_status *ac_status; + + ac_status = &priv->wmm.ac_status[eval_ac]; + + if (!ac_status->disabled) + /* 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++) { + ac_status = &priv->wmm.ac_status[down_ac]; + + if (!ac_status->disabled && !ac_status->flow_required) + /* AC is enabled and does not require admission + control */ + ret_ac = (enum mwifiex_wmm_ac_e) down_ac; + } + + return ret_ac; +} + +/* + * This function downgrades WMM priority queue. + */ +void +mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv) +{ + int ac_val; + + mwifiex_dbg(priv->adapter, INFO, "info: WMM: AC Priorities:\t" + "BK(0), BE(1), VI(2), VO(3)\n"); + + if (!priv->wmm_enabled) { + /* 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] = + (enum mwifiex_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] + = mwifiex_wmm_eval_downgrade_ac(priv, + (enum mwifiex_wmm_ac_e) ac_val); + mwifiex_dbg(priv->adapter, INFO, + "info: WMM: AC PRIO %d maps to %d\n", + ac_val, + priv->wmm.ac_down_graded_vals[ac_val]); + } + } +} + +/* + * This function converts the IP TOS field to an WMM AC + * Queue assignment. + */ +static enum mwifiex_wmm_ac_e +mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) +{ + /* Map of TOS UP values to WMM AC */ + const enum mwifiex_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 + }; + + if (tos >= ARRAY_SIZE(tos_to_ac)) + return WMM_AC_BE; + + return tos_to_ac[tos]; +} + +/* + * This function evaluates a given TID and downgrades 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 of internally. + */ +u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) +{ + enum mwifiex_wmm_ac_e ac, ac_down; + u8 new_tid; + + ac = mwifiex_wmm_convert_tos_to_ac(priv->adapter, tid); + ac_down = priv->wmm.ac_down_graded_vals[ac]; + + /* Send the index to tid array, picking from the array will be + * taken care by dequeuing function + */ + new_tid = ac_to_tid[ac_down][tid % 2]; + + return new_tid; +} + +/* + * This function initializes the WMM state information and the + * WMM data path queues. + */ +void +mwifiex_wmm_init(struct mwifiex_adapter *adapter) +{ + int i, j; + struct mwifiex_private *priv; + + for (j = 0; j < adapter->priv_num; ++j) { + priv = adapter->priv[j]; + if (!priv) + continue; + + for (i = 0; i < MAX_NUM_TID; ++i) { + if (!disable_tx_amsdu && + adapter->tx_buf_size > MWIFIEX_TX_DATA_BUF_SIZE_2K) + priv->aggr_prio_tbl[i].amsdu = + priv->tos_to_tid_inv[i]; + else + priv->aggr_prio_tbl[i].amsdu = + BA_STREAM_NOT_ALLOWED; + priv->aggr_prio_tbl[i].ampdu_ap = + priv->tos_to_tid_inv[i]; + priv->aggr_prio_tbl[i].ampdu_user = + priv->tos_to_tid_inv[i]; + } + + priv->aggr_prio_tbl[6].amsdu + = priv->aggr_prio_tbl[6].ampdu_ap + = priv->aggr_prio_tbl[6].ampdu_user + = BA_STREAM_NOT_ALLOWED; + + priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap + = priv->aggr_prio_tbl[7].ampdu_user + = BA_STREAM_NOT_ALLOWED; + + mwifiex_set_ba_params(priv); + mwifiex_reset_11n_rx_seq_num(priv); + + atomic_set(&priv->wmm.tx_pkts_queued, 0); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); + } +} + +int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter) +{ + struct mwifiex_private *priv; + int i; + + for (i = 0; i < adapter->priv_num; i++) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + if (!skb_queue_empty(&priv->bypass_txq)) + return false; + } + + return true; +} + +/* + * This function checks if WMM Tx queue is empty. + */ +int +mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) +{ + int i; + struct mwifiex_private *priv; + + for (i = 0; i < adapter->priv_num; ++i) { + priv = adapter->priv[i]; + if (!priv) + continue; + if (!priv->port_open) + continue; + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + if (atomic_read(&priv->wmm.tx_pkts_queued)) + return false; + } + + return true; +} + +/* + * This function deletes all packets in an RA list node. + * + * The packet sent completion callback handler are called with + * status failure, after they are dequeued to ensure proper + * cleanup. The RA list node itself is freed at the end. + */ +static void +mwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra_list) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct sk_buff *skb, *tmp; + + skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) + mwifiex_write_data_complete(adapter, skb, 0, -1); +} + +/* + * This function deletes all packets in an RA list. + * + * Each nodes in the RA list are freed individually first, and then + * the RA list itself is freed. + */ +static void +mwifiex_wmm_del_pkts_in_ralist(struct mwifiex_private *priv, + struct list_head *ra_list_head) +{ + struct mwifiex_ra_list_tbl *ra_list; + + list_for_each_entry(ra_list, ra_list_head, list) + mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); +} + +/* + * This function deletes all packets in all RA lists. + */ +static void mwifiex_wmm_cleanup_queues(struct mwifiex_private *priv) +{ + int i; + + for (i = 0; i < MAX_NUM_TID; i++) + mwifiex_wmm_del_pkts_in_ralist(priv, &priv->wmm.tid_tbl_ptr[i]. + ra_list); + + atomic_set(&priv->wmm.tx_pkts_queued, 0); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); +} + +/* + * This function deletes all route addresses from all RA lists. + */ +static void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv) +{ + struct mwifiex_ra_list_tbl *ra_list, *tmp_node; + int i; + + for (i = 0; i < MAX_NUM_TID; ++i) { + mwifiex_dbg(priv->adapter, INFO, + "info: ra_list: freeing buf for tid %d\n", i); + list_for_each_entry_safe(ra_list, tmp_node, + &priv->wmm.tid_tbl_ptr[i].ra_list, + list) { + list_del(&ra_list->list); + kfree(ra_list); + } + + INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list); + } +} + +static int mwifiex_free_ack_frame(int id, void *p, void *data) +{ + pr_warn("Have pending ack frames!\n"); + kfree_skb(p); + return 0; +} + +/* + * This function cleans up the Tx and Rx queues. + * + * Cleanup includes - + * - All packets in RA lists + * - All entries in Rx reorder table + * - All entries in Tx BA stream table + * - MPA buffer (if required) + * - All RA lists + */ +void +mwifiex_clean_txrx(struct mwifiex_private *priv) +{ + unsigned long flags; + struct sk_buff *skb, *tmp; + + mwifiex_11n_cleanup_reorder_tbl(priv); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + mwifiex_wmm_cleanup_queues(priv); + mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); + + if (priv->adapter->if_ops.cleanup_mpa_buf) + priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter); + + mwifiex_wmm_delete_all_ralist(priv); + memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); + + if (priv->adapter->if_ops.clean_pcie_ring && + !priv->adapter->surprise_removed) + priv->adapter->if_ops.clean_pcie_ring(priv->adapter); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + + skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + + skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) + mwifiex_write_data_complete(priv->adapter, skb, 0, -1); + atomic_set(&priv->adapter->bypass_tx_pending, 0); + + idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL); + idr_destroy(&priv->ack_status_frames); +} + +/* + * This function retrieves a particular RA list node, matching with the + * given TID and RA address. + */ +struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr) +{ + struct mwifiex_ra_list_tbl *ra_list; + + list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list, + list) { + if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN)) + return ra_list; + } + + return NULL; +} + +void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, + u8 tx_pause) +{ + struct mwifiex_ra_list_tbl *ra_list; + u32 pkt_cnt = 0, tx_pkts_queued; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = mwifiex_wmm_get_ralist_node(priv, i, mac); + if (ra_list && ra_list->tx_paused != tx_pause) { + pkt_cnt += ra_list->total_pkt_count; + ra_list->tx_paused = tx_pause; + if (tx_pause) + priv->wmm.pkts_paused[i] += + ra_list->total_pkt_count; + else + priv->wmm.pkts_paused[i] -= + ra_list->total_pkt_count; + } + } + + if (pkt_cnt) { + tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); + if (tx_pause) + tx_pkts_queued -= pkt_cnt; + else + tx_pkts_queued += pkt_cnt; + + atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); + } + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* This function update non-tdls peer ralist tx_pause while + * tdls channel swithing + */ +void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, + u8 *mac, u8 tx_pause) +{ + struct mwifiex_ra_list_tbl *ra_list; + u32 pkt_cnt = 0, tx_pkts_queued; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; ++i) { + list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[i].ra_list, + list) { + if (!memcmp(ra_list->ra, mac, ETH_ALEN)) + continue; + + if (ra_list->tx_paused != tx_pause) { + pkt_cnt += ra_list->total_pkt_count; + ra_list->tx_paused = tx_pause; + if (tx_pause) + priv->wmm.pkts_paused[i] += + ra_list->total_pkt_count; + else + priv->wmm.pkts_paused[i] -= + ra_list->total_pkt_count; + } + } + } + + if (pkt_cnt) { + tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); + if (tx_pause) + tx_pkts_queued -= pkt_cnt; + else + tx_pkts_queued += pkt_cnt; + + atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); + atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); + } + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* + * This function retrieves an RA list node for a given TID and + * RA address pair. + * + * If no such node is found, a new node is added first and then + * retrieved. + */ +struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr) +{ + struct mwifiex_ra_list_tbl *ra_list; + + ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); + if (ra_list) + return ra_list; + mwifiex_ralist_add(priv, ra_addr); + + return mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); +} + +/* + * This function deletes RA list nodes for given mac for all TIDs. + * Function also decrements TX pending count accordingly. + */ +void +mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr) +{ + struct mwifiex_ra_list_tbl *ra_list; + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + for (i = 0; i < MAX_NUM_TID; ++i) { + ra_list = mwifiex_wmm_get_ralist_node(priv, i, ra_addr); + + if (!ra_list) + continue; + mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); + if (ra_list->tx_paused) + priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count; + else + atomic_sub(ra_list->total_pkt_count, + &priv->wmm.tx_pkts_queued); + list_del(&ra_list->list); + kfree(ra_list); + } + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* + * This function checks if a particular RA list node exists in a given TID + * table index. + */ +int +mwifiex_is_ralist_valid(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra_list, int ptr_index) +{ + struct mwifiex_ra_list_tbl *rlist; + + list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list, + list) { + if (rlist == ra_list) + return true; + } + + return false; +} + +/* + * This function adds a packet to bypass TX queue. + * This is special TX queue for packets which can be sent even when port_open + * is false. + */ +void +mwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + skb_queue_tail(&priv->bypass_txq, skb); +} + +/* + * This function adds a packet to WMM queue. + * + * In disconnected state the packet is immediately dropped and the + * packet send completion callback is called with status failure. + * + * Otherwise, the correct RA list node is located and the packet + * is queued at the list tail. + */ +void +mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, + struct sk_buff *skb) +{ + struct mwifiex_adapter *adapter = priv->adapter; + u32 tid; + struct mwifiex_ra_list_tbl *ra_list; + u8 ra[ETH_ALEN], tid_down; + unsigned long flags; + struct list_head list_head; + int tdls_status = TDLS_NOT_SETUP; + struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; + struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); + + memcpy(ra, eth_hdr->h_dest, ETH_ALEN); + + if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && + ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) { + if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS) + mwifiex_dbg(adapter, DATA, + "TDLS setup packet for %pM.\t" + "Don't block\n", ra); + else if (memcmp(priv->cfg_bssid, ra, ETH_ALEN)) + tdls_status = mwifiex_get_tdls_link_status(priv, ra); + } + + if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) { + mwifiex_dbg(adapter, DATA, "data: drop packet in disconnect\n"); + mwifiex_write_data_complete(adapter, skb, 0, -1); + return; + } + + tid = skb->priority; + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + + tid_down = mwifiex_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 (!mwifiex_queuing_ra_based(priv) && + !mwifiex_is_skb_mgmt_frame(skb)) { + switch (tdls_status) { + case TDLS_SETUP_COMPLETE: + case TDLS_CHAN_SWITCHING: + case TDLS_IN_BASE_CHAN: + case TDLS_IN_OFF_CHAN: + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, + ra); + tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; + break; + case TDLS_SETUP_INPROGRESS: + skb_queue_tail(&priv->tdls_txq, skb); + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + flags); + return; + default: + list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list; + if (!list_empty(&list_head)) + ra_list = list_first_entry( + &list_head, struct mwifiex_ra_list_tbl, + list); + else + ra_list = NULL; + break; + } + } else { + memcpy(ra, skb->data, ETH_ALEN); + if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb)) + eth_broadcast_addr(ra); + ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra); + } + + if (!ra_list) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + mwifiex_write_data_complete(adapter, skb, 0, -1); + return; + } + + skb_queue_tail(&ra_list->skb_head, skb); + + ra_list->ba_pkt_count++; + ra_list->total_pkt_count++; + + if (atomic_read(&priv->wmm.highest_queued_prio) < + priv->tos_to_tid_inv[tid_down]) + atomic_set(&priv->wmm.highest_queued_prio, + priv->tos_to_tid_inv[tid_down]); + + if (ra_list->tx_paused) + priv->wmm.pkts_paused[tid_down]++; + else + atomic_inc(&priv->wmm.tx_pkts_queued); + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* + * This function processes the get WMM status command response from firmware. + * + * The response may contain multiple TLVs - + * - AC Queue status TLVs + * - Current WMM Parameter IE TLV + * - Admission Control action frame TLVs + * + * This function parses the TLVs and then calls further specific functions + * to process any changes in the queue prioritize or state. + */ +int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, + const struct host_cmd_ds_command *resp) +{ + u8 *curr = (u8 *) &resp->params.get_wmm_status; + uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; + int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; + bool valid = true; + + struct mwifiex_ie_types_data *tlv_hdr; + struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; + struct ieee_types_wmm_parameter *wmm_param_ie = NULL; + struct mwifiex_wmm_ac_status *ac_status; + + mwifiex_dbg(priv->adapter, INFO, + "info: WMM: WMM_GET_STATUS cmdresp received: %d\n", + resp_len); + + while ((resp_len >= sizeof(tlv_hdr->header)) && valid) { + tlv_hdr = (struct mwifiex_ie_types_data *) curr; + tlv_len = le16_to_cpu(tlv_hdr->header.len); + + if (resp_len < tlv_len + sizeof(tlv_hdr->header)) + break; + + switch (le16_to_cpu(tlv_hdr->header.type)) { + case TLV_TYPE_WMMQSTATUS: + tlv_wmm_qstatus = + (struct mwifiex_ie_types_wmm_queue_status *) + tlv_hdr; + mwifiex_dbg(priv->adapter, CMD, + "info: CMD_RESP: WMM_GET_STATUS:\t" + "QSTATUS TLV: %d, %d, %d\n", + tlv_wmm_qstatus->queue_index, + tlv_wmm_qstatus->flow_required, + tlv_wmm_qstatus->disabled); + + ac_status = &priv->wmm.ac_status[tlv_wmm_qstatus-> + queue_index]; + ac_status->disabled = tlv_wmm_qstatus->disabled; + ac_status->flow_required = + tlv_wmm_qstatus->flow_required; + ac_status->flow_created = tlv_wmm_qstatus->flow_created; + break; + + case WLAN_EID_VENDOR_SPECIFIC: + /* + * Point the regular IEEE IE 2 bytes into the Marvell IE + * and setup the IEEE IE type and length byte fields + */ + + wmm_param_ie = + (struct ieee_types_wmm_parameter *) (curr + + 2); + wmm_param_ie->vend_hdr.len = (u8) tlv_len; + wmm_param_ie->vend_hdr.element_id = + WLAN_EID_VENDOR_SPECIFIC; + + mwifiex_dbg(priv->adapter, CMD, + "info: CMD_RESP: WMM_GET_STATUS:\t" + "WMM Parameter Set Count: %d\n", + wmm_param_ie->qos_info_bitmap & mask); + + memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. + wmm_ie, wmm_param_ie, + wmm_param_ie->vend_hdr.len + 2); + + break; + + default: + valid = false; + break; + } + + curr += (tlv_len + sizeof(tlv_hdr->header)); + resp_len -= (tlv_len + sizeof(tlv_hdr->header)); + } + + mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); + mwifiex_wmm_setup_ac_downgrade(priv); + + return 0; +} + +/* + * Callback handler from the command module to allow insertion of a WMM TLV. + * + * If the BSS we are associating to supports WMM, this function adds the + * required WMM Information IE to the association request command buffer in + * the form of a Marvell extended IEEE IE. + */ +u32 +mwifiex_wmm_process_association_req(struct mwifiex_private *priv, + u8 **assoc_buf, + struct ieee_types_wmm_parameter *wmm_ie, + struct ieee80211_ht_cap *ht_cap) +{ + struct mwifiex_ie_types_wmm_param_set *wmm_tlv; + u32 ret_len = 0; + + /* Null checks */ + if (!assoc_buf) + return 0; + if (!(*assoc_buf)) + return 0; + + if (!wmm_ie) + return 0; + + mwifiex_dbg(priv->adapter, INFO, + "info: WMM: process assoc req: bss->wmm_ie=%#x\n", + wmm_ie->vend_hdr.element_id); + + if ((priv->wmm_required || + (ht_cap && (priv->adapter->config_bands & BAND_GN || + priv->adapter->config_bands & BAND_AN))) && + wmm_ie->vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) { + wmm_tlv = (struct mwifiex_ie_types_wmm_param_set *) *assoc_buf; + wmm_tlv->header.type = cpu_to_le16((u16) wmm_info_ie[0]); + wmm_tlv->header.len = cpu_to_le16((u16) wmm_info_ie[1]); + memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2], + le16_to_cpu(wmm_tlv->header.len)); + if (wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) + memcpy((u8 *) (wmm_tlv->wmm_ie + + le16_to_cpu(wmm_tlv->header.len) + - sizeof(priv->wmm_qosinfo)), + &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo)); + + ret_len = sizeof(wmm_tlv->header) + + le16_to_cpu(wmm_tlv->header.len); + + *assoc_buf += ret_len; + } + + return ret_len; +} + +/* + * This function computes 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. + */ +u8 +mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, + const struct sk_buff *skb) +{ + u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp)); + u8 ret_val; + + /* + * 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 = (u8) (min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); + + mwifiex_dbg(priv->adapter, DATA, "data: WMM: Pkt Delay: %d ms,\t" + "%d ms sent to FW\n", queue_delay, ret_val); + + return ret_val; +} + +/* + * This function retrieves the highest priority RA list table pointer. + */ +static struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, + struct mwifiex_private **priv, int *tid) +{ + struct mwifiex_private *priv_tmp; + struct mwifiex_ra_list_tbl *ptr; + struct mwifiex_tid_tbl *tid_ptr; + atomic_t *hqp; + unsigned long flags_ra; + int i, j; + + /* check the BSS with highest priority first */ + for (j = adapter->priv_num - 1; j >= 0; --j) { + /* iterate over BSS with the equal priority */ + list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur, + &adapter->bss_prio_tbl[j].bss_prio_head, + list) { + + priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv; + + if (!priv_tmp->port_open || + (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)) + continue; + + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv_tmp)) + continue; + + /* iterate over the WMM queues of the BSS */ + hqp = &priv_tmp->wmm.highest_queued_prio; + for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { + + spin_lock_irqsave(&priv_tmp->wmm. + ra_list_spinlock, flags_ra); + + tid_ptr = &(priv_tmp)->wmm. + tid_tbl_ptr[tos_to_tid[i]]; + + /* iterate over receiver addresses */ + list_for_each_entry(ptr, &tid_ptr->ra_list, + list) { + + if (!ptr->tx_paused && + !skb_queue_empty(&ptr->skb_head)) + /* holds both locks */ + goto found; + } + + spin_unlock_irqrestore(&priv_tmp->wmm. + ra_list_spinlock, + flags_ra); + } + } + + } + + return NULL; + +found: + /* holds ra_list_spinlock */ + if (atomic_read(hqp) > i) + atomic_set(hqp, i); + spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra); + + *priv = priv_tmp; + *tid = tos_to_tid[i]; + + return ptr; +} + +/* This functions rotates ra and bss lists so packets are picked round robin. + * + * After a packet is successfully transmitted, rotate the ra list, so the ra + * next to the one transmitted, will come first in the list. This way we pick + * the ra' in a round robin fashion. Same applies to bss nodes of equal + * priority. + * + * Function also increments wmm.packets_out counter. + */ +void mwifiex_rotate_priolists(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra, + int tid) +{ + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; + struct mwifiex_tid_tbl *tid_ptr = &priv->wmm.tid_tbl_ptr[tid]; + unsigned long flags; + + spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); + /* + * dirty trick: we remove 'head' temporarily and reinsert it after + * curr bss node. imagine list to stay fixed while head is moved + */ + list_move(&tbl[priv->bss_priority].bss_prio_head, + &tbl[priv->bss_priority].bss_prio_cur->list); + spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + if (mwifiex_is_ralist_valid(priv, ra, tid)) { + priv->wmm.packets_out[tid]++; + /* same as above */ + list_move(&tid_ptr->ra_list, &ra->list); + } + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); +} + +/* + * This function checks if 11n aggregation is possible. + */ +static int +mwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, + int max_buf_size) +{ + int count = 0, total_size = 0; + struct sk_buff *skb, *tmp; + int max_amsdu_size; + + if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP && priv->ap_11n_enabled && + ptr->is_11n_enabled) + max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size); + else + max_amsdu_size = max_buf_size; + + skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { + total_size += skb->len; + if (total_size >= max_amsdu_size) + break; + if (++count >= MIN_NUM_AMSDU) + return true; + } + + return false; +} + +/* + * This function sends a single packet to firmware for transmission. + */ +static void +mwifiex_send_single_packet(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int ptr_index, + unsigned long ra_list_flags) + __releases(&priv->wmm.ra_list_spinlock) +{ + struct sk_buff *skb, *skb_next; + struct mwifiex_tx_param tx_param; + struct mwifiex_adapter *adapter = priv->adapter; + struct mwifiex_txinfo *tx_info; + + if (skb_queue_empty(&ptr->skb_head)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_dbg(adapter, DATA, "data: nothing to send\n"); + return; + } + + skb = skb_dequeue(&ptr->skb_head); + + tx_info = MWIFIEX_SKB_TXCB(skb); + mwifiex_dbg(adapter, DATA, + "data: dequeuing the packet %p %p\n", ptr, skb); + + ptr->total_pkt_count--; + + if (!skb_queue_empty(&ptr->skb_head)) + skb_next = skb_peek(&ptr->skb_head); + else + skb_next = NULL; + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); + + tx_param.next_pkt_len = ((skb_next) ? skb_next->len + + sizeof(struct txpd) : 0); + + if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { + /* Queue the packet back at the head */ + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + + if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_write_data_complete(adapter, skb, 0, -1); + return; + } + + skb_queue_tail(&ptr->skb_head, skb); + + ptr->total_pkt_count++; + ptr->ba_pkt_count++; + tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + } else { + mwifiex_rotate_priolists(priv, ptr, ptr_index); + atomic_dec(&priv->wmm.tx_pkts_queued); + } +} + +/* + * This function checks if the first packet in the given RA list + * is already processed or not. + */ +static int +mwifiex_is_ptr_processed(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr) +{ + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + + if (skb_queue_empty(&ptr->skb_head)) + return false; + + skb = skb_peek(&ptr->skb_head); + + tx_info = MWIFIEX_SKB_TXCB(skb); + if (tx_info->flags & MWIFIEX_BUF_FLAG_REQUEUED_PKT) + return true; + + return false; +} + +/* + * This function sends a single processed packet to firmware for + * transmission. + */ +static void +mwifiex_send_processed_packet(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ptr, int ptr_index, + unsigned long ra_list_flags) + __releases(&priv->wmm.ra_list_spinlock) +{ + struct mwifiex_tx_param tx_param; + struct mwifiex_adapter *adapter = priv->adapter; + int ret = -1; + struct sk_buff *skb, *skb_next; + struct mwifiex_txinfo *tx_info; + + if (skb_queue_empty(&ptr->skb_head)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + return; + } + + skb = skb_dequeue(&ptr->skb_head); + + if (adapter->data_sent || adapter->tx_lock_flag) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + skb_queue_tail(&adapter->tx_data_q, skb); + atomic_inc(&adapter->tx_queued); + return; + } + + if (!skb_queue_empty(&ptr->skb_head)) + skb_next = skb_peek(&ptr->skb_head); + else + skb_next = NULL; + + tx_info = MWIFIEX_SKB_TXCB(skb); + + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); + + if (adapter->iface_type == MWIFIEX_USB) { + ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, + skb, NULL); + } else { + tx_param.next_pkt_len = + ((skb_next) ? skb_next->len + + sizeof(struct txpd) : 0); + ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, + skb, &tx_param); + } + + switch (ret) { + case -EBUSY: + mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); + + if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + mwifiex_write_data_complete(adapter, skb, 0, -1); + return; + } + + skb_queue_tail(&ptr->skb_head, skb); + + tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, + ra_list_flags); + break; + case -1: + mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret); + adapter->dbg.num_tx_host_to_card_failure++; + mwifiex_write_data_complete(adapter, skb, 0, ret); + break; + case -EINPROGRESS: + break; + case 0: + mwifiex_write_data_complete(adapter, skb, 0, ret); + default: + break; + } + if (ret != -EBUSY) { + mwifiex_rotate_priolists(priv, ptr, ptr_index); + atomic_dec(&priv->wmm.tx_pkts_queued); + } +} + +/* + * This function dequeues a packet from the highest priority list + * and transmits it. + */ +static int +mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) +{ + struct mwifiex_ra_list_tbl *ptr; + struct mwifiex_private *priv = NULL; + int ptr_index = 0; + u8 ra[ETH_ALEN]; + int tid_del = 0, tid = 0; + unsigned long flags; + + ptr = mwifiex_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); + if (!ptr) + return -1; + + tid = mwifiex_get_tid(ptr); + + mwifiex_dbg(adapter, DATA, "data: tid=%d\n", tid); + + spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); + if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { + spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); + return -1; + } + + if (mwifiex_is_ptr_processed(priv, ptr)) { + mwifiex_send_processed_packet(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + mwifiex_send_processed_packet() */ + return 0; + } + + if (!ptr->is_11n_enabled || + ptr->ba_status || + priv->wps.session_enable) { + if (ptr->is_11n_enabled && + ptr->ba_status && + ptr->amsdu_in_ampdu && + mwifiex_is_amsdu_allowed(priv, tid) && + mwifiex_is_11n_aggragation_possible(priv, ptr, + adapter->tx_buf_size)) + mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + * mwifiex_11n_aggregate_pkt() + */ + else + mwifiex_send_single_packet(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + * mwifiex_send_single_packet() + */ + } else { + if (mwifiex_is_ampdu_allowed(priv, ptr, tid) && + ptr->ba_pkt_count > ptr->ba_packet_thr) { + if (mwifiex_space_avail_for_new_ba_stream(adapter)) { + mwifiex_create_ba_tbl(priv, ptr->ra, tid, + BA_SETUP_INPROGRESS); + mwifiex_send_addba(priv, tid, ptr->ra); + } else if (mwifiex_find_stream_to_delete + (priv, tid, &tid_del, ra)) { + mwifiex_create_ba_tbl(priv, ptr->ra, tid, + BA_SETUP_INPROGRESS); + mwifiex_send_delba(priv, tid_del, ra, 1); + } + } + if (mwifiex_is_amsdu_allowed(priv, tid) && + mwifiex_is_11n_aggragation_possible(priv, ptr, + adapter->tx_buf_size)) + mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + mwifiex_11n_aggregate_pkt() */ + else + mwifiex_send_single_packet(priv, ptr, ptr_index, flags); + /* ra_list_spinlock has been freed in + mwifiex_send_single_packet() */ + } + return 0; +} + +void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter) +{ + struct mwifiex_tx_param tx_param; + struct sk_buff *skb; + struct mwifiex_txinfo *tx_info; + struct mwifiex_private *priv; + int i; + + if (adapter->data_sent || adapter->tx_lock_flag) + return; + + for (i = 0; i < adapter->priv_num; ++i) { + priv = adapter->priv[i]; + + if (!priv) + continue; + + if (adapter->if_ops.is_port_ready && + !adapter->if_ops.is_port_ready(priv)) + continue; + + if (skb_queue_empty(&priv->bypass_txq)) + continue; + + skb = skb_dequeue(&priv->bypass_txq); + tx_info = MWIFIEX_SKB_TXCB(skb); + + /* no aggregation for bypass packets */ + tx_param.next_pkt_len = 0; + + if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { + skb_queue_head(&priv->bypass_txq, skb); + tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; + } else { + atomic_dec(&adapter->bypass_tx_pending); + } + } +} + +/* + * This function transmits the highest priority packet awaiting in the + * WMM Queues. + */ +void +mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter) +{ + do { + if (mwifiex_dequeue_tx_packet(adapter)) + break; + if (adapter->iface_type != MWIFIEX_SDIO) { + if (adapter->data_sent || + adapter->tx_lock_flag) + break; + } else { + if (atomic_read(&adapter->tx_queued) >= + MWIFIEX_MAX_PKTS_TXQ) + break; + } + } while (!mwifiex_wmm_lists_empty(adapter)); +} diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.h b/drivers/net/wireless/marvell/mwifiex/wmm.h new file mode 100644 index 000000000000..38f09762bd2f --- /dev/null +++ b/drivers/net/wireless/marvell/mwifiex/wmm.h @@ -0,0 +1,140 @@ +/* + * Marvell Wireless LAN device driver: WMM + * + * Copyright (C) 2011-2014, Marvell International Ltd. + * + * This software file (the "File") is distributed by Marvell International + * Ltd. under the terms of the GNU General Public License Version 2, June 1991 + * (the "License"). You may use, redistribute and/or modify this File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + */ + +#ifndef _MWIFIEX_WMM_H_ +#define _MWIFIEX_WMM_H_ + +enum ieee_types_wmm_aciaifsn_bitmasks { + MWIFIEX_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), + MWIFIEX_ACM = BIT(4), + MWIFIEX_ACI = (BIT(5) | BIT(6)), +}; + +enum ieee_types_wmm_ecw_bitmasks { + MWIFIEX_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), + MWIFIEX_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)), +}; + +static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; + +/* + * 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. + */ +static const 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 function retrieves the TID of the given RA list. + */ +static inline int +mwifiex_get_tid(struct mwifiex_ra_list_tbl *ptr) +{ + struct sk_buff *skb; + + if (skb_queue_empty(&ptr->skb_head)) + return 0; + + skb = skb_peek(&ptr->skb_head); + + return skb->priority; +} + +/* + * This function gets the length of a list. + */ +static inline int +mwifiex_wmm_list_len(struct list_head *head) +{ + struct list_head *pos; + int count = 0; + + list_for_each(pos, head) + ++count; + + return count; +} + +/* + * This function checks if a RA list is empty or not. + */ +static inline u8 +mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) +{ + struct mwifiex_ra_list_tbl *ra_list; + int is_list_empty; + + list_for_each_entry(ra_list, ra_list_hhead, list) { + is_list_empty = skb_queue_empty(&ra_list->skb_head); + if (!is_list_empty) + return false; + } + + return true; +} + +void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, + struct sk_buff *skb); +void mwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv, + struct sk_buff *skb); +void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra); +void mwifiex_rotate_priolists(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra, int tid); + +int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter); +int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter); +void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter); +void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter); +int mwifiex_is_ralist_valid(struct mwifiex_private *priv, + struct mwifiex_ra_list_tbl *ra_list, int tid); + +u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, + const struct sk_buff *skb); +void mwifiex_wmm_init(struct mwifiex_adapter *adapter); + +u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, + u8 **assoc_buf, + struct ieee_types_wmm_parameter *wmmie, + struct ieee80211_ht_cap *htcap); + +void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, + struct ieee_types_wmm_parameter *wmm_ie); +void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); +int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, + const struct host_cmd_ds_command *resp); +struct mwifiex_ra_list_tbl * +mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, + const u8 *ra_addr); +u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid); +void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, + u8 tx_pause); +void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, + u8 *mac, u8 tx_pause); + +struct mwifiex_ra_list_tbl *mwifiex_wmm_get_ralist_node(struct mwifiex_private + *priv, u8 tid, const u8 *ra_addr); +#endif /* !_MWIFIEX_WMM_H_ */ diff --git a/drivers/net/wireless/mwifiex/11ac.c b/drivers/net/wireless/mwifiex/11ac.c deleted file mode 100644 index 59d23fb2365f..000000000000 --- a/drivers/net/wireless/mwifiex/11ac.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11ac - * - * Copyright (C) 2013-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "fw.h" -#include "main.h" -#include "11ac.h" - -/* Tables of the MCS map to the highest data rate (in Mbps) supported - * for long GI. - */ -static const 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 */ -}; - -static const 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 */ -}; - -/* This function converts the 2-bit MCS map to the highest long GI - * VHT data rate. - */ -static u16 -mwifiex_convert_mcsmap_to_maxrate(struct mwifiex_private *priv, - u8 bands, u16 mcs_map) -{ - u8 i, nss, mcs; - u16 max_rate = 0; - u32 usr_vht_cap_info = 0; - struct mwifiex_adapter *adapter = priv->adapter; - - if (bands & BAND_AAC) - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; - else - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; - - /* find the max NSS supported */ - nss = 1; - for (i = 1; i <= 8; i++) { - mcs = GET_VHTNSSMCS(mcs_map, i); - if (mcs < IEEE80211_VHT_MCS_NOT_SUPPORTED) - nss = i; - } - mcs = GET_VHTNSSMCS(mcs_map, nss); - - /* if mcs is 3, nss must be 1 (NSS = 1). Default mcs to MCS 0~9 */ - if (mcs == IEEE80211_VHT_MCS_NOT_SUPPORTED) - mcs = IEEE80211_VHT_MCS_SUPPORT_0_9; - - if (GET_VHTCAP_CHWDSET(usr_vht_cap_info)) { - /* support 160 MHz */ - max_rate = max_rate_lgi_160MHZ[nss - 1][mcs]; - if (!max_rate) - /* MCS9 is not supported in NSS6 */ - max_rate = max_rate_lgi_160MHZ[nss - 1][mcs - 1]; - } else { - max_rate = max_rate_lgi_80MHZ[nss - 1][mcs]; - if (!max_rate) - /* MCS9 is not supported in NSS3 */ - max_rate = max_rate_lgi_80MHZ[nss - 1][mcs - 1]; - } - - return max_rate; -} - -static void -mwifiex_fill_vht_cap_info(struct mwifiex_private *priv, - struct ieee80211_vht_cap *vht_cap, u8 bands) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - if (bands & BAND_A) - vht_cap->vht_cap_info = - cpu_to_le32(adapter->usr_dot_11ac_dev_cap_a); - else - vht_cap->vht_cap_info = - cpu_to_le32(adapter->usr_dot_11ac_dev_cap_bg); -} - -void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, - struct ieee80211_vht_cap *vht_cap, u8 bands) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u16 mcs_map_user, mcs_map_resp, mcs_map_result; - u16 mcs_user, mcs_resp, nss, tmp; - - /* Fill VHT cap info */ - mwifiex_fill_vht_cap_info(priv, vht_cap, bands); - - /* rx MCS Set: find the minimum of the user rx mcs and ap rx mcs */ - mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_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 ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || - (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) - SET_VHTNSSMCS(mcs_map_result, nss, - IEEE80211_VHT_MCS_NOT_SUPPORTED); - else - SET_VHTNSSMCS(mcs_map_result, nss, - min(mcs_user, mcs_resp)); - } - - vht_cap->supp_mcs.rx_mcs_map = cpu_to_le16(mcs_map_result); - - tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); - vht_cap->supp_mcs.rx_highest = cpu_to_le16(tmp); - - /* tx MCS Set: find the minimum of the user tx mcs and ap tx mcs */ - mcs_map_user = GET_DEVTXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.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 ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || - (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) - SET_VHTNSSMCS(mcs_map_result, nss, - IEEE80211_VHT_MCS_NOT_SUPPORTED); - else - SET_VHTNSSMCS(mcs_map_result, nss, - min(mcs_user, mcs_resp)); - } - - vht_cap->supp_mcs.tx_mcs_map = cpu_to_le16(mcs_map_result); - - tmp = mwifiex_convert_mcsmap_to_maxrate(priv, bands, mcs_map_result); - vht_cap->supp_mcs.tx_highest = cpu_to_le16(tmp); - - return; -} - -int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 **buffer) -{ - struct mwifiex_ie_types_vhtcap *vht_cap; - struct mwifiex_ie_types_oper_mode_ntf *oper_ntf; - struct ieee_types_oper_mode_ntf *ieee_oper_ntf; - struct mwifiex_ie_types_vht_oper *vht_op; - struct mwifiex_adapter *adapter = priv->adapter; - u8 supp_chwd_set; - u32 usr_vht_cap_info; - int ret_len = 0; - - if (bss_desc->bss_band & BAND_A) - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; - else - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; - - /* VHT Capabilities IE */ - if (bss_desc->bcn_vht_cap) { - vht_cap = (struct mwifiex_ie_types_vhtcap *)*buffer; - memset(vht_cap, 0, sizeof(*vht_cap)); - vht_cap->header.type = cpu_to_le16(WLAN_EID_VHT_CAPABILITY); - vht_cap->header.len = - cpu_to_le16(sizeof(struct ieee80211_vht_cap)); - memcpy((u8 *)vht_cap + sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_vht_cap, - le16_to_cpu(vht_cap->header.len)); - - mwifiex_fill_vht_cap_tlv(priv, &vht_cap->vht_cap, - bss_desc->bss_band); - *buffer += sizeof(*vht_cap); - ret_len += sizeof(*vht_cap); - } - - /* VHT Operation IE */ - if (bss_desc->bcn_vht_oper) { - if (priv->bss_mode == NL80211_IFTYPE_STATION) { - vht_op = (struct mwifiex_ie_types_vht_oper *)*buffer; - memset(vht_op, 0, sizeof(*vht_op)); - vht_op->header.type = - cpu_to_le16(WLAN_EID_VHT_OPERATION); - vht_op->header.len = cpu_to_le16(sizeof(*vht_op) - - sizeof(struct mwifiex_ie_types_header)); - memcpy((u8 *)vht_op + - sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_vht_oper, - le16_to_cpu(vht_op->header.len)); - - /* negotiate the channel width and central freq - * and keep the central freq as the peer suggests - */ - supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); - - switch (supp_chwd_set) { - case 0: - vht_op->chan_width = - min_t(u8, IEEE80211_VHT_CHANWIDTH_80MHZ, - bss_desc->bcn_vht_oper->chan_width); - break; - case 1: - vht_op->chan_width = - min_t(u8, IEEE80211_VHT_CHANWIDTH_160MHZ, - bss_desc->bcn_vht_oper->chan_width); - break; - case 2: - vht_op->chan_width = - min_t(u8, IEEE80211_VHT_CHANWIDTH_80P80MHZ, - bss_desc->bcn_vht_oper->chan_width); - break; - default: - vht_op->chan_width = - IEEE80211_VHT_CHANWIDTH_USE_HT; - break; - } - - *buffer += sizeof(*vht_op); - ret_len += sizeof(*vht_op); - } - } - - /* Operating Mode Notification IE */ - if (bss_desc->oper_mode) { - ieee_oper_ntf = bss_desc->oper_mode; - oper_ntf = (void *)*buffer; - memset(oper_ntf, 0, sizeof(*oper_ntf)); - oper_ntf->header.type = cpu_to_le16(WLAN_EID_OPMODE_NOTIF); - oper_ntf->header.len = cpu_to_le16(sizeof(u8)); - oper_ntf->oper_mode = ieee_oper_ntf->oper_mode; - *buffer += sizeof(*oper_ntf); - ret_len += sizeof(*oper_ntf); - } - - return ret_len; -} - -int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action, - struct mwifiex_11ac_vht_cfg *cfg) -{ - struct host_cmd_11ac_vht_cfg *vhtcfg = &cmd->params.vht_cfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_11AC_CFG); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_11ac_vht_cfg) + - S_DS_GEN); - vhtcfg->action = cpu_to_le16(cmd_action); - vhtcfg->band_config = cfg->band_config; - vhtcfg->misc_config = cfg->misc_config; - vhtcfg->cap_info = cpu_to_le32(cfg->cap_info); - vhtcfg->mcs_tx_set = cpu_to_le32(cfg->mcs_tx_set); - vhtcfg->mcs_rx_set = cpu_to_le32(cfg->mcs_rx_set); - - return 0; -} - -/* This function initializes the BlockACK setup information for given - * mwifiex_private structure for 11ac enabled networks. - */ -void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv) -{ - priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - priv->add_ba_param.tx_win_size = - MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE; - priv->add_ba_param.rx_win_size = - MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE; - } else { - priv->add_ba_param.tx_win_size = - MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; - priv->add_ba_param.rx_win_size = - MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; - } - - return; -} - -bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv) -{ - struct mwifiex_bssdescriptor *bss_desc; - struct ieee80211_vht_operation *vht_oper; - - bss_desc = &priv->curr_bss_params.bss_descriptor; - vht_oper = bss_desc->bcn_vht_oper; - - if (!bss_desc->bcn_vht_cap || !vht_oper) - return false; - - if (vht_oper->chan_width == IEEE80211_VHT_CHANWIDTH_USE_HT) - return false; - - return true; -} - -u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, - u32 pri_chan, u8 chan_bw) -{ - u8 center_freq_idx = 0; - - if (band & BAND_AAC) { - switch (pri_chan) { - case 36: - case 40: - case 44: - case 48: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 42; - break; - case 52: - case 56: - case 60: - case 64: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 58; - else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) - center_freq_idx = 50; - break; - case 100: - case 104: - case 108: - case 112: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 106; - break; - case 116: - case 120: - case 124: - case 128: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 122; - else if (chan_bw == IEEE80211_VHT_CHANWIDTH_160MHZ) - center_freq_idx = 114; - break; - case 132: - case 136: - case 140: - case 144: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 138; - break; - case 149: - case 153: - case 157: - case 161: - if (chan_bw == IEEE80211_VHT_CHANWIDTH_80MHZ) - center_freq_idx = 155; - break; - default: - center_freq_idx = 42; - } - } - - return center_freq_idx; -} diff --git a/drivers/net/wireless/mwifiex/11ac.h b/drivers/net/wireless/mwifiex/11ac.h deleted file mode 100644 index 1ca92c7a8a4a..000000000000 --- a/drivers/net/wireless/mwifiex/11ac.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11ac - * - * Copyright (C) 2013-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_11AC_H_ -#define _MWIFIEX_11AC_H_ - -#define VHT_CFG_2GHZ BIT(0) -#define VHT_CFG_5GHZ BIT(1) - -enum vht_cfg_misc_config { - VHT_CAP_TX_OPERATION = 1, - VHT_CAP_ASSOCIATION, - VHT_CAP_UAP_ONLY -}; - -#define DEFAULT_VHT_MCS_SET 0xfffa -#define DISABLE_VHT_MCS_SET 0xffff - -#define VHT_BW_80_160_80P80 BIT(2) - -int mwifiex_cmd_append_11ac_tlv(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 **buffer); -int mwifiex_cmd_11ac_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action, - struct mwifiex_11ac_vht_cfg *cfg); -void mwifiex_fill_vht_cap_tlv(struct mwifiex_private *priv, - struct ieee80211_vht_cap *vht_cap, u8 bands); -#endif /* _MWIFIEX_11AC_H_ */ diff --git a/drivers/net/wireless/mwifiex/11h.c b/drivers/net/wireless/mwifiex/11h.c deleted file mode 100644 index 71a1b580796f..000000000000 --- a/drivers/net/wireless/mwifiex/11h.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11h - * - * Copyright (C) 2013-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "main.h" -#include "fw.h" - - -void mwifiex_init_11h_params(struct mwifiex_private *priv) -{ - priv->state_11h.is_11h_enabled = true; - priv->state_11h.is_11h_active = false; -} - -inline int mwifiex_is_11h_active(struct mwifiex_private *priv) -{ - return priv->state_11h.is_11h_active; -} -/* This function appends 11h info to a buffer while joining an - * infrastructure BSS - */ -static void -mwifiex_11h_process_infra_join(struct mwifiex_private *priv, u8 **buffer, - struct mwifiex_bssdescriptor *bss_desc) -{ - struct mwifiex_ie_types_header *ie_header; - struct mwifiex_ie_types_pwr_capability *cap; - struct mwifiex_ie_types_local_pwr_constraint *constraint; - struct ieee80211_supported_band *sband; - u8 radio_type; - int i; - - if (!buffer || !(*buffer)) - return; - - radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - sband = priv->wdev.wiphy->bands[radio_type]; - - cap = (struct mwifiex_ie_types_pwr_capability *)*buffer; - cap->header.type = cpu_to_le16(WLAN_EID_PWR_CAPABILITY); - cap->header.len = cpu_to_le16(2); - cap->min_pwr = 0; - cap->max_pwr = 0; - *buffer += sizeof(*cap); - - constraint = (struct mwifiex_ie_types_local_pwr_constraint *)*buffer; - constraint->header.type = cpu_to_le16(WLAN_EID_PWR_CONSTRAINT); - constraint->header.len = cpu_to_le16(2); - constraint->chan = bss_desc->channel; - constraint->constraint = bss_desc->local_constraint; - *buffer += sizeof(*constraint); - - ie_header = (struct mwifiex_ie_types_header *)*buffer; - ie_header->type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); - ie_header->len = cpu_to_le16(2 * sband->n_channels + 2); - *buffer += sizeof(*ie_header); - *(*buffer)++ = WLAN_EID_SUPPORTED_CHANNELS; - *(*buffer)++ = 2 * sband->n_channels; - for (i = 0; i < sband->n_channels; i++) { - *(*buffer)++ = ieee80211_frequency_to_channel( - sband->channels[i].center_freq); - *(*buffer)++ = 1; /* one channel in the subband */ - } -} - -/* Enable or disable the 11h extensions in the firmware */ -int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag) -{ - u32 enable = flag; - - /* enable master mode radar detection on AP interface */ - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && enable) - enable |= MWIFIEX_MASTER_RADAR_DET_MASK; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11H_I, &enable, true); -} - -/* This functions processes TLV buffer for a pending BSS Join command. - * - * Activate 11h functionality in the firmware if the spectrum management - * capability bit is found in the network we are joining. Also, necessary - * TLVs are set based on requested network's 11h capability. - */ -void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (bss_desc->sensed_11h) { - /* Activate 11h functions in firmware, turns on capability - * bit - */ - mwifiex_11h_activate(priv, true); - priv->state_11h.is_11h_active = true; - bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_SPECTRUM_MGMT; - mwifiex_11h_process_infra_join(priv, buffer, bss_desc); - } else { - /* Deactivate 11h functions in the firmware */ - mwifiex_11h_activate(priv, false); - priv->state_11h.is_11h_active = false; - bss_desc->cap_info_bitmap &= ~WLAN_CAPABILITY_SPECTRUM_MGMT; - } -} - -/* This is DFS CAC work queue function. - * This delayed work emits CAC finished event for cfg80211 if - * CAC was started earlier. - */ -void mwifiex_dfs_cac_work_queue(struct work_struct *work) -{ - struct cfg80211_chan_def chandef; - struct delayed_work *delayed_work = - container_of(work, struct delayed_work, work); - struct mwifiex_private *priv = - container_of(delayed_work, struct mwifiex_private, - dfs_cac_work); - - if (WARN_ON(!priv)) - return; - - chandef = priv->dfs_chandef; - if (priv->wdev.cac_started) { - mwifiex_dbg(priv->adapter, MSG, - "CAC timer finished; No radar detected\n"); - cfg80211_cac_event(priv->netdev, &chandef, - NL80211_RADAR_CAC_FINISHED, - GFP_KERNEL); - } -} - -/* This function prepares channel report request command to FW for - * starting radar detection. - */ -int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf) -{ - struct host_cmd_ds_chan_rpt_req *cr_req = &cmd->params.chan_rpt_req; - struct mwifiex_radar_params *radar_params = (void *)data_buf; - - cmd->command = cpu_to_le16(HostCmd_CMD_CHAN_REPORT_REQUEST); - cmd->size = cpu_to_le16(S_DS_GEN); - le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_chan_rpt_req)); - - cr_req->chan_desc.start_freq = cpu_to_le16(MWIFIEX_A_BAND_START_FREQ); - cr_req->chan_desc.chan_num = radar_params->chandef->chan->hw_value; - cr_req->chan_desc.chan_width = radar_params->chandef->width; - cr_req->msec_dwell_time = cpu_to_le32(radar_params->cac_time_ms); - - if (radar_params->cac_time_ms) - mwifiex_dbg(priv->adapter, MSG, - "11h: issuing DFS Radar check for channel=%d\n", - radar_params->chandef->chan->hw_value); - else - mwifiex_dbg(priv->adapter, MSG, "cancelling CAC\n"); - - return 0; -} - -int mwifiex_stop_radar_detection(struct mwifiex_private *priv, - struct cfg80211_chan_def *chandef) -{ - struct mwifiex_radar_params radar_params; - - memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); - radar_params.chandef = chandef; - radar_params.cac_time_ms = 0; - - return mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, - HostCmd_ACT_GEN_SET, 0, &radar_params, true); -} - -/* This function is to abort ongoing CAC upon stopping AP operations - * or during unload. - */ -void mwifiex_abort_cac(struct mwifiex_private *priv) -{ - if (priv->wdev.cac_started) { - if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) - mwifiex_dbg(priv->adapter, ERROR, - "failed to stop CAC in FW\n"); - mwifiex_dbg(priv->adapter, MSG, - "Aborting delayed work for CAC.\n"); - cancel_delayed_work_sync(&priv->dfs_cac_work); - cfg80211_cac_event(priv->netdev, &priv->dfs_chandef, - NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); - } -} - -/* This function handles channel report event from FW during CAC period. - * If radar is detected during CAC, driver indicates the same to cfg80211 - * and also cancels ongoing delayed work. - */ -int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct host_cmd_ds_chan_rpt_event *rpt_event; - struct mwifiex_ie_types_chan_rpt_data *rpt; - u8 *evt_buf; - u16 event_len, tlv_len; - - rpt_event = (void *)(skb->data + sizeof(u32)); - event_len = skb->len - (sizeof(struct host_cmd_ds_chan_rpt_event)+ - sizeof(u32)); - - if (le32_to_cpu(rpt_event->result) != HostCmd_RESULT_OK) { - mwifiex_dbg(priv->adapter, ERROR, - "Error in channel report event\n"); - return -1; - } - - evt_buf = (void *)&rpt_event->tlvbuf; - - while (event_len >= sizeof(struct mwifiex_ie_types_header)) { - rpt = (void *)&rpt_event->tlvbuf; - tlv_len = le16_to_cpu(rpt->header.len); - - switch (le16_to_cpu(rpt->header.type)) { - case TLV_TYPE_CHANRPT_11H_BASIC: - if (rpt->map.radar) { - mwifiex_dbg(priv->adapter, MSG, - "RADAR Detected on channel %d!\n", - priv->dfs_chandef.chan->hw_value); - cancel_delayed_work_sync(&priv->dfs_cac_work); - cfg80211_cac_event(priv->netdev, - &priv->dfs_chandef, - NL80211_RADAR_DETECTED, - GFP_KERNEL); - } - break; - default: - break; - } - - evt_buf += (tlv_len + sizeof(rpt->header)); - event_len -= (tlv_len + sizeof(rpt->header)); - } - - return 0; -} - -/* Handler for radar detected event from FW.*/ -int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_radar_det_event *rdr_event; - - rdr_event = (void *)(skb->data + sizeof(u32)); - - if (le32_to_cpu(rdr_event->passed)) { - mwifiex_dbg(priv->adapter, MSG, - "radar detected; indicating kernel\n"); - if (mwifiex_stop_radar_detection(priv, &priv->dfs_chandef)) - mwifiex_dbg(priv->adapter, ERROR, - "Failed to stop CAC in FW\n"); - cfg80211_radar_event(priv->adapter->wiphy, &priv->dfs_chandef, - GFP_KERNEL); - mwifiex_dbg(priv->adapter, MSG, "regdomain: %d\n", - rdr_event->reg_domain); - mwifiex_dbg(priv->adapter, MSG, "radar detection type: %d\n", - rdr_event->det_type); - } else { - mwifiex_dbg(priv->adapter, MSG, - "false radar detection event!\n"); - } - - return 0; -} - -/* This is work queue function for channel switch handling. - * This function takes care of updating new channel definitin to - * bss config structure, restart AP and indicate channel switch success - * to cfg80211. - */ -void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work) -{ - struct mwifiex_uap_bss_param *bss_cfg; - struct delayed_work *delayed_work = - container_of(work, struct delayed_work, work); - struct mwifiex_private *priv = - container_of(delayed_work, struct mwifiex_private, - dfs_chan_sw_work); - - if (WARN_ON(!priv)) - return; - - bss_cfg = &priv->bss_cfg; - if (!bss_cfg->beacon_period) { - mwifiex_dbg(priv->adapter, ERROR, - "channel switch: AP already stopped\n"); - return; - } - - mwifiex_uap_set_channel(priv, bss_cfg, priv->dfs_chandef); - - if (mwifiex_config_start_uap(priv, bss_cfg)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to start AP after channel switch\n"); - return; - } - - mwifiex_dbg(priv->adapter, MSG, - "indicating channel switch completion to kernel\n"); - cfg80211_ch_switch_notify(priv->netdev, &priv->dfs_chandef); -} diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c deleted file mode 100644 index c174e79e6df2..000000000000 --- a/drivers/net/wireless/mwifiex/11n.c +++ /dev/null @@ -1,914 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - -/* - * Fills HT capability information field, AMPDU Parameters field, HT extended - * capability field, and supported MCS set fields. - * - * HT capability information field, AMPDU Parameters field, supported MCS set - * fields are retrieved from cfg80211 stack - * - * RD responder bit to set to clear in the extended capability header. - */ -int mwifiex_fill_cap_info(struct mwifiex_private *priv, u8 radio_type, - struct ieee80211_ht_cap *ht_cap) -{ - uint16_t ht_ext_cap = le16_to_cpu(ht_cap->extended_ht_cap_info); - struct ieee80211_supported_band *sband = - priv->wdev.wiphy->bands[radio_type]; - - if (WARN_ON_ONCE(!sband)) { - mwifiex_dbg(priv->adapter, ERROR, "Invalid radio type!\n"); - return -EINVAL; - } - - ht_cap->ampdu_params_info = - (sband->ht_cap.ampdu_factor & - IEEE80211_HT_AMPDU_PARM_FACTOR) | - ((sband->ht_cap.ampdu_density << - IEEE80211_HT_AMPDU_PARM_DENSITY_SHIFT) & - IEEE80211_HT_AMPDU_PARM_DENSITY); - - memcpy((u8 *)&ht_cap->mcs, &sband->ht_cap.mcs, - sizeof(sband->ht_cap.mcs)); - - if (priv->bss_mode == NL80211_IFTYPE_STATION || - (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && - (priv->adapter->sec_chan_offset != - IEEE80211_HT_PARAM_CHA_SEC_NONE))) - /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ - SETHT_MCS32(ht_cap->mcs.rx_mask); - - /* Clear RD responder bit */ - ht_ext_cap &= ~IEEE80211_HT_EXT_CAP_RD_RESPONDER; - - ht_cap->cap_info = cpu_to_le16(sband->ht_cap.cap); - ht_cap->extended_ht_cap_info = cpu_to_le16(ht_ext_cap); - - if (ISSUPP_BEAMFORMING(priv->adapter->hw_dot_11n_dev_cap)) - ht_cap->tx_BF_cap_info = cpu_to_le32(MWIFIEX_DEF_11N_TX_BF_CAP); - - return 0; -} - -/* - * This function returns the pointer to an entry in BA Stream - * table which matches the requested BA status. - */ -static struct mwifiex_tx_ba_stream_tbl * -mwifiex_get_ba_status(struct mwifiex_private *priv, - enum mwifiex_ba_status ba_status) -{ - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - if (tx_ba_tsr_tbl->ba_status == ba_status) { - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, - flags); - return tx_ba_tsr_tbl; - } - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - return NULL; -} - -/* - * This function handles the command response of delete a block - * ack request. - * - * The function checks the response success status and takes action - * accordingly (send an add BA request in case of success, or recreate - * the deleted stream in case of failure, if the add BA was also - * initiated by us). - */ -int mwifiex_ret_11n_delba(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - int tid; - struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; - struct host_cmd_ds_11n_delba *del_ba = &resp->params.del_ba; - uint16_t del_ba_param_set = le16_to_cpu(del_ba->del_ba_param_set); - - tid = del_ba_param_set >> DELBA_TID_POS; - if (del_ba->del_result == BA_RESULT_SUCCESS) { - mwifiex_del_ba_tbl(priv, tid, del_ba->peer_mac_addr, - TYPE_DELBA_SENT, - INITIATOR_BIT(del_ba_param_set)); - - tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); - if (tx_ba_tbl) - mwifiex_send_addba(priv, tx_ba_tbl->tid, - tx_ba_tbl->ra); - } else { /* - * In case of failure, recreate the deleted stream in case - * we initiated the ADDBA - */ - if (!INITIATOR_BIT(del_ba_param_set)) - return 0; - - mwifiex_create_ba_tbl(priv, del_ba->peer_mac_addr, tid, - BA_SETUP_INPROGRESS); - - tx_ba_tbl = mwifiex_get_ba_status(priv, BA_SETUP_INPROGRESS); - - if (tx_ba_tbl) - mwifiex_del_ba_tbl(priv, tx_ba_tbl->tid, tx_ba_tbl->ra, - TYPE_DELBA_SENT, true); - } - - return 0; -} - -/* - * This function handles the command response of add a block - * ack request. - * - * Handling includes changing the header fields to CPU formats, checking - * the response success status and taking actions accordingly (delete the - * BA stream table in case of failure). - */ -int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - int tid, tid_down; - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; - struct mwifiex_tx_ba_stream_tbl *tx_ba_tbl; - struct mwifiex_ra_list_tbl *ra_list; - u16 block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); - - add_ba_rsp->ssn = cpu_to_le16((le16_to_cpu(add_ba_rsp->ssn)) - & SSN_MASK); - - tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) - >> BLOCKACKPARAM_TID_POS; - - tid_down = mwifiex_wmm_downgrade_tid(priv, tid); - ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, add_ba_rsp-> - peer_mac_addr); - if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { - if (ra_list) { - ra_list->ba_status = BA_SETUP_NONE; - ra_list->amsdu_in_ampdu = false; - } - mwifiex_del_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr, - TYPE_DELBA_SENT, true); - if (add_ba_rsp->add_rsp_result != BA_RESULT_TIMEOUT) - priv->aggr_prio_tbl[tid].ampdu_ap = - BA_STREAM_NOT_ALLOWED; - return 0; - } - - tx_ba_tbl = mwifiex_get_ba_tbl(priv, tid, add_ba_rsp->peer_mac_addr); - if (tx_ba_tbl) { - mwifiex_dbg(priv->adapter, EVENT, "info: BA stream complete\n"); - tx_ba_tbl->ba_status = BA_SETUP_COMPLETE; - if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && - priv->add_ba_param.tx_amsdu && - (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) - tx_ba_tbl->amsdu = true; - else - tx_ba_tbl->amsdu = false; - if (ra_list) { - ra_list->amsdu_in_ampdu = tx_ba_tbl->amsdu; - ra_list->ba_status = BA_SETUP_COMPLETE; - } - } else { - mwifiex_dbg(priv->adapter, ERROR, "BA stream not created\n"); - } - - return 0; -} - -/* - * This function prepares command of reconfigure Tx buffer. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting Tx buffer size (for SET only) - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, int cmd_action, - u16 *buf_size) -{ - struct host_cmd_ds_txbuf_cfg *tx_buf = &cmd->params.tx_buf; - u16 action = (u16) cmd_action; - - cmd->command = cpu_to_le16(HostCmd_CMD_RECONFIGURE_TX_BUFF); - cmd->size = - cpu_to_le16(sizeof(struct host_cmd_ds_txbuf_cfg) + S_DS_GEN); - tx_buf->action = cpu_to_le16(action); - switch (action) { - case HostCmd_ACT_GEN_SET: - mwifiex_dbg(priv->adapter, CMD, - "cmd: set tx_buf=%d\n", *buf_size); - tx_buf->buff_size = cpu_to_le16(*buf_size); - break; - case HostCmd_ACT_GEN_GET: - default: - tx_buf->buff_size = 0; - break; - } - return 0; -} - -/* - * This function prepares command of AMSDU aggregation control. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting AMSDU control parameters (for SET only) - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, - int cmd_action, - struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl) -{ - struct host_cmd_ds_amsdu_aggr_ctrl *amsdu_ctrl = - &cmd->params.amsdu_aggr_ctrl; - u16 action = (u16) cmd_action; - - cmd->command = cpu_to_le16(HostCmd_CMD_AMSDU_AGGR_CTRL); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_amsdu_aggr_ctrl) - + S_DS_GEN); - amsdu_ctrl->action = cpu_to_le16(action); - switch (action) { - case HostCmd_ACT_GEN_SET: - amsdu_ctrl->enable = cpu_to_le16(aa_ctrl->enable); - amsdu_ctrl->curr_buf_size = 0; - break; - case HostCmd_ACT_GEN_GET: - default: - amsdu_ctrl->curr_buf_size = 0; - break; - } - return 0; -} - -/* - * This function prepares 11n configuration command. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting HT Tx capability and HT Tx information fields - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action, - struct mwifiex_ds_11n_tx_cfg *txcfg) -{ - struct host_cmd_ds_11n_cfg *htcfg = &cmd->params.htcfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_11N_CFG); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_11n_cfg) + S_DS_GEN); - htcfg->action = cpu_to_le16(cmd_action); - htcfg->ht_tx_cap = cpu_to_le16(txcfg->tx_htcap); - htcfg->ht_tx_info = cpu_to_le16(txcfg->tx_htinfo); - - if (priv->adapter->is_hw_11ac_capable) - htcfg->misc_config = cpu_to_le16(txcfg->misc_config); - - return 0; -} - -/* - * This function appends an 11n TLV to a buffer. - * - * Buffer allocation is responsibility of the calling - * function. No size validation is made here. - * - * The function fills up the following sections, if applicable - - * - HT capability IE - * - HT information IE (with channel list) - * - 20/40 BSS Coexistence IE - * - HT Extended Capabilities IE - */ -int -mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 **buffer) -{ - struct mwifiex_ie_types_htcap *ht_cap; - struct mwifiex_ie_types_htinfo *ht_info; - struct mwifiex_ie_types_chan_list_param_set *chan_list; - struct mwifiex_ie_types_2040bssco *bss_co_2040; - struct mwifiex_ie_types_extcap *ext_cap; - int ret_len = 0; - struct ieee80211_supported_band *sband; - struct ieee_types_header *hdr; - u8 radio_type; - - if (!buffer || !*buffer) - return ret_len; - - radio_type = mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - sband = priv->wdev.wiphy->bands[radio_type]; - - if (bss_desc->bcn_ht_cap) { - ht_cap = (struct mwifiex_ie_types_htcap *) *buffer; - memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); - ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); - ht_cap->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - memcpy((u8 *) ht_cap + sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_ht_cap, - le16_to_cpu(ht_cap->header.len)); - - mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); - - *buffer += sizeof(struct mwifiex_ie_types_htcap); - ret_len += sizeof(struct mwifiex_ie_types_htcap); - } - - if (bss_desc->bcn_ht_oper) { - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - ht_info = (struct mwifiex_ie_types_htinfo *) *buffer; - memset(ht_info, 0, - sizeof(struct mwifiex_ie_types_htinfo)); - ht_info->header.type = - cpu_to_le16(WLAN_EID_HT_OPERATION); - ht_info->header.len = - cpu_to_le16( - sizeof(struct ieee80211_ht_operation)); - - memcpy((u8 *) ht_info + - sizeof(struct mwifiex_ie_types_header), - (u8 *)bss_desc->bcn_ht_oper, - le16_to_cpu(ht_info->header.len)); - - if (!(sband->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40)) - ht_info->ht_oper.ht_param &= - ~(IEEE80211_HT_PARAM_CHAN_WIDTH_ANY | - IEEE80211_HT_PARAM_CHA_SEC_OFFSET); - - *buffer += sizeof(struct mwifiex_ie_types_htinfo); - ret_len += sizeof(struct mwifiex_ie_types_htinfo); - } - - chan_list = - (struct mwifiex_ie_types_chan_list_param_set *) *buffer; - memset(chan_list, 0, - sizeof(struct mwifiex_ie_types_chan_list_param_set)); - chan_list->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - chan_list->header.len = cpu_to_le16( - sizeof(struct mwifiex_ie_types_chan_list_param_set) - - sizeof(struct mwifiex_ie_types_header)); - chan_list->chan_scan_param[0].chan_number = - bss_desc->bcn_ht_oper->primary_chan; - chan_list->chan_scan_param[0].radio_type = - mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - - if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && - bss_desc->bcn_ht_oper->ht_param & - IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) - SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. - radio_type, - (bss_desc->bcn_ht_oper->ht_param & - IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); - - *buffer += sizeof(struct mwifiex_ie_types_chan_list_param_set); - ret_len += sizeof(struct mwifiex_ie_types_chan_list_param_set); - } - - if (bss_desc->bcn_bss_co_2040) { - bss_co_2040 = (struct mwifiex_ie_types_2040bssco *) *buffer; - memset(bss_co_2040, 0, - sizeof(struct mwifiex_ie_types_2040bssco)); - bss_co_2040->header.type = cpu_to_le16(WLAN_EID_BSS_COEX_2040); - bss_co_2040->header.len = - cpu_to_le16(sizeof(bss_co_2040->bss_co_2040)); - - memcpy((u8 *) bss_co_2040 + - sizeof(struct mwifiex_ie_types_header), - bss_desc->bcn_bss_co_2040 + - sizeof(struct ieee_types_header), - le16_to_cpu(bss_co_2040->header.len)); - - *buffer += sizeof(struct mwifiex_ie_types_2040bssco); - ret_len += sizeof(struct mwifiex_ie_types_2040bssco); - } - - if (bss_desc->bcn_ext_cap) { - hdr = (void *)bss_desc->bcn_ext_cap; - ext_cap = (struct mwifiex_ie_types_extcap *) *buffer; - memset(ext_cap, 0, sizeof(struct mwifiex_ie_types_extcap)); - ext_cap->header.type = cpu_to_le16(WLAN_EID_EXT_CAPABILITY); - ext_cap->header.len = cpu_to_le16(hdr->len); - - memcpy((u8 *)ext_cap->ext_capab, - bss_desc->bcn_ext_cap + sizeof(struct ieee_types_header), - le16_to_cpu(ext_cap->header.len)); - - if (hdr->len > 3 && - ext_cap->ext_capab[3] & WLAN_EXT_CAPA4_INTERWORKING_ENABLED) - priv->hs2_enabled = true; - else - priv->hs2_enabled = false; - - *buffer += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; - ret_len += sizeof(struct mwifiex_ie_types_extcap) + hdr->len; - } - - return ret_len; -} - -/* - * This function checks if the given pointer is valid entry of - * Tx BA Stream table. - */ -static int mwifiex_is_tx_ba_stream_ptr_valid(struct mwifiex_private *priv, - struct mwifiex_tx_ba_stream_tbl *tx_tbl_ptr) -{ - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - - list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - if (tx_ba_tsr_tbl == tx_tbl_ptr) - return true; - } - - return false; -} - -/* - * This function deletes the given entry in Tx BA Stream table. - * - * The function also performs a validity check on the supplied - * pointer before trying to delete. - */ -void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl) -{ - if (!tx_ba_tsr_tbl && - mwifiex_is_tx_ba_stream_ptr_valid(priv, tx_ba_tsr_tbl)) - return; - - mwifiex_dbg(priv->adapter, INFO, - "info: tx_ba_tsr_tbl %p\n", tx_ba_tsr_tbl); - - list_del(&tx_ba_tsr_tbl->list); - - kfree(tx_ba_tsr_tbl); -} - -/* - * This function deletes all the entries in Tx BA Stream table. - */ -void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv) -{ - int i; - struct mwifiex_tx_ba_stream_tbl *del_tbl_ptr, *tmp_node; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry_safe(del_tbl_ptr, tmp_node, - &priv->tx_ba_stream_tbl_ptr, list) - mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, del_tbl_ptr); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - - INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); - - for (i = 0; i < MAX_NUM_TID; ++i) - priv->aggr_prio_tbl[i].ampdu_ap = - priv->aggr_prio_tbl[i].ampdu_user; -} - -/* - * This function returns the pointer to an entry in BA Stream - * table which matches the given RA/TID pair. - */ -struct mwifiex_tx_ba_stream_tbl * -mwifiex_get_ba_tbl(struct mwifiex_private *priv, int tid, u8 *ra) -{ - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - if (ether_addr_equal_unaligned(tx_ba_tsr_tbl->ra, ra) && - tx_ba_tsr_tbl->tid == tid) { - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, - flags); - return tx_ba_tsr_tbl; - } - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - return NULL; -} - -/* - * This function creates an entry in Tx BA stream table for the - * given RA/TID pair. - */ -void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, - enum mwifiex_ba_status ba_status) -{ - struct mwifiex_tx_ba_stream_tbl *new_node; - struct mwifiex_ra_list_tbl *ra_list; - unsigned long flags; - int tid_down; - - if (!mwifiex_get_ba_tbl(priv, tid, ra)) { - new_node = kzalloc(sizeof(struct mwifiex_tx_ba_stream_tbl), - GFP_ATOMIC); - if (!new_node) - return; - - tid_down = mwifiex_wmm_downgrade_tid(priv, tid); - ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, ra); - if (ra_list) { - ra_list->ba_status = ba_status; - ra_list->amsdu_in_ampdu = false; - } - INIT_LIST_HEAD(&new_node->list); - - new_node->tid = tid; - new_node->ba_status = ba_status; - memcpy(new_node->ra, ra, ETH_ALEN); - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_add_tail(&new_node->list, &priv->tx_ba_stream_tbl_ptr); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - } -} - -/* - * This function sends an add BA request to the given TID/RA pair. - */ -int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) -{ - struct host_cmd_ds_11n_addba_req add_ba_req; - u32 tx_win_size = priv->add_ba_param.tx_win_size; - static u8 dialog_tok; - int ret; - unsigned long flags; - u16 block_ack_param_set; - - mwifiex_dbg(priv->adapter, CMD, "cmd: %s: tid %d\n", __func__, tid); - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->adapter->is_hw_11ac_capable && - memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) { - struct mwifiex_sta_node *sta_ptr; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, peer_mac); - if (!sta_ptr) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - mwifiex_dbg(priv->adapter, ERROR, - "BA setup with unknown TDLS peer %pM!\n", - peer_mac); - return -1; - } - if (sta_ptr->is_11ac_enabled) - tx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE; - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - } - - block_ack_param_set = (u16)((tid << BLOCKACKPARAM_TID_POS) | - 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)) - block_ack_param_set |= BLOCKACKPARAM_AMSDU_SUPP_MASK; - - add_ba_req.block_ack_param_set = cpu_to_le16(block_ack_param_set); - add_ba_req.block_ack_tmo = cpu_to_le16((u16)priv->add_ba_param.timeout); - - ++dialog_tok; - - if (dialog_tok == 0) - dialog_tok = 1; - - add_ba_req.dialog_token = dialog_tok; - memcpy(&add_ba_req.peer_mac_addr, peer_mac, ETH_ALEN); - - /* We don't wait for the response of this command */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_REQ, - 0, 0, &add_ba_req, false); - - return ret; -} - -/* - * This function sends a delete BA request to the given TID/RA pair. - */ -int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, - int initiator) -{ - struct host_cmd_ds_11n_delba delba; - int ret; - uint16_t del_ba_param_set; - - memset(&delba, 0, sizeof(delba)); - delba.del_ba_param_set = cpu_to_le16(tid << DELBA_TID_POS); - - del_ba_param_set = le16_to_cpu(delba.del_ba_param_set); - if (initiator) - del_ba_param_set |= IEEE80211_DELBA_PARAM_INITIATOR_MASK; - else - del_ba_param_set &= ~IEEE80211_DELBA_PARAM_INITIATOR_MASK; - - memcpy(&delba.peer_mac_addr, peer_mac, ETH_ALEN); - - /* We don't wait for the response of this command */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, - HostCmd_ACT_GEN_SET, 0, &delba, false); - - return ret; -} - -/* - * This function sends delba to specific tid - */ -void mwifiex_11n_delba(struct mwifiex_private *priv, int tid) -{ - struct mwifiex_rx_reorder_tbl *rx_reor_tbl_ptr; - - if (list_empty(&priv->rx_reorder_tbl_ptr)) { - dev_dbg(priv->adapter->dev, - "mwifiex_11n_delba: rx_reorder_tbl_ptr empty\n"); - return; - } - - list_for_each_entry(rx_reor_tbl_ptr, &priv->rx_reorder_tbl_ptr, list) { - if (rx_reor_tbl_ptr->tid == tid) { - dev_dbg(priv->adapter->dev, - "Send delba to tid=%d, %pM\n", - tid, rx_reor_tbl_ptr->ta); - mwifiex_send_delba(priv, tid, rx_reor_tbl_ptr->ta, 0); - return; - } - } -} - -/* - * This function handles the command response of a delete BA request. - */ -void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba) -{ - struct host_cmd_ds_11n_delba *cmd_del_ba = - (struct host_cmd_ds_11n_delba *) del_ba; - uint16_t del_ba_param_set = le16_to_cpu(cmd_del_ba->del_ba_param_set); - int tid; - - tid = del_ba_param_set >> DELBA_TID_POS; - - mwifiex_del_ba_tbl(priv, tid, cmd_del_ba->peer_mac_addr, - TYPE_DELBA_RECEIVE, INITIATOR_BIT(del_ba_param_set)); -} - -/* - * This function retrieves the Rx reordering table. - */ -int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, - struct mwifiex_ds_rx_reorder_tbl *buf) -{ - int i; - struct mwifiex_ds_rx_reorder_tbl *rx_reo_tbl = buf; - struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr; - int count = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_for_each_entry(rx_reorder_tbl_ptr, &priv->rx_reorder_tbl_ptr, - list) { - rx_reo_tbl->tid = (u16) rx_reorder_tbl_ptr->tid; - memcpy(rx_reo_tbl->ta, rx_reorder_tbl_ptr->ta, ETH_ALEN); - rx_reo_tbl->start_win = rx_reorder_tbl_ptr->start_win; - rx_reo_tbl->win_size = rx_reorder_tbl_ptr->win_size; - for (i = 0; i < rx_reorder_tbl_ptr->win_size; ++i) { - if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) - rx_reo_tbl->buffer[i] = true; - else - rx_reo_tbl->buffer[i] = false; - } - rx_reo_tbl++; - count++; - - if (count >= MWIFIEX_MAX_RX_BASTREAM_SUPPORTED) - break; - } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - return count; -} - -/* - * This function retrieves the Tx BA stream table. - */ -int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, - struct mwifiex_ds_tx_ba_stream_tbl *buf) -{ - struct mwifiex_tx_ba_stream_tbl *tx_ba_tsr_tbl; - struct mwifiex_ds_tx_ba_stream_tbl *rx_reo_tbl = buf; - int count = 0; - unsigned long flags; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry(tx_ba_tsr_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - rx_reo_tbl->tid = (u16) tx_ba_tsr_tbl->tid; - mwifiex_dbg(priv->adapter, DATA, "data: %s tid=%d\n", - __func__, rx_reo_tbl->tid); - memcpy(rx_reo_tbl->ra, tx_ba_tsr_tbl->ra, ETH_ALEN); - rx_reo_tbl->amsdu = tx_ba_tsr_tbl->amsdu; - rx_reo_tbl++; - count++; - if (count >= MWIFIEX_MAX_TX_BASTREAM_SUPPORTED) - break; - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - - return count; -} - -/* - * This function retrieves the entry for specific tx BA stream table by RA and - * deletes it. - */ -void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra) -{ - struct mwifiex_tx_ba_stream_tbl *tbl, *tmp; - unsigned long flags; - - if (!ra) - return; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry_safe(tbl, tmp, &priv->tx_ba_stream_tbl_ptr, list) { - if (!memcmp(tbl->ra, ra, ETH_ALEN)) { - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, - flags); - mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, tbl); - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - } - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - - return; -} - -/* This function initializes the BlockACK setup information for given - * mwifiex_private structure. - */ -void mwifiex_set_ba_params(struct mwifiex_private *priv) -{ - priv->add_ba_param.timeout = MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - priv->add_ba_param.tx_win_size = - MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE; - priv->add_ba_param.rx_win_size = - MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; - } else { - priv->add_ba_param.tx_win_size = - MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; - } - - priv->add_ba_param.tx_amsdu = true; - priv->add_ba_param.rx_amsdu = true; - - return; -} - -u8 mwifiex_get_sec_chan_offset(int chan) -{ - u8 sec_offset; - - 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: - sec_offset = IEEE80211_HT_PARAM_CHA_SEC_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: - sec_offset = IEEE80211_HT_PARAM_CHA_SEC_BELOW; - break; - case 165: - default: - sec_offset = IEEE80211_HT_PARAM_CHA_SEC_NONE; - break; - } - - return sec_offset; -} - -/* This function will send DELBA to entries in the priv's - * Tx BA stream table - */ -static void -mwifiex_send_delba_txbastream_tbl(struct mwifiex_private *priv, u8 tid) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_tx_ba_stream_tbl *tx_ba_stream_tbl_ptr; - - if (list_empty(&priv->tx_ba_stream_tbl_ptr)) - return; - - list_for_each_entry(tx_ba_stream_tbl_ptr, - &priv->tx_ba_stream_tbl_ptr, list) { - if (tx_ba_stream_tbl_ptr->ba_status == BA_SETUP_COMPLETE) { - if (tid == tx_ba_stream_tbl_ptr->tid) { - dev_dbg(adapter->dev, - "Tx:Send delba to tid=%d, %pM\n", tid, - tx_ba_stream_tbl_ptr->ra); - mwifiex_send_delba(priv, - tx_ba_stream_tbl_ptr->tid, - tx_ba_stream_tbl_ptr->ra, 1); - return; - } - } - } -} - -/* This function updates all the tx_win_size - */ -void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *adapter) -{ - u8 i; - u32 tx_win_size; - struct mwifiex_private *priv; - - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i]) - continue; - priv = adapter->priv[i]; - tx_win_size = priv->add_ba_param.tx_win_size; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) - priv->add_ba_param.tx_win_size = - MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) - priv->add_ba_param.tx_win_size = - MWIFIEX_STA_AMPDU_DEF_TXWINSIZE; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) - priv->add_ba_param.tx_win_size = - MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE; - - if (adapter->coex_win_size) { - if (adapter->coex_tx_win_size) - priv->add_ba_param.tx_win_size = - adapter->coex_tx_win_size; - } - - if (tx_win_size != priv->add_ba_param.tx_win_size) { - if (!priv->media_connected) - continue; - for (i = 0; i < MAX_NUM_TID; i++) - mwifiex_send_delba_txbastream_tbl(priv, i); - } - } -} diff --git a/drivers/net/wireless/mwifiex/11n.h b/drivers/net/wireless/mwifiex/11n.h deleted file mode 100644 index afdd58aa90de..000000000000 --- a/drivers/net/wireless/mwifiex/11n.h +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_11N_H_ -#define _MWIFIEX_11N_H_ - -#include "11n_aggr.h" -#include "11n_rxreorder.h" -#include "wmm.h" - -int mwifiex_ret_11n_delba(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_ret_11n_addba_req(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_cmd_11n_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action, - struct mwifiex_ds_11n_tx_cfg *txcfg); -int mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 **buffer); -int mwifiex_fill_cap_info(struct mwifiex_private *, u8 radio_type, - struct ieee80211_ht_cap *); -int mwifiex_set_get_11n_htcap_cfg(struct mwifiex_private *priv, - u16 action, int *htcap_cfg); -void mwifiex_11n_delete_tx_ba_stream_tbl_entry(struct mwifiex_private *priv, - struct mwifiex_tx_ba_stream_tbl - *tx_tbl); -void mwifiex_11n_delete_all_tx_ba_stream_tbl(struct mwifiex_private *priv); -struct mwifiex_tx_ba_stream_tbl *mwifiex_get_ba_tbl(struct - mwifiex_private - *priv, int tid, - u8 *ra); -void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, - enum mwifiex_ba_status ba_status); -int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac); -int mwifiex_send_delba(struct mwifiex_private *priv, int tid, u8 *peer_mac, - int initiator); -void mwifiex_11n_delete_ba_stream(struct mwifiex_private *priv, u8 *del_ba); -int mwifiex_get_rx_reorder_tbl(struct mwifiex_private *priv, - struct mwifiex_ds_rx_reorder_tbl *buf); -int mwifiex_get_tx_ba_stream_tbl(struct mwifiex_private *priv, - struct mwifiex_ds_tx_ba_stream_tbl *buf); -int mwifiex_cmd_recfg_tx_buf(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - int cmd_action, u16 *buf_size); -int mwifiex_cmd_amsdu_aggr_ctrl(struct host_cmd_ds_command *cmd, - int cmd_action, - struct mwifiex_ds_11n_amsdu_aggr_ctrl *aa_ctrl); -void mwifiex_del_tx_ba_stream_tbl_by_ra(struct mwifiex_private *priv, u8 *ra); -u8 mwifiex_get_sec_chan_offset(int chan); - -static inline u8 -mwifiex_is_station_ampdu_allowed(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int tid) -{ - struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ptr->ra); - - if (unlikely(!node)) - return false; - - return (node->ampdu_sta[tid] != BA_STREAM_NOT_ALLOWED) ? true : false; -} - -/* This function checks whether AMPDU is allowed or not for a particular TID. */ -static inline u8 -mwifiex_is_ampdu_allowed(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int tid) -{ - if (is_broadcast_ether_addr(ptr->ra)) - return false; - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); - } else { - if (ptr->tdls_link) - return mwifiex_is_station_ampdu_allowed(priv, ptr, tid); - - return (priv->aggr_prio_tbl[tid].ampdu_ap != - BA_STREAM_NOT_ALLOWED) ? true : false; - } -} - -/* - * This function checks whether AMSDU is allowed or not for a particular TID. - */ -static inline u8 -mwifiex_is_amsdu_allowed(struct mwifiex_private *priv, int tid) -{ - return (((priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED) && - (priv->is_data_rate_auto || !(priv->bitmap_rates[2] & 0x03))) - ? true : false); -} - -/* - * This function checks whether a space is available for new BA stream or not. - */ -static inline u8 mwifiex_space_avail_for_new_ba_stream( - struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - u8 i; - u32 ba_stream_num = 0, ba_stream_max; - - ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv) - ba_stream_num += mwifiex_wmm_list_len( - &priv->tx_ba_stream_tbl_ptr); - } - - if (adapter->fw_api_ver == MWIFIEX_FW_V15) { - ba_stream_max = - GETSUPP_TXBASTREAMS(adapter->hw_dot_11n_dev_cap); - if (!ba_stream_max) - ba_stream_max = MWIFIEX_MAX_TX_BASTREAM_SUPPORTED; - } - - return ((ba_stream_num < ba_stream_max) ? true : false); -} - -/* - * This function finds the correct Tx BA stream to delete. - * - * Upon successfully locating, both the TID and the RA are returned. - */ -static inline u8 -mwifiex_find_stream_to_delete(struct mwifiex_private *priv, int ptr_tid, - int *ptid, u8 *ra) -{ - int tid; - u8 ret = false; - struct mwifiex_tx_ba_stream_tbl *tx_tbl; - unsigned long flags; - - tid = priv->aggr_prio_tbl[ptr_tid].ampdu_user; - - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - list_for_each_entry(tx_tbl, &priv->tx_ba_stream_tbl_ptr, list) { - if (tid > priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user) { - tid = priv->aggr_prio_tbl[tx_tbl->tid].ampdu_user; - *ptid = tx_tbl->tid; - memcpy(ra, tx_tbl->ra, ETH_ALEN); - ret = true; - } - } - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - - return ret; -} - -/* - * This function checks whether associated station is 11n enabled - */ -static inline int mwifiex_is_sta_11n_enabled(struct mwifiex_private *priv, - struct mwifiex_sta_node *node) -{ - - if (!node || (priv->bss_role != MWIFIEX_BSS_ROLE_UAP) || - !priv->ap_11n_enabled) - return 0; - - return node->is_11n_enabled; -} - -static inline u8 -mwifiex_tdls_peer_11n_enabled(struct mwifiex_private *priv, const u8 *ra) -{ - struct mwifiex_sta_node *node = mwifiex_get_sta_entry(priv, ra); - if (node) - return node->is_11n_enabled; - - return false; -} -#endif /* !_MWIFIEX_11N_H_ */ diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c deleted file mode 100644 index aa498e0d2204..000000000000 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n Aggregation - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11n_aggr.h" - -/* - * Creates an AMSDU subframe for aggregation into one AMSDU packet. - * - * The resultant AMSDU subframe format is - - * - * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ - * | DA | SA | Length | SNAP header | MSDU | - * | data[0..5] | data[6..11] | | | data[14..] | - * +---- ~ -----+---- ~ ------+---- ~ -----+----- ~ -----+---- ~ -----+ - * <--6-bytes--> <--6-bytes--> <--2-bytes--><--8-bytes--> <--n-bytes--> - * - * This function also computes the amount of padding required to make the - * buffer length multiple of 4 bytes. - * - * Data => |DA|SA|SNAP-TYPE|........ .| - * MSDU => |DA|SA|Length|SNAP|...... ..| - */ -static int -mwifiex_11n_form_amsdu_pkt(struct sk_buff *skb_aggr, - struct sk_buff *skb_src, int *pad) - -{ - int dt_offset; - struct rfc_1042_hdr 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 - */ - }; - struct tx_packet_hdr *tx_header; - - tx_header = (void *)skb_put(skb_aggr, sizeof(*tx_header)); - - /* Copy DA and SA */ - dt_offset = 2 * ETH_ALEN; - memcpy(&tx_header->eth803_hdr, skb_src->data, dt_offset); - - /* Copy SNAP header */ - snap.snap_type = ((struct ethhdr *)skb_src->data)->h_proto; - - dt_offset += sizeof(__be16); - - memcpy(&tx_header->rfc1042_hdr, &snap, sizeof(struct rfc_1042_hdr)); - - skb_pull(skb_src, dt_offset); - - /* Update Length field */ - tx_header->eth803_hdr.h_proto = htons(skb_src->len + LLC_SNAP_LEN); - - /* Add payload */ - memcpy(skb_put(skb_aggr, skb_src->len), skb_src->data, skb_src->len); - - /* Add padding for new MSDU to start from 4 byte boundary */ - *pad = (4 - ((unsigned long)skb_aggr->tail & 0x3)) % 4; - - return skb_aggr->len + *pad; -} - -/* - * Adds TxPD to AMSDU header. - * - * Each AMSDU packet will contain one TxPD at the beginning, - * followed by multiple AMSDU subframes. - */ -static void -mwifiex_11n_form_amsdu_txpd(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct txpd *local_tx_pd; - struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - unsigned int pad; - int headroom = (priv->adapter->iface_type == - MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; - - pad = ((void *)skb->data - sizeof(*local_tx_pd) - - headroom - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); - skb_push(skb, pad); - - skb_push(skb, sizeof(*local_tx_pd)); - - local_tx_pd = (struct txpd *) skb->data; - memset(local_tx_pd, 0, sizeof(struct txpd)); - - /* Original priority has been overwritten */ - local_tx_pd->priority = (u8) skb->priority; - local_tx_pd->pkt_delay_2ms = - mwifiex_wmm_compute_drv_pkt_delay(priv, skb); - local_tx_pd->bss_num = priv->bss_num; - local_tx_pd->bss_type = priv->bss_type; - /* Always zero as the data is followed by struct txpd */ - local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd) + - pad); - local_tx_pd->tx_pkt_type = cpu_to_le16(PKT_TYPE_AMSDU); - local_tx_pd->tx_pkt_length = cpu_to_le16(skb->len - - sizeof(*local_tx_pd) - - pad); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) - local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; - - if (local_tx_pd->tx_control == 0) - /* TxCtrl set by user or default */ - local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && - priv->adapter->pps_uapsd_mode) { - if (true == mwifiex_check_last_packet_indication(priv)) { - priv->adapter->tx_lock_flag = true; - local_tx_pd->flags = - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; - } - } -} - -/* - * Create aggregated packet. - * - * This function creates an aggregated MSDU packet, by combining buffers - * from the RA list. Each individual buffer is encapsulated as an AMSDU - * subframe and all such subframes are concatenated together to form the - * AMSDU packet. - * - * A TxPD is also added to the front of the resultant AMSDU packets for - * transmission. The resultant packets format is - - * - * +---- ~ ----+------ ~ ------+------ ~ ------+-..-+------ ~ ------+ - * | TxPD |AMSDU sub-frame|AMSDU sub-frame| .. |AMSDU sub-frame| - * | | 1 | 2 | .. | n | - * +---- ~ ----+------ ~ ------+------ ~ ------+ .. +------ ~ ------+ - */ -int -mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *pra_list, - int ptrindex, unsigned long ra_list_flags) - __releases(&priv->wmm.ra_list_spinlock) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct sk_buff *skb_aggr, *skb_src; - struct mwifiex_txinfo *tx_info_aggr, *tx_info_src; - int pad = 0, aggr_num = 0, ret; - struct mwifiex_tx_param tx_param; - struct txpd *ptx_pd = NULL; - int headroom = adapter->iface_type == MWIFIEX_USB ? 0 : INTF_HEADER_LEN; - - skb_src = skb_peek(&pra_list->skb_head); - if (!skb_src) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - return 0; - } - - tx_info_src = MWIFIEX_SKB_TXCB(skb_src); - skb_aggr = mwifiex_alloc_dma_align_buf(adapter->tx_buf_size, - GFP_ATOMIC | GFP_DMA); - if (!skb_aggr) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - return -1; - } - skb_reserve(skb_aggr, MWIFIEX_MIN_DATA_HEADER_LEN); - tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); - - memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); - tx_info_aggr->bss_type = tx_info_src->bss_type; - tx_info_aggr->bss_num = tx_info_src->bss_num; - - if (tx_info_src->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) - tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; - tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_AGGR_PKT; - skb_aggr->priority = skb_src->priority; - skb_aggr->tstamp = skb_src->tstamp; - - skb_aggr->tstamp = ktime_get_real(); - - do { - /* Check if AMSDU can accommodate this MSDU */ - if (skb_tailroom(skb_aggr) < (skb_src->len + LLC_SNAP_LEN)) - break; - - skb_src = skb_dequeue(&pra_list->skb_head); - pra_list->total_pkt_count--; - atomic_dec(&priv->wmm.tx_pkts_queued); - aggr_num++; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); - - mwifiex_write_data_complete(adapter, skb_src, 0, 0); - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); - - if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - return -1; - } - - if (skb_tailroom(skb_aggr) < pad) { - pad = 0; - break; - } - skb_put(skb_aggr, pad); - - skb_src = skb_peek(&pra_list->skb_head); - - } while (skb_src); - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); - - /* Last AMSDU packet does not need padding */ - skb_trim(skb_aggr, skb_aggr->len - pad); - - /* Form AMSDU */ - mwifiex_11n_form_amsdu_txpd(priv, skb_aggr); - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) - ptx_pd = (struct txpd *)skb_aggr->data; - - skb_push(skb_aggr, headroom); - tx_info_aggr->aggr_num = aggr_num * 2; - if (adapter->data_sent || adapter->tx_lock_flag) { - atomic_add(aggr_num * 2, &adapter->tx_queued); - skb_queue_tail(&adapter->tx_data_q, skb_aggr); - return 0; - } - - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, - skb_aggr, NULL); - } else { - if (skb_src) - tx_param.next_pkt_len = - skb_src->len + sizeof(struct txpd); - else - tx_param.next_pkt_len = 0; - - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, - skb_aggr, &tx_param); - } - switch (ret) { - case -EBUSY: - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); - if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_write_data_complete(adapter, skb_aggr, 1, -1); - return -1; - } - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && - adapter->pps_uapsd_mode && adapter->tx_lock_flag) { - priv->adapter->tx_lock_flag = false; - if (ptx_pd) - ptx_pd->flags = 0; - } - - skb_queue_tail(&pra_list->skb_head, skb_aggr); - - pra_list->total_pkt_count++; - - atomic_inc(&priv->wmm.tx_pkts_queued); - - tx_info_aggr->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); - break; - case -1: - mwifiex_dbg(adapter, ERROR, "%s: host_to_card failed: %#x\n", - __func__, ret); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); - return 0; - case -EINPROGRESS: - break; - case 0: - mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); - break; - default: - break; - } - if (ret != -EBUSY) { - mwifiex_rotate_priolists(priv, pra_list, ptrindex); - } - - return 0; -} diff --git a/drivers/net/wireless/mwifiex/11n_aggr.h b/drivers/net/wireless/mwifiex/11n_aggr.h deleted file mode 100644 index 0cd2a3eb6c17..000000000000 --- a/drivers/net/wireless/mwifiex/11n_aggr.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n Aggregation - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_11N_AGGR_H_ -#define _MWIFIEX_11N_AGGR_H_ - -#define PKT_TYPE_AMSDU 0xE6 -#define MIN_NUM_AMSDU 2 - -int mwifiex_11n_deaggregate_pkt(struct mwifiex_private *priv, - struct sk_buff *skb); -int mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, - int ptr_index, unsigned long flags) - __releases(&priv->wmm.ra_list_spinlock); - -#endif /* !_MWIFIEX_11N_AGGR_H_ */ diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c deleted file mode 100644 index b3970a8c9e48..000000000000 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ /dev/null @@ -1,910 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n RX Re-ordering - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11n_rxreorder.h" - -/* This function will dispatch amsdu packet and forward it to kernel/upper - * layer. - */ -static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct rxpd *local_rx_pd = (struct rxpd *)(skb->data); - int ret; - - if (le16_to_cpu(local_rx_pd->rx_pkt_type) == PKT_TYPE_AMSDU) { - struct sk_buff_head list; - struct sk_buff *rx_skb; - - __skb_queue_head_init(&list); - - skb_pull(skb, le16_to_cpu(local_rx_pd->rx_pkt_offset)); - skb_trim(skb, le16_to_cpu(local_rx_pd->rx_pkt_length)); - - ieee80211_amsdu_to_8023s(skb, &list, priv->curr_addr, - priv->wdev.iftype, 0, false); - - while (!skb_queue_empty(&list)) { - rx_skb = __skb_dequeue(&list); - ret = mwifiex_recv_packet(priv, rx_skb); - if (ret == -1) - mwifiex_dbg(priv->adapter, ERROR, - "Rx of A-MSDU failed"); - } - return 0; - } - - return -1; -} - -/* This function will process the rx packet and forward it to kernel/upper - * layer. - */ -static int mwifiex_11n_dispatch_pkt(struct mwifiex_private *priv, void *payload) -{ - int ret = mwifiex_11n_dispatch_amsdu_pkt(priv, payload); - - if (!ret) - return 0; - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - return mwifiex_handle_uap_rx_forward(priv, payload); - - return mwifiex_process_rx_packet(priv, payload); -} - -/* - * This function dispatches all packets in the Rx reorder table until the - * start window. - * - * There could be holes in the buffer, which are skipped by the function. - * Since the buffer is linear, the function uses rotation to simulate - * circular buffer. - */ -static void -mwifiex_11n_dispatch_pkt_until_start_win(struct mwifiex_private *priv, - struct mwifiex_rx_reorder_tbl *tbl, - int start_win) -{ - int pkt_to_send, i; - void *rx_tmp_ptr; - unsigned long flags; - - pkt_to_send = (start_win > tbl->start_win) ? - min((start_win - tbl->start_win), tbl->win_size) : - tbl->win_size; - - for (i = 0; i < pkt_to_send; ++i) { - spin_lock_irqsave(&priv->rx_pkt_lock, flags); - rx_tmp_ptr = NULL; - if (tbl->rx_reorder_ptr[i]) { - rx_tmp_ptr = tbl->rx_reorder_ptr[i]; - tbl->rx_reorder_ptr[i] = NULL; - } - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); - if (rx_tmp_ptr) - mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); - } - - spin_lock_irqsave(&priv->rx_pkt_lock, flags); - /* - * We don't have a circular buffer, hence use rotation to simulate - * circular buffer - */ - for (i = 0; i < tbl->win_size - pkt_to_send; ++i) { - tbl->rx_reorder_ptr[i] = tbl->rx_reorder_ptr[pkt_to_send + i]; - tbl->rx_reorder_ptr[pkt_to_send + i] = NULL; - } - - tbl->start_win = start_win; - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); -} - -/* - * This function dispatches all packets in the Rx reorder table until - * a hole is found. - * - * The start window is adjusted automatically when a hole is located. - * Since the buffer is linear, the function uses rotation to simulate - * circular buffer. - */ -static void -mwifiex_11n_scan_and_dispatch(struct mwifiex_private *priv, - struct mwifiex_rx_reorder_tbl *tbl) -{ - int i, j, xchg; - void *rx_tmp_ptr; - unsigned long flags; - - for (i = 0; i < tbl->win_size; ++i) { - spin_lock_irqsave(&priv->rx_pkt_lock, flags); - if (!tbl->rx_reorder_ptr[i]) { - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); - break; - } - rx_tmp_ptr = tbl->rx_reorder_ptr[i]; - tbl->rx_reorder_ptr[i] = NULL; - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); - mwifiex_11n_dispatch_pkt(priv, rx_tmp_ptr); - } - - spin_lock_irqsave(&priv->rx_pkt_lock, flags); - /* - * We don't have a circular buffer, hence use rotation to simulate - * circular buffer - */ - if (i > 0) { - xchg = tbl->win_size - i; - for (j = 0; j < xchg; ++j) { - tbl->rx_reorder_ptr[j] = tbl->rx_reorder_ptr[i + j]; - tbl->rx_reorder_ptr[i + j] = NULL; - } - } - tbl->start_win = (tbl->start_win + i) & (MAX_TID_VALUE - 1); - spin_unlock_irqrestore(&priv->rx_pkt_lock, flags); -} - -/* - * This function deletes the Rx reorder table and frees the memory. - * - * The function stops the associated timer and dispatches all the - * pending packets in the Rx reorder table before deletion. - */ -static void -mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, - struct mwifiex_rx_reorder_tbl *tbl) -{ - unsigned long flags; - int start_win; - - if (!tbl) - return; - - spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); - priv->adapter->rx_locked = true; - if (priv->adapter->rx_processing) { - spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); - flush_workqueue(priv->adapter->rx_workqueue); - } else { - spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); - } - - start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); - mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); - - del_timer_sync(&tbl->timer_context.timer); - tbl->timer_context.timer_is_set = false; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_del(&tbl->list); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - kfree(tbl->rx_reorder_ptr); - kfree(tbl); - - spin_lock_irqsave(&priv->adapter->rx_proc_lock, flags); - priv->adapter->rx_locked = false; - spin_unlock_irqrestore(&priv->adapter->rx_proc_lock, flags); - -} - -/* - * This function returns the pointer to an entry in Rx reordering - * table which matches the given TA/TID pair. - */ -struct mwifiex_rx_reorder_tbl * -mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta) -{ - struct mwifiex_rx_reorder_tbl *tbl; - unsigned long flags; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) { - if (!memcmp(tbl->ta, ta, ETH_ALEN) && tbl->tid == tid) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); - return tbl; - } - } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - return NULL; -} - -/* This function retrieves the pointer to an entry in Rx reordering - * table which matches the given TA and deletes it. - */ -void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) -{ - struct mwifiex_rx_reorder_tbl *tbl, *tmp; - unsigned long flags; - - if (!ta) - return; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_for_each_entry_safe(tbl, tmp, &priv->rx_reorder_tbl_ptr, list) { - if (!memcmp(tbl->ta, ta, ETH_ALEN)) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); - mwifiex_del_rx_reorder_entry(priv, tbl); - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - } - } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - return; -} - -/* - * This function finds the last sequence number used in the packets - * buffered in Rx reordering table. - */ -static int -mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) -{ - struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr; - struct mwifiex_private *priv = ctx->priv; - unsigned long flags; - int i; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) { - if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - flags); - return i; - } - } - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - return -1; -} - -/* - * This function flushes all the packets in Rx reordering table. - * - * The function checks if any packets are currently buffered in the - * table or not. In case there are packets available, it dispatches - * them and then dumps the Rx reordering table. - */ -static void -mwifiex_flush_data(unsigned long context) -{ - struct reorder_tmr_cnxt *ctx = - (struct reorder_tmr_cnxt *) context; - int start_win, seq_num; - - ctx->timer_is_set = false; - seq_num = mwifiex_11n_find_last_seq_num(ctx); - - if (seq_num < 0) - return; - - mwifiex_dbg(ctx->priv->adapter, INFO, "info: flush data %d\n", seq_num); - start_win = (ctx->ptr->start_win + seq_num + 1) & (MAX_TID_VALUE - 1); - mwifiex_11n_dispatch_pkt_until_start_win(ctx->priv, ctx->ptr, - start_win); -} - -/* - * This function creates an entry in Rx reordering table for the - * given TA/TID. - * - * The function also initializes the entry with sequence number, window - * size as well as initializes the timer. - * - * If the received TA/TID pair is already present, all the packets are - * dispatched and the window size is moved until the SSN. - */ -static void -mwifiex_11n_create_rx_reorder_tbl(struct mwifiex_private *priv, u8 *ta, - int tid, int win_size, int seq_num) -{ - int i; - struct mwifiex_rx_reorder_tbl *tbl, *new_node; - u16 last_seq = 0; - unsigned long flags; - struct mwifiex_sta_node *node; - - /* - * If we get a TID, ta pair which is already present dispatch all the - * the packets and move the window size until the ssn - */ - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); - if (tbl) { - mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, seq_num); - return; - } - /* if !tbl then create one */ - new_node = kzalloc(sizeof(struct mwifiex_rx_reorder_tbl), GFP_KERNEL); - if (!new_node) - return; - - INIT_LIST_HEAD(&new_node->list); - new_node->tid = tid; - memcpy(new_node->ta, ta, ETH_ALEN); - new_node->start_win = seq_num; - new_node->init_win = seq_num; - new_node->flags = 0; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - if (mwifiex_queuing_ra_based(priv)) { - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { - node = mwifiex_get_sta_entry(priv, ta); - if (node) - last_seq = node->rx_seq[tid]; - } - } else { - node = mwifiex_get_sta_entry(priv, ta); - if (node) - last_seq = node->rx_seq[tid]; - else - last_seq = priv->rx_seq[tid]; - } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - mwifiex_dbg(priv->adapter, INFO, - "info: last_seq=%d start_win=%d\n", - last_seq, new_node->start_win); - - if (last_seq != MWIFIEX_DEF_11N_RX_SEQ_NUM && - last_seq >= new_node->start_win) { - new_node->start_win = last_seq + 1; - new_node->flags |= RXREOR_INIT_WINDOW_SHIFT; - } - - new_node->win_size = win_size; - - new_node->rx_reorder_ptr = kzalloc(sizeof(void *) * win_size, - GFP_KERNEL); - if (!new_node->rx_reorder_ptr) { - kfree((u8 *) new_node); - mwifiex_dbg(priv->adapter, ERROR, - "%s: failed to alloc reorder_ptr\n", __func__); - return; - } - - new_node->timer_context.ptr = new_node; - new_node->timer_context.priv = priv; - new_node->timer_context.timer_is_set = false; - - setup_timer(&new_node->timer_context.timer, mwifiex_flush_data, - (unsigned long)&new_node->timer_context); - - for (i = 0; i < win_size; ++i) - new_node->rx_reorder_ptr[i] = NULL; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_add_tail(&new_node->list, &priv->rx_reorder_tbl_ptr); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); -} - -static void -mwifiex_11n_rxreorder_timer_restart(struct mwifiex_rx_reorder_tbl *tbl) -{ - u32 min_flush_time; - - if (tbl->win_size >= MWIFIEX_BA_WIN_SIZE_32) - min_flush_time = MIN_FLUSH_TIMER_15_MS; - else - min_flush_time = MIN_FLUSH_TIMER_MS; - - mod_timer(&tbl->timer_context.timer, - jiffies + msecs_to_jiffies(min_flush_time * tbl->win_size)); - - tbl->timer_context.timer_is_set = true; -} - -/* - * This function prepares command for adding a BA request. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting add BA request buffer - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, void *data_buf) -{ - struct host_cmd_ds_11n_addba_req *add_ba_req = &cmd->params.add_ba_req; - - cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_REQ); - cmd->size = cpu_to_le16(sizeof(*add_ba_req) + S_DS_GEN); - memcpy(add_ba_req, data_buf, sizeof(*add_ba_req)); - - return 0; -} - -/* - * This function prepares command for adding a BA response. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting add BA response buffer - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct host_cmd_ds_11n_addba_req - *cmd_addba_req) -{ - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &cmd->params.add_ba_rsp; - struct mwifiex_sta_node *sta_ptr; - u32 rx_win_size = priv->add_ba_param.rx_win_size; - u8 tid; - int win_size; - unsigned long flags; - uint16_t block_ack_param_set; - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->adapter->is_hw_11ac_capable && - memcmp(priv->cfg_bssid, cmd_addba_req->peer_mac_addr, ETH_ALEN)) { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, - cmd_addba_req->peer_mac_addr); - if (!sta_ptr) { - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - mwifiex_dbg(priv->adapter, ERROR, - "BA setup with unknown TDLS peer %pM!\n", - cmd_addba_req->peer_mac_addr); - return -1; - } - if (sta_ptr->is_11ac_enabled) - rx_win_size = MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE; - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - } - - cmd->command = cpu_to_le16(HostCmd_CMD_11N_ADDBA_RSP); - cmd->size = cpu_to_le16(sizeof(*add_ba_rsp) + S_DS_GEN); - - memcpy(add_ba_rsp->peer_mac_addr, cmd_addba_req->peer_mac_addr, - ETH_ALEN); - add_ba_rsp->dialog_token = cmd_addba_req->dialog_token; - add_ba_rsp->block_ack_tmo = cmd_addba_req->block_ack_tmo; - add_ba_rsp->ssn = cmd_addba_req->ssn; - - block_ack_param_set = le16_to_cpu(cmd_addba_req->block_ack_param_set); - tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) - >> BLOCKACKPARAM_TID_POS; - add_ba_rsp->status_code = cpu_to_le16(ADDBA_RSP_STATUS_ACCEPT); - block_ack_param_set &= ~IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK; - - /* If we don't support AMSDU inside AMPDU, reset the bit */ - if (!priv->add_ba_param.rx_amsdu || - (priv->aggr_prio_tbl[tid].amsdu == BA_STREAM_NOT_ALLOWED)) - block_ack_param_set &= ~BLOCKACKPARAM_AMSDU_SUPP_MASK; - block_ack_param_set |= rx_win_size << BLOCKACKPARAM_WINSIZE_POS; - add_ba_rsp->block_ack_param_set = cpu_to_le16(block_ack_param_set); - win_size = (le16_to_cpu(add_ba_rsp->block_ack_param_set) - & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) - >> BLOCKACKPARAM_WINSIZE_POS; - cmd_addba_req->block_ack_param_set = cpu_to_le16(block_ack_param_set); - - mwifiex_11n_create_rx_reorder_tbl(priv, cmd_addba_req->peer_mac_addr, - tid, win_size, - le16_to_cpu(cmd_addba_req->ssn)); - return 0; -} - -/* - * This function prepares command for deleting a BA request. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting del BA request buffer - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, void *data_buf) -{ - struct host_cmd_ds_11n_delba *del_ba = &cmd->params.del_ba; - - cmd->command = cpu_to_le16(HostCmd_CMD_11N_DELBA); - cmd->size = cpu_to_le16(sizeof(*del_ba) + S_DS_GEN); - memcpy(del_ba, data_buf, sizeof(*del_ba)); - - return 0; -} - -/* - * This function identifies if Rx reordering is needed for a received packet. - * - * In case reordering is required, the function will do the reordering - * before sending it to kernel. - * - * The Rx reorder table is checked first with the received TID/TA pair. If - * not found, the received packet is dispatched immediately. But if found, - * the packet is reordered and all the packets in the updated Rx reordering - * table is dispatched until a hole is found. - * - * For sequence number less than the starting window, the packet is dropped. - */ -int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *priv, - u16 seq_num, u16 tid, - u8 *ta, u8 pkt_type, void *payload) -{ - struct mwifiex_rx_reorder_tbl *tbl; - int prev_start_win, start_win, end_win, win_size; - u16 pkt_index; - bool init_window_shift = false; - int ret = 0; - - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, ta); - if (!tbl) { - if (pkt_type != PKT_TYPE_BAR) - mwifiex_11n_dispatch_pkt(priv, payload); - return ret; - } - - if ((pkt_type == PKT_TYPE_AMSDU) && !tbl->amsdu) { - mwifiex_11n_dispatch_pkt(priv, payload); - return ret; - } - - start_win = tbl->start_win; - prev_start_win = start_win; - win_size = tbl->win_size; - end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); - if (tbl->flags & RXREOR_INIT_WINDOW_SHIFT) { - init_window_shift = true; - tbl->flags &= ~RXREOR_INIT_WINDOW_SHIFT; - } - - if (tbl->flags & RXREOR_FORCE_NO_DROP) { - mwifiex_dbg(priv->adapter, INFO, - "RXREOR_FORCE_NO_DROP when HS is activated\n"); - tbl->flags &= ~RXREOR_FORCE_NO_DROP; - } else if (init_window_shift && seq_num < start_win && - seq_num >= tbl->init_win) { - mwifiex_dbg(priv->adapter, INFO, - "Sender TID sequence number reset %d->%d for SSN %d\n", - start_win, seq_num, tbl->init_win); - tbl->start_win = start_win = seq_num; - end_win = ((start_win + win_size) - 1) & (MAX_TID_VALUE - 1); - } else { - /* - * If seq_num is less then starting win then ignore and drop - * the packet - */ - if ((start_win + TWOPOW11) > (MAX_TID_VALUE - 1)) { - if (seq_num >= ((start_win + TWOPOW11) & - (MAX_TID_VALUE - 1)) && - seq_num < start_win) { - ret = -1; - goto done; - } - } else if ((seq_num < start_win) || - (seq_num >= (start_win + TWOPOW11))) { - ret = -1; - 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); - - 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 (((end_win - win_size) + 1) >= 0) - start_win = (end_win - win_size) + 1; - else - start_win = (MAX_TID_VALUE - (win_size - end_win)) + 1; - mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); - } - - if (pkt_type != PKT_TYPE_BAR) { - if (seq_num >= start_win) - pkt_index = seq_num - start_win; - else - pkt_index = (seq_num+MAX_TID_VALUE) - start_win; - - if (tbl->rx_reorder_ptr[pkt_index]) { - ret = -1; - goto done; - } - - tbl->rx_reorder_ptr[pkt_index] = payload; - } - - /* - * Dispatch all packets sequentially from start_win until a - * hole is found and adjust the start_win appropriately - */ - mwifiex_11n_scan_and_dispatch(priv, tbl); - -done: - if (!tbl->timer_context.timer_is_set || - prev_start_win != tbl->start_win) - mwifiex_11n_rxreorder_timer_restart(tbl); - return ret; -} - -/* - * This function deletes an entry for a given TID/TA pair. - * - * The TID/TA are taken from del BA event body. - */ -void -mwifiex_del_ba_tbl(struct mwifiex_private *priv, int tid, u8 *peer_mac, - u8 type, int initiator) -{ - struct mwifiex_rx_reorder_tbl *tbl; - struct mwifiex_tx_ba_stream_tbl *ptx_tbl; - struct mwifiex_ra_list_tbl *ra_list; - u8 cleanup_rx_reorder_tbl; - unsigned long flags; - int tid_down; - - if (type == TYPE_DELBA_RECEIVE) - cleanup_rx_reorder_tbl = (initiator) ? true : false; - else - cleanup_rx_reorder_tbl = (initiator) ? false : true; - - mwifiex_dbg(priv->adapter, EVENT, "event: DELBA: %pM tid=%d initiator=%d\n", - peer_mac, tid, initiator); - - if (cleanup_rx_reorder_tbl) { - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, - peer_mac); - if (!tbl) { - mwifiex_dbg(priv->adapter, EVENT, - "event: TID, TA not found in table\n"); - return; - } - mwifiex_del_rx_reorder_entry(priv, tbl); - } else { - ptx_tbl = mwifiex_get_ba_tbl(priv, tid, peer_mac); - if (!ptx_tbl) { - mwifiex_dbg(priv->adapter, EVENT, - "event: TID, RA not found in table\n"); - return; - } - - tid_down = mwifiex_wmm_downgrade_tid(priv, tid); - ra_list = mwifiex_wmm_get_ralist_node(priv, tid_down, peer_mac); - if (ra_list) { - ra_list->amsdu_in_ampdu = false; - ra_list->ba_status = BA_SETUP_NONE; - } - spin_lock_irqsave(&priv->tx_ba_stream_tbl_lock, flags); - mwifiex_11n_delete_tx_ba_stream_tbl_entry(priv, ptx_tbl); - spin_unlock_irqrestore(&priv->tx_ba_stream_tbl_lock, flags); - } -} - -/* - * This function handles the command response of an add BA response. - * - * Handling includes changing the header fields into CPU format and - * creating the stream, provided the add BA is accepted. - */ -int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_11n_addba_rsp *add_ba_rsp = &resp->params.add_ba_rsp; - int tid, win_size; - struct mwifiex_rx_reorder_tbl *tbl; - uint16_t block_ack_param_set; - - block_ack_param_set = le16_to_cpu(add_ba_rsp->block_ack_param_set); - - tid = (block_ack_param_set & IEEE80211_ADDBA_PARAM_TID_MASK) - >> BLOCKACKPARAM_TID_POS; - /* - * Check if we had rejected the ADDBA, if yes then do not create - * the stream - */ - if (le16_to_cpu(add_ba_rsp->status_code) != BA_RESULT_SUCCESS) { - mwifiex_dbg(priv->adapter, ERROR, "ADDBA RSP: failed %pM tid=%d)\n", - add_ba_rsp->peer_mac_addr, tid); - - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, - add_ba_rsp->peer_mac_addr); - if (tbl) - mwifiex_del_rx_reorder_entry(priv, tbl); - - return 0; - } - - win_size = (block_ack_param_set & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) - >> BLOCKACKPARAM_WINSIZE_POS; - - tbl = mwifiex_11n_get_rx_reorder_tbl(priv, tid, - add_ba_rsp->peer_mac_addr); - if (tbl) { - if ((block_ack_param_set & BLOCKACKPARAM_AMSDU_SUPP_MASK) && - priv->add_ba_param.rx_amsdu && - (priv->aggr_prio_tbl[tid].amsdu != BA_STREAM_NOT_ALLOWED)) - tbl->amsdu = true; - else - tbl->amsdu = false; - } - - mwifiex_dbg(priv->adapter, CMD, - "cmd: ADDBA RSP: %pM tid=%d ssn=%d win_size=%d\n", - add_ba_rsp->peer_mac_addr, tid, add_ba_rsp->ssn, win_size); - - return 0; -} - -/* - * This function handles BA stream timeout event by preparing and sending - * a command to the firmware. - */ -void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, - struct host_cmd_ds_11n_batimeout *event) -{ - struct host_cmd_ds_11n_delba delba; - - memset(&delba, 0, sizeof(struct host_cmd_ds_11n_delba)); - memcpy(delba.peer_mac_addr, event->peer_mac_addr, ETH_ALEN); - - delba.del_ba_param_set |= - cpu_to_le16((u16) event->tid << DELBA_TID_POS); - delba.del_ba_param_set |= cpu_to_le16( - (u16) event->origninator << DELBA_INITIATOR_POS); - delba.reason_code = cpu_to_le16(WLAN_REASON_QSTA_TIMEOUT); - mwifiex_send_cmd(priv, HostCmd_CMD_11N_DELBA, 0, 0, &delba, false); -} - -/* - * This function cleans up the Rx reorder table by deleting all the entries - * and re-initializing. - */ -void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) -{ - struct mwifiex_rx_reorder_tbl *del_tbl_ptr, *tmp_node; - unsigned long flags; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - list_for_each_entry_safe(del_tbl_ptr, tmp_node, - &priv->rx_reorder_tbl_ptr, list) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr); - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); - } - INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - - mwifiex_reset_11n_rx_seq_num(priv); -} - -/* - * This function updates all rx_reorder_tbl's flags. - */ -void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) -{ - struct mwifiex_private *priv; - struct mwifiex_rx_reorder_tbl *tbl; - unsigned long lock_flags; - int i; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - - spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags); - if (list_empty(&priv->rx_reorder_tbl_ptr)) { - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, - lock_flags); - continue; - } - - list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) - tbl->flags = flags; - spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags); - } - - return; -} - -/* This function update all the rx_win_size based on coex flag - */ -static void mwifiex_update_ampdu_rxwinsize(struct mwifiex_adapter *adapter, - bool coex_flag) -{ - u8 i; - u32 rx_win_size; - struct mwifiex_private *priv; - - dev_dbg(adapter->dev, "Update rxwinsize %d\n", coex_flag); - - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i]) - continue; - priv = adapter->priv[i]; - rx_win_size = priv->add_ba_param.rx_win_size; - if (coex_flag) { - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; - if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE; - if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) - priv->add_ba_param.rx_win_size = - MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE; - } else { - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; - if (priv->bss_type == MWIFIEX_BSS_TYPE_P2P) - priv->add_ba_param.rx_win_size = - MWIFIEX_STA_AMPDU_DEF_RXWINSIZE; - if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) - priv->add_ba_param.rx_win_size = - MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE; - } - - if (adapter->coex_win_size && adapter->coex_rx_win_size) - priv->add_ba_param.rx_win_size = - adapter->coex_rx_win_size; - - if (rx_win_size != priv->add_ba_param.rx_win_size) { - if (!priv->media_connected) - continue; - for (i = 0; i < MAX_NUM_TID; i++) - mwifiex_11n_delba(priv, i); - } - } -} - -/* This function check coex for RX BA - */ -void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter) -{ - u8 i; - struct mwifiex_private *priv; - u8 count = 0; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { - if (priv->media_connected) - count++; - } - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - if (priv->bss_started) - count++; - } - } - if (count >= MWIFIEX_BSS_COEX_COUNT) - break; - } - if (count >= MWIFIEX_BSS_COEX_COUNT) - mwifiex_update_ampdu_rxwinsize(adapter, true); - else - mwifiex_update_ampdu_rxwinsize(adapter, false); -} diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.h b/drivers/net/wireless/mwifiex/11n_rxreorder.h deleted file mode 100644 index 63ecea89b4ab..000000000000 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Marvell Wireless LAN device driver: 802.11n RX Re-ordering - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_11N_RXREORDER_H_ -#define _MWIFIEX_11N_RXREORDER_H_ - -#define MIN_FLUSH_TIMER_MS 50 -#define MIN_FLUSH_TIMER_15_MS 15 -#define MWIFIEX_BA_WIN_SIZE_32 32 - -#define PKT_TYPE_BAR 0xE7 -#define MAX_TID_VALUE (2 << 11) -#define TWOPOW11 (2 << 10) - -#define BLOCKACKPARAM_TID_POS 2 -#define BLOCKACKPARAM_AMSDU_SUPP_MASK 0x1 -#define BLOCKACKPARAM_WINSIZE_POS 6 -#define DELBA_TID_POS 12 -#define DELBA_INITIATOR_POS 11 -#define TYPE_DELBA_SENT 1 -#define TYPE_DELBA_RECEIVE 2 -#define IMMEDIATE_BLOCK_ACK 0x2 - -#define ADDBA_RSP_STATUS_ACCEPT 0 - -#define MWIFIEX_DEF_11N_RX_SEQ_NUM 0xffff -#define BA_SETUP_MAX_PACKET_THRESHOLD 16 -#define BA_SETUP_PACKET_OFFSET 16 - -enum mwifiex_rxreor_flags { - RXREOR_FORCE_NO_DROP = 1<<0, - RXREOR_INIT_WINDOW_SHIFT = 1<<1, -}; - -static inline void mwifiex_reset_11n_rx_seq_num(struct mwifiex_private *priv) -{ - memset(priv->rx_seq, 0xff, sizeof(priv->rx_seq)); -} - -int mwifiex_11n_rx_reorder_pkt(struct mwifiex_private *, - u16 seqNum, - u16 tid, u8 *ta, - u8 pkttype, void *payload); -void mwifiex_del_ba_tbl(struct mwifiex_private *priv, int Tid, - u8 *PeerMACAddr, u8 type, int initiator); -void mwifiex_11n_ba_stream_timeout(struct mwifiex_private *priv, - struct host_cmd_ds_11n_batimeout *event); -int mwifiex_ret_11n_addba_resp(struct mwifiex_private *priv, - struct host_cmd_ds_command - *resp); -int mwifiex_cmd_11n_delba(struct host_cmd_ds_command *cmd, - void *data_buf); -int mwifiex_cmd_11n_addba_rsp_gen(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct host_cmd_ds_11n_addba_req - *cmd_addba_req); -int mwifiex_cmd_11n_addba_req(struct host_cmd_ds_command *cmd, - void *data_buf); -void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv); -struct mwifiex_rx_reorder_tbl *mwifiex_11n_get_rxreorder_tbl(struct - mwifiex_private - *priv, int tid, - u8 *ta); -struct mwifiex_rx_reorder_tbl * -mwifiex_11n_get_rx_reorder_tbl(struct mwifiex_private *priv, int tid, u8 *ta); -void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta); -void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags); - -#endif /* _MWIFIEX_11N_RXREORDER_H_ */ diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig deleted file mode 100644 index 279167ddd293..000000000000 --- a/drivers/net/wireless/mwifiex/Kconfig +++ /dev/null @@ -1,44 +0,0 @@ -config MWIFIEX - tristate "Marvell WiFi-Ex Driver" - depends on CFG80211 - ---help--- - This adds support for wireless adapters based on Marvell - 802.11n/ac chipsets. - - If you choose to build it as a module, it will be called - mwifiex. - -config MWIFIEX_SDIO - tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8997" - depends on MWIFIEX && MMC - select FW_LOADER - select WANT_DEV_COREDUMP - ---help--- - This adds support for wireless adapters based on Marvell - 8786/8787/8797/8887/8897/8997 chipsets with SDIO interface. - - If you choose to build it as a module, it will be called - mwifiex_sdio. - -config MWIFIEX_PCIE - tristate "Marvell WiFi-Ex Driver for PCIE 8766/8897/8997" - depends on MWIFIEX && PCI - select FW_LOADER - select WANT_DEV_COREDUMP - ---help--- - This adds support for wireless adapters based on Marvell - 8766/8897/8997 chipsets with PCIe interface. - - If you choose to build it as a module, it will be called - mwifiex_pcie. - -config MWIFIEX_USB - tristate "Marvell WiFi-Ex Driver for USB8766/8797/8997" - depends on MWIFIEX && USB - select FW_LOADER - ---help--- - This adds support for wireless adapters based on Marvell - 8797/8997 chipset with USB interface. - - If you choose to build it as a module, it will be called - mwifiex_usb. diff --git a/drivers/net/wireless/mwifiex/Makefile b/drivers/net/wireless/mwifiex/Makefile deleted file mode 100644 index fdfd9bf15ed4..000000000000 --- a/drivers/net/wireless/mwifiex/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -# -# Copyright (C) 2011-2014, Marvell International Ltd. -# -# This software file (the "File") is distributed by Marvell International -# Ltd. under the terms of the GNU General Public License Version 2, June 1991 -# (the "License"). You may use, redistribute and/or modify this File in -# accordance with the terms and conditions of the License, a copy of which -# is available by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the -# worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. -# -# THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -# ARE EXPRESSLY DISCLAIMED. The License provides additional details about -# this warranty disclaimer. - - -mwifiex-y += main.o -mwifiex-y += init.o -mwifiex-y += cfp.o -mwifiex-y += cmdevt.o -mwifiex-y += util.o -mwifiex-y += txrx.o -mwifiex-y += wmm.o -mwifiex-y += 11n.o -mwifiex-y += 11ac.o -mwifiex-y += 11n_aggr.o -mwifiex-y += 11n_rxreorder.o -mwifiex-y += scan.o -mwifiex-y += join.o -mwifiex-y += sta_ioctl.o -mwifiex-y += sta_cmd.o -mwifiex-y += uap_cmd.o -mwifiex-y += ie.o -mwifiex-y += sta_cmdresp.o -mwifiex-y += sta_event.o -mwifiex-y += uap_event.o -mwifiex-y += sta_tx.o -mwifiex-y += sta_rx.o -mwifiex-y += uap_txrx.o -mwifiex-y += cfg80211.o -mwifiex-y += ethtool.o -mwifiex-y += 11h.o -mwifiex-y += tdls.o -mwifiex-$(CONFIG_DEBUG_FS) += debugfs.o -obj-$(CONFIG_MWIFIEX) += mwifiex.o - -mwifiex_sdio-y += sdio.o -obj-$(CONFIG_MWIFIEX_SDIO) += mwifiex_sdio.o - -mwifiex_pcie-y += pcie.o -obj-$(CONFIG_MWIFIEX_PCIE) += mwifiex_pcie.o - -mwifiex_usb-y += usb.o -obj-$(CONFIG_MWIFIEX_USB) += mwifiex_usb.o - -ccflags-y += -D__CHECK_ENDIAN diff --git a/drivers/net/wireless/mwifiex/README b/drivers/net/wireless/mwifiex/README deleted file mode 100644 index 2f0f9b5609d0..000000000000 --- a/drivers/net/wireless/mwifiex/README +++ /dev/null @@ -1,240 +0,0 @@ -# Copyright (C) 2011-2014, Marvell International Ltd. -# -# This software file (the "File") is distributed by Marvell International -# Ltd. under the terms of the GNU General Public License Version 2, June 1991 -# (the "License"). You may use, redistribute and/or modify this File in -# accordance with the terms and conditions of the License, a copy of which -# is available by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the -# worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. -# -# THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -# ARE EXPRESSLY DISCLAIMED. The License provides additional details about -# this warranty disclaimer. - - -=============================================================================== - U S E R M A N U A L - -1) FOR DRIVER INSTALL - - a) Copy sd8787.bin to /lib/firmware/mrvl/ directory, - create the directory if it doesn't exist. - b) Install WLAN driver, - insmod mwifiex.ko - c) Uninstall WLAN driver, - ifconfig mlanX down - rmmod mwifiex - - -2) FOR DRIVER CONFIGURATION AND INFO - The configurations can be done either using the 'iw' user space - utility or debugfs. - - a) 'iw' utility commands - - Following are some useful iw commands:- - -iw dev mlan0 scan - - This command will trigger a scan. - The command will then display the scan table entries - -iw dev mlan0 connect -w [] [] [key 0:abcde d:1123456789a] - The above command can be used to connect to an AP with a particular SSID. - Ap's operating frequency can be specified or even the bssid. If the AP is using - WEP encryption, wep keys can be specified in the command. - Note: Every time before connecting to an AP scan command (iw dev mlan0 scan) should be used by user. - -iw dev mlan0 disconnect - This command will be used to disconnect from an AP. - - -iw dev mlan0 ibss join [fixed-freq] [fixed-bssid] [key 0:abcde] - The command will be used to join or create an ibss. Optionally, operating frequency, - bssid and the security related parameters can be specified while joining/creating - and ibss. - -iw dev mlan0 ibss leave - The command will be used to leave an ibss network. - -iw dev mlan0 link - The command will be used to get the connection status. The command will return parameters - such as SSID, operating frequency, rx/tx packets, signal strength, tx bitrate. - - Apart from the iw utility all standard configurations using the 'iwconfig' utility are also supported. - - b) Debugfs interface - - The debugfs interface can be used for configurations and for getting - some useful information from the driver. - The section below explains the configurations that can be - done. - - Mount debugfs to /debugfs mount point: - - mkdir /debugfs - mount -t debugfs debugfs /debugfs - - The information is provided in /debugfs/mwifiex/mlanX/: - -iw reg set - The command will be used to change the regulatory domain. - -iw reg get - The command will be used to get current regulatory domain. - -info - This command is used to get driver info. - - Usage: - cat info - - driver_name = "mwifiex" - driver_version = - interface_name = "mlanX" - bss_mode = "Ad-hoc" | "Managed" | "Auto" | "Unknown" - media_state = "Disconnected" | "Connected" - mac_address = <6-byte adapter MAC address> - multicase_count = - essid = - bssid = - channel = - region_code = - multicasr_address[n] = - num_tx_bytes = - num_rx_bytes = - num_tx_pkts = - num_rx_pkts = - num_tx_pkts_dropped = - num_rx_pkts_dropped = - num_tx_pkts_err = - num_rx_pkts_err = - carrier "on" | "off" - tx queue "stopped" | "started" - - The following debug info are provided in /debugfs/mwifiex/mlanX/debug: - - int_counter = - wmm_ac_vo = - wmm_ac_vi = - wmm_ac_be = - wmm_ac_bk = - tx_buf_size = - curr_tx_buf_size = - ps_mode = <0/1, CAM mode/PS mode> - ps_state = <0/1/2/3, full power state/awake state/pre-sleep state/sleep state> - is_deep_sleep = <0/1, not deep sleep state/deep sleep state> - wakeup_dev_req = <0/1, wakeup device not required/required> - wakeup_tries = - hs_configured = <0/1, host sleep not configured/configured> - hs_activated = <0/1, extended host sleep not activated/activated> - num_tx_timeout = - is_cmd_timedout = <0/1 command timeout not occurred/occurred> - timeout_cmd_id = - timeout_cmd_act = - last_cmd_id = - last_cmd_act = - last_cmd_index = <0 based last command index> - last_cmd_resp_id = - last_cmd_resp_index = <0 based last command response index> - last_event = - last_event_index = <0 based last event index> - num_cmd_h2c_fail = - num_cmd_sleep_cfm_fail = - num_tx_h2c_fail = - num_evt_deauth = - num_evt_disassoc = - num_evt_link_lost = - num_cmd_deauth = - num_cmd_assoc_ok = - num_cmd_assoc_fail = - cmd_sent = <0/1, send command resources available/sending command to device> - data_sent = <0/1, send data resources available/sending data to device> - mp_rd_bitmap = - mp_wr_bitmap = - cmd_resp_received = <0/1, no cmd response to process/response received and yet to process> - event_received = <0/1, no event to process/event received and yet to process> - cmd_pending = - tx_pending = - rx_pending = - - -3) FOR DRIVER CONFIGURATION - -regrdwr - This command is used to read/write the adapter register. - - Usage: - echo " [value]" > regrdwr - cat regrdwr - - where the parameters are, - : 1:MAC/SOC, 2:BBP, 3:RF, 4:PMIC, 5:CAU - : offset of register - [value]: value to be written - - Examples: - echo "1 0xa060" > regrdwr : Read the MAC register - echo "1 0xa060 0x12" > regrdwr : Write the MAC register - echo "1 0xa794 0x80000000" > regrdwr - : Write 0x80000000 to MAC register -rdeeprom - This command is used to read the EEPROM contents of the card. - - Usage: - echo " " > rdeeprom - cat rdeeprom - - where the parameters are, - : multiples of 4 - : 4-20, multiples of 4 - - Example: - echo "0 20" > rdeeprom : Read 20 bytes of EEPROM data from offset 0 - -hscfg - This command is used to debug/simulate host sleep feature using - different configuration parameters. - - Usage: - echo " [GPIO# [gap]]]" > hscfg - cat hscfg - - where the parameters are, - : bit 0 = 1 -- broadcast data - bit 1 = 1 -- unicast data - bit 2 = 1 -- mac event - bit 3 = 1 -- multicast data - [GPIO#]: pin number of GPIO used to wakeup the host. - GPIO pin# (e.g. 0-7) or 0xff (interface, e.g. SDIO - will be used instead). - [gap]: the gap in milliseconds between wakeup signal and - wakeup event or 0xff for special setting (host - acknowledge required) when GPIO is used to wakeup host. - - Examples: - echo "-1" > hscfg : Cancel host sleep mode - echo "3" > hscfg : Broadcast and unicast data; - Use GPIO and gap set previously - echo "2 3" > hscfg : Unicast data and GPIO 3; - Use gap set previously - echo "2 1 160" > hscfg : Unicast data, GPIO 1 and gap 160 ms - echo "2 1 0xff" > hscfg : Unicast data, GPIO 1; Wait for host - to ack before sending wakeup event - -getlog - This command is used to get the statistics available in the station. - Usage: - - cat getlog - -device_dump - This command is used to dump driver information and firmware memory - segments. - Usage: - - cat fw_dump - -=============================================================================== diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c deleted file mode 100644 index 4073116e6e9f..000000000000 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ /dev/null @@ -1,3887 +0,0 @@ -/* - * Marvell Wireless LAN device driver: CFG80211 - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "cfg80211.h" -#include "main.h" -#include "11n.h" - -static char *reg_alpha2; -module_param(reg_alpha2, charp, 0); - -static const struct ieee80211_iface_limit mwifiex_ap_sta_limits[] = { - { - .max = 2, .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_CLIENT), - }, - { - .max = 1, .types = BIT(NL80211_IFTYPE_AP), - }, -}; - -static const struct ieee80211_iface_combination -mwifiex_iface_comb_ap_sta = { - .limits = mwifiex_ap_sta_limits, - .num_different_channels = 1, - .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), - .max_interfaces = MWIFIEX_MAX_BSS_NUM, - .beacon_int_infra_match = true, - .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40), -}; - -static const struct ieee80211_iface_combination -mwifiex_iface_comb_ap_sta_vht = { - .limits = mwifiex_ap_sta_limits, - .num_different_channels = 1, - .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), - .max_interfaces = MWIFIEX_MAX_BSS_NUM, - .beacon_int_infra_match = true, - .radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40) | - BIT(NL80211_CHAN_WIDTH_80), -}; - -static const struct -ieee80211_iface_combination mwifiex_iface_comb_ap_sta_drcs = { - .limits = mwifiex_ap_sta_limits, - .num_different_channels = 2, - .n_limits = ARRAY_SIZE(mwifiex_ap_sta_limits), - .max_interfaces = MWIFIEX_MAX_BSS_NUM, - .beacon_int_infra_match = true, -}; - -/* - * This function maps the nl802.11 channel type into driver channel type. - * - * The mapping is as follows - - * NL80211_CHAN_NO_HT -> IEEE80211_HT_PARAM_CHA_SEC_NONE - * NL80211_CHAN_HT20 -> IEEE80211_HT_PARAM_CHA_SEC_NONE - * NL80211_CHAN_HT40PLUS -> IEEE80211_HT_PARAM_CHA_SEC_ABOVE - * NL80211_CHAN_HT40MINUS -> IEEE80211_HT_PARAM_CHA_SEC_BELOW - * Others -> IEEE80211_HT_PARAM_CHA_SEC_NONE - */ -u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type) -{ - switch (chan_type) { - case NL80211_CHAN_NO_HT: - case NL80211_CHAN_HT20: - return IEEE80211_HT_PARAM_CHA_SEC_NONE; - case NL80211_CHAN_HT40PLUS: - return IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - case NL80211_CHAN_HT40MINUS: - return IEEE80211_HT_PARAM_CHA_SEC_BELOW; - default: - return IEEE80211_HT_PARAM_CHA_SEC_NONE; - } -} - -/* This function maps IEEE HT secondary channel type to NL80211 channel type - */ -u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset) -{ - switch (second_chan_offset) { - case IEEE80211_HT_PARAM_CHA_SEC_NONE: - return NL80211_CHAN_HT20; - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - return NL80211_CHAN_HT40PLUS; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - return NL80211_CHAN_HT40MINUS; - default: - return NL80211_CHAN_HT20; - } -} - -/* - * This function checks whether WEP is set. - */ -static int -mwifiex_is_alg_wep(u32 cipher) -{ - switch (cipher) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - return 1; - default: - break; - } - - return 0; -} - -/* - * This function retrieves the private structure from kernel wiphy structure. - */ -static void *mwifiex_cfg80211_get_adapter(struct wiphy *wiphy) -{ - return (void *) (*(unsigned long *) wiphy_priv(wiphy)); -} - -/* - * CFG802.11 operation handler to delete a network key. - */ -static int -mwifiex_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, - u8 key_index, bool pairwise, const u8 *mac_addr) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); - const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - const u8 *peer_mac = pairwise ? mac_addr : bc_mac; - - if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, peer_mac, 1)) { - mwifiex_dbg(priv->adapter, ERROR, "deleting the crypto keys\n"); - return -EFAULT; - } - - mwifiex_dbg(priv->adapter, INFO, "info: crypto keys deleted\n"); - return 0; -} - -/* - * This function forms an skb for management frame. - */ -static int -mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) -{ - u8 addr[ETH_ALEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - u16 pkt_len; - u32 tx_control = 0, pkt_type = PKT_TYPE_MGMT; - - pkt_len = len + ETH_ALEN; - - skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); - memcpy(skb_push(skb, sizeof(pkt_len)), &pkt_len, sizeof(pkt_len)); - - memcpy(skb_push(skb, sizeof(tx_control)), - &tx_control, sizeof(tx_control)); - - memcpy(skb_push(skb, sizeof(pkt_type)), &pkt_type, sizeof(pkt_type)); - - /* Add packet data and address4 */ - memcpy(skb_put(skb, sizeof(struct ieee80211_hdr_3addr)), buf, - sizeof(struct ieee80211_hdr_3addr)); - memcpy(skb_put(skb, ETH_ALEN), addr, ETH_ALEN); - memcpy(skb_put(skb, len - sizeof(struct ieee80211_hdr_3addr)), - buf + sizeof(struct ieee80211_hdr_3addr), - len - sizeof(struct ieee80211_hdr_3addr)); - - skb->priority = LOW_PRIO_TID; - __net_timestamp(skb); - - return 0; -} - -/* - * CFG802.11 operation handler to transmit a management frame. - */ -static int -mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, - struct cfg80211_mgmt_tx_params *params, u64 *cookie) -{ - const u8 *buf = params->buf; - size_t len = params->len; - struct sk_buff *skb; - u16 pkt_len; - const struct ieee80211_mgmt *mgmt; - struct mwifiex_txinfo *tx_info; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - - if (!buf || !len) { - mwifiex_dbg(priv->adapter, ERROR, "invalid buffer and length\n"); - return -EFAULT; - } - - mgmt = (const struct ieee80211_mgmt *)buf; - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA && - ieee80211_is_probe_resp(mgmt->frame_control)) { - /* Since we support offload probe resp, we need to skip probe - * resp in AP or GO mode */ - mwifiex_dbg(priv->adapter, INFO, - "info: skip to send probe resp in AP or GO mode\n"); - return 0; - } - - pkt_len = len + ETH_ALEN; - skb = dev_alloc_skb(MWIFIEX_MIN_DATA_HEADER_LEN + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + - pkt_len + sizeof(pkt_len)); - - if (!skb) { - mwifiex_dbg(priv->adapter, ERROR, - "allocate skb failed for management frame\n"); - return -ENOMEM; - } - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->pkt_len = pkt_len; - - mwifiex_form_mgmt_frame(skb, buf, len); - *cookie = prandom_u32() | 1; - - if (ieee80211_is_action(mgmt->frame_control)) - skb = mwifiex_clone_skb_for_tx_status(priv, - skb, - MWIFIEX_BUF_FLAG_ACTION_TX_STATUS, cookie); - else - cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, - GFP_ATOMIC); - - mwifiex_queue_tx_pkt(priv, skb); - - mwifiex_dbg(priv->adapter, INFO, "info: management frame transmitted\n"); - return 0; -} - -/* - * CFG802.11 operation handler to register a mgmt frame. - */ -static void -mwifiex_cfg80211_mgmt_frame_register(struct wiphy *wiphy, - struct wireless_dev *wdev, - u16 frame_type, bool reg) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - u32 mask; - - if (reg) - mask = priv->mgmt_frame_mask | BIT(frame_type >> 4); - else - mask = priv->mgmt_frame_mask & ~BIT(frame_type >> 4); - - if (mask != priv->mgmt_frame_mask) { - priv->mgmt_frame_mask = mask; - mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, - HostCmd_ACT_GEN_SET, 0, - &priv->mgmt_frame_mask, false); - mwifiex_dbg(priv->adapter, INFO, "info: mgmt frame registered\n"); - } -} - -/* - * CFG802.11 operation handler to remain on channel. - */ -static int -mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, - struct ieee80211_channel *chan, - unsigned int duration, u64 *cookie) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - int ret; - - if (!chan || !cookie) { - mwifiex_dbg(priv->adapter, ERROR, "Invalid parameter for ROC\n"); - return -EINVAL; - } - - if (priv->roc_cfg.cookie) { - mwifiex_dbg(priv->adapter, INFO, - "info: ongoing ROC, cookie = 0x%llx\n", - priv->roc_cfg.cookie); - return -EBUSY; - } - - ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, chan, - duration); - - if (!ret) { - *cookie = prandom_u32() | 1; - priv->roc_cfg.cookie = *cookie; - priv->roc_cfg.chan = *chan; - - cfg80211_ready_on_channel(wdev, *cookie, chan, - duration, GFP_ATOMIC); - - mwifiex_dbg(priv->adapter, INFO, - "info: ROC, cookie = 0x%llx\n", *cookie); - } - - return ret; -} - -/* - * CFG802.11 operation handler to cancel remain on channel. - */ -static int -mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, u64 cookie) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - int ret; - - if (cookie != priv->roc_cfg.cookie) - return -ENOENT; - - ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE, - &priv->roc_cfg.chan, 0); - - if (!ret) { - cfg80211_remain_on_channel_expired(wdev, cookie, - &priv->roc_cfg.chan, - GFP_ATOMIC); - - memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg)); - - mwifiex_dbg(priv->adapter, INFO, - "info: cancel ROC, cookie = 0x%llx\n", cookie); - } - - return ret; -} - -/* - * CFG802.11 operation handler to set Tx power. - */ -static int -mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, - struct wireless_dev *wdev, - enum nl80211_tx_power_setting type, - int mbm) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv; - struct mwifiex_power_cfg power_cfg; - int dbm = MBM_TO_DBM(mbm); - - if (type == NL80211_TX_POWER_FIXED) { - power_cfg.is_power_auto = 0; - power_cfg.power_level = dbm; - } else { - power_cfg.is_power_auto = 1; - } - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - return mwifiex_set_tx_power(priv, &power_cfg); -} - -/* - * CFG802.11 operation handler to set Power Save option. - * - * The timeout value, if provided, is currently ignored. - */ -static int -mwifiex_cfg80211_set_power_mgmt(struct wiphy *wiphy, - struct net_device *dev, - bool enabled, int timeout) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - u32 ps_mode; - - if (timeout) - mwifiex_dbg(priv->adapter, INFO, - "info: ignore timeout value for IEEE Power Save\n"); - - ps_mode = enabled; - - return mwifiex_drv_set_power(priv, &ps_mode); -} - -/* - * CFG802.11 operation handler to set the default network key. - */ -static int -mwifiex_cfg80211_set_default_key(struct wiphy *wiphy, struct net_device *netdev, - u8 key_index, bool unicast, - bool multicast) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); - - /* Return if WEP key not configured */ - if (!priv->sec_info.wep_enabled) - return 0; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_UAP) { - priv->wep_key_curr_index = key_index; - } else if (mwifiex_set_encode(priv, NULL, NULL, 0, key_index, - NULL, 0)) { - mwifiex_dbg(priv->adapter, ERROR, "set default Tx key index\n"); - return -EFAULT; - } - - return 0; -} - -/* - * CFG802.11 operation handler to add a network key. - */ -static int -mwifiex_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, - u8 key_index, bool pairwise, const u8 *mac_addr, - struct key_params *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(netdev); - struct mwifiex_wep_key *wep_key; - const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - const u8 *peer_mac = pairwise ? mac_addr : bc_mac; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && - (params->cipher == WLAN_CIPHER_SUITE_WEP40 || - params->cipher == WLAN_CIPHER_SUITE_WEP104)) { - if (params->key && params->key_len) { - wep_key = &priv->wep_key[key_index]; - memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); - memcpy(wep_key->key_material, params->key, - params->key_len); - wep_key->key_index = key_index; - wep_key->key_length = params->key_len; - priv->sec_info.wep_enabled = 1; - } - return 0; - } - - if (mwifiex_set_encode(priv, params, params->key, params->key_len, - key_index, peer_mac, 0)) { - mwifiex_dbg(priv->adapter, ERROR, "crypto keys added\n"); - return -EFAULT; - } - - return 0; -} - -/* - * This function sends domain information to the firmware. - * - * The following information are passed to the firmware - - * - Country codes - * - Sub bands (first channel, number of channels, maximum Tx power) - */ -int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy) -{ - u8 no_of_triplet = 0; - struct ieee80211_country_ie_triplet *t; - u8 no_of_parsed_chan = 0; - u8 first_chan = 0, next_chan = 0, max_pwr = 0; - u8 i, flag = 0; - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch; - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv; - struct mwifiex_802_11d_domain_reg *domain_info = &adapter->domain_reg; - - /* Set country code */ - domain_info->country_code[0] = adapter->country_code[0]; - domain_info->country_code[1] = adapter->country_code[1]; - domain_info->country_code[2] = ' '; - - band = mwifiex_band_to_radio_type(adapter->config_bands); - if (!wiphy->bands[band]) { - mwifiex_dbg(adapter, ERROR, - "11D: setting domain info in FW\n"); - return -1; - } - - sband = wiphy->bands[band]; - - for (i = 0; i < sband->n_channels ; i++) { - ch = &sband->channels[i]; - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; - - if (!flag) { - flag = 1; - first_chan = (u32) ch->hw_value; - next_chan = first_chan; - max_pwr = ch->max_power; - no_of_parsed_chan = 1; - continue; - } - - if (ch->hw_value == next_chan + 1 && - ch->max_power == max_pwr) { - next_chan++; - no_of_parsed_chan++; - } else { - t = &domain_info->triplet[no_of_triplet]; - t->chans.first_channel = first_chan; - t->chans.num_channels = no_of_parsed_chan; - t->chans.max_power = max_pwr; - no_of_triplet++; - first_chan = (u32) ch->hw_value; - next_chan = first_chan; - max_pwr = ch->max_power; - no_of_parsed_chan = 1; - } - } - - if (flag) { - t = &domain_info->triplet[no_of_triplet]; - t->chans.first_channel = first_chan; - t->chans.num_channels = no_of_parsed_chan; - t->chans.max_power = max_pwr; - no_of_triplet++; - } - - domain_info->no_of_triplet = no_of_triplet; - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, - HostCmd_ACT_GEN_SET, 0, NULL, false)) { - mwifiex_dbg(adapter, INFO, - "11D: setting domain info in FW\n"); - return -1; - } - - return 0; -} - -/* - * CFG802.11 regulatory domain callback function. - * - * This function is called when the regulatory domain is changed due to the - * following reasons - - * - Set by driver - * - Set by system core - * - Set by user - * - Set bt Country IE - */ -static void mwifiex_reg_notifier(struct wiphy *wiphy, - struct regulatory_request *request) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv = mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY); - mwifiex_dbg(adapter, INFO, - "info: cfg80211 regulatory domain callback for %c%c\n", - request->alpha2[0], request->alpha2[1]); - - switch (request->initiator) { - case NL80211_REGDOM_SET_BY_DRIVER: - case NL80211_REGDOM_SET_BY_CORE: - case NL80211_REGDOM_SET_BY_USER: - case NL80211_REGDOM_SET_BY_COUNTRY_IE: - break; - default: - mwifiex_dbg(adapter, ERROR, - "unknown regdom initiator: %d\n", - request->initiator); - return; - } - - /* Don't send world or same regdom info to firmware */ - if (strncmp(request->alpha2, "00", 2) && - strncmp(request->alpha2, adapter->country_code, - sizeof(request->alpha2))) { - memcpy(adapter->country_code, request->alpha2, - sizeof(request->alpha2)); - mwifiex_send_domain_info_cmd_fw(wiphy); - mwifiex_dnld_txpwr_table(priv); - } -} - -/* - * This function sets the fragmentation threshold. - * - * The fragmentation threshold value must lie between MWIFIEX_FRAG_MIN_VALUE - * and MWIFIEX_FRAG_MAX_VALUE. - */ -static int -mwifiex_set_frag(struct mwifiex_private *priv, u32 frag_thr) -{ - if (frag_thr < MWIFIEX_FRAG_MIN_VALUE || - frag_thr > MWIFIEX_FRAG_MAX_VALUE) - frag_thr = MWIFIEX_FRAG_MAX_VALUE; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, FRAG_THRESH_I, - &frag_thr, true); -} - -/* - * This function sets the RTS threshold. - - * The rts value must lie between MWIFIEX_RTS_MIN_VALUE - * and MWIFIEX_RTS_MAX_VALUE. - */ -static int -mwifiex_set_rts(struct mwifiex_private *priv, u32 rts_thr) -{ - if (rts_thr < MWIFIEX_RTS_MIN_VALUE || rts_thr > MWIFIEX_RTS_MAX_VALUE) - rts_thr = MWIFIEX_RTS_MAX_VALUE; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, RTS_THRESH_I, - &rts_thr, true); -} - -/* - * CFG802.11 operation handler to set wiphy parameters. - * - * This function can be used to set the RTS threshold and the - * Fragmentation threshold of the driver. - */ -static int -mwifiex_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv; - struct mwifiex_uap_bss_param *bss_cfg; - int ret; - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - switch (priv->bss_role) { - case MWIFIEX_BSS_ROLE_UAP: - if (priv->bss_started) { - mwifiex_dbg(adapter, ERROR, - "cannot change wiphy params when bss started"); - return -EINVAL; - } - - bss_cfg = kzalloc(sizeof(*bss_cfg), GFP_KERNEL); - if (!bss_cfg) - return -ENOMEM; - - mwifiex_set_sys_config_invalid_data(bss_cfg); - - if (changed & WIPHY_PARAM_RTS_THRESHOLD) - bss_cfg->rts_threshold = wiphy->rts_threshold; - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) - bss_cfg->frag_threshold = wiphy->frag_threshold; - if (changed & WIPHY_PARAM_RETRY_LONG) - bss_cfg->retry_limit = wiphy->retry_long; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg, - false); - - kfree(bss_cfg); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "Failed to set wiphy phy params\n"); - return ret; - } - break; - - case MWIFIEX_BSS_ROLE_STA: - if (priv->media_connected) { - mwifiex_dbg(adapter, ERROR, - "cannot change wiphy params when connected"); - return -EINVAL; - } - if (changed & WIPHY_PARAM_RTS_THRESHOLD) { - ret = mwifiex_set_rts(priv, - wiphy->rts_threshold); - if (ret) - return ret; - } - if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { - ret = mwifiex_set_frag(priv, - wiphy->frag_threshold); - if (ret) - return ret; - } - break; - } - - return 0; -} - -static int -mwifiex_cfg80211_deinit_p2p(struct mwifiex_private *priv) -{ - u16 mode = P2P_MODE_DISABLE; - - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - return 0; -} - -/* - * This function initializes the functionalities for P2P client. - * The P2P client initialization sequence is: - * disable -> device -> client - */ -static int -mwifiex_cfg80211_init_p2p_client(struct mwifiex_private *priv) -{ - u16 mode; - - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -1; - - mode = P2P_MODE_DEVICE; - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - mode = P2P_MODE_CLIENT; - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - return 0; -} - -/* - * This function initializes the functionalities for P2P GO. - * The P2P GO initialization sequence is: - * disable -> device -> GO - */ -static int -mwifiex_cfg80211_init_p2p_go(struct mwifiex_private *priv) -{ - u16 mode; - - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -1; - - mode = P2P_MODE_DEVICE; - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - mode = P2P_MODE_GO; - if (mwifiex_send_cmd(priv, HostCmd_CMD_P2P_MODE_CFG, - HostCmd_ACT_GEN_SET, 0, &mode, true)) - return -1; - - return 0; -} - -static int mwifiex_deinit_priv_params(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - unsigned long flags; - - priv->mgmt_frame_mask = 0; - if (mwifiex_send_cmd(priv, HostCmd_CMD_MGMT_FRAME_REG, - HostCmd_ACT_GEN_SET, 0, - &priv->mgmt_frame_mask, false)) { - mwifiex_dbg(adapter, ERROR, - "could not unregister mgmt frame rx\n"); - return -1; - } - - mwifiex_deauthenticate(priv, NULL); - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - adapter->main_locked = true; - if (adapter->mwifiex_processing) { - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - flush_workqueue(adapter->workqueue); - } else { - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - } - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - adapter->rx_locked = true; - if (adapter->rx_processing) { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - flush_workqueue(adapter->rx_workqueue); - } else { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - } - - mwifiex_free_priv(priv); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; - - return 0; -} - -static int -mwifiex_init_new_priv_params(struct mwifiex_private *priv, - struct net_device *dev, - enum nl80211_iftype type) -{ - struct mwifiex_adapter *adapter = priv->adapter; - unsigned long flags; - - mwifiex_init_priv(priv); - - priv->bss_mode = type; - priv->wdev.iftype = type; - - mwifiex_init_priv_params(priv, priv->netdev); - priv->bss_started = 0; - - switch (type) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_type = MWIFIEX_BSS_TYPE_STA; - break; - case NL80211_IFTYPE_P2P_CLIENT: - priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_type = MWIFIEX_BSS_TYPE_P2P; - break; - case NL80211_IFTYPE_P2P_GO: - priv->bss_role = MWIFIEX_BSS_ROLE_UAP; - priv->bss_type = MWIFIEX_BSS_TYPE_P2P; - break; - case NL80211_IFTYPE_AP: - priv->bss_type = MWIFIEX_BSS_TYPE_UAP; - priv->bss_role = MWIFIEX_BSS_ROLE_UAP; - break; - default: - mwifiex_dbg(adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - adapter->main_locked = false; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - adapter->rx_locked = false; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - - return 0; -} - -static int -mwifiex_change_vif_to_p2p(struct net_device *dev, - enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct mwifiex_private *priv; - struct mwifiex_adapter *adapter; - - priv = mwifiex_netdev_get_priv(dev); - - if (!priv) - return -1; - - adapter = priv->adapter; - - if (adapter->curr_iface_comb.p2p_intf == - adapter->iface_limit.p2p_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple P2P ifaces\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "%s: changing role to p2p\n", dev->name); - - if (mwifiex_deinit_priv_params(priv)) - return -1; - if (mwifiex_init_new_priv_params(priv, dev, type)) - return -1; - - switch (type) { - case NL80211_IFTYPE_P2P_CLIENT: - if (mwifiex_cfg80211_init_p2p_client(priv)) - return -EFAULT; - break; - case NL80211_IFTYPE_P2P_GO: - if (mwifiex_cfg80211_init_p2p_go(priv)) - return -EFAULT; - break; - default: - mwifiex_dbg(adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - - if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, true)) - return -1; - - if (mwifiex_sta_init_cmd(priv, false, false)) - return -1; - - switch (curr_iftype) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf--; - break; - case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf--; - break; - default: - break; - } - - adapter->curr_iface_comb.p2p_intf++; - dev->ieee80211_ptr->iftype = type; - - return 0; -} - -static int -mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, - enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct mwifiex_private *priv; - struct mwifiex_adapter *adapter; - - priv = mwifiex_netdev_get_priv(dev); - - if (!priv) - return -1; - - adapter = priv->adapter; - - if ((curr_iftype != NL80211_IFTYPE_P2P_CLIENT && - curr_iftype != NL80211_IFTYPE_P2P_GO) && - (adapter->curr_iface_comb.sta_intf == - adapter->iface_limit.sta_intf)) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple station/adhoc ifaces\n"); - return -1; - } - - if (type == NL80211_IFTYPE_STATION) - mwifiex_dbg(adapter, INFO, - "%s: changing role to station\n", dev->name); - else - mwifiex_dbg(adapter, INFO, - "%s: changing role to adhoc\n", dev->name); - - if (mwifiex_deinit_priv_params(priv)) - return -1; - if (mwifiex_init_new_priv_params(priv, dev, type)) - return -1; - if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, true)) - return -1; - if (mwifiex_sta_init_cmd(priv, false, false)) - return -1; - - switch (curr_iftype) { - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - adapter->curr_iface_comb.p2p_intf--; - break; - case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf--; - break; - default: - break; - } - - adapter->curr_iface_comb.sta_intf++; - dev->ieee80211_ptr->iftype = type; - return 0; -} - -static int -mwifiex_change_vif_to_ap(struct net_device *dev, - enum nl80211_iftype curr_iftype, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct mwifiex_private *priv; - struct mwifiex_adapter *adapter; - - priv = mwifiex_netdev_get_priv(dev); - - if (!priv) - return -1; - - adapter = priv->adapter; - - if (adapter->curr_iface_comb.uap_intf == - adapter->iface_limit.uap_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple AP ifaces\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "%s: changing role to AP\n", dev->name); - - if (mwifiex_deinit_priv_params(priv)) - return -1; - if (mwifiex_init_new_priv_params(priv, dev, type)) - return -1; - if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, true)) - return -1; - if (mwifiex_sta_init_cmd(priv, false, false)) - return -1; - - switch (curr_iftype) { - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - adapter->curr_iface_comb.p2p_intf--; - break; - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf--; - break; - default: - break; - } - - adapter->curr_iface_comb.uap_intf++; - dev->ieee80211_ptr->iftype = type; - return 0; -} -/* - * CFG802.11 operation handler to change interface type. - */ -static int -mwifiex_cfg80211_change_virtual_intf(struct wiphy *wiphy, - struct net_device *dev, - enum nl80211_iftype type, u32 *flags, - struct vif_params *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - enum nl80211_iftype curr_iftype = dev->ieee80211_ptr->iftype; - - switch (curr_iftype) { - case NL80211_IFTYPE_ADHOC: - switch (type) { - case NL80211_IFTYPE_STATION: - priv->bss_mode = type; - priv->sec_info.authentication_mode = - NL80211_AUTHTYPE_OPEN_SYSTEM; - dev->ieee80211_ptr->iftype = type; - mwifiex_deauthenticate(priv, NULL); - return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, - true); - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); - case NL80211_IFTYPE_AP: - return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); - case NL80211_IFTYPE_UNSPECIFIED: - mwifiex_dbg(priv->adapter, INFO, - "%s: kept type as IBSS\n", dev->name); - case NL80211_IFTYPE_ADHOC: /* This shouldn't happen */ - return 0; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - break; - case NL80211_IFTYPE_STATION: - switch (type) { - case NL80211_IFTYPE_ADHOC: - priv->bss_mode = type; - priv->sec_info.authentication_mode = - NL80211_AUTHTYPE_OPEN_SYSTEM; - dev->ieee80211_ptr->iftype = type; - mwifiex_deauthenticate(priv, NULL); - return mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, - HostCmd_ACT_GEN_SET, 0, NULL, - true); - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); - case NL80211_IFTYPE_AP: - return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); - case NL80211_IFTYPE_UNSPECIFIED: - mwifiex_dbg(priv->adapter, INFO, - "%s: kept type as STA\n", dev->name); - case NL80211_IFTYPE_STATION: /* This shouldn't happen */ - return 0; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - break; - case NL80211_IFTYPE_AP: - switch (type) { - case NL80211_IFTYPE_ADHOC: - case NL80211_IFTYPE_STATION: - return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, - type, flags, - params); - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - return mwifiex_change_vif_to_p2p(dev, curr_iftype, - type, flags, params); - case NL80211_IFTYPE_UNSPECIFIED: - mwifiex_dbg(priv->adapter, INFO, - "%s: kept type as AP\n", dev->name); - case NL80211_IFTYPE_AP: /* This shouldn't happen */ - return 0; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - switch (type) { - case NL80211_IFTYPE_STATION: - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -EFAULT; - priv->adapter->curr_iface_comb.p2p_intf--; - priv->adapter->curr_iface_comb.sta_intf++; - dev->ieee80211_ptr->iftype = type; - break; - case NL80211_IFTYPE_ADHOC: - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -EFAULT; - return mwifiex_change_vif_to_sta_adhoc(dev, curr_iftype, - type, flags, - params); - break; - case NL80211_IFTYPE_AP: - if (mwifiex_cfg80211_deinit_p2p(priv)) - return -EFAULT; - return mwifiex_change_vif_to_ap(dev, curr_iftype, type, - flags, params); - case NL80211_IFTYPE_UNSPECIFIED: - mwifiex_dbg(priv->adapter, INFO, - "%s: kept type as P2P\n", dev->name); - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - return 0; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: changing to %d not supported\n", - dev->name, type); - return -EOPNOTSUPP; - } - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "%s: unknown iftype: %d\n", - dev->name, dev->ieee80211_ptr->iftype); - return -EOPNOTSUPP; - } - - - return 0; -} - -static void -mwifiex_parse_htinfo(struct mwifiex_private *priv, u8 tx_htinfo, - struct rate_info *rate) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - if (adapter->is_hw_11ac_capable) { - /* bit[1-0]: 00=LG 01=HT 10=VHT */ - if (tx_htinfo & BIT(0)) { - /* HT */ - rate->mcs = priv->tx_rate; - rate->flags |= RATE_INFO_FLAGS_MCS; - } - if (tx_htinfo & BIT(1)) { - /* VHT */ - rate->mcs = priv->tx_rate & 0x0F; - rate->flags |= RATE_INFO_FLAGS_VHT_MCS; - } - - if (tx_htinfo & (BIT(1) | BIT(0))) { - /* HT or VHT */ - switch (tx_htinfo & (BIT(3) | BIT(2))) { - case 0: - rate->bw = RATE_INFO_BW_20; - break; - case (BIT(2)): - rate->bw = RATE_INFO_BW_40; - break; - case (BIT(3)): - rate->bw = RATE_INFO_BW_80; - break; - case (BIT(3) | BIT(2)): - rate->bw = RATE_INFO_BW_160; - break; - } - - if (tx_htinfo & BIT(4)) - rate->flags |= RATE_INFO_FLAGS_SHORT_GI; - - if ((priv->tx_rate >> 4) == 1) - rate->nss = 2; - else - rate->nss = 1; - } - } else { - /* - * Bit 0 in tx_htinfo indicates that current Tx rate - * is 11n rate. Valid MCS index values for us are 0 to 15. - */ - if ((tx_htinfo & BIT(0)) && (priv->tx_rate < 16)) { - rate->mcs = priv->tx_rate; - rate->flags |= RATE_INFO_FLAGS_MCS; - rate->bw = RATE_INFO_BW_20; - if (tx_htinfo & BIT(1)) - rate->bw = RATE_INFO_BW_40; - if (tx_htinfo & BIT(2)) - rate->flags |= RATE_INFO_FLAGS_SHORT_GI; - } - } -} - -/* - * This function dumps the station information on a buffer. - * - * The following information are shown - - * - Total bytes transmitted - * - Total bytes received - * - Total packets transmitted - * - Total packets received - * - Signal quality level - * - Transmission rate - */ -static int -mwifiex_dump_station_info(struct mwifiex_private *priv, - struct mwifiex_sta_node *node, - struct station_info *sinfo) -{ - u32 rate; - - sinfo->filled = BIT(NL80211_STA_INFO_RX_BYTES) | BIT(NL80211_STA_INFO_TX_BYTES) | - BIT(NL80211_STA_INFO_RX_PACKETS) | BIT(NL80211_STA_INFO_TX_PACKETS) | - BIT(NL80211_STA_INFO_TX_BITRATE) | - BIT(NL80211_STA_INFO_SIGNAL) | BIT(NL80211_STA_INFO_SIGNAL_AVG); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - if (!node) - return -ENOENT; - - sinfo->filled |= BIT(NL80211_STA_INFO_INACTIVE_TIME) | - BIT(NL80211_STA_INFO_TX_FAILED); - sinfo->inactive_time = - jiffies_to_msecs(jiffies - node->stats.last_rx); - - sinfo->signal = node->stats.rssi; - sinfo->signal_avg = node->stats.rssi; - sinfo->rx_bytes = node->stats.rx_bytes; - sinfo->tx_bytes = node->stats.tx_bytes; - sinfo->rx_packets = node->stats.rx_packets; - sinfo->tx_packets = node->stats.tx_packets; - sinfo->tx_failed = node->stats.tx_failed; - - mwifiex_parse_htinfo(priv, node->stats.last_tx_htinfo, - &sinfo->txrate); - sinfo->txrate.legacy = node->stats.last_tx_rate * 5; - - return 0; - } - - /* Get signal information from the firmware */ - if (mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, - HostCmd_ACT_GEN_GET, 0, NULL, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "failed to get signal information\n"); - return -EFAULT; - } - - if (mwifiex_drv_get_data_rate(priv, &rate)) { - mwifiex_dbg(priv->adapter, ERROR, - "getting data rate error\n"); - return -EFAULT; - } - - /* Get DTIM period information from firmware */ - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, DTIM_PERIOD_I, - &priv->dtim_period, true); - - mwifiex_parse_htinfo(priv, priv->tx_htinfo, &sinfo->txrate); - - sinfo->signal_avg = priv->bcn_rssi_avg; - 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 = priv->bcn_rssi_avg; - /* bit rate is in 500 kb/s units. Convert it to 100kb/s units */ - sinfo->txrate.legacy = rate * 5; - - if (priv->bss_mode == NL80211_IFTYPE_STATION) { - sinfo->filled |= BIT(NL80211_STA_INFO_BSS_PARAM); - sinfo->bss_param.flags = 0; - if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & - WLAN_CAPABILITY_SHORT_PREAMBLE) - sinfo->bss_param.flags |= - BSS_PARAM_FLAGS_SHORT_PREAMBLE; - if (priv->curr_bss_params.bss_descriptor.cap_info_bitmap & - WLAN_CAPABILITY_SHORT_SLOT_TIME) - sinfo->bss_param.flags |= - BSS_PARAM_FLAGS_SHORT_SLOT_TIME; - sinfo->bss_param.dtim_period = priv->dtim_period; - sinfo->bss_param.beacon_interval = - priv->curr_bss_params.bss_descriptor.beacon_period; - } - - return 0; -} - -/* - * CFG802.11 operation handler to get station information. - * - * This function only works in connected mode, and dumps the - * requested station information, if available. - */ -static int -mwifiex_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, struct station_info *sinfo) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (!priv->media_connected) - return -ENOENT; - if (memcmp(mac, priv->cfg_bssid, ETH_ALEN)) - return -ENOENT; - - return mwifiex_dump_station_info(priv, NULL, sinfo); -} - -/* - * CFG802.11 operation handler to dump station information. - */ -static int -mwifiex_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, - int idx, u8 *mac, struct station_info *sinfo) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - static struct mwifiex_sta_node *node; - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - priv->media_connected && idx == 0) { - ether_addr_copy(mac, priv->cfg_bssid); - return mwifiex_dump_station_info(priv, NULL, sinfo); - } else if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - mwifiex_send_cmd(priv, HOST_CMD_APCMD_STA_LIST, - HostCmd_ACT_GEN_GET, 0, NULL, true); - - if (node && (&node->list == &priv->sta_list)) { - node = NULL; - return -ENOENT; - } - - node = list_prepare_entry(node, &priv->sta_list, list); - list_for_each_entry_continue(node, &priv->sta_list, list) { - ether_addr_copy(mac, node->mac_addr); - return mwifiex_dump_station_info(priv, node, sinfo); - } - } - - return -ENOENT; -} - -static int -mwifiex_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *dev, - int idx, struct survey_info *survey) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_chan_stats *pchan_stats = priv->adapter->chan_stats; - enum ieee80211_band band; - - mwifiex_dbg(priv->adapter, DUMP, "dump_survey idx=%d\n", idx); - - memset(survey, 0, sizeof(struct survey_info)); - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - priv->media_connected && idx == 0) { - u8 curr_bss_band = priv->curr_bss_params.band; - u32 chan = priv->curr_bss_params.bss_descriptor.channel; - - band = mwifiex_band_to_radio_type(curr_bss_band); - survey->channel = ieee80211_get_channel(wiphy, - ieee80211_channel_to_frequency(chan, band)); - - if (priv->bcn_nf_last) { - survey->filled = SURVEY_INFO_NOISE_DBM; - survey->noise = priv->bcn_nf_last; - } - return 0; - } - - if (idx >= priv->adapter->num_in_chan_stats) - return -ENOENT; - - if (!pchan_stats[idx].cca_scan_dur) - return 0; - - band = pchan_stats[idx].bandcfg; - survey->channel = ieee80211_get_channel(wiphy, - ieee80211_channel_to_frequency(pchan_stats[idx].chan_num, band)); - survey->filled = SURVEY_INFO_NOISE_DBM | - SURVEY_INFO_TIME | - SURVEY_INFO_TIME_BUSY; - survey->noise = pchan_stats[idx].noise; - survey->time = pchan_stats[idx].cca_scan_dur; - survey->time_busy = pchan_stats[idx].cca_busy_dur; - - return 0; -} - -/* Supported rates to be advertised to the cfg80211 */ -static struct ieee80211_rate mwifiex_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 to be advertised to cfg80211 */ -static struct ieee80211_channel mwifiex_channels_2ghz[] = { - {.center_freq = 2412, .hw_value = 1, }, - {.center_freq = 2417, .hw_value = 2, }, - {.center_freq = 2422, .hw_value = 3, }, - {.center_freq = 2427, .hw_value = 4, }, - {.center_freq = 2432, .hw_value = 5, }, - {.center_freq = 2437, .hw_value = 6, }, - {.center_freq = 2442, .hw_value = 7, }, - {.center_freq = 2447, .hw_value = 8, }, - {.center_freq = 2452, .hw_value = 9, }, - {.center_freq = 2457, .hw_value = 10, }, - {.center_freq = 2462, .hw_value = 11, }, - {.center_freq = 2467, .hw_value = 12, }, - {.center_freq = 2472, .hw_value = 13, }, - {.center_freq = 2484, .hw_value = 14, }, -}; - -static struct ieee80211_supported_band mwifiex_band_2ghz = { - .channels = mwifiex_channels_2ghz, - .n_channels = ARRAY_SIZE(mwifiex_channels_2ghz), - .bitrates = mwifiex_rates, - .n_bitrates = ARRAY_SIZE(mwifiex_rates), -}; - -static struct ieee80211_channel mwifiex_channels_5ghz[] = { - {.center_freq = 5040, .hw_value = 8, }, - {.center_freq = 5060, .hw_value = 12, }, - {.center_freq = 5080, .hw_value = 16, }, - {.center_freq = 5170, .hw_value = 34, }, - {.center_freq = 5190, .hw_value = 38, }, - {.center_freq = 5210, .hw_value = 42, }, - {.center_freq = 5230, .hw_value = 46, }, - {.center_freq = 5180, .hw_value = 36, }, - {.center_freq = 5200, .hw_value = 40, }, - {.center_freq = 5220, .hw_value = 44, }, - {.center_freq = 5240, .hw_value = 48, }, - {.center_freq = 5260, .hw_value = 52, }, - {.center_freq = 5280, .hw_value = 56, }, - {.center_freq = 5300, .hw_value = 60, }, - {.center_freq = 5320, .hw_value = 64, }, - {.center_freq = 5500, .hw_value = 100, }, - {.center_freq = 5520, .hw_value = 104, }, - {.center_freq = 5540, .hw_value = 108, }, - {.center_freq = 5560, .hw_value = 112, }, - {.center_freq = 5580, .hw_value = 116, }, - {.center_freq = 5600, .hw_value = 120, }, - {.center_freq = 5620, .hw_value = 124, }, - {.center_freq = 5640, .hw_value = 128, }, - {.center_freq = 5660, .hw_value = 132, }, - {.center_freq = 5680, .hw_value = 136, }, - {.center_freq = 5700, .hw_value = 140, }, - {.center_freq = 5745, .hw_value = 149, }, - {.center_freq = 5765, .hw_value = 153, }, - {.center_freq = 5785, .hw_value = 157, }, - {.center_freq = 5805, .hw_value = 161, }, - {.center_freq = 5825, .hw_value = 165, }, -}; - -static struct ieee80211_supported_band mwifiex_band_5ghz = { - .channels = mwifiex_channels_5ghz, - .n_channels = ARRAY_SIZE(mwifiex_channels_5ghz), - .bitrates = mwifiex_rates + 4, - .n_bitrates = ARRAY_SIZE(mwifiex_rates) - 4, -}; - - -/* Supported crypto cipher suits to be advertised to cfg80211 */ -static const u32 mwifiex_cipher_suites[] = { - WLAN_CIPHER_SUITE_WEP40, - WLAN_CIPHER_SUITE_WEP104, - WLAN_CIPHER_SUITE_TKIP, - WLAN_CIPHER_SUITE_CCMP, - WLAN_CIPHER_SUITE_AES_CMAC, -}; - -/* Supported mgmt frame types to be advertised to cfg80211 */ -static const struct ieee80211_txrx_stypes -mwifiex_mgmt_stypes[NUM_NL80211_IFTYPES] = { - [NL80211_IFTYPE_STATION] = { - .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_RESP >> 4), - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4), - }, - [NL80211_IFTYPE_AP] = { - .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_RESP >> 4), - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4), - }, - [NL80211_IFTYPE_P2P_CLIENT] = { - .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_RESP >> 4), - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4), - }, - [NL80211_IFTYPE_P2P_GO] = { - .tx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_RESP >> 4), - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4), - }, -}; - -/* - * CFG802.11 operation handler for setting bit rates. - * - * Function configures data rates to firmware using bitrate mask - * provided by cfg80211. - */ -static int mwifiex_cfg80211_set_bitrate_mask(struct wiphy *wiphy, - struct net_device *dev, - const u8 *peer, - const struct cfg80211_bitrate_mask *mask) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; - enum ieee80211_band band; - struct mwifiex_adapter *adapter = priv->adapter; - - if (!priv->media_connected) { - mwifiex_dbg(adapter, ERROR, - "Can not set Tx data rate in disconnected state\n"); - return -EINVAL; - } - - band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - - memset(bitmap_rates, 0, sizeof(bitmap_rates)); - - /* Fill HR/DSSS rates. */ - if (band == IEEE80211_BAND_2GHZ) - bitmap_rates[0] = mask->control[band].legacy & 0x000f; - - /* Fill OFDM rates */ - if (band == IEEE80211_BAND_2GHZ) - bitmap_rates[1] = (mask->control[band].legacy & 0x0ff0) >> 4; - else - bitmap_rates[1] = mask->control[band].legacy; - - /* Fill HT MCS rates */ - bitmap_rates[2] = mask->control[band].ht_mcs[0]; - if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) - bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; - - /* Fill VHT MCS rates */ - if (adapter->fw_api_ver == MWIFIEX_FW_V15) { - bitmap_rates[10] = mask->control[band].vht_mcs[0]; - if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) - bitmap_rates[11] = mask->control[band].vht_mcs[1]; - } - - return mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, - HostCmd_ACT_GEN_SET, 0, bitmap_rates, true); -} - -/* - * CFG802.11 operation handler for connection quality monitoring. - * - * This function subscribes/unsubscribes HIGH_RSSI and LOW_RSSI - * events to FW. - */ -static int mwifiex_cfg80211_set_cqm_rssi_config(struct wiphy *wiphy, - struct net_device *dev, - s32 rssi_thold, u32 rssi_hyst) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_ds_misc_subsc_evt subsc_evt; - - priv->cqm_rssi_thold = rssi_thold; - priv->cqm_rssi_hyst = rssi_hyst; - - memset(&subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); - subsc_evt.events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; - - /* Subscribe/unsubscribe low and high rssi events */ - if (rssi_thold && rssi_hyst) { - subsc_evt.action = HostCmd_ACT_BITWISE_SET; - subsc_evt.bcn_l_rssi_cfg.abs_value = abs(rssi_thold); - subsc_evt.bcn_h_rssi_cfg.abs_value = abs(rssi_thold); - subsc_evt.bcn_l_rssi_cfg.evt_freq = 1; - subsc_evt.bcn_h_rssi_cfg.evt_freq = 1; - return mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, &subsc_evt, true); - } else { - subsc_evt.action = HostCmd_ACT_BITWISE_CLR; - return mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, &subsc_evt, true); - } - - return 0; -} - -/* cfg80211 operation handler for change_beacon. - * Function retrieves and sets modified management IEs to FW. - */ -static int mwifiex_cfg80211_change_beacon(struct wiphy *wiphy, - struct net_device *dev, - struct cfg80211_beacon_data *data) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: bss_type mismatched\n", __func__); - return -EINVAL; - } - - if (!priv->bss_started) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: bss not started\n", __func__); - return -EINVAL; - } - - if (mwifiex_set_mgmt_ies(priv, data)) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: setting mgmt ies failed\n", __func__); - return -EFAULT; - } - - return 0; -} - -/* cfg80211 operation handler for del_station. - * Function deauthenticates station which value is provided in mac parameter. - * If mac is NULL/broadcast, all stations in associated station list are - * deauthenticated. If bss is not started or there are no stations in - * associated stations list, no action is taken. - */ -static int -mwifiex_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, - struct station_del_parameters *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_sta_node *sta_node; - u8 deauth_mac[ETH_ALEN]; - unsigned long flags; - - if (list_empty(&priv->sta_list) || !priv->bss_started) - return 0; - - if (!params->mac || is_broadcast_ether_addr(params->mac)) - return 0; - - mwifiex_dbg(priv->adapter, INFO, "%s: mac address %pM\n", - __func__, params->mac); - - eth_zero_addr(deauth_mac); - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_node = mwifiex_get_sta_entry(priv, params->mac); - if (sta_node) - ether_addr_copy(deauth_mac, params->mac); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (is_valid_ether_addr(deauth_mac)) { - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_STA_DEAUTH, - HostCmd_ACT_GEN_SET, 0, - deauth_mac, true)) - return -1; - } - - return 0; -} - -static int -mwifiex_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv = mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY); - struct mwifiex_ds_ant_cfg ant_cfg; - - if (!tx_ant || !rx_ant) - return -EOPNOTSUPP; - - if (adapter->hw_dev_mcs_support != HT_STREAM_2X2) { - /* Not a MIMO chip. User should provide specific antenna number - * for Tx/Rx path or enable all antennas for diversity - */ - if (tx_ant != rx_ant) - return -EOPNOTSUPP; - - if ((tx_ant & (tx_ant - 1)) && - (tx_ant != BIT(adapter->number_of_antenna) - 1)) - return -EOPNOTSUPP; - - if ((tx_ant == BIT(adapter->number_of_antenna) - 1) && - (priv->adapter->number_of_antenna > 1)) { - tx_ant = RF_ANTENNA_AUTO; - rx_ant = RF_ANTENNA_AUTO; - } - } else { - struct ieee80211_sta_ht_cap *ht_info; - int rx_mcs_supp; - enum ieee80211_band band; - - if ((tx_ant == 0x1 && rx_ant == 0x1)) { - adapter->user_dev_mcs_support = HT_STREAM_1X1; - if (adapter->is_hw_11ac_capable) - adapter->usr_dot_11ac_mcs_support = - MWIFIEX_11AC_MCS_MAP_1X1; - } else { - adapter->user_dev_mcs_support = HT_STREAM_2X2; - if (adapter->is_hw_11ac_capable) - adapter->usr_dot_11ac_mcs_support = - MWIFIEX_11AC_MCS_MAP_2X2; - } - - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { - if (!adapter->wiphy->bands[band]) - continue; - - ht_info = &adapter->wiphy->bands[band]->ht_cap; - rx_mcs_supp = - GET_RXMCSSUPP(adapter->user_dev_mcs_support); - memset(&ht_info->mcs, 0, adapter->number_of_antenna); - memset(&ht_info->mcs, 0xff, rx_mcs_supp); - } - } - - ant_cfg.tx_ant = tx_ant; - ant_cfg.rx_ant = rx_ant; - - return mwifiex_send_cmd(priv, HostCmd_CMD_RF_ANTENNA, - HostCmd_ACT_GEN_SET, 0, &ant_cfg, true); -} - -/* cfg80211 operation handler for stop ap. - * Function stops BSS running at uAP interface. - */ -static int mwifiex_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - mwifiex_abort_cac(priv); - - if (mwifiex_del_mgmt_ies(priv)) - mwifiex_dbg(priv->adapter, ERROR, - "Failed to delete mgmt IEs!\n"); - - priv->ap_11n_enabled = 0; - memset(&priv->bss_cfg, 0, sizeof(priv->bss_cfg)); - - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, NULL, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to stop the BSS\n"); - return -1; - } - - if (mwifiex_send_cmd(priv, HOST_CMD_APCMD_SYS_RESET, - HostCmd_ACT_GEN_SET, 0, NULL, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to reset BSS\n"); - return -1; - } - - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); - - return 0; -} - -/* cfg80211 operation handler for start_ap. - * Function sets beacon period, DTIM period, SSID and security into - * AP config structure. - * AP is configured with these settings and BSS is started. - */ -static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, - struct net_device *dev, - struct cfg80211_ap_settings *params) -{ - struct mwifiex_uap_bss_param *bss_cfg; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_UAP) - return -1; - - bss_cfg = kzalloc(sizeof(struct mwifiex_uap_bss_param), GFP_KERNEL); - if (!bss_cfg) - return -ENOMEM; - - mwifiex_set_sys_config_invalid_data(bss_cfg); - - if (params->beacon_interval) - bss_cfg->beacon_period = params->beacon_interval; - if (params->dtim_period) - bss_cfg->dtim_period = params->dtim_period; - - if (params->ssid && params->ssid_len) { - memcpy(bss_cfg->ssid.ssid, params->ssid, params->ssid_len); - bss_cfg->ssid.ssid_len = params->ssid_len; - } - if (params->inactivity_timeout > 0) { - /* sta_ao_timer/ps_sta_ao_timer is in unit of 100ms */ - bss_cfg->sta_ao_timer = 10 * params->inactivity_timeout; - bss_cfg->ps_sta_ao_timer = 10 * params->inactivity_timeout; - } - - switch (params->hidden_ssid) { - case NL80211_HIDDEN_SSID_NOT_IN_USE: - bss_cfg->bcast_ssid_ctl = 1; - break; - case NL80211_HIDDEN_SSID_ZERO_LEN: - bss_cfg->bcast_ssid_ctl = 0; - break; - case NL80211_HIDDEN_SSID_ZERO_CONTENTS: - /* firmware doesn't support this type of hidden SSID */ - default: - kfree(bss_cfg); - return -EINVAL; - } - - mwifiex_uap_set_channel(priv, bss_cfg, params->chandef); - mwifiex_set_uap_rates(bss_cfg, params); - - if (mwifiex_set_secure_params(priv, bss_cfg, params)) { - kfree(bss_cfg); - mwifiex_dbg(priv->adapter, ERROR, - "Failed to parse secuirty parameters!\n"); - return -1; - } - - mwifiex_set_ht_params(priv, bss_cfg, params); - - if (priv->adapter->is_hw_11ac_capable) { - mwifiex_set_vht_params(priv, bss_cfg, params); - mwifiex_set_vht_width(priv, params->chandef.width, - priv->ap_11ac_enabled); - } - - if (priv->ap_11ac_enabled) - mwifiex_set_11ac_ba_params(priv); - else - mwifiex_set_ba_params(priv); - - mwifiex_set_wmm_params(priv, bss_cfg, params); - - if (mwifiex_is_11h_active(priv)) - mwifiex_set_tpc_params(priv, bss_cfg, params); - - if (mwifiex_is_11h_active(priv) && - !cfg80211_chandef_dfs_required(wiphy, ¶ms->chandef, - priv->bss_mode)) { - mwifiex_dbg(priv->adapter, INFO, - "Disable 11h extensions in FW\n"); - if (mwifiex_11h_activate(priv, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to disable 11h extensions!!"); - return -1; - } - priv->state_11h.is_11h_active = false; - } - - if (mwifiex_config_start_uap(priv, bss_cfg)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to start AP\n"); - kfree(bss_cfg); - return -1; - } - - if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon)) - return -1; - - if (!netif_carrier_ok(priv->netdev)) - netif_carrier_on(priv->netdev); - mwifiex_wake_up_net_dev_queue(priv->netdev, priv->adapter); - - memcpy(&priv->bss_cfg, bss_cfg, sizeof(priv->bss_cfg)); - kfree(bss_cfg); - return 0; -} - -/* - * CFG802.11 operation handler for disconnection request. - * - * This function does not work when there is already a disconnection - * procedure going on. - */ -static int -mwifiex_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev, - u16 reason_code) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (mwifiex_deauthenticate(priv, NULL)) - return -EFAULT; - - mwifiex_dbg(priv->adapter, MSG, - "info: successfully disconnected from %pM:\t" - "reason code %d\n", priv->cfg_bssid, reason_code); - - eth_zero_addr(priv->cfg_bssid); - priv->hs2_enabled = false; - - return 0; -} - -/* - * This function informs the CFG802.11 subsystem of a new IBSS. - * - * The following information are sent to the CFG802.11 subsystem - * to register the new IBSS. If we do not register the new IBSS, - * a kernel panic will result. - * - SSID - * - SSID length - * - BSSID - * - Channel - */ -static int mwifiex_cfg80211_inform_ibss_bss(struct mwifiex_private *priv) -{ - struct ieee80211_channel *chan; - struct mwifiex_bss_info bss_info; - struct cfg80211_bss *bss; - int ie_len; - u8 ie_buf[IEEE80211_MAX_SSID_LEN + sizeof(struct ieee_types_header)]; - enum ieee80211_band band; - - if (mwifiex_get_bss_info(priv, &bss_info)) - return -1; - - ie_buf[0] = WLAN_EID_SSID; - ie_buf[1] = bss_info.ssid.ssid_len; - - memcpy(&ie_buf[sizeof(struct ieee_types_header)], - &bss_info.ssid.ssid, bss_info.ssid.ssid_len); - ie_len = ie_buf[1] + sizeof(struct ieee_types_header); - - band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - chan = __ieee80211_get_channel(priv->wdev.wiphy, - ieee80211_channel_to_frequency(bss_info.bss_chan, - band)); - - bss = cfg80211_inform_bss(priv->wdev.wiphy, chan, - CFG80211_BSS_FTYPE_UNKNOWN, - bss_info.bssid, 0, WLAN_CAPABILITY_IBSS, - 0, ie_buf, ie_len, 0, GFP_KERNEL); - if (bss) { - cfg80211_put_bss(priv->wdev.wiphy, bss); - ether_addr_copy(priv->cfg_bssid, bss_info.bssid); - } - - return 0; -} - -/* - * This function connects with a BSS. - * - * This function handles both Infra and Ad-Hoc modes. It also performs - * validity checking on the provided parameters, disconnects from the - * current BSS (if any), sets up the association/scan parameters, - * including security settings, and performs specific SSID scan before - * trying to connect. - * - * For Infra mode, the function returns failure if the specified SSID - * is not found in scan table. However, for Ad-Hoc mode, it can create - * the IBSS if it does not exist. On successful completion in either case, - * the function notifies the CFG802.11 subsystem of the new BSS connection. - */ -static int -mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, - const u8 *ssid, const u8 *bssid, int mode, - struct ieee80211_channel *channel, - struct cfg80211_connect_params *sme, bool privacy) -{ - struct cfg80211_ssid req_ssid; - int ret, auth_type = 0; - struct cfg80211_bss *bss = NULL; - u8 is_scanning_required = 0; - - memset(&req_ssid, 0, sizeof(struct cfg80211_ssid)); - - req_ssid.ssid_len = ssid_len; - if (ssid_len > IEEE80211_MAX_SSID_LEN) { - mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n"); - return -EINVAL; - } - - memcpy(req_ssid.ssid, ssid, ssid_len); - if (!req_ssid.ssid_len || req_ssid.ssid[0] < 0x20) { - mwifiex_dbg(priv->adapter, ERROR, "invalid SSID - aborting\n"); - return -EINVAL; - } - - /* As this is new association, clear locally stored - * keys and security related flags */ - priv->sec_info.wpa_enabled = false; - priv->sec_info.wpa2_enabled = false; - priv->wep_key_curr_index = 0; - priv->sec_info.encryption_mode = 0; - priv->sec_info.is_authtype_auto = 0; - ret = mwifiex_set_encode(priv, NULL, NULL, 0, 0, NULL, 1); - - if (mode == NL80211_IFTYPE_ADHOC) { - /* "privacy" is set only for ad-hoc mode */ - if (privacy) { - /* - * Keep WLAN_CIPHER_SUITE_WEP104 for now so that - * the firmware can find a matching network from the - * scan. The cfg80211 does not give us the encryption - * mode at this stage so just setting it to WEP here. - */ - priv->sec_info.encryption_mode = - WLAN_CIPHER_SUITE_WEP104; - priv->sec_info.authentication_mode = - NL80211_AUTHTYPE_OPEN_SYSTEM; - } - - goto done; - } - - /* Now handle infra mode. "sme" is valid for infra mode only */ - if (sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) { - auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; - priv->sec_info.is_authtype_auto = 1; - } else { - auth_type = sme->auth_type; - } - - if (sme->crypto.n_ciphers_pairwise) { - priv->sec_info.encryption_mode = - sme->crypto.ciphers_pairwise[0]; - priv->sec_info.authentication_mode = auth_type; - } - - if (sme->crypto.cipher_group) { - priv->sec_info.encryption_mode = sme->crypto.cipher_group; - priv->sec_info.authentication_mode = auth_type; - } - if (sme->ie) - ret = mwifiex_set_gen_ie(priv, sme->ie, sme->ie_len); - - if (sme->key) { - if (mwifiex_is_alg_wep(priv->sec_info.encryption_mode)) { - mwifiex_dbg(priv->adapter, INFO, - "info: setting wep encryption\t" - "with key len %d\n", sme->key_len); - priv->wep_key_curr_index = sme->key_idx; - ret = mwifiex_set_encode(priv, NULL, sme->key, - sme->key_len, sme->key_idx, - NULL, 0); - } - } -done: - /* - * Scan entries are valid for some time (15 sec). So we can save one - * active scan time if we just try cfg80211_get_bss first. If it fails - * then request scan and cfg80211_get_bss() again for final output. - */ - while (1) { - if (is_scanning_required) { - /* Do specific SSID scanning */ - if (mwifiex_request_scan(priv, &req_ssid)) { - mwifiex_dbg(priv->adapter, ERROR, "scan error\n"); - return -EFAULT; - } - } - - /* Find the BSS we want using available scan results */ - if (mode == NL80211_IFTYPE_ADHOC) - bss = cfg80211_get_bss(priv->wdev.wiphy, channel, - bssid, ssid, ssid_len, - IEEE80211_BSS_TYPE_IBSS, - IEEE80211_PRIVACY_ANY); - else - bss = cfg80211_get_bss(priv->wdev.wiphy, channel, - bssid, ssid, ssid_len, - IEEE80211_BSS_TYPE_ESS, - IEEE80211_PRIVACY_ANY); - - if (!bss) { - if (is_scanning_required) { - mwifiex_dbg(priv->adapter, WARN, - "assoc: requested bss not found in scan results\n"); - break; - } - is_scanning_required = 1; - } else { - mwifiex_dbg(priv->adapter, MSG, - "info: trying to associate to '%s' bssid %pM\n", - (char *)req_ssid.ssid, bss->bssid); - memcpy(&priv->cfg_bssid, bss->bssid, ETH_ALEN); - break; - } - } - - ret = mwifiex_bss_start(priv, bss, &req_ssid); - if (ret) - return ret; - - if (mode == NL80211_IFTYPE_ADHOC) { - /* Inform the BSS information to kernel, otherwise - * kernel will give a panic after successful assoc */ - if (mwifiex_cfg80211_inform_ibss_bss(priv)) - return -EFAULT; - } - - return ret; -} - -/* - * CFG802.11 operation handler for association request. - * - * This function does not work when the current mode is set to Ad-Hoc, or - * when there is already an association procedure going on. The given BSS - * information is used to associate. - */ -static int -mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_connect_params *sme) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - - if (GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) { - mwifiex_dbg(adapter, ERROR, - "%s: reject infra assoc request in non-STA role\n", - dev->name); - return -EINVAL; - } - - if (priv->wdev.current_bss) { - mwifiex_dbg(adapter, ERROR, - "%s: already connected\n", dev->name); - return -EALREADY; - } - - if (adapter->surprise_removed || adapter->is_cmd_timedout) { - mwifiex_dbg(adapter, ERROR, - "%s: Ignore connection.\t" - "Card removed or FW in bad state\n", - dev->name); - return -EFAULT; - } - - mwifiex_dbg(adapter, INFO, - "info: Trying to associate to %s and bssid %pM\n", - (char *)sme->ssid, sme->bssid); - - ret = mwifiex_cfg80211_assoc(priv, sme->ssid_len, sme->ssid, sme->bssid, - priv->bss_mode, sme->channel, sme, 0); - if (!ret) { - cfg80211_connect_result(priv->netdev, priv->cfg_bssid, NULL, 0, - NULL, 0, WLAN_STATUS_SUCCESS, - GFP_KERNEL); - mwifiex_dbg(priv->adapter, MSG, - "info: associated to bssid %pM successfully\n", - priv->cfg_bssid); - if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->adapter->auto_tdls && - priv->bss_type == MWIFIEX_BSS_TYPE_STA) - mwifiex_setup_auto_tdls_timer(priv); - } else { - mwifiex_dbg(priv->adapter, ERROR, - "info: association to bssid %pM failed\n", - priv->cfg_bssid); - eth_zero_addr(priv->cfg_bssid); - - if (ret > 0) - cfg80211_connect_result(priv->netdev, priv->cfg_bssid, - NULL, 0, NULL, 0, ret, - GFP_KERNEL); - else - cfg80211_connect_result(priv->netdev, priv->cfg_bssid, - NULL, 0, NULL, 0, - WLAN_STATUS_UNSPECIFIED_FAILURE, - GFP_KERNEL); - } - - return 0; -} - -/* - * This function sets following parameters for ibss network. - * - channel - * - start band - * - 11n flag - * - secondary channel offset - */ -static int mwifiex_set_ibss_params(struct mwifiex_private *priv, - struct cfg80211_ibss_params *params) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int index = 0, i; - u8 config_bands = 0; - - if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { - if (!params->basic_rates) { - config_bands = BAND_B | BAND_G; - } else { - for (i = 0; i < mwifiex_band_2ghz.n_bitrates; i++) { - /* - * Rates below 6 Mbps in the table are CCK - * rates; 802.11b and from 6 they are OFDM; - * 802.11G - */ - if (mwifiex_rates[i].bitrate == 60) { - index = 1 << i; - break; - } - } - - if (params->basic_rates < index) { - config_bands = BAND_B; - } else { - config_bands = BAND_G; - if (params->basic_rates % index) - config_bands |= BAND_B; - } - } - - if (cfg80211_get_chandef_type(¶ms->chandef) != - NL80211_CHAN_NO_HT) - config_bands |= BAND_G | BAND_GN; - } else { - if (cfg80211_get_chandef_type(¶ms->chandef) == - NL80211_CHAN_NO_HT) - config_bands = BAND_A; - else - config_bands = BAND_AN | BAND_A; - } - - if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) { - adapter->config_bands = config_bands; - adapter->adhoc_start_band = config_bands; - - if ((config_bands & BAND_GN) || (config_bands & BAND_AN)) - adapter->adhoc_11n_enabled = true; - else - adapter->adhoc_11n_enabled = false; - } - - adapter->sec_chan_offset = - mwifiex_chan_type_to_sec_chan_offset( - cfg80211_get_chandef_type(¶ms->chandef)); - priv->adhoc_channel = ieee80211_frequency_to_channel( - params->chandef.chan->center_freq); - - mwifiex_dbg(adapter, INFO, - "info: set ibss band %d, chan %d, chan offset %d\n", - config_bands, priv->adhoc_channel, - adapter->sec_chan_offset); - - return 0; -} - -/* - * CFG802.11 operation handler to join an IBSS. - * - * This function does not work in any mode other than Ad-Hoc, or if - * a join operation is already in progress. - */ -static int -mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_ibss_params *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int ret = 0; - - if (priv->bss_mode != NL80211_IFTYPE_ADHOC) { - mwifiex_dbg(priv->adapter, ERROR, - "request to join ibss received\t" - "when station is not in ibss mode\n"); - goto done; - } - - mwifiex_dbg(priv->adapter, MSG, - "info: trying to join to %s and bssid %pM\n", - (char *)params->ssid, params->bssid); - - mwifiex_set_ibss_params(priv, params); - - ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, - params->bssid, priv->bss_mode, - params->chandef.chan, NULL, - params->privacy); -done: - if (!ret) { - cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, - params->chandef.chan, GFP_KERNEL); - mwifiex_dbg(priv->adapter, MSG, - "info: joined/created adhoc network with bssid\t" - "%pM successfully\n", priv->cfg_bssid); - } else { - mwifiex_dbg(priv->adapter, ERROR, - "info: failed creating/joining adhoc network\n"); - } - - return ret; -} - -/* - * CFG802.11 operation handler to leave an IBSS. - * - * This function does not work if a leave operation is - * already in progress. - */ -static int -mwifiex_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - mwifiex_dbg(priv->adapter, MSG, "info: disconnecting from essid %pM\n", - priv->cfg_bssid); - if (mwifiex_deauthenticate(priv, NULL)) - return -EFAULT; - - eth_zero_addr(priv->cfg_bssid); - - return 0; -} - -/* - * CFG802.11 operation handler for scan request. - * - * This function issues a scan request to the firmware based upon - * the user specified scan configuration. On successful completion, - * it also informs the results. - */ -static int -mwifiex_cfg80211_scan(struct wiphy *wiphy, - struct cfg80211_scan_request *request) -{ - struct net_device *dev = request->wdev->netdev; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int i, offset, ret; - struct ieee80211_channel *chan; - struct ieee_types_header *ie; - struct mwifiex_user_scan_cfg *user_scan_cfg; - - mwifiex_dbg(priv->adapter, CMD, - "info: received scan request on %s\n", dev->name); - - /* Block scan request if scan operation or scan cleanup when interface - * is disabled is in process - */ - if (priv->scan_request || priv->scan_aborting) { - mwifiex_dbg(priv->adapter, WARN, - "cmd: Scan already in process..\n"); - return -EBUSY; - } - - user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); - if (!user_scan_cfg) - return -ENOMEM; - - priv->scan_request = request; - - user_scan_cfg->num_ssids = request->n_ssids; - user_scan_cfg->ssid_list = request->ssids; - - if (request->ie && request->ie_len) { - offset = 0; - for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { - if (priv->vs_ie[i].mask != MWIFIEX_VSIE_MASK_CLEAR) - continue; - priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_SCAN; - ie = (struct ieee_types_header *)(request->ie + offset); - memcpy(&priv->vs_ie[i].ie, ie, sizeof(*ie) + ie->len); - offset += sizeof(*ie) + ie->len; - - if (offset >= request->ie_len) - break; - } - } - - for (i = 0; i < min_t(u32, request->n_channels, - MWIFIEX_USER_SCAN_CHAN_MAX); i++) { - chan = request->channels[i]; - user_scan_cfg->chan_list[i].chan_number = chan->hw_value; - user_scan_cfg->chan_list[i].radio_type = chan->band; - - if ((chan->flags & IEEE80211_CHAN_NO_IR) || !request->n_ssids) - user_scan_cfg->chan_list[i].scan_type = - MWIFIEX_SCAN_TYPE_PASSIVE; - else - user_scan_cfg->chan_list[i].scan_type = - MWIFIEX_SCAN_TYPE_ACTIVE; - - user_scan_cfg->chan_list[i].scan_time = 0; - } - - if (priv->adapter->scan_chan_gap_enabled && - mwifiex_is_any_intf_active(priv)) - user_scan_cfg->scan_chan_gap = - priv->adapter->scan_chan_gap_time; - - ret = mwifiex_scan_networks(priv, user_scan_cfg); - kfree(user_scan_cfg); - if (ret) { - mwifiex_dbg(priv->adapter, ERROR, - "scan failed: %d\n", ret); - priv->scan_aborting = false; - priv->scan_request = NULL; - return ret; - } - - if (request->ie && request->ie_len) { - for (i = 0; i < MWIFIEX_MAX_VSIE_NUM; i++) { - if (priv->vs_ie[i].mask == MWIFIEX_VSIE_MASK_SCAN) { - priv->vs_ie[i].mask = MWIFIEX_VSIE_MASK_CLEAR; - memset(&priv->vs_ie[i].ie, 0, - MWIFIEX_MAX_VSIE_LEN); - } - } - } - return 0; -} - -static void mwifiex_setup_vht_caps(struct ieee80211_sta_vht_cap *vht_info, - struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - vht_info->vht_supported = true; - - vht_info->cap = adapter->hw_dot_11ac_dev_cap; - /* Update MCS support for VHT */ - vht_info->vht_mcs.rx_mcs_map = cpu_to_le16( - adapter->hw_dot_11ac_mcs_support & 0xFFFF); - vht_info->vht_mcs.rx_highest = 0; - vht_info->vht_mcs.tx_mcs_map = cpu_to_le16( - adapter->hw_dot_11ac_mcs_support >> 16); - vht_info->vht_mcs.tx_highest = 0; -} - -/* - * This function sets up the CFG802.11 specific HT capability fields - * with default values. - * - * The following default values are set - - * - HT Supported = True - * - Maximum AMPDU length factor = IEEE80211_HT_MAX_AMPDU_64K - * - Minimum AMPDU spacing = IEEE80211_HT_MPDU_DENSITY_NONE - * - HT Capabilities supported by firmware - * - MCS information, Rx mask = 0xff - * - MCD information, Tx parameters = IEEE80211_HT_MCS_TX_DEFINED (0x01) - */ -static void -mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, - struct mwifiex_private *priv) -{ - int rx_mcs_supp; - struct ieee80211_mcs_info mcs_set; - u8 *mcs = (u8 *)&mcs_set; - struct mwifiex_adapter *adapter = priv->adapter; - - ht_info->ht_supported = true; - ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; - ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; - - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - - /* Fill HT capability information */ - if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; - else - ht_info->cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - - if (ISSUPP_SHORTGI20(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_SGI_20; - else - ht_info->cap &= ~IEEE80211_HT_CAP_SGI_20; - - if (ISSUPP_SHORTGI40(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_SGI_40; - else - ht_info->cap &= ~IEEE80211_HT_CAP_SGI_40; - - if (adapter->user_dev_mcs_support == HT_STREAM_2X2) - ht_info->cap |= 3 << IEEE80211_HT_CAP_RX_STBC_SHIFT; - else - ht_info->cap |= 1 << IEEE80211_HT_CAP_RX_STBC_SHIFT; - - if (ISSUPP_TXSTBC(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; - else - ht_info->cap &= ~IEEE80211_HT_CAP_TX_STBC; - - if (ISSUPP_GREENFIELD(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; - else - ht_info->cap &= ~IEEE80211_HT_CAP_GRN_FLD; - - if (ISENABLED_40MHZ_INTOLERANT(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; - else - ht_info->cap &= ~IEEE80211_HT_CAP_40MHZ_INTOLERANT; - - if (ISSUPP_RXLDPC(adapter->hw_dot_11n_dev_cap)) - ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; - else - ht_info->cap &= ~IEEE80211_HT_CAP_LDPC_CODING; - - ht_info->cap &= ~IEEE80211_HT_CAP_MAX_AMSDU; - ht_info->cap |= IEEE80211_HT_CAP_SM_PS; - - rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support); - /* Set MCS for 1x1/2x2 */ - memset(mcs, 0xff, rx_mcs_supp); - /* Clear all the other values */ - memset(&mcs[rx_mcs_supp], 0, - sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); - if (priv->bss_mode == NL80211_IFTYPE_STATION || - ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) - /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ - SETHT_MCS32(mcs_set.rx_mask); - - memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info)); - - ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; -} - -/* - * create a new virtual interface with the given name and name assign type - */ -struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, - const char *name, - unsigned char name_assign_type, - enum nl80211_iftype type, - u32 *flags, - struct vif_params *params) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_private *priv; - struct net_device *dev; - void *mdev_priv; - - if (!adapter) - return ERR_PTR(-EFAULT); - - switch (type) { - case NL80211_IFTYPE_UNSPECIFIED: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - if (adapter->curr_iface_comb.sta_intf == - adapter->iface_limit.sta_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple sta/adhoc ifaces\n"); - return ERR_PTR(-EINVAL); - } - - priv = mwifiex_get_unused_priv(adapter); - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "could not get free private struct\n"); - return ERR_PTR(-EFAULT); - } - - priv->wdev.wiphy = wiphy; - priv->wdev.iftype = NL80211_IFTYPE_STATION; - - if (type == NL80211_IFTYPE_UNSPECIFIED) - priv->bss_mode = NL80211_IFTYPE_STATION; - else - priv->bss_mode = type; - - priv->bss_type = MWIFIEX_BSS_TYPE_STA; - priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = 0; - priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_num = adapter->curr_iface_comb.sta_intf; - - break; - case NL80211_IFTYPE_AP: - if (adapter->curr_iface_comb.uap_intf == - adapter->iface_limit.uap_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple AP ifaces\n"); - return ERR_PTR(-EINVAL); - } - - priv = mwifiex_get_unused_priv(adapter); - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "could not get free private struct\n"); - return ERR_PTR(-EFAULT); - } - - priv->wdev.wiphy = wiphy; - priv->wdev.iftype = NL80211_IFTYPE_AP; - - priv->bss_type = MWIFIEX_BSS_TYPE_UAP; - priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = 0; - priv->bss_role = MWIFIEX_BSS_ROLE_UAP; - priv->bss_started = 0; - priv->bss_num = adapter->curr_iface_comb.uap_intf; - priv->bss_mode = type; - - break; - case NL80211_IFTYPE_P2P_CLIENT: - if (adapter->curr_iface_comb.p2p_intf == - adapter->iface_limit.p2p_intf) { - mwifiex_dbg(adapter, ERROR, - "cannot create multiple P2P ifaces\n"); - return ERR_PTR(-EINVAL); - } - - priv = mwifiex_get_unused_priv(adapter); - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "could not get free private struct\n"); - return ERR_PTR(-EFAULT); - } - - priv->wdev.wiphy = wiphy; - /* At start-up, wpa_supplicant tries to change the interface - * to NL80211_IFTYPE_STATION if it is not managed mode. - */ - priv->wdev.iftype = NL80211_IFTYPE_P2P_CLIENT; - priv->bss_mode = NL80211_IFTYPE_P2P_CLIENT; - - /* Setting bss_type to P2P tells firmware that this interface - * is receiving P2P peers found during find phase and doing - * action frame handshake. - */ - priv->bss_type = MWIFIEX_BSS_TYPE_P2P; - - priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = MWIFIEX_BSS_ROLE_STA; - priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_started = 0; - priv->bss_num = adapter->curr_iface_comb.p2p_intf; - - if (mwifiex_cfg80211_init_p2p_client(priv)) { - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-EFAULT); - } - - break; - default: - mwifiex_dbg(adapter, ERROR, "type not supported\n"); - return ERR_PTR(-EINVAL); - } - - dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, - name_assign_type, ether_setup, - IEEE80211_NUM_ACS, 1); - if (!dev) { - mwifiex_dbg(adapter, ERROR, - "no memory available for netdevice\n"); - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-ENOMEM); - } - - mwifiex_init_priv_params(priv, dev); - priv->netdev = dev; - - mwifiex_setup_ht_caps(&wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap, priv); - if (adapter->is_hw_11ac_capable) - mwifiex_setup_vht_caps( - &wiphy->bands[IEEE80211_BAND_2GHZ]->vht_cap, priv); - - if (adapter->config_bands & BAND_A) - mwifiex_setup_ht_caps( - &wiphy->bands[IEEE80211_BAND_5GHZ]->ht_cap, priv); - - if ((adapter->config_bands & BAND_A) && adapter->is_hw_11ac_capable) - mwifiex_setup_vht_caps( - &wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap, priv); - - dev_net_set(dev, wiphy_net(wiphy)); - dev->ieee80211_ptr = &priv->wdev; - dev->ieee80211_ptr->iftype = priv->bss_mode; - memcpy(dev->dev_addr, wiphy->perm_addr, ETH_ALEN); - SET_NETDEV_DEV(dev, wiphy_dev(wiphy)); - - dev->flags |= IFF_BROADCAST | IFF_MULTICAST; - dev->watchdog_timeo = MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT; - dev->hard_header_len += MWIFIEX_MIN_DATA_HEADER_LEN; - dev->ethtool_ops = &mwifiex_ethtool_ops; - - mdev_priv = netdev_priv(dev); - *((unsigned long *) mdev_priv) = (unsigned long) priv; - - SET_NETDEV_DEV(dev, adapter->dev); - - /* Register network device */ - if (register_netdevice(dev)) { - mwifiex_dbg(adapter, ERROR, - "cannot register virtual network device\n"); - free_netdev(dev); - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->netdev = NULL; - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-EFAULT); - } - - priv->dfs_cac_workqueue = alloc_workqueue("MWIFIEX_DFS_CAC%s", - WQ_HIGHPRI | - WQ_MEM_RECLAIM | - WQ_UNBOUND, 1, name); - if (!priv->dfs_cac_workqueue) { - mwifiex_dbg(adapter, ERROR, - "cannot register virtual network device\n"); - free_netdev(dev); - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->netdev = NULL; - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-ENOMEM); - } - - INIT_DELAYED_WORK(&priv->dfs_cac_work, mwifiex_dfs_cac_work_queue); - - priv->dfs_chan_sw_workqueue = alloc_workqueue("MWIFIEX_DFS_CHSW%s", - WQ_HIGHPRI | WQ_UNBOUND | - WQ_MEM_RECLAIM, 1, name); - if (!priv->dfs_chan_sw_workqueue) { - mwifiex_dbg(adapter, ERROR, - "cannot register virtual network device\n"); - free_netdev(dev); - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->netdev = NULL; - memset(&priv->wdev, 0, sizeof(priv->wdev)); - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - return ERR_PTR(-ENOMEM); - } - - INIT_DELAYED_WORK(&priv->dfs_chan_sw_work, - mwifiex_dfs_chan_sw_work_queue); - - sema_init(&priv->async_sem, 1); - - mwifiex_dbg(adapter, INFO, - "info: %s: Marvell 802.11 Adapter\n", dev->name); - -#ifdef CONFIG_DEBUG_FS - mwifiex_dev_debugfs_init(priv); -#endif - - switch (type) { - case NL80211_IFTYPE_UNSPECIFIED: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf++; - break; - case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf++; - break; - case NL80211_IFTYPE_P2P_CLIENT: - adapter->curr_iface_comb.p2p_intf++; - break; - default: - mwifiex_dbg(adapter, ERROR, "type not supported\n"); - return ERR_PTR(-EINVAL); - } - - return &priv->wdev; -} -EXPORT_SYMBOL_GPL(mwifiex_add_virtual_intf); - -/* - * del_virtual_intf: remove the virtual interface determined by dev - */ -int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - struct mwifiex_adapter *adapter = priv->adapter; - struct sk_buff *skb, *tmp; - -#ifdef CONFIG_DEBUG_FS - mwifiex_dev_debugfs_remove(priv); -#endif - - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - - skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) - mwifiex_write_data_complete(priv->adapter, skb, 0, -1); - - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - - if (wdev->netdev->reg_state == NETREG_REGISTERED) - unregister_netdevice(wdev->netdev); - - if (priv->dfs_cac_workqueue) { - flush_workqueue(priv->dfs_cac_workqueue); - destroy_workqueue(priv->dfs_cac_workqueue); - priv->dfs_cac_workqueue = NULL; - } - - if (priv->dfs_chan_sw_workqueue) { - flush_workqueue(priv->dfs_chan_sw_workqueue); - destroy_workqueue(priv->dfs_chan_sw_workqueue); - priv->dfs_chan_sw_workqueue = NULL; - } - /* Clear the priv in adapter */ - priv->netdev->ieee80211_ptr = NULL; - priv->netdev = NULL; - priv->wdev.iftype = NL80211_IFTYPE_UNSPECIFIED; - - priv->media_connected = false; - - switch (priv->bss_mode) { - case NL80211_IFTYPE_UNSPECIFIED: - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - adapter->curr_iface_comb.sta_intf--; - break; - case NL80211_IFTYPE_AP: - adapter->curr_iface_comb.uap_intf--; - break; - case NL80211_IFTYPE_P2P_CLIENT: - case NL80211_IFTYPE_P2P_GO: - adapter->curr_iface_comb.p2p_intf--; - break; - default: - mwifiex_dbg(adapter, ERROR, - "del_virtual_intf: type not supported\n"); - break; - } - - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || - GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) - kfree(priv->hist_data); - - return 0; -} -EXPORT_SYMBOL_GPL(mwifiex_del_virtual_intf); - -static bool -mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq, - u8 max_byte_seq) -{ - int j, k, valid_byte_cnt = 0; - bool dont_care_byte = false; - - for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { - for (k = 0; k < 8; k++) { - if (pat->mask[j] & 1 << k) { - memcpy(byte_seq + valid_byte_cnt, - &pat->pattern[j * 8 + k], 1); - valid_byte_cnt++; - if (dont_care_byte) - return false; - } else { - if (valid_byte_cnt) - dont_care_byte = true; - } - - if (valid_byte_cnt > max_byte_seq) - return false; - } - } - - byte_seq[max_byte_seq] = valid_byte_cnt; - - return true; -} - -#ifdef CONFIG_PM -static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv, - struct mwifiex_mef_entry *mef_entry) -{ - int i, filt_num = 0, num_ipv4 = 0; - struct in_device *in_dev; - struct in_ifaddr *ifa; - __be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR]; - struct mwifiex_adapter *adapter = priv->adapter; - - mef_entry->mode = MEF_MODE_HOST_SLEEP; - mef_entry->action = MEF_ACTION_AUTO_ARP; - - /* Enable ARP offload feature */ - memset(ips, 0, sizeof(ips)); - for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { - if (adapter->priv[i]->netdev) { - in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev); - if (!in_dev) - continue; - ifa = in_dev->ifa_list; - if (!ifa || !ifa->ifa_local) - continue; - ips[i] = ifa->ifa_local; - num_ipv4++; - } - } - - for (i = 0; i < num_ipv4; i++) { - if (!ips[i]) - continue; - mef_entry->filter[filt_num].repeat = 1; - memcpy(mef_entry->filter[filt_num].byte_seq, - (u8 *)&ips[i], sizeof(ips[i])); - mef_entry->filter[filt_num]. - byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - sizeof(ips[i]); - mef_entry->filter[filt_num].offset = 46; - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - if (filt_num) { - mef_entry->filter[filt_num].filt_action = - TYPE_OR; - } - filt_num++; - } - - mef_entry->filter[filt_num].repeat = 1; - mef_entry->filter[filt_num].byte_seq[0] = 0x08; - mef_entry->filter[filt_num].byte_seq[1] = 0x06; - mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2; - mef_entry->filter[filt_num].offset = 20; - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - mef_entry->filter[filt_num].filt_action = TYPE_AND; -} - -static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv, - struct mwifiex_ds_mef_cfg *mef_cfg, - struct mwifiex_mef_entry *mef_entry, - struct cfg80211_wowlan *wowlan) -{ - int i, filt_num = 0, ret = 0; - bool first_pat = true; - u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; - const u8 ipv4_mc_mac[] = {0x33, 0x33}; - const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; - - mef_entry->mode = MEF_MODE_HOST_SLEEP; - mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST; - - for (i = 0; i < wowlan->n_patterns; i++) { - memset(byte_seq, 0, sizeof(byte_seq)); - if (!mwifiex_is_pattern_supported(&wowlan->patterns[i], - byte_seq, - MWIFIEX_MEF_MAX_BYTESEQ)) { - mwifiex_dbg(priv->adapter, ERROR, - "Pattern not supported\n"); - return -EOPNOTSUPP; - } - - if (!wowlan->patterns[i].pkt_offset) { - if (!(byte_seq[0] & 0x01) && - (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) { - mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; - continue; - } else if (is_broadcast_ether_addr(byte_seq)) { - mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST; - continue; - } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && - (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) || - (!memcmp(byte_seq, ipv6_mc_mac, 3) && - (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) { - mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST; - continue; - } - } - mef_entry->filter[filt_num].repeat = 1; - mef_entry->filter[filt_num].offset = - wowlan->patterns[i].pkt_offset; - memcpy(mef_entry->filter[filt_num].byte_seq, byte_seq, - sizeof(byte_seq)); - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - - if (first_pat) - first_pat = false; - else - mef_entry->filter[filt_num].filt_action = TYPE_AND; - - filt_num++; - } - - if (wowlan->magic_pkt) { - mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST; - mef_entry->filter[filt_num].repeat = 16; - memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, - ETH_ALEN); - mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - ETH_ALEN; - mef_entry->filter[filt_num].offset = 28; - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - if (filt_num) - mef_entry->filter[filt_num].filt_action = TYPE_OR; - - filt_num++; - mef_entry->filter[filt_num].repeat = 16; - memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr, - ETH_ALEN); - mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = - ETH_ALEN; - mef_entry->filter[filt_num].offset = 56; - mef_entry->filter[filt_num].filt_type = TYPE_EQ; - mef_entry->filter[filt_num].filt_action = TYPE_OR; - } - return ret; -} - -static int mwifiex_set_mef_filter(struct mwifiex_private *priv, - struct cfg80211_wowlan *wowlan) -{ - int ret = 0, num_entries = 1; - struct mwifiex_ds_mef_cfg mef_cfg; - struct mwifiex_mef_entry *mef_entry; - - if (wowlan->n_patterns || wowlan->magic_pkt) - num_entries++; - - mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL); - if (!mef_entry) - return -ENOMEM; - - memset(&mef_cfg, 0, sizeof(mef_cfg)); - mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST | - MWIFIEX_CRITERIA_UNICAST; - mef_cfg.num_entries = num_entries; - mef_cfg.mef_entry = mef_entry; - - mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]); - - if (wowlan->n_patterns || wowlan->magic_pkt) { - ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg, - &mef_entry[1], wowlan); - if (ret) - goto err; - } - - if (!mef_cfg.criteria) - mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST | - MWIFIEX_CRITERIA_UNICAST | - MWIFIEX_CRITERIA_MULTICAST; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG, - HostCmd_ACT_GEN_SET, 0, - &mef_cfg, true); - -err: - kfree(mef_entry); - return ret; -} - -static int mwifiex_cfg80211_suspend(struct wiphy *wiphy, - struct cfg80211_wowlan *wowlan) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - struct mwifiex_ds_hs_cfg hs_cfg; - int i, ret = 0; - struct mwifiex_private *priv; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - mwifiex_abort_cac(priv); - } - - mwifiex_cancel_all_pending_cmd(adapter); - - if (!wowlan) { - mwifiex_dbg(adapter, ERROR, - "None of the WOWLAN triggers enabled\n"); - return 0; - } - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); - - if (!priv->media_connected) { - mwifiex_dbg(adapter, ERROR, - "Can not configure WOWLAN in disconnected state\n"); - return 0; - } - - ret = mwifiex_set_mef_filter(priv, wowlan); - if (ret) { - mwifiex_dbg(adapter, ERROR, "Failed to set MEF filter\n"); - return ret; - } - - if (wowlan->disconnect) { - memset(&hs_cfg, 0, sizeof(hs_cfg)); - hs_cfg.is_invoke_hostcmd = false; - hs_cfg.conditions = HS_CFG_COND_MAC_EVENT; - hs_cfg.gpio = HS_CFG_GPIO_DEF; - hs_cfg.gap = HS_CFG_GAP_DEF; - ret = mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, - MWIFIEX_SYNC_CMD, &hs_cfg); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "Failed to set HS params\n"); - return ret; - } - } - - return ret; -} - -static int mwifiex_cfg80211_resume(struct wiphy *wiphy) -{ - return 0; -} - -static void mwifiex_cfg80211_set_wakeup(struct wiphy *wiphy, - bool enabled) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - - device_set_wakeup_enable(adapter->dev, enabled); -} -#endif - -static int mwifiex_get_coalesce_pkt_type(u8 *byte_seq) -{ - const u8 ipv4_mc_mac[] = {0x33, 0x33}; - const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e}; - const u8 bc_mac[] = {0xff, 0xff, 0xff, 0xff}; - - if ((byte_seq[0] & 0x01) && - (byte_seq[MWIFIEX_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[MWIFIEX_COALESCE_MAX_BYTESEQ] == 2) || - (!memcmp(byte_seq, ipv6_mc_mac, 3) && - byte_seq[MWIFIEX_COALESCE_MAX_BYTESEQ] == 3)) - return PACKET_TYPE_MULTICAST; - - return 0; -} - -static int -mwifiex_fill_coalesce_rule_info(struct mwifiex_private *priv, - struct cfg80211_coalesce_rules *crule, - struct mwifiex_coalesce_rule *mrule) -{ - u8 byte_seq[MWIFIEX_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 (!mwifiex_is_pattern_supported(&crule->patterns[i], - byte_seq, - MWIFIEX_COALESCE_MAX_BYTESEQ)) { - mwifiex_dbg(priv->adapter, ERROR, - "Pattern not supported\n"); - return -EOPNOTSUPP; - } - - if (!crule->patterns[i].pkt_offset) { - u8 pkt_type; - - pkt_type = mwifiex_get_coalesce_pkt_type(byte_seq); - if (pkt_type && mrule->pkt_type) { - mwifiex_dbg(priv->adapter, ERROR, - "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[MWIFIEX_COALESCE_MAX_BYTESEQ]; - memcpy(param->operand_byte_stream, byte_seq, - param->operand_len); - param->offset = crule->patterns[i].pkt_offset; - param++; - - mrule->num_of_fields++; - } - - if (!mrule->pkt_type) { - mwifiex_dbg(priv->adapter, ERROR, - "Packet type can not be determined\n"); - return -EOPNOTSUPP; - } - - return 0; -} - -static int mwifiex_cfg80211_set_coalesce(struct wiphy *wiphy, - struct cfg80211_coalesce *coalesce) -{ - struct mwifiex_adapter *adapter = mwifiex_cfg80211_get_adapter(wiphy); - int i, ret; - struct mwifiex_ds_coalesce_cfg coalesce_cfg; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); - - memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); - if (!coalesce) { - mwifiex_dbg(adapter, WARN, - "Disable coalesce and reset all previous rules\n"); - return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, - HostCmd_ACT_GEN_SET, 0, - &coalesce_cfg, true); - } - - coalesce_cfg.num_of_rules = coalesce->n_rules; - for (i = 0; i < coalesce->n_rules; i++) { - ret = mwifiex_fill_coalesce_rule_info(priv, &coalesce->rules[i], - &coalesce_cfg.rule[i]); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "Recheck the patterns provided for rule %d\n", - i + 1); - return ret; - } - } - - return mwifiex_send_cmd(priv, HostCmd_CMD_COALESCE_CFG, - HostCmd_ACT_GEN_SET, 0, &coalesce_cfg, true); -} - -/* cfg80211 ops handler for tdls_mgmt. - * Function prepares TDLS action frame packets and forwards them to FW - */ -static int -mwifiex_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, u8 action_code, u8 dialog_token, - u16 status_code, u32 peer_capability, - bool initiator, const u8 *extra_ies, - size_t extra_ies_len) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - int ret; - - if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) - return -ENOTSUPP; - - /* make sure we are in station mode and connected */ - if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) - return -ENOTSUPP; - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Setup Request to %pM status_code=%d\n", - peer, status_code); - mwifiex_add_auto_tdls_peer(priv, peer); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_TDLS_SETUP_RESPONSE: - mwifiex_add_auto_tdls_peer(priv, peer); - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Setup Response to %pM status_code=%d\n", - peer, status_code); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_TDLS_SETUP_CONFIRM: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Confirm to %pM status_code=%d\n", peer, - status_code); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_TDLS_TEARDOWN: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Tear down to %pM\n", peer); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_TDLS_DISCOVERY_REQUEST: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Discovery Request to %pM\n", peer); - ret = mwifiex_send_tdls_data_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - mwifiex_dbg(priv->adapter, MSG, - "Send TDLS Discovery Response to %pM\n", peer); - ret = mwifiex_send_tdls_action_frame(priv, peer, action_code, - dialog_token, status_code, - extra_ies, extra_ies_len); - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "Unknown TDLS mgmt/action frame %pM\n", peer); - ret = -EINVAL; - break; - } - - return ret; -} - -static int -mwifiex_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, - const u8 *peer, enum nl80211_tdls_operation action) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS) || - !(wiphy->flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)) - return -ENOTSUPP; - - /* make sure we are in station mode and connected */ - if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) - return -ENOTSUPP; - - mwifiex_dbg(priv->adapter, MSG, - "TDLS peer=%pM, oper=%d\n", peer, action); - - switch (action) { - case NL80211_TDLS_ENABLE_LINK: - action = MWIFIEX_TDLS_ENABLE_LINK; - break; - case NL80211_TDLS_DISABLE_LINK: - action = MWIFIEX_TDLS_DISABLE_LINK; - break; - case NL80211_TDLS_TEARDOWN: - /* shouldn't happen!*/ - mwifiex_dbg(priv->adapter, ERROR, - "tdls_oper: teardown from driver not supported\n"); - return -EINVAL; - case NL80211_TDLS_SETUP: - /* shouldn't happen!*/ - mwifiex_dbg(priv->adapter, ERROR, - "tdls_oper: setup from driver not supported\n"); - return -EINVAL; - case NL80211_TDLS_DISCOVERY_REQ: - /* shouldn't happen!*/ - mwifiex_dbg(priv->adapter, ERROR, - "tdls_oper: discovery from driver not supported\n"); - return -EINVAL; - default: - mwifiex_dbg(priv->adapter, ERROR, - "tdls_oper: operation not supported\n"); - return -ENOTSUPP; - } - - return mwifiex_tdls_oper(priv, peer, action); -} - -static int -mwifiex_cfg80211_tdls_chan_switch(struct wiphy *wiphy, struct net_device *dev, - const u8 *addr, u8 oper_class, - struct cfg80211_chan_def *chandef) -{ - struct mwifiex_sta_node *sta_ptr; - unsigned long flags; - u16 chan; - u8 second_chan_offset, band; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, addr); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (!sta_ptr) { - wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", - __func__, addr); - return -ENOENT; - } - - if (!(sta_ptr->tdls_cap.extcap.ext_capab[3] & - WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH)) { - wiphy_err(wiphy, "%pM do not support tdls cs\n", addr); - return -ENOENT; - } - - if (sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || - sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) { - wiphy_err(wiphy, "channel switch is running, abort request\n"); - return -EALREADY; - } - - chan = chandef->chan->hw_value; - second_chan_offset = mwifiex_get_sec_chan_offset(chan); - band = chandef->chan->band; - mwifiex_start_tdls_cs(priv, addr, chan, second_chan_offset, band); - - return 0; -} - -static void -mwifiex_cfg80211_tdls_cancel_chan_switch(struct wiphy *wiphy, - struct net_device *dev, - const u8 *addr) -{ - struct mwifiex_sta_node *sta_ptr; - unsigned long flags; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, addr); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (!sta_ptr) { - wiphy_err(wiphy, "%s: Invalid TDLS peer %pM\n", - __func__, addr); - } else if (!(sta_ptr->tdls_status == TDLS_CHAN_SWITCHING || - sta_ptr->tdls_status == TDLS_IN_BASE_CHAN || - sta_ptr->tdls_status == TDLS_IN_OFF_CHAN)) { - wiphy_err(wiphy, "tdls chan switch not initialize by %pM\n", - addr); - } else - mwifiex_stop_tdls_cs(priv, addr); -} - -static int -mwifiex_cfg80211_add_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, struct station_parameters *params) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) - return -ENOTSUPP; - - /* make sure we are in station mode and connected */ - if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) - return -ENOTSUPP; - - return mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CREATE_LINK); -} - -static int -mwifiex_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, - struct cfg80211_csa_settings *params) -{ - struct ieee_types_header *chsw_ie; - struct ieee80211_channel_sw_ie *channel_sw; - int chsw_msec; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (priv->adapter->scan_processing) { - mwifiex_dbg(priv->adapter, ERROR, - "radar detection: scan in process...\n"); - return -EBUSY; - } - - if (priv->wdev.cac_started) - return -EBUSY; - - if (cfg80211_chandef_identical(¶ms->chandef, - &priv->dfs_chandef)) - return -EINVAL; - - chsw_ie = (void *)cfg80211_find_ie(WLAN_EID_CHANNEL_SWITCH, - params->beacon_csa.tail, - params->beacon_csa.tail_len); - if (!chsw_ie) { - mwifiex_dbg(priv->adapter, ERROR, - "Could not parse channel switch announcement IE\n"); - return -EINVAL; - } - - channel_sw = (void *)(chsw_ie + 1); - if (channel_sw->mode) { - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); - } - - if (mwifiex_del_mgmt_ies(priv)) - mwifiex_dbg(priv->adapter, ERROR, - "Failed to delete mgmt IEs!\n"); - - if (mwifiex_set_mgmt_ies(priv, ¶ms->beacon_csa)) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: setting mgmt ies failed\n", __func__); - return -EFAULT; - } - - memcpy(&priv->dfs_chandef, ¶ms->chandef, sizeof(priv->dfs_chandef)); - memcpy(&priv->beacon_after, ¶ms->beacon_after, - sizeof(priv->beacon_after)); - - chsw_msec = max(channel_sw->count * priv->bss_cfg.beacon_period, 100); - queue_delayed_work(priv->dfs_chan_sw_workqueue, &priv->dfs_chan_sw_work, - msecs_to_jiffies(chsw_msec)); - return 0; -} - -static int mwifiex_cfg80211_get_channel(struct wiphy *wiphy, - struct wireless_dev *wdev, - struct cfg80211_chan_def *chandef) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); - struct mwifiex_bssdescriptor *curr_bss; - struct ieee80211_channel *chan; - u8 second_chan_offset; - enum nl80211_channel_type chan_type; - enum ieee80211_band band; - int freq; - int ret = -ENODATA; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP && - cfg80211_chandef_valid(&priv->bss_chandef)) { - *chandef = priv->bss_chandef; - ret = 0; - } else if (priv->media_connected) { - curr_bss = &priv->curr_bss_params.bss_descriptor; - band = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - freq = ieee80211_channel_to_frequency(curr_bss->channel, band); - chan = ieee80211_get_channel(wiphy, freq); - - if (curr_bss->bcn_ht_oper) { - second_chan_offset = curr_bss->bcn_ht_oper->ht_param & - IEEE80211_HT_PARAM_CHA_SEC_OFFSET; - chan_type = mwifiex_sec_chan_offset_to_chan_type - (second_chan_offset); - cfg80211_chandef_create(chandef, chan, chan_type); - } else { - cfg80211_chandef_create(chandef, chan, - NL80211_CHAN_NO_HT); - } - ret = 0; - } - - return ret; -} - -static int -mwifiex_cfg80211_start_radar_detection(struct wiphy *wiphy, - struct net_device *dev, - struct cfg80211_chan_def *chandef, - u32 cac_time_ms) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_radar_params radar_params; - - if (priv->adapter->scan_processing) { - mwifiex_dbg(priv->adapter, ERROR, - "radar detection: scan already in process...\n"); - return -EBUSY; - } - - if (!mwifiex_is_11h_active(priv)) { - mwifiex_dbg(priv->adapter, INFO, - "Enable 11h extensions in FW\n"); - if (mwifiex_11h_activate(priv, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to activate 11h extensions!!"); - return -1; - } - priv->state_11h.is_11h_active = true; - } - - memset(&radar_params, 0, sizeof(struct mwifiex_radar_params)); - radar_params.chandef = chandef; - radar_params.cac_time_ms = cac_time_ms; - - memcpy(&priv->dfs_chandef, chandef, sizeof(priv->dfs_chandef)); - - if (mwifiex_send_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST, - HostCmd_ACT_GEN_SET, 0, &radar_params, true)) - return -1; - - queue_delayed_work(priv->dfs_cac_workqueue, &priv->dfs_cac_work, - msecs_to_jiffies(cac_time_ms)); - return 0; -} - -static int -mwifiex_cfg80211_change_station(struct wiphy *wiphy, struct net_device *dev, - const u8 *mac, - struct station_parameters *params) -{ - int ret; - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - /* we support change_station handler only for TDLS peers*/ - if (!(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) - return -ENOTSUPP; - - /* make sure we are in station mode and connected */ - if ((priv->bss_type != MWIFIEX_BSS_TYPE_STA) || !priv->media_connected) - return -ENOTSUPP; - - priv->sta_params = params; - - ret = mwifiex_tdls_oper(priv, mac, MWIFIEX_TDLS_CONFIG_LINK); - priv->sta_params = NULL; - - return ret; -} - -/* station cfg80211 operations */ -static struct cfg80211_ops mwifiex_cfg80211_ops = { - .add_virtual_intf = mwifiex_add_virtual_intf, - .del_virtual_intf = mwifiex_del_virtual_intf, - .change_virtual_intf = mwifiex_cfg80211_change_virtual_intf, - .scan = mwifiex_cfg80211_scan, - .connect = mwifiex_cfg80211_connect, - .disconnect = mwifiex_cfg80211_disconnect, - .get_station = mwifiex_cfg80211_get_station, - .dump_station = mwifiex_cfg80211_dump_station, - .dump_survey = mwifiex_cfg80211_dump_survey, - .set_wiphy_params = mwifiex_cfg80211_set_wiphy_params, - .join_ibss = mwifiex_cfg80211_join_ibss, - .leave_ibss = mwifiex_cfg80211_leave_ibss, - .add_key = mwifiex_cfg80211_add_key, - .del_key = mwifiex_cfg80211_del_key, - .mgmt_tx = mwifiex_cfg80211_mgmt_tx, - .mgmt_frame_register = mwifiex_cfg80211_mgmt_frame_register, - .remain_on_channel = mwifiex_cfg80211_remain_on_channel, - .cancel_remain_on_channel = mwifiex_cfg80211_cancel_remain_on_channel, - .set_default_key = mwifiex_cfg80211_set_default_key, - .set_power_mgmt = mwifiex_cfg80211_set_power_mgmt, - .set_tx_power = mwifiex_cfg80211_set_tx_power, - .set_bitrate_mask = mwifiex_cfg80211_set_bitrate_mask, - .start_ap = mwifiex_cfg80211_start_ap, - .stop_ap = mwifiex_cfg80211_stop_ap, - .change_beacon = mwifiex_cfg80211_change_beacon, - .set_cqm_rssi_config = mwifiex_cfg80211_set_cqm_rssi_config, - .set_antenna = mwifiex_cfg80211_set_antenna, - .del_station = mwifiex_cfg80211_del_station, -#ifdef CONFIG_PM - .suspend = mwifiex_cfg80211_suspend, - .resume = mwifiex_cfg80211_resume, - .set_wakeup = mwifiex_cfg80211_set_wakeup, -#endif - .set_coalesce = mwifiex_cfg80211_set_coalesce, - .tdls_mgmt = mwifiex_cfg80211_tdls_mgmt, - .tdls_oper = mwifiex_cfg80211_tdls_oper, - .tdls_channel_switch = mwifiex_cfg80211_tdls_chan_switch, - .tdls_cancel_channel_switch = mwifiex_cfg80211_tdls_cancel_chan_switch, - .add_station = mwifiex_cfg80211_add_station, - .change_station = mwifiex_cfg80211_change_station, - .get_channel = mwifiex_cfg80211_get_channel, - .start_radar_detection = mwifiex_cfg80211_start_radar_detection, - .channel_switch = mwifiex_cfg80211_channel_switch, -}; - -#ifdef CONFIG_PM -static const struct wiphy_wowlan_support mwifiex_wowlan_support = { - .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, - .n_patterns = MWIFIEX_MEF_MAX_FILTERS, - .pattern_min_len = 1, - .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, - .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, -}; -#endif - -static bool mwifiex_is_valid_alpha2(const char *alpha2) -{ - if (!alpha2 || strlen(alpha2) != 2) - return false; - - if (isalpha(alpha2[0]) && isalpha(alpha2[1])) - return true; - - return false; -} - -static const struct wiphy_coalesce_support mwifiex_coalesce_support = { - .n_rules = MWIFIEX_COALESCE_MAX_RULES, - .max_delay = MWIFIEX_MAX_COALESCING_DELAY, - .n_patterns = MWIFIEX_COALESCE_MAX_FILTERS, - .pattern_min_len = 1, - .pattern_max_len = MWIFIEX_MAX_PATTERN_LEN, - .max_pkt_offset = MWIFIEX_MAX_OFFSET_LEN, -}; - -int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter) -{ - u32 n_channels_bg, n_channels_a = 0; - - n_channels_bg = mwifiex_band_2ghz.n_channels; - - if (adapter->config_bands & BAND_A) - n_channels_a = mwifiex_band_5ghz.n_channels; - - adapter->num_in_chan_stats = max_t(u32, n_channels_bg, n_channels_a); - adapter->chan_stats = vmalloc(sizeof(*adapter->chan_stats) * - adapter->num_in_chan_stats); - - if (!adapter->chan_stats) - return -ENOMEM; - - return 0; -} - -/* - * This function registers the device with CFG802.11 subsystem. - * - * The function creates the wireless device/wiphy, populates it with - * default parameters and handler function pointers, and finally - * registers the device. - */ - -int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) -{ - int ret; - void *wdev_priv; - struct wiphy *wiphy; - struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; - u8 *country_code; - u32 thr, retry; - - /* create a new wiphy for use with cfg80211 */ - wiphy = wiphy_new(&mwifiex_cfg80211_ops, - sizeof(struct mwifiex_adapter *)); - if (!wiphy) { - mwifiex_dbg(adapter, ERROR, - "%s: creating new wiphy\n", __func__); - return -ENOMEM; - } - wiphy->max_scan_ssids = MWIFIEX_MAX_SSID_LIST_LENGTH; - wiphy->max_scan_ie_len = MWIFIEX_MAX_VSIE_LEN; - wiphy->mgmt_stypes = mwifiex_mgmt_stypes; - wiphy->max_remain_on_channel_duration = 5000; - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_AP); - - wiphy->bands[IEEE80211_BAND_2GHZ] = &mwifiex_band_2ghz; - if (adapter->config_bands & BAND_A) - wiphy->bands[IEEE80211_BAND_5GHZ] = &mwifiex_band_5ghz; - else - wiphy->bands[IEEE80211_BAND_5GHZ] = NULL; - - if (adapter->drcs_enabled && ISSUPP_DRCS_ENABLED(adapter->fw_cap_info)) - wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_drcs; - else if (adapter->is_hw_11ac_capable) - wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta_vht; - else - wiphy->iface_combinations = &mwifiex_iface_comb_ap_sta; - wiphy->n_iface_combinations = 1; - - /* Initialize cipher suits */ - wiphy->cipher_suites = mwifiex_cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(mwifiex_cipher_suites); - - ether_addr_copy(wiphy->perm_addr, adapter->perm_addr); - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | - WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD | - WIPHY_FLAG_AP_UAPSD | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_HAS_CHANNEL_SWITCH | - WIPHY_FLAG_PS_ON_BY_DEFAULT; - - if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) - wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | - WIPHY_FLAG_TDLS_EXTERNAL_SETUP; - -#ifdef CONFIG_PM - wiphy->wowlan = &mwifiex_wowlan_support; -#endif - - wiphy->coalesce = &mwifiex_coalesce_support; - - wiphy->probe_resp_offload = NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | - NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; - - wiphy->available_antennas_tx = BIT(adapter->number_of_antenna) - 1; - wiphy->available_antennas_rx = BIT(adapter->number_of_antenna) - 1; - - wiphy->features |= NL80211_FEATURE_HT_IBSS | - NL80211_FEATURE_INACTIVITY_TIMER | - NL80211_FEATURE_NEED_OBSS_SCAN; - - if (ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) - wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; - - if (adapter->fw_api_ver == MWIFIEX_FW_V15) - wiphy->features |= NL80211_FEATURE_SK_TX_STATUS; - - /* Reserve space for mwifiex specific private data for BSS */ - wiphy->bss_priv_size = sizeof(struct mwifiex_bss_priv); - - wiphy->reg_notifier = mwifiex_reg_notifier; - - /* Set struct mwifiex_adapter pointer in wiphy_priv */ - wdev_priv = wiphy_priv(wiphy); - *(unsigned long *)wdev_priv = (unsigned long)adapter; - - set_wiphy_dev(wiphy, priv->adapter->dev); - - ret = wiphy_register(wiphy); - if (ret < 0) { - mwifiex_dbg(adapter, ERROR, - "%s: wiphy_register failed: %d\n", __func__, ret); - wiphy_free(wiphy); - return ret; - } - - if (reg_alpha2 && mwifiex_is_valid_alpha2(reg_alpha2)) { - mwifiex_dbg(adapter, INFO, - "driver hint alpha2: %2.2s\n", reg_alpha2); - regulatory_hint(wiphy, reg_alpha2); - } else { - country_code = mwifiex_11d_code_2_region(adapter->region_code); - if (country_code) - mwifiex_dbg(adapter, WARN, - "ignoring F/W country code %2.2s\n", - country_code); - } - - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, FRAG_THRESH_I, &thr, true); - wiphy->frag_threshold = thr; - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, RTS_THRESH_I, &thr, true); - wiphy->rts_threshold = thr; - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, SHORT_RETRY_LIM_I, &retry, true); - wiphy->retry_short = (u8) retry; - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_GET, LONG_RETRY_LIM_I, &retry, true); - wiphy->retry_long = (u8) retry; - - adapter->wiphy = wiphy; - return ret; -} diff --git a/drivers/net/wireless/mwifiex/cfg80211.h b/drivers/net/wireless/mwifiex/cfg80211.h deleted file mode 100644 index 908367857d58..000000000000 --- a/drivers/net/wireless/mwifiex/cfg80211.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Marvell Wireless LAN device driver: CFG80211 - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef __MWIFIEX_CFG80211__ -#define __MWIFIEX_CFG80211__ - -#include - -#include "main.h" - -int mwifiex_register_cfg80211(struct mwifiex_adapter *); - -#endif diff --git a/drivers/net/wireless/mwifiex/cfp.c b/drivers/net/wireless/mwifiex/cfp.c deleted file mode 100644 index 3ddb8ec676ed..000000000000 --- a/drivers/net/wireless/mwifiex/cfp.c +++ /dev/null @@ -1,537 +0,0 @@ -/* - * Marvell Wireless LAN device driver: Channel, Frequence and Power - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "cfg80211.h" - -/* 100mW */ -#define MWIFIEX_TX_PWR_DEFAULT 20 -/* 100mW */ -#define MWIFIEX_TX_PWR_US_DEFAULT 20 -/* 50mW */ -#define MWIFIEX_TX_PWR_JP_DEFAULT 16 -/* 100mW */ -#define MWIFIEX_TX_PWR_FR_100MW 20 -/* 10mW */ -#define MWIFIEX_TX_PWR_FR_10MW 10 -/* 100mW */ -#define MWIFIEX_TX_PWR_EMEA_DEFAULT 20 - -static u8 adhoc_rates_b[B_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, 0 }; - -static u8 adhoc_rates_g[G_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, - 0xb0, 0x48, 0x60, 0x6c, 0 }; - -static u8 adhoc_rates_bg[BG_SUPPORTED_RATES] = { 0x82, 0x84, 0x8b, 0x96, - 0x0c, 0x12, 0x18, 0x24, - 0x30, 0x48, 0x60, 0x6c, 0 }; - -static u8 adhoc_rates_a[A_SUPPORTED_RATES] = { 0x8c, 0x12, 0x98, 0x24, - 0xb0, 0x48, 0x60, 0x6c, 0 }; -static u8 supported_rates_a[A_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, - 0xb0, 0x48, 0x60, 0x6c, 0 }; -static u16 mwifiex_data_rates[MWIFIEX_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 }; - -static u8 supported_rates_b[B_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x16, 0 }; - -static u8 supported_rates_g[G_SUPPORTED_RATES] = { 0x0c, 0x12, 0x18, 0x24, - 0x30, 0x48, 0x60, 0x6c, 0 }; - -static u8 supported_rates_bg[BG_SUPPORTED_RATES] = { 0x02, 0x04, 0x0b, 0x0c, - 0x12, 0x16, 0x18, 0x24, 0x30, 0x48, - 0x60, 0x6c, 0 }; - -u16 region_code_index[MWIFIEX_MAX_REGION_CODE] = { 0x10, 0x20, 0x30, - 0x32, 0x40, 0x41, 0xff }; - -static u8 supported_rates_n[N_SUPPORTED_RATES] = { 0x02, 0x04, 0 }; - -/* For every mcs_rate line, the first 8 bytes are for stream 1x1, - * and all 16 bytes are for stream 2x2. - */ -static const u16 mcs_rate[4][16] = { - /* LGI 40M */ - { 0x1b, 0x36, 0x51, 0x6c, 0xa2, 0xd8, 0xf3, 0x10e, - 0x36, 0x6c, 0xa2, 0xd8, 0x144, 0x1b0, 0x1e6, 0x21c }, - - /* SGI 40M */ - { 0x1e, 0x3c, 0x5a, 0x78, 0xb4, 0xf0, 0x10e, 0x12c, - 0x3c, 0x78, 0xb4, 0xf0, 0x168, 0x1e0, 0x21c, 0x258 }, - - /* LGI 20M */ - { 0x0d, 0x1a, 0x27, 0x34, 0x4e, 0x68, 0x75, 0x82, - 0x1a, 0x34, 0x4e, 0x68, 0x9c, 0xd0, 0xea, 0x104 }, - - /* SGI 20M */ - { 0x0e, 0x1c, 0x2b, 0x39, 0x56, 0x73, 0x82, 0x90, - 0x1c, 0x39, 0x56, 0x73, 0xad, 0xe7, 0x104, 0x120 } -}; - -/* AC rates */ -static const u16 ac_mcs_rate_nss1[8][10] = { - /* LG 160M */ - { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, - 0x492, 0x57C, 0x618 }, - - /* SG 160M */ - { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, - 0x514, 0x618, 0x6C6 }, - - /* LG 80M */ - { 0x3B, 0x75, 0xB0, 0xEA, 0x15F, 0x1D4, 0x20F, - 0x249, 0x2BE, 0x30C }, - - /* SG 80M */ - { 0x41, 0x82, 0xC3, 0x104, 0x186, 0x208, 0x249, - 0x28A, 0x30C, 0x363 }, - - /* LG 40M */ - { 0x1B, 0x36, 0x51, 0x6C, 0xA2, 0xD8, 0xF3, - 0x10E, 0x144, 0x168 }, - - /* SG 40M */ - { 0x1E, 0x3C, 0x5A, 0x78, 0xB4, 0xF0, 0x10E, - 0x12C, 0x168, 0x190 }, - - /* LG 20M */ - { 0xD, 0x1A, 0x27, 0x34, 0x4E, 0x68, 0x75, 0x82, 0x9C, 0x00 }, - - /* SG 20M */ - { 0xF, 0x1D, 0x2C, 0x3A, 0x57, 0x74, 0x82, 0x91, 0xAE, 0x00 }, -}; - -/* NSS2 note: the value in the table is 2 multiplier of the actual rate */ -static const u16 ac_mcs_rate_nss2[8][10] = { - /* LG 160M */ - { 0xEA, 0x1D4, 0x2BE, 0x3A8, 0x57C, 0x750, 0x83A, - 0x924, 0xAF8, 0xC30 }, - - /* SG 160M */ - { 0x104, 0x208, 0x30C, 0x410, 0x618, 0x820, 0x924, - 0xA28, 0xC30, 0xD8B }, - - /* LG 80M */ - { 0x75, 0xEA, 0x15F, 0x1D4, 0x2BE, 0x3A8, 0x41D, - 0x492, 0x57C, 0x618 }, - - /* SG 80M */ - { 0x82, 0x104, 0x186, 0x208, 0x30C, 0x410, 0x492, - 0x514, 0x618, 0x6C6 }, - - /* LG 40M */ - { 0x36, 0x6C, 0xA2, 0xD8, 0x144, 0x1B0, 0x1E6, - 0x21C, 0x288, 0x2D0 }, - - /* SG 40M */ - { 0x3C, 0x78, 0xB4, 0xF0, 0x168, 0x1E0, 0x21C, - 0x258, 0x2D0, 0x320 }, - - /* LG 20M */ - { 0x1A, 0x34, 0x4A, 0x68, 0x9C, 0xD0, 0xEA, 0x104, - 0x138, 0x00 }, - - /* SG 20M */ - { 0x1D, 0x3A, 0x57, 0x74, 0xAE, 0xE6, 0x104, 0x121, - 0x15B, 0x00 }, -}; - -struct region_code_mapping { - u8 code; - u8 region[IEEE80211_COUNTRY_STRING_LEN]; -}; - -static struct region_code_mapping region_code_mapping_t[] = { - { 0x10, "US " }, /* US FCC */ - { 0x20, "CA " }, /* IC Canada */ - { 0x30, "EU " }, /* ETSI */ - { 0x31, "ES " }, /* Spain */ - { 0x32, "FR " }, /* France */ - { 0x40, "JP " }, /* Japan */ - { 0x41, "JP " }, /* Japan */ - { 0x50, "CN " }, /* China */ -}; - -/* This function converts integer code to region string */ -u8 *mwifiex_11d_code_2_region(u8 code) -{ - u8 i; - u8 size = sizeof(region_code_mapping_t)/ - sizeof(struct region_code_mapping); - - /* Look for code in mapping table */ - for (i = 0; i < size; i++) - if (region_code_mapping_t[i].code == code) - return region_code_mapping_t[i].region; - - return NULL; -} - -/* - * This function maps an index in supported rates table into - * the corresponding data rate. - */ -u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, - u8 index, u8 ht_info) -{ - u32 rate = 0; - u8 mcs_index = 0; - u8 bw = 0; - u8 gi = 0; - - if ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_VHT) { - mcs_index = min(index & 0xF, 9); - - /* 20M: bw=0, 40M: bw=1, 80M: bw=2, 160M: bw=3 */ - bw = (ht_info & 0xC) >> 2; - - /* LGI: gi =0, SGI: gi = 1 */ - gi = (ht_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 ((ht_info & 0x3) == MWIFIEX_RATE_FORMAT_HT) { - /* 20M: bw=0, 40M: bw=1 */ - bw = (ht_info & 0xC) >> 2; - - /* LGI: gi =0, SGI: gi = 1 */ - gi = (ht_info & 0x10) >> 4; - - if (index == MWIFIEX_RATE_BITMAP_MCS0) { - if (gi == 1) - rate = 0x0D; /* MCS 32 SGI rate */ - else - rate = 0x0C; /* MCS 32 LGI rate */ - } else if (index < 16) { - if ((bw == 1) || (bw == 0)) - rate = mcs_rate[2 * (1 - bw) + gi][index]; - else - rate = mwifiex_data_rates[0]; - } else { - rate = mwifiex_data_rates[0]; - } - } else { - /* 11n non-HT rates */ - if (index >= MWIFIEX_SUPPORTED_RATES_EXT) - index = 0; - rate = mwifiex_data_rates[index]; - } - - return rate; -} - -/* This function maps an index in supported rates table into - * the corresponding data rate. - */ -u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, - u8 index, u8 ht_info) -{ - u32 mcs_num_supp = - (priv->adapter->user_dev_mcs_support == HT_STREAM_2X2) ? 16 : 8; - u32 rate; - - if (priv->adapter->is_hw_11ac_capable) - return mwifiex_index_to_acs_data_rate(priv, index, ht_info); - - if (ht_info & BIT(0)) { - if (index == MWIFIEX_RATE_BITMAP_MCS0) { - if (ht_info & BIT(2)) - rate = 0x0D; /* MCS 32 SGI rate */ - else - rate = 0x0C; /* MCS 32 LGI rate */ - } else if (index < mcs_num_supp) { - if (ht_info & BIT(1)) { - if (ht_info & BIT(2)) - /* SGI, 40M */ - rate = mcs_rate[1][index]; - else - /* LGI, 40M */ - rate = mcs_rate[0][index]; - } else { - if (ht_info & BIT(2)) - /* SGI, 20M */ - rate = mcs_rate[3][index]; - else - /* LGI, 20M */ - rate = mcs_rate[2][index]; - } - } else - rate = mwifiex_data_rates[0]; - } else { - if (index >= MWIFIEX_SUPPORTED_RATES_EXT) - index = 0; - rate = mwifiex_data_rates[index]; - } - return rate; -} - -/* - * This function returns the current active data rates. - * - * The result may vary depending upon connection status. - */ -u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, u8 *rates) -{ - if (!priv->media_connected) - return mwifiex_get_supported_rates(priv, rates); - else - return mwifiex_copy_rates(rates, 0, - priv->curr_bss_params.data_rates, - priv->curr_bss_params.num_of_rates); -} - -/* - * This function locates the Channel-Frequency-Power triplet based upon - * band and channel/frequency parameters. - */ -struct mwifiex_chan_freq_power * -mwifiex_get_cfp(struct mwifiex_private *priv, u8 band, u16 channel, u32 freq) -{ - struct mwifiex_chan_freq_power *cfp = NULL; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch = NULL; - int i; - - if (!channel && !freq) - return cfp; - - if (mwifiex_band_to_radio_type(band) == HostCmd_SCAN_RADIO_TYPE_BG) - sband = priv->wdev.wiphy->bands[IEEE80211_BAND_2GHZ]; - else - sband = priv->wdev.wiphy->bands[IEEE80211_BAND_5GHZ]; - - if (!sband) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: cannot find cfp by band %d\n", - __func__, band); - return cfp; - } - - for (i = 0; i < sband->n_channels; i++) { - ch = &sband->channels[i]; - - if (ch->flags & IEEE80211_CHAN_DISABLED) - continue; - - if (freq) { - if (ch->center_freq == freq) - break; - } else { - /* find by valid channel*/ - if (ch->hw_value == channel || - channel == FIRST_VALID_CHANNEL) - break; - } - } - if (i == sband->n_channels) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: cannot find cfp by band %d\t" - "& channel=%d freq=%d\n", - __func__, band, channel, freq); - } else { - if (!ch) - return cfp; - - priv->cfp.channel = ch->hw_value; - priv->cfp.freq = ch->center_freq; - priv->cfp.max_tx_power = ch->max_power; - cfp = &priv->cfp; - } - - return cfp; -} - -/* - * This function checks if the data rate is set to auto. - */ -u8 -mwifiex_is_rate_auto(struct mwifiex_private *priv) -{ - u32 i; - int rate_num = 0; - - for (i = 0; i < ARRAY_SIZE(priv->bitmap_rates); i++) - if (priv->bitmap_rates[i]) - rate_num++; - - if (rate_num > 1) - return true; - else - return false; -} - -/* This function gets the supported data rates from bitmask inside - * cfg80211_scan_request. - */ -u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, - u8 *rates, u8 radio_type) -{ - struct wiphy *wiphy = priv->adapter->wiphy; - struct cfg80211_scan_request *request = priv->scan_request; - u32 num_rates, rate_mask; - struct ieee80211_supported_band *sband; - int i; - - if (radio_type) { - sband = wiphy->bands[IEEE80211_BAND_5GHZ]; - if (WARN_ON_ONCE(!sband)) - return 0; - rate_mask = request->rates[IEEE80211_BAND_5GHZ]; - } else { - sband = wiphy->bands[IEEE80211_BAND_2GHZ]; - if (WARN_ON_ONCE(!sband)) - return 0; - rate_mask = request->rates[IEEE80211_BAND_2GHZ]; - } - - num_rates = 0; - for (i = 0; i < sband->n_bitrates; i++) { - if ((BIT(i) & rate_mask) == 0) - continue; /* skip rate */ - rates[num_rates++] = (u8)(sband->bitrates[i].bitrate / 5); - } - - return num_rates; -} - -/* This function gets the supported data rates. The function works in - * both Ad-Hoc and infra mode by printing the band and returning the - * data rates. - */ -u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates) -{ - u32 k = 0; - struct mwifiex_adapter *adapter = priv->adapter; - - if (priv->bss_mode == NL80211_IFTYPE_STATION || - priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { - switch (adapter->config_bands) { - case BAND_B: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_b\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_b, - sizeof(supported_rates_b)); - break; - case BAND_G: - case BAND_G | BAND_GN: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_g\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_g, - sizeof(supported_rates_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_B | BAND_G | BAND_GN: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_bg\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_bg, - sizeof(supported_rates_bg)); - break; - case BAND_A: - case BAND_A | BAND_G: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_a\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_a, - sizeof(supported_rates_a)); - break; - case BAND_AN: - case BAND_A | BAND_AN: - case BAND_A | BAND_AN | BAND_AAC: - case BAND_A | BAND_G | BAND_AN | BAND_GN: - case BAND_A | BAND_G | BAND_AN | BAND_GN | BAND_AAC: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_a\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_a, - sizeof(supported_rates_a)); - break; - case BAND_GN: - mwifiex_dbg(adapter, INFO, "info: infra band=%d\t" - "supported_rates_n\n", - adapter->config_bands); - k = mwifiex_copy_rates(rates, k, supported_rates_n, - sizeof(supported_rates_n)); - break; - } - } else { - /* Ad-hoc mode */ - switch (adapter->adhoc_start_band) { - case BAND_B: - mwifiex_dbg(adapter, INFO, "info: adhoc B\n"); - k = mwifiex_copy_rates(rates, k, adhoc_rates_b, - sizeof(adhoc_rates_b)); - break; - case BAND_G: - case BAND_G | BAND_GN: - mwifiex_dbg(adapter, INFO, "info: adhoc G only\n"); - k = mwifiex_copy_rates(rates, k, adhoc_rates_g, - sizeof(adhoc_rates_g)); - break; - case BAND_B | BAND_G: - case BAND_B | BAND_G | BAND_GN: - mwifiex_dbg(adapter, INFO, "info: adhoc BG\n"); - k = mwifiex_copy_rates(rates, k, adhoc_rates_bg, - sizeof(adhoc_rates_bg)); - break; - case BAND_A: - case BAND_A | BAND_AN: - mwifiex_dbg(adapter, INFO, "info: adhoc A\n"); - k = mwifiex_copy_rates(rates, k, adhoc_rates_a, - sizeof(adhoc_rates_a)); - break; - } - } - - return k; -} - -u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, - u8 rx_rate, u8 rate_info) -{ - u8 rate_index = 0; - - /* HT40 */ - if ((rate_info & BIT(0)) && (rate_info & BIT(1))) - rate_index = MWIFIEX_RATE_INDEX_MCS0 + - MWIFIEX_BW20_MCS_NUM + rx_rate; - else if (rate_info & BIT(0)) /* HT20 */ - rate_index = MWIFIEX_RATE_INDEX_MCS0 + rx_rate; - else - rate_index = (rx_rate > MWIFIEX_RATE_INDEX_OFDM0) ? - rx_rate - 1 : rx_rate; - - return rate_index; -} diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c deleted file mode 100644 index 45ae38e32621..000000000000 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ /dev/null @@ -1,1659 +0,0 @@ -/* - * Marvell Wireless LAN device driver: commands and events - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11ac.h" - -/* - * This function initializes a command node. - * - * The actual allocation of the node is not done by this function. It only - * initiates a node by filling it with default parameters. Similarly, - * allocation of the different buffers used (IOCTL buffer, data buffer) are - * not done by this function either. - */ -static void -mwifiex_init_cmd_node(struct mwifiex_private *priv, - struct cmd_ctrl_node *cmd_node, - u32 cmd_oid, void *data_buf, bool sync) -{ - cmd_node->priv = priv; - cmd_node->cmd_oid = cmd_oid; - if (sync) { - cmd_node->wait_q_enabled = true; - cmd_node->cmd_wait_q_woken = false; - cmd_node->condition = &cmd_node->cmd_wait_q_woken; - } - cmd_node->data_buf = data_buf; - cmd_node->cmd_skb = cmd_node->skb; -} - -/* - * This function returns a command node from the free queue depending upon - * availability. - */ -static struct cmd_ctrl_node * -mwifiex_get_cmd_node(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_node; - unsigned long flags; - - spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); - if (list_empty(&adapter->cmd_free_q)) { - mwifiex_dbg(adapter, ERROR, - "GET_CMD_NODE: cmd node not available\n"); - spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); - return NULL; - } - cmd_node = list_first_entry(&adapter->cmd_free_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); - - return cmd_node; -} - -/* - * This function cleans up a command node. - * - * The function resets the fields including the buffer pointers. - * This function does not try to free the buffers. They must be - * freed before calling this function. - * - * This function will however call the receive completion callback - * in case a response buffer is still available before resetting - * the pointer. - */ -static void -mwifiex_clean_cmd_node(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node) -{ - cmd_node->cmd_oid = 0; - cmd_node->cmd_flag = 0; - cmd_node->data_buf = NULL; - cmd_node->wait_q_enabled = false; - - if (cmd_node->cmd_skb) - skb_trim(cmd_node->cmd_skb, 0); - - if (cmd_node->resp_skb) { - adapter->if_ops.cmdrsp_complete(adapter, cmd_node->resp_skb); - cmd_node->resp_skb = NULL; - } -} - -/* - * This function sends a host command to the firmware. - * - * The function copies the host command into the driver command - * buffer, which will be transferred to the firmware later by the - * main thread. - */ -static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_ds_misc_cmd *pcmd_ptr) -{ - /* Copy the HOST command to command buffer */ - memcpy(cmd, pcmd_ptr->cmd, pcmd_ptr->len); - mwifiex_dbg(priv->adapter, CMD, - "cmd: host cmd size = %d\n", pcmd_ptr->len); - return 0; -} - -/* - * This function downloads a command to the firmware. - * - * The function performs sanity tests, sets the command sequence - * number and size, converts the header fields to CPU format before - * sending. Afterwards, it logs the command ID and action for debugging - * and sets up the command timeout timer. - */ -static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, - struct cmd_ctrl_node *cmd_node) -{ - - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - struct host_cmd_ds_command *host_cmd; - uint16_t cmd_code; - uint16_t cmd_size; - unsigned long flags; - __le32 tmp; - - if (!adapter || !cmd_node) - return -1; - - host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); - - /* Sanity test */ - if (host_cmd == NULL || host_cmd->size == 0) { - mwifiex_dbg(adapter, ERROR, - "DNLD_CMD: host_cmd is null\t" - "or cmd size is 0, not sending\n"); - if (cmd_node->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - mwifiex_recycle_cmd_node(adapter, cmd_node); - return -1; - } - - cmd_code = le16_to_cpu(host_cmd->command); - cmd_size = le16_to_cpu(host_cmd->size); - - if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET && - cmd_code != HostCmd_CMD_FUNC_SHUTDOWN && - cmd_code != HostCmd_CMD_FUNC_INIT) { - mwifiex_dbg(adapter, ERROR, - "DNLD_CMD: FW in reset state, ignore cmd %#x\n", - cmd_code); - mwifiex_recycle_cmd_node(adapter, cmd_node); - queue_work(adapter->workqueue, &adapter->main_work); - return -1; - } - - /* Set command sequence number */ - adapter->seq_num++; - host_cmd->seq_num = cpu_to_le16(HostCmd_SET_SEQ_NO_BSS_INFO - (adapter->seq_num, - cmd_node->priv->bss_num, - cmd_node->priv->bss_type)); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = cmd_node; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - /* Adjust skb length */ - if (cmd_node->cmd_skb->len > cmd_size) - /* - * cmd_size is less than sizeof(struct host_cmd_ds_command). - * Trim off the unused portion. - */ - skb_trim(cmd_node->cmd_skb, cmd_size); - else if (cmd_node->cmd_skb->len < cmd_size) - /* - * cmd_size is larger than sizeof(struct host_cmd_ds_command) - * because we have appended custom IE TLV. Increase skb length - * accordingly. - */ - skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len); - - mwifiex_dbg(adapter, CMD, - "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", - cmd_code, - le16_to_cpu(*(__le16 *)((u8 *)host_cmd + S_DS_GEN)), - cmd_size, le16_to_cpu(host_cmd->seq_num)); - mwifiex_dbg_dump(adapter, CMD_D, "cmd buffer:", host_cmd, cmd_size); - - if (adapter->iface_type == MWIFIEX_USB) { - tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); - skb_push(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); - memcpy(cmd_node->cmd_skb->data, &tmp, MWIFIEX_TYPE_LEN); - adapter->cmd_sent = true; - ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_USB_EP_CMD_EVENT, - cmd_node->cmd_skb, NULL); - skb_pull(cmd_node->cmd_skb, MWIFIEX_TYPE_LEN); - if (ret == -EBUSY) - cmd_node->cmd_skb = NULL; - } else { - skb_push(cmd_node->cmd_skb, INTF_HEADER_LEN); - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, - cmd_node->cmd_skb, NULL); - skb_pull(cmd_node->cmd_skb, INTF_HEADER_LEN); - } - - if (ret == -1) { - mwifiex_dbg(adapter, ERROR, - "DNLD_CMD: host to card failed\n"); - if (adapter->iface_type == MWIFIEX_USB) - adapter->cmd_sent = false; - if (cmd_node->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - adapter->dbg.num_cmd_host_to_card_failure++; - return -1; - } - - /* Save the last command id and action to debug log */ - adapter->dbg.last_cmd_index = - (adapter->dbg.last_cmd_index + 1) % DBG_CMD_NUM; - adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index] = cmd_code; - adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index] = - le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)); - - /* Clear BSS_NO_BITS from HostCmd */ - cmd_code &= HostCmd_CMD_ID_MASK; - - /* Setup the timer after transmit command */ - mod_timer(&adapter->cmd_timer, - jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); - - return 0; -} - -/* - * This function downloads a sleep confirm command to the firmware. - * - * The function performs sanity tests, sets the command sequence - * number and size, converts the header fields to CPU format before - * sending. - * - * No responses are needed for sleep confirm command. - */ -static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) -{ - int ret; - struct mwifiex_private *priv; - struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = - (struct mwifiex_opt_sleep_confirm *) - adapter->sleep_cfm->data; - struct sk_buff *sleep_cfm_tmp; - __le32 tmp; - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - adapter->seq_num++; - sleep_cfm_buf->seq_num = - cpu_to_le16((HostCmd_SET_SEQ_NO_BSS_INFO - (adapter->seq_num, priv->bss_num, - priv->bss_type))); - - mwifiex_dbg(adapter, CMD, - "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", - le16_to_cpu(sleep_cfm_buf->command), - le16_to_cpu(sleep_cfm_buf->action), - le16_to_cpu(sleep_cfm_buf->size), - le16_to_cpu(sleep_cfm_buf->seq_num)); - mwifiex_dbg_dump(adapter, CMD_D, "SLEEP_CFM buffer: ", sleep_cfm_buf, - le16_to_cpu(sleep_cfm_buf->size)); - - if (adapter->iface_type == MWIFIEX_USB) { - sleep_cfm_tmp = - dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) - + MWIFIEX_TYPE_LEN); - skb_put(sleep_cfm_tmp, sizeof(struct mwifiex_opt_sleep_confirm) - + MWIFIEX_TYPE_LEN); - tmp = cpu_to_le32(MWIFIEX_USB_TYPE_CMD); - memcpy(sleep_cfm_tmp->data, &tmp, MWIFIEX_TYPE_LEN); - memcpy(sleep_cfm_tmp->data + MWIFIEX_TYPE_LEN, - adapter->sleep_cfm->data, - sizeof(struct mwifiex_opt_sleep_confirm)); - ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_USB_EP_CMD_EVENT, - sleep_cfm_tmp, NULL); - if (ret != -EBUSY) - dev_kfree_skb_any(sleep_cfm_tmp); - } else { - skb_push(adapter->sleep_cfm, INTF_HEADER_LEN); - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_CMD, - adapter->sleep_cfm, NULL); - skb_pull(adapter->sleep_cfm, INTF_HEADER_LEN); - } - - if (ret == -1) { - mwifiex_dbg(adapter, ERROR, "SLEEP_CFM: failed\n"); - adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure++; - return -1; - } - - if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl)) - /* Response is not needed for sleep confirm command */ - adapter->ps_state = PS_STATE_SLEEP; - else - adapter->ps_state = PS_STATE_SLEEP_CFM; - - if (!le16_to_cpu(sleep_cfm_buf->resp_ctrl) && - (adapter->is_hs_configured && - !adapter->sleep_period.period)) { - adapter->pm_wakeup_card_req = true; - mwifiex_hs_activated_event(mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), true); - } - - return ret; -} - -/* - * This function allocates the command buffers and links them to - * the command free queue. - * - * The driver uses a pre allocated number of command buffers, which - * are created at driver initializations and freed at driver cleanup. - * Every command needs to obtain a command buffer from this pool before - * it can be issued. The command free queue lists the command buffers - * currently free to use, while the command pending queue lists the - * command buffers already in use and awaiting handling. Command buffers - * are returned to the free queue after use. - */ -int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_array; - u32 i; - - /* Allocate and initialize struct cmd_ctrl_node */ - cmd_array = kcalloc(MWIFIEX_NUM_OF_CMD_BUFFER, - sizeof(struct cmd_ctrl_node), GFP_KERNEL); - if (!cmd_array) - return -ENOMEM; - - adapter->cmd_pool = cmd_array; - - /* Allocate and initialize command buffers */ - for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { - cmd_array[i].skb = dev_alloc_skb(MWIFIEX_SIZE_OF_CMD_BUFFER); - if (!cmd_array[i].skb) { - mwifiex_dbg(adapter, ERROR, - "unable to allocate command buffer\n"); - return -ENOMEM; - } - } - - for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) - mwifiex_insert_cmd_to_free_q(adapter, &cmd_array[i]); - - return 0; -} - -/* - * This function frees the command buffers. - * - * The function calls the completion callback for all the command - * buffers that still have response buffers associated with them. - */ -int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_array; - u32 i; - - /* Need to check if cmd pool is allocated or not */ - if (!adapter->cmd_pool) { - mwifiex_dbg(adapter, FATAL, - "info: FREE_CMD_BUF: cmd_pool is null\n"); - return 0; - } - - cmd_array = adapter->cmd_pool; - - /* Release shared memory buffers */ - for (i = 0; i < MWIFIEX_NUM_OF_CMD_BUFFER; i++) { - if (cmd_array[i].skb) { - mwifiex_dbg(adapter, CMD, - "cmd: free cmd buffer %d\n", i); - dev_kfree_skb_any(cmd_array[i].skb); - } - if (!cmd_array[i].resp_skb) - continue; - - if (adapter->iface_type == MWIFIEX_USB) - adapter->if_ops.cmdrsp_complete(adapter, - cmd_array[i].resp_skb); - else - dev_kfree_skb_any(cmd_array[i].resp_skb); - } - /* Release struct cmd_ctrl_node */ - if (adapter->cmd_pool) { - mwifiex_dbg(adapter, CMD, - "cmd: free cmd pool\n"); - kfree(adapter->cmd_pool); - adapter->cmd_pool = NULL; - } - - return 0; -} - -/* - * This function handles events generated by firmware. - * - * Event body of events received from firmware are not used (though they are - * saved), only the event ID is used. Some events are re-invoked by - * the driver, with a new event body. - * - * After processing, the function calls the completion callback - * for cleanup. - */ -int mwifiex_process_event(struct mwifiex_adapter *adapter) -{ - int ret; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - struct sk_buff *skb = adapter->event_skb; - u32 eventcause = adapter->event_cause; - struct mwifiex_rxinfo *rx_info; - - /* Save the last event to debug log */ - adapter->dbg.last_event_index = - (adapter->dbg.last_event_index + 1) % DBG_CMD_NUM; - adapter->dbg.last_event[adapter->dbg.last_event_index] = - (u16) eventcause; - - /* Get BSS number and corresponding priv */ - priv = mwifiex_get_priv_by_id(adapter, EVENT_GET_BSS_NUM(eventcause), - EVENT_GET_BSS_TYPE(eventcause)); - if (!priv) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - /* Clear BSS_NO_BITS from event */ - eventcause &= EVENT_ID_MASK; - adapter->event_cause = eventcause; - - if (skb) { - rx_info = MWIFIEX_SKB_RXCB(skb); - memset(rx_info, 0, sizeof(*rx_info)); - rx_info->bss_num = priv->bss_num; - rx_info->bss_type = priv->bss_type; - mwifiex_dbg_dump(adapter, EVT_D, "Event Buf:", - skb->data, skb->len); - } - - mwifiex_dbg(adapter, EVENT, "EVENT: cause: %#x\n", eventcause); - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - ret = mwifiex_process_uap_event(priv); - else - ret = mwifiex_process_sta_event(priv); - - adapter->event_cause = 0; - adapter->event_skb = NULL; - adapter->if_ops.event_complete(adapter, skb); - - return ret; -} - -/* - * This function prepares a command and send it to the firmware. - * - * Preparation includes - - * - Sanity tests to make sure the card is still present or the FW - * is not reset - * - Getting a new command node from the command free queue - * - Initializing the command node for default parameters - * - Fill up the non-default parameters and buffer pointers - * - Add the command to pending queue - */ -int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, - u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node; - struct host_cmd_ds_command *cmd_ptr; - - if (!adapter) { - pr_err("PREP_CMD: adapter is NULL\n"); - return -1; - } - - if (adapter->is_suspended) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: device in suspended state\n"); - return -1; - } - - if (adapter->hs_enabling && cmd_no != HostCmd_CMD_802_11_HS_CFG_ENH) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: host entering sleep state\n"); - return -1; - } - - if (adapter->surprise_removed) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: card is removed\n"); - return -1; - } - - if (adapter->is_cmd_timedout) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: FW is in bad state\n"); - return -1; - } - - if (adapter->hw_status == MWIFIEX_HW_STATUS_RESET) { - if (cmd_no != HostCmd_CMD_FUNC_INIT) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: FW in reset state\n"); - return -1; - } - } - - /* Get a new command node */ - cmd_node = mwifiex_get_cmd_node(adapter); - - if (!cmd_node) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: no free cmd node\n"); - return -1; - } - - /* Initialize the command node */ - mwifiex_init_cmd_node(priv, cmd_node, cmd_oid, data_buf, sync); - - if (!cmd_node->cmd_skb) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: no free cmd buf\n"); - return -1; - } - - memset(skb_put(cmd_node->cmd_skb, sizeof(struct host_cmd_ds_command)), - 0, sizeof(struct host_cmd_ds_command)); - - cmd_ptr = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->result = 0; - - /* Prepare command */ - if (cmd_no) { - switch (cmd_no) { - case HostCmd_CMD_UAP_SYS_CONFIG: - case HostCmd_CMD_UAP_BSS_START: - case HostCmd_CMD_UAP_BSS_STOP: - case HostCmd_CMD_UAP_STA_DEAUTH: - case HOST_CMD_APCMD_SYS_RESET: - case HOST_CMD_APCMD_STA_LIST: - ret = mwifiex_uap_prepare_cmd(priv, cmd_no, cmd_action, - cmd_oid, data_buf, - cmd_ptr); - break; - default: - ret = mwifiex_sta_prepare_cmd(priv, cmd_no, cmd_action, - cmd_oid, data_buf, - cmd_ptr); - break; - } - } else { - ret = mwifiex_cmd_host_cmd(priv, cmd_ptr, data_buf); - cmd_node->cmd_flag |= CMD_F_HOSTCMD; - } - - /* Return error, since the command preparation failed */ - if (ret) { - mwifiex_dbg(adapter, ERROR, - "PREP_CMD: cmd %#x preparation failed\n", - cmd_no); - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - return -1; - } - - /* Send command */ - if (cmd_no == HostCmd_CMD_802_11_SCAN || - cmd_no == HostCmd_CMD_802_11_SCAN_EXT) { - mwifiex_queue_scan_cmd(priv, cmd_node); - } else { - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); - queue_work(adapter->workqueue, &adapter->main_work); - if (cmd_node->wait_q_enabled) - ret = mwifiex_wait_queue_complete(adapter, cmd_node); - } - - return ret; -} - -/* - * This function returns a command to the command free queue. - * - * The function also calls the completion callback if required, before - * cleaning the command node and re-inserting it into the free queue. - */ -void -mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node) -{ - unsigned long flags; - - if (!cmd_node) - return; - - if (cmd_node->wait_q_enabled) - mwifiex_complete_cmd(adapter, cmd_node); - /* Clean the node */ - mwifiex_clean_cmd_node(adapter, cmd_node); - - /* Insert node into cmd_free_q */ - spin_lock_irqsave(&adapter->cmd_free_q_lock, flags); - list_add_tail(&cmd_node->list, &adapter->cmd_free_q); - spin_unlock_irqrestore(&adapter->cmd_free_q_lock, flags); -} - -/* This function reuses a command node. */ -void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node) -{ - struct host_cmd_ds_command *host_cmd = (void *)cmd_node->cmd_skb->data; - - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - - atomic_dec(&adapter->cmd_pending); - mwifiex_dbg(adapter, CMD, - "cmd: FREE_CMD: cmd=%#x, cmd_pending=%d\n", - le16_to_cpu(host_cmd->command), - atomic_read(&adapter->cmd_pending)); -} - -/* - * This function queues a command to the command pending queue. - * - * This in effect adds the command to the command list to be executed. - * Exit PS command is handled specially, by placing it always to the - * front of the command queue. - */ -void -mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node, u32 add_tail) -{ - struct host_cmd_ds_command *host_cmd = NULL; - u16 command; - unsigned long flags; - - host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); - if (!host_cmd) { - mwifiex_dbg(adapter, ERROR, "QUEUE_CMD: host_cmd is NULL\n"); - return; - } - - command = le16_to_cpu(host_cmd->command); - - /* Exit_PS command needs to be queued in the header always. */ - if (command == HostCmd_CMD_802_11_PS_MODE_ENH) { - struct host_cmd_ds_802_11_ps_mode_enh *pm = - &host_cmd->params.psmode_enh; - if ((le16_to_cpu(pm->action) == DIS_PS) || - (le16_to_cpu(pm->action) == DIS_AUTO_PS)) { - if (adapter->ps_state != PS_STATE_AWAKE) - add_tail = false; - } - } - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - if (add_tail) - list_add_tail(&cmd_node->list, &adapter->cmd_pending_q); - else - list_add(&cmd_node->list, &adapter->cmd_pending_q); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - - atomic_inc(&adapter->cmd_pending); - mwifiex_dbg(adapter, CMD, - "cmd: QUEUE_CMD: cmd=%#x, cmd_pending=%d\n", - command, atomic_read(&adapter->cmd_pending)); -} - -/* - * This function executes the next command in command pending queue. - * - * This function will fail if a command is already in processing stage, - * otherwise it will dequeue the first command from the command pending - * queue and send to the firmware. - * - * If the device is currently in host sleep mode, any commands, except the - * host sleep configuration command will de-activate the host sleep. For PS - * mode, the function will put the firmware back to sleep if applicable. - */ -int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - struct cmd_ctrl_node *cmd_node; - int ret = 0; - struct host_cmd_ds_command *host_cmd; - unsigned long cmd_flags; - unsigned long cmd_pending_q_flags; - - /* Check if already in processing */ - if (adapter->curr_cmd) { - mwifiex_dbg(adapter, FATAL, - "EXEC_NEXT_CMD: cmd in processing\n"); - return -1; - } - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - /* Check if any command is pending */ - spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); - if (list_empty(&adapter->cmd_pending_q)) { - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, - cmd_pending_q_flags); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - return 0; - } - cmd_node = list_first_entry(&adapter->cmd_pending_q, - struct cmd_ctrl_node, list); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, - cmd_pending_q_flags); - - host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); - priv = cmd_node->priv; - - if (adapter->ps_state != PS_STATE_AWAKE) { - mwifiex_dbg(adapter, ERROR, - "%s: cannot send cmd in sleep state,\t" - "this should not happen\n", __func__); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - return ret; - } - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_pending_q_flags); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, - cmd_pending_q_flags); - - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node); - priv = mwifiex_get_priv(adapter, MWIFIEX_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 && (host_cmd->command != - cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { - if (adapter->hs_activated) { - adapter->is_hs_configured = false; - mwifiex_hs_activated_event(priv, false); - } - } - - return ret; -} - -/* - * This function handles the command response. - * - * After processing, the function cleans the command node and puts - * it back to the command free queue. - */ -int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) -{ - struct host_cmd_ds_command *resp; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - int ret = 0; - uint16_t orig_cmdresp_no; - uint16_t cmdresp_no; - uint16_t cmdresp_result; - unsigned long flags; - - /* Now we got response from FW, cancel the command timer */ - del_timer_sync(&adapter->cmd_timer); - - if (!adapter->curr_cmd || !adapter->curr_cmd->resp_skb) { - resp = (struct host_cmd_ds_command *) adapter->upld_buf; - mwifiex_dbg(adapter, ERROR, - "CMD_RESP: NULL curr_cmd, %#x\n", - le16_to_cpu(resp->command)); - return -1; - } - - adapter->is_cmd_timedout = 0; - - resp = (struct host_cmd_ds_command *) adapter->curr_cmd->resp_skb->data; - if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { - /* Copy original response back to response buffer */ - struct mwifiex_ds_misc_cmd *hostcmd; - uint16_t size = le16_to_cpu(resp->size); - mwifiex_dbg(adapter, INFO, - "info: host cmd resp size = %d\n", size); - size = min_t(u16, size, MWIFIEX_SIZE_OF_CMD_BUFFER); - if (adapter->curr_cmd->data_buf) { - hostcmd = adapter->curr_cmd->data_buf; - hostcmd->len = size; - memcpy(hostcmd->cmd, resp, size); - } - } - orig_cmdresp_no = le16_to_cpu(resp->command); - - /* Get BSS number and corresponding priv */ - priv = mwifiex_get_priv_by_id(adapter, - HostCmd_GET_BSS_NO(le16_to_cpu(resp->seq_num)), - HostCmd_GET_BSS_TYPE(le16_to_cpu(resp->seq_num))); - if (!priv) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - /* Clear RET_BIT from HostCmd */ - resp->command = cpu_to_le16(orig_cmdresp_no & HostCmd_CMD_ID_MASK); - - cmdresp_no = le16_to_cpu(resp->command); - cmdresp_result = le16_to_cpu(resp->result); - - /* Save the last command response to debug log */ - adapter->dbg.last_cmd_resp_index = - (adapter->dbg.last_cmd_resp_index + 1) % DBG_CMD_NUM; - adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] = - orig_cmdresp_no; - - mwifiex_dbg(adapter, CMD, - "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", - orig_cmdresp_no, cmdresp_result, - le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); - mwifiex_dbg_dump(adapter, CMD_D, "CMD_RESP buffer:", resp, - le16_to_cpu(resp->size)); - - if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { - mwifiex_dbg(adapter, ERROR, "CMD_RESP: invalid cmd resp\n"); - if (adapter->curr_cmd->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - return -1; - } - - if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { - adapter->curr_cmd->cmd_flag &= ~CMD_F_HOSTCMD; - if ((cmdresp_result == HostCmd_RESULT_OK) && - (cmdresp_no == HostCmd_CMD_802_11_HS_CFG_ENH)) - ret = mwifiex_ret_802_11_hs_cfg(priv, resp); - } else { - /* handle response */ - ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp); - } - - /* Check init command response */ - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { - if (ret) { - mwifiex_dbg(adapter, ERROR, - "%s: cmd %#x failed during\t" - "initialization\n", __func__, cmdresp_no); - mwifiex_init_fw_complete(adapter); - return -1; - } else if (adapter->last_init_cmd == cmdresp_no) - adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE; - } - - if (adapter->curr_cmd) { - if (adapter->curr_cmd->wait_q_enabled) - adapter->cmd_wait_q.status = ret; - - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - } - - return ret; -} - -/* - * This function handles the timeout of command sending. - * - * It will re-send the same command again. - */ -void -mwifiex_cmd_timeout_func(unsigned long function_context) -{ - struct mwifiex_adapter *adapter = - (struct mwifiex_adapter *) function_context; - struct cmd_ctrl_node *cmd_node; - - adapter->is_cmd_timedout = 1; - if (!adapter->curr_cmd) { - mwifiex_dbg(adapter, ERROR, - "cmd: empty curr_cmd\n"); - return; - } - cmd_node = adapter->curr_cmd; - if (cmd_node) { - adapter->dbg.timeout_cmd_id = - adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; - adapter->dbg.timeout_cmd_act = - adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; - mwifiex_dbg(adapter, MSG, - "%s: Timeout cmd id = %#x, act = %#x\n", __func__, - adapter->dbg.timeout_cmd_id, - adapter->dbg.timeout_cmd_act); - - mwifiex_dbg(adapter, MSG, - "num_data_h2c_failure = %d\n", - adapter->dbg.num_tx_host_to_card_failure); - mwifiex_dbg(adapter, MSG, - "num_cmd_h2c_failure = %d\n", - adapter->dbg.num_cmd_host_to_card_failure); - - mwifiex_dbg(adapter, MSG, - "is_cmd_timedout = %d\n", - adapter->is_cmd_timedout); - mwifiex_dbg(adapter, MSG, - "num_tx_timeout = %d\n", - adapter->dbg.num_tx_timeout); - - mwifiex_dbg(adapter, MSG, - "last_cmd_index = %d\n", - adapter->dbg.last_cmd_index); - mwifiex_dbg(adapter, MSG, - "last_cmd_id: %*ph\n", - (int)sizeof(adapter->dbg.last_cmd_id), - adapter->dbg.last_cmd_id); - mwifiex_dbg(adapter, MSG, - "last_cmd_act: %*ph\n", - (int)sizeof(adapter->dbg.last_cmd_act), - adapter->dbg.last_cmd_act); - - mwifiex_dbg(adapter, MSG, - "last_cmd_resp_index = %d\n", - adapter->dbg.last_cmd_resp_index); - mwifiex_dbg(adapter, MSG, - "last_cmd_resp_id: %*ph\n", - (int)sizeof(adapter->dbg.last_cmd_resp_id), - adapter->dbg.last_cmd_resp_id); - - mwifiex_dbg(adapter, MSG, - "last_event_index = %d\n", - adapter->dbg.last_event_index); - mwifiex_dbg(adapter, MSG, - "last_event: %*ph\n", - (int)sizeof(adapter->dbg.last_event), - adapter->dbg.last_event); - - mwifiex_dbg(adapter, MSG, - "data_sent=%d cmd_sent=%d\n", - adapter->data_sent, adapter->cmd_sent); - - mwifiex_dbg(adapter, MSG, - "ps_mode=%d ps_state=%d\n", - adapter->ps_mode, adapter->ps_state); - - if (cmd_node->wait_q_enabled) { - adapter->cmd_wait_q.status = -ETIMEDOUT; - mwifiex_cancel_pending_ioctl(adapter); - } - } - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { - mwifiex_init_fw_complete(adapter); - return; - } - - if (adapter->if_ops.device_dump) - adapter->if_ops.device_dump(adapter); - - if (adapter->if_ops.card_reset) - adapter->if_ops.card_reset(adapter); -} - -/* - * This function cancels all the pending commands. - * - * The current command, all commands in command pending queue and all scan - * commands in scan pending queue are cancelled. All the completion callbacks - * are called with failure status to ensure cleanup. - */ -void -mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; - unsigned long flags, cmd_flags; - struct mwifiex_private *priv; - int i; - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - /* Cancel current cmd */ - if ((adapter->curr_cmd) && (adapter->curr_cmd->wait_q_enabled)) { - adapter->curr_cmd->wait_q_enabled = false; - adapter->cmd_wait_q.status = -1; - mwifiex_complete_cmd(adapter, adapter->curr_cmd); - /* no recycle probably wait for response */ - } - /* Cancel all pending command */ - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->cmd_pending_q, list) { - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - - if (cmd_node->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - mwifiex_recycle_cmd_node(adapter, cmd_node); - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - } - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - - /* Cancel all pending scan command */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - - cmd_node->wait_q_enabled = false; - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - - if (adapter->scan_processing) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if (priv->scan_request) { - mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - } - } - } -} - -/* - * This function cancels all pending commands that matches with - * the given IOCTL request. - * - * Both the current command buffer and the pending command queue are - * searched for matching IOCTL request. The completion callback of - * the matched command is called with failure status to ensure cleanup. - * In case of scan commands, all pending commands in scan pending queue - * are cancelled. - */ -void -mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter) -{ - struct cmd_ctrl_node *cmd_node = NULL, *tmp_node = NULL; - unsigned long cmd_flags; - unsigned long scan_pending_q_flags; - struct mwifiex_private *priv; - int i; - - if ((adapter->curr_cmd) && - (adapter->curr_cmd->wait_q_enabled)) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - cmd_node = adapter->curr_cmd; - /* setting curr_cmd to NULL is quite dangerous, because - * mwifiex_process_cmdresp checks curr_cmd to be != NULL - * at the beginning then relies on it and dereferences - * it at will - * this probably works since mwifiex_cmd_timeout_func - * is the only caller of this function and responses - * at that point - */ - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - - mwifiex_recycle_cmd_node(adapter, cmd_node); - } - - /* Cancel all pending scan command */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, - scan_pending_q_flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - cmd_node->wait_q_enabled = false; - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - scan_pending_q_flags); - - if (adapter->scan_processing) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, cmd_flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, cmd_flags); - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if (priv->scan_request) { - mwifiex_dbg(adapter, WARN, "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - } - } - } -} - -/* - * This function sends the sleep confirm command to firmware, if - * possible. - * - * The sleep confirm command cannot be issued if command response, - * data response or event response is awaiting handling, or if we - * are in the middle of sending a command, or expecting a command - * response. - */ -void -mwifiex_check_ps_cond(struct mwifiex_adapter *adapter) -{ - if (!adapter->cmd_sent && - !adapter->curr_cmd && !IS_CARD_RX_RCVD(adapter)) - mwifiex_dnld_sleep_confirm_cmd(adapter); - else - mwifiex_dbg(adapter, CMD, - "cmd: Delay Sleep Confirm (%s%s%s)\n", - (adapter->cmd_sent) ? "D" : "", - (adapter->curr_cmd) ? "C" : "", - (IS_CARD_RX_RCVD(adapter)) ? "R" : ""); -} - -/* - * This function sends a Host Sleep activated event to applications. - * - * This event is generated by the driver, with a blank event body. - */ -void -mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated) -{ - if (activated) { - if (priv->adapter->is_hs_configured) { - priv->adapter->hs_activated = true; - mwifiex_update_rxreor_flags(priv->adapter, - RXREOR_FORCE_NO_DROP); - mwifiex_dbg(priv->adapter, EVENT, - "event: hs_activated\n"); - priv->adapter->hs_activate_wait_q_woken = true; - wake_up_interruptible( - &priv->adapter->hs_activate_wait_q); - } else { - mwifiex_dbg(priv->adapter, EVENT, - "event: HS not configured\n"); - } - } else { - mwifiex_dbg(priv->adapter, EVENT, - "event: hs_deactivated\n"); - priv->adapter->hs_activated = false; - } -} - -/* - * This function handles the command response of a Host Sleep configuration - * command. - * - * Handling includes changing the header fields into CPU format - * and setting the current host sleep activation status in driver. - * - * In case host sleep status change, the function generates an event to - * notify the applications. - */ -int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_hs_cfg_enh *phs_cfg = - &resp->params.opt_hs_cfg; - uint32_t conditions = le32_to_cpu(phs_cfg->params.hs_config.conditions); - - if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) && - adapter->iface_type != MWIFIEX_USB) { - mwifiex_hs_activated_event(priv, true); - return 0; - } else { - mwifiex_dbg(adapter, CMD, - "cmd: CMD_RESP: HS_CFG cmd reply\t" - " result=%#x, conditions=0x%x gpio=0x%x gap=0x%x\n", - resp->result, conditions, - phs_cfg->params.hs_config.gpio, - phs_cfg->params.hs_config.gap); - } - if (conditions != HS_CFG_CANCEL) { - adapter->is_hs_configured = true; - if (adapter->iface_type == MWIFIEX_USB) - mwifiex_hs_activated_event(priv, true); - } else { - adapter->is_hs_configured = false; - if (adapter->hs_activated) - mwifiex_hs_activated_event(priv, false); - } - - return 0; -} - -/* - * This function wakes up the adapter and generates a Host Sleep - * cancel event on receiving the power up interrupt. - */ -void -mwifiex_process_hs_config(struct mwifiex_adapter *adapter) -{ - mwifiex_dbg(adapter, INFO, - "info: %s: auto cancelling host sleep\t" - "since there is interrupt from the firmware\n", - __func__); - - adapter->if_ops.wakeup(adapter); - adapter->hs_activated = false; - adapter->is_hs_configured = false; - adapter->is_suspended = false; - mwifiex_hs_activated_event(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY), - false); -} -EXPORT_SYMBOL_GPL(mwifiex_process_hs_config); - -/* - * This function handles the command response of a sleep confirm command. - * - * The function sets the card state to SLEEP if the response indicates success. - */ -void -mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, - u8 *pbuf, u32 upld_len) -{ - struct host_cmd_ds_command *cmd = (struct host_cmd_ds_command *) pbuf; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - uint16_t result = le16_to_cpu(cmd->result); - uint16_t command = le16_to_cpu(cmd->command); - uint16_t seq_num = le16_to_cpu(cmd->seq_num); - - if (!upld_len) { - mwifiex_dbg(adapter, ERROR, - "%s: cmd size is 0\n", __func__); - return; - } - - mwifiex_dbg(adapter, CMD, - "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", - command, result, le16_to_cpu(cmd->size), seq_num); - - /* Get BSS number and corresponding priv */ - priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num), - HostCmd_GET_BSS_TYPE(seq_num)); - if (!priv) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - /* Update sequence number */ - seq_num = HostCmd_GET_SEQ_NO(seq_num); - /* Clear RET_BIT from HostCmd */ - command &= HostCmd_CMD_ID_MASK; - - if (command != HostCmd_CMD_802_11_PS_MODE_ENH) { - mwifiex_dbg(adapter, ERROR, - "%s: rcvd unexpected resp for cmd %#x, result = %x\n", - __func__, command, result); - return; - } - - if (result) { - mwifiex_dbg(adapter, ERROR, - "%s: sleep confirm cmd failed\n", - __func__); - adapter->pm_wakeup_card_req = false; - adapter->ps_state = PS_STATE_AWAKE; - return; - } - adapter->pm_wakeup_card_req = true; - if (adapter->is_hs_configured) - mwifiex_hs_activated_event(mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - true); - adapter->ps_state = PS_STATE_SLEEP; - cmd->command = cpu_to_le16(command); - cmd->seq_num = cpu_to_le16(seq_num); -} -EXPORT_SYMBOL_GPL(mwifiex_process_sleep_confirm_resp); - -/* - * This function prepares an enhanced power mode command. - * - * This function can be used to disable power save or to configure - * power save with auto PS or STA PS or auto deep sleep. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting Power Save bitmap, PS parameters TLV, PS mode TLV, - * auto deep sleep TLV (as required) - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, uint16_t ps_bitmap, - struct mwifiex_ds_auto_ds *auto_ds) -{ - struct host_cmd_ds_802_11_ps_mode_enh *psmode_enh = - &cmd->params.psmode_enh; - u8 *tlv; - u16 cmd_size = 0; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); - if (cmd_action == DIS_AUTO_PS) { - psmode_enh->action = cpu_to_le16(DIS_AUTO_PS); - psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); - cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + - sizeof(psmode_enh->params.ps_bitmap)); - } else if (cmd_action == GET_PS) { - psmode_enh->action = cpu_to_le16(GET_PS); - psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); - cmd->size = cpu_to_le16(S_DS_GEN + sizeof(psmode_enh->action) + - sizeof(psmode_enh->params.ps_bitmap)); - } else if (cmd_action == EN_AUTO_PS) { - psmode_enh->action = cpu_to_le16(EN_AUTO_PS); - psmode_enh->params.ps_bitmap = cpu_to_le16(ps_bitmap); - cmd_size = S_DS_GEN + sizeof(psmode_enh->action) + - sizeof(psmode_enh->params.ps_bitmap); - tlv = (u8 *) cmd + cmd_size; - if (ps_bitmap & BITMAP_STA_PS) { - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_ps_param *ps_tlv = - (struct mwifiex_ie_types_ps_param *) tlv; - struct mwifiex_ps_param *ps_mode = &ps_tlv->param; - ps_tlv->header.type = cpu_to_le16(TLV_TYPE_PS_PARAM); - ps_tlv->header.len = cpu_to_le16(sizeof(*ps_tlv) - - sizeof(struct mwifiex_ie_types_header)); - cmd_size += sizeof(*ps_tlv); - tlv += sizeof(*ps_tlv); - mwifiex_dbg(priv->adapter, CMD, - "cmd: PS Command: Enter PS\n"); - ps_mode->null_pkt_interval = - cpu_to_le16(adapter->null_pkt_interval); - ps_mode->multiple_dtims = - cpu_to_le16(adapter->multiple_dtim); - ps_mode->bcn_miss_timeout = - cpu_to_le16(adapter->bcn_miss_time_out); - ps_mode->local_listen_interval = - cpu_to_le16(adapter->local_listen_interval); - ps_mode->adhoc_wake_period = - cpu_to_le16(adapter->adhoc_awake_period); - ps_mode->delay_to_ps = - cpu_to_le16(adapter->delay_to_ps); - ps_mode->mode = cpu_to_le16(adapter->enhanced_ps_mode); - - } - if (ps_bitmap & BITMAP_AUTO_DS) { - struct mwifiex_ie_types_auto_ds_param *auto_ds_tlv = - (struct mwifiex_ie_types_auto_ds_param *) tlv; - u16 idletime = 0; - - auto_ds_tlv->header.type = - cpu_to_le16(TLV_TYPE_AUTO_DS_PARAM); - auto_ds_tlv->header.len = - cpu_to_le16(sizeof(*auto_ds_tlv) - - sizeof(struct mwifiex_ie_types_header)); - cmd_size += sizeof(*auto_ds_tlv); - tlv += sizeof(*auto_ds_tlv); - if (auto_ds) - idletime = auto_ds->idle_time; - mwifiex_dbg(priv->adapter, CMD, - "cmd: PS Command: Enter Auto Deep Sleep\n"); - auto_ds_tlv->deep_sleep_timeout = cpu_to_le16(idletime); - } - cmd->size = cpu_to_le16(cmd_size); - } - return 0; -} - -/* - * This function handles the command response of an enhanced power mode - * command. - * - * Handling includes changing the header fields into CPU format - * and setting the current enhanced power mode in driver. - */ -int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct mwifiex_ds_pm_cfg *pm_cfg) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ps_mode_enh *ps_mode = - &resp->params.psmode_enh; - uint16_t action = le16_to_cpu(ps_mode->action); - uint16_t ps_bitmap = le16_to_cpu(ps_mode->params.ps_bitmap); - uint16_t auto_ps_bitmap = - le16_to_cpu(ps_mode->params.ps_bitmap); - - mwifiex_dbg(adapter, INFO, - "info: %s: PS_MODE cmd reply result=%#x action=%#X\n", - __func__, resp->result, action); - if (action == EN_AUTO_PS) { - if (auto_ps_bitmap & BITMAP_AUTO_DS) { - mwifiex_dbg(adapter, CMD, - "cmd: Enabled auto deep sleep\n"); - priv->adapter->is_deep_sleep = true; - } - if (auto_ps_bitmap & BITMAP_STA_PS) { - mwifiex_dbg(adapter, CMD, - "cmd: Enabled STA power save\n"); - if (adapter->sleep_period.period) - mwifiex_dbg(adapter, CMD, - "cmd: set to uapsd/pps mode\n"); - } - } else if (action == DIS_AUTO_PS) { - if (ps_bitmap & BITMAP_AUTO_DS) { - priv->adapter->is_deep_sleep = false; - mwifiex_dbg(adapter, CMD, - "cmd: Disabled auto deep sleep\n"); - } - if (ps_bitmap & BITMAP_STA_PS) { - mwifiex_dbg(adapter, CMD, - "cmd: Disabled STA power save\n"); - if (adapter->sleep_period.period) { - adapter->delay_null_pkt = false; - adapter->tx_lock_flag = false; - adapter->pps_uapsd_mode = false; - } - } - } else if (action == GET_PS) { - if (ps_bitmap & BITMAP_STA_PS) - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; - else - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; - - mwifiex_dbg(adapter, CMD, - "cmd: ps_bitmap=%#x\n", ps_bitmap); - - if (pm_cfg) { - /* This section is for get power save mode */ - if (ps_bitmap & BITMAP_STA_PS) - pm_cfg->param.ps_mode = 1; - else - pm_cfg->param.ps_mode = 0; - } - } - return 0; -} - -/* - * This function prepares command to get hardware specifications. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting permanent address parameter - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd) -{ - struct host_cmd_ds_get_hw_spec *hw_spec = &cmd->params.hw_spec; - - cmd->command = cpu_to_le16(HostCmd_CMD_GET_HW_SPEC); - cmd->size = - cpu_to_le16(sizeof(struct host_cmd_ds_get_hw_spec) + S_DS_GEN); - memcpy(hw_spec->permanent_addr, priv->curr_addr, ETH_ALEN); - - return 0; -} - -/* - * This function handles the command response of get hardware - * specifications. - * - * Handling includes changing the header fields into CPU format - * and saving/updating the following parameters in driver - - * - Firmware capability information - * - Firmware band settings - * - Ad-hoc start band and channel - * - Ad-hoc 11n activation status - * - Firmware release number - * - Number of antennas - * - Hardware address - * - Hardware interface version - * - Firmware version - * - Region code - * - 11n capabilities - * - MCS support fields - * - MP end port - */ -int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_get_hw_spec *hw_spec = &resp->params.hw_spec; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_header *tlv; - struct hw_spec_api_rev *api_rev; - u16 resp_size, api_id; - int i, left_len, parsed_len = 0; - - adapter->fw_cap_info = le32_to_cpu(hw_spec->fw_cap_info); - - if (IS_SUPPORT_MULTI_BANDS(adapter)) - adapter->fw_bands = (u8) GET_FW_DEFAULT_BANDS(adapter); - else - adapter->fw_bands = BAND_B; - - adapter->config_bands = adapter->fw_bands; - - if (adapter->fw_bands & BAND_A) { - if (adapter->fw_bands & BAND_GN) { - adapter->config_bands |= BAND_AN; - adapter->fw_bands |= BAND_AN; - } - if (adapter->fw_bands & BAND_AN) { - adapter->adhoc_start_band = BAND_A | BAND_AN; - adapter->adhoc_11n_enabled = true; - } else { - adapter->adhoc_start_band = BAND_A; - } - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL_A; - } else if (adapter->fw_bands & BAND_GN) { - adapter->adhoc_start_band = BAND_G | BAND_B | BAND_GN; - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; - adapter->adhoc_11n_enabled = true; - } else if (adapter->fw_bands & BAND_G) { - adapter->adhoc_start_band = BAND_G | BAND_B; - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; - } else if (adapter->fw_bands & BAND_B) { - adapter->adhoc_start_band = BAND_B; - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; - } - - adapter->fw_release_number = le32_to_cpu(hw_spec->fw_release_number); - adapter->fw_api_ver = (adapter->fw_release_number >> 16) & 0xff; - adapter->number_of_antenna = le16_to_cpu(hw_spec->number_of_antenna); - - if (le32_to_cpu(hw_spec->dot_11ac_dev_cap)) { - adapter->is_hw_11ac_capable = true; - - /* Copy 11AC cap */ - adapter->hw_dot_11ac_dev_cap = - le32_to_cpu(hw_spec->dot_11ac_dev_cap); - adapter->usr_dot_11ac_dev_cap_bg = adapter->hw_dot_11ac_dev_cap - & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; - adapter->usr_dot_11ac_dev_cap_a = adapter->hw_dot_11ac_dev_cap - & ~MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK; - - /* Copy 11AC mcs */ - adapter->hw_dot_11ac_mcs_support = - le32_to_cpu(hw_spec->dot_11ac_mcs_support); - adapter->usr_dot_11ac_mcs_support = - adapter->hw_dot_11ac_mcs_support; - } else { - adapter->is_hw_11ac_capable = false; - } - - resp_size = le16_to_cpu(resp->size) - S_DS_GEN; - if (resp_size > sizeof(struct host_cmd_ds_get_hw_spec)) { - /* we have variable HW SPEC information */ - left_len = resp_size - sizeof(struct host_cmd_ds_get_hw_spec); - while (left_len > sizeof(struct mwifiex_ie_types_header)) { - tlv = (void *)&hw_spec->tlvs + parsed_len; - switch (le16_to_cpu(tlv->type)) { - case TLV_TYPE_API_REV: - api_rev = (struct hw_spec_api_rev *)tlv; - api_id = le16_to_cpu(api_rev->api_id); - switch (api_id) { - case KEY_API_VER_ID: - adapter->key_api_major_ver = - api_rev->major_ver; - adapter->key_api_minor_ver = - api_rev->minor_ver; - mwifiex_dbg(adapter, INFO, - "key_api v%d.%d\n", - adapter->key_api_major_ver, - adapter->key_api_minor_ver); - break; - case FW_API_VER_ID: - adapter->fw_api_ver = - api_rev->major_ver; - mwifiex_dbg(adapter, INFO, - "Firmware api version %d\n", - adapter->fw_api_ver); - break; - default: - mwifiex_dbg(adapter, FATAL, - "Unknown api_id: %d\n", - api_id); - break; - } - break; - default: - mwifiex_dbg(adapter, FATAL, - "Unknown GET_HW_SPEC TLV type: %#x\n", - le16_to_cpu(tlv->type)); - break; - } - parsed_len += le16_to_cpu(tlv->len) + - sizeof(struct mwifiex_ie_types_header); - left_len -= le16_to_cpu(tlv->len) + - sizeof(struct mwifiex_ie_types_header); - } - } - - mwifiex_dbg(adapter, INFO, - "info: GET_HW_SPEC: fw_release_number- %#x\n", - adapter->fw_release_number); - mwifiex_dbg(adapter, INFO, - "info: GET_HW_SPEC: permanent addr: %pM\n", - hw_spec->permanent_addr); - mwifiex_dbg(adapter, INFO, - "info: GET_HW_SPEC: hw_if_version=%#x version=%#x\n", - le16_to_cpu(hw_spec->hw_if_version), - le16_to_cpu(hw_spec->version)); - - ether_addr_copy(priv->adapter->perm_addr, hw_spec->permanent_addr); - adapter->region_code = le16_to_cpu(hw_spec->region_code); - - for (i = 0; i < MWIFIEX_MAX_REGION_CODE; i++) - /* Use the region code to search for the index */ - if (adapter->region_code == region_code_index[i]) - break; - - /* If it's unidentified region code, use the default (USA) */ - if (i >= MWIFIEX_MAX_REGION_CODE) { - adapter->region_code = 0x10; - mwifiex_dbg(adapter, WARN, - "cmd: unknown region code, use default (USA)\n"); - } - - adapter->hw_dot_11n_dev_cap = le32_to_cpu(hw_spec->dot_11n_dev_cap); - adapter->hw_dev_mcs_support = hw_spec->dev_mcs_support; - adapter->user_dev_mcs_support = adapter->hw_dev_mcs_support; - - if (adapter->if_ops.update_mp_end_port) - adapter->if_ops.update_mp_end_port(adapter, - le16_to_cpu(hw_spec->mp_end_port)); - - if (adapter->fw_api_ver == MWIFIEX_FW_V15) - adapter->scan_chan_gap_enabled = true; - - return 0; -} diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c deleted file mode 100644 index 9824d8dd2b44..000000000000 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ /dev/null @@ -1,1005 +0,0 @@ -/* - * Marvell Wireless LAN device driver: debugfs - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include - -#include "main.h" -#include "11n.h" - - -static struct dentry *mwifiex_dfs_dir; - -static char *bss_modes[] = { - "UNSPECIFIED", - "ADHOC", - "STATION", - "AP", - "AP_VLAN", - "WDS", - "MONITOR", - "MESH_POINT", - "P2P_CLIENT", - "P2P_GO", - "P2P_DEVICE", -}; - -/* - * Proc info file read handler. - * - * This function is called when the 'info' file is opened for reading. - * It prints the following driver related information - - * - Driver name - * - Driver version - * - Driver extended version - * - Interface name - * - BSS mode - * - Media state (connected or disconnected) - * - MAC address - * - Total number of Tx bytes - * - Total number of Rx bytes - * - Total number of Tx packets - * - Total number of Rx packets - * - Total number of dropped Tx packets - * - Total number of dropped Rx packets - * - Total number of corrupted Tx packets - * - Total number of corrupted Rx packets - * - Carrier status (on or off) - * - Tx queue status (started or stopped) - * - * For STA mode drivers, it also prints the following extra - - * - ESSID - * - BSSID - * - Channel - * - Region code - * - Multicast count - * - Multicast addresses - */ -static ssize_t -mwifiex_info_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - struct net_device *netdev = priv->netdev; - struct netdev_hw_addr *ha; - struct netdev_queue *txq; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *p = (char *) page, fmt[64]; - struct mwifiex_bss_info info; - ssize_t ret; - int i = 0; - - if (!p) - return -ENOMEM; - - memset(&info, 0, sizeof(info)); - ret = mwifiex_get_bss_info(priv, &info); - if (ret) - goto free_and_exit; - - mwifiex_drv_get_driver_version(priv->adapter, fmt, sizeof(fmt) - 1); - - if (!priv->version_str[0]) - mwifiex_get_ver_ext(priv); - - p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); - p += sprintf(p, "driver_version = %s", fmt); - p += sprintf(p, "\nverext = %s", priv->version_str); - p += sprintf(p, "\ninterface_name=\"%s\"\n", netdev->name); - - if (info.bss_mode >= ARRAY_SIZE(bss_modes)) - p += sprintf(p, "bss_mode=\"%d\"\n", info.bss_mode); - else - p += sprintf(p, "bss_mode=\"%s\"\n", bss_modes[info.bss_mode]); - - p += sprintf(p, "media_state=\"%s\"\n", - (!priv->media_connected ? "Disconnected" : "Connected")); - p += sprintf(p, "mac_address=\"%pM\"\n", netdev->dev_addr); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { - p += sprintf(p, "multicast_count=\"%d\"\n", - netdev_mc_count(netdev)); - p += sprintf(p, "essid=\"%s\"\n", info.ssid.ssid); - p += sprintf(p, "bssid=\"%pM\"\n", info.bssid); - p += sprintf(p, "channel=\"%d\"\n", (int) info.bss_chan); - p += sprintf(p, "country_code = \"%s\"\n", info.country_code); - - netdev_for_each_mc_addr(ha, netdev) - p += sprintf(p, "multicast_address[%d]=\"%pM\"\n", - i++, ha->addr); - } - - p += sprintf(p, "num_tx_bytes = %lu\n", priv->stats.tx_bytes); - p += sprintf(p, "num_rx_bytes = %lu\n", priv->stats.rx_bytes); - p += sprintf(p, "num_tx_pkts = %lu\n", priv->stats.tx_packets); - p += sprintf(p, "num_rx_pkts = %lu\n", priv->stats.rx_packets); - p += sprintf(p, "num_tx_pkts_dropped = %lu\n", priv->stats.tx_dropped); - p += sprintf(p, "num_rx_pkts_dropped = %lu\n", priv->stats.rx_dropped); - p += sprintf(p, "num_tx_pkts_err = %lu\n", priv->stats.tx_errors); - p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); - p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) - ? "on" : "off")); - p += sprintf(p, "tx queue"); - for (i = 0; i < netdev->num_tx_queues; i++) { - txq = netdev_get_tx_queue(netdev, i); - p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ? - "stopped" : "started"); - } - p += sprintf(p, "\n"); - - ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, - (unsigned long) p - page); - -free_and_exit: - free_page(page); - return ret; -} - -/* - * Proc device dump read handler. - * - * This function is called when the 'device_dump' file is opened for - * reading. - * This function dumps driver information and firmware memory segments - * (ex. DTCM, ITCM, SQRAM etc.) for - * debugging. - */ -static ssize_t -mwifiex_device_dump_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = file->private_data; - - if (!priv->adapter->if_ops.device_dump) - return -EIO; - - priv->adapter->if_ops.device_dump(priv->adapter); - - return 0; -} - -/* - * Proc getlog file read handler. - * - * This function is called when the 'getlog' file is opened for reading - * It prints the following log information - - * - Number of multicast Tx frames - * - Number of failed packets - * - Number of Tx retries - * - Number of multicast Tx retries - * - Number of duplicate frames - * - Number of RTS successes - * - Number of RTS failures - * - Number of ACK failures - * - Number of fragmented Rx frames - * - Number of multicast Rx frames - * - Number of FCS errors - * - Number of Tx frames - * - WEP ICV error counts - * - Number of received beacons - * - Number of missed beacons - */ -static ssize_t -mwifiex_getlog_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *p = (char *) page; - ssize_t ret; - struct mwifiex_ds_get_stats stats; - - if (!p) - return -ENOMEM; - - memset(&stats, 0, sizeof(stats)); - ret = mwifiex_get_stats_info(priv, &stats); - if (ret) - goto free_and_exit; - - p += sprintf(p, "\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" - "bcn_rcv_cnt %u\n" - "bcn_miss_cnt %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); - - - ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, - (unsigned long) p - page); - -free_and_exit: - free_page(page); - return ret; -} - -/* Sysfs histogram file read handler. - * - * This function is called when the 'histogram' file is opened for reading - * It prints the following histogram information - - * - Number of histogram samples - * - Receive packet number of each rx_rate - * - Receive packet number of each snr - * - Receive packet number of each nosie_flr - * - Receive packet number of each signal streath - */ -static ssize_t -mwifiex_histogram_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *)file->private_data; - ssize_t ret; - struct mwifiex_histogram_data *phist_data; - int i, value; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *p = (char *)page; - - if (!p) - return -ENOMEM; - - if (!priv || !priv->hist_data) - return -EFAULT; - phist_data = priv->hist_data; - - p += sprintf(p, "\n" - "total samples = %d\n", - atomic_read(&phist_data->num_samples)); - - p += sprintf(p, "rx rates (in Mbps): 0=1M 1=2M"); - p += sprintf(p, "2=5.5M 3=11M 4=6M 5=9M 6=12M\n"); - p += sprintf(p, "7=18M 8=24M 9=36M 10=48M 11=54M"); - p += sprintf(p, "12-27=MCS0-15(BW20) 28-43=MCS0-15(BW40)\n"); - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { - p += sprintf(p, "44-53=MCS0-9(VHT:BW20)"); - p += sprintf(p, "54-63=MCS0-9(VHT:BW40)"); - p += sprintf(p, "64-73=MCS0-9(VHT:BW80)\n\n"); - } else { - p += sprintf(p, "\n"); - } - - for (i = 0; i < MWIFIEX_MAX_RX_RATES; i++) { - value = atomic_read(&phist_data->rx_rate[i]); - if (value) - p += sprintf(p, "rx_rate[%02d] = %d\n", i, value); - } - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info)) { - for (i = MWIFIEX_MAX_RX_RATES; i < MWIFIEX_MAX_AC_RX_RATES; - i++) { - value = atomic_read(&phist_data->rx_rate[i]); - if (value) - p += sprintf(p, "rx_rate[%02d] = %d\n", - i, value); - } - } - - for (i = 0; i < MWIFIEX_MAX_SNR; i++) { - value = atomic_read(&phist_data->snr[i]); - if (value) - p += sprintf(p, "snr[%02ddB] = %d\n", i, value); - } - for (i = 0; i < MWIFIEX_MAX_NOISE_FLR; i++) { - value = atomic_read(&phist_data->noise_flr[i]); - if (value) - p += sprintf(p, "noise_flr[-%02ddBm] = %d\n", - (int)(i-128), value); - } - for (i = 0; i < MWIFIEX_MAX_SIG_STRENGTH; i++) { - value = atomic_read(&phist_data->sig_str[i]); - if (value) - p += sprintf(p, "sig_strength[-%02ddBm] = %d\n", - i, value); - } - - ret = simple_read_from_buffer(ubuf, count, ppos, (char *)page, - (unsigned long)p - page); - - return ret; -} - -static ssize_t -mwifiex_histogram_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = (void *)file->private_data; - - if (priv && priv->hist_data) - mwifiex_hist_data_reset(priv); - return 0; -} - -static struct mwifiex_debug_info info; - -/* - * Proc debug file read handler. - * - * This function is called when the 'debug' file is opened for reading - * It prints the following log information - - * - Interrupt count - * - WMM AC VO packets count - * - WMM AC VI packets count - * - WMM AC BE packets count - * - WMM AC BK packets count - * - Maximum Tx buffer size - * - Tx buffer size - * - Current Tx buffer size - * - Power Save mode - * - Power Save state - * - Deep Sleep status - * - Device wakeup required status - * - Number of wakeup tries - * - Host Sleep configured status - * - Host Sleep activated status - * - Number of Tx timeouts - * - Number of command timeouts - * - Last timed out command ID - * - Last timed out command action - * - Last command ID - * - Last command action - * - Last command index - * - Last command response ID - * - Last command response index - * - Last event - * - Last event index - * - Number of host to card command failures - * - Number of sleep confirm command failures - * - Number of host to card data failure - * - Number of deauthentication events - * - Number of disassociation events - * - Number of link lost events - * - Number of deauthentication commands - * - Number of association success commands - * - Number of association failure commands - * - Number of commands sent - * - Number of data packets sent - * - Number of command responses received - * - Number of events received - * - Tx BA stream table (TID, RA) - * - Rx reorder table (TID, TA, Start window, Window size, Buffer) - */ -static ssize_t -mwifiex_debug_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *p = (char *) page; - ssize_t ret; - - if (!p) - return -ENOMEM; - - ret = mwifiex_get_debug_info(priv, &info); - if (ret) - goto free_and_exit; - - p += mwifiex_debug_info_to_buffer(priv, p, &info); - - ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, - (unsigned long) p - page); - -free_and_exit: - free_page(page); - return ret; -} - -static u32 saved_reg_type, saved_reg_offset, saved_reg_value; - -/* - * Proc regrdwr file write handler. - * - * This function is called when the 'regrdwr' file is opened for writing - * - * This function can be used to write to a register. - */ -static ssize_t -mwifiex_regrdwr_write(struct file *file, - const char __user *ubuf, size_t count, loff_t *ppos) -{ - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *) addr; - size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); - int ret; - u32 reg_type = 0, reg_offset = 0, reg_value = UINT_MAX; - - if (!buf) - return -ENOMEM; - - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - sscanf(buf, "%u %x %x", ®_type, ®_offset, ®_value); - - if (reg_type == 0 || reg_offset == 0) { - ret = -EINVAL; - goto done; - } else { - saved_reg_type = reg_type; - saved_reg_offset = reg_offset; - saved_reg_value = reg_value; - ret = count; - } -done: - free_page(addr); - return ret; -} - -/* - * Proc regrdwr file read handler. - * - * This function is called when the 'regrdwr' file is opened for reading - * - * This function can be used to read from a register. - */ -static ssize_t -mwifiex_regrdwr_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *) addr; - int pos = 0, ret = 0; - u32 reg_value; - - if (!buf) - return -ENOMEM; - - if (!saved_reg_type) { - /* No command has been given */ - pos += snprintf(buf, PAGE_SIZE, "0"); - goto done; - } - /* Set command has been given */ - if (saved_reg_value != UINT_MAX) { - ret = mwifiex_reg_write(priv, saved_reg_type, saved_reg_offset, - saved_reg_value); - - pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", - saved_reg_type, saved_reg_offset, - saved_reg_value); - - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - - goto done; - } - /* Get command has been given */ - ret = mwifiex_reg_read(priv, saved_reg_type, - saved_reg_offset, ®_value); - if (ret) { - ret = -EINVAL; - goto done; - } - - pos += snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", saved_reg_type, - saved_reg_offset, reg_value); - - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - -done: - free_page(addr); - return ret; -} - -/* Proc debug_mask file read handler. - * This function is called when the 'debug_mask' file is opened for reading - * This function can be used read driver debugging mask value. - */ -static ssize_t -mwifiex_debug_mask_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *)file->private_data; - unsigned long page = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)page; - size_t ret = 0; - int pos = 0; - - if (!buf) - return -ENOMEM; - - pos += snprintf(buf, PAGE_SIZE, "debug mask=0x%08x\n", - priv->adapter->debug_mask); - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - - free_page(page); - return ret; -} - -/* Proc debug_mask file read handler. - * This function is called when the 'debug_mask' file is opened for reading - * This function can be used read driver debugging mask value. - */ -static ssize_t -mwifiex_debug_mask_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - int ret; - unsigned long debug_mask; - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (void *)addr; - size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1)); - - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - if (kstrtoul(buf, 0, &debug_mask)) { - ret = -EINVAL; - goto done; - } - - priv->adapter->debug_mask = debug_mask; - ret = count; -done: - free_page(addr); - return ret; -} - -/* Proc memrw file write handler. - * This function is called when the 'memrw' file is opened for writing - * This function can be used to write to a memory location. - */ -static ssize_t -mwifiex_memrw_write(struct file *file, const char __user *ubuf, size_t count, - loff_t *ppos) -{ - int ret; - char cmd; - struct mwifiex_ds_mem_rw mem_rw; - u16 cmd_action; - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (void *)addr; - size_t buf_size = min(count, (size_t)(PAGE_SIZE - 1)); - - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - ret = sscanf(buf, "%c %x %x", &cmd, &mem_rw.addr, &mem_rw.value); - if (ret != 3) { - ret = -EINVAL; - goto done; - } - - if ((cmd == 'r') || (cmd == 'R')) { - cmd_action = HostCmd_ACT_GEN_GET; - mem_rw.value = 0; - } else if ((cmd == 'w') || (cmd == 'W')) { - cmd_action = HostCmd_ACT_GEN_SET; - } else { - ret = -EINVAL; - goto done; - } - - memcpy(&priv->mem_rw, &mem_rw, sizeof(mem_rw)); - if (mwifiex_send_cmd(priv, HostCmd_CMD_MEM_ACCESS, cmd_action, 0, - &mem_rw, true)) - ret = -1; - else - ret = count; - -done: - free_page(addr); - return ret; -} - -/* Proc memrw file read handler. - * This function is called when the 'memrw' file is opened for reading - * This function can be used to read from a memory location. - */ -static ssize_t -mwifiex_memrw_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - int ret, pos = 0; - - if (!buf) - return -ENOMEM; - - pos += snprintf(buf, PAGE_SIZE, "0x%x 0x%x\n", priv->mem_rw.addr, - priv->mem_rw.value); - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - - free_page(addr); - return ret; -} - -static u32 saved_offset = -1, saved_bytes = -1; - -/* - * Proc rdeeprom file write handler. - * - * This function is called when the 'rdeeprom' file is opened for writing - * - * This function can be used to write to a RDEEPROM location. - */ -static ssize_t -mwifiex_rdeeprom_write(struct file *file, - const char __user *ubuf, size_t count, loff_t *ppos) -{ - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *) addr; - size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); - int ret = 0; - int offset = -1, bytes = -1; - - if (!buf) - return -ENOMEM; - - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - sscanf(buf, "%d %d", &offset, &bytes); - - if (offset == -1 || bytes == -1) { - ret = -EINVAL; - goto done; - } else { - saved_offset = offset; - saved_bytes = bytes; - ret = count; - } -done: - free_page(addr); - return ret; -} - -/* - * Proc rdeeprom read write handler. - * - * This function is called when the 'rdeeprom' file is opened for reading - * - * This function can be used to read from a RDEEPROM location. - */ -static ssize_t -mwifiex_rdeeprom_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = - (struct mwifiex_private *) file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *) addr; - int pos, ret, i; - u8 value[MAX_EEPROM_DATA]; - - if (!buf) - return -ENOMEM; - - if (saved_offset == -1) { - /* No command has been given */ - pos = snprintf(buf, PAGE_SIZE, "0"); - goto done; - } - - /* Get command has been given */ - ret = mwifiex_eeprom_read(priv, (u16) saved_offset, - (u16) saved_bytes, value); - if (ret) { - ret = -EINVAL; - goto out_free; - } - - pos = snprintf(buf, PAGE_SIZE, "%d %d ", saved_offset, saved_bytes); - - for (i = 0; i < saved_bytes; i++) - pos += scnprintf(buf + pos, PAGE_SIZE - pos, "%d ", value[i]); - -done: - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); -out_free: - free_page(addr); - return ret; -} - -/* Proc hscfg file write handler - * This function can be used to configure the host sleep parameters. - */ -static ssize_t -mwifiex_hscfg_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - size_t buf_size = min_t(size_t, count, PAGE_SIZE - 1); - int ret, arg_num; - struct mwifiex_ds_hs_cfg hscfg; - int conditions = HS_CFG_COND_DEF; - u32 gpio = HS_CFG_GPIO_DEF, gap = HS_CFG_GAP_DEF; - - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, ubuf, buf_size)) { - ret = -EFAULT; - goto done; - } - - arg_num = sscanf(buf, "%d %x %x", &conditions, &gpio, &gap); - - memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); - - if (arg_num > 3) { - mwifiex_dbg(priv->adapter, ERROR, - "Too many arguments\n"); - ret = -EINVAL; - goto done; - } - - if (arg_num >= 1 && arg_num < 3) - mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, - MWIFIEX_SYNC_CMD, &hscfg); - - if (arg_num) { - if (conditions == HS_CFG_CANCEL) { - mwifiex_cancel_hs(priv, MWIFIEX_ASYNC_CMD); - ret = count; - goto done; - } - hscfg.conditions = conditions; - } - if (arg_num >= 2) - hscfg.gpio = gpio; - if (arg_num == 3) - hscfg.gap = gap; - - hscfg.is_invoke_hostcmd = false; - mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, - MWIFIEX_SYNC_CMD, &hscfg); - - mwifiex_enable_hs(priv->adapter); - priv->adapter->hs_enabling = false; - ret = count; -done: - free_page(addr); - return ret; -} - -/* Proc hscfg file read handler - * This function can be used to read host sleep configuration - * parameters from driver. - */ -static ssize_t -mwifiex_hscfg_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = (void *)file->private_data; - unsigned long addr = get_zeroed_page(GFP_KERNEL); - char *buf = (char *)addr; - int pos, ret; - struct mwifiex_ds_hs_cfg hscfg; - - if (!buf) - return -ENOMEM; - - mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_GET, - MWIFIEX_SYNC_CMD, &hscfg); - - pos = snprintf(buf, PAGE_SIZE, "%u 0x%x 0x%x\n", hscfg.conditions, - hscfg.gpio, hscfg.gap); - - ret = simple_read_from_buffer(ubuf, count, ppos, buf, pos); - - free_page(addr); - return ret; -} - -static ssize_t -mwifiex_timeshare_coex_read(struct file *file, char __user *ubuf, - size_t count, loff_t *ppos) -{ - struct mwifiex_private *priv = file->private_data; - char buf[3]; - bool timeshare_coex; - int ret; - unsigned int len; - - if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) - return -EOPNOTSUPP; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, - HostCmd_ACT_GEN_GET, 0, ×hare_coex, true); - if (ret) - return ret; - - len = sprintf(buf, "%d\n", timeshare_coex); - return simple_read_from_buffer(ubuf, count, ppos, buf, len); -} - -static ssize_t -mwifiex_timeshare_coex_write(struct file *file, const char __user *ubuf, - size_t count, loff_t *ppos) -{ - bool timeshare_coex; - struct mwifiex_private *priv = file->private_data; - char kbuf[16]; - int ret; - - if (priv->adapter->fw_api_ver != MWIFIEX_FW_V15) - return -EOPNOTSUPP; - - memset(kbuf, 0, sizeof(kbuf)); - - if (copy_from_user(&kbuf, ubuf, min_t(size_t, sizeof(kbuf) - 1, count))) - return -EFAULT; - - if (strtobool(kbuf, ×hare_coex)) - return -EINVAL; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_ROBUST_COEX, - HostCmd_ACT_GEN_SET, 0, ×hare_coex, true); - if (ret) - return ret; - else - return count; -} - -#define MWIFIEX_DFS_ADD_FILE(name) do { \ - if (!debugfs_create_file(#name, 0644, priv->dfs_dev_dir, \ - priv, &mwifiex_dfs_##name##_fops)) \ - return; \ -} while (0); - -#define MWIFIEX_DFS_FILE_OPS(name) \ -static const struct file_operations mwifiex_dfs_##name##_fops = { \ - .read = mwifiex_##name##_read, \ - .write = mwifiex_##name##_write, \ - .open = simple_open, \ -}; - -#define MWIFIEX_DFS_FILE_READ_OPS(name) \ -static const struct file_operations mwifiex_dfs_##name##_fops = { \ - .read = mwifiex_##name##_read, \ - .open = simple_open, \ -}; - -#define MWIFIEX_DFS_FILE_WRITE_OPS(name) \ -static const struct file_operations mwifiex_dfs_##name##_fops = { \ - .write = mwifiex_##name##_write, \ - .open = simple_open, \ -}; - - -MWIFIEX_DFS_FILE_READ_OPS(info); -MWIFIEX_DFS_FILE_READ_OPS(debug); -MWIFIEX_DFS_FILE_READ_OPS(getlog); -MWIFIEX_DFS_FILE_READ_OPS(device_dump); -MWIFIEX_DFS_FILE_OPS(regrdwr); -MWIFIEX_DFS_FILE_OPS(rdeeprom); -MWIFIEX_DFS_FILE_OPS(memrw); -MWIFIEX_DFS_FILE_OPS(hscfg); -MWIFIEX_DFS_FILE_OPS(histogram); -MWIFIEX_DFS_FILE_OPS(debug_mask); -MWIFIEX_DFS_FILE_OPS(timeshare_coex); - -/* - * This function creates the debug FS directory structure and the files. - */ -void -mwifiex_dev_debugfs_init(struct mwifiex_private *priv) -{ - if (!mwifiex_dfs_dir || !priv) - return; - - priv->dfs_dev_dir = debugfs_create_dir(priv->netdev->name, - mwifiex_dfs_dir); - - if (!priv->dfs_dev_dir) - return; - - MWIFIEX_DFS_ADD_FILE(info); - MWIFIEX_DFS_ADD_FILE(debug); - MWIFIEX_DFS_ADD_FILE(getlog); - MWIFIEX_DFS_ADD_FILE(regrdwr); - MWIFIEX_DFS_ADD_FILE(rdeeprom); - MWIFIEX_DFS_ADD_FILE(device_dump); - MWIFIEX_DFS_ADD_FILE(memrw); - MWIFIEX_DFS_ADD_FILE(hscfg); - MWIFIEX_DFS_ADD_FILE(histogram); - MWIFIEX_DFS_ADD_FILE(debug_mask); - MWIFIEX_DFS_ADD_FILE(timeshare_coex); -} - -/* - * This function removes the debug FS directory structure and the files. - */ -void -mwifiex_dev_debugfs_remove(struct mwifiex_private *priv) -{ - if (!priv) - return; - - debugfs_remove_recursive(priv->dfs_dev_dir); -} - -/* - * This function creates the top level proc directory. - */ -void -mwifiex_debugfs_init(void) -{ - if (!mwifiex_dfs_dir) - mwifiex_dfs_dir = debugfs_create_dir("mwifiex", NULL); -} - -/* - * This function removes the top level proc directory. - */ -void -mwifiex_debugfs_remove(void) -{ - if (mwifiex_dfs_dir) - debugfs_remove(mwifiex_dfs_dir); -} diff --git a/drivers/net/wireless/mwifiex/decl.h b/drivers/net/wireless/mwifiex/decl.h deleted file mode 100644 index 098e1f14dc9a..000000000000 --- a/drivers/net/wireless/mwifiex/decl.h +++ /dev/null @@ -1,273 +0,0 @@ -/* - * Marvell Wireless LAN device driver: generic data structures and APIs - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_DECL_H_ -#define _MWIFIEX_DECL_H_ - -#undef pr_fmt -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include - -#define MWIFIEX_BSS_COEX_COUNT 2 -#define MWIFIEX_MAX_BSS_NUM (3) - -#define MWIFIEX_DMA_ALIGN_SZ 64 -#define MWIFIEX_RX_HEADROOM 64 -#define MAX_TXPD_SZ 32 -#define INTF_HDR_ALIGN 4 - -#define MWIFIEX_MIN_DATA_HEADER_LEN (MWIFIEX_DMA_ALIGN_SZ + INTF_HDR_ALIGN + \ - MAX_TXPD_SZ) -#define MWIFIEX_MGMT_FRAME_HEADER_SIZE 8 /* sizeof(pkt_type) - * + sizeof(tx_control) - */ - -#define MWIFIEX_MAX_TX_BASTREAM_SUPPORTED 2 -#define MWIFIEX_MAX_RX_BASTREAM_SUPPORTED 16 -#define MWIFIEX_MAX_TDLS_PEER_SUPPORTED 8 - -#define MWIFIEX_STA_AMPDU_DEF_TXWINSIZE 64 -#define MWIFIEX_STA_AMPDU_DEF_RXWINSIZE 64 -#define MWIFIEX_STA_COEX_AMPDU_DEF_RXWINSIZE 16 - -#define MWIFIEX_UAP_AMPDU_DEF_TXWINSIZE 32 - -#define MWIFIEX_UAP_COEX_AMPDU_DEF_RXWINSIZE 16 - -#define MWIFIEX_UAP_AMPDU_DEF_RXWINSIZE 16 -#define MWIFIEX_11AC_STA_AMPDU_DEF_TXWINSIZE 64 -#define MWIFIEX_11AC_STA_AMPDU_DEF_RXWINSIZE 64 -#define MWIFIEX_11AC_UAP_AMPDU_DEF_TXWINSIZE 64 -#define MWIFIEX_11AC_UAP_AMPDU_DEF_RXWINSIZE 64 - -#define MWIFIEX_DEFAULT_BLOCK_ACK_TIMEOUT 0xffff - -#define MWIFIEX_RATE_BITMAP_MCS0 32 - -#define MWIFIEX_RX_DATA_BUF_SIZE (4 * 1024) -#define MWIFIEX_RX_CMD_BUF_SIZE (2 * 1024) - -#define MAX_BEACON_PERIOD (4000) -#define MIN_BEACON_PERIOD (50) -#define MAX_DTIM_PERIOD (100) -#define MIN_DTIM_PERIOD (1) - -#define MWIFIEX_RTS_MIN_VALUE (0) -#define MWIFIEX_RTS_MAX_VALUE (2347) -#define MWIFIEX_FRAG_MIN_VALUE (256) -#define MWIFIEX_FRAG_MAX_VALUE (2346) -#define MWIFIEX_WMM_VERSION 0x01 -#define MWIFIEX_WMM_SUBTYPE 0x01 - -#define MWIFIEX_RETRY_LIMIT 14 -#define MWIFIEX_SDIO_BLOCK_SIZE 256 - -#define MWIFIEX_BUF_FLAG_REQUEUED_PKT BIT(0) -#define MWIFIEX_BUF_FLAG_BRIDGED_PKT BIT(1) -#define MWIFIEX_BUF_FLAG_TDLS_PKT BIT(2) -#define MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS BIT(3) -#define MWIFIEX_BUF_FLAG_ACTION_TX_STATUS BIT(4) -#define MWIFIEX_BUF_FLAG_AGGR_PKT BIT(5) - -#define MWIFIEX_BRIDGED_PKTS_THR_HIGH 1024 -#define MWIFIEX_BRIDGED_PKTS_THR_LOW 128 - -#define MWIFIEX_TDLS_DISABLE_LINK 0x00 -#define MWIFIEX_TDLS_ENABLE_LINK 0x01 -#define MWIFIEX_TDLS_CREATE_LINK 0x02 -#define MWIFIEX_TDLS_CONFIG_LINK 0x03 - -#define MWIFIEX_TDLS_RSSI_HIGH 50 -#define MWIFIEX_TDLS_RSSI_LOW 55 -#define MWIFIEX_TDLS_MAX_FAIL_COUNT 4 -#define MWIFIEX_AUTO_TDLS_IDLE_TIME 10 - -/* 54M rates, index from 0 to 11 */ -#define MWIFIEX_RATE_INDEX_MCS0 12 -/* 12-27=MCS0-15(BW20) */ -#define MWIFIEX_BW20_MCS_NUM 15 - -/* Rate index for OFDM 0 */ -#define MWIFIEX_RATE_INDEX_OFDM0 4 - -#define MWIFIEX_MAX_STA_NUM 1 -#define MWIFIEX_MAX_UAP_NUM 1 -#define MWIFIEX_MAX_P2P_NUM 1 - -#define MWIFIEX_A_BAND_START_FREQ 5000 - -/* SDIO Aggr data packet special info */ -#define SDIO_MAX_AGGR_BUF_SIZE (256 * 255) -#define BLOCK_NUMBER_OFFSET 15 -#define SDIO_HEADER_OFFSET 28 - -enum mwifiex_bss_type { - MWIFIEX_BSS_TYPE_STA = 0, - MWIFIEX_BSS_TYPE_UAP = 1, - MWIFIEX_BSS_TYPE_P2P = 2, - MWIFIEX_BSS_TYPE_ANY = 0xff, -}; - -enum mwifiex_bss_role { - MWIFIEX_BSS_ROLE_STA = 0, - MWIFIEX_BSS_ROLE_UAP = 1, - MWIFIEX_BSS_ROLE_ANY = 0xff, -}; - -enum mwifiex_tdls_status { - TDLS_NOT_SETUP = 0, - TDLS_SETUP_INPROGRESS, - TDLS_SETUP_COMPLETE, - TDLS_SETUP_FAILURE, - TDLS_LINK_TEARDOWN, - TDLS_CHAN_SWITCHING, - TDLS_IN_BASE_CHAN, - TDLS_IN_OFF_CHAN, -}; - -enum mwifiex_tdls_error_code { - TDLS_ERR_NO_ERROR = 0, - TDLS_ERR_INTERNAL_ERROR, - TDLS_ERR_MAX_LINKS_EST, - TDLS_ERR_LINK_EXISTS, - TDLS_ERR_LINK_NONEXISTENT, - TDLS_ERR_PEER_STA_UNREACHABLE = 25, -}; - -#define BSS_ROLE_BIT_MASK BIT(0) - -#define GET_BSS_ROLE(priv) ((priv)->bss_role & BSS_ROLE_BIT_MASK) - -enum mwifiex_data_frame_type { - MWIFIEX_DATA_FRAME_TYPE_ETH_II = 0, - MWIFIEX_DATA_FRAME_TYPE_802_11, -}; - -struct mwifiex_fw_image { - u8 *helper_buf; - u32 helper_len; - u8 *fw_buf; - u32 fw_len; -}; - -struct mwifiex_802_11_ssid { - u32 ssid_len; - u8 ssid[IEEE80211_MAX_SSID_LEN]; -}; - -struct mwifiex_wait_queue { - wait_queue_head_t wait; - int status; -}; - -struct mwifiex_rxinfo { - struct sk_buff *parent; - u8 bss_num; - u8 bss_type; - u8 use_count; - u8 buf_type; -}; - -struct mwifiex_txinfo { - u32 status_code; - u8 flags; - u8 bss_num; - u8 bss_type; - u8 aggr_num; - u32 pkt_len; - u8 ack_frame_id; - u64 cookie; -}; - -enum mwifiex_wmm_ac_e { - WMM_AC_BK, - WMM_AC_BE, - WMM_AC_VI, - WMM_AC_VO -} __packed; - -struct ieee_types_wmm_ac_parameters { - u8 aci_aifsn_bitmap; - u8 ecw_bitmap; - __le16 tx_op_limit; -} __packed; - -struct mwifiex_types_wmm_info { - u8 oui[4]; - u8 subtype; - u8 version; - u8 qos_info; - u8 reserved; - struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; -} __packed; - -struct mwifiex_arp_eth_header { - struct arphdr hdr; - u8 ar_sha[ETH_ALEN]; - u8 ar_sip[4]; - u8 ar_tha[ETH_ALEN]; - u8 ar_tip[4]; -} __packed; - -struct mwifiex_chan_stats { - u8 chan_num; - u8 bandcfg; - u8 flags; - s8 noise; - u16 total_bss; - u16 cca_scan_dur; - u16 cca_busy_dur; -} __packed; - -#define MWIFIEX_HIST_MAX_SAMPLES 1048576 -#define MWIFIEX_MAX_RX_RATES 44 -#define MWIFIEX_MAX_AC_RX_RATES 74 -#define MWIFIEX_MAX_SNR 256 -#define MWIFIEX_MAX_NOISE_FLR 256 -#define MWIFIEX_MAX_SIG_STRENGTH 256 - -struct mwifiex_histogram_data { - atomic_t rx_rate[MWIFIEX_MAX_AC_RX_RATES]; - atomic_t snr[MWIFIEX_MAX_SNR]; - atomic_t noise_flr[MWIFIEX_MAX_NOISE_FLR]; - atomic_t sig_str[MWIFIEX_MAX_SIG_STRENGTH]; - atomic_t num_samples; -}; - -struct mwifiex_iface_comb { - u8 sta_intf; - u8 uap_intf; - u8 p2p_intf; -}; - -struct mwifiex_radar_params { - struct cfg80211_chan_def *chandef; - u32 cac_time_ms; -} __packed; - -struct mwifiex_11h_intf_state { - bool is_11h_enabled; - bool is_11h_active; -} __packed; -#endif /* !_MWIFIEX_DECL_H_ */ diff --git a/drivers/net/wireless/mwifiex/ethtool.c b/drivers/net/wireless/mwifiex/ethtool.c deleted file mode 100644 index 58400c69ab26..000000000000 --- a/drivers/net/wireless/mwifiex/ethtool.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Marvell Wireless LAN device driver: ethtool - * - * Copyright (C) 2013-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "main.h" - -static void mwifiex_ethtool_get_wol(struct net_device *dev, - struct ethtool_wolinfo *wol) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - u32 conditions = le32_to_cpu(priv->adapter->hs_cfg.conditions); - - wol->supported = WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY; - - if (conditions == HS_CFG_COND_DEF) - return; - - if (conditions & HS_CFG_COND_UNICAST_DATA) - wol->wolopts |= WAKE_UCAST; - if (conditions & HS_CFG_COND_MULTICAST_DATA) - wol->wolopts |= WAKE_MCAST; - if (conditions & HS_CFG_COND_BROADCAST_DATA) - wol->wolopts |= WAKE_BCAST; - if (conditions & HS_CFG_COND_MAC_EVENT) - wol->wolopts |= WAKE_PHY; -} - -static int mwifiex_ethtool_set_wol(struct net_device *dev, - struct ethtool_wolinfo *wol) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - u32 conditions = 0; - - if (wol->wolopts & ~(WAKE_UCAST|WAKE_MCAST|WAKE_BCAST|WAKE_PHY)) - return -EOPNOTSUPP; - - if (wol->wolopts & WAKE_UCAST) - conditions |= HS_CFG_COND_UNICAST_DATA; - if (wol->wolopts & WAKE_MCAST) - conditions |= HS_CFG_COND_MULTICAST_DATA; - if (wol->wolopts & WAKE_BCAST) - conditions |= HS_CFG_COND_BROADCAST_DATA; - if (wol->wolopts & WAKE_PHY) - conditions |= HS_CFG_COND_MAC_EVENT; - if (wol->wolopts == 0) - conditions |= HS_CFG_COND_DEF; - priv->adapter->hs_cfg.conditions = cpu_to_le32(conditions); - - return 0; -} - -const struct ethtool_ops mwifiex_ethtool_ops = { - .get_wol = mwifiex_ethtool_get_wol, - .set_wol = mwifiex_ethtool_set_wol, -}; diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h deleted file mode 100644 index 1e1e81a0a8d4..000000000000 --- a/drivers/net/wireless/mwifiex/fw.h +++ /dev/null @@ -1,2177 +0,0 @@ -/* - * Marvell Wireless LAN device driver: Firmware specific macros & structures - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_FW_H_ -#define _MWIFIEX_FW_H_ - -#include - - -#define INTF_HEADER_LEN 4 - -struct rfc_1042_hdr { - u8 llc_dsap; - u8 llc_ssap; - u8 llc_ctrl; - u8 snap_oui[3]; - __be16 snap_type; -}; - -struct rx_packet_hdr { - struct ethhdr eth803_hdr; - struct rfc_1042_hdr rfc1042_hdr; -}; - -struct tx_packet_hdr { - struct ethhdr eth803_hdr; - struct rfc_1042_hdr rfc1042_hdr; -}; - -#define B_SUPPORTED_RATES 5 -#define G_SUPPORTED_RATES 9 -#define BG_SUPPORTED_RATES 13 -#define A_SUPPORTED_RATES 9 -#define HOSTCMD_SUPPORTED_RATES 14 -#define N_SUPPORTED_RATES 3 -#define ALL_802_11_BANDS (BAND_A | BAND_B | BAND_G | BAND_GN | \ - BAND_AN | BAND_AAC) - -#define FW_MULTI_BANDS_SUPPORT (BIT(8) | BIT(9) | BIT(10) | BIT(11) | \ - BIT(13)) -#define IS_SUPPORT_MULTI_BANDS(adapter) \ - (adapter->fw_cap_info & FW_MULTI_BANDS_SUPPORT) - -/* bit 13: 11ac BAND_AAC - * bit 12: reserved for lab testing, will be reused for BAND_AN - * bit 11: 11n BAND_GN - * bit 10: 11a BAND_A - * bit 9: 11g BAND_G - * bit 8: 11b BAND_B - * Map these bits to band capability by right shifting 8 bits. - */ -#define GET_FW_DEFAULT_BANDS(adapter) \ - (((adapter->fw_cap_info & 0x2f00) >> 8) & \ - ALL_802_11_BANDS) - -#define HostCmd_WEP_KEY_INDEX_MASK 0x3fff - -#define KEY_INFO_ENABLED 0x01 -enum KEY_TYPE_ID { - KEY_TYPE_ID_WEP = 0, - KEY_TYPE_ID_TKIP, - KEY_TYPE_ID_AES, - KEY_TYPE_ID_WAPI, - KEY_TYPE_ID_AES_CMAC, -}; - -#define WPA_PN_SIZE 8 -#define KEY_PARAMS_FIXED_LEN 10 -#define KEY_INDEX_MASK 0xf -#define KEY_API_VER_MAJOR_V2 2 - -#define KEY_MCAST BIT(0) -#define KEY_UNICAST BIT(1) -#define KEY_ENABLED BIT(2) -#define KEY_DEFAULT BIT(3) -#define KEY_TX_KEY BIT(4) -#define KEY_RX_KEY BIT(5) -#define KEY_IGTK BIT(10) - -#define WAPI_KEY_LEN (WLAN_KEY_LEN_SMS4 + PN_LEN + 2) - -#define MAX_POLL_TRIES 100 -#define MAX_FIRMWARE_POLL_TRIES 100 - -#define FIRMWARE_READY_SDIO 0xfedc -#define FIRMWARE_READY_PCIE 0xfedcba00 - -#define MWIFIEX_COEX_MODE_TIMESHARE 0x01 -#define MWIFIEX_COEX_MODE_SPATIAL 0x82 - -enum mwifiex_usb_ep { - MWIFIEX_USB_EP_CMD_EVENT = 1, - MWIFIEX_USB_EP_DATA = 2, - MWIFIEX_USB_EP_DATA_CH2 = 3, -}; - -enum MWIFIEX_802_11_PRIVACY_FILTER { - MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL, - MWIFIEX_802_11_PRIV_FILTER_8021X_WEP -}; - -#define CAL_SNR(RSSI, NF) ((s16)((s16)(RSSI)-(s16)(NF))) -#define CAL_RSSI(SNR, NF) ((s16)((s16)(SNR)+(s16)(NF))) - -#define UAP_BSS_PARAMS_I 0 -#define UAP_CUSTOM_IE_I 1 -#define MWIFIEX_AUTO_IDX_MASK 0xffff -#define MWIFIEX_DELETE_MASK 0x0000 -#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 TLV_TYPE_UAP_SSID 0x0000 -#define TLV_TYPE_UAP_RATES 0x0001 -#define TLV_TYPE_PWR_CONSTRAINT 0x0020 - -#define PROPRIETARY_TLV_BASE_ID 0x0100 -#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) -#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) -#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) -#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) -#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) -#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) -#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) -#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) -#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) -#define TLV_TYPE_AUTH_TYPE (PROPRIETARY_TLV_BASE_ID + 31) -#define TLV_TYPE_STA_MAC_ADDR (PROPRIETARY_TLV_BASE_ID + 32) -#define TLV_TYPE_BSSID (PROPRIETARY_TLV_BASE_ID + 35) -#define TLV_TYPE_CHANNELBANDLIST (PROPRIETARY_TLV_BASE_ID + 42) -#define TLV_TYPE_UAP_BEACON_PERIOD (PROPRIETARY_TLV_BASE_ID + 44) -#define TLV_TYPE_UAP_DTIM_PERIOD (PROPRIETARY_TLV_BASE_ID + 45) -#define TLV_TYPE_UAP_BCAST_SSID (PROPRIETARY_TLV_BASE_ID + 48) -#define TLV_TYPE_UAP_RTS_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 51) -#define TLV_TYPE_UAP_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 57) -#define TLV_TYPE_UAP_WEP_KEY (PROPRIETARY_TLV_BASE_ID + 59) -#define TLV_TYPE_UAP_WPA_PASSPHRASE (PROPRIETARY_TLV_BASE_ID + 60) -#define TLV_TYPE_UAP_ENCRY_PROTOCOL (PROPRIETARY_TLV_BASE_ID + 64) -#define TLV_TYPE_UAP_AKMP (PROPRIETARY_TLV_BASE_ID + 65) -#define TLV_TYPE_UAP_FRAG_THRESHOLD (PROPRIETARY_TLV_BASE_ID + 70) -#define TLV_TYPE_RATE_DROP_CONTROL (PROPRIETARY_TLV_BASE_ID + 82) -#define TLV_TYPE_RATE_SCOPE (PROPRIETARY_TLV_BASE_ID + 83) -#define TLV_TYPE_POWER_GROUP (PROPRIETARY_TLV_BASE_ID + 84) -#define TLV_TYPE_BSS_SCAN_RSP (PROPRIETARY_TLV_BASE_ID + 86) -#define TLV_TYPE_BSS_SCAN_INFO (PROPRIETARY_TLV_BASE_ID + 87) -#define TLV_TYPE_CHANRPT_11H_BASIC (PROPRIETARY_TLV_BASE_ID + 91) -#define TLV_TYPE_UAP_RETRY_LIMIT (PROPRIETARY_TLV_BASE_ID + 93) -#define TLV_TYPE_WAPI_IE (PROPRIETARY_TLV_BASE_ID + 94) -#define TLV_TYPE_ROBUST_COEX (PROPRIETARY_TLV_BASE_ID + 96) -#define TLV_TYPE_UAP_MGMT_FRAME (PROPRIETARY_TLV_BASE_ID + 104) -#define TLV_TYPE_MGMT_IE (PROPRIETARY_TLV_BASE_ID + 105) -#define TLV_TYPE_AUTO_DS_PARAM (PROPRIETARY_TLV_BASE_ID + 113) -#define TLV_TYPE_PS_PARAM (PROPRIETARY_TLV_BASE_ID + 114) -#define TLV_TYPE_UAP_PS_AO_TIMER (PROPRIETARY_TLV_BASE_ID + 123) -#define TLV_TYPE_PWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 145) -#define TLV_TYPE_GWK_CIPHER (PROPRIETARY_TLV_BASE_ID + 146) -#define TLV_TYPE_TX_PAUSE (PROPRIETARY_TLV_BASE_ID + 148) -#define TLV_TYPE_COALESCE_RULE (PROPRIETARY_TLV_BASE_ID + 154) -#define TLV_TYPE_KEY_PARAM_V2 (PROPRIETARY_TLV_BASE_ID + 156) -#define TLV_TYPE_MULTI_CHAN_INFO (PROPRIETARY_TLV_BASE_ID + 183) -#define TLV_TYPE_MC_GROUP_INFO (PROPRIETARY_TLV_BASE_ID + 184) -#define TLV_TYPE_TDLS_IDLE_TIMEOUT (PROPRIETARY_TLV_BASE_ID + 194) -#define TLV_TYPE_SCAN_CHANNEL_GAP (PROPRIETARY_TLV_BASE_ID + 197) -#define TLV_TYPE_API_REV (PROPRIETARY_TLV_BASE_ID + 199) -#define TLV_TYPE_CHANNEL_STATS (PROPRIETARY_TLV_BASE_ID + 198) -#define TLV_BTCOEX_WL_AGGR_WINSIZE (PROPRIETARY_TLV_BASE_ID + 202) -#define TLV_BTCOEX_WL_SCANTIME (PROPRIETARY_TLV_BASE_ID + 203) -#define TLV_TYPE_BSS_MODE (PROPRIETARY_TLV_BASE_ID + 206) - -#define MWIFIEX_TX_DATA_BUF_SIZE_2K 2048 - -#define SSN_MASK 0xfff0 - -#define BA_RESULT_SUCCESS 0x0 -#define BA_RESULT_TIMEOUT 0x2 - -#define IS_BASTREAM_SETUP(ptr) (ptr->ba_status) - -#define BA_STREAM_NOT_ALLOWED 0xff - -#define IS_11N_ENABLED(priv) ((priv->adapter->config_bands & BAND_GN || \ - priv->adapter->config_bands & BAND_AN) && \ - priv->curr_bss_params.bss_descriptor.bcn_ht_cap) -#define INITIATOR_BIT(DelBAParamSet) (((DelBAParamSet) &\ - BIT(DELBA_INITIATOR_POS)) >> DELBA_INITIATOR_POS) - -#define MWIFIEX_TX_DATA_BUF_SIZE_4K 4096 -#define MWIFIEX_TX_DATA_BUF_SIZE_8K 8192 - -#define ISSUPP_11NENABLED(FwCapInfo) (FwCapInfo & BIT(11)) -#define ISSUPP_TDLS_ENABLED(FwCapInfo) (FwCapInfo & BIT(14)) -#define ISSUPP_DRCS_ENABLED(FwCapInfo) (FwCapInfo & BIT(15)) -#define ISSUPP_SDIO_SPA_ENABLED(FwCapInfo) (FwCapInfo & BIT(16)) - -#define MWIFIEX_DEF_HT_CAP (IEEE80211_HT_CAP_DSSSCCK40 | \ - (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT) | \ - IEEE80211_HT_CAP_SM_PS) - -#define MWIFIEX_DEF_11N_TX_BF_CAP 0x09E1E008 - -#define MWIFIEX_DEF_AMPDU IEEE80211_HT_AMPDU_PARM_FACTOR - -#define GET_RXSTBC(x) (x & IEEE80211_HT_CAP_RX_STBC) -#define MWIFIEX_RX_STBC1 0x0100 -#define MWIFIEX_RX_STBC12 0x0200 -#define MWIFIEX_RX_STBC123 0x0300 - -/* dev_cap bitmap - * BIT - * 0-16 reserved - * 17 IEEE80211_HT_CAP_SUP_WIDTH_20_40 - * 18-22 reserved - * 23 IEEE80211_HT_CAP_SGI_20 - * 24 IEEE80211_HT_CAP_SGI_40 - * 25 IEEE80211_HT_CAP_TX_STBC - * 26 IEEE80211_HT_CAP_RX_STBC - * 27-28 reserved - * 29 IEEE80211_HT_CAP_GRN_FLD - * 30-31 reserved - */ -#define ISSUPP_CHANWIDTH40(Dot11nDevCap) (Dot11nDevCap & BIT(17)) -#define ISSUPP_SHORTGI20(Dot11nDevCap) (Dot11nDevCap & BIT(23)) -#define ISSUPP_SHORTGI40(Dot11nDevCap) (Dot11nDevCap & BIT(24)) -#define ISSUPP_TXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(25)) -#define ISSUPP_RXSTBC(Dot11nDevCap) (Dot11nDevCap & BIT(26)) -#define ISSUPP_GREENFIELD(Dot11nDevCap) (Dot11nDevCap & BIT(29)) -#define ISENABLED_40MHZ_INTOLERANT(Dot11nDevCap) (Dot11nDevCap & BIT(8)) -#define ISSUPP_RXLDPC(Dot11nDevCap) (Dot11nDevCap & BIT(22)) -#define ISSUPP_BEAMFORMING(Dot11nDevCap) (Dot11nDevCap & BIT(30)) -#define ISALLOWED_CHANWIDTH40(ht_param) (ht_param & BIT(2)) -#define GETSUPP_TXBASTREAMS(Dot11nDevCap) ((Dot11nDevCap >> 18) & 0xF) - -/* httxcfg bitmap - * 0 reserved - * 1 20/40 Mhz enable(1)/disable(0) - * 2-3 reserved - * 4 green field enable(1)/disable(0) - * 5 short GI in 20 Mhz enable(1)/disable(0) - * 6 short GI in 40 Mhz enable(1)/disable(0) - * 7-15 reserved - */ -#define MWIFIEX_FW_DEF_HTTXCFG (BIT(1) | BIT(4) | BIT(5) | BIT(6)) - -/* 11AC Tx and Rx MCS map for 1x1 mode: - * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 - * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 7 streams - */ -#define MWIFIEX_11AC_MCS_MAP_1X1 0xfffefffe - -/* 11AC Tx and Rx MCS map for 2x2 mode: - * IEEE80211_VHT_MCS_SUPPORT_0_9 for stream 1 and 2 - * IEEE80211_VHT_MCS_NOT_SUPPORTED for remaining 6 streams - */ -#define MWIFIEX_11AC_MCS_MAP_2X2 0xfffafffa - -#define GET_RXMCSSUPP(DevMCSSupported) (DevMCSSupported & 0x0f) -#define SETHT_MCS32(x) (x[4] |= 1) -#define HT_STREAM_1X1 0x11 -#define HT_STREAM_2X2 0x22 - -#define SET_SECONDARYCHAN(RadioType, SECCHAN) (RadioType |= (SECCHAN << 4)) - -#define LLC_SNAP_LEN 8 - -/* HW_SPEC fw_cap_info */ - -#define ISSUPP_11ACENABLED(fw_cap_info) (fw_cap_info & BIT(13)) - -#define GET_VHTCAP_CHWDSET(vht_cap_info) ((vht_cap_info >> 2) & 0x3) -#define GET_VHTNSSMCS(mcs_mapset, nss) ((mcs_mapset >> (2 * (nss - 1))) & 0x3) -#define SET_VHTNSSMCS(mcs_mapset, nss, value) (mcs_mapset |= (value & 0x3) << \ - (2 * (nss - 1))) -#define GET_DEVTXMCSMAP(dev_mcs_map) (dev_mcs_map >> 16) -#define GET_DEVRXMCSMAP(dev_mcs_map) (dev_mcs_map & 0xFFFF) - -/* Clear SU Beanformer, MU beanformer, MU beanformee and - * sounding dimensions bits - */ -#define MWIFIEX_DEF_11AC_CAP_BF_RESET_MASK \ - (IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | \ - IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE | \ - IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE | \ - IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK) - -#define MOD_CLASS_HR_DSSS 0x03 -#define MOD_CLASS_OFDM 0x07 -#define MOD_CLASS_HT 0x08 -#define HT_BW_20 0 -#define HT_BW_40 1 - -#define DFS_CHAN_MOVE_TIME 10000 - -#define HostCmd_CMD_GET_HW_SPEC 0x0003 -#define HostCmd_CMD_802_11_SCAN 0x0006 -#define HostCmd_CMD_802_11_GET_LOG 0x000b -#define HostCmd_CMD_MAC_MULTICAST_ADR 0x0010 -#define HostCmd_CMD_802_11_EEPROM_ACCESS 0x0059 -#define HostCmd_CMD_802_11_ASSOCIATE 0x0012 -#define HostCmd_CMD_802_11_SNMP_MIB 0x0016 -#define HostCmd_CMD_MAC_REG_ACCESS 0x0019 -#define HostCmd_CMD_BBP_REG_ACCESS 0x001a -#define HostCmd_CMD_RF_REG_ACCESS 0x001b -#define HostCmd_CMD_PMIC_REG_ACCESS 0x00ad -#define HostCmd_CMD_RF_TX_PWR 0x001e -#define HostCmd_CMD_RF_ANTENNA 0x0020 -#define HostCmd_CMD_802_11_DEAUTHENTICATE 0x0024 -#define HostCmd_CMD_MAC_CONTROL 0x0028 -#define HostCmd_CMD_802_11_AD_HOC_START 0x002b -#define HostCmd_CMD_802_11_AD_HOC_JOIN 0x002c -#define HostCmd_CMD_802_11_AD_HOC_STOP 0x0040 -#define HostCmd_CMD_802_11_MAC_ADDRESS 0x004D -#define HostCmd_CMD_802_11D_DOMAIN_INFO 0x005b -#define HostCmd_CMD_802_11_KEY_MATERIAL 0x005e -#define HostCmd_CMD_802_11_BG_SCAN_QUERY 0x006c -#define HostCmd_CMD_WMM_GET_STATUS 0x0071 -#define HostCmd_CMD_802_11_SUBSCRIBE_EVENT 0x0075 -#define HostCmd_CMD_802_11_TX_RATE_QUERY 0x007f -#define HostCmd_CMD_802_11_IBSS_COALESCING_STATUS 0x0083 -#define HostCmd_CMD_MEM_ACCESS 0x0086 -#define HostCmd_CMD_CFG_DATA 0x008f -#define HostCmd_CMD_VERSION_EXT 0x0097 -#define HostCmd_CMD_MEF_CFG 0x009a -#define HostCmd_CMD_RSSI_INFO 0x00a4 -#define HostCmd_CMD_FUNC_INIT 0x00a9 -#define HostCmd_CMD_FUNC_SHUTDOWN 0x00aa -#define HOST_CMD_APCMD_SYS_RESET 0x00af -#define HostCmd_CMD_UAP_SYS_CONFIG 0x00b0 -#define HostCmd_CMD_UAP_BSS_START 0x00b1 -#define HostCmd_CMD_UAP_BSS_STOP 0x00b2 -#define HOST_CMD_APCMD_STA_LIST 0x00b3 -#define HostCmd_CMD_UAP_STA_DEAUTH 0x00b5 -#define HostCmd_CMD_11N_CFG 0x00cd -#define HostCmd_CMD_11N_ADDBA_REQ 0x00ce -#define HostCmd_CMD_11N_ADDBA_RSP 0x00cf -#define HostCmd_CMD_11N_DELBA 0x00d0 -#define HostCmd_CMD_RECONFIGURE_TX_BUFF 0x00d9 -#define HostCmd_CMD_CHAN_REPORT_REQUEST 0x00dd -#define HostCmd_CMD_AMSDU_AGGR_CTRL 0x00df -#define HostCmd_CMD_TXPWR_CFG 0x00d1 -#define HostCmd_CMD_TX_RATE_CFG 0x00d6 -#define HostCmd_CMD_ROBUST_COEX 0x00e0 -#define HostCmd_CMD_802_11_PS_MODE_ENH 0x00e4 -#define HostCmd_CMD_802_11_HS_CFG_ENH 0x00e5 -#define HostCmd_CMD_P2P_MODE_CFG 0x00eb -#define HostCmd_CMD_CAU_REG_ACCESS 0x00ed -#define HostCmd_CMD_SET_BSS_MODE 0x00f7 -#define HostCmd_CMD_PCIE_DESC_DETAILS 0x00fa -#define HostCmd_CMD_802_11_SCAN_EXT 0x0107 -#define HostCmd_CMD_COALESCE_CFG 0x010a -#define HostCmd_CMD_MGMT_FRAME_REG 0x010c -#define HostCmd_CMD_REMAIN_ON_CHAN 0x010d -#define HostCmd_CMD_11AC_CFG 0x0112 -#define HostCmd_CMD_TDLS_CONFIG 0x0100 -#define HostCmd_CMD_MC_POLICY 0x0121 -#define HostCmd_CMD_TDLS_OPER 0x0122 -#define HostCmd_CMD_SDIO_SP_RX_AGGR_CFG 0x0223 - -#define PROTOCOL_NO_SECURITY 0x01 -#define PROTOCOL_STATIC_WEP 0x02 -#define PROTOCOL_WPA 0x08 -#define PROTOCOL_WPA2 0x20 -#define PROTOCOL_WPA2_MIXED 0x28 -#define PROTOCOL_EAP 0x40 -#define KEY_MGMT_NONE 0x04 -#define KEY_MGMT_PSK 0x02 -#define KEY_MGMT_EAP 0x01 -#define CIPHER_TKIP 0x04 -#define CIPHER_AES_CCMP 0x08 -#define VALID_CIPHER_BITMAP 0x0c - -enum ENH_PS_MODES { - EN_PS = 1, - DIS_PS = 2, - EN_AUTO_DS = 3, - DIS_AUTO_DS = 4, - SLEEP_CONFIRM = 5, - GET_PS = 0, - EN_AUTO_PS = 0xff, - DIS_AUTO_PS = 0xfe, -}; - -enum P2P_MODES { - P2P_MODE_DISABLE = 0, - P2P_MODE_DEVICE = 1, - P2P_MODE_GO = 2, - P2P_MODE_CLIENT = 3, -}; - -#define HostCmd_RET_BIT 0x8000 -#define HostCmd_ACT_GEN_GET 0x0000 -#define HostCmd_ACT_GEN_SET 0x0001 -#define HostCmd_ACT_GEN_REMOVE 0x0004 -#define HostCmd_ACT_BITWISE_SET 0x0002 -#define HostCmd_ACT_BITWISE_CLR 0x0003 -#define HostCmd_RESULT_OK 0x0000 - -#define HostCmd_ACT_MAC_RX_ON 0x0001 -#define HostCmd_ACT_MAC_TX_ON 0x0002 -#define HostCmd_ACT_MAC_WEP_ENABLE 0x0008 -#define HostCmd_ACT_MAC_ETHERNETII_ENABLE 0x0010 -#define HostCmd_ACT_MAC_PROMISCUOUS_ENABLE 0x0080 -#define HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE 0x0100 -#define HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON 0x2000 - -#define HostCmd_BSS_MODE_IBSS 0x0002 -#define HostCmd_BSS_MODE_ANY 0x0003 - -#define HostCmd_SCAN_RADIO_TYPE_BG 0 -#define HostCmd_SCAN_RADIO_TYPE_A 1 - -#define HS_CFG_CANCEL 0xffffffff -#define HS_CFG_COND_DEF 0x00000000 -#define HS_CFG_GPIO_DEF 0xff -#define HS_CFG_GAP_DEF 0xff -#define HS_CFG_COND_BROADCAST_DATA 0x00000001 -#define HS_CFG_COND_UNICAST_DATA 0x00000002 -#define HS_CFG_COND_MAC_EVENT 0x00000004 -#define HS_CFG_COND_MULTICAST_DATA 0x00000008 - -#define CONNECT_ERR_AUTH_ERR_STA_FAILURE 0xFFFB -#define CONNECT_ERR_ASSOC_ERR_TIMEOUT 0xFFFC -#define CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED 0xFFFD -#define CONNECT_ERR_AUTH_MSG_UNHANDLED 0xFFFE -#define CONNECT_ERR_STA_FAILURE 0xFFFF - - -#define CMD_F_HOSTCMD (1 << 0) - -#define HostCmd_CMD_ID_MASK 0x0fff - -#define HostCmd_SEQ_NUM_MASK 0x00ff - -#define HostCmd_BSS_NUM_MASK 0x0f00 - -#define HostCmd_BSS_TYPE_MASK 0xf000 - -#define HostCmd_ACT_SET_RX 0x0001 -#define HostCmd_ACT_SET_TX 0x0002 -#define HostCmd_ACT_SET_BOTH 0x0003 - -#define RF_ANTENNA_AUTO 0xFFFF - -#define HostCmd_SET_SEQ_NO_BSS_INFO(seq, num, type) { \ - (((seq) & 0x00ff) | \ - (((num) & 0x000f) << 8)) | \ - (((type) & 0x000f) << 12); } - -#define HostCmd_GET_SEQ_NO(seq) \ - ((seq) & HostCmd_SEQ_NUM_MASK) - -#define HostCmd_GET_BSS_NO(seq) \ - (((seq) & HostCmd_BSS_NUM_MASK) >> 8) - -#define HostCmd_GET_BSS_TYPE(seq) \ - (((seq) & HostCmd_BSS_TYPE_MASK) >> 12) - -#define EVENT_DUMMY_HOST_WAKEUP_SIGNAL 0x00000001 -#define EVENT_LINK_LOST 0x00000003 -#define EVENT_LINK_SENSED 0x00000004 -#define EVENT_MIB_CHANGED 0x00000006 -#define EVENT_INIT_DONE 0x00000007 -#define EVENT_DEAUTHENTICATED 0x00000008 -#define EVENT_DISASSOCIATED 0x00000009 -#define EVENT_PS_AWAKE 0x0000000a -#define EVENT_PS_SLEEP 0x0000000b -#define EVENT_MIC_ERR_MULTICAST 0x0000000d -#define EVENT_MIC_ERR_UNICAST 0x0000000e -#define EVENT_DEEP_SLEEP_AWAKE 0x00000010 -#define EVENT_ADHOC_BCN_LOST 0x00000011 - -#define EVENT_WMM_STATUS_CHANGE 0x00000017 -#define EVENT_BG_SCAN_REPORT 0x00000018 -#define EVENT_RSSI_LOW 0x00000019 -#define EVENT_SNR_LOW 0x0000001a -#define EVENT_MAX_FAIL 0x0000001b -#define EVENT_RSSI_HIGH 0x0000001c -#define EVENT_SNR_HIGH 0x0000001d -#define EVENT_IBSS_COALESCED 0x0000001e -#define EVENT_DATA_RSSI_LOW 0x00000024 -#define EVENT_DATA_SNR_LOW 0x00000025 -#define EVENT_DATA_RSSI_HIGH 0x00000026 -#define EVENT_DATA_SNR_HIGH 0x00000027 -#define EVENT_LINK_QUALITY 0x00000028 -#define EVENT_PORT_RELEASE 0x0000002b -#define EVENT_UAP_STA_DEAUTH 0x0000002c -#define EVENT_UAP_STA_ASSOC 0x0000002d -#define EVENT_UAP_BSS_START 0x0000002e -#define EVENT_PRE_BEACON_LOST 0x00000031 -#define EVENT_ADDBA 0x00000033 -#define EVENT_DELBA 0x00000034 -#define EVENT_BA_STREAM_TIEMOUT 0x00000037 -#define EVENT_AMSDU_AGGR_CTRL 0x00000042 -#define EVENT_UAP_BSS_IDLE 0x00000043 -#define EVENT_UAP_BSS_ACTIVE 0x00000044 -#define EVENT_WEP_ICV_ERR 0x00000046 -#define EVENT_HS_ACT_REQ 0x00000047 -#define EVENT_BW_CHANGE 0x00000048 -#define EVENT_UAP_MIC_COUNTERMEASURES 0x0000004c -#define EVENT_HOSTWAKE_STAIE 0x0000004d -#define EVENT_CHANNEL_SWITCH_ANN 0x00000050 -#define EVENT_TDLS_GENERIC_EVENT 0x00000052 -#define EVENT_RADAR_DETECTED 0x00000053 -#define EVENT_CHANNEL_REPORT_RDY 0x00000054 -#define EVENT_TX_DATA_PAUSE 0x00000055 -#define EVENT_EXT_SCAN_REPORT 0x00000058 -#define EVENT_REMAIN_ON_CHAN_EXPIRED 0x0000005f -#define EVENT_MULTI_CHAN_INFO 0x0000006a -#define EVENT_TX_STATUS_REPORT 0x00000074 -#define EVENT_BT_COEX_WLAN_PARA_CHANGE 0X00000076 - -#define EVENT_ID_MASK 0xffff -#define BSS_NUM_MASK 0xf - -#define EVENT_GET_BSS_NUM(event_cause) \ - (((event_cause) >> 16) & BSS_NUM_MASK) - -#define EVENT_GET_BSS_TYPE(event_cause) \ - (((event_cause) >> 24) & 0x00ff) - -#define MWIFIEX_MAX_PATTERN_LEN 20 -#define MWIFIEX_MAX_OFFSET_LEN 100 -#define STACK_NBYTES 100 -#define TYPE_DNUM 1 -#define TYPE_BYTESEQ 2 -#define MAX_OPERAND 0x40 -#define TYPE_EQ (MAX_OPERAND+1) -#define TYPE_EQ_DNUM (MAX_OPERAND+2) -#define TYPE_EQ_BIT (MAX_OPERAND+3) -#define TYPE_AND (MAX_OPERAND+4) -#define TYPE_OR (MAX_OPERAND+5) -#define MEF_MODE_HOST_SLEEP 1 -#define MEF_ACTION_ALLOW_AND_WAKEUP_HOST 3 -#define MEF_ACTION_AUTO_ARP 0x10 -#define MWIFIEX_CRITERIA_BROADCAST BIT(0) -#define MWIFIEX_CRITERIA_UNICAST BIT(1) -#define MWIFIEX_CRITERIA_MULTICAST BIT(3) -#define MWIFIEX_MAX_SUPPORTED_IPADDR 4 - -#define ACT_TDLS_DELETE 0x00 -#define ACT_TDLS_CREATE 0x01 -#define ACT_TDLS_CONFIG 0x02 - -#define TDLS_EVENT_LINK_TEAR_DOWN 3 -#define TDLS_EVENT_CHAN_SWITCH_RESULT 7 -#define TDLS_EVENT_START_CHAN_SWITCH 8 -#define TDLS_EVENT_CHAN_SWITCH_STOPPED 9 - -#define TDLS_BASE_CHANNEL 0 -#define TDLS_OFF_CHANNEL 1 - -#define ACT_TDLS_CS_ENABLE_CONFIG 0x00 -#define ACT_TDLS_CS_INIT 0x06 -#define ACT_TDLS_CS_STOP 0x07 -#define ACT_TDLS_CS_PARAMS 0x08 - -#define MWIFIEX_DEF_CS_UNIT_TIME 2 -#define MWIFIEX_DEF_CS_THR_OTHERLINK 10 -#define MWIFIEX_DEF_THR_DIRECTLINK 0 -#define MWIFIEX_DEF_CS_TIME 10 -#define MWIFIEX_DEF_CS_TIMEOUT 16 -#define MWIFIEX_DEF_CS_REG_CLASS 12 -#define MWIFIEX_DEF_CS_PERIODICITY 1 - -#define MWIFIEX_FW_V15 15 - -#define MWIFIEX_MASTER_RADAR_DET_MASK BIT(1) - -struct mwifiex_ie_types_header { - __le16 type; - __le16 len; -} __packed; - -struct mwifiex_ie_types_data { - struct mwifiex_ie_types_header header; - u8 data[1]; -} __packed; - -#define MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET 0x01 -#define MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET 0x08 -#define MWIFIEX_TXPD_FLAGS_TDLS_PACKET 0x10 -#define MWIFIEX_RXPD_FLAGS_TDLS_PACKET 0x01 -#define MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS 0x20 - -struct txpd { - u8 bss_type; - u8 bss_num; - __le16 tx_pkt_length; - __le16 tx_pkt_offset; - __le16 tx_pkt_type; - __le32 tx_control; - u8 priority; - u8 flags; - u8 pkt_delay_2ms; - u8 reserved1[2]; - u8 tx_token_id; - u8 reserved[2]; -} __packed; - -struct rxpd { - u8 bss_type; - u8 bss_num; - __le16 rx_pkt_length; - __le16 rx_pkt_offset; - __le16 rx_pkt_type; - __le16 seq_num; - u8 priority; - u8 rx_rate; - s8 snr; - s8 nf; - - /* For: Non-802.11 AC cards - * - * Ht Info [Bit 0] RxRate format: LG=0, HT=1 - * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 - * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 - * - * For: 802.11 AC cards - * [Bit 1] [Bit 0] RxRate format: legacy rate = 00 HT = 01 VHT = 10 - * [Bit 3] [Bit 2] HT/VHT Bandwidth BW20 = 00 BW40 = 01 - * BW80 = 10 BW160 = 11 - * [Bit 4] HT/VHT Guard interval LGI = 0 SGI = 1 - * [Bit 5] STBC support Enabled = 1 - * [Bit 6] LDPC support Enabled = 1 - * [Bit 7] Reserved - */ - u8 ht_info; - u8 reserved[3]; - u8 flags; -} __packed; - -struct uap_txpd { - u8 bss_type; - u8 bss_num; - __le16 tx_pkt_length; - __le16 tx_pkt_offset; - __le16 tx_pkt_type; - __le32 tx_control; - u8 priority; - u8 flags; - u8 pkt_delay_2ms; - u8 reserved1[2]; - u8 tx_token_id; - u8 reserved[2]; -}; - -struct uap_rxpd { - u8 bss_type; - u8 bss_num; - __le16 rx_pkt_length; - __le16 rx_pkt_offset; - __le16 rx_pkt_type; - __le16 seq_num; - u8 priority; - u8 rx_rate; - s8 snr; - s8 nf; - u8 ht_info; - u8 reserved[3]; - u8 flags; -}; - -struct mwifiex_fw_chan_stats { - u8 chan_num; - u8 bandcfg; - u8 flags; - s8 noise; - __le16 total_bss; - __le16 cca_scan_dur; - __le16 cca_busy_dur; -} __packed; - -enum mwifiex_chan_scan_mode_bitmasks { - MWIFIEX_PASSIVE_SCAN = BIT(0), - MWIFIEX_DISABLE_CHAN_FILT = BIT(1), - MWIFIEX_HIDDEN_SSID_REPORT = BIT(4), -}; - -struct mwifiex_chan_scan_param_set { - u8 radio_type; - u8 chan_number; - u8 chan_scan_mode_bitmap; - __le16 min_scan_time; - __le16 max_scan_time; -} __packed; - -struct mwifiex_ie_types_chan_list_param_set { - struct mwifiex_ie_types_header header; - struct mwifiex_chan_scan_param_set chan_scan_param[1]; -} __packed; - -struct chan_band_param_set { - u8 radio_type; - u8 chan_number; -}; - -struct mwifiex_ie_types_chan_band_list_param_set { - struct mwifiex_ie_types_header header; - struct chan_band_param_set chan_band_param[1]; -} __packed; - -struct mwifiex_ie_types_rates_param_set { - struct mwifiex_ie_types_header header; - u8 rates[1]; -} __packed; - -struct mwifiex_ie_types_ssid_param_set { - struct mwifiex_ie_types_header header; - u8 ssid[1]; -} __packed; - -struct mwifiex_ie_types_num_probes { - struct mwifiex_ie_types_header header; - __le16 num_probes; -} __packed; - -struct mwifiex_ie_types_scan_chan_gap { - struct mwifiex_ie_types_header header; - /* time gap in TUs to be used between two consecutive channels scan */ - __le16 chan_gap; -} __packed; - -struct mwifiex_ietypes_chanstats { - struct mwifiex_ie_types_header header; - struct mwifiex_fw_chan_stats chanstats[0]; -} __packed; - -struct mwifiex_ie_types_wildcard_ssid_params { - struct mwifiex_ie_types_header header; - u8 max_ssid_length; - u8 ssid[1]; -} __packed; - -#define TSF_DATA_SIZE 8 -struct mwifiex_ie_types_tsf_timestamp { - struct mwifiex_ie_types_header header; - u8 tsf_data[1]; -} __packed; - -struct mwifiex_cf_param_set { - u8 cfp_cnt; - u8 cfp_period; - __le16 cfp_max_duration; - __le16 cfp_duration_remaining; -} __packed; - -struct mwifiex_ibss_param_set { - __le16 atim_window; -} __packed; - -struct mwifiex_ie_types_ss_param_set { - struct mwifiex_ie_types_header header; - union { - struct mwifiex_cf_param_set cf_param_set[1]; - struct mwifiex_ibss_param_set ibss_param_set[1]; - } cf_ibss; -} __packed; - -struct mwifiex_fh_param_set { - __le16 dwell_time; - u8 hop_set; - u8 hop_pattern; - u8 hop_index; -} __packed; - -struct mwifiex_ds_param_set { - u8 current_chan; -} __packed; - -struct mwifiex_ie_types_phy_param_set { - struct mwifiex_ie_types_header header; - union { - struct mwifiex_fh_param_set fh_param_set[1]; - struct mwifiex_ds_param_set ds_param_set[1]; - } fh_ds; -} __packed; - -struct mwifiex_ie_types_auth_type { - struct mwifiex_ie_types_header header; - __le16 auth_type; -} __packed; - -struct mwifiex_ie_types_vendor_param_set { - struct mwifiex_ie_types_header header; - u8 ie[MWIFIEX_MAX_VSIE_LEN]; -}; - -#define MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC 60 - -struct mwifiex_ie_types_tdls_idle_timeout { - struct mwifiex_ie_types_header header; - __le16 value; -} __packed; - -struct mwifiex_ie_types_rsn_param_set { - struct mwifiex_ie_types_header header; - u8 rsn_ie[1]; -} __packed; - -#define KEYPARAMSET_FIXED_LEN 6 - -struct mwifiex_ie_type_key_param_set { - __le16 type; - __le16 length; - __le16 key_type_id; - __le16 key_info; - __le16 key_len; - u8 key[50]; -} __packed; - -#define IGTK_PN_LEN 8 - -struct mwifiex_cmac_param { - u8 ipn[IGTK_PN_LEN]; - u8 key[WLAN_KEY_LEN_AES_CMAC]; -} __packed; - -struct mwifiex_wep_param { - __le16 key_len; - u8 key[WLAN_KEY_LEN_WEP104]; -} __packed; - -struct mwifiex_tkip_param { - u8 pn[WPA_PN_SIZE]; - __le16 key_len; - u8 key[WLAN_KEY_LEN_TKIP]; -} __packed; - -struct mwifiex_aes_param { - u8 pn[WPA_PN_SIZE]; - __le16 key_len; - u8 key[WLAN_KEY_LEN_CCMP]; -} __packed; - -struct mwifiex_wapi_param { - u8 pn[PN_LEN]; - __le16 key_len; - u8 key[WLAN_KEY_LEN_SMS4]; -} __packed; - -struct mwifiex_cmac_aes_param { - u8 ipn[IGTK_PN_LEN]; - __le16 key_len; - u8 key[WLAN_KEY_LEN_AES_CMAC]; -} __packed; - -struct mwifiex_ie_type_key_param_set_v2 { - __le16 type; - __le16 len; - u8 mac_addr[ETH_ALEN]; - u8 key_idx; - u8 key_type; - __le16 key_info; - union { - struct mwifiex_wep_param wep; - struct mwifiex_tkip_param tkip; - struct mwifiex_aes_param aes; - struct mwifiex_wapi_param wapi; - struct mwifiex_cmac_aes_param cmac_aes; - } key_params; -} __packed; - -struct host_cmd_ds_802_11_key_material_v2 { - __le16 action; - struct mwifiex_ie_type_key_param_set_v2 key_param_set; -} __packed; - -struct host_cmd_ds_802_11_key_material { - __le16 action; - struct mwifiex_ie_type_key_param_set key_param_set; -} __packed; - -struct host_cmd_ds_gen { - __le16 command; - __le16 size; - __le16 seq_num; - __le16 result; -}; - -#define S_DS_GEN sizeof(struct host_cmd_ds_gen) - -enum sleep_resp_ctrl { - RESP_NOT_NEEDED = 0, - RESP_NEEDED, -}; - -struct mwifiex_ps_param { - __le16 null_pkt_interval; - __le16 multiple_dtims; - __le16 bcn_miss_timeout; - __le16 local_listen_interval; - __le16 adhoc_wake_period; - __le16 mode; - __le16 delay_to_ps; -}; - -#define BITMAP_AUTO_DS 0x01 -#define BITMAP_STA_PS 0x10 - -struct mwifiex_ie_types_auto_ds_param { - struct mwifiex_ie_types_header header; - __le16 deep_sleep_timeout; -} __packed; - -struct mwifiex_ie_types_ps_param { - struct mwifiex_ie_types_header header; - struct mwifiex_ps_param param; -} __packed; - -struct host_cmd_ds_802_11_ps_mode_enh { - __le16 action; - - union { - struct mwifiex_ps_param opt_ps; - __le16 ps_bitmap; - } params; -} __packed; - -enum API_VER_ID { - KEY_API_VER_ID = 1, - FW_API_VER_ID = 2, -}; - -struct hw_spec_api_rev { - struct mwifiex_ie_types_header header; - __le16 api_id; - u8 major_ver; - u8 minor_ver; -} __packed; - -struct host_cmd_ds_get_hw_spec { - __le16 hw_if_version; - __le16 version; - __le16 reserved; - __le16 num_of_mcast_adr; - u8 permanent_addr[ETH_ALEN]; - __le16 region_code; - __le16 number_of_antenna; - __le32 fw_release_number; - __le32 reserved_1; - __le32 reserved_2; - __le32 reserved_3; - __le32 fw_cap_info; - __le32 dot_11n_dev_cap; - u8 dev_mcs_support; - __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ - __le16 mgmt_buf_count; /* mgmt IE buffer count */ - __le32 reserved_5; - __le32 reserved_6; - __le32 dot_11ac_dev_cap; - __le32 dot_11ac_mcs_support; - u8 tlvs[0]; -} __packed; - -struct host_cmd_ds_802_11_rssi_info { - __le16 action; - __le16 ndata; - __le16 nbcn; - __le16 reserved[9]; - long long reserved_1; -}; - -struct host_cmd_ds_802_11_rssi_info_rsp { - __le16 action; - __le16 ndata; - __le16 nbcn; - __le16 data_rssi_last; - __le16 data_nf_last; - __le16 data_rssi_avg; - __le16 data_nf_avg; - __le16 bcn_rssi_last; - __le16 bcn_nf_last; - __le16 bcn_rssi_avg; - __le16 bcn_nf_avg; - long long tsf_bcn; -}; - -struct host_cmd_ds_802_11_mac_address { - __le16 action; - u8 mac_addr[ETH_ALEN]; -}; - -struct host_cmd_ds_mac_control { - __le16 action; - __le16 reserved; -}; - -struct host_cmd_ds_mac_multicast_adr { - __le16 action; - __le16 num_of_adrs; - u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; -} __packed; - -struct host_cmd_ds_802_11_deauthenticate { - u8 mac_addr[ETH_ALEN]; - __le16 reason_code; -} __packed; - -struct host_cmd_ds_802_11_associate { - u8 peer_sta_addr[ETH_ALEN]; - __le16 cap_info_bitmap; - __le16 listen_interval; - __le16 beacon_period; - u8 dtim_period; -} __packed; - -struct ieee_types_assoc_rsp { - __le16 cap_info_bitmap; - __le16 status_code; - __le16 a_id; - u8 ie_buffer[1]; -} __packed; - -struct host_cmd_ds_802_11_associate_rsp { - struct ieee_types_assoc_rsp assoc_rsp; -} __packed; - -struct ieee_types_cf_param_set { - u8 element_id; - u8 len; - u8 cfp_cnt; - u8 cfp_period; - __le16 cfp_max_duration; - __le16 cfp_duration_remaining; -} __packed; - -struct ieee_types_ibss_param_set { - u8 element_id; - u8 len; - __le16 atim_window; -} __packed; - -union ieee_types_ss_param_set { - struct ieee_types_cf_param_set cf_param_set; - struct ieee_types_ibss_param_set ibss_param_set; -} __packed; - -struct ieee_types_fh_param_set { - u8 element_id; - u8 len; - __le16 dwell_time; - u8 hop_set; - u8 hop_pattern; - u8 hop_index; -} __packed; - -struct ieee_types_ds_param_set { - u8 element_id; - u8 len; - u8 current_chan; -} __packed; - -union ieee_types_phy_param_set { - struct ieee_types_fh_param_set fh_param_set; - struct ieee_types_ds_param_set ds_param_set; -} __packed; - -struct ieee_types_oper_mode_ntf { - u8 element_id; - u8 len; - u8 oper_mode; -} __packed; - -struct host_cmd_ds_802_11_ad_hoc_start { - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 bss_mode; - __le16 beacon_period; - u8 dtim_period; - union ieee_types_ss_param_set ss_param_set; - union ieee_types_phy_param_set phy_param_set; - u16 reserved1; - __le16 cap_info_bitmap; - u8 data_rate[HOSTCMD_SUPPORTED_RATES]; -} __packed; - -struct host_cmd_ds_802_11_ad_hoc_result { - u8 pad[3]; - u8 bssid[ETH_ALEN]; -} __packed; - -struct adhoc_bss_desc { - u8 bssid[ETH_ALEN]; - u8 ssid[IEEE80211_MAX_SSID_LEN]; - u8 bss_mode; - __le16 beacon_period; - u8 dtim_period; - u8 time_stamp[8]; - u8 local_time[8]; - union ieee_types_phy_param_set phy_param_set; - union ieee_types_ss_param_set ss_param_set; - __le16 cap_info_bitmap; - 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 - */ -} __packed; - -struct host_cmd_ds_802_11_ad_hoc_join { - struct adhoc_bss_desc bss_descriptor; - u16 reserved1; - u16 reserved2; -} __packed; - -struct host_cmd_ds_802_11_get_log { - __le32 mcast_tx_frame; - __le32 failed; - __le32 retry; - __le32 multi_retry; - __le32 frame_dup; - __le32 rts_success; - __le32 rts_failure; - __le32 ack_failure; - __le32 rx_frag; - __le32 mcast_rx_frame; - __le32 fcs_error; - __le32 tx_frame; - __le32 reserved; - __le32 wep_icv_err_cnt[4]; - __le32 bcn_rcv_cnt; - __le32 bcn_miss_cnt; -}; - -/* Enumeration for rate format */ -enum _mwifiex_rate_format { - MWIFIEX_RATE_FORMAT_LG = 0, - MWIFIEX_RATE_FORMAT_HT, - MWIFIEX_RATE_FORMAT_VHT, - MWIFIEX_RATE_FORMAT_AUTO = 0xFF, -}; - -struct host_cmd_ds_tx_rate_query { - u8 tx_rate; - /* Tx Rate Info: For 802.11 AC cards - * - * [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 - * - * For non-802.11 AC cards - * Ht Info [Bit 0] RxRate format: LG=0, HT=1 - * [Bit 1] HT Bandwidth: BW20 = 0, BW40 = 1 - * [Bit 2] HT Guard Interval: LGI = 0, SGI = 1 - */ - u8 ht_info; -} __packed; - -struct mwifiex_tx_pause_tlv { - struct mwifiex_ie_types_header header; - u8 peermac[ETH_ALEN]; - u8 tx_pause; - u8 pkt_cnt; -} __packed; - -enum Host_Sleep_Action { - HS_CONFIGURE = 0x0001, - HS_ACTIVATE = 0x0002, -}; - -struct mwifiex_hs_config_param { - __le32 conditions; - u8 gpio; - u8 gap; -} __packed; - -struct hs_activate_param { - __le16 resp_ctrl; -} __packed; - -struct host_cmd_ds_802_11_hs_cfg_enh { - __le16 action; - - union { - struct mwifiex_hs_config_param hs_config; - struct hs_activate_param hs_activate; - } params; -} __packed; - -enum SNMP_MIB_INDEX { - OP_RATE_SET_I = 1, - DTIM_PERIOD_I = 3, - RTS_THRESH_I = 5, - SHORT_RETRY_LIM_I = 6, - LONG_RETRY_LIM_I = 7, - FRAG_THRESH_I = 8, - DOT11D_I = 9, - DOT11H_I = 10, -}; - -enum mwifiex_assocmd_failurepoint { - MWIFIEX_ASSOC_CMD_SUCCESS = 0, - MWIFIEX_ASSOC_CMD_FAILURE_ASSOC, - MWIFIEX_ASSOC_CMD_FAILURE_AUTH, - MWIFIEX_ASSOC_CMD_FAILURE_JOIN -}; - -#define MAX_SNMP_BUF_SIZE 128 - -struct host_cmd_ds_802_11_snmp_mib { - __le16 query_type; - __le16 oid; - __le16 buf_size; - u8 value[1]; -} __packed; - -struct mwifiex_rate_scope { - __le16 type; - __le16 length; - __le16 hr_dsss_rate_bitmap; - __le16 ofdm_rate_bitmap; - __le16 ht_mcs_rate_bitmap[8]; - __le16 vht_mcs_rate_bitmap[8]; -} __packed; - -struct mwifiex_rate_drop_pattern { - __le16 type; - __le16 length; - __le32 rate_drop_mode; -} __packed; - -struct host_cmd_ds_tx_rate_cfg { - __le16 action; - __le16 cfg_index; -} __packed; - -struct mwifiex_power_group { - u8 modulation_class; - u8 first_rate_code; - u8 last_rate_code; - s8 power_step; - s8 power_min; - s8 power_max; - u8 ht_bandwidth; - u8 reserved; -} __packed; - -struct mwifiex_types_power_group { - __le16 type; - __le16 length; -} __packed; - -struct host_cmd_ds_txpwr_cfg { - __le16 action; - __le16 cfg_index; - __le32 mode; -} __packed; - -struct host_cmd_ds_rf_tx_pwr { - __le16 action; - __le16 cur_level; - u8 max_power; - u8 min_power; -} __packed; - -struct host_cmd_ds_rf_ant_mimo { - __le16 action_tx; - __le16 tx_ant_mode; - __le16 action_rx; - __le16 rx_ant_mode; -}; - -struct host_cmd_ds_rf_ant_siso { - __le16 action; - __le16 ant_mode; -}; - -struct host_cmd_ds_tdls_oper { - __le16 tdls_action; - __le16 reason; - u8 peer_mac[ETH_ALEN]; -} __packed; - -struct mwifiex_tdls_config { - __le16 enable; -}; - -struct mwifiex_tdls_config_cs_params { - u8 unit_time; - u8 thr_otherlink; - u8 thr_directlink; -}; - -struct mwifiex_tdls_init_cs_params { - u8 peer_mac[ETH_ALEN]; - u8 primary_chan; - u8 second_chan_offset; - u8 band; - __le16 switch_time; - __le16 switch_timeout; - u8 reg_class; - u8 periodicity; -} __packed; - -struct mwifiex_tdls_stop_cs_params { - u8 peer_mac[ETH_ALEN]; -}; - -struct host_cmd_ds_tdls_config { - __le16 tdls_action; - u8 tdls_data[1]; -} __packed; - -struct mwifiex_chan_desc { - __le16 start_freq; - u8 chan_width; - u8 chan_num; -} __packed; - -struct host_cmd_ds_chan_rpt_req { - struct mwifiex_chan_desc chan_desc; - __le32 msec_dwell_time; -} __packed; - -struct host_cmd_ds_chan_rpt_event { - __le32 result; - __le64 start_tsf; - __le32 duration; - u8 tlvbuf[0]; -} __packed; - -struct host_cmd_sdio_sp_rx_aggr_cfg { - u8 action; - u8 enable; - __le16 block_size; -} __packed; - -struct mwifiex_fixed_bcn_param { - __le64 timestamp; - __le16 beacon_period; - __le16 cap_info_bitmap; -} __packed; - -struct mwifiex_event_scan_result { - __le16 event_id; - u8 bss_index; - u8 bss_type; - u8 more_event; - u8 reserved[3]; - __le16 buf_size; - u8 num_of_set; -} __packed; - -struct tx_status_event { - u8 packet_type; - u8 tx_token_id; - u8 status; -} __packed; - -#define MWIFIEX_USER_SCAN_CHAN_MAX 50 - -#define MWIFIEX_MAX_SSID_LIST_LENGTH 10 - -struct mwifiex_scan_cmd_config { - /* - * BSS mode to be sent in the firmware command - */ - u8 bss_mode; - - /* Specific BSSID used to filter scan results in the firmware */ - u8 specific_bssid[ETH_ALEN]; - - /* Length of TLVs sent in command starting at tlvBuffer */ - u32 tlv_buf_len; - - /* - * SSID TLV(s) and ChanList TLVs to be sent in the firmware command - * - * TLV_TYPE_CHANLIST, mwifiex_ie_types_chan_list_param_set - * WLAN_EID_SSID, mwifiex_ie_types_ssid_param_set - */ - u8 tlv_buf[1]; /* SSID TLV(s) and ChanList TLVs are stored - here */ -} __packed; - -struct mwifiex_user_scan_chan { - u8 chan_number; - u8 radio_type; - u8 scan_type; - u8 reserved; - u32 scan_time; -} __packed; - -struct mwifiex_user_scan_cfg { - /* - * BSS mode to be sent in the firmware command - */ - u8 bss_mode; - /* Configure the number of probe requests for active chan scans */ - u8 num_probes; - u8 reserved; - /* BSSID filter sent in the firmware command to limit the results */ - u8 specific_bssid[ETH_ALEN]; - /* SSID filter list used in the firmware to limit the scan results */ - struct cfg80211_ssid *ssid_list; - u8 num_ssids; - /* Variable number (fixed maximum) of channels to scan up */ - struct mwifiex_user_scan_chan chan_list[MWIFIEX_USER_SCAN_CHAN_MAX]; - u16 scan_chan_gap; -} __packed; - -struct ie_body { - u8 grp_key_oui[4]; - u8 ptk_cnt[2]; - u8 ptk_body[4]; -} __packed; - -struct host_cmd_ds_802_11_scan { - u8 bss_mode; - u8 bssid[ETH_ALEN]; - u8 tlv_buffer[1]; -} __packed; - -struct host_cmd_ds_802_11_scan_rsp { - __le16 bss_descript_size; - u8 number_of_sets; - u8 bss_desc_and_tlv_buffer[1]; -} __packed; - -struct host_cmd_ds_802_11_scan_ext { - u32 reserved; - u8 tlv_buffer[1]; -} __packed; - -struct mwifiex_ie_types_bss_mode { - struct mwifiex_ie_types_header header; - u8 bss_mode; -} __packed; - -struct mwifiex_ie_types_bss_scan_rsp { - struct mwifiex_ie_types_header header; - u8 bssid[ETH_ALEN]; - u8 frame_body[1]; -} __packed; - -struct mwifiex_ie_types_bss_scan_info { - struct mwifiex_ie_types_header header; - __le16 rssi; - __le16 anpi; - u8 cca_busy_fraction; - u8 radio_type; - u8 channel; - u8 reserved; - __le64 tsf; -} __packed; - -struct host_cmd_ds_802_11_bg_scan_query { - u8 flush; -} __packed; - -struct host_cmd_ds_802_11_bg_scan_query_rsp { - __le32 report_condition; - struct host_cmd_ds_802_11_scan_rsp scan_resp; -} __packed; - -struct mwifiex_ietypes_domain_param_set { - struct mwifiex_ie_types_header header; - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - struct ieee80211_country_ie_triplet triplet[1]; -} __packed; - -struct host_cmd_ds_802_11d_domain_info { - __le16 action; - struct mwifiex_ietypes_domain_param_set domain; -} __packed; - -struct host_cmd_ds_802_11d_domain_info_rsp { - __le16 action; - struct mwifiex_ietypes_domain_param_set domain; -} __packed; - -struct host_cmd_ds_11n_addba_req { - u8 add_req_result; - u8 peer_mac_addr[ETH_ALEN]; - u8 dialog_token; - __le16 block_ack_param_set; - __le16 block_ack_tmo; - __le16 ssn; -} __packed; - -struct host_cmd_ds_11n_addba_rsp { - u8 add_rsp_result; - u8 peer_mac_addr[ETH_ALEN]; - u8 dialog_token; - __le16 status_code; - __le16 block_ack_param_set; - __le16 block_ack_tmo; - __le16 ssn; -} __packed; - -struct host_cmd_ds_11n_delba { - u8 del_result; - u8 peer_mac_addr[ETH_ALEN]; - __le16 del_ba_param_set; - __le16 reason_code; - u8 reserved; -} __packed; - -struct host_cmd_ds_11n_batimeout { - u8 tid; - u8 peer_mac_addr[ETH_ALEN]; - u8 origninator; -} __packed; - -struct host_cmd_ds_11n_cfg { - __le16 action; - __le16 ht_tx_cap; - __le16 ht_tx_info; - __le16 misc_config; /* Needed for 802.11AC cards only */ -} __packed; - -struct host_cmd_ds_txbuf_cfg { - __le16 action; - __le16 buff_size; - __le16 mp_end_port; /* SDIO only, reserved for other interfacces */ - __le16 reserved3; -} __packed; - -struct host_cmd_ds_amsdu_aggr_ctrl { - __le16 action; - __le16 enable; - __le16 curr_buf_size; -} __packed; - -struct host_cmd_ds_sta_deauth { - u8 mac[ETH_ALEN]; - __le16 reason; -} __packed; - -struct mwifiex_ie_types_sta_info { - struct mwifiex_ie_types_header header; - u8 mac[ETH_ALEN]; - u8 power_mfg_status; - s8 rssi; -}; - -struct host_cmd_ds_sta_list { - u16 sta_count; - u8 tlv[0]; -} __packed; - -struct mwifiex_ie_types_pwr_capability { - struct mwifiex_ie_types_header header; - s8 min_pwr; - s8 max_pwr; -}; - -struct mwifiex_ie_types_local_pwr_constraint { - struct mwifiex_ie_types_header header; - u8 chan; - u8 constraint; -}; - -struct mwifiex_ie_types_wmm_param_set { - struct mwifiex_ie_types_header header; - u8 wmm_ie[1]; -}; - -struct mwifiex_ie_types_wmm_queue_status { - struct mwifiex_ie_types_header header; - u8 queue_index; - u8 disabled; - __le16 medium_time; - u8 flow_required; - u8 flow_created; - u32 reserved; -}; - -struct ieee_types_vendor_header { - u8 element_id; - u8 len; - u8 oui[4]; /* 0~2: oui, 3: oui_type */ - u8 oui_subtype; - u8 version; -} __packed; - -struct ieee_types_wmm_parameter { - /* - * WMM Parameter IE - Vendor Specific Header: - * element_id [221/0xdd] - * Len [24] - * Oui [00:50:f2] - * OuiType [2] - * OuiSubType [1] - * Version [1] - */ - struct ieee_types_vendor_header vend_hdr; - u8 qos_info_bitmap; - u8 reserved; - struct ieee_types_wmm_ac_parameters ac_params[IEEE80211_NUM_ACS]; -} __packed; - -struct ieee_types_wmm_info { - - /* - * WMM Info IE - Vendor Specific Header: - * element_id [221/0xdd] - * Len [7] - * Oui [00:50:f2] - * OuiType [2] - * OuiSubType [0] - * Version [1] - */ - struct ieee_types_vendor_header vend_hdr; - - u8 qos_info_bitmap; -} __packed; - -struct host_cmd_ds_wmm_get_status { - u8 queue_status_tlv[sizeof(struct mwifiex_ie_types_wmm_queue_status) * - IEEE80211_NUM_ACS]; - u8 wmm_param_tlv[sizeof(struct ieee_types_wmm_parameter) + 2]; -} __packed; - -struct mwifiex_wmm_ac_status { - u8 disabled; - u8 flow_required; - u8 flow_created; -}; - -struct mwifiex_ie_types_htcap { - struct mwifiex_ie_types_header header; - struct ieee80211_ht_cap ht_cap; -} __packed; - -struct mwifiex_ie_types_vhtcap { - struct mwifiex_ie_types_header header; - struct ieee80211_vht_cap vht_cap; -} __packed; - -struct mwifiex_ie_types_aid { - struct mwifiex_ie_types_header header; - __le16 aid; -} __packed; - -struct mwifiex_ie_types_oper_mode_ntf { - struct mwifiex_ie_types_header header; - u8 oper_mode; -} __packed; - -/* VHT Operations IE */ -struct mwifiex_ie_types_vht_oper { - struct mwifiex_ie_types_header header; - u8 chan_width; - u8 chan_center_freq_1; - u8 chan_center_freq_2; - /* Basic MCS set map, each 2 bits stands for a NSS */ - __le16 basic_mcs_map; -} __packed; - -struct mwifiex_ie_types_wmmcap { - struct mwifiex_ie_types_header header; - struct mwifiex_types_wmm_info wmm_info; -} __packed; - -struct mwifiex_ie_types_htinfo { - struct mwifiex_ie_types_header header; - struct ieee80211_ht_operation ht_oper; -} __packed; - -struct mwifiex_ie_types_2040bssco { - struct mwifiex_ie_types_header header; - u8 bss_co_2040; -} __packed; - -struct mwifiex_ie_types_extcap { - struct mwifiex_ie_types_header header; - u8 ext_capab[0]; -} __packed; - -struct host_cmd_ds_mem_access { - __le16 action; - __le16 reserved; - __le32 addr; - __le32 value; -}; - -struct mwifiex_ie_types_qos_info { - struct mwifiex_ie_types_header header; - u8 qos_info; -} __packed; - -struct host_cmd_ds_mac_reg_access { - __le16 action; - __le16 offset; - __le32 value; -} __packed; - -struct host_cmd_ds_bbp_reg_access { - __le16 action; - __le16 offset; - u8 value; - u8 reserved[3]; -} __packed; - -struct host_cmd_ds_rf_reg_access { - __le16 action; - __le16 offset; - u8 value; - u8 reserved[3]; -} __packed; - -struct host_cmd_ds_pmic_reg_access { - __le16 action; - __le16 offset; - u8 value; - u8 reserved[3]; -} __packed; - -struct host_cmd_ds_802_11_eeprom_access { - __le16 action; - - __le16 offset; - __le16 byte_count; - u8 value; -} __packed; - -struct mwifiex_assoc_event { - u8 sta_addr[ETH_ALEN]; - __le16 type; - __le16 len; - __le16 frame_control; - __le16 cap_info; - __le16 listen_interval; - u8 data[0]; -} __packed; - -struct host_cmd_ds_sys_config { - __le16 action; - u8 tlv[0]; -}; - -struct host_cmd_11ac_vht_cfg { - __le16 action; - u8 band_config; - u8 misc_config; - __le32 cap_info; - __le32 mcs_tx_set; - __le32 mcs_rx_set; -} __packed; - -struct host_cmd_tlv_akmp { - struct mwifiex_ie_types_header header; - __le16 key_mgmt; - __le16 key_mgmt_operation; -} __packed; - -struct host_cmd_tlv_pwk_cipher { - struct mwifiex_ie_types_header header; - __le16 proto; - u8 cipher; - u8 reserved; -} __packed; - -struct host_cmd_tlv_gwk_cipher { - struct mwifiex_ie_types_header header; - u8 cipher; - u8 reserved; -} __packed; - -struct host_cmd_tlv_passphrase { - struct mwifiex_ie_types_header header; - u8 passphrase[0]; -} __packed; - -struct host_cmd_tlv_wep_key { - struct mwifiex_ie_types_header header; - u8 key_index; - u8 is_default; - u8 key[1]; -}; - -struct host_cmd_tlv_auth_type { - struct mwifiex_ie_types_header header; - u8 auth_type; -} __packed; - -struct host_cmd_tlv_encrypt_protocol { - struct mwifiex_ie_types_header header; - __le16 proto; -} __packed; - -struct host_cmd_tlv_ssid { - struct mwifiex_ie_types_header header; - u8 ssid[0]; -} __packed; - -struct host_cmd_tlv_rates { - struct mwifiex_ie_types_header header; - u8 rates[0]; -} __packed; - -struct mwifiex_ie_types_bssid_list { - struct mwifiex_ie_types_header header; - u8 bssid[ETH_ALEN]; -} __packed; - -struct host_cmd_tlv_bcast_ssid { - struct mwifiex_ie_types_header header; - u8 bcast_ctl; -} __packed; - -struct host_cmd_tlv_beacon_period { - struct mwifiex_ie_types_header header; - __le16 period; -} __packed; - -struct host_cmd_tlv_dtim_period { - struct mwifiex_ie_types_header header; - u8 period; -} __packed; - -struct host_cmd_tlv_frag_threshold { - struct mwifiex_ie_types_header header; - __le16 frag_thr; -} __packed; - -struct host_cmd_tlv_rts_threshold { - struct mwifiex_ie_types_header header; - __le16 rts_thr; -} __packed; - -struct host_cmd_tlv_retry_limit { - struct mwifiex_ie_types_header header; - u8 limit; -} __packed; - -struct host_cmd_tlv_mac_addr { - struct mwifiex_ie_types_header header; - u8 mac_addr[ETH_ALEN]; -} __packed; - -struct host_cmd_tlv_channel_band { - struct mwifiex_ie_types_header header; - u8 band_config; - u8 channel; -} __packed; - -struct host_cmd_tlv_ageout_timer { - struct mwifiex_ie_types_header header; - __le32 sta_ao_timer; -} __packed; - -struct host_cmd_tlv_power_constraint { - struct mwifiex_ie_types_header header; - u8 constraint; -} __packed; - -struct mwifiex_ie_types_btcoex_scan_time { - struct mwifiex_ie_types_header header; - u8 coex_scan; - u8 reserved; - u16 min_scan_time; - u16 max_scan_time; -} __packed; - -struct mwifiex_ie_types_btcoex_aggr_win_size { - struct mwifiex_ie_types_header header; - u8 coex_win_size; - u8 tx_win_size; - u8 rx_win_size; - u8 reserved; -} __packed; - -struct mwifiex_ie_types_robust_coex { - struct mwifiex_ie_types_header header; - __le32 mode; -} __packed; - -struct host_cmd_ds_version_ext { - u8 version_str_sel; - char version_str[128]; -} __packed; - -struct host_cmd_ds_mgmt_frame_reg { - __le16 action; - __le32 mask; -} __packed; - -struct host_cmd_ds_p2p_mode_cfg { - __le16 action; - __le16 mode; -} __packed; - -struct host_cmd_ds_remain_on_chan { - __le16 action; - u8 status; - u8 reserved; - u8 band_cfg; - u8 channel; - __le32 duration; -} __packed; - -struct host_cmd_ds_802_11_ibss_status { - __le16 action; - __le16 enable; - u8 bssid[ETH_ALEN]; - __le16 beacon_interval; - __le16 atim_window; - __le16 use_g_rate_protect; -} __packed; - -struct mwifiex_fw_mef_entry { - u8 mode; - u8 action; - __le16 exprsize; - u8 expr[0]; -} __packed; - -struct host_cmd_ds_mef_cfg { - __le32 criteria; - __le16 num_entries; - struct mwifiex_fw_mef_entry mef_entry[0]; -} __packed; - -#define CONNECTION_TYPE_INFRA 0 -#define CONNECTION_TYPE_ADHOC 1 -#define CONNECTION_TYPE_AP 2 - -struct host_cmd_ds_set_bss_mode { - u8 con_type; -} __packed; - -struct host_cmd_ds_pcie_details { - /* TX buffer descriptor ring address */ - u32 txbd_addr_lo; - u32 txbd_addr_hi; - /* TX buffer descriptor ring count */ - u32 txbd_count; - - /* RX buffer descriptor ring address */ - u32 rxbd_addr_lo; - u32 rxbd_addr_hi; - /* RX buffer descriptor ring count */ - u32 rxbd_count; - - /* Event buffer descriptor ring address */ - u32 evtbd_addr_lo; - u32 evtbd_addr_hi; - /* Event buffer descriptor ring count */ - u32 evtbd_count; - - /* Sleep cookie buffer physical address */ - u32 sleep_cookie_addr_lo; - u32 sleep_cookie_addr_hi; -} __packed; - -struct mwifiex_ie_types_rssi_threshold { - struct mwifiex_ie_types_header header; - u8 abs_value; - u8 evt_freq; -} __packed; - -#define MWIFIEX_DFS_REC_HDR_LEN 8 -#define MWIFIEX_DFS_REC_HDR_NUM 10 -#define MWIFIEX_BIN_COUNTER_LEN 7 - -struct mwifiex_radar_det_event { - __le32 detect_count; - u8 reg_domain; /*1=fcc, 2=etsi, 3=mic*/ - u8 det_type; /*0=none, 1=pw(chirp), 2=pri(radar)*/ - __le16 pw_chirp_type; - u8 pw_chirp_idx; - u8 pw_value; - u8 pri_radar_type; - u8 pri_bincnt; - u8 bin_counter[MWIFIEX_BIN_COUNTER_LEN]; - u8 num_dfs_records; - u8 dfs_record_hdr[MWIFIEX_DFS_REC_HDR_NUM][MWIFIEX_DFS_REC_HDR_LEN]; - __le32 passed; -} __packed; - -struct mwifiex_ie_types_multi_chan_info { - struct mwifiex_ie_types_header header; - __le16 status; - u8 tlv_buffer[0]; -} __packed; - -struct mwifiex_ie_types_mc_group_info { - struct mwifiex_ie_types_header header; - u8 chan_group_id; - u8 chan_buf_weight; - u8 band_config; - u8 chan_num; - u32 chan_time; - u32 reserved; - union { - u8 sdio_func_num; - u8 usb_ep_num; - } hid_num; - u8 intf_num; - u8 bss_type_numlist[0]; -} __packed; - -struct meas_rpt_map { - u8 rssi:3; - u8 unmeasured:1; - u8 radar:1; - u8 unidentified_sig:1; - u8 ofdm_preamble:1; - u8 bss:1; -} __packed; - -struct mwifiex_ie_types_chan_rpt_data { - struct mwifiex_ie_types_header header; - struct meas_rpt_map map; -} __packed; - -struct host_cmd_ds_802_11_subsc_evt { - __le16 action; - __le16 events; -} __packed; - -struct chan_switch_result { - u8 cur_chan; - u8 status; - u8 reason; -} __packed; - -struct mwifiex_tdls_generic_event { - __le16 type; - u8 peer_mac[ETH_ALEN]; - union { - struct chan_switch_result switch_result; - u8 cs_stop_reason; - __le16 reason_code; - __le16 reserved; - } u; -} __packed; - -struct mwifiex_ie { - __le16 ie_index; - __le16 mgmt_subtype_mask; - __le16 ie_length; - u8 ie_buffer[IEEE_MAX_IE_SIZE]; -} __packed; - -#define MAX_MGMT_IE_INDEX 16 -struct mwifiex_ie_list { - __le16 type; - __le16 len; - struct mwifiex_ie ie_list[MAX_MGMT_IE_INDEX]; -} __packed; - -struct coalesce_filt_field_param { - u8 operation; - u8 operand_len; - __le16 offset; - u8 operand_byte_stream[4]; -}; - -struct coalesce_receive_filt_rule { - struct mwifiex_ie_types_header header; - u8 num_of_fields; - u8 pkt_type; - __le16 max_coalescing_delay; - struct coalesce_filt_field_param params[0]; -} __packed; - -struct host_cmd_ds_coalesce_cfg { - __le16 action; - __le16 num_of_rules; - struct coalesce_receive_filt_rule rule[0]; -} __packed; - -struct host_cmd_ds_multi_chan_policy { - __le16 action; - __le16 policy; -} __packed; - -struct host_cmd_ds_robust_coex { - __le16 action; - __le16 reserved; -} __packed; - -struct host_cmd_ds_command { - __le16 command; - __le16 size; - __le16 seq_num; - __le16 result; - union { - struct host_cmd_ds_get_hw_spec hw_spec; - struct host_cmd_ds_mac_control mac_ctrl; - struct host_cmd_ds_802_11_mac_address mac_addr; - struct host_cmd_ds_mac_multicast_adr mc_addr; - struct host_cmd_ds_802_11_get_log get_log; - struct host_cmd_ds_802_11_rssi_info rssi_info; - struct host_cmd_ds_802_11_rssi_info_rsp rssi_info_rsp; - struct host_cmd_ds_802_11_snmp_mib smib; - struct host_cmd_ds_tx_rate_query tx_rate; - struct host_cmd_ds_tx_rate_cfg tx_rate_cfg; - struct host_cmd_ds_txpwr_cfg txp_cfg; - struct host_cmd_ds_rf_tx_pwr txp; - struct host_cmd_ds_rf_ant_mimo ant_mimo; - struct host_cmd_ds_rf_ant_siso ant_siso; - struct host_cmd_ds_802_11_ps_mode_enh psmode_enh; - struct host_cmd_ds_802_11_hs_cfg_enh opt_hs_cfg; - struct host_cmd_ds_802_11_scan scan; - struct host_cmd_ds_802_11_scan_ext ext_scan; - struct host_cmd_ds_802_11_scan_rsp scan_resp; - struct host_cmd_ds_802_11_bg_scan_query bg_scan_query; - struct host_cmd_ds_802_11_bg_scan_query_rsp bg_scan_query_resp; - struct host_cmd_ds_802_11_associate associate; - struct host_cmd_ds_802_11_associate_rsp associate_rsp; - struct host_cmd_ds_802_11_deauthenticate deauth; - struct host_cmd_ds_802_11_ad_hoc_start adhoc_start; - struct host_cmd_ds_802_11_ad_hoc_result adhoc_result; - struct host_cmd_ds_802_11_ad_hoc_join adhoc_join; - struct host_cmd_ds_802_11d_domain_info domain_info; - struct host_cmd_ds_802_11d_domain_info_rsp domain_info_resp; - struct host_cmd_ds_11n_addba_req add_ba_req; - struct host_cmd_ds_11n_addba_rsp add_ba_rsp; - struct host_cmd_ds_11n_delba del_ba; - struct host_cmd_ds_txbuf_cfg tx_buf; - struct host_cmd_ds_amsdu_aggr_ctrl amsdu_aggr_ctrl; - struct host_cmd_ds_11n_cfg htcfg; - struct host_cmd_ds_wmm_get_status get_wmm_status; - struct host_cmd_ds_802_11_key_material key_material; - struct host_cmd_ds_802_11_key_material_v2 key_material_v2; - struct host_cmd_ds_version_ext verext; - struct host_cmd_ds_mgmt_frame_reg reg_mask; - struct host_cmd_ds_remain_on_chan roc_cfg; - struct host_cmd_ds_p2p_mode_cfg mode_cfg; - struct host_cmd_ds_802_11_ibss_status ibss_coalescing; - struct host_cmd_ds_mef_cfg mef_cfg; - struct host_cmd_ds_mem_access mem; - struct host_cmd_ds_mac_reg_access mac_reg; - struct host_cmd_ds_bbp_reg_access bbp_reg; - struct host_cmd_ds_rf_reg_access rf_reg; - struct host_cmd_ds_pmic_reg_access pmic_reg; - struct host_cmd_ds_set_bss_mode bss_mode; - struct host_cmd_ds_pcie_details pcie_host_spec; - struct host_cmd_ds_802_11_eeprom_access eeprom; - struct host_cmd_ds_802_11_subsc_evt subsc_evt; - struct host_cmd_ds_sys_config uap_sys_config; - struct host_cmd_ds_sta_deauth sta_deauth; - struct host_cmd_ds_sta_list sta_list; - struct host_cmd_11ac_vht_cfg vht_cfg; - struct host_cmd_ds_coalesce_cfg coalesce_cfg; - struct host_cmd_ds_tdls_config tdls_config; - struct host_cmd_ds_tdls_oper tdls_oper; - struct host_cmd_ds_chan_rpt_req chan_rpt_req; - struct host_cmd_sdio_sp_rx_aggr_cfg sdio_rx_aggr_cfg; - struct host_cmd_ds_multi_chan_policy mc_policy; - struct host_cmd_ds_robust_coex coex; - } params; -} __packed; - -struct mwifiex_opt_sleep_confirm { - __le16 command; - __le16 size; - __le16 seq_num; - __le16 result; - __le16 action; - __le16 resp_ctrl; -} __packed; -#endif /* !_MWIFIEX_FW_H_ */ diff --git a/drivers/net/wireless/mwifiex/ie.c b/drivers/net/wireless/mwifiex/ie.c deleted file mode 100644 index abf52d25b981..000000000000 --- a/drivers/net/wireless/mwifiex/ie.c +++ /dev/null @@ -1,488 +0,0 @@ -/* - * Marvell Wireless LAN device driver: management IE handling- setting and - * deleting IE. - * - * Copyright (C) 2012-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "main.h" - -/* This function checks if current IE index is used by any on other interface. - * Return: -1: yes, current IE index is used by someone else. - * 0: no, current IE index is NOT used by other interface. - */ -static int -mwifiex_ie_index_used_by_other_intf(struct mwifiex_private *priv, u16 idx) -{ - int i; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie *ie; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i] != priv) { - ie = &adapter->priv[i]->mgmt_ie[idx]; - if (ie->mgmt_subtype_mask && ie->ie_length) - return -1; - } - } - - return 0; -} - -/* Get unused IE index. This index will be used for setting new IE */ -static int -mwifiex_ie_get_autoidx(struct mwifiex_private *priv, u16 subtype_mask, - struct mwifiex_ie *ie, u16 *index) -{ - u16 mask, len, i; - - for (i = 0; i < priv->adapter->max_mgmt_ie_index; i++) { - mask = le16_to_cpu(priv->mgmt_ie[i].mgmt_subtype_mask); - len = le16_to_cpu(ie->ie_length); - - if (mask == MWIFIEX_AUTO_IDX_MASK) - continue; - - if (mask == subtype_mask) { - if (len > IEEE_MAX_IE_SIZE) - continue; - - *index = i; - return 0; - } - - if (!priv->mgmt_ie[i].ie_length) { - if (mwifiex_ie_index_used_by_other_intf(priv, i)) - continue; - - *index = i; - return 0; - } - } - - return -1; -} - -/* This function prepares IE data buffer for command to be sent to FW */ -static int -mwifiex_update_autoindex_ies(struct mwifiex_private *priv, - struct mwifiex_ie_list *ie_list) -{ - u16 travel_len, index, mask; - s16 input_len, tlv_len; - struct mwifiex_ie *ie; - u8 *tmp; - - input_len = le16_to_cpu(ie_list->len); - travel_len = sizeof(struct mwifiex_ie_types_header); - - ie_list->len = 0; - - while (input_len >= sizeof(struct mwifiex_ie_types_header)) { - ie = (struct mwifiex_ie *)(((u8 *)ie_list) + travel_len); - tlv_len = le16_to_cpu(ie->ie_length); - travel_len += tlv_len + MWIFIEX_IE_HDR_SIZE; - - if (input_len < tlv_len + MWIFIEX_IE_HDR_SIZE) - return -1; - index = le16_to_cpu(ie->ie_index); - mask = le16_to_cpu(ie->mgmt_subtype_mask); - - if (index == MWIFIEX_AUTO_IDX_MASK) { - /* automatic addition */ - if (mwifiex_ie_get_autoidx(priv, mask, ie, &index)) - return -1; - if (index == MWIFIEX_AUTO_IDX_MASK) - return -1; - - tmp = (u8 *)&priv->mgmt_ie[index].ie_buffer; - memcpy(tmp, &ie->ie_buffer, le16_to_cpu(ie->ie_length)); - priv->mgmt_ie[index].ie_length = ie->ie_length; - priv->mgmt_ie[index].ie_index = cpu_to_le16(index); - priv->mgmt_ie[index].mgmt_subtype_mask = - cpu_to_le16(mask); - - ie->ie_index = cpu_to_le16(index); - } else { - if (mask != MWIFIEX_DELETE_MASK) - return -1; - /* - * Check if this index is being used on any - * other interface. - */ - if (mwifiex_ie_index_used_by_other_intf(priv, index)) - return -1; - - ie->ie_length = 0; - memcpy(&priv->mgmt_ie[index], ie, - sizeof(struct mwifiex_ie)); - } - - le16_add_cpu(&ie_list->len, - le16_to_cpu(priv->mgmt_ie[index].ie_length) + - MWIFIEX_IE_HDR_SIZE); - input_len -= tlv_len + MWIFIEX_IE_HDR_SIZE; - } - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) - return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_CUSTOM_IE_I, ie_list, false); - - return 0; -} - -/* Copy individual custom IEs for beacon, probe response and assoc response - * and prepare single structure for IE setting. - * This function also updates allocated IE indices from driver. - */ -static int -mwifiex_update_uap_custom_ie(struct mwifiex_private *priv, - struct mwifiex_ie *beacon_ie, u16 *beacon_idx, - struct mwifiex_ie *pr_ie, u16 *probe_idx, - struct mwifiex_ie *ar_ie, u16 *assoc_idx) -{ - struct mwifiex_ie_list *ap_custom_ie; - u8 *pos; - u16 len; - int ret; - - ap_custom_ie = kzalloc(sizeof(*ap_custom_ie), GFP_KERNEL); - if (!ap_custom_ie) - return -ENOMEM; - - ap_custom_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); - pos = (u8 *)ap_custom_ie->ie_list; - - if (beacon_ie) { - len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(beacon_ie->ie_length); - memcpy(pos, beacon_ie, len); - pos += len; - le16_add_cpu(&ap_custom_ie->len, len); - } - if (pr_ie) { - len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(pr_ie->ie_length); - memcpy(pos, pr_ie, len); - pos += len; - le16_add_cpu(&ap_custom_ie->len, len); - } - if (ar_ie) { - len = sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(ar_ie->ie_length); - memcpy(pos, ar_ie, len); - pos += len; - le16_add_cpu(&ap_custom_ie->len, len); - } - - ret = mwifiex_update_autoindex_ies(priv, ap_custom_ie); - - pos = (u8 *)(&ap_custom_ie->ie_list[0].ie_index); - if (beacon_ie && *beacon_idx == MWIFIEX_AUTO_IDX_MASK) { - /* save beacon ie index after auto-indexing */ - *beacon_idx = le16_to_cpu(ap_custom_ie->ie_list[0].ie_index); - len = sizeof(*beacon_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(beacon_ie->ie_length); - pos += len; - } - if (pr_ie && le16_to_cpu(pr_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) { - /* save probe resp ie index after auto-indexing */ - *probe_idx = *((u16 *)pos); - len = sizeof(*pr_ie) - IEEE_MAX_IE_SIZE + - le16_to_cpu(pr_ie->ie_length); - pos += len; - } - if (ar_ie && le16_to_cpu(ar_ie->ie_index) == MWIFIEX_AUTO_IDX_MASK) - /* save assoc resp ie index after auto-indexing */ - *assoc_idx = *((u16 *)pos); - - kfree(ap_custom_ie); - return ret; -} - -/* This function checks if the vendor specified IE is present in passed buffer - * and copies it to mwifiex_ie structure. - * Function takes pointer to struct mwifiex_ie pointer as argument. - * If the vendor specified IE is present then memory is allocated for - * mwifiex_ie pointer and filled in with IE. Caller should take care of freeing - * this memory. - */ -static int mwifiex_update_vs_ie(const u8 *ies, int ies_len, - struct mwifiex_ie **ie_ptr, u16 mask, - unsigned int oui, u8 oui_type) -{ - struct ieee_types_header *vs_ie; - struct mwifiex_ie *ie = *ie_ptr; - const u8 *vendor_ie; - - vendor_ie = cfg80211_find_vendor_ie(oui, oui_type, ies, ies_len); - if (vendor_ie) { - if (!*ie_ptr) { - *ie_ptr = kzalloc(sizeof(struct mwifiex_ie), - GFP_KERNEL); - if (!*ie_ptr) - return -ENOMEM; - ie = *ie_ptr; - } - - vs_ie = (struct ieee_types_header *)vendor_ie; - memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length), - vs_ie, vs_ie->len + 2); - le16_add_cpu(&ie->ie_length, vs_ie->len + 2); - ie->mgmt_subtype_mask = cpu_to_le16(mask); - ie->ie_index = cpu_to_le16(MWIFIEX_AUTO_IDX_MASK); - } - - *ie_ptr = ie; - return 0; -} - -/* This function parses beacon IEs, probe response IEs, association response IEs - * from cfg80211_ap_settings->beacon and sets these IE to FW. - */ -static int mwifiex_set_mgmt_beacon_data_ies(struct mwifiex_private *priv, - struct cfg80211_beacon_data *data) -{ - struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL, *ar_ie = NULL; - u16 beacon_idx = MWIFIEX_AUTO_IDX_MASK, pr_idx = MWIFIEX_AUTO_IDX_MASK; - u16 ar_idx = MWIFIEX_AUTO_IDX_MASK; - int ret = 0; - - if (data->beacon_ies && data->beacon_ies_len) { - mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, - &beacon_ie, MGMT_MASK_BEACON, - WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPS); - mwifiex_update_vs_ie(data->beacon_ies, data->beacon_ies_len, - &beacon_ie, MGMT_MASK_BEACON, - WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); - } - - if (data->proberesp_ies && data->proberesp_ies_len) { - mwifiex_update_vs_ie(data->proberesp_ies, - data->proberesp_ies_len, &pr_ie, - MGMT_MASK_PROBE_RESP, WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPS); - mwifiex_update_vs_ie(data->proberesp_ies, - data->proberesp_ies_len, &pr_ie, - MGMT_MASK_PROBE_RESP, - WLAN_OUI_WFA, WLAN_OUI_TYPE_WFA_P2P); - } - - if (data->assocresp_ies && data->assocresp_ies_len) { - mwifiex_update_vs_ie(data->assocresp_ies, - data->assocresp_ies_len, &ar_ie, - MGMT_MASK_ASSOC_RESP | - MGMT_MASK_REASSOC_RESP, - WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPS); - mwifiex_update_vs_ie(data->assocresp_ies, - data->assocresp_ies_len, &ar_ie, - MGMT_MASK_ASSOC_RESP | - MGMT_MASK_REASSOC_RESP, WLAN_OUI_WFA, - WLAN_OUI_TYPE_WFA_P2P); - } - - if (beacon_ie || pr_ie || ar_ie) { - ret = mwifiex_update_uap_custom_ie(priv, beacon_ie, - &beacon_idx, pr_ie, - &pr_idx, ar_ie, &ar_idx); - if (ret) - goto done; - } - - priv->beacon_idx = beacon_idx; - priv->proberesp_idx = pr_idx; - priv->assocresp_idx = ar_idx; - -done: - kfree(beacon_ie); - kfree(pr_ie); - kfree(ar_ie); - - return ret; -} - -/* This function parses head and tail IEs, from cfg80211_beacon_data and sets - * these IE to FW. - */ -static int mwifiex_uap_parse_tail_ies(struct mwifiex_private *priv, - struct cfg80211_beacon_data *info) -{ - struct mwifiex_ie *gen_ie; - struct ieee_types_header *hdr; - struct ieee80211_vendor_ie *vendorhdr; - u16 gen_idx = MWIFIEX_AUTO_IDX_MASK, ie_len = 0; - int left_len, parsed_len = 0; - - if (!info->tail || !info->tail_len) - return 0; - - gen_ie = kzalloc(sizeof(*gen_ie), GFP_KERNEL); - if (!gen_ie) - return -ENOMEM; - - left_len = info->tail_len; - - /* Many IEs are generated in FW by parsing bss configuration. - * Let's not add them here; else we may end up duplicating these IEs - */ - while (left_len > sizeof(struct ieee_types_header)) { - hdr = (void *)(info->tail + parsed_len); - switch (hdr->element_id) { - case WLAN_EID_SSID: - case WLAN_EID_SUPP_RATES: - case WLAN_EID_COUNTRY: - case WLAN_EID_PWR_CONSTRAINT: - case WLAN_EID_EXT_SUPP_RATES: - case WLAN_EID_HT_CAPABILITY: - case WLAN_EID_HT_OPERATION: - case WLAN_EID_VHT_CAPABILITY: - case WLAN_EID_VHT_OPERATION: - case WLAN_EID_VENDOR_SPECIFIC: - break; - default: - memcpy(gen_ie->ie_buffer + ie_len, hdr, - hdr->len + sizeof(struct ieee_types_header)); - ie_len += hdr->len + sizeof(struct ieee_types_header); - break; - } - left_len -= hdr->len + sizeof(struct ieee_types_header); - parsed_len += hdr->len + sizeof(struct ieee_types_header); - } - - /* parse only WPA vendor IE from tail, WMM IE is configured by - * bss_config command - */ - vendorhdr = (void *)cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WPA, - info->tail, info->tail_len); - if (vendorhdr) { - memcpy(gen_ie->ie_buffer + ie_len, vendorhdr, - vendorhdr->len + sizeof(struct ieee_types_header)); - ie_len += vendorhdr->len + sizeof(struct ieee_types_header); - } - - if (!ie_len) { - kfree(gen_ie); - return 0; - } - - gen_ie->ie_index = cpu_to_le16(gen_idx); - gen_ie->mgmt_subtype_mask = cpu_to_le16(MGMT_MASK_BEACON | - MGMT_MASK_PROBE_RESP | - MGMT_MASK_ASSOC_RESP); - gen_ie->ie_length = cpu_to_le16(ie_len); - - if (mwifiex_update_uap_custom_ie(priv, gen_ie, &gen_idx, NULL, NULL, - NULL, NULL)) { - kfree(gen_ie); - return -1; - } - - priv->gen_idx = gen_idx; - kfree(gen_ie); - return 0; -} - -/* This function parses different IEs-head & tail IEs, beacon IEs, - * probe response IEs, association response IEs from cfg80211_ap_settings - * function and sets these IE to FW. - */ -int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, - struct cfg80211_beacon_data *info) -{ - int ret; - - ret = mwifiex_uap_parse_tail_ies(priv, info); - - if (ret) - return ret; - - return mwifiex_set_mgmt_beacon_data_ies(priv, info); -} - -/* This function removes management IE set */ -int mwifiex_del_mgmt_ies(struct mwifiex_private *priv) -{ - struct mwifiex_ie *beacon_ie = NULL, *pr_ie = NULL; - struct mwifiex_ie *ar_ie = NULL, *gen_ie = NULL; - int ret = 0; - - if (priv->gen_idx != MWIFIEX_AUTO_IDX_MASK) { - gen_ie = kmalloc(sizeof(*gen_ie), GFP_KERNEL); - if (!gen_ie) - return -ENOMEM; - - gen_ie->ie_index = cpu_to_le16(priv->gen_idx); - gen_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); - gen_ie->ie_length = 0; - if (mwifiex_update_uap_custom_ie(priv, gen_ie, &priv->gen_idx, - NULL, &priv->proberesp_idx, - NULL, &priv->assocresp_idx)) { - ret = -1; - goto done; - } - - priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; - } - - if (priv->beacon_idx != MWIFIEX_AUTO_IDX_MASK) { - beacon_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!beacon_ie) { - ret = -ENOMEM; - goto done; - } - beacon_ie->ie_index = cpu_to_le16(priv->beacon_idx); - beacon_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); - beacon_ie->ie_length = 0; - } - if (priv->proberesp_idx != MWIFIEX_AUTO_IDX_MASK) { - pr_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!pr_ie) { - ret = -ENOMEM; - goto done; - } - pr_ie->ie_index = cpu_to_le16(priv->proberesp_idx); - pr_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); - pr_ie->ie_length = 0; - } - if (priv->assocresp_idx != MWIFIEX_AUTO_IDX_MASK) { - ar_ie = kmalloc(sizeof(struct mwifiex_ie), GFP_KERNEL); - if (!ar_ie) { - ret = -ENOMEM; - goto done; - } - ar_ie->ie_index = cpu_to_le16(priv->assocresp_idx); - ar_ie->mgmt_subtype_mask = cpu_to_le16(MWIFIEX_DELETE_MASK); - ar_ie->ie_length = 0; - } - - if (beacon_ie || pr_ie || ar_ie) - ret = mwifiex_update_uap_custom_ie(priv, - beacon_ie, &priv->beacon_idx, - pr_ie, &priv->proberesp_idx, - ar_ie, &priv->assocresp_idx); - -done: - kfree(gen_ie); - kfree(beacon_ie); - kfree(pr_ie); - kfree(ar_ie); - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c deleted file mode 100644 index de74a7773fb6..000000000000 --- a/drivers/net/wireless/mwifiex/init.c +++ /dev/null @@ -1,782 +0,0 @@ -/* - * Marvell Wireless LAN device driver: HW/FW Initialization - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - -/* - * This function adds a BSS priority table to the table list. - * - * The function allocates a new BSS priority table node and adds it to - * the end of BSS priority table list, kept in driver memory. - */ -static int mwifiex_add_bss_prio_tbl(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bss_prio_node *bss_prio; - struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; - unsigned long flags; - - bss_prio = kzalloc(sizeof(struct mwifiex_bss_prio_node), GFP_KERNEL); - if (!bss_prio) - return -ENOMEM; - - bss_prio->priv = priv; - INIT_LIST_HEAD(&bss_prio->list); - - spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); - list_add_tail(&bss_prio->list, &tbl[priv->bss_priority].bss_prio_head); - spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); - - return 0; -} - -static void wakeup_timer_fn(unsigned long data) -{ - struct mwifiex_adapter *adapter = (struct mwifiex_adapter *)data; - - mwifiex_dbg(adapter, ERROR, "Firmware wakeup failed\n"); - adapter->hw_status = MWIFIEX_HW_STATUS_RESET; - mwifiex_cancel_all_pending_cmd(adapter); - - if (adapter->if_ops.card_reset) - adapter->if_ops.card_reset(adapter); -} - -/* - * This function initializes the private structure and sets default - * values to the members. - * - * Additionally, it also initializes all the locks and sets up all the - * lists. - */ -int mwifiex_init_priv(struct mwifiex_private *priv) -{ - u32 i; - - priv->media_connected = false; - eth_broadcast_addr(priv->curr_addr); - priv->port_open = false; - priv->usb_port = MWIFIEX_USB_EP_DATA; - priv->pkt_tx_ctrl = 0; - priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; - priv->data_rate = 0; /* Initially indicate the rate as auto */ - priv->is_data_rate_auto = true; - priv->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; - priv->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; - - priv->sec_info.wep_enabled = 0; - priv->sec_info.authentication_mode = NL80211_AUTHTYPE_OPEN_SYSTEM; - priv->sec_info.encryption_mode = 0; - for (i = 0; i < ARRAY_SIZE(priv->wep_key); i++) - memset(&priv->wep_key[i], 0, sizeof(struct mwifiex_wep_key)); - priv->wep_key_curr_index = 0; - priv->curr_pkt_filter = HostCmd_ACT_MAC_RX_ON | HostCmd_ACT_MAC_TX_ON | - HostCmd_ACT_MAC_ETHERNETII_ENABLE; - - priv->beacon_period = 100; /* beacon interval */ ; - priv->attempted_bss_desc = NULL; - memset(&priv->curr_bss_params, 0, sizeof(priv->curr_bss_params)); - priv->listen_interval = MWIFIEX_DEFAULT_LISTEN_INTERVAL; - - memset(&priv->prev_ssid, 0, sizeof(priv->prev_ssid)); - memset(&priv->prev_bssid, 0, sizeof(priv->prev_bssid)); - memset(&priv->assoc_rsp_buf, 0, sizeof(priv->assoc_rsp_buf)); - priv->assoc_rsp_size = 0; - priv->adhoc_channel = DEFAULT_AD_HOC_CHANNEL; - priv->atim_window = 0; - priv->adhoc_state = ADHOC_IDLE; - priv->tx_power_level = 0; - priv->max_tx_power_level = 0; - priv->min_tx_power_level = 0; - priv->tx_rate = 0; - priv->rxpd_htinfo = 0; - priv->rxpd_rate = 0; - priv->rate_bitmap = 0; - priv->data_rssi_last = 0; - priv->data_rssi_avg = 0; - priv->data_nf_avg = 0; - priv->data_nf_last = 0; - priv->bcn_rssi_last = 0; - priv->bcn_rssi_avg = 0; - priv->bcn_nf_avg = 0; - priv->bcn_nf_last = 0; - memset(&priv->wpa_ie, 0, sizeof(priv->wpa_ie)); - memset(&priv->aes_key, 0, sizeof(priv->aes_key)); - priv->wpa_ie_len = 0; - priv->wpa_is_gtk_set = false; - - memset(&priv->assoc_tlv_buf, 0, sizeof(priv->assoc_tlv_buf)); - priv->assoc_tlv_buf_len = 0; - memset(&priv->wps, 0, sizeof(priv->wps)); - memset(&priv->gen_ie_buf, 0, sizeof(priv->gen_ie_buf)); - priv->gen_ie_buf_len = 0; - memset(priv->vs_ie, 0, sizeof(priv->vs_ie)); - - priv->wmm_required = true; - priv->wmm_enabled = false; - priv->wmm_qosinfo = 0; - priv->curr_bcn_buf = NULL; - priv->curr_bcn_size = 0; - priv->wps_ie = NULL; - priv->wps_ie_len = 0; - priv->ap_11n_enabled = 0; - memset(&priv->roc_cfg, 0, sizeof(priv->roc_cfg)); - - priv->scan_block = false; - - priv->csa_chan = 0; - priv->csa_expire_time = 0; - priv->del_list_idx = 0; - priv->hs2_enabled = false; - priv->check_tdls_tx = false; - memcpy(priv->tos_to_tid_inv, tos_to_tid_inv, MAX_NUM_TID); - - mwifiex_init_11h_params(priv); - - return mwifiex_add_bss_prio_tbl(priv); -} - -/* - * This function allocates buffers for members of the adapter - * structure. - * - * The memory allocated includes scan table, command buffers, and - * sleep confirm command buffer. In addition, the queues are - * also initialized. - */ -static int mwifiex_allocate_adapter(struct mwifiex_adapter *adapter) -{ - int ret; - - /* Allocate command buffer */ - ret = mwifiex_alloc_cmd_buffer(adapter); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to alloc cmd buffer\n", - __func__); - return -1; - } - - adapter->sleep_cfm = - dev_alloc_skb(sizeof(struct mwifiex_opt_sleep_confirm) - + INTF_HEADER_LEN); - - if (!adapter->sleep_cfm) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to alloc sleep cfm\t" - " cmd buffer\n", __func__); - return -1; - } - skb_reserve(adapter->sleep_cfm, INTF_HEADER_LEN); - - return 0; -} - -/* - * This function initializes the adapter structure and sets default - * values to the members of adapter. - * - * This also initializes the WMM related parameters in the driver private - * structures. - */ -static void mwifiex_init_adapter(struct mwifiex_adapter *adapter) -{ - struct mwifiex_opt_sleep_confirm *sleep_cfm_buf = NULL; - - skb_put(adapter->sleep_cfm, sizeof(struct mwifiex_opt_sleep_confirm)); - - adapter->cmd_sent = false; - - if (adapter->iface_type == MWIFIEX_SDIO) - adapter->data_sent = true; - else - adapter->data_sent = false; - - adapter->cmd_resp_received = false; - adapter->event_received = false; - adapter->data_received = false; - - adapter->surprise_removed = false; - - adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; - - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; - adapter->ps_state = PS_STATE_AWAKE; - adapter->need_to_wakeup = false; - - adapter->scan_mode = HostCmd_BSS_MODE_ANY; - adapter->specific_scan_time = MWIFIEX_SPECIFIC_SCAN_CHAN_TIME; - adapter->active_scan_time = MWIFIEX_ACTIVE_SCAN_CHAN_TIME; - adapter->passive_scan_time = MWIFIEX_PASSIVE_SCAN_CHAN_TIME; - adapter->scan_chan_gap_time = MWIFIEX_DEF_SCAN_CHAN_GAP_TIME; - - adapter->scan_probes = 1; - - adapter->multiple_dtim = 1; - - adapter->local_listen_interval = 0; /* default value in firmware - will be used */ - - adapter->is_deep_sleep = false; - - adapter->delay_null_pkt = false; - adapter->delay_to_ps = 1000; - adapter->enhanced_ps_mode = PS_MODE_AUTO; - - adapter->gen_null_pkt = false; /* Disable NULL Pkg generation by - default */ - adapter->pps_uapsd_mode = false; /* Disable pps/uapsd mode by - default */ - adapter->pm_wakeup_card_req = false; - - adapter->pm_wakeup_fw_try = false; - - adapter->curr_tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; - - adapter->is_hs_configured = false; - adapter->hs_cfg.conditions = cpu_to_le32(HS_CFG_COND_DEF); - adapter->hs_cfg.gpio = HS_CFG_GPIO_DEF; - adapter->hs_cfg.gap = HS_CFG_GAP_DEF; - adapter->hs_activated = false; - - memset(adapter->event_body, 0, sizeof(adapter->event_body)); - adapter->hw_dot_11n_dev_cap = 0; - adapter->hw_dev_mcs_support = 0; - adapter->sec_chan_offset = 0; - adapter->adhoc_11n_enabled = false; - - mwifiex_wmm_init(adapter); - - sleep_cfm_buf = (struct mwifiex_opt_sleep_confirm *) - adapter->sleep_cfm->data; - memset(sleep_cfm_buf, 0, adapter->sleep_cfm->len); - sleep_cfm_buf->command = cpu_to_le16(HostCmd_CMD_802_11_PS_MODE_ENH); - sleep_cfm_buf->size = cpu_to_le16(adapter->sleep_cfm->len); - sleep_cfm_buf->result = 0; - sleep_cfm_buf->action = cpu_to_le16(SLEEP_CONFIRM); - sleep_cfm_buf->resp_ctrl = cpu_to_le16(RESP_NEEDED); - - memset(&adapter->sleep_params, 0, sizeof(adapter->sleep_params)); - memset(&adapter->sleep_period, 0, sizeof(adapter->sleep_period)); - adapter->tx_lock_flag = false; - adapter->null_pkt_interval = 0; - adapter->fw_bands = 0; - adapter->config_bands = 0; - adapter->adhoc_start_band = 0; - adapter->scan_channels = NULL; - adapter->fw_release_number = 0; - adapter->fw_cap_info = 0; - memset(&adapter->upld_buf, 0, sizeof(adapter->upld_buf)); - adapter->event_cause = 0; - adapter->region_code = 0; - adapter->bcn_miss_time_out = DEFAULT_BCN_MISS_TIMEOUT; - adapter->adhoc_awake_period = 0; - memset(&adapter->arp_filter, 0, sizeof(adapter->arp_filter)); - adapter->arp_filter_size = 0; - adapter->max_mgmt_ie_index = MAX_MGMT_IE_INDEX; - adapter->key_api_major_ver = 0; - adapter->key_api_minor_ver = 0; - eth_broadcast_addr(adapter->perm_addr); - adapter->iface_limit.sta_intf = MWIFIEX_MAX_STA_NUM; - adapter->iface_limit.uap_intf = MWIFIEX_MAX_UAP_NUM; - adapter->iface_limit.p2p_intf = MWIFIEX_MAX_P2P_NUM; - adapter->active_scan_triggered = false; - setup_timer(&adapter->wakeup_timer, wakeup_timer_fn, - (unsigned long)adapter); -} - -/* - * This function sets trans_start per tx_queue - */ -void mwifiex_set_trans_start(struct net_device *dev) -{ - int i; - - for (i = 0; i < dev->num_tx_queues; i++) - netdev_get_tx_queue(dev, i)->trans_start = jiffies; - - dev->trans_start = jiffies; -} - -/* - * This function wakes up all queues in net_device - */ -void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, - struct mwifiex_adapter *adapter) -{ - unsigned long dev_queue_flags; - unsigned int i; - - spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); - - for (i = 0; i < netdev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); - - if (netif_tx_queue_stopped(txq)) - netif_tx_wake_queue(txq); - } - - spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); -} - -/* - * This function stops all queues in net_device - */ -void mwifiex_stop_net_dev_queue(struct net_device *netdev, - struct mwifiex_adapter *adapter) -{ - unsigned long dev_queue_flags; - unsigned int i; - - spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); - - for (i = 0; i < netdev->num_tx_queues; i++) { - struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); - - if (!netif_tx_queue_stopped(txq)) - netif_tx_stop_queue(txq); - } - - spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); -} - -/* - * This function releases the lock variables and frees the locks and - * associated locks. - */ -static void mwifiex_free_lock_list(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - s32 i, j; - - /* Free lists */ - list_del(&adapter->cmd_free_q); - list_del(&adapter->cmd_pending_q); - list_del(&adapter->scan_pending_q); - - for (i = 0; i < adapter->priv_num; i++) - list_del(&adapter->bss_prio_tbl[i].bss_prio_head); - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - for (j = 0; j < MAX_NUM_TID; ++j) - list_del(&priv->wmm.tid_tbl_ptr[j].ra_list); - list_del(&priv->tx_ba_stream_tbl_ptr); - list_del(&priv->rx_reorder_tbl_ptr); - list_del(&priv->sta_list); - list_del(&priv->auto_tdls_list); - } - } -} - -/* - * This function performs cleanup for adapter structure. - * - * The cleanup is done recursively, by canceling all pending - * commands, freeing the member buffers previously allocated - * (command buffers, scan table buffer, sleep confirm command - * buffer), stopping the timers and calling the cleanup routines - * for every interface. - */ -static void -mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) -{ - int idx; - - if (!adapter) { - pr_err("%s: adapter is NULL\n", __func__); - return; - } - - del_timer(&adapter->wakeup_timer); - mwifiex_cancel_all_pending_cmd(adapter); - wake_up_interruptible(&adapter->cmd_wait_q.wait); - wake_up_interruptible(&adapter->hs_activate_wait_q); - - /* Free lock variables */ - mwifiex_free_lock_list(adapter); - - /* Free command buffer */ - mwifiex_dbg(adapter, INFO, "info: free cmd buffer\n"); - mwifiex_free_cmd_buffer(adapter); - - for (idx = 0; idx < adapter->num_mem_types; idx++) { - struct memory_type_mapping *entry = - &adapter->mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - - if (adapter->drv_info_dump) { - vfree(adapter->drv_info_dump); - adapter->drv_info_dump = NULL; - adapter->drv_info_size = 0; - } - - if (adapter->sleep_cfm) - dev_kfree_skb_any(adapter->sleep_cfm); -} - -/* - * This function intializes the lock variables and - * the list heads. - */ -int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - s32 i, j; - - spin_lock_init(&adapter->mwifiex_lock); - spin_lock_init(&adapter->int_lock); - spin_lock_init(&adapter->main_proc_lock); - spin_lock_init(&adapter->mwifiex_cmd_lock); - spin_lock_init(&adapter->queue_lock); - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - spin_lock_init(&priv->rx_pkt_lock); - spin_lock_init(&priv->wmm.ra_list_spinlock); - spin_lock_init(&priv->curr_bcn_buf_lock); - spin_lock_init(&priv->sta_list_spinlock); - spin_lock_init(&priv->auto_tdls_lock); - } - } - - /* Initialize cmd_free_q */ - INIT_LIST_HEAD(&adapter->cmd_free_q); - /* Initialize cmd_pending_q */ - INIT_LIST_HEAD(&adapter->cmd_pending_q); - /* Initialize scan_pending_q */ - INIT_LIST_HEAD(&adapter->scan_pending_q); - - spin_lock_init(&adapter->cmd_free_q_lock); - spin_lock_init(&adapter->cmd_pending_q_lock); - spin_lock_init(&adapter->scan_pending_q_lock); - spin_lock_init(&adapter->rx_proc_lock); - - skb_queue_head_init(&adapter->rx_data_q); - skb_queue_head_init(&adapter->tx_data_q); - - for (i = 0; i < adapter->priv_num; ++i) { - INIT_LIST_HEAD(&adapter->bss_prio_tbl[i].bss_prio_head); - spin_lock_init(&adapter->bss_prio_tbl[i].bss_prio_lock); - } - - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i]) - continue; - priv = adapter->priv[i]; - for (j = 0; j < MAX_NUM_TID; ++j) - INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[j].ra_list); - INIT_LIST_HEAD(&priv->tx_ba_stream_tbl_ptr); - INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); - INIT_LIST_HEAD(&priv->sta_list); - INIT_LIST_HEAD(&priv->auto_tdls_list); - skb_queue_head_init(&priv->tdls_txq); - skb_queue_head_init(&priv->bypass_txq); - - spin_lock_init(&priv->tx_ba_stream_tbl_lock); - spin_lock_init(&priv->rx_reorder_tbl_lock); - - spin_lock_init(&priv->ack_status_lock); - idr_init(&priv->ack_status_frames); - } - - return 0; -} - -/* - * This function initializes the firmware. - * - * The following operations are performed sequentially - - * - Allocate adapter structure - * - Initialize the adapter structure - * - Initialize the private structure - * - Add BSS priority tables to the adapter structure - * - For each interface, send the init commands to firmware - * - Send the first command in command pending queue, if available - */ -int mwifiex_init_fw(struct mwifiex_adapter *adapter) -{ - int ret; - struct mwifiex_private *priv; - u8 i, first_sta = true; - int is_cmd_pend_q_empty; - unsigned long flags; - - adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; - - /* Allocate memory for member of adapter structure */ - ret = mwifiex_allocate_adapter(adapter); - if (ret) - return -1; - - /* Initialize adapter structure */ - mwifiex_init_adapter(adapter); - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - - /* Initialize private structure */ - ret = mwifiex_init_priv(priv); - if (ret) - return -1; - } - } - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - ret = mwifiex_sta_init_cmd(adapter->priv[i], first_sta, - true); - if (ret == -1) - return -1; - - first_sta = false; - } - } - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - if (!is_cmd_pend_q_empty) { - /* Send the first command in queue and return */ - if (mwifiex_main_process(adapter) != -1) - ret = -EINPROGRESS; - } else { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - } - - return ret; -} - -/* - * This function deletes the BSS priority tables. - * - * The function traverses through all the allocated BSS priority nodes - * in every BSS priority table and frees them. - */ -static void mwifiex_delete_bss_prio_tbl(struct mwifiex_private *priv) -{ - int i; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bss_prio_node *bssprio_node, *tmp_node; - struct list_head *head; - spinlock_t *lock; /* bss priority lock */ - unsigned long flags; - - for (i = 0; i < adapter->priv_num; ++i) { - head = &adapter->bss_prio_tbl[i].bss_prio_head; - lock = &adapter->bss_prio_tbl[i].bss_prio_lock; - mwifiex_dbg(adapter, INFO, - "info: delete BSS priority table,\t" - "bss_type = %d, bss_num = %d, i = %d,\t" - "head = %p\n", - priv->bss_type, priv->bss_num, i, head); - - { - spin_lock_irqsave(lock, flags); - if (list_empty(head)) { - spin_unlock_irqrestore(lock, flags); - continue; - } - list_for_each_entry_safe(bssprio_node, tmp_node, head, - list) { - if (bssprio_node->priv == priv) { - mwifiex_dbg(adapter, INFO, - "info: Delete\t" - "node %p, next = %p\n", - bssprio_node, tmp_node); - list_del(&bssprio_node->list); - kfree(bssprio_node); - } - } - spin_unlock_irqrestore(lock, flags); - } - } -} - -/* - * This function frees the private structure, including cleans - * up the TX and RX queues and frees the BSS priority tables. - */ -void mwifiex_free_priv(struct mwifiex_private *priv) -{ - mwifiex_clean_txrx(priv); - mwifiex_delete_bss_prio_tbl(priv); - mwifiex_free_curr_bcn(priv); -} - -/* - * This function is used to shutdown the driver. - * - * The following operations are performed sequentially - - * - Check if already shut down - * - Make sure the main process has stopped - * - Clean up the Tx and Rx queues - * - Delete BSS priority tables - * - Free the adapter - * - Notify completion - */ -int -mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) -{ - int ret = -EINPROGRESS; - struct mwifiex_private *priv; - s32 i; - unsigned long flags; - struct sk_buff *skb; - - /* mwifiex already shutdown */ - if (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY) - return 0; - - adapter->hw_status = MWIFIEX_HW_STATUS_CLOSING; - /* wait for mwifiex_process to complete */ - if (adapter->mwifiex_processing) { - mwifiex_dbg(adapter, WARN, - "main process is still running\n"); - return ret; - } - - /* cancel current command */ - if (adapter->curr_cmd) { - mwifiex_dbg(adapter, WARN, - "curr_cmd is still in processing\n"); - del_timer_sync(&adapter->cmd_timer); - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - adapter->curr_cmd = NULL; - } - - /* shut down mwifiex */ - mwifiex_dbg(adapter, MSG, - "info: shutdown mwifiex...\n"); - - /* Clean up Tx/Rx queues and delete BSS priority table */ - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - priv = adapter->priv[i]; - - mwifiex_clean_auto_tdls(priv); - mwifiex_abort_cac(priv); - mwifiex_clean_txrx(priv); - mwifiex_delete_bss_prio_tbl(priv); - } - } - - atomic_set(&adapter->tx_queued, 0); - while ((skb = skb_dequeue(&adapter->tx_data_q))) - mwifiex_write_data_complete(adapter, skb, 0, 0); - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - - while ((skb = skb_dequeue(&adapter->rx_data_q))) { - struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); - - atomic_dec(&adapter->rx_pending); - priv = adapter->priv[rx_info->bss_num]; - if (priv) - priv->stats.rx_dropped++; - - dev_kfree_skb_any(skb); - } - - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - - spin_lock(&adapter->mwifiex_lock); - - mwifiex_adapter_cleanup(adapter); - - spin_unlock(&adapter->mwifiex_lock); - - /* Notify completion */ - ret = mwifiex_shutdown_fw_complete(adapter); - - return ret; -} - -/* - * This function downloads the firmware to the card. - * - * The actual download is preceded by two sanity checks - - * - Check if firmware is already running - * - Check if the interface is the winner to download the firmware - * - * ...and followed by another - - * - Check if the firmware is downloaded successfully - * - * After download is successfully completed, the host interrupts are enabled. - */ -int mwifiex_dnld_fw(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *pmfw) -{ - int ret; - u32 poll_num = 1; - - if (adapter->if_ops.check_fw_status) { - adapter->winner = 0; - - /* check if firmware is already running */ - ret = adapter->if_ops.check_fw_status(adapter, poll_num); - if (!ret) { - mwifiex_dbg(adapter, MSG, - "WLAN FW already running! Skip FW dnld\n"); - return 0; - } - - poll_num = MAX_FIRMWARE_POLL_TRIES; - - /* check if we are the winner for downloading FW */ - if (!adapter->winner) { - mwifiex_dbg(adapter, MSG, - "FW already running! Skip FW dnld\n"); - goto poll_fw; - } - } - - if (pmfw) { - /* Download firmware with helper */ - ret = adapter->if_ops.prog_fw(adapter, pmfw); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "prog_fw failed ret=%#x\n", ret); - return ret; - } - } - -poll_fw: - /* Check if the firmware is downloaded successfully or not */ - ret = adapter->if_ops.check_fw_status(adapter, poll_num); - if (ret) - mwifiex_dbg(adapter, ERROR, - "FW failed to be active in time\n"); - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/ioctl.h b/drivers/net/wireless/mwifiex/ioctl.h deleted file mode 100644 index 4f0174c64946..000000000000 --- a/drivers/net/wireless/mwifiex/ioctl.h +++ /dev/null @@ -1,470 +0,0 @@ -/* - * Marvell Wireless LAN device driver: ioctl data structures & APIs - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_IOCTL_H_ -#define _MWIFIEX_IOCTL_H_ - -#include - -enum { - MWIFIEX_SCAN_TYPE_UNCHANGED = 0, - MWIFIEX_SCAN_TYPE_ACTIVE, - MWIFIEX_SCAN_TYPE_PASSIVE -}; - -struct mwifiex_user_scan { - u32 scan_cfg_len; - u8 scan_cfg_buf[1]; -}; - -#define MWIFIEX_PROMISC_MODE 1 -#define MWIFIEX_MULTICAST_MODE 2 -#define MWIFIEX_ALL_MULTI_MODE 4 -#define MWIFIEX_MAX_MULTICAST_LIST_SIZE 32 - -struct mwifiex_multicast_list { - u32 mode; - u32 num_multicast_addr; - u8 mac_list[MWIFIEX_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; -}; - -struct mwifiex_chan_freq { - u32 channel; - u32 freq; -}; - -struct mwifiex_ssid_bssid { - struct cfg80211_ssid ssid; - u8 bssid[ETH_ALEN]; -}; - -enum { - BAND_B = 1, - BAND_G = 2, - BAND_A = 4, - BAND_GN = 8, - BAND_AN = 16, - BAND_AAC = 32, -}; - -#define MWIFIEX_WPA_PASSHPHRASE_LEN 64 -struct wpa_param { - u8 pairwise_cipher_wpa; - u8 pairwise_cipher_wpa2; - u8 group_cipher; - u32 length; - u8 passphrase[MWIFIEX_WPA_PASSHPHRASE_LEN]; -}; - -struct wep_key { - u8 key_index; - u8 is_default; - u16 length; - u8 key[WLAN_KEY_LEN_WEP104]; -}; - -#define KEY_MGMT_ON_HOST 0x03 -#define MWIFIEX_AUTH_MODE_AUTO 0xFF -#define BAND_CONFIG_BG 0x00 -#define BAND_CONFIG_A 0x01 -#define MWIFIEX_SUPPORTED_RATES 14 -#define MWIFIEX_SUPPORTED_RATES_EXT 32 -#define MWIFIEX_TDLS_SUPPORTED_RATES 8 -#define MWIFIEX_TDLS_DEF_QOS_CAPAB 0xf -#define MWIFIEX_PRIO_BK 2 -#define MWIFIEX_PRIO_VI 5 - -struct mwifiex_uap_bss_param { - u8 channel; - u8 band_cfg; - u16 rts_threshold; - u16 frag_threshold; - u8 retry_limit; - struct mwifiex_802_11_ssid ssid; - u8 bcast_ssid_ctl; - u8 radio_ctl; - u8 dtim_period; - u16 beacon_period; - u16 auth_mode; - u16 protocol; - u16 key_mgmt; - u16 key_mgmt_operation; - struct wpa_param wpa_cfg; - struct wep_key wep_cfg[NUM_WEP_KEYS]; - struct ieee80211_ht_cap ht_cap; - struct ieee80211_vht_cap vht_cap; - u8 rates[MWIFIEX_SUPPORTED_RATES]; - u32 sta_ao_timer; - u32 ps_sta_ao_timer; - u8 qos_info; - u8 power_constraint; - struct mwifiex_types_wmm_info wmm_info; -}; - -enum { - ADHOC_IDLE, - ADHOC_STARTED, - ADHOC_JOINED, - ADHOC_COALESCED -}; - -struct mwifiex_ds_get_stats { - u32 mcast_tx_frame; - u32 failed; - u32 retry; - u32 multi_retry; - u32 frame_dup; - u32 rts_success; - u32 rts_failure; - u32 ack_failure; - u32 rx_frag; - u32 mcast_rx_frame; - u32 fcs_error; - u32 tx_frame; - u32 wep_icv_error[4]; - u32 bcn_rcv_cnt; - u32 bcn_miss_cnt; -}; - -#define MWIFIEX_MAX_VER_STR_LEN 128 - -struct mwifiex_ver_ext { - u32 version_str_sel; - char version_str[MWIFIEX_MAX_VER_STR_LEN]; -}; - -struct mwifiex_bss_info { - u32 bss_mode; - struct cfg80211_ssid ssid; - u32 bss_chan; - u8 country_code[3]; - u32 media_connected; - u32 max_power_level; - u32 min_power_level; - u32 adhoc_state; - signed int bcn_nf_last; - u32 wep_status; - u32 is_hs_configured; - u32 is_deep_sleep; - u8 bssid[ETH_ALEN]; -}; - -#define MAX_NUM_TID 8 - -#define MAX_RX_WINSIZE 64 - -struct mwifiex_ds_rx_reorder_tbl { - u16 tid; - u8 ta[ETH_ALEN]; - u32 start_win; - u32 win_size; - u32 buffer[MAX_RX_WINSIZE]; -}; - -struct mwifiex_ds_tx_ba_stream_tbl { - u16 tid; - u8 ra[ETH_ALEN]; - u8 amsdu; -}; - -#define DBG_CMD_NUM 5 - -struct tdls_peer_info { - u8 peer_addr[ETH_ALEN]; -}; - -struct mwifiex_debug_info { - unsigned int debug_mask; - u32 int_counter; - u32 packets_out[MAX_NUM_TID]; - u32 tx_buf_size; - u32 curr_tx_buf_size; - u32 tx_tbl_num; - struct mwifiex_ds_tx_ba_stream_tbl - tx_tbl[MWIFIEX_MAX_TX_BASTREAM_SUPPORTED]; - u32 rx_tbl_num; - struct mwifiex_ds_rx_reorder_tbl rx_tbl - [MWIFIEX_MAX_RX_BASTREAM_SUPPORTED]; - u32 tdls_peer_num; - struct tdls_peer_info tdls_list - [MWIFIEX_MAX_TDLS_PEER_SUPPORTED]; - u16 ps_mode; - u32 ps_state; - u8 is_deep_sleep; - u8 pm_wakeup_card_req; - u32 pm_wakeup_fw_try; - u8 is_hs_configured; - u8 hs_activated; - u32 num_cmd_host_to_card_failure; - u32 num_cmd_sleep_cfm_host_to_card_failure; - u32 num_tx_host_to_card_failure; - u32 num_event_deauth; - u32 num_event_disassoc; - u32 num_event_link_lost; - u32 num_cmd_deauth; - u32 num_cmd_assoc_success; - u32 num_cmd_assoc_failure; - u32 num_tx_timeout; - u8 is_cmd_timedout; - u16 timeout_cmd_id; - u16 timeout_cmd_act; - u16 last_cmd_id[DBG_CMD_NUM]; - u16 last_cmd_act[DBG_CMD_NUM]; - u16 last_cmd_index; - u16 last_cmd_resp_id[DBG_CMD_NUM]; - u16 last_cmd_resp_index; - u16 last_event[DBG_CMD_NUM]; - u16 last_event_index; - u8 data_sent; - u8 cmd_sent; - u8 cmd_resp_received; - u8 event_received; -}; - -#define MWIFIEX_KEY_INDEX_UNICAST 0x40000000 -#define PN_LEN 16 - -struct mwifiex_ds_encrypt_key { - u32 key_disable; - u32 key_index; - u32 key_len; - u8 key_material[WLAN_MAX_KEY_LEN]; - u8 mac_addr[ETH_ALEN]; - u32 is_wapi_key; - u8 pn[PN_LEN]; /* packet number */ - u8 pn_len; - u8 is_igtk_key; - u8 is_current_wep_key; - u8 is_rx_seq_valid; -}; - -struct mwifiex_power_cfg { - u32 is_power_auto; - u32 power_level; -}; - -struct mwifiex_ds_hs_cfg { - u32 is_invoke_hostcmd; - /* Bit0: non-unicast data - * Bit1: unicast data - * Bit2: mac events - * Bit3: magic packet - */ - u32 conditions; - u32 gpio; - u32 gap; -}; - -#define DEEP_SLEEP_ON 1 -#define DEEP_SLEEP_OFF 0 -#define DEEP_SLEEP_IDLE_TIME 100 -#define PS_MODE_AUTO 1 - -struct mwifiex_ds_auto_ds { - u16 auto_ds; - u16 idle_time; -}; - -struct mwifiex_ds_pm_cfg { - union { - u32 ps_mode; - struct mwifiex_ds_hs_cfg hs_cfg; - struct mwifiex_ds_auto_ds auto_deep_sleep; - u32 sleep_period; - } param; -}; - -struct mwifiex_11ac_vht_cfg { - u8 band_config; - u8 misc_config; - u32 cap_info; - u32 mcs_tx_set; - u32 mcs_rx_set; -}; - -struct mwifiex_ds_11n_tx_cfg { - u16 tx_htcap; - u16 tx_htinfo; - u16 misc_config; /* Needed for 802.11AC cards only */ -}; - -struct mwifiex_ds_11n_amsdu_aggr_ctrl { - u16 enable; - u16 curr_buf_size; -}; - -struct mwifiex_ds_ant_cfg { - u32 tx_ant; - u32 rx_ant; -}; - -#define MWIFIEX_NUM_OF_CMD_BUFFER 50 -#define MWIFIEX_SIZE_OF_CMD_BUFFER 2048 - -enum { - MWIFIEX_IE_TYPE_GEN_IE = 0, - MWIFIEX_IE_TYPE_ARP_FILTER, -}; - -enum { - MWIFIEX_REG_MAC = 1, - MWIFIEX_REG_BBP, - MWIFIEX_REG_RF, - MWIFIEX_REG_PMIC, - MWIFIEX_REG_CAU, -}; - -struct mwifiex_ds_reg_rw { - __le32 type; - __le32 offset; - __le32 value; -}; - -#define MAX_EEPROM_DATA 256 - -struct mwifiex_ds_read_eeprom { - __le16 offset; - __le16 byte_count; - u8 value[MAX_EEPROM_DATA]; -}; - -struct mwifiex_ds_mem_rw { - u32 addr; - u32 value; -}; - -#define IEEE_MAX_IE_SIZE 256 - -#define MWIFIEX_IE_HDR_SIZE (sizeof(struct mwifiex_ie) - IEEE_MAX_IE_SIZE) - -struct mwifiex_ds_misc_gen_ie { - u32 type; - u32 len; - u8 ie_data[IEEE_MAX_IE_SIZE]; -}; - -struct mwifiex_ds_misc_cmd { - u32 len; - u8 cmd[MWIFIEX_SIZE_OF_CMD_BUFFER]; -}; - -#define BITMASK_BCN_RSSI_LOW BIT(0) -#define BITMASK_BCN_RSSI_HIGH BIT(4) - -enum subsc_evt_rssi_state { - EVENT_HANDLED, - RSSI_LOW_RECVD, - RSSI_HIGH_RECVD -}; - -struct subsc_evt_cfg { - u8 abs_value; - u8 evt_freq; -}; - -struct mwifiex_ds_misc_subsc_evt { - u16 action; - u16 events; - struct subsc_evt_cfg bcn_l_rssi_cfg; - struct subsc_evt_cfg bcn_h_rssi_cfg; -}; - -#define MWIFIEX_MEF_MAX_BYTESEQ 6 /* non-adjustable */ -#define MWIFIEX_MEF_MAX_FILTERS 10 - -struct mwifiex_mef_filter { - u16 repeat; - u16 offset; - s8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1]; - u8 filt_type; - u8 filt_action; -}; - -struct mwifiex_mef_entry { - u8 mode; - u8 action; - struct mwifiex_mef_filter filter[MWIFIEX_MEF_MAX_FILTERS]; -}; - -struct mwifiex_ds_mef_cfg { - u32 criteria; - u16 num_entries; - struct mwifiex_mef_entry *mef_entry; -}; - -#define MWIFIEX_MAX_VSIE_LEN (256) -#define MWIFIEX_MAX_VSIE_NUM (8) -#define MWIFIEX_VSIE_MASK_CLEAR 0x00 -#define MWIFIEX_VSIE_MASK_SCAN 0x01 -#define MWIFIEX_VSIE_MASK_ASSOC 0x02 -#define MWIFIEX_VSIE_MASK_ADHOC 0x04 - -enum { - MWIFIEX_FUNC_INIT = 1, - MWIFIEX_FUNC_SHUTDOWN, -}; - -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 MWIFIEX_COALESCE_MAX_RULES 8 -#define MWIFIEX_COALESCE_MAX_BYTESEQ 4 /* non-adjustable */ -#define MWIFIEX_COALESCE_MAX_FILTERS 4 -#define MWIFIEX_MAX_COALESCING_DELAY 100 /* in msecs */ - -struct filt_field_param { - u8 operation; - u8 operand_len; - u16 offset; - u8 operand_byte_stream[MWIFIEX_COALESCE_MAX_BYTESEQ]; -}; - -struct mwifiex_coalesce_rule { - u16 max_coalescing_delay; - u8 num_of_fields; - u8 pkt_type; - struct filt_field_param params[MWIFIEX_COALESCE_MAX_FILTERS]; -}; - -struct mwifiex_ds_coalesce_cfg { - u16 num_of_rules; - struct mwifiex_coalesce_rule rule[MWIFIEX_COALESCE_MAX_RULES]; -}; - -struct mwifiex_ds_tdls_oper { - u16 tdls_action; - u8 peer_mac[ETH_ALEN]; - u16 capability; - u8 qos_info; - u8 *ext_capab; - u8 ext_capab_len; - u8 *supp_rates; - u8 supp_rates_len; - u8 *ht_capab; -}; - -#endif /* !_MWIFIEX_IOCTL_H_ */ diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c deleted file mode 100644 index 3cda1f956f0b..000000000000 --- a/drivers/net/wireless/mwifiex/join.c +++ /dev/null @@ -1,1525 +0,0 @@ -/* - * Marvell Wireless LAN device driver: association and ad-hoc start/join - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11ac.h" - -#define CAPINFO_MASK (~(BIT(15) | BIT(14) | BIT(12) | BIT(11) | BIT(9))) - -/* - * Append a generic IE as a pass through TLV to a TLV buffer. - * - * This function is called from the network join command preparation 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. - */ -static int -mwifiex_cmd_append_generic_ie(struct mwifiex_private *priv, u8 **buffer) -{ - int ret_len = 0; - struct mwifiex_ie_types_header ie_header; - - /* Null Checks */ - if (!buffer) - return 0; - if (!(*buffer)) - return 0; - - /* - * If there is a generic ie buffer setup, append it to the return - * parameter buffer pointer. - */ - if (priv->gen_ie_buf_len) { - mwifiex_dbg(priv->adapter, INFO, - "info: %s: append generic ie len %d to %p\n", - __func__, priv->gen_ie_buf_len, *buffer); - - /* Wrap the generic IE buffer with a pass through TLV type */ - ie_header.type = cpu_to_le16(TLV_TYPE_PASSTHROUGH); - ie_header.len = cpu_to_le16(priv->gen_ie_buf_len); - memcpy(*buffer, &ie_header, sizeof(ie_header)); - - /* Increment the return size and the return buffer pointer - param */ - *buffer += sizeof(ie_header); - ret_len += sizeof(ie_header); - - /* Copy the generic IE buffer to the output buffer, advance - pointer */ - memcpy(*buffer, priv->gen_ie_buf, priv->gen_ie_buf_len); - - /* Increment the return size and the return buffer pointer - param */ - *buffer += 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 */ - return ret_len; -} - -/* - * Append TSF tracking info from the scan table for the target AP. - * - * This function is called from the network join command preparation 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. - */ -static int -mwifiex_cmd_append_tsf_tlv(struct mwifiex_private *priv, u8 **buffer, - struct mwifiex_bssdescriptor *bss_desc) -{ - struct mwifiex_ie_types_tsf_timestamp tsf_tlv; - __le64 tsf_val; - - /* Null Checks */ - if (buffer == NULL) - return 0; - if (*buffer == NULL) - return 0; - - memset(&tsf_tlv, 0x00, sizeof(struct mwifiex_ie_types_tsf_timestamp)); - - tsf_tlv.header.type = cpu_to_le16(TLV_TYPE_TSFTIMESTAMP); - tsf_tlv.header.len = cpu_to_le16(2 * sizeof(tsf_val)); - - memcpy(*buffer, &tsf_tlv, sizeof(tsf_tlv.header)); - *buffer += sizeof(tsf_tlv.header); - - /* TSF at the time when beacon/probe_response was received */ - tsf_val = cpu_to_le64(bss_desc->fw_tsf); - memcpy(*buffer, &tsf_val, sizeof(tsf_val)); - *buffer += sizeof(tsf_val); - - tsf_val = cpu_to_le64(bss_desc->timestamp); - - mwifiex_dbg(priv->adapter, INFO, - "info: %s: TSF offset calc: %016llx - %016llx\n", - __func__, bss_desc->timestamp, bss_desc->fw_tsf); - - memcpy(*buffer, &tsf_val, sizeof(tsf_val)); - *buffer += sizeof(tsf_val); - - return sizeof(tsf_tlv.header) + (2 * sizeof(tsf_val)); -} - -/* - * 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. - */ -static int mwifiex_get_common_rates(struct mwifiex_private *priv, u8 *rate1, - u32 rate1_size, u8 *rate2, u32 rate2_size) -{ - int ret; - u8 *ptr = rate1, *tmp; - u32 i, j; - - tmp = kmemdup(rate1, rate1_size, GFP_KERNEL); - if (!tmp) { - mwifiex_dbg(priv->adapter, ERROR, "failed to alloc tmp buf\n"); - return -ENOMEM; - } - - memset(rate1, 0, rate1_size); - - for (i = 0; i < rate2_size && rate2[i]; i++) { - for (j = 0; j < rate1_size && tmp[j]; j++) { - /* Check common rate, excluding the bit for - basic rate */ - if ((rate2[i] & 0x7F) == (tmp[j] & 0x7F)) { - *rate1++ = tmp[j]; - break; - } - } - } - - mwifiex_dbg(priv->adapter, INFO, "info: Tx data rate set to %#x\n", - priv->data_rate); - - if (!priv->is_data_rate_auto) { - while (*ptr) { - if ((*ptr & 0x7f) == priv->data_rate) { - ret = 0; - goto done; - } - ptr++; - } - mwifiex_dbg(priv->adapter, ERROR, - "previously set fixed data rate %#x\t" - "is not compatible with the network\n", - priv->data_rate); - - ret = -1; - goto done; - } - - ret = 0; -done: - kfree(tmp); - return ret; -} - -/* - * This function creates the intersection of the rates supported by a - * target BSS and our adapter settings for use in an assoc/join command. - */ -static int -mwifiex_setup_rates_from_bssdesc(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, - u8 *out_rates, u32 *out_rates_size) -{ - u8 card_rates[MWIFIEX_SUPPORTED_RATES]; - u32 card_rates_size; - - /* Copy AP supported rates */ - memcpy(out_rates, bss_desc->supported_rates, MWIFIEX_SUPPORTED_RATES); - /* Get the STA supported rates */ - card_rates_size = mwifiex_get_active_data_rates(priv, card_rates); - /* Get the common rates between AP and STA supported rates */ - if (mwifiex_get_common_rates(priv, out_rates, MWIFIEX_SUPPORTED_RATES, - card_rates, card_rates_size)) { - *out_rates_size = 0; - mwifiex_dbg(priv->adapter, ERROR, - "%s: cannot get common rates\n", - __func__); - return -1; - } - - *out_rates_size = - min_t(size_t, strlen(out_rates), MWIFIEX_SUPPORTED_RATES); - - return 0; -} - -/* - * This function appends a WPS IE. It is called from the network join command - * preparation routine. - * - * If the IE buffer has been setup by the application, this routine appends - * the buffer as a WPS TLV type to the request. - */ -static int -mwifiex_cmd_append_wps_ie(struct mwifiex_private *priv, u8 **buffer) -{ - int retLen = 0; - struct mwifiex_ie_types_header ie_header; - - if (!buffer || !*buffer) - return 0; - - /* - * If there is a wps ie buffer setup, append it to the return - * parameter buffer pointer. - */ - if (priv->wps_ie_len) { - mwifiex_dbg(priv->adapter, CMD, - "cmd: append wps ie %d to %p\n", - priv->wps_ie_len, *buffer); - - /* Wrap the generic IE buffer with a pass through TLV type */ - ie_header.type = cpu_to_le16(TLV_TYPE_MGMT_IE); - ie_header.len = cpu_to_le16(priv->wps_ie_len); - memcpy(*buffer, &ie_header, sizeof(ie_header)); - *buffer += sizeof(ie_header); - retLen += sizeof(ie_header); - - memcpy(*buffer, priv->wps_ie, priv->wps_ie_len); - *buffer += priv->wps_ie_len; - retLen += priv->wps_ie_len; - - } - - kfree(priv->wps_ie); - priv->wps_ie_len = 0; - return retLen; -} - -/* - * This function appends a WAPI IE. - * - * This function is called from the network join command preparation routine. - * - * If the IE buffer has been setup by the application, this routine appends - * the buffer as a WAPI TLV type to the request. - */ -static int -mwifiex_cmd_append_wapi_ie(struct mwifiex_private *priv, u8 **buffer) -{ - int retLen = 0; - struct mwifiex_ie_types_header ie_header; - - /* Null Checks */ - if (buffer == NULL) - return 0; - if (*buffer == NULL) - return 0; - - /* - * If there is a wapi ie buffer setup, append it to the return - * parameter buffer pointer. - */ - if (priv->wapi_ie_len) { - mwifiex_dbg(priv->adapter, CMD, - "cmd: append wapi ie %d to %p\n", - priv->wapi_ie_len, *buffer); - - /* Wrap the generic IE buffer with a pass through TLV type */ - ie_header.type = cpu_to_le16(TLV_TYPE_WAPI_IE); - ie_header.len = cpu_to_le16(priv->wapi_ie_len); - memcpy(*buffer, &ie_header, sizeof(ie_header)); - - /* Increment the return size and the return buffer pointer - param */ - *buffer += sizeof(ie_header); - retLen += sizeof(ie_header); - - /* Copy the wapi IE buffer to the output buffer, advance - pointer */ - memcpy(*buffer, priv->wapi_ie, priv->wapi_ie_len); - - /* Increment the return size and the return buffer pointer - param */ - *buffer += priv->wapi_ie_len; - retLen += priv->wapi_ie_len; - - } - /* return the length appended to the buffer */ - return retLen; -} - -/* - * This function appends rsn ie tlv for wpa/wpa2 security modes. - * It is called from the network join command preparation routine. - */ -static int mwifiex_append_rsn_ie_wpa_wpa2(struct mwifiex_private *priv, - u8 **buffer) -{ - struct mwifiex_ie_types_rsn_param_set *rsn_ie_tlv; - int rsn_ie_len; - - if (!buffer || !(*buffer)) - return 0; - - rsn_ie_tlv = (struct mwifiex_ie_types_rsn_param_set *) (*buffer); - rsn_ie_tlv->header.type = cpu_to_le16((u16) priv->wpa_ie[0]); - rsn_ie_tlv->header.type = cpu_to_le16( - le16_to_cpu(rsn_ie_tlv->header.type) & 0x00FF); - rsn_ie_tlv->header.len = cpu_to_le16((u16) priv->wpa_ie[1]); - rsn_ie_tlv->header.len = cpu_to_le16(le16_to_cpu(rsn_ie_tlv->header.len) - & 0x00FF); - if (le16_to_cpu(rsn_ie_tlv->header.len) <= (sizeof(priv->wpa_ie) - 2)) - memcpy(rsn_ie_tlv->rsn_ie, &priv->wpa_ie[2], - le16_to_cpu(rsn_ie_tlv->header.len)); - else - return -1; - - rsn_ie_len = sizeof(rsn_ie_tlv->header) + - le16_to_cpu(rsn_ie_tlv->header.len); - *buffer += rsn_ie_len; - - return rsn_ie_len; -} - -/* - * This function prepares command for association. - * - * This sets the following parameters - - * - Peer MAC address - * - Listen interval - * - Beacon interval - * - Capability information - * - * ...and the following TLVs, as required - - * - SSID TLV - * - PHY TLV - * - SS TLV - * - Rates TLV - * - Authentication TLV - * - Channel TLV - * - WPA/WPA2 IE - * - 11n TLV - * - Vendor specific TLV - * - WMM TLV - * - WAPI IE - * - Generic IE - * - TSF TLV - * - * Preparation also includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_bssdescriptor *bss_desc) -{ - struct host_cmd_ds_802_11_associate *assoc = &cmd->params.associate; - struct mwifiex_ie_types_ssid_param_set *ssid_tlv; - struct mwifiex_ie_types_phy_param_set *phy_tlv; - struct mwifiex_ie_types_ss_param_set *ss_tlv; - struct mwifiex_ie_types_rates_param_set *rates_tlv; - struct mwifiex_ie_types_auth_type *auth_tlv; - struct mwifiex_ie_types_chan_list_param_set *chan_tlv; - u8 rates[MWIFIEX_SUPPORTED_RATES]; - u32 rates_size; - u16 tmp_cap; - u8 *pos; - int rsn_ie_len = 0; - - pos = (u8 *) assoc; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_ASSOCIATE); - - /* Save so we know which BSS Desc to use in the response handler */ - priv->attempted_bss_desc = bss_desc; - - memcpy(assoc->peer_sta_addr, - bss_desc->mac_address, sizeof(assoc->peer_sta_addr)); - pos += sizeof(assoc->peer_sta_addr); - - /* Set the listen interval */ - assoc->listen_interval = cpu_to_le16(priv->listen_interval); - /* Set the beacon period */ - assoc->beacon_period = cpu_to_le16(bss_desc->beacon_period); - - pos += sizeof(assoc->cap_info_bitmap); - pos += sizeof(assoc->listen_interval); - pos += sizeof(assoc->beacon_period); - pos += sizeof(assoc->dtim_period); - - ssid_tlv = (struct mwifiex_ie_types_ssid_param_set *) pos; - ssid_tlv->header.type = cpu_to_le16(WLAN_EID_SSID); - ssid_tlv->header.len = cpu_to_le16((u16) bss_desc->ssid.ssid_len); - memcpy(ssid_tlv->ssid, bss_desc->ssid.ssid, - le16_to_cpu(ssid_tlv->header.len)); - pos += sizeof(ssid_tlv->header) + le16_to_cpu(ssid_tlv->header.len); - - phy_tlv = (struct mwifiex_ie_types_phy_param_set *) pos; - phy_tlv->header.type = cpu_to_le16(WLAN_EID_DS_PARAMS); - phy_tlv->header.len = cpu_to_le16(sizeof(phy_tlv->fh_ds.ds_param_set)); - memcpy(&phy_tlv->fh_ds.ds_param_set, - &bss_desc->phy_param_set.ds_param_set.current_chan, - sizeof(phy_tlv->fh_ds.ds_param_set)); - pos += sizeof(phy_tlv->header) + le16_to_cpu(phy_tlv->header.len); - - ss_tlv = (struct mwifiex_ie_types_ss_param_set *) pos; - ss_tlv->header.type = cpu_to_le16(WLAN_EID_CF_PARAMS); - ss_tlv->header.len = cpu_to_le16(sizeof(ss_tlv->cf_ibss.cf_param_set)); - pos += sizeof(ss_tlv->header) + le16_to_cpu(ss_tlv->header.len); - - /* Get the common rates supported between the driver and the BSS Desc */ - if (mwifiex_setup_rates_from_bssdesc - (priv, bss_desc, rates, &rates_size)) - return -1; - - /* Save the data rates into Current BSS state structure */ - priv->curr_bss_params.num_of_rates = rates_size; - memcpy(&priv->curr_bss_params.data_rates, rates, rates_size); - - /* Setup the Rates TLV in the association command */ - rates_tlv = (struct mwifiex_ie_types_rates_param_set *) pos; - rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); - rates_tlv->header.len = cpu_to_le16((u16) rates_size); - memcpy(rates_tlv->rates, rates, rates_size); - pos += sizeof(rates_tlv->header) + rates_size; - mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_CMD: rates size = %d\n", - rates_size); - - /* Add the Authentication type to be used for Auth frames */ - auth_tlv = (struct mwifiex_ie_types_auth_type *) pos; - auth_tlv->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); - auth_tlv->header.len = cpu_to_le16(sizeof(auth_tlv->auth_type)); - if (priv->sec_info.wep_enabled) - auth_tlv->auth_type = cpu_to_le16( - (u16) priv->sec_info.authentication_mode); - else - auth_tlv->auth_type = cpu_to_le16(NL80211_AUTHTYPE_OPEN_SYSTEM); - - pos += sizeof(auth_tlv->header) + le16_to_cpu(auth_tlv->header.len); - - if (IS_SUPPORT_MULTI_BANDS(priv->adapter) && - !(ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && - (!bss_desc->disable_11n) && - (priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN) && - (bss_desc->bcn_ht_cap) - ) - ) { - /* Append a channel TLV for the channel the attempted AP was - found on */ - chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; - chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - chan_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); - - memset(chan_tlv->chan_scan_param, 0x00, - sizeof(struct mwifiex_chan_scan_param_set)); - chan_tlv->chan_scan_param[0].chan_number = - (bss_desc->phy_param_set.ds_param_set.current_chan); - mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Chan = %d\n", - chan_tlv->chan_scan_param[0].chan_number); - - chan_tlv->chan_scan_param[0].radio_type = - mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - - mwifiex_dbg(priv->adapter, INFO, "info: Assoc: TLV Band = %d\n", - chan_tlv->chan_scan_param[0].radio_type); - pos += sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - } - - if (!priv->wps.session_enable) { - if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) - rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); - - if (rsn_ie_len == -1) - return -1; - } - - if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && - (!bss_desc->disable_11n) && - (priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN)) - mwifiex_cmd_append_11n_tlv(priv, bss_desc, &pos); - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - !bss_desc->disable_11n && !bss_desc->disable_11ac && - priv->adapter->config_bands & BAND_AAC) - mwifiex_cmd_append_11ac_tlv(priv, bss_desc, &pos); - - /* Append vendor specific IE TLV */ - mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_ASSOC, &pos); - - mwifiex_wmm_process_association_req(priv, &pos, &bss_desc->wmm_ie, - bss_desc->bcn_ht_cap); - if (priv->sec_info.wapi_enabled && priv->wapi_ie_len) - mwifiex_cmd_append_wapi_ie(priv, &pos); - - if (priv->wps.session_enable && priv->wps_ie_len) - mwifiex_cmd_append_wps_ie(priv, &pos); - - mwifiex_cmd_append_generic_ie(priv, &pos); - - mwifiex_cmd_append_tsf_tlv(priv, &pos, bss_desc); - - mwifiex_11h_process_join(priv, &pos, bss_desc); - - cmd->size = cpu_to_le16((u16) (pos - (u8 *) assoc) + S_DS_GEN); - - /* Set the Capability info at last */ - tmp_cap = bss_desc->cap_info_bitmap; - - if (priv->adapter->config_bands == BAND_B) - tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; - - tmp_cap &= CAPINFO_MASK; - mwifiex_dbg(priv->adapter, INFO, - "info: ASSOC_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", - tmp_cap, CAPINFO_MASK); - assoc->cap_info_bitmap = cpu_to_le16(tmp_cap); - - return 0; -} - -static const char *assoc_failure_reason_to_str(u16 cap_info) -{ - switch (cap_info) { - case CONNECT_ERR_AUTH_ERR_STA_FAILURE: - return "CONNECT_ERR_AUTH_ERR_STA_FAILURE"; - case CONNECT_ERR_AUTH_MSG_UNHANDLED: - return "CONNECT_ERR_AUTH_MSG_UNHANDLED"; - case CONNECT_ERR_ASSOC_ERR_TIMEOUT: - return "CONNECT_ERR_ASSOC_ERR_TIMEOUT"; - case CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED: - return "CONNECT_ERR_ASSOC_ERR_AUTH_REFUSED"; - case CONNECT_ERR_STA_FAILURE: - return "CONNECT_ERR_STA_FAILURE"; - } - - return "Unknown connect failure"; -} -/* - * 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 | - * | 0xFFFE(-2): Authentication unhandled message | - * | 0xFFFD(-3): Authentication refused | - * | 0xFFFC(-4): Timeout waiting for AP response | - * .------------------------------------------------------------. - * | status_code(t_u16): | - * | If cap_info is -1: | - * | An internal firmware failure prevented the | - * | command from being processed. The status_code | - * | will be set to 1. | - * | | - * | 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 | - * .------------------------------------------------------------. - * | 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). - */ -int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret = 0; - struct ieee_types_assoc_rsp *assoc_rsp; - struct mwifiex_bssdescriptor *bss_desc; - bool enable_data = true; - u16 cap_info, status_code, aid; - - assoc_rsp = (struct ieee_types_assoc_rsp *) &resp->params; - - cap_info = le16_to_cpu(assoc_rsp->cap_info_bitmap); - status_code = le16_to_cpu(assoc_rsp->status_code); - aid = le16_to_cpu(assoc_rsp->a_id); - - if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) - dev_err(priv->adapter->dev, - "invalid AID value 0x%x; bits 15:14 not set\n", - aid); - - aid &= ~(BIT(15) | BIT(14)); - - priv->assoc_rsp_size = min(le16_to_cpu(resp->size) - S_DS_GEN, - sizeof(priv->assoc_rsp_buf)); - - memcpy(priv->assoc_rsp_buf, &resp->params, priv->assoc_rsp_size); - - assoc_rsp->a_id = cpu_to_le16(aid); - - if (status_code) { - priv->adapter->dbg.num_cmd_assoc_failure++; - mwifiex_dbg(priv->adapter, ERROR, - "ASSOC_RESP: failed,\t" - "status code=%d err=%#x a_id=%#x\n", - status_code, cap_info, - le16_to_cpu(assoc_rsp->a_id)); - - mwifiex_dbg(priv->adapter, ERROR, "assoc failure: reason %s\n", - assoc_failure_reason_to_str(cap_info)); - if (cap_info == CONNECT_ERR_ASSOC_ERR_TIMEOUT) { - if (status_code == MWIFIEX_ASSOC_CMD_FAILURE_AUTH) { - ret = WLAN_STATUS_AUTH_TIMEOUT; - mwifiex_dbg(priv->adapter, ERROR, - "ASSOC_RESP: AUTH timeout\n"); - } else { - ret = WLAN_STATUS_UNSPECIFIED_FAILURE; - mwifiex_dbg(priv->adapter, ERROR, - "ASSOC_RESP: UNSPECIFIED failure\n"); - } - } else { - ret = status_code; - } - - goto done; - } - - /* Send a Media Connected event, according to the Spec */ - priv->media_connected = true; - - priv->adapter->ps_state = PS_STATE_AWAKE; - priv->adapter->pps_uapsd_mode = false; - priv->adapter->tx_lock_flag = false; - - /* Set the attempted BSSID Index to current */ - bss_desc = priv->attempted_bss_desc; - - mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: %s\n", - bss_desc->ssid.ssid); - - /* Make a copy of current BSSID descriptor */ - memcpy(&priv->curr_bss_params.bss_descriptor, - bss_desc, sizeof(struct mwifiex_bssdescriptor)); - - /* Update curr_bss_params */ - priv->curr_bss_params.bss_descriptor.channel - = bss_desc->phy_param_set.ds_param_set.current_chan; - - priv->curr_bss_params.band = (u8) bss_desc->bss_band; - - if (bss_desc->wmm_ie.vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) - priv->curr_bss_params.wmm_enabled = true; - else - priv->curr_bss_params.wmm_enabled = false; - - if ((priv->wmm_required || bss_desc->bcn_ht_cap) && - priv->curr_bss_params.wmm_enabled) - priv->wmm_enabled = true; - else - priv->wmm_enabled = false; - - priv->curr_bss_params.wmm_uapsd_enabled = false; - - if (priv->wmm_enabled) - priv->curr_bss_params.wmm_uapsd_enabled - = ((bss_desc->wmm_ie.qos_info_bitmap & - IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) ? 1 : 0); - - mwifiex_dbg(priv->adapter, INFO, - "info: ASSOC_RESP: curr_pkt_filter is %#x\n", - priv->curr_pkt_filter); - if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) - priv->wpa_is_gtk_set = false; - - if (priv->wmm_enabled) { - /* Don't re-enable carrier until we get the WMM_GET_STATUS - event */ - enable_data = false; - } else { - /* Since WMM is not enabled, setup the queues with the - defaults */ - mwifiex_wmm_setup_queue_priorities(priv, NULL); - mwifiex_wmm_setup_ac_downgrade(priv); - } - - if (enable_data) - mwifiex_dbg(priv->adapter, INFO, - "info: post association, re-enabling data flow\n"); - - /* Reset SNR/NF/RSSI values */ - priv->data_rssi_last = 0; - priv->data_nf_last = 0; - priv->data_rssi_avg = 0; - priv->data_nf_avg = 0; - priv->bcn_rssi_last = 0; - priv->bcn_nf_last = 0; - priv->bcn_rssi_avg = 0; - priv->bcn_nf_avg = 0; - priv->rxpd_rate = 0; - priv->rxpd_htinfo = 0; - - mwifiex_save_curr_bcn(priv); - - priv->adapter->dbg.num_cmd_assoc_success++; - - mwifiex_dbg(priv->adapter, INFO, "info: ASSOC_RESP: associated\n"); - - /* Add the ra_list here for infra mode as there will be only 1 ra - always */ - mwifiex_ralist_add(priv, - priv->curr_bss_params.bss_descriptor.mac_address); - - if (!netif_carrier_ok(priv->netdev)) - netif_carrier_on(priv->netdev); - mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); - - if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) - priv->scan_block = true; - else - priv->port_open = true; - -done: - /* Need to indicate IOCTL complete */ - if (adapter->curr_cmd->wait_q_enabled) { - if (ret) - adapter->cmd_wait_q.status = -1; - else - adapter->cmd_wait_q.status = 0; - } - - return ret; -} - -/* - * This function prepares command for ad-hoc start. - * - * Driver will fill up SSID, BSS mode, IBSS parameters, physical - * parameters, probe delay, and capability information. Firmware - * will fill up beacon period, basic rates and operational rates. - * - * In addition, the following TLVs are added - - * - Channel TLV - * - Vendor specific IE - * - WPA/WPA2 IE - * - HT Capabilities IE - * - HT Information IE - * - * Preparation also includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -int -mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct cfg80211_ssid *req_ssid) -{ - int rsn_ie_len = 0; - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ad_hoc_start *adhoc_start = - &cmd->params.adhoc_start; - struct mwifiex_bssdescriptor *bss_desc; - u32 cmd_append_size = 0; - u32 i; - u16 tmp_cap; - struct mwifiex_ie_types_chan_list_param_set *chan_tlv; - u8 radio_type; - - struct mwifiex_ie_types_htcap *ht_cap; - struct mwifiex_ie_types_htinfo *ht_info; - u8 *pos = (u8 *) adhoc_start + - sizeof(struct host_cmd_ds_802_11_ad_hoc_start); - - if (!adapter) - return -1; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_START); - - bss_desc = &priv->curr_bss_params.bss_descriptor; - priv->attempted_bss_desc = bss_desc; - - /* - * Fill in the parameters for 2 data structures: - * 1. struct host_cmd_ds_802_11_ad_hoc_start command - * 2. bss_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(adhoc_start->ssid, 0, IEEE80211_MAX_SSID_LEN); - - memcpy(adhoc_start->ssid, req_ssid->ssid, req_ssid->ssid_len); - - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: SSID = %s\n", - adhoc_start->ssid); - - memset(bss_desc->ssid.ssid, 0, IEEE80211_MAX_SSID_LEN); - memcpy(bss_desc->ssid.ssid, req_ssid->ssid, req_ssid->ssid_len); - - bss_desc->ssid.ssid_len = req_ssid->ssid_len; - - /* Set the BSS mode */ - adhoc_start->bss_mode = HostCmd_BSS_MODE_IBSS; - bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; - adhoc_start->beacon_period = cpu_to_le16(priv->beacon_period); - bss_desc->beacon_period = priv->beacon_period; - - /* Set Physical param set */ -/* Parameter IE Id */ -#define DS_PARA_IE_ID 3 -/* Parameter IE length */ -#define DS_PARA_IE_LEN 1 - - adhoc_start->phy_param_set.ds_param_set.element_id = DS_PARA_IE_ID; - adhoc_start->phy_param_set.ds_param_set.len = DS_PARA_IE_LEN; - - if (!mwifiex_get_cfp(priv, adapter->adhoc_start_band, - (u16) priv->adhoc_channel, 0)) { - struct mwifiex_chan_freq_power *cfp; - cfp = mwifiex_get_cfp(priv, adapter->adhoc_start_band, - FIRST_VALID_CHANNEL, 0); - if (cfp) - priv->adhoc_channel = (u8) cfp->channel; - } - - if (!priv->adhoc_channel) { - mwifiex_dbg(adapter, ERROR, - "ADHOC_S_CMD: adhoc_channel cannot be 0\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "info: ADHOC_S_CMD: creating ADHOC on channel %d\n", - priv->adhoc_channel); - - priv->curr_bss_params.bss_descriptor.channel = priv->adhoc_channel; - priv->curr_bss_params.band = adapter->adhoc_start_band; - - bss_desc->channel = priv->adhoc_channel; - adhoc_start->phy_param_set.ds_param_set.current_chan = - priv->adhoc_channel; - - memcpy(&bss_desc->phy_param_set, &adhoc_start->phy_param_set, - sizeof(union ieee_types_phy_param_set)); - - /* Set IBSS param set */ -/* IBSS parameter IE Id */ -#define IBSS_PARA_IE_ID 6 -/* IBSS parameter IE length */ -#define IBSS_PARA_IE_LEN 2 - - adhoc_start->ss_param_set.ibss_param_set.element_id = IBSS_PARA_IE_ID; - adhoc_start->ss_param_set.ibss_param_set.len = IBSS_PARA_IE_LEN; - adhoc_start->ss_param_set.ibss_param_set.atim_window - = cpu_to_le16(priv->atim_window); - memcpy(&bss_desc->ss_param_set, &adhoc_start->ss_param_set, - sizeof(union ieee_types_ss_param_set)); - - /* Set Capability info */ - bss_desc->cap_info_bitmap |= WLAN_CAPABILITY_IBSS; - tmp_cap = WLAN_CAPABILITY_IBSS; - - /* Set up privacy in bss_desc */ - if (priv->sec_info.encryption_mode) { - /* Ad-Hoc capability privacy on */ - mwifiex_dbg(adapter, INFO, - "info: ADHOC_S_CMD: wep_status set privacy to WEP\n"); - bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; - tmp_cap |= WLAN_CAPABILITY_PRIVACY; - } else { - mwifiex_dbg(adapter, INFO, - "info: ADHOC_S_CMD: wep_status NOT set,\t" - "setting privacy to ACCEPT ALL\n"); - bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; - } - - memset(adhoc_start->data_rate, 0, sizeof(adhoc_start->data_rate)); - mwifiex_get_active_data_rates(priv, adhoc_start->data_rate); - if ((adapter->adhoc_start_band & BAND_G) && - (priv->curr_pkt_filter & HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON)) { - if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter, false)) { - mwifiex_dbg(adapter, ERROR, - "ADHOC_S_CMD: G Protection config failed\n"); - return -1; - } - } - /* Find the last non zero */ - for (i = 0; i < sizeof(adhoc_start->data_rate); i++) - if (!adhoc_start->data_rate[i]) - break; - - priv->curr_bss_params.num_of_rates = i; - - /* Copy the ad-hoc creating rates into Current BSS rate structure */ - memcpy(&priv->curr_bss_params.data_rates, - &adhoc_start->data_rate, priv->curr_bss_params.num_of_rates); - - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: rates=%4ph\n", - adhoc_start->data_rate); - - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: AD-HOC Start command is ready\n"); - - if (IS_SUPPORT_MULTI_BANDS(adapter)) { - /* Append a channel TLV */ - chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; - chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - chan_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); - - memset(chan_tlv->chan_scan_param, 0x00, - sizeof(struct mwifiex_chan_scan_param_set)); - chan_tlv->chan_scan_param[0].chan_number = - (u8) priv->curr_bss_params.bss_descriptor.channel; - - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Chan = %d\n", - chan_tlv->chan_scan_param[0].chan_number); - - chan_tlv->chan_scan_param[0].radio_type - = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - if (adapter->adhoc_start_band & BAND_GN || - adapter->adhoc_start_band & BAND_AN) { - if (adapter->sec_chan_offset == - IEEE80211_HT_PARAM_CHA_SEC_ABOVE) - chan_tlv->chan_scan_param[0].radio_type |= - (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4); - else if (adapter->sec_chan_offset == - IEEE80211_HT_PARAM_CHA_SEC_BELOW) - chan_tlv->chan_scan_param[0].radio_type |= - (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4); - } - mwifiex_dbg(adapter, INFO, "info: ADHOC_S_CMD: TLV Band = %d\n", - chan_tlv->chan_scan_param[0].radio_type); - pos += sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - cmd_append_size += - sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - } - - /* Append vendor specific IE TLV */ - cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, - MWIFIEX_VSIE_MASK_ADHOC, &pos); - - if (priv->sec_info.wpa_enabled) { - rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); - if (rsn_ie_len == -1) - return -1; - cmd_append_size += rsn_ie_len; - } - - if (adapter->adhoc_11n_enabled) { - /* Fill HT CAPABILITY */ - ht_cap = (struct mwifiex_ie_types_htcap *) pos; - memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); - ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); - ht_cap->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - radio_type = mwifiex_band_to_radio_type( - priv->adapter->config_bands); - mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); - - if (adapter->sec_chan_offset == - IEEE80211_HT_PARAM_CHA_SEC_NONE) { - u16 tmp_ht_cap; - - tmp_ht_cap = le16_to_cpu(ht_cap->ht_cap.cap_info); - tmp_ht_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - tmp_ht_cap &= ~IEEE80211_HT_CAP_SGI_40; - ht_cap->ht_cap.cap_info = cpu_to_le16(tmp_ht_cap); - } - - pos += sizeof(struct mwifiex_ie_types_htcap); - cmd_append_size += sizeof(struct mwifiex_ie_types_htcap); - - /* Fill HT INFORMATION */ - ht_info = (struct mwifiex_ie_types_htinfo *) pos; - memset(ht_info, 0, sizeof(struct mwifiex_ie_types_htinfo)); - ht_info->header.type = cpu_to_le16(WLAN_EID_HT_OPERATION); - ht_info->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_operation)); - - ht_info->ht_oper.primary_chan = - (u8) priv->curr_bss_params.bss_descriptor.channel; - if (adapter->sec_chan_offset) { - ht_info->ht_oper.ht_param = adapter->sec_chan_offset; - ht_info->ht_oper.ht_param |= - IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; - } - ht_info->ht_oper.operation_mode = - cpu_to_le16(IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - ht_info->ht_oper.basic_set[0] = 0xff; - pos += sizeof(struct mwifiex_ie_types_htinfo); - cmd_append_size += - sizeof(struct mwifiex_ie_types_htinfo); - } - - cmd->size = - cpu_to_le16((u16)(sizeof(struct host_cmd_ds_802_11_ad_hoc_start) - + S_DS_GEN + cmd_append_size)); - - if (adapter->adhoc_start_band == BAND_B) - tmp_cap &= ~WLAN_CAPABILITY_SHORT_SLOT_TIME; - else - tmp_cap |= WLAN_CAPABILITY_SHORT_SLOT_TIME; - - adhoc_start->cap_info_bitmap = cpu_to_le16(tmp_cap); - - return 0; -} - -/* - * This function prepares command for ad-hoc join. - * - * Most of the parameters are set up by copying from the target BSS descriptor - * from the scan response. - * - * In addition, the following TLVs are added - - * - Channel TLV - * - Vendor specific IE - * - WPA/WPA2 IE - * - 11n IE - * - * Preparation also includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -int -mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_bssdescriptor *bss_desc) -{ - int rsn_ie_len = 0; - struct host_cmd_ds_802_11_ad_hoc_join *adhoc_join = - &cmd->params.adhoc_join; - struct mwifiex_ie_types_chan_list_param_set *chan_tlv; - u32 cmd_append_size = 0; - u16 tmp_cap; - u32 i, rates_size = 0; - u16 curr_pkt_filter; - u8 *pos = - (u8 *) adhoc_join + - sizeof(struct host_cmd_ds_802_11_ad_hoc_join); - -/* Use G protection */ -#define USE_G_PROTECTION 0x02 - if (bss_desc->erp_flags & USE_G_PROTECTION) { - curr_pkt_filter = - priv-> - curr_pkt_filter | HostCmd_ACT_MAC_ADHOC_G_PROTECTION_ON; - - if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &curr_pkt_filter, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "ADHOC_J_CMD: G Protection config failed\n"); - return -1; - } - } - - priv->attempted_bss_desc = bss_desc; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_JOIN); - - adhoc_join->bss_descriptor.bss_mode = HostCmd_BSS_MODE_IBSS; - - adhoc_join->bss_descriptor.beacon_period - = cpu_to_le16(bss_desc->beacon_period); - - memcpy(&adhoc_join->bss_descriptor.bssid, - &bss_desc->mac_address, ETH_ALEN); - - memcpy(&adhoc_join->bss_descriptor.ssid, - &bss_desc->ssid.ssid, bss_desc->ssid.ssid_len); - - memcpy(&adhoc_join->bss_descriptor.phy_param_set, - &bss_desc->phy_param_set, - sizeof(union ieee_types_phy_param_set)); - - memcpy(&adhoc_join->bss_descriptor.ss_param_set, - &bss_desc->ss_param_set, sizeof(union ieee_types_ss_param_set)); - - tmp_cap = bss_desc->cap_info_bitmap; - - tmp_cap &= CAPINFO_MASK; - - mwifiex_dbg(priv->adapter, INFO, - "info: ADHOC_J_CMD: tmp_cap=%4X CAPINFO_MASK=%4lX\n", - tmp_cap, CAPINFO_MASK); - - /* Information on BSSID descriptor passed to FW */ - mwifiex_dbg(priv->adapter, INFO, - "info: ADHOC_J_CMD: BSSID=%pM, SSID='%s'\n", - adhoc_join->bss_descriptor.bssid, - adhoc_join->bss_descriptor.ssid); - - for (i = 0; i < MWIFIEX_SUPPORTED_RATES && - bss_desc->supported_rates[i]; i++) - ; - rates_size = i; - - /* Copy Data Rates from the Rates recorded in scan response */ - memset(adhoc_join->bss_descriptor.data_rates, 0, - sizeof(adhoc_join->bss_descriptor.data_rates)); - memcpy(adhoc_join->bss_descriptor.data_rates, - bss_desc->supported_rates, rates_size); - - /* Copy the adhoc join rates into Current BSS state structure */ - priv->curr_bss_params.num_of_rates = rates_size; - memcpy(&priv->curr_bss_params.data_rates, bss_desc->supported_rates, - rates_size); - - /* Copy the channel information */ - priv->curr_bss_params.bss_descriptor.channel = bss_desc->channel; - priv->curr_bss_params.band = (u8) bss_desc->bss_band; - - if (priv->sec_info.wep_enabled || priv->sec_info.wpa_enabled) - tmp_cap |= WLAN_CAPABILITY_PRIVACY; - - if (IS_SUPPORT_MULTI_BANDS(priv->adapter)) { - /* Append a channel TLV */ - chan_tlv = (struct mwifiex_ie_types_chan_list_param_set *) pos; - chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - chan_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_chan_scan_param_set)); - - memset(chan_tlv->chan_scan_param, 0x00, - sizeof(struct mwifiex_chan_scan_param_set)); - chan_tlv->chan_scan_param[0].chan_number = - (bss_desc->phy_param_set.ds_param_set.current_chan); - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Chan=%d\n", - chan_tlv->chan_scan_param[0].chan_number); - - chan_tlv->chan_scan_param[0].radio_type = - mwifiex_band_to_radio_type((u8) bss_desc->bss_band); - - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_J_CMD: TLV Band=%d\n", - chan_tlv->chan_scan_param[0].radio_type); - pos += sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - cmd_append_size += sizeof(chan_tlv->header) + - sizeof(struct mwifiex_chan_scan_param_set); - } - - if (priv->sec_info.wpa_enabled) - rsn_ie_len = mwifiex_append_rsn_ie_wpa_wpa2(priv, &pos); - if (rsn_ie_len == -1) - return -1; - cmd_append_size += rsn_ie_len; - - if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) - cmd_append_size += mwifiex_cmd_append_11n_tlv(priv, - bss_desc, &pos); - - /* Append vendor specific IE TLV */ - cmd_append_size += mwifiex_cmd_append_vsie_tlv(priv, - MWIFIEX_VSIE_MASK_ADHOC, &pos); - - cmd->size = cpu_to_le16 - ((u16) (sizeof(struct host_cmd_ds_802_11_ad_hoc_join) - + S_DS_GEN + cmd_append_size)); - - adhoc_join->bss_descriptor.cap_info_bitmap = cpu_to_le16(tmp_cap); - - return 0; -} - -/* - * This function handles the command response of ad-hoc start and - * ad-hoc join. - * - * The function generates a device-connected event to notify - * the applications, in case of successful ad-hoc start/join, and - * saves the beacon buffer. - */ -int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ad_hoc_result *adhoc_result; - struct mwifiex_bssdescriptor *bss_desc; - u16 reason_code; - - adhoc_result = &resp->params.adhoc_result; - - bss_desc = priv->attempted_bss_desc; - - /* Join result code 0 --> SUCCESS */ - reason_code = le16_to_cpu(resp->result); - if (reason_code) { - mwifiex_dbg(priv->adapter, ERROR, "ADHOC_RESP: failed\n"); - if (priv->media_connected) - mwifiex_reset_connect_state(priv, reason_code); - - memset(&priv->curr_bss_params.bss_descriptor, - 0x00, sizeof(struct mwifiex_bssdescriptor)); - - ret = -1; - goto done; - } - - /* Send a Media Connected event, according to the Spec */ - priv->media_connected = true; - - if (le16_to_cpu(resp->command) == HostCmd_CMD_802_11_AD_HOC_START) { - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_S_RESP %s\n", - bss_desc->ssid.ssid); - - /* Update the created network descriptor with the new BSSID */ - memcpy(bss_desc->mac_address, - adhoc_result->bssid, ETH_ALEN); - - priv->adhoc_state = ADHOC_STARTED; - } else { - /* - * Now the join cmd should be successful. - * If BSSID has changed use SSID to compare instead of BSSID - */ - mwifiex_dbg(priv->adapter, INFO, - "info: ADHOC_J_RESP %s\n", - bss_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(&priv->curr_bss_params.bss_descriptor, - bss_desc, sizeof(struct mwifiex_bssdescriptor)); - - priv->adhoc_state = ADHOC_JOINED; - } - - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: channel = %d\n", - priv->adhoc_channel); - mwifiex_dbg(priv->adapter, INFO, "info: ADHOC_RESP: BSSID = %pM\n", - priv->curr_bss_params.bss_descriptor.mac_address); - - if (!netif_carrier_ok(priv->netdev)) - netif_carrier_on(priv->netdev); - mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); - - mwifiex_save_curr_bcn(priv); - -done: - /* Need to indicate IOCTL complete */ - if (adapter->curr_cmd->wait_q_enabled) { - if (ret) - adapter->cmd_wait_q.status = -1; - else - adapter->cmd_wait_q.status = 0; - - } - - return ret; -} - -/* - * This function associates to a specific BSS discovered in a scan. - * - * It clears any past association response stored for application - * retrieval and calls the command preparation routine to send the - * command to firmware. - */ -int mwifiex_associate(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - /* Return error if the adapter is not STA role or table entry - * is not marked as infra. - */ - if ((GET_BSS_ROLE(priv) != MWIFIEX_BSS_ROLE_STA) || - (bss_desc->bss_mode != NL80211_IFTYPE_STATION)) - return -1; - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - !bss_desc->disable_11n && !bss_desc->disable_11ac && - priv->adapter->config_bands & BAND_AAC) - mwifiex_set_11ac_ba_params(priv); - else - mwifiex_set_ba_params(priv); - - /* Clear any past association response stored for application - retrieval */ - priv->assoc_rsp_size = 0; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_ASSOCIATE, - HostCmd_ACT_GEN_SET, 0, bss_desc, true); -} - -/* - * This function starts an ad-hoc network. - * - * It calls the command preparation routine to send the command to firmware. - */ -int -mwifiex_adhoc_start(struct mwifiex_private *priv, - struct cfg80211_ssid *adhoc_ssid) -{ - mwifiex_dbg(priv->adapter, INFO, "info: Adhoc Channel = %d\n", - priv->adhoc_channel); - mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.channel = %d\n", - priv->curr_bss_params.bss_descriptor.channel); - mwifiex_dbg(priv->adapter, INFO, "info: curr_bss_params.band = %d\n", - priv->curr_bss_params.band); - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - priv->adapter->config_bands & BAND_AAC) - mwifiex_set_11ac_ba_params(priv); - else - mwifiex_set_ba_params(priv); - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_START, - HostCmd_ACT_GEN_SET, 0, adhoc_ssid, true); -} - -/* - * This function joins an ad-hoc network found in a previous scan. - * - * It calls the command preparation routine to send the command to firmware, - * if already not connected to the requested SSID. - */ -int mwifiex_adhoc_join(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - mwifiex_dbg(priv->adapter, INFO, - "info: adhoc join: curr_bss ssid =%s\n", - priv->curr_bss_params.bss_descriptor.ssid.ssid); - mwifiex_dbg(priv->adapter, INFO, - "info: adhoc join: curr_bss ssid_len =%u\n", - priv->curr_bss_params.bss_descriptor.ssid.ssid_len); - mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid =%s\n", - bss_desc->ssid.ssid); - mwifiex_dbg(priv->adapter, INFO, "info: adhoc join: ssid_len =%u\n", - bss_desc->ssid.ssid_len); - - /* Check if the requested SSID is already joined */ - if (priv->curr_bss_params.bss_descriptor.ssid.ssid_len && - !mwifiex_ssid_cmp(&bss_desc->ssid, - &priv->curr_bss_params.bss_descriptor.ssid) && - (priv->curr_bss_params.bss_descriptor.bss_mode == - NL80211_IFTYPE_ADHOC)) { - mwifiex_dbg(priv->adapter, INFO, - "info: ADHOC_J_CMD: new ad-hoc SSID\t" - "is the same as current; not attempting to re-join\n"); - return -1; - } - - if (ISSUPP_11ACENABLED(priv->adapter->fw_cap_info) && - !bss_desc->disable_11n && !bss_desc->disable_11ac && - priv->adapter->config_bands & BAND_AAC) - mwifiex_set_11ac_ba_params(priv); - else - mwifiex_set_ba_params(priv); - - mwifiex_dbg(priv->adapter, INFO, - "info: curr_bss_params.channel = %d\n", - priv->curr_bss_params.bss_descriptor.channel); - mwifiex_dbg(priv->adapter, INFO, - "info: curr_bss_params.band = %c\n", - priv->curr_bss_params.band); - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_JOIN, - HostCmd_ACT_GEN_SET, 0, bss_desc, true); -} - -/* - * This function deauthenticates/disconnects from infra network by sending - * deauthentication request. - */ -static int mwifiex_deauthenticate_infra(struct mwifiex_private *priv, u8 *mac) -{ - u8 mac_address[ETH_ALEN]; - int ret; - - if (!mac || is_zero_ether_addr(mac)) - memcpy(mac_address, - priv->curr_bss_params.bss_descriptor.mac_address, - ETH_ALEN); - else - memcpy(mac_address, mac, ETH_ALEN); - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, - HostCmd_ACT_GEN_SET, 0, mac_address, true); - - return ret; -} - -/* - * This function deauthenticates/disconnects from a BSS. - * - * In case of infra made, it sends deauthentication request, and - * in case of ad-hoc mode, a stop network request is sent to the firmware. - * In AP mode, a command to stop bss is sent to firmware. - */ -int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac) -{ - int ret = 0; - - if (!priv->media_connected) - return 0; - - switch (priv->bss_mode) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_P2P_CLIENT: - ret = mwifiex_deauthenticate_infra(priv, mac); - if (ret) - cfg80211_disconnected(priv->netdev, 0, NULL, 0, - true, GFP_KERNEL); - break; - case NL80211_IFTYPE_ADHOC: - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_AD_HOC_STOP, - HostCmd_ACT_GEN_SET, 0, NULL, true); - case NL80211_IFTYPE_AP: - return mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_STOP, - HostCmd_ACT_GEN_SET, 0, NULL, true); - default: - break; - } - - return ret; -} - -/* This function deauthenticates/disconnects from all BSS. */ -void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - int i; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv) - mwifiex_deauthenticate(priv, NULL); - } -} -EXPORT_SYMBOL_GPL(mwifiex_deauthenticate_all); - -/* - * This function converts band to radio type used in channel TLV. - */ -u8 -mwifiex_band_to_radio_type(u8 band) -{ - switch (band) { - case BAND_A: - case BAND_AN: - case BAND_A | BAND_AN: - case BAND_A | BAND_AN | BAND_AAC: - return HostCmd_SCAN_RADIO_TYPE_A; - case BAND_B: - case BAND_G: - case BAND_B | BAND_G: - default: - return HostCmd_SCAN_RADIO_TYPE_BG; - } -} diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c deleted file mode 100644 index 969ca1e1f3e9..000000000000 --- a/drivers/net/wireless/mwifiex/main.c +++ /dev/null @@ -1,1552 +0,0 @@ -/* - * Marvell Wireless LAN device driver: major functions - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "main.h" -#include "wmm.h" -#include "cfg80211.h" -#include "11n.h" - -#define VERSION "1.0" - -static unsigned int debug_mask = MWIFIEX_DEFAULT_DEBUG_MASK; -module_param(debug_mask, uint, 0); -MODULE_PARM_DESC(debug_mask, "bitmap for debug flags"); - -const char driver_version[] = "mwifiex " VERSION " (%s) "; -static char *cal_data_cfg; -module_param(cal_data_cfg, charp, 0); - -static unsigned short driver_mode; -module_param(driver_mode, ushort, 0); -MODULE_PARM_DESC(driver_mode, - "station=0x1(default), ap-sta=0x3, station-p2p=0x5, ap-sta-p2p=0x7"); - -/* - * This function registers the device and performs all the necessary - * initializations. - * - * The following initialization operations are performed - - * - Allocate adapter structure - * - Save interface specific operations table in adapter - * - Call interface specific initialization routine - * - Allocate private structures - * - Set default adapter structure parameters - * - Initialize locks - * - * In case of any errors during inittialization, this function also ensures - * proper cleanup before exiting. - */ -static int mwifiex_register(void *card, struct mwifiex_if_ops *if_ops, - void **padapter) -{ - struct mwifiex_adapter *adapter; - int i; - - adapter = kzalloc(sizeof(struct mwifiex_adapter), GFP_KERNEL); - if (!adapter) - return -ENOMEM; - - *padapter = adapter; - adapter->card = card; - - /* Save interface specific operations in adapter */ - memmove(&adapter->if_ops, if_ops, sizeof(struct mwifiex_if_ops)); - adapter->debug_mask = debug_mask; - - /* card specific initialization has been deferred until now .. */ - if (adapter->if_ops.init_if) - if (adapter->if_ops.init_if(adapter)) - goto error; - - adapter->priv_num = 0; - - for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) { - /* Allocate memory for private structure */ - adapter->priv[i] = - kzalloc(sizeof(struct mwifiex_private), GFP_KERNEL); - if (!adapter->priv[i]) - goto error; - - adapter->priv[i]->adapter = adapter; - adapter->priv_num++; - } - mwifiex_init_lock_list(adapter); - - setup_timer(&adapter->cmd_timer, mwifiex_cmd_timeout_func, - (unsigned long)adapter); - - return 0; - -error: - mwifiex_dbg(adapter, ERROR, - "info: leave mwifiex_register with error\n"); - - for (i = 0; i < adapter->priv_num; i++) - kfree(adapter->priv[i]); - - kfree(adapter); - - return -1; -} - -/* - * This function unregisters the device and performs all the necessary - * cleanups. - * - * The following cleanup operations are performed - - * - Free the timers - * - Free beacon buffers - * - Free private structures - * - Free adapter structure - */ -static int mwifiex_unregister(struct mwifiex_adapter *adapter) -{ - s32 i; - - if (adapter->if_ops.cleanup_if) - adapter->if_ops.cleanup_if(adapter); - - del_timer_sync(&adapter->cmd_timer); - - /* Free private structures */ - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - mwifiex_free_curr_bcn(adapter->priv[i]); - kfree(adapter->priv[i]); - } - } - - vfree(adapter->chan_stats); - kfree(adapter); - return 0; -} - -void mwifiex_queue_main_work(struct mwifiex_adapter *adapter) -{ - unsigned long flags; - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - if (adapter->mwifiex_processing) { - adapter->more_task_flag = true; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - } else { - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - queue_work(adapter->workqueue, &adapter->main_work); - } -} -EXPORT_SYMBOL_GPL(mwifiex_queue_main_work); - -static void mwifiex_queue_rx_work(struct mwifiex_adapter *adapter) -{ - unsigned long flags; - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - if (adapter->rx_processing) { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - } else { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - queue_work(adapter->rx_workqueue, &adapter->rx_work); - } -} - -static int mwifiex_process_rx(struct mwifiex_adapter *adapter) -{ - unsigned long flags; - struct sk_buff *skb; - struct mwifiex_rxinfo *rx_info; - - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - if (adapter->rx_processing || adapter->rx_locked) { - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - goto exit_rx_proc; - } else { - adapter->rx_processing = true; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - } - - /* Check for Rx data */ - while ((skb = skb_dequeue(&adapter->rx_data_q))) { - atomic_dec(&adapter->rx_pending); - if ((adapter->delay_main_work || - adapter->iface_type == MWIFIEX_USB) && - (atomic_read(&adapter->rx_pending) < LOW_RX_PENDING)) { - if (adapter->if_ops.submit_rem_rx_urbs) - adapter->if_ops.submit_rem_rx_urbs(adapter); - adapter->delay_main_work = false; - mwifiex_queue_main_work(adapter); - } - rx_info = MWIFIEX_SKB_RXCB(skb); - if (rx_info->buf_type == MWIFIEX_TYPE_AGGR_DATA) { - if (adapter->if_ops.deaggr_pkt) - adapter->if_ops.deaggr_pkt(adapter, skb); - dev_kfree_skb_any(skb); - } else { - mwifiex_handle_rx_packet(adapter, skb); - } - } - spin_lock_irqsave(&adapter->rx_proc_lock, flags); - adapter->rx_processing = false; - spin_unlock_irqrestore(&adapter->rx_proc_lock, flags); - -exit_rx_proc: - return 0; -} - -/* - * The main process. - * - * This function is the main procedure of the driver and handles various driver - * operations. It runs in a loop and provides the core functionalities. - * - * The main responsibilities of this function are - - * - Ensure concurrency control - * - Handle pending interrupts and call interrupt handlers - * - Wake up the card if required - * - Handle command responses and call response handlers - * - Handle events and call event handlers - * - Execute pending commands - * - Transmit pending data packets - */ -int mwifiex_main_process(struct mwifiex_adapter *adapter) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - - /* Check if already processing */ - if (adapter->mwifiex_processing || adapter->main_locked) { - adapter->more_task_flag = true; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - goto exit_main_proc; - } else { - adapter->mwifiex_processing = true; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - } -process_start: - do { - if ((adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) || - (adapter->hw_status == MWIFIEX_HW_STATUS_NOT_READY)) - break; - - /* For non-USB interfaces, If we process interrupts first, it - * would increase RX pending even further. Avoid this by - * checking if rx_pending has crossed high threshold and - * schedule rx work queue and then process interrupts. - * For USB interface, there are no interrupts. We already have - * HIGH_RX_PENDING check in usb.c - */ - if (atomic_read(&adapter->rx_pending) >= HIGH_RX_PENDING && - adapter->iface_type != MWIFIEX_USB) { - adapter->delay_main_work = true; - mwifiex_queue_rx_work(adapter); - break; - } - - /* Handle pending interrupt if any */ - if (adapter->int_status) { - if (adapter->hs_activated) - mwifiex_process_hs_config(adapter); - if (adapter->if_ops.process_int_status) - adapter->if_ops.process_int_status(adapter); - } - - if (adapter->rx_work_enabled && adapter->data_received) - mwifiex_queue_rx_work(adapter); - - /* Need to wake up the card ? */ - if ((adapter->ps_state == PS_STATE_SLEEP) && - (adapter->pm_wakeup_card_req && - !adapter->pm_wakeup_fw_try) && - (is_command_pending(adapter) || - !skb_queue_empty(&adapter->tx_data_q) || - !mwifiex_bypass_txlist_empty(adapter) || - !mwifiex_wmm_lists_empty(adapter))) { - adapter->pm_wakeup_fw_try = true; - mod_timer(&adapter->wakeup_timer, jiffies + (HZ*3)); - adapter->if_ops.wakeup(adapter); - continue; - } - - if (IS_CARD_RX_RCVD(adapter)) { - adapter->data_received = false; - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - if (adapter->ps_state == PS_STATE_SLEEP) - adapter->ps_state = PS_STATE_AWAKE; - } else { - /* We have tried to wakeup the card already */ - if (adapter->pm_wakeup_fw_try) - break; - if (adapter->ps_state != PS_STATE_AWAKE) - break; - if (adapter->tx_lock_flag) { - if (adapter->iface_type == MWIFIEX_USB) { - if (!adapter->usb_mc_setup) - break; - } else - break; - } - - if ((!adapter->scan_chan_gap_enabled && - adapter->scan_processing) || adapter->data_sent || - mwifiex_is_tdls_chan_switching - (mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_STA)) || - (mwifiex_wmm_lists_empty(adapter) && - mwifiex_bypass_txlist_empty(adapter) && - skb_queue_empty(&adapter->tx_data_q))) { - if (adapter->cmd_sent || adapter->curr_cmd || - !mwifiex_is_send_cmd_allowed - (mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_STA)) || - (!is_command_pending(adapter))) - break; - } - } - - /* Check for event */ - if (adapter->event_received) { - adapter->event_received = false; - mwifiex_process_event(adapter); - } - - /* Check for Cmd Resp */ - if (adapter->cmd_resp_received) { - adapter->cmd_resp_received = false; - mwifiex_process_cmdresp(adapter); - - /* call mwifiex back when init_fw is done */ - if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - mwifiex_init_fw_complete(adapter); - } - } - - /* Check if we need to confirm Sleep Request - received previously */ - if (adapter->ps_state == PS_STATE_PRE_SLEEP) { - if (!adapter->cmd_sent && !adapter->curr_cmd) - mwifiex_check_ps_cond(adapter); - } - - /* * The ps_state may have been changed during processing of - * Sleep Request event. - */ - if ((adapter->ps_state == PS_STATE_SLEEP) || - (adapter->ps_state == PS_STATE_PRE_SLEEP) || - (adapter->ps_state == PS_STATE_SLEEP_CFM)) { - continue; - } - - if (adapter->tx_lock_flag) { - if (adapter->iface_type == MWIFIEX_USB) { - if (!adapter->usb_mc_setup) - continue; - } else - continue; - } - - if (!adapter->cmd_sent && !adapter->curr_cmd && - mwifiex_is_send_cmd_allowed - (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { - if (mwifiex_exec_next_cmd(adapter) == -1) { - ret = -1; - break; - } - } - - /** If USB Multi channel setup ongoing, - * wait for ready to tx data. - */ - if (adapter->iface_type == MWIFIEX_USB && - adapter->usb_mc_setup) - continue; - - if ((adapter->scan_chan_gap_enabled || - !adapter->scan_processing) && - !adapter->data_sent && - !skb_queue_empty(&adapter->tx_data_q)) { - mwifiex_process_tx_queue(adapter); - if (adapter->hs_activated) { - adapter->is_hs_configured = false; - mwifiex_hs_activated_event - (mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - false); - } - } - - if ((adapter->scan_chan_gap_enabled || - !adapter->scan_processing) && - !adapter->data_sent && - !mwifiex_bypass_txlist_empty(adapter) && - !mwifiex_is_tdls_chan_switching - (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { - mwifiex_process_bypass_tx(adapter); - if (adapter->hs_activated) { - adapter->is_hs_configured = false; - mwifiex_hs_activated_event - (mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - false); - } - } - - if ((adapter->scan_chan_gap_enabled || - !adapter->scan_processing) && - !adapter->data_sent && !mwifiex_wmm_lists_empty(adapter) && - !mwifiex_is_tdls_chan_switching - (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA))) { - mwifiex_wmm_process_tx(adapter); - if (adapter->hs_activated) { - adapter->is_hs_configured = false; - mwifiex_hs_activated_event - (mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - false); - } - } - - if (adapter->delay_null_pkt && !adapter->cmd_sent && - !adapter->curr_cmd && !is_command_pending(adapter) && - (mwifiex_wmm_lists_empty(adapter) && - mwifiex_bypass_txlist_empty(adapter) && - skb_queue_empty(&adapter->tx_data_q))) { - if (!mwifiex_send_null_packet - (mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), - MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) { - adapter->delay_null_pkt = false; - adapter->ps_state = PS_STATE_SLEEP; - } - break; - } - } while (true); - - spin_lock_irqsave(&adapter->main_proc_lock, flags); - if (adapter->more_task_flag) { - adapter->more_task_flag = false; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - goto process_start; - } - adapter->mwifiex_processing = false; - spin_unlock_irqrestore(&adapter->main_proc_lock, flags); - -exit_main_proc: - if (adapter->hw_status == MWIFIEX_HW_STATUS_CLOSING) - mwifiex_shutdown_drv(adapter); - return ret; -} -EXPORT_SYMBOL_GPL(mwifiex_main_process); - -/* - * This function frees the adapter structure. - * - * Additionally, this closes the netlink socket, frees the timers - * and private structures. - */ -static void mwifiex_free_adapter(struct mwifiex_adapter *adapter) -{ - if (!adapter) { - pr_err("%s: adapter is NULL\n", __func__); - return; - } - - mwifiex_unregister(adapter); - pr_debug("info: %s: free adapter\n", __func__); -} - -/* - * This function cancels all works in the queue and destroys - * the main workqueue. - */ -static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter) -{ - flush_workqueue(adapter->workqueue); - destroy_workqueue(adapter->workqueue); - adapter->workqueue = NULL; - - if (adapter->rx_workqueue) { - flush_workqueue(adapter->rx_workqueue); - destroy_workqueue(adapter->rx_workqueue); - adapter->rx_workqueue = NULL; - } -} - -/* - * This function gets firmware and initializes it. - * - * The main initialization steps followed are - - * - Download the correct firmware to card - * - Issue the init commands to firmware - */ -static void mwifiex_fw_dpc(const struct firmware *firmware, void *context) -{ - int ret; - char fmt[64]; - struct mwifiex_private *priv; - struct mwifiex_adapter *adapter = context; - struct mwifiex_fw_image fw; - struct semaphore *sem = adapter->card_sem; - bool init_failed = false; - struct wireless_dev *wdev; - - if (!firmware) { - mwifiex_dbg(adapter, ERROR, - "Failed to get firmware %s\n", adapter->fw_name); - goto err_dnld_fw; - } - - memset(&fw, 0, sizeof(struct mwifiex_fw_image)); - adapter->firmware = firmware; - fw.fw_buf = (u8 *) adapter->firmware->data; - fw.fw_len = adapter->firmware->size; - - if (adapter->if_ops.dnld_fw) - ret = adapter->if_ops.dnld_fw(adapter, &fw); - else - ret = mwifiex_dnld_fw(adapter, &fw); - if (ret == -1) - goto err_dnld_fw; - - mwifiex_dbg(adapter, MSG, "WLAN FW is active\n"); - - if (cal_data_cfg) { - if ((request_firmware(&adapter->cal_data, cal_data_cfg, - adapter->dev)) < 0) - mwifiex_dbg(adapter, ERROR, - "Cal data request_firmware() failed\n"); - } - - /* enable host interrupt after fw dnld is successful */ - if (adapter->if_ops.enable_int) { - if (adapter->if_ops.enable_int(adapter)) - goto err_dnld_fw; - } - - adapter->init_wait_q_woken = false; - ret = mwifiex_init_fw(adapter); - if (ret == -1) { - goto err_init_fw; - } else if (!ret) { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - goto done; - } - /* Wait for mwifiex_init to complete */ - wait_event_interruptible(adapter->init_wait_q, - adapter->init_wait_q_woken); - if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) - goto err_init_fw; - - priv = adapter->priv[MWIFIEX_BSS_ROLE_STA]; - if (mwifiex_register_cfg80211(adapter)) { - mwifiex_dbg(adapter, ERROR, - "cannot register with cfg80211\n"); - goto err_init_fw; - } - - if (mwifiex_init_channel_scan_gap(adapter)) { - mwifiex_dbg(adapter, ERROR, - "could not init channel stats table\n"); - goto err_init_fw; - } - - if (driver_mode) { - driver_mode &= MWIFIEX_DRIVER_MODE_BITMASK; - driver_mode |= MWIFIEX_DRIVER_MODE_STA; - } - - rtnl_lock(); - /* Create station interface by default */ - wdev = mwifiex_add_virtual_intf(adapter->wiphy, "mlan%d", NET_NAME_ENUM, - NL80211_IFTYPE_STATION, NULL, NULL); - if (IS_ERR(wdev)) { - mwifiex_dbg(adapter, ERROR, - "cannot create default STA interface\n"); - rtnl_unlock(); - goto err_add_intf; - } - - if (driver_mode & MWIFIEX_DRIVER_MODE_UAP) { - wdev = mwifiex_add_virtual_intf(adapter->wiphy, "uap%d", NET_NAME_ENUM, - NL80211_IFTYPE_AP, NULL, NULL); - if (IS_ERR(wdev)) { - mwifiex_dbg(adapter, ERROR, - "cannot create AP interface\n"); - rtnl_unlock(); - goto err_add_intf; - } - } - - if (driver_mode & MWIFIEX_DRIVER_MODE_P2P) { - wdev = mwifiex_add_virtual_intf(adapter->wiphy, "p2p%d", NET_NAME_ENUM, - NL80211_IFTYPE_P2P_CLIENT, NULL, - NULL); - if (IS_ERR(wdev)) { - mwifiex_dbg(adapter, ERROR, - "cannot create p2p client interface\n"); - rtnl_unlock(); - goto err_add_intf; - } - } - rtnl_unlock(); - - mwifiex_drv_get_driver_version(adapter, fmt, sizeof(fmt) - 1); - mwifiex_dbg(adapter, MSG, "driver_version = %s\n", fmt); - goto done; - -err_add_intf: - wiphy_unregister(adapter->wiphy); - wiphy_free(adapter->wiphy); -err_init_fw: - if (adapter->if_ops.disable_int) - adapter->if_ops.disable_int(adapter); -err_dnld_fw: - mwifiex_dbg(adapter, ERROR, - "info: %s: unregister device\n", __func__); - if (adapter->if_ops.unregister_dev) - adapter->if_ops.unregister_dev(adapter); - - if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { - pr_debug("info: %s: shutdown mwifiex\n", __func__); - adapter->init_wait_q_woken = false; - - if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) - wait_event_interruptible(adapter->init_wait_q, - adapter->init_wait_q_woken); - } - adapter->surprise_removed = true; - mwifiex_terminate_workqueue(adapter); - init_failed = true; -done: - if (adapter->cal_data) { - release_firmware(adapter->cal_data); - adapter->cal_data = NULL; - } - if (adapter->firmware) { - release_firmware(adapter->firmware); - adapter->firmware = NULL; - } - if (init_failed) - mwifiex_free_adapter(adapter); - up(sem); - return; -} - -/* - * This function initializes the hardware and gets firmware. - */ -static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) -{ - int ret; - - ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name, - adapter->dev, GFP_KERNEL, adapter, - mwifiex_fw_dpc); - if (ret < 0) - mwifiex_dbg(adapter, ERROR, - "request_firmware_nowait error %d\n", ret); - return ret; -} - -/* - * CFG802.11 network device handler for open. - * - * Starts the data queue. - */ -static int -mwifiex_open(struct net_device *dev) -{ - netif_carrier_off(dev); - - return 0; -} - -/* - * CFG802.11 network device handler for close. - */ -static int -mwifiex_close(struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - if (priv->scan_request) { - mwifiex_dbg(priv->adapter, INFO, - "aborting scan on ndo_stop\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - priv->scan_aborting = true; - } - - return 0; -} - -static bool -mwifiex_bypass_tx_queue(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; - - if (ntohs(eth_hdr->h_proto) == ETH_P_PAE || - mwifiex_is_skb_mgmt_frame(skb) || - (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && - ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - (ntohs(eth_hdr->h_proto) == ETH_P_TDLS))) { - mwifiex_dbg(priv->adapter, DATA, - "bypass txqueue; eth type %#x, mgmt %d\n", - ntohs(eth_hdr->h_proto), - mwifiex_is_skb_mgmt_frame(skb)); - return true; - } - - return false; -} -/* - * Add buffer into wmm tx queue and queue work to transmit it. - */ -int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb) -{ - struct netdev_queue *txq; - int index = mwifiex_1d_to_wmm_queue[skb->priority]; - - if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) { - txq = netdev_get_tx_queue(priv->netdev, index); - if (!netif_tx_queue_stopped(txq)) { - netif_tx_stop_queue(txq); - mwifiex_dbg(priv->adapter, DATA, - "stop queue: %d\n", index); - } - } - - if (mwifiex_bypass_tx_queue(priv, skb)) { - atomic_inc(&priv->adapter->tx_pending); - atomic_inc(&priv->adapter->bypass_tx_pending); - mwifiex_wmm_add_buf_bypass_txqueue(priv, skb); - } else { - atomic_inc(&priv->adapter->tx_pending); - mwifiex_wmm_add_buf_txqueue(priv, skb); - } - - mwifiex_queue_main_work(priv->adapter); - - return 0; -} - -struct sk_buff * -mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, - struct sk_buff *skb, u8 flag, u64 *cookie) -{ - struct sk_buff *orig_skb = skb; - struct mwifiex_txinfo *tx_info, *orig_tx_info; - - skb = skb_clone(skb, GFP_ATOMIC); - if (skb) { - unsigned long flags; - int id; - - spin_lock_irqsave(&priv->ack_status_lock, flags); - id = idr_alloc(&priv->ack_status_frames, orig_skb, - 1, 0xff, GFP_ATOMIC); - spin_unlock_irqrestore(&priv->ack_status_lock, flags); - - if (id >= 0) { - tx_info = MWIFIEX_SKB_TXCB(skb); - tx_info->ack_frame_id = id; - tx_info->flags |= flag; - orig_tx_info = MWIFIEX_SKB_TXCB(orig_skb); - orig_tx_info->ack_frame_id = id; - orig_tx_info->flags |= flag; - - if (flag == MWIFIEX_BUF_FLAG_ACTION_TX_STATUS && cookie) - orig_tx_info->cookie = *cookie; - - } else if (skb_shared(skb)) { - kfree_skb(orig_skb); - } else { - kfree_skb(skb); - skb = orig_skb; - } - } else { - /* couldn't clone -- lose tx status ... */ - skb = orig_skb; - } - - return skb; -} - -/* - * CFG802.11 network device handler for data transmission. - */ -static int -mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct sk_buff *new_skb; - struct mwifiex_txinfo *tx_info; - bool multicast; - - mwifiex_dbg(priv->adapter, DATA, - "data: %lu BSS(%d-%d): Data <= kernel\n", - jiffies, priv->bss_type, priv->bss_num); - - if (priv->adapter->surprise_removed) { - kfree_skb(skb); - priv->stats.tx_dropped++; - return 0; - } - if (!skb->len || (skb->len > ETH_FRAME_LEN)) { - mwifiex_dbg(priv->adapter, ERROR, - "Tx: bad skb len %d\n", skb->len); - kfree_skb(skb); - priv->stats.tx_dropped++; - return 0; - } - if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { - mwifiex_dbg(priv->adapter, DATA, - "data: Tx: insufficient skb headroom %d\n", - skb_headroom(skb)); - /* Insufficient skb headroom - allocate a new skb */ - new_skb = - skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); - if (unlikely(!new_skb)) { - mwifiex_dbg(priv->adapter, ERROR, - "Tx: cannot alloca new_skb\n"); - kfree_skb(skb); - priv->stats.tx_dropped++; - return 0; - } - kfree_skb(skb); - skb = new_skb; - mwifiex_dbg(priv->adapter, INFO, - "info: new skb headroomd %d\n", - skb_headroom(skb)); - } - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->pkt_len = skb->len; - - multicast = is_multicast_ether_addr(skb->data); - - if (unlikely(!multicast && skb->sk && - skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS && - priv->adapter->fw_api_ver == MWIFIEX_FW_V15)) - skb = mwifiex_clone_skb_for_tx_status(priv, - skb, - MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS, NULL); - - /* 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. - */ - __net_timestamp(skb); - - if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->bss_type == MWIFIEX_BSS_TYPE_STA && - !ether_addr_equal_unaligned(priv->cfg_bssid, skb->data)) { - if (priv->adapter->auto_tdls && priv->check_tdls_tx) - mwifiex_tdls_check_tx(priv, skb); - } - - mwifiex_queue_tx_pkt(priv, skb); - - return 0; -} - -/* - * CFG802.11 network device handler for setting MAC address. - */ -static int -mwifiex_set_mac_address(struct net_device *dev, void *addr) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct sockaddr *hw_addr = addr; - int ret; - - memcpy(priv->curr_addr, hw_addr->sa_data, ETH_ALEN); - - /* Send request to firmware */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_MAC_ADDRESS, - HostCmd_ACT_GEN_SET, 0, NULL, true); - - if (!ret) - memcpy(priv->netdev->dev_addr, priv->curr_addr, ETH_ALEN); - else - mwifiex_dbg(priv->adapter, ERROR, - "set mac address failed: ret=%d\n", ret); - - memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); - - return ret; -} - -/* - * CFG802.11 network device handler for setting multicast list. - */ -static void mwifiex_set_multicast_list(struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - struct mwifiex_multicast_list mcast_list; - - if (dev->flags & IFF_PROMISC) { - mcast_list.mode = MWIFIEX_PROMISC_MODE; - } else if (dev->flags & IFF_ALLMULTI || - netdev_mc_count(dev) > MWIFIEX_MAX_MULTICAST_LIST_SIZE) { - mcast_list.mode = MWIFIEX_ALL_MULTI_MODE; - } else { - mcast_list.mode = MWIFIEX_MULTICAST_MODE; - mcast_list.num_multicast_addr = - mwifiex_copy_mcast_addr(&mcast_list, dev); - } - mwifiex_request_set_multicast_list(priv, &mcast_list); -} - -/* - * CFG802.11 network device handler for transmission timeout. - */ -static void -mwifiex_tx_timeout(struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - priv->num_tx_timeout++; - priv->tx_timeout_cnt++; - mwifiex_dbg(priv->adapter, ERROR, - "%lu : Tx timeout(#%d), bss_type-num = %d-%d\n", - jiffies, priv->tx_timeout_cnt, priv->bss_type, - priv->bss_num); - mwifiex_set_trans_start(dev); - - if (priv->tx_timeout_cnt > TX_TIMEOUT_THRESHOLD && - priv->adapter->if_ops.card_reset) { - mwifiex_dbg(priv->adapter, ERROR, - "tx_timeout_cnt exceeds threshold.\t" - "Triggering card reset!\n"); - priv->adapter->if_ops.card_reset(priv->adapter); - } -} - -void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = adapter->card; - struct mwifiex_private *priv; - u16 tx_buf_size; - int i, ret; - - card->mc_resync_flag = true; - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - if (atomic_read(&card->port[i].tx_data_urb_pending)) { - mwifiex_dbg(adapter, WARN, "pending data urb in sys\n"); - return; - } - } - - card->mc_resync_flag = false; - tx_buf_size = 0xffff; - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, - HostCmd_ACT_GEN_SET, 0, &tx_buf_size, false); - if (ret) - mwifiex_dbg(adapter, ERROR, - "send reconfig tx buf size cmd err\n"); -} -EXPORT_SYMBOL_GPL(mwifiex_multi_chan_resync); - -void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter) -{ - void *p; - char drv_version[64]; - struct usb_card_rec *cardp; - struct sdio_mmc_card *sdio_card; - struct mwifiex_private *priv; - int i, idx; - struct netdev_queue *txq; - struct mwifiex_debug_info *debug_info; - - if (adapter->drv_info_dump) { - vfree(adapter->drv_info_dump); - adapter->drv_info_dump = NULL; - adapter->drv_info_size = 0; - } - - mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump start===\n"); - - adapter->drv_info_dump = vzalloc(MWIFIEX_DRV_INFO_SIZE_MAX); - - if (!adapter->drv_info_dump) - return; - - p = (char *)(adapter->drv_info_dump); - p += sprintf(p, "driver_name = " "\"mwifiex\"\n"); - - mwifiex_drv_get_driver_version(adapter, drv_version, - sizeof(drv_version) - 1); - p += sprintf(p, "driver_version = %s\n", drv_version); - - if (adapter->iface_type == MWIFIEX_USB) { - cardp = (struct usb_card_rec *)adapter->card; - p += sprintf(p, "tx_cmd_urb_pending = %d\n", - atomic_read(&cardp->tx_cmd_urb_pending)); - p += sprintf(p, "tx_data_urb_pending_port_0 = %d\n", - atomic_read(&cardp->port[0].tx_data_urb_pending)); - p += sprintf(p, "tx_data_urb_pending_port_1 = %d\n", - atomic_read(&cardp->port[1].tx_data_urb_pending)); - p += sprintf(p, "rx_cmd_urb_pending = %d\n", - atomic_read(&cardp->rx_cmd_urb_pending)); - p += sprintf(p, "rx_data_urb_pending = %d\n", - atomic_read(&cardp->rx_data_urb_pending)); - } - - p += sprintf(p, "tx_pending = %d\n", - atomic_read(&adapter->tx_pending)); - p += sprintf(p, "rx_pending = %d\n", - atomic_read(&adapter->rx_pending)); - - if (adapter->iface_type == MWIFIEX_SDIO) { - sdio_card = (struct sdio_mmc_card *)adapter->card; - p += sprintf(p, "\nmp_rd_bitmap=0x%x curr_rd_port=0x%x\n", - sdio_card->mp_rd_bitmap, sdio_card->curr_rd_port); - p += sprintf(p, "mp_wr_bitmap=0x%x curr_wr_port=0x%x\n", - sdio_card->mp_wr_bitmap, sdio_card->curr_wr_port); - } - - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i] || !adapter->priv[i]->netdev) - continue; - priv = adapter->priv[i]; - p += sprintf(p, "\n[interface : \"%s\"]\n", - priv->netdev->name); - p += sprintf(p, "wmm_tx_pending[0] = %d\n", - atomic_read(&priv->wmm_tx_pending[0])); - p += sprintf(p, "wmm_tx_pending[1] = %d\n", - atomic_read(&priv->wmm_tx_pending[1])); - p += sprintf(p, "wmm_tx_pending[2] = %d\n", - atomic_read(&priv->wmm_tx_pending[2])); - p += sprintf(p, "wmm_tx_pending[3] = %d\n", - atomic_read(&priv->wmm_tx_pending[3])); - p += sprintf(p, "media_state=\"%s\"\n", !priv->media_connected ? - "Disconnected" : "Connected"); - p += sprintf(p, "carrier %s\n", (netif_carrier_ok(priv->netdev) - ? "on" : "off")); - for (idx = 0; idx < priv->netdev->num_tx_queues; idx++) { - txq = netdev_get_tx_queue(priv->netdev, idx); - p += sprintf(p, "tx queue %d:%s ", idx, - netif_tx_queue_stopped(txq) ? - "stopped" : "started"); - } - p += sprintf(p, "\n%s: num_tx_timeout = %d\n", - priv->netdev->name, priv->num_tx_timeout); - } - - if (adapter->iface_type == MWIFIEX_SDIO) { - p += sprintf(p, "\n=== SDIO register dump===\n"); - if (adapter->if_ops.reg_dump) - p += adapter->if_ops.reg_dump(adapter, p); - } - - p += sprintf(p, "\n=== more debug information\n"); - debug_info = kzalloc(sizeof(*debug_info), GFP_KERNEL); - if (debug_info) { - for (i = 0; i < adapter->priv_num; i++) { - if (!adapter->priv[i] || !adapter->priv[i]->netdev) - continue; - priv = adapter->priv[i]; - mwifiex_get_debug_info(priv, debug_info); - p += mwifiex_debug_info_to_buffer(priv, p, debug_info); - break; - } - kfree(debug_info); - } - - adapter->drv_info_size = p - adapter->drv_info_dump; - mwifiex_dbg(adapter, MSG, "===mwifiex driverinfo dump end===\n"); -} -EXPORT_SYMBOL_GPL(mwifiex_drv_info_dump); - -void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter) -{ - u8 idx, *dump_data, *fw_dump_ptr; - u32 dump_len; - - dump_len = (strlen("========Start dump driverinfo========\n") + - adapter->drv_info_size + - strlen("\n========End dump========\n")); - - for (idx = 0; idx < adapter->num_mem_types; idx++) { - struct memory_type_mapping *entry = - &adapter->mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - dump_len += (strlen("========Start dump ") + - strlen(entry->mem_name) + - strlen("========\n") + - (entry->mem_size + 1) + - strlen("\n========End dump========\n")); - } - } - - dump_data = vzalloc(dump_len + 1); - if (!dump_data) - goto done; - - fw_dump_ptr = dump_data; - - /* Dump all the memory data into single file, a userspace script will - * be used to split all the memory data to multiple files - */ - mwifiex_dbg(adapter, MSG, - "== mwifiex dump information to /sys/class/devcoredump start"); - - strcpy(fw_dump_ptr, "========Start dump driverinfo========\n"); - fw_dump_ptr += strlen("========Start dump driverinfo========\n"); - memcpy(fw_dump_ptr, adapter->drv_info_dump, adapter->drv_info_size); - fw_dump_ptr += adapter->drv_info_size; - strcpy(fw_dump_ptr, "\n========End dump========\n"); - fw_dump_ptr += strlen("\n========End dump========\n"); - - for (idx = 0; idx < adapter->num_mem_types; idx++) { - struct memory_type_mapping *entry = - &adapter->mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - strcpy(fw_dump_ptr, "========Start dump "); - fw_dump_ptr += strlen("========Start dump "); - - strcpy(fw_dump_ptr, entry->mem_name); - fw_dump_ptr += strlen(entry->mem_name); - - strcpy(fw_dump_ptr, "========\n"); - fw_dump_ptr += strlen("========\n"); - - memcpy(fw_dump_ptr, entry->mem_ptr, entry->mem_size); - fw_dump_ptr += entry->mem_size; - - strcpy(fw_dump_ptr, "\n========End dump========\n"); - fw_dump_ptr += strlen("\n========End dump========\n"); - } - } - - /* device dump data will be free in device coredump release function - * after 5 min - */ - dev_coredumpv(adapter->dev, dump_data, dump_len, GFP_KERNEL); - mwifiex_dbg(adapter, MSG, - "== mwifiex dump information to /sys/class/devcoredump end"); - -done: - for (idx = 0; idx < adapter->num_mem_types; idx++) { - struct memory_type_mapping *entry = - &adapter->mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - - if (adapter->drv_info_dump) { - vfree(adapter->drv_info_dump); - adapter->drv_info_dump = NULL; - adapter->drv_info_size = 0; - } -} -EXPORT_SYMBOL_GPL(mwifiex_upload_device_dump); - -/* - * CFG802.11 network device handler for statistics retrieval. - */ -static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) -{ - struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); - - return &priv->stats; -} - -static u16 -mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb, - void *accel_priv, select_queue_fallback_t fallback) -{ - skb->priority = cfg80211_classify8021d(skb, NULL); - return mwifiex_1d_to_wmm_queue[skb->priority]; -} - -/* Network device handlers */ -static const struct net_device_ops mwifiex_netdev_ops = { - .ndo_open = mwifiex_open, - .ndo_stop = mwifiex_close, - .ndo_start_xmit = mwifiex_hard_start_xmit, - .ndo_set_mac_address = mwifiex_set_mac_address, - .ndo_validate_addr = eth_validate_addr, - .ndo_tx_timeout = mwifiex_tx_timeout, - .ndo_get_stats = mwifiex_get_stats, - .ndo_set_rx_mode = mwifiex_set_multicast_list, - .ndo_select_queue = mwifiex_netdev_select_wmm_queue, -}; - -/* - * This function initializes the private structure parameters. - * - * The following wait queues are initialized - - * - IOCTL wait queue - * - Command wait queue - * - Statistics wait queue - * - * ...and the following default parameters are set - - * - Current key index : Set to 0 - * - Rate index : Set to auto - * - Media connected : Set to disconnected - * - Adhoc link sensed : Set to false - * - Nick name : Set to null - * - Number of Tx timeout : Set to 0 - * - Device address : Set to current address - * - Rx histogram statistc : Set to 0 - * - * In addition, the CFG80211 work queue is also created. - */ -void mwifiex_init_priv_params(struct mwifiex_private *priv, - struct net_device *dev) -{ - dev->netdev_ops = &mwifiex_netdev_ops; - dev->destructor = free_netdev; - /* Initialize private structure */ - priv->current_key_index = 0; - priv->media_connected = false; - memset(priv->mgmt_ie, 0, - sizeof(struct mwifiex_ie) * MAX_MGMT_IE_INDEX); - priv->beacon_idx = MWIFIEX_AUTO_IDX_MASK; - priv->proberesp_idx = MWIFIEX_AUTO_IDX_MASK; - priv->assocresp_idx = MWIFIEX_AUTO_IDX_MASK; - priv->gen_idx = MWIFIEX_AUTO_IDX_MASK; - priv->num_tx_timeout = 0; - ether_addr_copy(priv->curr_addr, priv->adapter->perm_addr); - memcpy(dev->dev_addr, priv->curr_addr, ETH_ALEN); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || - GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - priv->hist_data = kmalloc(sizeof(*priv->hist_data), GFP_KERNEL); - if (priv->hist_data) - mwifiex_hist_data_reset(priv); - } -} - -/* - * This function check if command is pending. - */ -int is_command_pending(struct mwifiex_adapter *adapter) -{ - unsigned long flags; - int is_cmd_pend_q_empty; - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, flags); - is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, flags); - - return !is_cmd_pend_q_empty; -} - -/* - * This is the RX work queue function. - * - * It handles the RX operations. - */ -static void mwifiex_rx_work_queue(struct work_struct *work) -{ - struct mwifiex_adapter *adapter = - container_of(work, struct mwifiex_adapter, rx_work); - - if (adapter->surprise_removed) - return; - mwifiex_process_rx(adapter); -} - -/* - * This is the main work queue function. - * - * It handles the main process, which in turn handles the complete - * driver operations. - */ -static void mwifiex_main_work_queue(struct work_struct *work) -{ - struct mwifiex_adapter *adapter = - container_of(work, struct mwifiex_adapter, main_work); - - if (adapter->surprise_removed) - return; - mwifiex_main_process(adapter); -} - -/* - * This function adds the card. - * - * This function follows the following major steps to set up the device - - * - Initialize software. This includes probing the card, registering - * the interface operations table, and allocating/initializing the - * adapter structure - * - Set up the netlink socket - * - Create and start the main work queue - * - Register the device - * - Initialize firmware and hardware - * - Add logical interfaces - */ -int -mwifiex_add_card(void *card, struct semaphore *sem, - struct mwifiex_if_ops *if_ops, u8 iface_type) -{ - struct mwifiex_adapter *adapter; - - if (down_interruptible(sem)) - goto exit_sem_err; - - if (mwifiex_register(card, if_ops, (void **)&adapter)) { - pr_err("%s: software init failed\n", __func__); - goto err_init_sw; - } - - adapter->iface_type = iface_type; - adapter->card_sem = sem; - - adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; - adapter->surprise_removed = false; - init_waitqueue_head(&adapter->init_wait_q); - adapter->is_suspended = false; - adapter->hs_activated = false; - init_waitqueue_head(&adapter->hs_activate_wait_q); - init_waitqueue_head(&adapter->cmd_wait_q.wait); - adapter->cmd_wait_q.status = 0; - adapter->scan_wait_q_woken = false; - - if ((num_possible_cpus() > 1) || adapter->iface_type == MWIFIEX_USB) { - adapter->rx_work_enabled = true; - pr_notice("rx work enabled, cpus %d\n", num_possible_cpus()); - } - - adapter->workqueue = - alloc_workqueue("MWIFIEX_WORK_QUEUE", - WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); - if (!adapter->workqueue) - goto err_kmalloc; - - INIT_WORK(&adapter->main_work, mwifiex_main_work_queue); - - if (adapter->rx_work_enabled) { - adapter->rx_workqueue = alloc_workqueue("MWIFIEX_RX_WORK_QUEUE", - WQ_HIGHPRI | - WQ_MEM_RECLAIM | - WQ_UNBOUND, 1); - if (!adapter->rx_workqueue) - goto err_kmalloc; - - INIT_WORK(&adapter->rx_work, mwifiex_rx_work_queue); - } - - /* Register the device. Fill up the private data structure with relevant - information from the card. */ - if (adapter->if_ops.register_dev(adapter)) { - pr_err("%s: failed to register mwifiex device\n", __func__); - goto err_registerdev; - } - - if (mwifiex_init_hw_fw(adapter)) { - pr_err("%s: firmware init failed\n", __func__); - goto err_init_fw; - } - - return 0; - -err_init_fw: - pr_debug("info: %s: unregister device\n", __func__); - if (adapter->if_ops.unregister_dev) - adapter->if_ops.unregister_dev(adapter); - if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) { - pr_debug("info: %s: shutdown mwifiex\n", __func__); - adapter->init_wait_q_woken = false; - - if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) - wait_event_interruptible(adapter->init_wait_q, - adapter->init_wait_q_woken); - } -err_registerdev: - adapter->surprise_removed = true; - mwifiex_terminate_workqueue(adapter); -err_kmalloc: - mwifiex_free_adapter(adapter); - -err_init_sw: - up(sem); - -exit_sem_err: - return -1; -} -EXPORT_SYMBOL_GPL(mwifiex_add_card); - -/* - * This function removes the card. - * - * This function follows the following major steps to remove the device - - * - Stop data traffic - * - Shutdown firmware - * - Remove the logical interfaces - * - Terminate the work queue - * - Unregister the device - * - Free the adapter structure - */ -int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) -{ - struct mwifiex_private *priv = NULL; - int i; - - if (down_interruptible(sem)) - goto exit_sem_err; - - if (!adapter) - goto exit_remove; - - /* We can no longer handle interrupts once we start doing the teardown - * below. */ - if (adapter->if_ops.disable_int) - adapter->if_ops.disable_int(adapter); - - adapter->surprise_removed = true; - - mwifiex_terminate_workqueue(adapter); - - /* Stop data */ - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv && priv->netdev) { - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - } - } - - mwifiex_dbg(adapter, CMD, - "cmd: calling mwifiex_shutdown_drv...\n"); - adapter->init_wait_q_woken = false; - - if (mwifiex_shutdown_drv(adapter) == -EINPROGRESS) - wait_event_interruptible(adapter->init_wait_q, - adapter->init_wait_q_woken); - mwifiex_dbg(adapter, CMD, - "cmd: mwifiex_shutdown_drv done\n"); - if (atomic_read(&adapter->rx_pending) || - atomic_read(&adapter->tx_pending) || - atomic_read(&adapter->cmd_pending)) { - mwifiex_dbg(adapter, ERROR, - "rx_pending=%d, tx_pending=%d,\t" - "cmd_pending=%d\n", - atomic_read(&adapter->rx_pending), - atomic_read(&adapter->tx_pending), - atomic_read(&adapter->cmd_pending)); - } - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - - if (!priv) - continue; - - rtnl_lock(); - if (priv->netdev && - priv->wdev.iftype != NL80211_IFTYPE_UNSPECIFIED) - mwifiex_del_virtual_intf(adapter->wiphy, &priv->wdev); - rtnl_unlock(); - } - - wiphy_unregister(adapter->wiphy); - wiphy_free(adapter->wiphy); - - /* Unregister device */ - mwifiex_dbg(adapter, INFO, - "info: unregister device\n"); - if (adapter->if_ops.unregister_dev) - adapter->if_ops.unregister_dev(adapter); - /* Free adapter structure */ - mwifiex_dbg(adapter, INFO, - "info: free adapter\n"); - mwifiex_free_adapter(adapter); - -exit_remove: - up(sem); -exit_sem_err: - return 0; -} -EXPORT_SYMBOL_GPL(mwifiex_remove_card); - -void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, - const char *fmt, ...) -{ - struct va_format vaf; - va_list args; - - if (!adapter->dev || !(adapter->debug_mask & mask)) - return; - - va_start(args, fmt); - - vaf.fmt = fmt; - vaf.va = &args; - - dev_info(adapter->dev, "%pV", &vaf); - - va_end(args); -} -EXPORT_SYMBOL_GPL(_mwifiex_dbg); - -/* - * This function initializes the module. - * - * The debug FS is also initialized if configured. - */ -static int -mwifiex_init_module(void) -{ -#ifdef CONFIG_DEBUG_FS - mwifiex_debugfs_init(); -#endif - return 0; -} - -/* - * This function cleans up the module. - * - * The debug FS is removed if available. - */ -static void -mwifiex_cleanup_module(void) -{ -#ifdef CONFIG_DEBUG_FS - mwifiex_debugfs_remove(); -#endif -} - -module_init(mwifiex_init_module); -module_exit(mwifiex_cleanup_module); - -MODULE_AUTHOR("Marvell International Ltd."); -MODULE_DESCRIPTION("Marvell WiFi-Ex Driver version " VERSION); -MODULE_VERSION(VERSION); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h deleted file mode 100644 index 3959f1c97f4e..000000000000 --- a/drivers/net/wireless/mwifiex/main.h +++ /dev/null @@ -1,1579 +0,0 @@ -/* - * Marvell Wireless LAN device driver: major data structures and prototypes - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_MAIN_H_ -#define _MWIFIEX_MAIN_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "pcie.h" -#include "usb.h" -#include "sdio.h" - -extern const char driver_version[]; - -struct mwifiex_adapter; -struct mwifiex_private; - -enum { - MWIFIEX_ASYNC_CMD, - MWIFIEX_SYNC_CMD -}; - -#define MWIFIEX_DRIVER_MODE_STA BIT(0) -#define MWIFIEX_DRIVER_MODE_UAP BIT(1) -#define MWIFIEX_DRIVER_MODE_P2P BIT(2) -#define MWIFIEX_DRIVER_MODE_BITMASK (BIT(0) | BIT(1) | BIT(2)) - -#define MWIFIEX_MAX_AP 64 - -#define MWIFIEX_MAX_PKTS_TXQ 16 - -#define MWIFIEX_DEFAULT_WATCHDOG_TIMEOUT (5 * HZ) - -#define MWIFIEX_TIMER_10S 10000 -#define MWIFIEX_TIMER_1S 1000 - -#define MAX_TX_PENDING 100 -#define LOW_TX_PENDING 80 - -#define HIGH_RX_PENDING 50 -#define LOW_RX_PENDING 20 - -#define MWIFIEX_UPLD_SIZE (2312) - -#define MAX_EVENT_SIZE 2048 - -#define ARP_FILTER_MAX_BUF_SIZE 68 - -#define MWIFIEX_KEY_BUFFER_SIZE 16 -#define MWIFIEX_DEFAULT_LISTEN_INTERVAL 10 -#define MWIFIEX_MAX_REGION_CODE 7 - -#define DEFAULT_BCN_AVG_FACTOR 8 -#define DEFAULT_DATA_AVG_FACTOR 8 - -#define FIRST_VALID_CHANNEL 0xff -#define DEFAULT_AD_HOC_CHANNEL 6 -#define DEFAULT_AD_HOC_CHANNEL_A 36 - -#define DEFAULT_BCN_MISS_TIMEOUT 5 - -#define MAX_SCAN_BEACON_BUFFER 8000 - -#define SCAN_BEACON_ENTRY_PAD 6 - -#define MWIFIEX_PASSIVE_SCAN_CHAN_TIME 110 -#define MWIFIEX_ACTIVE_SCAN_CHAN_TIME 30 -#define MWIFIEX_SPECIFIC_SCAN_CHAN_TIME 30 -#define MWIFIEX_DEF_SCAN_CHAN_GAP_TIME 50 - -#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) - -#define MWIFIEX_MAX_TOTAL_SCAN_TIME (MWIFIEX_TIMER_10S - MWIFIEX_TIMER_1S) - -#define RSN_GTK_OUI_OFFSET 2 - -#define MWIFIEX_OUI_NOT_PRESENT 0 -#define MWIFIEX_OUI_PRESENT 1 - -#define PKT_TYPE_MGMT 0xE5 - -/* - * Do not check for data_received for USB, as data_received - * is handled in mwifiex_usb_recv for USB - */ -#define IS_CARD_RX_RCVD(adapter) (adapter->cmd_resp_received || \ - adapter->event_received || \ - adapter->data_received) - -#define MWIFIEX_TYPE_CMD 1 -#define MWIFIEX_TYPE_DATA 0 -#define MWIFIEX_TYPE_AGGR_DATA 10 -#define MWIFIEX_TYPE_EVENT 3 - -#define MAX_BITMAP_RATES_SIZE 18 - -#define MAX_CHANNEL_BAND_BG 14 -#define MAX_CHANNEL_BAND_A 165 - -#define MAX_FREQUENCY_BAND_BG 2484 - -#define MWIFIEX_EVENT_HEADER_LEN 4 -#define MWIFIEX_UAP_EVENT_EXTRA_HEADER 2 - -#define MWIFIEX_TYPE_LEN 4 -#define MWIFIEX_USB_TYPE_CMD 0xF00DFACE -#define MWIFIEX_USB_TYPE_DATA 0xBEADC0DE -#define MWIFIEX_USB_TYPE_EVENT 0xBEEFFACE - -/* Threshold for tx_timeout_cnt before we trigger a card reset */ -#define TX_TIMEOUT_THRESHOLD 6 - -#define MWIFIEX_DRV_INFO_SIZE_MAX 0x40000 - -/* Address alignment */ -#define MWIFIEX_ALIGN_ADDR(p, a) (((long)(p) + (a) - 1) & ~((a) - 1)) - -/** - *enum mwifiex_debug_level - marvell wifi debug level - */ -enum MWIFIEX_DEBUG_LEVEL { - MWIFIEX_DBG_MSG = 0x00000001, - MWIFIEX_DBG_FATAL = 0x00000002, - MWIFIEX_DBG_ERROR = 0x00000004, - MWIFIEX_DBG_DATA = 0x00000008, - MWIFIEX_DBG_CMD = 0x00000010, - MWIFIEX_DBG_EVENT = 0x00000020, - MWIFIEX_DBG_INTR = 0x00000040, - MWIFIEX_DBG_IOCTL = 0x00000080, - - MWIFIEX_DBG_MPA_D = 0x00008000, - MWIFIEX_DBG_DAT_D = 0x00010000, - MWIFIEX_DBG_CMD_D = 0x00020000, - MWIFIEX_DBG_EVT_D = 0x00040000, - MWIFIEX_DBG_FW_D = 0x00080000, - MWIFIEX_DBG_IF_D = 0x00100000, - - MWIFIEX_DBG_ENTRY = 0x10000000, - MWIFIEX_DBG_WARN = 0x20000000, - MWIFIEX_DBG_INFO = 0x40000000, - MWIFIEX_DBG_DUMP = 0x80000000, - - MWIFIEX_DBG_ANY = 0xffffffff -}; - -#define MWIFIEX_DEFAULT_DEBUG_MASK (MWIFIEX_DBG_MSG | \ - MWIFIEX_DBG_FATAL | \ - MWIFIEX_DBG_ERROR) - -__printf(3, 4) -void _mwifiex_dbg(const struct mwifiex_adapter *adapter, int mask, - const char *fmt, ...); -#define mwifiex_dbg(adapter, mask, fmt, ...) \ - _mwifiex_dbg(adapter, MWIFIEX_DBG_##mask, fmt, ##__VA_ARGS__) - -#define DEBUG_DUMP_DATA_MAX_LEN 128 -#define mwifiex_dbg_dump(adapter, dbg_mask, str, buf, len) \ -do { \ - if ((adapter)->debug_mask & MWIFIEX_DBG_##dbg_mask) \ - print_hex_dump(KERN_DEBUG, str, \ - DUMP_PREFIX_OFFSET, 16, 1, \ - buf, len, false); \ -} while (0) - -struct mwifiex_dbg { - u32 num_cmd_host_to_card_failure; - u32 num_cmd_sleep_cfm_host_to_card_failure; - u32 num_tx_host_to_card_failure; - u32 num_event_deauth; - u32 num_event_disassoc; - u32 num_event_link_lost; - u32 num_cmd_deauth; - u32 num_cmd_assoc_success; - u32 num_cmd_assoc_failure; - u32 num_tx_timeout; - u16 timeout_cmd_id; - u16 timeout_cmd_act; - u16 last_cmd_id[DBG_CMD_NUM]; - u16 last_cmd_act[DBG_CMD_NUM]; - u16 last_cmd_index; - u16 last_cmd_resp_id[DBG_CMD_NUM]; - u16 last_cmd_resp_index; - u16 last_event[DBG_CMD_NUM]; - u16 last_event_index; -}; - -enum MWIFIEX_HARDWARE_STATUS { - MWIFIEX_HW_STATUS_READY, - MWIFIEX_HW_STATUS_INITIALIZING, - MWIFIEX_HW_STATUS_INIT_DONE, - MWIFIEX_HW_STATUS_RESET, - MWIFIEX_HW_STATUS_CLOSING, - MWIFIEX_HW_STATUS_NOT_READY -}; - -enum MWIFIEX_802_11_POWER_MODE { - MWIFIEX_802_11_POWER_MODE_CAM, - MWIFIEX_802_11_POWER_MODE_PSP -}; - -struct mwifiex_tx_param { - u32 next_pkt_len; -}; - -enum MWIFIEX_PS_STATE { - PS_STATE_AWAKE, - PS_STATE_PRE_SLEEP, - PS_STATE_SLEEP_CFM, - PS_STATE_SLEEP -}; - -enum mwifiex_iface_type { - MWIFIEX_SDIO, - MWIFIEX_PCIE, - MWIFIEX_USB -}; - -struct mwifiex_add_ba_param { - u32 tx_win_size; - u32 rx_win_size; - u32 timeout; - u8 tx_amsdu; - u8 rx_amsdu; -}; - -struct mwifiex_tx_aggr { - u8 ampdu_user; - u8 ampdu_ap; - u8 amsdu; -}; - -enum mwifiex_ba_status { - BA_SETUP_NONE = 0, - BA_SETUP_INPROGRESS, - BA_SETUP_COMPLETE -}; - -struct mwifiex_ra_list_tbl { - struct list_head list; - struct sk_buff_head skb_head; - u8 ra[ETH_ALEN]; - u32 is_11n_enabled; - u16 max_amsdu; - u16 ba_pkt_count; - u8 ba_packet_thr; - enum mwifiex_ba_status ba_status; - u8 amsdu_in_ampdu; - u16 total_pkt_count; - bool tdls_link; - bool tx_paused; -}; - -struct mwifiex_tid_tbl { - struct list_head ra_list; -}; - -#define WMM_HIGHEST_PRIORITY 7 -#define HIGH_PRIO_TID 7 -#define LOW_PRIO_TID 0 - -struct mwifiex_wmm_desc { - struct mwifiex_tid_tbl tid_tbl_ptr[MAX_NUM_TID]; - u32 packets_out[MAX_NUM_TID]; - u32 pkts_paused[MAX_NUM_TID]; - /* spin lock to protect ra_list */ - spinlock_t ra_list_spinlock; - struct mwifiex_wmm_ac_status ac_status[IEEE80211_NUM_ACS]; - enum mwifiex_wmm_ac_e ac_down_graded_vals[IEEE80211_NUM_ACS]; - u32 drv_pkt_delay_max; - u8 queue_priority[IEEE80211_NUM_ACS]; - u32 user_pri_pkt_tx_ctrl[WMM_HIGHEST_PRIORITY + 1]; /* UP: 0 to 7 */ - /* Number of transmit packets queued */ - atomic_t tx_pkts_queued; - /* Tracks highest priority with a packet queued */ - atomic_t highest_queued_prio; -}; - -struct mwifiex_802_11_security { - u8 wpa_enabled; - u8 wpa2_enabled; - u8 wapi_enabled; - u8 wapi_key_on; - u8 wep_enabled; - u32 authentication_mode; - u8 is_authtype_auto; - u32 encryption_mode; -}; - -struct ieee_types_header { - u8 element_id; - u8 len; -} __packed; - -struct ieee_types_vendor_specific { - struct ieee_types_vendor_header vend_hdr; - u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_vendor_header)]; -} __packed; - -struct ieee_types_generic { - struct ieee_types_header ieee_hdr; - u8 data[IEEE_MAX_IE_SIZE - sizeof(struct ieee_types_header)]; -} __packed; - -struct ieee_types_bss_co_2040 { - struct ieee_types_header ieee_hdr; - u8 bss_2040co; -} __packed; - -struct ieee_types_extcap { - struct ieee_types_header ieee_hdr; - u8 ext_capab[8]; -} __packed; - -struct ieee_types_vht_cap { - struct ieee_types_header ieee_hdr; - struct ieee80211_vht_cap vhtcap; -} __packed; - -struct ieee_types_vht_oper { - struct ieee_types_header ieee_hdr; - struct ieee80211_vht_operation vhtoper; -} __packed; - -struct ieee_types_aid { - struct ieee_types_header ieee_hdr; - u16 aid; -} __packed; - -struct mwifiex_bssdescriptor { - u8 mac_address[ETH_ALEN]; - struct cfg80211_ssid ssid; - u32 privacy; - s32 rssi; - u32 channel; - u32 freq; - u16 beacon_period; - u8 erp_flags; - u32 bss_mode; - u8 supported_rates[MWIFIEX_SUPPORTED_RATES]; - u8 data_rates[MWIFIEX_SUPPORTED_RATES]; - /* Network band. - * BAND_B(0x01): 'b' band - * BAND_G(0x02): 'g' band - * BAND_A(0X04): 'a' band - */ - u16 bss_band; - u64 fw_tsf; - u64 timestamp; - union ieee_types_phy_param_set phy_param_set; - union ieee_types_ss_param_set ss_param_set; - u16 cap_info_bitmap; - struct ieee_types_wmm_parameter wmm_ie; - u8 disable_11n; - struct ieee80211_ht_cap *bcn_ht_cap; - u16 ht_cap_offset; - struct ieee80211_ht_operation *bcn_ht_oper; - u16 ht_info_offset; - u8 *bcn_bss_co_2040; - u16 bss_co_2040_offset; - u8 *bcn_ext_cap; - u16 ext_cap_offset; - struct ieee80211_vht_cap *bcn_vht_cap; - u16 vht_cap_offset; - struct ieee80211_vht_operation *bcn_vht_oper; - u16 vht_info_offset; - struct ieee_types_oper_mode_ntf *oper_mode; - u16 oper_mode_offset; - u8 disable_11ac; - struct ieee_types_vendor_specific *bcn_wpa_ie; - u16 wpa_offset; - struct ieee_types_generic *bcn_rsn_ie; - u16 rsn_offset; - struct ieee_types_generic *bcn_wapi_ie; - u16 wapi_offset; - u8 *beacon_buf; - u32 beacon_buf_size; - u8 sensed_11h; - u8 local_constraint; - u8 chan_sw_ie_present; -}; - -struct mwifiex_current_bss_params { - struct mwifiex_bssdescriptor bss_descriptor; - u8 wmm_enabled; - u8 wmm_uapsd_enabled; - u8 band; - u32 num_of_rates; - u8 data_rates[MWIFIEX_SUPPORTED_RATES]; -}; - -struct mwifiex_sleep_params { - u16 sp_error; - u16 sp_offset; - u16 sp_stable_time; - u8 sp_cal_control; - u8 sp_ext_sleep_clk; - u16 sp_reserved; -}; - -struct mwifiex_sleep_period { - u16 period; - u16 reserved; -}; - -struct mwifiex_wep_key { - u32 length; - u32 key_index; - u32 key_length; - u8 key_material[MWIFIEX_KEY_BUFFER_SIZE]; -}; - -#define MAX_REGION_CHANNEL_NUM 2 - -struct mwifiex_chan_freq_power { - u16 channel; - u32 freq; - u16 max_tx_power; - u8 unsupported; -}; - -enum state_11d_t { - DISABLE_11D = 0, - ENABLE_11D = 1, -}; - -#define MWIFIEX_MAX_TRIPLET_802_11D 83 - -struct mwifiex_802_11d_domain_reg { - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - u8 no_of_triplet; - struct ieee80211_country_ie_triplet - triplet[MWIFIEX_MAX_TRIPLET_802_11D]; -}; - -struct mwifiex_vendor_spec_cfg_ie { - u16 mask; - u16 flag; - u8 ie[MWIFIEX_MAX_VSIE_LEN]; -}; - -struct wps { - u8 session_enable; -}; - -struct mwifiex_roc_cfg { - u64 cookie; - struct ieee80211_channel chan; -}; - -#define MWIFIEX_FW_DUMP_IDX 0xff -#define MWIFIEX_DRV_INFO_IDX 20 -#define FW_DUMP_MAX_NAME_LEN 8 -#define FW_DUMP_HOST_READY 0xEE -#define FW_DUMP_DONE 0xFF -#define FW_DUMP_READ_DONE 0xFE - -struct memory_type_mapping { - u8 mem_name[FW_DUMP_MAX_NAME_LEN]; - u8 *mem_ptr; - u32 mem_size; - u8 done_flag; -}; - -enum rdwr_status { - RDWR_STATUS_SUCCESS = 0, - RDWR_STATUS_FAILURE = 1, - RDWR_STATUS_DONE = 2 -}; - -enum mwifiex_iface_work_flags { - MWIFIEX_IFACE_WORK_DEVICE_DUMP, - MWIFIEX_IFACE_WORK_CARD_RESET, -}; - -struct mwifiex_private { - struct mwifiex_adapter *adapter; - u8 bss_type; - u8 bss_role; - u8 bss_priority; - u8 bss_num; - u8 bss_started; - u8 frame_type; - u8 curr_addr[ETH_ALEN]; - u8 media_connected; - u8 port_open; - u8 usb_port; - u32 num_tx_timeout; - /* track consecutive timeout */ - u8 tx_timeout_cnt; - struct net_device *netdev; - struct net_device_stats stats; - u16 curr_pkt_filter; - u32 bss_mode; - u32 pkt_tx_ctrl; - u16 tx_power_level; - u8 max_tx_power_level; - u8 min_tx_power_level; - u8 tx_rate; - u8 tx_htinfo; - u8 rxpd_htinfo; - u8 rxpd_rate; - u16 rate_bitmap; - u16 bitmap_rates[MAX_BITMAP_RATES_SIZE]; - u32 data_rate; - u8 is_data_rate_auto; - u16 bcn_avg_factor; - u16 data_avg_factor; - s16 data_rssi_last; - s16 data_nf_last; - s16 data_rssi_avg; - s16 data_nf_avg; - s16 bcn_rssi_last; - s16 bcn_nf_last; - s16 bcn_rssi_avg; - s16 bcn_nf_avg; - struct mwifiex_bssdescriptor *attempted_bss_desc; - struct cfg80211_ssid prev_ssid; - u8 prev_bssid[ETH_ALEN]; - struct mwifiex_current_bss_params curr_bss_params; - u16 beacon_period; - u8 dtim_period; - u16 listen_interval; - u16 atim_window; - u8 adhoc_channel; - u8 adhoc_is_link_sensed; - u8 adhoc_state; - struct mwifiex_802_11_security sec_info; - struct mwifiex_wep_key wep_key[NUM_WEP_KEYS]; - u16 wep_key_curr_index; - u8 wpa_ie[256]; - u8 wpa_ie_len; - u8 wpa_is_gtk_set; - struct host_cmd_ds_802_11_key_material aes_key; - struct host_cmd_ds_802_11_key_material_v2 aes_key_v2; - u8 wapi_ie[256]; - u8 wapi_ie_len; - u8 *wps_ie; - u8 wps_ie_len; - u8 wmm_required; - u8 wmm_enabled; - u8 wmm_qosinfo; - struct mwifiex_wmm_desc wmm; - atomic_t wmm_tx_pending[IEEE80211_NUM_ACS]; - struct list_head sta_list; - /* spin lock for associated station/TDLS peers list */ - spinlock_t sta_list_spinlock; - struct list_head auto_tdls_list; - /* spin lock for auto TDLS peer list */ - spinlock_t auto_tdls_lock; - struct list_head tx_ba_stream_tbl_ptr; - /* spin lock for tx_ba_stream_tbl_ptr queue */ - spinlock_t tx_ba_stream_tbl_lock; - struct mwifiex_tx_aggr aggr_prio_tbl[MAX_NUM_TID]; - struct mwifiex_add_ba_param add_ba_param; - u16 rx_seq[MAX_NUM_TID]; - u8 tos_to_tid_inv[MAX_NUM_TID]; - struct list_head rx_reorder_tbl_ptr; - /* spin lock for rx_reorder_tbl_ptr queue */ - spinlock_t rx_reorder_tbl_lock; - /* spin lock for Rx packets */ - spinlock_t rx_pkt_lock; - -#define MWIFIEX_ASSOC_RSP_BUF_SIZE 500 - u8 assoc_rsp_buf[MWIFIEX_ASSOC_RSP_BUF_SIZE]; - u32 assoc_rsp_size; - -#define MWIFIEX_GENIE_BUF_SIZE 256 - u8 gen_ie_buf[MWIFIEX_GENIE_BUF_SIZE]; - u8 gen_ie_buf_len; - - struct mwifiex_vendor_spec_cfg_ie vs_ie[MWIFIEX_MAX_VSIE_NUM]; - -#define MWIFIEX_ASSOC_TLV_BUF_SIZE 256 - u8 assoc_tlv_buf[MWIFIEX_ASSOC_TLV_BUF_SIZE]; - u8 assoc_tlv_buf_len; - - u8 *curr_bcn_buf; - u32 curr_bcn_size; - /* spin lock for beacon buffer */ - spinlock_t curr_bcn_buf_lock; - struct wireless_dev wdev; - struct mwifiex_chan_freq_power cfp; - char version_str[128]; -#ifdef CONFIG_DEBUG_FS - struct dentry *dfs_dev_dir; -#endif - u16 current_key_index; - struct semaphore async_sem; - struct cfg80211_scan_request *scan_request; - u8 cfg_bssid[6]; - struct wps wps; - u8 scan_block; - s32 cqm_rssi_thold; - u32 cqm_rssi_hyst; - u8 subsc_evt_rssi_state; - struct mwifiex_ds_misc_subsc_evt async_subsc_evt_storage; - struct mwifiex_ie mgmt_ie[MAX_MGMT_IE_INDEX]; - u16 beacon_idx; - u16 proberesp_idx; - u16 assocresp_idx; - u16 gen_idx; - u8 ap_11n_enabled; - u8 ap_11ac_enabled; - u32 mgmt_frame_mask; - struct mwifiex_roc_cfg roc_cfg; - bool scan_aborting; - u8 csa_chan; - unsigned long csa_expire_time; - u8 del_list_idx; - bool hs2_enabled; - struct mwifiex_uap_bss_param bss_cfg; - struct cfg80211_chan_def bss_chandef; - struct station_parameters *sta_params; - struct sk_buff_head tdls_txq; - u8 check_tdls_tx; - struct timer_list auto_tdls_timer; - bool auto_tdls_timer_active; - struct idr ack_status_frames; - /* spin lock for ack status */ - spinlock_t ack_status_lock; - /** rx histogram data */ - struct mwifiex_histogram_data *hist_data; - struct cfg80211_chan_def dfs_chandef; - struct workqueue_struct *dfs_cac_workqueue; - struct delayed_work dfs_cac_work; - struct timer_list dfs_chan_switch_timer; - struct workqueue_struct *dfs_chan_sw_workqueue; - struct delayed_work dfs_chan_sw_work; - struct cfg80211_beacon_data beacon_after; - struct mwifiex_11h_intf_state state_11h; - struct mwifiex_ds_mem_rw mem_rw; - struct sk_buff_head bypass_txq; - struct mwifiex_user_scan_chan hidden_chan[MWIFIEX_USER_SCAN_CHAN_MAX]; -}; - - -struct mwifiex_tx_ba_stream_tbl { - struct list_head list; - int tid; - u8 ra[ETH_ALEN]; - enum mwifiex_ba_status ba_status; - u8 amsdu; -}; - -struct mwifiex_rx_reorder_tbl; - -struct reorder_tmr_cnxt { - struct timer_list timer; - struct mwifiex_rx_reorder_tbl *ptr; - struct mwifiex_private *priv; - u8 timer_is_set; -}; - -struct mwifiex_rx_reorder_tbl { - struct list_head list; - int tid; - u8 ta[ETH_ALEN]; - int init_win; - int start_win; - int win_size; - void **rx_reorder_ptr; - struct reorder_tmr_cnxt timer_context; - u8 amsdu; - u8 flags; -}; - -struct mwifiex_bss_prio_node { - struct list_head list; - struct mwifiex_private *priv; -}; - -struct mwifiex_bss_prio_tbl { - struct list_head bss_prio_head; - /* spin lock for bss priority */ - spinlock_t bss_prio_lock; - struct mwifiex_bss_prio_node *bss_prio_cur; -}; - -struct cmd_ctrl_node { - struct list_head list; - struct mwifiex_private *priv; - u32 cmd_oid; - u32 cmd_flag; - struct sk_buff *cmd_skb; - struct sk_buff *resp_skb; - void *data_buf; - u32 wait_q_enabled; - struct sk_buff *skb; - u8 *condition; - u8 cmd_wait_q_woken; -}; - -struct mwifiex_bss_priv { - u8 band; - u64 fw_tsf; -}; - -struct mwifiex_tdls_capab { - __le16 capab; - u8 rates[32]; - u8 rates_len; - u8 qos_info; - u8 coex_2040; - u16 aid; - struct ieee80211_ht_cap ht_capb; - struct ieee80211_ht_operation ht_oper; - struct ieee_types_extcap extcap; - struct ieee_types_generic rsn_ie; - struct ieee80211_vht_cap vhtcap; - struct ieee80211_vht_operation vhtoper; -}; - -struct mwifiex_station_stats { - u64 last_rx; - s8 rssi; - u64 rx_bytes; - u64 tx_bytes; - u32 rx_packets; - u32 tx_packets; - u32 tx_failed; - u8 last_tx_rate; - u8 last_tx_htinfo; -}; - -/* This is AP/TDLS specific structure which stores information - * about associated/peer STA - */ -struct mwifiex_sta_node { - struct list_head list; - u8 mac_addr[ETH_ALEN]; - u8 is_wmm_enabled; - u8 is_11n_enabled; - u8 is_11ac_enabled; - u8 ampdu_sta[MAX_NUM_TID]; - u16 rx_seq[MAX_NUM_TID]; - u16 max_amsdu; - u8 tdls_status; - struct mwifiex_tdls_capab tdls_cap; - struct mwifiex_station_stats stats; - u8 tx_pause; -}; - -struct mwifiex_auto_tdls_peer { - struct list_head list; - u8 mac_addr[ETH_ALEN]; - u8 tdls_status; - int rssi; - long rssi_jiffies; - u8 failure_count; - u8 do_discover; - u8 do_setup; -}; - -struct mwifiex_if_ops { - int (*init_if) (struct mwifiex_adapter *); - void (*cleanup_if) (struct mwifiex_adapter *); - int (*check_fw_status) (struct mwifiex_adapter *, u32); - int (*prog_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); - int (*register_dev) (struct mwifiex_adapter *); - void (*unregister_dev) (struct mwifiex_adapter *); - int (*enable_int) (struct mwifiex_adapter *); - void (*disable_int) (struct mwifiex_adapter *); - int (*process_int_status) (struct mwifiex_adapter *); - int (*host_to_card) (struct mwifiex_adapter *, u8, struct sk_buff *, - struct mwifiex_tx_param *); - int (*wakeup) (struct mwifiex_adapter *); - int (*wakeup_complete) (struct mwifiex_adapter *); - - /* Interface specific functions */ - void (*update_mp_end_port) (struct mwifiex_adapter *, u16); - void (*cleanup_mpa_buf) (struct mwifiex_adapter *); - int (*cmdrsp_complete) (struct mwifiex_adapter *, struct sk_buff *); - int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); - int (*init_fw_port) (struct mwifiex_adapter *); - int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); - void (*card_reset) (struct mwifiex_adapter *); - int (*reg_dump)(struct mwifiex_adapter *, char *); - void (*device_dump)(struct mwifiex_adapter *); - int (*clean_pcie_ring) (struct mwifiex_adapter *adapter); - void (*iface_work)(struct work_struct *work); - void (*submit_rem_rx_urbs)(struct mwifiex_adapter *adapter); - void (*deaggr_pkt)(struct mwifiex_adapter *, struct sk_buff *); - void (*multi_port_resync)(struct mwifiex_adapter *); - bool (*is_port_ready)(struct mwifiex_private *); -}; - -struct mwifiex_adapter { - u8 iface_type; - unsigned int debug_mask; - struct mwifiex_iface_comb iface_limit; - struct mwifiex_iface_comb curr_iface_comb; - struct mwifiex_private *priv[MWIFIEX_MAX_BSS_NUM]; - u8 priv_num; - const struct firmware *firmware; - char fw_name[32]; - int winner; - struct device *dev; - struct wiphy *wiphy; - u8 perm_addr[ETH_ALEN]; - bool surprise_removed; - u32 fw_release_number; - u16 init_wait_q_woken; - wait_queue_head_t init_wait_q; - void *card; - struct mwifiex_if_ops if_ops; - atomic_t bypass_tx_pending; - atomic_t rx_pending; - atomic_t tx_pending; - atomic_t cmd_pending; - struct workqueue_struct *workqueue; - struct work_struct main_work; - struct workqueue_struct *rx_workqueue; - struct work_struct rx_work; - struct workqueue_struct *dfs_workqueue; - struct work_struct dfs_work; - bool rx_work_enabled; - bool rx_processing; - bool delay_main_work; - bool rx_locked; - bool main_locked; - struct mwifiex_bss_prio_tbl bss_prio_tbl[MWIFIEX_MAX_BSS_NUM]; - /* spin lock for init/shutdown */ - spinlock_t mwifiex_lock; - /* spin lock for main process */ - spinlock_t main_proc_lock; - u32 mwifiex_processing; - u8 more_task_flag; - u16 tx_buf_size; - u16 curr_tx_buf_size; - /* sdio single port rx aggregation capability */ - bool host_disable_sdio_rx_aggr; - bool sdio_rx_aggr_enable; - u16 sdio_rx_block_size; - u32 ioport; - enum MWIFIEX_HARDWARE_STATUS hw_status; - u16 number_of_antenna; - u32 fw_cap_info; - /* spin lock for interrupt handling */ - spinlock_t int_lock; - u8 int_status; - u32 event_cause; - struct sk_buff *event_skb; - u8 upld_buf[MWIFIEX_UPLD_SIZE]; - u8 data_sent; - u8 cmd_sent; - u8 cmd_resp_received; - u8 event_received; - u8 data_received; - u16 seq_num; - struct cmd_ctrl_node *cmd_pool; - struct cmd_ctrl_node *curr_cmd; - /* spin lock for command */ - spinlock_t mwifiex_cmd_lock; - u8 is_cmd_timedout; - u16 last_init_cmd; - struct timer_list cmd_timer; - struct list_head cmd_free_q; - /* spin lock for cmd_free_q */ - spinlock_t cmd_free_q_lock; - struct list_head cmd_pending_q; - /* spin lock for cmd_pending_q */ - spinlock_t cmd_pending_q_lock; - struct list_head scan_pending_q; - /* spin lock for scan_pending_q */ - spinlock_t scan_pending_q_lock; - /* spin lock for RX processing routine */ - spinlock_t rx_proc_lock; - struct sk_buff_head tx_data_q; - atomic_t tx_queued; - u32 scan_processing; - u16 region_code; - struct mwifiex_802_11d_domain_reg domain_reg; - u16 scan_probes; - u32 scan_mode; - u16 specific_scan_time; - u16 active_scan_time; - u16 passive_scan_time; - u16 scan_chan_gap_time; - u8 fw_bands; - u8 adhoc_start_band; - u8 config_bands; - struct mwifiex_chan_scan_param_set *scan_channels; - u8 tx_lock_flag; - struct mwifiex_sleep_params sleep_params; - struct mwifiex_sleep_period sleep_period; - u16 ps_mode; - u32 ps_state; - u8 need_to_wakeup; - u16 multiple_dtim; - u16 local_listen_interval; - u16 null_pkt_interval; - struct sk_buff *sleep_cfm; - u16 bcn_miss_time_out; - u16 adhoc_awake_period; - u8 is_deep_sleep; - u8 delay_null_pkt; - u16 delay_to_ps; - u16 enhanced_ps_mode; - u8 pm_wakeup_card_req; - u16 gen_null_pkt; - u16 pps_uapsd_mode; - u32 pm_wakeup_fw_try; - struct timer_list wakeup_timer; - u8 is_hs_configured; - struct mwifiex_hs_config_param hs_cfg; - u8 hs_activated; - u16 hs_activate_wait_q_woken; - wait_queue_head_t hs_activate_wait_q; - bool is_suspended; - bool hs_enabling; - u8 event_body[MAX_EVENT_SIZE]; - u32 hw_dot_11n_dev_cap; - u8 hw_dev_mcs_support; - u8 user_dev_mcs_support; - u8 adhoc_11n_enabled; - u8 sec_chan_offset; - struct mwifiex_dbg dbg; - u8 arp_filter[ARP_FILTER_MAX_BUF_SIZE]; - u32 arp_filter_size; - struct mwifiex_wait_queue cmd_wait_q; - u8 scan_wait_q_woken; - spinlock_t queue_lock; /* lock for tx queues */ - u8 country_code[IEEE80211_COUNTRY_STRING_LEN]; - u16 max_mgmt_ie_index; - const struct firmware *cal_data; - struct device_node *dt_node; - - /* 11AC */ - u32 is_hw_11ac_capable; - u32 hw_dot_11ac_dev_cap; - u32 hw_dot_11ac_mcs_support; - u32 usr_dot_11ac_dev_cap_bg; - u32 usr_dot_11ac_dev_cap_a; - u32 usr_dot_11ac_mcs_support; - - atomic_t pending_bridged_pkts; - struct semaphore *card_sem; - bool ext_scan; - u8 fw_api_ver; - u8 key_api_major_ver, key_api_minor_ver; - struct memory_type_mapping *mem_type_mapping_tbl; - u8 num_mem_types; - void *drv_info_dump; - u32 drv_info_size; - bool scan_chan_gap_enabled; - struct sk_buff_head rx_data_q; - struct mwifiex_chan_stats *chan_stats; - u32 num_in_chan_stats; - int survey_idx; - bool auto_tdls; - u8 coex_scan; - u8 coex_min_scan_time; - u8 coex_max_scan_time; - u8 coex_win_size; - u8 coex_tx_win_size; - u8 coex_rx_win_size; - bool drcs_enabled; - u8 active_scan_triggered; - bool usb_mc_status; - bool usb_mc_setup; -}; - -void mwifiex_process_tx_queue(struct mwifiex_adapter *adapter); - -int mwifiex_init_lock_list(struct mwifiex_adapter *adapter); - -void mwifiex_set_trans_start(struct net_device *dev); - -void mwifiex_stop_net_dev_queue(struct net_device *netdev, - struct mwifiex_adapter *adapter); - -void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, - struct mwifiex_adapter *adapter); - -int mwifiex_init_priv(struct mwifiex_private *priv); -void mwifiex_free_priv(struct mwifiex_private *priv); - -int mwifiex_init_fw(struct mwifiex_adapter *adapter); - -int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter); - -int mwifiex_shutdown_drv(struct mwifiex_adapter *adapter); - -int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter); - -int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *); - -int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb); - -int mwifiex_process_mgmt_packet(struct mwifiex_private *priv, - struct sk_buff *skb); - -int mwifiex_process_event(struct mwifiex_adapter *adapter); - -int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node); - -int mwifiex_send_cmd(struct mwifiex_private *priv, u16 cmd_no, - u16 cmd_action, u32 cmd_oid, void *data_buf, bool sync); - -void mwifiex_cmd_timeout_func(unsigned long function_context); - -int mwifiex_get_debug_info(struct mwifiex_private *, - struct mwifiex_debug_info *); - -int mwifiex_alloc_cmd_buffer(struct mwifiex_adapter *adapter); -int mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter); -void mwifiex_cancel_all_pending_cmd(struct mwifiex_adapter *adapter); -void mwifiex_cancel_pending_ioctl(struct mwifiex_adapter *adapter); - -void mwifiex_insert_cmd_to_free_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node); -void mwifiex_recycle_cmd_node(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node); - -void mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node, - u32 addtail); - -int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter); -int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter); -int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, - struct sk_buff *skb); -int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, - struct mwifiex_tx_param *tx_param); -int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags); -int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb, int aggr, int status); -void mwifiex_clean_txrx(struct mwifiex_private *priv); -u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv); -void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter); -void mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *, u8 *, - u32); -int mwifiex_cmd_enh_power_mode(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, uint16_t ps_bitmap, - struct mwifiex_ds_auto_ds *auto_ds); -int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct mwifiex_ds_pm_cfg *pm_cfg); -void mwifiex_process_hs_config(struct mwifiex_adapter *adapter); -void mwifiex_hs_activated_event(struct mwifiex_private *priv, - u8 activated); -int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, - int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg); -int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_process_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb); -int mwifiex_sta_prepare_cmd(struct mwifiex_private *, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, - void *data_buf, void *cmd_buf); -int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, - void *data_buf, void *cmd_buf); -int mwifiex_process_sta_cmdresp(struct mwifiex_private *, u16 cmdresp_no, - struct host_cmd_ds_command *resp); -int mwifiex_process_sta_rx_packet(struct mwifiex_private *, - struct sk_buff *skb); -int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb); -int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, - struct sk_buff *skb); -int mwifiex_process_sta_event(struct mwifiex_private *); -int mwifiex_process_uap_event(struct mwifiex_private *); -void mwifiex_delete_all_station_list(struct mwifiex_private *priv); -void mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, - const u8 *ra_addr); -void *mwifiex_process_sta_txpd(struct mwifiex_private *, struct sk_buff *skb); -void *mwifiex_process_uap_txpd(struct mwifiex_private *, struct sk_buff *skb); -int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init); -int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, - struct mwifiex_scan_cmd_config *scan_cfg); -void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, - struct cmd_ctrl_node *cmd_node); -int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -s32 mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2); -int mwifiex_associate(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_cmd_802_11_associate(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -void mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason); -u8 mwifiex_band_to_radio_type(u8 band); -int mwifiex_deauthenticate(struct mwifiex_private *priv, u8 *mac); -void mwifiex_deauthenticate_all(struct mwifiex_adapter *adapter); -int mwifiex_adhoc_start(struct mwifiex_private *priv, - struct cfg80211_ssid *adhoc_ssid); -int mwifiex_adhoc_join(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct cfg80211_ssid *req_ssid); -int mwifiex_cmd_802_11_ad_hoc_join(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd); -struct mwifiex_chan_freq_power *mwifiex_get_cfp(struct mwifiex_private *priv, - u8 band, u16 channel, u32 freq); -u32 mwifiex_index_to_data_rate(struct mwifiex_private *priv, - u8 index, u8 ht_info); -u32 mwifiex_index_to_acs_data_rate(struct mwifiex_private *priv, - u8 index, u8 ht_info); -u32 mwifiex_find_freq_from_band_chan(u8, u8); -int mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, u16 vsie_mask, - u8 **buffer); -u32 mwifiex_get_active_data_rates(struct mwifiex_private *priv, - u8 *rates); -u32 mwifiex_get_supported_rates(struct mwifiex_private *priv, u8 *rates); -u32 mwifiex_get_rates_from_cfg80211(struct mwifiex_private *priv, - u8 *rates, u8 radio_type); -u8 mwifiex_is_rate_auto(struct mwifiex_private *priv); -extern u16 region_code_index[MWIFIEX_MAX_REGION_CODE]; -void mwifiex_save_curr_bcn(struct mwifiex_private *priv); -void mwifiex_free_curr_bcn(struct mwifiex_private *priv); -int mwifiex_cmd_get_hw_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd); -int mwifiex_ret_get_hw_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int is_command_pending(struct mwifiex_adapter *adapter); -void mwifiex_init_priv_params(struct mwifiex_private *priv, - struct net_device *dev); -int mwifiex_set_secure_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_config, - struct cfg80211_ap_settings *params); -void mwifiex_set_ht_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_vht_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_tpc_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_vht_width(struct mwifiex_private *priv, - enum nl80211_chan_width width, - bool ap_11ac_disable); -void -mwifiex_set_wmm_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params); -void mwifiex_set_ba_params(struct mwifiex_private *priv); - -void mwifiex_update_ampdu_txwinsize(struct mwifiex_adapter *pmadapter); -void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv, - struct sk_buff *event_skb); - -void mwifiex_set_11ac_ba_params(struct mwifiex_private *priv); -int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf); -int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp); -int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, - void *buf); - -/* - * This function checks if the queuing is RA based or not. - */ -static inline u8 -mwifiex_queuing_ra_based(struct mwifiex_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 == NL80211_IFTYPE_STATION) && - (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA)) - return false; - - return true; -} - -/* - * This function copies rates. - */ -static inline u32 -mwifiex_copy_rates(u8 *dest, u32 pos, u8 *src, int len) -{ - int i; - - for (i = 0; i < len && src[i]; i++, pos++) { - if (pos >= MWIFIEX_SUPPORTED_RATES) - break; - dest[pos] = src[i]; - } - - return pos; -} - -/* - * This function returns the correct private structure pointer based - * upon the BSS type and BSS number. - */ -static inline struct mwifiex_private * -mwifiex_get_priv_by_id(struct mwifiex_adapter *adapter, - u8 bss_num, u8 bss_type) -{ - int i; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - if ((adapter->priv[i]->bss_num == bss_num) && - (adapter->priv[i]->bss_type == bss_type)) - break; - } - } - return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); -} - -/* - * This function returns the first available private structure pointer - * based upon the BSS role. - */ -static inline struct mwifiex_private * -mwifiex_get_priv(struct mwifiex_adapter *adapter, - enum mwifiex_bss_role bss_role) -{ - int i; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - if (bss_role == MWIFIEX_BSS_ROLE_ANY || - GET_BSS_ROLE(adapter->priv[i]) == bss_role) - break; - } - } - - return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); -} - -/* - * This function returns the first available unused private structure pointer. - */ -static inline struct mwifiex_private * -mwifiex_get_unused_priv(struct mwifiex_adapter *adapter) -{ - int i; - - for (i = 0; i < adapter->priv_num; i++) { - if (adapter->priv[i]) { - if (adapter->priv[i]->bss_mode == - NL80211_IFTYPE_UNSPECIFIED) - break; - } - } - - return ((i < adapter->priv_num) ? adapter->priv[i] : NULL); -} - -/* - * This function returns the driver private structure of a network device. - */ -static inline struct mwifiex_private * -mwifiex_netdev_get_priv(struct net_device *dev) -{ - return (struct mwifiex_private *) (*(unsigned long *) netdev_priv(dev)); -} - -/* - * This function checks if a skb holds a management frame. - */ -static inline bool mwifiex_is_skb_mgmt_frame(struct sk_buff *skb) -{ - return (le32_to_cpu(*(__le32 *)skb->data) == PKT_TYPE_MGMT); -} - -/* This function retrieves channel closed for operation by Channel - * Switch Announcement. - */ -static inline u8 -mwifiex_11h_get_csa_closed_channel(struct mwifiex_private *priv) -{ - if (!priv->csa_chan) - return 0; - - /* Clear csa channel, if DFS channel move time has passed */ - if (time_after(jiffies, priv->csa_expire_time)) { - priv->csa_chan = 0; - priv->csa_expire_time = 0; - } - - return priv->csa_chan; -} - -static inline u8 mwifiex_is_any_intf_active(struct mwifiex_private *priv) -{ - struct mwifiex_private *priv_num; - int i; - - for (i = 0; i < priv->adapter->priv_num; i++) { - priv_num = priv->adapter->priv[i]; - if (priv_num) { - if ((GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_UAP && - priv_num->bss_started) || - (GET_BSS_ROLE(priv_num) == MWIFIEX_BSS_ROLE_STA && - priv_num->media_connected)) - return 1; - } - } - - return 0; -} - -static inline u8 mwifiex_is_tdls_link_setup(u8 status) -{ - switch (status) { - case TDLS_SETUP_COMPLETE: - case TDLS_CHAN_SWITCHING: - case TDLS_IN_BASE_CHAN: - case TDLS_IN_OFF_CHAN: - return true; - default: - break; - } - - return false; -} - -int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, - u32 func_init_shutdown); -int mwifiex_add_card(void *, struct semaphore *, struct mwifiex_if_ops *, u8); -int mwifiex_remove_card(struct mwifiex_adapter *, struct semaphore *); - -void mwifiex_get_version(struct mwifiex_adapter *adapter, char *version, - int maxlen); -int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, - struct mwifiex_multicast_list *mcast_list); -int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, - struct net_device *dev); -int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_queued); -int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, - struct cfg80211_ssid *req_ssid); -int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type); -int mwifiex_enable_hs(struct mwifiex_adapter *adapter); -int mwifiex_disable_auto_ds(struct mwifiex_private *priv); -int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate); -int mwifiex_request_scan(struct mwifiex_private *priv, - struct cfg80211_ssid *req_ssid); -int mwifiex_scan_networks(struct mwifiex_private *priv, - const struct mwifiex_user_scan_cfg *user_scan_in); -int mwifiex_set_radio(struct mwifiex_private *priv, u8 option); - -int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, - const u8 *key, int key_len, u8 key_index, - const u8 *mac_addr, int disable); - -int mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len); - -int mwifiex_get_ver_ext(struct mwifiex_private *priv); - -int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, - struct ieee80211_channel *chan, - unsigned int duration); - -int mwifiex_get_stats_info(struct mwifiex_private *priv, - struct mwifiex_ds_get_stats *log); - -int mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, - u32 reg_offset, u32 reg_value); - -int mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, - u32 reg_offset, u32 *value); - -int mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, - u8 *value); - -int mwifiex_set_11n_httx_cfg(struct mwifiex_private *priv, int data); - -int mwifiex_get_11n_httx_cfg(struct mwifiex_private *priv, int *data); - -int mwifiex_set_tx_rate_cfg(struct mwifiex_private *priv, int tx_rate_index); - -int mwifiex_get_tx_rate_cfg(struct mwifiex_private *priv, int *tx_rate_index); - -int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode); - -int mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, - char *version, int max_len); - -int mwifiex_set_tx_power(struct mwifiex_private *priv, - struct mwifiex_power_cfg *power_cfg); - -int mwifiex_main_process(struct mwifiex_adapter *); - -int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb); - -int mwifiex_get_bss_info(struct mwifiex_private *, - struct mwifiex_bss_info *); -int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, - struct cfg80211_bss *bss, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, - struct mwifiex_bssdescriptor *bss_entry); -int mwifiex_check_network_compatibility(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc); - -u8 mwifiex_chan_type_to_sec_chan_offset(enum nl80211_channel_type chan_type); -u8 mwifiex_sec_chan_offset_to_chan_type(u8 second_chan_offset); - -struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, - const char *name, - unsigned char name_assign_type, - enum nl80211_iftype type, - u32 *flags, - struct vif_params *params); -int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev); - -void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config); - -int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter); - -int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, - struct cfg80211_beacon_data *data); -int mwifiex_del_mgmt_ies(struct mwifiex_private *priv); -u8 *mwifiex_11d_code_2_region(u8 code); -void mwifiex_uap_set_channel(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_chan_def chandef); -int mwifiex_config_start_uap(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg); -void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, - struct mwifiex_sta_node *node); - -void mwifiex_init_11h_params(struct mwifiex_private *priv); -int mwifiex_is_11h_active(struct mwifiex_private *priv); -int mwifiex_11h_activate(struct mwifiex_private *priv, bool flag); - -void mwifiex_11h_process_join(struct mwifiex_private *priv, u8 **buffer, - struct mwifiex_bssdescriptor *bss_desc); -int mwifiex_11h_handle_event_chanswann(struct mwifiex_private *priv); -int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, - struct device_node *node, const char *prefix); -void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv); - -extern const struct ethtool_ops mwifiex_ethtool_ops; - -void mwifiex_del_all_sta_list(struct mwifiex_private *priv); -void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac); -void -mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, - int ies_len, struct mwifiex_sta_node *node); -struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac); -struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac); -u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv); -u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv); -u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv); -int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len); -int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len); -void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, - u8 *buf, int len); -int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action); -int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac); -int mwifiex_get_tdls_list(struct mwifiex_private *priv, - struct tdls_peer_info *buf); -void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv); -bool mwifiex_is_bss_in_11ac_mode(struct mwifiex_private *priv); -u8 mwifiex_get_center_freq_index(struct mwifiex_private *priv, u8 band, - u32 pri_chan, u8 chan_bw); -int mwifiex_init_channel_scan_gap(struct mwifiex_adapter *adapter); - -int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb); -void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv); -void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, - const u8 *mac, u8 link_status); -void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, - u8 *mac, s8 snr, s8 nflr); -void mwifiex_check_auto_tdls(unsigned long context); -void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac); -void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv); -void mwifiex_clean_auto_tdls(struct mwifiex_private *priv); -int mwifiex_config_tdls_enable(struct mwifiex_private *priv); -int mwifiex_config_tdls_disable(struct mwifiex_private *priv); -int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv); -int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac); -int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac, - u8 primary_chan, u8 second_chan_offset, u8 band); - -int mwifiex_cmd_issue_chan_report_request(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf); -int mwifiex_11h_handle_chanrpt_ready(struct mwifiex_private *priv, - struct sk_buff *skb); - -void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, - void *event_body); - -struct sk_buff * -mwifiex_clone_skb_for_tx_status(struct mwifiex_private *priv, - struct sk_buff *skb, u8 flag, u64 *cookie); -void mwifiex_dfs_cac_work_queue(struct work_struct *work); -void mwifiex_dfs_chan_sw_work_queue(struct work_struct *work); -void mwifiex_abort_cac(struct mwifiex_private *priv); -int mwifiex_stop_radar_detection(struct mwifiex_private *priv, - struct cfg80211_chan_def *chandef); -int mwifiex_11h_handle_radar_detected(struct mwifiex_private *priv, - struct sk_buff *skb); - -void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, - s8 nflr); -void mwifiex_hist_data_reset(struct mwifiex_private *priv); -void mwifiex_hist_data_add(struct mwifiex_private *priv, - u8 rx_rate, s8 snr, s8 nflr); -u8 mwifiex_adjust_data_rate(struct mwifiex_private *priv, - u8 rx_rate, u8 ht_info); - -void mwifiex_drv_info_dump(struct mwifiex_adapter *adapter); -void mwifiex_upload_device_dump(struct mwifiex_adapter *adapter); -void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags); -void mwifiex_queue_main_work(struct mwifiex_adapter *adapter); -void mwifiex_coex_ampdu_rxwinsize(struct mwifiex_adapter *adapter); -void mwifiex_11n_delba(struct mwifiex_private *priv, int tid); -int mwifiex_send_domain_info_cmd_fw(struct wiphy *wiphy); -void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, - struct sk_buff *event); -void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, - struct sk_buff *event_skb); -void mwifiex_multi_chan_resync(struct mwifiex_adapter *adapter); - -#ifdef CONFIG_DEBUG_FS -void mwifiex_debugfs_init(void); -void mwifiex_debugfs_remove(void); - -void mwifiex_dev_debugfs_init(struct mwifiex_private *priv); -void mwifiex_dev_debugfs_remove(struct mwifiex_private *priv); -#endif -#endif /* !_MWIFIEX_MAIN_H_ */ diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c deleted file mode 100644 index 21192b6f9c64..000000000000 --- a/drivers/net/wireless/mwifiex/pcie.c +++ /dev/null @@ -1,2742 +0,0 @@ -/* - * Marvell Wireless LAN device driver: PCIE specific handling - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "pcie.h" - -#define PCIE_VERSION "1.0" -#define DRV_NAME "Marvell mwifiex PCIe" - -static u8 user_rmmod; - -static struct mwifiex_if_ops pcie_ops; - -static struct semaphore add_remove_card_sem; - -static struct memory_type_mapping mem_type_mapping_tbl[] = { - {"ITCM", NULL, 0, 0xF0}, - {"DTCM", NULL, 0, 0xF1}, - {"SQRAM", NULL, 0, 0xF2}, - {"IRAM", NULL, 0, 0xF3}, - {"APU", NULL, 0, 0xF4}, - {"CIU", NULL, 0, 0xF5}, - {"ICU", NULL, 0, 0xF6}, - {"MAC", NULL, 0, 0xF7}, -}; - -static int -mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, - size_t size, int flags) -{ - struct pcie_service_card *card = adapter->card; - struct mwifiex_dma_mapping mapping; - - mapping.addr = pci_map_single(card->dev, skb->data, size, flags); - if (pci_dma_mapping_error(card->dev, mapping.addr)) { - mwifiex_dbg(adapter, ERROR, "failed to map pci memory!\n"); - return -1; - } - mapping.len = size; - mwifiex_store_mapping(skb, &mapping); - return 0; -} - -static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter, - struct sk_buff *skb, int flags) -{ - struct pcie_service_card *card = adapter->card; - struct mwifiex_dma_mapping mapping; - - mwifiex_get_mapping(skb, &mapping); - pci_unmap_single(card->dev, mapping.addr, mapping.len, flags); -} - -/* - * This function reads sleep cookie and checks if FW is ready - */ -static bool mwifiex_pcie_ok_to_access_hw(struct mwifiex_adapter *adapter) -{ - u32 *cookie_addr; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (!reg->sleep_cookie) - return true; - - if (card->sleep_cookie_vbase) { - cookie_addr = (u32 *)card->sleep_cookie_vbase; - mwifiex_dbg(adapter, INFO, - "info: ACCESS_HW: sleep cookie=0x%x\n", - *cookie_addr); - if (*cookie_addr == FW_AWAKE_COOKIE) - return true; - } - - return false; -} - -#ifdef CONFIG_PM_SLEEP -/* - * Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not suspended, this function allocates and sends a host - * sleep activate request to the firmware and turns off the traffic. - */ -static int mwifiex_pcie_suspend(struct device *dev) -{ - struct mwifiex_adapter *adapter; - struct pcie_service_card *card; - int hs_actived; - struct pci_dev *pdev = to_pci_dev(dev); - - if (pdev) { - card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_err("Card or adapter structure is not valid\n"); - return 0; - } - } else { - pr_err("PCIE device is not specified\n"); - return 0; - } - - adapter = card->adapter; - - hs_actived = mwifiex_enable_hs(adapter); - - /* Indicate device suspended */ - adapter->is_suspended = true; - adapter->hs_enabling = false; - - return 0; -} - -/* - * Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not resumed, this function turns on the traffic and - * sends a host sleep cancel request to the firmware. - */ -static int mwifiex_pcie_resume(struct device *dev) -{ - struct mwifiex_adapter *adapter; - struct pcie_service_card *card; - struct pci_dev *pdev = to_pci_dev(dev); - - if (pdev) { - card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_err("Card or adapter structure is not valid\n"); - return 0; - } - } else { - pr_err("PCIE device is not specified\n"); - return 0; - } - - adapter = card->adapter; - - if (!adapter->is_suspended) { - mwifiex_dbg(adapter, WARN, - "Device already resumed\n"); - return 0; - } - - adapter->is_suspended = false; - - mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), - MWIFIEX_ASYNC_CMD); - - return 0; -} -#endif - -/* - * This function probes an mwifiex device and registers it. It allocates - * the card structure, enables PCIE function number and initiates the - * device registration and initialization procedure by adding a logical - * interface. - */ -static int mwifiex_pcie_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) -{ - struct pcie_service_card *card; - - pr_debug("info: vendor=0x%4.04X device=0x%4.04X rev=%d\n", - pdev->vendor, pdev->device, pdev->revision); - - card = kzalloc(sizeof(struct pcie_service_card), GFP_KERNEL); - if (!card) - return -ENOMEM; - - card->dev = pdev; - - if (ent->driver_data) { - struct mwifiex_pcie_device *data = (void *)ent->driver_data; - card->pcie.firmware = data->firmware; - card->pcie.reg = data->reg; - card->pcie.blksz_fw_dl = data->blksz_fw_dl; - card->pcie.tx_buf_size = data->tx_buf_size; - card->pcie.can_dump_fw = data->can_dump_fw; - card->pcie.can_ext_scan = data->can_ext_scan; - } - - if (mwifiex_add_card(card, &add_remove_card_sem, &pcie_ops, - MWIFIEX_PCIE)) { - pr_err("%s failed\n", __func__); - kfree(card); - return -1; - } - - return 0; -} - -/* - * This function removes the interface and frees up the card structure. - */ -static void mwifiex_pcie_remove(struct pci_dev *pdev) -{ - struct pcie_service_card *card; - struct mwifiex_adapter *adapter; - struct mwifiex_private *priv; - - card = pci_get_drvdata(pdev); - if (!card) - return; - - adapter = card->adapter; - if (!adapter || !adapter->priv_num) - return; - - if (user_rmmod) { -#ifdef CONFIG_PM_SLEEP - if (adapter->is_suspended) - mwifiex_pcie_resume(&pdev->dev); -#endif - - mwifiex_deauthenticate_all(adapter); - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - mwifiex_disable_auto_ds(priv); - - mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); - } - - mwifiex_remove_card(card->adapter, &add_remove_card_sem); -} - -static void mwifiex_pcie_shutdown(struct pci_dev *pdev) -{ - user_rmmod = 1; - mwifiex_pcie_remove(pdev); - - return; -} - -static const struct pci_device_id mwifiex_ids[] = { - { - PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8766P, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - .driver_data = (unsigned long)&mwifiex_pcie8766, - }, - { - PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8897, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - .driver_data = (unsigned long)&mwifiex_pcie8897, - }, - { - PCIE_VENDOR_ID_MARVELL, PCIE_DEVICE_ID_MARVELL_88W8997, - PCI_ANY_ID, PCI_ANY_ID, 0, 0, - .driver_data = (unsigned long)&mwifiex_pcie8997, - }, - {}, -}; - -MODULE_DEVICE_TABLE(pci, mwifiex_ids); - -#ifdef CONFIG_PM_SLEEP -/* Power Management Hooks */ -static SIMPLE_DEV_PM_OPS(mwifiex_pcie_pm_ops, mwifiex_pcie_suspend, - mwifiex_pcie_resume); -#endif - -/* PCI Device Driver */ -static struct pci_driver __refdata mwifiex_pcie = { - .name = "mwifiex_pcie", - .id_table = mwifiex_ids, - .probe = mwifiex_pcie_probe, - .remove = mwifiex_pcie_remove, -#ifdef CONFIG_PM_SLEEP - .driver = { - .pm = &mwifiex_pcie_pm_ops, - }, -#endif - .shutdown = mwifiex_pcie_shutdown, -}; - -/* - * This function writes data into PCIE card register. - */ -static int mwifiex_write_reg(struct mwifiex_adapter *adapter, int reg, u32 data) -{ - struct pcie_service_card *card = adapter->card; - - iowrite32(data, card->pci_mmap1 + reg); - - return 0; -} - -/* - * This function reads data from PCIE card register. - */ -static int mwifiex_read_reg(struct mwifiex_adapter *adapter, int reg, u32 *data) -{ - struct pcie_service_card *card = adapter->card; - - *data = ioread32(card->pci_mmap1 + reg); - - return 0; -} - -/* This function reads u8 data from PCIE card register. */ -static int mwifiex_read_reg_byte(struct mwifiex_adapter *adapter, - int reg, u8 *data) -{ - struct pcie_service_card *card = adapter->card; - - *data = ioread8(card->pci_mmap1 + reg); - - return 0; -} - -/* - * This function adds delay loop to ensure FW is awake before proceeding. - */ -static void mwifiex_pcie_dev_wakeup_delay(struct mwifiex_adapter *adapter) -{ - int i = 0; - - while (mwifiex_pcie_ok_to_access_hw(adapter)) { - i++; - usleep_range(10, 20); - /* 50ms max wait */ - if (i == 5000) - break; - } - - return; -} - -static void mwifiex_delay_for_sleep_cookie(struct mwifiex_adapter *adapter, - u32 max_delay_loop_cnt) -{ - struct pcie_service_card *card = adapter->card; - u8 *buffer; - u32 sleep_cookie, count; - - for (count = 0; count < max_delay_loop_cnt; count++) { - buffer = card->cmdrsp_buf->data - INTF_HEADER_LEN; - sleep_cookie = *(u32 *)buffer; - - if (sleep_cookie == MWIFIEX_DEF_SLEEP_COOKIE) { - mwifiex_dbg(adapter, INFO, - "sleep cookie found at count %d\n", count); - break; - } - usleep_range(20, 30); - } - - if (count >= max_delay_loop_cnt) - mwifiex_dbg(adapter, INFO, - "max count reached while accessing sleep cookie\n"); -} - -/* This function wakes up the card by reading fw_status register. */ -static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) -{ - u32 fw_status; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - mwifiex_dbg(adapter, EVENT, - "event: Wakeup device...\n"); - - if (reg->sleep_cookie) - mwifiex_pcie_dev_wakeup_delay(adapter); - - /* Reading fw_status register will wakeup device */ - if (mwifiex_read_reg(adapter, reg->fw_status, &fw_status)) { - mwifiex_dbg(adapter, ERROR, - "Reading fw_status register failed\n"); - return -1; - } - - if (reg->sleep_cookie) { - mwifiex_pcie_dev_wakeup_delay(adapter); - mwifiex_dbg(adapter, INFO, - "PCIE wakeup: Setting PS_STATE_AWAKE\n"); - adapter->ps_state = PS_STATE_AWAKE; - } - - return 0; -} - -/* - * This function is called after the card has woken up. - * - * The card configuration register is reset. - */ -static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) -{ - mwifiex_dbg(adapter, CMD, - "cmd: Wakeup device completed\n"); - - return 0; -} - -/* - * This function disables the host interrupt. - * - * The host interrupt mask is read, the disable bit is reset and - * written back to the card host interrupt mask register. - */ -static int mwifiex_pcie_disable_host_int(struct mwifiex_adapter *adapter) -{ - if (mwifiex_pcie_ok_to_access_hw(adapter)) { - if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, - 0x00000000)) { - mwifiex_dbg(adapter, ERROR, - "Disable host interrupt failed\n"); - return -1; - } - } - - return 0; -} - -/* - * This function enables the host interrupt. - * - * The host interrupt enable mask is written to the card - * host interrupt mask register. - */ -static int mwifiex_pcie_enable_host_int(struct mwifiex_adapter *adapter) -{ - if (mwifiex_pcie_ok_to_access_hw(adapter)) { - /* Simply write the mask to the register */ - if (mwifiex_write_reg(adapter, PCIE_HOST_INT_MASK, - HOST_INTR_MASK)) { - mwifiex_dbg(adapter, ERROR, - "Enable host interrupt failed\n"); - return -1; - } - } - - return 0; -} - -/* - * This function initializes TX buffer ring descriptors - */ -static int mwifiex_init_txq_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - int i; - - for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - card->tx_buf_list[i] = NULL; - if (reg->pfu_enabled) { - card->txbd_ring[i] = (void *)card->txbd_ring_vbase + - (sizeof(*desc2) * i); - desc2 = card->txbd_ring[i]; - memset(desc2, 0, sizeof(*desc2)); - } else { - card->txbd_ring[i] = (void *)card->txbd_ring_vbase + - (sizeof(*desc) * i); - desc = card->txbd_ring[i]; - memset(desc, 0, sizeof(*desc)); - } - } - - return 0; -} - -/* This function initializes RX buffer ring descriptors. Each SKB is allocated - * here and after mapping PCI memory, its physical address is assigned to - * PCIE Rx buffer descriptor's physical address. - */ -static int mwifiex_init_rxq_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct sk_buff *skb; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - dma_addr_t buf_pa; - int i; - - for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - /* Allocate skb here so that firmware can DMA data from it */ - skb = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, - GFP_KERNEL | GFP_DMA); - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "Unable to allocate skb for RX ring.\n"); - kfree(card->rxbd_ring_vbase); - return -ENOMEM; - } - - if (mwifiex_map_pci_memory(adapter, skb, - MWIFIEX_RX_DATA_BUF_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); - - mwifiex_dbg(adapter, INFO, - "info: RX ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", - skb, skb->len, skb->data, (u32)buf_pa, - (u32)((u64)buf_pa >> 32)); - - card->rx_buf_list[i] = skb; - if (reg->pfu_enabled) { - card->rxbd_ring[i] = (void *)card->rxbd_ring_vbase + - (sizeof(*desc2) * i); - desc2 = card->rxbd_ring[i]; - desc2->paddr = buf_pa; - desc2->len = (u16)skb->len; - desc2->frag_len = (u16)skb->len; - desc2->flags = reg->ring_flag_eop | reg->ring_flag_sop; - desc2->offset = 0; - } else { - card->rxbd_ring[i] = (void *)(card->rxbd_ring_vbase + - (sizeof(*desc) * i)); - desc = card->rxbd_ring[i]; - desc->paddr = buf_pa; - desc->len = (u16)skb->len; - desc->flags = 0; - } - } - - return 0; -} - -/* This function initializes event buffer ring descriptors. Each SKB is - * allocated here and after mapping PCI memory, its physical address is assigned - * to PCIE Rx buffer descriptor's physical address - */ -static int mwifiex_pcie_init_evt_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - struct mwifiex_evt_buf_desc *desc; - struct sk_buff *skb; - dma_addr_t buf_pa; - int i; - - for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { - /* Allocate skb here so that firmware can DMA data from it */ - skb = dev_alloc_skb(MAX_EVENT_SIZE); - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "Unable to allocate skb for EVENT buf.\n"); - kfree(card->evtbd_ring_vbase); - return -ENOMEM; - } - skb_put(skb, MAX_EVENT_SIZE); - - if (mwifiex_map_pci_memory(adapter, skb, MAX_EVENT_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); - - mwifiex_dbg(adapter, EVENT, - "info: EVT ring: skb=%p len=%d data=%p buf_pa=%#x:%x\n", - skb, skb->len, skb->data, (u32)buf_pa, - (u32)((u64)buf_pa >> 32)); - - card->evt_buf_list[i] = skb; - card->evtbd_ring[i] = (void *)(card->evtbd_ring_vbase + - (sizeof(*desc) * i)); - desc = card->evtbd_ring[i]; - desc->paddr = buf_pa; - desc->len = (u16)skb->len; - desc->flags = 0; - } - - return 0; -} - -/* This function cleans up TX buffer rings. If any of the buffer list has valid - * SKB address, associated SKB is freed. - */ -static void mwifiex_cleanup_txq_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct sk_buff *skb; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - int i; - - for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - if (reg->pfu_enabled) { - desc2 = card->txbd_ring[i]; - if (card->tx_buf_list[i]) { - skb = card->tx_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_TODEVICE); - dev_kfree_skb_any(skb); - } - memset(desc2, 0, sizeof(*desc2)); - } else { - desc = card->txbd_ring[i]; - if (card->tx_buf_list[i]) { - skb = card->tx_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_TODEVICE); - dev_kfree_skb_any(skb); - } - memset(desc, 0, sizeof(*desc)); - } - card->tx_buf_list[i] = NULL; - } - - return; -} - -/* This function cleans up RX buffer rings. If any of the buffer list has valid - * SKB address, associated SKB is freed. - */ -static void mwifiex_cleanup_rxq_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - struct sk_buff *skb; - int i; - - for (i = 0; i < MWIFIEX_MAX_TXRX_BD; i++) { - if (reg->pfu_enabled) { - desc2 = card->rxbd_ring[i]; - if (card->rx_buf_list[i]) { - skb = card->rx_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(skb); - } - memset(desc2, 0, sizeof(*desc2)); - } else { - desc = card->rxbd_ring[i]; - if (card->rx_buf_list[i]) { - skb = card->rx_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(skb); - } - memset(desc, 0, sizeof(*desc)); - } - card->rx_buf_list[i] = NULL; - } - - return; -} - -/* This function cleans up event buffer rings. If any of the buffer list has - * valid SKB address, associated SKB is freed. - */ -static void mwifiex_cleanup_evt_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - struct mwifiex_evt_buf_desc *desc; - struct sk_buff *skb; - int i; - - for (i = 0; i < MWIFIEX_MAX_EVT_BD; i++) { - desc = card->evtbd_ring[i]; - if (card->evt_buf_list[i]) { - skb = card->evt_buf_list[i]; - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(skb); - } - card->evt_buf_list[i] = NULL; - memset(desc, 0, sizeof(*desc)); - } - - return; -} - -/* This function creates buffer descriptor ring for TX - */ -static int mwifiex_pcie_create_txbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - /* - * driver maintaines the write pointer and firmware maintaines the read - * pointer. The write pointer starts at 0 (zero) while the read pointer - * starts at zero with rollover bit set - */ - card->txbd_wrptr = 0; - - if (reg->pfu_enabled) - card->txbd_rdptr = 0; - else - card->txbd_rdptr |= reg->tx_rollover_ind; - - /* allocate shared memory for the BD ring and divide the same in to - several descriptors */ - if (reg->pfu_enabled) - card->txbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * - MWIFIEX_MAX_TXRX_BD; - else - card->txbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * - MWIFIEX_MAX_TXRX_BD; - - mwifiex_dbg(adapter, INFO, - "info: txbd_ring: Allocating %d bytes\n", - card->txbd_ring_size); - card->txbd_ring_vbase = pci_alloc_consistent(card->dev, - card->txbd_ring_size, - &card->txbd_ring_pbase); - if (!card->txbd_ring_vbase) { - mwifiex_dbg(adapter, ERROR, - "allocate consistent memory (%d bytes) failed!\n", - card->txbd_ring_size); - return -ENOMEM; - } - mwifiex_dbg(adapter, DATA, - "info: txbd_ring - base: %p, pbase: %#x:%x, len: %x\n", - card->txbd_ring_vbase, (unsigned int)card->txbd_ring_pbase, - (u32)((u64)card->txbd_ring_pbase >> 32), - card->txbd_ring_size); - - return mwifiex_init_txq_ring(adapter); -} - -static int mwifiex_pcie_delete_txbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - mwifiex_cleanup_txq_ring(adapter); - - if (card->txbd_ring_vbase) - pci_free_consistent(card->dev, card->txbd_ring_size, - card->txbd_ring_vbase, - card->txbd_ring_pbase); - card->txbd_ring_size = 0; - card->txbd_wrptr = 0; - card->txbd_rdptr = 0 | reg->tx_rollover_ind; - card->txbd_ring_vbase = NULL; - card->txbd_ring_pbase = 0; - - return 0; -} - -/* - * This function creates buffer descriptor ring for RX - */ -static int mwifiex_pcie_create_rxbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - /* - * driver maintaines the read pointer and firmware maintaines the write - * pointer. The write pointer starts at 0 (zero) while the read pointer - * starts at zero with rollover bit set - */ - card->rxbd_wrptr = 0; - card->rxbd_rdptr = reg->rx_rollover_ind; - - if (reg->pfu_enabled) - card->rxbd_ring_size = sizeof(struct mwifiex_pfu_buf_desc) * - MWIFIEX_MAX_TXRX_BD; - else - card->rxbd_ring_size = sizeof(struct mwifiex_pcie_buf_desc) * - MWIFIEX_MAX_TXRX_BD; - - mwifiex_dbg(adapter, INFO, - "info: rxbd_ring: Allocating %d bytes\n", - card->rxbd_ring_size); - card->rxbd_ring_vbase = pci_alloc_consistent(card->dev, - card->rxbd_ring_size, - &card->rxbd_ring_pbase); - if (!card->rxbd_ring_vbase) { - mwifiex_dbg(adapter, ERROR, - "allocate consistent memory (%d bytes) failed!\n", - card->rxbd_ring_size); - return -ENOMEM; - } - - mwifiex_dbg(adapter, DATA, - "info: rxbd_ring - base: %p, pbase: %#x:%x, len: %#x\n", - card->rxbd_ring_vbase, (u32)card->rxbd_ring_pbase, - (u32)((u64)card->rxbd_ring_pbase >> 32), - card->rxbd_ring_size); - - return mwifiex_init_rxq_ring(adapter); -} - -/* - * This function deletes Buffer descriptor ring for RX - */ -static int mwifiex_pcie_delete_rxbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - mwifiex_cleanup_rxq_ring(adapter); - - if (card->rxbd_ring_vbase) - pci_free_consistent(card->dev, card->rxbd_ring_size, - card->rxbd_ring_vbase, - card->rxbd_ring_pbase); - card->rxbd_ring_size = 0; - card->rxbd_wrptr = 0; - card->rxbd_rdptr = 0 | reg->rx_rollover_ind; - card->rxbd_ring_vbase = NULL; - card->rxbd_ring_pbase = 0; - - return 0; -} - -/* - * This function creates buffer descriptor ring for Events - */ -static int mwifiex_pcie_create_evtbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - /* - * driver maintaines the read pointer and firmware maintaines the write - * pointer. The write pointer starts at 0 (zero) while the read pointer - * starts at zero with rollover bit set - */ - card->evtbd_wrptr = 0; - card->evtbd_rdptr = reg->evt_rollover_ind; - - card->evtbd_ring_size = sizeof(struct mwifiex_evt_buf_desc) * - MWIFIEX_MAX_EVT_BD; - - mwifiex_dbg(adapter, INFO, - "info: evtbd_ring: Allocating %d bytes\n", - card->evtbd_ring_size); - card->evtbd_ring_vbase = pci_alloc_consistent(card->dev, - card->evtbd_ring_size, - &card->evtbd_ring_pbase); - if (!card->evtbd_ring_vbase) { - mwifiex_dbg(adapter, ERROR, - "allocate consistent memory (%d bytes) failed!\n", - card->evtbd_ring_size); - return -ENOMEM; - } - - mwifiex_dbg(adapter, EVENT, - "info: CMDRSP/EVT bd_ring - base: %p pbase: %#x:%x len: %#x\n", - card->evtbd_ring_vbase, (u32)card->evtbd_ring_pbase, - (u32)((u64)card->evtbd_ring_pbase >> 32), - card->evtbd_ring_size); - - return mwifiex_pcie_init_evt_ring(adapter); -} - -/* - * This function deletes Buffer descriptor ring for Events - */ -static int mwifiex_pcie_delete_evtbd_ring(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - mwifiex_cleanup_evt_ring(adapter); - - if (card->evtbd_ring_vbase) - pci_free_consistent(card->dev, card->evtbd_ring_size, - card->evtbd_ring_vbase, - card->evtbd_ring_pbase); - card->evtbd_wrptr = 0; - card->evtbd_rdptr = 0 | reg->evt_rollover_ind; - card->evtbd_ring_size = 0; - card->evtbd_ring_vbase = NULL; - card->evtbd_ring_pbase = 0; - - return 0; -} - -/* - * This function allocates a buffer for CMDRSP - */ -static int mwifiex_pcie_alloc_cmdrsp_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - struct sk_buff *skb; - - /* Allocate memory for receiving command response data */ - skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "Unable to allocate skb for command response data.\n"); - return -ENOMEM; - } - skb_put(skb, MWIFIEX_UPLD_SIZE); - if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - - card->cmdrsp_buf = skb; - - return 0; -} - -/* - * This function deletes a buffer for CMDRSP - */ -static int mwifiex_pcie_delete_cmdrsp_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card; - - if (!adapter) - return 0; - - card = adapter->card; - - if (card && card->cmdrsp_buf) { - mwifiex_unmap_pci_memory(adapter, card->cmdrsp_buf, - PCI_DMA_FROMDEVICE); - dev_kfree_skb_any(card->cmdrsp_buf); - } - - if (card && card->cmd_buf) { - mwifiex_unmap_pci_memory(adapter, card->cmd_buf, - PCI_DMA_TODEVICE); - } - return 0; -} - -/* - * This function allocates a buffer for sleep cookie - */ -static int mwifiex_pcie_alloc_sleep_cookie_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - - card->sleep_cookie_vbase = pci_alloc_consistent(card->dev, sizeof(u32), - &card->sleep_cookie_pbase); - if (!card->sleep_cookie_vbase) { - mwifiex_dbg(adapter, ERROR, - "pci_alloc_consistent failed!\n"); - return -ENOMEM; - } - /* Init val of Sleep Cookie */ - *(u32 *)card->sleep_cookie_vbase = FW_AWAKE_COOKIE; - - mwifiex_dbg(adapter, INFO, - "alloc_scook: sleep cookie=0x%x\n", - *((u32 *)card->sleep_cookie_vbase)); - - return 0; -} - -/* - * This function deletes buffer for sleep cookie - */ -static int mwifiex_pcie_delete_sleep_cookie_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card; - - if (!adapter) - return 0; - - card = adapter->card; - - if (card && card->sleep_cookie_vbase) { - pci_free_consistent(card->dev, sizeof(u32), - card->sleep_cookie_vbase, - card->sleep_cookie_pbase); - card->sleep_cookie_vbase = NULL; - } - - return 0; -} - -/* This function flushes the TX buffer descriptor ring - * This function defined as handler is also called while cleaning TXRX - * during disconnect/ bss stop. - */ -static int mwifiex_clean_pcie_ring_buf(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - - if (!mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) { - card->txbd_flush = 1; - /* write pointer already set at last send - * send dnld-rdy intr again, wait for completion. - */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DNLD_RDY)) { - mwifiex_dbg(adapter, ERROR, - "failed to assert dnld-rdy interrupt.\n"); - return -1; - } - } - return 0; -} - -/* - * This function unmaps and frees downloaded data buffer - */ -static int mwifiex_pcie_send_data_complete(struct mwifiex_adapter *adapter) -{ - struct sk_buff *skb; - u32 wrdoneidx, rdptr, num_tx_buffs, unmap_count = 0; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - /* Read the TX ring read pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->tx_rdptr, &rdptr)) { - mwifiex_dbg(adapter, ERROR, - "SEND COMP: failed to read reg->tx_rdptr\n"); - return -1; - } - - mwifiex_dbg(adapter, DATA, - "SEND COMP: rdptr_prev=0x%x, rdptr=0x%x\n", - card->txbd_rdptr, rdptr); - - num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; - /* free from previous txbd_rdptr to current txbd_rdptr */ - while (((card->txbd_rdptr & reg->tx_mask) != - (rdptr & reg->tx_mask)) || - ((card->txbd_rdptr & reg->tx_rollover_ind) != - (rdptr & reg->tx_rollover_ind))) { - wrdoneidx = (card->txbd_rdptr & reg->tx_mask) >> - reg->tx_start_ptr; - - skb = card->tx_buf_list[wrdoneidx]; - - if (skb) { - mwifiex_dbg(adapter, DATA, - "SEND COMP: Detach skb %p at txbd_rdidx=%d\n", - skb, wrdoneidx); - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_TODEVICE); - - unmap_count++; - - if (card->txbd_flush) - mwifiex_write_data_complete(adapter, skb, 0, - -1); - else - mwifiex_write_data_complete(adapter, skb, 0, 0); - } - - card->tx_buf_list[wrdoneidx] = NULL; - - if (reg->pfu_enabled) { - desc2 = card->txbd_ring[wrdoneidx]; - memset(desc2, 0, sizeof(*desc2)); - } else { - desc = card->txbd_ring[wrdoneidx]; - memset(desc, 0, sizeof(*desc)); - } - switch (card->dev->device) { - case PCIE_DEVICE_ID_MARVELL_88W8766P: - card->txbd_rdptr++; - break; - case PCIE_DEVICE_ID_MARVELL_88W8897: - case PCIE_DEVICE_ID_MARVELL_88W8997: - card->txbd_rdptr += reg->ring_tx_start_ptr; - break; - } - - - if ((card->txbd_rdptr & reg->tx_mask) == num_tx_buffs) - card->txbd_rdptr = ((card->txbd_rdptr & - reg->tx_rollover_ind) ^ - reg->tx_rollover_ind); - } - - if (unmap_count) - adapter->data_sent = false; - - if (card->txbd_flush) { - if (mwifiex_pcie_txbd_empty(card, card->txbd_rdptr)) - card->txbd_flush = 0; - else - mwifiex_clean_pcie_ring_buf(adapter); - } - - return 0; -} - -/* This function sends data buffer to device. First 4 bytes of payload - * are filled with payload length and payload type. Then this payload - * is mapped to PCI device memory. Tx ring pointers are advanced accordingly. - * Download ready interrupt to FW is deffered if Tx ring is not full and - * additional payload can be accomodated. - * Caller must ensure tx_param parameter to this function is not NULL. - */ -static int -mwifiex_pcie_send_data(struct mwifiex_adapter *adapter, struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - u32 wrindx, num_tx_buffs, rx_val; - int ret; - dma_addr_t buf_pa; - struct mwifiex_pcie_buf_desc *desc = NULL; - struct mwifiex_pfu_buf_desc *desc2 = NULL; - __le16 *tmp; - - if (!(skb->data && skb->len)) { - mwifiex_dbg(adapter, ERROR, - "%s(): invalid parameter <%p, %#x>\n", - __func__, skb->data, skb->len); - return -1; - } - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - num_tx_buffs = MWIFIEX_MAX_TXRX_BD << reg->tx_start_ptr; - mwifiex_dbg(adapter, DATA, - "info: SEND DATA: \n", - card->txbd_rdptr, card->txbd_wrptr); - if (mwifiex_pcie_txbd_not_full(card)) { - u8 *payload; - - adapter->data_sent = true; - payload = skb->data; - tmp = (__le16 *)&payload[0]; - *tmp = cpu_to_le16((u16)skb->len); - tmp = (__le16 *)&payload[2]; - *tmp = cpu_to_le16(MWIFIEX_TYPE_DATA); - - if (mwifiex_map_pci_memory(adapter, skb, skb->len, - PCI_DMA_TODEVICE)) - return -1; - - wrindx = (card->txbd_wrptr & reg->tx_mask) >> reg->tx_start_ptr; - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); - card->tx_buf_list[wrindx] = skb; - - if (reg->pfu_enabled) { - desc2 = card->txbd_ring[wrindx]; - desc2->paddr = buf_pa; - desc2->len = (u16)skb->len; - desc2->frag_len = (u16)skb->len; - desc2->offset = 0; - desc2->flags = MWIFIEX_BD_FLAG_FIRST_DESC | - MWIFIEX_BD_FLAG_LAST_DESC; - } else { - desc = card->txbd_ring[wrindx]; - desc->paddr = buf_pa; - desc->len = (u16)skb->len; - desc->flags = MWIFIEX_BD_FLAG_FIRST_DESC | - MWIFIEX_BD_FLAG_LAST_DESC; - } - - switch (card->dev->device) { - case PCIE_DEVICE_ID_MARVELL_88W8766P: - card->txbd_wrptr++; - break; - case PCIE_DEVICE_ID_MARVELL_88W8897: - case PCIE_DEVICE_ID_MARVELL_88W8997: - card->txbd_wrptr += reg->ring_tx_start_ptr; - break; - } - - if ((card->txbd_wrptr & reg->tx_mask) == num_tx_buffs) - card->txbd_wrptr = ((card->txbd_wrptr & - reg->tx_rollover_ind) ^ - reg->tx_rollover_ind); - - rx_val = card->rxbd_rdptr & reg->rx_wrap_mask; - /* Write the TX ring write pointer in to reg->tx_wrptr */ - if (mwifiex_write_reg(adapter, reg->tx_wrptr, - card->txbd_wrptr | rx_val)) { - mwifiex_dbg(adapter, ERROR, - "SEND DATA: failed to write reg->tx_wrptr\n"); - ret = -1; - goto done_unmap; - } - if ((mwifiex_pcie_txbd_not_full(card)) && - tx_param->next_pkt_len) { - /* have more packets and TxBD still can hold more */ - mwifiex_dbg(adapter, DATA, - "SEND DATA: delay dnld-rdy interrupt.\n"); - adapter->data_sent = false; - } else { - /* Send the TX ready interrupt */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DNLD_RDY)) { - mwifiex_dbg(adapter, ERROR, - "SEND DATA: failed to assert dnld-rdy interrupt.\n"); - ret = -1; - goto done_unmap; - } - } - mwifiex_dbg(adapter, DATA, - "info: SEND DATA: Updated and sent packet to firmware successfully\n", - card->txbd_rdptr, card->txbd_wrptr); - } else { - mwifiex_dbg(adapter, DATA, - "info: TX Ring full, can't send packets to fw\n"); - adapter->data_sent = true; - /* Send the TX ready interrupt */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DNLD_RDY)) - mwifiex_dbg(adapter, ERROR, - "SEND DATA: failed to assert door-bell intr\n"); - return -EBUSY; - } - - return -EINPROGRESS; -done_unmap: - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - card->tx_buf_list[wrindx] = NULL; - if (reg->pfu_enabled) - memset(desc2, 0, sizeof(*desc2)); - else - memset(desc, 0, sizeof(*desc)); - - return ret; -} - -/* - * This function handles received buffer ring and - * dispatches packets to upper - */ -static int mwifiex_pcie_process_recv_data(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - u32 wrptr, rd_index, tx_val; - dma_addr_t buf_pa; - int ret = 0; - struct sk_buff *skb_tmp = NULL; - struct mwifiex_pcie_buf_desc *desc; - struct mwifiex_pfu_buf_desc *desc2; - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - /* Read the RX ring Write pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { - mwifiex_dbg(adapter, ERROR, - "RECV DATA: failed to read reg->rx_wrptr\n"); - ret = -1; - goto done; - } - card->rxbd_wrptr = wrptr; - - while (((wrptr & reg->rx_mask) != - (card->rxbd_rdptr & reg->rx_mask)) || - ((wrptr & reg->rx_rollover_ind) == - (card->rxbd_rdptr & reg->rx_rollover_ind))) { - struct sk_buff *skb_data; - u16 rx_len; - __le16 pkt_len; - - rd_index = card->rxbd_rdptr & reg->rx_mask; - skb_data = card->rx_buf_list[rd_index]; - - /* If skb allocation was failed earlier for Rx packet, - * rx_buf_list[rd_index] would have been left with a NULL. - */ - if (!skb_data) - return -ENOMEM; - - mwifiex_unmap_pci_memory(adapter, skb_data, PCI_DMA_FROMDEVICE); - card->rx_buf_list[rd_index] = NULL; - - /* Get data length from interface header - - * first 2 bytes for len, next 2 bytes is for type - */ - pkt_len = *((__le16 *)skb_data->data); - rx_len = le16_to_cpu(pkt_len); - if (WARN_ON(rx_len <= INTF_HEADER_LEN || - rx_len > MWIFIEX_RX_DATA_BUF_SIZE)) { - mwifiex_dbg(adapter, ERROR, - "Invalid RX len %d, Rd=%#x, Wr=%#x\n", - rx_len, card->rxbd_rdptr, wrptr); - dev_kfree_skb_any(skb_data); - } else { - skb_put(skb_data, rx_len); - mwifiex_dbg(adapter, DATA, - "info: RECV DATA: Rd=%#x, Wr=%#x, Len=%d\n", - card->rxbd_rdptr, wrptr, rx_len); - skb_pull(skb_data, INTF_HEADER_LEN); - if (adapter->rx_work_enabled) { - skb_queue_tail(&adapter->rx_data_q, skb_data); - adapter->data_received = true; - atomic_inc(&adapter->rx_pending); - } else { - mwifiex_handle_rx_packet(adapter, skb_data); - } - } - - skb_tmp = mwifiex_alloc_dma_align_buf(MWIFIEX_RX_DATA_BUF_SIZE, - GFP_KERNEL | GFP_DMA); - if (!skb_tmp) { - mwifiex_dbg(adapter, ERROR, - "Unable to allocate skb.\n"); - return -ENOMEM; - } - - if (mwifiex_map_pci_memory(adapter, skb_tmp, - MWIFIEX_RX_DATA_BUF_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb_tmp); - - mwifiex_dbg(adapter, INFO, - "RECV DATA: Attach new sk_buff %p at rxbd_rdidx=%d\n", - skb_tmp, rd_index); - card->rx_buf_list[rd_index] = skb_tmp; - - if (reg->pfu_enabled) { - desc2 = card->rxbd_ring[rd_index]; - desc2->paddr = buf_pa; - desc2->len = skb_tmp->len; - desc2->frag_len = skb_tmp->len; - desc2->offset = 0; - desc2->flags = reg->ring_flag_sop | reg->ring_flag_eop; - } else { - desc = card->rxbd_ring[rd_index]; - desc->paddr = buf_pa; - desc->len = skb_tmp->len; - desc->flags = 0; - } - - if ((++card->rxbd_rdptr & reg->rx_mask) == - MWIFIEX_MAX_TXRX_BD) { - card->rxbd_rdptr = ((card->rxbd_rdptr & - reg->rx_rollover_ind) ^ - reg->rx_rollover_ind); - } - mwifiex_dbg(adapter, DATA, - "info: RECV DATA: \n", - card->rxbd_rdptr, wrptr); - - tx_val = card->txbd_wrptr & reg->tx_wrap_mask; - /* Write the RX ring read pointer in to reg->rx_rdptr */ - if (mwifiex_write_reg(adapter, reg->rx_rdptr, - card->rxbd_rdptr | tx_val)) { - mwifiex_dbg(adapter, DATA, - "RECV DATA: failed to write reg->rx_rdptr\n"); - ret = -1; - goto done; - } - - /* Read the RX ring Write pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->rx_wrptr, &wrptr)) { - mwifiex_dbg(adapter, ERROR, - "RECV DATA: failed to read reg->rx_wrptr\n"); - ret = -1; - goto done; - } - mwifiex_dbg(adapter, DATA, - "info: RECV DATA: Rcvd packet from fw successfully\n"); - card->rxbd_wrptr = wrptr; - } - -done: - return ret; -} - -/* - * This function downloads the boot command to device - */ -static int -mwifiex_pcie_send_boot_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) -{ - dma_addr_t buf_pa; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (!(skb->data && skb->len)) { - mwifiex_dbg(adapter, ERROR, - "Invalid parameter in %s <%p. len %d>\n", - __func__, skb->data, skb->len); - return -1; - } - - if (mwifiex_map_pci_memory(adapter, skb, skb->len , PCI_DMA_TODEVICE)) - return -1; - - buf_pa = MWIFIEX_SKB_DMA_ADDR(skb); - - /* Write the lower 32bits of the physical address to low command - * address scratch register - */ - if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, (u32)buf_pa)) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to write download command to boot code.\n", - __func__); - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - return -1; - } - - /* Write the upper 32bits of the physical address to high command - * address scratch register - */ - if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, - (u32)((u64)buf_pa >> 32))) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to write download command to boot code.\n", - __func__); - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - return -1; - } - - /* Write the command length to cmd_size scratch register */ - if (mwifiex_write_reg(adapter, reg->cmd_size, skb->len)) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to write command len to cmd_size scratch reg\n", - __func__); - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - return -1; - } - - /* Ring the door bell */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DOOR_BELL)) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to assert door-bell intr\n", __func__); - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - return -1; - } - - return 0; -} - -/* This function init rx port in firmware which in turn enables to receive data - * from device before transmitting any packet. - */ -static int mwifiex_pcie_init_fw_port(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - int tx_wrap = card->txbd_wrptr & reg->tx_wrap_mask; - - /* Write the RX ring read pointer in to reg->rx_rdptr */ - if (mwifiex_write_reg(adapter, reg->rx_rdptr, card->rxbd_rdptr | - tx_wrap)) { - mwifiex_dbg(adapter, ERROR, - "RECV DATA: failed to write reg->rx_rdptr\n"); - return -1; - } - return 0; -} - -/* This function downloads commands to the device - */ -static int -mwifiex_pcie_send_cmd(struct mwifiex_adapter *adapter, struct sk_buff *skb) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - int ret = 0; - dma_addr_t cmd_buf_pa, cmdrsp_buf_pa; - u8 *payload = (u8 *)skb->data; - - if (!(skb->data && skb->len)) { - mwifiex_dbg(adapter, ERROR, - "Invalid parameter in %s <%p, %#x>\n", - __func__, skb->data, skb->len); - return -1; - } - - /* Make sure a command response buffer is available */ - if (!card->cmdrsp_buf) { - mwifiex_dbg(adapter, ERROR, - "No response buffer available, send command failed\n"); - return -EBUSY; - } - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - adapter->cmd_sent = true; - - *(__le16 *)&payload[0] = cpu_to_le16((u16)skb->len); - *(__le16 *)&payload[2] = cpu_to_le16(MWIFIEX_TYPE_CMD); - - if (mwifiex_map_pci_memory(adapter, skb, skb->len, PCI_DMA_TODEVICE)) - return -1; - - card->cmd_buf = skb; - - /* To send a command, the driver will: - 1. Write the 64bit physical address of the data buffer to - cmd response address low + cmd response address high - 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 (card->cmdrsp_buf) { - cmdrsp_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmdrsp_buf); - /* Write the lower 32bits of the cmdrsp buffer physical - address */ - if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, - (u32)cmdrsp_buf_pa)) { - mwifiex_dbg(adapter, ERROR, - "Failed to write download cmd to boot code.\n"); - ret = -1; - goto done; - } - /* Write the upper 32bits of the cmdrsp buffer physical - address */ - if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, - (u32)((u64)cmdrsp_buf_pa >> 32))) { - mwifiex_dbg(adapter, ERROR, - "Failed to write download cmd to boot code.\n"); - ret = -1; - goto done; - } - } - - cmd_buf_pa = MWIFIEX_SKB_DMA_ADDR(card->cmd_buf); - /* Write the lower 32bits of the physical address to reg->cmd_addr_lo */ - if (mwifiex_write_reg(adapter, reg->cmd_addr_lo, - (u32)cmd_buf_pa)) { - mwifiex_dbg(adapter, ERROR, - "Failed to write download cmd to boot code.\n"); - ret = -1; - goto done; - } - /* Write the upper 32bits of the physical address to reg->cmd_addr_hi */ - if (mwifiex_write_reg(adapter, reg->cmd_addr_hi, - (u32)((u64)cmd_buf_pa >> 32))) { - mwifiex_dbg(adapter, ERROR, - "Failed to write download cmd to boot code.\n"); - ret = -1; - goto done; - } - - /* Write the command length to reg->cmd_size */ - if (mwifiex_write_reg(adapter, reg->cmd_size, - card->cmd_buf->len)) { - mwifiex_dbg(adapter, ERROR, - "Failed to write cmd len to reg->cmd_size\n"); - ret = -1; - goto done; - } - - /* Ring the door bell */ - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_DOOR_BELL)) { - mwifiex_dbg(adapter, ERROR, - "Failed to assert door-bell intr\n"); - ret = -1; - goto done; - } - -done: - if (ret) - adapter->cmd_sent = false; - - return 0; -} - -/* - * This function handles command complete interrupt - */ -static int mwifiex_pcie_process_cmd_complete(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - struct sk_buff *skb = card->cmdrsp_buf; - int count = 0; - u16 rx_len; - __le16 pkt_len; - - mwifiex_dbg(adapter, CMD, - "info: Rx CMD Response\n"); - - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_FROMDEVICE); - - /* Unmap the command as a response has been received. */ - if (card->cmd_buf) { - mwifiex_unmap_pci_memory(adapter, card->cmd_buf, - PCI_DMA_TODEVICE); - card->cmd_buf = NULL; - } - - pkt_len = *((__le16 *)skb->data); - rx_len = le16_to_cpu(pkt_len); - skb_trim(skb, rx_len); - skb_pull(skb, INTF_HEADER_LEN); - - if (!adapter->curr_cmd) { - if (adapter->ps_state == PS_STATE_SLEEP_CFM) { - mwifiex_process_sleep_confirm_resp(adapter, skb->data, - skb->len); - mwifiex_pcie_enable_host_int(adapter); - if (mwifiex_write_reg(adapter, - PCIE_CPU_INT_EVENT, - CPU_INTR_SLEEP_CFM_DONE)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return -1; - } - mwifiex_delay_for_sleep_cookie(adapter, - MWIFIEX_MAX_DELAY_COUNT); - while (reg->sleep_cookie && (count++ < 10) && - mwifiex_pcie_ok_to_access_hw(adapter)) - usleep_range(50, 60); - } else { - mwifiex_dbg(adapter, ERROR, - "There is no command but got cmdrsp\n"); - } - memcpy(adapter->upld_buf, skb->data, - min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, skb->len)); - skb_push(skb, INTF_HEADER_LEN); - if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - } else if (mwifiex_pcie_ok_to_access_hw(adapter)) { - adapter->curr_cmd->resp_skb = skb; - adapter->cmd_resp_received = true; - /* Take the pointer and set it to CMD node and will - return in the response complete callback */ - card->cmdrsp_buf = NULL; - - /* Clear the cmd-rsp buffer address in scratch registers. This - will prevent firmware from writing to the same response - buffer again. */ - if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_lo, 0)) { - mwifiex_dbg(adapter, ERROR, - "cmd_done: failed to clear cmd_rsp_addr_lo\n"); - return -1; - } - /* Write the upper 32bits of the cmdrsp buffer physical - address */ - if (mwifiex_write_reg(adapter, reg->cmdrsp_addr_hi, 0)) { - mwifiex_dbg(adapter, ERROR, - "cmd_done: failed to clear cmd_rsp_addr_hi\n"); - return -1; - } - } - - return 0; -} - -/* - * Command Response processing complete handler - */ -static int mwifiex_pcie_cmdrsp_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - struct pcie_service_card *card = adapter->card; - - if (skb) { - card->cmdrsp_buf = skb; - skb_push(card->cmdrsp_buf, INTF_HEADER_LEN); - if (mwifiex_map_pci_memory(adapter, skb, MWIFIEX_UPLD_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - } - - return 0; -} - -/* - * This function handles firmware event ready interrupt - */ -static int mwifiex_pcie_process_event_ready(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; - u32 wrptr, event; - struct mwifiex_evt_buf_desc *desc; - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - mwifiex_pm_wakeup_card(adapter); - - if (adapter->event_received) { - mwifiex_dbg(adapter, EVENT, - "info: Event being processed,\t" - "do not process this interrupt just yet\n"); - return 0; - } - - if (rdptr >= MWIFIEX_MAX_EVT_BD) { - mwifiex_dbg(adapter, ERROR, - "info: Invalid read pointer...\n"); - return -1; - } - - /* Read the event ring write pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { - mwifiex_dbg(adapter, ERROR, - "EventReady: failed to read reg->evt_wrptr\n"); - return -1; - } - - mwifiex_dbg(adapter, EVENT, - "info: EventReady: Initial ", - card->evtbd_rdptr, wrptr); - if (((wrptr & MWIFIEX_EVTBD_MASK) != (card->evtbd_rdptr - & MWIFIEX_EVTBD_MASK)) || - ((wrptr & reg->evt_rollover_ind) == - (card->evtbd_rdptr & reg->evt_rollover_ind))) { - struct sk_buff *skb_cmd; - __le16 data_len = 0; - u16 evt_len; - - mwifiex_dbg(adapter, INFO, - "info: Read Index: %d\n", rdptr); - skb_cmd = card->evt_buf_list[rdptr]; - mwifiex_unmap_pci_memory(adapter, skb_cmd, PCI_DMA_FROMDEVICE); - - /* Take the pointer and set it to event pointer in adapter - and will return back after event handling callback */ - card->evt_buf_list[rdptr] = NULL; - desc = card->evtbd_ring[rdptr]; - memset(desc, 0, sizeof(*desc)); - - event = *(u32 *) &skb_cmd->data[INTF_HEADER_LEN]; - adapter->event_cause = event; - /* The first 4bytes will be the event transfer header - len is 2 bytes followed by type which is 2 bytes */ - memcpy(&data_len, skb_cmd->data, sizeof(__le16)); - evt_len = le16_to_cpu(data_len); - skb_trim(skb_cmd, evt_len); - skb_pull(skb_cmd, INTF_HEADER_LEN); - mwifiex_dbg(adapter, EVENT, - "info: Event length: %d\n", evt_len); - - if ((evt_len > 0) && (evt_len < MAX_EVENT_SIZE)) - memcpy(adapter->event_body, skb_cmd->data + - MWIFIEX_EVENT_HEADER_LEN, evt_len - - MWIFIEX_EVENT_HEADER_LEN); - - adapter->event_received = true; - adapter->event_skb = skb_cmd; - - /* Do not update the event read 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 { - if (mwifiex_write_reg(adapter, PCIE_CPU_INT_EVENT, - CPU_INTR_EVENT_DONE)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return -1; - } - } - - return 0; -} - -/* - * Event processing complete handler - */ -static int mwifiex_pcie_event_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - int ret = 0; - u32 rdptr = card->evtbd_rdptr & MWIFIEX_EVTBD_MASK; - u32 wrptr; - struct mwifiex_evt_buf_desc *desc; - - if (!skb) - return 0; - - if (rdptr >= MWIFIEX_MAX_EVT_BD) { - mwifiex_dbg(adapter, ERROR, - "event_complete: Invalid rdptr 0x%x\n", - rdptr); - return -EINVAL; - } - - /* Read the event ring write pointer set by firmware */ - if (mwifiex_read_reg(adapter, reg->evt_wrptr, &wrptr)) { - mwifiex_dbg(adapter, ERROR, - "event_complete: failed to read reg->evt_wrptr\n"); - return -1; - } - - if (!card->evt_buf_list[rdptr]) { - skb_push(skb, INTF_HEADER_LEN); - skb_put(skb, MAX_EVENT_SIZE - skb->len); - if (mwifiex_map_pci_memory(adapter, skb, - MAX_EVENT_SIZE, - PCI_DMA_FROMDEVICE)) - return -1; - card->evt_buf_list[rdptr] = skb; - desc = card->evtbd_ring[rdptr]; - desc->paddr = MWIFIEX_SKB_DMA_ADDR(skb); - desc->len = (u16)skb->len; - desc->flags = 0; - skb = NULL; - } else { - mwifiex_dbg(adapter, ERROR, - "info: ERROR: buf still valid at index %d, <%p, %p>\n", - rdptr, card->evt_buf_list[rdptr], skb); - } - - if ((++card->evtbd_rdptr & MWIFIEX_EVTBD_MASK) == MWIFIEX_MAX_EVT_BD) { - card->evtbd_rdptr = ((card->evtbd_rdptr & - reg->evt_rollover_ind) ^ - reg->evt_rollover_ind); - } - - mwifiex_dbg(adapter, EVENT, - "info: Updated ", - card->evtbd_rdptr, wrptr); - - /* Write the event ring read pointer in to reg->evt_rdptr */ - if (mwifiex_write_reg(adapter, reg->evt_rdptr, - card->evtbd_rdptr)) { - mwifiex_dbg(adapter, ERROR, - "event_complete: failed to read reg->evt_rdptr\n"); - return -1; - } - - mwifiex_dbg(adapter, EVENT, - "info: Check Events Again\n"); - ret = mwifiex_pcie_process_event_ready(adapter); - - return ret; -} - -/* - * This function downloads the firmware to the card. - * - * Firmware is downloaded to the card in blocks. Every block download - * is tested for CRC errors, and retried a number of times before - * returning failure. - */ -static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *fw) -{ - int ret; - u8 *firmware = fw->fw_buf; - u32 firmware_len = fw->fw_len; - u32 offset = 0; - struct sk_buff *skb; - u32 txlen, tx_blocks = 0, tries, len; - u32 block_retry_cnt = 0; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (!firmware || !firmware_len) { - mwifiex_dbg(adapter, ERROR, - "No firmware image found! Terminating download\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "info: Downloading FW image (%d bytes)\n", - firmware_len); - - if (mwifiex_pcie_disable_host_int(adapter)) { - mwifiex_dbg(adapter, ERROR, - "%s: Disabling interrupts failed.\n", __func__); - return -1; - } - - skb = dev_alloc_skb(MWIFIEX_UPLD_SIZE); - if (!skb) { - ret = -ENOMEM; - goto done; - } - - /* Perform firmware data transfer */ - do { - u32 ireg_intr = 0; - - /* More data? */ - if (offset >= firmware_len) - break; - - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - ret = mwifiex_read_reg(adapter, reg->cmd_size, - &len); - if (ret) { - mwifiex_dbg(adapter, FATAL, - "Failed reading len from boot code\n"); - goto done; - } - if (len) - break; - usleep_range(10, 20); - } - - if (!len) { - break; - } else if (len > MWIFIEX_UPLD_SIZE) { - mwifiex_dbg(adapter, ERROR, - "FW download failure @ %d, invalid length %d\n", - offset, len); - ret = -1; - goto done; - } - - txlen = len; - - if (len & BIT(0)) { - block_retry_cnt++; - if (block_retry_cnt > MAX_WRITE_IOMEM_RETRY) { - mwifiex_dbg(adapter, ERROR, - "FW download failure @ %d, over max\t" - "retry count\n", offset); - ret = -1; - goto done; - } - mwifiex_dbg(adapter, ERROR, - "FW CRC error indicated by the\t" - "helper: len = 0x%04X, txlen = %d\n", - len, txlen); - len &= ~BIT(0); - /* Setting this to 0 to resend from same offset */ - txlen = 0; - } else { - block_retry_cnt = 0; - /* Set blocksize to transfer - checking for - last block */ - if (firmware_len - offset < txlen) - txlen = firmware_len - offset; - - mwifiex_dbg(adapter, INFO, "."); - - tx_blocks = (txlen + card->pcie.blksz_fw_dl - 1) / - card->pcie.blksz_fw_dl; - - /* Copy payload to buffer */ - memmove(skb->data, &firmware[offset], txlen); - } - - skb_put(skb, MWIFIEX_UPLD_SIZE - skb->len); - skb_trim(skb, tx_blocks * card->pcie.blksz_fw_dl); - - /* Send the boot command to device */ - if (mwifiex_pcie_send_boot_cmd(adapter, skb)) { - mwifiex_dbg(adapter, ERROR, - "Failed to send firmware download command\n"); - ret = -1; - goto done; - } - - /* Wait for the command done interrupt */ - do { - if (mwifiex_read_reg(adapter, PCIE_CPU_INT_STATUS, - &ireg_intr)) { - mwifiex_dbg(adapter, ERROR, - "%s: Failed to read\t" - "interrupt status during fw dnld.\n", - __func__); - mwifiex_unmap_pci_memory(adapter, skb, - PCI_DMA_TODEVICE); - ret = -1; - goto done; - } - } while ((ireg_intr & CPU_INTR_DOOR_BELL) == - CPU_INTR_DOOR_BELL); - - mwifiex_unmap_pci_memory(adapter, skb, PCI_DMA_TODEVICE); - - offset += txlen; - } while (true); - - mwifiex_dbg(adapter, MSG, - "info: FW download over, size %d bytes\n", offset); - - ret = 0; - -done: - dev_kfree_skb_any(skb); - return ret; -} - -/* - * This function checks the firmware status in card. - * - * The winner interface is also determined by this function. - */ -static int -mwifiex_check_fw_status(struct mwifiex_adapter *adapter, u32 poll_num) -{ - int ret = 0; - u32 firmware_stat, winner_status; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - u32 tries; - - /* Mask spurios interrupts */ - if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS_MASK, - HOST_INTR_MASK)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "Setting driver ready signature\n"); - if (mwifiex_write_reg(adapter, reg->drv_rdy, - FIRMWARE_READY_PCIE)) { - mwifiex_dbg(adapter, ERROR, - "Failed to write driver ready signature\n"); - return -1; - } - - /* Wait for firmware initialization event */ - for (tries = 0; tries < poll_num; tries++) { - if (mwifiex_read_reg(adapter, reg->fw_status, - &firmware_stat)) - ret = -1; - else - ret = 0; - if (ret) - continue; - if (firmware_stat == FIRMWARE_READY_PCIE) { - ret = 0; - break; - } else { - msleep(100); - ret = -1; - } - } - - if (ret) { - if (mwifiex_read_reg(adapter, reg->fw_status, - &winner_status)) - ret = -1; - else if (!winner_status) { - mwifiex_dbg(adapter, INFO, - "PCI-E is the winner\n"); - adapter->winner = 1; - } else { - mwifiex_dbg(adapter, ERROR, - "PCI-E is not the winner <%#x,%d>, exit dnld\n", - ret, adapter->winner); - } - } - - return ret; -} - -/* - * This function reads the interrupt status from card. - */ -static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) -{ - u32 pcie_ireg; - unsigned long flags; - - if (!mwifiex_pcie_ok_to_access_hw(adapter)) - return; - - if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, &pcie_ireg)) { - mwifiex_dbg(adapter, ERROR, "Read register failed\n"); - return; - } - - if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { - - mwifiex_pcie_disable_host_int(adapter); - - /* Clear the pending interrupts */ - if (mwifiex_write_reg(adapter, PCIE_HOST_INT_STATUS, - ~pcie_ireg)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return; - } - spin_lock_irqsave(&adapter->int_lock, flags); - adapter->int_status |= pcie_ireg; - spin_unlock_irqrestore(&adapter->int_lock, flags); - - if (!adapter->pps_uapsd_mode && - adapter->ps_state == PS_STATE_SLEEP && - mwifiex_pcie_ok_to_access_hw(adapter)) { - /* Potentially for PCIe we could get other - * interrupts like shared. Don't change power - * state until cookie is set */ - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - } - } -} - -/* - * Interrupt handler for PCIe root port - * - * This function reads the interrupt status from firmware and assigns - * the main process in workqueue which will handle the interrupt. - */ -static irqreturn_t mwifiex_pcie_interrupt(int irq, void *context) -{ - struct pci_dev *pdev = (struct pci_dev *)context; - struct pcie_service_card *card; - struct mwifiex_adapter *adapter; - - if (!pdev) { - pr_debug("info: %s: pdev is NULL\n", (u8 *)pdev); - goto exit; - } - - card = pci_get_drvdata(pdev); - if (!card || !card->adapter) { - pr_debug("info: %s: card=%p adapter=%p\n", __func__, card, - card ? card->adapter : NULL); - goto exit; - } - adapter = card->adapter; - - if (adapter->surprise_removed) - goto exit; - - mwifiex_interrupt_status(adapter); - mwifiex_queue_main_work(adapter); - -exit: - return IRQ_HANDLED; -} - -/* - * This function checks the current interrupt status. - * - * The following interrupts are checked and handled by this function - - * - Data sent - * - Command sent - * - Command received - * - Packets received - * - Events received - * - * In case of Rx packets received, the packets are uploaded from card to - * host and processed accordingly. - */ -static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) -{ - int ret; - u32 pcie_ireg; - unsigned long flags; - - spin_lock_irqsave(&adapter->int_lock, flags); - /* Clear out unused interrupts */ - pcie_ireg = adapter->int_status; - adapter->int_status = 0; - spin_unlock_irqrestore(&adapter->int_lock, flags); - - while (pcie_ireg & HOST_INTR_MASK) { - if (pcie_ireg & HOST_INTR_DNLD_DONE) { - pcie_ireg &= ~HOST_INTR_DNLD_DONE; - mwifiex_dbg(adapter, INTR, - "info: TX DNLD Done\n"); - ret = mwifiex_pcie_send_data_complete(adapter); - if (ret) - return ret; - } - if (pcie_ireg & HOST_INTR_UPLD_RDY) { - pcie_ireg &= ~HOST_INTR_UPLD_RDY; - mwifiex_dbg(adapter, INTR, - "info: Rx DATA\n"); - ret = mwifiex_pcie_process_recv_data(adapter); - if (ret) - return ret; - } - if (pcie_ireg & HOST_INTR_EVENT_RDY) { - pcie_ireg &= ~HOST_INTR_EVENT_RDY; - mwifiex_dbg(adapter, INTR, - "info: Rx EVENT\n"); - ret = mwifiex_pcie_process_event_ready(adapter); - if (ret) - return ret; - } - - if (pcie_ireg & HOST_INTR_CMD_DONE) { - pcie_ireg &= ~HOST_INTR_CMD_DONE; - if (adapter->cmd_sent) { - mwifiex_dbg(adapter, INTR, - "info: CMD sent Interrupt\n"); - adapter->cmd_sent = false; - } - /* Handle command response */ - ret = mwifiex_pcie_process_cmd_complete(adapter); - if (ret) - return ret; - } - - if (mwifiex_pcie_ok_to_access_hw(adapter)) { - if (mwifiex_read_reg(adapter, PCIE_HOST_INT_STATUS, - &pcie_ireg)) { - mwifiex_dbg(adapter, ERROR, - "Read register failed\n"); - return -1; - } - - if ((pcie_ireg != 0xFFFFFFFF) && (pcie_ireg)) { - if (mwifiex_write_reg(adapter, - PCIE_HOST_INT_STATUS, - ~pcie_ireg)) { - mwifiex_dbg(adapter, ERROR, - "Write register failed\n"); - return -1; - } - } - - } - } - mwifiex_dbg(adapter, INTR, - "info: cmd_sent=%d data_sent=%d\n", - adapter->cmd_sent, adapter->data_sent); - if (adapter->ps_state != PS_STATE_SLEEP) - mwifiex_pcie_enable_host_int(adapter); - - return 0; -} - -/* - * 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. - */ -static int mwifiex_pcie_host_to_card(struct mwifiex_adapter *adapter, u8 type, - struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "Passed NULL skb to %s\n", __func__); - return -1; - } - - if (type == MWIFIEX_TYPE_DATA) - return mwifiex_pcie_send_data(adapter, skb, tx_param); - else if (type == MWIFIEX_TYPE_CMD) - return mwifiex_pcie_send_cmd(adapter, skb); - - return 0; -} - -/* This function read/write firmware */ -static enum rdwr_status -mwifiex_pcie_rdwr_firmware(struct mwifiex_adapter *adapter, u8 doneflag) -{ - int ret, tries; - u8 ctrl_data; - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, FW_DUMP_HOST_READY); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "PCIE write err\n"); - return RDWR_STATUS_FAILURE; - } - - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - mwifiex_read_reg_byte(adapter, reg->fw_dump_ctrl, &ctrl_data); - if (ctrl_data == FW_DUMP_DONE) - return RDWR_STATUS_SUCCESS; - if (doneflag && ctrl_data == doneflag) - return RDWR_STATUS_DONE; - if (ctrl_data != FW_DUMP_HOST_READY) { - mwifiex_dbg(adapter, WARN, - "The ctrl reg was changed, re-try again!\n"); - ret = mwifiex_write_reg(adapter, reg->fw_dump_ctrl, - FW_DUMP_HOST_READY); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "PCIE write err\n"); - return RDWR_STATUS_FAILURE; - } - } - usleep_range(100, 200); - } - - mwifiex_dbg(adapter, ERROR, "Fail to pull ctrl_data\n"); - return RDWR_STATUS_FAILURE; -} - -/* This function dump firmware memory to file */ -static void mwifiex_pcie_fw_dump(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *creg = card->pcie.reg; - unsigned int reg, reg_start, reg_end; - u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; - enum rdwr_status stat; - u32 memory_size; - int ret; - - if (!card->pcie.can_dump_fw) - return; - - for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - - mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump start ==\n"); - - /* Read the number of the memories which will dump */ - stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - return; - - reg = creg->fw_dump_start; - mwifiex_read_reg_byte(adapter, reg, &dump_num); - - /* Read the length of every memory which will dump */ - for (idx = 0; idx < dump_num; idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; - - stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - return; - - memory_size = 0; - reg = creg->fw_dump_start; - for (i = 0; i < 4; i++) { - mwifiex_read_reg_byte(adapter, reg, &read_reg); - memory_size |= (read_reg << (i * 8)); - reg++; - } - - if (memory_size == 0) { - mwifiex_dbg(adapter, MSG, "Firmware dump Finished!\n"); - ret = mwifiex_write_reg(adapter, creg->fw_dump_ctrl, - FW_DUMP_READ_DONE); - if (ret) { - mwifiex_dbg(adapter, ERROR, "PCIE write err\n"); - return; - } - break; - } - - mwifiex_dbg(adapter, DUMP, - "%s_SIZE=0x%x\n", entry->mem_name, memory_size); - entry->mem_ptr = vmalloc(memory_size + 1); - entry->mem_size = memory_size; - if (!entry->mem_ptr) { - mwifiex_dbg(adapter, ERROR, - "Vmalloc %s failed\n", entry->mem_name); - return; - } - dbg_ptr = entry->mem_ptr; - end_ptr = dbg_ptr + memory_size; - - doneflag = entry->done_flag; - mwifiex_dbg(adapter, DUMP, "Start %s output, please wait...\n", - entry->mem_name); - - do { - stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); - if (RDWR_STATUS_FAILURE == stat) - return; - - reg_start = creg->fw_dump_start; - reg_end = creg->fw_dump_end; - for (reg = reg_start; reg <= reg_end; reg++) { - mwifiex_read_reg_byte(adapter, reg, dbg_ptr); - if (dbg_ptr < end_ptr) { - dbg_ptr++; - } else { - mwifiex_dbg(adapter, ERROR, - "Allocated buf not enough\n"); - return; - } - } - - if (stat != RDWR_STATUS_DONE) - continue; - - mwifiex_dbg(adapter, DUMP, - "%s done: size=0x%tx\n", - entry->mem_name, dbg_ptr - entry->mem_ptr); - break; - } while (true); - } - mwifiex_dbg(adapter, DUMP, "== mwifiex firmware dump end ==\n"); -} - -static void mwifiex_pcie_device_dump_work(struct mwifiex_adapter *adapter) -{ - mwifiex_drv_info_dump(adapter); - mwifiex_pcie_fw_dump(adapter); - mwifiex_upload_device_dump(adapter); -} - -static unsigned long iface_work_flags; -static struct mwifiex_adapter *save_adapter; -static void mwifiex_pcie_work(struct work_struct *work) -{ - if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, - &iface_work_flags)) - mwifiex_pcie_device_dump_work(save_adapter); -} - -static DECLARE_WORK(pcie_work, mwifiex_pcie_work); -/* This function dumps FW information */ -static void mwifiex_pcie_device_dump(struct mwifiex_adapter *adapter) -{ - save_adapter = adapter; - if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags)) - return; - - set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags); - - schedule_work(&pcie_work); -} - -/* - * This function initializes the PCI-E host memory space, WCB rings, etc. - * - * The following initializations steps are followed - - * - Allocate TXBD ring buffers - * - Allocate RXBD ring buffers - * - Allocate event BD ring buffers - * - Allocate command response ring buffer - * - Allocate sleep cookie buffer - */ -static int mwifiex_pcie_init(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - int ret; - struct pci_dev *pdev = card->dev; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - pci_set_drvdata(pdev, card); - - ret = pci_enable_device(pdev); - if (ret) - goto err_enable_dev; - - pci_set_master(pdev); - - mwifiex_dbg(adapter, INFO, - "try set_consistent_dma_mask(32)\n"); - ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "set_dma_mask(32) failed\n"); - goto err_set_dma_mask; - } - - ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "set_consistent_dma_mask(64) failed\n"); - goto err_set_dma_mask; - } - - ret = pci_request_region(pdev, 0, DRV_NAME); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "req_reg(0) error\n"); - goto err_req_region0; - } - card->pci_mmap = pci_iomap(pdev, 0, 0); - if (!card->pci_mmap) { - mwifiex_dbg(adapter, ERROR, "iomap(0) error\n"); - ret = -EIO; - goto err_iomap0; - } - ret = pci_request_region(pdev, 2, DRV_NAME); - if (ret) { - mwifiex_dbg(adapter, ERROR, "req_reg(2) error\n"); - goto err_req_region2; - } - card->pci_mmap1 = pci_iomap(pdev, 2, 0); - if (!card->pci_mmap1) { - mwifiex_dbg(adapter, ERROR, - "iomap(2) error\n"); - ret = -EIO; - goto err_iomap2; - } - - mwifiex_dbg(adapter, INFO, - "PCI memory map Virt0: %p PCI memory map Virt2: %p\n", - card->pci_mmap, card->pci_mmap1); - - card->cmdrsp_buf = NULL; - ret = mwifiex_pcie_create_txbd_ring(adapter); - if (ret) - goto err_cre_txbd; - ret = mwifiex_pcie_create_rxbd_ring(adapter); - if (ret) - goto err_cre_rxbd; - ret = mwifiex_pcie_create_evtbd_ring(adapter); - if (ret) - goto err_cre_evtbd; - ret = mwifiex_pcie_alloc_cmdrsp_buf(adapter); - if (ret) - goto err_alloc_cmdbuf; - if (reg->sleep_cookie) { - ret = mwifiex_pcie_alloc_sleep_cookie_buf(adapter); - if (ret) - goto err_alloc_cookie; - } else { - card->sleep_cookie_vbase = NULL; - } - return ret; - -err_alloc_cookie: - mwifiex_pcie_delete_cmdrsp_buf(adapter); -err_alloc_cmdbuf: - mwifiex_pcie_delete_evtbd_ring(adapter); -err_cre_evtbd: - mwifiex_pcie_delete_rxbd_ring(adapter); -err_cre_rxbd: - mwifiex_pcie_delete_txbd_ring(adapter); -err_cre_txbd: - pci_iounmap(pdev, card->pci_mmap1); -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: -err_set_dma_mask: - pci_disable_device(pdev); -err_enable_dev: - pci_set_drvdata(pdev, NULL); - return ret; -} - -/* - * This function cleans up the allocated card buffers. - * - * The following are freed by this function - - * - TXBD ring buffers - * - RXBD ring buffers - * - Event BD ring buffers - * - Command response ring buffer - * - Sleep cookie buffer - */ -static void mwifiex_pcie_cleanup(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - struct pci_dev *pdev = card->dev; - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - if (user_rmmod) { - mwifiex_dbg(adapter, INFO, - "Clearing driver ready signature\n"); - if (mwifiex_write_reg(adapter, reg->drv_rdy, 0x00000000)) - mwifiex_dbg(adapter, ERROR, - "Failed to write driver not-ready signature\n"); - } - - if (pdev) { - pci_iounmap(pdev, card->pci_mmap); - pci_iounmap(pdev, card->pci_mmap1); - pci_disable_device(pdev); - pci_release_region(pdev, 2); - pci_release_region(pdev, 0); - pci_set_drvdata(pdev, NULL); - } - kfree(card); -} - -/* - * This function registers the PCIE device. - * - * PCIE IRQ is claimed, block size is set and driver data is initialized. - */ -static int mwifiex_register_dev(struct mwifiex_adapter *adapter) -{ - int ret; - struct pcie_service_card *card = adapter->card; - struct pci_dev *pdev = card->dev; - - /* save adapter pointer in card */ - card->adapter = adapter; - - ret = request_irq(pdev->irq, mwifiex_pcie_interrupt, IRQF_SHARED, - "MRVL_PCIE", pdev); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "request_irq failed: ret=%d\n", ret); - adapter->card = NULL; - return -1; - } - - adapter->dev = &pdev->dev; - adapter->tx_buf_size = card->pcie.tx_buf_size; - adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; - adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); - strcpy(adapter->fw_name, card->pcie.firmware); - adapter->ext_scan = card->pcie.can_ext_scan; - - return 0; -} - -/* - * This function unregisters the PCIE device. - * - * The PCIE IRQ is released, the function is disabled and driver - * data is set to null. - */ -static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) -{ - struct pcie_service_card *card = adapter->card; - const struct mwifiex_pcie_card_reg *reg; - - if (card) { - mwifiex_dbg(adapter, INFO, - "%s(): calling free_irq()\n", __func__); - free_irq(card->dev->irq, card->dev); - - reg = card->pcie.reg; - if (reg->sleep_cookie) - mwifiex_pcie_delete_sleep_cookie_buf(adapter); - - mwifiex_pcie_delete_cmdrsp_buf(adapter); - mwifiex_pcie_delete_evtbd_ring(adapter); - mwifiex_pcie_delete_rxbd_ring(adapter); - mwifiex_pcie_delete_txbd_ring(adapter); - card->cmdrsp_buf = NULL; - } -} - -static struct mwifiex_if_ops pcie_ops = { - .init_if = mwifiex_pcie_init, - .cleanup_if = mwifiex_pcie_cleanup, - .check_fw_status = mwifiex_check_fw_status, - .prog_fw = mwifiex_prog_fw_w_helper, - .register_dev = mwifiex_register_dev, - .unregister_dev = mwifiex_unregister_dev, - .enable_int = mwifiex_pcie_enable_host_int, - .process_int_status = mwifiex_process_int_status, - .host_to_card = mwifiex_pcie_host_to_card, - .wakeup = mwifiex_pm_wakeup_card, - .wakeup_complete = mwifiex_pm_wakeup_card_complete, - - /* PCIE specific */ - .cmdrsp_complete = mwifiex_pcie_cmdrsp_complete, - .event_complete = mwifiex_pcie_event_complete, - .update_mp_end_port = NULL, - .cleanup_mpa_buf = NULL, - .init_fw_port = mwifiex_pcie_init_fw_port, - .clean_pcie_ring = mwifiex_clean_pcie_ring_buf, - .device_dump = mwifiex_pcie_device_dump, -}; - -/* - * This function initializes the PCIE driver module. - * - * This initiates the semaphore and registers the device with - * PCIE bus. - */ -static int mwifiex_pcie_init_module(void) -{ - int ret; - - pr_debug("Marvell PCIe Driver\n"); - - sema_init(&add_remove_card_sem, 1); - - /* Clear the flag in case user removes the card. */ - user_rmmod = 0; - - ret = pci_register_driver(&mwifiex_pcie); - if (ret) - pr_err("Driver register failed!\n"); - else - pr_debug("info: Driver registered successfully!\n"); - - return ret; -} - -/* - * This function cleans up the PCIE driver. - * - * The following major steps are followed for cleanup - - * - Resume the device if its suspended - * - Disconnect the device if connected - * - Shutdown the firmware - * - Unregister the device from PCIE bus. - */ -static void mwifiex_pcie_cleanup_module(void) -{ - if (!down_interruptible(&add_remove_card_sem)) - up(&add_remove_card_sem); - - /* Set the flag as user is removing this module. */ - user_rmmod = 1; - - cancel_work_sync(&pcie_work); - pci_unregister_driver(&mwifiex_pcie); -} - -module_init(mwifiex_pcie_init_module); -module_exit(mwifiex_pcie_cleanup_module); - -MODULE_AUTHOR("Marvell International Ltd."); -MODULE_DESCRIPTION("Marvell WiFi-Ex PCI-Express Driver version " PCIE_VERSION); -MODULE_VERSION(PCIE_VERSION); -MODULE_LICENSE("GPL v2"); -MODULE_FIRMWARE(PCIE8766_DEFAULT_FW_NAME); -MODULE_FIRMWARE(PCIE8897_DEFAULT_FW_NAME); -MODULE_FIRMWARE(PCIE8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/pcie.h b/drivers/net/wireless/mwifiex/pcie.h deleted file mode 100644 index 48e549c3b285..000000000000 --- a/drivers/net/wireless/mwifiex/pcie.h +++ /dev/null @@ -1,382 +0,0 @@ -/* @file mwifiex_pcie.h - * - * @brief This file contains definitions for PCI-E interface. - * driver. - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_PCIE_H -#define _MWIFIEX_PCIE_H - -#include -#include -#include - -#include "main.h" - -#define PCIE8766_DEFAULT_FW_NAME "mrvl/pcie8766_uapsta.bin" -#define PCIE8897_DEFAULT_FW_NAME "mrvl/pcie8897_uapsta.bin" -#define PCIE8997_DEFAULT_FW_NAME "mrvl/pcie8997_uapsta.bin" - -#define PCIE_VENDOR_ID_MARVELL (0x11ab) -#define PCIE_DEVICE_ID_MARVELL_88W8766P (0x2b30) -#define PCIE_DEVICE_ID_MARVELL_88W8897 (0x2b38) -#define PCIE_DEVICE_ID_MARVELL_88W8997 (0x2b42) - -/* Constants for Buffer Descriptor (BD) rings */ -#define MWIFIEX_MAX_TXRX_BD 0x20 -#define MWIFIEX_TXBD_MASK 0x3F -#define MWIFIEX_RXBD_MASK 0x3F - -#define MWIFIEX_MAX_EVT_BD 0x08 -#define MWIFIEX_EVTBD_MASK 0x0f - -/* PCIE INTERNAL REGISTERS */ -#define PCIE_SCRATCH_0_REG 0xC10 -#define PCIE_SCRATCH_1_REG 0xC14 -#define PCIE_CPU_INT_EVENT 0xC18 -#define PCIE_CPU_INT_STATUS 0xC1C -#define PCIE_HOST_INT_STATUS 0xC30 -#define PCIE_HOST_INT_MASK 0xC34 -#define PCIE_HOST_INT_STATUS_MASK 0xC3C -#define PCIE_SCRATCH_2_REG 0xC40 -#define PCIE_SCRATCH_3_REG 0xC44 -#define PCIE_SCRATCH_4_REG 0xCD0 -#define PCIE_SCRATCH_5_REG 0xCD4 -#define PCIE_SCRATCH_6_REG 0xCD8 -#define PCIE_SCRATCH_7_REG 0xCDC -#define PCIE_SCRATCH_8_REG 0xCE0 -#define PCIE_SCRATCH_9_REG 0xCE4 -#define PCIE_SCRATCH_10_REG 0xCE8 -#define PCIE_SCRATCH_11_REG 0xCEC -#define PCIE_SCRATCH_12_REG 0xCF0 -#define PCIE_RD_DATA_PTR_Q0_Q1 0xC08C -#define PCIE_WR_DATA_PTR_Q0_Q1 0xC05C - -#define CPU_INTR_DNLD_RDY BIT(0) -#define CPU_INTR_DOOR_BELL BIT(1) -#define CPU_INTR_SLEEP_CFM_DONE BIT(2) -#define CPU_INTR_RESET BIT(3) -#define CPU_INTR_EVENT_DONE BIT(5) - -#define HOST_INTR_DNLD_DONE BIT(0) -#define HOST_INTR_UPLD_RDY BIT(1) -#define HOST_INTR_CMD_DONE BIT(2) -#define HOST_INTR_EVENT_RDY BIT(3) -#define HOST_INTR_MASK (HOST_INTR_DNLD_DONE | \ - HOST_INTR_UPLD_RDY | \ - HOST_INTR_CMD_DONE | \ - HOST_INTR_EVENT_RDY) - -#define MWIFIEX_BD_FLAG_ROLLOVER_IND BIT(7) -#define MWIFIEX_BD_FLAG_FIRST_DESC BIT(0) -#define MWIFIEX_BD_FLAG_LAST_DESC BIT(1) -#define MWIFIEX_BD_FLAG_SOP BIT(0) -#define MWIFIEX_BD_FLAG_EOP BIT(1) -#define MWIFIEX_BD_FLAG_XS_SOP BIT(2) -#define MWIFIEX_BD_FLAG_XS_EOP BIT(3) -#define MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND BIT(7) -#define MWIFIEX_BD_FLAG_RX_ROLLOVER_IND BIT(10) -#define MWIFIEX_BD_FLAG_TX_START_PTR BIT(16) -#define MWIFIEX_BD_FLAG_TX_ROLLOVER_IND BIT(26) - -/* Max retry number of command write */ -#define MAX_WRITE_IOMEM_RETRY 2 -/* Define PCIE block size for firmware download */ -#define MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD 256 -/* FW awake cookie after FW ready */ -#define FW_AWAKE_COOKIE (0xAA55AA55) -#define MWIFIEX_DEF_SLEEP_COOKIE 0xBEEFBEEF -#define MWIFIEX_MAX_DELAY_COUNT 5 - -struct mwifiex_pcie_card_reg { - u16 cmd_addr_lo; - u16 cmd_addr_hi; - u16 fw_status; - u16 cmd_size; - u16 cmdrsp_addr_lo; - u16 cmdrsp_addr_hi; - u16 tx_rdptr; - u16 tx_wrptr; - u16 rx_rdptr; - u16 rx_wrptr; - u16 evt_rdptr; - u16 evt_wrptr; - u16 drv_rdy; - u16 tx_start_ptr; - u32 tx_mask; - u32 tx_wrap_mask; - u32 rx_mask; - u32 rx_wrap_mask; - u32 tx_rollover_ind; - u32 rx_rollover_ind; - u32 evt_rollover_ind; - u8 ring_flag_sop; - u8 ring_flag_eop; - u8 ring_flag_xs_sop; - u8 ring_flag_xs_eop; - u32 ring_tx_start_ptr; - u8 pfu_enabled; - u8 sleep_cookie; - u16 fw_dump_ctrl; - u16 fw_dump_start; - u16 fw_dump_end; -}; - -static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { - .cmd_addr_lo = PCIE_SCRATCH_0_REG, - .cmd_addr_hi = PCIE_SCRATCH_1_REG, - .cmd_size = PCIE_SCRATCH_2_REG, - .fw_status = PCIE_SCRATCH_3_REG, - .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, - .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, - .tx_rdptr = PCIE_SCRATCH_6_REG, - .tx_wrptr = PCIE_SCRATCH_7_REG, - .rx_rdptr = PCIE_SCRATCH_8_REG, - .rx_wrptr = PCIE_SCRATCH_9_REG, - .evt_rdptr = PCIE_SCRATCH_10_REG, - .evt_wrptr = PCIE_SCRATCH_11_REG, - .drv_rdy = PCIE_SCRATCH_12_REG, - .tx_start_ptr = 0, - .tx_mask = MWIFIEX_TXBD_MASK, - .tx_wrap_mask = 0, - .rx_mask = MWIFIEX_RXBD_MASK, - .rx_wrap_mask = 0, - .tx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, - .rx_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, - .evt_rollover_ind = MWIFIEX_BD_FLAG_ROLLOVER_IND, - .ring_flag_sop = 0, - .ring_flag_eop = 0, - .ring_flag_xs_sop = 0, - .ring_flag_xs_eop = 0, - .ring_tx_start_ptr = 0, - .pfu_enabled = 0, - .sleep_cookie = 1, -}; - -static const struct mwifiex_pcie_card_reg mwifiex_reg_8897 = { - .cmd_addr_lo = PCIE_SCRATCH_0_REG, - .cmd_addr_hi = PCIE_SCRATCH_1_REG, - .cmd_size = PCIE_SCRATCH_2_REG, - .fw_status = PCIE_SCRATCH_3_REG, - .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, - .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, - .tx_rdptr = PCIE_RD_DATA_PTR_Q0_Q1, - .tx_wrptr = PCIE_WR_DATA_PTR_Q0_Q1, - .rx_rdptr = PCIE_WR_DATA_PTR_Q0_Q1, - .rx_wrptr = PCIE_RD_DATA_PTR_Q0_Q1, - .evt_rdptr = PCIE_SCRATCH_10_REG, - .evt_wrptr = PCIE_SCRATCH_11_REG, - .drv_rdy = PCIE_SCRATCH_12_REG, - .tx_start_ptr = 16, - .tx_mask = 0x03FF0000, - .tx_wrap_mask = 0x07FF0000, - .rx_mask = 0x000003FF, - .rx_wrap_mask = 0x000007FF, - .tx_rollover_ind = MWIFIEX_BD_FLAG_TX_ROLLOVER_IND, - .rx_rollover_ind = MWIFIEX_BD_FLAG_RX_ROLLOVER_IND, - .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, - .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, - .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, - .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, - .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, - .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, - .pfu_enabled = 1, - .sleep_cookie = 0, - .fw_dump_ctrl = 0xcf4, - .fw_dump_start = 0xcf8, - .fw_dump_end = 0xcff, -}; - -static const struct mwifiex_pcie_card_reg mwifiex_reg_8997 = { - .cmd_addr_lo = PCIE_SCRATCH_0_REG, - .cmd_addr_hi = PCIE_SCRATCH_1_REG, - .cmd_size = PCIE_SCRATCH_2_REG, - .fw_status = PCIE_SCRATCH_3_REG, - .cmdrsp_addr_lo = PCIE_SCRATCH_4_REG, - .cmdrsp_addr_hi = PCIE_SCRATCH_5_REG, - .tx_rdptr = 0xC1A4, - .tx_wrptr = 0xC1A8, - .rx_rdptr = 0xC1A8, - .rx_wrptr = 0xC1A4, - .evt_rdptr = PCIE_SCRATCH_10_REG, - .evt_wrptr = PCIE_SCRATCH_11_REG, - .drv_rdy = PCIE_SCRATCH_12_REG, - .tx_start_ptr = 16, - .tx_mask = 0x0FFF0000, - .tx_wrap_mask = 0x01FF0000, - .rx_mask = 0x00000FFF, - .rx_wrap_mask = 0x000001FF, - .tx_rollover_ind = BIT(28), - .rx_rollover_ind = BIT(12), - .evt_rollover_ind = MWIFIEX_BD_FLAG_EVT_ROLLOVER_IND, - .ring_flag_sop = MWIFIEX_BD_FLAG_SOP, - .ring_flag_eop = MWIFIEX_BD_FLAG_EOP, - .ring_flag_xs_sop = MWIFIEX_BD_FLAG_XS_SOP, - .ring_flag_xs_eop = MWIFIEX_BD_FLAG_XS_EOP, - .ring_tx_start_ptr = MWIFIEX_BD_FLAG_TX_START_PTR, - .pfu_enabled = 1, - .sleep_cookie = 0, -}; - -struct mwifiex_pcie_device { - const char *firmware; - const struct mwifiex_pcie_card_reg *reg; - u16 blksz_fw_dl; - u16 tx_buf_size; - bool can_dump_fw; - bool can_ext_scan; -}; - -static const struct mwifiex_pcie_device mwifiex_pcie8766 = { - .firmware = PCIE8766_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_8766, - .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .can_dump_fw = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_pcie_device mwifiex_pcie8897 = { - .firmware = PCIE8897_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_8897, - .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .can_dump_fw = true, - .can_ext_scan = true, -}; - -static const struct mwifiex_pcie_device mwifiex_pcie8997 = { - .firmware = PCIE8997_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_8997, - .blksz_fw_dl = MWIFIEX_PCIE_BLOCK_SIZE_FW_DNLD, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .can_dump_fw = false, - .can_ext_scan = true, -}; - -struct mwifiex_evt_buf_desc { - u64 paddr; - u16 len; - u16 flags; -} __packed; - -struct mwifiex_pcie_buf_desc { - u64 paddr; - u16 len; - u16 flags; -} __packed; - -struct mwifiex_pfu_buf_desc { - u16 flags; - u16 offset; - u16 frag_len; - u16 len; - u64 paddr; - u32 reserved; -} __packed; - -struct pcie_service_card { - struct pci_dev *dev; - struct mwifiex_adapter *adapter; - struct mwifiex_pcie_device pcie; - - u8 txbd_flush; - u32 txbd_wrptr; - u32 txbd_rdptr; - u32 txbd_ring_size; - u8 *txbd_ring_vbase; - dma_addr_t txbd_ring_pbase; - void *txbd_ring[MWIFIEX_MAX_TXRX_BD]; - struct sk_buff *tx_buf_list[MWIFIEX_MAX_TXRX_BD]; - - u32 rxbd_wrptr; - u32 rxbd_rdptr; - u32 rxbd_ring_size; - u8 *rxbd_ring_vbase; - dma_addr_t rxbd_ring_pbase; - void *rxbd_ring[MWIFIEX_MAX_TXRX_BD]; - struct sk_buff *rx_buf_list[MWIFIEX_MAX_TXRX_BD]; - - u32 evtbd_wrptr; - u32 evtbd_rdptr; - u32 evtbd_ring_size; - u8 *evtbd_ring_vbase; - dma_addr_t evtbd_ring_pbase; - void *evtbd_ring[MWIFIEX_MAX_EVT_BD]; - struct sk_buff *evt_buf_list[MWIFIEX_MAX_EVT_BD]; - - struct sk_buff *cmd_buf; - struct sk_buff *cmdrsp_buf; - u8 *sleep_cookie_vbase; - dma_addr_t sleep_cookie_pbase; - void __iomem *pci_mmap; - void __iomem *pci_mmap1; -}; - -static inline int -mwifiex_pcie_txbd_empty(struct pcie_service_card *card, u32 rdptr) -{ - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - switch (card->dev->device) { - case PCIE_DEVICE_ID_MARVELL_88W8766P: - if (((card->txbd_wrptr & reg->tx_mask) == - (rdptr & reg->tx_mask)) && - ((card->txbd_wrptr & reg->tx_rollover_ind) != - (rdptr & reg->tx_rollover_ind))) - return 1; - break; - case PCIE_DEVICE_ID_MARVELL_88W8897: - if (((card->txbd_wrptr & reg->tx_mask) == - (rdptr & reg->tx_mask)) && - ((card->txbd_wrptr & reg->tx_rollover_ind) == - (rdptr & reg->tx_rollover_ind))) - return 1; - break; - } - - return 0; -} - -static inline int -mwifiex_pcie_txbd_not_full(struct pcie_service_card *card) -{ - const struct mwifiex_pcie_card_reg *reg = card->pcie.reg; - - switch (card->dev->device) { - case PCIE_DEVICE_ID_MARVELL_88W8766P: - if (((card->txbd_wrptr & reg->tx_mask) != - (card->txbd_rdptr & reg->tx_mask)) || - ((card->txbd_wrptr & reg->tx_rollover_ind) != - (card->txbd_rdptr & reg->tx_rollover_ind))) - return 1; - break; - case PCIE_DEVICE_ID_MARVELL_88W8897: - case PCIE_DEVICE_ID_MARVELL_88W8997: - if (((card->txbd_wrptr & reg->tx_mask) != - (card->txbd_rdptr & reg->tx_mask)) || - ((card->txbd_wrptr & reg->tx_rollover_ind) == - (card->txbd_rdptr & reg->tx_rollover_ind))) - return 1; - break; - } - - return 0; -} - -#endif /* _MWIFIEX_PCIE_H */ diff --git a/drivers/net/wireless/mwifiex/scan.c b/drivers/net/wireless/mwifiex/scan.c deleted file mode 100644 index c20017ced566..000000000000 --- a/drivers/net/wireless/mwifiex/scan.c +++ /dev/null @@ -1,2639 +0,0 @@ -/* - * Marvell Wireless LAN device driver: scan ioctl and command handling - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "11n.h" -#include "cfg80211.h" - -/* The maximum number of channels the firmware can scan per command */ -#define MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN 14 - -#define MWIFIEX_DEF_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(struct mwifiex_ie_types_header) \ - + (MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN \ - *sizeof(struct mwifiex_chan_scan_param_set))) - -/* Memory needed to store supported rate */ -#define RATE_TLV_MAX_SIZE (sizeof(struct mwifiex_ie_types_rates_param_set) \ - + HOSTCMD_SUPPORTED_RATES) - -/* Memory needed to store a max number/size WildCard SSID TLV for a firmware - scan */ -#define WILDCARD_SSID_TLV_MAX_SIZE \ - (MWIFIEX_MAX_SSID_LIST_LENGTH * \ - (sizeof(struct mwifiex_ie_types_wildcard_ssid_params) \ - + IEEE80211_MAX_SSID_LEN)) - -/* Maximum memory needed for a mwifiex_scan_cmd_config with all TLVs at max */ -#define MAX_SCAN_CFG_ALLOC (sizeof(struct mwifiex_scan_cmd_config) \ - + sizeof(struct mwifiex_ie_types_num_probes) \ - + sizeof(struct mwifiex_ie_types_htcap) \ - + CHAN_TLV_MAX_SIZE \ - + RATE_TLV_MAX_SIZE \ - + WILDCARD_SSID_TLV_MAX_SIZE) - - -union mwifiex_scan_cmd_config_tlv { - /* Scan configuration (variable length) */ - struct mwifiex_scan_cmd_config config; - /* Max allocated block */ - u8 config_alloc_buf[MAX_SCAN_CFG_ALLOC]; -}; - -enum cipher_suite { - CIPHER_SUITE_TKIP, - CIPHER_SUITE_CCMP, - CIPHER_SUITE_MAX -}; -static u8 mwifiex_wpa_oui[CIPHER_SUITE_MAX][4] = { - { 0x00, 0x50, 0xf2, 0x02 }, /* TKIP */ - { 0x00, 0x50, 0xf2, 0x04 }, /* AES */ -}; -static u8 mwifiex_rsn_oui[CIPHER_SUITE_MAX][4] = { - { 0x00, 0x0f, 0xac, 0x02 }, /* TKIP */ - { 0x00, 0x0f, 0xac, 0x04 }, /* AES */ -}; - -/* - * This function parses a given IE for a given OUI. - * - * This is used to parse a WPA/RSN IE to find if it has - * a given oui in PTK. - */ -static u8 -mwifiex_search_oui_in_ie(struct ie_body *iebody, u8 *oui) -{ - u8 count; - - count = iebody->ptk_cnt[0]; - - /* 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(iebody->ptk_body, oui, sizeof(iebody->ptk_body))) - return MWIFIEX_OUI_PRESENT; - - --count; - if (count) - iebody = (struct ie_body *) ((u8 *) iebody + - sizeof(iebody->ptk_body)); - } - - pr_debug("info: %s: OUI is not found in PTK\n", __func__); - return MWIFIEX_OUI_NOT_PRESENT; -} - -/* - * This function checks if a given OUI is present in a RSN IE. - * - * The function first checks if a RSN IE is present or not in the - * BSS descriptor. It tries to locate the OUI only if such an IE is - * present. - */ -static u8 -mwifiex_is_rsn_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) -{ - u8 *oui; - struct ie_body *iebody; - u8 ret = MWIFIEX_OUI_NOT_PRESENT; - - if (((bss_desc->bcn_rsn_ie) && ((*(bss_desc->bcn_rsn_ie)). - ieee_hdr.element_id == WLAN_EID_RSN))) { - iebody = (struct ie_body *) - (((u8 *) bss_desc->bcn_rsn_ie->data) + - RSN_GTK_OUI_OFFSET); - oui = &mwifiex_rsn_oui[cipher][0]; - ret = mwifiex_search_oui_in_ie(iebody, oui); - if (ret) - return ret; - } - return ret; -} - -/* - * This function checks if a given OUI is present in a WPA IE. - * - * The function first checks if a WPA IE is present or not in the - * BSS descriptor. It tries to locate the OUI only if such an IE is - * present. - */ -static u8 -mwifiex_is_wpa_oui_present(struct mwifiex_bssdescriptor *bss_desc, u32 cipher) -{ - u8 *oui; - struct ie_body *iebody; - u8 ret = MWIFIEX_OUI_NOT_PRESENT; - - if (((bss_desc->bcn_wpa_ie) && - ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id == - WLAN_EID_VENDOR_SPECIFIC))) { - iebody = (struct ie_body *) bss_desc->bcn_wpa_ie->data; - oui = &mwifiex_wpa_oui[cipher][0]; - ret = mwifiex_search_oui_in_ie(iebody, oui); - if (ret) - return ret; - } - return ret; -} - -/* - * This function compares two SSIDs and checks if they match. - */ -s32 -mwifiex_ssid_cmp(struct cfg80211_ssid *ssid1, struct cfg80211_ssid *ssid2) -{ - if (!ssid1 || !ssid2 || (ssid1->ssid_len != ssid2->ssid_len)) - return -1; - return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssid_len); -} - -/* - * This function checks if wapi is enabled in driver and scanned network is - * compatible with it. - */ -static bool -mwifiex_is_bss_wapi(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (priv->sec_info.wapi_enabled && - (bss_desc->bcn_wapi_ie && - ((*(bss_desc->bcn_wapi_ie)).ieee_hdr.element_id == - WLAN_EID_BSS_AC_ACCESS_DELAY))) { - return true; - } - return false; -} - -/* - * This function checks if driver is configured with no security mode and - * scanned network is compatible with it. - */ -static bool -mwifiex_is_bss_no_sec(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && ((!bss_desc->bcn_wpa_ie) || - ((*(bss_desc->bcn_wpa_ie)).vend_hdr.element_id != - WLAN_EID_VENDOR_SPECIFIC)) && - ((!bss_desc->bcn_rsn_ie) || - ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != - WLAN_EID_RSN)) && - !priv->sec_info.encryption_mode && !bss_desc->privacy) { - return true; - } - return false; -} - -/* - * This function checks if static WEP is enabled in driver and scanned network - * is compatible with it. - */ -static bool -mwifiex_is_bss_static_wep(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && bss_desc->privacy) { - return true; - } - return false; -} - -/* - * This function checks if wpa is enabled in driver and scanned network is - * compatible with it. - */ -static bool -mwifiex_is_bss_wpa(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && ((bss_desc->bcn_wpa_ie) && - ((*(bss_desc->bcn_wpa_ie)). - vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC)) - /* - * Privacy bit may NOT be set in some APs like - * LinkSys WRT54G && bss_desc->privacy - */ - ) { - mwifiex_dbg(priv->adapter, INFO, - "info: %s: WPA:\t" - "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\t" - "EncMode=%#x privacy=%#x\n", __func__, - (bss_desc->bcn_wpa_ie) ? - (*bss_desc->bcn_wpa_ie). - vend_hdr.element_id : 0, - (bss_desc->bcn_rsn_ie) ? - (*bss_desc->bcn_rsn_ie). - ieee_hdr.element_id : 0, - (priv->sec_info.wep_enabled) ? "e" : "d", - (priv->sec_info.wpa_enabled) ? "e" : "d", - (priv->sec_info.wpa2_enabled) ? "e" : "d", - priv->sec_info.encryption_mode, - bss_desc->privacy); - return true; - } - return false; -} - -/* - * This function checks if wpa2 is enabled in driver and scanned network is - * compatible with it. - */ -static bool -mwifiex_is_bss_wpa2(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && - !priv->sec_info.wpa_enabled && - priv->sec_info.wpa2_enabled && - ((bss_desc->bcn_rsn_ie) && - ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id == WLAN_EID_RSN))) { - /* - * Privacy bit may NOT be set in some APs like - * LinkSys WRT54G && bss_desc->privacy - */ - mwifiex_dbg(priv->adapter, INFO, - "info: %s: WPA2:\t" - "wpa_ie=%#x wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s\t" - "EncMode=%#x privacy=%#x\n", __func__, - (bss_desc->bcn_wpa_ie) ? - (*bss_desc->bcn_wpa_ie). - vend_hdr.element_id : 0, - (bss_desc->bcn_rsn_ie) ? - (*bss_desc->bcn_rsn_ie). - ieee_hdr.element_id : 0, - (priv->sec_info.wep_enabled) ? "e" : "d", - (priv->sec_info.wpa_enabled) ? "e" : "d", - (priv->sec_info.wpa2_enabled) ? "e" : "d", - priv->sec_info.encryption_mode, - bss_desc->privacy); - return true; - } - return false; -} - -/* - * This function checks if adhoc AES is enabled in driver and scanned network is - * compatible with it. - */ -static bool -mwifiex_is_bss_adhoc_aes(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && - ((!bss_desc->bcn_wpa_ie) || - ((*(bss_desc->bcn_wpa_ie)). - vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && - ((!bss_desc->bcn_rsn_ie) || - ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && - !priv->sec_info.encryption_mode && bss_desc->privacy) { - return true; - } - return false; -} - -/* - * This function checks if dynamic WEP is enabled in driver and scanned network - * is compatible with it. - */ -static bool -mwifiex_is_bss_dynamic_wep(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - if (!priv->sec_info.wep_enabled && !priv->sec_info.wpa_enabled && - !priv->sec_info.wpa2_enabled && - ((!bss_desc->bcn_wpa_ie) || - ((*(bss_desc->bcn_wpa_ie)). - vend_hdr.element_id != WLAN_EID_VENDOR_SPECIFIC)) && - ((!bss_desc->bcn_rsn_ie) || - ((*(bss_desc->bcn_rsn_ie)).ieee_hdr.element_id != WLAN_EID_RSN)) && - priv->sec_info.encryption_mode && bss_desc->privacy) { - mwifiex_dbg(priv->adapter, INFO, - "info: %s: dynamic\t" - "WEP: wpa_ie=%#x wpa2_ie=%#x\t" - "EncMode=%#x privacy=%#x\n", - __func__, - (bss_desc->bcn_wpa_ie) ? - (*bss_desc->bcn_wpa_ie). - vend_hdr.element_id : 0, - (bss_desc->bcn_rsn_ie) ? - (*bss_desc->bcn_rsn_ie). - ieee_hdr.element_id : 0, - priv->sec_info.encryption_mode, - bss_desc->privacy); - return true; - } - return false; -} - -/* - * This function checks if a scanned network is 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 - * - * Compatibility is not matched while roaming, except for mode. - */ -static s32 -mwifiex_is_network_compatible(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc, u32 mode) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - bss_desc->disable_11n = false; - - /* Don't check for compatibility if roaming */ - if (priv->media_connected && - (priv->bss_mode == NL80211_IFTYPE_STATION) && - (bss_desc->bss_mode == NL80211_IFTYPE_STATION)) - return 0; - - if (priv->wps.session_enable) { - mwifiex_dbg(adapter, IOCTL, - "info: return success directly in WPS period\n"); - return 0; - } - - if (bss_desc->chan_sw_ie_present) { - mwifiex_dbg(adapter, INFO, - "Don't connect to AP with WLAN_EID_CHANNEL_SWITCH\n"); - return -1; - } - - if (mwifiex_is_bss_wapi(priv, bss_desc)) { - mwifiex_dbg(adapter, INFO, - "info: return success for WAPI AP\n"); - return 0; - } - - if (bss_desc->bss_mode == mode) { - if (mwifiex_is_bss_no_sec(priv, bss_desc)) { - /* No security */ - return 0; - } else if (mwifiex_is_bss_static_wep(priv, bss_desc)) { - /* Static WEP enabled */ - mwifiex_dbg(adapter, INFO, - "info: Disable 11n in WEP mode.\n"); - bss_desc->disable_11n = true; - return 0; - } else if (mwifiex_is_bss_wpa(priv, bss_desc)) { - /* WPA enabled */ - if (((priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN) && - bss_desc->bcn_ht_cap) && - !mwifiex_is_wpa_oui_present(bss_desc, - CIPHER_SUITE_CCMP)) { - - if (mwifiex_is_wpa_oui_present - (bss_desc, CIPHER_SUITE_TKIP)) { - mwifiex_dbg(adapter, INFO, - "info: Disable 11n if AES\t" - "is not supported by AP\n"); - bss_desc->disable_11n = true; - } else { - return -1; - } - } - return 0; - } else if (mwifiex_is_bss_wpa2(priv, bss_desc)) { - /* WPA2 enabled */ - if (((priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN) && - bss_desc->bcn_ht_cap) && - !mwifiex_is_rsn_oui_present(bss_desc, - CIPHER_SUITE_CCMP)) { - - if (mwifiex_is_rsn_oui_present - (bss_desc, CIPHER_SUITE_TKIP)) { - mwifiex_dbg(adapter, INFO, - "info: Disable 11n if AES\t" - "is not supported by AP\n"); - bss_desc->disable_11n = true; - } else { - return -1; - } - } - return 0; - } else if (mwifiex_is_bss_adhoc_aes(priv, bss_desc)) { - /* Ad-hoc AES enabled */ - return 0; - } else if (mwifiex_is_bss_dynamic_wep(priv, bss_desc)) { - /* Dynamic WEP enabled */ - return 0; - } - - /* Security doesn't match */ - mwifiex_dbg(adapter, ERROR, - "info: %s: failed: wpa_ie=%#x wpa2_ie=%#x WEP=%s\t" - "WPA=%s WPA2=%s EncMode=%#x privacy=%#x\n", - __func__, - (bss_desc->bcn_wpa_ie) ? - (*bss_desc->bcn_wpa_ie).vend_hdr.element_id : 0, - (bss_desc->bcn_rsn_ie) ? - (*bss_desc->bcn_rsn_ie).ieee_hdr.element_id : 0, - (priv->sec_info.wep_enabled) ? "e" : "d", - (priv->sec_info.wpa_enabled) ? "e" : "d", - (priv->sec_info.wpa2_enabled) ? "e" : "d", - priv->sec_info.encryption_mode, bss_desc->privacy); - return -1; - } - - /* Mode doesn't match */ - return -1; -} - -/* - * This function creates a channel list for the driver to scan, based - * on region/band information. - * - * This routine is used for any scan that is not provided with a - * specific channel list to scan. - */ -static int -mwifiex_scan_create_channel_list(struct mwifiex_private *priv, - const struct mwifiex_user_scan_cfg - *user_scan_in, - struct mwifiex_chan_scan_param_set - *scan_chan_list, - u8 filtered_scan) -{ - enum ieee80211_band band; - struct ieee80211_supported_band *sband; - struct ieee80211_channel *ch; - struct mwifiex_adapter *adapter = priv->adapter; - int chan_idx = 0, i; - - 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_chan_list[chan_idx].radio_type = band; - - if (user_scan_in && - user_scan_in->chan_list[0].scan_time) - scan_chan_list[chan_idx].max_scan_time = - cpu_to_le16((u16) user_scan_in-> - chan_list[0].scan_time); - else if (ch->flags & IEEE80211_CHAN_NO_IR) - scan_chan_list[chan_idx].max_scan_time = - cpu_to_le16(adapter->passive_scan_time); - else - scan_chan_list[chan_idx].max_scan_time = - cpu_to_le16(adapter->active_scan_time); - - if (ch->flags & IEEE80211_CHAN_NO_IR) - scan_chan_list[chan_idx].chan_scan_mode_bitmap - |= (MWIFIEX_PASSIVE_SCAN | - MWIFIEX_HIDDEN_SSID_REPORT); - else - scan_chan_list[chan_idx].chan_scan_mode_bitmap - &= ~MWIFIEX_PASSIVE_SCAN; - scan_chan_list[chan_idx].chan_number = - (u32) ch->hw_value; - if (filtered_scan) { - scan_chan_list[chan_idx].max_scan_time = - cpu_to_le16(adapter->specific_scan_time); - scan_chan_list[chan_idx].chan_scan_mode_bitmap - |= MWIFIEX_DISABLE_CHAN_FILT; - } - chan_idx++; - } - - } - return chan_idx; -} - -/* This function appends rate TLV to scan config command. */ -static int -mwifiex_append_rate_tlv(struct mwifiex_private *priv, - struct mwifiex_scan_cmd_config *scan_cfg_out, - u8 radio) -{ - struct mwifiex_ie_types_rates_param_set *rates_tlv; - u8 rates[MWIFIEX_SUPPORTED_RATES], *tlv_pos; - u32 rates_size; - - memset(rates, 0, sizeof(rates)); - - tlv_pos = (u8 *)scan_cfg_out->tlv_buf + scan_cfg_out->tlv_buf_len; - - if (priv->scan_request) - rates_size = mwifiex_get_rates_from_cfg80211(priv, rates, - radio); - else - rates_size = mwifiex_get_supported_rates(priv, rates); - - mwifiex_dbg(priv->adapter, CMD, - "info: SCAN_CMD: Rates size = %d\n", - rates_size); - rates_tlv = (struct mwifiex_ie_types_rates_param_set *)tlv_pos; - rates_tlv->header.type = cpu_to_le16(WLAN_EID_SUPP_RATES); - rates_tlv->header.len = cpu_to_le16((u16) rates_size); - memcpy(rates_tlv->rates, rates, rates_size); - scan_cfg_out->tlv_buf_len += sizeof(rates_tlv->header) + rates_size; - - return rates_size; -} - -/* - * This function constructs and sends multiple scan config commands to - * the firmware. - * - * Previous routines in the code flow have created a scan command configuration - * with any requested TLVs. This function splits the channel TLV into maximum - * channels supported per scan lists and sends the portion of the channel TLV, - * along with the other TLVs, to the firmware. - */ -static int -mwifiex_scan_channel_list(struct mwifiex_private *priv, - u32 max_chan_per_scan, u8 filtered_scan, - struct mwifiex_scan_cmd_config *scan_cfg_out, - struct mwifiex_ie_types_chan_list_param_set - *chan_tlv_out, - struct mwifiex_chan_scan_param_set *scan_chan_list) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret = 0; - struct mwifiex_chan_scan_param_set *tmp_chan_list; - struct mwifiex_chan_scan_param_set *start_chan; - struct cmd_ctrl_node *cmd_node, *tmp_node; - unsigned long flags; - u32 tlv_idx, rates_size, cmd_no; - u32 total_scan_time; - u32 done_early; - u8 radio_type; - - if (!scan_cfg_out || !chan_tlv_out || !scan_chan_list) { - mwifiex_dbg(priv->adapter, ERROR, - "info: Scan: Null detect: %p, %p, %p\n", - scan_cfg_out, chan_tlv_out, scan_chan_list); - return -1; - } - - /* Check csa channel expiry before preparing scan list */ - mwifiex_11h_get_csa_closed_channel(priv); - - chan_tlv_out->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); - - /* Set the temp channel struct pointer to the start of the desired - list */ - tmp_chan_list = scan_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 (tmp_chan_list->chan_number) { - - tlv_idx = 0; - total_scan_time = 0; - radio_type = 0; - chan_tlv_out->header.len = 0; - start_chan = tmp_chan_list; - done_early = false; - - /* - * 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 && - tmp_chan_list->chan_number && !done_early) { - - if (tmp_chan_list->chan_number == priv->csa_chan) { - tmp_chan_list++; - continue; - } - - radio_type = tmp_chan_list->radio_type; - mwifiex_dbg(priv->adapter, INFO, - "info: Scan: Chan(%3d), Radio(%d),\t" - "Mode(%d, %d), Dur(%d)\n", - tmp_chan_list->chan_number, - tmp_chan_list->radio_type, - tmp_chan_list->chan_scan_mode_bitmap - & MWIFIEX_PASSIVE_SCAN, - (tmp_chan_list->chan_scan_mode_bitmap - & MWIFIEX_DISABLE_CHAN_FILT) >> 1, - le16_to_cpu(tmp_chan_list->max_scan_time)); - - /* Copy the current channel TLV to the command being - prepared */ - memcpy(chan_tlv_out->chan_scan_param + tlv_idx, - tmp_chan_list, - sizeof(chan_tlv_out->chan_scan_param)); - - /* Increment the TLV header length by the size - appended */ - le16_add_cpu(&chan_tlv_out->header.len, - sizeof(chan_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. - */ - scan_cfg_out->tlv_buf_len = (u32) ((u8 *) chan_tlv_out - - scan_cfg_out->tlv_buf); - - /* Add the size of the channel tlv header and the data - length */ - scan_cfg_out->tlv_buf_len += - (sizeof(chan_tlv_out->header) - + le16_to_cpu(chan_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 += - le16_to_cpu(tmp_chan_list->max_scan_time); - - done_early = false; - - /* 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 && - (tmp_chan_list->chan_number == 1 || - tmp_chan_list->chan_number == 6 || - tmp_chan_list->chan_number == 11)) - done_early = true; - - /* Increment the tmp pointer to the next channel to - be scanned */ - tmp_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 && - (tmp_chan_list->chan_number == 1 || - tmp_chan_list->chan_number == 6 || - tmp_chan_list->chan_number == 11)) - done_early = true; - } - - /* The total scan time should be less than scan command timeout - value */ - if (total_scan_time > MWIFIEX_MAX_TOTAL_SCAN_TIME) { - mwifiex_dbg(priv->adapter, ERROR, - "total scan time %dms\t" - "is over limit (%dms), scan skipped\n", - total_scan_time, - MWIFIEX_MAX_TOTAL_SCAN_TIME); - ret = -1; - break; - } - - rates_size = mwifiex_append_rate_tlv(priv, scan_cfg_out, - radio_type); - - priv->adapter->scan_channels = start_chan; - - /* Send the scan command to the firmware with the specified - cfg */ - if (priv->adapter->ext_scan) - cmd_no = HostCmd_CMD_802_11_SCAN_EXT; - else - cmd_no = HostCmd_CMD_802_11_SCAN; - - ret = mwifiex_send_cmd(priv, cmd_no, HostCmd_ACT_GEN_SET, - 0, scan_cfg_out, false); - - /* rate IE is updated per scan command but same starting - * pointer is used each time so that rate IE from earlier - * scan_cfg_out->buf is overwritten with new one. - */ - scan_cfg_out->tlv_buf_len -= - sizeof(struct mwifiex_ie_types_header) + rates_size; - - if (ret) { - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, - list) { - list_del(&cmd_node->list); - cmd_node->wait_q_enabled = false; - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - break; - } - } - - if (ret) - return -1; - - return 0; -} - -/* - * This function constructs a scan command configuration structure to use - * in scan commands. - * - * Application layer or other functions can invoke network scanning - * with a scan configuration supplied in a user scan configuration structure. - * This structure is used as the basis of one or many scan command configuration - * commands that are sent to the command processing module and eventually to the - * firmware. - * - * This function creates a scan command configuration structure 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, the filter is disabled/cleared. - * If the number of probes is not set, adapter default setting is used. - */ -static void -mwifiex_config_scan(struct mwifiex_private *priv, - const struct mwifiex_user_scan_cfg *user_scan_in, - struct mwifiex_scan_cmd_config *scan_cfg_out, - struct mwifiex_ie_types_chan_list_param_set **chan_list_out, - struct mwifiex_chan_scan_param_set *scan_chan_list, - u8 *max_chan_per_scan, u8 *filtered_scan, - u8 *scan_current_only) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_num_probes *num_probes_tlv; - struct mwifiex_ie_types_scan_chan_gap *chan_gap_tlv; - struct mwifiex_ie_types_wildcard_ssid_params *wildcard_ssid_tlv; - struct mwifiex_ie_types_bssid_list *bssid_tlv; - u8 *tlv_pos; - u32 num_probes; - u32 ssid_len; - u32 chan_idx; - u32 chan_num; - u32 scan_type; - u16 scan_dur; - u8 channel; - u8 radio_type; - int i; - u8 ssid_filter; - struct mwifiex_ie_types_htcap *ht_cap; - struct mwifiex_ie_types_bss_mode *bss_mode; - - /* 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 *chan_list_out. The difference - between the *chan_list_out and the tlv_buf start will be used to - calculate the size of anything we add in this routine. */ - scan_cfg_out->tlv_buf_len = 0; - - /* Running tlv pointer. Assigned to chan_list_out at end of function - so later routines know where channels can be added to the command - buf */ - tlv_pos = scan_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 */ - *filtered_scan = false; - - /* 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. */ - *scan_current_only = false; - - if (user_scan_in) { - - /* Default the ssid_filter flag to TRUE, set false under - certain wildcard conditions and qualified by the existence - of an SSID list before marking the scan as filtered */ - ssid_filter = true; - - /* Set the BSS type scan filter, use Adapter setting if - unset */ - scan_cfg_out->bss_mode = - (user_scan_in->bss_mode ? (u8) user_scan_in-> - bss_mode : (u8) adapter->scan_mode); - - /* Set the number of probes to send, use Adapter setting - if unset */ - num_probes = - (user_scan_in->num_probes ? user_scan_in-> - num_probes : adapter->scan_probes); - - /* - * Set the BSSID filter to the incoming configuration, - * if non-zero. If not set, it will remain disabled - * (all zeros). - */ - memcpy(scan_cfg_out->specific_bssid, - user_scan_in->specific_bssid, - sizeof(scan_cfg_out->specific_bssid)); - - if (adapter->ext_scan && - !is_zero_ether_addr(scan_cfg_out->specific_bssid)) { - bssid_tlv = - (struct mwifiex_ie_types_bssid_list *)tlv_pos; - bssid_tlv->header.type = cpu_to_le16(TLV_TYPE_BSSID); - bssid_tlv->header.len = cpu_to_le16(ETH_ALEN); - memcpy(bssid_tlv->bssid, user_scan_in->specific_bssid, - ETH_ALEN); - tlv_pos += sizeof(struct mwifiex_ie_types_bssid_list); - } - - for (i = 0; i < user_scan_in->num_ssids; i++) { - ssid_len = user_scan_in->ssid_list[i].ssid_len; - - wildcard_ssid_tlv = - (struct mwifiex_ie_types_wildcard_ssid_params *) - tlv_pos; - wildcard_ssid_tlv->header.type = - cpu_to_le16(TLV_TYPE_WILDCARDSSID); - wildcard_ssid_tlv->header.len = cpu_to_le16( - (u16) (ssid_len + sizeof(wildcard_ssid_tlv-> - max_ssid_length))); - - /* - * max_ssid_length = 0 tells firmware to perform - * specific scan for the SSID filled, whereas - * max_ssid_length = IEEE80211_MAX_SSID_LEN is for - * wildcard scan. - */ - if (ssid_len) - wildcard_ssid_tlv->max_ssid_length = 0; - else - wildcard_ssid_tlv->max_ssid_length = - IEEE80211_MAX_SSID_LEN; - - if (!memcmp(user_scan_in->ssid_list[i].ssid, - "DIRECT-", 7)) - wildcard_ssid_tlv->max_ssid_length = 0xfe; - - memcpy(wildcard_ssid_tlv->ssid, - user_scan_in->ssid_list[i].ssid, ssid_len); - - tlv_pos += (sizeof(wildcard_ssid_tlv->header) - + le16_to_cpu(wildcard_ssid_tlv->header.len)); - - mwifiex_dbg(adapter, INFO, - "info: scan: ssid[%d]: %s, %d\n", - i, wildcard_ssid_tlv->ssid, - wildcard_ssid_tlv->max_ssid_length); - - /* Empty wildcard ssid with a maxlen will match many or - potentially all SSIDs (maxlen == 32), therefore do - not treat the scan as - filtered. */ - if (!ssid_len && wildcard_ssid_tlv->max_ssid_length) - ssid_filter = false; - } - - /* - * 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 ((i && ssid_filter) || - !is_zero_ether_addr(scan_cfg_out->specific_bssid)) - *filtered_scan = true; - - if (user_scan_in->scan_chan_gap) { - mwifiex_dbg(adapter, INFO, - "info: scan: channel gap = %d\n", - user_scan_in->scan_chan_gap); - *max_chan_per_scan = - MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; - - chan_gap_tlv = (void *)tlv_pos; - chan_gap_tlv->header.type = - cpu_to_le16(TLV_TYPE_SCAN_CHANNEL_GAP); - chan_gap_tlv->header.len = - cpu_to_le16(sizeof(chan_gap_tlv->chan_gap)); - chan_gap_tlv->chan_gap = - cpu_to_le16((user_scan_in->scan_chan_gap)); - tlv_pos += - sizeof(struct mwifiex_ie_types_scan_chan_gap); - } - } else { - scan_cfg_out->bss_mode = (u8) adapter->scan_mode; - num_probes = adapter->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 (*filtered_scan) - *max_chan_per_scan = MWIFIEX_MAX_CHANNELS_PER_SPECIFIC_SCAN; - else - *max_chan_per_scan = MWIFIEX_DEF_CHANNELS_PER_SCAN_CMD; - - if (adapter->ext_scan) { - bss_mode = (struct mwifiex_ie_types_bss_mode *)tlv_pos; - bss_mode->header.type = cpu_to_le16(TLV_TYPE_BSS_MODE); - bss_mode->header.len = cpu_to_le16(sizeof(bss_mode->bss_mode)); - bss_mode->bss_mode = scan_cfg_out->bss_mode; - tlv_pos += sizeof(bss_mode->header) + - le16_to_cpu(bss_mode->header.len); - } - - /* If the input config or adapter has the number of Probes set, - add tlv */ - if (num_probes) { - - mwifiex_dbg(adapter, INFO, - "info: scan: num_probes = %d\n", - num_probes); - - num_probes_tlv = (struct mwifiex_ie_types_num_probes *) tlv_pos; - num_probes_tlv->header.type = cpu_to_le16(TLV_TYPE_NUMPROBES); - num_probes_tlv->header.len = - cpu_to_le16(sizeof(num_probes_tlv->num_probes)); - num_probes_tlv->num_probes = cpu_to_le16((u16) num_probes); - - tlv_pos += sizeof(num_probes_tlv->header) + - le16_to_cpu(num_probes_tlv->header.len); - - } - - if (ISSUPP_11NENABLED(priv->adapter->fw_cap_info) && - (priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN)) { - ht_cap = (struct mwifiex_ie_types_htcap *) tlv_pos; - memset(ht_cap, 0, sizeof(struct mwifiex_ie_types_htcap)); - ht_cap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); - ht_cap->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - radio_type = - mwifiex_band_to_radio_type(priv->adapter->config_bands); - mwifiex_fill_cap_info(priv, radio_type, &ht_cap->ht_cap); - tlv_pos += sizeof(struct mwifiex_ie_types_htcap); - } - - /* Append vendor specific IE TLV */ - mwifiex_cmd_append_vsie_tlv(priv, MWIFIEX_VSIE_MASK_SCAN, &tlv_pos); - - /* - * Set the output for the channel TLV to the address in the tlv buffer - * past any TLVs that were added in this function (SSID, num_probes). - * Channel TLVs will be added past this for each scan command, - * preserving the TLVs that were previously added. - */ - *chan_list_out = - (struct mwifiex_ie_types_chan_list_param_set *) tlv_pos; - - if (user_scan_in && user_scan_in->chan_list[0].chan_number) { - - mwifiex_dbg(adapter, INFO, - "info: Scan: Using supplied channel list\n"); - - for (chan_idx = 0; - chan_idx < MWIFIEX_USER_SCAN_CHAN_MAX && - user_scan_in->chan_list[chan_idx].chan_number; - chan_idx++) { - - channel = user_scan_in->chan_list[chan_idx].chan_number; - (scan_chan_list + chan_idx)->chan_number = channel; - - radio_type = - user_scan_in->chan_list[chan_idx].radio_type; - (scan_chan_list + chan_idx)->radio_type = radio_type; - - scan_type = user_scan_in->chan_list[chan_idx].scan_type; - - if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) - (scan_chan_list + - chan_idx)->chan_scan_mode_bitmap - |= (MWIFIEX_PASSIVE_SCAN | - MWIFIEX_HIDDEN_SSID_REPORT); - else - (scan_chan_list + - chan_idx)->chan_scan_mode_bitmap - &= ~MWIFIEX_PASSIVE_SCAN; - - if (*filtered_scan) - (scan_chan_list + - chan_idx)->chan_scan_mode_bitmap - |= MWIFIEX_DISABLE_CHAN_FILT; - - if (user_scan_in->chan_list[chan_idx].scan_time) { - scan_dur = (u16) user_scan_in-> - chan_list[chan_idx].scan_time; - } else { - if (scan_type == MWIFIEX_SCAN_TYPE_PASSIVE) - scan_dur = adapter->passive_scan_time; - else if (*filtered_scan) - scan_dur = adapter->specific_scan_time; - else - scan_dur = adapter->active_scan_time; - } - - (scan_chan_list + chan_idx)->min_scan_time = - cpu_to_le16(scan_dur); - (scan_chan_list + chan_idx)->max_scan_time = - cpu_to_le16(scan_dur); - } - - /* Check if we are only scanning the current channel */ - if ((chan_idx == 1) && - (user_scan_in->chan_list[0].chan_number == - priv->curr_bss_params.bss_descriptor.channel)) { - *scan_current_only = true; - mwifiex_dbg(adapter, INFO, - "info: Scan: Scanning current channel only\n"); - } - chan_num = chan_idx; - } else { - mwifiex_dbg(adapter, INFO, - "info: Scan: Creating full region channel list\n"); - chan_num = mwifiex_scan_create_channel_list(priv, user_scan_in, - scan_chan_list, - *filtered_scan); - } - -} - -/* - * This function inspects the scan response buffer for pointers to - * expected TLVs. - * - * TLVs can be included at the end of the scan response BSS information. - * - * Data in the buffer is parsed pointers to TLVs that can potentially - * be passed back in the response. - */ -static void -mwifiex_ret_802_11_scan_get_tlv_ptrs(struct mwifiex_adapter *adapter, - struct mwifiex_ie_types_data *tlv, - u32 tlv_buf_size, u32 req_tlv_type, - struct mwifiex_ie_types_data **tlv_data) -{ - struct mwifiex_ie_types_data *current_tlv; - u32 tlv_buf_left; - u32 tlv_type; - u32 tlv_len; - - current_tlv = tlv; - tlv_buf_left = tlv_buf_size; - *tlv_data = NULL; - - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: tlv_buf_size = %d\n", - tlv_buf_size); - - while (tlv_buf_left >= sizeof(struct mwifiex_ie_types_header)) { - - tlv_type = le16_to_cpu(current_tlv->header.type); - tlv_len = le16_to_cpu(current_tlv->header.len); - - if (sizeof(tlv->header) + tlv_len > tlv_buf_left) { - mwifiex_dbg(adapter, ERROR, - "SCAN_RESP: TLV buffer corrupt\n"); - break; - } - - if (req_tlv_type == tlv_type) { - switch (tlv_type) { - case TLV_TYPE_TSFTIMESTAMP: - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: TSF\t" - "timestamp TLV, len = %d\n", - tlv_len); - *tlv_data = current_tlv; - break; - case TLV_TYPE_CHANNELBANDLIST: - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: channel\t" - "band list TLV, len = %d\n", - tlv_len); - *tlv_data = current_tlv; - break; - default: - mwifiex_dbg(adapter, ERROR, - "SCAN_RESP: unhandled TLV = %d\n", - tlv_type); - /* Give up, this seems corrupted */ - return; - } - } - - if (*tlv_data) - break; - - - tlv_buf_left -= (sizeof(tlv->header) + tlv_len); - current_tlv = - (struct mwifiex_ie_types_data *) (current_tlv->data + - tlv_len); - - } /* while */ -} - -/* - * This function parses provided beacon buffer and updates - * respective fields in bss descriptor structure. - */ -int mwifiex_update_bss_desc_with_ie(struct mwifiex_adapter *adapter, - struct mwifiex_bssdescriptor *bss_entry) -{ - int ret = 0; - u8 element_id; - struct ieee_types_fh_param_set *fh_param_set; - struct ieee_types_ds_param_set *ds_param_set; - struct ieee_types_cf_param_set *cf_param_set; - struct ieee_types_ibss_param_set *ibss_param_set; - u8 *current_ptr; - u8 *rate; - u8 element_len; - u16 total_ie_len; - u8 bytes_to_copy; - u8 rate_size; - u8 found_data_rate_ie; - u32 bytes_left; - struct ieee_types_vendor_specific *vendor_ie; - const u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; - const u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; - - found_data_rate_ie = false; - rate_size = 0; - current_ptr = bss_entry->beacon_buf; - bytes_left = bss_entry->beacon_buf_size; - - /* Process variable IE */ - while (bytes_left >= 2) { - element_id = *current_ptr; - element_len = *(current_ptr + 1); - total_ie_len = element_len + sizeof(struct ieee_types_header); - - if (bytes_left < total_ie_len) { - mwifiex_dbg(adapter, ERROR, - "err: InterpretIE: in processing\t" - "IE, bytes left < IE length\n"); - return -1; - } - switch (element_id) { - case WLAN_EID_SSID: - bss_entry->ssid.ssid_len = element_len; - memcpy(bss_entry->ssid.ssid, (current_ptr + 2), - element_len); - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: ssid: %-32s\n", - bss_entry->ssid.ssid); - break; - - case WLAN_EID_SUPP_RATES: - memcpy(bss_entry->data_rates, current_ptr + 2, - element_len); - memcpy(bss_entry->supported_rates, current_ptr + 2, - element_len); - rate_size = element_len; - found_data_rate_ie = true; - break; - - case WLAN_EID_FH_PARAMS: - fh_param_set = - (struct ieee_types_fh_param_set *) current_ptr; - memcpy(&bss_entry->phy_param_set.fh_param_set, - fh_param_set, - sizeof(struct ieee_types_fh_param_set)); - break; - - case WLAN_EID_DS_PARAMS: - ds_param_set = - (struct ieee_types_ds_param_set *) current_ptr; - - bss_entry->channel = ds_param_set->current_chan; - - memcpy(&bss_entry->phy_param_set.ds_param_set, - ds_param_set, - sizeof(struct ieee_types_ds_param_set)); - break; - - case WLAN_EID_CF_PARAMS: - cf_param_set = - (struct ieee_types_cf_param_set *) current_ptr; - memcpy(&bss_entry->ss_param_set.cf_param_set, - cf_param_set, - sizeof(struct ieee_types_cf_param_set)); - break; - - case WLAN_EID_IBSS_PARAMS: - ibss_param_set = - (struct ieee_types_ibss_param_set *) - current_ptr; - memcpy(&bss_entry->ss_param_set.ibss_param_set, - ibss_param_set, - sizeof(struct ieee_types_ibss_param_set)); - break; - - case WLAN_EID_ERP_INFO: - bss_entry->erp_flags = *(current_ptr + 2); - break; - - case WLAN_EID_PWR_CONSTRAINT: - bss_entry->local_constraint = *(current_ptr + 2); - bss_entry->sensed_11h = true; - break; - - case WLAN_EID_CHANNEL_SWITCH: - bss_entry->chan_sw_ie_present = true; - case WLAN_EID_PWR_CAPABILITY: - case WLAN_EID_TPC_REPORT: - case WLAN_EID_QUIET: - bss_entry->sensed_11h = true; - break; - - case WLAN_EID_EXT_SUPP_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) > - MWIFIEX_SUPPORTED_RATES) - bytes_to_copy = - (MWIFIEX_SUPPORTED_RATES - - rate_size); - else - bytes_to_copy = element_len; - - rate = (u8 *) bss_entry->data_rates; - rate += rate_size; - memcpy(rate, current_ptr + 2, bytes_to_copy); - - rate = (u8 *) bss_entry->supported_rates; - rate += rate_size; - memcpy(rate, current_ptr + 2, bytes_to_copy); - } - break; - - case WLAN_EID_VENDOR_SPECIFIC: - vendor_ie = (struct ieee_types_vendor_specific *) - current_ptr; - - if (!memcmp - (vendor_ie->vend_hdr.oui, wpa_oui, - sizeof(wpa_oui))) { - bss_entry->bcn_wpa_ie = - (struct ieee_types_vendor_specific *) - current_ptr; - bss_entry->wpa_offset = (u16) - (current_ptr - bss_entry->beacon_buf); - } else if (!memcmp(vendor_ie->vend_hdr.oui, wmm_oui, - sizeof(wmm_oui))) { - if (total_ie_len == - sizeof(struct ieee_types_wmm_parameter) || - total_ie_len == - sizeof(struct ieee_types_wmm_info)) - /* - * Only accept and copy the WMM IE if - * it matches the size expected for the - * WMM Info IE or the WMM Parameter IE. - */ - memcpy((u8 *) &bss_entry->wmm_ie, - current_ptr, total_ie_len); - } - break; - case WLAN_EID_RSN: - bss_entry->bcn_rsn_ie = - (struct ieee_types_generic *) current_ptr; - bss_entry->rsn_offset = (u16) (current_ptr - - bss_entry->beacon_buf); - break; - case WLAN_EID_BSS_AC_ACCESS_DELAY: - bss_entry->bcn_wapi_ie = - (struct ieee_types_generic *) current_ptr; - bss_entry->wapi_offset = (u16) (current_ptr - - bss_entry->beacon_buf); - break; - case WLAN_EID_HT_CAPABILITY: - bss_entry->bcn_ht_cap = (struct ieee80211_ht_cap *) - (current_ptr + - sizeof(struct ieee_types_header)); - bss_entry->ht_cap_offset = (u16) (current_ptr + - sizeof(struct ieee_types_header) - - bss_entry->beacon_buf); - break; - case WLAN_EID_HT_OPERATION: - bss_entry->bcn_ht_oper = - (struct ieee80211_ht_operation *)(current_ptr + - sizeof(struct ieee_types_header)); - bss_entry->ht_info_offset = (u16) (current_ptr + - sizeof(struct ieee_types_header) - - bss_entry->beacon_buf); - break; - case WLAN_EID_VHT_CAPABILITY: - bss_entry->disable_11ac = false; - bss_entry->bcn_vht_cap = - (void *)(current_ptr + - sizeof(struct ieee_types_header)); - bss_entry->vht_cap_offset = - (u16)((u8 *)bss_entry->bcn_vht_cap - - bss_entry->beacon_buf); - break; - case WLAN_EID_VHT_OPERATION: - bss_entry->bcn_vht_oper = - (void *)(current_ptr + - sizeof(struct ieee_types_header)); - bss_entry->vht_info_offset = - (u16)((u8 *)bss_entry->bcn_vht_oper - - bss_entry->beacon_buf); - break; - case WLAN_EID_BSS_COEX_2040: - bss_entry->bcn_bss_co_2040 = current_ptr; - bss_entry->bss_co_2040_offset = - (u16) (current_ptr - bss_entry->beacon_buf); - break; - case WLAN_EID_EXT_CAPABILITY: - bss_entry->bcn_ext_cap = current_ptr; - bss_entry->ext_cap_offset = - (u16) (current_ptr - bss_entry->beacon_buf); - break; - case WLAN_EID_OPMODE_NOTIF: - bss_entry->oper_mode = (void *)current_ptr; - bss_entry->oper_mode_offset = - (u16)((u8 *)bss_entry->oper_mode - - bss_entry->beacon_buf); - break; - default: - break; - } - - current_ptr += element_len + 2; - - /* Need to account for IE ID and IE Len */ - bytes_left -= (element_len + 2); - - } /* while (bytes_left > 2) */ - return ret; -} - -/* - * This function converts radio type scan parameter to a band configuration - * to be used in join command. - */ -static u8 -mwifiex_radio_type_to_band(u8 radio_type) -{ - switch (radio_type) { - case HostCmd_SCAN_RADIO_TYPE_A: - return BAND_A; - case HostCmd_SCAN_RADIO_TYPE_BG: - default: - return BAND_G; - } -} - -/* - * This is an internal function used to start a scan based on an input - * configuration. - * - * This uses 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. - */ -int mwifiex_scan_networks(struct mwifiex_private *priv, - const struct mwifiex_user_scan_cfg *user_scan_in) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node; - union mwifiex_scan_cmd_config_tlv *scan_cfg_out; - struct mwifiex_ie_types_chan_list_param_set *chan_list_out; - struct mwifiex_chan_scan_param_set *scan_chan_list; - u8 filtered_scan; - u8 scan_current_chan_only; - u8 max_chan_per_scan; - unsigned long flags; - - if (adapter->scan_processing) { - mwifiex_dbg(adapter, WARN, - "cmd: Scan already in process...\n"); - return -EBUSY; - } - - if (priv->scan_block) { - mwifiex_dbg(adapter, WARN, - "cmd: Scan is blocked during association...\n"); - return -EBUSY; - } - - if (adapter->surprise_removed || adapter->is_cmd_timedout) { - mwifiex_dbg(adapter, ERROR, - "Ignore scan. Card removed or firmware in bad state\n"); - return -EFAULT; - } - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = true; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - scan_cfg_out = kzalloc(sizeof(union mwifiex_scan_cmd_config_tlv), - GFP_KERNEL); - if (!scan_cfg_out) { - ret = -ENOMEM; - goto done; - } - - scan_chan_list = kcalloc(MWIFIEX_USER_SCAN_CHAN_MAX, - sizeof(struct mwifiex_chan_scan_param_set), - GFP_KERNEL); - if (!scan_chan_list) { - kfree(scan_cfg_out); - ret = -ENOMEM; - goto done; - } - - mwifiex_config_scan(priv, user_scan_in, &scan_cfg_out->config, - &chan_list_out, scan_chan_list, &max_chan_per_scan, - &filtered_scan, &scan_current_chan_only); - - ret = mwifiex_scan_channel_list(priv, max_chan_per_scan, filtered_scan, - &scan_cfg_out->config, chan_list_out, - scan_chan_list); - - /* Get scan command from scan_pending_q and put to cmd_pending_q */ - if (!ret) { - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - if (!list_empty(&adapter->scan_pending_q)) { - cmd_node = list_first_entry(&adapter->scan_pending_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, - true); - queue_work(adapter->workqueue, &adapter->main_work); - - /* Perform internal scan synchronously */ - if (!priv->scan_request) { - mwifiex_dbg(adapter, INFO, - "wait internal scan\n"); - mwifiex_wait_queue_complete(adapter, cmd_node); - } - } else { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - } - } - - kfree(scan_cfg_out); - kfree(scan_chan_list); -done: - if (ret) { - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - } - return ret; -} - -/* - * This function prepares a scan command to be sent to the firmware. - * - * This uses the scan command configuration sent to the command processing - * module in command preparation stage to configure a scan command structure - * 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. - * - * Preparation also includes - - * - Setting command ID, and proper size - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, - struct mwifiex_scan_cmd_config *scan_cfg) -{ - struct host_cmd_ds_802_11_scan *scan_cmd = &cmd->params.scan; - - /* Set fixed field variables in scan command */ - scan_cmd->bss_mode = scan_cfg->bss_mode; - memcpy(scan_cmd->bssid, scan_cfg->specific_bssid, - sizeof(scan_cmd->bssid)); - memcpy(scan_cmd->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN); - - /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ - cmd->size = cpu_to_le16((u16) (sizeof(scan_cmd->bss_mode) - + sizeof(scan_cmd->bssid) - + scan_cfg->tlv_buf_len + S_DS_GEN)); - - return 0; -} - -/* - * This function checks compatibility of requested network with current - * driver settings. - */ -int mwifiex_check_network_compatibility(struct mwifiex_private *priv, - struct mwifiex_bssdescriptor *bss_desc) -{ - int ret = -1; - - if (!bss_desc) - return -1; - - if ((mwifiex_get_cfp(priv, (u8) bss_desc->bss_band, - (u16) bss_desc->channel, 0))) { - switch (priv->bss_mode) { - case NL80211_IFTYPE_STATION: - case NL80211_IFTYPE_ADHOC: - ret = mwifiex_is_network_compatible(priv, bss_desc, - priv->bss_mode); - if (ret) - mwifiex_dbg(priv->adapter, ERROR, - "Incompatible network settings\n"); - break; - default: - ret = 0; - } - } - - return ret; -} - -/* This function checks if SSID string contains all zeroes or length is zero */ -static bool mwifiex_is_hidden_ssid(struct cfg80211_ssid *ssid) -{ - int idx; - - for (idx = 0; idx < ssid->ssid_len; idx++) { - if (ssid->ssid[idx]) - return false; - } - - return true; -} - -/* This function checks if any hidden SSID found in passive scan channels - * and save those channels for specific SSID active scan - */ -static int mwifiex_save_hidden_ssid_channels(struct mwifiex_private *priv, - struct cfg80211_bss *bss) -{ - struct mwifiex_bssdescriptor *bss_desc; - int ret; - int chid; - - /* Allocate and fill new bss descriptor */ - bss_desc = kzalloc(sizeof(*bss_desc), GFP_KERNEL); - if (!bss_desc) - return -ENOMEM; - - ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); - if (ret) - goto done; - - if (mwifiex_is_hidden_ssid(&bss_desc->ssid)) { - mwifiex_dbg(priv->adapter, INFO, "found hidden SSID\n"); - for (chid = 0 ; chid < MWIFIEX_USER_SCAN_CHAN_MAX; chid++) { - if (priv->hidden_chan[chid].chan_number == - bss->channel->hw_value) - break; - - if (!priv->hidden_chan[chid].chan_number) { - priv->hidden_chan[chid].chan_number = - bss->channel->hw_value; - priv->hidden_chan[chid].radio_type = - bss->channel->band; - priv->hidden_chan[chid].scan_type = - MWIFIEX_SCAN_TYPE_ACTIVE; - break; - } - } - } - -done: - kfree(bss_desc); - return 0; -} - -static int mwifiex_update_curr_bss_params(struct mwifiex_private *priv, - struct cfg80211_bss *bss) -{ - struct mwifiex_bssdescriptor *bss_desc; - int ret; - unsigned long flags; - - /* Allocate and fill new bss descriptor */ - bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), GFP_KERNEL); - if (!bss_desc) - return -ENOMEM; - - ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); - if (ret) - goto done; - - ret = mwifiex_check_network_compatibility(priv, bss_desc); - if (ret) - goto done; - - spin_lock_irqsave(&priv->curr_bcn_buf_lock, flags); - /* Make a copy of current BSSID descriptor */ - memcpy(&priv->curr_bss_params.bss_descriptor, bss_desc, - sizeof(priv->curr_bss_params.bss_descriptor)); - - /* The contents of beacon_ie will be copied to its own buffer - * in mwifiex_save_curr_bcn() - */ - mwifiex_save_curr_bcn(priv); - spin_unlock_irqrestore(&priv->curr_bcn_buf_lock, flags); - -done: - /* beacon_ie buffer was allocated in function - * mwifiex_fill_new_bss_desc(). Free it now. - */ - kfree(bss_desc->beacon_buf); - kfree(bss_desc); - return 0; -} - -static int -mwifiex_parse_single_response_buf(struct mwifiex_private *priv, u8 **bss_info, - u32 *bytes_left, u64 fw_tsf, u8 *radio_type, - bool ext_scan, s32 rssi_val) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_chan_freq_power *cfp; - struct cfg80211_bss *bss; - u8 bssid[ETH_ALEN]; - s32 rssi; - const u8 *ie_buf; - size_t ie_len; - u16 channel = 0; - u16 beacon_size = 0; - u32 curr_bcn_bytes; - u32 freq; - u16 beacon_period; - u16 cap_info_bitmap; - u8 *current_ptr; - u64 timestamp; - struct mwifiex_fixed_bcn_param *bcn_param; - struct mwifiex_bss_priv *bss_priv; - - if (*bytes_left >= sizeof(beacon_size)) { - /* Extract & convert beacon size from command buffer */ - beacon_size = le16_to_cpu(*(__le16 *)(*bss_info)); - *bytes_left -= sizeof(beacon_size); - *bss_info += sizeof(beacon_size); - } - - if (!beacon_size || beacon_size > *bytes_left) { - *bss_info += *bytes_left; - *bytes_left = 0; - return -EFAULT; - } - - /* Initialize the current working beacon pointer for this BSS - * iteration - */ - current_ptr = *bss_info; - - /* Advance the return beacon pointer past the current beacon */ - *bss_info += beacon_size; - *bytes_left -= beacon_size; - - curr_bcn_bytes = beacon_size; - - /* First 5 fields are bssid, RSSI(for legacy scan only), - * time stamp, beacon interval, and capability information - */ - if (curr_bcn_bytes < ETH_ALEN + sizeof(u8) + - sizeof(struct mwifiex_fixed_bcn_param)) { - mwifiex_dbg(adapter, ERROR, - "InterpretIE: not enough bytes left\n"); - return -EFAULT; - } - - memcpy(bssid, current_ptr, ETH_ALEN); - current_ptr += ETH_ALEN; - curr_bcn_bytes -= ETH_ALEN; - - if (!ext_scan) { - rssi = (s32) *current_ptr; - rssi = (-rssi) * 100; /* Convert dBm to mBm */ - current_ptr += sizeof(u8); - curr_bcn_bytes -= sizeof(u8); - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: RSSI=%d\n", rssi); - } else { - rssi = rssi_val; - } - - bcn_param = (struct mwifiex_fixed_bcn_param *)current_ptr; - current_ptr += sizeof(*bcn_param); - curr_bcn_bytes -= sizeof(*bcn_param); - - timestamp = le64_to_cpu(bcn_param->timestamp); - beacon_period = le16_to_cpu(bcn_param->beacon_period); - - cap_info_bitmap = le16_to_cpu(bcn_param->cap_info_bitmap); - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: capabilities=0x%X\n", - cap_info_bitmap); - - /* Rest of the current buffer are IE's */ - ie_buf = current_ptr; - ie_len = curr_bcn_bytes; - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: IELength for this AP = %d\n", - curr_bcn_bytes); - - while (curr_bcn_bytes >= sizeof(struct ieee_types_header)) { - u8 element_id, element_len; - - element_id = *current_ptr; - element_len = *(current_ptr + 1); - if (curr_bcn_bytes < element_len + - sizeof(struct ieee_types_header)) { - mwifiex_dbg(adapter, ERROR, - "%s: bytes left < IE length\n", __func__); - return -EFAULT; - } - if (element_id == WLAN_EID_DS_PARAMS) { - channel = *(current_ptr + - sizeof(struct ieee_types_header)); - break; - } - - current_ptr += element_len + sizeof(struct ieee_types_header); - curr_bcn_bytes -= element_len + - sizeof(struct ieee_types_header); - } - - if (channel) { - struct ieee80211_channel *chan; - u8 band; - - /* Skip entry if on csa closed channel */ - if (channel == priv->csa_chan) { - mwifiex_dbg(adapter, WARN, - "Dropping entry on csa closed channel\n"); - return 0; - } - - band = BAND_G; - if (radio_type) - band = mwifiex_radio_type_to_band(*radio_type & - (BIT(0) | BIT(1))); - - cfp = mwifiex_get_cfp(priv, band, channel, 0); - - freq = cfp ? cfp->freq : 0; - - chan = ieee80211_get_channel(priv->wdev.wiphy, freq); - - if (chan && !(chan->flags & IEEE80211_CHAN_DISABLED)) { - bss = cfg80211_inform_bss(priv->wdev.wiphy, - chan, CFG80211_BSS_FTYPE_UNKNOWN, - bssid, timestamp, - cap_info_bitmap, beacon_period, - ie_buf, ie_len, rssi, GFP_KERNEL); - if (bss) { - bss_priv = (struct mwifiex_bss_priv *)bss->priv; - bss_priv->band = band; - bss_priv->fw_tsf = fw_tsf; - if (priv->media_connected && - !memcmp(bssid, priv->curr_bss_params. - bss_descriptor.mac_address, - ETH_ALEN)) - mwifiex_update_curr_bss_params(priv, - bss); - cfg80211_put_bss(priv->wdev.wiphy, bss); - } - - if ((chan->flags & IEEE80211_CHAN_RADAR) || - (chan->flags & IEEE80211_CHAN_NO_IR)) { - mwifiex_dbg(adapter, INFO, - "radar or passive channel %d\n", - channel); - mwifiex_save_hidden_ssid_channels(priv, bss); - } - } - } else { - mwifiex_dbg(adapter, WARN, "missing BSS channel IE\n"); - } - - return 0; -} - -static void mwifiex_complete_scan(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - adapter->survey_idx = 0; - if (adapter->curr_cmd->wait_q_enabled) { - adapter->cmd_wait_q.status = 0; - if (!priv->scan_request) { - mwifiex_dbg(adapter, INFO, - "complete internal scan\n"); - mwifiex_complete_cmd(adapter, adapter->curr_cmd); - } - } -} - -/* This function checks if any hidden SSID found in passive scan channels - * and do specific SSID active scan for those channels - */ -static int -mwifiex_active_scan_req_for_passive_chan(struct mwifiex_private *priv) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - u8 id = 0; - struct mwifiex_user_scan_cfg *user_scan_cfg; - - if (adapter->active_scan_triggered || !priv->scan_request) { - adapter->active_scan_triggered = false; - return 0; - } - - if (!priv->hidden_chan[0].chan_number) { - mwifiex_dbg(adapter, INFO, "No BSS with hidden SSID found on DFS channels\n"); - return 0; - } - user_scan_cfg = kzalloc(sizeof(*user_scan_cfg), GFP_KERNEL); - - if (!user_scan_cfg) - return -ENOMEM; - - memset(user_scan_cfg, 0, sizeof(*user_scan_cfg)); - - for (id = 0; id < MWIFIEX_USER_SCAN_CHAN_MAX; id++) { - if (!priv->hidden_chan[id].chan_number) - break; - memcpy(&user_scan_cfg->chan_list[id], - &priv->hidden_chan[id], - sizeof(struct mwifiex_user_scan_chan)); - } - - adapter->active_scan_triggered = true; - user_scan_cfg->num_ssids = priv->scan_request->n_ssids; - user_scan_cfg->ssid_list = priv->scan_request->ssids; - - ret = mwifiex_scan_networks(priv, user_scan_cfg); - kfree(user_scan_cfg); - - memset(&priv->hidden_chan, 0, sizeof(priv->hidden_chan)); - - if (ret) { - dev_err(priv->adapter->dev, "scan failed: %d\n", ret); - return ret; - } - - return 0; -} -static void mwifiex_check_next_scan_command(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct cmd_ctrl_node *cmd_node, *tmp_node; - unsigned long flags; - - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - if (list_empty(&adapter->scan_pending_q)) { - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - mwifiex_active_scan_req_for_passive_chan(priv); - - if (!adapter->ext_scan) - mwifiex_complete_scan(priv); - - if (priv->scan_request) { - mwifiex_dbg(adapter, INFO, - "info: notifying scan done\n"); - cfg80211_scan_done(priv->scan_request, 0); - priv->scan_request = NULL; - } else { - priv->scan_aborting = false; - mwifiex_dbg(adapter, INFO, - "info: scan already aborted\n"); - } - } else if ((priv->scan_aborting && !priv->scan_request) || - priv->scan_block) { - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - - if (!adapter->active_scan_triggered) { - if (priv->scan_request) { - mwifiex_dbg(adapter, INFO, - "info: aborting scan\n"); - cfg80211_scan_done(priv->scan_request, 1); - priv->scan_request = NULL; - } else { - priv->scan_aborting = false; - mwifiex_dbg(adapter, INFO, - "info: scan already aborted\n"); - } - } - } else { - /* Get scan command from scan_pending_q and put to - * cmd_pending_q - */ - cmd_node = list_first_entry(&adapter->scan_pending_q, - struct cmd_ctrl_node, list); - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); - } - - return; -} - -/* - * 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) | - * .-------------------------------------------------------------. - */ -int mwifiex_ret_802_11_scan(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_scan_rsp *scan_rsp; - struct mwifiex_ie_types_data *tlv_data; - struct mwifiex_ie_types_tsf_timestamp *tsf_tlv; - u8 *bss_info; - u32 scan_resp_size; - u32 bytes_left; - u32 idx; - u32 tlv_buf_size; - struct mwifiex_ie_types_chan_band_list_param_set *chan_band_tlv; - struct chan_band_param_set *chan_band; - u8 is_bgscan_resp; - __le64 fw_tsf = 0; - u8 *radio_type; - - is_bgscan_resp = (le16_to_cpu(resp->command) - == HostCmd_CMD_802_11_BG_SCAN_QUERY); - if (is_bgscan_resp) - scan_rsp = &resp->params.bg_scan_query_resp.scan_resp; - else - scan_rsp = &resp->params.scan_resp; - - - if (scan_rsp->number_of_sets > MWIFIEX_MAX_AP) { - mwifiex_dbg(adapter, ERROR, - "SCAN_RESP: too many AP returned (%d)\n", - scan_rsp->number_of_sets); - ret = -1; - goto check_next_scan; - } - - /* Check csa channel expiry before parsing scan response */ - mwifiex_11h_get_csa_closed_channel(priv); - - bytes_left = le16_to_cpu(scan_rsp->bss_descript_size); - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: bss_descript_size %d\n", - bytes_left); - - scan_resp_size = le16_to_cpu(resp->size); - - mwifiex_dbg(adapter, INFO, - "info: SCAN_RESP: returned %d APs before parsing\n", - scan_rsp->number_of_sets); - - bss_info = scan_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(scan_rsp->bss_descript_size) - + sizeof(scan_rsp->number_of_sets) - + S_DS_GEN); - - tlv_data = (struct mwifiex_ie_types_data *) (scan_rsp-> - bss_desc_and_tlv_buffer + - bytes_left); - - /* Search the TLV buffer space in the scan response for any valid - TLVs */ - mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, - TLV_TYPE_TSFTIMESTAMP, - (struct mwifiex_ie_types_data **) - &tsf_tlv); - - /* Search the TLV buffer space in the scan response for any valid - TLVs */ - mwifiex_ret_802_11_scan_get_tlv_ptrs(adapter, tlv_data, tlv_buf_size, - TLV_TYPE_CHANNELBANDLIST, - (struct mwifiex_ie_types_data **) - &chan_band_tlv); - - for (idx = 0; idx < scan_rsp->number_of_sets && bytes_left; idx++) { - /* - * If the TSF TLV was appended to the scan results, save this - * entry's TSF value in the fw_tsf field. It is the firmware's - * TSF value at the time the beacon or probe response was - * received. - */ - if (tsf_tlv) - memcpy(&fw_tsf, &tsf_tlv->tsf_data[idx * TSF_DATA_SIZE], - sizeof(fw_tsf)); - - if (chan_band_tlv) { - chan_band = &chan_band_tlv->chan_band_param[idx]; - radio_type = &chan_band->radio_type; - } else { - radio_type = NULL; - } - - ret = mwifiex_parse_single_response_buf(priv, &bss_info, - &bytes_left, - le64_to_cpu(fw_tsf), - radio_type, false, 0); - if (ret) - goto check_next_scan; - } - -check_next_scan: - mwifiex_check_next_scan_command(priv); - return ret; -} - -/* - * This function prepares an extended scan command to be sent to the firmware - * - * This uses the scan command configuration sent to the command processing - * module in command preparation stage to configure a extended scan command - * structure to send to firmware. - */ -int mwifiex_cmd_802_11_scan_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf) -{ - struct host_cmd_ds_802_11_scan_ext *ext_scan = &cmd->params.ext_scan; - struct mwifiex_scan_cmd_config *scan_cfg = data_buf; - - memcpy(ext_scan->tlv_buffer, scan_cfg->tlv_buf, scan_cfg->tlv_buf_len); - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SCAN_EXT); - - /* Size is equal to the sizeof(fixed portions) + the TLV len + header */ - cmd->size = cpu_to_le16((u16)(sizeof(ext_scan->reserved) - + scan_cfg->tlv_buf_len + S_DS_GEN)); - - return 0; -} - -static void -mwifiex_update_chan_statistics(struct mwifiex_private *priv, - struct mwifiex_ietypes_chanstats *tlv_stat) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u8 i, num_chan; - struct mwifiex_fw_chan_stats *fw_chan_stats; - struct mwifiex_chan_stats chan_stats; - - fw_chan_stats = (void *)((u8 *)tlv_stat + - sizeof(struct mwifiex_ie_types_header)); - num_chan = le16_to_cpu(tlv_stat->header.len) / - sizeof(struct mwifiex_chan_stats); - - for (i = 0 ; i < num_chan; i++) { - chan_stats.chan_num = fw_chan_stats->chan_num; - chan_stats.bandcfg = fw_chan_stats->bandcfg; - chan_stats.flags = fw_chan_stats->flags; - chan_stats.noise = fw_chan_stats->noise; - chan_stats.total_bss = le16_to_cpu(fw_chan_stats->total_bss); - chan_stats.cca_scan_dur = - le16_to_cpu(fw_chan_stats->cca_scan_dur); - chan_stats.cca_busy_dur = - le16_to_cpu(fw_chan_stats->cca_busy_dur); - mwifiex_dbg(adapter, INFO, - "chan=%d, noise=%d, total_network=%d scan_duration=%d, busy_duration=%d\n", - chan_stats.chan_num, - chan_stats.noise, - chan_stats.total_bss, - chan_stats.cca_scan_dur, - chan_stats.cca_busy_dur); - memcpy(&adapter->chan_stats[adapter->survey_idx++], &chan_stats, - sizeof(struct mwifiex_chan_stats)); - fw_chan_stats++; - } -} - -/* This function handles the command response of extended scan */ -int mwifiex_ret_802_11_scan_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_scan_ext *ext_scan_resp; - struct mwifiex_ie_types_header *tlv; - struct mwifiex_ietypes_chanstats *tlv_stat; - u16 buf_left, type, len; - - struct host_cmd_ds_command *cmd_ptr; - struct cmd_ctrl_node *cmd_node; - unsigned long cmd_flags, scan_flags; - bool complete_scan = false; - - mwifiex_dbg(adapter, INFO, "info: EXT scan returns successfully\n"); - - ext_scan_resp = &resp->params.ext_scan; - - tlv = (void *)ext_scan_resp->tlv_buffer; - buf_left = le16_to_cpu(resp->size) - (sizeof(*ext_scan_resp) + S_DS_GEN - - 1); - - while (buf_left >= sizeof(struct mwifiex_ie_types_header)) { - type = le16_to_cpu(tlv->type); - len = le16_to_cpu(tlv->len); - - if (buf_left < (sizeof(struct mwifiex_ie_types_header) + len)) { - mwifiex_dbg(adapter, ERROR, - "error processing scan response TLVs"); - break; - } - - switch (type) { - case TLV_TYPE_CHANNEL_STATS: - tlv_stat = (void *)tlv; - mwifiex_update_chan_statistics(priv, tlv_stat); - break; - default: - break; - } - - buf_left -= len + sizeof(struct mwifiex_ie_types_header); - tlv = (void *)((u8 *)tlv + len + - sizeof(struct mwifiex_ie_types_header)); - } - - spin_lock_irqsave(&adapter->cmd_pending_q_lock, cmd_flags); - spin_lock_irqsave(&adapter->scan_pending_q_lock, scan_flags); - if (list_empty(&adapter->scan_pending_q)) { - complete_scan = true; - list_for_each_entry(cmd_node, &adapter->cmd_pending_q, list) { - cmd_ptr = (void *)cmd_node->cmd_skb->data; - if (le16_to_cpu(cmd_ptr->command) == - HostCmd_CMD_802_11_SCAN_EXT) { - mwifiex_dbg(adapter, INFO, - "Scan pending in command pending list"); - complete_scan = false; - break; - } - } - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, scan_flags); - spin_unlock_irqrestore(&adapter->cmd_pending_q_lock, cmd_flags); - - if (complete_scan) - mwifiex_complete_scan(priv); - - return 0; -} - -/* This function This function handles the event extended scan report. It - * parses extended scan results and informs to cfg80211 stack. - */ -int mwifiex_handle_event_ext_scan_report(struct mwifiex_private *priv, - void *buf) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - u8 *bss_info; - u32 bytes_left, bytes_left_for_tlv, idx; - u16 type, len; - struct mwifiex_ie_types_data *tlv; - struct mwifiex_ie_types_bss_scan_rsp *scan_rsp_tlv; - struct mwifiex_ie_types_bss_scan_info *scan_info_tlv; - u8 *radio_type; - u64 fw_tsf = 0; - s32 rssi = 0; - struct mwifiex_event_scan_result *event_scan = buf; - u8 num_of_set = event_scan->num_of_set; - u8 *scan_resp = buf + sizeof(struct mwifiex_event_scan_result); - u16 scan_resp_size = le16_to_cpu(event_scan->buf_size); - - if (num_of_set > MWIFIEX_MAX_AP) { - mwifiex_dbg(adapter, ERROR, - "EXT_SCAN: Invalid number of AP returned (%d)!!\n", - num_of_set); - ret = -1; - goto check_next_scan; - } - - bytes_left = scan_resp_size; - mwifiex_dbg(adapter, INFO, - "EXT_SCAN: size %d, returned %d APs...", - scan_resp_size, num_of_set); - mwifiex_dbg_dump(adapter, CMD_D, "EXT_SCAN buffer:", buf, - scan_resp_size + - sizeof(struct mwifiex_event_scan_result)); - - tlv = (struct mwifiex_ie_types_data *)scan_resp; - - for (idx = 0; idx < num_of_set && bytes_left; idx++) { - type = le16_to_cpu(tlv->header.type); - len = le16_to_cpu(tlv->header.len); - if (bytes_left < sizeof(struct mwifiex_ie_types_header) + len) { - mwifiex_dbg(adapter, ERROR, - "EXT_SCAN: Error bytes left < TLV length\n"); - break; - } - scan_rsp_tlv = NULL; - scan_info_tlv = NULL; - bytes_left_for_tlv = bytes_left; - - /* BSS response TLV with beacon or probe response buffer - * at the initial position of each descriptor - */ - if (type != TLV_TYPE_BSS_SCAN_RSP) - break; - - bss_info = (u8 *)tlv; - scan_rsp_tlv = (struct mwifiex_ie_types_bss_scan_rsp *)tlv; - tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); - bytes_left_for_tlv -= - (len + sizeof(struct mwifiex_ie_types_header)); - - while (bytes_left_for_tlv >= - sizeof(struct mwifiex_ie_types_header) && - le16_to_cpu(tlv->header.type) != TLV_TYPE_BSS_SCAN_RSP) { - type = le16_to_cpu(tlv->header.type); - len = le16_to_cpu(tlv->header.len); - if (bytes_left_for_tlv < - sizeof(struct mwifiex_ie_types_header) + len) { - mwifiex_dbg(adapter, ERROR, - "EXT_SCAN: Error in processing TLV,\t" - "bytes left < TLV length\n"); - scan_rsp_tlv = NULL; - bytes_left_for_tlv = 0; - continue; - } - switch (type) { - case TLV_TYPE_BSS_SCAN_INFO: - scan_info_tlv = - (struct mwifiex_ie_types_bss_scan_info *)tlv; - if (len != - sizeof(struct mwifiex_ie_types_bss_scan_info) - - sizeof(struct mwifiex_ie_types_header)) { - bytes_left_for_tlv = 0; - continue; - } - break; - default: - break; - } - tlv = (struct mwifiex_ie_types_data *)(tlv->data + len); - bytes_left -= - (len + sizeof(struct mwifiex_ie_types_header)); - bytes_left_for_tlv -= - (len + sizeof(struct mwifiex_ie_types_header)); - } - - if (!scan_rsp_tlv) - 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 - */ - bss_info += sizeof(u16); - bytes_left -= sizeof(u16); - - if (scan_info_tlv) { - rssi = (s32)(s16)(le16_to_cpu(scan_info_tlv->rssi)); - rssi *= 100; /* Convert dBm to mBm */ - mwifiex_dbg(adapter, INFO, - "info: InterpretIE: RSSI=%d\n", rssi); - fw_tsf = le64_to_cpu(scan_info_tlv->tsf); - radio_type = &scan_info_tlv->radio_type; - } else { - radio_type = NULL; - } - ret = mwifiex_parse_single_response_buf(priv, &bss_info, - &bytes_left, fw_tsf, - radio_type, true, rssi); - if (ret) - goto check_next_scan; - } - -check_next_scan: - if (!event_scan->more_event) - mwifiex_check_next_scan_command(priv); - - return ret; -} - -/* - * This function prepares command for background scan query. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting background scan flush parameter - * - Ensuring correct endian-ness - */ -int mwifiex_cmd_802_11_bg_scan_query(struct host_cmd_ds_command *cmd) -{ - struct host_cmd_ds_802_11_bg_scan_query *bg_query = - &cmd->params.bg_scan_query; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_BG_SCAN_QUERY); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_bg_scan_query) - + S_DS_GEN); - - bg_query->flush = 1; - - return 0; -} - -/* - * This function inserts scan command node to the scan pending queue. - */ -void -mwifiex_queue_scan_cmd(struct mwifiex_private *priv, - struct cmd_ctrl_node *cmd_node) -{ - struct mwifiex_adapter *adapter = priv->adapter; - unsigned long flags; - - cmd_node->wait_q_enabled = true; - cmd_node->condition = &adapter->scan_wait_q_woken; - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_add_tail(&cmd_node->list, &adapter->scan_pending_q); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); -} - -/* - * This function sends a scan command for all available channels to the - * firmware, filtered on a specific SSID. - */ -static int mwifiex_scan_specific_ssid(struct mwifiex_private *priv, - struct cfg80211_ssid *req_ssid) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - struct mwifiex_user_scan_cfg *scan_cfg; - - if (adapter->scan_processing) { - mwifiex_dbg(adapter, WARN, - "cmd: Scan already in process...\n"); - return -EBUSY; - } - - if (priv->scan_block) { - mwifiex_dbg(adapter, WARN, - "cmd: Scan is blocked during association...\n"); - return -EBUSY; - } - - scan_cfg = kzalloc(sizeof(struct mwifiex_user_scan_cfg), GFP_KERNEL); - if (!scan_cfg) - return -ENOMEM; - - scan_cfg->ssid_list = req_ssid; - scan_cfg->num_ssids = 1; - - ret = mwifiex_scan_networks(priv, scan_cfg); - - kfree(scan_cfg); - return ret; -} - -/* - * Sends IOCTL request to start a scan. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - * - * Scan command can be issued for both normal scan and specific SSID - * scan, depending upon whether an SSID is provided or not. - */ -int mwifiex_request_scan(struct mwifiex_private *priv, - struct cfg80211_ssid *req_ssid) -{ - int ret; - - if (down_interruptible(&priv->async_sem)) { - mwifiex_dbg(priv->adapter, ERROR, - "%s: acquire semaphore fail\n", - __func__); - return -1; - } - - priv->adapter->scan_wait_q_woken = false; - - if (req_ssid && req_ssid->ssid_len != 0) - /* Specific SSID scan */ - ret = mwifiex_scan_specific_ssid(priv, req_ssid); - else - /* Normal scan */ - ret = mwifiex_scan_networks(priv, NULL); - - up(&priv->async_sem); - - return ret; -} - -/* - * This function appends the vendor specific IE TLV to a buffer. - */ -int -mwifiex_cmd_append_vsie_tlv(struct mwifiex_private *priv, - u16 vsie_mask, u8 **buffer) -{ - int id, ret_len = 0; - struct mwifiex_ie_types_vendor_param_set *vs_param_set; - - if (!buffer) - return 0; - if (!(*buffer)) - return 0; - - /* - * Traverse through the saved vendor specific IE array and append - * the selected(scan/assoc/adhoc) IE as TLV to the command - */ - for (id = 0; id < MWIFIEX_MAX_VSIE_NUM; id++) { - if (priv->vs_ie[id].mask & vsie_mask) { - vs_param_set = - (struct mwifiex_ie_types_vendor_param_set *) - *buffer; - vs_param_set->header.type = - cpu_to_le16(TLV_TYPE_PASSTHROUGH); - vs_param_set->header.len = - cpu_to_le16((((u16) priv->vs_ie[id].ie[1]) - & 0x00FF) + 2); - memcpy(vs_param_set->ie, priv->vs_ie[id].ie, - le16_to_cpu(vs_param_set->header.len)); - *buffer += le16_to_cpu(vs_param_set->header.len) + - sizeof(struct mwifiex_ie_types_header); - ret_len += le16_to_cpu(vs_param_set->header.len) + - sizeof(struct mwifiex_ie_types_header); - } - } - return ret_len; -} - -/* - * This function saves a beacon buffer of the current BSS descriptor. - * - * The current beacon buffer is saved so that it can be restored in the - * following cases that makes the beacon buffer 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. - */ -void -mwifiex_save_curr_bcn(struct mwifiex_private *priv) -{ - struct mwifiex_bssdescriptor *curr_bss = - &priv->curr_bss_params.bss_descriptor; - - if (!curr_bss->beacon_buf_size) - return; - - /* allocate beacon buffer at 1st time; or if it's size has changed */ - if (!priv->curr_bcn_buf || - priv->curr_bcn_size != curr_bss->beacon_buf_size) { - priv->curr_bcn_size = curr_bss->beacon_buf_size; - - kfree(priv->curr_bcn_buf); - priv->curr_bcn_buf = kmalloc(curr_bss->beacon_buf_size, - GFP_ATOMIC); - if (!priv->curr_bcn_buf) - return; - } - - memcpy(priv->curr_bcn_buf, curr_bss->beacon_buf, - curr_bss->beacon_buf_size); - mwifiex_dbg(priv->adapter, INFO, - "info: current beacon saved %d\n", - priv->curr_bcn_size); - - curr_bss->beacon_buf = priv->curr_bcn_buf; - - /* adjust the pointers in the current BSS descriptor */ - if (curr_bss->bcn_wpa_ie) - curr_bss->bcn_wpa_ie = - (struct ieee_types_vendor_specific *) - (curr_bss->beacon_buf + - curr_bss->wpa_offset); - - if (curr_bss->bcn_rsn_ie) - curr_bss->bcn_rsn_ie = (struct ieee_types_generic *) - (curr_bss->beacon_buf + - curr_bss->rsn_offset); - - if (curr_bss->bcn_ht_cap) - curr_bss->bcn_ht_cap = (struct ieee80211_ht_cap *) - (curr_bss->beacon_buf + - curr_bss->ht_cap_offset); - - if (curr_bss->bcn_ht_oper) - curr_bss->bcn_ht_oper = (struct ieee80211_ht_operation *) - (curr_bss->beacon_buf + - curr_bss->ht_info_offset); - - if (curr_bss->bcn_vht_cap) - curr_bss->bcn_vht_cap = (void *)(curr_bss->beacon_buf + - curr_bss->vht_cap_offset); - - if (curr_bss->bcn_vht_oper) - curr_bss->bcn_vht_oper = (void *)(curr_bss->beacon_buf + - curr_bss->vht_info_offset); - - if (curr_bss->bcn_bss_co_2040) - curr_bss->bcn_bss_co_2040 = - (curr_bss->beacon_buf + curr_bss->bss_co_2040_offset); - - if (curr_bss->bcn_ext_cap) - curr_bss->bcn_ext_cap = curr_bss->beacon_buf + - curr_bss->ext_cap_offset; - - if (curr_bss->oper_mode) - curr_bss->oper_mode = (void *)(curr_bss->beacon_buf + - curr_bss->oper_mode_offset); -} - -/* - * This function frees the current BSS descriptor beacon buffer. - */ -void -mwifiex_free_curr_bcn(struct mwifiex_private *priv) -{ - kfree(priv->curr_bcn_buf); - priv->curr_bcn_buf = NULL; -} diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c deleted file mode 100644 index 78a8474e1a3d..000000000000 --- a/drivers/net/wireless/mwifiex/sdio.c +++ /dev/null @@ -1,2684 +0,0 @@ -/* - * Marvell Wireless LAN device driver: SDIO specific handling - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "sdio.h" - - -#define SDIO_VERSION "1.0" - -/* The mwifiex_sdio_remove() callback function is called when - * user removes this module from kernel space or ejects - * the card from the slot. The driver handles these 2 cases - * differently. - * If the user is removing the module, the few commands (FUNC_SHUTDOWN, - * HS_CANCEL etc.) are sent to the firmware. - * If the card is removed, there is no need to send these command. - * - * The variable 'user_rmmod' is used to distinguish these two - * scenarios. This flag is initialized as FALSE in case the card - * is removed, and will be set to TRUE for module removal when - * module_exit function is called. - */ -static u8 user_rmmod; - -static struct mwifiex_if_ops sdio_ops; -static unsigned long iface_work_flags; - -static struct semaphore add_remove_card_sem; - -static struct memory_type_mapping generic_mem_type_map[] = { - {"DUMP", NULL, 0, 0xDD}, -}; - -static struct memory_type_mapping mem_type_mapping_tbl[] = { - {"ITCM", NULL, 0, 0xF0}, - {"DTCM", NULL, 0, 0xF1}, - {"SQRAM", NULL, 0, 0xF2}, - {"APU", NULL, 0, 0xF3}, - {"CIU", NULL, 0, 0xF4}, - {"ICU", NULL, 0, 0xF5}, - {"MAC", NULL, 0, 0xF6}, - {"EXT7", NULL, 0, 0xF7}, - {"EXT8", NULL, 0, 0xF8}, - {"EXT9", NULL, 0, 0xF9}, - {"EXT10", NULL, 0, 0xFA}, - {"EXT11", NULL, 0, 0xFB}, - {"EXT12", NULL, 0, 0xFC}, - {"EXT13", NULL, 0, 0xFD}, - {"EXTLAST", NULL, 0, 0xFE}, -}; - -/* - * SDIO probe. - * - * This function probes an mwifiex device and registers it. It allocates - * the card structure, enables SDIO function number and initiates the - * device registration and initialization procedure by adding a logical - * interface. - */ -static int -mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) -{ - int ret; - struct sdio_mmc_card *card = NULL; - - pr_debug("info: 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) - return -ENOMEM; - - card->func = func; - card->device_id = id; - - func->card->quirks |= MMC_QUIRK_BLKSZ_FOR_BYTE_MODE; - - if (id->driver_data) { - struct mwifiex_sdio_device *data = (void *)id->driver_data; - - card->firmware = data->firmware; - card->reg = data->reg; - card->max_ports = data->max_ports; - card->mp_agg_pkt_limit = data->mp_agg_pkt_limit; - card->supports_sdio_new_mode = data->supports_sdio_new_mode; - card->has_control_mask = data->has_control_mask; - card->tx_buf_size = data->tx_buf_size; - card->mp_tx_agg_buf_size = data->mp_tx_agg_buf_size; - card->mp_rx_agg_buf_size = data->mp_rx_agg_buf_size; - card->can_dump_fw = data->can_dump_fw; - card->fw_dump_enh = data->fw_dump_enh; - card->can_auto_tdls = data->can_auto_tdls; - card->can_ext_scan = data->can_ext_scan; - } - - sdio_claim_host(func); - ret = sdio_enable_func(func); - sdio_release_host(func); - - if (ret) { - pr_err("%s: failed to enable function\n", __func__); - kfree(card); - return -EIO; - } - - if (mwifiex_add_card(card, &add_remove_card_sem, &sdio_ops, - MWIFIEX_SDIO)) { - pr_err("%s: add card failed\n", __func__); - kfree(card); - sdio_claim_host(func); - ret = sdio_disable_func(func); - sdio_release_host(func); - ret = -1; - } - - return ret; -} - -/* - * SDIO resume. - * - * Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not resumed, this function turns on the traffic and - * sends a host sleep cancel request to the firmware. - */ -static int mwifiex_sdio_resume(struct device *dev) -{ - struct sdio_func *func = dev_to_sdio_func(dev); - struct sdio_mmc_card *card; - struct mwifiex_adapter *adapter; - mmc_pm_flag_t pm_flag = 0; - - if (func) { - pm_flag = sdio_get_host_pm_caps(func); - card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_err("resume: invalid card or adapter\n"); - return 0; - } - } else { - pr_err("resume: sdio_func is not specified\n"); - return 0; - } - - adapter = card->adapter; - - if (!adapter->is_suspended) { - mwifiex_dbg(adapter, WARN, - "device already resumed\n"); - return 0; - } - - adapter->is_suspended = false; - - /* Disable Host Sleep */ - mwifiex_cancel_hs(mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA), - MWIFIEX_ASYNC_CMD); - - return 0; -} - -/* - * SDIO remove. - * - * This function removes the interface and frees up the card structure. - */ -static void -mwifiex_sdio_remove(struct sdio_func *func) -{ - struct sdio_mmc_card *card; - struct mwifiex_adapter *adapter; - struct mwifiex_private *priv; - - card = sdio_get_drvdata(func); - if (!card) - return; - - adapter = card->adapter; - if (!adapter || !adapter->priv_num) - return; - - mwifiex_dbg(adapter, INFO, "info: SDIO func num=%d\n", func->num); - - if (user_rmmod) { - if (adapter->is_suspended) - mwifiex_sdio_resume(adapter->dev); - - mwifiex_deauthenticate_all(adapter); - - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - mwifiex_disable_auto_ds(priv); - mwifiex_init_shutdown_fw(priv, MWIFIEX_FUNC_SHUTDOWN); - } - - mwifiex_remove_card(card->adapter, &add_remove_card_sem); -} - -/* - * SDIO suspend. - * - * Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not suspended, this function allocates and sends a host - * sleep activate request to the firmware and turns off the traffic. - */ -static int mwifiex_sdio_suspend(struct device *dev) -{ - struct sdio_func *func = dev_to_sdio_func(dev); - struct sdio_mmc_card *card; - struct mwifiex_adapter *adapter; - mmc_pm_flag_t pm_flag = 0; - int ret = 0; - - if (func) { - pm_flag = sdio_get_host_pm_caps(func); - pr_debug("cmd: %s: suspend: PM flag = 0x%x\n", - sdio_func_id(func), pm_flag); - if (!(pm_flag & MMC_PM_KEEP_POWER)) { - pr_err("%s: cannot remain alive while host is" - " suspended\n", sdio_func_id(func)); - return -ENOSYS; - } - - card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_err("suspend: invalid card or adapter\n"); - return 0; - } - } else { - pr_err("suspend: sdio_func is not specified\n"); - return 0; - } - - adapter = card->adapter; - - /* Enable the Host Sleep */ - if (!mwifiex_enable_hs(adapter)) { - mwifiex_dbg(adapter, ERROR, - "cmd: failed to suspend\n"); - adapter->hs_enabling = false; - return -EFAULT; - } - - mwifiex_dbg(adapter, INFO, - "cmd: suspend with MMC_PM_KEEP_POWER\n"); - ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); - - /* Indicate device suspended */ - adapter->is_suspended = true; - adapter->hs_enabling = false; - - return ret; -} - -/* Device ID for SD8786 */ -#define SDIO_DEVICE_ID_MARVELL_8786 (0x9116) -/* Device ID for SD8787 */ -#define SDIO_DEVICE_ID_MARVELL_8787 (0x9119) -/* Device ID for SD8797 */ -#define SDIO_DEVICE_ID_MARVELL_8797 (0x9129) -/* Device ID for SD8897 */ -#define SDIO_DEVICE_ID_MARVELL_8897 (0x912d) -/* Device ID for SD8887 */ -#define SDIO_DEVICE_ID_MARVELL_8887 (0x9135) -/* Device ID for SD8801 */ -#define SDIO_DEVICE_ID_MARVELL_8801 (0x9139) -/* Device ID for SD8997 */ -#define SDIO_DEVICE_ID_MARVELL_8997 (0x9141) - - -/* WLAN IDs */ -static const struct sdio_device_id mwifiex_ids[] = { - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8786), - .driver_data = (unsigned long) &mwifiex_sdio_sd8786}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8787), - .driver_data = (unsigned long) &mwifiex_sdio_sd8787}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797), - .driver_data = (unsigned long) &mwifiex_sdio_sd8797}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897), - .driver_data = (unsigned long) &mwifiex_sdio_sd8897}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8887), - .driver_data = (unsigned long)&mwifiex_sdio_sd8887}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8801), - .driver_data = (unsigned long)&mwifiex_sdio_sd8801}, - {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997), - .driver_data = (unsigned long)&mwifiex_sdio_sd8997}, - {}, -}; - -MODULE_DEVICE_TABLE(sdio, mwifiex_ids); - -static const struct dev_pm_ops mwifiex_sdio_pm_ops = { - .suspend = mwifiex_sdio_suspend, - .resume = mwifiex_sdio_resume, -}; - -static struct sdio_driver mwifiex_sdio = { - .name = "mwifiex_sdio", - .id_table = mwifiex_ids, - .probe = mwifiex_sdio_probe, - .remove = mwifiex_sdio_remove, - .drv = { - .owner = THIS_MODULE, - .pm = &mwifiex_sdio_pm_ops, - } -}; - -/* Write data into SDIO card register. Caller claims SDIO device. */ -static int -mwifiex_write_reg_locked(struct sdio_func *func, u32 reg, u8 data) -{ - int ret = -1; - sdio_writeb(func, data, reg, &ret); - return ret; -} - -/* - * This function writes data into SDIO card register. - */ -static int -mwifiex_write_reg(struct mwifiex_adapter *adapter, u32 reg, u8 data) -{ - struct sdio_mmc_card *card = adapter->card; - int ret; - - sdio_claim_host(card->func); - ret = mwifiex_write_reg_locked(card->func, reg, data); - sdio_release_host(card->func); - - return ret; -} - -/* - * This function reads data from SDIO card register. - */ -static int -mwifiex_read_reg(struct mwifiex_adapter *adapter, u32 reg, u8 *data) -{ - struct sdio_mmc_card *card = adapter->card; - int ret = -1; - u8 val; - - sdio_claim_host(card->func); - val = sdio_readb(card->func, reg, &ret); - sdio_release_host(card->func); - - *data = val; - - return ret; -} - -/* - * This function writes multiple data into SDIO card memory. - * - * This does not work in suspended mode. - */ -static int -mwifiex_write_data_sync(struct mwifiex_adapter *adapter, - u8 *buffer, u32 pkt_len, u32 port) -{ - struct sdio_mmc_card *card = adapter->card; - int ret; - u8 blk_mode = - (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE : BLOCK_MODE; - u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; - u32 blk_cnt = - (blk_mode == - BLOCK_MODE) ? (pkt_len / - MWIFIEX_SDIO_BLOCK_SIZE) : pkt_len; - u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); - - if (adapter->is_suspended) { - mwifiex_dbg(adapter, ERROR, - "%s: not allowed while suspended\n", __func__); - return -1; - } - - sdio_claim_host(card->func); - - ret = sdio_writesb(card->func, ioport, buffer, blk_cnt * blk_size); - - sdio_release_host(card->func); - - return ret; -} - -/* - * This function reads multiple data from SDIO card memory. - */ -static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *buffer, - u32 len, u32 port, u8 claim) -{ - struct sdio_mmc_card *card = adapter->card; - int ret; - u8 blk_mode = (port & MWIFIEX_SDIO_BYTE_MODE_MASK) ? BYTE_MODE - : BLOCK_MODE; - u32 blk_size = (blk_mode == BLOCK_MODE) ? MWIFIEX_SDIO_BLOCK_SIZE : 1; - u32 blk_cnt = (blk_mode == BLOCK_MODE) ? (len / MWIFIEX_SDIO_BLOCK_SIZE) - : len; - u32 ioport = (port & MWIFIEX_SDIO_IO_PORT_MASK); - - if (claim) - sdio_claim_host(card->func); - - ret = sdio_readsb(card->func, buffer, ioport, blk_cnt * blk_size); - - if (claim) - sdio_release_host(card->func); - - return ret; -} - -/* - * This function wakes up the card. - * - * A host power up command is written to the card configuration - * register to wake up the card. - */ -static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) -{ - mwifiex_dbg(adapter, EVENT, - "event: wakeup device...\n"); - - return mwifiex_write_reg(adapter, CONFIGURATION_REG, HOST_POWER_UP); -} - -/* - * This function is called after the card has woken up. - * - * The card configuration register is reset. - */ -static int mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) -{ - mwifiex_dbg(adapter, EVENT, - "cmd: wakeup device completed\n"); - - return mwifiex_write_reg(adapter, CONFIGURATION_REG, 0); -} - -/* - * This function is used to initialize IO ports for the - * chipsets supporting SDIO new mode eg SD8897. - */ -static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter) -{ - u8 reg; - struct sdio_mmc_card *card = adapter->card; - - adapter->ioport = MEM_PORT; - - /* enable sdio new mode */ - if (mwifiex_read_reg(adapter, card->reg->card_cfg_2_1_reg, ®)) - return -1; - if (mwifiex_write_reg(adapter, card->reg->card_cfg_2_1_reg, - reg | CMD53_NEW_MODE)) - return -1; - - /* Configure cmd port and enable reading rx length from the register */ - if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_0, ®)) - return -1; - if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_0, - reg | CMD_PORT_RD_LEN_EN)) - return -1; - - /* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is - * completed - */ - if (mwifiex_read_reg(adapter, card->reg->cmd_cfg_1, ®)) - return -1; - if (mwifiex_write_reg(adapter, card->reg->cmd_cfg_1, - reg | CMD_PORT_AUTO_EN)) - return -1; - - return 0; -} - -/* This function initializes the IO ports. - * - * The following operations are performed - - * - Read the IO ports (0, 1 and 2) - * - Set host interrupt Reset-To-Read to clear - * - Set auto re-enable interrupt - */ -static int mwifiex_init_sdio_ioport(struct mwifiex_adapter *adapter) -{ - u8 reg; - struct sdio_mmc_card *card = adapter->card; - - adapter->ioport = 0; - - if (card->supports_sdio_new_mode) { - if (mwifiex_init_sdio_new_mode(adapter)) - return -1; - goto cont; - } - - /* Read the IO port */ - if (!mwifiex_read_reg(adapter, card->reg->io_port_0_reg, ®)) - adapter->ioport |= (reg & 0xff); - else - return -1; - - if (!mwifiex_read_reg(adapter, card->reg->io_port_1_reg, ®)) - adapter->ioport |= ((reg & 0xff) << 8); - else - return -1; - - if (!mwifiex_read_reg(adapter, card->reg->io_port_2_reg, ®)) - adapter->ioport |= ((reg & 0xff) << 16); - else - return -1; -cont: - mwifiex_dbg(adapter, INFO, - "info: SDIO FUNC1 IO port: %#x\n", adapter->ioport); - - /* Set Host interrupt reset to read to clear */ - if (!mwifiex_read_reg(adapter, card->reg->host_int_rsr_reg, ®)) - mwifiex_write_reg(adapter, card->reg->host_int_rsr_reg, - reg | card->reg->sdio_int_mask); - else - return -1; - - /* Dnld/Upld ready set to auto reset */ - if (!mwifiex_read_reg(adapter, card->reg->card_misc_cfg_reg, ®)) - mwifiex_write_reg(adapter, card->reg->card_misc_cfg_reg, - reg | AUTO_RE_ENABLE_INT); - else - return -1; - - return 0; -} - -/* - * This function sends data to the card. - */ -static int mwifiex_write_data_to_card(struct mwifiex_adapter *adapter, - u8 *payload, u32 pkt_len, u32 port) -{ - u32 i = 0; - int ret; - - do { - ret = mwifiex_write_data_sync(adapter, payload, pkt_len, port); - if (ret) { - i++; - mwifiex_dbg(adapter, ERROR, - "host_to_card, write iomem\t" - "(%d) failed: %d\n", i, ret); - if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) - mwifiex_dbg(adapter, ERROR, - "write CFG reg failed\n"); - - ret = -1; - if (i > MAX_WRITE_IOMEM_RETRY) - return ret; - } - } while (ret == -1); - - return ret; -} - -/* - * This function gets the read port. - * - * If control port bit is set in MP read bitmap, the control port - * is returned, otherwise the current read port is returned and - * the value is increased (provided it does not reach the maximum - * limit, in which case it is reset to 1) - */ -static int mwifiex_get_rd_port(struct mwifiex_adapter *adapter, u8 *port) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - u32 rd_bitmap = card->mp_rd_bitmap; - - mwifiex_dbg(adapter, DATA, - "data: mp_rd_bitmap=0x%08x\n", rd_bitmap); - - if (card->supports_sdio_new_mode) { - if (!(rd_bitmap & reg->data_port_mask)) - return -1; - } else { - if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask))) - return -1; - } - - if ((card->has_control_mask) && - (card->mp_rd_bitmap & CTRL_PORT_MASK)) { - card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK); - *port = CTRL_PORT; - mwifiex_dbg(adapter, DATA, - "data: port=%d mp_rd_bitmap=0x%08x\n", - *port, card->mp_rd_bitmap); - return 0; - } - - if (!(card->mp_rd_bitmap & (1 << card->curr_rd_port))) - return -1; - - /* We are now handling the SDIO data ports */ - card->mp_rd_bitmap &= (u32)(~(1 << card->curr_rd_port)); - *port = card->curr_rd_port; - - if (++card->curr_rd_port == card->max_ports) - card->curr_rd_port = reg->start_rd_port; - - mwifiex_dbg(adapter, DATA, - "data: port=%d mp_rd_bitmap=0x%08x -> 0x%08x\n", - *port, rd_bitmap, card->mp_rd_bitmap); - - return 0; -} - -/* - * This function gets the write port for data. - * - * The current write port is returned if available and the value is - * increased (provided it does not reach the maximum limit, in which - * case it is reset to 1) - */ -static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - u32 wr_bitmap = card->mp_wr_bitmap; - - mwifiex_dbg(adapter, DATA, - "data: mp_wr_bitmap=0x%08x\n", wr_bitmap); - - if (!(wr_bitmap & card->mp_data_port_mask)) { - adapter->data_sent = true; - return -EBUSY; - } - - if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) { - card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port)); - *port = card->curr_wr_port; - if (++card->curr_wr_port == card->mp_end_port) - card->curr_wr_port = reg->start_wr_port; - } else { - adapter->data_sent = true; - return -EBUSY; - } - - if ((card->has_control_mask) && (*port == CTRL_PORT)) { - mwifiex_dbg(adapter, ERROR, - "invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", - *port, card->curr_wr_port, wr_bitmap, - card->mp_wr_bitmap); - return -1; - } - - mwifiex_dbg(adapter, DATA, - "data: port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n", - *port, wr_bitmap, card->mp_wr_bitmap); - - return 0; -} - -/* - * This function polls the card status. - */ -static int -mwifiex_sdio_poll_card_status(struct mwifiex_adapter *adapter, u8 bits) -{ - struct sdio_mmc_card *card = adapter->card; - u32 tries; - u8 cs; - - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - if (mwifiex_read_reg(adapter, card->reg->poll_reg, &cs)) - break; - else if ((cs & bits) == bits) - return 0; - - usleep_range(10, 20); - } - - mwifiex_dbg(adapter, ERROR, - "poll card status failed, tries = %d\n", tries); - - return -1; -} - -/* - * This function reads the firmware status. - */ -static int -mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - u8 fws0, fws1; - - if (mwifiex_read_reg(adapter, reg->status_reg_0, &fws0)) - return -1; - - if (mwifiex_read_reg(adapter, reg->status_reg_1, &fws1)) - return -1; - - *dat = (u16) ((fws1 << 8) | fws0); - - return 0; -} - -/* - * This function disables the host interrupt. - * - * The host interrupt mask is read, the disable bit is reset and - * written back to the card host interrupt mask register. - */ -static void mwifiex_sdio_disable_host_int(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - struct sdio_func *func = card->func; - - sdio_claim_host(func); - mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, 0); - sdio_release_irq(func); - sdio_release_host(func); -} - -/* - * This function reads the interrupt status from card. - */ -static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - u8 sdio_ireg; - unsigned long flags; - - if (mwifiex_read_data_sync(adapter, card->mp_regs, - card->reg->max_mp_regs, - REG_PORT | MWIFIEX_SDIO_BYTE_MODE_MASK, 0)) { - mwifiex_dbg(adapter, ERROR, "read mp_regs failed\n"); - return; - } - - sdio_ireg = card->mp_regs[card->reg->host_int_status_reg]; - if (sdio_ireg) { - /* - * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS - * For SDIO new mode CMD port interrupts - * DN_LD_CMD_PORT_HOST_INT_STATUS and/or - * UP_LD_CMD_PORT_HOST_INT_STATUS - * Clear the interrupt status register - */ - mwifiex_dbg(adapter, INTR, - "int: sdio_ireg = %#x\n", sdio_ireg); - spin_lock_irqsave(&adapter->int_lock, flags); - adapter->int_status |= sdio_ireg; - spin_unlock_irqrestore(&adapter->int_lock, flags); - } -} - -/* - * SDIO interrupt handler. - * - * This function reads the interrupt status from firmware and handles - * the interrupt in current thread (ksdioirqd) right away. - */ -static void -mwifiex_sdio_interrupt(struct sdio_func *func) -{ - struct mwifiex_adapter *adapter; - struct sdio_mmc_card *card; - - card = sdio_get_drvdata(func); - if (!card || !card->adapter) { - pr_debug("int: func=%p card=%p adapter=%p\n", - func, card, card ? card->adapter : NULL); - return; - } - adapter = card->adapter; - - if (!adapter->pps_uapsd_mode && adapter->ps_state == PS_STATE_SLEEP) - adapter->ps_state = PS_STATE_AWAKE; - - mwifiex_interrupt_status(adapter); - mwifiex_main_process(adapter); -} - -/* - * This function enables the host interrupt. - * - * The host interrupt enable mask is written to the card - * host interrupt mask register. - */ -static int mwifiex_sdio_enable_host_int(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - struct sdio_func *func = card->func; - int ret; - - sdio_claim_host(func); - - /* Request the SDIO IRQ */ - ret = sdio_claim_irq(func, mwifiex_sdio_interrupt); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "claim irq failed: ret=%d\n", ret); - goto out; - } - - /* Simply write the mask to the register */ - ret = mwifiex_write_reg_locked(func, card->reg->host_int_mask_reg, - card->reg->host_int_enable); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "enable host interrupt failed\n"); - sdio_release_irq(func); - } - -out: - sdio_release_host(func); - return ret; -} - -/* - * This function sends a data buffer to the card. - */ -static int mwifiex_sdio_card_to_host(struct mwifiex_adapter *adapter, - u32 *type, u8 *buffer, - u32 npayload, u32 ioport) -{ - int ret; - u32 nb; - - if (!buffer) { - mwifiex_dbg(adapter, ERROR, - "%s: buffer is NULL\n", __func__); - return -1; - } - - ret = mwifiex_read_data_sync(adapter, buffer, npayload, ioport, 1); - - if (ret) { - mwifiex_dbg(adapter, ERROR, - "%s: read iomem failed: %d\n", __func__, - ret); - return -1; - } - - nb = le16_to_cpu(*(__le16 *) (buffer)); - if (nb > npayload) { - mwifiex_dbg(adapter, ERROR, - "%s: invalid packet, nb=%d npayload=%d\n", - __func__, nb, npayload); - return -1; - } - - *type = le16_to_cpu(*(__le16 *) (buffer + 2)); - - return ret; -} - -/* - * This function downloads the firmware to the card. - * - * Firmware is downloaded to the card in blocks. Every block download - * is tested for CRC errors, and retried a number of times before - * returning failure. - */ -static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *fw) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - int ret; - u8 *firmware = fw->fw_buf; - u32 firmware_len = fw->fw_len; - u32 offset = 0; - u8 base0, base1; - u8 *fwbuf; - u16 len = 0; - u32 txlen, tx_blocks = 0, tries; - u32 i = 0; - - if (!firmware_len) { - mwifiex_dbg(adapter, ERROR, - "firmware image not found! Terminating download\n"); - return -1; - } - - mwifiex_dbg(adapter, INFO, - "info: downloading FW image (%d bytes)\n", - firmware_len); - - /* Assume that the allocated buffer is 8-byte aligned */ - fwbuf = kzalloc(MWIFIEX_UPLD_SIZE, GFP_KERNEL); - if (!fwbuf) - return -ENOMEM; - - sdio_claim_host(card->func); - - /* Perform firmware data transfer */ - do { - /* The host polls for the DN_LD_CARD_RDY and CARD_IO_READY - bits */ - ret = mwifiex_sdio_poll_card_status(adapter, CARD_IO_READY | - DN_LD_CARD_RDY); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "FW download with helper:\t" - "poll status timeout @ %d\n", offset); - goto done; - } - - /* More data? */ - if (offset >= firmware_len) - break; - - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - ret = mwifiex_read_reg(adapter, reg->base_0_reg, - &base0); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "dev BASE0 register read failed:\t" - "base0=%#04X(%d). Terminating dnld\n", - base0, base0); - goto done; - } - ret = mwifiex_read_reg(adapter, reg->base_1_reg, - &base1); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "dev BASE1 register read failed:\t" - "base1=%#04X(%d). Terminating dnld\n", - base1, base1); - goto done; - } - len = (u16) (((base1 & 0xff) << 8) | (base0 & 0xff)); - - if (len) - break; - - usleep_range(10, 20); - } - - if (!len) { - break; - } else if (len > MWIFIEX_UPLD_SIZE) { - mwifiex_dbg(adapter, ERROR, - "FW dnld failed @ %d, invalid length %d\n", - offset, len); - ret = -1; - goto done; - } - - txlen = len; - - if (len & BIT(0)) { - i++; - if (i > MAX_WRITE_IOMEM_RETRY) { - mwifiex_dbg(adapter, ERROR, - "FW dnld failed @ %d, over max retry\n", - offset); - ret = -1; - goto done; - } - mwifiex_dbg(adapter, ERROR, - "CRC indicated by the helper:\t" - "len = 0x%04X, txlen = %d\n", len, txlen); - len &= ~BIT(0); - /* Setting this to 0 to resend from same offset */ - txlen = 0; - } else { - i = 0; - - /* Set blocksize to transfer - checking for last - block */ - if (firmware_len - offset < txlen) - txlen = firmware_len - offset; - - tx_blocks = (txlen + MWIFIEX_SDIO_BLOCK_SIZE - 1) - / MWIFIEX_SDIO_BLOCK_SIZE; - - /* Copy payload to buffer */ - memmove(fwbuf, &firmware[offset], txlen); - } - - ret = mwifiex_write_data_sync(adapter, fwbuf, tx_blocks * - MWIFIEX_SDIO_BLOCK_SIZE, - adapter->ioport); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "FW download, write iomem (%d) failed @ %d\n", - i, offset); - if (mwifiex_write_reg(adapter, CONFIGURATION_REG, 0x04)) - mwifiex_dbg(adapter, ERROR, - "write CFG reg failed\n"); - - ret = -1; - goto done; - } - - offset += txlen; - } while (true); - - sdio_release_host(card->func); - - mwifiex_dbg(adapter, MSG, - "info: FW download over, size %d bytes\n", offset); - - ret = 0; -done: - kfree(fwbuf); - return ret; -} - -/* - * This function checks the firmware status in card. - * - * The winner interface is also determined by this function. - */ -static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter, - u32 poll_num) -{ - struct sdio_mmc_card *card = adapter->card; - int ret = 0; - u16 firmware_stat; - u32 tries; - u8 winner_status; - - /* Wait for firmware initialization event */ - for (tries = 0; tries < poll_num; tries++) { - ret = mwifiex_sdio_read_fw_status(adapter, &firmware_stat); - if (ret) - continue; - if (firmware_stat == FIRMWARE_READY_SDIO) { - ret = 0; - break; - } else { - msleep(100); - ret = -1; - } - } - - if (ret) { - if (mwifiex_read_reg - (adapter, card->reg->status_reg_0, &winner_status)) - winner_status = 0; - - if (winner_status) - adapter->winner = 0; - else - adapter->winner = 1; - } - return ret; -} - -/* - * This function decode sdio aggreation pkt. - * - * Based on the the data block size and pkt_len, - * skb data will be decoded to few packets. - */ -static void mwifiex_deaggr_sdio_pkt(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - u32 total_pkt_len, pkt_len; - struct sk_buff *skb_deaggr; - u32 pkt_type; - u16 blk_size; - u8 blk_num; - u8 *data; - - data = skb->data; - total_pkt_len = skb->len; - - while (total_pkt_len >= (SDIO_HEADER_OFFSET + INTF_HEADER_LEN)) { - if (total_pkt_len < adapter->sdio_rx_block_size) - break; - blk_num = *(data + BLOCK_NUMBER_OFFSET); - blk_size = adapter->sdio_rx_block_size * blk_num; - if (blk_size > total_pkt_len) { - mwifiex_dbg(adapter, ERROR, - "%s: error in blk_size,\t" - "blk_num=%d, blk_size=%d, total_pkt_len=%d\n", - __func__, blk_num, blk_size, total_pkt_len); - break; - } - pkt_len = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET)); - pkt_type = le16_to_cpu(*(__le16 *)(data + SDIO_HEADER_OFFSET + - 2)); - if ((pkt_len + SDIO_HEADER_OFFSET) > blk_size) { - mwifiex_dbg(adapter, ERROR, - "%s: error in pkt_len,\t" - "pkt_len=%d, blk_size=%d\n", - __func__, pkt_len, blk_size); - break; - } - skb_deaggr = mwifiex_alloc_dma_align_buf(pkt_len, - GFP_KERNEL | GFP_DMA); - if (!skb_deaggr) - break; - skb_put(skb_deaggr, pkt_len); - memcpy(skb_deaggr->data, data + SDIO_HEADER_OFFSET, pkt_len); - skb_pull(skb_deaggr, INTF_HEADER_LEN); - - mwifiex_handle_rx_packet(adapter, skb_deaggr); - data += blk_size; - total_pkt_len -= blk_size; - } -} - -/* - * This function decodes a received packet. - * - * Based on the type, the packet is treated as either a data, or - * a command response, or an event, and the correct handler - * function is invoked. - */ -static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter, - struct sk_buff *skb, u32 upld_typ) -{ - u8 *cmd_buf; - __le16 *curr_ptr = (__le16 *)skb->data; - u16 pkt_len = le16_to_cpu(*curr_ptr); - struct mwifiex_rxinfo *rx_info; - - if (upld_typ != MWIFIEX_TYPE_AGGR_DATA) { - skb_trim(skb, pkt_len); - skb_pull(skb, INTF_HEADER_LEN); - } - - switch (upld_typ) { - case MWIFIEX_TYPE_AGGR_DATA: - mwifiex_dbg(adapter, INFO, - "info: --- Rx: Aggr Data packet ---\n"); - rx_info = MWIFIEX_SKB_RXCB(skb); - rx_info->buf_type = MWIFIEX_TYPE_AGGR_DATA; - if (adapter->rx_work_enabled) { - skb_queue_tail(&adapter->rx_data_q, skb); - atomic_inc(&adapter->rx_pending); - adapter->data_received = true; - } else { - mwifiex_deaggr_sdio_pkt(adapter, skb); - dev_kfree_skb_any(skb); - } - break; - - case MWIFIEX_TYPE_DATA: - mwifiex_dbg(adapter, DATA, - "info: --- Rx: Data packet ---\n"); - if (adapter->rx_work_enabled) { - skb_queue_tail(&adapter->rx_data_q, skb); - adapter->data_received = true; - atomic_inc(&adapter->rx_pending); - } else { - mwifiex_handle_rx_packet(adapter, skb); - } - break; - - case MWIFIEX_TYPE_CMD: - mwifiex_dbg(adapter, CMD, - "info: --- Rx: Cmd Response ---\n"); - /* take care of curr_cmd = NULL case */ - if (!adapter->curr_cmd) { - cmd_buf = adapter->upld_buf; - - if (adapter->ps_state == PS_STATE_SLEEP_CFM) - mwifiex_process_sleep_confirm_resp(adapter, - skb->data, - skb->len); - - memcpy(cmd_buf, skb->data, - min_t(u32, MWIFIEX_SIZE_OF_CMD_BUFFER, - skb->len)); - - dev_kfree_skb_any(skb); - } else { - adapter->cmd_resp_received = true; - adapter->curr_cmd->resp_skb = skb; - } - break; - - case MWIFIEX_TYPE_EVENT: - mwifiex_dbg(adapter, EVENT, - "info: --- Rx: Event ---\n"); - adapter->event_cause = le32_to_cpu(*(__le32 *) skb->data); - - if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE)) - memcpy(adapter->event_body, - skb->data + MWIFIEX_EVENT_HEADER_LEN, - skb->len); - - /* event cause has been saved to adapter->event_cause */ - adapter->event_received = true; - adapter->event_skb = skb; - - break; - - default: - mwifiex_dbg(adapter, ERROR, - "unknown upload type %#x\n", upld_typ); - dev_kfree_skb_any(skb); - break; - } - - return 0; -} - -/* - * This function transfers received packets from card to driver, performing - * aggregation if required. - * - * For data received on control port, or if aggregation is disabled, the - * received buffers are uploaded as separate packets. However, if aggregation - * is enabled and required, the buffers are copied onto an aggregation buffer, - * provided there is space left, processed and finally uploaded. - */ -static int mwifiex_sdio_card_to_host_mp_aggr(struct mwifiex_adapter *adapter, - u16 rx_len, u8 port) -{ - struct sdio_mmc_card *card = adapter->card; - s32 f_do_rx_aggr = 0; - s32 f_do_rx_cur = 0; - s32 f_aggr_cur = 0; - s32 f_post_aggr_cur = 0; - struct sk_buff *skb_deaggr; - struct sk_buff *skb = NULL; - u32 pkt_len, pkt_type, mport, pind; - u8 *curr_ptr; - - if ((card->has_control_mask) && (port == CTRL_PORT)) { - /* Read the command Resp without aggr */ - mwifiex_dbg(adapter, CMD, - "info: %s: no aggregation for cmd\t" - "response\n", __func__); - - f_do_rx_cur = 1; - goto rx_curr_single; - } - - if (!card->mpa_rx.enabled) { - mwifiex_dbg(adapter, WARN, - "info: %s: rx aggregation disabled\n", - __func__); - - f_do_rx_cur = 1; - goto rx_curr_single; - } - - if ((!card->has_control_mask && (card->mp_rd_bitmap & - card->reg->data_port_mask)) || - (card->has_control_mask && (card->mp_rd_bitmap & - (~((u32) CTRL_PORT_MASK))))) { - /* Some more data RX pending */ - mwifiex_dbg(adapter, INFO, - "info: %s: not last packet\n", __func__); - - if (MP_RX_AGGR_IN_PROGRESS(card)) { - if (MP_RX_AGGR_BUF_HAS_ROOM(card, 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 */ - mwifiex_dbg(adapter, INFO, - "info: %s: last packet\n", __func__); - - if (MP_RX_AGGR_IN_PROGRESS(card)) { - f_do_rx_aggr = 1; - if (MP_RX_AGGR_BUF_HAS_ROOM(card, 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) { - mwifiex_dbg(adapter, INFO, - "info: current packet aggregation\n"); - /* Curr pkt can be aggregated */ - mp_rx_aggr_setup(card, rx_len, port); - - if (MP_RX_AGGR_PKT_LIMIT_REACHED(card) || - mp_rx_aggr_port_limit_reached(card)) { - mwifiex_dbg(adapter, INFO, - "info: %s: aggregated packet\t" - "limit reached\n", __func__); - /* No more pkts allowed in Aggr buf, rx it */ - f_do_rx_aggr = 1; - } - } - - if (f_do_rx_aggr) { - /* do aggr RX now */ - mwifiex_dbg(adapter, DATA, - "info: do_rx_aggr: num of packets: %d\n", - card->mpa_rx.pkt_cnt); - - if (card->supports_sdio_new_mode) { - int i; - u32 port_count; - - for (i = 0, port_count = 0; i < card->max_ports; i++) - if (card->mpa_rx.ports & BIT(i)) - port_count++; - - /* Reading data from "start_port + 0" to "start_port + - * port_count -1", so decrease the count by 1 - */ - port_count--; - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (port_count << 8)) + card->mpa_rx.start_port; - } else { - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (card->mpa_rx.ports << 4)) + - card->mpa_rx.start_port; - } - - if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf, - card->mpa_rx.buf_len, mport, 1)) - goto error; - - curr_ptr = card->mpa_rx.buf; - - for (pind = 0; pind < card->mpa_rx.pkt_cnt; pind++) { - u32 *len_arr = card->mpa_rx.len_arr; - - /* get curr PKT len & type */ - pkt_len = le16_to_cpu(*(__le16 *) &curr_ptr[0]); - pkt_type = le16_to_cpu(*(__le16 *) &curr_ptr[2]); - - /* copy pkt to deaggr buf */ - skb_deaggr = mwifiex_alloc_dma_align_buf(len_arr[pind], - GFP_KERNEL | - GFP_DMA); - if (!skb_deaggr) { - mwifiex_dbg(adapter, ERROR, "skb allocation failure\t" - "drop pkt len=%d type=%d\n", - pkt_len, pkt_type); - curr_ptr += len_arr[pind]; - continue; - } - - skb_put(skb_deaggr, len_arr[pind]); - - if ((pkt_type == MWIFIEX_TYPE_DATA || - (pkt_type == MWIFIEX_TYPE_AGGR_DATA && - adapter->sdio_rx_aggr_enable)) && - (pkt_len <= len_arr[pind])) { - - memcpy(skb_deaggr->data, curr_ptr, pkt_len); - - skb_trim(skb_deaggr, pkt_len); - - /* Process de-aggr packet */ - mwifiex_decode_rx_packet(adapter, skb_deaggr, - pkt_type); - } else { - mwifiex_dbg(adapter, ERROR, - "drop wrong aggr pkt:\t" - "sdio_single_port_rx_aggr=%d\t" - "type=%d len=%d max_len=%d\n", - adapter->sdio_rx_aggr_enable, - pkt_type, pkt_len, len_arr[pind]); - dev_kfree_skb_any(skb_deaggr); - } - curr_ptr += len_arr[pind]; - } - MP_RX_AGGR_BUF_RESET(card); - } - -rx_curr_single: - if (f_do_rx_cur) { - mwifiex_dbg(adapter, INFO, "info: RX: port: %d, rx_len: %d\n", - port, rx_len); - - skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); - if (!skb) { - mwifiex_dbg(adapter, ERROR, - "single skb allocated fail,\t" - "drop pkt port=%d len=%d\n", port, rx_len); - if (mwifiex_sdio_card_to_host(adapter, &pkt_type, - card->mpa_rx.buf, rx_len, - adapter->ioport + port)) - goto error; - return 0; - } - - skb_put(skb, rx_len); - - if (mwifiex_sdio_card_to_host(adapter, &pkt_type, - skb->data, skb->len, - adapter->ioport + port)) - goto error; - if (!adapter->sdio_rx_aggr_enable && - pkt_type == MWIFIEX_TYPE_AGGR_DATA) { - mwifiex_dbg(adapter, ERROR, "drop wrong pkt type %d\t" - "current SDIO RX Aggr not enabled\n", - pkt_type); - dev_kfree_skb_any(skb); - return 0; - } - - mwifiex_decode_rx_packet(adapter, skb, pkt_type); - } - if (f_post_aggr_cur) { - mwifiex_dbg(adapter, INFO, - "info: current packet aggregation\n"); - /* Curr pkt can be aggregated */ - mp_rx_aggr_setup(card, rx_len, port); - } - - return 0; -error: - if (MP_RX_AGGR_IN_PROGRESS(card)) - MP_RX_AGGR_BUF_RESET(card); - - if (f_do_rx_cur && skb) - /* Single transfer pending. Free curr buff also */ - dev_kfree_skb_any(skb); - - return -1; -} - -/* - * This function checks the current interrupt status. - * - * The following interrupts are checked and handled by this function - - * - Data sent - * - Command sent - * - Packets received - * - * Since the firmware does not generate download ready interrupt if the - * port updated is command port only, command sent interrupt checking - * should be done manually, and for every SDIO interrupt. - * - * In case of Rx packets received, the packets are uploaded from card to - * host and processed accordingly. - */ -static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - int ret = 0; - u8 sdio_ireg; - struct sk_buff *skb; - u8 port = CTRL_PORT; - u32 len_reg_l, len_reg_u; - u32 rx_blocks; - u16 rx_len; - unsigned long flags; - u32 bitmap; - u8 cr; - - spin_lock_irqsave(&adapter->int_lock, flags); - sdio_ireg = adapter->int_status; - adapter->int_status = 0; - spin_unlock_irqrestore(&adapter->int_lock, flags); - - if (!sdio_ireg) - return ret; - - /* Following interrupt is only for SDIO new mode */ - if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent) - adapter->cmd_sent = false; - - /* Following interrupt is only for SDIO new mode */ - if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) { - u32 pkt_type; - - /* read the len of control packet */ - rx_len = card->mp_regs[reg->cmd_rd_len_1] << 8; - rx_len |= (u16)card->mp_regs[reg->cmd_rd_len_0]; - rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE); - if (rx_len <= INTF_HEADER_LEN || - (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > - MWIFIEX_RX_DATA_BUF_SIZE) - return -1; - rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); - mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", rx_len); - - skb = mwifiex_alloc_dma_align_buf(rx_len, GFP_KERNEL | GFP_DMA); - if (!skb) - return -1; - - skb_put(skb, rx_len); - - if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data, - skb->len, adapter->ioport | - CMD_PORT_SLCT)) { - mwifiex_dbg(adapter, ERROR, - "%s: failed to card_to_host", __func__); - dev_kfree_skb_any(skb); - goto term_cmd; - } - - if ((pkt_type != MWIFIEX_TYPE_CMD) && - (pkt_type != MWIFIEX_TYPE_EVENT)) - mwifiex_dbg(adapter, ERROR, - "%s:Received wrong packet on cmd port", - __func__); - - mwifiex_decode_rx_packet(adapter, skb, pkt_type); - } - - if (sdio_ireg & DN_LD_HOST_INT_STATUS) { - bitmap = (u32) card->mp_regs[reg->wr_bitmap_l]; - bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8; - if (card->supports_sdio_new_mode) { - bitmap |= - ((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16; - bitmap |= - ((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24; - } - card->mp_wr_bitmap = bitmap; - - mwifiex_dbg(adapter, INTR, - "int: DNLD: wr_bitmap=0x%x\n", - card->mp_wr_bitmap); - if (adapter->data_sent && - (card->mp_wr_bitmap & card->mp_data_port_mask)) { - mwifiex_dbg(adapter, INTR, - "info: <--- Tx DONE Interrupt --->\n"); - adapter->data_sent = false; - } - } - - /* As firmware will not generate download ready interrupt if the port - updated is command port only, cmd_sent should be done for any SDIO - interrupt. */ - if (card->has_control_mask && adapter->cmd_sent) { - /* Check if firmware has attach buffer at command port and - update just that in wr_bit_map. */ - card->mp_wr_bitmap |= - (u32) card->mp_regs[reg->wr_bitmap_l] & CTRL_PORT_MASK; - if (card->mp_wr_bitmap & CTRL_PORT_MASK) - adapter->cmd_sent = false; - } - - mwifiex_dbg(adapter, INTR, "info: cmd_sent=%d data_sent=%d\n", - adapter->cmd_sent, adapter->data_sent); - if (sdio_ireg & UP_LD_HOST_INT_STATUS) { - bitmap = (u32) card->mp_regs[reg->rd_bitmap_l]; - bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8; - if (card->supports_sdio_new_mode) { - bitmap |= - ((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16; - bitmap |= - ((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24; - } - card->mp_rd_bitmap = bitmap; - mwifiex_dbg(adapter, INTR, - "int: UPLD: rd_bitmap=0x%x\n", - card->mp_rd_bitmap); - - while (true) { - ret = mwifiex_get_rd_port(adapter, &port); - if (ret) { - mwifiex_dbg(adapter, INFO, - "info: no more rd_port available\n"); - break; - } - len_reg_l = reg->rd_len_p0_l + (port << 1); - len_reg_u = reg->rd_len_p0_u + (port << 1); - rx_len = ((u16) card->mp_regs[len_reg_u]) << 8; - rx_len |= (u16) card->mp_regs[len_reg_l]; - mwifiex_dbg(adapter, INFO, - "info: RX: port=%d rx_len=%u\n", - port, rx_len); - rx_blocks = - (rx_len + MWIFIEX_SDIO_BLOCK_SIZE - - 1) / MWIFIEX_SDIO_BLOCK_SIZE; - if (rx_len <= INTF_HEADER_LEN || - (card->mpa_rx.enabled && - ((rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) > - card->mpa_rx.buf_size))) { - mwifiex_dbg(adapter, ERROR, - "invalid rx_len=%d\n", - rx_len); - return -1; - } - - rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE); - mwifiex_dbg(adapter, INFO, "info: rx_len = %d\n", - rx_len); - - if (mwifiex_sdio_card_to_host_mp_aggr(adapter, rx_len, - port)) { - mwifiex_dbg(adapter, ERROR, - "card_to_host_mpa failed: int status=%#x\n", - sdio_ireg); - goto term_cmd; - } - } - } - - return 0; - -term_cmd: - /* terminate cmd */ - if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) - mwifiex_dbg(adapter, ERROR, "read CFG reg failed\n"); - else - mwifiex_dbg(adapter, INFO, - "info: CFG reg val = %d\n", cr); - - if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04))) - mwifiex_dbg(adapter, ERROR, - "write CFG reg failed\n"); - else - mwifiex_dbg(adapter, INFO, "info: write success\n"); - - if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr)) - mwifiex_dbg(adapter, ERROR, - "read CFG reg failed\n"); - else - mwifiex_dbg(adapter, INFO, - "info: CFG reg val =%x\n", cr); - - return -1; -} - -/* - * This function aggregates transmission buffers in driver and downloads - * the aggregated packet to card. - * - * The individual packets are aggregated by copying into an aggregation - * buffer and then downloaded to the card. Previous unsent packets in the - * aggregation buffer are pre-copied first before new packets are added. - * Aggregation is done till there is space left in the aggregation buffer, - * or till new packets are available. - * - * The function will only download the packet to the card when aggregation - * stops, otherwise it will just aggregate the packet in aggregation buffer - * and return. - */ -static int mwifiex_host_to_card_mp_aggr(struct mwifiex_adapter *adapter, - u8 *payload, u32 pkt_len, u32 port, - u32 next_pkt_len) -{ - struct sdio_mmc_card *card = adapter->card; - int ret = 0; - s32 f_send_aggr_buf = 0; - s32 f_send_cur_buf = 0; - s32 f_precopy_cur_buf = 0; - s32 f_postcopy_cur_buf = 0; - u32 mport; - - if (!card->mpa_tx.enabled || - (card->has_control_mask && (port == CTRL_PORT)) || - (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) { - mwifiex_dbg(adapter, WARN, - "info: %s: tx aggregation disabled\n", - __func__); - - f_send_cur_buf = 1; - goto tx_curr_single; - } - - if (next_pkt_len) { - /* More pkt in TX queue */ - mwifiex_dbg(adapter, INFO, - "info: %s: more packets in queue.\n", - __func__); - - if (MP_TX_AGGR_IN_PROGRESS(card)) { - if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) { - f_precopy_cur_buf = 1; - - if (!(card->mp_wr_bitmap & - (1 << card->curr_wr_port)) || - !MP_TX_AGGR_BUF_HAS_ROOM( - card, pkt_len + next_pkt_len)) - f_send_aggr_buf = 1; - } else { - /* No room in Aggr buf, send it */ - f_send_aggr_buf = 1; - - if (!(card->mp_wr_bitmap & - (1 << card->curr_wr_port))) - f_send_cur_buf = 1; - else - f_postcopy_cur_buf = 1; - } - } else { - if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len) && - (card->mp_wr_bitmap & (1 << card->curr_wr_port))) - f_precopy_cur_buf = 1; - else - f_send_cur_buf = 1; - } - } else { - /* Last pkt in TX queue */ - mwifiex_dbg(adapter, INFO, - "info: %s: Last packet in Tx Queue.\n", - __func__); - - if (MP_TX_AGGR_IN_PROGRESS(card)) { - /* some packs in Aggr buf already */ - f_send_aggr_buf = 1; - - if (MP_TX_AGGR_BUF_HAS_ROOM(card, pkt_len)) - f_precopy_cur_buf = 1; - else - /* No room in Aggr buf, send it */ - f_send_cur_buf = 1; - } else { - f_send_cur_buf = 1; - } - } - - if (f_precopy_cur_buf) { - mwifiex_dbg(adapter, DATA, - "data: %s: precopy current buffer\n", - __func__); - MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); - - if (MP_TX_AGGR_PKT_LIMIT_REACHED(card) || - mp_tx_aggr_port_limit_reached(card)) - /* No more pkts allowed in Aggr buf, send it */ - f_send_aggr_buf = 1; - } - - if (f_send_aggr_buf) { - mwifiex_dbg(adapter, DATA, - "data: %s: send aggr buffer: %d %d\n", - __func__, card->mpa_tx.start_port, - card->mpa_tx.ports); - if (card->supports_sdio_new_mode) { - u32 port_count; - int i; - - for (i = 0, port_count = 0; i < card->max_ports; i++) - if (card->mpa_tx.ports & BIT(i)) - port_count++; - - /* Writing data from "start_port + 0" to "start_port + - * port_count -1", so decrease the count by 1 - */ - port_count--; - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (port_count << 8)) + card->mpa_tx.start_port; - } else { - mport = (adapter->ioport | SDIO_MPA_ADDR_BASE | - (card->mpa_tx.ports << 4)) + - card->mpa_tx.start_port; - } - - ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf, - card->mpa_tx.buf_len, mport); - - MP_TX_AGGR_BUF_RESET(card); - } - -tx_curr_single: - if (f_send_cur_buf) { - mwifiex_dbg(adapter, DATA, - "data: %s: send current buffer %d\n", - __func__, port); - ret = mwifiex_write_data_to_card(adapter, payload, pkt_len, - adapter->ioport + port); - } - - if (f_postcopy_cur_buf) { - mwifiex_dbg(adapter, DATA, - "data: %s: postcopy current buffer\n", - __func__); - MP_TX_AGGR_BUF_PUT(card, payload, pkt_len, port); - } - - return ret; -} - -/* - * 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 SDIO 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. - */ -static int mwifiex_sdio_host_to_card(struct mwifiex_adapter *adapter, - u8 type, struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - struct sdio_mmc_card *card = adapter->card; - int ret; - u32 buf_block_len; - u32 blk_size; - u32 port = CTRL_PORT; - u8 *payload = (u8 *)skb->data; - u32 pkt_len = skb->len; - - /* Allocate buffer and copy payload */ - blk_size = MWIFIEX_SDIO_BLOCK_SIZE; - buf_block_len = (pkt_len + blk_size - 1) / blk_size; - *(__le16 *)&payload[0] = cpu_to_le16((u16)pkt_len); - *(__le16 *)&payload[2] = cpu_to_le16(type); - - /* - * This is SDIO specific header - * u16 length, - * u16 type (MWIFIEX_TYPE_DATA = 0, MWIFIEX_TYPE_CMD = 1, - * MWIFIEX_TYPE_EVENT = 3) - */ - if (type == MWIFIEX_TYPE_DATA) { - ret = mwifiex_get_wr_port_data(adapter, &port); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "%s: no wr_port available\n", - __func__); - return ret; - } - } else { - adapter->cmd_sent = true; - /* Type must be MWIFIEX_TYPE_CMD */ - - if (pkt_len <= INTF_HEADER_LEN || - pkt_len > MWIFIEX_UPLD_SIZE) - mwifiex_dbg(adapter, ERROR, - "%s: payload=%p, nb=%d\n", - __func__, payload, pkt_len); - - if (card->supports_sdio_new_mode) - port = CMD_PORT_SLCT; - } - - /* Transfer data to card */ - pkt_len = buf_block_len * blk_size; - - if (tx_param) - ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, - port, tx_param->next_pkt_len - ); - else - ret = mwifiex_host_to_card_mp_aggr(adapter, payload, pkt_len, - port, 0); - - if (ret) { - if (type == MWIFIEX_TYPE_CMD) - adapter->cmd_sent = false; - if (type == MWIFIEX_TYPE_DATA) { - adapter->data_sent = false; - /* restore curr_wr_port in error cases */ - card->curr_wr_port = port; - card->mp_wr_bitmap |= (u32)(1 << card->curr_wr_port); - } - } else { - if (type == MWIFIEX_TYPE_DATA) { - if (!(card->mp_wr_bitmap & (1 << card->curr_wr_port))) - adapter->data_sent = true; - else - adapter->data_sent = false; - } - } - - return ret; -} - -/* - * This function allocates the MPA Tx and Rx buffers. - */ -static int mwifiex_alloc_sdio_mpa_buffers(struct mwifiex_adapter *adapter, - u32 mpa_tx_buf_size, u32 mpa_rx_buf_size) -{ - struct sdio_mmc_card *card = adapter->card; - u32 rx_buf_size; - int ret = 0; - - card->mpa_tx.buf = kzalloc(mpa_tx_buf_size, GFP_KERNEL); - if (!card->mpa_tx.buf) { - ret = -1; - goto error; - } - - card->mpa_tx.buf_size = mpa_tx_buf_size; - - rx_buf_size = max_t(u32, mpa_rx_buf_size, - (u32)SDIO_MAX_AGGR_BUF_SIZE); - card->mpa_rx.buf = kzalloc(rx_buf_size, GFP_KERNEL); - if (!card->mpa_rx.buf) { - ret = -1; - goto error; - } - - card->mpa_rx.buf_size = rx_buf_size; - -error: - if (ret) { - kfree(card->mpa_tx.buf); - kfree(card->mpa_rx.buf); - card->mpa_tx.buf_size = 0; - card->mpa_rx.buf_size = 0; - } - - return ret; -} - -/* - * This function unregisters the SDIO device. - * - * The SDIO IRQ is released, the function is disabled and driver - * data is set to null. - */ -static void -mwifiex_unregister_dev(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - if (adapter->card) { - sdio_claim_host(card->func); - sdio_disable_func(card->func); - sdio_release_host(card->func); - } -} - -/* - * This function registers the SDIO device. - * - * SDIO IRQ is claimed, block size is set and driver data is initialized. - */ -static int mwifiex_register_dev(struct mwifiex_adapter *adapter) -{ - int ret; - struct sdio_mmc_card *card = adapter->card; - struct sdio_func *func = card->func; - - /* save adapter pointer in card */ - card->adapter = adapter; - adapter->tx_buf_size = card->tx_buf_size; - - sdio_claim_host(func); - - /* Set block size */ - ret = sdio_set_block_size(card->func, MWIFIEX_SDIO_BLOCK_SIZE); - sdio_release_host(func); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "cannot set SDIO block size\n"); - return ret; - } - - - adapter->dev = &func->dev; - - strcpy(adapter->fw_name, card->firmware); - if (card->fw_dump_enh) { - adapter->mem_type_mapping_tbl = generic_mem_type_map; - adapter->num_mem_types = 1; - } else { - adapter->mem_type_mapping_tbl = mem_type_mapping_tbl; - adapter->num_mem_types = ARRAY_SIZE(mem_type_mapping_tbl); - } - - return 0; -} - -/* - * This function initializes the SDIO driver. - * - * The following initializations steps are followed - - * - Read the Host interrupt status register to acknowledge - * the first interrupt got from bootloader - * - Disable host interrupt mask register - * - Get SDIO port - * - Initialize SDIO variables in card - * - Allocate MP registers - * - Allocate MPA Tx and Rx buffers - */ -static int mwifiex_init_sdio(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - int ret; - u8 sdio_ireg; - - sdio_set_drvdata(card->func, card); - - /* - * 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. - */ - mwifiex_read_reg(adapter, card->reg->host_int_status_reg, &sdio_ireg); - - /* Get SDIO ioport */ - mwifiex_init_sdio_ioport(adapter); - - /* Initialize SDIO variables in card */ - card->mp_rd_bitmap = 0; - card->mp_wr_bitmap = 0; - card->curr_rd_port = reg->start_rd_port; - card->curr_wr_port = reg->start_wr_port; - - card->mp_data_port_mask = reg->data_port_mask; - - card->mpa_tx.buf_len = 0; - card->mpa_tx.pkt_cnt = 0; - card->mpa_tx.start_port = 0; - - card->mpa_tx.enabled = 1; - card->mpa_tx.pkt_aggr_limit = card->mp_agg_pkt_limit; - - card->mpa_rx.buf_len = 0; - card->mpa_rx.pkt_cnt = 0; - card->mpa_rx.start_port = 0; - - card->mpa_rx.enabled = 1; - card->mpa_rx.pkt_aggr_limit = card->mp_agg_pkt_limit; - - /* Allocate buffers for SDIO MP-A */ - card->mp_regs = kzalloc(reg->max_mp_regs, GFP_KERNEL); - if (!card->mp_regs) - return -ENOMEM; - - /* Allocate skb pointer buffers */ - card->mpa_rx.skb_arr = kzalloc((sizeof(void *)) * - card->mp_agg_pkt_limit, GFP_KERNEL); - card->mpa_rx.len_arr = kzalloc(sizeof(*card->mpa_rx.len_arr) * - card->mp_agg_pkt_limit, GFP_KERNEL); - ret = mwifiex_alloc_sdio_mpa_buffers(adapter, - card->mp_tx_agg_buf_size, - card->mp_rx_agg_buf_size); - - /* Allocate 32k MPA Tx/Rx buffers if 64k memory allocation fails */ - if (ret && (card->mp_tx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX || - card->mp_rx_agg_buf_size == MWIFIEX_MP_AGGR_BUF_SIZE_MAX)) { - /* Disable rx single port aggregation */ - adapter->host_disable_sdio_rx_aggr = true; - - ret = mwifiex_alloc_sdio_mpa_buffers - (adapter, MWIFIEX_MP_AGGR_BUF_SIZE_32K, - MWIFIEX_MP_AGGR_BUF_SIZE_32K); - if (ret) { - /* Disable multi port aggregation */ - card->mpa_tx.enabled = 0; - card->mpa_rx.enabled = 0; - } - } - - adapter->auto_tdls = card->can_auto_tdls; - adapter->ext_scan = card->can_ext_scan; - return 0; -} - -/* - * This function resets the MPA Tx and Rx buffers. - */ -static void mwifiex_cleanup_mpa_buf(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - MP_TX_AGGR_BUF_RESET(card); - MP_RX_AGGR_BUF_RESET(card); -} - -/* - * This function cleans up the allocated card buffers. - * - * The following are freed by this function - - * - MP registers - * - MPA Tx buffer - * - MPA Rx buffer - */ -static void mwifiex_cleanup_sdio(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - kfree(card->mp_regs); - kfree(card->mpa_rx.skb_arr); - kfree(card->mpa_rx.len_arr); - kfree(card->mpa_tx.buf); - kfree(card->mpa_rx.buf); - sdio_set_drvdata(card->func, NULL); - kfree(card); -} - -/* - * This function updates the MP end port in card. - */ -static void -mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) -{ - struct sdio_mmc_card *card = adapter->card; - const struct mwifiex_sdio_card_reg *reg = card->reg; - int i; - - card->mp_end_port = port; - - card->mp_data_port_mask = reg->data_port_mask; - - if (reg->start_wr_port) { - for (i = 1; i <= card->max_ports - card->mp_end_port; i++) - card->mp_data_port_mask &= - ~(1 << (card->max_ports - i)); - } - - card->curr_wr_port = reg->start_wr_port; - - mwifiex_dbg(adapter, CMD, - "cmd: mp_end_port %d, data port mask 0x%x\n", - port, card->mp_data_port_mask); -} - -static void mwifiex_recreate_adapter(struct sdio_mmc_card *card) -{ - struct sdio_func *func = card->func; - const struct sdio_device_id *device_id = card->device_id; - - /* TODO mmc_hw_reset does not require destroying and re-probing the - * whole adapter. Hence there was no need to for this rube-goldberg - * design to reload the fw from an external workqueue. If we don't - * destroy the adapter we could reload the fw from - * mwifiex_main_work_queue directly. - * The real difficulty with fw reset is to restore all the user - * settings applied through ioctl. By destroying and recreating the - * adapter, we take the easy way out, since we rely on user space to - * restore them. We assume that user space will treat the new - * incarnation of the adapter(interfaces) as if they had been just - * discovered and initializes them from scratch. - */ - - mwifiex_sdio_remove(func); - - /* power cycle the adapter */ - sdio_claim_host(func); - mmc_hw_reset(func->card->host); - sdio_release_host(func); - - mwifiex_sdio_probe(func, device_id); -} - -static struct mwifiex_adapter *save_adapter; -static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - /* TODO card pointer is unprotected. If the adapter is removed - * physically, sdio core might trigger mwifiex_sdio_remove, before this - * workqueue is run, which will destroy the adapter struct. When this - * workqueue eventually exceutes it will dereference an invalid adapter - * pointer - */ - mwifiex_recreate_adapter(card); -} - -/* This function read/write firmware */ -static enum -rdwr_status mwifiex_sdio_rdwr_firmware(struct mwifiex_adapter *adapter, - u8 doneflag) -{ - struct sdio_mmc_card *card = adapter->card; - int ret, tries; - u8 ctrl_data = 0; - - sdio_writeb(card->func, card->reg->fw_dump_host_ready, - card->reg->fw_dump_ctrl, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO Write ERR\n"); - return RDWR_STATUS_FAILURE; - } - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - ctrl_data = sdio_readb(card->func, card->reg->fw_dump_ctrl, - &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); - return RDWR_STATUS_FAILURE; - } - if (ctrl_data == FW_DUMP_DONE) - break; - if (doneflag && ctrl_data == doneflag) - return RDWR_STATUS_DONE; - if (ctrl_data != card->reg->fw_dump_host_ready) { - mwifiex_dbg(adapter, WARN, - "The ctrl reg was changed, re-try again\n"); - sdio_writeb(card->func, card->reg->fw_dump_host_ready, - card->reg->fw_dump_ctrl, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); - return RDWR_STATUS_FAILURE; - } - } - usleep_range(100, 200); - } - if (ctrl_data == card->reg->fw_dump_host_ready) { - mwifiex_dbg(adapter, ERROR, - "Fail to pull ctrl_data\n"); - return RDWR_STATUS_FAILURE; - } - - return RDWR_STATUS_SUCCESS; -} - -/* This function dump firmware memory to file */ -static void mwifiex_sdio_fw_dump(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - int ret = 0; - unsigned int reg, reg_start, reg_end; - u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; - enum rdwr_status stat; - u32 memory_size; - - if (!card->can_dump_fw) - return; - - for (idx = 0; idx < ARRAY_SIZE(mem_type_mapping_tbl); idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - - mwifiex_pm_wakeup_card(adapter); - sdio_claim_host(card->func); - - mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); - - stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - - reg = card->reg->fw_dump_start; - /* Read the number of the memories which will dump */ - dump_num = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO read memory length err\n"); - goto done; - } - - /* Read the length of every memory which will dump */ - for (idx = 0; idx < dump_num; idx++) { - struct memory_type_mapping *entry = &mem_type_mapping_tbl[idx]; - - stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - - memory_size = 0; - reg = card->reg->fw_dump_start; - for (i = 0; i < 4; i++) { - read_reg = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO read err\n"); - goto done; - } - memory_size |= (read_reg << i*8); - reg++; - } - - if (memory_size == 0) { - mwifiex_dbg(adapter, DUMP, "Firmware dump Finished!\n"); - ret = mwifiex_write_reg(adapter, - card->reg->fw_dump_ctrl, - FW_DUMP_READ_DONE); - if (ret) { - mwifiex_dbg(adapter, ERROR, "SDIO write err\n"); - return; - } - break; - } - - mwifiex_dbg(adapter, DUMP, - "%s_SIZE=0x%x\n", entry->mem_name, memory_size); - entry->mem_ptr = vmalloc(memory_size + 1); - entry->mem_size = memory_size; - if (!entry->mem_ptr) { - mwifiex_dbg(adapter, ERROR, "Vmalloc %s failed\n", - entry->mem_name); - goto done; - } - dbg_ptr = entry->mem_ptr; - end_ptr = dbg_ptr + memory_size; - - doneflag = entry->done_flag; - mwifiex_dbg(adapter, DUMP, - "Start %s output, please wait...\n", - entry->mem_name); - - do { - stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - - reg_start = card->reg->fw_dump_start; - reg_end = card->reg->fw_dump_end; - for (reg = reg_start; reg <= reg_end; reg++) { - *dbg_ptr = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "SDIO read err\n"); - goto done; - } - if (dbg_ptr < end_ptr) - dbg_ptr++; - else - mwifiex_dbg(adapter, ERROR, - "Allocated buf not enough\n"); - } - - if (stat != RDWR_STATUS_DONE) - continue; - - mwifiex_dbg(adapter, DUMP, "%s done: size=0x%tx\n", - entry->mem_name, dbg_ptr - entry->mem_ptr); - break; - } while (1); - } - mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); - -done: - sdio_release_host(card->func); -} - -static void mwifiex_sdio_generic_fw_dump(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - struct memory_type_mapping *entry = &generic_mem_type_map[0]; - unsigned int reg, reg_start, reg_end; - u8 start_flag = 0, done_flag = 0; - u8 *dbg_ptr, *end_ptr; - enum rdwr_status stat; - int ret = -1, tries; - - if (!card->fw_dump_enh) - return; - - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - - mwifiex_pm_wakeup_card(adapter); - sdio_claim_host(card->func); - - mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump start ==\n"); - - stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - - reg_start = card->reg->fw_dump_start; - reg_end = card->reg->fw_dump_end; - for (reg = reg_start; reg <= reg_end; reg++) { - for (tries = 0; tries < MAX_POLL_TRIES; tries++) { - start_flag = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "SDIO read err\n"); - goto done; - } - if (start_flag == 0) - break; - if (tries == MAX_POLL_TRIES) { - mwifiex_dbg(adapter, ERROR, - "FW not ready to dump\n"); - ret = -1; - goto done; - } - } - usleep_range(100, 200); - } - - entry->mem_ptr = vmalloc(0xf0000 + 1); - if (!entry->mem_ptr) { - ret = -1; - goto done; - } - dbg_ptr = entry->mem_ptr; - entry->mem_size = 0xf0000; - end_ptr = dbg_ptr + entry->mem_size; - - done_flag = entry->done_flag; - mwifiex_dbg(adapter, DUMP, - "Start %s output, please wait...\n", entry->mem_name); - - while (true) { - stat = mwifiex_sdio_rdwr_firmware(adapter, done_flag); - if (stat == RDWR_STATUS_FAILURE) - goto done; - for (reg = reg_start; reg <= reg_end; reg++) { - *dbg_ptr = sdio_readb(card->func, reg, &ret); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "SDIO read err\n"); - goto done; - } - dbg_ptr++; - if (dbg_ptr >= end_ptr) { - u8 *tmp_ptr; - - tmp_ptr = vmalloc(entry->mem_size + 0x4000 + 1); - if (!tmp_ptr) - goto done; - - memcpy(tmp_ptr, entry->mem_ptr, - entry->mem_size); - vfree(entry->mem_ptr); - entry->mem_ptr = tmp_ptr; - tmp_ptr = NULL; - dbg_ptr = entry->mem_ptr + entry->mem_size; - entry->mem_size += 0x4000; - end_ptr = entry->mem_ptr + entry->mem_size; - } - } - if (stat == RDWR_STATUS_DONE) { - entry->mem_size = dbg_ptr - entry->mem_ptr; - mwifiex_dbg(adapter, DUMP, "dump %s done size=0x%x\n", - entry->mem_name, entry->mem_size); - ret = 0; - break; - } - } - mwifiex_dbg(adapter, MSG, "== mwifiex firmware dump end ==\n"); - -done: - if (ret) { - mwifiex_dbg(adapter, ERROR, "firmware dump failed\n"); - if (entry->mem_ptr) { - vfree(entry->mem_ptr); - entry->mem_ptr = NULL; - } - entry->mem_size = 0; - } - sdio_release_host(card->func); -} - -static void mwifiex_sdio_device_dump_work(struct mwifiex_adapter *adapter) -{ - struct sdio_mmc_card *card = adapter->card; - - mwifiex_drv_info_dump(adapter); - if (card->fw_dump_enh) - mwifiex_sdio_generic_fw_dump(adapter); - else - mwifiex_sdio_fw_dump(adapter); - mwifiex_upload_device_dump(adapter); -} - -static void mwifiex_sdio_work(struct work_struct *work) -{ - if (test_and_clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, - &iface_work_flags)) - mwifiex_sdio_device_dump_work(save_adapter); - if (test_and_clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, - &iface_work_flags)) - mwifiex_sdio_card_reset_work(save_adapter); -} - -static DECLARE_WORK(sdio_work, mwifiex_sdio_work); -/* This function resets the card */ -static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) -{ - save_adapter = adapter; - if (test_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags)) - return; - - set_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &iface_work_flags); - - schedule_work(&sdio_work); -} - -/* This function dumps FW information */ -static void mwifiex_sdio_device_dump(struct mwifiex_adapter *adapter) -{ - save_adapter = adapter; - if (test_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags)) - return; - - set_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &iface_work_flags); - schedule_work(&sdio_work); -} - -/* Function to dump SDIO function registers and SDIO scratch registers in case - * of FW crash - */ -static int -mwifiex_sdio_reg_dump(struct mwifiex_adapter *adapter, char *drv_buf) -{ - char *p = drv_buf; - struct sdio_mmc_card *cardp = adapter->card; - int ret = 0; - u8 count, func, data, index = 0, size = 0; - u8 reg, reg_start, reg_end; - char buf[256], *ptr; - - if (!p) - return 0; - - mwifiex_dbg(adapter, MSG, "SDIO register dump start\n"); - - mwifiex_pm_wakeup_card(adapter); - - sdio_claim_host(cardp->func); - - for (count = 0; count < 5; count++) { - memset(buf, 0, sizeof(buf)); - ptr = buf; - - switch (count) { - case 0: - /* Read the registers of SDIO function0 */ - func = count; - reg_start = 0; - reg_end = 9; - break; - case 1: - /* Read the registers of SDIO function1 */ - func = count; - reg_start = cardp->reg->func1_dump_reg_start; - reg_end = cardp->reg->func1_dump_reg_end; - break; - case 2: - index = 0; - func = 1; - reg_start = cardp->reg->func1_spec_reg_table[index++]; - size = cardp->reg->func1_spec_reg_num; - reg_end = cardp->reg->func1_spec_reg_table[size-1]; - break; - default: - /* Read the scratch registers of SDIO function1 */ - if (count == 4) - mdelay(100); - func = 1; - reg_start = cardp->reg->func1_scratch_reg; - reg_end = reg_start + MWIFIEX_SDIO_SCRATCH_SIZE; - } - - if (count != 2) - ptr += sprintf(ptr, "SDIO Func%d (%#x-%#x): ", - func, reg_start, reg_end); - else - ptr += sprintf(ptr, "SDIO Func%d: ", func); - - for (reg = reg_start; reg <= reg_end;) { - if (func == 0) - data = sdio_f0_readb(cardp->func, reg, &ret); - else - data = sdio_readb(cardp->func, reg, &ret); - - if (count == 2) - ptr += sprintf(ptr, "(%#x) ", reg); - if (!ret) { - ptr += sprintf(ptr, "%02x ", data); - } else { - ptr += sprintf(ptr, "ERR"); - break; - } - - if (count == 2 && reg < reg_end) - reg = cardp->reg->func1_spec_reg_table[index++]; - else - reg++; - } - - mwifiex_dbg(adapter, MSG, "%s\n", buf); - p += sprintf(p, "%s\n", buf); - } - - sdio_release_host(cardp->func); - - mwifiex_dbg(adapter, MSG, "SDIO register dump end\n"); - - return p - drv_buf; -} - -static struct mwifiex_if_ops sdio_ops = { - .init_if = mwifiex_init_sdio, - .cleanup_if = mwifiex_cleanup_sdio, - .check_fw_status = mwifiex_check_fw_status, - .prog_fw = mwifiex_prog_fw_w_helper, - .register_dev = mwifiex_register_dev, - .unregister_dev = mwifiex_unregister_dev, - .enable_int = mwifiex_sdio_enable_host_int, - .disable_int = mwifiex_sdio_disable_host_int, - .process_int_status = mwifiex_process_int_status, - .host_to_card = mwifiex_sdio_host_to_card, - .wakeup = mwifiex_pm_wakeup_card, - .wakeup_complete = mwifiex_pm_wakeup_card_complete, - - /* SDIO specific */ - .update_mp_end_port = mwifiex_update_mp_end_port, - .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, - .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, - .event_complete = mwifiex_sdio_event_complete, - .card_reset = mwifiex_sdio_card_reset, - .reg_dump = mwifiex_sdio_reg_dump, - .device_dump = mwifiex_sdio_device_dump, - .deaggr_pkt = mwifiex_deaggr_sdio_pkt, -}; - -/* - * This function initializes the SDIO driver. - * - * This initiates the semaphore and registers the device with - * SDIO bus. - */ -static int -mwifiex_sdio_init_module(void) -{ - sema_init(&add_remove_card_sem, 1); - - /* Clear the flag in case user removes the card. */ - user_rmmod = 0; - - return sdio_register_driver(&mwifiex_sdio); -} - -/* - * This function cleans up the SDIO driver. - * - * The following major steps are followed for cleanup - - * - Resume the device if its suspended - * - Disconnect the device if connected - * - Shutdown the firmware - * - Unregister the device from SDIO bus. - */ -static void -mwifiex_sdio_cleanup_module(void) -{ - if (!down_interruptible(&add_remove_card_sem)) - up(&add_remove_card_sem); - - /* Set the flag as user is removing this module. */ - user_rmmod = 1; - cancel_work_sync(&sdio_work); - - sdio_unregister_driver(&mwifiex_sdio); -} - -module_init(mwifiex_sdio_init_module); -module_exit(mwifiex_sdio_cleanup_module); - -MODULE_AUTHOR("Marvell International Ltd."); -MODULE_DESCRIPTION("Marvell WiFi-Ex SDIO Driver version " SDIO_VERSION); -MODULE_VERSION(SDIO_VERSION); -MODULE_LICENSE("GPL v2"); -MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME); -MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h deleted file mode 100644 index b9fbc5cf6262..000000000000 --- a/drivers/net/wireless/mwifiex/sdio.h +++ /dev/null @@ -1,672 +0,0 @@ -/* - * Marvell Wireless LAN device driver: SDIO specific definitions - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_SDIO_H -#define _MWIFIEX_SDIO_H - - -#include -#include -#include -#include -#include - -#include "main.h" - -#define SD8786_DEFAULT_FW_NAME "mrvl/sd8786_uapsta.bin" -#define SD8787_DEFAULT_FW_NAME "mrvl/sd8787_uapsta.bin" -#define SD8797_DEFAULT_FW_NAME "mrvl/sd8797_uapsta.bin" -#define SD8897_DEFAULT_FW_NAME "mrvl/sd8897_uapsta.bin" -#define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin" -#define SD8801_DEFAULT_FW_NAME "mrvl/sd8801_uapsta.bin" -#define SD8997_DEFAULT_FW_NAME "mrvl/sd8997_uapsta.bin" - -#define BLOCK_MODE 1 -#define BYTE_MODE 0 - -#define REG_PORT 0 - -#define MWIFIEX_SDIO_IO_PORT_MASK 0xfffff - -#define MWIFIEX_SDIO_BYTE_MODE_MASK 0x80000000 - -#define MWIFIEX_MAX_FUNC2_REG_NUM 13 -#define MWIFIEX_SDIO_SCRATCH_SIZE 10 - -#define SDIO_MPA_ADDR_BASE 0x1000 -#define CTRL_PORT 0 -#define CTRL_PORT_MASK 0x0001 - -#define CMD_PORT_UPLD_INT_MASK (0x1U<<6) -#define CMD_PORT_DNLD_INT_MASK (0x1U<<7) -#define HOST_TERM_CMD53 (0x1U << 2) -#define REG_PORT 0 -#define MEM_PORT 0x10000 - -#define CMD53_NEW_MODE (0x1U << 0) -#define CMD_PORT_RD_LEN_EN (0x1U << 2) -#define CMD_PORT_AUTO_EN (0x1U << 0) -#define CMD_PORT_SLCT 0x8000 -#define UP_LD_CMD_PORT_HOST_INT_STATUS (0x40U) -#define DN_LD_CMD_PORT_HOST_INT_STATUS (0x80U) - -#define MWIFIEX_MP_AGGR_BUF_SIZE_16K (16384) -#define MWIFIEX_MP_AGGR_BUF_SIZE_32K (32768) -/* we leave one block of 256 bytes for DMA alignment*/ -#define MWIFIEX_MP_AGGR_BUF_SIZE_MAX (65280) - -/* Misc. Config Register : Auto Re-enable interrupts */ -#define AUTO_RE_ENABLE_INT BIT(4) - -/* Host Control Registers : Configuration */ -#define CONFIGURATION_REG 0x00 -/* Host Control Registers : Host power up */ -#define HOST_POWER_UP (0x1U << 1) - -/* 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 : 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 : Host interrupt status */ -#define CARD_INT_STATUS_REG 0x28 - -/* Card Control Registers : Card I/O ready */ -#define CARD_IO_READY (0x1U << 3) -/* Card Control Registers : Download card ready */ -#define DN_LD_CARD_RDY (0x1U << 0) - -/* Max retry number of CMD53 write */ -#define MAX_WRITE_IOMEM_RETRY 2 - -/* SDIO Tx aggregation in progress ? */ -#define MP_TX_AGGR_IN_PROGRESS(a) (a->mpa_tx.pkt_cnt > 0) - -/* SDIO Tx aggregation buffer room for next packet ? */ -#define MP_TX_AGGR_BUF_HAS_ROOM(a, len) ((a->mpa_tx.buf_len+len) \ - <= a->mpa_tx.buf_size) - -/* Copy current packet (SDIO Tx aggregation buffer) to SDIO buffer */ -#define MP_TX_AGGR_BUF_PUT(a, payload, pkt_len, port) do { \ - memmove(&a->mpa_tx.buf[a->mpa_tx.buf_len], \ - payload, pkt_len); \ - a->mpa_tx.buf_len += pkt_len; \ - if (!a->mpa_tx.pkt_cnt) \ - a->mpa_tx.start_port = port; \ - if (a->mpa_tx.start_port <= port) \ - a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt)); \ - else \ - a->mpa_tx.ports |= (1<<(a->mpa_tx.pkt_cnt+1+ \ - (a->max_ports - \ - a->mp_end_port))); \ - a->mpa_tx.pkt_cnt++; \ -} while (0) - -/* SDIO Tx aggregation limit ? */ -#define MP_TX_AGGR_PKT_LIMIT_REACHED(a) \ - (a->mpa_tx.pkt_cnt == a->mpa_tx.pkt_aggr_limit) - -/* Reset SDIO Tx aggregation buffer parameters */ -#define MP_TX_AGGR_BUF_RESET(a) do { \ - a->mpa_tx.pkt_cnt = 0; \ - a->mpa_tx.buf_len = 0; \ - a->mpa_tx.ports = 0; \ - a->mpa_tx.start_port = 0; \ -} while (0) - -/* SDIO Rx aggregation limit ? */ -#define MP_RX_AGGR_PKT_LIMIT_REACHED(a) \ - (a->mpa_rx.pkt_cnt == a->mpa_rx.pkt_aggr_limit) - -/* SDIO Rx aggregation in progress ? */ -#define MP_RX_AGGR_IN_PROGRESS(a) (a->mpa_rx.pkt_cnt > 0) - -/* SDIO Rx aggregation buffer room for next packet ? */ -#define MP_RX_AGGR_BUF_HAS_ROOM(a, rx_len) \ - ((a->mpa_rx.buf_len+rx_len) <= a->mpa_rx.buf_size) - -/* Reset SDIO Rx aggregation buffer parameters */ -#define MP_RX_AGGR_BUF_RESET(a) do { \ - a->mpa_rx.pkt_cnt = 0; \ - a->mpa_rx.buf_len = 0; \ - a->mpa_rx.ports = 0; \ - a->mpa_rx.start_port = 0; \ -} while (0) - -/* data structure for SDIO MPA TX */ -struct mwifiex_sdio_mpa_tx { - /* multiport tx aggregation buffer pointer */ - u8 *buf; - u32 buf_len; - u32 pkt_cnt; - u32 ports; - u16 start_port; - u8 enabled; - u32 buf_size; - u32 pkt_aggr_limit; -}; - -struct mwifiex_sdio_mpa_rx { - u8 *buf; - u32 buf_len; - u32 pkt_cnt; - u32 ports; - u16 start_port; - - struct sk_buff **skb_arr; - u32 *len_arr; - - u8 enabled; - u32 buf_size; - u32 pkt_aggr_limit; -}; - -int mwifiex_bus_register(void); -void mwifiex_bus_unregister(void); - -struct mwifiex_sdio_card_reg { - u8 start_rd_port; - u8 start_wr_port; - u8 base_0_reg; - u8 base_1_reg; - u8 poll_reg; - u8 host_int_enable; - u8 host_int_rsr_reg; - u8 host_int_status_reg; - u8 host_int_mask_reg; - u8 status_reg_0; - u8 status_reg_1; - u8 sdio_int_mask; - u32 data_port_mask; - u8 io_port_0_reg; - u8 io_port_1_reg; - u8 io_port_2_reg; - u8 max_mp_regs; - u8 rd_bitmap_l; - u8 rd_bitmap_u; - u8 rd_bitmap_1l; - u8 rd_bitmap_1u; - u8 wr_bitmap_l; - u8 wr_bitmap_u; - u8 wr_bitmap_1l; - u8 wr_bitmap_1u; - u8 rd_len_p0_l; - u8 rd_len_p0_u; - u8 card_misc_cfg_reg; - u8 card_cfg_2_1_reg; - u8 cmd_rd_len_0; - u8 cmd_rd_len_1; - u8 cmd_rd_len_2; - u8 cmd_rd_len_3; - u8 cmd_cfg_0; - u8 cmd_cfg_1; - u8 cmd_cfg_2; - u8 cmd_cfg_3; - u8 fw_dump_host_ready; - u8 fw_dump_ctrl; - u8 fw_dump_start; - u8 fw_dump_end; - u8 func1_dump_reg_start; - u8 func1_dump_reg_end; - u8 func1_scratch_reg; - u8 func1_spec_reg_num; - u8 func1_spec_reg_table[MWIFIEX_MAX_FUNC2_REG_NUM]; -}; - -struct sdio_mmc_card { - struct sdio_func *func; - struct mwifiex_adapter *adapter; - - const char *firmware; - const struct mwifiex_sdio_card_reg *reg; - u8 max_ports; - u8 mp_agg_pkt_limit; - u16 tx_buf_size; - u32 mp_tx_agg_buf_size; - u32 mp_rx_agg_buf_size; - - u32 mp_rd_bitmap; - u32 mp_wr_bitmap; - - u16 mp_end_port; - u32 mp_data_port_mask; - - u8 curr_rd_port; - u8 curr_wr_port; - - u8 *mp_regs; - bool supports_sdio_new_mode; - bool has_control_mask; - bool can_dump_fw; - bool fw_dump_enh; - bool can_auto_tdls; - bool can_ext_scan; - - struct mwifiex_sdio_mpa_tx mpa_tx; - struct mwifiex_sdio_mpa_rx mpa_rx; - - /* needed for card reset */ - const struct sdio_device_id *device_id; -}; - -struct mwifiex_sdio_device { - const char *firmware; - const struct mwifiex_sdio_card_reg *reg; - u8 max_ports; - u8 mp_agg_pkt_limit; - u16 tx_buf_size; - u32 mp_tx_agg_buf_size; - u32 mp_rx_agg_buf_size; - bool supports_sdio_new_mode; - bool has_control_mask; - bool can_dump_fw; - bool fw_dump_enh; - bool can_auto_tdls; - bool can_ext_scan; -}; - -static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { - .start_rd_port = 1, - .start_wr_port = 1, - .base_0_reg = 0x0040, - .base_1_reg = 0x0041, - .poll_reg = 0x30, - .host_int_enable = UP_LD_HOST_INT_MASK | DN_LD_HOST_INT_MASK, - .host_int_rsr_reg = 0x1, - .host_int_mask_reg = 0x02, - .host_int_status_reg = 0x03, - .status_reg_0 = 0x60, - .status_reg_1 = 0x61, - .sdio_int_mask = 0x3f, - .data_port_mask = 0x0000fffe, - .io_port_0_reg = 0x78, - .io_port_1_reg = 0x79, - .io_port_2_reg = 0x7A, - .max_mp_regs = 64, - .rd_bitmap_l = 0x04, - .rd_bitmap_u = 0x05, - .wr_bitmap_l = 0x06, - .wr_bitmap_u = 0x07, - .rd_len_p0_l = 0x08, - .rd_len_p0_u = 0x09, - .card_misc_cfg_reg = 0x6c, - .func1_dump_reg_start = 0x0, - .func1_dump_reg_end = 0x9, - .func1_scratch_reg = 0x60, - .func1_spec_reg_num = 5, - .func1_spec_reg_table = {0x28, 0x30, 0x34, 0x38, 0x3c}, -}; - -static const struct mwifiex_sdio_card_reg mwifiex_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_rsr_reg = 0x1, - .host_int_status_reg = 0x03, - .host_int_mask_reg = 0x02, - .status_reg_0 = 0xc0, - .status_reg_1 = 0xc1, - .sdio_int_mask = 0xff, - .data_port_mask = 0xffffffff, - .io_port_0_reg = 0xD8, - .io_port_1_reg = 0xD9, - .io_port_2_reg = 0xDA, - .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_misc_cfg_reg = 0xcc, - .card_cfg_2_1_reg = 0xcd, - .cmd_rd_len_0 = 0xb4, - .cmd_rd_len_1 = 0xb5, - .cmd_rd_len_2 = 0xb6, - .cmd_rd_len_3 = 0xb7, - .cmd_cfg_0 = 0xb8, - .cmd_cfg_1 = 0xb9, - .cmd_cfg_2 = 0xba, - .cmd_cfg_3 = 0xbb, - .fw_dump_host_ready = 0xee, - .fw_dump_ctrl = 0xe2, - .fw_dump_start = 0xe3, - .fw_dump_end = 0xea, - .func1_dump_reg_start = 0x0, - .func1_dump_reg_end = 0xb, - .func1_scratch_reg = 0xc0, - .func1_spec_reg_num = 8, - .func1_spec_reg_table = {0x4C, 0x50, 0x54, 0x55, 0x58, - 0x59, 0x5c, 0x5d}, -}; - -static const struct mwifiex_sdio_card_reg mwifiex_reg_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_rsr_reg = 0x4, - .host_int_status_reg = 0x0C, - .host_int_mask_reg = 0x08, - .status_reg_0 = 0xE8, - .status_reg_1 = 0xE9, - .sdio_int_mask = 0xff, - .data_port_mask = 0xffffffff, - .io_port_0_reg = 0xE4, - .io_port_1_reg = 0xE5, - .io_port_2_reg = 0xE6, - .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_misc_cfg_reg = 0xd8, - .card_cfg_2_1_reg = 0xd9, - .cmd_rd_len_0 = 0xc0, - .cmd_rd_len_1 = 0xc1, - .cmd_rd_len_2 = 0xc2, - .cmd_rd_len_3 = 0xc3, - .cmd_cfg_0 = 0xc4, - .cmd_cfg_1 = 0xc5, - .cmd_cfg_2 = 0xc6, - .cmd_cfg_3 = 0xc7, - .fw_dump_host_ready = 0xcc, - .fw_dump_ctrl = 0xf0, - .fw_dump_start = 0xf1, - .fw_dump_end = 0xf8, - .func1_dump_reg_start = 0x10, - .func1_dump_reg_end = 0x17, - .func1_scratch_reg = 0xe8, - .func1_spec_reg_num = 13, - .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, - 0x60, 0x61, 0x62, 0x64, - 0x65, 0x66, 0x68, 0x69, - 0x6a}, -}; - -static const struct mwifiex_sdio_card_reg mwifiex_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_rsr_reg = 0x4, - .host_int_status_reg = 0x0C, - .host_int_mask_reg = 0x08, - .status_reg_0 = 0x90, - .status_reg_1 = 0x91, - .sdio_int_mask = 0xff, - .data_port_mask = 0xffffffff, - .io_port_0_reg = 0xE4, - .io_port_1_reg = 0xE5, - .io_port_2_reg = 0xE6, - .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_misc_cfg_reg = 0xd8, - .card_cfg_2_1_reg = 0xd9, - .cmd_rd_len_0 = 0xc0, - .cmd_rd_len_1 = 0xc1, - .cmd_rd_len_2 = 0xc2, - .cmd_rd_len_3 = 0xc3, - .cmd_cfg_0 = 0xc4, - .cmd_cfg_1 = 0xc5, - .cmd_cfg_2 = 0xc6, - .cmd_cfg_3 = 0xc7, - .func1_dump_reg_start = 0x10, - .func1_dump_reg_end = 0x17, - .func1_scratch_reg = 0x90, - .func1_spec_reg_num = 13, - .func1_spec_reg_table = {0x08, 0x58, 0x5C, 0x5D, 0x60, - 0x61, 0x62, 0x64, 0x65, 0x66, - 0x68, 0x69, 0x6a}, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = { - .firmware = SD8786_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd87xx, - .max_ports = 16, - .mp_agg_pkt_limit = 8, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .supports_sdio_new_mode = false, - .has_control_mask = true, - .can_dump_fw = false, - .can_auto_tdls = false, - .can_ext_scan = false, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = { - .firmware = SD8787_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd87xx, - .max_ports = 16, - .mp_agg_pkt_limit = 8, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .supports_sdio_new_mode = false, - .has_control_mask = true, - .can_dump_fw = false, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = { - .firmware = SD8797_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd87xx, - .max_ports = 16, - .mp_agg_pkt_limit = 8, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .supports_sdio_new_mode = false, - .has_control_mask = true, - .can_dump_fw = false, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = { - .firmware = SD8897_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd8897, - .max_ports = 32, - .mp_agg_pkt_limit = 16, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, - .supports_sdio_new_mode = true, - .has_control_mask = false, - .can_dump_fw = true, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = { - .firmware = SD8997_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd8997, - .max_ports = 32, - .mp_agg_pkt_limit = 16, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX, - .supports_sdio_new_mode = true, - .has_control_mask = false, - .can_dump_fw = true, - .fw_dump_enh = true, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = { - .firmware = SD8887_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd8887, - .max_ports = 32, - .mp_agg_pkt_limit = 16, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_32K, - .supports_sdio_new_mode = true, - .has_control_mask = false, - .can_dump_fw = false, - .can_auto_tdls = true, - .can_ext_scan = true, -}; - -static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = { - .firmware = SD8801_DEFAULT_FW_NAME, - .reg = &mwifiex_reg_sd87xx, - .max_ports = 16, - .mp_agg_pkt_limit = 8, - .supports_sdio_new_mode = false, - .has_control_mask = true, - .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K, - .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_16K, - .can_dump_fw = false, - .can_auto_tdls = false, - .can_ext_scan = true, -}; - -/* - * .cmdrsp_complete handler - */ -static inline int mwifiex_sdio_cmdrsp_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - dev_kfree_skb_any(skb); - return 0; -} - -/* - * .event_complete handler - */ -static inline int mwifiex_sdio_event_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - dev_kfree_skb_any(skb); - return 0; -} - -static inline bool -mp_rx_aggr_port_limit_reached(struct sdio_mmc_card *card) -{ - u8 tmp; - - if (card->curr_rd_port < card->mpa_rx.start_port) { - if (card->supports_sdio_new_mode) - tmp = card->mp_end_port >> 1; - else - tmp = card->mp_agg_pkt_limit; - - if (((card->max_ports - card->mpa_rx.start_port) + - card->curr_rd_port) >= tmp) - return true; - } - - if (!card->supports_sdio_new_mode) - return false; - - if ((card->curr_rd_port - card->mpa_rx.start_port) >= - (card->mp_end_port >> 1)) - return true; - - return false; -} - -static inline bool -mp_tx_aggr_port_limit_reached(struct sdio_mmc_card *card) -{ - u16 tmp; - - if (card->curr_wr_port < card->mpa_tx.start_port) { - if (card->supports_sdio_new_mode) - tmp = card->mp_end_port >> 1; - else - tmp = card->mp_agg_pkt_limit; - - if (((card->max_ports - card->mpa_tx.start_port) + - card->curr_wr_port) >= tmp) - return true; - } - - if (!card->supports_sdio_new_mode) - return false; - - if ((card->curr_wr_port - card->mpa_tx.start_port) >= - (card->mp_end_port >> 1)) - return true; - - return false; -} - -/* Prepare to copy current packet from card to SDIO Rx aggregation buffer */ -static inline void mp_rx_aggr_setup(struct sdio_mmc_card *card, - u16 rx_len, u8 port) -{ - card->mpa_rx.buf_len += rx_len; - - if (!card->mpa_rx.pkt_cnt) - card->mpa_rx.start_port = port; - - if (card->supports_sdio_new_mode) { - card->mpa_rx.ports |= (1 << port); - } else { - if (card->mpa_rx.start_port <= port) - card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt); - else - card->mpa_rx.ports |= 1 << (card->mpa_rx.pkt_cnt + 1); - } - card->mpa_rx.skb_arr[card->mpa_rx.pkt_cnt] = NULL; - card->mpa_rx.len_arr[card->mpa_rx.pkt_cnt] = rx_len; - card->mpa_rx.pkt_cnt++; -} -#endif /* _MWIFIEX_SDIO_H */ diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c deleted file mode 100644 index e486867a4c67..000000000000 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ /dev/null @@ -1,2282 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station command handling - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11ac.h" - -static bool drcs; -module_param(drcs, bool, 0644); -MODULE_PARM_DESC(drcs, "multi-channel operation:1, single-channel operation:0"); - -static bool disable_auto_ds; -module_param(disable_auto_ds, bool, 0); -MODULE_PARM_DESC(disable_auto_ds, - "deepsleep enabled=0(default), deepsleep disabled=1"); -/* - * This function prepares command to set/get RSSI information. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting data/beacon average factors - * - Resetting SNR/NF/RSSI values in private structure - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_rssi_info(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 cmd_action) -{ - cmd->command = cpu_to_le16(HostCmd_CMD_RSSI_INFO); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_rssi_info) + - S_DS_GEN); - cmd->params.rssi_info.action = cpu_to_le16(cmd_action); - cmd->params.rssi_info.ndata = cpu_to_le16(priv->data_avg_factor); - cmd->params.rssi_info.nbcn = cpu_to_le16(priv->bcn_avg_factor); - - /* Reset SNR/NF/RSSI values in private structure */ - 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; - - return 0; -} - -/* - * This function prepares command to set MAC control. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_mac_control(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u16 *action) -{ - struct host_cmd_ds_mac_control *mac_ctrl = &cmd->params.mac_ctrl; - - if (cmd_action != HostCmd_ACT_GEN_SET) { - mwifiex_dbg(priv->adapter, ERROR, - "mac_control: only support set cmd\n"); - return -1; - } - - cmd->command = cpu_to_le16(HostCmd_CMD_MAC_CONTROL); - cmd->size = - cpu_to_le16(sizeof(struct host_cmd_ds_mac_control) + S_DS_GEN); - mac_ctrl->action = cpu_to_le16(*action); - - return 0; -} - -/* - * This function prepares command to set/get SNMP MIB. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting SNMP MIB OID number and value - * (as required) - * - Ensuring correct endian-ness - * - * The following SNMP MIB OIDs are supported - - * - FRAG_THRESH_I : Fragmentation threshold - * - RTS_THRESH_I : RTS threshold - * - SHORT_RETRY_LIM_I : Short retry limit - * - DOT11D_I : 11d support - */ -static int mwifiex_cmd_802_11_snmp_mib(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - u16 *ul_temp) -{ - struct host_cmd_ds_802_11_snmp_mib *snmp_mib = &cmd->params.smib; - - mwifiex_dbg(priv->adapter, CMD, - "cmd: SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SNMP_MIB); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_snmp_mib) - - 1 + S_DS_GEN); - - snmp_mib->oid = cpu_to_le16((u16)cmd_oid); - if (cmd_action == HostCmd_ACT_GEN_GET) { - snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_GET); - snmp_mib->buf_size = cpu_to_le16(MAX_SNMP_BUF_SIZE); - le16_add_cpu(&cmd->size, MAX_SNMP_BUF_SIZE); - } else if (cmd_action == HostCmd_ACT_GEN_SET) { - snmp_mib->query_type = cpu_to_le16(HostCmd_ACT_GEN_SET); - snmp_mib->buf_size = cpu_to_le16(sizeof(u16)); - *((__le16 *) (snmp_mib->value)) = cpu_to_le16(*ul_temp); - le16_add_cpu(&cmd->size, sizeof(u16)); - } - - mwifiex_dbg(priv->adapter, CMD, - "cmd: SNMP_CMD: Action=0x%x, OID=0x%x,\t" - "OIDSize=0x%x, Value=0x%x\n", - cmd_action, cmd_oid, le16_to_cpu(snmp_mib->buf_size), - le16_to_cpu(*(__le16 *)snmp_mib->value)); - return 0; -} - -/* - * This function prepares command to get log. - * - * Preparation includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_get_log(struct host_cmd_ds_command *cmd) -{ - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_GET_LOG); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_get_log) + - S_DS_GEN); - return 0; -} - -/* - * This function prepares command to set/get Tx data rate configuration. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting configuration index, rate scope and rate drop pattern - * parameters (as required) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u16 *pbitmap_rates) -{ - struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; - struct mwifiex_rate_scope *rate_scope; - struct mwifiex_rate_drop_pattern *rate_drop; - u32 i; - - cmd->command = cpu_to_le16(HostCmd_CMD_TX_RATE_CFG); - - rate_cfg->action = cpu_to_le16(cmd_action); - rate_cfg->cfg_index = 0; - - rate_scope = (struct mwifiex_rate_scope *) ((u8 *) rate_cfg + - sizeof(struct host_cmd_ds_tx_rate_cfg)); - rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); - rate_scope->length = cpu_to_le16 - (sizeof(*rate_scope) - sizeof(struct mwifiex_ie_types_header)); - if (pbitmap_rates != NULL) { - rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); - rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); - for (i = 0; - i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); - i++) - rate_scope->ht_mcs_rate_bitmap[i] = - cpu_to_le16(pbitmap_rates[2 + i]); - if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { - for (i = 0; - i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); - i++) - rate_scope->vht_mcs_rate_bitmap[i] = - cpu_to_le16(pbitmap_rates[10 + i]); - } - } else { - rate_scope->hr_dsss_rate_bitmap = - cpu_to_le16(priv->bitmap_rates[0]); - rate_scope->ofdm_rate_bitmap = - cpu_to_le16(priv->bitmap_rates[1]); - for (i = 0; - i < sizeof(rate_scope->ht_mcs_rate_bitmap) / sizeof(u16); - i++) - rate_scope->ht_mcs_rate_bitmap[i] = - cpu_to_le16(priv->bitmap_rates[2 + i]); - if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { - for (i = 0; - i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); - i++) - rate_scope->vht_mcs_rate_bitmap[i] = - cpu_to_le16(priv->bitmap_rates[10 + i]); - } - } - - rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope + - sizeof(struct mwifiex_rate_scope)); - rate_drop->type = cpu_to_le16(TLV_TYPE_RATE_DROP_CONTROL); - rate_drop->length = cpu_to_le16(sizeof(rate_drop->rate_drop_mode)); - rate_drop->rate_drop_mode = 0; - - cmd->size = - cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_tx_rate_cfg) + - sizeof(struct mwifiex_rate_scope) + - sizeof(struct mwifiex_rate_drop_pattern)); - - return 0; -} - -/* - * This function prepares command to set/get Tx power configuration. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting Tx power mode, power group TLV - * (as required) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_tx_power_cfg(struct host_cmd_ds_command *cmd, - u16 cmd_action, - struct host_cmd_ds_txpwr_cfg *txp) -{ - struct mwifiex_types_power_group *pg_tlv; - struct host_cmd_ds_txpwr_cfg *cmd_txp_cfg = &cmd->params.txp_cfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_TXPWR_CFG); - cmd->size = - cpu_to_le16(S_DS_GEN + sizeof(struct host_cmd_ds_txpwr_cfg)); - switch (cmd_action) { - case HostCmd_ACT_GEN_SET: - if (txp->mode) { - pg_tlv = (struct mwifiex_types_power_group - *) ((unsigned long) txp + - sizeof(struct host_cmd_ds_txpwr_cfg)); - memmove(cmd_txp_cfg, txp, - sizeof(struct host_cmd_ds_txpwr_cfg) + - sizeof(struct mwifiex_types_power_group) + - le16_to_cpu(pg_tlv->length)); - - pg_tlv = (struct mwifiex_types_power_group *) ((u8 *) - cmd_txp_cfg + - sizeof(struct host_cmd_ds_txpwr_cfg)); - cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + - sizeof(struct mwifiex_types_power_group) + - le16_to_cpu(pg_tlv->length)); - } else { - memmove(cmd_txp_cfg, txp, sizeof(*txp)); - } - cmd_txp_cfg->action = cpu_to_le16(cmd_action); - break; - case HostCmd_ACT_GEN_GET: - cmd_txp_cfg->action = cpu_to_le16(cmd_action); - break; - } - - return 0; -} - -/* - * This function prepares command to get RF Tx power. - */ -static int mwifiex_cmd_rf_tx_power(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_ds_rf_tx_pwr *txp = &cmd->params.txp; - - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_tx_pwr) - + S_DS_GEN); - cmd->command = cpu_to_le16(HostCmd_CMD_RF_TX_PWR); - txp->action = cpu_to_le16(cmd_action); - - return 0; -} - -/* - * This function prepares command to set rf antenna. - */ -static int mwifiex_cmd_rf_antenna(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, - struct mwifiex_ds_ant_cfg *ant_cfg) -{ - struct host_cmd_ds_rf_ant_mimo *ant_mimo = &cmd->params.ant_mimo; - struct host_cmd_ds_rf_ant_siso *ant_siso = &cmd->params.ant_siso; - - cmd->command = cpu_to_le16(HostCmd_CMD_RF_ANTENNA); - - if (cmd_action != HostCmd_ACT_GEN_SET) - return 0; - - if (priv->adapter->hw_dev_mcs_support == HT_STREAM_2X2) { - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_mimo) + - S_DS_GEN); - ant_mimo->action_tx = cpu_to_le16(HostCmd_ACT_SET_TX); - ant_mimo->tx_ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); - ant_mimo->action_rx = cpu_to_le16(HostCmd_ACT_SET_RX); - ant_mimo->rx_ant_mode = cpu_to_le16((u16)ant_cfg->rx_ant); - } else { - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_rf_ant_siso) + - S_DS_GEN); - ant_siso->action = cpu_to_le16(HostCmd_ACT_SET_BOTH); - ant_siso->ant_mode = cpu_to_le16((u16)ant_cfg->tx_ant); - } - - return 0; -} - -/* - * This function prepares command to set Host Sleep configuration. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting Host Sleep action, conditions, ARP filters - * (as required) - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_hs_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, - struct mwifiex_hs_config_param *hscfg_param) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_hs_cfg_enh *hs_cfg = &cmd->params.opt_hs_cfg; - bool hs_activate = false; - - if (!hscfg_param) - /* New Activate command */ - hs_activate = true; - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH); - - if (!hs_activate && - (hscfg_param->conditions != cpu_to_le32(HS_CFG_CANCEL)) && - ((adapter->arp_filter_size > 0) && - (adapter->arp_filter_size <= ARP_FILTER_MAX_BUF_SIZE))) { - mwifiex_dbg(adapter, CMD, - "cmd: Attach %d bytes ArpFilter to HSCfg cmd\n", - adapter->arp_filter_size); - memcpy(((u8 *) hs_cfg) + - sizeof(struct host_cmd_ds_802_11_hs_cfg_enh), - adapter->arp_filter, adapter->arp_filter_size); - cmd->size = cpu_to_le16 - (adapter->arp_filter_size + - sizeof(struct host_cmd_ds_802_11_hs_cfg_enh) - + S_DS_GEN); - } else { - cmd->size = cpu_to_le16(S_DS_GEN + sizeof(struct - host_cmd_ds_802_11_hs_cfg_enh)); - } - if (hs_activate) { - hs_cfg->action = cpu_to_le16(HS_ACTIVATE); - hs_cfg->params.hs_activate.resp_ctrl = cpu_to_le16(RESP_NEEDED); - } else { - hs_cfg->action = cpu_to_le16(HS_CONFIGURE); - hs_cfg->params.hs_config.conditions = hscfg_param->conditions; - hs_cfg->params.hs_config.gpio = hscfg_param->gpio; - hs_cfg->params.hs_config.gap = hscfg_param->gap; - mwifiex_dbg(adapter, CMD, - "cmd: HS_CFG_CMD: condition:0x%x gpio:0x%x gap:0x%x\n", - hs_cfg->params.hs_config.conditions, - hs_cfg->params.hs_config.gpio, - hs_cfg->params.hs_config.gap); - } - - return 0; -} - -/* - * This function prepares command to set/get MAC address. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting MAC address (for SET only) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_802_11_mac_address(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action) -{ - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_MAC_ADDRESS); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_mac_address) + - S_DS_GEN); - cmd->result = 0; - - cmd->params.mac_addr.action = cpu_to_le16(cmd_action); - - if (cmd_action == HostCmd_ACT_GEN_SET) - memcpy(cmd->params.mac_addr.mac_addr, priv->curr_addr, - ETH_ALEN); - return 0; -} - -/* - * This function prepares command to set MAC multicast address. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting MAC multicast address - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_mac_multicast_adr(struct host_cmd_ds_command *cmd, - u16 cmd_action, - struct mwifiex_multicast_list *mcast_list) -{ - struct host_cmd_ds_mac_multicast_adr *mcast_addr = &cmd->params.mc_addr; - - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mac_multicast_adr) + - S_DS_GEN); - cmd->command = cpu_to_le16(HostCmd_CMD_MAC_MULTICAST_ADR); - - mcast_addr->action = cpu_to_le16(cmd_action); - mcast_addr->num_of_adrs = - cpu_to_le16((u16) mcast_list->num_multicast_addr); - memcpy(mcast_addr->mac_list, mcast_list->mac_list, - mcast_list->num_multicast_addr * ETH_ALEN); - - return 0; -} - -/* - * This function prepares command to deauthenticate. - * - * Preparation includes - - * - Setting command ID and proper size - * - Setting AP MAC address and reason code - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_802_11_deauthenticate(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u8 *mac) -{ - struct host_cmd_ds_802_11_deauthenticate *deauth = &cmd->params.deauth; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_DEAUTHENTICATE); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_deauthenticate) - + S_DS_GEN); - - /* Set AP MAC address */ - memcpy(deauth->mac_addr, mac, ETH_ALEN); - - mwifiex_dbg(priv->adapter, CMD, "cmd: Deauth: %pM\n", deauth->mac_addr); - - deauth->reason_code = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); - - return 0; -} - -/* - * This function prepares command to stop Ad-Hoc network. - * - * Preparation includes - - * - Setting command ID and proper size - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_802_11_ad_hoc_stop(struct host_cmd_ds_command *cmd) -{ - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_AD_HOC_STOP); - cmd->size = cpu_to_le16(S_DS_GEN); - return 0; -} - -/* - * This function sets WEP key(s) to key parameter TLV(s). - * - * Multi-key parameter TLVs are supported, so we can send multiple - * WEP keys in a single buffer. - */ -static int -mwifiex_set_keyparamset_wep(struct mwifiex_private *priv, - struct mwifiex_ie_type_key_param_set *key_param_set, - u16 *key_param_len) -{ - int cur_key_param_len; - u8 i; - - /* Multi-key_param_set TLV is supported */ - for (i = 0; i < NUM_WEP_KEYS; i++) { - if ((priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP40) || - (priv->wep_key[i].key_length == WLAN_KEY_LEN_WEP104)) { - key_param_set->type = - cpu_to_le16(TLV_TYPE_KEY_MATERIAL); -/* Key_param_set WEP fixed length */ -#define KEYPARAMSET_WEP_FIXED_LEN 8 - key_param_set->length = cpu_to_le16((u16) - (priv->wep_key[i]. - key_length + - KEYPARAMSET_WEP_FIXED_LEN)); - key_param_set->key_type_id = - cpu_to_le16(KEY_TYPE_ID_WEP); - key_param_set->key_info = - cpu_to_le16(KEY_ENABLED | KEY_UNICAST | - KEY_MCAST); - key_param_set->key_len = - cpu_to_le16(priv->wep_key[i].key_length); - /* Set WEP key index */ - key_param_set->key[0] = i; - /* Set default Tx key flag */ - if (i == - (priv-> - wep_key_curr_index & HostCmd_WEP_KEY_INDEX_MASK)) - key_param_set->key[1] = 1; - else - key_param_set->key[1] = 0; - memmove(&key_param_set->key[2], - priv->wep_key[i].key_material, - priv->wep_key[i].key_length); - - cur_key_param_len = priv->wep_key[i].key_length + - KEYPARAMSET_WEP_FIXED_LEN + - sizeof(struct mwifiex_ie_types_header); - *key_param_len += (u16) cur_key_param_len; - key_param_set = - (struct mwifiex_ie_type_key_param_set *) - ((u8 *)key_param_set + - cur_key_param_len); - } else if (!priv->wep_key[i].key_length) { - continue; - } else { - mwifiex_dbg(priv->adapter, ERROR, - "key%d Length = %d is incorrect\n", - (i + 1), priv->wep_key[i].key_length); - return -1; - } - } - - return 0; -} - -/* This function populates key material v2 command - * to set network key for AES & CMAC AES. - */ -static int mwifiex_set_aes_key_v2(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_ds_encrypt_key *enc_key, - struct host_cmd_ds_802_11_key_material_v2 *km) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u16 size, len = KEY_PARAMS_FIXED_LEN; - - if (enc_key->is_igtk_key) { - mwifiex_dbg(adapter, INFO, - "%s: Set CMAC AES Key\n", __func__); - if (enc_key->is_rx_seq_valid) - memcpy(km->key_param_set.key_params.cmac_aes.ipn, - enc_key->pn, enc_key->pn_len); - km->key_param_set.key_info &= cpu_to_le16(~KEY_MCAST); - km->key_param_set.key_info |= cpu_to_le16(KEY_IGTK); - km->key_param_set.key_type = KEY_TYPE_ID_AES_CMAC; - km->key_param_set.key_params.cmac_aes.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.cmac_aes.key, - enc_key->key_material, enc_key->key_len); - len += sizeof(struct mwifiex_cmac_aes_param); - } else { - mwifiex_dbg(adapter, INFO, - "%s: Set AES Key\n", __func__); - if (enc_key->is_rx_seq_valid) - memcpy(km->key_param_set.key_params.aes.pn, - enc_key->pn, enc_key->pn_len); - km->key_param_set.key_type = KEY_TYPE_ID_AES; - km->key_param_set.key_params.aes.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.aes.key, - enc_key->key_material, enc_key->key_len); - len += sizeof(struct mwifiex_aes_param); - } - - km->key_param_set.len = cpu_to_le16(len); - size = len + sizeof(struct mwifiex_ie_types_header) + - sizeof(km->action) + S_DS_GEN; - cmd->size = cpu_to_le16(size); - - return 0; -} - -/* This function prepares command to set/get/reset network key(s). - * This function prepares key material command for V2 format. - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting WEP keys, WAPI keys or WPA keys along with required - * encryption (TKIP, AES) (as required) - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_key_material_v2(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - struct mwifiex_ds_encrypt_key *enc_key) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u8 *mac = enc_key->mac_addr; - u16 key_info, len = KEY_PARAMS_FIXED_LEN; - struct host_cmd_ds_802_11_key_material_v2 *km = - &cmd->params.key_material_v2; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); - km->action = cpu_to_le16(cmd_action); - - if (cmd_action == HostCmd_ACT_GEN_GET) { - mwifiex_dbg(adapter, INFO, "%s: Get key\n", __func__); - km->key_param_set.key_idx = - enc_key->key_index & KEY_INDEX_MASK; - km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); - km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); - memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); - - if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) - key_info = KEY_UNICAST; - else - key_info = KEY_MCAST; - - if (enc_key->is_igtk_key) - key_info |= KEY_IGTK; - - km->key_param_set.key_info = cpu_to_le16(key_info); - - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - S_DS_GEN + KEY_PARAMS_FIXED_LEN + - sizeof(km->action)); - return 0; - } - - memset(&km->key_param_set, 0, - sizeof(struct mwifiex_ie_type_key_param_set_v2)); - - if (enc_key->key_disable) { - mwifiex_dbg(adapter, INFO, "%s: Remove key\n", __func__); - km->action = cpu_to_le16(HostCmd_ACT_GEN_REMOVE); - km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); - km->key_param_set.len = cpu_to_le16(KEY_PARAMS_FIXED_LEN); - km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; - key_info = KEY_MCAST | KEY_UNICAST; - km->key_param_set.key_info = cpu_to_le16(key_info); - memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - S_DS_GEN + KEY_PARAMS_FIXED_LEN + - sizeof(km->action)); - return 0; - } - - km->action = cpu_to_le16(HostCmd_ACT_GEN_SET); - km->key_param_set.key_idx = enc_key->key_index & KEY_INDEX_MASK; - km->key_param_set.type = cpu_to_le16(TLV_TYPE_KEY_PARAM_V2); - key_info = KEY_ENABLED; - memcpy(km->key_param_set.mac_addr, mac, ETH_ALEN); - - if (enc_key->key_len <= WLAN_KEY_LEN_WEP104) { - mwifiex_dbg(adapter, INFO, "%s: Set WEP Key\n", __func__); - len += sizeof(struct mwifiex_wep_param); - km->key_param_set.len = cpu_to_le16(len); - km->key_param_set.key_type = KEY_TYPE_ID_WEP; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - key_info |= KEY_MCAST | KEY_UNICAST; - } else { - if (enc_key->is_current_wep_key) { - key_info |= KEY_MCAST | KEY_UNICAST; - if (km->key_param_set.key_idx == - (priv->wep_key_curr_index & KEY_INDEX_MASK)) - key_info |= KEY_DEFAULT; - } else { - if (mac) { - if (is_broadcast_ether_addr(mac)) - key_info |= KEY_MCAST; - else - key_info |= KEY_UNICAST | - KEY_DEFAULT; - } else { - key_info |= KEY_MCAST; - } - } - } - km->key_param_set.key_info = cpu_to_le16(key_info); - - km->key_param_set.key_params.wep.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.wep.key, - enc_key->key_material, enc_key->key_len); - - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - len + sizeof(km->action) + S_DS_GEN); - return 0; - } - - if (is_broadcast_ether_addr(mac)) - key_info |= KEY_MCAST | KEY_RX_KEY; - else - key_info |= KEY_UNICAST | KEY_TX_KEY | KEY_RX_KEY; - - if (enc_key->is_wapi_key) { - mwifiex_dbg(adapter, INFO, "%s: Set WAPI Key\n", __func__); - km->key_param_set.key_type = KEY_TYPE_ID_WAPI; - memcpy(km->key_param_set.key_params.wapi.pn, enc_key->pn, - PN_LEN); - km->key_param_set.key_params.wapi.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.wapi.key, - enc_key->key_material, enc_key->key_len); - if (is_broadcast_ether_addr(mac)) - priv->sec_info.wapi_key_on = true; - - if (!priv->sec_info.wapi_key_on) - key_info |= KEY_DEFAULT; - km->key_param_set.key_info = cpu_to_le16(key_info); - - len += sizeof(struct mwifiex_wapi_param); - km->key_param_set.len = cpu_to_le16(len); - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - len + sizeof(km->action) + S_DS_GEN); - return 0; - } - - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - key_info |= KEY_DEFAULT; - /* Enable unicast bit for WPA-NONE/ADHOC_AES */ - if (!priv->sec_info.wpa2_enabled && - !is_broadcast_ether_addr(mac)) - key_info |= KEY_UNICAST; - } else { - /* Enable default key for WPA/WPA2 */ - if (!priv->wpa_is_gtk_set) - key_info |= KEY_DEFAULT; - } - - km->key_param_set.key_info = cpu_to_le16(key_info); - - if (enc_key->key_len == WLAN_KEY_LEN_CCMP) - return mwifiex_set_aes_key_v2(priv, cmd, enc_key, km); - - if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { - mwifiex_dbg(adapter, INFO, - "%s: Set TKIP Key\n", __func__); - if (enc_key->is_rx_seq_valid) - memcpy(km->key_param_set.key_params.tkip.pn, - enc_key->pn, enc_key->pn_len); - km->key_param_set.key_type = KEY_TYPE_ID_TKIP; - km->key_param_set.key_params.tkip.key_len = - cpu_to_le16(enc_key->key_len); - memcpy(km->key_param_set.key_params.tkip.key, - enc_key->key_material, enc_key->key_len); - - len += sizeof(struct mwifiex_tkip_param); - km->key_param_set.len = cpu_to_le16(len); - cmd->size = cpu_to_le16(sizeof(struct mwifiex_ie_types_header) + - len + sizeof(km->action) + S_DS_GEN); - } - - return 0; -} - -/* - * This function prepares command to set/get/reset network key(s). - * This function prepares key material command for V1 format. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting WEP keys, WAPI keys or WPA keys along with required - * encryption (TKIP, AES) (as required) - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_802_11_key_material_v1(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - struct mwifiex_ds_encrypt_key *enc_key) -{ - struct host_cmd_ds_802_11_key_material *key_material = - &cmd->params.key_material; - struct host_cmd_tlv_mac_addr *tlv_mac; - u16 key_param_len = 0, cmd_size; - int ret = 0; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_KEY_MATERIAL); - key_material->action = cpu_to_le16(cmd_action); - - if (cmd_action == HostCmd_ACT_GEN_GET) { - cmd->size = - cpu_to_le16(sizeof(key_material->action) + S_DS_GEN); - return ret; - } - - if (!enc_key) { - memset(&key_material->key_param_set, 0, - (NUM_WEP_KEYS * - sizeof(struct mwifiex_ie_type_key_param_set))); - ret = mwifiex_set_keyparamset_wep(priv, - &key_material->key_param_set, - &key_param_len); - cmd->size = cpu_to_le16(key_param_len + - sizeof(key_material->action) + S_DS_GEN); - return ret; - } else - memset(&key_material->key_param_set, 0, - sizeof(struct mwifiex_ie_type_key_param_set)); - if (enc_key->is_wapi_key) { - mwifiex_dbg(priv->adapter, INFO, "info: Set WAPI Key\n"); - key_material->key_param_set.key_type_id = - cpu_to_le16(KEY_TYPE_ID_WAPI); - if (cmd_oid == KEY_INFO_ENABLED) - key_material->key_param_set.key_info = - cpu_to_le16(KEY_ENABLED); - else - key_material->key_param_set.key_info = - cpu_to_le16(!KEY_ENABLED); - - key_material->key_param_set.key[0] = enc_key->key_index; - if (!priv->sec_info.wapi_key_on) - key_material->key_param_set.key[1] = 1; - else - /* set 0 when re-key */ - key_material->key_param_set.key[1] = 0; - - if (!is_broadcast_ether_addr(enc_key->mac_addr)) { - /* WAPI pairwise key: unicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_UNICAST); - } else { /* WAPI group key: multicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_MCAST); - priv->sec_info.wapi_key_on = true; - } - - key_material->key_param_set.type = - cpu_to_le16(TLV_TYPE_KEY_MATERIAL); - key_material->key_param_set.key_len = - cpu_to_le16(WAPI_KEY_LEN); - memcpy(&key_material->key_param_set.key[2], - enc_key->key_material, enc_key->key_len); - memcpy(&key_material->key_param_set.key[2 + enc_key->key_len], - enc_key->pn, PN_LEN); - key_material->key_param_set.length = - cpu_to_le16(WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN); - - key_param_len = (WAPI_KEY_LEN + KEYPARAMSET_FIXED_LEN) + - sizeof(struct mwifiex_ie_types_header); - cmd->size = cpu_to_le16(sizeof(key_material->action) - + S_DS_GEN + key_param_len); - return ret; - } - if (enc_key->key_len == WLAN_KEY_LEN_CCMP) { - if (enc_key->is_igtk_key) { - mwifiex_dbg(priv->adapter, CMD, "cmd: CMAC_AES\n"); - key_material->key_param_set.key_type_id = - cpu_to_le16(KEY_TYPE_ID_AES_CMAC); - if (cmd_oid == KEY_INFO_ENABLED) - key_material->key_param_set.key_info = - cpu_to_le16(KEY_ENABLED); - else - key_material->key_param_set.key_info = - cpu_to_le16(!KEY_ENABLED); - - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_IGTK); - } else { - mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_AES\n"); - key_material->key_param_set.key_type_id = - cpu_to_le16(KEY_TYPE_ID_AES); - if (cmd_oid == KEY_INFO_ENABLED) - key_material->key_param_set.key_info = - cpu_to_le16(KEY_ENABLED); - else - key_material->key_param_set.key_info = - cpu_to_le16(!KEY_ENABLED); - - if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) - /* AES pairwise key: unicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_UNICAST); - else /* AES group key: multicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_MCAST); - } - } else if (enc_key->key_len == WLAN_KEY_LEN_TKIP) { - mwifiex_dbg(priv->adapter, CMD, "cmd: WPA_TKIP\n"); - key_material->key_param_set.key_type_id = - cpu_to_le16(KEY_TYPE_ID_TKIP); - key_material->key_param_set.key_info = - cpu_to_le16(KEY_ENABLED); - - if (enc_key->key_index & MWIFIEX_KEY_INDEX_UNICAST) - /* TKIP pairwise key: unicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_UNICAST); - else /* TKIP group key: multicast */ - key_material->key_param_set.key_info |= - cpu_to_le16(KEY_MCAST); - } - - if (key_material->key_param_set.key_type_id) { - key_material->key_param_set.type = - cpu_to_le16(TLV_TYPE_KEY_MATERIAL); - key_material->key_param_set.key_len = - cpu_to_le16((u16) enc_key->key_len); - memcpy(key_material->key_param_set.key, enc_key->key_material, - enc_key->key_len); - key_material->key_param_set.length = - cpu_to_le16((u16) enc_key->key_len + - KEYPARAMSET_FIXED_LEN); - - key_param_len = (u16)(enc_key->key_len + KEYPARAMSET_FIXED_LEN) - + sizeof(struct mwifiex_ie_types_header); - - if (le16_to_cpu(key_material->key_param_set.key_type_id) == - KEY_TYPE_ID_AES_CMAC) { - struct mwifiex_cmac_param *param = - (void *)key_material->key_param_set.key; - - memcpy(param->ipn, enc_key->pn, IGTK_PN_LEN); - memcpy(param->key, enc_key->key_material, - WLAN_KEY_LEN_AES_CMAC); - - key_param_len = sizeof(struct mwifiex_cmac_param); - key_material->key_param_set.key_len = - cpu_to_le16(key_param_len); - key_param_len += KEYPARAMSET_FIXED_LEN; - key_material->key_param_set.length = - cpu_to_le16(key_param_len); - key_param_len += sizeof(struct mwifiex_ie_types_header); - } - - cmd->size = cpu_to_le16(sizeof(key_material->action) + S_DS_GEN - + key_param_len); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - tlv_mac = (void *)((u8 *)&key_material->key_param_set + - key_param_len); - tlv_mac->header.type = - cpu_to_le16(TLV_TYPE_STA_MAC_ADDR); - tlv_mac->header.len = cpu_to_le16(ETH_ALEN); - memcpy(tlv_mac->mac_addr, enc_key->mac_addr, ETH_ALEN); - cmd_size = key_param_len + S_DS_GEN + - sizeof(key_material->action) + - sizeof(struct host_cmd_tlv_mac_addr); - } else { - cmd_size = key_param_len + S_DS_GEN + - sizeof(key_material->action); - } - cmd->size = cpu_to_le16(cmd_size); - } - - return ret; -} - -/* Wrapper function for setting network key depending upon FW KEY API version */ -static int -mwifiex_cmd_802_11_key_material(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, u32 cmd_oid, - struct mwifiex_ds_encrypt_key *enc_key) -{ - if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) - return mwifiex_cmd_802_11_key_material_v2(priv, cmd, - cmd_action, cmd_oid, - enc_key); - - else - return mwifiex_cmd_802_11_key_material_v1(priv, cmd, - cmd_action, cmd_oid, - enc_key); -} - -/* - * This function prepares command to set/get 11d domain information. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting domain information fields (for SET only) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_802_11d_domain_info(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11d_domain_info *domain_info = - &cmd->params.domain_info; - struct mwifiex_ietypes_domain_param_set *domain = - &domain_info->domain; - u8 no_of_triplet = adapter->domain_reg.no_of_triplet; - - mwifiex_dbg(adapter, INFO, - "info: 11D: no_of_triplet=0x%x\n", no_of_triplet); - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO); - domain_info->action = cpu_to_le16(cmd_action); - if (cmd_action == HostCmd_ACT_GEN_GET) { - cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); - return 0; - } - - /* Set domain info fields */ - domain->header.type = cpu_to_le16(WLAN_EID_COUNTRY); - memcpy(domain->country_code, adapter->domain_reg.country_code, - sizeof(domain->country_code)); - - domain->header.len = - cpu_to_le16((no_of_triplet * - sizeof(struct ieee80211_country_ie_triplet)) - + sizeof(domain->country_code)); - - if (no_of_triplet) { - memcpy(domain->triplet, adapter->domain_reg.triplet, - no_of_triplet * sizeof(struct - ieee80211_country_ie_triplet)); - - cmd->size = cpu_to_le16(sizeof(domain_info->action) + - le16_to_cpu(domain->header.len) + - sizeof(struct mwifiex_ie_types_header) - + S_DS_GEN); - } else { - cmd->size = cpu_to_le16(sizeof(domain_info->action) + S_DS_GEN); - } - - return 0; -} - -/* - * This function prepares command to set/get IBSS coalescing status. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting status to enable or disable (for SET only) - * - Ensuring correct endian-ness - */ -static int mwifiex_cmd_ibss_coalescing_status(struct host_cmd_ds_command *cmd, - u16 cmd_action, u16 *enable) -{ - struct host_cmd_ds_802_11_ibss_status *ibss_coal = - &(cmd->params.ibss_coalescing); - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_IBSS_COALESCING_STATUS); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_ibss_status) + - S_DS_GEN); - cmd->result = 0; - ibss_coal->action = cpu_to_le16(cmd_action); - - switch (cmd_action) { - case HostCmd_ACT_GEN_SET: - if (enable) - ibss_coal->enable = cpu_to_le16(*enable); - else - ibss_coal->enable = 0; - break; - - /* In other case.. Nothing to do */ - case HostCmd_ACT_GEN_GET: - default: - break; - } - - return 0; -} - -/* This function prepares command buffer to get/set memory location value. - */ -static int -mwifiex_cmd_mem_access(struct host_cmd_ds_command *cmd, u16 cmd_action, - void *pdata_buf) -{ - struct mwifiex_ds_mem_rw *mem_rw = (void *)pdata_buf; - struct host_cmd_ds_mem_access *mem_access = (void *)&cmd->params.mem; - - cmd->command = cpu_to_le16(HostCmd_CMD_MEM_ACCESS); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_mem_access) + - S_DS_GEN); - - mem_access->action = cpu_to_le16(cmd_action); - mem_access->addr = cpu_to_le32(mem_rw->addr); - mem_access->value = cpu_to_le32(mem_rw->value); - - return 0; -} - -/* - * This function prepares command to set/get register value. - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting register offset (for both GET and SET) and - * register value (for SET only) - * - Ensuring correct endian-ness - * - * The following type of registers can be accessed with this function - - * - MAC register - * - BBP register - * - RF register - * - PMIC register - * - CAU register - * - EEPROM - */ -static int mwifiex_cmd_reg_access(struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct mwifiex_ds_reg_rw *reg_rw = data_buf; - - switch (le16_to_cpu(cmd->command)) { - case HostCmd_CMD_MAC_REG_ACCESS: - { - struct host_cmd_ds_mac_reg_access *mac_reg; - - cmd->size = cpu_to_le16(sizeof(*mac_reg) + S_DS_GEN); - mac_reg = &cmd->params.mac_reg; - mac_reg->action = cpu_to_le16(cmd_action); - mac_reg->offset = - cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - mac_reg->value = reg_rw->value; - break; - } - case HostCmd_CMD_BBP_REG_ACCESS: - { - struct host_cmd_ds_bbp_reg_access *bbp_reg; - - cmd->size = cpu_to_le16(sizeof(*bbp_reg) + S_DS_GEN); - bbp_reg = &cmd->params.bbp_reg; - bbp_reg->action = cpu_to_le16(cmd_action); - bbp_reg->offset = - cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - bbp_reg->value = (u8) le32_to_cpu(reg_rw->value); - break; - } - case HostCmd_CMD_RF_REG_ACCESS: - { - struct host_cmd_ds_rf_reg_access *rf_reg; - - cmd->size = cpu_to_le16(sizeof(*rf_reg) + S_DS_GEN); - rf_reg = &cmd->params.rf_reg; - rf_reg->action = cpu_to_le16(cmd_action); - rf_reg->offset = cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - rf_reg->value = (u8) le32_to_cpu(reg_rw->value); - break; - } - case HostCmd_CMD_PMIC_REG_ACCESS: - { - struct host_cmd_ds_pmic_reg_access *pmic_reg; - - cmd->size = cpu_to_le16(sizeof(*pmic_reg) + S_DS_GEN); - pmic_reg = &cmd->params.pmic_reg; - pmic_reg->action = cpu_to_le16(cmd_action); - pmic_reg->offset = - cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - pmic_reg->value = (u8) le32_to_cpu(reg_rw->value); - break; - } - case HostCmd_CMD_CAU_REG_ACCESS: - { - struct host_cmd_ds_rf_reg_access *cau_reg; - - cmd->size = cpu_to_le16(sizeof(*cau_reg) + S_DS_GEN); - cau_reg = &cmd->params.rf_reg; - cau_reg->action = cpu_to_le16(cmd_action); - cau_reg->offset = - cpu_to_le16((u16) le32_to_cpu(reg_rw->offset)); - cau_reg->value = (u8) le32_to_cpu(reg_rw->value); - break; - } - case HostCmd_CMD_802_11_EEPROM_ACCESS: - { - struct mwifiex_ds_read_eeprom *rd_eeprom = data_buf; - struct host_cmd_ds_802_11_eeprom_access *cmd_eeprom = - &cmd->params.eeprom; - - cmd->size = cpu_to_le16(sizeof(*cmd_eeprom) + S_DS_GEN); - cmd_eeprom->action = cpu_to_le16(cmd_action); - cmd_eeprom->offset = rd_eeprom->offset; - cmd_eeprom->byte_count = rd_eeprom->byte_count; - cmd_eeprom->value = 0; - break; - } - default: - return -1; - } - - return 0; -} - -/* - * This function prepares command to set PCI-Express - * host buffer configuration - * - * Preparation includes - - * - Setting command ID, action and proper size - * - Setting host buffer configuration - * - Ensuring correct endian-ness - */ -static int -mwifiex_cmd_pcie_host_spec(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u16 action) -{ - struct host_cmd_ds_pcie_details *host_spec = - &cmd->params.pcie_host_spec; - struct pcie_service_card *card = priv->adapter->card; - - cmd->command = cpu_to_le16(HostCmd_CMD_PCIE_DESC_DETAILS); - cmd->size = cpu_to_le16(sizeof(struct - host_cmd_ds_pcie_details) + S_DS_GEN); - cmd->result = 0; - - memset(host_spec, 0, sizeof(struct host_cmd_ds_pcie_details)); - - if (action != HostCmd_ACT_GEN_SET) - return 0; - - /* Send the ring base addresses and count to firmware */ - host_spec->txbd_addr_lo = (u32)(card->txbd_ring_pbase); - host_spec->txbd_addr_hi = (u32)(((u64)card->txbd_ring_pbase)>>32); - host_spec->txbd_count = MWIFIEX_MAX_TXRX_BD; - host_spec->rxbd_addr_lo = (u32)(card->rxbd_ring_pbase); - host_spec->rxbd_addr_hi = (u32)(((u64)card->rxbd_ring_pbase)>>32); - host_spec->rxbd_count = MWIFIEX_MAX_TXRX_BD; - host_spec->evtbd_addr_lo = (u32)(card->evtbd_ring_pbase); - host_spec->evtbd_addr_hi = (u32)(((u64)card->evtbd_ring_pbase)>>32); - host_spec->evtbd_count = MWIFIEX_MAX_EVT_BD; - if (card->sleep_cookie_vbase) { - host_spec->sleep_cookie_addr_lo = - (u32)(card->sleep_cookie_pbase); - host_spec->sleep_cookie_addr_hi = - (u32)(((u64)(card->sleep_cookie_pbase)) >> 32); - mwifiex_dbg(priv->adapter, INFO, - "sleep_cook_lo phy addr: 0x%x\n", - host_spec->sleep_cookie_addr_lo); - } - - return 0; -} - -/* - * This function prepares command for event subscription, configuration - * and query. Events can be subscribed or unsubscribed. Current subscribed - * events can be queried. Also, current subscribed events are reported in - * every FW response. - */ -static int -mwifiex_cmd_802_11_subsc_evt(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_ds_misc_subsc_evt *subsc_evt_cfg) -{ - struct host_cmd_ds_802_11_subsc_evt *subsc_evt = &cmd->params.subsc_evt; - struct mwifiex_ie_types_rssi_threshold *rssi_tlv; - u16 event_bitmap; - u8 *pos; - - cmd->command = cpu_to_le16(HostCmd_CMD_802_11_SUBSCRIBE_EVENT); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_802_11_subsc_evt) + - S_DS_GEN); - - subsc_evt->action = cpu_to_le16(subsc_evt_cfg->action); - mwifiex_dbg(priv->adapter, CMD, - "cmd: action: %d\n", subsc_evt_cfg->action); - - /*For query requests, no configuration TLV structures are to be added.*/ - if (subsc_evt_cfg->action == HostCmd_ACT_GEN_GET) - return 0; - - subsc_evt->events = cpu_to_le16(subsc_evt_cfg->events); - - event_bitmap = subsc_evt_cfg->events; - mwifiex_dbg(priv->adapter, CMD, "cmd: event bitmap : %16x\n", - event_bitmap); - - if (((subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) || - (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_SET)) && - (event_bitmap == 0)) { - mwifiex_dbg(priv->adapter, ERROR, - "Error: No event specified\t" - "for bitwise action type\n"); - return -EINVAL; - } - - /* - * Append TLV structures for each of the specified events for - * subscribing or re-configuring. This is not required for - * bitwise unsubscribing request. - */ - if (subsc_evt_cfg->action == HostCmd_ACT_BITWISE_CLR) - return 0; - - pos = ((u8 *)subsc_evt) + - sizeof(struct host_cmd_ds_802_11_subsc_evt); - - if (event_bitmap & BITMASK_BCN_RSSI_LOW) { - rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; - - rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_LOW); - rssi_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - - sizeof(struct mwifiex_ie_types_header)); - rssi_tlv->abs_value = subsc_evt_cfg->bcn_l_rssi_cfg.abs_value; - rssi_tlv->evt_freq = subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq; - - mwifiex_dbg(priv->adapter, EVENT, - "Cfg Beacon Low Rssi event,\t" - "RSSI:-%d dBm, Freq:%d\n", - subsc_evt_cfg->bcn_l_rssi_cfg.abs_value, - subsc_evt_cfg->bcn_l_rssi_cfg.evt_freq); - - pos += sizeof(struct mwifiex_ie_types_rssi_threshold); - le16_add_cpu(&cmd->size, - sizeof(struct mwifiex_ie_types_rssi_threshold)); - } - - if (event_bitmap & BITMASK_BCN_RSSI_HIGH) { - rssi_tlv = (struct mwifiex_ie_types_rssi_threshold *) pos; - - rssi_tlv->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); - rssi_tlv->header.len = - cpu_to_le16(sizeof(struct mwifiex_ie_types_rssi_threshold) - - sizeof(struct mwifiex_ie_types_header)); - rssi_tlv->abs_value = subsc_evt_cfg->bcn_h_rssi_cfg.abs_value; - rssi_tlv->evt_freq = subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq; - - mwifiex_dbg(priv->adapter, EVENT, - "Cfg Beacon High Rssi event,\t" - "RSSI:-%d dBm, Freq:%d\n", - subsc_evt_cfg->bcn_h_rssi_cfg.abs_value, - subsc_evt_cfg->bcn_h_rssi_cfg.evt_freq); - - pos += sizeof(struct mwifiex_ie_types_rssi_threshold); - le16_add_cpu(&cmd->size, - sizeof(struct mwifiex_ie_types_rssi_threshold)); - } - - return 0; -} - -static int -mwifiex_cmd_append_rpn_expression(struct mwifiex_private *priv, - struct mwifiex_mef_entry *mef_entry, - u8 **buffer) -{ - struct mwifiex_mef_filter *filter = mef_entry->filter; - int i, byte_len; - u8 *stack_ptr = *buffer; - - for (i = 0; i < MWIFIEX_MEF_MAX_FILTERS; i++) { - filter = &mef_entry->filter[i]; - if (!filter->filt_type) - break; - *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->repeat); - stack_ptr += 4; - *stack_ptr = TYPE_DNUM; - stack_ptr += 1; - - byte_len = filter->byte_seq[MWIFIEX_MEF_MAX_BYTESEQ]; - memcpy(stack_ptr, filter->byte_seq, byte_len); - stack_ptr += byte_len; - *stack_ptr = byte_len; - stack_ptr += 1; - *stack_ptr = TYPE_BYTESEQ; - stack_ptr += 1; - - *(__le32 *)stack_ptr = cpu_to_le32((u32)filter->offset); - stack_ptr += 4; - *stack_ptr = TYPE_DNUM; - stack_ptr += 1; - - *stack_ptr = filter->filt_type; - stack_ptr += 1; - - if (filter->filt_action) { - *stack_ptr = filter->filt_action; - stack_ptr += 1; - } - - if (stack_ptr - *buffer > STACK_NBYTES) - return -1; - } - - *buffer = stack_ptr; - return 0; -} - -static int -mwifiex_cmd_mef_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - struct mwifiex_ds_mef_cfg *mef) -{ - struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg; - struct mwifiex_fw_mef_entry *mef_entry = NULL; - u8 *pos = (u8 *)mef_cfg; - u16 i; - - cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG); - - mef_cfg->criteria = cpu_to_le32(mef->criteria); - mef_cfg->num_entries = cpu_to_le16(mef->num_entries); - pos += sizeof(*mef_cfg); - - for (i = 0; i < mef->num_entries; i++) { - mef_entry = (struct mwifiex_fw_mef_entry *)pos; - mef_entry->mode = mef->mef_entry[i].mode; - mef_entry->action = mef->mef_entry[i].action; - pos += sizeof(*mef_cfg->mef_entry); - - if (mwifiex_cmd_append_rpn_expression(priv, - &mef->mef_entry[i], &pos)) - return -1; - - mef_entry->exprsize = - cpu_to_le16(pos - mef_entry->expr); - } - cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN); - - return 0; -} - -/* This function parse cal data from ASCII to hex */ -static u32 mwifiex_parse_cal_cfg(u8 *src, size_t len, u8 *dst) -{ - u8 *s = src, *d = dst; - - while (s - src < len) { - if (*s && (isspace(*s) || *s == '\t')) { - s++; - continue; - } - if (isxdigit(*s)) { - *d++ = simple_strtol(s, NULL, 16); - s += 2; - } else { - s++; - } - } - - return d - dst; -} - -int mwifiex_dnld_dt_cfgdata(struct mwifiex_private *priv, - struct device_node *node, const char *prefix) -{ -#ifdef CONFIG_OF - struct property *prop; - size_t len = strlen(prefix); - int ret; - - /* look for all matching property names */ - for_each_property_of_node(node, prop) { - if (len > strlen(prop->name) || - strncmp(prop->name, prefix, len)) - continue; - - /* property header is 6 bytes, data must fit in cmd buffer */ - if (prop && prop->value && prop->length > 6 && - prop->length <= MWIFIEX_SIZE_OF_CMD_BUFFER - S_DS_GEN) { - ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, - HostCmd_ACT_GEN_SET, 0, - prop, true); - if (ret) - return ret; - } - } -#endif - return 0; -} - -/* This function prepares command of set_cfg_data. */ -static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, void *data_buf) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct property *prop = data_buf; - u32 len; - u8 *data = (u8 *)cmd + S_DS_GEN; - int ret; - - if (prop) { - len = prop->length; - ret = of_property_read_u8_array(adapter->dt_node, prop->name, - data, len); - if (ret) - return ret; - mwifiex_dbg(adapter, INFO, - "download cfg_data from device tree: %s\n", - prop->name); - } else if (adapter->cal_data->data && adapter->cal_data->size > 0) { - len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, - adapter->cal_data->size, data); - mwifiex_dbg(adapter, INFO, - "download cfg_data from config file\n"); - } else { - return -1; - } - - cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); - cmd->size = cpu_to_le16(S_DS_GEN + len); - - return 0; -} - -static int -mwifiex_cmd_set_mc_policy(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_ds_multi_chan_policy *mc_pol = &cmd->params.mc_policy; - const u16 *drcs_info = data_buf; - - mc_pol->action = cpu_to_le16(cmd_action); - mc_pol->policy = cpu_to_le16(*drcs_info); - cmd->command = cpu_to_le16(HostCmd_CMD_MC_POLICY); - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_multi_chan_policy) + - S_DS_GEN); - return 0; -} - -static int mwifiex_cmd_robust_coex(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, bool *is_timeshare) -{ - struct host_cmd_ds_robust_coex *coex = &cmd->params.coex; - struct mwifiex_ie_types_robust_coex *coex_tlv; - - cmd->command = cpu_to_le16(HostCmd_CMD_ROBUST_COEX); - cmd->size = cpu_to_le16(sizeof(*coex) + sizeof(*coex_tlv) + S_DS_GEN); - - coex->action = cpu_to_le16(cmd_action); - coex_tlv = (struct mwifiex_ie_types_robust_coex *) - ((u8 *)coex + sizeof(*coex)); - coex_tlv->header.type = cpu_to_le16(TLV_TYPE_ROBUST_COEX); - coex_tlv->header.len = cpu_to_le16(sizeof(coex_tlv->mode)); - - if (coex->action == HostCmd_ACT_GEN_GET) - return 0; - - if (*is_timeshare) - coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_TIMESHARE); - else - coex_tlv->mode = cpu_to_le32(MWIFIEX_COEX_MODE_SPATIAL); - - return 0; -} - -static int -mwifiex_cmd_coalesce_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_ds_coalesce_cfg *coalesce_cfg = - &cmd->params.coalesce_cfg; - struct mwifiex_ds_coalesce_cfg *cfg = data_buf; - struct coalesce_filt_field_param *param; - u16 cnt, idx, length; - struct coalesce_receive_filt_rule *rule; - - cmd->command = cpu_to_le16(HostCmd_CMD_COALESCE_CFG); - cmd->size = cpu_to_le16(S_DS_GEN); - - coalesce_cfg->action = cpu_to_le16(cmd_action); - coalesce_cfg->num_of_rules = cpu_to_le16(cfg->num_of_rules); - rule = coalesce_cfg->rule; - - for (cnt = 0; cnt < cfg->num_of_rules; cnt++) { - rule->header.type = cpu_to_le16(TLV_TYPE_COALESCE_RULE); - rule->max_coalescing_delay = - 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 = - cpu_to_le16(cfg->rule[cnt].params[idx].offset); - memcpy(param->operand_byte_stream, - cfg->rule[cnt].params[idx].operand_byte_stream, - param->operand_len); - - length += sizeof(struct coalesce_filt_field_param); - - param++; - } - - /* Total rule length is sizeof max_coalescing_delay(u16), - * num_of_fields(u8), pkt_type(u8) and total length of the all - * params - */ - rule->header.len = cpu_to_le16(length + sizeof(u16) + - sizeof(u8) + sizeof(u8)); - - /* Add the rule length to the command size*/ - le16_add_cpu(&cmd->size, le16_to_cpu(rule->header.len) + - sizeof(struct mwifiex_ie_types_header)); - - rule = (void *)((u8 *)rule->params + length); - } - - /* Add sizeof action, num_of_rules to total command length */ - le16_add_cpu(&cmd->size, sizeof(u16) + sizeof(u16)); - - return 0; -} - -static int -mwifiex_cmd_tdls_config(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_ds_tdls_config *tdls_config = &cmd->params.tdls_config; - struct mwifiex_tdls_init_cs_params *config; - struct mwifiex_tdls_config *init_config; - u16 len; - - cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_CONFIG); - cmd->size = cpu_to_le16(S_DS_GEN); - tdls_config->tdls_action = cpu_to_le16(cmd_action); - le16_add_cpu(&cmd->size, sizeof(tdls_config->tdls_action)); - - switch (cmd_action) { - case ACT_TDLS_CS_ENABLE_CONFIG: - init_config = data_buf; - len = sizeof(*init_config); - memcpy(tdls_config->tdls_data, init_config, len); - break; - case ACT_TDLS_CS_INIT: - config = data_buf; - len = sizeof(*config); - memcpy(tdls_config->tdls_data, config, len); - break; - case ACT_TDLS_CS_STOP: - len = sizeof(struct mwifiex_tdls_stop_cs_params); - memcpy(tdls_config->tdls_data, data_buf, len); - break; - case ACT_TDLS_CS_PARAMS: - len = sizeof(struct mwifiex_tdls_config_cs_params); - memcpy(tdls_config->tdls_data, data_buf, len); - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "Unknown TDLS configuration\n"); - return -ENOTSUPP; - } - - le16_add_cpu(&cmd->size, len); - return 0; -} - -static int -mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, - void *data_buf) -{ - struct host_cmd_ds_tdls_oper *tdls_oper = &cmd->params.tdls_oper; - struct mwifiex_ds_tdls_oper *oper = data_buf; - struct mwifiex_sta_node *sta_ptr; - struct host_cmd_tlv_rates *tlv_rates; - struct mwifiex_ie_types_htcap *ht_capab; - struct mwifiex_ie_types_qos_info *wmm_qos_info; - struct mwifiex_ie_types_extcap *extcap; - struct mwifiex_ie_types_vhtcap *vht_capab; - struct mwifiex_ie_types_aid *aid; - struct mwifiex_ie_types_tdls_idle_timeout *timeout; - u8 *pos, qos_info; - u16 config_len = 0; - struct station_parameters *params = priv->sta_params; - - cmd->command = cpu_to_le16(HostCmd_CMD_TDLS_OPER); - cmd->size = cpu_to_le16(S_DS_GEN); - le16_add_cpu(&cmd->size, sizeof(struct host_cmd_ds_tdls_oper)); - - tdls_oper->reason = 0; - memcpy(tdls_oper->peer_mac, oper->peer_mac, ETH_ALEN); - sta_ptr = mwifiex_get_sta_entry(priv, oper->peer_mac); - - pos = (u8 *)tdls_oper + sizeof(struct host_cmd_ds_tdls_oper); - - switch (oper->tdls_action) { - case MWIFIEX_TDLS_DISABLE_LINK: - tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_DELETE); - break; - case MWIFIEX_TDLS_CREATE_LINK: - tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CREATE); - break; - case MWIFIEX_TDLS_CONFIG_LINK: - tdls_oper->tdls_action = cpu_to_le16(ACT_TDLS_CONFIG); - - if (!params) { - mwifiex_dbg(priv->adapter, ERROR, - "TDLS config params not available for %pM\n", - oper->peer_mac); - return -ENODATA; - } - - *(__le16 *)pos = cpu_to_le16(params->capability); - config_len += sizeof(params->capability); - - qos_info = params->uapsd_queues | (params->max_sp << 5); - wmm_qos_info = (struct mwifiex_ie_types_qos_info *)(pos + - config_len); - wmm_qos_info->header.type = cpu_to_le16(WLAN_EID_QOS_CAPA); - wmm_qos_info->header.len = cpu_to_le16(sizeof(qos_info)); - wmm_qos_info->qos_info = qos_info; - config_len += sizeof(struct mwifiex_ie_types_qos_info); - - if (params->ht_capa) { - ht_capab = (struct mwifiex_ie_types_htcap *)(pos + - config_len); - ht_capab->header.type = - cpu_to_le16(WLAN_EID_HT_CAPABILITY); - ht_capab->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - memcpy(&ht_capab->ht_cap, params->ht_capa, - sizeof(struct ieee80211_ht_cap)); - config_len += sizeof(struct mwifiex_ie_types_htcap); - } - - if (params->supported_rates && params->supported_rates_len) { - tlv_rates = (struct host_cmd_tlv_rates *)(pos + - config_len); - tlv_rates->header.type = - cpu_to_le16(WLAN_EID_SUPP_RATES); - tlv_rates->header.len = - cpu_to_le16(params->supported_rates_len); - memcpy(tlv_rates->rates, params->supported_rates, - params->supported_rates_len); - config_len += sizeof(struct host_cmd_tlv_rates) + - params->supported_rates_len; - } - - if (params->ext_capab && params->ext_capab_len) { - extcap = (struct mwifiex_ie_types_extcap *)(pos + - config_len); - extcap->header.type = - cpu_to_le16(WLAN_EID_EXT_CAPABILITY); - extcap->header.len = cpu_to_le16(params->ext_capab_len); - memcpy(extcap->ext_capab, params->ext_capab, - params->ext_capab_len); - config_len += sizeof(struct mwifiex_ie_types_extcap) + - params->ext_capab_len; - } - if (params->vht_capa) { - vht_capab = (struct mwifiex_ie_types_vhtcap *)(pos + - config_len); - vht_capab->header.type = - cpu_to_le16(WLAN_EID_VHT_CAPABILITY); - vht_capab->header.len = - cpu_to_le16(sizeof(struct ieee80211_vht_cap)); - memcpy(&vht_capab->vht_cap, params->vht_capa, - sizeof(struct ieee80211_vht_cap)); - config_len += sizeof(struct mwifiex_ie_types_vhtcap); - } - if (params->aid) { - aid = (struct mwifiex_ie_types_aid *)(pos + config_len); - aid->header.type = cpu_to_le16(WLAN_EID_AID); - aid->header.len = cpu_to_le16(sizeof(params->aid)); - aid->aid = cpu_to_le16(params->aid); - config_len += sizeof(struct mwifiex_ie_types_aid); - } - - timeout = (void *)(pos + config_len); - timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT); - timeout->header.len = cpu_to_le16(sizeof(timeout->value)); - timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC); - config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout); - - break; - default: - mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS operation\n"); - return -ENOTSUPP; - } - - le16_add_cpu(&cmd->size, config_len); - - return 0; -} - -/* This function prepares command of sdio rx aggr info. */ -static int mwifiex_cmd_sdio_rx_aggr_cfg(struct host_cmd_ds_command *cmd, - u16 cmd_action, void *data_buf) -{ - struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = - &cmd->params.sdio_rx_aggr_cfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_SDIO_SP_RX_AGGR_CFG); - cmd->size = - cpu_to_le16(sizeof(struct host_cmd_sdio_sp_rx_aggr_cfg) + - S_DS_GEN); - cfg->action = cmd_action; - if (cmd_action == HostCmd_ACT_GEN_SET) - cfg->enable = *(u8 *)data_buf; - - return 0; -} - -/* - * This function prepares the commands before sending them to the firmware. - * - * This is a generic function which calls specific command preparation - * routines based upon the command number. - */ -int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, - u16 cmd_action, u32 cmd_oid, - void *data_buf, void *cmd_buf) -{ - struct host_cmd_ds_command *cmd_ptr = cmd_buf; - int ret = 0; - - /* Prepare command */ - switch (cmd_no) { - case HostCmd_CMD_GET_HW_SPEC: - ret = mwifiex_cmd_get_hw_spec(priv, cmd_ptr); - break; - case HostCmd_CMD_CFG_DATA: - ret = mwifiex_cmd_cfg_data(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_MAC_CONTROL: - ret = mwifiex_cmd_mac_control(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_802_11_MAC_ADDRESS: - ret = mwifiex_cmd_802_11_mac_address(priv, cmd_ptr, - cmd_action); - break; - case HostCmd_CMD_MAC_MULTICAST_ADR: - ret = mwifiex_cmd_mac_multicast_adr(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_TX_RATE_CFG: - ret = mwifiex_cmd_tx_rate_cfg(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_TXPWR_CFG: - ret = mwifiex_cmd_tx_power_cfg(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_RF_TX_PWR: - ret = mwifiex_cmd_rf_tx_power(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_RF_ANTENNA: - ret = mwifiex_cmd_rf_antenna(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_802_11_PS_MODE_ENH: - ret = mwifiex_cmd_enh_power_mode(priv, cmd_ptr, cmd_action, - (uint16_t)cmd_oid, data_buf); - break; - case HostCmd_CMD_802_11_HS_CFG_ENH: - ret = mwifiex_cmd_802_11_hs_cfg(priv, cmd_ptr, cmd_action, - (struct mwifiex_hs_config_param *) data_buf); - break; - case HostCmd_CMD_802_11_SCAN: - ret = mwifiex_cmd_802_11_scan(cmd_ptr, data_buf); - break; - case HostCmd_CMD_802_11_BG_SCAN_QUERY: - ret = mwifiex_cmd_802_11_bg_scan_query(cmd_ptr); - break; - case HostCmd_CMD_802_11_ASSOCIATE: - ret = mwifiex_cmd_802_11_associate(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_802_11_DEAUTHENTICATE: - ret = mwifiex_cmd_802_11_deauthenticate(priv, cmd_ptr, - data_buf); - break; - case HostCmd_CMD_802_11_AD_HOC_START: - ret = mwifiex_cmd_802_11_ad_hoc_start(priv, cmd_ptr, - data_buf); - break; - case HostCmd_CMD_802_11_GET_LOG: - ret = mwifiex_cmd_802_11_get_log(cmd_ptr); - break; - case HostCmd_CMD_802_11_AD_HOC_JOIN: - ret = mwifiex_cmd_802_11_ad_hoc_join(priv, cmd_ptr, - data_buf); - break; - case HostCmd_CMD_802_11_AD_HOC_STOP: - ret = mwifiex_cmd_802_11_ad_hoc_stop(cmd_ptr); - break; - case HostCmd_CMD_RSSI_INFO: - ret = mwifiex_cmd_802_11_rssi_info(priv, cmd_ptr, cmd_action); - break; - case HostCmd_CMD_802_11_SNMP_MIB: - ret = mwifiex_cmd_802_11_snmp_mib(priv, cmd_ptr, cmd_action, - cmd_oid, data_buf); - break; - case HostCmd_CMD_802_11_TX_RATE_QUERY: - cmd_ptr->command = - cpu_to_le16(HostCmd_CMD_802_11_TX_RATE_QUERY); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_tx_rate_query) + - S_DS_GEN); - priv->tx_rate = 0; - ret = 0; - break; - case HostCmd_CMD_VERSION_EXT: - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->params.verext.version_str_sel = - (u8) (*((u32 *) data_buf)); - memcpy(&cmd_ptr->params, data_buf, - sizeof(struct host_cmd_ds_version_ext)); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_version_ext) + - S_DS_GEN); - ret = 0; - break; - case HostCmd_CMD_MGMT_FRAME_REG: - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->params.reg_mask.action = cpu_to_le16(cmd_action); - cmd_ptr->params.reg_mask.mask = cpu_to_le32(*(u32 *)data_buf); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_mgmt_frame_reg) + - S_DS_GEN); - ret = 0; - break; - case HostCmd_CMD_REMAIN_ON_CHAN: - cmd_ptr->command = cpu_to_le16(cmd_no); - memcpy(&cmd_ptr->params, data_buf, - sizeof(struct host_cmd_ds_remain_on_chan)); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_remain_on_chan) + - S_DS_GEN); - break; - case HostCmd_CMD_11AC_CFG: - ret = mwifiex_cmd_11ac_cfg(priv, cmd_ptr, cmd_action, data_buf); - break; - case HostCmd_CMD_P2P_MODE_CFG: - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->params.mode_cfg.action = cpu_to_le16(cmd_action); - cmd_ptr->params.mode_cfg.mode = cpu_to_le16(*(u16 *)data_buf); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_p2p_mode_cfg) + - S_DS_GEN); - break; - case HostCmd_CMD_FUNC_INIT: - if (priv->adapter->hw_status == MWIFIEX_HW_STATUS_RESET) - priv->adapter->hw_status = MWIFIEX_HW_STATUS_READY; - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->size = cpu_to_le16(S_DS_GEN); - break; - case HostCmd_CMD_FUNC_SHUTDOWN: - priv->adapter->hw_status = MWIFIEX_HW_STATUS_RESET; - cmd_ptr->command = cpu_to_le16(cmd_no); - cmd_ptr->size = cpu_to_le16(S_DS_GEN); - break; - case HostCmd_CMD_11N_ADDBA_REQ: - ret = mwifiex_cmd_11n_addba_req(cmd_ptr, data_buf); - break; - case HostCmd_CMD_11N_DELBA: - ret = mwifiex_cmd_11n_delba(cmd_ptr, data_buf); - break; - case HostCmd_CMD_11N_ADDBA_RSP: - ret = mwifiex_cmd_11n_addba_rsp_gen(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_802_11_KEY_MATERIAL: - ret = mwifiex_cmd_802_11_key_material(priv, cmd_ptr, - cmd_action, cmd_oid, - data_buf); - break; - case HostCmd_CMD_802_11D_DOMAIN_INFO: - ret = mwifiex_cmd_802_11d_domain_info(priv, cmd_ptr, - cmd_action); - break; - case HostCmd_CMD_RECONFIGURE_TX_BUFF: - ret = mwifiex_cmd_recfg_tx_buf(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_AMSDU_AGGR_CTRL: - ret = mwifiex_cmd_amsdu_aggr_ctrl(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_11N_CFG: - ret = mwifiex_cmd_11n_cfg(priv, cmd_ptr, cmd_action, data_buf); - break; - case HostCmd_CMD_WMM_GET_STATUS: - mwifiex_dbg(priv->adapter, CMD, - "cmd: WMM: WMM_GET_STATUS cmd sent\n"); - cmd_ptr->command = cpu_to_le16(HostCmd_CMD_WMM_GET_STATUS); - cmd_ptr->size = - cpu_to_le16(sizeof(struct host_cmd_ds_wmm_get_status) + - S_DS_GEN); - ret = 0; - break; - case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: - ret = mwifiex_cmd_ibss_coalescing_status(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_802_11_SCAN_EXT: - ret = mwifiex_cmd_802_11_scan_ext(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_MEM_ACCESS: - ret = mwifiex_cmd_mem_access(cmd_ptr, cmd_action, data_buf); - break; - case HostCmd_CMD_MAC_REG_ACCESS: - case HostCmd_CMD_BBP_REG_ACCESS: - case HostCmd_CMD_RF_REG_ACCESS: - case HostCmd_CMD_PMIC_REG_ACCESS: - case HostCmd_CMD_CAU_REG_ACCESS: - case HostCmd_CMD_802_11_EEPROM_ACCESS: - ret = mwifiex_cmd_reg_access(cmd_ptr, cmd_action, data_buf); - break; - case HostCmd_CMD_SET_BSS_MODE: - cmd_ptr->command = cpu_to_le16(cmd_no); - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) - cmd_ptr->params.bss_mode.con_type = - CONNECTION_TYPE_ADHOC; - else if (priv->bss_mode == NL80211_IFTYPE_STATION || - priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) - cmd_ptr->params.bss_mode.con_type = - CONNECTION_TYPE_INFRA; - else if (priv->bss_mode == NL80211_IFTYPE_AP || - priv->bss_mode == NL80211_IFTYPE_P2P_GO) - cmd_ptr->params.bss_mode.con_type = CONNECTION_TYPE_AP; - cmd_ptr->size = cpu_to_le16(sizeof(struct - host_cmd_ds_set_bss_mode) + S_DS_GEN); - ret = 0; - break; - case HostCmd_CMD_PCIE_DESC_DETAILS: - ret = mwifiex_cmd_pcie_host_spec(priv, cmd_ptr, cmd_action); - break; - case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: - ret = mwifiex_cmd_802_11_subsc_evt(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_MEF_CFG: - ret = mwifiex_cmd_mef_cfg(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_COALESCE_CFG: - ret = mwifiex_cmd_coalesce_cfg(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_TDLS_OPER: - ret = mwifiex_cmd_tdls_oper(priv, cmd_ptr, data_buf); - break; - case HostCmd_CMD_TDLS_CONFIG: - ret = mwifiex_cmd_tdls_config(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_CHAN_REPORT_REQUEST: - ret = mwifiex_cmd_issue_chan_report_request(priv, cmd_ptr, - data_buf); - break; - case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: - ret = mwifiex_cmd_sdio_rx_aggr_cfg(cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_MC_POLICY: - ret = mwifiex_cmd_set_mc_policy(priv, cmd_ptr, cmd_action, - data_buf); - break; - case HostCmd_CMD_ROBUST_COEX: - ret = mwifiex_cmd_robust_coex(priv, cmd_ptr, cmd_action, - data_buf); - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "PREP_CMD: unknown cmd- %#x\n", cmd_no); - ret = -1; - break; - } - return ret; -} - -/* - * This function issues commands to initialize firmware. - * - * This is called after firmware download to bring the card to - * working state. - * Function is also called during reinitialization of virtual - * interfaces. - * - * The following commands are issued sequentially - - * - Set PCI-Express host buffer configuration (PCIE only) - * - Function init (for first interface only) - * - Read MAC address (for first interface only) - * - Reconfigure Tx buffer size (for first interface only) - * - Enable auto deep sleep (for first interface only) - * - Get Tx rate - * - Get Tx power - * - Set IBSS coalescing status - * - Set AMSDU aggregation control - * - Set 11d control - * - Set MAC control (this must be the last command to initialize firmware) - */ -int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - u16 enable = true; - struct mwifiex_ds_11n_amsdu_aggr_ctrl amsdu_aggr_ctrl; - struct mwifiex_ds_auto_ds auto_ds; - enum state_11d_t state_11d; - struct mwifiex_ds_11n_tx_cfg tx_cfg; - u8 sdio_sp_rx_aggr_enable; - - if (first_sta) { - if (priv->adapter->iface_type == MWIFIEX_PCIE) { - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_PCIE_DESC_DETAILS, - HostCmd_ACT_GEN_SET, 0, NULL, - true); - if (ret) - return -1; - } - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_FUNC_INIT, - HostCmd_ACT_GEN_SET, 0, NULL, true); - if (ret) - return -1; - - /* Download calibration data to firmware. - * The cal-data can be read from device tree and/or - * a configuration file and downloaded to firmware. - */ - adapter->dt_node = - of_find_node_by_name(NULL, "marvell_cfgdata"); - if (adapter->dt_node) { - ret = mwifiex_dnld_dt_cfgdata(priv, adapter->dt_node, - "marvell,caldata"); - if (ret) - return -1; - } - - if (adapter->cal_data) { - ret = mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, - HostCmd_ACT_GEN_SET, 0, NULL, - true); - if (ret) - return -1; - } - - /* Read MAC address from HW */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC, - HostCmd_ACT_GEN_GET, 0, NULL, true); - if (ret) - return -1; - - /** Set SDIO Single Port RX Aggr Info */ - if (priv->adapter->iface_type == MWIFIEX_SDIO && - ISSUPP_SDIO_SPA_ENABLED(priv->adapter->fw_cap_info) && - !priv->adapter->host_disable_sdio_rx_aggr) { - sdio_sp_rx_aggr_enable = true; - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_SDIO_SP_RX_AGGR_CFG, - HostCmd_ACT_GEN_SET, 0, - &sdio_sp_rx_aggr_enable, - true); - if (ret) { - mwifiex_dbg(priv->adapter, ERROR, - "error while enabling SP aggregation..disable it"); - adapter->sdio_rx_aggr_enable = false; - } - } - - /* Reconfigure tx buf size */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_RECONFIGURE_TX_BUFF, - HostCmd_ACT_GEN_SET, 0, - &priv->adapter->tx_buf_size, true); - if (ret) - return -1; - - if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { - /* Enable IEEE PS by default */ - priv->adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_PS_MODE_ENH, - EN_AUTO_PS, BITMAP_STA_PS, NULL, - true); - if (ret) - return -1; - } - - if (drcs) { - adapter->drcs_enabled = true; - if (ISSUPP_DRCS_ENABLED(adapter->fw_cap_info)) - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_MC_POLICY, - HostCmd_ACT_GEN_SET, 0, - &adapter->drcs_enabled, - true); - if (ret) - return -1; - } - } - - /* get tx rate */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_TX_RATE_CFG, - HostCmd_ACT_GEN_GET, 0, NULL, true); - if (ret) - return -1; - priv->data_rate = 0; - - /* get tx power */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_RF_TX_PWR, - HostCmd_ACT_GEN_GET, 0, NULL, true); - if (ret) - return -1; - - if (priv->bss_type == MWIFIEX_BSS_TYPE_STA) { - /* set ibss coalescing_status */ - ret = mwifiex_send_cmd( - priv, - HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, - HostCmd_ACT_GEN_SET, 0, &enable, true); - if (ret) - return -1; - } - - memset(&amsdu_aggr_ctrl, 0, sizeof(amsdu_aggr_ctrl)); - amsdu_aggr_ctrl.enable = true; - /* Send request to firmware */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_AMSDU_AGGR_CTRL, - HostCmd_ACT_GEN_SET, 0, - &amsdu_aggr_ctrl, true); - if (ret) - return -1; - /* MAC Control must be the last command in init_fw */ - /* set MAC Control */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter, true); - if (ret) - return -1; - - if (!disable_auto_ds && - first_sta && priv->adapter->iface_type != MWIFIEX_USB && - priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { - /* Enable auto deep sleep */ - auto_ds.auto_ds = DEEP_SLEEP_ON; - auto_ds.idle_time = DEEP_SLEEP_IDLE_TIME; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - EN_AUTO_PS, BITMAP_AUTO_DS, - &auto_ds, true); - if (ret) - return -1; - } - - if (priv->bss_type != MWIFIEX_BSS_TYPE_UAP) { - /* Send cmd to FW to enable/disable 11D function */ - state_11d = ENABLE_11D; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11D_I, - &state_11d, true); - if (ret) - mwifiex_dbg(priv->adapter, ERROR, - "11D: failed to enable 11D\n"); - } - - /* Send cmd to FW to configure 11n specific configuration - * (Short GI, Channel BW, Green field support etc.) for transmit - */ - tx_cfg.tx_htcap = MWIFIEX_FW_DEF_HTTXCFG; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG, - HostCmd_ACT_GEN_SET, 0, &tx_cfg, true); - - if (init) { - /* set last_init_cmd before sending the command */ - priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; - ret = -EINPROGRESS; - } - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c deleted file mode 100644 index 9ac7aa2431b4..000000000000 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ /dev/null @@ -1,1249 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station command response handling - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "11ac.h" - - -/* - * This function handles the command response error case. - * - * For scan response error, the function cancels all the pending - * scan commands and generates an event to inform the applications - * of the scan completion. - * - * For Power Save command failure, we do not retry enter PS - * command in case of Ad-hoc mode. - * - * For all other response errors, the current command buffer is freed - * and returned to the free command queue. - */ -static void -mwifiex_process_cmdresp_error(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct cmd_ctrl_node *cmd_node = NULL, *tmp_node; - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_802_11_ps_mode_enh *pm; - unsigned long flags; - - mwifiex_dbg(adapter, ERROR, - "CMD_RESP: cmd %#x error, result=%#x\n", - resp->command, resp->result); - - if (adapter->curr_cmd->wait_q_enabled) - adapter->cmd_wait_q.status = -1; - - switch (le16_to_cpu(resp->command)) { - case HostCmd_CMD_802_11_PS_MODE_ENH: - pm = &resp->params.psmode_enh; - mwifiex_dbg(adapter, ERROR, - "PS_MODE_ENH cmd failed: result=0x%x action=0x%X\n", - resp->result, le16_to_cpu(pm->action)); - /* We do not re-try enter-ps command in ad-hoc mode. */ - if (le16_to_cpu(pm->action) == EN_AUTO_PS && - (le16_to_cpu(pm->params.ps_bitmap) & BITMAP_STA_PS) && - priv->bss_mode == NL80211_IFTYPE_ADHOC) - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; - - break; - case HostCmd_CMD_802_11_SCAN: - case HostCmd_CMD_802_11_SCAN_EXT: - /* Cancel all pending scan command */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - list_for_each_entry_safe(cmd_node, tmp_node, - &adapter->scan_pending_q, list) { - list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); - mwifiex_insert_cmd_to_free_q(adapter, cmd_node); - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); - } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->scan_processing = false; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); - break; - - case HostCmd_CMD_MAC_CONTROL: - break; - - case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: - mwifiex_dbg(adapter, MSG, - "SDIO RX single-port aggregation Not support\n"); - break; - - default: - break; - } - /* Handling errors here */ - mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); - - spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); - adapter->curr_cmd = NULL; - spin_unlock_irqrestore(&adapter->mwifiex_cmd_lock, flags); -} - -/* - * This function handles the command response of get RSSI info. - * - * Handling includes changing the header fields into CPU format - * and saving the following parameters in driver - - * - Last data and beacon RSSI value - * - Average data and beacon RSSI value - * - Last data and beacon NF value - * - Average data and beacon NF value - * - * The parameters are send to the application as well, along with - * calculated SNR values. - */ -static int mwifiex_ret_802_11_rssi_info(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_rssi_info_rsp *rssi_info_rsp = - &resp->params.rssi_info_rsp; - struct mwifiex_ds_misc_subsc_evt *subsc_evt = - &priv->async_subsc_evt_storage; - - priv->data_rssi_last = le16_to_cpu(rssi_info_rsp->data_rssi_last); - priv->data_nf_last = le16_to_cpu(rssi_info_rsp->data_nf_last); - - priv->data_rssi_avg = le16_to_cpu(rssi_info_rsp->data_rssi_avg); - priv->data_nf_avg = le16_to_cpu(rssi_info_rsp->data_nf_avg); - - priv->bcn_rssi_last = le16_to_cpu(rssi_info_rsp->bcn_rssi_last); - priv->bcn_nf_last = le16_to_cpu(rssi_info_rsp->bcn_nf_last); - - priv->bcn_rssi_avg = le16_to_cpu(rssi_info_rsp->bcn_rssi_avg); - priv->bcn_nf_avg = le16_to_cpu(rssi_info_rsp->bcn_nf_avg); - - if (priv->subsc_evt_rssi_state == EVENT_HANDLED) - return 0; - - memset(subsc_evt, 0x00, sizeof(struct mwifiex_ds_misc_subsc_evt)); - - /* Resubscribe low and high rssi events with new thresholds */ - subsc_evt->events = BITMASK_BCN_RSSI_LOW | BITMASK_BCN_RSSI_HIGH; - subsc_evt->action = HostCmd_ACT_BITWISE_SET; - if (priv->subsc_evt_rssi_state == RSSI_LOW_RECVD) { - subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg - - priv->cqm_rssi_hyst); - subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); - } else if (priv->subsc_evt_rssi_state == RSSI_HIGH_RECVD) { - subsc_evt->bcn_l_rssi_cfg.abs_value = abs(priv->cqm_rssi_thold); - subsc_evt->bcn_h_rssi_cfg.abs_value = abs(priv->bcn_rssi_avg + - priv->cqm_rssi_hyst); - } - subsc_evt->bcn_l_rssi_cfg.evt_freq = 1; - subsc_evt->bcn_h_rssi_cfg.evt_freq = 1; - - priv->subsc_evt_rssi_state = EVENT_HANDLED; - - mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SUBSCRIBE_EVENT, - 0, 0, subsc_evt, false); - - return 0; -} - -/* - * This function handles the command response of set/get SNMP - * MIB parameters. - * - * Handling includes changing the header fields into CPU format - * and saving the parameter in driver. - * - * The following parameters are supported - - * - Fragmentation threshold - * - RTS threshold - * - Short retry limit - */ -static int mwifiex_ret_802_11_snmp_mib(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - u32 *data_buf) -{ - struct host_cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; - u16 oid = le16_to_cpu(smib->oid); - u16 query_type = le16_to_cpu(smib->query_type); - u32 ul_temp; - - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: oid value = %#x,\t" - "query_type = %#x, buf size = %#x\n", - oid, query_type, le16_to_cpu(smib->buf_size)); - if (query_type == HostCmd_ACT_GEN_GET) { - ul_temp = le16_to_cpu(*((__le16 *) (smib->value))); - if (data_buf) - *data_buf = ul_temp; - switch (oid) { - case FRAG_THRESH_I: - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: FragThsd =%u\n", - ul_temp); - break; - case RTS_THRESH_I: - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: RTSThsd =%u\n", - ul_temp); - break; - case SHORT_RETRY_LIM_I: - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: TxRetryCount=%u\n", - ul_temp); - break; - case DTIM_PERIOD_I: - mwifiex_dbg(priv->adapter, INFO, - "info: SNMP_RESP: DTIM period=%u\n", - ul_temp); - default: - break; - } - } - - return 0; -} - -/* - * This function handles the command response of get log request - * - * Handling includes changing the header fields into CPU format - * and sending the received parameters to application. - */ -static int mwifiex_ret_get_log(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct mwifiex_ds_get_stats *stats) -{ - struct host_cmd_ds_802_11_get_log *get_log = - &resp->params.get_log; - - if (stats) { - stats->mcast_tx_frame = le32_to_cpu(get_log->mcast_tx_frame); - stats->failed = le32_to_cpu(get_log->failed); - stats->retry = le32_to_cpu(get_log->retry); - stats->multi_retry = le32_to_cpu(get_log->multi_retry); - stats->frame_dup = le32_to_cpu(get_log->frame_dup); - stats->rts_success = le32_to_cpu(get_log->rts_success); - stats->rts_failure = le32_to_cpu(get_log->rts_failure); - stats->ack_failure = le32_to_cpu(get_log->ack_failure); - stats->rx_frag = le32_to_cpu(get_log->rx_frag); - stats->mcast_rx_frame = le32_to_cpu(get_log->mcast_rx_frame); - stats->fcs_error = le32_to_cpu(get_log->fcs_error); - stats->tx_frame = le32_to_cpu(get_log->tx_frame); - stats->wep_icv_error[0] = - le32_to_cpu(get_log->wep_icv_err_cnt[0]); - stats->wep_icv_error[1] = - le32_to_cpu(get_log->wep_icv_err_cnt[1]); - stats->wep_icv_error[2] = - le32_to_cpu(get_log->wep_icv_err_cnt[2]); - stats->wep_icv_error[3] = - le32_to_cpu(get_log->wep_icv_err_cnt[3]); - stats->bcn_rcv_cnt = le32_to_cpu(get_log->bcn_rcv_cnt); - stats->bcn_miss_cnt = le32_to_cpu(get_log->bcn_miss_cnt); - } - - return 0; -} - -/* - * This function handles the command response of set/get Tx rate - * configurations. - * - * Handling includes changing the header fields into CPU format - * and saving the following parameters in driver - - * - DSSS rate bitmap - * - OFDM rate bitmap - * - HT MCS rate bitmaps - * - * Based on the new rate bitmaps, the function re-evaluates if - * auto data rate has been activated. If not, it sends another - * query to the firmware to get the current Tx data rate. - */ -static int mwifiex_ret_tx_rate_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_tx_rate_cfg *rate_cfg = &resp->params.tx_rate_cfg; - struct mwifiex_rate_scope *rate_scope; - struct mwifiex_ie_types_header *head; - u16 tlv, tlv_buf_len, tlv_buf_left; - u8 *tlv_buf; - u32 i; - - tlv_buf = ((u8 *)rate_cfg) + sizeof(struct host_cmd_ds_tx_rate_cfg); - tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*rate_cfg); - - while (tlv_buf_left >= sizeof(*head)) { - head = (struct mwifiex_ie_types_header *)tlv_buf; - tlv = le16_to_cpu(head->type); - tlv_buf_len = le16_to_cpu(head->len); - - if (tlv_buf_left < (sizeof(*head) + tlv_buf_len)) - break; - - switch (tlv) { - case TLV_TYPE_RATE_SCOPE: - rate_scope = (struct mwifiex_rate_scope *) tlv_buf; - priv->bitmap_rates[0] = - le16_to_cpu(rate_scope->hr_dsss_rate_bitmap); - priv->bitmap_rates[1] = - le16_to_cpu(rate_scope->ofdm_rate_bitmap); - for (i = 0; - i < - sizeof(rate_scope->ht_mcs_rate_bitmap) / - sizeof(u16); i++) - priv->bitmap_rates[2 + i] = - le16_to_cpu(rate_scope-> - ht_mcs_rate_bitmap[i]); - - if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { - for (i = 0; i < ARRAY_SIZE(rate_scope-> - vht_mcs_rate_bitmap); - i++) - priv->bitmap_rates[10 + i] = - le16_to_cpu(rate_scope-> - vht_mcs_rate_bitmap[i]); - } - break; - /* Add RATE_DROP tlv here */ - } - - tlv_buf += (sizeof(*head) + tlv_buf_len); - tlv_buf_left -= (sizeof(*head) + tlv_buf_len); - } - - priv->is_data_rate_auto = mwifiex_is_rate_auto(priv); - - if (priv->is_data_rate_auto) - priv->data_rate = 0; - else - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL, false); - - return 0; -} - -/* - * This function handles the command response of get Tx power level. - * - * Handling includes saving the maximum and minimum Tx power levels - * in driver, as well as sending the values to user. - */ -static int mwifiex_get_power_level(struct mwifiex_private *priv, void *data_buf) -{ - int length, max_power = -1, min_power = -1; - struct mwifiex_types_power_group *pg_tlv_hdr; - struct mwifiex_power_group *pg; - - if (!data_buf) - return -1; - - pg_tlv_hdr = (struct mwifiex_types_power_group *)((u8 *)data_buf); - pg = (struct mwifiex_power_group *) - ((u8 *) pg_tlv_hdr + sizeof(struct mwifiex_types_power_group)); - length = le16_to_cpu(pg_tlv_hdr->length); - - /* At least one structure required to update power */ - if (length < sizeof(struct mwifiex_power_group)) - return 0; - - max_power = pg->power_max; - min_power = pg->power_min; - length -= sizeof(struct mwifiex_power_group); - - while (length >= sizeof(struct mwifiex_power_group)) { - 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(struct mwifiex_power_group); - } - priv->min_tx_power_level = (u8) min_power; - priv->max_tx_power_level = (u8) max_power; - - return 0; -} - -/* - * This function handles the command response of set/get Tx power - * configurations. - * - * Handling includes changing the header fields into CPU format - * and saving the current Tx power level in driver. - */ -static int mwifiex_ret_tx_power_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_ds_txpwr_cfg *txp_cfg = &resp->params.txp_cfg; - struct mwifiex_types_power_group *pg_tlv_hdr; - struct mwifiex_power_group *pg; - u16 action = le16_to_cpu(txp_cfg->action); - u16 tlv_buf_left; - - pg_tlv_hdr = (struct mwifiex_types_power_group *) - ((u8 *)txp_cfg + - sizeof(struct host_cmd_ds_txpwr_cfg)); - - pg = (struct mwifiex_power_group *) - ((u8 *)pg_tlv_hdr + - sizeof(struct mwifiex_types_power_group)); - - tlv_buf_left = le16_to_cpu(resp->size) - S_DS_GEN - sizeof(*txp_cfg); - if (tlv_buf_left < - le16_to_cpu(pg_tlv_hdr->length) + sizeof(*pg_tlv_hdr)) - return 0; - - switch (action) { - case HostCmd_ACT_GEN_GET: - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) - mwifiex_get_power_level(priv, pg_tlv_hdr); - - priv->tx_power_level = (u16) pg->power_min; - break; - - case HostCmd_ACT_GEN_SET: - if (!le32_to_cpu(txp_cfg->mode)) - break; - - if (pg->power_max == pg->power_min) - priv->tx_power_level = (u16) pg->power_min; - break; - default: - mwifiex_dbg(adapter, ERROR, - "CMD_RESP: unknown cmd action %d\n", - action); - return 0; - } - mwifiex_dbg(adapter, INFO, - "info: Current TxPower Level = %d, Max Power=%d, Min Power=%d\n", - priv->tx_power_level, priv->max_tx_power_level, - priv->min_tx_power_level); - - return 0; -} - -/* - * This function handles the command response of get RF Tx power. - */ -static int mwifiex_ret_rf_tx_power(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_rf_tx_pwr *txp = &resp->params.txp; - u16 action = le16_to_cpu(txp->action); - - priv->tx_power_level = le16_to_cpu(txp->cur_level); - - if (action == HostCmd_ACT_GEN_GET) { - priv->max_tx_power_level = txp->max_power; - priv->min_tx_power_level = txp->min_power; - } - - mwifiex_dbg(priv->adapter, INFO, - "Current TxPower Level=%d, Max Power=%d, Min Power=%d\n", - priv->tx_power_level, priv->max_tx_power_level, - priv->min_tx_power_level); - - return 0; -} - -/* - * This function handles the command response of set rf antenna - */ -static int mwifiex_ret_rf_antenna(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_rf_ant_mimo *ant_mimo = &resp->params.ant_mimo; - struct host_cmd_ds_rf_ant_siso *ant_siso = &resp->params.ant_siso; - struct mwifiex_adapter *adapter = priv->adapter; - - if (adapter->hw_dev_mcs_support == HT_STREAM_2X2) - mwifiex_dbg(adapter, INFO, - "RF_ANT_RESP: Tx action = 0x%x, Tx Mode = 0x%04x\t" - "Rx action = 0x%x, Rx Mode = 0x%04x\n", - le16_to_cpu(ant_mimo->action_tx), - le16_to_cpu(ant_mimo->tx_ant_mode), - le16_to_cpu(ant_mimo->action_rx), - le16_to_cpu(ant_mimo->rx_ant_mode)); - else - mwifiex_dbg(adapter, INFO, - "RF_ANT_RESP: action = 0x%x, Mode = 0x%04x\n", - le16_to_cpu(ant_siso->action), - le16_to_cpu(ant_siso->ant_mode)); - - return 0; -} - -/* - * This function handles the command response of set/get MAC address. - * - * Handling includes saving the MAC address in driver. - */ -static int mwifiex_ret_802_11_mac_address(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_mac_address *cmd_mac_addr = - &resp->params.mac_addr; - - memcpy(priv->curr_addr, cmd_mac_addr->mac_addr, ETH_ALEN); - - mwifiex_dbg(priv->adapter, INFO, - "info: set mac address: %pM\n", priv->curr_addr); - - return 0; -} - -/* - * This function handles the command response of set/get MAC multicast - * address. - */ -static int mwifiex_ret_mac_multicast_adr(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - return 0; -} - -/* - * This function handles the command response of get Tx rate query. - * - * Handling includes changing the header fields into CPU format - * and saving the Tx rate and HT information parameters in driver. - * - * Both rate configuration and current data rate can be retrieved - * with this request. - */ -static int mwifiex_ret_802_11_tx_rate_query(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - priv->tx_rate = resp->params.tx_rate.tx_rate; - priv->tx_htinfo = resp->params.tx_rate.ht_info; - if (!priv->is_data_rate_auto) - priv->data_rate = - mwifiex_index_to_data_rate(priv, priv->tx_rate, - priv->tx_htinfo); - - return 0; -} - -/* - * This function handles the command response of a deauthenticate - * command. - * - * If the deauthenticated MAC matches the current BSS MAC, the connection - * state is reset. - */ -static int mwifiex_ret_802_11_deauthenticate(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - adapter->dbg.num_cmd_deauth++; - if (!memcmp(resp->params.deauth.mac_addr, - &priv->curr_bss_params.bss_descriptor.mac_address, - sizeof(resp->params.deauth.mac_addr))) - mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); - - return 0; -} - -/* - * This function handles the command response of ad-hoc stop. - * - * The function resets the connection state in driver. - */ -static int mwifiex_ret_802_11_ad_hoc_stop(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - mwifiex_reset_connect_state(priv, WLAN_REASON_DEAUTH_LEAVING); - return 0; -} - -/* - * This function handles the command response of set/get v1 key material. - * - * Handling includes updating the driver parameters to reflect the - * changes. - */ -static int mwifiex_ret_802_11_key_material_v1(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_key_material *key = - &resp->params.key_material; - - if (le16_to_cpu(key->action) == HostCmd_ACT_GEN_SET) { - if ((le16_to_cpu(key->key_param_set.key_info) & KEY_MCAST)) { - mwifiex_dbg(priv->adapter, INFO, - "info: key: GTK is set\n"); - priv->wpa_is_gtk_set = true; - priv->scan_block = false; - priv->port_open = true; - } - } - - memset(priv->aes_key.key_param_set.key, 0, - sizeof(key->key_param_set.key)); - priv->aes_key.key_param_set.key_len = key->key_param_set.key_len; - memcpy(priv->aes_key.key_param_set.key, key->key_param_set.key, - le16_to_cpu(priv->aes_key.key_param_set.key_len)); - - return 0; -} - -/* - * This function handles the command response of set/get v2 key material. - * - * Handling includes updating the driver parameters to reflect the - * changes. - */ -static int mwifiex_ret_802_11_key_material_v2(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_key_material_v2 *key_v2; - __le16 len; - - key_v2 = &resp->params.key_material_v2; - if (le16_to_cpu(key_v2->action) == HostCmd_ACT_GEN_SET) { - if ((le16_to_cpu(key_v2->key_param_set.key_info) & KEY_MCAST)) { - mwifiex_dbg(priv->adapter, INFO, "info: key: GTK is set\n"); - priv->wpa_is_gtk_set = true; - priv->scan_block = false; - priv->port_open = true; - } - } - - if (key_v2->key_param_set.key_type != KEY_TYPE_ID_AES) - return 0; - - memset(priv->aes_key_v2.key_param_set.key_params.aes.key, 0, - WLAN_KEY_LEN_CCMP); - priv->aes_key_v2.key_param_set.key_params.aes.key_len = - key_v2->key_param_set.key_params.aes.key_len; - len = priv->aes_key_v2.key_param_set.key_params.aes.key_len; - memcpy(priv->aes_key_v2.key_param_set.key_params.aes.key, - key_v2->key_param_set.key_params.aes.key, le16_to_cpu(len)); - - return 0; -} - -/* Wrapper function for processing response of key material command */ -static int mwifiex_ret_802_11_key_material(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - if (priv->adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) - return mwifiex_ret_802_11_key_material_v2(priv, resp); - else - return mwifiex_ret_802_11_key_material_v1(priv, resp); -} - -/* - * This function handles the command response of get 11d domain information. - */ -static int mwifiex_ret_802_11d_domain_info(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11d_domain_info_rsp *domain_info = - &resp->params.domain_info_resp; - struct mwifiex_ietypes_domain_param_set *domain = &domain_info->domain; - u16 action = le16_to_cpu(domain_info->action); - u8 no_of_triplet; - - no_of_triplet = (u8) ((le16_to_cpu(domain->header.len) - - IEEE80211_COUNTRY_STRING_LEN) - / sizeof(struct ieee80211_country_ie_triplet)); - - mwifiex_dbg(priv->adapter, INFO, - "info: 11D Domain Info Resp: no_of_triplet=%d\n", - no_of_triplet); - - if (no_of_triplet > MWIFIEX_MAX_TRIPLET_802_11D) { - mwifiex_dbg(priv->adapter, FATAL, - "11D: invalid number of triplets %d returned\n", - no_of_triplet); - return -1; - } - - switch (action) { - case HostCmd_ACT_GEN_SET: /* Proc Set Action */ - break; - case HostCmd_ACT_GEN_GET: - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "11D: invalid action:%d\n", domain_info->action); - return -1; - } - - return 0; -} - -/* - * This function handles the command response of get extended version. - * - * Handling includes forming the extended version string and sending it - * to application. - */ -static int mwifiex_ret_ver_ext(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct host_cmd_ds_version_ext *version_ext) -{ - struct host_cmd_ds_version_ext *ver_ext = &resp->params.verext; - - if (version_ext) { - version_ext->version_str_sel = ver_ext->version_str_sel; - memcpy(version_ext->version_str, ver_ext->version_str, - sizeof(char) * 128); - memcpy(priv->version_str, ver_ext->version_str, 128); - } - return 0; -} - -/* - * This function handles the command response of remain on channel. - */ -static int -mwifiex_ret_remain_on_chan(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - struct host_cmd_ds_remain_on_chan *roc_cfg) -{ - struct host_cmd_ds_remain_on_chan *resp_cfg = &resp->params.roc_cfg; - - if (roc_cfg) - memcpy(roc_cfg, resp_cfg, sizeof(*roc_cfg)); - - return 0; -} - -/* - * This function handles the command response of P2P mode cfg. - */ -static int -mwifiex_ret_p2p_mode_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - void *data_buf) -{ - struct host_cmd_ds_p2p_mode_cfg *mode_cfg = &resp->params.mode_cfg; - - if (data_buf) - *((u16 *)data_buf) = le16_to_cpu(mode_cfg->mode); - - return 0; -} - -/* This function handles the command response of mem_access command - */ -static int -mwifiex_ret_mem_access(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, void *pioctl_buf) -{ - struct host_cmd_ds_mem_access *mem = (void *)&resp->params.mem; - - priv->mem_rw.addr = le32_to_cpu(mem->addr); - priv->mem_rw.value = le32_to_cpu(mem->value); - - return 0; -} -/* - * This function handles the command response of register access. - * - * The register value and offset are returned to the user. For EEPROM - * access, the byte count is also returned. - */ -static int mwifiex_ret_reg_access(u16 type, struct host_cmd_ds_command *resp, - void *data_buf) -{ - struct mwifiex_ds_reg_rw *reg_rw; - struct mwifiex_ds_read_eeprom *eeprom; - union reg { - struct host_cmd_ds_mac_reg_access *mac; - struct host_cmd_ds_bbp_reg_access *bbp; - struct host_cmd_ds_rf_reg_access *rf; - struct host_cmd_ds_pmic_reg_access *pmic; - struct host_cmd_ds_802_11_eeprom_access *eeprom; - } r; - - if (!data_buf) - return 0; - - reg_rw = data_buf; - eeprom = data_buf; - switch (type) { - case HostCmd_CMD_MAC_REG_ACCESS: - r.mac = &resp->params.mac_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.mac->offset)); - reg_rw->value = r.mac->value; - break; - case HostCmd_CMD_BBP_REG_ACCESS: - r.bbp = &resp->params.bbp_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.bbp->offset)); - reg_rw->value = cpu_to_le32((u32) r.bbp->value); - break; - - case HostCmd_CMD_RF_REG_ACCESS: - r.rf = &resp->params.rf_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); - reg_rw->value = cpu_to_le32((u32) r.bbp->value); - break; - case HostCmd_CMD_PMIC_REG_ACCESS: - r.pmic = &resp->params.pmic_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.pmic->offset)); - reg_rw->value = cpu_to_le32((u32) r.pmic->value); - break; - case HostCmd_CMD_CAU_REG_ACCESS: - r.rf = &resp->params.rf_reg; - reg_rw->offset = cpu_to_le32((u32) le16_to_cpu(r.rf->offset)); - reg_rw->value = cpu_to_le32((u32) r.rf->value); - break; - case HostCmd_CMD_802_11_EEPROM_ACCESS: - r.eeprom = &resp->params.eeprom; - pr_debug("info: EEPROM read len=%x\n", r.eeprom->byte_count); - if (le16_to_cpu(eeprom->byte_count) < - le16_to_cpu(r.eeprom->byte_count)) { - eeprom->byte_count = cpu_to_le16(0); - pr_debug("info: EEPROM read length is too big\n"); - return -1; - } - eeprom->offset = r.eeprom->offset; - eeprom->byte_count = r.eeprom->byte_count; - if (le16_to_cpu(eeprom->byte_count) > 0) - memcpy(&eeprom->value, &r.eeprom->value, - le16_to_cpu(r.eeprom->byte_count)); - - break; - default: - return -1; - } - return 0; -} - -/* - * This function handles the command response of get IBSS coalescing status. - * - * If the received BSSID is different than the current one, the current BSSID, - * beacon interval, ATIM window and ERP information are updated, along with - * changing the ad-hoc state accordingly. - */ -static int mwifiex_ret_ibss_coalescing_status(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_ibss_status *ibss_coal_resp = - &(resp->params.ibss_coalescing); - - if (le16_to_cpu(ibss_coal_resp->action) == HostCmd_ACT_GEN_SET) - return 0; - - mwifiex_dbg(priv->adapter, INFO, - "info: new BSSID %pM\n", ibss_coal_resp->bssid); - - /* If rsp has NULL BSSID, Just return..... No Action */ - if (is_zero_ether_addr(ibss_coal_resp->bssid)) { - mwifiex_dbg(priv->adapter, FATAL, "new BSSID is NULL\n"); - return 0; - } - - /* If BSSID is diff, modify current BSS parameters */ - if (!ether_addr_equal(priv->curr_bss_params.bss_descriptor.mac_address, ibss_coal_resp->bssid)) { - /* BSSID */ - memcpy(priv->curr_bss_params.bss_descriptor.mac_address, - ibss_coal_resp->bssid, ETH_ALEN); - - /* Beacon Interval */ - priv->curr_bss_params.bss_descriptor.beacon_period - = le16_to_cpu(ibss_coal_resp->beacon_interval); - - /* ERP Information */ - priv->curr_bss_params.bss_descriptor.erp_flags = - (u8) le16_to_cpu(ibss_coal_resp->use_g_rate_protect); - - priv->adhoc_state = ADHOC_COALESCED; - } - - return 0; -} -static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_tdls_oper *cmd_tdls_oper = &resp->params.tdls_oper; - u16 reason = le16_to_cpu(cmd_tdls_oper->reason); - u16 action = le16_to_cpu(cmd_tdls_oper->tdls_action); - struct mwifiex_sta_node *node = - mwifiex_get_sta_entry(priv, cmd_tdls_oper->peer_mac); - - switch (action) { - case ACT_TDLS_DELETE: - if (reason) { - if (!node || reason == TDLS_ERR_LINK_NONEXISTENT) - mwifiex_dbg(priv->adapter, MSG, - "TDLS link delete for %pM failed: reason %d\n", - cmd_tdls_oper->peer_mac, reason); - else - mwifiex_dbg(priv->adapter, ERROR, - "TDLS link delete for %pM failed: reason %d\n", - cmd_tdls_oper->peer_mac, reason); - } else { - mwifiex_dbg(priv->adapter, MSG, - "TDLS link delete for %pM successful\n", - cmd_tdls_oper->peer_mac); - } - break; - case ACT_TDLS_CREATE: - if (reason) { - mwifiex_dbg(priv->adapter, ERROR, - "TDLS link creation for %pM failed: reason %d", - cmd_tdls_oper->peer_mac, reason); - if (node && reason != TDLS_ERR_LINK_EXISTS) - node->tdls_status = TDLS_SETUP_FAILURE; - } else { - mwifiex_dbg(priv->adapter, MSG, - "TDLS link creation for %pM successful", - cmd_tdls_oper->peer_mac); - } - break; - case ACT_TDLS_CONFIG: - if (reason) { - mwifiex_dbg(priv->adapter, ERROR, - "TDLS link config for %pM failed, reason %d\n", - cmd_tdls_oper->peer_mac, reason); - if (node) - node->tdls_status = TDLS_SETUP_FAILURE; - } else { - mwifiex_dbg(priv->adapter, MSG, - "TDLS link config for %pM successful\n", - cmd_tdls_oper->peer_mac); - } - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "Unknown TDLS command action response %d", action); - return -1; - } - - return 0; -} -/* - * This function handles the command response for subscribe event command. - */ -static int mwifiex_ret_subsc_evt(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_802_11_subsc_evt *cmd_sub_event = - &resp->params.subsc_evt; - - /* For every subscribe event command (Get/Set/Clear), FW reports the - * current set of subscribed events*/ - mwifiex_dbg(priv->adapter, EVENT, - "Bitmap of currently subscribed events: %16x\n", - le16_to_cpu(cmd_sub_event->events)); - - return 0; -} - -static int mwifiex_ret_uap_sta_list(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct host_cmd_ds_sta_list *sta_list = - &resp->params.sta_list; - struct mwifiex_ie_types_sta_info *sta_info = (void *)&sta_list->tlv; - int i; - struct mwifiex_sta_node *sta_node; - - for (i = 0; i < sta_list->sta_count; i++) { - sta_node = mwifiex_get_sta_entry(priv, sta_info->mac); - if (unlikely(!sta_node)) - continue; - - sta_node->stats.rssi = sta_info->rssi; - sta_info++; - } - - return 0; -} - -/* This function handles the command response of set_cfg_data */ -static int mwifiex_ret_cfg_data(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - if (resp->result != HostCmd_RESULT_OK) { - mwifiex_dbg(priv->adapter, ERROR, "Cal data cmd resp failed\n"); - return -1; - } - - return 0; -} - -/** This Function handles the command response of sdio rx aggr */ -static int mwifiex_ret_sdio_rx_aggr_cfg(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct host_cmd_sdio_sp_rx_aggr_cfg *cfg = - &resp->params.sdio_rx_aggr_cfg; - - adapter->sdio_rx_aggr_enable = cfg->enable; - adapter->sdio_rx_block_size = le16_to_cpu(cfg->block_size); - - return 0; -} - -static int mwifiex_ret_robust_coex(struct mwifiex_private *priv, - struct host_cmd_ds_command *resp, - bool *is_timeshare) -{ - struct host_cmd_ds_robust_coex *coex = &resp->params.coex; - struct mwifiex_ie_types_robust_coex *coex_tlv; - u16 action = le16_to_cpu(coex->action); - u32 mode; - - coex_tlv = (struct mwifiex_ie_types_robust_coex - *)((u8 *)coex + sizeof(struct host_cmd_ds_robust_coex)); - if (action == HostCmd_ACT_GEN_GET) { - mode = le32_to_cpu(coex_tlv->mode); - if (mode == MWIFIEX_COEX_MODE_TIMESHARE) - *is_timeshare = true; - else - *is_timeshare = false; - } - - return 0; -} - -/* - * This function handles the command responses. - * - * This is a generic function, which calls command specific - * response handlers based on the command ID. - */ -int mwifiex_process_sta_cmdresp(struct mwifiex_private *priv, u16 cmdresp_no, - struct host_cmd_ds_command *resp) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - void *data_buf = adapter->curr_cmd->data_buf; - - /* If the command is not successful, cleanup and return failure */ - if (resp->result != HostCmd_RESULT_OK) { - mwifiex_process_cmdresp_error(priv, resp); - return -1; - } - /* Command successful, handle response */ - switch (cmdresp_no) { - case HostCmd_CMD_GET_HW_SPEC: - ret = mwifiex_ret_get_hw_spec(priv, resp); - break; - case HostCmd_CMD_CFG_DATA: - ret = mwifiex_ret_cfg_data(priv, resp); - break; - case HostCmd_CMD_MAC_CONTROL: - break; - case HostCmd_CMD_802_11_MAC_ADDRESS: - ret = mwifiex_ret_802_11_mac_address(priv, resp); - break; - case HostCmd_CMD_MAC_MULTICAST_ADR: - ret = mwifiex_ret_mac_multicast_adr(priv, resp); - break; - case HostCmd_CMD_TX_RATE_CFG: - ret = mwifiex_ret_tx_rate_cfg(priv, resp); - break; - case HostCmd_CMD_802_11_SCAN: - ret = mwifiex_ret_802_11_scan(priv, resp); - adapter->curr_cmd->wait_q_enabled = false; - break; - case HostCmd_CMD_802_11_SCAN_EXT: - ret = mwifiex_ret_802_11_scan_ext(priv, resp); - adapter->curr_cmd->wait_q_enabled = false; - break; - case HostCmd_CMD_802_11_BG_SCAN_QUERY: - ret = mwifiex_ret_802_11_scan(priv, resp); - mwifiex_dbg(adapter, CMD, - "info: CMD_RESP: BG_SCAN result is ready!\n"); - break; - case HostCmd_CMD_TXPWR_CFG: - ret = mwifiex_ret_tx_power_cfg(priv, resp); - break; - case HostCmd_CMD_RF_TX_PWR: - ret = mwifiex_ret_rf_tx_power(priv, resp); - break; - case HostCmd_CMD_RF_ANTENNA: - ret = mwifiex_ret_rf_antenna(priv, resp); - break; - case HostCmd_CMD_802_11_PS_MODE_ENH: - ret = mwifiex_ret_enh_power_mode(priv, resp, data_buf); - break; - case HostCmd_CMD_802_11_HS_CFG_ENH: - ret = mwifiex_ret_802_11_hs_cfg(priv, resp); - break; - case HostCmd_CMD_802_11_ASSOCIATE: - ret = mwifiex_ret_802_11_associate(priv, resp); - break; - case HostCmd_CMD_802_11_DEAUTHENTICATE: - ret = mwifiex_ret_802_11_deauthenticate(priv, resp); - break; - case HostCmd_CMD_802_11_AD_HOC_START: - case HostCmd_CMD_802_11_AD_HOC_JOIN: - ret = mwifiex_ret_802_11_ad_hoc(priv, resp); - break; - case HostCmd_CMD_802_11_AD_HOC_STOP: - ret = mwifiex_ret_802_11_ad_hoc_stop(priv, resp); - break; - case HostCmd_CMD_802_11_GET_LOG: - ret = mwifiex_ret_get_log(priv, resp, data_buf); - break; - case HostCmd_CMD_RSSI_INFO: - ret = mwifiex_ret_802_11_rssi_info(priv, resp); - break; - case HostCmd_CMD_802_11_SNMP_MIB: - ret = mwifiex_ret_802_11_snmp_mib(priv, resp, data_buf); - break; - case HostCmd_CMD_802_11_TX_RATE_QUERY: - ret = mwifiex_ret_802_11_tx_rate_query(priv, resp); - break; - case HostCmd_CMD_VERSION_EXT: - ret = mwifiex_ret_ver_ext(priv, resp, data_buf); - break; - case HostCmd_CMD_REMAIN_ON_CHAN: - ret = mwifiex_ret_remain_on_chan(priv, resp, data_buf); - break; - case HostCmd_CMD_11AC_CFG: - break; - case HostCmd_CMD_P2P_MODE_CFG: - ret = mwifiex_ret_p2p_mode_cfg(priv, resp, data_buf); - break; - case HostCmd_CMD_MGMT_FRAME_REG: - case HostCmd_CMD_FUNC_INIT: - case HostCmd_CMD_FUNC_SHUTDOWN: - break; - case HostCmd_CMD_802_11_KEY_MATERIAL: - ret = mwifiex_ret_802_11_key_material(priv, resp); - break; - case HostCmd_CMD_802_11D_DOMAIN_INFO: - ret = mwifiex_ret_802_11d_domain_info(priv, resp); - break; - case HostCmd_CMD_11N_ADDBA_REQ: - ret = mwifiex_ret_11n_addba_req(priv, resp); - break; - case HostCmd_CMD_11N_DELBA: - ret = mwifiex_ret_11n_delba(priv, resp); - break; - case HostCmd_CMD_11N_ADDBA_RSP: - ret = mwifiex_ret_11n_addba_resp(priv, resp); - break; - case HostCmd_CMD_RECONFIGURE_TX_BUFF: - if (0xffff == (u16)le16_to_cpu(resp->params.tx_buf.buff_size)) { - if (adapter->iface_type == MWIFIEX_USB && - adapter->usb_mc_setup) { - if (adapter->if_ops.multi_port_resync) - adapter->if_ops. - multi_port_resync(adapter); - adapter->usb_mc_setup = false; - adapter->tx_lock_flag = false; - } - break; - } - adapter->tx_buf_size = (u16) le16_to_cpu(resp->params. - tx_buf.buff_size); - adapter->tx_buf_size = (adapter->tx_buf_size - / MWIFIEX_SDIO_BLOCK_SIZE) - * MWIFIEX_SDIO_BLOCK_SIZE; - adapter->curr_tx_buf_size = adapter->tx_buf_size; - mwifiex_dbg(adapter, CMD, "cmd: curr_tx_buf_size=%d\n", - adapter->curr_tx_buf_size); - - if (adapter->if_ops.update_mp_end_port) - adapter->if_ops.update_mp_end_port(adapter, - le16_to_cpu(resp->params.tx_buf.mp_end_port)); - break; - case HostCmd_CMD_AMSDU_AGGR_CTRL: - break; - case HostCmd_CMD_WMM_GET_STATUS: - ret = mwifiex_ret_wmm_get_status(priv, resp); - break; - case HostCmd_CMD_802_11_IBSS_COALESCING_STATUS: - ret = mwifiex_ret_ibss_coalescing_status(priv, resp); - break; - case HostCmd_CMD_MEM_ACCESS: - ret = mwifiex_ret_mem_access(priv, resp, data_buf); - break; - case HostCmd_CMD_MAC_REG_ACCESS: - case HostCmd_CMD_BBP_REG_ACCESS: - case HostCmd_CMD_RF_REG_ACCESS: - case HostCmd_CMD_PMIC_REG_ACCESS: - case HostCmd_CMD_CAU_REG_ACCESS: - case HostCmd_CMD_802_11_EEPROM_ACCESS: - ret = mwifiex_ret_reg_access(cmdresp_no, resp, data_buf); - break; - case HostCmd_CMD_SET_BSS_MODE: - break; - case HostCmd_CMD_11N_CFG: - break; - case HostCmd_CMD_PCIE_DESC_DETAILS: - break; - case HostCmd_CMD_802_11_SUBSCRIBE_EVENT: - ret = mwifiex_ret_subsc_evt(priv, resp); - break; - case HostCmd_CMD_UAP_SYS_CONFIG: - break; - case HOST_CMD_APCMD_STA_LIST: - ret = mwifiex_ret_uap_sta_list(priv, resp); - break; - case HostCmd_CMD_UAP_BSS_START: - adapter->tx_lock_flag = false; - adapter->pps_uapsd_mode = false; - adapter->delay_null_pkt = false; - priv->bss_started = 1; - break; - case HostCmd_CMD_UAP_BSS_STOP: - priv->bss_started = 0; - break; - case HostCmd_CMD_UAP_STA_DEAUTH: - break; - case HOST_CMD_APCMD_SYS_RESET: - break; - case HostCmd_CMD_MEF_CFG: - break; - case HostCmd_CMD_COALESCE_CFG: - break; - case HostCmd_CMD_TDLS_OPER: - ret = mwifiex_ret_tdls_oper(priv, resp); - case HostCmd_CMD_MC_POLICY: - break; - case HostCmd_CMD_CHAN_REPORT_REQUEST: - break; - case HostCmd_CMD_SDIO_SP_RX_AGGR_CFG: - ret = mwifiex_ret_sdio_rx_aggr_cfg(priv, resp); - break; - case HostCmd_CMD_TDLS_CONFIG: - break; - case HostCmd_CMD_ROBUST_COEX: - ret = mwifiex_ret_robust_coex(priv, resp, data_buf); - break; - default: - mwifiex_dbg(adapter, ERROR, - "CMD_RESP: unknown cmd response %#x\n", - resp->command); - break; - } - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c deleted file mode 100644 index ff3ee9dfbbd5..000000000000 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ /dev/null @@ -1,864 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station event handling - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - -/* - * This function resets the connection state. - * - * The function is invoked after receiving a disconnect event from firmware, - * and performs the following actions - - * - Set media status to disconnected - * - Clean up Tx and Rx packets - * - Resets SNR/NF/RSSI value in driver - * - Resets security configurations in driver - * - Enables auto data rate - * - Saves the previous SSID and BSSID so that they can - * be used for re-association, if required - * - Erases current SSID and BSSID information - * - Sends a disconnect event to upper layers/applications. - */ -void -mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - if (!priv->media_connected) - return; - - mwifiex_dbg(adapter, INFO, - "info: handles disconnect event\n"); - - priv->media_connected = false; - - priv->scan_block = false; - priv->port_open = false; - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) { - mwifiex_disable_all_tdls_links(priv); - - if (priv->adapter->auto_tdls) - mwifiex_clean_auto_tdls(priv); - } - - /* Free Tx and Rx packets, report disconnect to upper layer */ - mwifiex_clean_txrx(priv); - - /* Reset SNR/NF/RSSI values */ - priv->data_rssi_last = 0; - priv->data_nf_last = 0; - priv->data_rssi_avg = 0; - priv->data_nf_avg = 0; - priv->bcn_rssi_last = 0; - priv->bcn_nf_last = 0; - priv->bcn_rssi_avg = 0; - priv->bcn_nf_avg = 0; - priv->rxpd_rate = 0; - priv->rxpd_htinfo = 0; - priv->sec_info.wpa_enabled = false; - priv->sec_info.wpa2_enabled = false; - priv->wpa_ie_len = 0; - - priv->sec_info.wapi_enabled = false; - priv->wapi_ie_len = 0; - priv->sec_info.wapi_key_on = false; - - priv->sec_info.encryption_mode = 0; - - /* Enable auto data rate */ - priv->is_data_rate_auto = true; - priv->data_rate = 0; - - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || - GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) && priv->hist_data) - mwifiex_hist_data_reset(priv); - - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - priv->adhoc_state = ADHOC_IDLE; - priv->adhoc_is_link_sensed = false; - } - - /* - * Memorize the previous SSID and BSSID so - * it could be used for re-assoc - */ - - mwifiex_dbg(adapter, INFO, - "info: previous SSID=%s, SSID len=%u\n", - priv->prev_ssid.ssid, priv->prev_ssid.ssid_len); - - mwifiex_dbg(adapter, INFO, - "info: current SSID=%s, SSID len=%u\n", - priv->curr_bss_params.bss_descriptor.ssid.ssid, - priv->curr_bss_params.bss_descriptor.ssid.ssid_len); - - memcpy(&priv->prev_ssid, - &priv->curr_bss_params.bss_descriptor.ssid, - sizeof(struct cfg80211_ssid)); - - memcpy(priv->prev_bssid, - priv->curr_bss_params.bss_descriptor.mac_address, ETH_ALEN); - - /* Need to erase the current SSID and BSSID info */ - memset(&priv->curr_bss_params, 0x00, sizeof(priv->curr_bss_params)); - - adapter->tx_lock_flag = false; - adapter->pps_uapsd_mode = false; - - if (adapter->is_cmd_timedout && adapter->curr_cmd) - return; - priv->media_connected = false; - mwifiex_dbg(adapter, MSG, - "info: successfully disconnected from %pM: reason code %d\n", - priv->cfg_bssid, reason_code); - if (priv->bss_mode == NL80211_IFTYPE_STATION || - priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { - cfg80211_disconnected(priv->netdev, reason_code, NULL, 0, - false, GFP_KERNEL); - } - eth_zero_addr(priv->cfg_bssid); - - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); -} - -static int mwifiex_parse_tdls_event(struct mwifiex_private *priv, - struct sk_buff *event_skb) -{ - int ret = 0; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_tdls_generic_event *tdls_evt = - (void *)event_skb->data + sizeof(adapter->event_cause); - u8 *mac = tdls_evt->peer_mac; - - /* reserved 2 bytes are not mandatory in tdls event */ - if (event_skb->len < (sizeof(struct mwifiex_tdls_generic_event) - - sizeof(u16) - sizeof(adapter->event_cause))) { - mwifiex_dbg(adapter, ERROR, "Invalid event length!\n"); - return -1; - } - - sta_ptr = mwifiex_get_sta_entry(priv, tdls_evt->peer_mac); - if (!sta_ptr) { - mwifiex_dbg(adapter, ERROR, "cannot get sta entry!\n"); - return -1; - } - - switch (le16_to_cpu(tdls_evt->type)) { - case TDLS_EVENT_LINK_TEAR_DOWN: - cfg80211_tdls_oper_request(priv->netdev, - tdls_evt->peer_mac, - NL80211_TDLS_TEARDOWN, - le16_to_cpu(tdls_evt->u.reason_code), - GFP_KERNEL); - break; - case TDLS_EVENT_CHAN_SWITCH_RESULT: - mwifiex_dbg(adapter, EVENT, "tdls channel switch result :\n"); - mwifiex_dbg(adapter, EVENT, - "status=0x%x, reason=0x%x cur_chan=%d\n", - tdls_evt->u.switch_result.status, - tdls_evt->u.switch_result.reason, - tdls_evt->u.switch_result.cur_chan); - - /* tdls channel switch failed */ - if (tdls_evt->u.switch_result.status != 0) { - switch (tdls_evt->u.switch_result.cur_chan) { - case TDLS_BASE_CHANNEL: - sta_ptr->tdls_status = TDLS_IN_BASE_CHAN; - break; - case TDLS_OFF_CHANNEL: - sta_ptr->tdls_status = TDLS_IN_OFF_CHAN; - break; - default: - break; - } - return ret; - } - - /* tdls channel switch success */ - switch (tdls_evt->u.switch_result.cur_chan) { - case TDLS_BASE_CHANNEL: - if (sta_ptr->tdls_status == TDLS_IN_BASE_CHAN) - break; - mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac, - false); - sta_ptr->tdls_status = TDLS_IN_BASE_CHAN; - break; - case TDLS_OFF_CHANNEL: - if (sta_ptr->tdls_status == TDLS_IN_OFF_CHAN) - break; - mwifiex_update_ralist_tx_pause_in_tdls_cs(priv, mac, - true); - sta_ptr->tdls_status = TDLS_IN_OFF_CHAN; - break; - default: - break; - } - - break; - case TDLS_EVENT_START_CHAN_SWITCH: - mwifiex_dbg(adapter, EVENT, "tdls start channel switch...\n"); - sta_ptr->tdls_status = TDLS_CHAN_SWITCHING; - break; - case TDLS_EVENT_CHAN_SWITCH_STOPPED: - mwifiex_dbg(adapter, EVENT, - "tdls chan switch stopped, reason=%d\n", - tdls_evt->u.cs_stop_reason); - break; - default: - break; - } - - return ret; -} - -static void mwifiex_process_uap_tx_pause(struct mwifiex_private *priv, - struct mwifiex_ie_types_header *tlv) -{ - struct mwifiex_tx_pause_tlv *tp; - struct mwifiex_sta_node *sta_ptr; - unsigned long flags; - - tp = (void *)tlv; - mwifiex_dbg(priv->adapter, EVENT, - "uap tx_pause: %pM pause=%d, pkts=%d\n", - tp->peermac, tp->tx_pause, - tp->pkt_cnt); - - if (ether_addr_equal(tp->peermac, priv->netdev->dev_addr)) { - if (tp->tx_pause) - priv->port_open = false; - else - priv->port_open = true; - } else if (is_multicast_ether_addr(tp->peermac)) { - mwifiex_update_ralist_tx_pause(priv, tp->peermac, tp->tx_pause); - } else { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { - sta_ptr->tx_pause = tp->tx_pause; - mwifiex_update_ralist_tx_pause(priv, tp->peermac, - tp->tx_pause); - } - } -} - -static void mwifiex_process_sta_tx_pause(struct mwifiex_private *priv, - struct mwifiex_ie_types_header *tlv) -{ - struct mwifiex_tx_pause_tlv *tp; - struct mwifiex_sta_node *sta_ptr; - int status; - unsigned long flags; - - tp = (void *)tlv; - mwifiex_dbg(priv->adapter, EVENT, - "sta tx_pause: %pM pause=%d, pkts=%d\n", - tp->peermac, tp->tx_pause, - tp->pkt_cnt); - - if (ether_addr_equal(tp->peermac, priv->cfg_bssid)) { - if (tp->tx_pause) - priv->port_open = false; - else - priv->port_open = true; - } else { - if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return; - - status = mwifiex_get_tdls_link_status(priv, tp->peermac); - if (mwifiex_is_tdls_link_setup(status)) { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - sta_ptr = mwifiex_get_sta_entry(priv, tp->peermac); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - if (sta_ptr && sta_ptr->tx_pause != tp->tx_pause) { - sta_ptr->tx_pause = tp->tx_pause; - mwifiex_update_ralist_tx_pause(priv, - tp->peermac, - tp->tx_pause); - } - } - } -} - -void mwifiex_process_multi_chan_event(struct mwifiex_private *priv, - struct sk_buff *event_skb) -{ - struct mwifiex_ie_types_multi_chan_info *chan_info; - struct mwifiex_ie_types_mc_group_info *grp_info; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_header *tlv; - u16 tlv_buf_left, tlv_type, tlv_len; - int intf_num, bss_type, bss_num, i; - struct mwifiex_private *intf_priv; - - tlv_buf_left = event_skb->len - sizeof(u32); - chan_info = (void *)event_skb->data + sizeof(u32); - - if (le16_to_cpu(chan_info->header.type) != TLV_TYPE_MULTI_CHAN_INFO || - tlv_buf_left < sizeof(struct mwifiex_ie_types_multi_chan_info)) { - mwifiex_dbg(adapter, ERROR, - "unknown TLV in chan_info event\n"); - return; - } - - adapter->usb_mc_status = le16_to_cpu(chan_info->status); - mwifiex_dbg(adapter, EVENT, "multi chan operation %s\n", - adapter->usb_mc_status ? "started" : "over"); - - tlv_buf_left -= sizeof(struct mwifiex_ie_types_multi_chan_info); - tlv = (struct mwifiex_ie_types_header *)chan_info->tlv_buffer; - - while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { - tlv_type = le16_to_cpu(tlv->type); - tlv_len = le16_to_cpu(tlv->len); - if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > - tlv_buf_left) { - mwifiex_dbg(adapter, ERROR, "wrong tlv: tlvLen=%d,\t" - "tlvBufLeft=%d\n", tlv_len, tlv_buf_left); - break; - } - if (tlv_type != TLV_TYPE_MC_GROUP_INFO) { - mwifiex_dbg(adapter, ERROR, "wrong tlv type: 0x%x\n", - tlv_type); - break; - } - - grp_info = (struct mwifiex_ie_types_mc_group_info *)tlv; - intf_num = grp_info->intf_num; - for (i = 0; i < intf_num; i++) { - bss_type = grp_info->bss_type_numlist[i] >> 4; - bss_num = grp_info->bss_type_numlist[i] & BSS_NUM_MASK; - intf_priv = mwifiex_get_priv_by_id(adapter, bss_num, - bss_type); - if (!intf_priv) { - mwifiex_dbg(adapter, ERROR, - "Invalid bss_type bss_num\t" - "in multi channel event\n"); - continue; - } - if (adapter->iface_type == MWIFIEX_USB) { - u8 ep; - - ep = grp_info->hid_num.usb_ep_num; - if (ep == MWIFIEX_USB_EP_DATA || - ep == MWIFIEX_USB_EP_DATA_CH2) - intf_priv->usb_port = ep; - } - } - - tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + - tlv_len; - tlv = (void *)((u8 *)tlv + tlv_len + - sizeof(struct mwifiex_ie_types_header)); - } - - if (adapter->iface_type == MWIFIEX_USB) { - adapter->tx_lock_flag = true; - adapter->usb_mc_setup = true; - mwifiex_multi_chan_resync(adapter); - } -} - -void mwifiex_process_tx_pause_event(struct mwifiex_private *priv, - struct sk_buff *event_skb) -{ - struct mwifiex_ie_types_header *tlv; - u16 tlv_type, tlv_len; - int tlv_buf_left; - - if (!priv->media_connected) { - mwifiex_dbg(priv->adapter, ERROR, - "tx_pause event while disconnected; bss_role=%d\n", - priv->bss_role); - return; - } - - tlv_buf_left = event_skb->len - sizeof(u32); - tlv = (void *)event_skb->data + sizeof(u32); - - while (tlv_buf_left >= (int)sizeof(struct mwifiex_ie_types_header)) { - tlv_type = le16_to_cpu(tlv->type); - tlv_len = le16_to_cpu(tlv->len); - if ((sizeof(struct mwifiex_ie_types_header) + tlv_len) > - tlv_buf_left) { - mwifiex_dbg(priv->adapter, ERROR, - "wrong tlv: tlvLen=%d, tlvBufLeft=%d\n", - tlv_len, tlv_buf_left); - break; - } - if (tlv_type == TLV_TYPE_TX_PAUSE) { - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) - mwifiex_process_sta_tx_pause(priv, tlv); - else - mwifiex_process_uap_tx_pause(priv, tlv); - } - - tlv_buf_left -= sizeof(struct mwifiex_ie_types_header) + - tlv_len; - tlv = (void *)((u8 *)tlv + tlv_len + - sizeof(struct mwifiex_ie_types_header)); - } - -} - -/* -* This function handles coex events generated by firmware -*/ -void mwifiex_bt_coex_wlan_param_update_event(struct mwifiex_private *priv, - struct sk_buff *event_skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_ie_types_header *tlv; - struct mwifiex_ie_types_btcoex_aggr_win_size *winsizetlv; - struct mwifiex_ie_types_btcoex_scan_time *scantlv; - s32 len = event_skb->len - sizeof(u32); - u8 *cur_ptr = event_skb->data + sizeof(u32); - u16 tlv_type, tlv_len; - - while (len >= sizeof(struct mwifiex_ie_types_header)) { - tlv = (struct mwifiex_ie_types_header *)cur_ptr; - tlv_len = le16_to_cpu(tlv->len); - tlv_type = le16_to_cpu(tlv->type); - - if ((tlv_len + sizeof(struct mwifiex_ie_types_header)) > len) - break; - switch (tlv_type) { - case TLV_BTCOEX_WL_AGGR_WINSIZE: - winsizetlv = - (struct mwifiex_ie_types_btcoex_aggr_win_size *)tlv; - adapter->coex_win_size = winsizetlv->coex_win_size; - adapter->coex_tx_win_size = - winsizetlv->tx_win_size; - adapter->coex_rx_win_size = - winsizetlv->rx_win_size; - mwifiex_coex_ampdu_rxwinsize(adapter); - mwifiex_update_ampdu_txwinsize(adapter); - break; - - case TLV_BTCOEX_WL_SCANTIME: - scantlv = - (struct mwifiex_ie_types_btcoex_scan_time *)tlv; - adapter->coex_scan = scantlv->coex_scan; - adapter->coex_min_scan_time = scantlv->min_scan_time; - adapter->coex_max_scan_time = scantlv->max_scan_time; - break; - - default: - break; - } - - len -= tlv_len + sizeof(struct mwifiex_ie_types_header); - cur_ptr += tlv_len + - sizeof(struct mwifiex_ie_types_header); - } - - dev_dbg(adapter->dev, "coex_scan=%d min_scan=%d coex_win=%d, tx_win=%d rx_win=%d\n", - adapter->coex_scan, adapter->coex_min_scan_time, - adapter->coex_win_size, adapter->coex_tx_win_size, - adapter->coex_rx_win_size); -} - -/* - * This function handles events generated by firmware. - * - * This is a generic function and handles all events. - * - * Event specific routines are called by this function based - * upon the generated event cause. - * - * For the following events, the function just forwards them to upper - * layers, optionally recording the change - - * - EVENT_LINK_SENSED - * - EVENT_MIC_ERR_UNICAST - * - EVENT_MIC_ERR_MULTICAST - * - EVENT_PORT_RELEASE - * - EVENT_RSSI_LOW - * - EVENT_SNR_LOW - * - EVENT_MAX_FAIL - * - EVENT_RSSI_HIGH - * - EVENT_SNR_HIGH - * - EVENT_DATA_RSSI_LOW - * - EVENT_DATA_SNR_LOW - * - EVENT_DATA_RSSI_HIGH - * - EVENT_DATA_SNR_HIGH - * - EVENT_LINK_QUALITY - * - EVENT_PRE_BEACON_LOST - * - EVENT_IBSS_COALESCED - * - EVENT_WEP_ICV_ERR - * - EVENT_BW_CHANGE - * - EVENT_HOSTWAKE_STAIE - * - * For the following events, no action is taken - - * - EVENT_MIB_CHANGED - * - EVENT_INIT_DONE - * - EVENT_DUMMY_HOST_WAKEUP_SIGNAL - * - * Rest of the supported events requires driver handling - - * - EVENT_DEAUTHENTICATED - * - EVENT_DISASSOCIATED - * - EVENT_LINK_LOST - * - EVENT_PS_SLEEP - * - EVENT_PS_AWAKE - * - EVENT_DEEP_SLEEP_AWAKE - * - EVENT_HS_ACT_REQ - * - EVENT_ADHOC_BCN_LOST - * - EVENT_BG_SCAN_REPORT - * - EVENT_WMM_STATUS_CHANGE - * - EVENT_ADDBA - * - EVENT_DELBA - * - EVENT_BA_STREAM_TIEMOUT - * - EVENT_AMSDU_AGGR_CTRL - */ -int mwifiex_process_sta_event(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret = 0; - u32 eventcause = adapter->event_cause; - u16 ctrl, reason_code; - - switch (eventcause) { - case EVENT_DUMMY_HOST_WAKEUP_SIGNAL: - mwifiex_dbg(adapter, ERROR, - "invalid EVENT: DUMMY_HOST_WAKEUP_SIGNAL, ignore it\n"); - break; - case EVENT_LINK_SENSED: - mwifiex_dbg(adapter, EVENT, "event: LINK_SENSED\n"); - if (!netif_carrier_ok(priv->netdev)) - netif_carrier_on(priv->netdev); - mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); - break; - - case EVENT_DEAUTHENTICATED: - mwifiex_dbg(adapter, EVENT, "event: Deauthenticated\n"); - if (priv->wps.session_enable) { - mwifiex_dbg(adapter, INFO, - "info: receive deauth event in wps session\n"); - break; - } - adapter->dbg.num_event_deauth++; - if (priv->media_connected) { - reason_code = - le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_reset_connect_state(priv, reason_code); - } - break; - - case EVENT_DISASSOCIATED: - mwifiex_dbg(adapter, EVENT, "event: Disassociated\n"); - if (priv->wps.session_enable) { - mwifiex_dbg(adapter, INFO, - "info: receive disassoc event in wps session\n"); - break; - } - adapter->dbg.num_event_disassoc++; - if (priv->media_connected) { - reason_code = - le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_reset_connect_state(priv, reason_code); - } - break; - - case EVENT_LINK_LOST: - mwifiex_dbg(adapter, EVENT, "event: Link lost\n"); - adapter->dbg.num_event_link_lost++; - if (priv->media_connected) { - reason_code = - le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_reset_connect_state(priv, reason_code); - } - break; - - case EVENT_PS_SLEEP: - mwifiex_dbg(adapter, EVENT, "info: EVENT: SLEEP\n"); - - adapter->ps_state = PS_STATE_PRE_SLEEP; - - mwifiex_check_ps_cond(adapter); - break; - - case EVENT_PS_AWAKE: - mwifiex_dbg(adapter, EVENT, "info: EVENT: AWAKE\n"); - if (!adapter->pps_uapsd_mode && priv->port_open && - priv->media_connected && adapter->sleep_period.period) { - adapter->pps_uapsd_mode = true; - mwifiex_dbg(adapter, EVENT, - "event: PPS/UAPSD mode activated\n"); - } - adapter->tx_lock_flag = false; - if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { - if (mwifiex_check_last_packet_indication(priv)) { - if (adapter->data_sent || - (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv))) { - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_card_req = false; - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - break; - } - if (!mwifiex_send_null_packet - (priv, - MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) - adapter->ps_state = - PS_STATE_SLEEP; - return 0; - } - } - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_card_req = false; - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - - break; - - case EVENT_DEEP_SLEEP_AWAKE: - adapter->if_ops.wakeup_complete(adapter); - mwifiex_dbg(adapter, EVENT, "event: DS_AWAKE\n"); - if (adapter->is_deep_sleep) - adapter->is_deep_sleep = false; - break; - - case EVENT_HS_ACT_REQ: - mwifiex_dbg(adapter, EVENT, "event: HS_ACT_REQ\n"); - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_HS_CFG_ENH, - 0, 0, NULL, false); - break; - - case EVENT_MIC_ERR_UNICAST: - mwifiex_dbg(adapter, EVENT, "event: UNICAST MIC ERROR\n"); - cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, - NL80211_KEYTYPE_PAIRWISE, - -1, NULL, GFP_KERNEL); - break; - - case EVENT_MIC_ERR_MULTICAST: - mwifiex_dbg(adapter, EVENT, "event: MULTICAST MIC ERROR\n"); - cfg80211_michael_mic_failure(priv->netdev, priv->cfg_bssid, - NL80211_KEYTYPE_GROUP, - -1, NULL, GFP_KERNEL); - break; - case EVENT_MIB_CHANGED: - case EVENT_INIT_DONE: - break; - - case EVENT_ADHOC_BCN_LOST: - mwifiex_dbg(adapter, EVENT, "event: ADHOC_BCN_LOST\n"); - priv->adhoc_is_link_sensed = false; - mwifiex_clean_txrx(priv); - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - break; - - case EVENT_BG_SCAN_REPORT: - mwifiex_dbg(adapter, EVENT, "event: BGS_REPORT\n"); - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_BG_SCAN_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL, false); - break; - - case EVENT_PORT_RELEASE: - mwifiex_dbg(adapter, EVENT, "event: PORT RELEASE\n"); - priv->port_open = true; - break; - - case EVENT_EXT_SCAN_REPORT: - mwifiex_dbg(adapter, EVENT, "event: EXT_SCAN Report\n"); - if (adapter->ext_scan) - ret = mwifiex_handle_event_ext_scan_report(priv, - adapter->event_skb->data); - - break; - - case EVENT_WMM_STATUS_CHANGE: - mwifiex_dbg(adapter, EVENT, "event: WMM status changed\n"); - ret = mwifiex_send_cmd(priv, HostCmd_CMD_WMM_GET_STATUS, - 0, 0, NULL, false); - break; - - case EVENT_RSSI_LOW: - cfg80211_cqm_rssi_notify(priv->netdev, - NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, - GFP_KERNEL); - mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, - HostCmd_ACT_GEN_GET, 0, NULL, false); - priv->subsc_evt_rssi_state = RSSI_LOW_RECVD; - mwifiex_dbg(adapter, EVENT, "event: Beacon RSSI_LOW\n"); - break; - case EVENT_SNR_LOW: - mwifiex_dbg(adapter, EVENT, "event: Beacon SNR_LOW\n"); - break; - case EVENT_MAX_FAIL: - mwifiex_dbg(adapter, EVENT, "event: MAX_FAIL\n"); - break; - case EVENT_RSSI_HIGH: - cfg80211_cqm_rssi_notify(priv->netdev, - NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, - GFP_KERNEL); - mwifiex_send_cmd(priv, HostCmd_CMD_RSSI_INFO, - HostCmd_ACT_GEN_GET, 0, NULL, false); - priv->subsc_evt_rssi_state = RSSI_HIGH_RECVD; - mwifiex_dbg(adapter, EVENT, "event: Beacon RSSI_HIGH\n"); - break; - case EVENT_SNR_HIGH: - mwifiex_dbg(adapter, EVENT, "event: Beacon SNR_HIGH\n"); - break; - case EVENT_DATA_RSSI_LOW: - mwifiex_dbg(adapter, EVENT, "event: Data RSSI_LOW\n"); - break; - case EVENT_DATA_SNR_LOW: - mwifiex_dbg(adapter, EVENT, "event: Data SNR_LOW\n"); - break; - case EVENT_DATA_RSSI_HIGH: - mwifiex_dbg(adapter, EVENT, "event: Data RSSI_HIGH\n"); - break; - case EVENT_DATA_SNR_HIGH: - mwifiex_dbg(adapter, EVENT, "event: Data SNR_HIGH\n"); - break; - case EVENT_LINK_QUALITY: - mwifiex_dbg(adapter, EVENT, "event: Link Quality\n"); - break; - case EVENT_PRE_BEACON_LOST: - mwifiex_dbg(adapter, EVENT, "event: Pre-Beacon Lost\n"); - break; - case EVENT_IBSS_COALESCED: - mwifiex_dbg(adapter, EVENT, "event: IBSS_COALESCED\n"); - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_IBSS_COALESCING_STATUS, - HostCmd_ACT_GEN_GET, 0, NULL, false); - break; - case EVENT_ADDBA: - mwifiex_dbg(adapter, EVENT, "event: ADDBA Request\n"); - mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, - HostCmd_ACT_GEN_SET, 0, - adapter->event_body, false); - break; - case EVENT_DELBA: - mwifiex_dbg(adapter, EVENT, "event: DELBA Request\n"); - mwifiex_11n_delete_ba_stream(priv, adapter->event_body); - break; - case EVENT_BA_STREAM_TIEMOUT: - mwifiex_dbg(adapter, EVENT, "event: BA Stream timeout\n"); - mwifiex_11n_ba_stream_timeout(priv, - (struct host_cmd_ds_11n_batimeout - *) - adapter->event_body); - break; - case EVENT_AMSDU_AGGR_CTRL: - ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_dbg(adapter, EVENT, - "event: AMSDU_AGGR_CTRL %d\n", ctrl); - - adapter->tx_buf_size = - min_t(u16, adapter->curr_tx_buf_size, ctrl); - mwifiex_dbg(adapter, EVENT, "event: tx_buf_size %d\n", - adapter->tx_buf_size); - break; - - case EVENT_WEP_ICV_ERR: - mwifiex_dbg(adapter, EVENT, "event: WEP ICV error\n"); - break; - - case EVENT_BW_CHANGE: - mwifiex_dbg(adapter, EVENT, "event: BW Change\n"); - break; - - case EVENT_HOSTWAKE_STAIE: - mwifiex_dbg(adapter, EVENT, - "event: HOSTWAKE_STAIE %d\n", eventcause); - break; - - case EVENT_REMAIN_ON_CHAN_EXPIRED: - mwifiex_dbg(adapter, EVENT, - "event: Remain on channel expired\n"); - cfg80211_remain_on_channel_expired(&priv->wdev, - priv->roc_cfg.cookie, - &priv->roc_cfg.chan, - GFP_ATOMIC); - - memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg)); - - break; - - case EVENT_CHANNEL_SWITCH_ANN: - mwifiex_dbg(adapter, EVENT, "event: Channel Switch Announcement\n"); - priv->csa_expire_time = - jiffies + msecs_to_jiffies(DFS_CHAN_MOVE_TIME); - priv->csa_chan = priv->curr_bss_params.bss_descriptor.channel; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_DEAUTHENTICATE, - HostCmd_ACT_GEN_SET, 0, - priv->curr_bss_params.bss_descriptor.mac_address, - false); - break; - - case EVENT_TDLS_GENERIC_EVENT: - ret = mwifiex_parse_tdls_event(priv, adapter->event_skb); - break; - - case EVENT_TX_DATA_PAUSE: - mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n"); - mwifiex_process_tx_pause_event(priv, adapter->event_skb); - break; - - case EVENT_MULTI_CHAN_INFO: - mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n"); - mwifiex_process_multi_chan_event(priv, adapter->event_skb); - break; - - case EVENT_TX_STATUS_REPORT: - mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n"); - mwifiex_parse_tx_status_event(priv, adapter->event_body); - break; - - case EVENT_CHANNEL_REPORT_RDY: - mwifiex_dbg(adapter, EVENT, "event: Channel Report\n"); - ret = mwifiex_11h_handle_chanrpt_ready(priv, - adapter->event_skb); - break; - case EVENT_RADAR_DETECTED: - mwifiex_dbg(adapter, EVENT, "event: Radar detected\n"); - ret = mwifiex_11h_handle_radar_detected(priv, - adapter->event_skb); - break; - case EVENT_BT_COEX_WLAN_PARA_CHANGE: - dev_dbg(adapter->dev, "EVENT: BT coex wlan param update\n"); - mwifiex_bt_coex_wlan_param_update_event(priv, - adapter->event_skb); - break; - default: - mwifiex_dbg(adapter, ERROR, "event: unknown event id: %#x\n", - eventcause); - break; - } - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c deleted file mode 100644 index a6c8a4f7bfe9..000000000000 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ /dev/null @@ -1,1421 +0,0 @@ -/* - * Marvell Wireless LAN device driver: functions for station ioctl - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" -#include "cfg80211.h" - -static int disconnect_on_suspend; -module_param(disconnect_on_suspend, int, 0644); - -/* - * Copies the multicast address list from device to driver. - * - * This function does not validate the destination memory for - * size, and the calling function must ensure enough memory is - * available. - */ -int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist, - struct net_device *dev) -{ - int i = 0; - struct netdev_hw_addr *ha; - - netdev_for_each_mc_addr(ha, dev) - memcpy(&mlist->mac_list[i++], ha->addr, ETH_ALEN); - - return i; -} - -/* - * Wait queue completion handler. - * - * This function waits on a cmd wait queue. It also cancels the pending - * request after waking up, in case of errors. - */ -int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_queued) -{ - int status; - - /* Wait for completion */ - status = wait_event_interruptible_timeout(adapter->cmd_wait_q.wait, - *(cmd_queued->condition), - (12 * HZ)); - if (status <= 0) { - if (status == 0) - status = -ETIMEDOUT; - mwifiex_dbg(adapter, ERROR, "cmd_wait_q terminated: %d\n", - status); - mwifiex_cancel_all_pending_cmd(adapter); - return status; - } - - status = adapter->cmd_wait_q.status; - adapter->cmd_wait_q.status = 0; - - return status; -} - -/* - * This function prepares the correct firmware command and - * issues it to set the multicast list. - * - * This function can be used to enable promiscuous mode, or enable all - * multicast packets, or to enable selective multicast. - */ -int mwifiex_request_set_multicast_list(struct mwifiex_private *priv, - struct mwifiex_multicast_list *mcast_list) -{ - int ret = 0; - u16 old_pkt_filter; - - old_pkt_filter = priv->curr_pkt_filter; - - if (mcast_list->mode == MWIFIEX_PROMISC_MODE) { - mwifiex_dbg(priv->adapter, INFO, - "info: Enable Promiscuous mode\n"); - priv->curr_pkt_filter |= HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; - priv->curr_pkt_filter &= - ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; - } else { - /* Multicast */ - priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_PROMISCUOUS_ENABLE; - if (mcast_list->mode == MWIFIEX_ALL_MULTI_MODE) { - mwifiex_dbg(priv->adapter, INFO, - "info: Enabling All Multicast!\n"); - priv->curr_pkt_filter |= - HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; - } else { - priv->curr_pkt_filter &= - ~HostCmd_ACT_MAC_ALL_MULTICAST_ENABLE; - mwifiex_dbg(priv->adapter, INFO, - "info: Set multicast list=%d\n", - mcast_list->num_multicast_addr); - /* Send multicast addresses to firmware */ - ret = mwifiex_send_cmd(priv, - HostCmd_CMD_MAC_MULTICAST_ADR, - HostCmd_ACT_GEN_SET, 0, - mcast_list, false); - } - } - mwifiex_dbg(priv->adapter, INFO, - "info: old_pkt_filter=%#x, curr_pkt_filter=%#x\n", - old_pkt_filter, priv->curr_pkt_filter); - if (old_pkt_filter != priv->curr_pkt_filter) { - ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, - 0, &priv->curr_pkt_filter, false); - } - - return ret; -} - -/* - * This function fills bss descriptor structure using provided - * information. - * beacon_ie buffer is allocated in this function. It is caller's - * responsibility to free the memory. - */ -int mwifiex_fill_new_bss_desc(struct mwifiex_private *priv, - struct cfg80211_bss *bss, - struct mwifiex_bssdescriptor *bss_desc) -{ - u8 *beacon_ie; - size_t beacon_ie_len; - struct mwifiex_bss_priv *bss_priv = (void *)bss->priv; - const struct cfg80211_bss_ies *ies; - - rcu_read_lock(); - ies = rcu_dereference(bss->ies); - beacon_ie = kmemdup(ies->data, ies->len, GFP_ATOMIC); - beacon_ie_len = ies->len; - bss_desc->timestamp = ies->tsf; - rcu_read_unlock(); - - if (!beacon_ie) { - mwifiex_dbg(priv->adapter, ERROR, - " failed to alloc beacon_ie\n"); - return -ENOMEM; - } - - memcpy(bss_desc->mac_address, bss->bssid, ETH_ALEN); - bss_desc->rssi = bss->signal; - /* The caller of this function will free beacon_ie */ - bss_desc->beacon_buf = beacon_ie; - bss_desc->beacon_buf_size = beacon_ie_len; - bss_desc->beacon_period = bss->beacon_interval; - bss_desc->cap_info_bitmap = bss->capability; - bss_desc->bss_band = bss_priv->band; - bss_desc->fw_tsf = bss_priv->fw_tsf; - if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_PRIVACY) { - mwifiex_dbg(priv->adapter, INFO, - "info: InterpretIE: AP WEP enabled\n"); - bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_8021X_WEP; - } else { - bss_desc->privacy = MWIFIEX_802_11_PRIV_FILTER_ACCEPT_ALL; - } - if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_IBSS) - bss_desc->bss_mode = NL80211_IFTYPE_ADHOC; - else - bss_desc->bss_mode = NL80211_IFTYPE_STATION; - - /* Disable 11ac by default. Enable it only where there - * exist VHT_CAP IE in AP beacon - */ - bss_desc->disable_11ac = true; - - if (bss_desc->cap_info_bitmap & WLAN_CAPABILITY_SPECTRUM_MGMT) - bss_desc->sensed_11h = true; - - return mwifiex_update_bss_desc_with_ie(priv->adapter, bss_desc); -} - -void mwifiex_dnld_txpwr_table(struct mwifiex_private *priv) -{ - if (priv->adapter->dt_node) { - char txpwr[] = {"marvell,00_txpwrlimit"}; - - memcpy(&txpwr[8], priv->adapter->country_code, 2); - mwifiex_dnld_dt_cfgdata(priv, priv->adapter->dt_node, txpwr); - } -} - -static int mwifiex_process_country_ie(struct mwifiex_private *priv, - struct cfg80211_bss *bss) -{ - const u8 *country_ie; - u8 country_ie_len; - struct mwifiex_802_11d_domain_reg *domain_info = - &priv->adapter->domain_reg; - - rcu_read_lock(); - country_ie = ieee80211_bss_get_ie(bss, WLAN_EID_COUNTRY); - if (!country_ie) { - rcu_read_unlock(); - return 0; - } - - country_ie_len = country_ie[1]; - if (country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) { - rcu_read_unlock(); - return 0; - } - - if (!strncmp(priv->adapter->country_code, &country_ie[2], 2)) { - rcu_read_unlock(); - mwifiex_dbg(priv->adapter, INFO, - "11D: skip setting domain info in FW\n"); - return 0; - } - memcpy(priv->adapter->country_code, &country_ie[2], 2); - - domain_info->country_code[0] = country_ie[2]; - domain_info->country_code[1] = country_ie[3]; - domain_info->country_code[2] = ' '; - - country_ie_len -= IEEE80211_COUNTRY_STRING_LEN; - - domain_info->no_of_triplet = - country_ie_len / sizeof(struct ieee80211_country_ie_triplet); - - memcpy((u8 *)domain_info->triplet, - &country_ie[2] + IEEE80211_COUNTRY_STRING_LEN, country_ie_len); - - rcu_read_unlock(); - - if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11D_DOMAIN_INFO, - HostCmd_ACT_GEN_SET, 0, NULL, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "11D: setting domain info in FW fail\n"); - return -1; - } - - mwifiex_dnld_txpwr_table(priv); - - return 0; -} - -/* - * In Ad-Hoc mode, the IBSS is created if not found in scan list. - * In both Ad-Hoc and infra mode, an deauthentication is performed - * first. - */ -int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, - struct cfg80211_ssid *req_ssid) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bssdescriptor *bss_desc = NULL; - - priv->scan_block = false; - - if (bss) { - mwifiex_process_country_ie(priv, bss); - - /* Allocate and fill new bss descriptor */ - bss_desc = kzalloc(sizeof(struct mwifiex_bssdescriptor), - GFP_KERNEL); - if (!bss_desc) - return -ENOMEM; - - ret = mwifiex_fill_new_bss_desc(priv, bss, bss_desc); - if (ret) - goto done; - } - - if (priv->bss_mode == NL80211_IFTYPE_STATION || - priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { - u8 config_bands; - - if (!bss_desc) - return -1; - - if (mwifiex_band_to_radio_type(bss_desc->bss_band) == - HostCmd_SCAN_RADIO_TYPE_BG) { - config_bands = BAND_B | BAND_G | BAND_GN; - } else { - config_bands = BAND_A | BAND_AN; - if (adapter->fw_bands & BAND_AAC) - config_bands |= BAND_AAC; - } - - if (!((config_bands | adapter->fw_bands) & ~adapter->fw_bands)) - adapter->config_bands = config_bands; - - ret = mwifiex_check_network_compatibility(priv, bss_desc); - if (ret) - goto done; - - if (mwifiex_11h_get_csa_closed_channel(priv) == - (u8)bss_desc->channel) { - mwifiex_dbg(adapter, ERROR, - "Attempt to reconnect on csa closed chan(%d)\n", - bss_desc->channel); - goto done; - } - - mwifiex_dbg(adapter, INFO, - "info: SSID found in scan list ...\t" - "associating...\n"); - - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - - /* Clear any past association response stored for - * application retrieval */ - priv->assoc_rsp_size = 0; - ret = mwifiex_associate(priv, bss_desc); - - /* If auth type is auto and association fails using open mode, - * try to connect using shared mode */ - if (ret == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG && - priv->sec_info.is_authtype_auto && - priv->sec_info.wep_enabled) { - priv->sec_info.authentication_mode = - NL80211_AUTHTYPE_SHARED_KEY; - ret = mwifiex_associate(priv, bss_desc); - } - - if (bss) - cfg80211_put_bss(priv->adapter->wiphy, bss); - } else { - /* Adhoc mode */ - /* If the requested SSID matches current SSID, return */ - if (bss_desc && bss_desc->ssid.ssid_len && - (!mwifiex_ssid_cmp(&priv->curr_bss_params.bss_descriptor. - ssid, &bss_desc->ssid))) { - ret = 0; - goto done; - } - - priv->adhoc_is_link_sensed = false; - - ret = mwifiex_check_network_compatibility(priv, bss_desc); - - mwifiex_stop_net_dev_queue(priv->netdev, adapter); - if (netif_carrier_ok(priv->netdev)) - netif_carrier_off(priv->netdev); - - if (!ret) { - mwifiex_dbg(adapter, INFO, - "info: network found in scan\t" - " list. Joining...\n"); - ret = mwifiex_adhoc_join(priv, bss_desc); - if (bss) - cfg80211_put_bss(priv->adapter->wiphy, bss); - } else { - mwifiex_dbg(adapter, INFO, - "info: Network not found in\t" - "the list, creating adhoc with ssid = %s\n", - req_ssid->ssid); - ret = mwifiex_adhoc_start(priv, req_ssid); - } - } - -done: - /* beacon_ie buffer was allocated in function - * mwifiex_fill_new_bss_desc(). Free it now. - */ - if (bss_desc) - kfree(bss_desc->beacon_buf); - kfree(bss_desc); - return ret; -} - -/* - * IOCTL request handler to set host sleep configuration. - * - * This function prepares the correct firmware command and - * issues it. - */ -int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, - int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg) - -{ - struct mwifiex_adapter *adapter = priv->adapter; - int status = 0; - u32 prev_cond = 0; - - if (!hs_cfg) - return -ENOMEM; - - switch (action) { - case HostCmd_ACT_GEN_SET: - if (adapter->pps_uapsd_mode) { - mwifiex_dbg(adapter, INFO, - "info: Host Sleep IOCTL\t" - "is blocked in UAPSD/PPS mode\n"); - status = -1; - break; - } - if (hs_cfg->is_invoke_hostcmd) { - if (hs_cfg->conditions == HS_CFG_CANCEL) { - if (!adapter->is_hs_configured) - /* Already cancelled */ - break; - /* Save previous condition */ - prev_cond = le32_to_cpu(adapter->hs_cfg - .conditions); - adapter->hs_cfg.conditions = - cpu_to_le32(hs_cfg->conditions); - } else if (hs_cfg->conditions) { - adapter->hs_cfg.conditions = - cpu_to_le32(hs_cfg->conditions); - adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; - if (hs_cfg->gap) - adapter->hs_cfg.gap = (u8)hs_cfg->gap; - } else if (adapter->hs_cfg.conditions == - cpu_to_le32(HS_CFG_CANCEL)) { - /* Return failure if no parameters for HS - enable */ - status = -1; - break; - } - - status = mwifiex_send_cmd(priv, - HostCmd_CMD_802_11_HS_CFG_ENH, - HostCmd_ACT_GEN_SET, 0, - &adapter->hs_cfg, - cmd_type == MWIFIEX_SYNC_CMD); - - if (hs_cfg->conditions == HS_CFG_CANCEL) - /* Restore previous condition */ - adapter->hs_cfg.conditions = - cpu_to_le32(prev_cond); - } else { - adapter->hs_cfg.conditions = - cpu_to_le32(hs_cfg->conditions); - adapter->hs_cfg.gpio = (u8)hs_cfg->gpio; - adapter->hs_cfg.gap = (u8)hs_cfg->gap; - } - break; - case HostCmd_ACT_GEN_GET: - hs_cfg->conditions = le32_to_cpu(adapter->hs_cfg.conditions); - hs_cfg->gpio = adapter->hs_cfg.gpio; - hs_cfg->gap = adapter->hs_cfg.gap; - break; - default: - status = -1; - break; - } - - return status; -} - -/* - * Sends IOCTL request to cancel the existing Host Sleep configuration. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int mwifiex_cancel_hs(struct mwifiex_private *priv, int cmd_type) -{ - struct mwifiex_ds_hs_cfg hscfg; - - hscfg.conditions = HS_CFG_CANCEL; - hscfg.is_invoke_hostcmd = true; - - return mwifiex_set_hs_params(priv, HostCmd_ACT_GEN_SET, - cmd_type, &hscfg); -} -EXPORT_SYMBOL_GPL(mwifiex_cancel_hs); - -/* - * Sends IOCTL request to cancel the existing Host Sleep configuration. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int mwifiex_enable_hs(struct mwifiex_adapter *adapter) -{ - struct mwifiex_ds_hs_cfg hscfg; - struct mwifiex_private *priv; - int i; - - if (disconnect_on_suspend) { - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv) - mwifiex_deauthenticate(priv, NULL); - } - } - - if (adapter->hs_activated) { - mwifiex_dbg(adapter, CMD, - "cmd: HS Already activated\n"); - return true; - } - - adapter->hs_activate_wait_q_woken = false; - - memset(&hscfg, 0, sizeof(struct mwifiex_ds_hs_cfg)); - hscfg.is_invoke_hostcmd = true; - - adapter->hs_enabling = true; - mwifiex_cancel_all_pending_cmd(adapter); - - if (mwifiex_set_hs_params(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_STA), - HostCmd_ACT_GEN_SET, MWIFIEX_SYNC_CMD, - &hscfg)) { - mwifiex_dbg(adapter, ERROR, - "IOCTL request HS enable failed\n"); - return false; - } - - if (wait_event_interruptible_timeout(adapter->hs_activate_wait_q, - adapter->hs_activate_wait_q_woken, - (10 * HZ)) <= 0) { - mwifiex_dbg(adapter, ERROR, - "hs_activate_wait_q terminated\n"); - return false; - } - - return true; -} -EXPORT_SYMBOL_GPL(mwifiex_enable_hs); - -/* - * IOCTL request handler to get BSS information. - * - * This function collates the information from different driver structures - * to send to the user. - */ -int mwifiex_get_bss_info(struct mwifiex_private *priv, - struct mwifiex_bss_info *info) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bssdescriptor *bss_desc; - - if (!info) - return -1; - - bss_desc = &priv->curr_bss_params.bss_descriptor; - - info->bss_mode = priv->bss_mode; - - memcpy(&info->ssid, &bss_desc->ssid, sizeof(struct cfg80211_ssid)); - - memcpy(&info->bssid, &bss_desc->mac_address, ETH_ALEN); - - info->bss_chan = bss_desc->channel; - - memcpy(info->country_code, adapter->country_code, - IEEE80211_COUNTRY_STRING_LEN); - - info->media_connected = priv->media_connected; - - info->max_power_level = priv->max_tx_power_level; - info->min_power_level = priv->min_tx_power_level; - - info->adhoc_state = priv->adhoc_state; - - info->bcn_nf_last = priv->bcn_nf_last; - - if (priv->sec_info.wep_enabled) - info->wep_status = true; - else - info->wep_status = false; - - info->is_hs_configured = adapter->is_hs_configured; - info->is_deep_sleep = adapter->is_deep_sleep; - - return 0; -} - -/* - * The function disables auto deep sleep mode. - */ -int mwifiex_disable_auto_ds(struct mwifiex_private *priv) -{ - struct mwifiex_ds_auto_ds auto_ds; - - auto_ds.auto_ds = DEEP_SLEEP_OFF; - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - DIS_AUTO_PS, BITMAP_AUTO_DS, &auto_ds, true); -} -EXPORT_SYMBOL_GPL(mwifiex_disable_auto_ds); - -/* - * Sends IOCTL request to get the data rate. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int mwifiex_drv_get_data_rate(struct mwifiex_private *priv, u32 *rate) -{ - int ret; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_TX_RATE_QUERY, - HostCmd_ACT_GEN_GET, 0, NULL, true); - - if (!ret) { - if (priv->is_data_rate_auto) - *rate = mwifiex_index_to_data_rate(priv, priv->tx_rate, - priv->tx_htinfo); - else - *rate = priv->data_rate; - } - - return ret; -} - -/* - * IOCTL request handler to set tx power configuration. - * - * This function prepares the correct firmware command and - * issues it. - * - * For non-auto power mode, all the following power groups are set - - * - Modulation class HR/DSSS - * - Modulation class OFDM - * - Modulation class HTBW20 - * - Modulation class HTBW40 - */ -int mwifiex_set_tx_power(struct mwifiex_private *priv, - struct mwifiex_power_cfg *power_cfg) -{ - int ret; - struct host_cmd_ds_txpwr_cfg *txp_cfg; - struct mwifiex_types_power_group *pg_tlv; - struct mwifiex_power_group *pg; - u8 *buf; - u16 dbm = 0; - - if (!power_cfg->is_power_auto) { - dbm = (u16) power_cfg->power_level; - if ((dbm < priv->min_tx_power_level) || - (dbm > priv->max_tx_power_level)) { - mwifiex_dbg(priv->adapter, ERROR, - "txpower value %d dBm\t" - "is out of range (%d dBm-%d dBm)\n", - dbm, priv->min_tx_power_level, - priv->max_tx_power_level); - return -1; - } - } - buf = kzalloc(MWIFIEX_SIZE_OF_CMD_BUFFER, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - txp_cfg = (struct host_cmd_ds_txpwr_cfg *) buf; - txp_cfg->action = cpu_to_le16(HostCmd_ACT_GEN_SET); - if (!power_cfg->is_power_auto) { - txp_cfg->mode = cpu_to_le32(1); - pg_tlv = (struct mwifiex_types_power_group *) - (buf + sizeof(struct host_cmd_ds_txpwr_cfg)); - pg_tlv->type = cpu_to_le16(TLV_TYPE_POWER_GROUP); - pg_tlv->length = - cpu_to_le16(4 * sizeof(struct mwifiex_power_group)); - pg = (struct mwifiex_power_group *) - (buf + sizeof(struct host_cmd_ds_txpwr_cfg) - + sizeof(struct mwifiex_types_power_group)); - /* 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 = (s8) dbm; - pg->power_max = (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 = (s8) dbm; - pg->power_max = (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 = (s8) dbm; - pg->power_max = (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 = (s8) dbm; - pg->power_max = (s8) dbm; - pg->ht_bandwidth = HT_BW_40; - } - ret = mwifiex_send_cmd(priv, HostCmd_CMD_TXPWR_CFG, - HostCmd_ACT_GEN_SET, 0, buf, true); - - kfree(buf); - return ret; -} - -/* - * IOCTL request handler to get power save mode. - * - * This function prepares the correct firmware command and - * issues it. - */ -int mwifiex_drv_set_power(struct mwifiex_private *priv, u32 *ps_mode) -{ - int ret; - struct mwifiex_adapter *adapter = priv->adapter; - u16 sub_cmd; - - if (*ps_mode) - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_PSP; - else - adapter->ps_mode = MWIFIEX_802_11_POWER_MODE_CAM; - sub_cmd = (*ps_mode) ? EN_AUTO_PS : DIS_AUTO_PS; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - sub_cmd, BITMAP_STA_PS, NULL, true); - if ((!ret) && (sub_cmd == DIS_AUTO_PS)) - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_PS_MODE_ENH, - GET_PS, 0, NULL, false); - - return ret; -} - -/* - * IOCTL request handler to set/reset WPA IE. - * - * The supplied WPA IE is treated as a opaque buffer. Only the first field - * is checked to determine WPA version. If buffer length is zero, the existing - * WPA IE is reset. - */ -static int mwifiex_set_wpa_ie_helper(struct mwifiex_private *priv, - u8 *ie_data_ptr, u16 ie_len) -{ - if (ie_len) { - if (ie_len > sizeof(priv->wpa_ie)) { - mwifiex_dbg(priv->adapter, ERROR, - "failed to copy WPA IE, too big\n"); - return -1; - } - memcpy(priv->wpa_ie, ie_data_ptr, ie_len); - priv->wpa_ie_len = (u8) ie_len; - mwifiex_dbg(priv->adapter, CMD, - "cmd: Set Wpa_ie_len=%d IE=%#x\n", - priv->wpa_ie_len, priv->wpa_ie[0]); - - if (priv->wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC) { - priv->sec_info.wpa_enabled = true; - } else if (priv->wpa_ie[0] == WLAN_EID_RSN) { - priv->sec_info.wpa2_enabled = true; - } else { - priv->sec_info.wpa_enabled = false; - priv->sec_info.wpa2_enabled = false; - } - } else { - memset(priv->wpa_ie, 0, sizeof(priv->wpa_ie)); - priv->wpa_ie_len = 0; - mwifiex_dbg(priv->adapter, INFO, - "info: reset wpa_ie_len=%d IE=%#x\n", - priv->wpa_ie_len, priv->wpa_ie[0]); - priv->sec_info.wpa_enabled = false; - priv->sec_info.wpa2_enabled = false; - } - - return 0; -} - -/* - * IOCTL request handler to set/reset WAPI IE. - * - * The supplied WAPI IE is treated as a opaque buffer. Only the first field - * is checked to internally enable WAPI. If buffer length is zero, the existing - * WAPI IE is reset. - */ -static int mwifiex_set_wapi_ie(struct mwifiex_private *priv, - u8 *ie_data_ptr, u16 ie_len) -{ - if (ie_len) { - if (ie_len > sizeof(priv->wapi_ie)) { - mwifiex_dbg(priv->adapter, ERROR, - "info: failed to copy WAPI IE, too big\n"); - return -1; - } - memcpy(priv->wapi_ie, ie_data_ptr, ie_len); - priv->wapi_ie_len = ie_len; - mwifiex_dbg(priv->adapter, CMD, - "cmd: Set wapi_ie_len=%d IE=%#x\n", - priv->wapi_ie_len, priv->wapi_ie[0]); - - if (priv->wapi_ie[0] == WLAN_EID_BSS_AC_ACCESS_DELAY) - priv->sec_info.wapi_enabled = true; - } else { - memset(priv->wapi_ie, 0, sizeof(priv->wapi_ie)); - priv->wapi_ie_len = ie_len; - mwifiex_dbg(priv->adapter, INFO, - "info: Reset wapi_ie_len=%d IE=%#x\n", - priv->wapi_ie_len, priv->wapi_ie[0]); - priv->sec_info.wapi_enabled = false; - } - return 0; -} - -/* - * IOCTL request handler to set/reset WPS IE. - * - * The supplied WPS IE is treated as a opaque buffer. Only the first field - * is checked to internally enable WPS. If buffer length is zero, the existing - * WPS IE is reset. - */ -static int mwifiex_set_wps_ie(struct mwifiex_private *priv, - u8 *ie_data_ptr, u16 ie_len) -{ - if (ie_len) { - if (ie_len > MWIFIEX_MAX_VSIE_LEN) { - mwifiex_dbg(priv->adapter, ERROR, - "info: failed to copy WPS IE, too big\n"); - return -1; - } - - priv->wps_ie = kzalloc(MWIFIEX_MAX_VSIE_LEN, GFP_KERNEL); - if (!priv->wps_ie) - return -ENOMEM; - - memcpy(priv->wps_ie, ie_data_ptr, ie_len); - priv->wps_ie_len = ie_len; - mwifiex_dbg(priv->adapter, CMD, - "cmd: Set wps_ie_len=%d IE=%#x\n", - priv->wps_ie_len, priv->wps_ie[0]); - } else { - kfree(priv->wps_ie); - priv->wps_ie_len = ie_len; - mwifiex_dbg(priv->adapter, INFO, - "info: Reset wps_ie_len=%d\n", priv->wps_ie_len); - } - return 0; -} - -/* - * IOCTL request handler to set WAPI key. - * - * This function prepares the correct firmware command and - * issues it. - */ -static int mwifiex_sec_ioctl_set_wapi_key(struct mwifiex_private *priv, - struct mwifiex_ds_encrypt_key *encrypt_key) -{ - - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, KEY_INFO_ENABLED, - encrypt_key, true); -} - -/* - * IOCTL request handler to set WEP network key. - * - * This function prepares the correct firmware command and - * issues it, after validation checks. - */ -static int mwifiex_sec_ioctl_set_wep_key(struct mwifiex_private *priv, - struct mwifiex_ds_encrypt_key *encrypt_key) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - struct mwifiex_wep_key *wep_key; - int index; - - if (priv->wep_key_curr_index >= NUM_WEP_KEYS) - priv->wep_key_curr_index = 0; - wep_key = &priv->wep_key[priv->wep_key_curr_index]; - index = encrypt_key->key_index; - if (encrypt_key->key_disable) { - priv->sec_info.wep_enabled = 0; - } else if (!encrypt_key->key_len) { - /* Copy the required key as the current key */ - wep_key = &priv->wep_key[index]; - if (!wep_key->key_length) { - mwifiex_dbg(adapter, ERROR, - "key not set, so cannot enable it\n"); - return -1; - } - - if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) { - memcpy(encrypt_key->key_material, - wep_key->key_material, wep_key->key_length); - encrypt_key->key_len = wep_key->key_length; - } - - priv->wep_key_curr_index = (u16) index; - priv->sec_info.wep_enabled = 1; - } else { - wep_key = &priv->wep_key[index]; - memset(wep_key, 0, sizeof(struct mwifiex_wep_key)); - /* Copy the key in the driver */ - memcpy(wep_key->key_material, - encrypt_key->key_material, - encrypt_key->key_len); - wep_key->key_index = index; - wep_key->key_length = encrypt_key->key_len; - priv->sec_info.wep_enabled = 1; - } - if (wep_key->key_length) { - void *enc_key; - - if (encrypt_key->key_disable) { - memset(&priv->wep_key[index], 0, - sizeof(struct mwifiex_wep_key)); - if (wep_key->key_length) - goto done; - } - - if (adapter->key_api_major_ver == KEY_API_VER_MAJOR_V2) - enc_key = encrypt_key; - else - enc_key = NULL; - - /* Send request to firmware */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, 0, enc_key, false); - if (ret) - return ret; - } - -done: - if (priv->sec_info.wep_enabled) - priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; - else - priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; - - ret = mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter, true); - - return ret; -} - -/* - * IOCTL request handler to set WPA key. - * - * This function prepares the correct firmware command and - * issues it, after validation checks. - * - * Current driver only supports key length of up to 32 bytes. - * - * This function can also be used to disable a currently set key. - */ -static int mwifiex_sec_ioctl_set_wpa_key(struct mwifiex_private *priv, - struct mwifiex_ds_encrypt_key *encrypt_key) -{ - int ret; - u8 remove_key = false; - struct host_cmd_ds_802_11_key_material *ibss_key; - - /* Current driver only supports key length of up to 32 bytes */ - if (encrypt_key->key_len > WLAN_MAX_KEY_LEN) { - mwifiex_dbg(priv->adapter, ERROR, - "key length too long\n"); - return -1; - } - - if (priv->bss_mode == NL80211_IFTYPE_ADHOC) { - /* - * IBSS/WPA-None uses only one key (Group) for both receiving - * and sending unicast and multicast packets. - */ - /* Send the key as PTK to firmware */ - encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, - KEY_INFO_ENABLED, encrypt_key, false); - if (ret) - return ret; - - ibss_key = &priv->aes_key; - memset(ibss_key, 0, - sizeof(struct host_cmd_ds_802_11_key_material)); - /* Copy the key in the driver */ - memcpy(ibss_key->key_param_set.key, encrypt_key->key_material, - encrypt_key->key_len); - memcpy(&ibss_key->key_param_set.key_len, &encrypt_key->key_len, - sizeof(ibss_key->key_param_set.key_len)); - ibss_key->key_param_set.key_type_id - = cpu_to_le16(KEY_TYPE_ID_TKIP); - ibss_key->key_param_set.key_info = cpu_to_le16(KEY_ENABLED); - - /* Send the key as GTK to firmware */ - encrypt_key->key_index = ~MWIFIEX_KEY_INDEX_UNICAST; - } - - if (!encrypt_key->key_index) - encrypt_key->key_index = MWIFIEX_KEY_INDEX_UNICAST; - - if (remove_key) - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, - !KEY_INFO_ENABLED, encrypt_key, true); - else - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_KEY_MATERIAL, - HostCmd_ACT_GEN_SET, - KEY_INFO_ENABLED, encrypt_key, true); - - return ret; -} - -/* - * IOCTL request handler to set/get network keys. - * - * This is a generic key handling function which supports WEP, WPA - * and WAPI. - */ -static int -mwifiex_sec_ioctl_encrypt_key(struct mwifiex_private *priv, - struct mwifiex_ds_encrypt_key *encrypt_key) -{ - int status; - - if (encrypt_key->is_wapi_key) - status = mwifiex_sec_ioctl_set_wapi_key(priv, encrypt_key); - else if (encrypt_key->key_len > WLAN_KEY_LEN_WEP104) - status = mwifiex_sec_ioctl_set_wpa_key(priv, encrypt_key); - else - status = mwifiex_sec_ioctl_set_wep_key(priv, encrypt_key); - return status; -} - -/* - * This function returns the driver version. - */ -int -mwifiex_drv_get_driver_version(struct mwifiex_adapter *adapter, char *version, - int max_len) -{ - union { - __le32 l; - u8 c[4]; - } ver; - char fw_ver[32]; - - ver.l = cpu_to_le32(adapter->fw_release_number); - sprintf(fw_ver, "%u.%u.%u.p%u", ver.c[2], ver.c[1], ver.c[0], ver.c[3]); - - snprintf(version, max_len, driver_version, fw_ver); - - mwifiex_dbg(adapter, MSG, "info: MWIFIEX VERSION: %s\n", version); - - return 0; -} - -/* - * Sends IOCTL request to set encoding parameters. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int mwifiex_set_encode(struct mwifiex_private *priv, struct key_params *kp, - const u8 *key, int key_len, u8 key_index, - const u8 *mac_addr, int disable) -{ - struct mwifiex_ds_encrypt_key encrypt_key; - - memset(&encrypt_key, 0, sizeof(struct mwifiex_ds_encrypt_key)); - encrypt_key.key_len = key_len; - encrypt_key.key_index = key_index; - - if (kp && kp->cipher == WLAN_CIPHER_SUITE_AES_CMAC) - encrypt_key.is_igtk_key = true; - - if (!disable) { - if (key_len) - memcpy(encrypt_key.key_material, key, key_len); - else - encrypt_key.is_current_wep_key = true; - - if (mac_addr) - memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); - if (kp && kp->seq && kp->seq_len) { - memcpy(encrypt_key.pn, kp->seq, kp->seq_len); - encrypt_key.pn_len = kp->seq_len; - encrypt_key.is_rx_seq_valid = true; - } - } else { - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) - return 0; - encrypt_key.key_disable = true; - if (mac_addr) - memcpy(encrypt_key.mac_addr, mac_addr, ETH_ALEN); - } - - return mwifiex_sec_ioctl_encrypt_key(priv, &encrypt_key); -} - -/* - * Sends IOCTL request to get extended version. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_get_ver_ext(struct mwifiex_private *priv) -{ - struct mwifiex_ver_ext ver_ext; - - memset(&ver_ext, 0, sizeof(struct host_cmd_ds_version_ext)); - if (mwifiex_send_cmd(priv, HostCmd_CMD_VERSION_EXT, - HostCmd_ACT_GEN_GET, 0, &ver_ext, true)) - return -1; - - return 0; -} - -int -mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, - struct ieee80211_channel *chan, - unsigned int duration) -{ - struct host_cmd_ds_remain_on_chan roc_cfg; - u8 sc; - - memset(&roc_cfg, 0, sizeof(roc_cfg)); - roc_cfg.action = cpu_to_le16(action); - if (action == HostCmd_ACT_GEN_SET) { - roc_cfg.band_cfg = chan->band; - sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT); - roc_cfg.band_cfg |= (sc << 2); - - roc_cfg.channel = - ieee80211_frequency_to_channel(chan->center_freq); - roc_cfg.duration = cpu_to_le32(duration); - } - if (mwifiex_send_cmd(priv, HostCmd_CMD_REMAIN_ON_CHAN, - action, 0, &roc_cfg, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "failed to remain on channel\n"); - return -1; - } - - return roc_cfg.status; -} - -/* - * Sends IOCTL request to get statistics information. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_get_stats_info(struct mwifiex_private *priv, - struct mwifiex_ds_get_stats *log) -{ - return mwifiex_send_cmd(priv, HostCmd_CMD_802_11_GET_LOG, - HostCmd_ACT_GEN_GET, 0, log, true); -} - -/* - * IOCTL request handler to read/write register. - * - * This function prepares the correct firmware command and - * issues it. - * - * Access to the following registers are supported - - * - MAC - * - BBP - * - RF - * - PMIC - * - CAU - */ -static int mwifiex_reg_mem_ioctl_reg_rw(struct mwifiex_private *priv, - struct mwifiex_ds_reg_rw *reg_rw, - u16 action) -{ - u16 cmd_no; - - switch (le32_to_cpu(reg_rw->type)) { - case MWIFIEX_REG_MAC: - cmd_no = HostCmd_CMD_MAC_REG_ACCESS; - break; - case MWIFIEX_REG_BBP: - cmd_no = HostCmd_CMD_BBP_REG_ACCESS; - break; - case MWIFIEX_REG_RF: - cmd_no = HostCmd_CMD_RF_REG_ACCESS; - break; - case MWIFIEX_REG_PMIC: - cmd_no = HostCmd_CMD_PMIC_REG_ACCESS; - break; - case MWIFIEX_REG_CAU: - cmd_no = HostCmd_CMD_CAU_REG_ACCESS; - break; - default: - return -1; - } - - return mwifiex_send_cmd(priv, cmd_no, action, 0, reg_rw, true); -} - -/* - * Sends IOCTL request to write to a register. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_reg_write(struct mwifiex_private *priv, u32 reg_type, - u32 reg_offset, u32 reg_value) -{ - struct mwifiex_ds_reg_rw reg_rw; - - reg_rw.type = cpu_to_le32(reg_type); - reg_rw.offset = cpu_to_le32(reg_offset); - reg_rw.value = cpu_to_le32(reg_value); - - return mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_SET); -} - -/* - * Sends IOCTL request to read from a register. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_reg_read(struct mwifiex_private *priv, u32 reg_type, - u32 reg_offset, u32 *value) -{ - int ret; - struct mwifiex_ds_reg_rw reg_rw; - - reg_rw.type = cpu_to_le32(reg_type); - reg_rw.offset = cpu_to_le32(reg_offset); - ret = mwifiex_reg_mem_ioctl_reg_rw(priv, ®_rw, HostCmd_ACT_GEN_GET); - - if (ret) - goto done; - - *value = le32_to_cpu(reg_rw.value); - -done: - return ret; -} - -/* - * Sends IOCTL request to read from EEPROM. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_eeprom_read(struct mwifiex_private *priv, u16 offset, u16 bytes, - u8 *value) -{ - int ret; - struct mwifiex_ds_read_eeprom rd_eeprom; - - rd_eeprom.offset = cpu_to_le16((u16) offset); - rd_eeprom.byte_count = cpu_to_le16((u16) bytes); - - /* Send request to firmware */ - ret = mwifiex_send_cmd(priv, HostCmd_CMD_802_11_EEPROM_ACCESS, - HostCmd_ACT_GEN_GET, 0, &rd_eeprom, true); - - if (!ret) - memcpy(value, rd_eeprom.value, MAX_EEPROM_DATA); - return ret; -} - -/* - * This function sets a generic IE. In addition to generic IE, it can - * also handle WPA, WPA2 and WAPI IEs. - */ -static int -mwifiex_set_gen_ie_helper(struct mwifiex_private *priv, u8 *ie_data_ptr, - u16 ie_len) -{ - int ret = 0; - struct ieee_types_vendor_header *pvendor_ie; - const u8 wpa_oui[] = { 0x00, 0x50, 0xf2, 0x01 }; - const u8 wps_oui[] = { 0x00, 0x50, 0xf2, 0x04 }; - - /* If the passed length is zero, reset the buffer */ - if (!ie_len) { - priv->gen_ie_buf_len = 0; - priv->wps.session_enable = false; - - return 0; - } else if (!ie_data_ptr) { - return -1; - } - pvendor_ie = (struct ieee_types_vendor_header *) ie_data_ptr; - /* Test to see if it is a WPA IE, if not, then it is a gen IE */ - if (((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && - (!memcmp(pvendor_ie->oui, wpa_oui, sizeof(wpa_oui)))) || - (pvendor_ie->element_id == WLAN_EID_RSN)) { - - /* IE is a WPA/WPA2 IE so call set_wpa function */ - ret = mwifiex_set_wpa_ie_helper(priv, ie_data_ptr, ie_len); - priv->wps.session_enable = false; - - return ret; - } else if (pvendor_ie->element_id == WLAN_EID_BSS_AC_ACCESS_DELAY) { - /* IE is a WAPI IE so call set_wapi function */ - ret = mwifiex_set_wapi_ie(priv, ie_data_ptr, ie_len); - - return ret; - } - /* - * 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 = (struct ieee_types_vendor_header *) ie_data_ptr; - if ((pvendor_ie->element_id == WLAN_EID_VENDOR_SPECIFIC) && - (!memcmp(pvendor_ie->oui, wps_oui, sizeof(wps_oui)))) { - priv->wps.session_enable = true; - mwifiex_dbg(priv->adapter, INFO, - "info: WPS Session Enabled.\n"); - ret = mwifiex_set_wps_ie(priv, ie_data_ptr, ie_len); - } - - /* Append the passed data to the end of the - genIeBuffer */ - memcpy(priv->gen_ie_buf + priv->gen_ie_buf_len, ie_data_ptr, - ie_len); - /* Increment the stored buffer length by the - size passed */ - priv->gen_ie_buf_len += ie_len; - } else { - /* Passed data does not fit in the remaining - buffer space */ - ret = -1; - } - - /* Return 0, or -1 for error case */ - return ret; -} - -/* - * IOCTL request handler to set/get generic IE. - * - * In addition to various generic IEs, this function can also be - * used to set the ARP filter. - */ -static int mwifiex_misc_ioctl_gen_ie(struct mwifiex_private *priv, - struct mwifiex_ds_misc_gen_ie *gen_ie, - u16 action) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - switch (gen_ie->type) { - case MWIFIEX_IE_TYPE_GEN_IE: - if (action == HostCmd_ACT_GEN_GET) { - gen_ie->len = priv->wpa_ie_len; - memcpy(gen_ie->ie_data, priv->wpa_ie, gen_ie->len); - } else { - mwifiex_set_gen_ie_helper(priv, gen_ie->ie_data, - (u16) gen_ie->len); - } - break; - case MWIFIEX_IE_TYPE_ARP_FILTER: - memset(adapter->arp_filter, 0, sizeof(adapter->arp_filter)); - if (gen_ie->len > ARP_FILTER_MAX_BUF_SIZE) { - adapter->arp_filter_size = 0; - mwifiex_dbg(adapter, ERROR, - "invalid ARP filter size\n"); - return -1; - } else { - memcpy(adapter->arp_filter, gen_ie->ie_data, - gen_ie->len); - adapter->arp_filter_size = gen_ie->len; - } - break; - default: - mwifiex_dbg(adapter, ERROR, "invalid IE type\n"); - return -1; - } - return 0; -} - -/* - * Sends IOCTL request to set a generic IE. - * - * This function allocates the IOCTL request buffer, fills it - * with requisite parameters and calls the IOCTL handler. - */ -int -mwifiex_set_gen_ie(struct mwifiex_private *priv, const u8 *ie, int ie_len) -{ - struct mwifiex_ds_misc_gen_ie gen_ie; - - if (ie_len > IEEE_MAX_IE_SIZE) - return -EFAULT; - - gen_ie.type = MWIFIEX_IE_TYPE_GEN_IE; - gen_ie.len = ie_len; - memcpy(gen_ie.ie_data, ie, ie_len); - if (mwifiex_misc_ioctl_gen_ie(priv, &gen_ie, HostCmd_ACT_GEN_SET)) - return -EFAULT; - - return 0; -} diff --git a/drivers/net/wireless/mwifiex/sta_rx.c b/drivers/net/wireless/mwifiex/sta_rx.c deleted file mode 100644 index d4d4cb1ce95b..000000000000 --- a/drivers/net/wireless/mwifiex/sta_rx.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station RX data handling - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include -#include -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "11n_aggr.h" -#include "11n_rxreorder.h" - -/* This function checks if a frame is IPv4 ARP or IPv6 Neighbour advertisement - * frame. If frame has both source and destination mac address as same, this - * function drops such gratuitous frames. - */ -static bool -mwifiex_discard_gratuitous_arp(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - const struct mwifiex_arp_eth_header *arp; - struct ethhdr *eth; - struct ipv6hdr *ipv6; - struct icmp6hdr *icmpv6; - - eth = (struct ethhdr *)skb->data; - switch (ntohs(eth->h_proto)) { - case ETH_P_ARP: - arp = (void *)(skb->data + sizeof(struct ethhdr)); - if (arp->hdr.ar_op == htons(ARPOP_REPLY) || - arp->hdr.ar_op == htons(ARPOP_REQUEST)) { - if (!memcmp(arp->ar_sip, arp->ar_tip, 4)) - return true; - } - break; - case ETH_P_IPV6: - ipv6 = (void *)(skb->data + sizeof(struct ethhdr)); - icmpv6 = (void *)(skb->data + sizeof(struct ethhdr) + - sizeof(struct ipv6hdr)); - if (NDISC_NEIGHBOUR_ADVERTISEMENT == icmpv6->icmp6_type) { - if (!memcmp(&ipv6->saddr, &ipv6->daddr, - sizeof(struct in6_addr))) - return true; - } - break; - default: - break; - } - - return false; -} - -/* - * This function processes the received packet and forwards it - * to kernel/upper layer. - * - * This function parses through the received packet and determines - * if it is a debug packet or normal packet. - * - * For non-debug packets, the function chops off unnecessary leading - * header bytes, reconstructs the packet as an ethernet frame or - * 802.2/llc/snap frame as required, and sends it to kernel/upper layer. - * - * The completion callback is called after processing in complete. - */ -int mwifiex_process_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - int ret; - struct rx_packet_hdr *rx_pkt_hdr; - struct rxpd *local_rx_pd; - int hdr_chop; - struct ethhdr *eth; - u16 rx_pkt_off, rx_pkt_len; - u8 *offset; - u8 adj_rx_rate = 0; - - local_rx_pd = (struct rxpd *) (skb->data); - - rx_pkt_off = le16_to_cpu(local_rx_pd->rx_pkt_offset); - rx_pkt_len = le16_to_cpu(local_rx_pd->rx_pkt_length); - rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_off; - - if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, - sizeof(bridge_tunnel_header))) || - (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, - sizeof(rfc1042_header)) && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { - /* - * 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. - */ - eth = (struct ethhdr *) - ((u8 *) &rx_pkt_hdr->eth803_hdr - + sizeof(rx_pkt_hdr->eth803_hdr) + - sizeof(rx_pkt_hdr->rfc1042_hdr) - - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) - - sizeof(rx_pkt_hdr->eth803_hdr.h_source) - - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); - - memcpy(eth->h_source, rx_pkt_hdr->eth803_hdr.h_source, - sizeof(eth->h_source)); - memcpy(eth->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, - sizeof(eth->h_dest)); - - /* Chop off the rxpd + the excess memory from the 802.2/llc/snap - header that was removed. */ - hdr_chop = (u8 *) eth - (u8 *) local_rx_pd; - } else { - /* Chop off the rxpd */ - hdr_chop = (u8 *) &rx_pkt_hdr->eth803_hdr - - (u8 *) local_rx_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 */ - skb_pull(skb, hdr_chop); - - if (priv->hs2_enabled && - mwifiex_discard_gratuitous_arp(priv, skb)) { - mwifiex_dbg(priv->adapter, INFO, "Bypassed Gratuitous ARP\n"); - dev_kfree_skb_any(skb); - return 0; - } - - if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - ntohs(rx_pkt_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) { - offset = (u8 *)local_rx_pd + rx_pkt_off; - mwifiex_process_tdls_action_frame(priv, offset, rx_pkt_len); - } - - priv->rxpd_rate = local_rx_pd->rx_rate; - - priv->rxpd_htinfo = local_rx_pd->ht_info; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA || - GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - adj_rx_rate = mwifiex_adjust_data_rate(priv, priv->rxpd_rate, - priv->rxpd_htinfo); - mwifiex_hist_data_add(priv, adj_rx_rate, local_rx_pd->snr, - local_rx_pd->nf); - } - - ret = mwifiex_recv_packet(priv, skb); - if (ret == -1) - mwifiex_dbg(priv->adapter, ERROR, - "recv packet failed\n"); - - return ret; -} - -/* - * This function processes the received buffer. - * - * The function looks into the RxPD and performs sanity tests on the - * received buffer to ensure its a valid packet, before processing it - * further. If the packet is determined to be aggregated, it is - * de-aggregated accordingly. Non-unicast packets are sent directly to - * the kernel/upper layers. Unicast packets are handed over to the - * Rx reordering routine if 11n is enabled. - * - * The completion callback is called after processing in complete. - */ -int mwifiex_process_sta_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret = 0; - struct rxpd *local_rx_pd; - struct rx_packet_hdr *rx_pkt_hdr; - u8 ta[ETH_ALEN]; - u16 rx_pkt_type, rx_pkt_offset, rx_pkt_length, seq_num; - struct mwifiex_sta_node *sta_ptr; - - local_rx_pd = (struct rxpd *) (skb->data); - rx_pkt_type = le16_to_cpu(local_rx_pd->rx_pkt_type); - rx_pkt_offset = le16_to_cpu(local_rx_pd->rx_pkt_offset); - rx_pkt_length = le16_to_cpu(local_rx_pd->rx_pkt_length); - seq_num = le16_to_cpu(local_rx_pd->seq_num); - - rx_pkt_hdr = (void *)local_rx_pd + rx_pkt_offset; - - if ((rx_pkt_offset + rx_pkt_length) > (u16) skb->len) { - mwifiex_dbg(adapter, ERROR, - "wrong rx packet: len=%d, rx_pkt_offset=%d, rx_pkt_length=%d\n", - skb->len, rx_pkt_offset, rx_pkt_length); - priv->stats.rx_dropped++; - dev_kfree_skb_any(skb); - return ret; - } - - if (rx_pkt_type == PKT_TYPE_MGMT) { - ret = mwifiex_process_mgmt_packet(priv, skb); - if (ret) - mwifiex_dbg(adapter, ERROR, "Rx of mgmt packet failed"); - dev_kfree_skb_any(skb); - return ret; - } - - /* - * 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) && - !(ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - !(local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET))) || - !ether_addr_equal_unaligned(priv->curr_addr, rx_pkt_hdr->eth803_hdr.h_dest)) { - mwifiex_process_rx_packet(priv, skb); - return ret; - } - - if (mwifiex_queuing_ra_based(priv) || - (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET)) { - memcpy(ta, rx_pkt_hdr->eth803_hdr.h_source, ETH_ALEN); - if (local_rx_pd->flags & MWIFIEX_RXPD_FLAGS_TDLS_PACKET && - local_rx_pd->priority < MAX_NUM_TID) { - sta_ptr = mwifiex_get_sta_entry(priv, ta); - if (sta_ptr) - sta_ptr->rx_seq[local_rx_pd->priority] = - le16_to_cpu(local_rx_pd->seq_num); - mwifiex_auto_tdls_update_peer_signal(priv, ta, - local_rx_pd->snr, - local_rx_pd->nf); - } - } else { - if (rx_pkt_type != PKT_TYPE_BAR) - priv->rx_seq[local_rx_pd->priority] = seq_num; - memcpy(ta, priv->curr_bss_params.bss_descriptor.mac_address, - ETH_ALEN); - } - - /* Reorder and send to OS */ - ret = mwifiex_11n_rx_reorder_pkt(priv, seq_num, local_rx_pd->priority, - ta, (u8) rx_pkt_type, skb); - - if (ret || (rx_pkt_type == PKT_TYPE_BAR)) - dev_kfree_skb_any(skb); - - if (ret) - priv->stats.rx_dropped++; - - return ret; -} diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c deleted file mode 100644 index f6683ea6bd5d..000000000000 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Marvell Wireless LAN device driver: station TX data handling - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" - -/* - * This function fills the TxPD for tx packets. - * - * The Tx buffer received by this function should already have the - * header space allocated for TxPD. - * - * This function inserts the TxPD in between interface header and actual - * data and adjusts the buffer pointers accordingly. - * - * The following TxPD fields are set by this function, as required - - * - BSS number - * - Tx packet length and offset - * - Priority - * - Packet delay - * - Priority specific Tx control - * - Flags - */ -void *mwifiex_process_sta_txpd(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct txpd *local_tx_pd; - struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - unsigned int pad; - u16 pkt_type, pkt_offset; - int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : - INTF_HEADER_LEN; - - if (!skb->len) { - mwifiex_dbg(adapter, ERROR, - "Tx: bad packet length: %d\n", skb->len); - tx_info->status_code = -1; - return skb->data; - } - - BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); - - pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - - pad = ((void *)skb->data - (sizeof(*local_tx_pd) + hroom)- - NULL) & (MWIFIEX_DMA_ALIGN_SZ - 1); - skb_push(skb, sizeof(*local_tx_pd) + pad); - - local_tx_pd = (struct txpd *) skb->data; - memset(local_tx_pd, 0, sizeof(struct txpd)); - local_tx_pd->bss_num = priv->bss_num; - local_tx_pd->bss_type = priv->bss_type; - local_tx_pd->tx_pkt_length = cpu_to_le16((u16)(skb->len - - (sizeof(struct txpd) + - pad))); - - local_tx_pd->priority = (u8) skb->priority; - local_tx_pd->pkt_delay_2ms = - mwifiex_wmm_compute_drv_pkt_delay(priv, skb); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS || - tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) { - local_tx_pd->tx_token_id = tx_info->ack_frame_id; - local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS; - } - - if (local_tx_pd->priority < - ARRAY_SIZE(priv->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 - */ - local_tx_pd->tx_control = - cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[local_tx_pd-> - priority]); - - if (adapter->pps_uapsd_mode) { - if (mwifiex_check_last_packet_indication(priv)) { - adapter->tx_lock_flag = true; - local_tx_pd->flags = - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET; - } - } - - if (tx_info->flags & MWIFIEX_BUF_FLAG_TDLS_PKT) - local_tx_pd->flags |= MWIFIEX_TXPD_FLAGS_TDLS_PACKET; - - /* Offset of actual data */ - pkt_offset = sizeof(struct txpd) + pad; - if (pkt_type == PKT_TYPE_MGMT) { - /* Set the packet type and add header for management frame */ - local_tx_pd->tx_pkt_type = cpu_to_le16(pkt_type); - pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; - } - - local_tx_pd->tx_pkt_offset = cpu_to_le16(pkt_offset); - - /* make space for INTF_HEADER_LEN */ - skb_push(skb, hroom); - - if (!local_tx_pd->tx_control) - /* TxCtrl set by user or default */ - local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); - - return skb->data; -} - -/* - * This function tells firmware to send a NULL data packet. - * - * The function creates a NULL data packet with TxPD and sends to the - * firmware for transmission, with highest priority setting. - */ -int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct txpd *local_tx_pd; - struct mwifiex_tx_param tx_param; -/* sizeof(struct txpd) + Interface specific header */ -#define NULL_PACKET_HDR 64 - u32 data_len = NULL_PACKET_HDR; - struct sk_buff *skb; - int ret; - struct mwifiex_txinfo *tx_info = NULL; - - if (adapter->surprise_removed) - return -1; - - if (!priv->media_connected) - return -1; - - if (adapter->data_sent) - return -1; - - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv)) - return -1; - - skb = dev_alloc_skb(data_len); - if (!skb) - return -1; - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->pkt_len = data_len - (sizeof(struct txpd) + INTF_HEADER_LEN); - skb_reserve(skb, sizeof(struct txpd) + INTF_HEADER_LEN); - skb_push(skb, sizeof(struct txpd)); - - local_tx_pd = (struct txpd *) skb->data; - local_tx_pd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); - local_tx_pd->flags = flags; - local_tx_pd->priority = WMM_HIGHEST_PRIORITY; - local_tx_pd->tx_pkt_offset = cpu_to_le16(sizeof(struct txpd)); - local_tx_pd->bss_num = priv->bss_num; - local_tx_pd->bss_type = priv->bss_type; - - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, - skb, NULL); - } else { - skb_push(skb, INTF_HEADER_LEN); - tx_param.next_pkt_len = 0; - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, - skb, &tx_param); - } - switch (ret) { - case -EBUSY: - dev_kfree_skb_any(skb); - mwifiex_dbg(adapter, ERROR, - "%s: host_to_card failed: ret=%d\n", - __func__, ret); - adapter->dbg.num_tx_host_to_card_failure++; - break; - case -1: - dev_kfree_skb_any(skb); - mwifiex_dbg(adapter, ERROR, - "%s: host_to_card failed: ret=%d\n", - __func__, ret); - adapter->dbg.num_tx_host_to_card_failure++; - break; - case 0: - dev_kfree_skb_any(skb); - mwifiex_dbg(adapter, DATA, - "data: %s: host_to_card succeeded\n", - __func__); - adapter->tx_lock_flag = true; - break; - case -EINPROGRESS: - adapter->tx_lock_flag = true; - break; - default: - break; - } - - return ret; -} - -/* - * This function checks if we need to send last packet indication. - */ -u8 -mwifiex_check_last_packet_indication(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u8 ret = false; - - if (!adapter->sleep_period.period) - return ret; - if (mwifiex_wmm_lists_empty(adapter)) - ret = true; - - if (ret && !adapter->cmd_sent && !adapter->curr_cmd && - !is_command_pending(adapter)) { - adapter->delay_null_pkt = false; - ret = true; - } else { - ret = false; - adapter->delay_null_pkt = true; - } - return ret; -} diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c deleted file mode 100644 index 9275f9c3f869..000000000000 --- a/drivers/net/wireless/mwifiex/tdls.c +++ /dev/null @@ -1,1500 +0,0 @@ -/* Marvell Wireless LAN device driver: TDLS handling - * - * Copyright (C) 2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available on the worldwide web at - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, 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 "main.h" -#include "wmm.h" -#include "11n.h" -#include "11n_rxreorder.h" -#include "11ac.h" - -#define TDLS_REQ_FIX_LEN 6 -#define TDLS_RESP_FIX_LEN 8 -#define TDLS_CONFIRM_FIX_LEN 6 -#define MWIFIEX_TDLS_WMM_INFO_SIZE 7 - -static void mwifiex_restore_tdls_packets(struct mwifiex_private *priv, - const u8 *mac, u8 status) -{ - struct mwifiex_ra_list_tbl *ra_list; - struct list_head *tid_list; - struct sk_buff *skb, *tmp; - struct mwifiex_txinfo *tx_info; - unsigned long flags; - u32 tid; - u8 tid_down; - - mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) { - if (!ether_addr_equal(mac, skb->data)) - continue; - - __skb_unlink(skb, &priv->tdls_txq); - tx_info = MWIFIEX_SKB_TXCB(skb); - tid = skb->priority; - tid_down = mwifiex_wmm_downgrade_tid(priv, tid); - - if (mwifiex_is_tdls_link_setup(status)) { - ra_list = mwifiex_wmm_get_queue_raptr(priv, tid, mac); - ra_list->tdls_link = true; - tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; - } else { - tid_list = &priv->wmm.tid_tbl_ptr[tid_down].ra_list; - if (!list_empty(tid_list)) - ra_list = list_first_entry(tid_list, - struct mwifiex_ra_list_tbl, list); - else - ra_list = NULL; - tx_info->flags &= ~MWIFIEX_BUF_FLAG_TDLS_PKT; - } - - if (!ra_list) { - mwifiex_write_data_complete(priv->adapter, skb, 0, -1); - continue; - } - - skb_queue_tail(&ra_list->skb_head, skb); - - ra_list->ba_pkt_count++; - ra_list->total_pkt_count++; - - if (atomic_read(&priv->wmm.highest_queued_prio) < - tos_to_tid_inv[tid_down]) - atomic_set(&priv->wmm.highest_queued_prio, - tos_to_tid_inv[tid_down]); - - atomic_inc(&priv->wmm.tx_pkts_queued); - } - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - return; -} - -static void mwifiex_hold_tdls_packets(struct mwifiex_private *priv, - const u8 *mac) -{ - struct mwifiex_ra_list_tbl *ra_list; - struct list_head *ra_list_head; - struct sk_buff *skb, *tmp; - unsigned long flags; - int i; - - mwifiex_dbg(priv->adapter, DATA, "%s: %pM\n", __func__, mac); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; i++) { - if (!list_empty(&priv->wmm.tid_tbl_ptr[i].ra_list)) { - ra_list_head = &priv->wmm.tid_tbl_ptr[i].ra_list; - list_for_each_entry(ra_list, ra_list_head, list) { - skb_queue_walk_safe(&ra_list->skb_head, skb, - tmp) { - if (!ether_addr_equal(mac, skb->data)) - continue; - __skb_unlink(skb, &ra_list->skb_head); - atomic_dec(&priv->wmm.tx_pkts_queued); - ra_list->total_pkt_count--; - skb_queue_tail(&priv->tdls_txq, skb); - } - } - } - } - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - return; -} - -/* This function appends rate TLV to scan config command. */ -static int -mwifiex_tdls_append_rates_ie(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - u8 rates[MWIFIEX_SUPPORTED_RATES], *pos; - u16 rates_size, supp_rates_size, ext_rates_size; - - memset(rates, 0, sizeof(rates)); - rates_size = mwifiex_get_supported_rates(priv, rates); - - supp_rates_size = min_t(u16, rates_size, MWIFIEX_TDLS_SUPPORTED_RATES); - - if (skb_tailroom(skb) < rates_size + 4) { - mwifiex_dbg(priv->adapter, ERROR, - "Insuffient space while adding rates\n"); - return -ENOMEM; - } - - pos = skb_put(skb, supp_rates_size + 2); - *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = supp_rates_size; - memcpy(pos, rates, supp_rates_size); - - if (rates_size > MWIFIEX_TDLS_SUPPORTED_RATES) { - ext_rates_size = rates_size - MWIFIEX_TDLS_SUPPORTED_RATES; - pos = skb_put(skb, ext_rates_size + 2); - *pos++ = WLAN_EID_EXT_SUPP_RATES; - *pos++ = ext_rates_size; - memcpy(pos, rates + MWIFIEX_TDLS_SUPPORTED_RATES, - ext_rates_size); - } - - return 0; -} - -static void mwifiex_tdls_add_aid(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct ieee_types_assoc_rsp *assoc_rsp; - u8 *pos; - - assoc_rsp = (struct ieee_types_assoc_rsp *)&priv->assoc_rsp_buf; - pos = (void *)skb_put(skb, 4); - *pos++ = WLAN_EID_AID; - *pos++ = 2; - memcpy(pos, &assoc_rsp->a_id, sizeof(assoc_rsp->a_id)); - - return; -} - -static int mwifiex_tdls_add_vht_capab(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct ieee80211_vht_cap vht_cap; - u8 *pos; - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_cap) + 2); - *pos++ = WLAN_EID_VHT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_vht_cap); - - memset(&vht_cap, 0, sizeof(struct ieee80211_vht_cap)); - - mwifiex_fill_vht_cap_tlv(priv, &vht_cap, priv->curr_bss_params.band); - memcpy(pos, &vht_cap, sizeof(vht_cap)); - - return 0; -} - -static int -mwifiex_tdls_add_ht_oper(struct mwifiex_private *priv, const u8 *mac, - u8 vht_enabled, struct sk_buff *skb) -{ - struct ieee80211_ht_operation *ht_oper; - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_bssdescriptor *bss_desc = - &priv->curr_bss_params.bss_descriptor; - u8 *pos; - - sta_ptr = mwifiex_get_sta_entry(priv, mac); - if (unlikely(!sta_ptr)) { - mwifiex_dbg(priv->adapter, ERROR, - "TDLS peer station not found in list\n"); - return -1; - } - - if (!(le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info))) { - mwifiex_dbg(priv->adapter, WARN, - "TDLS peer doesn't support ht capabilities\n"); - return 0; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_operation) + 2); - *pos++ = WLAN_EID_HT_OPERATION; - *pos++ = sizeof(struct ieee80211_ht_operation); - ht_oper = (void *)pos; - - ht_oper->primary_chan = bss_desc->channel; - - /* follow AP's channel bandwidth */ - if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) && - bss_desc->bcn_ht_cap && - ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param)) - ht_oper->ht_param = bss_desc->bcn_ht_oper->ht_param; - - if (vht_enabled) { - ht_oper->ht_param = - mwifiex_get_sec_chan_offset(bss_desc->channel); - ht_oper->ht_param |= BIT(2); - } - - memcpy(&sta_ptr->tdls_cap.ht_oper, ht_oper, - sizeof(struct ieee80211_ht_operation)); - - return 0; -} - -static int mwifiex_tdls_add_vht_oper(struct mwifiex_private *priv, - const u8 *mac, struct sk_buff *skb) -{ - struct mwifiex_bssdescriptor *bss_desc; - struct ieee80211_vht_operation *vht_oper; - struct ieee80211_vht_cap *vht_cap, *ap_vht_cap = NULL; - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_adapter *adapter = priv->adapter; - u8 supp_chwd_set, peer_supp_chwd_set; - u8 *pos, ap_supp_chwd_set, chan_bw; - u16 mcs_map_user, mcs_map_resp, mcs_map_result; - u16 mcs_user, mcs_resp, nss; - u32 usr_vht_cap_info; - - bss_desc = &priv->curr_bss_params.bss_descriptor; - - sta_ptr = mwifiex_get_sta_entry(priv, mac); - if (unlikely(!sta_ptr)) { - mwifiex_dbg(adapter, ERROR, - "TDLS peer station not found in list\n"); - return -1; - } - - if (!(le32_to_cpu(sta_ptr->tdls_cap.vhtcap.vht_cap_info))) { - mwifiex_dbg(adapter, WARN, - "TDLS peer doesn't support vht capabilities\n"); - return 0; - } - - if (!mwifiex_is_bss_in_11ac_mode(priv)) { - if (sta_ptr->tdls_cap.extcap.ext_capab[7] & - WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { - mwifiex_dbg(adapter, WARN, - "TDLS peer doesn't support wider bandwidth\n"); - return 0; - } - } else { - ap_vht_cap = bss_desc->bcn_vht_cap; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_vht_operation) + 2); - *pos++ = WLAN_EID_VHT_OPERATION; - *pos++ = sizeof(struct ieee80211_vht_operation); - vht_oper = (struct ieee80211_vht_operation *)pos; - - if (bss_desc->bss_band & BAND_A) - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_a; - else - usr_vht_cap_info = adapter->usr_dot_11ac_dev_cap_bg; - - /* find the minmum bandwith between AP/TDLS peers */ - vht_cap = &sta_ptr->tdls_cap.vhtcap; - supp_chwd_set = GET_VHTCAP_CHWDSET(usr_vht_cap_info); - peer_supp_chwd_set = - GET_VHTCAP_CHWDSET(le32_to_cpu(vht_cap->vht_cap_info)); - supp_chwd_set = min_t(u8, supp_chwd_set, peer_supp_chwd_set); - - /* We need check AP's bandwidth when TDLS_WIDER_BANDWIDTH is off */ - - if (ap_vht_cap && sta_ptr->tdls_cap.extcap.ext_capab[7] & - WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED) { - ap_supp_chwd_set = - GET_VHTCAP_CHWDSET(le32_to_cpu(ap_vht_cap->vht_cap_info)); - supp_chwd_set = min_t(u8, supp_chwd_set, ap_supp_chwd_set); - } - - switch (supp_chwd_set) { - case IEEE80211_VHT_CHANWIDTH_80MHZ: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80MHZ; - break; - case IEEE80211_VHT_CHANWIDTH_160MHZ: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_160MHZ; - break; - case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_80P80MHZ; - break; - default: - vht_oper->chan_width = IEEE80211_VHT_CHANWIDTH_USE_HT; - break; - } - - mcs_map_user = GET_DEVRXMCSMAP(adapter->usr_dot_11ac_mcs_support); - mcs_map_resp = le16_to_cpu(vht_cap->supp_mcs.rx_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 ((mcs_user == IEEE80211_VHT_MCS_NOT_SUPPORTED) || - (mcs_resp == IEEE80211_VHT_MCS_NOT_SUPPORTED)) - SET_VHTNSSMCS(mcs_map_result, nss, - IEEE80211_VHT_MCS_NOT_SUPPORTED); - else - SET_VHTNSSMCS(mcs_map_result, nss, - min_t(u16, mcs_user, mcs_resp)); - } - - vht_oper->basic_mcs_set = cpu_to_le16(mcs_map_result); - - switch (vht_oper->chan_width) { - case IEEE80211_VHT_CHANWIDTH_80MHZ: - chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; - break; - case IEEE80211_VHT_CHANWIDTH_160MHZ: - chan_bw = IEEE80211_VHT_CHANWIDTH_160MHZ; - break; - case IEEE80211_VHT_CHANWIDTH_80P80MHZ: - chan_bw = IEEE80211_VHT_CHANWIDTH_80MHZ; - break; - default: - chan_bw = IEEE80211_VHT_CHANWIDTH_USE_HT; - break; - } - vht_oper->center_freq_seg1_idx = - mwifiex_get_center_freq_index(priv, BAND_AAC, - bss_desc->channel, - chan_bw); - - return 0; -} - -static void mwifiex_tdls_add_ext_capab(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct ieee_types_extcap *extcap; - - extcap = (void *)skb_put(skb, sizeof(struct ieee_types_extcap)); - extcap->ieee_hdr.element_id = WLAN_EID_EXT_CAPABILITY; - extcap->ieee_hdr.len = 8; - memset(extcap->ext_capab, 0, 8); - extcap->ext_capab[4] |= WLAN_EXT_CAPA5_TDLS_ENABLED; - extcap->ext_capab[3] |= WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH; - - if (priv->adapter->is_hw_11ac_capable) - extcap->ext_capab[7] |= WLAN_EXT_CAPA8_TDLS_WIDE_BW_ENABLED; -} - -static void mwifiex_tdls_add_qos_capab(struct sk_buff *skb) -{ - u8 *pos = (void *)skb_put(skb, 3); - - *pos++ = WLAN_EID_QOS_CAPA; - *pos++ = 1; - *pos++ = MWIFIEX_TDLS_DEF_QOS_CAPAB; -} - -static void -mwifiex_tdls_add_wmm_param_ie(struct mwifiex_private *priv, struct sk_buff *skb) -{ - struct ieee80211_wmm_param_ie *wmm; - u8 ac_vi[] = {0x42, 0x43, 0x5e, 0x00}; - u8 ac_vo[] = {0x62, 0x32, 0x2f, 0x00}; - u8 ac_be[] = {0x03, 0xa4, 0x00, 0x00}; - u8 ac_bk[] = {0x27, 0xa4, 0x00, 0x00}; - - wmm = (void *)skb_put(skb, sizeof(*wmm)); - memset(wmm, 0, sizeof(*wmm)); - - wmm->element_id = WLAN_EID_VENDOR_SPECIFIC; - wmm->len = sizeof(*wmm) - 2; - wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */ - wmm->oui[1] = 0x50; - wmm->oui[2] = 0xf2; - wmm->oui_type = 2; /* WME */ - wmm->oui_subtype = 1; /* WME param */ - wmm->version = 1; /* WME ver */ - wmm->qos_info = 0; /* U-APSD not in use */ - - /* use default WMM AC parameters for TDLS link*/ - memcpy(&wmm->ac[0], ac_be, sizeof(ac_be)); - memcpy(&wmm->ac[1], ac_bk, sizeof(ac_bk)); - memcpy(&wmm->ac[2], ac_vi, sizeof(ac_vi)); - memcpy(&wmm->ac[3], ac_vo, sizeof(ac_vo)); -} - -static void -mwifiex_add_wmm_info_ie(struct mwifiex_private *priv, struct sk_buff *skb, - u8 qosinfo) -{ - u8 *buf; - - buf = (void *)skb_put(skb, MWIFIEX_TDLS_WMM_INFO_SIZE + - sizeof(struct ieee_types_header)); - - *buf++ = WLAN_EID_VENDOR_SPECIFIC; - *buf++ = 7; /* len */ - *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */ - *buf++ = 0x50; - *buf++ = 0xf2; - *buf++ = 2; /* WME */ - *buf++ = 0; /* WME info */ - *buf++ = 1; /* WME ver */ - *buf++ = qosinfo; /* U-APSD no in use */ -} - -static int mwifiex_prep_tdls_encap_data(struct mwifiex_private *priv, - const u8 *peer, u8 action_code, - u8 dialog_token, - u16 status_code, struct sk_buff *skb) -{ - struct ieee80211_tdls_data *tf; - int ret; - u16 capab; - struct ieee80211_ht_cap *ht_cap; - u8 radio, *pos; - - capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; - - tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); - memcpy(tf->da, peer, ETH_ALEN); - memcpy(tf->sa, priv->curr_addr, ETH_ALEN); - tf->ether_type = cpu_to_be16(ETH_P_TDLS); - tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_SETUP_REQUEST; - skb_put(skb, sizeof(tf->u.setup_req)); - tf->u.setup_req.dialog_token = dialog_token; - tf->u.setup_req.capability = cpu_to_le16(capab); - ret = mwifiex_tdls_append_rates_ie(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - ht_cap = (void *)pos; - radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - ret = mwifiex_fill_cap_info(priv, radio, ht_cap); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - if (priv->adapter->is_hw_11ac_capable) { - ret = mwifiex_tdls_add_vht_capab(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - mwifiex_tdls_add_aid(priv, skb); - } - - mwifiex_tdls_add_ext_capab(priv, skb); - mwifiex_tdls_add_qos_capab(skb); - mwifiex_add_wmm_info_ie(priv, skb, 0); - break; - - case WLAN_TDLS_SETUP_RESPONSE: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_SETUP_RESPONSE; - skb_put(skb, sizeof(tf->u.setup_resp)); - tf->u.setup_resp.status_code = cpu_to_le16(status_code); - tf->u.setup_resp.dialog_token = dialog_token; - tf->u.setup_resp.capability = cpu_to_le16(capab); - ret = mwifiex_tdls_append_rates_ie(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - ht_cap = (void *)pos; - radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - ret = mwifiex_fill_cap_info(priv, radio, ht_cap); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - if (priv->adapter->is_hw_11ac_capable) { - ret = mwifiex_tdls_add_vht_capab(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - mwifiex_tdls_add_aid(priv, skb); - } - - mwifiex_tdls_add_ext_capab(priv, skb); - mwifiex_tdls_add_qos_capab(skb); - mwifiex_add_wmm_info_ie(priv, skb, 0); - break; - - case WLAN_TDLS_SETUP_CONFIRM: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_SETUP_CONFIRM; - skb_put(skb, sizeof(tf->u.setup_cfm)); - tf->u.setup_cfm.status_code = cpu_to_le16(status_code); - tf->u.setup_cfm.dialog_token = dialog_token; - - mwifiex_tdls_add_wmm_param_ie(priv, skb); - if (priv->adapter->is_hw_11ac_capable) { - ret = mwifiex_tdls_add_vht_oper(priv, peer, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - ret = mwifiex_tdls_add_ht_oper(priv, peer, 1, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - } else { - ret = mwifiex_tdls_add_ht_oper(priv, peer, 0, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - } - break; - - case WLAN_TDLS_TEARDOWN: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_TEARDOWN; - skb_put(skb, sizeof(tf->u.teardown)); - tf->u.teardown.reason_code = cpu_to_le16(status_code); - break; - - case WLAN_TDLS_DISCOVERY_REQUEST: - tf->category = WLAN_CATEGORY_TDLS; - tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; - skb_put(skb, sizeof(tf->u.discover_req)); - tf->u.discover_req.dialog_token = dialog_token; - break; - default: - mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); - return -EINVAL; - } - - return 0; -} - -static void -mwifiex_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, - const u8 *peer, const u8 *bssid) -{ - struct ieee80211_tdls_lnkie *lnkid; - - lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); - lnkid->ie_type = WLAN_EID_LINK_ID; - lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - - sizeof(struct ieee_types_header); - - memcpy(lnkid->bssid, bssid, ETH_ALEN); - memcpy(lnkid->init_sta, src_addr, ETH_ALEN); - memcpy(lnkid->resp_sta, peer, ETH_ALEN); -} - -int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len) -{ - struct sk_buff *skb; - struct mwifiex_txinfo *tx_info; - int ret; - u16 skb_len; - - skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + - max(sizeof(struct ieee80211_mgmt), - sizeof(struct ieee80211_tdls_data)) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + - MWIFIEX_SUPPORTED_RATES + - 3 + /* Qos Info */ - sizeof(struct ieee_types_extcap) + - sizeof(struct ieee80211_ht_cap) + - sizeof(struct ieee_types_bss_co_2040) + - sizeof(struct ieee80211_ht_operation) + - sizeof(struct ieee80211_tdls_lnkie) + - sizeof(struct ieee80211_wmm_param_ie) + - extra_ies_len; - - if (priv->adapter->is_hw_11ac_capable) - skb_len += sizeof(struct ieee_types_vht_cap) + - sizeof(struct ieee_types_vht_oper) + - sizeof(struct ieee_types_aid); - - skb = dev_alloc_skb(skb_len); - if (!skb) { - mwifiex_dbg(priv->adapter, ERROR, - "allocate skb failed for management frame\n"); - return -ENOMEM; - } - skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - case WLAN_TDLS_SETUP_CONFIRM: - case WLAN_TDLS_TEARDOWN: - case WLAN_TDLS_DISCOVERY_REQUEST: - ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, - dialog_token, status_code, - skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - if (extra_ies_len) - memcpy(skb_put(skb, extra_ies_len), extra_ies, - extra_ies_len); - mwifiex_tdls_add_link_ie(skb, priv->curr_addr, peer, - priv->cfg_bssid); - break; - case WLAN_TDLS_SETUP_RESPONSE: - ret = mwifiex_prep_tdls_encap_data(priv, peer, action_code, - dialog_token, status_code, - skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - if (extra_ies_len) - memcpy(skb_put(skb, extra_ies_len), extra_ies, - extra_ies_len); - mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, - priv->cfg_bssid); - break; - } - - switch (action_code) { - case WLAN_TDLS_SETUP_REQUEST: - case WLAN_TDLS_SETUP_RESPONSE: - skb->priority = MWIFIEX_PRIO_BK; - break; - default: - skb->priority = MWIFIEX_PRIO_VI; - break; - } - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - - __net_timestamp(skb); - mwifiex_queue_tx_pkt(priv, skb); - - return 0; -} - -static int -mwifiex_construct_tdls_action_frame(struct mwifiex_private *priv, - const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, struct sk_buff *skb) -{ - struct ieee80211_mgmt *mgmt; - u8 bc_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - int ret; - u16 capab; - struct ieee80211_ht_cap *ht_cap; - u8 radio, *pos; - - capab = priv->curr_bss_params.bss_descriptor.cap_info_bitmap; - - mgmt = (void *)skb_put(skb, offsetof(struct ieee80211_mgmt, u)); - - memset(mgmt, 0, 24); - memcpy(mgmt->da, peer, ETH_ALEN); - memcpy(mgmt->sa, priv->curr_addr, ETH_ALEN); - memcpy(mgmt->bssid, priv->cfg_bssid, ETH_ALEN); - mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | - IEEE80211_STYPE_ACTION); - - /* add address 4 */ - pos = skb_put(skb, ETH_ALEN); - - switch (action_code) { - case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - skb_put(skb, sizeof(mgmt->u.action.u.tdls_discover_resp) + 1); - mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; - mgmt->u.action.u.tdls_discover_resp.action_code = - WLAN_PUB_ACTION_TDLS_DISCOVER_RES; - mgmt->u.action.u.tdls_discover_resp.dialog_token = - dialog_token; - mgmt->u.action.u.tdls_discover_resp.capability = - cpu_to_le16(capab); - /* move back for addr4 */ - memmove(pos + ETH_ALEN, &mgmt->u.action.category, - sizeof(mgmt->u.action.u.tdls_discover_resp)); - /* init address 4 */ - memcpy(pos, bc_addr, ETH_ALEN); - - ret = mwifiex_tdls_append_rates_ie(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - pos = (void *)skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); - *pos++ = WLAN_EID_HT_CAPABILITY; - *pos++ = sizeof(struct ieee80211_ht_cap); - ht_cap = (void *)pos; - radio = mwifiex_band_to_radio_type(priv->curr_bss_params.band); - ret = mwifiex_fill_cap_info(priv, radio, ht_cap); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - - if (priv->adapter->is_hw_11ac_capable) { - ret = mwifiex_tdls_add_vht_capab(priv, skb); - if (ret) { - dev_kfree_skb_any(skb); - return ret; - } - mwifiex_tdls_add_aid(priv, skb); - } - - mwifiex_tdls_add_ext_capab(priv, skb); - mwifiex_tdls_add_qos_capab(skb); - break; - default: - mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS action frame type\n"); - return -EINVAL; - } - - return 0; -} - -int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, - u8 action_code, u8 dialog_token, - u16 status_code, const u8 *extra_ies, - size_t extra_ies_len) -{ - struct sk_buff *skb; - struct mwifiex_txinfo *tx_info; - u8 *pos; - u32 pkt_type, tx_control; - u16 pkt_len, skb_len; - - skb_len = MWIFIEX_MIN_DATA_HEADER_LEN + - max(sizeof(struct ieee80211_mgmt), - sizeof(struct ieee80211_tdls_data)) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + - MWIFIEX_SUPPORTED_RATES + - sizeof(struct ieee_types_extcap) + - sizeof(struct ieee80211_ht_cap) + - sizeof(struct ieee_types_bss_co_2040) + - sizeof(struct ieee80211_ht_operation) + - sizeof(struct ieee80211_tdls_lnkie) + - extra_ies_len + - 3 + /* Qos Info */ - ETH_ALEN; /* Address4 */ - - if (priv->adapter->is_hw_11ac_capable) - skb_len += sizeof(struct ieee_types_vht_cap) + - sizeof(struct ieee_types_vht_oper) + - sizeof(struct ieee_types_aid); - - skb = dev_alloc_skb(skb_len); - if (!skb) { - mwifiex_dbg(priv->adapter, ERROR, - "allocate skb failed for management frame\n"); - return -ENOMEM; - } - - skb_reserve(skb, MWIFIEX_MIN_DATA_HEADER_LEN); - - pkt_type = PKT_TYPE_MGMT; - tx_control = 0; - pos = skb_put(skb, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); - memset(pos, 0, MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(pkt_len)); - memcpy(pos, &pkt_type, sizeof(pkt_type)); - memcpy(pos + sizeof(pkt_type), &tx_control, sizeof(tx_control)); - - if (mwifiex_construct_tdls_action_frame(priv, peer, action_code, - dialog_token, status_code, - skb)) { - dev_kfree_skb_any(skb); - return -EINVAL; - } - - if (extra_ies_len) - memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); - - /* the TDLS link IE is always added last we are the responder */ - - mwifiex_tdls_add_link_ie(skb, peer, priv->curr_addr, - priv->cfg_bssid); - - skb->priority = MWIFIEX_PRIO_VI; - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; - - pkt_len = skb->len - MWIFIEX_MGMT_FRAME_HEADER_SIZE - sizeof(pkt_len); - memcpy(skb->data + MWIFIEX_MGMT_FRAME_HEADER_SIZE, &pkt_len, - sizeof(pkt_len)); - __net_timestamp(skb); - mwifiex_queue_tx_pkt(priv, skb); - - return 0; -} - -/* This function process tdls action frame from peer. - * Peer capabilities are stored into station node structure. - */ -void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, - u8 *buf, int len) -{ - struct mwifiex_sta_node *sta_ptr; - u8 *peer, *pos, *end; - u8 i, action, basic; - __le16 cap = 0; - int ie_len = 0; - - if (len < (sizeof(struct ethhdr) + 3)) - return; - if (*(buf + sizeof(struct ethhdr)) != WLAN_TDLS_SNAP_RFTYPE) - return; - if (*(buf + sizeof(struct ethhdr) + 1) != WLAN_CATEGORY_TDLS) - return; - - peer = buf + ETH_ALEN; - action = *(buf + sizeof(struct ethhdr) + 2); - mwifiex_dbg(priv->adapter, DATA, - "rx:tdls action: peer=%pM, action=%d\n", peer, action); - - switch (action) { - case WLAN_TDLS_SETUP_REQUEST: - if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN)) - return; - - pos = buf + sizeof(struct ethhdr) + 4; - /* payload 1+ category 1 + action 1 + dialog 1 */ - cap = cpu_to_le16(*(u16 *)pos); - ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; - pos += 2; - break; - - case WLAN_TDLS_SETUP_RESPONSE: - if (len < (sizeof(struct ethhdr) + TDLS_RESP_FIX_LEN)) - return; - /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ - pos = buf + sizeof(struct ethhdr) + 6; - cap = cpu_to_le16(*(u16 *)pos); - ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; - pos += 2; - break; - - case WLAN_TDLS_SETUP_CONFIRM: - if (len < (sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN)) - return; - pos = buf + sizeof(struct ethhdr) + TDLS_CONFIRM_FIX_LEN; - ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; - break; - default: - mwifiex_dbg(priv->adapter, ERROR, "Unknown TDLS frame type.\n"); - return; - } - - sta_ptr = mwifiex_add_sta_entry(priv, peer); - if (!sta_ptr) - return; - - sta_ptr->tdls_cap.capab = cap; - - for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { - if (pos + 2 + pos[1] > end) - break; - - switch (*pos) { - case WLAN_EID_SUPP_RATES: - sta_ptr->tdls_cap.rates_len = pos[1]; - for (i = 0; i < pos[1]; i++) - sta_ptr->tdls_cap.rates[i] = pos[i + 2]; - break; - - case WLAN_EID_EXT_SUPP_RATES: - basic = sta_ptr->tdls_cap.rates_len; - for (i = 0; i < pos[1]; i++) - sta_ptr->tdls_cap.rates[basic + i] = pos[i + 2]; - sta_ptr->tdls_cap.rates_len += pos[1]; - break; - case WLAN_EID_HT_CAPABILITY: - memcpy((u8 *)&sta_ptr->tdls_cap.ht_capb, pos, - sizeof(struct ieee80211_ht_cap)); - sta_ptr->is_11n_enabled = 1; - break; - case WLAN_EID_HT_OPERATION: - memcpy(&sta_ptr->tdls_cap.ht_oper, pos, - sizeof(struct ieee80211_ht_operation)); - break; - case WLAN_EID_BSS_COEX_2040: - sta_ptr->tdls_cap.coex_2040 = pos[2]; - break; - case WLAN_EID_EXT_CAPABILITY: - memcpy((u8 *)&sta_ptr->tdls_cap.extcap, pos, - sizeof(struct ieee_types_header) + - min_t(u8, pos[1], 8)); - break; - case WLAN_EID_RSN: - memcpy((u8 *)&sta_ptr->tdls_cap.rsn_ie, pos, - sizeof(struct ieee_types_header) + - min_t(u8, pos[1], IEEE_MAX_IE_SIZE - - sizeof(struct ieee_types_header))); - break; - case WLAN_EID_QOS_CAPA: - sta_ptr->tdls_cap.qos_info = pos[2]; - break; - case WLAN_EID_VHT_OPERATION: - if (priv->adapter->is_hw_11ac_capable) - memcpy(&sta_ptr->tdls_cap.vhtoper, pos, - sizeof(struct ieee80211_vht_operation)); - break; - case WLAN_EID_VHT_CAPABILITY: - if (priv->adapter->is_hw_11ac_capable) { - memcpy((u8 *)&sta_ptr->tdls_cap.vhtcap, pos, - sizeof(struct ieee80211_vht_cap)); - sta_ptr->is_11ac_enabled = 1; - } - break; - case WLAN_EID_AID: - if (priv->adapter->is_hw_11ac_capable) - sta_ptr->tdls_cap.aid = - le16_to_cpu(*(__le16 *)(pos + 2)); - default: - break; - } - } - - return; -} - -static int -mwifiex_tdls_process_config_link(struct mwifiex_private *priv, const u8 *peer) -{ - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_ds_tdls_oper tdls_oper; - - memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); - sta_ptr = mwifiex_get_sta_entry(priv, peer); - - if (!sta_ptr || sta_ptr->tdls_status == TDLS_SETUP_FAILURE) { - mwifiex_dbg(priv->adapter, ERROR, - "link absent for peer %pM; cannot config\n", peer); - return -EINVAL; - } - - memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); - tdls_oper.tdls_action = MWIFIEX_TDLS_CONFIG_LINK; - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, - HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); -} - -static int -mwifiex_tdls_process_create_link(struct mwifiex_private *priv, const u8 *peer) -{ - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_ds_tdls_oper tdls_oper; - - memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); - sta_ptr = mwifiex_get_sta_entry(priv, peer); - - if (sta_ptr && sta_ptr->tdls_status == TDLS_SETUP_INPROGRESS) { - mwifiex_dbg(priv->adapter, WARN, - "Setup already in progress for peer %pM\n", peer); - return 0; - } - - sta_ptr = mwifiex_add_sta_entry(priv, peer); - if (!sta_ptr) - return -ENOMEM; - - sta_ptr->tdls_status = TDLS_SETUP_INPROGRESS; - mwifiex_hold_tdls_packets(priv, peer); - memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); - tdls_oper.tdls_action = MWIFIEX_TDLS_CREATE_LINK; - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, - HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); -} - -static int -mwifiex_tdls_process_disable_link(struct mwifiex_private *priv, const u8 *peer) -{ - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_ds_tdls_oper tdls_oper; - unsigned long flags; - - memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); - sta_ptr = mwifiex_get_sta_entry(priv, peer); - - if (sta_ptr) { - if (sta_ptr->is_11n_enabled) { - mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, - flags); - mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); - } - mwifiex_del_sta_entry(priv, peer); - } - - mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); - mwifiex_auto_tdls_update_peer_status(priv, peer, TDLS_NOT_SETUP); - memcpy(&tdls_oper.peer_mac, peer, ETH_ALEN); - tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, - HostCmd_ACT_GEN_SET, 0, &tdls_oper, true); -} - -static int -mwifiex_tdls_process_enable_link(struct mwifiex_private *priv, const u8 *peer) -{ - struct mwifiex_sta_node *sta_ptr; - struct ieee80211_mcs_info mcs; - unsigned long flags; - int i; - - sta_ptr = mwifiex_get_sta_entry(priv, peer); - - if (sta_ptr && (sta_ptr->tdls_status != TDLS_SETUP_FAILURE)) { - mwifiex_dbg(priv->adapter, MSG, - "tdls: enable link %pM success\n", peer); - - sta_ptr->tdls_status = TDLS_SETUP_COMPLETE; - - mcs = sta_ptr->tdls_cap.ht_capb.mcs; - if (mcs.rx_mask[0] != 0xff) - sta_ptr->is_11n_enabled = true; - if (sta_ptr->is_11n_enabled) { - if (le16_to_cpu(sta_ptr->tdls_cap.ht_capb.cap_info) & - IEEE80211_HT_CAP_MAX_AMSDU) - sta_ptr->max_amsdu = - MWIFIEX_TX_DATA_BUF_SIZE_8K; - else - sta_ptr->max_amsdu = - MWIFIEX_TX_DATA_BUF_SIZE_4K; - - for (i = 0; i < MAX_NUM_TID; i++) - sta_ptr->ampdu_sta[i] = - priv->aggr_prio_tbl[i].ampdu_user; - } else { - for (i = 0; i < MAX_NUM_TID; i++) - sta_ptr->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; - } - if (sta_ptr->tdls_cap.extcap.ext_capab[3] & - WLAN_EXT_CAPA4_TDLS_CHAN_SWITCH) { - mwifiex_config_tdls_enable(priv); - mwifiex_config_tdls_cs_params(priv); - } - - memset(sta_ptr->rx_seq, 0xff, sizeof(sta_ptr->rx_seq)); - mwifiex_restore_tdls_packets(priv, peer, TDLS_SETUP_COMPLETE); - mwifiex_auto_tdls_update_peer_status(priv, peer, - TDLS_SETUP_COMPLETE); - } else { - mwifiex_dbg(priv->adapter, ERROR, - "tdls: enable link %pM failed\n", peer); - if (sta_ptr) { - mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, - flags); - mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); - mwifiex_del_sta_entry(priv, peer); - } - mwifiex_restore_tdls_packets(priv, peer, TDLS_LINK_TEARDOWN); - mwifiex_auto_tdls_update_peer_status(priv, peer, - TDLS_NOT_SETUP); - - return -1; - } - - return 0; -} - -int mwifiex_tdls_oper(struct mwifiex_private *priv, const u8 *peer, u8 action) -{ - switch (action) { - case MWIFIEX_TDLS_ENABLE_LINK: - return mwifiex_tdls_process_enable_link(priv, peer); - case MWIFIEX_TDLS_DISABLE_LINK: - return mwifiex_tdls_process_disable_link(priv, peer); - case MWIFIEX_TDLS_CREATE_LINK: - return mwifiex_tdls_process_create_link(priv, peer); - case MWIFIEX_TDLS_CONFIG_LINK: - return mwifiex_tdls_process_config_link(priv, peer); - } - return 0; -} - -int mwifiex_get_tdls_link_status(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_sta_node *sta_ptr; - - sta_ptr = mwifiex_get_sta_entry(priv, mac); - if (sta_ptr) - return sta_ptr->tdls_status; - - return TDLS_NOT_SETUP; -} - -int mwifiex_get_tdls_list(struct mwifiex_private *priv, - struct tdls_peer_info *buf) -{ - struct mwifiex_sta_node *sta_ptr; - struct tdls_peer_info *peer = buf; - int count = 0; - unsigned long flags; - - if (!ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return 0; - - /* make sure we are in station mode and connected */ - if (!(priv->bss_type == MWIFIEX_BSS_TYPE_STA && priv->media_connected)) - return 0; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - list_for_each_entry(sta_ptr, &priv->sta_list, list) { - if (mwifiex_is_tdls_link_setup(sta_ptr->tdls_status)) { - ether_addr_copy(peer->peer_addr, sta_ptr->mac_addr); - peer++; - count++; - if (count >= MWIFIEX_MAX_TDLS_PEER_SUPPORTED) - break; - } - } - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - - return count; -} - -void mwifiex_disable_all_tdls_links(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *sta_ptr; - struct mwifiex_ds_tdls_oper tdls_oper; - unsigned long flags; - - if (list_empty(&priv->sta_list)) - return; - - list_for_each_entry(sta_ptr, &priv->sta_list, list) { - memset(&tdls_oper, 0, sizeof(struct mwifiex_ds_tdls_oper)); - - if (sta_ptr->is_11n_enabled) { - mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, - flags); - mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); - } - - mwifiex_restore_tdls_packets(priv, sta_ptr->mac_addr, - TDLS_LINK_TEARDOWN); - memcpy(&tdls_oper.peer_mac, sta_ptr->mac_addr, ETH_ALEN); - tdls_oper.tdls_action = MWIFIEX_TDLS_DISABLE_LINK; - if (mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_OPER, - HostCmd_ACT_GEN_SET, 0, &tdls_oper, false)) - mwifiex_dbg(priv->adapter, ERROR, - "Disable link failed for TDLS peer %pM", - sta_ptr->mac_addr); - } - - mwifiex_del_all_sta_list(priv); -} - -int mwifiex_tdls_check_tx(struct mwifiex_private *priv, struct sk_buff *skb) -{ - struct mwifiex_auto_tdls_peer *peer; - unsigned long flags; - u8 mac[ETH_ALEN]; - - ether_addr_copy(mac, skb->data); - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(peer, &priv->auto_tdls_list, list) { - if (!memcmp(mac, peer->mac_addr, ETH_ALEN)) { - if (peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && - peer->tdls_status == TDLS_NOT_SETUP && - (peer->failure_count < - MWIFIEX_TDLS_MAX_FAIL_COUNT)) { - peer->tdls_status = TDLS_SETUP_INPROGRESS; - mwifiex_dbg(priv->adapter, INFO, - "setup TDLS link, peer=%pM rssi=%d\n", - peer->mac_addr, peer->rssi); - - cfg80211_tdls_oper_request(priv->netdev, - peer->mac_addr, - NL80211_TDLS_SETUP, - 0, GFP_ATOMIC); - peer->do_setup = false; - priv->check_tdls_tx = false; - } else if (peer->failure_count < - MWIFIEX_TDLS_MAX_FAIL_COUNT && - peer->do_discover) { - mwifiex_send_tdls_data_frame(priv, - peer->mac_addr, - WLAN_TDLS_DISCOVERY_REQUEST, - 1, 0, NULL, 0); - peer->do_discover = false; - } - } - } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); - - return 0; -} - -void mwifiex_flush_auto_tdls_list(struct mwifiex_private *priv) -{ - struct mwifiex_auto_tdls_peer *peer, *tmp_node; - unsigned long flags; - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry_safe(peer, tmp_node, &priv->auto_tdls_list, list) { - list_del(&peer->list); - kfree(peer); - } - - INIT_LIST_HEAD(&priv->auto_tdls_list); - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); - priv->check_tdls_tx = false; -} - -void mwifiex_add_auto_tdls_peer(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_auto_tdls_peer *tdls_peer; - unsigned long flags; - - if (!priv->adapter->auto_tdls) - return; - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { - if (!memcmp(tdls_peer->mac_addr, mac, ETH_ALEN)) { - tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; - tdls_peer->rssi_jiffies = jiffies; - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); - return; - } - } - - /* create new TDLS peer */ - tdls_peer = kzalloc(sizeof(*tdls_peer), GFP_ATOMIC); - if (tdls_peer) { - ether_addr_copy(tdls_peer->mac_addr, mac); - tdls_peer->tdls_status = TDLS_SETUP_INPROGRESS; - tdls_peer->rssi_jiffies = jiffies; - INIT_LIST_HEAD(&tdls_peer->list); - list_add_tail(&tdls_peer->list, &priv->auto_tdls_list); - mwifiex_dbg(priv->adapter, INFO, - "Add auto TDLS peer= %pM to list\n", mac); - } - - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); -} - -void mwifiex_auto_tdls_update_peer_status(struct mwifiex_private *priv, - const u8 *mac, u8 link_status) -{ - struct mwifiex_auto_tdls_peer *peer; - unsigned long flags; - - if (!priv->adapter->auto_tdls) - return; - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(peer, &priv->auto_tdls_list, list) { - if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { - if ((link_status == TDLS_NOT_SETUP) && - (peer->tdls_status == TDLS_SETUP_INPROGRESS)) - peer->failure_count++; - else if (mwifiex_is_tdls_link_setup(link_status)) - peer->failure_count = 0; - - peer->tdls_status = link_status; - break; - } - } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); -} - -void mwifiex_auto_tdls_update_peer_signal(struct mwifiex_private *priv, - u8 *mac, s8 snr, s8 nflr) -{ - struct mwifiex_auto_tdls_peer *peer; - unsigned long flags; - - if (!priv->adapter->auto_tdls) - return; - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(peer, &priv->auto_tdls_list, list) { - if (!memcmp(peer->mac_addr, mac, ETH_ALEN)) { - peer->rssi = nflr - snr; - peer->rssi_jiffies = jiffies; - break; - } - } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); -} - -void mwifiex_check_auto_tdls(unsigned long context) -{ - struct mwifiex_private *priv = (struct mwifiex_private *)context; - struct mwifiex_auto_tdls_peer *tdls_peer; - unsigned long flags; - u16 reason = WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED; - - if (WARN_ON_ONCE(!priv || !priv->adapter)) { - pr_err("mwifiex: %s: adapter or private structure is NULL\n", - __func__); - return; - } - - if (unlikely(!priv->adapter->auto_tdls)) - return; - - if (!priv->auto_tdls_timer_active) { - mwifiex_dbg(priv->adapter, INFO, - "auto TDLS timer inactive; return"); - return; - } - - priv->check_tdls_tx = false; - - if (list_empty(&priv->auto_tdls_list)) { - mod_timer(&priv->auto_tdls_timer, - jiffies + - msecs_to_jiffies(MWIFIEX_TIMER_10S)); - return; - } - - spin_lock_irqsave(&priv->auto_tdls_lock, flags); - list_for_each_entry(tdls_peer, &priv->auto_tdls_list, list) { - if ((jiffies - tdls_peer->rssi_jiffies) > - (MWIFIEX_AUTO_TDLS_IDLE_TIME * HZ)) { - tdls_peer->rssi = 0; - tdls_peer->do_discover = true; - priv->check_tdls_tx = true; - } - - if (((tdls_peer->rssi >= MWIFIEX_TDLS_RSSI_LOW) || - !tdls_peer->rssi) && - mwifiex_is_tdls_link_setup(tdls_peer->tdls_status)) { - tdls_peer->tdls_status = TDLS_LINK_TEARDOWN; - mwifiex_dbg(priv->adapter, MSG, - "teardown TDLS link,peer=%pM rssi=%d\n", - tdls_peer->mac_addr, -tdls_peer->rssi); - tdls_peer->do_discover = true; - priv->check_tdls_tx = true; - cfg80211_tdls_oper_request(priv->netdev, - tdls_peer->mac_addr, - NL80211_TDLS_TEARDOWN, - reason, GFP_ATOMIC); - } else if (tdls_peer->rssi && - tdls_peer->rssi <= MWIFIEX_TDLS_RSSI_HIGH && - tdls_peer->tdls_status == TDLS_NOT_SETUP && - tdls_peer->failure_count < - MWIFIEX_TDLS_MAX_FAIL_COUNT) { - priv->check_tdls_tx = true; - tdls_peer->do_setup = true; - mwifiex_dbg(priv->adapter, INFO, - "check TDLS with peer=%pM\t" - "rssi=%d\n", tdls_peer->mac_addr, - tdls_peer->rssi); - } - } - spin_unlock_irqrestore(&priv->auto_tdls_lock, flags); - - mod_timer(&priv->auto_tdls_timer, - jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); -} - -void mwifiex_setup_auto_tdls_timer(struct mwifiex_private *priv) -{ - setup_timer(&priv->auto_tdls_timer, mwifiex_check_auto_tdls, - (unsigned long)priv); - priv->auto_tdls_timer_active = true; - mod_timer(&priv->auto_tdls_timer, - jiffies + msecs_to_jiffies(MWIFIEX_TIMER_10S)); -} - -void mwifiex_clean_auto_tdls(struct mwifiex_private *priv) -{ - if (ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && - priv->adapter->auto_tdls && - priv->bss_type == MWIFIEX_BSS_TYPE_STA) { - priv->auto_tdls_timer_active = false; - del_timer(&priv->auto_tdls_timer); - mwifiex_flush_auto_tdls_list(priv); - } -} - -static int mwifiex_config_tdls(struct mwifiex_private *priv, u8 enable) -{ - struct mwifiex_tdls_config config; - - config.enable = cpu_to_le16(enable); - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, - ACT_TDLS_CS_ENABLE_CONFIG, 0, &config, true); -} - -int mwifiex_config_tdls_enable(struct mwifiex_private *priv) -{ - return mwifiex_config_tdls(priv, true); -} - -int mwifiex_config_tdls_disable(struct mwifiex_private *priv) -{ - return mwifiex_config_tdls(priv, false); -} - -int mwifiex_config_tdls_cs_params(struct mwifiex_private *priv) -{ - struct mwifiex_tdls_config_cs_params config_tdls_cs_params; - - config_tdls_cs_params.unit_time = MWIFIEX_DEF_CS_UNIT_TIME; - config_tdls_cs_params.thr_otherlink = MWIFIEX_DEF_CS_THR_OTHERLINK; - config_tdls_cs_params.thr_directlink = MWIFIEX_DEF_THR_DIRECTLINK; - - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, - ACT_TDLS_CS_PARAMS, 0, - &config_tdls_cs_params, true); -} - -int mwifiex_stop_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac) -{ - struct mwifiex_tdls_stop_cs_params stop_tdls_cs_params; - - ether_addr_copy(stop_tdls_cs_params.peer_mac, peer_mac); - - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, - ACT_TDLS_CS_STOP, 0, - &stop_tdls_cs_params, true); -} - -int mwifiex_start_tdls_cs(struct mwifiex_private *priv, const u8 *peer_mac, - u8 primary_chan, u8 second_chan_offset, u8 band) -{ - struct mwifiex_tdls_init_cs_params start_tdls_cs_params; - - ether_addr_copy(start_tdls_cs_params.peer_mac, peer_mac); - start_tdls_cs_params.primary_chan = primary_chan; - start_tdls_cs_params.second_chan_offset = second_chan_offset; - start_tdls_cs_params.band = band; - - start_tdls_cs_params.switch_time = cpu_to_le16(MWIFIEX_DEF_CS_TIME); - start_tdls_cs_params.switch_timeout = - cpu_to_le16(MWIFIEX_DEF_CS_TIMEOUT); - start_tdls_cs_params.reg_class = MWIFIEX_DEF_CS_REG_CLASS; - start_tdls_cs_params.periodicity = MWIFIEX_DEF_CS_PERIODICITY; - - return mwifiex_send_cmd(priv, HostCmd_CMD_TDLS_CONFIG, - ACT_TDLS_CS_INIT, 0, - &start_tdls_cs_params, true); -} diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c deleted file mode 100644 index bf6182b646a5..000000000000 --- a/drivers/net/wireless/mwifiex/txrx.c +++ /dev/null @@ -1,386 +0,0 @@ -/* - * Marvell Wireless LAN device driver: generic TX/RX data handling - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" - -/* - * This function processes the received buffer. - * - * Main responsibility of this function is to parse the RxPD to - * identify the correct interface this packet is headed for and - * forwarding it to the associated handling function, where the - * packet will be further processed and sent to kernel/upper layer - * if required. - */ -int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - struct rxpd *local_rx_pd; - struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); - int ret; - - local_rx_pd = (struct rxpd *) (skb->data); - /* Get the BSS number from rxpd, get corresponding priv */ - priv = mwifiex_get_priv_by_id(adapter, local_rx_pd->bss_num & - BSS_NUM_MASK, local_rx_pd->bss_type); - if (!priv) - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); - - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "data: priv not found. Drop RX packet\n"); - dev_kfree_skb_any(skb); - return -1; - } - - mwifiex_dbg_dump(adapter, DAT_D, "rx pkt:", skb->data, - min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); - - memset(rx_info, 0, sizeof(*rx_info)); - rx_info->bss_num = priv->bss_num; - rx_info->bss_type = priv->bss_type; - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) - ret = mwifiex_process_uap_rx_packet(priv, skb); - else - ret = mwifiex_process_sta_rx_packet(priv, skb); - - return ret; -} -EXPORT_SYMBOL_GPL(mwifiex_handle_rx_packet); - -/* - * This function sends a packet to device. - * - * It processes the packet to add the TxPD, checks condition and - * sends the processed packet to firmware for transmission. - * - * On successful completion, the function calls the completion callback - * and logs the time. - */ -int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - int hroom, ret = -1; - struct mwifiex_adapter *adapter = priv->adapter; - u8 *head_ptr; - struct txpd *local_tx_pd = NULL; - struct mwifiex_sta_node *dest_node; - struct ethhdr *hdr = (void *)skb->data; - - hroom = (adapter->iface_type == MWIFIEX_USB) ? 0 : INTF_HEADER_LEN; - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP) { - dest_node = mwifiex_get_sta_entry(priv, hdr->h_dest); - if (dest_node) { - dest_node->stats.tx_bytes += skb->len; - dest_node->stats.tx_packets++; - } - - head_ptr = mwifiex_process_uap_txpd(priv, skb); - } else { - head_ptr = mwifiex_process_sta_txpd(priv, skb); - } - - if ((adapter->data_sent || adapter->tx_lock_flag) && head_ptr) { - skb_queue_tail(&adapter->tx_data_q, skb); - atomic_inc(&adapter->tx_queued); - return 0; - } - - if (head_ptr) { - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) - local_tx_pd = (struct txpd *)(head_ptr + hroom); - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, - priv->usb_port, - skb, NULL); - } else { - ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_TYPE_DATA, - skb, tx_param); - } - } - mwifiex_dbg_dump(adapter, DAT_D, "tx pkt:", skb->data, - min_t(size_t, skb->len, DEBUG_DUMP_DATA_MAX_LEN)); - - switch (ret) { - case -ENOSR: - mwifiex_dbg(adapter, DATA, "data: -ENOSR is returned\n"); - break; - case -EBUSY: - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - (adapter->pps_uapsd_mode) && (adapter->tx_lock_flag)) { - priv->adapter->tx_lock_flag = false; - if (local_tx_pd) - local_tx_pd->flags = 0; - } - mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); - break; - case -1: - mwifiex_dbg(adapter, ERROR, - "mwifiex_write_data_async failed: 0x%X\n", - ret); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - case -EINPROGRESS: - break; - case 0: - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - default: - break; - } - - return ret; -} - -static int mwifiex_host_to_card(struct mwifiex_adapter *adapter, - struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - struct txpd *local_tx_pd = NULL; - u8 *head_ptr = skb->data; - int ret = 0; - struct mwifiex_private *priv; - struct mwifiex_txinfo *tx_info; - - tx_info = MWIFIEX_SKB_TXCB(skb); - priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, - tx_info->bss_type); - if (!priv) { - mwifiex_dbg(adapter, ERROR, - "data: priv not found. Drop TX packet\n"); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, 0, 0); - return ret; - } - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) { - if (adapter->iface_type == MWIFIEX_USB) - local_tx_pd = (struct txpd *)head_ptr; - else - local_tx_pd = (struct txpd *) (head_ptr + - INTF_HEADER_LEN); - } - - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, - priv->usb_port, - skb, NULL); - } else { - ret = adapter->if_ops.host_to_card(adapter, - MWIFIEX_TYPE_DATA, - skb, tx_param); - } - switch (ret) { - case -ENOSR: - mwifiex_dbg(adapter, ERROR, "data: -ENOSR is returned\n"); - break; - case -EBUSY: - if ((GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA) && - (adapter->pps_uapsd_mode) && - (adapter->tx_lock_flag)) { - priv->adapter->tx_lock_flag = false; - if (local_tx_pd) - local_tx_pd->flags = 0; - } - skb_queue_head(&adapter->tx_data_q, skb); - if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) - atomic_add(tx_info->aggr_num, &adapter->tx_queued); - else - atomic_inc(&adapter->tx_queued); - mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); - break; - case -1: - mwifiex_dbg(adapter, ERROR, - "mwifiex_write_data_async failed: 0x%X\n", ret); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - case -EINPROGRESS: - break; - case 0: - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - default: - break; - } - return ret; -} - -static int -mwifiex_dequeue_tx_queue(struct mwifiex_adapter *adapter) -{ - struct sk_buff *skb, *skb_next; - struct mwifiex_txinfo *tx_info; - struct mwifiex_tx_param tx_param; - - skb = skb_dequeue(&adapter->tx_data_q); - if (!skb) - return -1; - - tx_info = MWIFIEX_SKB_TXCB(skb); - if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) - atomic_sub(tx_info->aggr_num, &adapter->tx_queued); - else - atomic_dec(&adapter->tx_queued); - - if (!skb_queue_empty(&adapter->tx_data_q)) - skb_next = skb_peek(&adapter->tx_data_q); - else - skb_next = NULL; - tx_param.next_pkt_len = ((skb_next) ? skb_next->len : 0); - if (!tx_param.next_pkt_len) { - if (!mwifiex_wmm_lists_empty(adapter)) - tx_param.next_pkt_len = 1; - } - return mwifiex_host_to_card(adapter, skb, &tx_param); -} - -void -mwifiex_process_tx_queue(struct mwifiex_adapter *adapter) -{ - do { - if (adapter->data_sent || adapter->tx_lock_flag) - break; - if (mwifiex_dequeue_tx_queue(adapter)) - break; - } while (!skb_queue_empty(&adapter->tx_data_q)); -} - -/* - * Packet send completion callback handler. - * - * It either frees the buffer directly or forwards it to another - * completion callback which checks conditions, updates statistics, - * wakes up stalled traffic queue if required, and then frees the buffer. - */ -int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb, int aggr, int status) -{ - struct mwifiex_private *priv; - struct mwifiex_txinfo *tx_info; - struct netdev_queue *txq; - int index; - - if (!skb) - return 0; - - tx_info = MWIFIEX_SKB_TXCB(skb); - priv = mwifiex_get_priv_by_id(adapter, tx_info->bss_num, - tx_info->bss_type); - if (!priv) - goto done; - - mwifiex_set_trans_start(priv->netdev); - if (!status) { - priv->stats.tx_packets++; - priv->stats.tx_bytes += tx_info->pkt_len; - if (priv->tx_timeout_cnt) - priv->tx_timeout_cnt = 0; - } else { - priv->stats.tx_errors++; - } - - if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) - atomic_dec_return(&adapter->pending_bridged_pkts); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_AGGR_PKT) - goto done; - - if (aggr) - /* For skb_aggr, do not wake up tx queue */ - goto done; - - atomic_dec(&adapter->tx_pending); - - index = mwifiex_1d_to_wmm_queue[skb->priority]; - if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) { - txq = netdev_get_tx_queue(priv->netdev, index); - if (netif_tx_queue_stopped(txq)) { - netif_tx_wake_queue(txq); - mwifiex_dbg(adapter, DATA, "wake queue: %d\n", index); - } - } -done: - dev_kfree_skb_any(skb); - - return 0; -} -EXPORT_SYMBOL_GPL(mwifiex_write_data_complete); - -void mwifiex_parse_tx_status_event(struct mwifiex_private *priv, - void *event_body) -{ - struct tx_status_event *tx_status = (void *)priv->adapter->event_body; - struct sk_buff *ack_skb; - unsigned long flags; - struct mwifiex_txinfo *tx_info; - - if (!tx_status->tx_token_id) - return; - - spin_lock_irqsave(&priv->ack_status_lock, flags); - ack_skb = idr_find(&priv->ack_status_frames, tx_status->tx_token_id); - if (ack_skb) - idr_remove(&priv->ack_status_frames, tx_status->tx_token_id); - spin_unlock_irqrestore(&priv->ack_status_lock, flags); - - if (ack_skb) { - tx_info = MWIFIEX_SKB_TXCB(ack_skb); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS) { - /* consumes ack_skb */ - skb_complete_wifi_ack(ack_skb, !tx_status->status); - } else { - /* Remove broadcast address which was added by driver */ - memmove(ack_skb->data + - sizeof(struct ieee80211_hdr_3addr) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16), - ack_skb->data + - sizeof(struct ieee80211_hdr_3addr) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + - ETH_ALEN, ack_skb->len - - (sizeof(struct ieee80211_hdr_3addr) + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + sizeof(u16) + - ETH_ALEN)); - ack_skb->len = ack_skb->len - ETH_ALEN; - /* Remove driver's proprietary header including 2 bytes - * of packet length and pass actual management frame buffer - * to cfg80211. - */ - cfg80211_mgmt_tx_status(&priv->wdev, tx_info->cookie, - ack_skb->data + - MWIFIEX_MGMT_FRAME_HEADER_SIZE + - sizeof(u16), ack_skb->len - - (MWIFIEX_MGMT_FRAME_HEADER_SIZE - + sizeof(u16)), - !tx_status->status, GFP_ATOMIC); - dev_kfree_skb_any(ack_skb); - } - } -} diff --git a/drivers/net/wireless/mwifiex/uap_cmd.c b/drivers/net/wireless/mwifiex/uap_cmd.c deleted file mode 100644 index 759a6ada5b0f..000000000000 --- a/drivers/net/wireless/mwifiex/uap_cmd.c +++ /dev/null @@ -1,885 +0,0 @@ -/* - * Marvell Wireless LAN device driver: AP specific command handling - * - * Copyright (C) 2012-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "main.h" -#include "11ac.h" - -/* This function parses security related parameters from cfg80211_ap_settings - * and sets into FW understandable bss_config structure. - */ -int mwifiex_set_secure_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_config, - struct cfg80211_ap_settings *params) { - int i; - struct mwifiex_wep_key wep_key; - - if (!params->privacy) { - bss_config->protocol = PROTOCOL_NO_SECURITY; - bss_config->key_mgmt = KEY_MGMT_NONE; - bss_config->wpa_cfg.length = 0; - priv->sec_info.wep_enabled = 0; - priv->sec_info.wpa_enabled = 0; - priv->sec_info.wpa2_enabled = 0; - - return 0; - } - - switch (params->auth_type) { - case NL80211_AUTHTYPE_OPEN_SYSTEM: - bss_config->auth_mode = WLAN_AUTH_OPEN; - break; - case NL80211_AUTHTYPE_SHARED_KEY: - bss_config->auth_mode = WLAN_AUTH_SHARED_KEY; - break; - case NL80211_AUTHTYPE_NETWORK_EAP: - bss_config->auth_mode = WLAN_AUTH_LEAP; - break; - default: - bss_config->auth_mode = MWIFIEX_AUTH_MODE_AUTO; - break; - } - - bss_config->key_mgmt_operation |= KEY_MGMT_ON_HOST; - - for (i = 0; i < params->crypto.n_akm_suites; i++) { - switch (params->crypto.akm_suites[i]) { - case WLAN_AKM_SUITE_8021X: - if (params->crypto.wpa_versions & - NL80211_WPA_VERSION_1) { - bss_config->protocol = PROTOCOL_WPA; - bss_config->key_mgmt = KEY_MGMT_EAP; - } - if (params->crypto.wpa_versions & - NL80211_WPA_VERSION_2) { - bss_config->protocol |= PROTOCOL_WPA2; - bss_config->key_mgmt = KEY_MGMT_EAP; - } - break; - case WLAN_AKM_SUITE_PSK: - if (params->crypto.wpa_versions & - NL80211_WPA_VERSION_1) { - bss_config->protocol = PROTOCOL_WPA; - bss_config->key_mgmt = KEY_MGMT_PSK; - } - if (params->crypto.wpa_versions & - NL80211_WPA_VERSION_2) { - bss_config->protocol |= PROTOCOL_WPA2; - bss_config->key_mgmt = KEY_MGMT_PSK; - } - break; - default: - break; - } - } - 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) - bss_config->wpa_cfg.pairwise_cipher_wpa |= - CIPHER_TKIP; - if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) - bss_config->wpa_cfg.pairwise_cipher_wpa2 |= - CIPHER_TKIP; - break; - case WLAN_CIPHER_SUITE_CCMP: - if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) - bss_config->wpa_cfg.pairwise_cipher_wpa |= - CIPHER_AES_CCMP; - if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) - bss_config->wpa_cfg.pairwise_cipher_wpa2 |= - CIPHER_AES_CCMP; - default: - break; - } - } - - switch (params->crypto.cipher_group) { - case WLAN_CIPHER_SUITE_WEP40: - case WLAN_CIPHER_SUITE_WEP104: - if (priv->sec_info.wep_enabled) { - bss_config->protocol = PROTOCOL_STATIC_WEP; - bss_config->key_mgmt = KEY_MGMT_NONE; - bss_config->wpa_cfg.length = 0; - - for (i = 0; i < NUM_WEP_KEYS; i++) { - wep_key = priv->wep_key[i]; - bss_config->wep_cfg[i].key_index = i; - - if (priv->wep_key_curr_index == i) - bss_config->wep_cfg[i].is_default = 1; - else - bss_config->wep_cfg[i].is_default = 0; - - bss_config->wep_cfg[i].length = - wep_key.key_length; - memcpy(&bss_config->wep_cfg[i].key, - &wep_key.key_material, - wep_key.key_length); - } - } - break; - case WLAN_CIPHER_SUITE_TKIP: - bss_config->wpa_cfg.group_cipher = CIPHER_TKIP; - break; - case WLAN_CIPHER_SUITE_CCMP: - bss_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; - break; - default: - break; - } - - return 0; -} - -/* This function updates 11n related parameters from IE and sets them into - * bss_config structure. - */ -void -mwifiex_set_ht_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - const u8 *ht_ie; - u16 cap_info; - - if (!ISSUPP_11NENABLED(priv->adapter->fw_cap_info)) - return; - - ht_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, params->beacon.tail, - params->beacon.tail_len); - if (ht_ie) { - memcpy(&bss_cfg->ht_cap, ht_ie + 2, - sizeof(struct ieee80211_ht_cap)); - cap_info = le16_to_cpu(bss_cfg->ht_cap.cap_info); - memset(&bss_cfg->ht_cap.mcs, 0, - priv->adapter->number_of_antenna); - switch (GET_RXSTBC(cap_info)) { - case MWIFIEX_RX_STBC1: - /* HT_CAP 1X1 mode */ - bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; - break; - case MWIFIEX_RX_STBC12: /* fall through */ - case MWIFIEX_RX_STBC123: - /* HT_CAP 2X2 mode */ - bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; - bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; - break; - default: - mwifiex_dbg(priv->adapter, WARN, - "Unsupported RX-STBC, default to 2x2\n"); - bss_cfg->ht_cap.mcs.rx_mask[0] = 0xff; - bss_cfg->ht_cap.mcs.rx_mask[1] = 0xff; - break; - } - priv->ap_11n_enabled = 1; - } else { - memset(&bss_cfg->ht_cap , 0, sizeof(struct ieee80211_ht_cap)); - bss_cfg->ht_cap.cap_info = cpu_to_le16(MWIFIEX_DEF_HT_CAP); - bss_cfg->ht_cap.ampdu_params_info = MWIFIEX_DEF_AMPDU; - } - - return; -} - -/* This function updates 11ac related parameters from IE - * and sets them into bss_config structure. - */ -void mwifiex_set_vht_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - const u8 *vht_ie; - - vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail, - params->beacon.tail_len); - if (vht_ie) { - memcpy(&bss_cfg->vht_cap, vht_ie + 2, - sizeof(struct ieee80211_vht_cap)); - priv->ap_11ac_enabled = 1; - } else { - priv->ap_11ac_enabled = 0; - } - - return; -} - -/* This function updates 11ac related parameters from IE - * and sets them into bss_config structure. - */ -void mwifiex_set_tpc_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - const u8 *tpc_ie; - - tpc_ie = cfg80211_find_ie(WLAN_EID_TPC_REQUEST, params->beacon.tail, - params->beacon.tail_len); - if (tpc_ie) - bss_cfg->power_constraint = *(tpc_ie + 2); - else - bss_cfg->power_constraint = 0; -} - -/* Enable VHT only when cfg80211_ap_settings has VHT IE. - * Otherwise disable VHT. - */ -void mwifiex_set_vht_width(struct mwifiex_private *priv, - enum nl80211_chan_width width, - bool ap_11ac_enable) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_11ac_vht_cfg vht_cfg; - - vht_cfg.band_config = VHT_CFG_5GHZ; - vht_cfg.cap_info = adapter->hw_dot_11ac_dev_cap; - - if (!ap_11ac_enable) { - vht_cfg.mcs_tx_set = DISABLE_VHT_MCS_SET; - vht_cfg.mcs_rx_set = DISABLE_VHT_MCS_SET; - } else { - vht_cfg.mcs_tx_set = DEFAULT_VHT_MCS_SET; - vht_cfg.mcs_rx_set = DEFAULT_VHT_MCS_SET; - } - - vht_cfg.misc_config = VHT_CAP_UAP_ONLY; - - if (ap_11ac_enable && width >= NL80211_CHAN_WIDTH_80) - vht_cfg.misc_config |= VHT_BW_80_160_80P80; - - mwifiex_send_cmd(priv, HostCmd_CMD_11AC_CFG, - HostCmd_ACT_GEN_SET, 0, &vht_cfg, true); - - return; -} - -/* This function finds supported rates IE from beacon parameter and sets - * these rates into bss_config structure. - */ -void -mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - struct ieee_types_header *rate_ie; - int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); - const u8 *var_pos = params->beacon.head + var_offset; - int len = params->beacon.head_len - var_offset; - u8 rate_len = 0; - - rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len); - if (rate_ie) { - memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len); - rate_len = rate_ie->len; - } - - rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES, - params->beacon.tail, - params->beacon.tail_len); - if (rate_ie) - memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len); - - return; -} - -/* This function initializes some of mwifiex_uap_bss_param variables. - * This helps FW in ignoring invalid values. These values may or may not - * be get updated to valid ones at later stage. - */ -void mwifiex_set_sys_config_invalid_data(struct mwifiex_uap_bss_param *config) -{ - config->bcast_ssid_ctl = 0x7F; - config->radio_ctl = 0x7F; - config->dtim_period = 0x7F; - config->beacon_period = 0x7FFF; - config->auth_mode = 0x7F; - config->rts_threshold = 0x7FFF; - config->frag_threshold = 0x7FFF; - config->retry_limit = 0x7F; - config->qos_info = 0xFF; -} - -/* This function parses BSS related parameters from structure - * and prepares TLVs specific to WPA/WPA2 security. - * These TLVs are appended to command buffer. - */ -static void -mwifiex_uap_bss_wpa(u8 **tlv_buf, void *cmd_buf, u16 *param_size) -{ - struct host_cmd_tlv_pwk_cipher *pwk_cipher; - struct host_cmd_tlv_gwk_cipher *gwk_cipher; - struct host_cmd_tlv_passphrase *passphrase; - struct host_cmd_tlv_akmp *tlv_akmp; - struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; - u16 cmd_size = *param_size; - u8 *tlv = *tlv_buf; - - tlv_akmp = (struct host_cmd_tlv_akmp *)tlv; - tlv_akmp->header.type = cpu_to_le16(TLV_TYPE_UAP_AKMP); - tlv_akmp->header.len = cpu_to_le16(sizeof(struct host_cmd_tlv_akmp) - - sizeof(struct mwifiex_ie_types_header)); - tlv_akmp->key_mgmt_operation = cpu_to_le16(bss_cfg->key_mgmt_operation); - tlv_akmp->key_mgmt = cpu_to_le16(bss_cfg->key_mgmt); - cmd_size += sizeof(struct host_cmd_tlv_akmp); - tlv += sizeof(struct host_cmd_tlv_akmp); - - if (bss_cfg->wpa_cfg.pairwise_cipher_wpa & VALID_CIPHER_BITMAP) { - pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; - pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); - pwk_cipher->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - - sizeof(struct mwifiex_ie_types_header)); - pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA); - pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa; - cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); - tlv += sizeof(struct host_cmd_tlv_pwk_cipher); - } - - if (bss_cfg->wpa_cfg.pairwise_cipher_wpa2 & VALID_CIPHER_BITMAP) { - pwk_cipher = (struct host_cmd_tlv_pwk_cipher *)tlv; - pwk_cipher->header.type = cpu_to_le16(TLV_TYPE_PWK_CIPHER); - pwk_cipher->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_pwk_cipher) - - sizeof(struct mwifiex_ie_types_header)); - pwk_cipher->proto = cpu_to_le16(PROTOCOL_WPA2); - pwk_cipher->cipher = bss_cfg->wpa_cfg.pairwise_cipher_wpa2; - cmd_size += sizeof(struct host_cmd_tlv_pwk_cipher); - tlv += sizeof(struct host_cmd_tlv_pwk_cipher); - } - - if (bss_cfg->wpa_cfg.group_cipher & VALID_CIPHER_BITMAP) { - gwk_cipher = (struct host_cmd_tlv_gwk_cipher *)tlv; - gwk_cipher->header.type = cpu_to_le16(TLV_TYPE_GWK_CIPHER); - gwk_cipher->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_gwk_cipher) - - sizeof(struct mwifiex_ie_types_header)); - gwk_cipher->cipher = bss_cfg->wpa_cfg.group_cipher; - cmd_size += sizeof(struct host_cmd_tlv_gwk_cipher); - tlv += sizeof(struct host_cmd_tlv_gwk_cipher); - } - - if (bss_cfg->wpa_cfg.length) { - passphrase = (struct host_cmd_tlv_passphrase *)tlv; - passphrase->header.type = - cpu_to_le16(TLV_TYPE_UAP_WPA_PASSPHRASE); - passphrase->header.len = cpu_to_le16(bss_cfg->wpa_cfg.length); - memcpy(passphrase->passphrase, bss_cfg->wpa_cfg.passphrase, - bss_cfg->wpa_cfg.length); - cmd_size += sizeof(struct mwifiex_ie_types_header) + - bss_cfg->wpa_cfg.length; - tlv += sizeof(struct mwifiex_ie_types_header) + - bss_cfg->wpa_cfg.length; - } - - *param_size = cmd_size; - *tlv_buf = tlv; - - return; -} - -/* This function parses WMM related parameters from cfg80211_ap_settings - * structure and updates bss_config structure. - */ -void -mwifiex_set_wmm_params(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_ap_settings *params) -{ - const u8 *vendor_ie; - struct ieee_types_header *wmm_ie; - u8 wmm_oui[] = {0x00, 0x50, 0xf2, 0x02}; - - vendor_ie = cfg80211_find_vendor_ie(WLAN_OUI_MICROSOFT, - WLAN_OUI_TYPE_MICROSOFT_WMM, - params->beacon.tail, - params->beacon.tail_len); - if (vendor_ie) { - wmm_ie = (struct ieee_types_header *)vendor_ie; - memcpy(&bss_cfg->wmm_info, wmm_ie + 1, - sizeof(bss_cfg->wmm_info)); - priv->wmm_enabled = 1; - } else { - memset(&bss_cfg->wmm_info, 0, sizeof(bss_cfg->wmm_info)); - memcpy(&bss_cfg->wmm_info.oui, wmm_oui, sizeof(wmm_oui)); - bss_cfg->wmm_info.subtype = MWIFIEX_WMM_SUBTYPE; - bss_cfg->wmm_info.version = MWIFIEX_WMM_VERSION; - priv->wmm_enabled = 0; - } - - bss_cfg->qos_info = 0x00; - return; -} -/* This function parses BSS related parameters from structure - * and prepares TLVs specific to WEP encryption. - * These TLVs are appended to command buffer. - */ -static void -mwifiex_uap_bss_wep(u8 **tlv_buf, void *cmd_buf, u16 *param_size) -{ - struct host_cmd_tlv_wep_key *wep_key; - u16 cmd_size = *param_size; - int i; - u8 *tlv = *tlv_buf; - struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; - - for (i = 0; i < NUM_WEP_KEYS; i++) { - if (bss_cfg->wep_cfg[i].length && - (bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP40 || - bss_cfg->wep_cfg[i].length == WLAN_KEY_LEN_WEP104)) { - wep_key = (struct host_cmd_tlv_wep_key *)tlv; - wep_key->header.type = - cpu_to_le16(TLV_TYPE_UAP_WEP_KEY); - wep_key->header.len = - cpu_to_le16(bss_cfg->wep_cfg[i].length + 2); - wep_key->key_index = bss_cfg->wep_cfg[i].key_index; - wep_key->is_default = bss_cfg->wep_cfg[i].is_default; - memcpy(wep_key->key, bss_cfg->wep_cfg[i].key, - bss_cfg->wep_cfg[i].length); - cmd_size += sizeof(struct mwifiex_ie_types_header) + 2 + - bss_cfg->wep_cfg[i].length; - tlv += sizeof(struct mwifiex_ie_types_header) + 2 + - bss_cfg->wep_cfg[i].length; - } - } - - *param_size = cmd_size; - *tlv_buf = tlv; - - return; -} - -/* This function parses BSS related parameters from structure - * and prepares TLVs. These TLVs are appended to command buffer. -*/ -static int -mwifiex_uap_bss_param_prepare(u8 *tlv, void *cmd_buf, u16 *param_size) -{ - struct host_cmd_tlv_dtim_period *dtim_period; - struct host_cmd_tlv_beacon_period *beacon_period; - struct host_cmd_tlv_ssid *ssid; - struct host_cmd_tlv_bcast_ssid *bcast_ssid; - struct host_cmd_tlv_channel_band *chan_band; - struct host_cmd_tlv_frag_threshold *frag_threshold; - struct host_cmd_tlv_rts_threshold *rts_threshold; - struct host_cmd_tlv_retry_limit *retry_limit; - struct host_cmd_tlv_encrypt_protocol *encrypt_protocol; - struct host_cmd_tlv_auth_type *auth_type; - struct host_cmd_tlv_rates *tlv_rates; - struct host_cmd_tlv_ageout_timer *ao_timer, *ps_ao_timer; - struct host_cmd_tlv_power_constraint *pwr_ct; - struct mwifiex_ie_types_htcap *htcap; - struct mwifiex_ie_types_wmmcap *wmm_cap; - struct mwifiex_uap_bss_param *bss_cfg = cmd_buf; - int i; - u16 cmd_size = *param_size; - - if (bss_cfg->ssid.ssid_len) { - ssid = (struct host_cmd_tlv_ssid *)tlv; - ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_SSID); - ssid->header.len = cpu_to_le16((u16)bss_cfg->ssid.ssid_len); - memcpy(ssid->ssid, bss_cfg->ssid.ssid, bss_cfg->ssid.ssid_len); - cmd_size += sizeof(struct mwifiex_ie_types_header) + - bss_cfg->ssid.ssid_len; - tlv += sizeof(struct mwifiex_ie_types_header) + - bss_cfg->ssid.ssid_len; - - bcast_ssid = (struct host_cmd_tlv_bcast_ssid *)tlv; - bcast_ssid->header.type = cpu_to_le16(TLV_TYPE_UAP_BCAST_SSID); - bcast_ssid->header.len = - cpu_to_le16(sizeof(bcast_ssid->bcast_ctl)); - bcast_ssid->bcast_ctl = bss_cfg->bcast_ssid_ctl; - cmd_size += sizeof(struct host_cmd_tlv_bcast_ssid); - tlv += sizeof(struct host_cmd_tlv_bcast_ssid); - } - if (bss_cfg->rates[0]) { - tlv_rates = (struct host_cmd_tlv_rates *)tlv; - tlv_rates->header.type = cpu_to_le16(TLV_TYPE_UAP_RATES); - - for (i = 0; i < MWIFIEX_SUPPORTED_RATES && bss_cfg->rates[i]; - i++) - tlv_rates->rates[i] = bss_cfg->rates[i]; - - tlv_rates->header.len = cpu_to_le16(i); - cmd_size += sizeof(struct host_cmd_tlv_rates) + i; - tlv += sizeof(struct host_cmd_tlv_rates) + i; - } - if (bss_cfg->channel && - ((bss_cfg->band_cfg == BAND_CONFIG_BG && - bss_cfg->channel <= MAX_CHANNEL_BAND_BG) || - (bss_cfg->band_cfg == BAND_CONFIG_A && - bss_cfg->channel <= MAX_CHANNEL_BAND_A))) { - chan_band = (struct host_cmd_tlv_channel_band *)tlv; - chan_band->header.type = cpu_to_le16(TLV_TYPE_CHANNELBANDLIST); - chan_band->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_channel_band) - - sizeof(struct mwifiex_ie_types_header)); - chan_band->band_config = bss_cfg->band_cfg; - chan_band->channel = bss_cfg->channel; - cmd_size += sizeof(struct host_cmd_tlv_channel_band); - tlv += sizeof(struct host_cmd_tlv_channel_band); - } - if (bss_cfg->beacon_period >= MIN_BEACON_PERIOD && - bss_cfg->beacon_period <= MAX_BEACON_PERIOD) { - beacon_period = (struct host_cmd_tlv_beacon_period *)tlv; - beacon_period->header.type = - cpu_to_le16(TLV_TYPE_UAP_BEACON_PERIOD); - beacon_period->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_beacon_period) - - sizeof(struct mwifiex_ie_types_header)); - beacon_period->period = cpu_to_le16(bss_cfg->beacon_period); - cmd_size += sizeof(struct host_cmd_tlv_beacon_period); - tlv += sizeof(struct host_cmd_tlv_beacon_period); - } - if (bss_cfg->dtim_period >= MIN_DTIM_PERIOD && - bss_cfg->dtim_period <= MAX_DTIM_PERIOD) { - dtim_period = (struct host_cmd_tlv_dtim_period *)tlv; - dtim_period->header.type = - cpu_to_le16(TLV_TYPE_UAP_DTIM_PERIOD); - dtim_period->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_dtim_period) - - sizeof(struct mwifiex_ie_types_header)); - dtim_period->period = bss_cfg->dtim_period; - cmd_size += sizeof(struct host_cmd_tlv_dtim_period); - tlv += sizeof(struct host_cmd_tlv_dtim_period); - } - if (bss_cfg->rts_threshold <= MWIFIEX_RTS_MAX_VALUE) { - rts_threshold = (struct host_cmd_tlv_rts_threshold *)tlv; - rts_threshold->header.type = - cpu_to_le16(TLV_TYPE_UAP_RTS_THRESHOLD); - rts_threshold->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_rts_threshold) - - sizeof(struct mwifiex_ie_types_header)); - rts_threshold->rts_thr = cpu_to_le16(bss_cfg->rts_threshold); - cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); - tlv += sizeof(struct host_cmd_tlv_frag_threshold); - } - if ((bss_cfg->frag_threshold >= MWIFIEX_FRAG_MIN_VALUE) && - (bss_cfg->frag_threshold <= MWIFIEX_FRAG_MAX_VALUE)) { - frag_threshold = (struct host_cmd_tlv_frag_threshold *)tlv; - frag_threshold->header.type = - cpu_to_le16(TLV_TYPE_UAP_FRAG_THRESHOLD); - frag_threshold->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_frag_threshold) - - sizeof(struct mwifiex_ie_types_header)); - frag_threshold->frag_thr = cpu_to_le16(bss_cfg->frag_threshold); - cmd_size += sizeof(struct host_cmd_tlv_frag_threshold); - tlv += sizeof(struct host_cmd_tlv_frag_threshold); - } - if (bss_cfg->retry_limit <= MWIFIEX_RETRY_LIMIT) { - retry_limit = (struct host_cmd_tlv_retry_limit *)tlv; - retry_limit->header.type = - cpu_to_le16(TLV_TYPE_UAP_RETRY_LIMIT); - retry_limit->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_retry_limit) - - sizeof(struct mwifiex_ie_types_header)); - retry_limit->limit = (u8)bss_cfg->retry_limit; - cmd_size += sizeof(struct host_cmd_tlv_retry_limit); - tlv += sizeof(struct host_cmd_tlv_retry_limit); - } - if ((bss_cfg->protocol & PROTOCOL_WPA) || - (bss_cfg->protocol & PROTOCOL_WPA2) || - (bss_cfg->protocol & PROTOCOL_EAP)) - mwifiex_uap_bss_wpa(&tlv, cmd_buf, &cmd_size); - else - mwifiex_uap_bss_wep(&tlv, cmd_buf, &cmd_size); - - if ((bss_cfg->auth_mode <= WLAN_AUTH_SHARED_KEY) || - (bss_cfg->auth_mode == MWIFIEX_AUTH_MODE_AUTO)) { - auth_type = (struct host_cmd_tlv_auth_type *)tlv; - auth_type->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE); - auth_type->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_auth_type) - - sizeof(struct mwifiex_ie_types_header)); - auth_type->auth_type = (u8)bss_cfg->auth_mode; - cmd_size += sizeof(struct host_cmd_tlv_auth_type); - tlv += sizeof(struct host_cmd_tlv_auth_type); - } - if (bss_cfg->protocol) { - encrypt_protocol = (struct host_cmd_tlv_encrypt_protocol *)tlv; - encrypt_protocol->header.type = - cpu_to_le16(TLV_TYPE_UAP_ENCRY_PROTOCOL); - encrypt_protocol->header.len = - cpu_to_le16(sizeof(struct host_cmd_tlv_encrypt_protocol) - - sizeof(struct mwifiex_ie_types_header)); - encrypt_protocol->proto = cpu_to_le16(bss_cfg->protocol); - cmd_size += sizeof(struct host_cmd_tlv_encrypt_protocol); - tlv += sizeof(struct host_cmd_tlv_encrypt_protocol); - } - - if (bss_cfg->ht_cap.cap_info) { - htcap = (struct mwifiex_ie_types_htcap *)tlv; - htcap->header.type = cpu_to_le16(WLAN_EID_HT_CAPABILITY); - htcap->header.len = - cpu_to_le16(sizeof(struct ieee80211_ht_cap)); - htcap->ht_cap.cap_info = bss_cfg->ht_cap.cap_info; - htcap->ht_cap.ampdu_params_info = - bss_cfg->ht_cap.ampdu_params_info; - memcpy(&htcap->ht_cap.mcs, &bss_cfg->ht_cap.mcs, - sizeof(struct ieee80211_mcs_info)); - htcap->ht_cap.extended_ht_cap_info = - bss_cfg->ht_cap.extended_ht_cap_info; - htcap->ht_cap.tx_BF_cap_info = bss_cfg->ht_cap.tx_BF_cap_info; - htcap->ht_cap.antenna_selection_info = - bss_cfg->ht_cap.antenna_selection_info; - cmd_size += sizeof(struct mwifiex_ie_types_htcap); - tlv += sizeof(struct mwifiex_ie_types_htcap); - } - - if (bss_cfg->wmm_info.qos_info != 0xFF) { - wmm_cap = (struct mwifiex_ie_types_wmmcap *)tlv; - wmm_cap->header.type = cpu_to_le16(WLAN_EID_VENDOR_SPECIFIC); - wmm_cap->header.len = cpu_to_le16(sizeof(wmm_cap->wmm_info)); - memcpy(&wmm_cap->wmm_info, &bss_cfg->wmm_info, - sizeof(wmm_cap->wmm_info)); - cmd_size += sizeof(struct mwifiex_ie_types_wmmcap); - tlv += sizeof(struct mwifiex_ie_types_wmmcap); - } - - if (bss_cfg->sta_ao_timer) { - ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; - ao_timer->header.type = cpu_to_le16(TLV_TYPE_UAP_AO_TIMER); - ao_timer->header.len = cpu_to_le16(sizeof(*ao_timer) - - sizeof(struct mwifiex_ie_types_header)); - ao_timer->sta_ao_timer = cpu_to_le32(bss_cfg->sta_ao_timer); - cmd_size += sizeof(*ao_timer); - tlv += sizeof(*ao_timer); - } - - if (bss_cfg->power_constraint) { - pwr_ct = (void *)tlv; - pwr_ct->header.type = cpu_to_le16(TLV_TYPE_PWR_CONSTRAINT); - pwr_ct->header.len = cpu_to_le16(sizeof(u8)); - pwr_ct->constraint = bss_cfg->power_constraint; - cmd_size += sizeof(*pwr_ct); - tlv += sizeof(*pwr_ct); - } - - if (bss_cfg->ps_sta_ao_timer) { - ps_ao_timer = (struct host_cmd_tlv_ageout_timer *)tlv; - ps_ao_timer->header.type = - cpu_to_le16(TLV_TYPE_UAP_PS_AO_TIMER); - ps_ao_timer->header.len = cpu_to_le16(sizeof(*ps_ao_timer) - - sizeof(struct mwifiex_ie_types_header)); - ps_ao_timer->sta_ao_timer = - cpu_to_le32(bss_cfg->ps_sta_ao_timer); - cmd_size += sizeof(*ps_ao_timer); - tlv += sizeof(*ps_ao_timer); - } - - *param_size = cmd_size; - - return 0; -} - -/* This function parses custom IEs from IE list and prepares command buffer */ -static int mwifiex_uap_custom_ie_prepare(u8 *tlv, void *cmd_buf, u16 *ie_size) -{ - struct mwifiex_ie_list *ap_ie = cmd_buf; - struct mwifiex_ie_types_header *tlv_ie = (void *)tlv; - - if (!ap_ie || !ap_ie->len || !ap_ie->ie_list) - return -1; - - *ie_size += le16_to_cpu(ap_ie->len) + - sizeof(struct mwifiex_ie_types_header); - - tlv_ie->type = cpu_to_le16(TLV_TYPE_MGMT_IE); - tlv_ie->len = ap_ie->len; - tlv += sizeof(struct mwifiex_ie_types_header); - - memcpy(tlv, ap_ie->ie_list, le16_to_cpu(ap_ie->len)); - - return 0; -} - -/* Parse AP config structure and prepare TLV based command structure - * to be sent to FW for uAP configuration - */ -static int -mwifiex_cmd_uap_sys_config(struct host_cmd_ds_command *cmd, u16 cmd_action, - u32 type, void *cmd_buf) -{ - u8 *tlv; - u16 cmd_size, param_size, ie_size; - struct host_cmd_ds_sys_config *sys_cfg; - - cmd->command = cpu_to_le16(HostCmd_CMD_UAP_SYS_CONFIG); - cmd_size = (u16)(sizeof(struct host_cmd_ds_sys_config) + S_DS_GEN); - sys_cfg = (struct host_cmd_ds_sys_config *)&cmd->params.uap_sys_config; - sys_cfg->action = cpu_to_le16(cmd_action); - tlv = sys_cfg->tlv; - - switch (type) { - case UAP_BSS_PARAMS_I: - param_size = cmd_size; - if (mwifiex_uap_bss_param_prepare(tlv, cmd_buf, ¶m_size)) - return -1; - cmd->size = cpu_to_le16(param_size); - break; - case UAP_CUSTOM_IE_I: - ie_size = cmd_size; - if (mwifiex_uap_custom_ie_prepare(tlv, cmd_buf, &ie_size)) - return -1; - cmd->size = cpu_to_le16(ie_size); - break; - default: - return -1; - } - - return 0; -} - -/* This function prepares AP specific deauth command with mac supplied in - * function parameter. - */ -static int mwifiex_cmd_uap_sta_deauth(struct mwifiex_private *priv, - struct host_cmd_ds_command *cmd, u8 *mac) -{ - struct host_cmd_ds_sta_deauth *sta_deauth = &cmd->params.sta_deauth; - - cmd->command = cpu_to_le16(HostCmd_CMD_UAP_STA_DEAUTH); - memcpy(sta_deauth->mac, mac, ETH_ALEN); - sta_deauth->reason = cpu_to_le16(WLAN_REASON_DEAUTH_LEAVING); - - cmd->size = cpu_to_le16(sizeof(struct host_cmd_ds_sta_deauth) + - S_DS_GEN); - return 0; -} - -/* This function prepares the AP specific commands before sending them - * to the firmware. - * This is a generic function which calls specific command preparation - * routines based upon the command number. - */ -int mwifiex_uap_prepare_cmd(struct mwifiex_private *priv, u16 cmd_no, - u16 cmd_action, u32 type, - void *data_buf, void *cmd_buf) -{ - struct host_cmd_ds_command *cmd = cmd_buf; - - switch (cmd_no) { - case HostCmd_CMD_UAP_SYS_CONFIG: - if (mwifiex_cmd_uap_sys_config(cmd, cmd_action, type, data_buf)) - return -1; - break; - case HostCmd_CMD_UAP_BSS_START: - case HostCmd_CMD_UAP_BSS_STOP: - case HOST_CMD_APCMD_SYS_RESET: - case HOST_CMD_APCMD_STA_LIST: - cmd->command = cpu_to_le16(cmd_no); - cmd->size = cpu_to_le16(S_DS_GEN); - break; - case HostCmd_CMD_UAP_STA_DEAUTH: - if (mwifiex_cmd_uap_sta_deauth(priv, cmd, data_buf)) - return -1; - break; - case HostCmd_CMD_CHAN_REPORT_REQUEST: - if (mwifiex_cmd_issue_chan_report_request(priv, cmd_buf, - data_buf)) - return -1; - break; - default: - mwifiex_dbg(priv->adapter, ERROR, - "PREP_CMD: unknown cmd %#x\n", cmd_no); - return -1; - } - - return 0; -} - -void mwifiex_uap_set_channel(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg, - struct cfg80211_chan_def chandef) -{ - u8 config_bands = 0, old_bands = priv->adapter->config_bands; - - priv->bss_chandef = chandef; - - bss_cfg->channel = ieee80211_frequency_to_channel( - chandef.chan->center_freq); - - /* Set appropriate bands */ - if (chandef.chan->band == IEEE80211_BAND_2GHZ) { - bss_cfg->band_cfg = BAND_CONFIG_BG; - config_bands = BAND_B | BAND_G; - - if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) - config_bands |= BAND_GN; - } else { - bss_cfg->band_cfg = BAND_CONFIG_A; - config_bands = BAND_A; - - if (chandef.width > NL80211_CHAN_WIDTH_20_NOHT) - config_bands |= BAND_AN; - - if (chandef.width > NL80211_CHAN_WIDTH_40) - config_bands |= BAND_AAC; - } - - priv->adapter->config_bands = config_bands; - - if (old_bands != config_bands) { - mwifiex_send_domain_info_cmd_fw(priv->adapter->wiphy); - mwifiex_dnld_txpwr_table(priv); - } -} - -int mwifiex_config_start_uap(struct mwifiex_private *priv, - struct mwifiex_uap_bss_param *bss_cfg) -{ - enum state_11d_t state_11d; - - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_SYS_CONFIG, - HostCmd_ACT_GEN_SET, - UAP_BSS_PARAMS_I, bss_cfg, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to set the SSID\n"); - return -1; - } - - /* Send cmd to FW to enable 11D function */ - state_11d = ENABLE_11D; - if (mwifiex_send_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB, - HostCmd_ACT_GEN_SET, DOT11D_I, - &state_11d, true)) { - mwifiex_dbg(priv->adapter, ERROR, - "11D: failed to enable 11D\n"); - return -1; - } - - if (mwifiex_send_cmd(priv, HostCmd_CMD_UAP_BSS_START, - HostCmd_ACT_GEN_SET, 0, NULL, false)) { - mwifiex_dbg(priv->adapter, ERROR, - "Failed to start the BSS\n"); - return -1; - } - - if (priv->sec_info.wep_enabled) - priv->curr_pkt_filter |= HostCmd_ACT_MAC_WEP_ENABLE; - else - priv->curr_pkt_filter &= ~HostCmd_ACT_MAC_WEP_ENABLE; - - if (mwifiex_send_cmd(priv, HostCmd_CMD_MAC_CONTROL, - HostCmd_ACT_GEN_SET, 0, - &priv->curr_pkt_filter, true)) - return -1; - - return 0; -} diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c deleted file mode 100644 index 86ff54296f39..000000000000 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ /dev/null @@ -1,333 +0,0 @@ -/* - * Marvell Wireless LAN device driver: AP event handling - * - * Copyright (C) 2012-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "main.h" -#include "11n.h" - -#define MWIFIEX_BSS_START_EVT_FIX_SIZE 12 - -static int mwifiex_check_uap_capabilties(struct mwifiex_private *priv, - struct sk_buff *event) -{ - int evt_len; - u8 *curr; - u16 tlv_len; - struct mwifiex_ie_types_data *tlv_hdr; - struct ieee_types_wmm_parameter *wmm_param_ie = NULL; - int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; - - priv->wmm_enabled = false; - skb_pull(event, MWIFIEX_BSS_START_EVT_FIX_SIZE); - evt_len = event->len; - curr = event->data; - - mwifiex_dbg_dump(priv->adapter, EVT_D, "uap capabilties:", - event->data, event->len); - - skb_push(event, MWIFIEX_BSS_START_EVT_FIX_SIZE); - - while ((evt_len >= sizeof(tlv_hdr->header))) { - tlv_hdr = (struct mwifiex_ie_types_data *)curr; - tlv_len = le16_to_cpu(tlv_hdr->header.len); - - if (evt_len < tlv_len + sizeof(tlv_hdr->header)) - break; - - switch (le16_to_cpu(tlv_hdr->header.type)) { - case WLAN_EID_HT_CAPABILITY: - priv->ap_11n_enabled = true; - break; - - case WLAN_EID_VHT_CAPABILITY: - priv->ap_11ac_enabled = true; - break; - - case WLAN_EID_VENDOR_SPECIFIC: - /* Point the regular IEEE IE 2 bytes into the Marvell IE - * and setup the IEEE IE type and length byte fields - */ - wmm_param_ie = (void *)(curr + 2); - wmm_param_ie->vend_hdr.len = (u8)tlv_len; - wmm_param_ie->vend_hdr.element_id = - WLAN_EID_VENDOR_SPECIFIC; - mwifiex_dbg(priv->adapter, EVENT, - "info: check uap capabilities:\t" - "wmm parameter set count: %d\n", - wmm_param_ie->qos_info_bitmap & mask); - - mwifiex_wmm_setup_ac_downgrade(priv); - priv->wmm_enabled = true; - mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); - break; - - default: - break; - } - - curr += (tlv_len + sizeof(tlv_hdr->header)); - evt_len -= (tlv_len + sizeof(tlv_hdr->header)); - } - - return 0; -} - -/* - * This function handles AP interface specific events generated by firmware. - * - * Event specific routines are called by this function based - * upon the generated event cause. - * - * - * Events supported for AP - - * - EVENT_UAP_STA_ASSOC - * - EVENT_UAP_STA_DEAUTH - * - EVENT_UAP_BSS_ACTIVE - * - EVENT_UAP_BSS_START - * - EVENT_UAP_BSS_IDLE - * - EVENT_UAP_MIC_COUNTERMEASURES: - */ -int mwifiex_process_uap_event(struct mwifiex_private *priv) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int len, i; - u32 eventcause = adapter->event_cause; - struct station_info sinfo; - struct mwifiex_assoc_event *event; - struct mwifiex_sta_node *node; - u8 *deauth_mac; - struct host_cmd_ds_11n_batimeout *ba_timeout; - u16 ctrl; - - switch (eventcause) { - case EVENT_UAP_STA_ASSOC: - memset(&sinfo, 0, sizeof(sinfo)); - event = (struct mwifiex_assoc_event *) - (adapter->event_body + MWIFIEX_UAP_EVENT_EXTRA_HEADER); - if (le16_to_cpu(event->type) == TLV_TYPE_UAP_MGMT_FRAME) { - len = -1; - - if (ieee80211_is_assoc_req(event->frame_control)) - len = 0; - else if (ieee80211_is_reassoc_req(event->frame_control)) - /* There will be ETH_ALEN bytes of - * current_ap_addr before the re-assoc ies. - */ - len = ETH_ALEN; - - if (len != -1) { - sinfo.assoc_req_ies = &event->data[len]; - len = (u8 *)sinfo.assoc_req_ies - - (u8 *)&event->frame_control; - sinfo.assoc_req_ies_len = - le16_to_cpu(event->len) - (u16)len; - } - } - cfg80211_new_sta(priv->netdev, event->sta_addr, &sinfo, - GFP_KERNEL); - - node = mwifiex_add_sta_entry(priv, event->sta_addr); - if (!node) { - mwifiex_dbg(adapter, ERROR, - "could not create station entry!\n"); - return -1; - } - - if (!priv->ap_11n_enabled) - break; - - mwifiex_set_sta_ht_cap(priv, sinfo.assoc_req_ies, - sinfo.assoc_req_ies_len, node); - - for (i = 0; i < MAX_NUM_TID; i++) { - if (node->is_11n_enabled) - node->ampdu_sta[i] = - priv->aggr_prio_tbl[i].ampdu_user; - else - node->ampdu_sta[i] = BA_STREAM_NOT_ALLOWED; - } - memset(node->rx_seq, 0xff, sizeof(node->rx_seq)); - break; - case EVENT_UAP_STA_DEAUTH: - deauth_mac = adapter->event_body + - MWIFIEX_UAP_EVENT_EXTRA_HEADER; - cfg80211_del_sta(priv->netdev, deauth_mac, GFP_KERNEL); - - if (priv->ap_11n_enabled) { - mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, deauth_mac); - mwifiex_del_tx_ba_stream_tbl_by_ra(priv, deauth_mac); - } - mwifiex_wmm_del_peer_ra_list(priv, deauth_mac); - mwifiex_del_sta_entry(priv, deauth_mac); - break; - case EVENT_UAP_BSS_IDLE: - priv->media_connected = false; - priv->port_open = false; - mwifiex_clean_txrx(priv); - mwifiex_del_all_sta_list(priv); - break; - case EVENT_UAP_BSS_ACTIVE: - priv->media_connected = true; - priv->port_open = true; - break; - case EVENT_UAP_BSS_START: - mwifiex_dbg(adapter, EVENT, - "AP EVENT: event id: %#x\n", eventcause); - priv->port_open = false; - memcpy(priv->netdev->dev_addr, adapter->event_body + 2, - ETH_ALEN); - if (priv->hist_data) - mwifiex_hist_data_reset(priv); - mwifiex_check_uap_capabilties(priv, adapter->event_skb); - break; - case EVENT_UAP_MIC_COUNTERMEASURES: - /* For future development */ - mwifiex_dbg(adapter, EVENT, - "AP EVENT: event id: %#x\n", eventcause); - break; - case EVENT_AMSDU_AGGR_CTRL: - ctrl = le16_to_cpu(*(__le16 *)adapter->event_body); - mwifiex_dbg(adapter, EVENT, - "event: AMSDU_AGGR_CTRL %d\n", ctrl); - - if (priv->media_connected) { - adapter->tx_buf_size = - min_t(u16, adapter->curr_tx_buf_size, ctrl); - mwifiex_dbg(adapter, EVENT, - "event: tx_buf_size %d\n", - adapter->tx_buf_size); - } - break; - case EVENT_ADDBA: - mwifiex_dbg(adapter, EVENT, "event: ADDBA Request\n"); - if (priv->media_connected) - mwifiex_send_cmd(priv, HostCmd_CMD_11N_ADDBA_RSP, - HostCmd_ACT_GEN_SET, 0, - adapter->event_body, false); - break; - case EVENT_DELBA: - mwifiex_dbg(adapter, EVENT, "event: DELBA Request\n"); - if (priv->media_connected) - mwifiex_11n_delete_ba_stream(priv, adapter->event_body); - break; - case EVENT_BA_STREAM_TIEMOUT: - mwifiex_dbg(adapter, EVENT, "event: BA Stream timeout\n"); - if (priv->media_connected) { - ba_timeout = (void *)adapter->event_body; - mwifiex_11n_ba_stream_timeout(priv, ba_timeout); - } - break; - case EVENT_EXT_SCAN_REPORT: - mwifiex_dbg(adapter, EVENT, "event: EXT_SCAN Report\n"); - if (adapter->ext_scan) - return mwifiex_handle_event_ext_scan_report(priv, - adapter->event_skb->data); - break; - case EVENT_TX_STATUS_REPORT: - mwifiex_dbg(adapter, EVENT, "event: TX_STATUS Report\n"); - mwifiex_parse_tx_status_event(priv, adapter->event_body); - break; - case EVENT_PS_SLEEP: - mwifiex_dbg(adapter, EVENT, "info: EVENT: SLEEP\n"); - - adapter->ps_state = PS_STATE_PRE_SLEEP; - - mwifiex_check_ps_cond(adapter); - break; - - case EVENT_PS_AWAKE: - mwifiex_dbg(adapter, EVENT, "info: EVENT: AWAKE\n"); - if (!adapter->pps_uapsd_mode && - priv->media_connected && adapter->sleep_period.period) { - adapter->pps_uapsd_mode = true; - mwifiex_dbg(adapter, EVENT, - "event: PPS/UAPSD mode activated\n"); - } - adapter->tx_lock_flag = false; - if (adapter->pps_uapsd_mode && adapter->gen_null_pkt) { - if (mwifiex_check_last_packet_indication(priv)) { - if (adapter->data_sent || - (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv))) { - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_card_req = false; - adapter->pm_wakeup_fw_try = false; - break; - } - if (!mwifiex_send_null_packet - (priv, - MWIFIEX_TxPD_POWER_MGMT_NULL_PACKET | - MWIFIEX_TxPD_POWER_MGMT_LAST_PACKET)) - adapter->ps_state = - PS_STATE_SLEEP; - return 0; - } - } - adapter->ps_state = PS_STATE_AWAKE; - adapter->pm_wakeup_card_req = false; - adapter->pm_wakeup_fw_try = false; - break; - - case EVENT_CHANNEL_REPORT_RDY: - mwifiex_dbg(adapter, EVENT, "event: Channel Report\n"); - mwifiex_11h_handle_chanrpt_ready(priv, adapter->event_skb); - break; - case EVENT_RADAR_DETECTED: - mwifiex_dbg(adapter, EVENT, "event: Radar detected\n"); - mwifiex_11h_handle_radar_detected(priv, adapter->event_skb); - break; - case EVENT_BT_COEX_WLAN_PARA_CHANGE: - dev_err(adapter->dev, "EVENT: BT coex wlan param update\n"); - mwifiex_bt_coex_wlan_param_update_event(priv, - adapter->event_skb); - break; - case EVENT_TX_DATA_PAUSE: - mwifiex_dbg(adapter, EVENT, "event: TX DATA PAUSE\n"); - mwifiex_process_tx_pause_event(priv, adapter->event_skb); - break; - - case EVENT_MULTI_CHAN_INFO: - mwifiex_dbg(adapter, EVENT, "event: multi-chan info\n"); - mwifiex_process_multi_chan_event(priv, adapter->event_skb); - break; - - default: - mwifiex_dbg(adapter, EVENT, - "event: unknown event id: %#x\n", eventcause); - break; - } - - return 0; -} - -/* This function deletes station entry from associated station list. - * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream - * tables created for this station are deleted. - */ -void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, - struct mwifiex_sta_node *node) -{ - if (priv->ap_11n_enabled && node->is_11n_enabled) { - mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr); - mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr); - } - mwifiex_del_sta_entry(priv, node->mac_addr); - - return; -} diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c deleted file mode 100644 index 74d5d7238633..000000000000 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ /dev/null @@ -1,437 +0,0 @@ -/* - * Marvell Wireless LAN device driver: AP TX and RX data handling - * - * Copyright (C) 2012-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "main.h" -#include "wmm.h" -#include "11n_aggr.h" -#include "11n_rxreorder.h" - -/* This function checks if particular RA list has packets more than low bridge - * packet threshold and then deletes packet from this RA list. - * Function deletes packets from such RA list and returns true. If no such list - * is found, false is returned. - */ -static bool -mwifiex_uap_del_tx_pkts_in_ralist(struct mwifiex_private *priv, - struct list_head *ra_list_head, - int tid) -{ - struct mwifiex_ra_list_tbl *ra_list; - struct sk_buff *skb, *tmp; - bool pkt_deleted = false; - struct mwifiex_txinfo *tx_info; - struct mwifiex_adapter *adapter = priv->adapter; - - list_for_each_entry(ra_list, ra_list_head, list) { - if (skb_queue_empty(&ra_list->skb_head)) - continue; - - skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) { - tx_info = MWIFIEX_SKB_TXCB(skb); - if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) { - __skb_unlink(skb, &ra_list->skb_head); - mwifiex_write_data_complete(adapter, skb, 0, - -1); - if (ra_list->tx_paused) - priv->wmm.pkts_paused[tid]--; - else - atomic_dec(&priv->wmm.tx_pkts_queued); - pkt_deleted = true; - } - if ((atomic_read(&adapter->pending_bridged_pkts) <= - MWIFIEX_BRIDGED_PKTS_THR_LOW)) - break; - } - } - - return pkt_deleted; -} - -/* This function deletes packets from particular RA List. RA list index - * from which packets are deleted is preserved so that packets from next RA - * list are deleted upon subsequent call thus maintaining fairness. - */ -static void mwifiex_uap_cleanup_tx_queues(struct mwifiex_private *priv) -{ - unsigned long flags; - struct list_head *ra_list; - int i; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; i++, priv->del_list_idx++) { - if (priv->del_list_idx == MAX_NUM_TID) - priv->del_list_idx = 0; - ra_list = &priv->wmm.tid_tbl_ptr[priv->del_list_idx].ra_list; - if (mwifiex_uap_del_tx_pkts_in_ralist(priv, ra_list, i)) { - priv->del_list_idx++; - break; - } - } - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - - -static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct uap_rxpd *uap_rx_pd; - struct rx_packet_hdr *rx_pkt_hdr; - struct sk_buff *new_skb; - struct mwifiex_txinfo *tx_info; - int hdr_chop; - struct ethhdr *p_ethhdr; - struct mwifiex_sta_node *src_node; - - uap_rx_pd = (struct uap_rxpd *)(skb->data); - rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); - - if ((atomic_read(&adapter->pending_bridged_pkts) >= - MWIFIEX_BRIDGED_PKTS_THR_HIGH)) { - mwifiex_dbg(priv->adapter, ERROR, - "Tx: Bridge packet limit reached. Drop packet!\n"); - kfree_skb(skb); - mwifiex_uap_cleanup_tx_queues(priv); - return; - } - - if ((!memcmp(&rx_pkt_hdr->rfc1042_hdr, bridge_tunnel_header, - sizeof(bridge_tunnel_header))) || - (!memcmp(&rx_pkt_hdr->rfc1042_hdr, rfc1042_header, - sizeof(rfc1042_header)) && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_AARP && - ntohs(rx_pkt_hdr->rfc1042_hdr.snap_type) != ETH_P_IPX)) { - /* Replace the 803 header and rfc1042 header (llc/snap) with - * an Ethernet II 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. - */ - p_ethhdr = (struct ethhdr *) - ((u8 *)(&rx_pkt_hdr->eth803_hdr) - + sizeof(rx_pkt_hdr->eth803_hdr) - + sizeof(rx_pkt_hdr->rfc1042_hdr) - - sizeof(rx_pkt_hdr->eth803_hdr.h_dest) - - sizeof(rx_pkt_hdr->eth803_hdr.h_source) - - sizeof(rx_pkt_hdr->rfc1042_hdr.snap_type)); - memcpy(p_ethhdr->h_source, rx_pkt_hdr->eth803_hdr.h_source, - sizeof(p_ethhdr->h_source)); - memcpy(p_ethhdr->h_dest, rx_pkt_hdr->eth803_hdr.h_dest, - sizeof(p_ethhdr->h_dest)); - /* Chop off the rxpd + the excess memory from - * 802.2/llc/snap header that was removed. - */ - hdr_chop = (u8 *)p_ethhdr - (u8 *)uap_rx_pd; - } else { - /* Chop off the rxpd */ - hdr_chop = (u8 *)&rx_pkt_hdr->eth803_hdr - (u8 *)uap_rx_pd; - } - - /* Chop off the leading header bytes so that it points - * to the start of either the reconstructed EthII frame - * or the 802.2/llc/snap frame. - */ - skb_pull(skb, hdr_chop); - - if (skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN) { - mwifiex_dbg(priv->adapter, ERROR, - "data: Tx: insufficient skb headroom %d\n", - skb_headroom(skb)); - /* Insufficient skb headroom - allocate a new skb */ - new_skb = - skb_realloc_headroom(skb, MWIFIEX_MIN_DATA_HEADER_LEN); - if (unlikely(!new_skb)) { - mwifiex_dbg(priv->adapter, ERROR, - "Tx: cannot allocate new_skb\n"); - kfree_skb(skb); - priv->stats.tx_dropped++; - return; - } - - kfree_skb(skb); - skb = new_skb; - mwifiex_dbg(priv->adapter, INFO, - "info: new skb headroom %d\n", - skb_headroom(skb)); - } - - tx_info = MWIFIEX_SKB_TXCB(skb); - memset(tx_info, 0, sizeof(*tx_info)); - tx_info->bss_num = priv->bss_num; - tx_info->bss_type = priv->bss_type; - tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT; - - src_node = mwifiex_get_sta_entry(priv, rx_pkt_hdr->eth803_hdr.h_source); - if (src_node) { - src_node->stats.last_rx = jiffies; - src_node->stats.rx_bytes += skb->len; - src_node->stats.rx_packets++; - src_node->stats.last_tx_rate = uap_rx_pd->rx_rate; - src_node->stats.last_tx_htinfo = uap_rx_pd->ht_info; - } - - if (is_unicast_ether_addr(rx_pkt_hdr->eth803_hdr.h_dest)) { - /* Update bridge packet statistics as the - * packet is not going to kernel/upper layer. - */ - priv->stats.rx_bytes += skb->len; - priv->stats.rx_packets++; - - /* Sending bridge packet to TX queue, so save the packet - * length in TXCB to update statistics in TX complete. - */ - tx_info->pkt_len = skb->len; - } - - __net_timestamp(skb); - mwifiex_wmm_add_buf_txqueue(priv, skb); - atomic_inc(&adapter->tx_pending); - atomic_inc(&adapter->pending_bridged_pkts); - - return; -} - -/* - * This function contains logic for AP packet forwarding. - * - * If a packet is multicast/broadcast, it is sent to kernel/upper layer - * as well as queued back to AP TX queue so that it can be sent to other - * associated stations. - * If a packet is unicast and RA is present in associated station list, - * it is again requeued into AP TX queue. - * If a packet is unicast and RA is not in associated station list, - * packet is forwarded to kernel to handle routing logic. - */ -int mwifiex_handle_uap_rx_forward(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct uap_rxpd *uap_rx_pd; - struct rx_packet_hdr *rx_pkt_hdr; - u8 ra[ETH_ALEN]; - struct sk_buff *skb_uap; - - uap_rx_pd = (struct uap_rxpd *)(skb->data); - rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); - - /* don't do packet forwarding in disconnected state */ - if (!priv->media_connected) { - mwifiex_dbg(adapter, ERROR, - "drop packet in disconnected state.\n"); - dev_kfree_skb_any(skb); - return 0; - } - - memcpy(ra, rx_pkt_hdr->eth803_hdr.h_dest, ETH_ALEN); - - if (is_multicast_ether_addr(ra)) { - skb_uap = skb_copy(skb, GFP_ATOMIC); - mwifiex_uap_queue_bridged_pkt(priv, skb_uap); - } else { - if (mwifiex_get_sta_entry(priv, ra)) { - /* Requeue Intra-BSS packet */ - mwifiex_uap_queue_bridged_pkt(priv, skb); - return 0; - } - } - - /* Forward unicat/Inter-BSS packets to kernel. */ - return mwifiex_process_rx_packet(priv, skb); -} - -/* - * This function processes the packet received on AP interface. - * - * The function looks into the RxPD and performs sanity tests on the - * received buffer to ensure its a valid packet before processing it - * further. If the packet is determined to be aggregated, it is - * de-aggregated accordingly. Then skb is passed to AP packet forwarding logic. - * - * The completion callback is called after processing is complete. - */ -int mwifiex_process_uap_rx_packet(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - int ret; - struct uap_rxpd *uap_rx_pd; - struct rx_packet_hdr *rx_pkt_hdr; - u16 rx_pkt_type; - u8 ta[ETH_ALEN], pkt_type; - unsigned long flags; - struct mwifiex_sta_node *node; - - uap_rx_pd = (struct uap_rxpd *)(skb->data); - rx_pkt_type = le16_to_cpu(uap_rx_pd->rx_pkt_type); - rx_pkt_hdr = (void *)uap_rx_pd + le16_to_cpu(uap_rx_pd->rx_pkt_offset); - - ether_addr_copy(ta, rx_pkt_hdr->eth803_hdr.h_source); - - if ((le16_to_cpu(uap_rx_pd->rx_pkt_offset) + - le16_to_cpu(uap_rx_pd->rx_pkt_length)) > (u16) skb->len) { - mwifiex_dbg(adapter, ERROR, - "wrong rx packet: len=%d, offset=%d, length=%d\n", - skb->len, le16_to_cpu(uap_rx_pd->rx_pkt_offset), - le16_to_cpu(uap_rx_pd->rx_pkt_length)); - priv->stats.rx_dropped++; - - node = mwifiex_get_sta_entry(priv, ta); - if (node) - node->stats.tx_failed++; - - dev_kfree_skb_any(skb); - return 0; - } - - if (rx_pkt_type == PKT_TYPE_MGMT) { - ret = mwifiex_process_mgmt_packet(priv, skb); - if (ret) - mwifiex_dbg(adapter, ERROR, - "Rx of mgmt packet failed"); - dev_kfree_skb_any(skb); - return ret; - } - - - if (rx_pkt_type != PKT_TYPE_BAR && uap_rx_pd->priority < MAX_NUM_TID) { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - node = mwifiex_get_sta_entry(priv, ta); - if (node) - node->rx_seq[uap_rx_pd->priority] = - le16_to_cpu(uap_rx_pd->seq_num); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - } - - if (!priv->ap_11n_enabled || - (!mwifiex_11n_get_rx_reorder_tbl(priv, uap_rx_pd->priority, ta) && - (le16_to_cpu(uap_rx_pd->rx_pkt_type) != PKT_TYPE_AMSDU))) { - ret = mwifiex_handle_uap_rx_forward(priv, skb); - return ret; - } - - /* Reorder and send to kernel */ - pkt_type = (u8)le16_to_cpu(uap_rx_pd->rx_pkt_type); - ret = mwifiex_11n_rx_reorder_pkt(priv, le16_to_cpu(uap_rx_pd->seq_num), - uap_rx_pd->priority, ta, pkt_type, - skb); - - if (ret || (rx_pkt_type == PKT_TYPE_BAR)) - dev_kfree_skb_any(skb); - - if (ret) - priv->stats.rx_dropped++; - - return ret; -} - -/* - * This function fills the TxPD for AP tx packets. - * - * The Tx buffer received by this function should already have the - * header space allocated for TxPD. - * - * This function inserts the TxPD in between interface header and actual - * data and adjusts the buffer pointers accordingly. - * - * The following TxPD fields are set by this function, as required - - * - BSS number - * - Tx packet length and offset - * - Priority - * - Packet delay - * - Priority specific Tx control - * - Flags - */ -void *mwifiex_process_uap_txpd(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct uap_txpd *txpd; - struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - int pad; - u16 pkt_type, pkt_offset; - int hroom = (priv->adapter->iface_type == MWIFIEX_USB) ? 0 : - INTF_HEADER_LEN; - - if (!skb->len) { - mwifiex_dbg(adapter, ERROR, - "Tx: bad packet length: %d\n", skb->len); - tx_info->status_code = -1; - return skb->data; - } - - BUG_ON(skb_headroom(skb) < MWIFIEX_MIN_DATA_HEADER_LEN); - - pkt_type = mwifiex_is_skb_mgmt_frame(skb) ? PKT_TYPE_MGMT : 0; - - pad = ((void *)skb->data - (sizeof(*txpd) + hroom) - NULL) & - (MWIFIEX_DMA_ALIGN_SZ - 1); - - skb_push(skb, sizeof(*txpd) + pad); - - txpd = (struct uap_txpd *)skb->data; - memset(txpd, 0, sizeof(*txpd)); - txpd->bss_num = priv->bss_num; - txpd->bss_type = priv->bss_type; - txpd->tx_pkt_length = cpu_to_le16((u16)(skb->len - (sizeof(*txpd) + - pad))); - txpd->priority = (u8)skb->priority; - - txpd->pkt_delay_2ms = mwifiex_wmm_compute_drv_pkt_delay(priv, skb); - - if (tx_info->flags & MWIFIEX_BUF_FLAG_EAPOL_TX_STATUS || - tx_info->flags & MWIFIEX_BUF_FLAG_ACTION_TX_STATUS) { - txpd->tx_token_id = tx_info->ack_frame_id; - txpd->flags |= MWIFIEX_TXPD_FLAGS_REQ_TX_STATUS; - } - - if (txpd->priority < ARRAY_SIZE(priv->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. - */ - txpd->tx_control = - cpu_to_le32(priv->wmm.user_pri_pkt_tx_ctrl[txpd->priority]); - - /* Offset of actual data */ - pkt_offset = sizeof(*txpd) + pad; - if (pkt_type == PKT_TYPE_MGMT) { - /* Set the packet type and add header for management frame */ - txpd->tx_pkt_type = cpu_to_le16(pkt_type); - pkt_offset += MWIFIEX_MGMT_FRAME_HEADER_SIZE; - } - - txpd->tx_pkt_offset = cpu_to_le16(pkt_offset); - - /* make space for INTF_HEADER_LEN */ - skb_push(skb, hroom); - - if (!txpd->tx_control) - /* TxCtrl set by user or default */ - txpd->tx_control = cpu_to_le32(priv->pkt_tx_ctrl); - - return skb->data; -} diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c deleted file mode 100644 index e43aff932360..000000000000 --- a/drivers/net/wireless/mwifiex/usb.c +++ /dev/null @@ -1,1267 +0,0 @@ -/* - * Marvell Wireless LAN device driver: USB specific handling - * - * Copyright (C) 2012-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "main.h" -#include "usb.h" - -#define USB_VERSION "1.0" - -static u8 user_rmmod; -static struct mwifiex_if_ops usb_ops; -static struct semaphore add_remove_card_sem; - -static struct usb_device_id mwifiex_usb_table[] = { - /* 8766 */ - {USB_DEVICE(USB8XXX_VID, USB8766_PID_1)}, - {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8766_PID_2, - USB_CLASS_VENDOR_SPEC, - USB_SUBCLASS_VENDOR_SPEC, 0xff)}, - /* 8797 */ - {USB_DEVICE(USB8XXX_VID, USB8797_PID_1)}, - {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8797_PID_2, - USB_CLASS_VENDOR_SPEC, - USB_SUBCLASS_VENDOR_SPEC, 0xff)}, - /* 8801 */ - {USB_DEVICE(USB8XXX_VID, USB8801_PID_1)}, - {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8801_PID_2, - USB_CLASS_VENDOR_SPEC, - USB_SUBCLASS_VENDOR_SPEC, 0xff)}, - /* 8997 */ - {USB_DEVICE(USB8XXX_VID, USB8997_PID_1)}, - {USB_DEVICE_AND_INTERFACE_INFO(USB8XXX_VID, USB8997_PID_2, - USB_CLASS_VENDOR_SPEC, - USB_SUBCLASS_VENDOR_SPEC, 0xff)}, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, mwifiex_usb_table); - -static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size); - -/* This function handles received packet. Necessary action is taken based on - * cmd/event/data. - */ -static int mwifiex_usb_recv(struct mwifiex_adapter *adapter, - struct sk_buff *skb, u8 ep) -{ - u32 recv_type; - __le32 tmp; - int ret; - - if (adapter->hs_activated) - mwifiex_process_hs_config(adapter); - - if (skb->len < INTF_HEADER_LEN) { - mwifiex_dbg(adapter, ERROR, - "%s: invalid skb->len\n", __func__); - return -1; - } - - switch (ep) { - case MWIFIEX_USB_EP_CMD_EVENT: - mwifiex_dbg(adapter, EVENT, - "%s: EP_CMD_EVENT\n", __func__); - skb_copy_from_linear_data(skb, &tmp, INTF_HEADER_LEN); - recv_type = le32_to_cpu(tmp); - skb_pull(skb, INTF_HEADER_LEN); - - switch (recv_type) { - case MWIFIEX_USB_TYPE_CMD: - if (skb->len > MWIFIEX_SIZE_OF_CMD_BUFFER) { - mwifiex_dbg(adapter, ERROR, - "CMD: skb->len too large\n"); - ret = -1; - goto exit_restore_skb; - } else if (!adapter->curr_cmd) { - mwifiex_dbg(adapter, WARN, "CMD: no curr_cmd\n"); - if (adapter->ps_state == PS_STATE_SLEEP_CFM) { - mwifiex_process_sleep_confirm_resp( - adapter, skb->data, - skb->len); - ret = 0; - goto exit_restore_skb; - } - ret = -1; - goto exit_restore_skb; - } - - adapter->curr_cmd->resp_skb = skb; - adapter->cmd_resp_received = true; - break; - case MWIFIEX_USB_TYPE_EVENT: - if (skb->len < sizeof(u32)) { - mwifiex_dbg(adapter, ERROR, - "EVENT: skb->len too small\n"); - ret = -1; - goto exit_restore_skb; - } - skb_copy_from_linear_data(skb, &tmp, sizeof(u32)); - adapter->event_cause = le32_to_cpu(tmp); - mwifiex_dbg(adapter, EVENT, - "event_cause %#x\n", adapter->event_cause); - - if (skb->len > MAX_EVENT_SIZE) { - mwifiex_dbg(adapter, ERROR, - "EVENT: event body too large\n"); - ret = -1; - goto exit_restore_skb; - } - - memcpy(adapter->event_body, skb->data + - MWIFIEX_EVENT_HEADER_LEN, skb->len); - - adapter->event_received = true; - adapter->event_skb = skb; - break; - default: - mwifiex_dbg(adapter, ERROR, - "unknown recv_type %#x\n", recv_type); - return -1; - } - break; - case MWIFIEX_USB_EP_DATA: - mwifiex_dbg(adapter, DATA, "%s: EP_DATA\n", __func__); - if (skb->len > MWIFIEX_RX_DATA_BUF_SIZE) { - mwifiex_dbg(adapter, ERROR, - "DATA: skb->len too large\n"); - return -1; - } - - skb_queue_tail(&adapter->rx_data_q, skb); - adapter->data_received = true; - atomic_inc(&adapter->rx_pending); - break; - default: - mwifiex_dbg(adapter, ERROR, - "%s: unknown endport %#x\n", __func__, ep); - return -1; - } - - return -EINPROGRESS; - -exit_restore_skb: - /* The buffer will be reused for further cmds/events */ - skb_push(skb, INTF_HEADER_LEN); - - return ret; -} - -static void mwifiex_usb_rx_complete(struct urb *urb) -{ - struct urb_context *context = (struct urb_context *)urb->context; - struct mwifiex_adapter *adapter = context->adapter; - struct sk_buff *skb = context->skb; - struct usb_card_rec *card; - int recv_length = urb->actual_length; - int size, status; - - if (!adapter || !adapter->card) { - pr_err("mwifiex adapter or card structure is not valid\n"); - return; - } - - card = (struct usb_card_rec *)adapter->card; - if (card->rx_cmd_ep == context->ep) - atomic_dec(&card->rx_cmd_urb_pending); - else - atomic_dec(&card->rx_data_urb_pending); - - if (recv_length) { - if (urb->status || (adapter->surprise_removed)) { - mwifiex_dbg(adapter, ERROR, - "URB status is failed: %d\n", urb->status); - /* Do not free skb in case of command ep */ - if (card->rx_cmd_ep != context->ep) - dev_kfree_skb_any(skb); - goto setup_for_next; - } - if (skb->len > recv_length) - skb_trim(skb, recv_length); - else - skb_put(skb, recv_length - skb->len); - - status = mwifiex_usb_recv(adapter, skb, context->ep); - - mwifiex_dbg(adapter, INFO, - "info: recv_length=%d, status=%d\n", - recv_length, status); - if (status == -EINPROGRESS) { - mwifiex_queue_main_work(adapter); - - /* urb for data_ep is re-submitted now; - * urb for cmd_ep will be re-submitted in callback - * mwifiex_usb_recv_complete - */ - if (card->rx_cmd_ep == context->ep) - return; - } else { - if (status == -1) - mwifiex_dbg(adapter, ERROR, - "received data processing failed!\n"); - - /* Do not free skb in case of command ep */ - if (card->rx_cmd_ep != context->ep) - dev_kfree_skb_any(skb); - } - } else if (urb->status) { - if (!adapter->is_suspended) { - mwifiex_dbg(adapter, FATAL, - "Card is removed: %d\n", urb->status); - adapter->surprise_removed = true; - } - dev_kfree_skb_any(skb); - return; - } else { - /* Do not free skb in case of command ep */ - if (card->rx_cmd_ep != context->ep) - dev_kfree_skb_any(skb); - - /* fall through setup_for_next */ - } - -setup_for_next: - if (card->rx_cmd_ep == context->ep) - size = MWIFIEX_RX_CMD_BUF_SIZE; - else - size = MWIFIEX_RX_DATA_BUF_SIZE; - - if (card->rx_cmd_ep == context->ep) { - mwifiex_usb_submit_rx_urb(context, size); - } else { - if (atomic_read(&adapter->rx_pending) <= HIGH_RX_PENDING){ - mwifiex_usb_submit_rx_urb(context, size); - }else{ - context->skb = NULL; - } - } - - return; -} - -static void mwifiex_usb_tx_complete(struct urb *urb) -{ - struct urb_context *context = (struct urb_context *)(urb->context); - struct mwifiex_adapter *adapter = context->adapter; - struct usb_card_rec *card = adapter->card; - struct usb_tx_data_port *port; - int i; - - mwifiex_dbg(adapter, INFO, - "%s: status: %d\n", __func__, urb->status); - - if (context->ep == card->tx_cmd_ep) { - mwifiex_dbg(adapter, CMD, - "%s: CMD\n", __func__); - atomic_dec(&card->tx_cmd_urb_pending); - adapter->cmd_sent = false; - } else { - mwifiex_dbg(adapter, DATA, - "%s: DATA\n", __func__); - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - port = &card->port[i]; - if (context->ep == port->tx_data_ep) { - atomic_dec(&port->tx_data_urb_pending); - port->block_status = false; - break; - } - } - adapter->data_sent = false; - mwifiex_write_data_complete(adapter, context->skb, 0, - urb->status ? -1 : 0); - } - - if (card->mc_resync_flag) - mwifiex_multi_chan_resync(adapter); - - mwifiex_queue_main_work(adapter); - - return; -} - -static int mwifiex_usb_submit_rx_urb(struct urb_context *ctx, int size) -{ - struct mwifiex_adapter *adapter = ctx->adapter; - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - if (card->rx_cmd_ep != ctx->ep) { - ctx->skb = dev_alloc_skb(size); - if (!ctx->skb) { - mwifiex_dbg(adapter, ERROR, - "%s: dev_alloc_skb failed\n", __func__); - return -ENOMEM; - } - } - - usb_fill_bulk_urb(ctx->urb, card->udev, - usb_rcvbulkpipe(card->udev, ctx->ep), ctx->skb->data, - size, mwifiex_usb_rx_complete, (void *)ctx); - - if (card->rx_cmd_ep == ctx->ep) - atomic_inc(&card->rx_cmd_urb_pending); - else - atomic_inc(&card->rx_data_urb_pending); - - if (usb_submit_urb(ctx->urb, GFP_ATOMIC)) { - mwifiex_dbg(adapter, ERROR, "usb_submit_urb failed\n"); - dev_kfree_skb_any(ctx->skb); - ctx->skb = NULL; - - if (card->rx_cmd_ep == ctx->ep) - atomic_dec(&card->rx_cmd_urb_pending); - else - atomic_dec(&card->rx_data_urb_pending); - - return -1; - } - - return 0; -} - -static void mwifiex_usb_free(struct usb_card_rec *card) -{ - struct usb_tx_data_port *port; - int i, j; - - if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) - usb_kill_urb(card->rx_cmd.urb); - - usb_free_urb(card->rx_cmd.urb); - card->rx_cmd.urb = NULL; - - if (atomic_read(&card->rx_data_urb_pending)) - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) - if (card->rx_data_list[i].urb) - usb_kill_urb(card->rx_data_list[i].urb); - - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { - usb_free_urb(card->rx_data_list[i].urb); - card->rx_data_list[i].urb = NULL; - } - - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - port = &card->port[i]; - for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { - usb_free_urb(port->tx_data_list[j].urb); - port->tx_data_list[j].urb = NULL; - } - } - - usb_free_urb(card->tx_cmd.urb); - card->tx_cmd.urb = NULL; - - return; -} - -/* This function probes an mwifiex device and registers it. It allocates - * the card structure, initiates the device registration and initialization - * procedure by adding a logical interface. - */ -static int mwifiex_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct usb_host_interface *iface_desc = intf->cur_altsetting; - struct usb_endpoint_descriptor *epd; - int ret, i; - struct usb_card_rec *card; - u16 id_vendor, id_product, bcd_device, bcd_usb; - - card = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); - if (!card) - return -ENOMEM; - - id_vendor = le16_to_cpu(udev->descriptor.idVendor); - id_product = le16_to_cpu(udev->descriptor.idProduct); - bcd_device = le16_to_cpu(udev->descriptor.bcdDevice); - bcd_usb = le16_to_cpu(udev->descriptor.bcdUSB); - pr_debug("info: VID/PID = %X/%X, Boot2 version = %X\n", - id_vendor, id_product, bcd_device); - - /* PID_1 is used for firmware downloading only */ - switch (id_product) { - case USB8766_PID_1: - case USB8797_PID_1: - case USB8801_PID_1: - case USB8997_PID_1: - card->usb_boot_state = USB8XXX_FW_DNLD; - break; - case USB8766_PID_2: - case USB8797_PID_2: - case USB8801_PID_2: - case USB8997_PID_2: - card->usb_boot_state = USB8XXX_FW_READY; - break; - default: - pr_warn("unknown id_product %#x\n", id_product); - card->usb_boot_state = USB8XXX_FW_DNLD; - break; - } - - card->udev = udev; - card->intf = intf; - - pr_debug("info: bcdUSB=%#x Device Class=%#x SubClass=%#x Protocol=%#x\n", - udev->descriptor.bcdUSB, udev->descriptor.bDeviceClass, - udev->descriptor.bDeviceSubClass, - udev->descriptor.bDeviceProtocol); - - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - epd = &iface_desc->endpoint[i].desc; - if (usb_endpoint_dir_in(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->rx_cmd_ep = usb_endpoint_num(epd); - atomic_set(&card->rx_cmd_urb_pending, 0); - } - if (usb_endpoint_dir_in(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk IN: max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->rx_data_ep = usb_endpoint_num(epd); - atomic_set(&card->rx_data_urb_pending, 0); - } - if (usb_endpoint_dir_out(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->port[0].tx_data_ep = usb_endpoint_num(epd); - atomic_set(&card->port[0].tx_data_urb_pending, 0); - } - if (usb_endpoint_dir_out(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_DATA_CH2 && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk OUT chan2:\t" - "max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->port[1].tx_data_ep = usb_endpoint_num(epd); - atomic_set(&card->port[1].tx_data_urb_pending, 0); - } - if (usb_endpoint_dir_out(epd) && - usb_endpoint_num(epd) == MWIFIEX_USB_EP_CMD_EVENT && - usb_endpoint_xfer_bulk(epd)) { - pr_debug("info: bulk OUT: max pkt size: %d, addr: %d\n", - le16_to_cpu(epd->wMaxPacketSize), - epd->bEndpointAddress); - card->tx_cmd_ep = usb_endpoint_num(epd); - atomic_set(&card->tx_cmd_urb_pending, 0); - card->bulk_out_maxpktsize = - le16_to_cpu(epd->wMaxPacketSize); - } - } - - usb_set_intfdata(intf, card); - - ret = mwifiex_add_card(card, &add_remove_card_sem, &usb_ops, - MWIFIEX_USB); - if (ret) { - pr_err("%s: mwifiex_add_card failed: %d\n", __func__, ret); - usb_reset_device(udev); - kfree(card); - return ret; - } - - usb_get_dev(udev); - - return 0; -} - -/* Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not suspended, this function allocates and sends a - * 'host sleep activate' request to the firmware and turns off the traffic. - */ -static int mwifiex_usb_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct usb_card_rec *card = usb_get_intfdata(intf); - struct mwifiex_adapter *adapter; - struct usb_tx_data_port *port; - int i, j; - - if (!card || !card->adapter) { - pr_err("%s: card or card->adapter is NULL\n", __func__); - return 0; - } - adapter = card->adapter; - - if (unlikely(adapter->is_suspended)) - mwifiex_dbg(adapter, WARN, - "Device already suspended\n"); - - mwifiex_enable_hs(adapter); - - /* 'is_suspended' flag indicates device is suspended. - * It must be set here before the usb_kill_urb() calls. Reason - * is in the complete handlers, urb->status(= -ENOENT) and - * this flag is used in combination to distinguish between a - * 'suspended' state and a 'disconnect' one. - */ - adapter->is_suspended = true; - adapter->hs_enabling = false; - - if (atomic_read(&card->rx_cmd_urb_pending) && card->rx_cmd.urb) - usb_kill_urb(card->rx_cmd.urb); - - if (atomic_read(&card->rx_data_urb_pending)) - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) - if (card->rx_data_list[i].urb) - usb_kill_urb(card->rx_data_list[i].urb); - - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - port = &card->port[i]; - for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { - if (port->tx_data_list[j].urb) - usb_kill_urb(port->tx_data_list[j].urb); - } - } - - if (card->tx_cmd.urb) - usb_kill_urb(card->tx_cmd.urb); - - return 0; -} - -/* Kernel needs to suspend all functions separately. Therefore all - * registered functions must have drivers with suspend and resume - * methods. Failing that the kernel simply removes the whole card. - * - * If already not resumed, this function turns on the traffic and - * sends a 'host sleep cancel' request to the firmware. - */ -static int mwifiex_usb_resume(struct usb_interface *intf) -{ - struct usb_card_rec *card = usb_get_intfdata(intf); - struct mwifiex_adapter *adapter; - int i; - - if (!card || !card->adapter) { - pr_err("%s: card or card->adapter is NULL\n", __func__); - return 0; - } - adapter = card->adapter; - - if (unlikely(!adapter->is_suspended)) { - mwifiex_dbg(adapter, WARN, - "Device already resumed\n"); - return 0; - } - - /* Indicate device resumed. The netdev queue will be resumed only - * after the urbs have been re-submitted - */ - adapter->is_suspended = false; - - if (!atomic_read(&card->rx_data_urb_pending)) - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) - mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], - MWIFIEX_RX_DATA_BUF_SIZE); - - if (!atomic_read(&card->rx_cmd_urb_pending)) { - card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); - if (card->rx_cmd.skb) - mwifiex_usb_submit_rx_urb(&card->rx_cmd, - MWIFIEX_RX_CMD_BUF_SIZE); - } - - /* Disable Host Sleep */ - if (adapter->hs_activated) - mwifiex_cancel_hs(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY), - MWIFIEX_ASYNC_CMD); - - return 0; -} - -static void mwifiex_usb_disconnect(struct usb_interface *intf) -{ - struct usb_card_rec *card = usb_get_intfdata(intf); - struct mwifiex_adapter *adapter; - - if (!card || !card->adapter) { - pr_err("%s: card or card->adapter is NULL\n", __func__); - return; - } - - adapter = card->adapter; - if (!adapter->priv_num) - return; - - if (user_rmmod) { -#ifdef CONFIG_PM - if (adapter->is_suspended) - mwifiex_usb_resume(intf); -#endif - - mwifiex_deauthenticate_all(adapter); - - mwifiex_init_shutdown_fw(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY), - MWIFIEX_FUNC_SHUTDOWN); - } - - mwifiex_usb_free(card); - - mwifiex_dbg(adapter, FATAL, - "%s: removing card\n", __func__); - mwifiex_remove_card(adapter, &add_remove_card_sem); - - usb_set_intfdata(intf, NULL); - usb_put_dev(interface_to_usbdev(intf)); - kfree(card); - - return; -} - -static struct usb_driver mwifiex_usb_driver = { - .name = "mwifiex_usb", - .probe = mwifiex_usb_probe, - .disconnect = mwifiex_usb_disconnect, - .id_table = mwifiex_usb_table, - .suspend = mwifiex_usb_suspend, - .resume = mwifiex_usb_resume, - .soft_unbind = 1, -}; - -static int mwifiex_usb_tx_init(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - struct usb_tx_data_port *port; - int i, j; - - card->tx_cmd.adapter = adapter; - card->tx_cmd.ep = card->tx_cmd_ep; - - card->tx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->tx_cmd.urb) { - mwifiex_dbg(adapter, ERROR, - "tx_cmd.urb allocation failed\n"); - return -ENOMEM; - } - - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - port = &card->port[i]; - if (!port->tx_data_ep) - continue; - port->tx_data_ix = 0; - if (port->tx_data_ep == MWIFIEX_USB_EP_DATA) - port->block_status = false; - else - port->block_status = true; - for (j = 0; j < MWIFIEX_TX_DATA_URB; j++) { - port->tx_data_list[j].adapter = adapter; - port->tx_data_list[j].ep = port->tx_data_ep; - port->tx_data_list[j].urb = - usb_alloc_urb(0, GFP_KERNEL); - if (!port->tx_data_list[j].urb) { - mwifiex_dbg(adapter, ERROR, - "urb allocation failed\n"); - return -ENOMEM; - } - } - } - - return 0; -} - -static int mwifiex_usb_rx_init(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - int i; - - card->rx_cmd.adapter = adapter; - card->rx_cmd.ep = card->rx_cmd_ep; - - card->rx_cmd.urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->rx_cmd.urb) { - mwifiex_dbg(adapter, ERROR, "rx_cmd.urb allocation failed\n"); - return -ENOMEM; - } - - card->rx_cmd.skb = dev_alloc_skb(MWIFIEX_RX_CMD_BUF_SIZE); - if (!card->rx_cmd.skb) - return -ENOMEM; - - if (mwifiex_usb_submit_rx_urb(&card->rx_cmd, MWIFIEX_RX_CMD_BUF_SIZE)) - return -1; - - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { - card->rx_data_list[i].adapter = adapter; - card->rx_data_list[i].ep = card->rx_data_ep; - - card->rx_data_list[i].urb = usb_alloc_urb(0, GFP_KERNEL); - if (!card->rx_data_list[i].urb) { - mwifiex_dbg(adapter, ERROR, - "rx_data_list[] urb allocation failed\n"); - return -1; - } - if (mwifiex_usb_submit_rx_urb(&card->rx_data_list[i], - MWIFIEX_RX_DATA_BUF_SIZE)) - return -1; - } - - return 0; -} - -static int mwifiex_write_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, - u32 *len, u8 ep, u32 timeout) -{ - struct usb_card_rec *card = adapter->card; - int actual_length, ret; - - if (!(*len % card->bulk_out_maxpktsize)) - (*len)++; - - /* Send the data block */ - ret = usb_bulk_msg(card->udev, usb_sndbulkpipe(card->udev, ep), pbuf, - *len, &actual_length, timeout); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "usb_bulk_msg for tx failed: %d\n", ret); - return ret; - } - - *len = actual_length; - - return ret; -} - -static int mwifiex_read_data_sync(struct mwifiex_adapter *adapter, u8 *pbuf, - u32 *len, u8 ep, u32 timeout) -{ - struct usb_card_rec *card = adapter->card; - int actual_length, ret; - - /* Receive the data response */ - ret = usb_bulk_msg(card->udev, usb_rcvbulkpipe(card->udev, ep), pbuf, - *len, &actual_length, timeout); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "usb_bulk_msg for rx failed: %d\n", ret); - return ret; - } - - *len = actual_length; - - return ret; -} - -static void mwifiex_usb_port_resync(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = adapter->card; - u8 active_port = MWIFIEX_USB_EP_DATA; - struct mwifiex_private *priv = NULL; - int i; - - if (adapter->usb_mc_status) { - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && - !priv->bss_started) || - (priv->bss_role == MWIFIEX_BSS_ROLE_STA && - !priv->media_connected)) - priv->usb_port = MWIFIEX_USB_EP_DATA; - } - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) - card->port[i].block_status = false; - } else { - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if ((priv->bss_role == MWIFIEX_BSS_ROLE_UAP && - priv->bss_started) || - (priv->bss_role == MWIFIEX_BSS_ROLE_STA && - priv->media_connected)) { - active_port = priv->usb_port; - break; - } - } - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (priv) - priv->usb_port = active_port; - } - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) { - if (active_port == card->port[i].tx_data_ep) - card->port[i].block_status = false; - else - card->port[i].block_status = true; - } - } -} - -static bool mwifiex_usb_is_port_ready(struct mwifiex_private *priv) -{ - struct usb_card_rec *card = priv->adapter->card; - int idx; - - for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { - if (priv->usb_port == card->port[idx].tx_data_ep) - return !card->port[idx].block_status; - } - - return false; -} - -static inline u8 mwifiex_usb_data_sent(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = adapter->card; - int i; - - for (i = 0; i < MWIFIEX_TX_DATA_PORT; i++) - if (!card->port[i].block_status) - return false; - - return true; -} - -/* This function write a command/data packet to card. */ -static int mwifiex_usb_host_to_card(struct mwifiex_adapter *adapter, u8 ep, - struct sk_buff *skb, - struct mwifiex_tx_param *tx_param) -{ - struct usb_card_rec *card = adapter->card; - struct urb_context *context = NULL; - struct usb_tx_data_port *port = NULL; - u8 *data = (u8 *)skb->data; - struct urb *tx_urb; - int idx, ret; - - if (adapter->is_suspended) { - mwifiex_dbg(adapter, ERROR, - "%s: not allowed while suspended\n", __func__); - return -1; - } - - if (adapter->surprise_removed) { - mwifiex_dbg(adapter, ERROR, "%s: device removed\n", __func__); - return -1; - } - - mwifiex_dbg(adapter, INFO, "%s: ep=%d\n", __func__, ep); - - if (ep == card->tx_cmd_ep) { - context = &card->tx_cmd; - } else { - for (idx = 0; idx < MWIFIEX_TX_DATA_PORT; idx++) { - if (ep == card->port[idx].tx_data_ep) { - port = &card->port[idx]; - if (atomic_read(&port->tx_data_urb_pending) - >= MWIFIEX_TX_DATA_URB) { - port->block_status = true; - ret = -EBUSY; - goto done; - } - if (port->tx_data_ix >= MWIFIEX_TX_DATA_URB) - port->tx_data_ix = 0; - context = - &port->tx_data_list[port->tx_data_ix++]; - break; - } - } - if (!port) { - mwifiex_dbg(adapter, ERROR, "Wrong usb tx data port\n"); - return -1; - } - } - - context->adapter = adapter; - context->ep = ep; - context->skb = skb; - tx_urb = context->urb; - - usb_fill_bulk_urb(tx_urb, card->udev, usb_sndbulkpipe(card->udev, ep), - data, skb->len, mwifiex_usb_tx_complete, - (void *)context); - - tx_urb->transfer_flags |= URB_ZERO_PACKET; - - if (ep == card->tx_cmd_ep) - atomic_inc(&card->tx_cmd_urb_pending); - else - atomic_inc(&port->tx_data_urb_pending); - - if (usb_submit_urb(tx_urb, GFP_ATOMIC)) { - mwifiex_dbg(adapter, ERROR, - "%s: usb_submit_urb failed\n", __func__); - if (ep == card->tx_cmd_ep) { - atomic_dec(&card->tx_cmd_urb_pending); - } else { - atomic_dec(&port->tx_data_urb_pending); - port->block_status = false; - if (port->tx_data_ix) - port->tx_data_ix--; - else - port->tx_data_ix = MWIFIEX_TX_DATA_URB; - } - - return -1; - } else { - if (ep != card->tx_cmd_ep && - atomic_read(&port->tx_data_urb_pending) == - MWIFIEX_TX_DATA_URB) { - port->block_status = true; - ret = -ENOSR; - goto done; - } - } - - return -EINPROGRESS; - -done: - if (ep != card->tx_cmd_ep) - adapter->data_sent = mwifiex_usb_data_sent(adapter); - - return ret; -} - -/* This function register usb device and initialize parameter. */ -static int mwifiex_register_dev(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - card->adapter = adapter; - adapter->dev = &card->udev->dev; - - switch (le16_to_cpu(card->udev->descriptor.idProduct)) { - case USB8997_PID_1: - case USB8997_PID_2: - adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K; - strcpy(adapter->fw_name, USB8997_DEFAULT_FW_NAME); - adapter->ext_scan = true; - break; - case USB8766_PID_1: - case USB8766_PID_2: - adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; - strcpy(adapter->fw_name, USB8766_DEFAULT_FW_NAME); - adapter->ext_scan = true; - break; - case USB8801_PID_1: - case USB8801_PID_2: - adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; - strcpy(adapter->fw_name, USB8801_DEFAULT_FW_NAME); - adapter->ext_scan = false; - break; - case USB8797_PID_1: - case USB8797_PID_2: - default: - adapter->tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K; - strcpy(adapter->fw_name, USB8797_DEFAULT_FW_NAME); - break; - } - - adapter->usb_mc_status = false; - adapter->usb_mc_setup = false; - - return 0; -} - -static void mwifiex_unregister_dev(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - card->adapter = NULL; -} - -static int mwifiex_prog_fw_w_helper(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *fw) -{ - int ret = 0; - u8 *firmware = fw->fw_buf, *recv_buff; - u32 retries = USB8XXX_FW_MAX_RETRY, dlen; - u32 fw_seqnum = 0, tlen = 0, dnld_cmd = 0; - struct fw_data *fwdata; - struct fw_sync_header sync_fw; - u8 check_winner = 1; - - if (!firmware) { - mwifiex_dbg(adapter, ERROR, - "No firmware image found! Terminating download\n"); - ret = -1; - goto fw_exit; - } - - /* Allocate memory for transmit */ - fwdata = kzalloc(FW_DNLD_TX_BUF_SIZE, GFP_KERNEL); - if (!fwdata) { - ret = -ENOMEM; - goto fw_exit; - } - - /* Allocate memory for receive */ - recv_buff = kzalloc(FW_DNLD_RX_BUF_SIZE, GFP_KERNEL); - if (!recv_buff) - goto cleanup; - - do { - /* Send pseudo data to check winner status first */ - if (check_winner) { - memset(&fwdata->fw_hdr, 0, sizeof(struct fw_header)); - dlen = 0; - } else { - /* copy the header of the fw_data to get the length */ - memcpy(&fwdata->fw_hdr, &firmware[tlen], - sizeof(struct fw_header)); - - dlen = le32_to_cpu(fwdata->fw_hdr.data_len); - dnld_cmd = le32_to_cpu(fwdata->fw_hdr.dnld_cmd); - tlen += sizeof(struct fw_header); - - memcpy(fwdata->data, &firmware[tlen], dlen); - - fwdata->seq_num = cpu_to_le32(fw_seqnum); - tlen += dlen; - } - - /* If the send/receive fails or CRC occurs then retry */ - while (retries--) { - u8 *buf = (u8 *)fwdata; - u32 len = FW_DATA_XMIT_SIZE; - - /* send the firmware block */ - ret = mwifiex_write_data_sync(adapter, buf, &len, - MWIFIEX_USB_EP_CMD_EVENT, - MWIFIEX_USB_TIMEOUT); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "write_data_sync: failed: %d\n", - ret); - continue; - } - - buf = recv_buff; - len = FW_DNLD_RX_BUF_SIZE; - - /* Receive the firmware block response */ - ret = mwifiex_read_data_sync(adapter, buf, &len, - MWIFIEX_USB_EP_CMD_EVENT, - MWIFIEX_USB_TIMEOUT); - if (ret) { - mwifiex_dbg(adapter, ERROR, - "read_data_sync: failed: %d\n", - ret); - continue; - } - - memcpy(&sync_fw, recv_buff, - sizeof(struct fw_sync_header)); - - /* check 1st firmware block resp for highest bit set */ - if (check_winner) { - if (le32_to_cpu(sync_fw.cmd) & 0x80000000) { - mwifiex_dbg(adapter, WARN, - "USB is not the winner %#x\n", - sync_fw.cmd); - - /* returning success */ - ret = 0; - goto cleanup; - } - - mwifiex_dbg(adapter, MSG, - "start to download FW...\n"); - - check_winner = 0; - break; - } - - /* check the firmware block response for CRC errors */ - if (sync_fw.cmd) { - mwifiex_dbg(adapter, ERROR, - "FW received block with CRC %#x\n", - sync_fw.cmd); - ret = -1; - continue; - } - - retries = USB8XXX_FW_MAX_RETRY; - break; - } - fw_seqnum++; - } while ((dnld_cmd != FW_HAS_LAST_BLOCK) && retries); - -cleanup: - mwifiex_dbg(adapter, MSG, - "info: FW download over, size %d bytes\n", tlen); - - kfree(recv_buff); - kfree(fwdata); - - if (retries) - ret = 0; -fw_exit: - return ret; -} - -static int mwifiex_usb_dnld_fw(struct mwifiex_adapter *adapter, - struct mwifiex_fw_image *fw) -{ - int ret; - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - if (card->usb_boot_state == USB8XXX_FW_DNLD) { - ret = mwifiex_prog_fw_w_helper(adapter, fw); - if (ret) - return -1; - - /* Boot state changes after successful firmware download */ - if (card->usb_boot_state == USB8XXX_FW_DNLD) - return -1; - } - - ret = mwifiex_usb_rx_init(adapter); - if (!ret) - ret = mwifiex_usb_tx_init(adapter); - - return ret; -} - -static void mwifiex_submit_rx_urb(struct mwifiex_adapter *adapter, u8 ep) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - - skb_push(card->rx_cmd.skb, INTF_HEADER_LEN); - if ((ep == card->rx_cmd_ep) && - (!atomic_read(&card->rx_cmd_urb_pending))) - mwifiex_usb_submit_rx_urb(&card->rx_cmd, - MWIFIEX_RX_CMD_BUF_SIZE); - - return; -} - -static int mwifiex_usb_cmd_event_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb) -{ - mwifiex_submit_rx_urb(adapter, MWIFIEX_USB_EP_CMD_EVENT); - - return 0; -} - -/* This function wakes up the card. */ -static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) -{ - /* Simulation of HS_AWAKE event */ - adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); - adapter->pm_wakeup_card_req = false; - adapter->ps_state = PS_STATE_AWAKE; - - return 0; -} - -static void mwifiex_usb_submit_rem_rx_urbs(struct mwifiex_adapter *adapter) -{ - struct usb_card_rec *card = (struct usb_card_rec *)adapter->card; - int i; - struct urb_context *ctx; - - for (i = 0; i < MWIFIEX_RX_DATA_URB; i++) { - if (card->rx_data_list[i].skb) - continue; - ctx = &card->rx_data_list[i]; - mwifiex_usb_submit_rx_urb(ctx, MWIFIEX_RX_DATA_BUF_SIZE); - } -} - -/* This function is called after the card has woken up. */ -static inline int -mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) -{ - return 0; -} - -static struct mwifiex_if_ops usb_ops = { - .register_dev = mwifiex_register_dev, - .unregister_dev = mwifiex_unregister_dev, - .wakeup = mwifiex_pm_wakeup_card, - .wakeup_complete = mwifiex_pm_wakeup_card_complete, - - /* USB specific */ - .dnld_fw = mwifiex_usb_dnld_fw, - .cmdrsp_complete = mwifiex_usb_cmd_event_complete, - .event_complete = mwifiex_usb_cmd_event_complete, - .host_to_card = mwifiex_usb_host_to_card, - .submit_rem_rx_urbs = mwifiex_usb_submit_rem_rx_urbs, - .multi_port_resync = mwifiex_usb_port_resync, - .is_port_ready = mwifiex_usb_is_port_ready, -}; - -/* This function initializes the USB driver module. - * - * This initiates the semaphore and registers the device with - * USB bus. - */ -static int mwifiex_usb_init_module(void) -{ - int ret; - - pr_debug("Marvell USB8797 Driver\n"); - - sema_init(&add_remove_card_sem, 1); - - ret = usb_register(&mwifiex_usb_driver); - if (ret) - pr_err("Driver register failed!\n"); - else - pr_debug("info: Driver registered successfully!\n"); - - return ret; -} - -/* This function cleans up the USB driver. - * - * The following major steps are followed in .disconnect for cleanup: - * - Resume the device if its suspended - * - Disconnect the device if connected - * - Shutdown the firmware - * - Unregister the device from USB bus. - */ -static void mwifiex_usb_cleanup_module(void) -{ - if (!down_interruptible(&add_remove_card_sem)) - up(&add_remove_card_sem); - - /* set the flag as user is removing this module */ - user_rmmod = 1; - - usb_deregister(&mwifiex_usb_driver); -} - -module_init(mwifiex_usb_init_module); -module_exit(mwifiex_usb_cleanup_module); - -MODULE_AUTHOR("Marvell International Ltd."); -MODULE_DESCRIPTION("Marvell WiFi-Ex USB Driver version" USB_VERSION); -MODULE_VERSION(USB_VERSION); -MODULE_LICENSE("GPL v2"); -MODULE_FIRMWARE(USB8766_DEFAULT_FW_NAME); -MODULE_FIRMWARE(USB8797_DEFAULT_FW_NAME); -MODULE_FIRMWARE(USB8801_DEFAULT_FW_NAME); -MODULE_FIRMWARE(USB8997_DEFAULT_FW_NAME); diff --git a/drivers/net/wireless/mwifiex/usb.h b/drivers/net/wireless/mwifiex/usb.h deleted file mode 100644 index b4e9246bbcdc..000000000000 --- a/drivers/net/wireless/mwifiex/usb.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file contains definitions for mwifiex USB interface driver. - * - * Copyright (C) 2012-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_USB_H -#define _MWIFIEX_USB_H - -#include - -#define USB8XXX_VID 0x1286 - -#define USB8766_PID_1 0x2041 -#define USB8766_PID_2 0x2042 -#define USB8797_PID_1 0x2043 -#define USB8797_PID_2 0x2044 -#define USB8801_PID_1 0x2049 -#define USB8801_PID_2 0x204a -#define USB8997_PID_1 0x2052 -#define USB8997_PID_2 0x204e - - -#define USB8XXX_FW_DNLD 1 -#define USB8XXX_FW_READY 2 -#define USB8XXX_FW_MAX_RETRY 3 - -#define MWIFIEX_TX_DATA_PORT 2 -#define MWIFIEX_TX_DATA_URB 6 -#define MWIFIEX_RX_DATA_URB 6 -#define MWIFIEX_USB_TIMEOUT 100 - -#define USB8766_DEFAULT_FW_NAME "mrvl/usb8766_uapsta.bin" -#define USB8797_DEFAULT_FW_NAME "mrvl/usb8797_uapsta.bin" -#define USB8801_DEFAULT_FW_NAME "mrvl/usb8801_uapsta.bin" -#define USB8997_DEFAULT_FW_NAME "mrvl/usb8997_uapsta.bin" - -#define FW_DNLD_TX_BUF_SIZE 620 -#define FW_DNLD_RX_BUF_SIZE 2048 -#define FW_HAS_LAST_BLOCK 0x00000004 - -#define FW_DATA_XMIT_SIZE \ - (sizeof(struct fw_header) + dlen + sizeof(u32)) - -struct urb_context { - struct mwifiex_adapter *adapter; - struct sk_buff *skb; - struct urb *urb; - u8 ep; -}; - -struct usb_tx_data_port { - u8 tx_data_ep; - u8 block_status; - atomic_t tx_data_urb_pending; - int tx_data_ix; - struct urb_context tx_data_list[MWIFIEX_TX_DATA_URB]; -}; - -struct usb_card_rec { - struct mwifiex_adapter *adapter; - struct usb_device *udev; - struct usb_interface *intf; - u8 rx_cmd_ep; - struct urb_context rx_cmd; - atomic_t rx_cmd_urb_pending; - struct urb_context rx_data_list[MWIFIEX_RX_DATA_URB]; - u8 usb_boot_state; - u8 rx_data_ep; - atomic_t rx_data_urb_pending; - u8 tx_cmd_ep; - atomic_t tx_cmd_urb_pending; - int bulk_out_maxpktsize; - struct urb_context tx_cmd; - u8 mc_resync_flag; - struct usb_tx_data_port port[MWIFIEX_TX_DATA_PORT]; -}; - -struct fw_header { - __le32 dnld_cmd; - __le32 base_addr; - __le32 data_len; - __le32 crc; -}; - -struct fw_sync_header { - __le32 cmd; - __le32 seq_num; -}; - -struct fw_data { - struct fw_header fw_hdr; - __le32 seq_num; - u8 data[1]; -}; - -#endif /*_MWIFIEX_USB_H */ diff --git a/drivers/net/wireless/mwifiex/util.c b/drivers/net/wireless/mwifiex/util.c deleted file mode 100644 index 0cec8a64473e..000000000000 --- a/drivers/net/wireless/mwifiex/util.c +++ /dev/null @@ -1,751 +0,0 @@ -/* - * Marvell Wireless LAN device driver: utility functions - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - -static struct mwifiex_debug_data items[] = { - {"debug_mask", item_size(debug_mask), - item_addr(debug_mask), 1}, - {"int_counter", item_size(int_counter), - item_addr(int_counter), 1}, - {"wmm_ac_vo", item_size(packets_out[WMM_AC_VO]), - item_addr(packets_out[WMM_AC_VO]), 1}, - {"wmm_ac_vi", item_size(packets_out[WMM_AC_VI]), - item_addr(packets_out[WMM_AC_VI]), 1}, - {"wmm_ac_be", item_size(packets_out[WMM_AC_BE]), - item_addr(packets_out[WMM_AC_BE]), 1}, - {"wmm_ac_bk", item_size(packets_out[WMM_AC_BK]), - item_addr(packets_out[WMM_AC_BK]), 1}, - {"tx_buf_size", item_size(tx_buf_size), - item_addr(tx_buf_size), 1}, - {"curr_tx_buf_size", item_size(curr_tx_buf_size), - item_addr(curr_tx_buf_size), 1}, - {"ps_mode", item_size(ps_mode), - item_addr(ps_mode), 1}, - {"ps_state", item_size(ps_state), - item_addr(ps_state), 1}, - {"is_deep_sleep", item_size(is_deep_sleep), - item_addr(is_deep_sleep), 1}, - {"wakeup_dev_req", item_size(pm_wakeup_card_req), - item_addr(pm_wakeup_card_req), 1}, - {"wakeup_tries", item_size(pm_wakeup_fw_try), - item_addr(pm_wakeup_fw_try), 1}, - {"hs_configured", item_size(is_hs_configured), - item_addr(is_hs_configured), 1}, - {"hs_activated", item_size(hs_activated), - item_addr(hs_activated), 1}, - {"num_tx_timeout", item_size(num_tx_timeout), - item_addr(num_tx_timeout), 1}, - {"is_cmd_timedout", item_size(is_cmd_timedout), - item_addr(is_cmd_timedout), 1}, - {"timeout_cmd_id", item_size(timeout_cmd_id), - item_addr(timeout_cmd_id), 1}, - {"timeout_cmd_act", item_size(timeout_cmd_act), - item_addr(timeout_cmd_act), 1}, - {"last_cmd_id", item_size(last_cmd_id), - item_addr(last_cmd_id), DBG_CMD_NUM}, - {"last_cmd_act", item_size(last_cmd_act), - item_addr(last_cmd_act), DBG_CMD_NUM}, - {"last_cmd_index", item_size(last_cmd_index), - item_addr(last_cmd_index), 1}, - {"last_cmd_resp_id", item_size(last_cmd_resp_id), - item_addr(last_cmd_resp_id), DBG_CMD_NUM}, - {"last_cmd_resp_index", item_size(last_cmd_resp_index), - item_addr(last_cmd_resp_index), 1}, - {"last_event", item_size(last_event), - item_addr(last_event), DBG_CMD_NUM}, - {"last_event_index", item_size(last_event_index), - item_addr(last_event_index), 1}, - {"num_cmd_h2c_fail", item_size(num_cmd_host_to_card_failure), - item_addr(num_cmd_host_to_card_failure), 1}, - {"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), 1}, - {"num_tx_h2c_fail", item_size(num_tx_host_to_card_failure), - item_addr(num_tx_host_to_card_failure), 1}, - {"num_evt_deauth", item_size(num_event_deauth), - item_addr(num_event_deauth), 1}, - {"num_evt_disassoc", item_size(num_event_disassoc), - item_addr(num_event_disassoc), 1}, - {"num_evt_link_lost", item_size(num_event_link_lost), - item_addr(num_event_link_lost), 1}, - {"num_cmd_deauth", item_size(num_cmd_deauth), - item_addr(num_cmd_deauth), 1}, - {"num_cmd_assoc_ok", item_size(num_cmd_assoc_success), - item_addr(num_cmd_assoc_success), 1}, - {"num_cmd_assoc_fail", item_size(num_cmd_assoc_failure), - item_addr(num_cmd_assoc_failure), 1}, - {"cmd_sent", item_size(cmd_sent), - item_addr(cmd_sent), 1}, - {"data_sent", item_size(data_sent), - item_addr(data_sent), 1}, - {"cmd_resp_received", item_size(cmd_resp_received), - item_addr(cmd_resp_received), 1}, - {"event_received", item_size(event_received), - item_addr(event_received), 1}, - - /* variables defined in struct mwifiex_adapter */ - {"cmd_pending", adapter_item_size(cmd_pending), - adapter_item_addr(cmd_pending), 1}, - {"tx_pending", adapter_item_size(tx_pending), - adapter_item_addr(tx_pending), 1}, - {"rx_pending", adapter_item_size(rx_pending), - adapter_item_addr(rx_pending), 1}, -}; - -static int num_of_items = ARRAY_SIZE(items); - -/* - * Firmware initialization complete callback handler. - * - * This function wakes up the function waiting on the init - * wait queue for the firmware initialization to complete. - */ -int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter) -{ - - if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) - if (adapter->if_ops.init_fw_port) - adapter->if_ops.init_fw_port(adapter); - - adapter->init_wait_q_woken = true; - wake_up_interruptible(&adapter->init_wait_q); - return 0; -} - -/* - * Firmware shutdown complete callback handler. - * - * This function sets the hardware status to not ready and wakes up - * the function waiting on the init wait queue for the firmware - * shutdown to complete. - */ -int mwifiex_shutdown_fw_complete(struct mwifiex_adapter *adapter) -{ - adapter->hw_status = MWIFIEX_HW_STATUS_NOT_READY; - adapter->init_wait_q_woken = true; - wake_up_interruptible(&adapter->init_wait_q); - return 0; -} - -/* - * This function sends init/shutdown command - * to firmware. - */ -int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, - u32 func_init_shutdown) -{ - u16 cmd; - - if (func_init_shutdown == MWIFIEX_FUNC_INIT) { - cmd = HostCmd_CMD_FUNC_INIT; - } else if (func_init_shutdown == MWIFIEX_FUNC_SHUTDOWN) { - cmd = HostCmd_CMD_FUNC_SHUTDOWN; - } else { - mwifiex_dbg(priv->adapter, ERROR, - "unsupported parameter\n"); - return -1; - } - - return mwifiex_send_cmd(priv, cmd, HostCmd_ACT_GEN_SET, 0, NULL, true); -} -EXPORT_SYMBOL_GPL(mwifiex_init_shutdown_fw); - -/* - * IOCTL request handler to set/get debug information. - * - * This function collates/sets the information from/to different driver - * structures. - */ -int mwifiex_get_debug_info(struct mwifiex_private *priv, - struct mwifiex_debug_info *info) -{ - struct mwifiex_adapter *adapter = priv->adapter; - - if (info) { - info->debug_mask = adapter->debug_mask; - memcpy(info->packets_out, - priv->wmm.packets_out, - sizeof(priv->wmm.packets_out)); - info->curr_tx_buf_size = (u32) adapter->curr_tx_buf_size; - info->tx_buf_size = (u32) adapter->tx_buf_size; - info->rx_tbl_num = mwifiex_get_rx_reorder_tbl(priv, - info->rx_tbl); - info->tx_tbl_num = mwifiex_get_tx_ba_stream_tbl(priv, - info->tx_tbl); - info->tdls_peer_num = mwifiex_get_tdls_list(priv, - info->tdls_list); - info->ps_mode = adapter->ps_mode; - info->ps_state = adapter->ps_state; - info->is_deep_sleep = adapter->is_deep_sleep; - info->pm_wakeup_card_req = adapter->pm_wakeup_card_req; - info->pm_wakeup_fw_try = adapter->pm_wakeup_fw_try; - info->is_hs_configured = adapter->is_hs_configured; - info->hs_activated = adapter->hs_activated; - info->is_cmd_timedout = adapter->is_cmd_timedout; - info->num_cmd_host_to_card_failure - = adapter->dbg.num_cmd_host_to_card_failure; - info->num_cmd_sleep_cfm_host_to_card_failure - = adapter->dbg.num_cmd_sleep_cfm_host_to_card_failure; - info->num_tx_host_to_card_failure - = adapter->dbg.num_tx_host_to_card_failure; - info->num_event_deauth = adapter->dbg.num_event_deauth; - info->num_event_disassoc = adapter->dbg.num_event_disassoc; - info->num_event_link_lost = adapter->dbg.num_event_link_lost; - info->num_cmd_deauth = adapter->dbg.num_cmd_deauth; - info->num_cmd_assoc_success = - adapter->dbg.num_cmd_assoc_success; - info->num_cmd_assoc_failure = - adapter->dbg.num_cmd_assoc_failure; - info->num_tx_timeout = adapter->dbg.num_tx_timeout; - info->timeout_cmd_id = adapter->dbg.timeout_cmd_id; - info->timeout_cmd_act = adapter->dbg.timeout_cmd_act; - memcpy(info->last_cmd_id, adapter->dbg.last_cmd_id, - sizeof(adapter->dbg.last_cmd_id)); - memcpy(info->last_cmd_act, adapter->dbg.last_cmd_act, - sizeof(adapter->dbg.last_cmd_act)); - info->last_cmd_index = adapter->dbg.last_cmd_index; - memcpy(info->last_cmd_resp_id, adapter->dbg.last_cmd_resp_id, - sizeof(adapter->dbg.last_cmd_resp_id)); - info->last_cmd_resp_index = adapter->dbg.last_cmd_resp_index; - memcpy(info->last_event, adapter->dbg.last_event, - sizeof(adapter->dbg.last_event)); - info->last_event_index = adapter->dbg.last_event_index; - info->data_sent = adapter->data_sent; - info->cmd_sent = adapter->cmd_sent; - info->cmd_resp_received = adapter->cmd_resp_received; - } - - return 0; -} - -int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, - struct mwifiex_debug_info *info) -{ - char *p = buf; - struct mwifiex_debug_data *d = &items[0]; - size_t size, addr; - long val; - int i, j; - - if (!info) - return 0; - - for (i = 0; i < num_of_items; i++) { - p += sprintf(p, "%s=", d[i].name); - - size = d[i].size / d[i].num; - - if (i < (num_of_items - 3)) - addr = d[i].addr + (size_t)info; - else /* The last 3 items are struct mwifiex_adapter variables */ - addr = d[i].addr + (size_t)priv->adapter; - - for (j = 0; j < d[i].num; j++) { - switch (size) { - case 1: - val = *((u8 *)addr); - break; - case 2: - val = *((u16 *)addr); - break; - case 4: - val = *((u32 *)addr); - break; - case 8: - val = *((long long *)addr); - break; - default: - val = -1; - break; - } - - p += sprintf(p, "%#lx ", val); - addr += size; - } - - p += sprintf(p, "\n"); - } - - if (info->tx_tbl_num) { - p += sprintf(p, "Tx BA stream table:\n"); - for (i = 0; i < info->tx_tbl_num; i++) - p += sprintf(p, "tid = %d, ra = %pM\n", - info->tx_tbl[i].tid, info->tx_tbl[i].ra); - } - - if (info->rx_tbl_num) { - p += sprintf(p, "Rx reorder table:\n"); - for (i = 0; i < info->rx_tbl_num; i++) { - p += sprintf(p, "tid = %d, ta = %pM, ", - info->rx_tbl[i].tid, - info->rx_tbl[i].ta); - p += sprintf(p, "start_win = %d, ", - info->rx_tbl[i].start_win); - p += sprintf(p, "win_size = %d, buffer: ", - info->rx_tbl[i].win_size); - - for (j = 0; j < info->rx_tbl[i].win_size; j++) - p += sprintf(p, "%c ", - info->rx_tbl[i].buffer[j] ? - '1' : '0'); - - p += sprintf(p, "\n"); - } - } - - if (info->tdls_peer_num) { - p += sprintf(p, "TDLS peer table:\n"); - for (i = 0; i < info->tdls_peer_num; i++) { - p += sprintf(p, "peer = %pM", - info->tdls_list[i].peer_addr); - p += sprintf(p, "\n"); - } - } - - return p - buf; -} - -static int -mwifiex_parse_mgmt_packet(struct mwifiex_private *priv, u8 *payload, u16 len, - struct rxpd *rx_pd) -{ - u16 stype; - u8 category, action_code, *addr2; - struct ieee80211_hdr *ieee_hdr = (void *)payload; - - stype = (le16_to_cpu(ieee_hdr->frame_control) & IEEE80211_FCTL_STYPE); - - switch (stype) { - case IEEE80211_STYPE_ACTION: - category = *(payload + sizeof(struct ieee80211_hdr)); - switch (category) { - case WLAN_CATEGORY_PUBLIC: - action_code = *(payload + sizeof(struct ieee80211_hdr) - + 1); - if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) { - addr2 = ieee_hdr->addr2; - mwifiex_dbg(priv->adapter, INFO, - "TDLS discovery response %pM nf=%d, snr=%d\n", - addr2, rx_pd->nf, rx_pd->snr); - mwifiex_auto_tdls_update_peer_signal(priv, - addr2, - rx_pd->snr, - rx_pd->nf); - } - break; - case WLAN_CATEGORY_BACK: - /*we dont indicate BACK action frames to cfg80211*/ - mwifiex_dbg(priv->adapter, INFO, - "drop BACK action frames"); - return -1; - default: - mwifiex_dbg(priv->adapter, INFO, - "unknown public action frame category %d\n", - category); - } - default: - mwifiex_dbg(priv->adapter, INFO, - "unknown mgmt frame subtype %#x\n", stype); - return 0; - } - - return 0; -} -/* - * This function processes the received management packet and send it - * to the kernel. - */ -int -mwifiex_process_mgmt_packet(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct rxpd *rx_pd; - u16 pkt_len; - struct ieee80211_hdr *ieee_hdr; - - if (!skb) - return -1; - - if (!priv->mgmt_frame_mask || - priv->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) { - mwifiex_dbg(priv->adapter, ERROR, - "do not receive mgmt frames on uninitialized intf"); - return -1; - } - - rx_pd = (struct rxpd *)skb->data; - - skb_pull(skb, le16_to_cpu(rx_pd->rx_pkt_offset)); - skb_pull(skb, sizeof(pkt_len)); - - pkt_len = le16_to_cpu(rx_pd->rx_pkt_length); - - ieee_hdr = (void *)skb->data; - if (ieee80211_is_mgmt(ieee_hdr->frame_control)) { - if (mwifiex_parse_mgmt_packet(priv, (u8 *)ieee_hdr, - pkt_len, rx_pd)) - return -1; - } - /* Remove address4 */ - memmove(skb->data + sizeof(struct ieee80211_hdr_3addr), - skb->data + sizeof(struct ieee80211_hdr), - pkt_len - sizeof(struct ieee80211_hdr)); - - pkt_len -= ETH_ALEN + sizeof(pkt_len); - rx_pd->rx_pkt_length = cpu_to_le16(pkt_len); - - cfg80211_rx_mgmt(&priv->wdev, priv->roc_cfg.chan.center_freq, - CAL_RSSI(rx_pd->snr, rx_pd->nf), skb->data, pkt_len, - 0); - - return 0; -} - -/* - * This function processes the received packet before sending it to the - * kernel. - * - * It extracts the SKB from the received buffer and sends it to kernel. - * In case the received buffer does not contain the data in SKB format, - * the function creates a blank SKB, fills it with the data from the - * received buffer and then sends this new SKB to the kernel. - */ -int mwifiex_recv_packet(struct mwifiex_private *priv, struct sk_buff *skb) -{ - struct mwifiex_sta_node *src_node; - struct ethhdr *p_ethhdr; - - if (!skb) - return -1; - - priv->stats.rx_bytes += skb->len; - priv->stats.rx_packets++; - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_UAP) { - p_ethhdr = (void *)skb->data; - src_node = mwifiex_get_sta_entry(priv, p_ethhdr->h_source); - if (src_node) { - src_node->stats.last_rx = jiffies; - src_node->stats.rx_bytes += skb->len; - src_node->stats.rx_packets++; - } - } - - skb->dev = priv->netdev; - skb->protocol = eth_type_trans(skb, priv->netdev); - skb->ip_summed = CHECKSUM_NONE; - - /* This is required only in case of 11n and USB/PCIE as we alloc - * 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 ((priv->adapter->iface_type == MWIFIEX_USB || - priv->adapter->iface_type == MWIFIEX_PCIE) && - (skb->truesize > MWIFIEX_RX_DATA_BUF_SIZE)) - skb->truesize += (skb->len - MWIFIEX_RX_DATA_BUF_SIZE); - - if (in_interrupt()) - netif_rx(skb); - else - netif_rx_ni(skb); - - return 0; -} - -/* - * IOCTL completion callback handler. - * - * This function is called when a pending IOCTL is completed. - * - * If work queue support is enabled, the function wakes up the - * corresponding waiting function. Otherwise, it processes the - * IOCTL response and frees the response buffer. - */ -int mwifiex_complete_cmd(struct mwifiex_adapter *adapter, - struct cmd_ctrl_node *cmd_node) -{ - WARN_ON(!cmd_node->wait_q_enabled); - mwifiex_dbg(adapter, CMD, "cmd completed: status=%d\n", - adapter->cmd_wait_q.status); - - *cmd_node->condition = true; - wake_up_interruptible(&adapter->cmd_wait_q.wait); - - return 0; -} - -/* This function will return the pointer to station entry in station list - * table which matches specified mac address. - * This function should be called after acquiring RA list spinlock. - * NULL is returned if station entry is not found in associated STA list. - */ -struct mwifiex_sta_node * -mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_sta_node *node; - - if (!mac) - return NULL; - - list_for_each_entry(node, &priv->sta_list, list) { - if (!memcmp(node->mac_addr, mac, ETH_ALEN)) - return node; - } - - return NULL; -} - -static struct mwifiex_sta_node * -mwifiex_get_tdls_sta_entry(struct mwifiex_private *priv, u8 status) -{ - struct mwifiex_sta_node *node; - - list_for_each_entry(node, &priv->sta_list, list) { - if (node->tdls_status == status) - return node; - } - - return NULL; -} - -/* If tdls channel switching is on-going, tx data traffic should be - * blocked until the switching stage completed. - */ -u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *sta_ptr; - - if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return false; - - sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_CHAN_SWITCHING); - if (sta_ptr) - return true; - - return false; -} - -u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *sta_ptr; - - if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return false; - - sta_ptr = mwifiex_get_tdls_sta_entry(priv, TDLS_IN_OFF_CHAN); - if (sta_ptr) - return true; - - return false; -} - -/* If tdls channel switching is on-going or tdls operate on off-channel, - * cmd path should be blocked until tdls switched to base-channel. - */ -u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv) -{ - if (!priv || !ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info)) - return true; - - if (mwifiex_is_tdls_chan_switching(priv) || - mwifiex_is_tdls_off_chan(priv)) - return false; - - return true; -} - -/* This function will add a sta_node entry to associated station list - * table with the given mac address. - * If entry exist already, existing entry is returned. - * If received mac address is NULL, NULL is returned. - */ -struct mwifiex_sta_node * -mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_sta_node *node; - unsigned long flags; - - if (!mac) - return NULL; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - node = mwifiex_get_sta_entry(priv, mac); - if (node) - goto done; - - node = kzalloc(sizeof(*node), GFP_ATOMIC); - if (!node) - goto done; - - memcpy(node->mac_addr, mac, ETH_ALEN); - list_add_tail(&node->list, &priv->sta_list); - -done: - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return node; -} - -/* This function will search for HT IE in association request IEs - * and set station HT parameters accordingly. - */ -void -mwifiex_set_sta_ht_cap(struct mwifiex_private *priv, const u8 *ies, - int ies_len, struct mwifiex_sta_node *node) -{ - struct ieee_types_header *ht_cap_ie; - const struct ieee80211_ht_cap *ht_cap; - - if (!ies) - return; - - ht_cap_ie = (void *)cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, ies, - ies_len); - if (ht_cap_ie) { - ht_cap = (void *)(ht_cap_ie + 1); - node->is_11n_enabled = 1; - node->max_amsdu = le16_to_cpu(ht_cap->cap_info) & - IEEE80211_HT_CAP_MAX_AMSDU ? - MWIFIEX_TX_DATA_BUF_SIZE_8K : - MWIFIEX_TX_DATA_BUF_SIZE_4K; - } else { - node->is_11n_enabled = 0; - } - - return; -} - -/* This function will delete a station entry from station list */ -void mwifiex_del_sta_entry(struct mwifiex_private *priv, const u8 *mac) -{ - struct mwifiex_sta_node *node; - unsigned long flags; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - - node = mwifiex_get_sta_entry(priv, mac); - if (node) { - list_del(&node->list); - kfree(node); - } - - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return; -} - -/* This function will delete all stations from associated station list. */ -void mwifiex_del_all_sta_list(struct mwifiex_private *priv) -{ - struct mwifiex_sta_node *node, *tmp; - unsigned long flags; - - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - - list_for_each_entry_safe(node, tmp, &priv->sta_list, list) { - list_del(&node->list); - kfree(node); - } - - INIT_LIST_HEAD(&priv->sta_list); - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - return; -} - -/* This function adds histogram data to histogram array*/ -void mwifiex_hist_data_add(struct mwifiex_private *priv, - u8 rx_rate, s8 snr, s8 nflr) -{ - struct mwifiex_histogram_data *phist_data = priv->hist_data; - - if (atomic_read(&phist_data->num_samples) > MWIFIEX_HIST_MAX_SAMPLES) - mwifiex_hist_data_reset(priv); - mwifiex_hist_data_set(priv, rx_rate, snr, nflr); -} - -/* function to add histogram record */ -void mwifiex_hist_data_set(struct mwifiex_private *priv, u8 rx_rate, s8 snr, - s8 nflr) -{ - struct mwifiex_histogram_data *phist_data = priv->hist_data; - - atomic_inc(&phist_data->num_samples); - atomic_inc(&phist_data->rx_rate[rx_rate]); - atomic_inc(&phist_data->snr[snr]); - atomic_inc(&phist_data->noise_flr[128 + nflr]); - atomic_inc(&phist_data->sig_str[nflr - snr]); -} - -/* function to reset histogram data during init/reset */ -void mwifiex_hist_data_reset(struct mwifiex_private *priv) -{ - int ix; - struct mwifiex_histogram_data *phist_data = priv->hist_data; - - atomic_set(&phist_data->num_samples, 0); - for (ix = 0; ix < MWIFIEX_MAX_AC_RX_RATES; ix++) - atomic_set(&phist_data->rx_rate[ix], 0); - for (ix = 0; ix < MWIFIEX_MAX_SNR; ix++) - atomic_set(&phist_data->snr[ix], 0); - for (ix = 0; ix < MWIFIEX_MAX_NOISE_FLR; ix++) - atomic_set(&phist_data->noise_flr[ix], 0); - for (ix = 0; ix < MWIFIEX_MAX_SIG_STRENGTH; ix++) - atomic_set(&phist_data->sig_str[ix], 0); -} - -void *mwifiex_alloc_dma_align_buf(int rx_len, gfp_t flags) -{ - struct sk_buff *skb; - int buf_len, pad; - - buf_len = rx_len + MWIFIEX_RX_HEADROOM + MWIFIEX_DMA_ALIGN_SZ; - - skb = __dev_alloc_skb(buf_len, flags); - - if (!skb) - return NULL; - - skb_reserve(skb, MWIFIEX_RX_HEADROOM); - - pad = MWIFIEX_ALIGN_ADDR(skb->data, MWIFIEX_DMA_ALIGN_SZ) - - (long)skb->data; - - skb_reserve(skb, pad); - - return skb; -} -EXPORT_SYMBOL_GPL(mwifiex_alloc_dma_align_buf); diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h deleted file mode 100644 index b541d66c01eb..000000000000 --- a/drivers/net/wireless/mwifiex/util.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Marvell Wireless LAN device driver: utility functions - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_UTIL_H_ -#define _MWIFIEX_UTIL_H_ - -struct mwifiex_private; - -struct mwifiex_dma_mapping { - dma_addr_t addr; - size_t len; -}; - -struct mwifiex_cb { - struct mwifiex_dma_mapping dma_mapping; - union { - struct mwifiex_rxinfo rx_info; - struct mwifiex_txinfo tx_info; - }; -}; - -/* size/addr for mwifiex_debug_info */ -#define item_size(n) (FIELD_SIZEOF(struct mwifiex_debug_info, n)) -#define item_addr(n) (offsetof(struct mwifiex_debug_info, n)) - -/* size/addr for struct mwifiex_adapter */ -#define adapter_item_size(n) (FIELD_SIZEOF(struct mwifiex_adapter, n)) -#define adapter_item_addr(n) (offsetof(struct mwifiex_adapter, n)) - -struct mwifiex_debug_data { - char name[32]; /* variable/array name */ - u32 size; /* size of the variable/array */ - size_t addr; /* address of the variable/array */ - int num; /* number of variables in an array */ -}; - -static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) -{ - struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; - - BUILD_BUG_ON(sizeof(struct mwifiex_cb) > sizeof(skb->cb)); - return &cb->rx_info; -} - -static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) -{ - struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; - - return &cb->tx_info; -} - -static inline void mwifiex_store_mapping(struct sk_buff *skb, - struct mwifiex_dma_mapping *mapping) -{ - struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; - - memcpy(&cb->dma_mapping, mapping, sizeof(*mapping)); -} - -static inline void mwifiex_get_mapping(struct sk_buff *skb, - struct mwifiex_dma_mapping *mapping) -{ - struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; - - memcpy(mapping, &cb->dma_mapping, sizeof(*mapping)); -} - -static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) -{ - struct mwifiex_dma_mapping mapping; - - mwifiex_get_mapping(skb, &mapping); - - return mapping.addr; -} - -int mwifiex_debug_info_to_buffer(struct mwifiex_private *priv, char *buf, - struct mwifiex_debug_info *info); - -#endif /* !_MWIFIEX_UTIL_H_ */ diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c deleted file mode 100644 index acccd6734e3b..000000000000 --- a/drivers/net/wireless/mwifiex/wmm.c +++ /dev/null @@ -1,1531 +0,0 @@ -/* - * Marvell Wireless LAN device driver: WMM - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#include "decl.h" -#include "ioctl.h" -#include "util.h" -#include "fw.h" -#include "main.h" -#include "wmm.h" -#include "11n.h" - - -/* Maximum value FW can accept for driver delay in packet transmission */ -#define DRV_PKT_DELAY_TO_FW_MAX 512 - - -#define WMM_QUEUED_PACKET_LOWER_LIMIT 180 - -#define WMM_QUEUED_PACKET_UPPER_LIMIT 200 - -/* Offset for TOS field in the IP header */ -#define IPTOS_OFFSET 5 - -static bool disable_tx_amsdu; -module_param(disable_tx_amsdu, bool, 0644); - -/* WMM information IE */ -static const u8 wmm_info_ie[] = { WLAN_EID_VENDOR_SPECIFIC, 0x07, - 0x00, 0x50, 0xf2, 0x02, - 0x00, 0x01, 0x00 -}; - -static const u8 wmm_aci_to_qidx_map[] = { WMM_AC_BE, - WMM_AC_BK, - WMM_AC_VI, - WMM_AC_VO -}; - -static 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 */ -}; - -static u8 ac_to_tid[4][2] = { {1, 2}, {0, 3}, {4, 5}, {6, 7} }; - -/* - * This function debug prints the priority parameters for a WMM AC. - */ -static void -mwifiex_wmm_ac_debug_print(const struct ieee_types_wmm_ac_parameters *ac_param) -{ - const char *ac_str[] = { "BK", "BE", "VI", "VO" }; - - pr_debug("info: WMM AC_%s: ACI=%d, ACM=%d, Aifsn=%d, " - "EcwMin=%d, EcwMax=%d, TxopLimit=%d\n", - ac_str[wmm_aci_to_qidx_map[(ac_param->aci_aifsn_bitmap - & MWIFIEX_ACI) >> 5]], - (ac_param->aci_aifsn_bitmap & MWIFIEX_ACI) >> 5, - (ac_param->aci_aifsn_bitmap & MWIFIEX_ACM) >> 4, - ac_param->aci_aifsn_bitmap & MWIFIEX_AIFSN, - ac_param->ecw_bitmap & MWIFIEX_ECW_MIN, - (ac_param->ecw_bitmap & MWIFIEX_ECW_MAX) >> 4, - le16_to_cpu(ac_param->tx_op_limit)); -} - -/* - * This function allocates a route address list. - * - * The function also initializes the list with the provided RA. - */ -static struct mwifiex_ra_list_tbl * -mwifiex_wmm_allocate_ralist_node(struct mwifiex_adapter *adapter, const u8 *ra) -{ - struct mwifiex_ra_list_tbl *ra_list; - - ra_list = kzalloc(sizeof(struct mwifiex_ra_list_tbl), GFP_ATOMIC); - if (!ra_list) - return NULL; - - INIT_LIST_HEAD(&ra_list->list); - skb_queue_head_init(&ra_list->skb_head); - - memcpy(ra_list->ra, ra, ETH_ALEN); - - ra_list->total_pkt_count = 0; - - mwifiex_dbg(adapter, INFO, "info: allocated ra_list %p\n", ra_list); - - return ra_list; -} - -/* This function returns random no between 16 and 32 to be used as threshold - * for no of packets after which BA setup is initiated. - */ -static u8 mwifiex_get_random_ba_threshold(void) -{ - u64 ns; - /* setup ba_packet_threshold here random number between - * [BA_SETUP_PACKET_OFFSET, - * BA_SETUP_PACKET_OFFSET+BA_SETUP_MAX_PACKET_THRESHOLD-1] - */ - ns = ktime_get_ns(); - ns += (ns >> 32) + (ns >> 16); - - return ((u8)ns % BA_SETUP_MAX_PACKET_THRESHOLD) + BA_SETUP_PACKET_OFFSET; -} - -/* - * This function allocates and adds a RA list for all TIDs - * with the given RA. - */ -void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra) -{ - int i; - struct mwifiex_ra_list_tbl *ra_list; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_sta_node *node; - unsigned long flags; - - - for (i = 0; i < MAX_NUM_TID; ++i) { - ra_list = mwifiex_wmm_allocate_ralist_node(adapter, ra); - mwifiex_dbg(adapter, INFO, - "info: created ra_list %p\n", ra_list); - - if (!ra_list) - break; - - ra_list->is_11n_enabled = 0; - ra_list->tdls_link = false; - ra_list->ba_status = BA_SETUP_NONE; - ra_list->amsdu_in_ampdu = false; - if (!mwifiex_queuing_ra_based(priv)) { - if (mwifiex_is_tdls_link_setup - (mwifiex_get_tdls_link_status(priv, ra))) { - ra_list->tdls_link = true; - ra_list->is_11n_enabled = - mwifiex_tdls_peer_11n_enabled(priv, ra); - } else { - ra_list->is_11n_enabled = IS_11N_ENABLED(priv); - } - } else { - spin_lock_irqsave(&priv->sta_list_spinlock, flags); - node = mwifiex_get_sta_entry(priv, ra); - if (node) - ra_list->tx_paused = node->tx_pause; - ra_list->is_11n_enabled = - mwifiex_is_sta_11n_enabled(priv, node); - if (ra_list->is_11n_enabled) - ra_list->max_amsdu = node->max_amsdu; - spin_unlock_irqrestore(&priv->sta_list_spinlock, flags); - } - - mwifiex_dbg(adapter, DATA, "data: ralist %p: is_11n_enabled=%d\n", - ra_list, ra_list->is_11n_enabled); - - if (ra_list->is_11n_enabled) { - ra_list->ba_pkt_count = 0; - ra_list->ba_packet_thr = - mwifiex_get_random_ba_threshold(); - } - list_add_tail(&ra_list->list, - &priv->wmm.tid_tbl_ptr[i].ra_list); - } -} - -/* - * This function sets the WMM queue priorities to their default values. - */ -static void mwifiex_wmm_default_queue_priorities(struct mwifiex_private *priv) -{ - /* 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; -} - -/* - * This function map ACs to TIDs. - */ -static void -mwifiex_wmm_queue_priorities_tid(struct mwifiex_private *priv) -{ - struct mwifiex_wmm_desc *wmm = &priv->wmm; - u8 *queue_priority = wmm->queue_priority; - int i; - - 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) - priv->tos_to_tid_inv[tos_to_tid[i]] = (u8)i; - - atomic_set(&wmm->highest_queued_prio, HIGH_PRIO_TID); -} - -/* - * This function initializes WMM priority queues. - */ -void -mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, - struct ieee_types_wmm_parameter *wmm_ie) -{ - u16 cw_min, avg_back_off, tmp[4]; - u32 i, j, num_ac; - u8 ac_idx; - - if (!wmm_ie || !priv->wmm_enabled) { - /* WMM is not enabled, just set the defaults and return */ - mwifiex_wmm_default_queue_priorities(priv); - return; - } - - mwifiex_dbg(priv->adapter, INFO, - "info: WMM Parameter IE: version=%d,\t" - "qos_info Parameter Set Count=%d, Reserved=%#x\n", - wmm_ie->vend_hdr.version, wmm_ie->qos_info_bitmap & - IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK, - wmm_ie->reserved); - - for (num_ac = 0; num_ac < ARRAY_SIZE(wmm_ie->ac_params); num_ac++) { - u8 ecw = wmm_ie->ac_params[num_ac].ecw_bitmap; - u8 aci_aifsn = wmm_ie->ac_params[num_ac].aci_aifsn_bitmap; - cw_min = (1 << (ecw & MWIFIEX_ECW_MIN)) - 1; - avg_back_off = (cw_min >> 1) + (aci_aifsn & MWIFIEX_AIFSN); - - ac_idx = wmm_aci_to_qidx_map[(aci_aifsn & MWIFIEX_ACI) >> 5]; - priv->wmm.queue_priority[ac_idx] = ac_idx; - tmp[ac_idx] = avg_back_off; - - mwifiex_dbg(priv->adapter, INFO, - "info: WMM: CWmax=%d CWmin=%d Avg Back-off=%d\n", - (1 << ((ecw & MWIFIEX_ECW_MAX) >> 4)) - 1, - cw_min, avg_back_off); - mwifiex_wmm_ac_debug_print(&wmm_ie->ac_params[num_ac]); - } - - /* Bubble sort */ - for (i = 0; i < num_ac; i++) { - for (j = 1; j < num_ac - i; j++) { - if (tmp[j - 1] > tmp[j]) { - swap(tmp[j - 1], tmp[j]); - swap(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(priv->wmm.queue_priority[j - 1], - priv->wmm.queue_priority[j]); - } - } - } - - mwifiex_wmm_queue_priorities_tid(priv); -} - -/* - * This function evaluates whether or not an AC is to be downgraded. - * - * In case the AC is not enabled, the highest AC is returned that is - * enabled and does not require admission control. - */ -static enum mwifiex_wmm_ac_e -mwifiex_wmm_eval_downgrade_ac(struct mwifiex_private *priv, - enum mwifiex_wmm_ac_e eval_ac) -{ - int down_ac; - enum mwifiex_wmm_ac_e ret_ac; - struct mwifiex_wmm_ac_status *ac_status; - - ac_status = &priv->wmm.ac_status[eval_ac]; - - if (!ac_status->disabled) - /* 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++) { - ac_status = &priv->wmm.ac_status[down_ac]; - - if (!ac_status->disabled && !ac_status->flow_required) - /* AC is enabled and does not require admission - control */ - ret_ac = (enum mwifiex_wmm_ac_e) down_ac; - } - - return ret_ac; -} - -/* - * This function downgrades WMM priority queue. - */ -void -mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv) -{ - int ac_val; - - mwifiex_dbg(priv->adapter, INFO, "info: WMM: AC Priorities:\t" - "BK(0), BE(1), VI(2), VO(3)\n"); - - if (!priv->wmm_enabled) { - /* 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] = - (enum mwifiex_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] - = mwifiex_wmm_eval_downgrade_ac(priv, - (enum mwifiex_wmm_ac_e) ac_val); - mwifiex_dbg(priv->adapter, INFO, - "info: WMM: AC PRIO %d maps to %d\n", - ac_val, - priv->wmm.ac_down_graded_vals[ac_val]); - } - } -} - -/* - * This function converts the IP TOS field to an WMM AC - * Queue assignment. - */ -static enum mwifiex_wmm_ac_e -mwifiex_wmm_convert_tos_to_ac(struct mwifiex_adapter *adapter, u32 tos) -{ - /* Map of TOS UP values to WMM AC */ - const enum mwifiex_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 - }; - - if (tos >= ARRAY_SIZE(tos_to_ac)) - return WMM_AC_BE; - - return tos_to_ac[tos]; -} - -/* - * This function evaluates a given TID and downgrades 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 of internally. - */ -u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid) -{ - enum mwifiex_wmm_ac_e ac, ac_down; - u8 new_tid; - - ac = mwifiex_wmm_convert_tos_to_ac(priv->adapter, tid); - ac_down = priv->wmm.ac_down_graded_vals[ac]; - - /* Send the index to tid array, picking from the array will be - * taken care by dequeuing function - */ - new_tid = ac_to_tid[ac_down][tid % 2]; - - return new_tid; -} - -/* - * This function initializes the WMM state information and the - * WMM data path queues. - */ -void -mwifiex_wmm_init(struct mwifiex_adapter *adapter) -{ - int i, j; - struct mwifiex_private *priv; - - for (j = 0; j < adapter->priv_num; ++j) { - priv = adapter->priv[j]; - if (!priv) - continue; - - for (i = 0; i < MAX_NUM_TID; ++i) { - if (!disable_tx_amsdu && - adapter->tx_buf_size > MWIFIEX_TX_DATA_BUF_SIZE_2K) - priv->aggr_prio_tbl[i].amsdu = - priv->tos_to_tid_inv[i]; - else - priv->aggr_prio_tbl[i].amsdu = - BA_STREAM_NOT_ALLOWED; - priv->aggr_prio_tbl[i].ampdu_ap = - priv->tos_to_tid_inv[i]; - priv->aggr_prio_tbl[i].ampdu_user = - priv->tos_to_tid_inv[i]; - } - - priv->aggr_prio_tbl[6].amsdu - = priv->aggr_prio_tbl[6].ampdu_ap - = priv->aggr_prio_tbl[6].ampdu_user - = BA_STREAM_NOT_ALLOWED; - - priv->aggr_prio_tbl[7].amsdu = priv->aggr_prio_tbl[7].ampdu_ap - = priv->aggr_prio_tbl[7].ampdu_user - = BA_STREAM_NOT_ALLOWED; - - mwifiex_set_ba_params(priv); - mwifiex_reset_11n_rx_seq_num(priv); - - atomic_set(&priv->wmm.tx_pkts_queued, 0); - atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); - } -} - -int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter) -{ - struct mwifiex_private *priv; - int i; - - for (i = 0; i < adapter->priv_num; i++) { - priv = adapter->priv[i]; - if (!priv) - continue; - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv)) - continue; - if (!skb_queue_empty(&priv->bypass_txq)) - return false; - } - - return true; -} - -/* - * This function checks if WMM Tx queue is empty. - */ -int -mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter) -{ - int i; - struct mwifiex_private *priv; - - for (i = 0; i < adapter->priv_num; ++i) { - priv = adapter->priv[i]; - if (!priv) - continue; - if (!priv->port_open) - continue; - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv)) - continue; - if (atomic_read(&priv->wmm.tx_pkts_queued)) - return false; - } - - return true; -} - -/* - * This function deletes all packets in an RA list node. - * - * The packet sent completion callback handler are called with - * status failure, after they are dequeued to ensure proper - * cleanup. The RA list node itself is freed at the end. - */ -static void -mwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra_list) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct sk_buff *skb, *tmp; - - skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) - mwifiex_write_data_complete(adapter, skb, 0, -1); -} - -/* - * This function deletes all packets in an RA list. - * - * Each nodes in the RA list are freed individually first, and then - * the RA list itself is freed. - */ -static void -mwifiex_wmm_del_pkts_in_ralist(struct mwifiex_private *priv, - struct list_head *ra_list_head) -{ - struct mwifiex_ra_list_tbl *ra_list; - - list_for_each_entry(ra_list, ra_list_head, list) - mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); -} - -/* - * This function deletes all packets in all RA lists. - */ -static void mwifiex_wmm_cleanup_queues(struct mwifiex_private *priv) -{ - int i; - - for (i = 0; i < MAX_NUM_TID; i++) - mwifiex_wmm_del_pkts_in_ralist(priv, &priv->wmm.tid_tbl_ptr[i]. - ra_list); - - atomic_set(&priv->wmm.tx_pkts_queued, 0); - atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); -} - -/* - * This function deletes all route addresses from all RA lists. - */ -static void mwifiex_wmm_delete_all_ralist(struct mwifiex_private *priv) -{ - struct mwifiex_ra_list_tbl *ra_list, *tmp_node; - int i; - - for (i = 0; i < MAX_NUM_TID; ++i) { - mwifiex_dbg(priv->adapter, INFO, - "info: ra_list: freeing buf for tid %d\n", i); - list_for_each_entry_safe(ra_list, tmp_node, - &priv->wmm.tid_tbl_ptr[i].ra_list, - list) { - list_del(&ra_list->list); - kfree(ra_list); - } - - INIT_LIST_HEAD(&priv->wmm.tid_tbl_ptr[i].ra_list); - } -} - -static int mwifiex_free_ack_frame(int id, void *p, void *data) -{ - pr_warn("Have pending ack frames!\n"); - kfree_skb(p); - return 0; -} - -/* - * This function cleans up the Tx and Rx queues. - * - * Cleanup includes - - * - All packets in RA lists - * - All entries in Rx reorder table - * - All entries in Tx BA stream table - * - MPA buffer (if required) - * - All RA lists - */ -void -mwifiex_clean_txrx(struct mwifiex_private *priv) -{ - unsigned long flags; - struct sk_buff *skb, *tmp; - - mwifiex_11n_cleanup_reorder_tbl(priv); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - mwifiex_wmm_cleanup_queues(priv); - mwifiex_11n_delete_all_tx_ba_stream_tbl(priv); - - if (priv->adapter->if_ops.cleanup_mpa_buf) - priv->adapter->if_ops.cleanup_mpa_buf(priv->adapter); - - mwifiex_wmm_delete_all_ralist(priv); - memcpy(tos_to_tid, ac_to_tid, sizeof(tos_to_tid)); - - if (priv->adapter->if_ops.clean_pcie_ring && - !priv->adapter->surprise_removed) - priv->adapter->if_ops.clean_pcie_ring(priv->adapter); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - - skb_queue_walk_safe(&priv->tdls_txq, skb, tmp) - mwifiex_write_data_complete(priv->adapter, skb, 0, -1); - - skb_queue_walk_safe(&priv->bypass_txq, skb, tmp) - mwifiex_write_data_complete(priv->adapter, skb, 0, -1); - atomic_set(&priv->adapter->bypass_tx_pending, 0); - - idr_for_each(&priv->ack_status_frames, mwifiex_free_ack_frame, NULL); - idr_destroy(&priv->ack_status_frames); -} - -/* - * This function retrieves a particular RA list node, matching with the - * given TID and RA address. - */ -struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_ralist_node(struct mwifiex_private *priv, u8 tid, - const u8 *ra_addr) -{ - struct mwifiex_ra_list_tbl *ra_list; - - list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[tid].ra_list, - list) { - if (!memcmp(ra_list->ra, ra_addr, ETH_ALEN)) - return ra_list; - } - - return NULL; -} - -void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, - u8 tx_pause) -{ - struct mwifiex_ra_list_tbl *ra_list; - u32 pkt_cnt = 0, tx_pkts_queued; - unsigned long flags; - int i; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; ++i) { - ra_list = mwifiex_wmm_get_ralist_node(priv, i, mac); - if (ra_list && ra_list->tx_paused != tx_pause) { - pkt_cnt += ra_list->total_pkt_count; - ra_list->tx_paused = tx_pause; - if (tx_pause) - priv->wmm.pkts_paused[i] += - ra_list->total_pkt_count; - else - priv->wmm.pkts_paused[i] -= - ra_list->total_pkt_count; - } - } - - if (pkt_cnt) { - tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); - if (tx_pause) - tx_pkts_queued -= pkt_cnt; - else - tx_pkts_queued += pkt_cnt; - - atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); - atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); - } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* This function update non-tdls peer ralist tx_pause while - * tdls channel swithing - */ -void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, - u8 *mac, u8 tx_pause) -{ - struct mwifiex_ra_list_tbl *ra_list; - u32 pkt_cnt = 0, tx_pkts_queued; - unsigned long flags; - int i; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; ++i) { - list_for_each_entry(ra_list, &priv->wmm.tid_tbl_ptr[i].ra_list, - list) { - if (!memcmp(ra_list->ra, mac, ETH_ALEN)) - continue; - - if (ra_list->tx_paused != tx_pause) { - pkt_cnt += ra_list->total_pkt_count; - ra_list->tx_paused = tx_pause; - if (tx_pause) - priv->wmm.pkts_paused[i] += - ra_list->total_pkt_count; - else - priv->wmm.pkts_paused[i] -= - ra_list->total_pkt_count; - } - } - } - - if (pkt_cnt) { - tx_pkts_queued = atomic_read(&priv->wmm.tx_pkts_queued); - if (tx_pause) - tx_pkts_queued -= pkt_cnt; - else - tx_pkts_queued += pkt_cnt; - - atomic_set(&priv->wmm.tx_pkts_queued, tx_pkts_queued); - atomic_set(&priv->wmm.highest_queued_prio, HIGH_PRIO_TID); - } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* - * This function retrieves an RA list node for a given TID and - * RA address pair. - * - * If no such node is found, a new node is added first and then - * retrieved. - */ -struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, - const u8 *ra_addr) -{ - struct mwifiex_ra_list_tbl *ra_list; - - ra_list = mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); - if (ra_list) - return ra_list; - mwifiex_ralist_add(priv, ra_addr); - - return mwifiex_wmm_get_ralist_node(priv, tid, ra_addr); -} - -/* - * This function deletes RA list nodes for given mac for all TIDs. - * Function also decrements TX pending count accordingly. - */ -void -mwifiex_wmm_del_peer_ra_list(struct mwifiex_private *priv, const u8 *ra_addr) -{ - struct mwifiex_ra_list_tbl *ra_list; - unsigned long flags; - int i; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - for (i = 0; i < MAX_NUM_TID; ++i) { - ra_list = mwifiex_wmm_get_ralist_node(priv, i, ra_addr); - - if (!ra_list) - continue; - mwifiex_wmm_del_pkts_in_ralist_node(priv, ra_list); - if (ra_list->tx_paused) - priv->wmm.pkts_paused[i] -= ra_list->total_pkt_count; - else - atomic_sub(ra_list->total_pkt_count, - &priv->wmm.tx_pkts_queued); - list_del(&ra_list->list); - kfree(ra_list); - } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* - * This function checks if a particular RA list node exists in a given TID - * table index. - */ -int -mwifiex_is_ralist_valid(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra_list, int ptr_index) -{ - struct mwifiex_ra_list_tbl *rlist; - - list_for_each_entry(rlist, &priv->wmm.tid_tbl_ptr[ptr_index].ra_list, - list) { - if (rlist == ra_list) - return true; - } - - return false; -} - -/* - * This function adds a packet to bypass TX queue. - * This is special TX queue for packets which can be sent even when port_open - * is false. - */ -void -mwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - skb_queue_tail(&priv->bypass_txq, skb); -} - -/* - * This function adds a packet to WMM queue. - * - * In disconnected state the packet is immediately dropped and the - * packet send completion callback is called with status failure. - * - * Otherwise, the correct RA list node is located and the packet - * is queued at the list tail. - */ -void -mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb) -{ - struct mwifiex_adapter *adapter = priv->adapter; - u32 tid; - struct mwifiex_ra_list_tbl *ra_list; - u8 ra[ETH_ALEN], tid_down; - unsigned long flags; - struct list_head list_head; - int tdls_status = TDLS_NOT_SETUP; - struct ethhdr *eth_hdr = (struct ethhdr *)skb->data; - struct mwifiex_txinfo *tx_info = MWIFIEX_SKB_TXCB(skb); - - memcpy(ra, eth_hdr->h_dest, ETH_ALEN); - - if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && - ISSUPP_TDLS_ENABLED(adapter->fw_cap_info)) { - if (ntohs(eth_hdr->h_proto) == ETH_P_TDLS) - mwifiex_dbg(adapter, DATA, - "TDLS setup packet for %pM.\t" - "Don't block\n", ra); - else if (memcmp(priv->cfg_bssid, ra, ETH_ALEN)) - tdls_status = mwifiex_get_tdls_link_status(priv, ra); - } - - if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) { - mwifiex_dbg(adapter, DATA, "data: drop packet in disconnect\n"); - mwifiex_write_data_complete(adapter, skb, 0, -1); - return; - } - - tid = skb->priority; - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - - tid_down = mwifiex_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 (!mwifiex_queuing_ra_based(priv) && - !mwifiex_is_skb_mgmt_frame(skb)) { - switch (tdls_status) { - case TDLS_SETUP_COMPLETE: - case TDLS_CHAN_SWITCHING: - case TDLS_IN_BASE_CHAN: - case TDLS_IN_OFF_CHAN: - ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, - ra); - tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; - break; - case TDLS_SETUP_INPROGRESS: - skb_queue_tail(&priv->tdls_txq, skb); - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - flags); - return; - default: - list_head = priv->wmm.tid_tbl_ptr[tid_down].ra_list; - if (!list_empty(&list_head)) - ra_list = list_first_entry( - &list_head, struct mwifiex_ra_list_tbl, - list); - else - ra_list = NULL; - break; - } - } else { - memcpy(ra, skb->data, ETH_ALEN); - if (ra[0] & 0x01 || mwifiex_is_skb_mgmt_frame(skb)) - eth_broadcast_addr(ra); - ra_list = mwifiex_wmm_get_queue_raptr(priv, tid_down, ra); - } - - if (!ra_list) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - mwifiex_write_data_complete(adapter, skb, 0, -1); - return; - } - - skb_queue_tail(&ra_list->skb_head, skb); - - ra_list->ba_pkt_count++; - ra_list->total_pkt_count++; - - if (atomic_read(&priv->wmm.highest_queued_prio) < - priv->tos_to_tid_inv[tid_down]) - atomic_set(&priv->wmm.highest_queued_prio, - priv->tos_to_tid_inv[tid_down]); - - if (ra_list->tx_paused) - priv->wmm.pkts_paused[tid_down]++; - else - atomic_inc(&priv->wmm.tx_pkts_queued); - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* - * This function processes the get WMM status command response from firmware. - * - * The response may contain multiple TLVs - - * - AC Queue status TLVs - * - Current WMM Parameter IE TLV - * - Admission Control action frame TLVs - * - * This function parses the TLVs and then calls further specific functions - * to process any changes in the queue prioritize or state. - */ -int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, - const struct host_cmd_ds_command *resp) -{ - u8 *curr = (u8 *) &resp->params.get_wmm_status; - uint16_t resp_len = le16_to_cpu(resp->size), tlv_len; - int mask = IEEE80211_WMM_IE_AP_QOSINFO_PARAM_SET_CNT_MASK; - bool valid = true; - - struct mwifiex_ie_types_data *tlv_hdr; - struct mwifiex_ie_types_wmm_queue_status *tlv_wmm_qstatus; - struct ieee_types_wmm_parameter *wmm_param_ie = NULL; - struct mwifiex_wmm_ac_status *ac_status; - - mwifiex_dbg(priv->adapter, INFO, - "info: WMM: WMM_GET_STATUS cmdresp received: %d\n", - resp_len); - - while ((resp_len >= sizeof(tlv_hdr->header)) && valid) { - tlv_hdr = (struct mwifiex_ie_types_data *) curr; - tlv_len = le16_to_cpu(tlv_hdr->header.len); - - if (resp_len < tlv_len + sizeof(tlv_hdr->header)) - break; - - switch (le16_to_cpu(tlv_hdr->header.type)) { - case TLV_TYPE_WMMQSTATUS: - tlv_wmm_qstatus = - (struct mwifiex_ie_types_wmm_queue_status *) - tlv_hdr; - mwifiex_dbg(priv->adapter, CMD, - "info: CMD_RESP: WMM_GET_STATUS:\t" - "QSTATUS TLV: %d, %d, %d\n", - tlv_wmm_qstatus->queue_index, - tlv_wmm_qstatus->flow_required, - tlv_wmm_qstatus->disabled); - - ac_status = &priv->wmm.ac_status[tlv_wmm_qstatus-> - queue_index]; - ac_status->disabled = tlv_wmm_qstatus->disabled; - ac_status->flow_required = - tlv_wmm_qstatus->flow_required; - ac_status->flow_created = tlv_wmm_qstatus->flow_created; - break; - - case WLAN_EID_VENDOR_SPECIFIC: - /* - * Point the regular IEEE IE 2 bytes into the Marvell IE - * and setup the IEEE IE type and length byte fields - */ - - wmm_param_ie = - (struct ieee_types_wmm_parameter *) (curr + - 2); - wmm_param_ie->vend_hdr.len = (u8) tlv_len; - wmm_param_ie->vend_hdr.element_id = - WLAN_EID_VENDOR_SPECIFIC; - - mwifiex_dbg(priv->adapter, CMD, - "info: CMD_RESP: WMM_GET_STATUS:\t" - "WMM Parameter Set Count: %d\n", - wmm_param_ie->qos_info_bitmap & mask); - - memcpy((u8 *) &priv->curr_bss_params.bss_descriptor. - wmm_ie, wmm_param_ie, - wmm_param_ie->vend_hdr.len + 2); - - break; - - default: - valid = false; - break; - } - - curr += (tlv_len + sizeof(tlv_hdr->header)); - resp_len -= (tlv_len + sizeof(tlv_hdr->header)); - } - - mwifiex_wmm_setup_queue_priorities(priv, wmm_param_ie); - mwifiex_wmm_setup_ac_downgrade(priv); - - return 0; -} - -/* - * Callback handler from the command module to allow insertion of a WMM TLV. - * - * If the BSS we are associating to supports WMM, this function adds the - * required WMM Information IE to the association request command buffer in - * the form of a Marvell extended IEEE IE. - */ -u32 -mwifiex_wmm_process_association_req(struct mwifiex_private *priv, - u8 **assoc_buf, - struct ieee_types_wmm_parameter *wmm_ie, - struct ieee80211_ht_cap *ht_cap) -{ - struct mwifiex_ie_types_wmm_param_set *wmm_tlv; - u32 ret_len = 0; - - /* Null checks */ - if (!assoc_buf) - return 0; - if (!(*assoc_buf)) - return 0; - - if (!wmm_ie) - return 0; - - mwifiex_dbg(priv->adapter, INFO, - "info: WMM: process assoc req: bss->wmm_ie=%#x\n", - wmm_ie->vend_hdr.element_id); - - if ((priv->wmm_required || - (ht_cap && (priv->adapter->config_bands & BAND_GN || - priv->adapter->config_bands & BAND_AN))) && - wmm_ie->vend_hdr.element_id == WLAN_EID_VENDOR_SPECIFIC) { - wmm_tlv = (struct mwifiex_ie_types_wmm_param_set *) *assoc_buf; - wmm_tlv->header.type = cpu_to_le16((u16) wmm_info_ie[0]); - wmm_tlv->header.len = cpu_to_le16((u16) wmm_info_ie[1]); - memcpy(wmm_tlv->wmm_ie, &wmm_info_ie[2], - le16_to_cpu(wmm_tlv->header.len)); - if (wmm_ie->qos_info_bitmap & IEEE80211_WMM_IE_AP_QOSINFO_UAPSD) - memcpy((u8 *) (wmm_tlv->wmm_ie - + le16_to_cpu(wmm_tlv->header.len) - - sizeof(priv->wmm_qosinfo)), - &priv->wmm_qosinfo, sizeof(priv->wmm_qosinfo)); - - ret_len = sizeof(wmm_tlv->header) - + le16_to_cpu(wmm_tlv->header.len); - - *assoc_buf += ret_len; - } - - return ret_len; -} - -/* - * This function computes 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. - */ -u8 -mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, - const struct sk_buff *skb) -{ - u32 queue_delay = ktime_to_ms(net_timedelta(skb->tstamp)); - u8 ret_val; - - /* - * 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 = (u8) (min(queue_delay, priv->wmm.drv_pkt_delay_max) >> 1); - - mwifiex_dbg(priv->adapter, DATA, "data: WMM: Pkt Delay: %d ms,\t" - "%d ms sent to FW\n", queue_delay, ret_val); - - return ret_val; -} - -/* - * This function retrieves the highest priority RA list table pointer. - */ -static struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_highest_priolist_ptr(struct mwifiex_adapter *adapter, - struct mwifiex_private **priv, int *tid) -{ - struct mwifiex_private *priv_tmp; - struct mwifiex_ra_list_tbl *ptr; - struct mwifiex_tid_tbl *tid_ptr; - atomic_t *hqp; - unsigned long flags_ra; - int i, j; - - /* check the BSS with highest priority first */ - for (j = adapter->priv_num - 1; j >= 0; --j) { - /* iterate over BSS with the equal priority */ - list_for_each_entry(adapter->bss_prio_tbl[j].bss_prio_cur, - &adapter->bss_prio_tbl[j].bss_prio_head, - list) { - - priv_tmp = adapter->bss_prio_tbl[j].bss_prio_cur->priv; - - if (!priv_tmp->port_open || - (atomic_read(&priv_tmp->wmm.tx_pkts_queued) == 0)) - continue; - - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv_tmp)) - continue; - - /* iterate over the WMM queues of the BSS */ - hqp = &priv_tmp->wmm.highest_queued_prio; - for (i = atomic_read(hqp); i >= LOW_PRIO_TID; --i) { - - spin_lock_irqsave(&priv_tmp->wmm. - ra_list_spinlock, flags_ra); - - tid_ptr = &(priv_tmp)->wmm. - tid_tbl_ptr[tos_to_tid[i]]; - - /* iterate over receiver addresses */ - list_for_each_entry(ptr, &tid_ptr->ra_list, - list) { - - if (!ptr->tx_paused && - !skb_queue_empty(&ptr->skb_head)) - /* holds both locks */ - goto found; - } - - spin_unlock_irqrestore(&priv_tmp->wmm. - ra_list_spinlock, - flags_ra); - } - } - - } - - return NULL; - -found: - /* holds ra_list_spinlock */ - if (atomic_read(hqp) > i) - atomic_set(hqp, i); - spin_unlock_irqrestore(&priv_tmp->wmm.ra_list_spinlock, flags_ra); - - *priv = priv_tmp; - *tid = tos_to_tid[i]; - - return ptr; -} - -/* This functions rotates ra and bss lists so packets are picked round robin. - * - * After a packet is successfully transmitted, rotate the ra list, so the ra - * next to the one transmitted, will come first in the list. This way we pick - * the ra' in a round robin fashion. Same applies to bss nodes of equal - * priority. - * - * Function also increments wmm.packets_out counter. - */ -void mwifiex_rotate_priolists(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra, - int tid) -{ - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_bss_prio_tbl *tbl = adapter->bss_prio_tbl; - struct mwifiex_tid_tbl *tid_ptr = &priv->wmm.tid_tbl_ptr[tid]; - unsigned long flags; - - spin_lock_irqsave(&tbl[priv->bss_priority].bss_prio_lock, flags); - /* - * dirty trick: we remove 'head' temporarily and reinsert it after - * curr bss node. imagine list to stay fixed while head is moved - */ - list_move(&tbl[priv->bss_priority].bss_prio_head, - &tbl[priv->bss_priority].bss_prio_cur->list); - spin_unlock_irqrestore(&tbl[priv->bss_priority].bss_prio_lock, flags); - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - if (mwifiex_is_ralist_valid(priv, ra, tid)) { - priv->wmm.packets_out[tid]++; - /* same as above */ - list_move(&tid_ptr->ra_list, &ra->list); - } - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); -} - -/* - * This function checks if 11n aggregation is possible. - */ -static int -mwifiex_is_11n_aggragation_possible(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, - int max_buf_size) -{ - int count = 0, total_size = 0; - struct sk_buff *skb, *tmp; - int max_amsdu_size; - - if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP && priv->ap_11n_enabled && - ptr->is_11n_enabled) - max_amsdu_size = min_t(int, ptr->max_amsdu, max_buf_size); - else - max_amsdu_size = max_buf_size; - - skb_queue_walk_safe(&ptr->skb_head, skb, tmp) { - total_size += skb->len; - if (total_size >= max_amsdu_size) - break; - if (++count >= MIN_NUM_AMSDU) - return true; - } - - return false; -} - -/* - * This function sends a single packet to firmware for transmission. - */ -static void -mwifiex_send_single_packet(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int ptr_index, - unsigned long ra_list_flags) - __releases(&priv->wmm.ra_list_spinlock) -{ - struct sk_buff *skb, *skb_next; - struct mwifiex_tx_param tx_param; - struct mwifiex_adapter *adapter = priv->adapter; - struct mwifiex_txinfo *tx_info; - - if (skb_queue_empty(&ptr->skb_head)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_dbg(adapter, DATA, "data: nothing to send\n"); - return; - } - - skb = skb_dequeue(&ptr->skb_head); - - tx_info = MWIFIEX_SKB_TXCB(skb); - mwifiex_dbg(adapter, DATA, - "data: dequeuing the packet %p %p\n", ptr, skb); - - ptr->total_pkt_count--; - - if (!skb_queue_empty(&ptr->skb_head)) - skb_next = skb_peek(&ptr->skb_head); - else - skb_next = NULL; - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); - - tx_param.next_pkt_len = ((skb_next) ? skb_next->len + - sizeof(struct txpd) : 0); - - if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { - /* Queue the packet back at the head */ - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); - - if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_write_data_complete(adapter, skb, 0, -1); - return; - } - - skb_queue_tail(&ptr->skb_head, skb); - - ptr->total_pkt_count++; - ptr->ba_pkt_count++; - tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - } else { - mwifiex_rotate_priolists(priv, ptr, ptr_index); - atomic_dec(&priv->wmm.tx_pkts_queued); - } -} - -/* - * This function checks if the first packet in the given RA list - * is already processed or not. - */ -static int -mwifiex_is_ptr_processed(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr) -{ - struct sk_buff *skb; - struct mwifiex_txinfo *tx_info; - - if (skb_queue_empty(&ptr->skb_head)) - return false; - - skb = skb_peek(&ptr->skb_head); - - tx_info = MWIFIEX_SKB_TXCB(skb); - if (tx_info->flags & MWIFIEX_BUF_FLAG_REQUEUED_PKT) - return true; - - return false; -} - -/* - * This function sends a single processed packet to firmware for - * transmission. - */ -static void -mwifiex_send_processed_packet(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ptr, int ptr_index, - unsigned long ra_list_flags) - __releases(&priv->wmm.ra_list_spinlock) -{ - struct mwifiex_tx_param tx_param; - struct mwifiex_adapter *adapter = priv->adapter; - int ret = -1; - struct sk_buff *skb, *skb_next; - struct mwifiex_txinfo *tx_info; - - if (skb_queue_empty(&ptr->skb_head)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - return; - } - - skb = skb_dequeue(&ptr->skb_head); - - if (adapter->data_sent || adapter->tx_lock_flag) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - skb_queue_tail(&adapter->tx_data_q, skb); - atomic_inc(&adapter->tx_queued); - return; - } - - if (!skb_queue_empty(&ptr->skb_head)) - skb_next = skb_peek(&ptr->skb_head); - else - skb_next = NULL; - - tx_info = MWIFIEX_SKB_TXCB(skb); - - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); - - if (adapter->iface_type == MWIFIEX_USB) { - ret = adapter->if_ops.host_to_card(adapter, priv->usb_port, - skb, NULL); - } else { - tx_param.next_pkt_len = - ((skb_next) ? skb_next->len + - sizeof(struct txpd) : 0); - ret = adapter->if_ops.host_to_card(adapter, MWIFIEX_TYPE_DATA, - skb, &tx_param); - } - - switch (ret) { - case -EBUSY: - mwifiex_dbg(adapter, ERROR, "data: -EBUSY is returned\n"); - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); - - if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - mwifiex_write_data_complete(adapter, skb, 0, -1); - return; - } - - skb_queue_tail(&ptr->skb_head, skb); - - tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, - ra_list_flags); - break; - case -1: - mwifiex_dbg(adapter, ERROR, "host_to_card failed: %#x\n", ret); - adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, 0, ret); - break; - case -EINPROGRESS: - break; - case 0: - mwifiex_write_data_complete(adapter, skb, 0, ret); - default: - break; - } - if (ret != -EBUSY) { - mwifiex_rotate_priolists(priv, ptr, ptr_index); - atomic_dec(&priv->wmm.tx_pkts_queued); - } -} - -/* - * This function dequeues a packet from the highest priority list - * and transmits it. - */ -static int -mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) -{ - struct mwifiex_ra_list_tbl *ptr; - struct mwifiex_private *priv = NULL; - int ptr_index = 0; - u8 ra[ETH_ALEN]; - int tid_del = 0, tid = 0; - unsigned long flags; - - ptr = mwifiex_wmm_get_highest_priolist_ptr(adapter, &priv, &ptr_index); - if (!ptr) - return -1; - - tid = mwifiex_get_tid(ptr); - - mwifiex_dbg(adapter, DATA, "data: tid=%d\n", tid); - - spin_lock_irqsave(&priv->wmm.ra_list_spinlock, flags); - if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { - spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - return -1; - } - - if (mwifiex_is_ptr_processed(priv, ptr)) { - mwifiex_send_processed_packet(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - mwifiex_send_processed_packet() */ - return 0; - } - - if (!ptr->is_11n_enabled || - ptr->ba_status || - priv->wps.session_enable) { - if (ptr->is_11n_enabled && - ptr->ba_status && - ptr->amsdu_in_ampdu && - mwifiex_is_amsdu_allowed(priv, tid) && - mwifiex_is_11n_aggragation_possible(priv, ptr, - adapter->tx_buf_size)) - mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - * mwifiex_11n_aggregate_pkt() - */ - else - mwifiex_send_single_packet(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - * mwifiex_send_single_packet() - */ - } else { - if (mwifiex_is_ampdu_allowed(priv, ptr, tid) && - ptr->ba_pkt_count > ptr->ba_packet_thr) { - if (mwifiex_space_avail_for_new_ba_stream(adapter)) { - mwifiex_create_ba_tbl(priv, ptr->ra, tid, - BA_SETUP_INPROGRESS); - mwifiex_send_addba(priv, tid, ptr->ra); - } else if (mwifiex_find_stream_to_delete - (priv, tid, &tid_del, ra)) { - mwifiex_create_ba_tbl(priv, ptr->ra, tid, - BA_SETUP_INPROGRESS); - mwifiex_send_delba(priv, tid_del, ra, 1); - } - } - if (mwifiex_is_amsdu_allowed(priv, tid) && - mwifiex_is_11n_aggragation_possible(priv, ptr, - adapter->tx_buf_size)) - mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - mwifiex_11n_aggregate_pkt() */ - else - mwifiex_send_single_packet(priv, ptr, ptr_index, flags); - /* ra_list_spinlock has been freed in - mwifiex_send_single_packet() */ - } - return 0; -} - -void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter) -{ - struct mwifiex_tx_param tx_param; - struct sk_buff *skb; - struct mwifiex_txinfo *tx_info; - struct mwifiex_private *priv; - int i; - - if (adapter->data_sent || adapter->tx_lock_flag) - return; - - for (i = 0; i < adapter->priv_num; ++i) { - priv = adapter->priv[i]; - - if (!priv) - continue; - - if (adapter->if_ops.is_port_ready && - !adapter->if_ops.is_port_ready(priv)) - continue; - - if (skb_queue_empty(&priv->bypass_txq)) - continue; - - skb = skb_dequeue(&priv->bypass_txq); - tx_info = MWIFIEX_SKB_TXCB(skb); - - /* no aggregation for bypass packets */ - tx_param.next_pkt_len = 0; - - if (mwifiex_process_tx(priv, skb, &tx_param) == -EBUSY) { - skb_queue_head(&priv->bypass_txq, skb); - tx_info->flags |= MWIFIEX_BUF_FLAG_REQUEUED_PKT; - } else { - atomic_dec(&adapter->bypass_tx_pending); - } - } -} - -/* - * This function transmits the highest priority packet awaiting in the - * WMM Queues. - */ -void -mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter) -{ - do { - if (mwifiex_dequeue_tx_packet(adapter)) - break; - if (adapter->iface_type != MWIFIEX_SDIO) { - if (adapter->data_sent || - adapter->tx_lock_flag) - break; - } else { - if (atomic_read(&adapter->tx_queued) >= - MWIFIEX_MAX_PKTS_TXQ) - break; - } - } while (!mwifiex_wmm_lists_empty(adapter)); -} diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h deleted file mode 100644 index 38f09762bd2f..000000000000 --- a/drivers/net/wireless/mwifiex/wmm.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Marvell Wireless LAN device driver: WMM - * - * Copyright (C) 2011-2014, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ - -#ifndef _MWIFIEX_WMM_H_ -#define _MWIFIEX_WMM_H_ - -enum ieee_types_wmm_aciaifsn_bitmasks { - MWIFIEX_AIFSN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), - MWIFIEX_ACM = BIT(4), - MWIFIEX_ACI = (BIT(5) | BIT(6)), -}; - -enum ieee_types_wmm_ecw_bitmasks { - MWIFIEX_ECW_MIN = (BIT(0) | BIT(1) | BIT(2) | BIT(3)), - MWIFIEX_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)), -}; - -static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; - -/* - * 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. - */ -static const 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 function retrieves the TID of the given RA list. - */ -static inline int -mwifiex_get_tid(struct mwifiex_ra_list_tbl *ptr) -{ - struct sk_buff *skb; - - if (skb_queue_empty(&ptr->skb_head)) - return 0; - - skb = skb_peek(&ptr->skb_head); - - return skb->priority; -} - -/* - * This function gets the length of a list. - */ -static inline int -mwifiex_wmm_list_len(struct list_head *head) -{ - struct list_head *pos; - int count = 0; - - list_for_each(pos, head) - ++count; - - return count; -} - -/* - * This function checks if a RA list is empty or not. - */ -static inline u8 -mwifiex_wmm_is_ra_list_empty(struct list_head *ra_list_hhead) -{ - struct mwifiex_ra_list_tbl *ra_list; - int is_list_empty; - - list_for_each_entry(ra_list, ra_list_hhead, list) { - is_list_empty = skb_queue_empty(&ra_list->skb_head); - if (!is_list_empty) - return false; - } - - return true; -} - -void mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb); -void mwifiex_wmm_add_buf_bypass_txqueue(struct mwifiex_private *priv, - struct sk_buff *skb); -void mwifiex_ralist_add(struct mwifiex_private *priv, const u8 *ra); -void mwifiex_rotate_priolists(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra, int tid); - -int mwifiex_wmm_lists_empty(struct mwifiex_adapter *adapter); -int mwifiex_bypass_txlist_empty(struct mwifiex_adapter *adapter); -void mwifiex_wmm_process_tx(struct mwifiex_adapter *adapter); -void mwifiex_process_bypass_tx(struct mwifiex_adapter *adapter); -int mwifiex_is_ralist_valid(struct mwifiex_private *priv, - struct mwifiex_ra_list_tbl *ra_list, int tid); - -u8 mwifiex_wmm_compute_drv_pkt_delay(struct mwifiex_private *priv, - const struct sk_buff *skb); -void mwifiex_wmm_init(struct mwifiex_adapter *adapter); - -u32 mwifiex_wmm_process_association_req(struct mwifiex_private *priv, - u8 **assoc_buf, - struct ieee_types_wmm_parameter *wmmie, - struct ieee80211_ht_cap *htcap); - -void mwifiex_wmm_setup_queue_priorities(struct mwifiex_private *priv, - struct ieee_types_wmm_parameter *wmm_ie); -void mwifiex_wmm_setup_ac_downgrade(struct mwifiex_private *priv); -int mwifiex_ret_wmm_get_status(struct mwifiex_private *priv, - const struct host_cmd_ds_command *resp); -struct mwifiex_ra_list_tbl * -mwifiex_wmm_get_queue_raptr(struct mwifiex_private *priv, u8 tid, - const u8 *ra_addr); -u8 mwifiex_wmm_downgrade_tid(struct mwifiex_private *priv, u32 tid); -void mwifiex_update_ralist_tx_pause(struct mwifiex_private *priv, u8 *mac, - u8 tx_pause); -void mwifiex_update_ralist_tx_pause_in_tdls_cs(struct mwifiex_private *priv, - u8 *mac, u8 tx_pause); - -struct mwifiex_ra_list_tbl *mwifiex_wmm_get_ralist_node(struct mwifiex_private - *priv, u8 tid, const u8 *ra_addr); -#endif /* !_MWIFIEX_WMM_H_ */