diff options
Diffstat (limited to 'drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.c')
-rw-r--r-- | drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.c | 4505 |
1 files changed, 4505 insertions, 0 deletions
diff --git a/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.c b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.c new file mode 100644 index 000000000000..3608d9483ded --- /dev/null +++ b/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_cfg80211.c @@ -0,0 +1,4505 @@ +/** @file moal_cfg80211.c + * + * @brief This file contains the functions for CFG80211. + * + * + * Copyright 2014-2020 NXP + * + * This software file (the File) is distributed by NXP + * under the terms of the GNU General Public License Version 2, June 1991 + * (the License). You may use, redistribute and/or modify the File in + * accordance with the terms and conditions of the License, a copy of which + * is available by writing to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the + * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. + * + * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + * ARE EXPRESSLY DISCLAIMED. The License provides additional details about + * this warranty disclaimer. + * + */ + +#include "moal_cfg80211.h" +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT +#include "moal_uap.h" +#endif +#endif + +/******************************************************** + Local Variables +********************************************************/ +/** Supported rates to be advertised to the cfg80211 */ +static struct ieee80211_rate cfg80211_rates[] = { + {.bitrate = 10,.hw_value = 2,}, + {.bitrate = 20,.hw_value = 4,}, + {.bitrate = 55,.hw_value = 11,}, + {.bitrate = 110,.hw_value = 22,}, + {.bitrate = 60,.hw_value = 12,}, + {.bitrate = 90,.hw_value = 18,}, + {.bitrate = 120,.hw_value = 24,}, + {.bitrate = 180,.hw_value = 36,}, + {.bitrate = 240,.hw_value = 48,}, + {.bitrate = 360,.hw_value = 72,}, + {.bitrate = 480,.hw_value = 96,}, + {.bitrate = 540,.hw_value = 108,}, +}; + +/** Channel definitions for 2 GHz to be advertised to cfg80211 */ +static struct ieee80211_channel cfg80211_channels_2ghz[] = { + {.center_freq = 2412,.hw_value = 1,.max_power = 20}, + {.center_freq = 2417,.hw_value = 2,.max_power = 20}, + {.center_freq = 2422,.hw_value = 3,.max_power = 20}, + {.center_freq = 2427,.hw_value = 4,.max_power = 20}, + {.center_freq = 2432,.hw_value = 5,.max_power = 20}, + {.center_freq = 2437,.hw_value = 6,.max_power = 20}, + {.center_freq = 2442,.hw_value = 7,.max_power = 20}, + {.center_freq = 2447,.hw_value = 8,.max_power = 20}, + {.center_freq = 2452,.hw_value = 9,.max_power = 20}, + {.center_freq = 2457,.hw_value = 10,.max_power = 20}, + {.center_freq = 2462,.hw_value = 11,.max_power = 20}, + {.center_freq = 2467,.hw_value = 12,.max_power = 20}, + {.center_freq = 2472,.hw_value = 13,.max_power = 20}, + {.center_freq = 2484,.hw_value = 14,.max_power = 20}, +}; + +/** Channel definitions for 5 GHz to be advertised to cfg80211 */ +static struct ieee80211_channel cfg80211_channels_5ghz[] = { + {.center_freq = 5180,.hw_value = 36,.max_power = 20}, + {.center_freq = 5200,.hw_value = 40,.max_power = 20}, + {.center_freq = 5220,.hw_value = 44,.max_power = 20}, + {.center_freq = 5240,.hw_value = 48,.max_power = 20}, + {.center_freq = 5260,.hw_value = 52,.max_power = 20}, + {.center_freq = 5280,.hw_value = 56,.max_power = 20}, + {.center_freq = 5300,.hw_value = 60,.max_power = 20}, + {.center_freq = 5320,.hw_value = 64,.max_power = 20}, + {.center_freq = 5500,.hw_value = 100,.max_power = 20}, + {.center_freq = 5520,.hw_value = 104,.max_power = 20}, + {.center_freq = 5540,.hw_value = 108,.max_power = 20}, + {.center_freq = 5560,.hw_value = 112,.max_power = 20}, + {.center_freq = 5580,.hw_value = 116,.max_power = 20}, + {.center_freq = 5600,.hw_value = 120,.max_power = 20}, + {.center_freq = 5620,.hw_value = 124,.max_power = 20}, + {.center_freq = 5640,.hw_value = 128,.max_power = 20}, + {.center_freq = 5660,.hw_value = 132,.max_power = 20}, + {.center_freq = 5680,.hw_value = 136,.max_power = 20}, + {.center_freq = 5700,.hw_value = 140,.max_power = 20}, + {.center_freq = 5720,.hw_value = 144,.max_power = 20}, + {.center_freq = 5745,.hw_value = 149,.max_power = 20}, + {.center_freq = 5765,.hw_value = 153,.max_power = 20}, + {.center_freq = 5785,.hw_value = 157,.max_power = 20}, + {.center_freq = 5805,.hw_value = 161,.max_power = 20}, + {.center_freq = 5825,.hw_value = 165,.max_power = 20}, +}; + +struct ieee80211_supported_band cfg80211_band_2ghz = { + .channels = cfg80211_channels_2ghz, + .band = IEEE80211_BAND_2GHZ, + .n_channels = ARRAY_SIZE(cfg80211_channels_2ghz), + .bitrates = cfg80211_rates, + .n_bitrates = ARRAY_SIZE(cfg80211_rates), +}; + +struct ieee80211_supported_band cfg80211_band_5ghz = { + .channels = cfg80211_channels_5ghz, + .band = IEEE80211_BAND_5GHZ, + .n_channels = ARRAY_SIZE(cfg80211_channels_5ghz), + .bitrates = cfg80211_rates + 4, + .n_bitrates = ARRAY_SIZE(cfg80211_rates) - 4, +}; + +/** Channel definitions for 5 GHz to be advertised to cfg80211 */ +static struct ieee80211_channel mac1_cfg80211_channels_5ghz[] = { + {.center_freq = 5180,.hw_value = 36,.max_power = 20}, + {.center_freq = 5200,.hw_value = 40,.max_power = 20}, + {.center_freq = 5220,.hw_value = 44,.max_power = 20}, + {.center_freq = 5240,.hw_value = 48,.max_power = 20}, + {.center_freq = 5260,.hw_value = 52,.max_power = 20}, + {.center_freq = 5280,.hw_value = 56,.max_power = 20}, + {.center_freq = 5300,.hw_value = 60,.max_power = 20}, + {.center_freq = 5320,.hw_value = 64,.max_power = 20}, + {.center_freq = 5500,.hw_value = 100,.max_power = 20}, + {.center_freq = 5520,.hw_value = 104,.max_power = 20}, + {.center_freq = 5540,.hw_value = 108,.max_power = 20}, + {.center_freq = 5560,.hw_value = 112,.max_power = 20}, + {.center_freq = 5580,.hw_value = 116,.max_power = 20}, + {.center_freq = 5600,.hw_value = 120,.max_power = 20}, + {.center_freq = 5620,.hw_value = 124,.max_power = 20}, + {.center_freq = 5640,.hw_value = 128,.max_power = 20}, + {.center_freq = 5660,.hw_value = 132,.max_power = 20}, + {.center_freq = 5680,.hw_value = 136,.max_power = 20}, + {.center_freq = 5700,.hw_value = 140,.max_power = 20}, + {.center_freq = 5720,.hw_value = 144,.max_power = 20}, + {.center_freq = 5745,.hw_value = 149,.max_power = 20}, + {.center_freq = 5765,.hw_value = 153,.max_power = 20}, + {.center_freq = 5785,.hw_value = 157,.max_power = 20}, + {.center_freq = 5805,.hw_value = 161,.max_power = 20}, + {.center_freq = 5825,.hw_value = 165,.max_power = 20}, +}; + +struct ieee80211_supported_band mac1_cfg80211_band_5ghz = { + .channels = mac1_cfg80211_channels_5ghz, + .band = IEEE80211_BAND_5GHZ, + .n_channels = ARRAY_SIZE(mac1_cfg80211_channels_5ghz), + .bitrates = cfg80211_rates + 4, + .n_bitrates = ARRAY_SIZE(cfg80211_rates) - 4, +}; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) +#ifdef UAP_SUPPORT +/** Network device handlers for uAP */ +extern const struct net_device_ops woal_uap_netdev_ops; +#endif +#ifdef STA_SUPPORT +/** Network device handlers for STA */ +extern const struct net_device_ops woal_netdev_ops; +#endif +#endif +/******************************************************** + Local Functions +********************************************************/ + +/******************************************************** + Global Functions +********************************************************/ + +/** + * @brief Get the private structure from wiphy + * + * @param wiphy A pointer to wiphy structure + * + * @return Pointer to moal_private + */ +void * +woal_get_wiphy_priv(struct wiphy *wiphy) +{ + return (void *)(*(unsigned long *)wiphy_priv(wiphy)); +} + +/** + * @brief Get the private structure from net device + * + * @param dev A pointer to net_device structure + * + * @return Pointer to moal_private + */ +void * +woal_get_netdev_priv(struct net_device *dev) +{ + return (void *)netdev_priv(dev); +} + +/** + * @brief Get current frequency of active interface + * + * @param priv A pointer to moal_private + * + * @return channel frequency + */ +int +woal_get_active_intf_freq(moal_private *priv) +{ + moal_handle *handle = priv->phandle; + int i; + + if (priv->media_connected == MTRUE +#ifdef UAP_SUPPORT + || priv->bss_started == MTRUE +#endif + ) + return ieee80211_channel_to_frequency(priv->channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + (priv->channel <= + 14 ? IEEE80211_BAND_2GHZ + : IEEE80211_BAND_5GHZ) +#endif + ); + + for (i = 0; i < handle->priv_num; i++) { +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_STA) { + if (handle->priv[i]->media_connected == MTRUE) + return ieee80211_channel_to_frequency(handle-> + priv[i]-> + channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + (handle-> + priv[i]-> + channel + <= + 14 ? + IEEE80211_BAND_2GHZ + : + IEEE80211_BAND_5GHZ) +#endif + ); + + } +#endif +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(handle->priv[i]) == MLAN_BSS_ROLE_UAP) { + if (handle->priv[i]->bss_started == MTRUE) + return ieee80211_channel_to_frequency(handle-> + priv[i]-> + channel +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + , + (handle-> + priv[i]-> + channel + <= + 14 ? + IEEE80211_BAND_2GHZ + : + IEEE80211_BAND_5GHZ) +#endif + ); + } +#endif + } + return 0; +} + +/** + * @brief Convert driver band configuration to IEEE band type + * + * @param band Driver band configuration + * + * @return IEEE band type + */ +t_u8 +woal_band_cfg_to_ieee_band(t_u32 band) +{ + t_u8 ret_radio_type; + + ENTER(); + + switch (band) { + case BAND_A: + case BAND_AN: + case BAND_A | BAND_AN: + ret_radio_type = IEEE80211_BAND_5GHZ; + break; + case BAND_B: + case BAND_G: + case BAND_B | BAND_G: + case BAND_GN: + case BAND_B | BAND_GN: + default: + ret_radio_type = IEEE80211_BAND_2GHZ; + break; + } + + LEAVE(); + return ret_radio_type; +} + +/** + * @brief Set/Enable encryption key + * + * @param priv A pointer to moal_private structure + * @param is_enable_wep Enable WEP default key + * @param cipher Cipher suite selector + * @param key A pointer to key + * @param key_len Key length + * @param seq A pointer to sequence + * @param seq_len Sequence length + * @param key_index Key index + * @param addr Mac for which key is to be set + * @param disable Key disabled or not + * @param wait_option wait option + * + * @return MLAN_STATUS_SUCCESS -- success, otherwise fail + */ +mlan_status +woal_cfg80211_set_key(moal_private *priv, t_u8 is_enable_wep, + t_u32 cipher, const t_u8 *key, int key_len, + const t_u8 *seq, int seq_len, t_u8 key_index, + const t_u8 *addr, int disable, t_u8 wait_option) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_sec_cfg *sec = NULL; + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u8 bcast_addr[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + ENTER(); + +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + if (is_enable_wep) { + PRINTM(MIOCTL, "Enable UAP default key=%d\n", + key_index); + priv->uap_wep_key[key_index].is_default = MTRUE; + goto done; + } + if (key && key_len && + ((cipher == WLAN_CIPHER_SUITE_WEP40) || + (cipher == WLAN_CIPHER_SUITE_WEP104))) { + priv->uap_wep_key[key_index].length = key_len; + moal_memcpy_ext(priv->phandle, + priv->uap_wep_key[key_index].key, key, + key_len, + sizeof(priv->uap_wep_key[key_index]. + key)); + priv->cipher = cipher; + priv->uap_wep_key[key_index].key_index = key_index; + priv->uap_wep_key[key_index].is_default = MFALSE; + PRINTM(MIOCTL, "Set UAP WEP key: key_index=%d len=%d\n", + key_index, key_len); + goto done; + } + } +#endif +#endif + + /* Allocate an IOCTL request buffer */ + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_sec_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + /* Fill request buffer */ + sec = (mlan_ds_sec_cfg *)req->pbuf; + sec->sub_command = MLAN_OID_SEC_CFG_ENCRYPT_KEY; + req->req_id = MLAN_IOCTL_SEC_CFG; + req->action = MLAN_ACT_SET; + + if (is_enable_wep) { + sec->param.encrypt_key.key_index = key_index; + sec->param.encrypt_key.is_current_wep_key = MTRUE; + } else if (!disable) { + if (cipher != WLAN_CIPHER_SUITE_WEP40 && + cipher != WLAN_CIPHER_SUITE_WEP104 && + cipher != WLAN_CIPHER_SUITE_TKIP && + cipher != WLAN_CIPHER_SUITE_SMS4 && + cipher != WLAN_CIPHER_SUITE_AES_CMAC && +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4,0,0) + cipher != WLAN_CIPHER_SUITE_CCMP_256 && +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,6,0) + cipher != WLAN_CIPHER_SUITE_GCMP && +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4,0,0) + cipher != WLAN_CIPHER_SUITE_BIP_GMAC_256 && + cipher != WLAN_CIPHER_SUITE_GCMP_256 && +#endif + cipher != WLAN_CIPHER_SUITE_CCMP) { + PRINTM(MERROR, "Invalid cipher suite specified\n"); + ret = MLAN_STATUS_FAILURE; + goto done; + } + sec->param.encrypt_key.key_index = key_index; + if (key && key_len) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.key_material, + key, key_len, MLAN_MAX_KEY_LENGTH); + sec->param.encrypt_key.key_len = key_len; + } + /* Set WAPI key */ + if (cipher == WLAN_CIPHER_SUITE_SMS4) { + sec->param.encrypt_key.is_wapi_key = MTRUE; + if (seq_len) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.pn, seq, + PN_SIZE, PN_SIZE); + DBG_HEXDUMP(MCMD_D, "WAPI PN", + sec->param.encrypt_key.pn, seq_len); + } + } + if (addr) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.mac_addr, addr, + ETH_ALEN, MLAN_MAC_ADDR_LENGTH); + if (0 == + memcmp(sec->param.encrypt_key.mac_addr, bcast_addr, + ETH_ALEN)) + sec->param.encrypt_key.key_flags = + KEY_FLAG_GROUP_KEY; + else + sec->param.encrypt_key.key_flags = + KEY_FLAG_SET_TX_KEY; + } else { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.mac_addr, + bcast_addr, ETH_ALEN, + MLAN_MAC_ADDR_LENGTH); + sec->param.encrypt_key.key_flags = + KEY_FLAG_GROUP_KEY | KEY_FLAG_SET_TX_KEY; + } + if (seq && seq_len) { + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.pn, seq, seq_len, + PN_SIZE); + sec->param.encrypt_key.key_flags |= + KEY_FLAG_RX_SEQ_VALID; + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,6,0) + if (cipher == WLAN_CIPHER_SUITE_GCMP) + sec->param.encrypt_key.key_flags |= KEY_FLAG_GCMP; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4,0,0) + else if (cipher == WLAN_CIPHER_SUITE_GCMP_256) + sec->param.encrypt_key.key_flags |= KEY_FLAG_GCMP_256; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4,0,0) + if (cipher == WLAN_CIPHER_SUITE_CCMP_256) + sec->param.encrypt_key.key_flags |= KEY_FLAG_CCMP_256; +#endif + + if (cipher == WLAN_CIPHER_SUITE_AES_CMAC +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4,0,0) + || cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256 +#endif + ) { + sec->param.encrypt_key.key_flags |= + KEY_FLAG_AES_MCAST_IGTK; + } + } else { + if (key_index == KEY_INDEX_CLEAR_ALL) + sec->param.encrypt_key.key_disable = MTRUE; + else { + sec->param.encrypt_key.key_remove = MTRUE; + sec->param.encrypt_key.key_index = key_index; + } + sec->param.encrypt_key.key_flags = KEY_FLAG_REMOVE_KEY; + if (addr) + moal_memcpy_ext(priv->phandle, + sec->param.encrypt_key.mac_addr, addr, + ETH_ALEN, MLAN_MAC_ADDR_LENGTH); + } + + /* Send IOCTL request to MLAN */ + ret = woal_request_ioctl(priv, req, wait_option); + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Set/Enable the WEP key to driver + * + * @param priv A pointer to moal_private structure + * @param key A pointer to key data + * @param key_len Length of the key data + * @param index Key index + * @param wait_option wait_option + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_cfg80211_set_wep_keys(moal_private *priv, const t_u8 *key, int key_len, + t_u8 index, t_u8 wait_option) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + t_u32 cipher = 0; + + ENTER(); + + if (key_len) { + if (key_len == 5) + cipher = WLAN_CIPHER_SUITE_WEP40; + else + cipher = WLAN_CIPHER_SUITE_WEP104; + ret = woal_cfg80211_set_key(priv, 0, cipher, key, key_len, NULL, + 0, index, NULL, 0, wait_option); + } else { + /* No key provided so it is enable key. We + * want to just set the transmit key index */ + woal_cfg80211_set_key(priv, 1, cipher, key, key_len, NULL, 0, + index, NULL, 0, wait_option); + } + + LEAVE(); + return ret; +} + +/** + * @brief clear all mgmt ies + * + * @param priv A pointer to moal private structure + * @param wait_option wait_option + * @return N/A + */ +void +woal_clear_all_mgmt_ies(moal_private *priv, t_u8 wait_option) +{ + t_u16 mask = 0; + /* clear BEACON WPS/P2P IE */ + if (priv->beacon_wps_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + PRINTM(MCMND, "Clear BEACON WPS ie\n"); + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, MGMT_MASK_BEACON_WPS_P2P, + wait_option); + priv->beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if (priv->assocresp_qos_map_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + PRINTM(MCMND, "Clear associate response QOS map ie\n"); + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, + MGMT_MASK_ASSOC_RESP_QOS_MAP, + wait_option); + priv->assocresp_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + /* clear mgmt frame ies */ + if (priv->probereq_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_REQ; + if (priv->beacon_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_BEACON; + if (priv->proberesp_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_RESP; + if (priv->assocresp_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_ASSOC_RESP; + if (priv->proberesp_p2p_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_PROBE_RESP; + if (priv->beacon_vendor_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) + mask |= MGMT_MASK_BEACON; + if (mask) { + PRINTM(MCMND, "Clear IES: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", + priv->beacon_index, priv->probereq_index, + priv->proberesp_index, priv->assocresp_index, + priv->proberesp_p2p_index, priv->beacon_vendor_index); + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, 0, NULL, 0, + NULL, 0, mask, wait_option); + } + priv->probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + priv->beacon_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; +} + +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) +/** + * @brief set bss role + * + * @param priv A pointer to moal private structure + * @param action Action: set or get + * @param role A pointer to bss role + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_bss_role_cfg(moal_private *priv, t_u16 action, t_u8 *bss_role) +{ + int ret = 0; + + ENTER(); + + if (action == MLAN_ACT_SET) { + /* Reset interface */ + woal_reset_intf(priv, MOAL_IOCTL_WAIT, MFALSE); + } + + if (MLAN_STATUS_SUCCESS != + woal_bss_role_cfg(priv, action, MOAL_IOCTL_WAIT, bss_role)) { + ret = -EFAULT; + goto done; + } + + if (action == MLAN_ACT_SET) { + /* set back the mac address */ + woal_request_set_mac_address(priv, MOAL_IOCTL_WAIT); + /* clear the mgmt ies */ + woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); + /* Initialize private structures */ + woal_init_priv(priv, MOAL_IOCTL_WAIT); + + /* Enable interfaces */ + netif_device_attach(priv->netdev); + woal_start_queue(priv->netdev); + } + +done: + LEAVE(); + return ret; +} +#endif + +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION +/** + * @brief This function display P2P public action frame type + * + * @param buf A buffer to a management frame + * @param len buffer len + * @param chan the channel + * @param flag Tx/Rx flag. Tx:flag = 1;Rx:flag = 0; + * + * @return N/A + */ +void +woal_cfg80211_display_p2p_actframe(const t_u8 *buf, int len, + struct ieee80211_channel *chan, + const t_u8 flag) +{ + const t_u8 p2p_oui[] = { 0x50, 0x6f, 0x9a, 0x09 }; + t_u8 subtype; + + ENTER(); + + if (!buf || len < (P2P_ACT_FRAME_OUI_SUBTYPE_OFFSET + 1)) { + LEAVE(); + return; + } + + if (((struct ieee80211_mgmt *)buf)->u.action.category == + P2P_ACT_FRAME_CATEGORY && + !memcmp(buf + P2P_ACT_FRAME_OUI_OFFSET, p2p_oui, sizeof(p2p_oui))) { + subtype = *(buf + P2P_ACT_FRAME_OUI_SUBTYPE_OFFSET); + switch (subtype) { + case P2P_GO_NEG_REQ: + PRINTM(MMSG, + "wlan: %s P2P Group Owner Negotiation Req Frame, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_GO_NEG_RSP: + PRINTM(MMSG, + "wlan: %s P2P Group Owner Negotiation Rsp Frame, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_GO_NEG_CONF: + PRINTM(MMSG, + "wlan: %s P2P Group Owner Negotiation Confirm Frame, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_INVITE_REQ: + PRINTM(MMSG, + "wlan: %s P2P Invitation Request, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_INVITE_RSP: + PRINTM(MMSG, + "wlan: %s P2P Invitation Response, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_DEVDIS_REQ: + PRINTM(MMSG, + "wlan: %s P2P Device Discoverability Request, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_DEVDIS_RSP: + PRINTM(MIOCTL, + "wlan: %s P2P Device Discoverability Response, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_PROVDIS_REQ: + PRINTM(MMSG, + "wlan: %s P2P Provision Discovery Request, channel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + case P2P_PROVDIS_RSP: + PRINTM(MMSG, + "wlan: %s P2P Provision Discovery Response, channnel=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0); + break; + default: + PRINTM(MMSG, + "wlan: %s Unknow P2P Action Frame, channel=%d, subtype=%d\n", + (flag) ? "TX" : "RX", + (chan) ? chan->hw_value : 0, subtype); + break; + } + } + + LEAVE(); + return; +} + +/** + * @brief initialize p2p client for wpa_supplicant + * + * @param priv A pointer to moal private structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_init_p2p_client(moal_private *priv) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u16 wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + t_u8 bss_role; + + ENTER(); + + /* bss type check */ + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MERROR, "Unexpected bss type when init p2p client\n"); + ret = -EFAULT; + goto done; + } + + /* get the bss role */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_GET, &bss_role)) { + ret = -EFAULT; + goto done; + } + + if (bss_role != MLAN_BSS_ROLE_STA) { + bss_role = MLAN_BSS_ROLE_STA; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, &bss_role)) { + ret = -EFAULT; + goto done; + } + } + + wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* first, init wifi direct to listen mode */ + wifi_direct_mode = WIFI_DIRECT_MODE_LISTEN; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* second, init wifi direct client */ + wifi_direct_mode = WIFI_DIRECT_MODE_CLIENT; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} + +/** + * @brief initialize p2p GO for wpa_supplicant + * + * @param priv A pointer to moal private structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_init_p2p_go(moal_private *priv) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u16 wifi_direct_mode; + t_u8 bss_role; + mlan_ds_wifi_direct_config p2p_config; + mlan_ds_ps_mgmt ps_mgmt; + + ENTER(); + + /* bss type check */ + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MERROR, "Unexpected bss type when init p2p GO\n"); + ret = -EFAULT; + goto done; + } + + wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* first, init wifi direct to listen mode */ + wifi_direct_mode = WIFI_DIRECT_MODE_LISTEN; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* second, init wifi direct to GO mode */ + wifi_direct_mode = WIFI_DIRECT_MODE_GO; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } + + /* get the bss role, and set it to uAP */ + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_GET, &bss_role)) { + ret = -EFAULT; + goto done; + } + + if (bss_role != MLAN_BSS_ROLE_UAP) { + bss_role = MLAN_BSS_ROLE_UAP; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, &bss_role)) { + ret = -EFAULT; + goto done; + } + } +/* NoA:-- Interval = 100TUs and Duration= 50TUs, count=255*/ +#define DEF_NOA_COUNT 255 + if (priv->phandle->noa_duration && priv->phandle->card_info->go_noa) { + memset(&p2p_config, 0, sizeof(p2p_config)); + p2p_config.noa_enable = MTRUE; + p2p_config.index = 0; + p2p_config.noa_count = DEF_NOA_COUNT; + p2p_config.noa_duration = priv->phandle->noa_duration; + p2p_config.noa_interval = priv->phandle->noa_interval; + p2p_config.flags = WIFI_DIRECT_NOA; + woal_p2p_config(priv, MLAN_ACT_SET, &p2p_config); + memset(&ps_mgmt, 0, sizeof(ps_mgmt)); + ps_mgmt.flags = PS_FLAG_PS_MODE; + ps_mgmt.ps_mode = PS_MODE_INACTIVITY; + woal_set_get_uap_power_mode(priv, MLAN_ACT_SET, &ps_mgmt); + PRINTM(MMSG, "Enable NOA: duration=%d, interval=%d\n", + priv->phandle->noa_duration, + priv->phandle->noa_interval); + } +done: + LEAVE(); + return ret; +} + +/** + * @brief reset bss role and wifi direct mode for wpa_supplicant + * + * @param priv A pointer to moal private structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_deinit_p2p(moal_private *priv) +{ + int ret = MLAN_STATUS_SUCCESS; + t_u16 wifi_direct_mode; + t_u8 bss_role; + t_u8 channel_status; + moal_private *remain_priv = NULL; + mlan_ds_ps_mgmt ps_mgmt; + + ENTER(); + + /* bss type check */ + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + PRINTM(MERROR, "Unexpected bss type when deinit p2p\n"); + ret = -EFAULT; + goto done; + } + /* unregister mgmt frame from FW */ + if (priv->mgmt_subtype_mask) { + priv->mgmt_subtype_mask = 0; + if (woal_reg_rx_mgmt_ind(priv, MLAN_ACT_SET, + &priv->mgmt_subtype_mask, + MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, + "deinit_p2p: fail to unregister mgmt frame\n"); + ret = -EFAULT; + goto done; + } + } + + /* cancel previous remain on channel */ + if (priv->phandle->remain_on_channel) { + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "deinit_p2p: wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, + 0, 0)) { + PRINTM(MERROR, + "deinit_p2p: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv-> + netdev, +#else + remain_priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } + + /* get the bss role */ + if (MLAN_STATUS_SUCCESS != woal_cfg80211_bss_role_cfg(priv, + MLAN_ACT_GET, + &bss_role)) { + ret = -EFAULT; + goto done; + } + + /* reset bss role */ + if (bss_role != MLAN_BSS_ROLE_STA) { + memset(&ps_mgmt, 0, sizeof(ps_mgmt)); + ps_mgmt.flags = PS_FLAG_PS_MODE; + ps_mgmt.ps_mode = PS_MODE_DISABLE; + woal_set_get_uap_power_mode(priv, MLAN_ACT_SET, &ps_mgmt); + bss_role = MLAN_BSS_ROLE_STA; + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, &bss_role)) { + ret = -EFAULT; + goto done; + } + } + + wifi_direct_mode = WIFI_DIRECT_MODE_DISABLE; + if (MLAN_STATUS_SUCCESS != + woal_wifi_direct_mode_cfg(priv, MLAN_ACT_SET, &wifi_direct_mode)) { + ret = -EFAULT; + goto done; + } +done: + LEAVE(); + return ret; +} +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + +/** + * @brief Request the driver to change the interface type + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param type Virtual interface types + * @param flags Flags + * @param params A pointer to vif_params structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_change_virtual_intf(struct wiphy *wiphy, + struct net_device *dev, + enum nl80211_iftype type, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + u32 *flags, +#endif + struct vif_params *params) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_ds_bss *bss = NULL; + mlan_ioctl_req *req = NULL; +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + t_u8 bss_role; +#endif + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + + if (priv->wdev->iftype == type) { + PRINTM(MINFO, "Already set to required type\n"); + goto done; + } +#ifdef UAP_SUPPORT + if ((priv->bss_type == MLAN_BSS_TYPE_UAP) && (priv->bss_index > 0)) { + priv->wdev->iftype = type; + PRINTM(MMSG, "%s: Skip change virtual intf on uap: type=%d\n", + dev->name, type); + goto done; + } +#endif + + PRINTM(MIOCTL, "%s: change virturl intf=%d\n", dev->name, type); +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + /** cancel previous remain on channel to avoid firmware hang */ + if (priv->phandle->remain_on_channel) { + t_u8 channel_status; + moal_private *remain_priv = NULL; + remain_priv = + priv->phandle->priv[priv->phandle->remain_bss_index]; + if (!remain_priv) { + PRINTM(MERROR, + "change_virtual_intf:wrong remain_bss_index=%d\n", + priv->phandle->remain_bss_index); + ret = -EFAULT; + goto done; + } + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, &channel_status, NULL, + 0, 0)) { + PRINTM(MERROR, + "change_virtual_intf: Fail to cancel remain on channel\n"); + ret = -EFAULT; + goto done; + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv-> + netdev, +#else + remain_priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle->chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } +#endif +#endif + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + bss = (mlan_ds_bss *)req->pbuf; + bss->sub_command = MLAN_OID_BSS_MODE; + req->req_id = MLAN_IOCTL_BSS; + req->action = MLAN_ACT_SET; + + switch (type) { + case NL80211_IFTYPE_STATION: +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT + && (priv->wdev->iftype == NL80211_IFTYPE_AP + || priv->wdev->iftype == NL80211_IFTYPE_P2P_GO + || priv->wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { + if (priv->phandle->is_go_timer_set) { + woal_cancel_timer(&priv->phandle->go_timer); + priv->phandle->is_go_timer_set = MFALSE; + } + /* if we support wifi direct && priv->bss_type == wifi_direct, + * and currently the interface type is AP or GO or client, + * that means wpa_supplicant deinit() wifi direct interface, + * so we should deinit bss_role and wifi direct mode, + * for other bss_type, we should not update bss_role + * and wifi direct mode */ + + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_deinit_p2p(priv)) { + ret = -EFAULT; + goto done; + } + } +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + woal_cfg80211_del_beacon(wiphy, dev); + bss_role = MLAN_BSS_ROLE_STA; + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, + &bss_role); + PRINTM(MIOCTL, "set bss role for STA\n"); + } +#endif + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + priv->wdev->iftype = NL80211_IFTYPE_STATION; + PRINTM(MINFO, "Setting interface type to managed\n"); + break; +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + case NL80211_IFTYPE_P2P_CLIENT: + if (priv->phandle->is_go_timer_set) { + woal_cancel_timer(&priv->phandle->go_timer); + priv->phandle->is_go_timer_set = MFALSE; + } + + if (MLAN_STATUS_SUCCESS != woal_cfg80211_init_p2p_client(priv)) { + ret = -EFAULT; + goto done; + } + + bss->param.bss_mode = MLAN_BSS_MODE_INFRA; + priv->wdev->iftype = NL80211_IFTYPE_P2P_CLIENT; + PRINTM(MINFO, "Setting interface type to P2P client\n"); + + break; +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + case NL80211_IFTYPE_AP: +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + case NL80211_IFTYPE_P2P_GO: + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) { + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_init_p2p_go(priv)) { + ret = -EFAULT; + goto done; + } + priv->phandle->is_go_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->go_timer, + MOAL_TIMER_10S); + } + if (type == NL80211_IFTYPE_P2P_GO) + priv->wdev->iftype = NL80211_IFTYPE_P2P_GO; +#endif +#endif +#if defined(STA_SUPPORT) && defined(UAP_SUPPORT) + if (priv->bss_type == MLAN_BSS_TYPE_STA) { +#ifdef STA_CFG80211 + /** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#endif + if (priv->probereq_index != + MLAN_CUSTOM_IE_AUTO_IDX_MASK) + woal_cfg80211_mgmt_frame_ie(priv, NULL, 0, NULL, + 0, NULL, 0, NULL, 0, + MGMT_MASK_PROBE_REQ, + MOAL_IOCTL_WAIT); + bss_role = MLAN_BSS_ROLE_UAP; + woal_cfg80211_bss_role_cfg(priv, MLAN_ACT_SET, + &bss_role); + PRINTM(MIOCTL, "set bss role for AP\n"); + } +#endif + if (type == NL80211_IFTYPE_AP) + priv->wdev->iftype = NL80211_IFTYPE_AP; + PRINTM(MINFO, "Setting interface type to P2P GO\n"); + + /* there is no need for P2P GO to set bss_mode */ + goto done; + + break; + + case NL80211_IFTYPE_UNSPECIFIED: + bss->param.bss_mode = MLAN_BSS_MODE_AUTO; + priv->wdev->iftype = NL80211_IFTYPE_STATION; + PRINTM(MINFO, "Setting interface type to auto\n"); + break; + default: + ret = -EINVAL; + break; + } + if (ret) + goto done; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to change the value of fragment + * threshold or rts threshold or retry limit + * + * @param wiphy A pointer to wiphy structure + * @param changed Change flags + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) +{ + moal_private *priv = NULL; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + mlan_uap_bss_param *sys_cfg = NULL; +#endif +#endif + int frag_thr = wiphy->frag_threshold; + int rts_thr = wiphy->rts_threshold; + int retry = wiphy->retry_long; + + ENTER(); + + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + LEAVE(); + return -EFAULT; + } + if (rts_thr == MLAN_FRAG_RTS_DISABLED) + rts_thr = MLAN_RTS_MAX_VALUE; + if (frag_thr == MLAN_FRAG_RTS_DISABLED) + frag_thr = MLAN_FRAG_MAX_VALUE; + +#ifdef UAP_CFG80211 +#ifdef UAP_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { + sys_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); + if (!sys_cfg) { + PRINTM(MERROR, + "Fail to alloc memory for mlan_uap_bss_param\n"); + LEAVE(); + return -EFAULT; + } + /* Initialize the invalid values so that the correct + * values below are downloaded to firmware */ + woal_set_sys_config_invalid_data(sys_cfg); + sys_cfg->frag_threshold = frag_thr; + sys_cfg->rts_threshold = rts_thr; + sys_cfg->retry_limit = retry; + + if ((changed & WIPHY_PARAM_RTS_THRESHOLD) || + (changed & WIPHY_PARAM_FRAG_THRESHOLD) || + (changed & + (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT))) { + if (woal_set_get_sys_config + (priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, sys_cfg)) { + kfree(sys_cfg); + goto fail; + } + } + kfree(sys_cfg); + } +#endif +#endif + +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (changed & WIPHY_PARAM_RTS_THRESHOLD) { + if (woal_set_get_rts + (priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &rts_thr)) + goto fail; + } + if (changed & WIPHY_PARAM_FRAG_THRESHOLD) { + if (woal_set_get_frag + (priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &frag_thr)) + goto fail; + } + if (changed & + (WIPHY_PARAM_RETRY_LONG | WIPHY_PARAM_RETRY_SHORT)) + if (woal_set_get_retry + (priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, &retry)) + goto fail; + } +#endif +#endif + LEAVE(); + return 0; + +fail: + PRINTM(MERROR, "Failed to change wiphy params %x\n", changed); + LEAVE(); + return -EFAULT; +} + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) +/** + * @brief Request the driver to add a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param pairwise Flag to indicate pairwise or group (for kernel > 2.6.36) + * @param mac_addr MAC address (NULL for group key) + * @param params A pointer to key_params structure + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request the driver to add a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param mac_addr MAC address (NULL for group key) + * @param params A pointer to key_params structure + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_add_key(struct wiphy *wiphy, struct net_device *netdev, + t_u8 key_index, +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) + bool pairwise, +#endif + const t_u8 *mac_addr, struct key_params *params) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(netdev); + + ENTER(); + if (priv->ft_pre_connect) { + PRINTM(MINFO, "Skip set keys during ft connecting\n"); + return -EFAULT; + } + if (woal_cfg80211_set_key(priv, 0, params->cipher, params->key, + params->key_len, params->seq, params->seq_len, + key_index, mac_addr, 0, MOAL_IOCTL_WAIT)) { + PRINTM(MERROR, "Error adding the crypto keys\n"); + LEAVE(); + return -EFAULT; + } + + PRINTM(MINFO, "Crypto keys added\n"); + + LEAVE(); + return 0; +} + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) +/** + * @brief Request the driver to delete a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param pairwise Flag to indicate pairwise or group (for kernel > 2.6.36) + * @param mac_addr MAC address (NULL for group key) + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request the driver to delete a key + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param mac_addr MAC address (NULL for group key) + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_del_key(struct wiphy *wiphy, struct net_device *netdev, + t_u8 key_index, +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 36) + bool pairwise, +#endif + const t_u8 *mac_addr) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(netdev); + + ENTER(); + if (priv->phandle->driver_status) { + PRINTM(MERROR, + "Block woal_cfg80211_del_key in abnormal driver state\n"); + LEAVE(); + return -EFAULT; + } + + if (MLAN_STATUS_FAILURE == + woal_cfg80211_set_key(priv, 0, 0, NULL, 0, NULL, 0, key_index, + mac_addr, 1, MOAL_NO_WAIT)) { + PRINTM(MERROR, "Error deleting the crypto keys\n"); + LEAVE(); + return -EFAULT; + } + + PRINTM(MINFO, "Crypto keys deleted\n"); + LEAVE(); + return 0; +} + +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) +/** + * @brief Request to enable WEP key to driver + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * @param ucast Unicast flag (for kernel > 2.6.37) + * @param mcast Multicast flag (for kernel > 2.6.37) + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request to enable WEP key to driver + * + * @param wiphy A pointer to wiphy structure + * @param netdev A pointer to net_device structure + * @param key_index Key index + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_set_default_key(struct wiphy *wiphy, + struct net_device *netdev, t_u8 key_index +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) + , bool ucast, bool mcast +#endif + ) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(netdev); + mlan_bss_info bss_info; + + ENTER(); + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (!bss_info.wep_status) { + LEAVE(); + return ret; + } + } + if (MLAN_STATUS_SUCCESS != + woal_cfg80211_set_wep_keys(priv, NULL, 0, key_index, + MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) +int +woal_cfg80211_set_default_mgmt_key(struct wiphy *wiphy, + struct net_device *netdev, t_u8 key_index) +{ + PRINTM(MINFO, "set default mgmt key, key index=%d\n", key_index); + + return 0; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) +/** + * @brief Set GTK rekey data to driver + * + * @param priv A pointer to moal_private structure + * @param gtk_rekey A pointer to mlan_ds_misc_gtk_rekey_data structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * + * @return 0 --success, otherwise fail + */ +mlan_status +woal_set_rekey_data(moal_private *priv, mlan_ds_misc_gtk_rekey_data * gtk_rekey, + t_u8 action, t_u8 wait_option) +{ + mlan_ioctl_req *req; + mlan_ds_misc_cfg *misc_cfg; + int ret = 0; + mlan_status status; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + + if (NULL == req) { + ret = -ENOMEM; + } else { + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_GTK_REKEY_OFFLOAD; + req->req_id = MLAN_IOCTL_MISC_CFG; + + req->action = action; + if (action == MLAN_ACT_SET) + moal_memcpy_ext(priv->phandle, + &misc_cfg->param.gtk_rekey, gtk_rekey, + sizeof(mlan_ds_misc_gtk_rekey_data), + sizeof(mlan_ds_misc_gtk_rekey_data)); + + status = woal_request_ioctl(priv, req, wait_option); + if (MLAN_STATUS_SUCCESS != status) + ret = -EFAULT; + if (status != MLAN_STATUS_PENDING) + kfree(req); + } + + LEAVE(); + return ret; +} + +/** + * @brief Give the data necessary for GTK rekeying to the driver + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param data A pointer to cfg80211_gtk_rekey_data structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_gtk_rekey_data *data) +{ + int ret = 0; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_ds_misc_gtk_rekey_data rekey; + mlan_fw_info fw_info; + + ENTER(); + + if (priv->phandle->params.gtk_rekey_offload == + GTK_REKEY_OFFLOAD_DISABLE) { + PRINTM(MMSG, + "woal_cfg80211_set_rekey_data return: gtk_rekey_offload is DISABLE\n"); + LEAVE(); + return ret; + } + + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if (!fw_info.fw_supplicant_support) { + LEAVE(); + return -1; + } + + moal_memcpy_ext(priv->phandle, rekey.kek, data->kek, MLAN_KEK_LEN, + MLAN_KEK_LEN); + moal_memcpy_ext(priv->phandle, rekey.kck, data->kck, MLAN_KCK_LEN, + MLAN_KCK_LEN); + moal_memcpy_ext(priv->phandle, rekey.replay_ctr, data->replay_ctr, + MLAN_REPLAY_CTR_LEN, MLAN_REPLAY_CTR_LEN); + + moal_memcpy_ext(priv->phandle, &priv->gtk_rekey_data, &rekey, + sizeof(mlan_ds_misc_gtk_rekey_data), + sizeof(mlan_ds_misc_gtk_rekey_data)); + if (priv->phandle->params.gtk_rekey_offload == + GTK_REKEY_OFFLOAD_SUSPEND) { + priv->gtk_data_ready = MTRUE; + LEAVE(); + return ret; + } + + if (MLAN_STATUS_SUCCESS != + woal_set_rekey_data(priv, &rekey, MLAN_ACT_SET, MOAL_IOCTL_WAIT)) { + ret = -EFAULT; + } + + LEAVE(); + return ret; +} +#endif + +#ifdef STA_SUPPORT +/* Opportunistic Key Caching APIs functions support + * + * this function get pmksa entry list in private structure + * @param priv A pointer to moal_private structure + * @param bssid A pointer to bssid + * @return pointer to target entry or NULL + */ +struct pmksa_entry * +woal_get_pmksa_entry(moal_private *priv, const u8 *bssid) +{ + struct pmksa_entry *entry = NULL; + unsigned long flags; + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + return NULL; + } + + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_for_each_entry(entry, &priv->pmksa_cache_list, link) { + if (!memcmp(entry->bssid, bssid, ETH_ALEN)) { + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + return entry; + } + } + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + + return NULL; +} + +/** + * This function flush pmksa entry list in private structure + * @param priv A pointer to moal_private structure + * @return success of failure + */ +int +woal_flush_pmksa_list(moal_private *priv) +{ + struct pmksa_entry *entry, *tmp; + unsigned long flags; + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + return -1; + } + + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_for_each_entry_safe(entry, tmp, &priv->pmksa_cache_list, link) { + list_del(&entry->link); + kfree(entry); + } + INIT_LIST_HEAD(&priv->pmksa_cache_list); + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + + return 0; +} + +/** + * This function add new pmksa entry to list + * @param wiphy A pointer to struct wiphy + * @param dev A pointer to net_device structure + * @param pmksa A pointer to cfg80211_pmksa structure + * @return success of failure + */ +int +woal_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct pmksa_entry *entry = NULL; + unsigned long flags; + int ret = 0; + + ENTER(); + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + ret = -1; + goto done; + } + + PRINTM(MIOCTL, "Set pmksa entry: bssid=" MACSTR "\n", + MAC2STR(pmksa->bssid)); + entry = woal_get_pmksa_entry(priv, pmksa->bssid); + if (!entry) { + entry = kzalloc(sizeof(struct pmksa_entry), GFP_ATOMIC); + if (!entry) { + PRINTM(MERROR, "Fail to allocate pmksa entry\n"); + goto done; + } + INIT_LIST_HEAD(&entry->link); + moal_memcpy_ext(priv->phandle, entry->bssid, pmksa->bssid, + ETH_ALEN, ETH_ALEN); + moal_memcpy_ext(priv->phandle, entry->pmkid, pmksa->pmkid, + PMKID_LEN, PMKID_LEN); + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_add_tail(&entry->link, &priv->pmksa_cache_list); + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + } else { + /** pmkid is differnt from previous value? */ + memset(entry->pmkid, 0, PMKID_LEN); + moal_memcpy_ext(priv->phandle, entry->pmkid, pmksa->pmkid, + PMKID_LEN, PMKID_LEN); + } + + /** Check if current roaming is going and received target pmkid */ + if (priv->wait_target_ap_pmkid) { + struct cfg80211_connect_params *param = &priv->sme_current; + if (param && !memcmp(pmksa->bssid, param->bssid, ETH_ALEN)) { + PRINTM(MIOCTL, + "Current roaming target bssid=" MACSTR "\n", + MAC2STR(param->bssid)); + priv->target_ap_pmksa = entry; + priv->wait_target_ap_pmkid = MFALSE; + wake_up_interruptible(&priv->okc_wait_q); + } + } + +done: + LEAVE(); + return ret; +} + +/** + * This function delete pmksa entry + * @param wiphy A pointer to struct wiphy + * @param dev A pointer to net_device structure + * @param pmksa A pointer to cfg80211_pmksa structure + * @return success of failure + */ +int +woal_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_pmksa *pmksa) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + struct pmksa_entry *entry, *tmp; + unsigned long flags; + + ENTER(); + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + PRINTM(MERROR, "Invalid interface structure\n"); + LEAVE(); + return -1; + } + + PRINTM(MIOCTL, "Delete pmksa: bssid=" MACSTR "\n", + MAC2STR(pmksa->bssid)); + spin_lock_irqsave(&priv->pmksa_list_lock, flags); + list_for_each_entry_safe(entry, tmp, &priv->pmksa_cache_list, link) { + if (!memcmp(entry->bssid, pmksa->bssid, ETH_ALEN)) { + list_del(&entry->link); + kfree(entry); + } + } + spin_unlock_irqrestore(&priv->pmksa_list_lock, flags); + + LEAVE(); + return 0; +} + +/** + * This function flush pmksa entry list + * @param wiphy A pointer to struct wiphy + * @param dev A pointer to net_device structure + * @return success of failure + */ +int +woal_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + if (!priv || priv->bss_type != MLAN_BSS_TYPE_STA) { + return -1; + } + + PRINTM(MIOCTL, "Flush pmksa list.\n"); + return woal_flush_pmksa_list(priv); +} +#endif + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 5, 0) +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 34) +/** + * @brief Request the driver to change the channel + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type of nl80211_channel_type + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief Request the driver to change the channel + * + * @param wiphy A pointer to wiphy structure + * @param chan A pointer to ieee80211_channel structure + * @param channel_type Channel type of nl80211_channel_type + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_set_channel(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 34) + struct net_device *dev, +#endif + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type) +{ + int ret = 0; + moal_private *priv = NULL; + + ENTER(); + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 5, 0) +#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 34) + if (dev) + priv = woal_get_netdev_priv(dev); +#endif +#endif + if (!priv) { + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + if (handle) + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + } + if (priv) { +#ifdef STA_CFG80211 +#ifdef STA_SUPPORT + if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) { + if (priv->media_connected == MTRUE) { + PRINTM(MERROR, + "This configuration is valid only when station " + "is not connected\n"); + LEAVE(); + return -EINVAL; + } + ret = woal_set_rf_channel(priv, chan, channel_type, + MOAL_IOCTL_WAIT); + } +#endif +#endif + priv->channel = + ieee80211_frequency_to_channel(chan->center_freq); + } + /* set monitor channel support */ + + LEAVE(); + return ret; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) +static bool +woal_is_pattern_supported(struct cfg80211_pkt_pattern *pat, t_u8 *byte_seq, + t_u8 max_byte_seq) +{ + int j, k, valid_byte_cnt = 0; + bool dont_care_byte = false; + + for (j = 0; j < DIV_ROUND_UP(pat->pattern_len, 8); j++) { + for (k = 0; k < 8; k++) { + if (pat->mask[j] & 1 << k) { + moal_memcpy_ext(NULL, byte_seq + valid_byte_cnt, + &pat->pattern[j * 8 + k], 1, + (t_u32)max_byte_seq - + (t_u32)valid_byte_cnt); + valid_byte_cnt++; + if (dont_care_byte) + return false; + } else { + if (valid_byte_cnt) + dont_care_byte = true; + } + + if (valid_byte_cnt > max_byte_seq) + return false; + } + } + + byte_seq[max_byte_seq] = valid_byte_cnt; + + return true; +} + +static int +woal_get_coalesce_pkt_type(t_u8 *byte_seq) +{ + const t_u8 ipv4_mc_mac[] = { 0x33, 0x33 }; + const t_u8 ipv6_mc_mac[] = { 0x01, 0x00, 0x5e }; + const t_u8 bc_mac[] = { 0xff, 0xff, 0xff, 0xff }; + + if ((byte_seq[0] & 0x01) && (byte_seq[COALESCE_MAX_BYTESEQ] == 1)) + return PACKET_TYPE_UNICAST; + else if (!memcmp(byte_seq, bc_mac, 4)) + return PACKET_TYPE_BROADCAST; + else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) && + byte_seq[COALESCE_MAX_BYTESEQ] == 2) || + (!memcmp(byte_seq, ipv6_mc_mac, 3) && + byte_seq[COALESCE_MAX_BYTESEQ] == 3)) + return PACKET_TYPE_MULTICAST; + + return 0; +} + +static int +woal_fill_coalesce_rule_info(struct cfg80211_coalesce_rules *crule, + struct coalesce_rule *mrule) +{ + t_u8 byte_seq[COALESCE_MAX_BYTESEQ + 1]; + struct filt_field_param *param; + int i; + + mrule->max_coalescing_delay = crule->delay; + + param = mrule->params; + + for (i = 0; i < crule->n_patterns; i++) { + memset(byte_seq, 0, sizeof(byte_seq)); + if (!woal_is_pattern_supported(&crule->patterns[i], + byte_seq, + COALESCE_MAX_BYTESEQ)) { + PRINTM(MERROR, "Pattern not supported\n"); + return -EOPNOTSUPP; + } + + if (!crule->patterns[i].pkt_offset) { + u8 pkt_type; + + pkt_type = woal_get_coalesce_pkt_type(byte_seq); + if (pkt_type && mrule->pkt_type) { + PRINTM(MERROR, + "Multiple packet types not allowed\n"); + return -EOPNOTSUPP; + } else if (pkt_type) { + mrule->pkt_type = pkt_type; + continue; + } + } + + if (crule->condition == NL80211_COALESCE_CONDITION_MATCH) + param->operation = RECV_FILTER_MATCH_TYPE_EQ; + else + param->operation = RECV_FILTER_MATCH_TYPE_NE; + + param->operand_len = byte_seq[COALESCE_MAX_BYTESEQ]; + moal_memcpy_ext(NULL, param->operand_byte_stream, byte_seq, + param->operand_len, COALESCE_MAX_BYTESEQ); + param->offset = crule->patterns[i].pkt_offset; + param++; + + mrule->num_of_fields++; + } + + if (!mrule->pkt_type) { + PRINTM(MERROR, "Packet type can not be determined\n"); + return -EOPNOTSUPP; + } + + return 0; +} + +/** + * @brief Set coalesce parameter + * + * @param priv A pointer to moal_private structure + * @param action MLAN_ACT_SET or MLAN_ACT_GET + * @param coalesce_cfg A pointer to coalesce structure + * + * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE + */ +mlan_status +woal_set_coalesce(moal_private *priv, t_u16 action, + mlan_ds_coalesce_cfg * coalesce_cfg) +{ + mlan_status ret = MLAN_STATUS_SUCCESS; + mlan_ds_misc_cfg *misc_cfg = NULL; + mlan_ioctl_req *req = NULL; + + ENTER(); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (req == NULL) { + ret = MLAN_STATUS_FAILURE; + goto done; + } + + misc_cfg = (mlan_ds_misc_cfg *)req->pbuf; + misc_cfg->sub_command = MLAN_OID_MISC_COALESCE_CFG; + req->req_id = MLAN_IOCTL_MISC_CFG; + req->action = action; + + moal_memcpy_ext(priv->phandle, &misc_cfg->param.coalesce_cfg, + coalesce_cfg, sizeof(mlan_ds_coalesce_cfg), + sizeof(mlan_ds_coalesce_cfg)); + + ret = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (ret != MLAN_STATUS_SUCCESS) + goto done; + +done: + if (ret != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +/** + * @brief Request the driver to set the coalesce + * + * @param wiphy A pointer to wiphy structure + * @param coalesce A pointer to cfg80211_coalesce structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_coalesce(struct wiphy *wiphy, + struct cfg80211_coalesce *coalesce) +{ + int ret = 0; + int i; + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + mlan_ds_coalesce_cfg coalesce_cfg; + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + ret = -EINVAL; + goto done; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = -EINVAL; + goto done; + } + + memset(&coalesce_cfg, 0, sizeof(coalesce_cfg)); + if (!coalesce) { + PRINTM(MMSG, "Disable coalesce and reset all previous rules\n"); + } else { + coalesce_cfg.num_of_rules = coalesce->n_rules; + for (i = 0; i < coalesce->n_rules; i++) { + ret = woal_fill_coalesce_rule_info(&coalesce->rules[i], + &coalesce_cfg. + rule[i]); + if (ret) { + PRINTM(MERROR, + "Recheck the patterns provided for rule %d\n", + i + 1); + return ret; + } + } + } + + if (MLAN_STATUS_SUCCESS != + woal_set_coalesce(priv, MLAN_ACT_SET, &coalesce_cfg)) { + PRINTM(MERROR, "wlan: Fail to set coalesce\n"); + ret = -EFAULT; + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief Request the driver to set the bitrate + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param peer A pointer to peer address + * @param mask A pointer to cfg80211_bitrate_mask structure + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_bitrate_mask(struct wiphy *wiphy, + struct net_device *dev, + const u8 *peer, + const struct cfg80211_bitrate_mask *mask) +{ + int ret = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + mlan_bss_info bss_info; + enum ieee80211_band band; + mlan_ioctl_req *req = NULL; + mlan_ds_rate *rate = NULL; + mlan_rate_cfg_t *rate_cfg = NULL; + + ENTER(); + + if (priv->media_connected == MFALSE) { + PRINTM(MERROR, "Can not set data rate in disconnected state\n"); + ret = -EINVAL; + goto done; + } + + status = woal_get_bss_info(priv, MOAL_IOCTL_WAIT, &bss_info); + if (status) + goto done; + band = woal_band_cfg_to_ieee_band(bss_info.bss_band); + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_rate)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + rate = (mlan_ds_rate *)req->pbuf; + rate_cfg = &rate->param.rate_cfg; + rate->sub_command = MLAN_OID_RATE_CFG; + req->req_id = MLAN_IOCTL_RATE; + req->action = MLAN_ACT_SET; + rate_cfg->rate_type = MLAN_RATE_BITMAP; + + /* Fill HR/DSSS rates. */ + if (band == IEEE80211_BAND_2GHZ) + rate_cfg->bitmap_rates[0] = mask->control[band].legacy & 0x000f; + + /* Fill OFDM rates */ + if (band == IEEE80211_BAND_2GHZ) + rate_cfg->bitmap_rates[1] = + (mask->control[band].legacy & 0x0ff0) >> 4; + else + rate_cfg->bitmap_rates[1] = mask->control[band].legacy; + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) + /* Fill MCS rates */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + rate_cfg->bitmap_rates[2] = mask->control[band].ht_mcs[0]; +#else + rate_cfg->bitmap_rates[2] = mask->control[band].mcs[0]; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + rate_cfg->bitmap_rates[2] |= mask->control[band].ht_mcs[1] << 8; +#else + rate_cfg->bitmap_rates[2] |= mask->control[band].mcs[1] << 8; +#endif +#endif + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return ret; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) +/** + * @brief Request the driver to get antenna configuration + * + * @param wiphy A pointer to wiphy structure + * @param tx_ant Bitmaps of allowed antennas to use for TX + * @param rx_ant Bitmaps of allowed antennas to use for RX + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_get_antenna(struct wiphy *wiphy, u32 *tx_ant, u32 *rx_ant) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + ret = -EINVAL; + goto done; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_GET; + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto done; + } + + if (handle->feature_control & FEATURE_CTRL_STREAM_2X2) { + *tx_ant = radio->param.ant_cfg.tx_antenna; + *rx_ant = radio->param.ant_cfg.rx_antenna; + } else { + *tx_ant = radio->param.ant_cfg_1x1.antenna; + *rx_ant = radio->param.ant_cfg_1x1.antenna; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + /* Driver must return -EINVAL to cfg80211 */ + if (ret) + ret = -EINVAL; + LEAVE(); + return ret; + +} + +/** + * @brief Request the driver to set antenna configuration + * + * @param wiphy A pointer to wiphy structure + * @param tx_ant Bitmaps of allowed antennas to use for TX + * @param rx_ant Bitmaps of allowed antennas to use for RX + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_antenna(struct wiphy *wiphy, u32 tx_ant, u32 rx_ant) +{ + moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); + moal_private *priv = NULL; + mlan_ds_radio_cfg *radio = NULL; + mlan_ioctl_req *req = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + int ret = 0; + + ENTER(); + + if (!handle) { + PRINTM(MFATAL, "Unable to get handle\n"); + ret = -EINVAL; + goto done; + } + priv = woal_get_priv(handle, MLAN_BSS_ROLE_ANY); + if (!priv) { + ret = -EINVAL; + goto done; + } + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_radio_cfg)); + if (req == NULL) { + ret = -ENOMEM; + goto done; + } + radio = (mlan_ds_radio_cfg *)req->pbuf; + radio->sub_command = MLAN_OID_ANT_CFG; + req->req_id = MLAN_IOCTL_RADIO_CFG; + req->action = MLAN_ACT_SET; + if (handle->feature_control & FEATURE_CTRL_STREAM_2X2) { + radio->param.ant_cfg.tx_antenna = tx_ant; + radio->param.ant_cfg.rx_antenna = rx_ant; + } else { + radio->param.ant_cfg_1x1.antenna = tx_ant; + } + + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) { + ret = -EFAULT; + goto done; + } + +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + /* Driver must return -EINVAL to cfg80211 */ + if (ret) + ret = -EINVAL; + LEAVE(); + return ret; +} +#endif +/** + * @brief register/unregister mgmt frame forwarding + * + * @param priv A pointer to moal_private structure + * @param frame_type Bit mask for mgmt frame type + * @param reg Register or unregister + * + * @return 0 -- success, otherwise fail + */ +void +woal_mgmt_frame_register(moal_private *priv, u16 frame_type, bool reg) +{ + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 mgmt_subtype_mask = 0x0; + t_u32 last_mgmt_subtype_mask = priv->mgmt_subtype_mask; + + ENTER(); + +#ifdef SDIO_SUSPEND_RESUME + if (priv->phandle->shutdown_hs_in_process) { + LEAVE(); + return; + } +#endif + + if (reg == MTRUE) { + /* set mgmt_subtype_mask based on origin value */ + mgmt_subtype_mask = + last_mgmt_subtype_mask | BIT(frame_type >> 4); + } else { + /* clear mgmt_subtype_mask */ + mgmt_subtype_mask = + last_mgmt_subtype_mask & ~BIT(frame_type >> 4); + } + PRINTM(MIOCTL, + "%s: frame_type=0x%x mgmt_subtype_mask=0x%x last_mgmt_subtype_mask=0x%x\n", + priv->netdev->name, frame_type, mgmt_subtype_mask, + last_mgmt_subtype_mask); + if (mgmt_subtype_mask != last_mgmt_subtype_mask) { + + last_mgmt_subtype_mask = mgmt_subtype_mask; + /* Notify driver that a mgmt frame type was registered. + * Note that this callback may not sleep, and cannot run + * concurrently with itself. */ + status = woal_reg_rx_mgmt_ind(priv, MLAN_ACT_SET, + &mgmt_subtype_mask, MOAL_NO_WAIT); + priv->mgmt_subtype_mask = last_mgmt_subtype_mask; + } + + LEAVE(); +} + +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) +/** + * @brief register/unregister mgmt frame forwarding + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param frame_type Bit mask for mgmt frame type + * @param reg Register or unregister + * + * @return 0 -- success, otherwise fail + */ +void +woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct net_device *dev, u16 frame_type, + bool reg) +#else +/** + * @brief register/unregister mgmt frame forwarding + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param frame_type Bit mask for mgmt frame type + * @param reg Register or unregister + * + * @return 0 -- success, otherwise fail + */ +void +woal_cfg80211_mgmt_frame_register(struct wiphy *wiphy, + struct wireless_dev *wdev, u16 frame_type, + bool reg) +#endif +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + + ENTER(); + + if (frame_type == IEEE80211_STYPE_AUTH +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + /** Supplicant 2.8 always register auth, FW will handle auth when host_mlme=0 */ + && !moal_extflg_isset(priv->phandle, EXT_HOST_MLME) +#endif + ) { + LEAVE(); + return; + } + woal_mgmt_frame_register(priv, frame_type, reg); + + LEAVE(); +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param params A pointer to cfg80211_mgmt_tx_params structure + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param dont_wait_for_ack Do not wait for ACK + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param wdev A pointer to wireless_dev structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param dont_wait_for_ack Do not wait for ACK + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param dont_wait_for_ack Do not wait for ACK + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param no_cck No CCK check + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +#else +/** + * @brief tx mgmt frame + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param chan A pointer to ieee80211_channel structure + * @param offchan Off channel or not + * @param channel_type Channel type + * @param channel_type_valid Is channel type valid or not + * @param wait Duration to wait + * @param buf Frame buffer + * @param len Frame length + * @param cookie A pointer to frame cookie + * + * @return 0 -- success, otherwise fail + */ +#endif +int +woal_cfg80211_mgmt_tx(struct wiphy *wiphy, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + struct net_device *dev, +#else + struct wireless_dev *wdev, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + struct cfg80211_mgmt_tx_params *params, +#else + struct ieee80211_channel *chan, bool offchan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + enum nl80211_channel_type channel_type, + bool channel_type_valid, +#endif + unsigned int wait, const u8 *buf, size_t len, +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) + bool no_cck, +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) + bool dont_wait_for_ack, +#endif +#endif + u64 * cookie) +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) + struct net_device *dev = wdev->netdev; +#endif +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) + struct ieee80211_channel *chan = params->chan; + unsigned int wait = params->wait; + const u8 *buf = params->buf; + size_t len = params->len; +#endif + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int ret = 0; + pmlan_buffer pmbuf = NULL; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u16 packet_len = 0; + t_u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + t_u32 pkt_type; + t_u32 tx_control; +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + t_u8 channel_status; + t_u32 duration; + moal_private *remain_priv = NULL; +#endif + + unsigned long flags; + struct sk_buff *skb = NULL; + struct tx_status_info *tx_info = NULL; + t_u32 remain_len = 0; + t_u16 fc, type, stype; + + ENTER(); + + if (buf == NULL || len == 0) { + PRINTM(MERROR, "woal_cfg80211_mgmt_tx() corrupt data\n"); + LEAVE(); + return -EFAULT; + } + + /* If the packet is probe response, that means we are in listen phase, + so we should not call remain_on_channel_cfg because + remain_on_channl already handled it. If the packet if action, that + means we are in PD/GO negotiation, so we should call + remain_on_channel_cfg in order to receive action frame from peer + device */ + if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP)) { + if (ieee80211_is_probe_resp + (((struct ieee80211_mgmt *)buf)->frame_control)) { + PRINTM(MIOCTL, "Skip send probe_resp in GO/UAP mode\n"); + goto done; + } + fc = le16_to_cpu(((struct ieee80211_mgmt *)buf)->frame_control); + type = fc & IEEE80211_FCTL_FTYPE; + stype = fc & IEEE80211_FCTL_STYPE; + if (type == IEEE80211_FTYPE_MGMT) { + switch (stype) { + case IEEE80211_STYPE_AUTH: + PRINTM(MMSG, "wlan: HostMlme %s send Auth\n", + priv->netdev->name); + break; + case IEEE80211_STYPE_DEAUTH: + case IEEE80211_STYPE_DISASSOC: + PRINTM(MMSG, + "wlan: HostMlme %s send deauth/disassoc\n", + priv->netdev->name); + break; + case IEEE80211_STYPE_ASSOC_RESP: + case IEEE80211_STYPE_REASSOC_RESP: + PRINTM(MMSG, + "wlan: HostMlme %s send assoc/reassoc resp\n", + priv->netdev->name); + break; + default: + break; + } + } + } +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) + if ((ieee80211_is_action(((struct ieee80211_mgmt *)buf)->frame_control)) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + || moal_extflg_isset(priv->phandle, EXT_HOST_MLME) +#endif + ) { +#ifdef WIFI_DIRECT_SUPPORT + if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) + woal_cfg80211_display_p2p_actframe(buf, len, chan, + MTRUE); + if (priv->phandle->is_go_timer_set) { + woal_cancel_timer(&priv->phandle->go_timer); + priv->phandle->is_go_timer_set = MFALSE; + } +#endif + if (priv->phandle->is_remain_timer_set) { + woal_cancel_timer(&priv->phandle->remain_timer); + woal_remain_timer_func(priv->phandle); + } + /* With sd8777 We have difficulty to receive response packet in 500ms */ +#define MGMT_TX_DEFAULT_WAIT_TIME 1500 + if (priv->phandle->remain_on_channel) + remain_priv = + priv->phandle->priv[priv->phandle-> + remain_bss_index]; + /** cancel previous remain on channel */ + if (priv->phandle->remain_on_channel && remain_priv) { + if ((priv->phandle->chan.center_freq != + chan->center_freq) + ) { + if (woal_cfg80211_remain_on_channel_cfg + (remain_priv, MOAL_IOCTL_WAIT, MTRUE, + &channel_status, NULL, 0, 0)) + PRINTM(MERROR, + "mgmt_tx:Fail to cancel remain on channel\n"); + } + if (priv->phandle->cookie) { + cfg80211_remain_on_channel_expired( +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + remain_priv-> + netdev, +#else + remain_priv-> + wdev, +#endif + priv-> + phandle-> + cookie, + &priv-> + phandle-> + chan, +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv-> + phandle-> + channel_type, +#endif + GFP_ATOMIC); + priv->phandle->cookie = 0; + } + priv->phandle->remain_on_channel = MFALSE; + } +#ifdef STA_CFG80211 + /** cancel pending scan */ + woal_cancel_scan(priv, MOAL_IOCTL_WAIT); +#endif + + if (chan) { + duration = + (wait > + MGMT_TX_DEFAULT_WAIT_TIME) ? wait : + MGMT_TX_DEFAULT_WAIT_TIME; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + if (channel_type_valid) + ret = woal_cfg80211_remain_on_channel_cfg(priv, + MOAL_IOCTL_WAIT, + MFALSE, + &channel_status, + chan, + channel_type, + duration); + else +#endif + ret = woal_cfg80211_remain_on_channel_cfg(priv, + MOAL_IOCTL_WAIT, + MFALSE, + &channel_status, + chan, + 0, + duration); + if (ret) { + /* Return fail will cause p2p connnection fail */ + woal_sched_timeout(2); +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + if (channel_type_valid) + ret = woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, MFALSE, &channel_status, chan, channel_type, duration); + else +#endif + ret = woal_cfg80211_remain_on_channel_cfg(priv, MOAL_IOCTL_WAIT, MFALSE, &channel_status, chan, 0, duration); + PRINTM(MERROR, + "Try configure remain on channel again, ret=%d\n", + ret); + ret = 0; + } else { + priv->phandle->remain_on_channel = MTRUE; + priv->phandle->remain_bss_index = + priv->bss_index; +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + priv->phandle->channel_type = channel_type; +#endif + moal_memcpy_ext(priv->phandle, + &priv->phandle->chan, chan, + sizeof(struct + ieee80211_channel), + sizeof(struct + ieee80211_channel)); + PRINTM(MIOCTL, + "%s: Mgmt Tx: Set remain channel=%d duration=%d\n", + dev->name, + ieee80211_frequency_to_channel(chan-> + center_freq), + duration); + } + } + } +#endif + + /* pkt_type + tx_control */ +#define HEADER_SIZE 8 + packet_len = (t_u16)len + MLAN_MAC_ADDR_LENGTH; + pmbuf = woal_alloc_mlan_buffer(priv->phandle, + MLAN_MIN_DATA_HEADER_LEN + HEADER_SIZE + + packet_len + sizeof(packet_len)); + if (!pmbuf) { + PRINTM(MERROR, "Fail to allocate mlan_buffer\n"); + ret = -ENOMEM; + goto done; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) + *cookie = random32() | 1; +#else + *cookie = prandom_u32() | 1; +#endif + pmbuf->data_offset = MLAN_MIN_DATA_HEADER_LEN; + pkt_type = MRVL_PKT_TYPE_MGMT_FRAME; + tx_control = 0; + remain_len = HEADER_SIZE + packet_len + sizeof(packet_len); + /* Add pkt_type and tx_control */ + moal_memcpy_ext(priv->phandle, pmbuf->pbuf + pmbuf->data_offset, + &pkt_type, sizeof(pkt_type), remain_len); + remain_len -= sizeof(pkt_type); + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + sizeof(pkt_type), + &tx_control, sizeof(tx_control), remain_len); + remain_len -= sizeof(tx_control); + /* frmctl + durationid + addr1 + addr2 + addr3 + seqctl */ +#define PACKET_ADDR4_POS (2 + 2 + 6 + 6 + 6 + 2) + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE, + &packet_len, sizeof(packet_len), remain_len); + remain_len -= sizeof(packet_len); + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE + + sizeof(packet_len), buf, PACKET_ADDR4_POS, remain_len); + remain_len -= PACKET_ADDR4_POS; + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE + + sizeof(packet_len) + + PACKET_ADDR4_POS, addr, MLAN_MAC_ADDR_LENGTH, + remain_len); + remain_len -= MLAN_MAC_ADDR_LENGTH; + moal_memcpy_ext(priv->phandle, + pmbuf->pbuf + pmbuf->data_offset + HEADER_SIZE + + sizeof(packet_len) + + PACKET_ADDR4_POS + MLAN_MAC_ADDR_LENGTH, + buf + PACKET_ADDR4_POS, len - PACKET_ADDR4_POS, + remain_len); + + pmbuf->data_len = HEADER_SIZE + packet_len + sizeof(packet_len); + pmbuf->buf_type = MLAN_BUF_TYPE_RAW_DATA; + pmbuf->bss_index = priv->bss_index; + if ((ieee80211_is_action(((struct ieee80211_mgmt *)buf)->frame_control)) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + || moal_extflg_isset(priv->phandle, EXT_HOST_MLME) +#endif + ) { + pmbuf->flags = MLAN_BUF_FLAG_TX_STATUS; + pmbuf->tx_seq_num = ++priv->tx_seq_num; + tx_info = kzalloc(sizeof(struct tx_status_info), GFP_ATOMIC); + if (tx_info) { + skb = alloc_skb(len, GFP_ATOMIC); + if (skb) { + moal_memcpy_ext(priv->phandle, skb->data, buf, + len, len); + skb_put(skb, len); + spin_lock_irqsave(&priv->tx_stat_lock, flags); + tx_info->tx_cookie = *cookie; + tx_info->tx_skb = skb; + tx_info->tx_seq_num = pmbuf->tx_seq_num; + if (priv->phandle->remain_on_channel && !wait) + tx_info->cancel_remain_on_channel = + MTRUE; + INIT_LIST_HEAD(&tx_info->link); + list_add_tail(&tx_info->link, + &priv->tx_stat_queue); + spin_unlock_irqrestore(&priv->tx_stat_lock, + flags); + } else { + kfree(tx_info); + tx_info = NULL; + } + } + } + + status = mlan_send_packet(priv->phandle->pmlan_adapter, pmbuf); + + switch (status) { + case MLAN_STATUS_PENDING: + atomic_inc(&priv->phandle->tx_pending); + queue_work(priv->phandle->workqueue, &priv->phandle->main_work); + + /* Delay 30ms to guarantee the packet has been already tx'ed, + * becuase if we call cfg80211_mgmt_tx_status() immediately, + * then wpa_supplicant will call cancel_remain_on_channel(), + * which may affect the mgmt frame tx. Meanwhile it is only + * necessary for P2P action handshake to wait 30ms. */ + if ((ieee80211_is_action + (((struct ieee80211_mgmt *)buf)->frame_control)) +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + || moal_extflg_isset(priv->phandle, EXT_HOST_MLME) +#endif + ) { + if (tx_info) + break; + else + woal_sched_timeout(30); + } + /* Notify the mgmt tx status */ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) +#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) + cfg80211_mgmt_tx_status(dev, *cookie, buf, len, true, + GFP_ATOMIC); +#else + cfg80211_mgmt_tx_status(priv->wdev, *cookie, buf, len, true, + GFP_ATOMIC); +#endif +#endif + break; + case MLAN_STATUS_SUCCESS: + woal_free_mlan_buffer(priv->phandle, pmbuf); + break; + case MLAN_STATUS_FAILURE: + default: + woal_free_mlan_buffer(priv->phandle, pmbuf); + ret = -EFAULT; + break; + } + +done: + + if (status != MLAN_STATUS_PENDING) { + if (tx_info) + woal_remove_tx_info(priv, tx_info->tx_seq_num); + + } + + LEAVE(); + return ret; +} + +/** + * @brief Add custom ie to mgmt frames. + * + * @param priv A pointer to moal private structure + * @param beacon_ies_data Beacon ie + * @param beacon_index The index for beacon when auto index + * @param proberesp_ies_data Probe resp ie + * @param proberesp_index The index for probe resp when auto index + * @param assocresp_ies_data Assoc resp ie + * @param assocresp_index The index for assoc resp when auto index + * @param probereq_ies_data Probe req ie + * @param probereq_index The index for probe req when auto index + * @param wait_option wait option + * + * @return 0 -- success, otherwise fail + */ +static mlan_status +woal_cfg80211_custom_ie(moal_private *priv, + custom_ie *beacon_ies_data, t_u16 *beacon_index, + custom_ie *proberesp_ies_data, t_u16 *proberesp_index, + custom_ie *assocresp_ies_data, t_u16 *assocresp_index, + custom_ie *probereq_ies_data, t_u16 *probereq_index, + t_u8 wait_option) +{ + mlan_ioctl_req *ioctl_req = NULL; + mlan_ds_misc_cfg *misc = NULL; + mlan_ds_misc_custom_ie *custom_ie = NULL; + t_u8 *pos = NULL; + t_u16 len = 0; + mlan_status status = MLAN_STATUS_SUCCESS; + t_u32 remain_len = 0; + + ENTER(); + + custom_ie = kzalloc(sizeof(mlan_ds_misc_custom_ie), GFP_KERNEL); + if (!custom_ie) { + PRINTM(MERROR, "Fail to allocate custome_ie\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + + custom_ie->type = TLV_TYPE_MGMT_IE; + + pos = (t_u8 *)custom_ie->ie_data_list; + remain_len = sizeof(custom_ie->ie_data_list); + if (beacon_ies_data) { + len = sizeof(*beacon_ies_data) - MAX_IE_SIZE + + beacon_ies_data->ie_length; + moal_memcpy_ext(priv->phandle, pos, beacon_ies_data, len, + remain_len); + pos += len; + remain_len -= len; + custom_ie->len += len; + } + + if (proberesp_ies_data) { + len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE + + proberesp_ies_data->ie_length; + moal_memcpy_ext(priv->phandle, pos, proberesp_ies_data, len, + remain_len); + pos += len; + remain_len -= len; + custom_ie->len += len; + } + + if (assocresp_ies_data) { + len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE + + assocresp_ies_data->ie_length; + moal_memcpy_ext(priv->phandle, pos, assocresp_ies_data, len, + remain_len); + pos += len; + remain_len -= len; + custom_ie->len += len; + } + + if (probereq_ies_data) { + len = sizeof(*probereq_ies_data) - MAX_IE_SIZE + + probereq_ies_data->ie_length; + moal_memcpy_ext(priv->phandle, pos, probereq_ies_data, len, + remain_len); + pos += len; + remain_len -= len; + custom_ie->len += len; + } + ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_misc_cfg)); + if (ioctl_req == NULL) { + PRINTM(MERROR, "Fail to allocate ioctl_req\n"); + status = MLAN_STATUS_FAILURE; + goto done; + } + + misc = (mlan_ds_misc_cfg *)ioctl_req->pbuf; + misc->sub_command = MLAN_OID_MISC_CUSTOM_IE; + ioctl_req->req_id = MLAN_IOCTL_MISC_CFG; + ioctl_req->action = MLAN_ACT_SET; + + moal_memcpy_ext(priv->phandle, &misc->param.cust_ie, custom_ie, + sizeof(mlan_ds_misc_custom_ie), + sizeof(mlan_ds_misc_custom_ie)); + + status = woal_request_ioctl(priv, ioctl_req, wait_option); + if (MLAN_STATUS_SUCCESS != status) { + goto done; + } + + /* get the assigned index */ + pos = (t_u8 *)(&misc->param.cust_ie.ie_data_list[0].ie_index); + if (beacon_ies_data && beacon_ies_data->ie_length + && beacon_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save beacon ie index after auto-indexing */ + *beacon_index = misc->param.cust_ie.ie_data_list[0].ie_index; + len = sizeof(*beacon_ies_data) - MAX_IE_SIZE + + beacon_ies_data->ie_length; + pos += len; + } + + if (proberesp_ies_data && proberesp_ies_data->ie_length + && proberesp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save probe resp ie index after auto-indexing */ + *proberesp_index = *((t_u16 *)pos); + len = sizeof(*proberesp_ies_data) - MAX_IE_SIZE + + proberesp_ies_data->ie_length; + pos += len; + } + + if (assocresp_ies_data && assocresp_ies_data->ie_length + && assocresp_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save assoc resp ie index after auto-indexing */ + *assocresp_index = *((t_u16 *)pos); + len = sizeof(*assocresp_ies_data) - MAX_IE_SIZE + + assocresp_ies_data->ie_length; + pos += len; + } + if (probereq_ies_data && probereq_ies_data->ie_length + && probereq_ies_data->ie_index == MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* save probe resp ie index after auto-indexing */ + *probereq_index = *((t_u16 *)pos); + len = sizeof(*probereq_ies_data) - MAX_IE_SIZE + + probereq_ies_data->ie_length; + pos += len; + } + //TODO why we check status_code at end + if (ioctl_req->status_code == MLAN_ERROR_IOCTL_FAIL) + status = MLAN_STATUS_FAILURE; + +done: + if (status != MLAN_STATUS_PENDING) + kfree(ioctl_req); + kfree(custom_ie); + LEAVE(); + return status; +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3,14,0) +/** + * @brief set Qos map + * + * @param wiphy A pointer to wiphy structure + * @param dev A pointer to net_device structure + * @param qos_map A pointer to cfg80211_qos_map structure + * + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_set_qos_map(struct wiphy *wiphy, + struct net_device *dev, + struct cfg80211_qos_map *qos_map) +{ + moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); + int i, j, ret = 0; + + ENTER(); + /**clear dscp map*/ + if (!qos_map) { + memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); + goto done; + } + + /**update dscp map*/ + for (i = 0; i < MAX_NUM_TID; i++) { + PRINTM(MINFO, "TID %d: dscp_low=%d, dscp_high=%d\n", i, + qos_map->up[i].low, qos_map->up[i].high); + if (qos_map->up[i].low != 0xff && qos_map->up[i].high != 0xff + && qos_map->up[i].high <= 63) { + for (j = qos_map->up[i].low; j <= qos_map->up[i].high; + j++) + priv->dscp_map[j] = i; + } + } + + for (i = 0; i < qos_map->num_des; i++) { + if ((qos_map->dscp_exception[i].dscp <= 63) && + (qos_map->dscp_exception[i].up <= 7)) { + PRINTM(MINFO, "dscp excpt: value=%d priority=%d\n", + qos_map->dscp_exception[i].dscp, + qos_map->dscp_exception[i].up); + priv->dscp_map[qos_map->dscp_exception[i].dscp] = + qos_map->dscp_exception[i].up; + } + } + + /**UAP update (re)associate response*/ + if (priv->bss_type == MLAN_BSS_TYPE_UAP) { + IEEEtypes_Generic_t qos_map_ie; + t_u16 qos_map_ies_len; + + qos_map_ie.ieee_hdr.element_id = QOS_MAPPING; + qos_map_ie.ieee_hdr.len = + 2 * qos_map->num_des + sizeof(qos_map->up); + qos_map_ies_len = + qos_map_ie.ieee_hdr.len + sizeof(qos_map_ie.ieee_hdr); + + if (qos_map_ies_len > sizeof(qos_map_ie.data)) { + PRINTM(MERROR, + "QoS MAP IE size exceeds the buffer len\n"); + goto done; + } + moal_memcpy_ext(priv->phandle, qos_map_ie.data, + (t_u8 *)qos_map->dscp_exception, + 2 * qos_map->num_des, sizeof(qos_map_ie.data)); + moal_memcpy_ext(priv->phandle, + &qos_map_ie.data[2 * qos_map->num_des], + (t_u8 *)qos_map->up, sizeof(qos_map->up), + sizeof(qos_map_ie.data) - 2 * qos_map->num_des); + + /* set the assoc response ies */ + ret = woal_cfg80211_mgmt_frame_ie(priv, + NULL, 0, NULL, 0, + (t_u8 *)&qos_map_ie, + qos_map_ies_len, NULL, 0, + MGMT_MASK_ASSOC_RESP_QOS_MAP, + MOAL_IOCTL_WAIT); + if (ret) { + PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); + goto done; + } + + } + +done: + LEAVE(); + return ret; +} +#endif + +/** + * @brief get specific ie + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ie_out Pointer to out IE buf + * @param ie_out_len Total length of ie_out + * @param mask IE mask + * + * @return out IE length + */ +static t_u16 +woal_get_specific_ie(const t_u8 *ie, int len, t_u8 *ie_out, t_u32 ie_out_len, + t_u16 mask) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + t_u16 out_len = 0; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 }; + const u8 p2p_oui[4] = { 0x50, 0x6f, 0x9a, 0x09 }; + const u8 wfd_oui[4] = { 0x50, 0x6f, 0x9a, 0x0a }; + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + + ENTER(); + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + if (id == VENDOR_SPECIFIC_221) { + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp + (pvendor_ie->vend_hdr.oui, wmm_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wmm_oui[3]) { + PRINTM(MIOCTL, "find WMM IE\n"); + } else if (!memcmp + (pvendor_ie->vend_hdr.oui, p2p_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == + p2p_oui[3]) { + if (mask & IE_MASK_P2P) { + /** only get first p2p ie here */ + moal_memcpy_ext(NULL, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + break; + } + } else if (!memcmp + (pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == + wps_oui[3]) { + if (mask & IE_MASK_WPS) { + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(NULL, + ie_out + + out_len, pos, + length + 2, + ie_out_len - + out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "get_specific_ie: IE too big, fail copy WPS IE\n"); + break; + } + } + } else if (!memcmp + (pvendor_ie->vend_hdr.oui, wfd_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == + wfd_oui[3]) { + if (mask & IE_MASK_WFD) { + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(NULL, + ie_out + + out_len, pos, + length + 2, + ie_out_len - + out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "get_specific_ie: IE too big, fail copy WFD IE\n"); + break; + } + } + } else if (mask & IE_MASK_VENDOR) { + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(NULL, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "get_specific_ie:IE too big, fail copy VENDOR IE\n"); + break; + } + } + } + pos += (length + 2); + left_len -= (length + 2); + } + LEAVE(); + return out_len; +} + +/** + * @brief Find specific IE from IE buffer + * + * @param ie Pointer to IEs + * @param len Total length of ie + * @param spec_ie Pointer to specific IE buffer + * @param spec_len Total length of specifc IE + * + * @return out IE length + */ +static t_u8 +woal_find_ie(const t_u8 *ie, int len, const t_u8 *spec_ie, int spec_len) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + if ((length + 2) == spec_len) { + if (!memcmp(pos, spec_ie, spec_len)) { + return MTRUE; + } + } + pos += (length + 2); + left_len -= (length + 2); + } + return MFALSE; +} + +/** + * @brief Filter specific IE in ie buf + * + * @param priv pointer to moal private structure + * @param ie Pointer to IEs + * @param len Total length of ie + * @param ie_out Pointer to out IE buf + * @param ie_out_len Total length of ie_out + * @param wps_flag flag for wps/p2p + * @param dup_ie Pointer to duplicate ie + * @param dup_ie_len duplicate IE len + * + * @return out IE length + */ +static t_u16 +woal_filter_beacon_ies(moal_private *priv, const t_u8 *ie, int len, + t_u8 *ie_out, t_u32 ie_out_len, t_u16 wps_flag, + const t_u8 *dup_ie, int dup_ie_len) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + t_u16 out_len = 0; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 }; + const u8 p2p_oui[4] = { 0x50, 0x6f, 0x9a, 0x09 }; + const u8 wfd_oui[4] = { 0x50, 0x6f, 0x9a, 0x0a }; + const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; + t_u8 find_p2p_ie = MFALSE; + t_u8 enable_11d = MFALSE; + t_u8 ext_id = 0; + int ie_len; + + /* ERP_INFO/EXTENDED_SUPPORT_RATES/HT_CAPABILITY/HT_OPERATION/WMM + * and WPS/P2P/WFD IE will be fileter out */ + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + if (dup_ie && dup_ie_len && + woal_find_ie(dup_ie, dup_ie_len, pos, length + 2)) { + PRINTM(MIOCTL, "skip duplicate IE\n"); + pos += (length + 2); + left_len -= (length + 2); + continue; + } + switch (id) { + case COUNTRY_INFO: + enable_11d = MTRUE; + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(priv->phandle, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "IE too big, fail copy COUNTRY INFO IE\n"); + } + break; + case EXTENDED_SUPPORTED_RATES: + case WLAN_EID_ERP_INFO: + case HT_CAPABILITY: + case HT_OPERATION: + case VHT_CAPABILITY: + case VHT_OPERATION: + case REGULATORY_CLASS: + case OVERLAPBSSSCANPARAM: + case WAPI_IE: + break; + case EXTENSION: + ext_id = *(pos + 2); + if (ext_id == HE_CAPABILITY || ext_id == HE_OPERATION) + break; + else { + if ((out_len + length + 2) < ie_out_len) { + memcpy(ie_out + out_len, pos, + length + 2); + out_len += length + 2; + } else { + PRINTM(MERROR, + "IE too big, fail copy EXTENSION IE\n"); + } + break; + } + case EXT_CAPABILITY: + /* filter out EXTCAP */ + if (wps_flag & IE_MASK_EXTCAP) { + ie_len = length + 2; + woal_set_get_gen_ie(priv, MLAN_ACT_SET, + (t_u8 *)pos, &ie_len, + MOAL_IOCTL_WAIT); + break; + } + if ((out_len + length + 2) < ie_out_len) { + memcpy(ie_out + out_len, pos, length + 2); + out_len += length + 2; + } else { + PRINTM(MERROR, + "IE too big, fail copy EXTCAP IE\n"); + } + break; + case VENDOR_SPECIFIC_221: + /* filter out wmm ie */ + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp + (pvendor_ie->vend_hdr.oui, wmm_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wmm_oui[3]) { + break; + } + /* filter out wps ie */ + else if (!memcmp + (pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wps_oui[3]) { + if (wps_flag & IE_MASK_WPS) + break; + } + /* filter out first p2p ie */ + else if (!memcmp + (pvendor_ie->vend_hdr.oui, p2p_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == p2p_oui[3]) { + if (!find_p2p_ie && (wps_flag & IE_MASK_P2P)) { + find_p2p_ie = MTRUE; + break; + } + } + /* filter out wfd ie */ + else if (!memcmp + (pvendor_ie->vend_hdr.oui, wfd_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wfd_oui[3]) { + if (wps_flag & IE_MASK_WFD) + break; + } else if (wps_flag & IE_MASK_VENDOR) { + //filter out vendor IE + break; + } + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(priv->phandle, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, + "IE too big, fail copy VENDOR_SPECIFIC_221 IE\n"); + } + break; + default: + if ((out_len + length + 2) < ie_out_len) { + moal_memcpy_ext(priv->phandle, ie_out + out_len, + pos, length + 2, + ie_out_len - out_len); + out_len += length + 2; + } else { + PRINTM(MERROR, "IE too big, fail copy %d IE\n", + id); + } + break; + } + pos += (length + 2); + left_len -= (length + 2); + } + + if (enable_11d) + woal_set_11d(priv, MOAL_IOCTL_WAIT, MTRUE); + return out_len; +} + +#ifdef WIFI_DIRECT_SUPPORT +/** + * @brief Check if selected_registrar_on in wps_ie + * + * @param ie Pointer to IEs + * @param len Total length of ie + * + * @return MTRUE/MFALSE + */ +t_u8 +is_selected_registrar_on(const t_u8 *ie, int len) +{ +#define WPS_IE_FIX_LEN 6 +#define TLV_ID_SELECTED_REGISTRAR 0x1041 + int left_len = len - WPS_IE_FIX_LEN; + TLV_Generic_t *tlv = (TLV_Generic_t *)(ie + WPS_IE_FIX_LEN); + u16 tlv_type, tlv_len; + u8 *pos = NULL; + while (left_len > sizeof(TLV_Generic_t)) { + tlv_type = ntohs(tlv->type); + tlv_len = ntohs(tlv->len); + if (tlv_type == TLV_ID_SELECTED_REGISTRAR) { + PRINTM(MIOCTL, "Selected Registrar found !"); + pos = (u8 *)tlv + sizeof(TLV_Generic_t); + if (*pos == 1) + return MTRUE; + else + return MFALSE; + } + tlv = (TLV_Generic_t *)((u8 *)tlv + tlv_len + + sizeof(TLV_Generic_t)); + left_len -= tlv_len + sizeof(TLV_Generic_t); + } + return MFALSE; +} + +/** + * @brief Check if selected_registrar_on in ies + * + * @param ie Pointer to IEs + * @param len Total length of ie + * + * + * @return MTRUE/MFALSE + */ +static t_u16 +woal_is_selected_registrar_on(const t_u8 *ie, int len) +{ + int left_len = len; + const t_u8 *pos = ie; + int length; + t_u8 id = 0; + IEEEtypes_VendorSpecific_t *pvendor_ie = NULL; + const u8 wps_oui[4] = { 0x00, 0x50, 0xf2, 0x04 }; + + while (left_len >= 2) { + length = *(pos + 1); + id = *pos; + if ((length + 2) > left_len) + break; + switch (id) { + case VENDOR_SPECIFIC_221: + pvendor_ie = (IEEEtypes_VendorSpecific_t *)pos; + if (!memcmp + (pvendor_ie->vend_hdr.oui, wps_oui, + sizeof(pvendor_ie->vend_hdr.oui)) && + pvendor_ie->vend_hdr.oui_type == wps_oui[3]) { + PRINTM(MIOCTL, "Find WPS ie\n"); + return is_selected_registrar_on(pos, + length + 2); + } + break; + default: + break; + } + pos += (length + 2); + left_len -= (length + 2); + } + return MFALSE; +} +#endif + +/** + * @brief config AP or GO for mgmt frame ies. + * + * @param priv A pointer to moal private structure + * @param beacon_ies A pointer to beacon ies + * @param beacon_ies_len Beacon ies length + * @param proberesp_ies A pointer to probe resp ies + * @param proberesp_ies_len Probe resp ies length + * @param assocresp_ies A pointer to probe resp ies + * @param assocresp_ies_len Assoc resp ies length + * @param probereq_ies A pointer to probe req ies + * @param probereq_ies_len Probe req ies length * + * @param mask Mgmt frame mask + * @param wait_option wait_option + * + * @return 0 -- success, otherwise fail + */ +int +woal_cfg80211_mgmt_frame_ie(moal_private *priv, + const t_u8 *beacon_ies, size_t beacon_ies_len, + const t_u8 *proberesp_ies, size_t proberesp_ies_len, + const t_u8 *assocresp_ies, size_t assocresp_ies_len, + const t_u8 *probereq_ies, size_t probereq_ies_len, + t_u16 mask, t_u8 wait_option) +{ + int ret = 0; + t_u8 *pos = NULL; + custom_ie *beacon_ies_data = NULL; + custom_ie *proberesp_ies_data = NULL; + custom_ie *assocresp_ies_data = NULL; + custom_ie *probereq_ies_data = NULL; + + /* static variables for mgmt frame ie auto-indexing */ + t_u16 beacon_index = priv->beacon_index; + t_u16 proberesp_index = priv->proberesp_index; + t_u16 assocresp_index = priv->assocresp_index; + t_u16 probereq_index = priv->probereq_index; + t_u16 beacon_wps_index = priv->beacon_wps_index; + t_u16 proberesp_p2p_index = priv->proberesp_p2p_index; + t_u16 assocrep_qos_map_index = priv->assocresp_qos_map_index; + t_u16 beacon_vendor_index = priv->beacon_vendor_index; + + ENTER(); + + /* we need remove vendor IE from beacon extra IE, vendor IE will be configure through proberesp_vendor_index */ + if (mask & MGMT_MASK_BEACON_WPS_P2P) { + beacon_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!beacon_ies_data) { + ret = -ENOMEM; + goto done; + } + if (beacon_ies && beacon_ies_len) { +#ifdef WIFI_DIRECT_SUPPORT + if (woal_is_selected_registrar_on + (beacon_ies, beacon_ies_len)) { + PRINTM(MIOCTL, "selected_registrar is on\n"); + priv->phandle->is_go_timer_set = MTRUE; + woal_mod_timer(&priv->phandle->go_timer, + MOAL_TIMER_10S); + } else + PRINTM(MIOCTL, "selected_registrar is off\n"); +#endif + beacon_ies_data->ie_index = beacon_wps_index; + beacon_ies_data->mgmt_subtype_mask = MGMT_MASK_BEACON; + beacon_ies_data->ie_length = + woal_filter_beacon_ies(priv, beacon_ies, + beacon_ies_len, + beacon_ies_data-> + ie_buffer, MAX_IE_SIZE, + IE_MASK_VENDOR, NULL, 0); + DBG_HEXDUMP(MCMD_D, "beacon extra ie", + beacon_ies_data->ie_buffer, + beacon_ies_data->ie_length); + } else { + /* clear the beacon wps ies */ + if (beacon_wps_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid beacon wps index for mgmt frame ie.\n"); + goto done; + } + + beacon_ies_data->ie_index = beacon_wps_index; + beacon_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + beacon_wps_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if ((beacon_ies && beacon_ies_len && beacon_ies_data->ie_length) + || (beacon_ies_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK)) { + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie(priv, beacon_ies_data, + &beacon_wps_index, + proberesp_ies_data, + &proberesp_index, + assocresp_ies_data, + &assocresp_index, + probereq_ies_data, + &probereq_index, + wait_option)) { + PRINTM(MERROR, "Fail to set beacon wps IE\n"); + ret = -EFAULT; + } + priv->beacon_wps_index = beacon_wps_index; + PRINTM(MCMND, "beacon_wps_index=0x%x len=%d\n", + beacon_wps_index, beacon_ies_data->ie_length); + goto done; + } + kfree(beacon_ies_data); // Further allocation of beacon_ies_data is happening, so need to free here. + beacon_ies_data = NULL; + } + + if (mask & MGMT_MASK_ASSOC_RESP_QOS_MAP) { + assocresp_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!assocresp_ies_data) { + ret = -ENOMEM; + goto done; + } + if (assocresp_ies && assocresp_ies_len) { + /* set the assoc response qos map ies */ + assocresp_ies_data->ie_index = assocrep_qos_map_index; + assocresp_ies_data->mgmt_subtype_mask = + MGMT_MASK_ASSOC_RESP; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == + assocrep_qos_map_index) + assocresp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + if (assocresp_ies_len > MAX_IE_SIZE) { + PRINTM(MERROR, + "IE too big: assocresp_ies_len=%d\n", + (int)assocresp_ies_len); + goto done; + } + assocresp_ies_data->ie_length = assocresp_ies_len; + pos = assocresp_ies_data->ie_buffer; + moal_memcpy_ext(priv->phandle, pos, assocresp_ies, + assocresp_ies_len, MAX_IE_SIZE); + DBG_HEXDUMP(MCMD_D, "Qos Map", + assocresp_ies_data->ie_buffer, + assocresp_ies_data->ie_length); + } else { + /* clear the assoc response qos map ie */ + if (assocrep_qos_map_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid Qos map index for mgmt frame ie.\n"); + goto done; + } + + assocresp_ies_data->ie_index = assocrep_qos_map_index; + assocresp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + assocresp_ies_data->ie_length = 0; + assocrep_qos_map_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie(priv, NULL, &beacon_wps_index, + NULL, &proberesp_index, + assocresp_ies_data, + &assocrep_qos_map_index, NULL, + &probereq_index, wait_option)) { + PRINTM(MERROR, "Fail to set Qos map IE\n"); + ret = -EFAULT; + } + priv->assocresp_qos_map_index = assocrep_qos_map_index; + PRINTM(MCMND, "qos map ie index=0x%x len=%d\n", + assocrep_qos_map_index, assocresp_ies_data->ie_length); + goto done; + } + + if (mask & MGMT_MASK_BEACON) { + beacon_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!beacon_ies_data) { + ret = -ENOMEM; + goto done; + } + } + + if (mask & MGMT_MASK_PROBE_RESP) { + /** set or clear proberesp ie */ + if (proberesp_ies_len || + (!proberesp_ies_len && !beacon_ies_len)) { + proberesp_ies_data = + kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!proberesp_ies_data) { + ret = -ENOMEM; + goto done; + } + } + } + + if (mask & MGMT_MASK_ASSOC_RESP) { + /** set or clear assocresp ie */ + if (assocresp_ies_len || + (!assocresp_ies_len && !beacon_ies_len)) { + assocresp_ies_data = + kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!assocresp_ies_data) { + ret = -ENOMEM; + goto done; + } + } + } + if (mask & MGMT_MASK_PROBE_REQ) { + probereq_ies_data = kzalloc(sizeof(custom_ie), GFP_KERNEL); + if (!probereq_ies_data) { + ret = -ENOMEM; + goto done; + } + } + + if (beacon_ies_data) { + if (beacon_ies && beacon_ies_len) { + /* set the probe response/beacon vendor ies which includes wpa IE */ + beacon_ies_data->ie_index = beacon_vendor_index; + beacon_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_RESP | MGMT_MASK_BEACON; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == beacon_vendor_index) + beacon_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + beacon_ies_data->ie_length = + woal_get_specific_ie(beacon_ies, beacon_ies_len, + beacon_ies_data->ie_buffer, + MAX_IE_SIZE, + IE_MASK_VENDOR); + DBG_HEXDUMP(MCMD_D, "beacon vendor IE", + beacon_ies_data->ie_buffer, + beacon_ies_data->ie_length); + } else if (beacon_vendor_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* clear the beacon vendor ies */ + if (beacon_vendor_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid beacon_vendor_index for mgmt frame ie.\n"); + goto done; + } + beacon_ies_data->ie_index = beacon_vendor_index; + beacon_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + beacon_vendor_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if ((beacon_ies && beacon_ies_len && beacon_ies_data->ie_length) + || (beacon_ies_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK)) { + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie(priv, beacon_ies_data, + &beacon_vendor_index, NULL, + &proberesp_index, NULL, + &assocresp_index, NULL, + &probereq_index, + wait_option)) { + PRINTM(MERROR, + "Fail to set beacon vendor IE\n"); + ret = -EFAULT; + goto done; + } + priv->beacon_vendor_index = beacon_vendor_index; + PRINTM(MCMND, "beacon_vendor=0x%x len=%d\n", + beacon_vendor_index, beacon_ies_data->ie_length); + } + memset(beacon_ies_data, 0x00, sizeof(custom_ie)); + if (beacon_ies && beacon_ies_len) { + /* set the beacon ies */ + /* we need remove vendor IE from beacon tail, vendor/wpa IE will be configure through beacon_vendor_index */ + beacon_ies_data->ie_index = beacon_index; + beacon_ies_data->mgmt_subtype_mask = MGMT_MASK_BEACON | + MGMT_MASK_ASSOC_RESP | MGMT_MASK_PROBE_RESP; + beacon_ies_data->ie_length = + woal_filter_beacon_ies(priv, beacon_ies, + beacon_ies_len, + beacon_ies_data-> + ie_buffer, MAX_IE_SIZE, + IE_MASK_WPS | IE_MASK_WFD + | IE_MASK_P2P | + IE_MASK_VENDOR, + proberesp_ies, + proberesp_ies_len); + if (beacon_ies_data->ie_length) + DBG_HEXDUMP(MCMD_D, "beacon ie", + beacon_ies_data->ie_buffer, + beacon_ies_data->ie_length); + else { + kfree(beacon_ies_data); + beacon_ies_data = NULL; + } + } else { + /* clear the beacon ies */ + if (beacon_index > MAX_MGMT_IE_INDEX) { + PRINTM(MINFO, + "Invalid beacon index for mgmt frame ie.\n"); + goto done; + } + + beacon_ies_data->ie_index = beacon_index; + beacon_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + beacon_ies_data->ie_length = 0; + beacon_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (proberesp_ies_data) { + if (proberesp_ies && proberesp_ies_len) { + /* set the probe response p2p ies */ + proberesp_ies_data->ie_index = proberesp_p2p_index; + proberesp_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_RESP; + proberesp_ies_data->ie_length = + woal_get_specific_ie(proberesp_ies, + proberesp_ies_len, + proberesp_ies_data-> + ie_buffer, MAX_IE_SIZE, + IE_MASK_P2P); + DBG_HEXDUMP(MCMD_D, "proberesp p2p ie", + proberesp_ies_data->ie_buffer, + proberesp_ies_data->ie_length); + } else if (proberesp_p2p_index != MLAN_CUSTOM_IE_AUTO_IDX_MASK) { + /* clear the probe response p2p ies */ + if (proberesp_p2p_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid proberesp_p2p_index for mgmt frame ie.\n"); + goto done; + } + proberesp_ies_data->ie_index = proberesp_p2p_index; + proberesp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + proberesp_ies_data->ie_length = 0; + proberesp_p2p_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + if ((proberesp_ies && proberesp_ies_len && + proberesp_ies_data->ie_length) || + (proberesp_ies_data->mgmt_subtype_mask == + MLAN_CUSTOM_IE_DELETE_MASK)) { + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie(priv, NULL, &beacon_index, + proberesp_ies_data, + &proberesp_p2p_index, NULL, + &assocresp_index, NULL, + &probereq_index, + wait_option)) { + PRINTM(MERROR, + "Fail to set proberesp p2p IE\n"); + ret = -EFAULT; + goto done; + } + priv->proberesp_p2p_index = proberesp_p2p_index; + PRINTM(MCMND, "proberesp_p2p=0x%x len=%d\n", + proberesp_p2p_index, + proberesp_ies_data->ie_length); + } + memset(proberesp_ies_data, 0x00, sizeof(custom_ie)); + if (proberesp_ies && proberesp_ies_len) { + /* set the probe response ies */ + proberesp_ies_data->ie_index = proberesp_index; + proberesp_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_RESP; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == proberesp_index) + proberesp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + proberesp_ies_data->ie_length = + woal_filter_beacon_ies(priv, proberesp_ies, + proberesp_ies_len, + proberesp_ies_data-> + ie_buffer, MAX_IE_SIZE, + IE_MASK_P2P | + IE_MASK_VENDOR, NULL, 0); + if (proberesp_ies_data->ie_length) { + DBG_HEXDUMP(MCMD_D, "proberesp ie", + proberesp_ies_data->ie_buffer, + proberesp_ies_data->ie_length); + } else { + kfree(proberesp_ies_data); + proberesp_ies_data = NULL; + } + } else { + /* clear the probe response ies */ + if (proberesp_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid probe resp index for mgmt frame ie.\n"); + goto done; + } + proberesp_ies_data->ie_index = proberesp_index; + proberesp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + proberesp_ies_data->ie_length = 0; + proberesp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + if (assocresp_ies_data) { + if (assocresp_ies && assocresp_ies_len) { + /* set the assoc response ies */ + assocresp_ies_data->ie_index = assocresp_index; + assocresp_ies_data->mgmt_subtype_mask = + MGMT_MASK_ASSOC_RESP; + if (MLAN_CUSTOM_IE_AUTO_IDX_MASK == assocresp_index) + assocresp_ies_data->mgmt_subtype_mask |= + MLAN_CUSTOM_IE_NEW_MASK; + if (assocresp_ies_len > MAX_IE_SIZE) { + PRINTM(MERROR, + "IE too big, assocresp_ies_len=%d\n", + (int)assocresp_ies_len); + goto done; + } + assocresp_ies_data->ie_length = assocresp_ies_len; + pos = assocresp_ies_data->ie_buffer; + moal_memcpy_ext(priv->phandle, pos, assocresp_ies, + assocresp_ies_len, MAX_IE_SIZE); + DBG_HEXDUMP(MCMD_D, "assocresp ie", + assocresp_ies_data->ie_buffer, + assocresp_ies_data->ie_length); + } else { + /* clear the assoc response ies */ + if (assocresp_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid assoc resp index for mgmt frame ie.\n"); + goto done; + } + + assocresp_ies_data->ie_index = assocresp_index; + assocresp_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + assocresp_ies_data->ie_length = 0; + assocresp_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (probereq_ies_data) { + if (probereq_ies && probereq_ies_len) { + /* set the probe req ies */ + probereq_ies_data->ie_index = probereq_index; + probereq_ies_data->mgmt_subtype_mask = + MGMT_MASK_PROBE_REQ; +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + if (priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT) { + /* filter out P2P/WFD ie/EXT_CAP ie */ + probereq_ies_data->ie_length = + woal_filter_beacon_ies(priv, + probereq_ies, + probereq_ies_len, + probereq_ies_data-> + ie_buffer, + MAX_IE_SIZE, + IE_MASK_P2P | + IE_MASK_WFD | + IE_MASK_EXTCAP, + NULL, 0); + } else { +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + if (probereq_ies_len > MAX_IE_SIZE) { + PRINTM(MERROR, + "IE too big, probereq_ies_len=%d\n", + (int)probereq_ies_len); + goto done; + } + probereq_ies_data->ie_length = probereq_ies_len; + pos = probereq_ies_data->ie_buffer; + moal_memcpy_ext(priv->phandle, pos, + probereq_ies, probereq_ies_len, + MAX_IE_SIZE); +#ifdef WIFI_DIRECT_SUPPORT +#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION + } +#endif /* KERNEL_VERSION */ +#endif /* WIFI_DIRECT_SUPPORT */ + if (probereq_ies_data->ie_length) + DBG_HEXDUMP(MCMD_D, "probereq ie", + probereq_ies_data->ie_buffer, + probereq_ies_data->ie_length); + else { + kfree(probereq_ies_data); + probereq_ies_data = NULL; + } + } else { + /* clear the probe req ies */ + if (probereq_index > MAX_MGMT_IE_INDEX) { + PRINTM(MERROR, + "Invalid probe req index for mgmt frame ie.\n"); + goto done; + } + probereq_ies_data->ie_index = probereq_index; + probereq_ies_data->mgmt_subtype_mask = + MLAN_CUSTOM_IE_DELETE_MASK; + probereq_ies_data->ie_length = 0; + probereq_index = MLAN_CUSTOM_IE_AUTO_IDX_MASK; + } + } + + if (beacon_ies_data || proberesp_ies_data || assocresp_ies_data || + probereq_ies_data) { + if (MLAN_STATUS_FAILURE == + woal_cfg80211_custom_ie(priv, beacon_ies_data, + &beacon_index, proberesp_ies_data, + &proberesp_index, + assocresp_ies_data, + &assocresp_index, probereq_ies_data, + &probereq_index, wait_option)) { + PRINTM(MERROR, + "Fail to set beacon proberesp assoc probereq IES\n"); + ret = -EFAULT; + goto done; + } + } + if (beacon_ies_data) { + priv->beacon_index = beacon_index; + PRINTM(MCMND, "beacon ie length = %d\n", + beacon_ies_data->ie_length); + } + if (assocresp_ies_data) { + priv->assocresp_index = assocresp_index; + PRINTM(MCMND, "assocresp ie length = %d\n", + assocresp_ies_data->ie_length); + } + if (proberesp_ies_data) { + priv->proberesp_index = proberesp_index; + PRINTM(MCMND, "proberesp ie length = %d\n", + proberesp_ies_data->ie_length); + } + if (probereq_ies_data) { + priv->probereq_index = probereq_index; + PRINTM(MCMND, "probereq ie length = %d\n", + probereq_ies_data->ie_length); + } + PRINTM(MCMND, "beacon=%x assocresp=%x proberesp=%x probereq=%x\n", + beacon_index, assocresp_index, proberesp_index, probereq_index); +done: + kfree(beacon_ies_data); + kfree(proberesp_ies_data); + kfree(assocresp_ies_data); + kfree(probereq_ies_data); + + LEAVE(); + + return ret; +} + +/** + * @brief Sets up the CFG802.11 specific HT capability fields + * with default values + * + * @param ht_info A pointer to ieee80211_sta_ht_cap structure + * @param dev_cap Device capability informations + * @param mcs_set Device MCS sets + * + * @return N/A + */ +void +woal_cfg80211_setup_ht_cap(struct ieee80211_sta_ht_cap *ht_info, + t_u32 dev_cap, t_u8 *mcs_set) +{ + ENTER(); + + ht_info->ht_supported = true; + ht_info->ampdu_factor = 0x3; + ht_info->ampdu_density = 0; + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + ht_info->cap = 0; + if (mcs_set) + moal_memcpy_ext(NULL, ht_info->mcs.rx_mask, mcs_set, + sizeof(ht_info->mcs.rx_mask), + sizeof(ht_info->mcs.rx_mask)); + if (dev_cap & MBIT(8)) /* 40Mhz intolarance enabled */ + ht_info->cap |= IEEE80211_HT_CAP_40MHZ_INTOLERANT; + if (dev_cap & MBIT(17)) /* Channel width 20/40Mhz support */ + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + if ((dev_cap >> 20) & 0x03) /* Delayed ACK supported */ + ht_info->cap |= IEEE80211_HT_CAP_DELAY_BA; + if (dev_cap & MBIT(22)) /* Rx LDPC supported */ + ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; + if (dev_cap & MBIT(23)) /* Short GI @ 20Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + if (dev_cap & MBIT(24)) /* Short GI @ 40Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + if (dev_cap & MBIT(25)) /* Tx STBC supported */ + ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; + if (dev_cap & MBIT(26)) /* Rx STBC supported */ + ht_info->cap |= IEEE80211_HT_CAP_RX_STBC; + if (dev_cap & MBIT(27)) /* MIMO PS supported */ + ht_info->cap |= 0; /* WLAN_HT_CAP_SM_PS_STATIC */ + else /* Disable HT SM PS */ + ht_info->cap |= IEEE80211_HT_CAP_SM_PS; + if (dev_cap & MBIT(29)) /* Green field supported */ + ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; + if (dev_cap & MBIT(31)) /* MAX AMSDU supported */ + ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; + /* DSSS/CCK in 40Mhz supported */ + ht_info->cap |= IEEE80211_HT_CAP_DSSSCCK40; + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + + LEAVE(); +} + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) +/** + * @brief Sets up the CFG802.11 specific VHT capability fields + * with default values + * + * @param priv A pointer to moal private structure + * @param vht_cap A pointer to ieee80211_sta_vht_cap structure + * + * @return N/A + */ +void +woal_cfg80211_setup_vht_cap(moal_private *priv, + struct ieee80211_sta_vht_cap *vht_cap) +{ + mlan_ioctl_req *req = NULL; + mlan_ds_11ac_cfg *cfg_11ac = NULL; + mlan_status status; + + req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11ac_cfg)); + if (req == NULL) { + status = MLAN_STATUS_FAILURE; + PRINTM(MERROR, "Fail to allocate buf for setup vht_cap\n"); + goto done; + } + cfg_11ac = (mlan_ds_11ac_cfg *)req->pbuf; + cfg_11ac->sub_command = MLAN_OID_11AC_VHT_CFG; + req->req_id = MLAN_IOCTL_11AC_CFG; + req->action = MLAN_ACT_GET; + cfg_11ac->param.vht_cfg.band = BAND_SELECT_A; + cfg_11ac->param.vht_cfg.txrx = MLAN_RADIO_RX; + status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); + if (MLAN_STATUS_SUCCESS != status) { + PRINTM(MERROR, "Fail to get vht_cfg\n"); + goto done; + } + vht_cap->vht_supported = true; + vht_cap->cap = cfg_11ac->param.vht_cfg.vht_cap_info; + vht_cap->vht_mcs.rx_mcs_map = (t_u16)cfg_11ac->param.vht_cfg.vht_rx_mcs; + vht_cap->vht_mcs.rx_highest = + (t_u16)cfg_11ac->param.vht_cfg.vht_rx_max_rate; + vht_cap->vht_mcs.tx_mcs_map = (t_u16)cfg_11ac->param.vht_cfg.vht_tx_mcs; + vht_cap->vht_mcs.tx_highest = + (t_u16)cfg_11ac->param.vht_cfg.vht_tx_max_rate; + PRINTM(MCMND, + "vht_cap=0x%x rx_mcs_map=0x%x rx_max=0x%x tx_mcs_map=0x%x tx_max=0x%x\n", + vht_cap->cap, vht_cap->vht_mcs.rx_mcs_map, + vht_cap->vht_mcs.rx_highest, vht_cap->vht_mcs.tx_mcs_map, + vht_cap->vht_mcs.tx_highest); +done: + if (status != MLAN_STATUS_PENDING) + kfree(req); + LEAVE(); + return; +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) +/** + * @brief Sets up the CFG802.11 specific HE capability fields * with default values + * + * @param priv A pointer to moal private structure + * @param iftype_data A pointer to ieee80211_sband_iftype_data structure + * + * @return N/A + */ +void +woal_cfg80211_setup_he_cap(moal_private *priv, + struct ieee80211_supported_band *band) +{ + mlan_fw_info fw_info; + struct ieee80211_sband_iftype_data *iftype_data = NULL; + t_u8 extra_mcs_size = 0; + int ppe_threshold_len = 0; + mlan_ds_11ax_he_capa *phe_cap = NULL; + t_u8 hw_hecap_len; + woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); + if (band->band == NL80211_BAND_5GHZ) { + phe_cap = (mlan_ds_11ax_he_capa *) fw_info.hw_he_cap; + hw_hecap_len = fw_info.hw_hecap_len; + } else { + phe_cap = (mlan_ds_11ax_he_capa *) fw_info.hw_2g_he_cap; + hw_hecap_len = fw_info.hw_2g_hecap_len; + } + if (!hw_hecap_len) + return; + DBG_HEXDUMP(MCMD_D, "Setup HECAP", (u8 *)phe_cap, hw_hecap_len); + iftype_data = + kmalloc(sizeof(struct ieee80211_sband_iftype_data), GFP_KERNEL); + if (!iftype_data) { + PRINTM(MERROR, "Fail to allocate iftype data\n"); + goto done; + } + iftype_data->types_mask = + MBIT(NL80211_IFTYPE_STATION) | MBIT(NL80211_IFTYPE_AP) | + MBIT(NL80211_IFTYPE_P2P_CLIENT) | MBIT(NL80211_IFTYPE_P2P_GO); + iftype_data->he_cap.has_he = true; + moal_memcpy_ext(priv->phandle, + iftype_data->he_cap.he_cap_elem.mac_cap_info, + phe_cap->he_mac_cap, sizeof(phe_cap->he_mac_cap), + sizeof(iftype_data->he_cap.he_cap_elem.mac_cap_info)); + moal_memcpy_ext(priv->phandle, + iftype_data->he_cap.he_cap_elem.phy_cap_info, + phe_cap->he_phy_cap, sizeof(phe_cap->he_phy_cap), + sizeof(iftype_data->he_cap.he_cap_elem.phy_cap_info)); + memset(&iftype_data->he_cap.he_mcs_nss_supp, 0xff, + sizeof(struct ieee80211_he_mcs_nss_supp)); + moal_memcpy_ext(priv->phandle, &iftype_data->he_cap.he_mcs_nss_supp, + phe_cap->he_txrx_mcs_support, + sizeof(phe_cap->he_txrx_mcs_support), + sizeof(struct ieee80211_he_mcs_nss_supp)); + //Support 160Mhz + if (phe_cap->he_phy_cap[0] & MBIT(3)) + extra_mcs_size += 4; + + //Support 80+80 + if (phe_cap->he_phy_cap[0] & MBIT(4)) + extra_mcs_size += 4; + if (extra_mcs_size) + moal_memcpy_ext(priv->phandle, + (t_u8 *)&iftype_data->he_cap.he_mcs_nss_supp. + rx_mcs_160, phe_cap->val, extra_mcs_size, + sizeof(struct ieee80211_he_mcs_nss_supp) - 4); + +#define HE_CAP_FIX_SIZE 22 + //Support PPE threshold + ppe_threshold_len = phe_cap->len - HE_CAP_FIX_SIZE - extra_mcs_size; + if (phe_cap->he_phy_cap[6] & MBIT(7) && ppe_threshold_len) { + moal_memcpy_ext(priv->phandle, iftype_data->he_cap.ppe_thres, + &phe_cap->val[extra_mcs_size], + ppe_threshold_len, + sizeof(iftype_data->he_cap.ppe_thres)); + } else { + iftype_data->he_cap.he_cap_elem.phy_cap_info[6] &= ~MBIT(7); + PRINTM(MCMND, "Clear PPE threshold 0x%x\n", + iftype_data->he_cap.he_cap_elem.phy_cap_info[7]); + } + band->n_iftype_data = 1; + band->iftype_data = iftype_data; +done: + LEAVE(); + return; +} + +/** + * @brief free iftype_data + * + * @param wiphy A pointer to struct wiphy + * + * + * @return N/A + */ +void +woal_cfg80211_free_iftype_data(struct wiphy *wiphy) +{ + enum nl80211_band band; + for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; ++band) { + if (!wiphy->bands[band]) + continue; + kfree(wiphy->bands[band]->iftype_data); + wiphy->bands[band]->n_iftype_data = 0; + } +} +#endif + +#ifdef STA_CFG80211 +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) +/** + * @brief prepare woal_bgscan_stop event + * + * @param priv A pointer moal_private structure + * @param pchan_info A pointer to chan_band structure + * + * @return N/A + */ +void +woal_bgscan_stop_event(moal_private *priv) +{ + struct woal_event *evt; + unsigned long flags; + moal_handle *handle = priv->phandle; + evt = kzalloc(sizeof(struct woal_event), GFP_ATOMIC); + if (evt) { + evt->priv = priv; + evt->type = WOAL_EVENT_BGSCAN_STOP; + INIT_LIST_HEAD(&evt->link); + spin_lock_irqsave(&handle->evt_lock, flags); + list_add_tail(&evt->link, &handle->evt_queue); + spin_unlock_irqrestore(&handle->evt_lock, flags); + queue_work(handle->evt_workqueue, &handle->evt_work); + } +} + +/** + * @brief Notify cfg80211 schedule scan stopped + * + * @param priv A pointer moal_private structure + * + * @return N/A + */ +void +woal_cfg80211_notify_sched_scan_stop(moal_private *priv) +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 6) + if (rtnl_is_locked()) + cfg80211_sched_scan_stopped_rtnl(priv->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , 0 +#endif + ); + else +#endif + cfg80211_sched_scan_stopped(priv->wdev->wiphy +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + , 0 +#endif + ); + priv->sched_scanning = MFALSE; + PRINTM(MEVENT, "Sched_Scan stopped\n"); +} +#endif +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) +/** + * @brief Handle woal_channel_switch event + * + * @param priv A pointer moal_private structure + * @param pchan_info A pointer to chan_band structure + * + * @return N/A + */ +void +woal_channel_switch_event(moal_private *priv, chan_band_info * pchan_info) +{ + struct woal_event *evt; + unsigned long flags; + moal_handle *handle = priv->phandle; + evt = kzalloc(sizeof(struct woal_event), GFP_ATOMIC); + if (evt) { + evt->priv = priv; + evt->type = WOAL_EVENT_CHAN_SWITCH; + memcpy(&evt->chan_info, pchan_info, sizeof(chan_band_info)); + INIT_LIST_HEAD(&evt->link); + spin_lock_irqsave(&handle->evt_lock, flags); + list_add_tail(&evt->link, &handle->evt_queue); + spin_unlock_irqrestore(&handle->evt_lock, flags); + queue_work(handle->evt_workqueue, &handle->evt_work); + } +} + +/** + * @brief Notify cfg80211 supplicant channel changed + * + * @param priv A pointer moal_private structure + * @param pchan_info A pointer to chan_band structure + * + * @return N/A + */ +void +woal_cfg80211_notify_channel(moal_private *priv, chan_band_info * pchan_info) +{ +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + struct cfg80211_chan_def chandef; +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + enum nl80211_channel_type type; + enum ieee80211_band band; + int freq = 0; +#endif +#endif + ENTER(); + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + if (MLAN_STATUS_SUCCESS == + woal_chandef_create(priv, &chandef, pchan_info)) { + cfg80211_ch_switch_notify(priv->netdev, &chandef); + priv->channel = pchan_info->channel; +#ifdef UAP_CFG80211 + moal_memcpy_ext(priv->phandle, &priv->chan, &chandef, + sizeof(struct cfg80211_chan_def), + sizeof(struct cfg80211_chan_def)); +#endif + } +#else +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) + if (pchan_info->bandcfg.chanBand == BAND_2GHZ) + band = IEEE80211_BAND_2GHZ; + else if (pchan_info->bandcfg.chanBand == BAND_5GHZ) + band = IEEE80211_BAND_5GHZ; + else { + LEAVE(); + return; + } + priv->channel = pchan_info->channel; + freq = ieee80211_channel_to_frequency(pchan_info->channel, band); + switch (pchan_info->bandcfg.chanWidth) { + case CHAN_BW_20MHZ: + if (pchan_info->is_11n_enabled) + type = NL80211_CHAN_HT20; + else + type = NL80211_CHAN_NO_HT; + break; + default: + if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_ABOVE) + type = NL80211_CHAN_HT40PLUS; + else if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_BELOW) + type = NL80211_CHAN_HT40MINUS; + else + type = NL80211_CHAN_HT20; + break; + } + cfg80211_ch_switch_notify(priv->netdev, freq, type); +#endif +#endif + LEAVE(); +} +#endif + +#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +/** + * @brief create cfg80211_chan_def structure based on chan_band info + * + * @param priv A pointer moal_private structure + * @param chandef A pointer to cfg80211_chan_def structure + * @param pchan_info A pointer to chan_band_info structure + * + * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE + */ +mlan_status +woal_chandef_create(moal_private *priv, struct cfg80211_chan_def *chandef, + chan_band_info * pchan_info) +{ + enum ieee80211_band band = IEEE80211_BAND_2GHZ; + mlan_status status = MLAN_STATUS_SUCCESS; + + ENTER(); + chandef->center_freq2 = 0; + if (pchan_info->bandcfg.chanBand == BAND_2GHZ) + band = IEEE80211_BAND_2GHZ; + else if (pchan_info->bandcfg.chanBand == BAND_5GHZ) + band = IEEE80211_BAND_5GHZ; + chandef->chan = ieee80211_get_channel(priv->wdev->wiphy, + ieee80211_channel_to_frequency + (pchan_info->channel, band)); + if (chandef->chan == NULL) { + PRINTM(MERROR, + "Fail on ieee80211_get_channel, channel=%d, band=%d\n", + pchan_info->channel, band); + status = MLAN_STATUS_FAILURE; + goto done; + } + switch (pchan_info->bandcfg.chanWidth) { + case CHAN_BW_20MHZ: + if (pchan_info->is_11n_enabled) + chandef->width = NL80211_CHAN_WIDTH_20; + else + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = chandef->chan->center_freq; + break; + case CHAN_BW_40MHZ: + chandef->width = NL80211_CHAN_WIDTH_40; + if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_ABOVE) + chandef->center_freq1 = chandef->chan->center_freq + 10; + else if (pchan_info->bandcfg.chan2Offset == SEC_CHAN_BELOW) + chandef->center_freq1 = chandef->chan->center_freq - 10; + break; + case CHAN_BW_80MHZ: + chandef->width = NL80211_CHAN_WIDTH_80; + chandef->center_freq1 = + ieee80211_channel_to_frequency(pchan_info->center_chan, + band); + break; + default: + break; + } +done: + LEAVE(); + return status; +} +#endif |