| /** @file moal_uap_cfg80211.c |
| * |
| * @brief This file contains the functions for uAP CFG80211. |
| * |
| * |
| * Copyright 2011-2021 NXP |
| * |
| * This software file (the File) is distributed by NXP |
| * under the terms of the GNU General Public License Version 2, June 1991 |
| * (the License). You may use, redistribute and/or modify the File in |
| * accordance with the terms and conditions of the License, a copy of which |
| * is available by writing to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the |
| * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. |
| * |
| * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE |
| * ARE EXPRESSLY DISCLAIMED. The License provides additional details about |
| * this warranty disclaimer. |
| * |
| */ |
| |
| #include "moal_cfg80211.h" |
| #include "moal_uap_cfg80211.h" |
| /** deauth reason code */ |
| #define REASON_CODE_DEAUTH_LEAVING 3 |
| /******************************************************** |
| Local Variables |
| ********************************************************/ |
| |
| /******************************************************** |
| Global Variables |
| ********************************************************/ |
| /******************************************************** |
| Local Functions |
| ********************************************************/ |
| |
| /******************************************************** |
| Global Functions |
| ********************************************************/ |
| /** |
| * @brief send deauth to station |
| * |
| * @param A pointer to moal_private |
| * @param mac A pointer to station mac address |
| * @param reason_code ieee deauth reason code |
| * @return 0 -- success, otherwise fail |
| */ |
| static int |
| woal_deauth_station(moal_private *priv, u8 *mac_addr, u16 reason_code) |
| { |
| mlan_ioctl_req *ioctl_req = NULL; |
| mlan_ds_bss *bss = NULL; |
| int ret = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); |
| if (ioctl_req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| bss = (mlan_ds_bss *)ioctl_req->pbuf; |
| bss->sub_command = MLAN_OID_UAP_DEAUTH_STA; |
| ioctl_req->req_id = MLAN_IOCTL_BSS; |
| ioctl_req->action = MLAN_ACT_SET; |
| |
| moal_memcpy_ext(priv->phandle, bss->param.deauth_param.mac_addr, |
| mac_addr, MLAN_MAC_ADDR_LENGTH, |
| sizeof(bss->param.deauth_param.mac_addr)); |
| bss->param.deauth_param.reason_code = reason_code; |
| status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(ioctl_req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief send deauth to station, that has been added and associated |
| * |
| * @param A pointer to moal_private |
| * @param mac A pointer to station mac address |
| * @param reason_code ieee deauth reason code |
| * @return 0 -- success, otherwise fail |
| */ |
| static int |
| woal_deauth_assoc_station(moal_private *priv, u8 *mac_addr, u16 reason_code) |
| { |
| int ret = -EFAULT; |
| int i = 0; |
| mlan_ds_get_info *info = NULL; |
| mlan_ioctl_req *ioctl_req = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| if (!mac_addr) { |
| LEAVE(); |
| return -EINVAL; |
| } |
| |
| ioctl_req = |
| (mlan_ioctl_req *) |
| woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); |
| if (ioctl_req == NULL) { |
| LEAVE(); |
| return -ENOMEM; |
| } |
| |
| info = (mlan_ds_get_info *)ioctl_req->pbuf; |
| info->sub_command = MLAN_OID_UAP_STA_LIST; |
| ioctl_req->req_id = MLAN_IOCTL_GET_INFO; |
| ioctl_req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) |
| goto done; |
| if (!info->param.sta_list.sta_count) { |
| PRINTM(MCMND, "wlan: skip deauth to station " MACSTR "\n", |
| MAC2STR(mac_addr)); |
| goto done; |
| } |
| |
| for (i = 0; i < info->param.sta_list.sta_count; i++) { |
| if (!memcmp(info->param.sta_list.info[i].mac_address, |
| mac_addr, ETH_ALEN)) |
| ret = woal_deauth_station(priv, mac_addr, reason_code); |
| } |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(ioctl_req); |
| |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief send deauth to all station |
| * |
| * @param A pointer to moal_private |
| * @param mac A pointer to station mac address |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| static int |
| woal_deauth_all_station(moal_private *priv) |
| { |
| int ret = -EFAULT; |
| int i = 0; |
| mlan_ds_get_info *info = NULL; |
| mlan_ioctl_req *ioctl_req = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| if (priv->media_connected == MFALSE) { |
| PRINTM(MINFO, "cfg80211: Media not connected!\n"); |
| LEAVE(); |
| return 0; |
| } |
| PRINTM(MIOCTL, "del all station\n"); |
| /* Allocate an IOCTL request buffer */ |
| ioctl_req = |
| (mlan_ioctl_req *) |
| woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); |
| if (ioctl_req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| info = (mlan_ds_get_info *)ioctl_req->pbuf; |
| info->sub_command = MLAN_OID_UAP_STA_LIST; |
| ioctl_req->req_id = MLAN_IOCTL_GET_INFO; |
| ioctl_req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) |
| goto done; |
| if (!info->param.sta_list.sta_count) |
| goto done; |
| for (i = 0; i < info->param.sta_list.sta_count; i++) { |
| PRINTM(MIOCTL, "deauth station " MACSTR "\n", |
| MAC2STR(info->param.sta_list.info[i].mac_address)); |
| ret = woal_deauth_station(priv, |
| info->param.sta_list.info[i]. |
| mac_address, |
| REASON_CODE_DEAUTH_LEAVING); |
| } |
| woal_sched_timeout(200); |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(ioctl_req); |
| return ret; |
| } |
| |
| /** |
| * @brief Verify RSN IE |
| * |
| * @param rsn_ie Pointer RSN IE |
| * @param sys_config Pointer to mlan_uap_bss_param structure |
| * |
| * @return MTRUE/MFALSE |
| */ |
| static t_u8 |
| woal_check_rsn_ie(IEEEtypes_Rsn_t *rsn_ie, mlan_uap_bss_param *sys_config) |
| { |
| int left = 0; |
| int count = 0; |
| int i = 0; |
| wpa_suite_auth_key_mgmt_t *key_mgmt = NULL; |
| left = rsn_ie->len + 2; |
| if (left < (int)sizeof(IEEEtypes_Rsn_t)) |
| return MFALSE; |
| sys_config->wpa_cfg.group_cipher = 0; |
| sys_config->wpa_cfg.pairwise_cipher_wpa2 = 0; |
| sys_config->key_mgmt = 0; |
| /* check the group cipher */ |
| switch (rsn_ie->group_cipher.type) { |
| case WPA_CIPHER_TKIP: |
| sys_config->wpa_cfg.group_cipher = CIPHER_TKIP; |
| break; |
| case WPA_CIPHER_AES_CCM: |
| sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; |
| break; |
| default: |
| break; |
| } |
| count = woal_le16_to_cpu(rsn_ie->pairwise_cipher.count); |
| for (i = 0; i < count; i++) { |
| switch (rsn_ie->pairwise_cipher.list[i].type) { |
| case WPA_CIPHER_TKIP: |
| sys_config->wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_TKIP; |
| break; |
| case WPA_CIPHER_AES_CCM: |
| sys_config->wpa_cfg.pairwise_cipher_wpa2 |= |
| CIPHER_AES_CCMP; |
| break; |
| default: |
| break; |
| } |
| } |
| left -= sizeof(IEEEtypes_Rsn_t) + (count - 1) * sizeof(wpa_suite); |
| if (left < (int)sizeof(wpa_suite_auth_key_mgmt_t)) |
| return MFALSE; |
| key_mgmt = |
| (wpa_suite_auth_key_mgmt_t *)((u8 *)rsn_ie + |
| sizeof(IEEEtypes_Rsn_t) + |
| (count - 1) * sizeof(wpa_suite)); |
| count = woal_le16_to_cpu(key_mgmt->count); |
| if (left < (int)(sizeof(wpa_suite_auth_key_mgmt_t) + |
| (count - 1) * sizeof(wpa_suite))) |
| return MFALSE; |
| for (i = 0; i < count; i++) { |
| switch (key_mgmt->list[i].type) { |
| case RSN_AKM_8021X: |
| sys_config->key_mgmt |= KEY_MGMT_EAP; |
| break; |
| case RSN_AKM_PSK: |
| sys_config->key_mgmt |= KEY_MGMT_PSK; |
| break; |
| case RSN_AKM_PSK_SHA256: |
| sys_config->key_mgmt |= KEY_MGMT_PSK_SHA256; |
| break; |
| case RSN_AKM_SAE: |
| sys_config->key_mgmt |= KEY_MGMT_SAE; |
| break; |
| case RSN_AKM_OWE: |
| sys_config->key_mgmt |= KEY_MGMT_OWE; |
| break; |
| } |
| } |
| return MTRUE; |
| } |
| |
| /** |
| * @brief Verify WPA IE |
| * |
| * @param wpa_ie Pointer WPA IE |
| * @param sys_config Pointer to mlan_uap_bss_param structure |
| * |
| * @return MTRUE/MFALSE |
| */ |
| static t_u8 |
| woal_check_wpa_ie(IEEEtypes_Wpa_t *wpa_ie, mlan_uap_bss_param *sys_config) |
| { |
| int left = 0; |
| int count = 0; |
| int i = 0; |
| wpa_suite_auth_key_mgmt_t *key_mgmt = NULL; |
| left = wpa_ie->len + 2; |
| if (left < (int)sizeof(IEEEtypes_Wpa_t)) |
| return MFALSE; |
| sys_config->wpa_cfg.group_cipher = 0; |
| sys_config->wpa_cfg.pairwise_cipher_wpa = 0; |
| switch (wpa_ie->group_cipher.type) { |
| case WPA_CIPHER_TKIP: |
| sys_config->wpa_cfg.group_cipher = CIPHER_TKIP; |
| break; |
| case WPA_CIPHER_AES_CCM: |
| sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; |
| break; |
| default: |
| break; |
| } |
| count = woal_le16_to_cpu(wpa_ie->pairwise_cipher.count); |
| for (i = 0; i < count; i++) { |
| switch (wpa_ie->pairwise_cipher.list[i].type) { |
| case WPA_CIPHER_TKIP: |
| sys_config->wpa_cfg.pairwise_cipher_wpa |= CIPHER_TKIP; |
| break; |
| case WPA_CIPHER_AES_CCM: |
| sys_config->wpa_cfg.pairwise_cipher_wpa |= |
| CIPHER_AES_CCMP; |
| break; |
| default: |
| break; |
| } |
| } |
| left -= sizeof(IEEEtypes_Wpa_t) + (count - 1) * sizeof(wpa_suite); |
| if (left < (int)sizeof(wpa_suite_auth_key_mgmt_t)) |
| return MFALSE; |
| key_mgmt = |
| (wpa_suite_auth_key_mgmt_t *)((u8 *)wpa_ie + |
| sizeof(IEEEtypes_Wpa_t) + |
| (count - 1) * sizeof(wpa_suite)); |
| count = woal_le16_to_cpu(key_mgmt->count); |
| if (left < (int)(sizeof(wpa_suite_auth_key_mgmt_t) + |
| (count - 1) * sizeof(wpa_suite))) |
| return MFALSE; |
| for (i = 0; i < count; i++) { |
| switch (key_mgmt->list[i].type) { |
| case RSN_AKM_8021X: |
| sys_config->key_mgmt = KEY_MGMT_EAP; |
| break; |
| case RSN_AKM_PSK: |
| sys_config->key_mgmt = KEY_MGMT_PSK; |
| break; |
| } |
| } |
| return MTRUE; |
| } |
| |
| /** |
| * @brief Find RSN/WPA IES |
| * |
| * @param ie Pointer IE buffer |
| * @param sys_config Pointer to mlan_uap_bss_param structure |
| * |
| * @return MTRUE/MFALSE |
| */ |
| static t_u8 |
| woal_find_wpa_ies(const t_u8 *ie, int len, mlan_uap_bss_param *sys_config) |
| { |
| int bytes_left = len; |
| const t_u8 *pcurrent_ptr = ie; |
| t_u16 total_ie_len; |
| t_u8 element_len; |
| t_u8 wpa2 = 0; |
| t_u8 wpa = 0; |
| t_u8 ret = MFALSE; |
| IEEEtypes_ElementId_e element_id; |
| IEEEtypes_VendorSpecific_t *pvendor_ie; |
| const t_u8 wpa_oui[4] = { 0x00, 0x50, 0xf2, 0x01 }; |
| |
| while (bytes_left >= 2) { |
| element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); |
| element_len = *((t_u8 *)pcurrent_ptr + 1); |
| total_ie_len = element_len + sizeof(IEEEtypes_Header_t); |
| if (bytes_left < total_ie_len) { |
| PRINTM(MERROR, |
| "InterpretIE: Error in processing IE, bytes left < IE length\n"); |
| bytes_left = 0; |
| continue; |
| } |
| switch (element_id) { |
| case RSN_IE: |
| wpa2 = woal_check_rsn_ie((IEEEtypes_Rsn_t *) |
| pcurrent_ptr, sys_config); |
| break; |
| case VENDOR_SPECIFIC_221: |
| pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; |
| if (!memcmp(pvendor_ie->vend_hdr.oui, wpa_oui, |
| sizeof(pvendor_ie->vend_hdr.oui)) && |
| (pvendor_ie->vend_hdr.oui_type == wpa_oui[3])) { |
| wpa = woal_check_wpa_ie((IEEEtypes_Wpa_t *) |
| pcurrent_ptr, |
| sys_config); |
| } |
| break; |
| default: |
| break; |
| } |
| pcurrent_ptr += element_len + 2; |
| /* Need to account for IE ID and IE Len */ |
| bytes_left -= (element_len + 2); |
| } |
| if (wpa && wpa2) { |
| sys_config->protocol = PROTOCOL_WPA | PROTOCOL_WPA2; |
| ret = MTRUE; |
| } else if (wpa2) { |
| sys_config->protocol = PROTOCOL_WPA2; |
| ret = MTRUE; |
| } else if (wpa) { |
| sys_config->protocol = PROTOCOL_WPA; |
| ret = MTRUE; |
| } |
| return ret; |
| } |
| |
| /** |
| * @brief Find and set WMM IES |
| * |
| * @param priv Pointer to moal_private |
| * @param ie Pointer IE buffer |
| * @param sys_config Pointer to mlan_uap_bss_param structure |
| * |
| * @return N/A |
| */ |
| static t_void |
| woal_set_wmm_ies(moal_private *priv, const t_u8 *ie, int len, |
| mlan_uap_bss_param *sys_config) |
| { |
| int bytes_left = len; |
| const t_u8 *pcurrent_ptr = ie; |
| t_u16 total_ie_len; |
| t_u8 element_len; |
| IEEEtypes_VendorSpecific_t *pvendor_ie; |
| IEEEtypes_ElementId_e element_id; |
| const t_u8 wmm_oui[4] = { 0x00, 0x50, 0xf2, 0x02 }; |
| |
| while (bytes_left >= 2) { |
| element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr)); |
| element_len = *((t_u8 *)pcurrent_ptr + 1); |
| total_ie_len = element_len + sizeof(IEEEtypes_Header_t); |
| if (bytes_left < total_ie_len) { |
| PRINTM(MERROR, |
| "InterpretIE: Error in processing IE, bytes left < IE length\n"); |
| bytes_left = 0; |
| continue; |
| } |
| switch (element_id) { |
| case VENDOR_SPECIFIC_221: |
| pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr; |
| if (!memcmp(pvendor_ie->vend_hdr.oui, wmm_oui, |
| sizeof(pvendor_ie->vend_hdr.oui)) && |
| pvendor_ie->vend_hdr.oui_type == wmm_oui[3]) { |
| if (total_ie_len == |
| sizeof(IEEEtypes_WmmParameter_t)) { |
| /* |
| * Only accept and copy the WMM IE if |
| * it matches the size expected for the |
| * WMM Parameter IE. |
| */ |
| moal_memcpy_ext(priv->phandle, |
| &sys_config->wmm_para, |
| pcurrent_ptr + |
| sizeof |
| (IEEEtypes_Header_t), |
| element_len, |
| sizeof(sys_config-> |
| wmm_para)); |
| /** set uap_host_based_config to true */ |
| sys_config->uap_host_based_config = |
| MTRUE; |
| } |
| } |
| |
| break; |
| default: |
| break; |
| } |
| pcurrent_ptr += element_len + 2; |
| /* Need to account for IE ID and IE Len */ |
| bytes_left -= (element_len + 2); |
| } |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| /** |
| * @brief initialize AP or GO bss config |
| * @param priv A pointer to moal private structure |
| * @param band BAND_2GHZ/BAND_5GHZ |
| * @param params A pointer to cfg80211_ap_settings structure |
| * @return 0 -- success, otherwise fail |
| */ |
| static t_u8 |
| woal_check_11ac_capability(moal_private *priv, t_u8 band, |
| struct cfg80211_ap_settings *params) |
| #else |
| /** |
| * @brief initialize AP or GO bss config |
| * @param band BAND_2GHZ/BAND_5GHZ |
| * @param priv A pointer to moal private structure |
| * @return 0 -- success, otherwise fail |
| */ |
| static t_u8 |
| woal_check_11ac_capability(moal_private *priv, t_u8 band) |
| #endif |
| { |
| mlan_fw_info fw_info; |
| t_u8 enable_11ac = MFALSE; |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| const u8 *vht_ie = NULL; |
| #endif |
| ENTER(); |
| memset(&fw_info, 0, sizeof(mlan_fw_info)); |
| woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); |
| if ((band == BAND_5GHZ) && !(fw_info.fw_bands & BAND_AAC)) { |
| PRINTM(MCMND, "FW don't support 5G AC"); |
| LEAVE(); |
| return enable_11ac; |
| } |
| if ((band == BAND_2GHZ) && !(fw_info.fw_bands & BAND_GAC)) { |
| PRINTM(MCMND, "FW don't support 2G AC"); |
| LEAVE(); |
| return enable_11ac; |
| } |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail, |
| params->beacon.tail_len); |
| if (vht_ie) |
| enable_11ac = MTRUE; |
| else |
| enable_11ac = MFALSE; |
| #else |
| enable_11ac = MTRUE; |
| #endif |
| LEAVE(); |
| return enable_11ac; |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| /** |
| * @brief initialize AP or GO bss config |
| * @param priv A pointer to moal private structure |
| * @param band BAND_5G/BAND_2GHZ |
| * @param params A pointer to cfg80211_ap_settings structure |
| * @return 0 -- success, otherwise fail |
| */ |
| static t_u8 |
| woal_check_11ax_capability(moal_private *priv, t_u8 band, |
| struct cfg80211_ap_settings *params) |
| { |
| mlan_fw_info fw_info; |
| t_u8 enable_11ax = MFALSE; |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 20, 0) |
| mlan_ds_11ax_he_cfg he_cfg; |
| t_u8 he_txrx_mcs_support[4] = { 0xff, 0xff, 0xff, 0xff }; |
| #endif |
| ENTER(); |
| memset(&fw_info, 0, sizeof(mlan_fw_info)); |
| woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info); |
| if ((band == BAND_5GHZ) && !(fw_info.fw_bands & BAND_AAX)) { |
| PRINTM(MCMND, "FW don't support 5G AX\n"); |
| LEAVE(); |
| return enable_11ax; |
| } |
| if ((band == BAND_2GHZ) && !(fw_info.fw_bands & BAND_GAX)) { |
| PRINTM(MCMND, "FW don't support 2G AX"); |
| LEAVE(); |
| return enable_11ax; |
| } |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) |
| if (params->he_cap) |
| enable_11ax = MTRUE; |
| else |
| enable_11ax = MFALSE; |
| #else |
| memset(&he_cfg, 0, sizeof(he_cfg)); |
| if (band == BAND_5GHZ) |
| he_cfg.band = MBIT(1); |
| else if (band == BAND_2GHZ) |
| he_cfg.band = MBIT(0); |
| if (0 == woal_11ax_cfg(priv, MLAN_ACT_GET, &he_cfg)) { |
| if (he_cfg.he_cap.len && |
| (he_cfg.he_cap.ext_id == HE_CAPABILITY)) { |
| if (memcmp(he_cfg.he_cap.he_txrx_mcs_support, |
| he_txrx_mcs_support, |
| sizeof(he_txrx_mcs_support))) |
| enable_11ax = MTRUE; |
| } |
| } |
| #endif |
| PRINTM(MCMND, "enable_11ax=%d\n", enable_11ax); |
| LEAVE(); |
| return enable_11ax; |
| } |
| #endif |
| |
| /** |
| * @brief get ht_cap from beacon ie |
| * |
| * @param ie Pointer to IEs |
| * @param len Total length of ie |
| * |
| * @return ht_cap |
| */ |
| static t_u16 |
| woal_get_htcap_info(const t_u8 *ie, int len) |
| { |
| t_u16 ht_cap_info = 0; |
| IEEEtypes_HTCap_t *htcap_ie = NULL; |
| htcap_ie = |
| (IEEEtypes_HTCap_t *)woal_parse_ie_tlv(ie, len, HT_CAPABILITY); |
| if (htcap_ie) { |
| /* hostap has converted ht_cap_info to little endian, here |
| * conver to host endian */ |
| ht_cap_info = woal_le16_to_cpu(htcap_ie->ht_cap.ht_cap_info); |
| PRINTM(MMSG, "Get ht_cap from beacon ies: 0x%x\n", ht_cap_info); |
| } |
| return ht_cap_info; |
| } |
| |
| /** |
| * @brief get vht_cap from beacon ie |
| * |
| * @param ie Pointer to IEs |
| * @param len Total length of ie |
| * |
| * @return Pointer to vht_cap ie |
| */ |
| static IEEEtypes_VHTCap_t * |
| woal_get_vhtcap_info(const t_u8 *ie, int len) |
| { |
| IEEEtypes_VHTCap_t *vhtcap_ie = NULL; |
| vhtcap_ie = (IEEEtypes_VHTCap_t *)woal_parse_ie_tlv(ie, len, |
| VHT_CAPABILITY); |
| if (vhtcap_ie) |
| PRINTM(MMSG, "Get vht_cap from beacon ies: 0x%x\n", |
| vhtcap_ie->vht_cap.vht_cap_info); |
| return vhtcap_ie; |
| } |
| |
| /** |
| * @brief get vht_oper from beacon ie |
| * |
| * @param ie Pointer to IEs |
| * @param len Total length of ie |
| * |
| * @return Pointer to vht_opr ie |
| */ |
| static IEEEtypes_VHTOprat_t * |
| woal_get_vht_oprat_ie(const t_u8 *ie, int len) |
| { |
| IEEEtypes_VHTOprat_t *vht_oprat_ie = NULL; |
| vht_oprat_ie = (IEEEtypes_VHTOprat_t *)woal_parse_ie_tlv(ie, len, |
| VHT_OPERATION); |
| if (vht_oprat_ie) |
| PRINTM(MMSG, |
| "Get vht_oprat_ie from beacon ies: chan_width=%d\n", |
| vht_oprat_ie->chan_width); |
| return vht_oprat_ie; |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) |
| /** Starting Frequency for 11A band */ |
| #define START_FREQ_11A_BAND 5000 /* in MHz */ |
| /** |
| * @brief convert cfg80211_chan_def to Band_Config |
| * |
| * @param priv A pointer to moal private structure |
| * @param bandcfg A pointer to (Band_Config_t structure |
| * @param chandef A pointer to cfg80211_chan_def structure |
| * |
| * @return N/A |
| */ |
| static void |
| woal_convert_chan_to_bandconfig(moal_private *priv, |
| Band_Config_t *bandcfg, |
| struct cfg80211_chan_def *chandef) |
| { |
| ENTER(); |
| |
| if (chandef->chan->hw_value <= MAX_BG_CHANNEL) |
| bandcfg->chanBand = BAND_2GHZ; |
| else |
| bandcfg->chanBand = BAND_5GHZ; |
| switch (chandef->width) { |
| case NL80211_CHAN_WIDTH_20_NOHT: |
| case NL80211_CHAN_WIDTH_20: |
| bandcfg->chanWidth = CHAN_BW_20MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_40: |
| bandcfg->chanWidth = CHAN_BW_40MHZ; |
| if (chandef->center_freq1 > chandef->chan->center_freq) |
| bandcfg->chan2Offset = SEC_CHAN_ABOVE; |
| else |
| bandcfg->chan2Offset = SEC_CHAN_BELOW; |
| break; |
| case NL80211_CHAN_WIDTH_80: |
| bandcfg->chan2Offset = |
| woal_get_second_channel_offset(priv, |
| chandef->chan->hw_value); |
| bandcfg->chanWidth = CHAN_BW_80MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_80P80: |
| case NL80211_CHAN_WIDTH_160: |
| default: |
| break; |
| } |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief Enable radar detect for DFS channel |
| * |
| * @param priv A pointer to moal private structure |
| * @param chandef A pointer to cfg80211_chan_def structure |
| * @return N/A |
| */ |
| static void |
| woal_enable_dfs_support(moal_private *priv, struct cfg80211_chan_def *chandef) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11h_chan_rep_req *pchan_rpt_req = NULL; |
| mlan_ds_11h_cfg *p11h_cfg = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| ENTER(); |
| if (!(chandef->chan->flags & IEEE80211_CHAN_RADAR)) { |
| PRINTM(MIOCTL, "No radar channel\n"); |
| LEAVE(); |
| return; |
| } |
| PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d \n", |
| chandef->chan->hw_value, chandef->width); |
| |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); |
| if (NULL == req) { |
| PRINTM(MIOCTL, "No Memory to allocate ioctl buffer\n"); |
| LEAVE(); |
| return; |
| } |
| p11h_cfg = (mlan_ds_11h_cfg *)req->pbuf; |
| pchan_rpt_req = &p11h_cfg->param.chan_rpt_req; |
| pchan_rpt_req->startFreq = 5000; |
| pchan_rpt_req->chanNum = (t_u8)chandef->chan->hw_value; |
| woal_convert_chan_to_bandconfig(priv, &pchan_rpt_req->bandcfg, chandef); |
| pchan_rpt_req->host_based = MTRUE; |
| pchan_rpt_req->millisec_dwell_time = 0; |
| |
| p11h_cfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK; |
| req->req_id = MLAN_IOCTL_11H_CFG; |
| req->action = MLAN_ACT_SET; |
| /* Send Channel Check command and wait until the report is ready */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return; |
| } |
| #endif |
| |
| /** |
| * @brief Prase supported rates from beacon data, set bss cfg accordingly |
| * |
| * @param priv A pointer to moal_private |
| * @param bss_cfg A pointer to bss configuration structure |
| * @param head_ie A pointer to beacon head IE buffer |
| * @param head_len head IE buffer length |
| * @param tail_ie A pointer to beacon tail IE buffer |
| * @param tail_len tail IE buffer length * |
| * @return N/A |
| */ |
| static void |
| woal_set_uap_rates(moal_private *priv, mlan_uap_bss_param *bss_cfg, |
| const t_u8 *head_ie, int head_len, |
| const t_u8 *tail_ie, int tail_len) |
| { |
| pIEEEtypes_Header_t rate_ie; |
| pIEEEtypes_Header_t ext_rate_ie; |
| int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable); |
| const u8 *var_pos = head_ie + var_offset; |
| int len = head_len - var_offset; |
| int rate_len = 0; |
| |
| rate_ie = (void *)woal_parse_ie_tlv(var_pos, len, WLAN_EID_SUPP_RATES); |
| if (rate_ie) { |
| memset(bss_cfg->rates, 0, sizeof(bss_cfg->rates)); |
| moal_memcpy_ext(priv->phandle, bss_cfg->rates, rate_ie + 1, |
| rate_ie->len, sizeof(bss_cfg->rates)); |
| rate_len = MIN(rate_ie->len, sizeof(bss_cfg->rates)); |
| } |
| ext_rate_ie = (void *)woal_parse_ie_tlv(tail_ie, tail_len, |
| WLAN_EID_EXT_SUPP_RATES); |
| if (ext_rate_ie) { |
| moal_memcpy_ext(priv->phandle, &bss_cfg->rates[rate_len], |
| ext_rate_ie + 1, ext_rate_ie->len, |
| sizeof(bss_cfg->rates) - rate_len); |
| rate_len += MIN(ext_rate_ie->len, |
| (sizeof(bss_cfg->rates) - rate_len)); |
| } |
| DBG_HEXDUMP(MCMD_D, "rates", bss_cfg->rates, sizeof(bss_cfg->rates)); |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| /** |
| * @brief initialize AP or GO bss config |
| * |
| * @param priv A pointer to moal private structure |
| * @param params A pointer to cfg80211_ap_settings structure |
| * @return 0 -- success, otherwise fail |
| */ |
| static int |
| woal_cfg80211_beacon_config(moal_private *priv, |
| struct cfg80211_ap_settings *params) |
| #else |
| /** |
| * @brief initialize AP or GO bss config |
| * |
| * @param priv A pointer to moal private structure |
| * @param params A pointer to beacon_parameters structure |
| * @return 0 -- success, otherwise fail |
| */ |
| static int |
| woal_cfg80211_beacon_config(moal_private *priv, |
| struct beacon_parameters *params) |
| #endif |
| { |
| struct wiphy *wiphy = NULL; |
| const t_u8 *ie = NULL; |
| int ret = 0, ie_len; |
| mlan_uap_bss_param *sys_config = NULL; |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) |
| int i = 0; |
| #else |
| t_u8 wpa_ies; |
| const t_u8 *ssid_ie = NULL; |
| struct ieee80211_mgmt *head = NULL; |
| t_u16 capab_info = 0; |
| #endif |
| t_u8 rates_bg[13] = { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, |
| 0x24, 0x30, 0x48, 0x60, 0x6c, 0x00 |
| }; |
| t_u8 rates_a[9] = { 0x8c, 0x12, 0x98, 0x24, 0xb0, |
| 0x48, 0x60, 0x6c, 0x00 |
| }; |
| #ifdef WIFI_DIRECT_SUPPORT |
| t_u8 rates_wfd[9] = { 0x8c, 0x12, 0x18, 0x24, 0x30, |
| 0x48, 0x60, 0x6c, 0x00 |
| }; |
| #endif |
| t_u8 chan2Offset = SEC_CHAN_NONE; |
| t_u8 enable_11n = MTRUE; |
| t_u16 ht_cap = 0; |
| t_u8 enable_11ac = MFALSE; |
| t_u8 vht20_40 = MFALSE; |
| IEEEtypes_VHTCap_t *vhtcap_ie = NULL; |
| IEEEtypes_VHTOprat_t *vhtopr_ie = NULL; |
| IEEEtypes_HECap_t *hecap_ie = NULL; |
| t_u8 enable_11ax = MFALSE; |
| t_u8 *wapi_ie = NULL; |
| int wapi_ie_len = 0; |
| #ifdef WIFI_DIRECT_SUPPORT |
| int GoAgeoutTime = priv->phandle->params.GoAgeoutTime; |
| #endif |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) |
| mlan_ds_11h_chan_nop_info chan_nop_info; |
| Band_Config_t bandcfg; |
| #endif |
| ENTER(); |
| |
| if (!params) { |
| ret = -EFAULT; |
| goto done; |
| } |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| ie = ((struct cfg80211_ap_settings *)params)->beacon.tail; |
| ie_len = ((struct cfg80211_ap_settings *)params)->beacon.tail_len; |
| #else |
| ie = ((struct beacon_parameters *)params)->tail; |
| ie_len = ((struct beacon_parameters *)params)->tail_len; |
| #endif |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| wapi_ie = (t_u8 *)woal_parse_ie_tlv(params->beacon.tail, |
| params->beacon.tail_len, WAPI_IE); |
| #else |
| wapi_ie = (t_u8 *)woal_parse_ie_tlv(params->tail, params->tail_len, |
| WAPI_IE); |
| #endif |
| if (wapi_ie) { |
| wapi_ie_len = *(wapi_ie + 1) + 2; |
| if (MLAN_STATUS_FAILURE == |
| woal_set_get_gen_ie(priv, MLAN_ACT_SET, wapi_ie, |
| &wapi_ie_len, MOAL_IOCTL_WAIT)) { |
| PRINTM(MERROR, "Failed to set wapi ie\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| |
| wiphy = priv->phandle->wiphy; |
| if (priv->bss_type != MLAN_BSS_TYPE_UAP |
| #ifdef WIFI_DIRECT_SUPPORT |
| && priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT |
| #endif |
| ) { |
| ret = -EFAULT; |
| goto done; |
| } |
| sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); |
| if (!sys_config) { |
| PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| /* Initialize the uap bss values which are uploaded from firmware */ |
| if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, |
| MOAL_IOCTL_WAIT, |
| sys_config)) { |
| PRINTM(MERROR, "Error getting AP confiruration\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (priv->phandle->params.uap_max_sta) |
| sys_config->max_sta_count = priv->phandle->params.uap_max_sta; |
| |
| /* Setting the default values */ |
| sys_config->channel = 6; |
| sys_config->preamble_type = 0; |
| sys_config->mgmt_ie_passthru_mask = priv->mgmt_subtype_mask; |
| moal_memcpy_ext(priv->phandle, sys_config->mac_addr, priv->current_addr, |
| ETH_ALEN, sizeof(sys_config->mac_addr)); |
| |
| #ifdef WIFI_DIRECT_SUPPORT |
| if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && GoAgeoutTime) { |
| sys_config->sta_ageout_timer = GoAgeoutTime; |
| sys_config->ps_sta_ageout_timer = GoAgeoutTime; |
| } |
| #endif |
| /* Set frag_threshold, rts_threshold, and retry limit */ |
| sys_config->frag_threshold = wiphy->frag_threshold; |
| sys_config->rts_threshold = wiphy->rts_threshold; |
| sys_config->retry_limit = wiphy->retry_long; |
| if (sys_config->frag_threshold == (t_u16)MLAN_FRAG_RTS_DISABLED) { |
| sys_config->frag_threshold = MLAN_FRAG_MAX_VALUE; |
| } |
| if (sys_config->rts_threshold == (t_u16)MLAN_FRAG_RTS_DISABLED) { |
| sys_config->rts_threshold = MLAN_RTS_MAX_VALUE; |
| } |
| |
| if (priv->bss_type == MLAN_BSS_TYPE_UAP) { |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| if (params->beacon_interval) |
| sys_config->beacon_period = params->beacon_interval; |
| #else |
| if (params->interval) |
| sys_config->beacon_period = params->interval; |
| #endif |
| if (params->dtim_period) |
| sys_config->dtim_period = params->dtim_period; |
| } |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| /** back up ap's channel */ |
| moal_memcpy_ext(priv->phandle, &priv->chan, ¶ms->chandef, |
| sizeof(struct cfg80211_chan_def), sizeof(priv->chan)); |
| #endif |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) |
| if (priv->phandle->usr_nop_period_sec) { |
| PRINTM(MCMND, "Checking if AP's channel %d is under NOP\n", |
| priv->channel); |
| woal_convert_chan_to_bandconfig(priv, &bandcfg, |
| ¶ms->chandef); |
| memset(&chan_nop_info, 0, sizeof(chan_nop_info)); |
| chan_nop_info.curr_chan = priv->channel; |
| chan_nop_info.chan_width = bandcfg.chanWidth; |
| if (params->chandef.width >= NL80211_CHAN_WIDTH_20) |
| chan_nop_info.new_chan.is_11n_enabled = MTRUE; |
| chan_nop_info.new_chan.bandcfg = bandcfg; |
| woal_uap_get_channel_nop_info(priv, MOAL_IOCTL_WAIT, |
| &chan_nop_info); |
| if (chan_nop_info.chan_under_nop) { |
| PRINTM(MCMND, |
| "cfg80211: Channel %d is under NOP, New channel=%d\n", |
| priv->channel, chan_nop_info.new_chan.channel); |
| priv->chan_under_nop = chan_nop_info.chan_under_nop; |
| priv->channel = chan_nop_info.new_chan.channel; |
| woal_chandef_create(priv, &priv->chan, |
| &chan_nop_info.new_chan); |
| } |
| } |
| #endif |
| |
| if (priv->channel) { |
| memset(sys_config->rates, 0, sizeof(sys_config->rates)); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| switch (priv->chan.width) { |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0) |
| case NL80211_CHAN_WIDTH_5: |
| case NL80211_CHAN_WIDTH_10: |
| #endif |
| case NL80211_CHAN_WIDTH_20_NOHT: |
| enable_11n = MFALSE; |
| break; |
| case NL80211_CHAN_WIDTH_20: |
| break; |
| case NL80211_CHAN_WIDTH_40: |
| if (priv->chan.center_freq1 < |
| priv->chan.chan->center_freq) |
| chan2Offset = SEC_CHAN_BELOW; |
| else |
| chan2Offset = SEC_CHAN_ABOVE; |
| break; |
| case NL80211_CHAN_WIDTH_80: |
| case NL80211_CHAN_WIDTH_80P80: |
| case NL80211_CHAN_WIDTH_160: |
| chan2Offset = |
| woal_get_second_channel_offset(priv, |
| priv->channel); |
| break; |
| default: |
| PRINTM(MWARN, "Unknown channel width: %d\n", |
| priv->chan.width); |
| break; |
| } |
| #else |
| switch (params->channel_type) { |
| case NL80211_CHAN_NO_HT: |
| enable_11n = MFALSE; |
| break; |
| case NL80211_CHAN_HT20: |
| break; |
| case NL80211_CHAN_HT40PLUS: |
| chan2Offset = SEC_CHAN_ABOVE; |
| break; |
| case NL80211_CHAN_HT40MINUS: |
| chan2Offset = SEC_CHAN_BELOW; |
| break; |
| default: |
| PRINTM(MWARN, "Unknown channel type: %d\n", |
| params->channel_type); |
| break; |
| } |
| #endif |
| #endif /* CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) */ |
| |
| sys_config->channel = priv->channel; |
| if (priv->channel <= MAX_BG_CHANNEL) { |
| sys_config->bandcfg.chanBand = BAND_2GHZ; |
| #ifdef WIFI_DIRECT_SUPPORT |
| if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) |
| moal_memcpy_ext(priv->phandle, |
| sys_config->rates, rates_wfd, |
| sizeof(rates_wfd), |
| sizeof(sys_config->rates)); |
| else |
| #endif |
| moal_memcpy_ext(priv->phandle, |
| sys_config->rates, rates_bg, |
| sizeof(rates_bg), |
| sizeof(sys_config->rates)); |
| } else { |
| sys_config->bandcfg.chanBand = BAND_5GHZ; |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) |
| chan2Offset = |
| woal_get_second_channel_offset(priv, |
| priv->channel); |
| #endif |
| |
| #ifdef WIFI_DIRECT_SUPPORT |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) |
| /* Force enable 40MHZ on WFD interface */ |
| if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) |
| chan2Offset = |
| woal_get_second_channel_offset(priv, |
| priv-> |
| channel); |
| #endif |
| #endif |
| #ifdef WIFI_DIRECT_SUPPORT |
| if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT) |
| moal_memcpy_ext(priv->phandle, |
| sys_config->rates, rates_wfd, |
| sizeof(rates_wfd), |
| sizeof(sys_config->rates)); |
| else |
| #endif |
| moal_memcpy_ext(priv->phandle, |
| sys_config->rates, rates_a, |
| sizeof(rates_a), |
| sizeof(sys_config->rates)); |
| } |
| |
| /* Replaced with rate from userspace, if exist */ |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| woal_set_uap_rates(priv, sys_config, params->beacon.head, |
| params->beacon.head_len, params->beacon.tail, |
| params->beacon.tail_len); |
| #else |
| woal_set_uap_rates(priv, sys_config, params->head, |
| params->head_len, params->tail, |
| params->tail_len); |
| #endif |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| enable_11ac = woal_check_11ac_capability(priv, |
| sys_config->bandcfg. |
| chanBand, params); |
| if (enable_11ac && ((priv->chan.width == NL80211_CHAN_WIDTH_20) |
| || (priv->chan.width == |
| NL80211_CHAN_WIDTH_40))) |
| vht20_40 = MTRUE; |
| #else |
| enable_11ac = woal_check_11ac_capability(priv, |
| sys_config->bandcfg. |
| chanBand); |
| #endif |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| enable_11ax = |
| woal_check_11ax_capability(priv, |
| sys_config->bandcfg.chanBand, |
| params); |
| #endif |
| |
| /* Disable GreenField by default */ |
| sys_config->ht_cap_info = 0x10c; |
| if (enable_11n) |
| sys_config->ht_cap_info |= 0x20; |
| if (chan2Offset) { |
| sys_config->bandcfg.chan2Offset = chan2Offset; |
| sys_config->ht_cap_info |= 0x1042; |
| sys_config->ampdu_param = 3; |
| } else { |
| sys_config->bandcfg.chan2Offset = 0; |
| } |
| ht_cap = woal_get_htcap_info(ie, ie_len); |
| if (ht_cap) { |
| if (sys_config->bandcfg.chanBand == BAND_2GHZ) |
| sys_config->ht_cap_info = |
| (ht_cap & |
| (wiphy->bands[IEEE80211_BAND_2GHZ] |
| ->ht_cap.cap & 0x13ff)) | 0x0c; |
| else |
| sys_config->ht_cap_info = |
| (ht_cap & |
| (wiphy->bands[IEEE80211_BAND_5GHZ] |
| ->ht_cap.cap & 0x13ff)) | 0x0c; |
| } |
| PRINTM(MCMND, |
| "11n=%d, ht_cap=0x%x, channel=%d, bandcfg:chanBand=0x%x chanWidth=0x%x chan2Offset=0x%x scanMode=0x%x\n", |
| enable_11n, sys_config->ht_cap_info, |
| priv->channel, sys_config->bandcfg.chanBand, |
| sys_config->bandcfg.chanWidth, |
| sys_config->bandcfg.chan2Offset, |
| sys_config->bandcfg.scanMode); |
| } |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) |
| if (!params->ssid || !params->ssid_len) { |
| ret = -EINVAL; |
| goto done; |
| } |
| moal_memcpy_ext(priv->phandle, sys_config->ssid.ssid, params->ssid, |
| MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len), |
| sizeof(sys_config->ssid.ssid)); |
| sys_config->ssid.ssid_len = MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len); |
| /** |
| * hidden_ssid=0: broadcast SSID in beacons. |
| * hidden_ssid=1: send empty SSID (length=0) in beacon. |
| * hidden_ssid=2: clear SSID (ACSII 0), but keep the original length |
| */ |
| if (!params->hidden_ssid) |
| sys_config->bcast_ssid_ctl = 1; |
| else if (params->hidden_ssid == 1) |
| sys_config->bcast_ssid_ctl = 0; |
| else if (params->hidden_ssid == 2) |
| sys_config->bcast_ssid_ctl = 2; |
| switch (params->auth_type) { |
| case NL80211_AUTHTYPE_SHARED_KEY: |
| sys_config->auth_mode = MLAN_AUTH_MODE_SHARED; |
| break; |
| case NL80211_AUTHTYPE_AUTOMATIC: |
| sys_config->auth_mode = MLAN_AUTH_MODE_AUTO; |
| break; |
| case NL80211_AUTHTYPE_OPEN_SYSTEM: |
| default: |
| sys_config->auth_mode = MLAN_AUTH_MODE_OPEN; |
| break; |
| } |
| |
| sys_config->protocol = PROTOCOL_NO_SECURITY; |
| if ((params->crypto.wpa_versions & NL80211_WPA_VERSION_1) && |
| (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)) |
| sys_config->protocol = PROTOCOL_WPA | PROTOCOL_WPA2; |
| else if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) |
| sys_config->protocol = PROTOCOL_WPA2; |
| else if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) |
| sys_config->protocol = PROTOCOL_WPA; |
| if (params->crypto.n_akm_suites || |
| (params->privacy && params->crypto.wpa_versions)) |
| woal_find_wpa_ies(ie, ie_len, sys_config); |
| for (i = 0; i < params->crypto.n_akm_suites; i++) { |
| switch (params->crypto.akm_suites[i]) { |
| case WLAN_AKM_SUITE_8021X: |
| sys_config->key_mgmt |= KEY_MGMT_EAP; |
| break; |
| case WLAN_AKM_SUITE_PSK: |
| sys_config->key_mgmt |= KEY_MGMT_PSK; |
| break; |
| } |
| } |
| sys_config->wpa_cfg.pairwise_cipher_wpa = 0; |
| sys_config->wpa_cfg.pairwise_cipher_wpa2 = 0; |
| for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) { |
| switch (params->crypto.ciphers_pairwise[i]) { |
| case WLAN_CIPHER_SUITE_WEP40: |
| case WLAN_CIPHER_SUITE_WEP104: |
| break; |
| case WLAN_CIPHER_SUITE_TKIP: |
| if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) |
| sys_config->wpa_cfg.pairwise_cipher_wpa |= |
| CIPHER_TKIP; |
| if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) |
| sys_config->wpa_cfg.pairwise_cipher_wpa2 |= |
| CIPHER_TKIP; |
| break; |
| case WLAN_CIPHER_SUITE_CCMP: |
| if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1) |
| sys_config->wpa_cfg.pairwise_cipher_wpa |= |
| CIPHER_AES_CCMP; |
| if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2) |
| sys_config->wpa_cfg.pairwise_cipher_wpa2 |= |
| CIPHER_AES_CCMP; |
| break; |
| case WLAN_CIPHER_SUITE_SMS4: |
| sys_config->protocol = PROTOCOL_WAPI; |
| break; |
| } |
| } |
| switch (params->crypto.cipher_group) { |
| case WLAN_CIPHER_SUITE_WEP40: |
| case WLAN_CIPHER_SUITE_WEP104: |
| if ((priv->cipher == WLAN_CIPHER_SUITE_WEP40) || |
| (priv->cipher == WLAN_CIPHER_SUITE_WEP104)) { |
| sys_config->protocol = PROTOCOL_STATIC_WEP; |
| sys_config->key_mgmt = KEY_MGMT_NONE; |
| sys_config->wpa_cfg.length = 0; |
| moal_memcpy_ext(priv->phandle, |
| &sys_config->wep_cfg.key0, |
| &priv->uap_wep_key[0], sizeof(wep_key), |
| sizeof(sys_config->wep_cfg.key0)); |
| moal_memcpy_ext(priv->phandle, |
| &sys_config->wep_cfg.key1, |
| &priv->uap_wep_key[1], sizeof(wep_key), |
| sizeof(sys_config->wep_cfg.key1)); |
| moal_memcpy_ext(priv->phandle, |
| &sys_config->wep_cfg.key2, |
| &priv->uap_wep_key[2], sizeof(wep_key), |
| sizeof(sys_config->wep_cfg.key2)); |
| moal_memcpy_ext(priv->phandle, |
| &sys_config->wep_cfg.key3, |
| &priv->uap_wep_key[3], sizeof(wep_key), |
| sizeof(sys_config->wep_cfg.key3)); |
| } |
| break; |
| case WLAN_CIPHER_SUITE_TKIP: |
| sys_config->wpa_cfg.group_cipher = CIPHER_TKIP; |
| break; |
| case WLAN_CIPHER_SUITE_CCMP: |
| sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP; |
| break; |
| case WLAN_CIPHER_SUITE_SMS4: |
| sys_config->protocol = PROTOCOL_WAPI; |
| break; |
| } |
| #else |
| /* Since in Android ICS 4.0.1's wpa_supplicant, there is no way to set ssid |
| * when GO (AP) starts up, so get it from beacon head parameter |
| * TODO: right now use hard code |
| * 24 -- ieee80211 header lenth, 12 -- fixed element length for beacon |
| */ |
| #define BEACON_IE_OFFSET 36 |
| /* Find SSID in head |
| * SSID IE id: 0, right now use hard code |
| */ |
| ssid_ie = woal_parse_ie_tlv(params->head + BEACON_IE_OFFSET, |
| params->head_len - BEACON_IE_OFFSET, 0); |
| |
| if (!ssid_ie) { |
| PRINTM(MERROR, "No ssid IE found.\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| if (*(ssid_ie + 1) > 32) { |
| PRINTM(MERROR, "ssid len error: %d\n", *(ssid_ie + 1)); |
| ret = -EFAULT; |
| goto done; |
| } |
| moal_memcpy_ext(priv->phandle, sys_config->ssid.ssid, ssid_ie + 2, |
| *(ssid_ie + 1), sizeof(sys_config->ssid.ssid)); |
| sys_config->ssid.ssid_len = *(ssid_ie + 1); |
| head = (struct ieee80211_mgmt *)params->head; |
| |
| capab_info = le16_to_cpu(head->u.beacon.capab_info); |
| PRINTM(MIOCTL, "capab_info=0x%x\n", head->u.beacon.capab_info); |
| sys_config->auth_mode = MLAN_AUTH_MODE_OPEN; |
| /** For ICS, we don't support OPEN mode */ |
| if ((priv->cipher == WLAN_CIPHER_SUITE_WEP40) || |
| (priv->cipher == WLAN_CIPHER_SUITE_WEP104)) { |
| sys_config->protocol = PROTOCOL_STATIC_WEP; |
| sys_config->key_mgmt = KEY_MGMT_NONE; |
| sys_config->.wpa_cfg.length = 0; |
| moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key0, |
| &priv->uap_wep_key[0], sizeof(wep_key), |
| sizeof(sys_config->wep_cfg.key0)); |
| moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key1, |
| &priv->uap_wep_key[1], sizeof(wep_key), |
| sizeof(sys_config->wep_cfg.key1)); |
| moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key2, |
| &priv->uap_wep_key[2], sizeof(wep_key), |
| sizeof(sys_config->wep_cfg.key2)); |
| moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key3, |
| &priv->uap_wep_key[3], sizeof(wep_key), |
| sizeof(sys_config->wep_cfg.key3)); |
| } else { |
| /** Get cipher and key_mgmt from RSN/WPA IE */ |
| if (capab_info & WLAN_CAPABILITY_PRIVACY) { |
| wpa_ies = |
| woal_find_wpa_ies(params->tail, |
| params->tail_len, sys_config); |
| if (wpa_ies == MFALSE) { |
| /* hard code setting to wpa2-psk */ |
| sys_config->protocol = PROTOCOL_WPA2; |
| sys_config->key_mgmt = KEY_MGMT_PSK; |
| sys_config->wpa_cfg.pairwise_cipher_wpa2 = |
| CIPHER_AES_CCMP; |
| sys_config->wpa_cfg.group_cipher = |
| CIPHER_AES_CCMP; |
| } |
| } |
| } |
| #endif |
| |
| if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) { |
| /*find and set wmm ie */ |
| woal_set_wmm_ies(priv, ie, ie_len, sys_config); |
| } |
| /* If the security mode is configured as WEP or WPA-PSK, |
| * it will disable 11n automatically, and if configured as |
| * open(off) or wpa2-psk, it will automatically enable 11n */ |
| if ((sys_config->protocol == PROTOCOL_STATIC_WEP) || |
| (sys_config->protocol == PROTOCOL_WPA)) |
| enable_11n = MFALSE; |
| if (!enable_11n) { |
| woal_set_uap_ht_tx_cfg(priv, sys_config->bandcfg, ht_cap, |
| MFALSE); |
| woal_uap_set_11n_status(priv, sys_config, MLAN_ACT_DISABLE); |
| } else { |
| woal_set_uap_ht_tx_cfg(priv, sys_config->bandcfg, ht_cap, |
| MTRUE); |
| woal_uap_set_11n_status(priv, sys_config, MLAN_ACT_ENABLE); |
| woal_set_get_tx_bf_cap(priv, MLAN_ACT_GET, |
| &sys_config->tx_bf_cap); |
| } |
| if (enable_11ac && enable_11n) { |
| vhtcap_ie = woal_get_vhtcap_info(ie, ie_len); |
| vhtopr_ie = woal_get_vht_oprat_ie(ie, ie_len); |
| //Enable VHT80 |
| if (vhtopr_ie && vhtopr_ie->chan_width) |
| vht20_40 = 0; |
| woal_uap_set_11ac_status(priv, MLAN_ACT_ENABLE, |
| vht20_40, vhtcap_ie); |
| } else { |
| woal_uap_set_11ac_status(priv, MLAN_ACT_DISABLE, |
| vht20_40, NULL); |
| } |
| if (enable_11ax && enable_11n) { |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) |
| hecap_ie = |
| (IEEEtypes_HECap_t *) woal_parse_ext_ie_tlv(ie, ie_len, |
| HE_CAPABILITY); |
| #endif |
| woal_uap_set_11ax_status(priv, MLAN_ACT_ENABLE, |
| sys_config->bandcfg.chanBand, |
| hecap_ie); |
| } else |
| woal_uap_set_11ax_status(priv, MLAN_ACT_DISABLE, |
| sys_config->bandcfg.chanBand, NULL); |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| if (params->inactivity_timeout) { |
| sys_config->sta_ageout_timer = params->inactivity_timeout * 10; |
| sys_config->ps_sta_ageout_timer = |
| params->inactivity_timeout * 10; |
| } |
| PRINTM(MIOCTL, "inactivity_timeout=%d\n", params->inactivity_timeout); |
| PRINTM(MIOCTL, "sta_ageout_timer=%d ps_sta_ageout_timer=%d\n", |
| sys_config->sta_ageout_timer, sys_config->ps_sta_ageout_timer); |
| #endif |
| |
| if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET, |
| MOAL_IOCTL_WAIT, |
| sys_config)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| /** Set wacp_mode for uAP/P2P-GO */ |
| if (priv->phandle->params.wacp_mode) { |
| PRINTM(MIOCTL, "wacp_mode: %d\n", |
| priv->phandle->params.wacp_mode); |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_wacp_mode(priv, MOAL_IOCTL_WAIT)) { |
| PRINTM(MERROR, "Set wacp_mode failed\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) |
| woal_enable_dfs_support(priv, &priv->chan); |
| #endif |
| done: |
| kfree(sys_config); |
| LEAVE(); |
| return ret; |
| } |
| |
| #ifdef WIFI_DIRECT_SUPPORT |
| #if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION |
| /** |
| * @brief Callback function for virtual interface |
| * setup |
| * |
| * @param dev A pointer to structure net_device |
| * |
| * @return N/A |
| */ |
| static void |
| woal_virt_if_setup(struct net_device *dev) |
| { |
| ENTER(); |
| ether_setup(dev); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 9) |
| dev->needs_free_netdev = true; |
| #else |
| dev->destructor = free_netdev; |
| #endif |
| LEAVE(); |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) |
| /** |
| * @brief This function adds a new interface. It will |
| * allocate, initialize and register the device. |
| * |
| * @param handle A pointer to moal_handle structure |
| * @param bss_index BSS index number |
| * @param name_assign_type Interface name assignment type |
| * @param bss_type BSS type |
| * |
| * @return A pointer to the new priv structure |
| */ |
| static moal_private * |
| woal_alloc_virt_interface(moal_handle *handle, t_u8 bss_index, |
| unsigned char name_assign_type, |
| t_u8 bss_type, const char *name) |
| #else |
| /** |
| * @brief This function adds a new interface. It will |
| * allocate, initialize and register the device. |
| * |
| * @param handle A pointer to moal_handle structure |
| * @param bss_index BSS index number |
| * @param bss_type BSS type |
| * |
| * @return A pointer to the new priv structure |
| */ |
| moal_private * |
| woal_alloc_virt_interface(moal_handle *handle, t_u8 bss_index, t_u8 bss_type, |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) |
| const |
| #endif |
| char *name) |
| #endif |
| { |
| struct net_device *dev = NULL; |
| moal_private *priv = NULL; |
| ENTER(); |
| |
| #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29) |
| #ifndef MAX_WMM_QUEUE |
| #define MAX_WMM_QUEUE 4 |
| #endif |
| /* Allocate an Ethernet device */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) |
| dev = alloc_netdev_mq(sizeof(moal_private), name, name_assign_type, |
| woal_virt_if_setup, MAX_WMM_QUEUE); |
| #else |
| dev = alloc_netdev_mq(sizeof(moal_private), name, NET_NAME_UNKNOWN, |
| woal_virt_if_setup, MAX_WMM_QUEUE); |
| #endif |
| #else |
| dev = alloc_netdev_mq(sizeof(moal_private), name, woal_virt_if_setup, |
| MAX_WMM_QUEUE); |
| #endif |
| #else |
| dev = alloc_netdev(sizeof(moal_private), name, woal_virt_if_setup); |
| #endif |
| if (!dev) { |
| PRINTM(MFATAL, "Init virtual ethernet device failed\n"); |
| goto error; |
| } |
| /* Allocate device name */ |
| if ((dev_alloc_name(dev, name) < 0)) { |
| PRINTM(MERROR, "Could not allocate device name\n"); |
| goto error; |
| } |
| |
| priv = (moal_private *)netdev_priv(dev); |
| /* Save the priv to handle */ |
| handle->priv[bss_index] = priv; |
| |
| /* Use the same handle structure */ |
| priv->phandle = handle; |
| priv->netdev = dev; |
| priv->bss_index = bss_index; |
| priv->bss_type = bss_type; |
| priv->bss_role = MLAN_BSS_ROLE_STA; |
| |
| INIT_LIST_HEAD(&priv->tcp_sess_queue); |
| spin_lock_init(&priv->tcp_sess_lock); |
| |
| INIT_LIST_HEAD(&priv->tx_stat_queue); |
| spin_lock_init(&priv->tx_stat_lock); |
| spin_lock_init(&priv->connect_lock); |
| |
| #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) |
| SET_MODULE_OWNER(dev); |
| #endif |
| |
| PRINTM(MCMND, "Alloc virtual interface%s\n", dev->name); |
| |
| LEAVE(); |
| return priv; |
| error: |
| if (dev) |
| free_netdev(dev); |
| LEAVE(); |
| return NULL; |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) |
| /** |
| * @brief Request the driver to add a virtual interface |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param name Virtual interface name |
| * @param name_assign_type Interface name assignment type |
| * @param type Virtual interface type |
| * @param flags Flags for the virtual interface |
| * @param params A pointer to vif_params structure |
| * @param new_dev new net_device to return |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| static int |
| woal_cfg80211_add_virt_if(struct wiphy *wiphy, const char *name, |
| unsigned char name_assign_type, |
| enum nl80211_iftype type, u32 *flags, |
| struct vif_params *params, |
| struct net_device **new_dev) |
| #else |
| /** |
| * @brief Request the driver to add a virtual interface |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param name Virtual interface name |
| * @param type Virtual interface type |
| * @param flags Flags for the virtual interface |
| * @param params A pointer to vif_params structure |
| * @param new_dev new net_device to return |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_add_virt_if(struct wiphy *wiphy, |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) |
| const |
| #endif |
| char *name, |
| enum nl80211_iftype type, u32 *flags, |
| struct vif_params *params, |
| struct net_device **new_dev) |
| #endif |
| { |
| int ret = 0; |
| struct net_device *ndev = NULL; |
| moal_private *priv = NULL, *new_priv = NULL; |
| moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); |
| struct wireless_dev *wdev = NULL; |
| moal_private *vir_priv; |
| int i = 0; |
| |
| ENTER(); |
| ASSERT_RTNL(); |
| priv = woal_get_vir_priv_bss_type(handle, MLAN_BSS_TYPE_WIFIDIRECT); |
| if (priv && priv->bss_role == MLAN_BSS_ROLE_UAP && |
| priv->bss_started == MTRUE) { |
| if (handle->pref_mac) |
| handle = (moal_handle *)handle->pref_mac; |
| } |
| priv = (moal_private *)woal_get_priv_bss_type(handle, |
| MLAN_BSS_TYPE_WIFIDIRECT); |
| if (!priv || !priv->phandle) { |
| PRINTM(MERROR, "priv or handle is NULL\n"); |
| LEAVE(); |
| return -EFAULT; |
| } |
| if (priv->phandle->drv_mode.intf_num == priv->phandle->priv_num) { |
| PRINTM(MERROR, "max virtual interface limit reached\n"); |
| for (i = 0; i < priv->phandle->priv_num; i++) { |
| vir_priv = priv->phandle->priv[i]; |
| if (vir_priv->bss_virtual) { |
| woal_cfg80211_del_virt_if(wiphy, |
| vir_priv->netdev); |
| break; |
| } |
| } |
| if (priv->phandle->drv_mode.intf_num == priv->phandle->priv_num) { |
| LEAVE(); |
| return -ENOMEM; |
| } |
| } |
| PRINTM(MMSG, "Add virtual interface %s\n", name); |
| if ((type != NL80211_IFTYPE_P2P_CLIENT) && |
| (type != NL80211_IFTYPE_P2P_GO)) { |
| PRINTM(MERROR, "Invalid iftype: %d\n", type); |
| LEAVE(); |
| return -EINVAL; |
| } |
| |
| handle = priv->phandle; |
| /* Cancel previous scan req */ |
| woal_cancel_scan(priv, MOAL_IOCTL_WAIT); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) |
| new_priv = woal_alloc_virt_interface(handle, handle->priv_num, |
| name_assign_type, |
| MLAN_BSS_TYPE_WIFIDIRECT, name); |
| #else |
| new_priv = woal_alloc_virt_interface(handle, handle->priv_num, |
| MLAN_BSS_TYPE_WIFIDIRECT, name); |
| #endif |
| if (!new_priv) { |
| PRINTM(MERROR, "Add virtual interface fail."); |
| LEAVE(); |
| return -EFAULT; |
| } |
| handle->priv_num++; |
| |
| wdev = (struct wireless_dev *)&new_priv->w_dev; |
| memset(wdev, 0, sizeof(struct wireless_dev)); |
| ndev = new_priv->netdev; |
| SET_NETDEV_DEV(ndev, wiphy_dev(wiphy)); |
| ndev->ieee80211_ptr = wdev; |
| wdev->iftype = type; |
| wdev->wiphy = wiphy; |
| new_priv->wdev = wdev; |
| new_priv->bss_virtual = MTRUE; |
| new_priv->pa_netdev = priv->netdev; |
| |
| /* Create workqueue for main process */ |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) |
| new_priv->mclist_workqueue = |
| alloc_workqueue("MCLIST_WORK_QUEUE", |
| WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1); |
| #else |
| new_priv->mclist_workqueue = create_workqueue("MCLIST_WORK_QUEUE"); |
| #endif |
| if (!new_priv->mclist_workqueue) { |
| PRINTM(MERROR, "cannot alloc mclist workqueue \n"); |
| return -EFAULT; |
| } |
| MLAN_INIT_WORK(&new_priv->mclist_work, woal_mclist_work_queue); |
| |
| woal_init_sta_dev(ndev, new_priv); |
| |
| /* Initialize priv structure */ |
| woal_init_priv(new_priv, MOAL_IOCTL_WAIT); |
| /** Init to GO/CLIENT mode */ |
| if (type == NL80211_IFTYPE_P2P_CLIENT) |
| woal_cfg80211_init_p2p_client(new_priv); |
| else if (type == NL80211_IFTYPE_P2P_GO) |
| woal_cfg80211_init_p2p_go(new_priv); |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) |
| ret = cfg80211_register_netdevice(ndev); |
| #else |
| ret = register_netdevice(ndev); |
| #endif |
| if (ret) { |
| handle->priv[new_priv->bss_index] = NULL; |
| handle->priv_num--; |
| if (ndev->reg_state == NETREG_REGISTERED) { |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) |
| cfg80211_unregister_netdevice(ndev); |
| #else |
| unregister_netdevice(ndev); |
| #endif |
| free_netdev(ndev); |
| ndev = NULL; |
| } |
| PRINTM(MFATAL, "register net_device failed, ret=%d\n", ret); |
| goto done; |
| } |
| netif_carrier_off(ndev); |
| woal_stop_queue(ndev); |
| if (new_dev) |
| *new_dev = ndev; |
| #ifdef CONFIG_PROC_FS |
| woal_create_proc_entry(new_priv); |
| woal_debug_entry(new_priv); |
| #endif /* CONFIG_PROC_FS */ |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Notify mlan BSS will be removed. |
| * |
| * @param priv A pointer to moal_private structure |
| * |
| * @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success, |
| * otherwise fail |
| */ |
| static mlan_status |
| woal_bss_remove(moal_private *priv) |
| { |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_bss *bss = NULL; |
| mlan_status status; |
| |
| ENTER(); |
| |
| /* Allocate an IOCTL request buffer */ |
| req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss)); |
| if (req == NULL) { |
| status = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| |
| /* Fill request buffer */ |
| bss = (mlan_ds_bss *)req->pbuf; |
| bss->sub_command = MLAN_OID_BSS_REMOVE; |
| req->req_id = MLAN_IOCTL_BSS; |
| req->action = MLAN_ACT_SET; |
| /* Send IOCTL request to MLAN */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return status; |
| } |
| |
| /** |
| * @brief This function removes an virtual interface. |
| * |
| * @param wiphy A pointer to the wiphy structure |
| * @param dev A pointer to the net_device structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_del_virt_if(struct wiphy *wiphy, struct net_device *dev) |
| { |
| int ret = 0; |
| int i = 0; |
| moal_private *priv = NULL; |
| moal_private *vir_priv = NULL; |
| moal_private *remain_priv = NULL; |
| moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); |
| t_u8 find_bss = MFALSE; |
| |
| for (i = 0; i < handle->priv_num; i++) { |
| vir_priv = handle->priv[i]; |
| if (vir_priv) { |
| if (vir_priv->netdev == dev) { |
| find_bss = MTRUE; |
| PRINTM(MMSG, |
| "Del virtual interface %s, index=%d\n", |
| dev->name, i); |
| break; |
| } |
| } |
| } |
| if (!find_bss) { |
| /* Switch to the other MAC */ |
| if (handle->pref_mac) |
| handle = (moal_handle *)handle->pref_mac; |
| for (i = 0; i < handle->priv_num; i++) { |
| vir_priv = handle->priv[i]; |
| if (vir_priv) { |
| if (vir_priv->netdev == dev) { |
| find_bss = MTRUE; |
| PRINTM(MMSG, |
| "Del virtual interface %s, index=%d\n", |
| dev->name, i); |
| break; |
| } |
| } |
| } |
| } |
| |
| priv = (moal_private *)woal_get_priv_bss_type(handle, |
| MLAN_BSS_TYPE_WIFIDIRECT); |
| if (!priv) |
| return ret; |
| if (vir_priv && vir_priv->netdev == dev) { |
| woal_stop_queue(dev); |
| netif_carrier_off(dev); |
| netif_device_detach(dev); |
| if (handle->is_remain_timer_set) { |
| woal_cancel_timer(&handle->remain_timer); |
| woal_remain_timer_func(handle); |
| } |
| |
| /*** cancel pending scan */ |
| woal_cancel_scan(vir_priv, MOAL_IOCTL_WAIT); |
| |
| woal_flush_tx_stat_queue(vir_priv); |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) |
| /* cancel previous remain on channel to avoid firmware hang */ |
| if (priv->phandle->remain_on_channel) { |
| t_u8 channel_status; |
| remain_priv = |
| priv->phandle->priv[priv->phandle-> |
| remain_bss_index]; |
| if (remain_priv) { |
| if (woal_cfg80211_remain_on_channel_cfg |
| (remain_priv, MOAL_IOCTL_WAIT, MTRUE, |
| &channel_status, NULL, 0, 0)) |
| PRINTM(MERROR, |
| "del_virt_if: Fail to cancel remain on channel\n"); |
| |
| if (priv->phandle->cookie) { |
| cfg80211_remain_on_channel_expired( |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) |
| remain_priv-> |
| netdev, |
| #else |
| remain_priv-> |
| wdev, |
| #endif |
| priv-> |
| phandle-> |
| cookie, |
| &priv-> |
| phandle-> |
| chan, |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) |
| priv-> |
| phandle-> |
| channel_type, |
| #endif |
| GFP_ATOMIC); |
| priv->phandle->cookie = 0; |
| } |
| priv->phandle->remain_on_channel = MFALSE; |
| } |
| } |
| #endif |
| if (vir_priv->mclist_workqueue) { |
| flush_workqueue(vir_priv->mclist_workqueue); |
| destroy_workqueue(vir_priv->mclist_workqueue); |
| vir_priv->mclist_workqueue = NULL; |
| } |
| woal_clear_all_mgmt_ies(vir_priv, MOAL_IOCTL_WAIT); |
| woal_cfg80211_deinit_p2p(vir_priv); |
| woal_bss_remove(vir_priv); |
| #ifdef CONFIG_PROC_FS |
| /* Remove proc debug */ |
| woal_debug_remove(vir_priv); |
| woal_proc_remove(vir_priv); |
| #endif /* CONFIG_PROC_FS */ |
| /* Last reference is our one */ |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 37) |
| PRINTM(MINFO, "refcnt = %d\n", atomic_read(&dev->refcnt)); |
| #else |
| PRINTM(MINFO, "refcnt = %d\n", netdev_refcnt_read(dev)); |
| #endif |
| PRINTM(MINFO, "netdev_finish_unregister: %s\n", dev->name); |
| /* Clear the priv in handle */ |
| vir_priv->phandle->priv[vir_priv->bss_index] = NULL; |
| priv->phandle->priv_num--; |
| if (dev->reg_state == NETREG_REGISTERED) |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) |
| cfg80211_unregister_netdevice(dev); |
| #else |
| unregister_netdevice(dev); |
| #endif |
| } |
| return ret; |
| } |
| #endif |
| #endif |
| |
| #if defined(WIFI_DIRECT_SUPPORT) |
| /** |
| * @brief This function removes an virtual interface. |
| * |
| * @param handle A pointer to the moal_handle structure |
| * |
| * @return N/A |
| */ |
| void |
| woal_remove_virtual_interface(moal_handle *handle) |
| { |
| #ifdef WIFI_DIRECT_SUPPORT |
| moal_private *priv = NULL; |
| int vir_intf = 0; |
| int i = 0; |
| #endif |
| ENTER(); |
| rtnl_lock(); |
| #ifdef WIFI_DIRECT_SUPPORT |
| for (i = 0; i < handle->priv_num; i++) { |
| priv = handle->priv[i]; |
| if (priv) { |
| if (priv->bss_virtual) { |
| PRINTM(MCMND, "Remove virtual interface %s\n", |
| priv->netdev->name); |
| #ifdef CONFIG_PROC_FS |
| /* Remove proc debug */ |
| woal_debug_remove(priv); |
| woal_proc_remove(priv); |
| #endif /* CONFIG_PROC_FS */ |
| netif_device_detach(priv->netdev); |
| if (priv->netdev->reg_state == |
| NETREG_REGISTERED) |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) |
| cfg80211_unregister_netdevice(priv-> |
| netdev); |
| #else |
| unregister_netdevice(priv->netdev); |
| #endif |
| handle->priv[i] = NULL; |
| vir_intf++; |
| } |
| } |
| } |
| #endif |
| rtnl_unlock(); |
| #ifdef WIFI_DIRECT_SUPPORT |
| handle->priv_num -= vir_intf; |
| #endif |
| LEAVE(); |
| } |
| #endif |
| |
| /** |
| * @brief This function check if uap interface is ready |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param name Virtual interface name |
| * |
| * @return MTRUE/MFALSE; |
| */ |
| static t_u8 |
| woal_uap_interface_ready(struct wiphy *wiphy, char *name, |
| struct net_device **new_dev) |
| { |
| moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); |
| moal_private *priv = NULL; |
| int i; |
| |
| for (i = 0; i < handle->priv_num; i++) { |
| priv = handle->priv[i]; |
| if (priv && (priv->bss_type == MLAN_BSS_TYPE_UAP) && |
| !strcmp(priv->netdev->name, name)) { |
| priv->wdev->iftype = NL80211_IFTYPE_AP; |
| *new_dev = priv->netdev; |
| break; |
| } |
| } |
| if (priv && *new_dev) |
| return MTRUE; |
| else |
| return MFALSE; |
| } |
| |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) |
| #if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) |
| /** |
| * @brief Request the driver to add a virtual interface |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param name Virtual interface name |
| * @param type Virtual interface type |
| * @param flags Flags for the virtual interface |
| * @param params A pointer to vif_params structure |
| * |
| * @return A pointer to net_device -- success, otherwise null |
| */ |
| struct net_device * |
| woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, |
| char *name, |
| enum nl80211_iftype type, |
| u32 *flags, struct vif_params *params) |
| #else |
| /** |
| * @brief Request the driver to add a virtual interface |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param name Virtual interface name |
| * @param type Virtual interface type |
| * @param flags Flags for the virtual interface |
| * @param params A pointer to vif_params structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, char *name, |
| enum nl80211_iftype type, u32 *flags, |
| struct vif_params *params) |
| #endif |
| #else |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 1, 0) |
| /** |
| * @brief Request the driver to add a virtual interface |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param name Virtual interface name |
| * @param type Virtual interface type |
| * @param flags Flags for the virtual interface |
| * @param params A pointer to vif_params structure |
| * |
| * @return A pointer to wireless_dev -- success, otherwise null |
| */ |
| struct wireless_dev * |
| woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) |
| const |
| #endif |
| char *name, |
| enum nl80211_iftype type, |
| u32 *flags, struct vif_params *params) |
| #else |
| /** |
| * @brief Request the driver to add a virtual interface |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param name Virtual interface name |
| * @param name_assign_type Interface name assignment type |
| * @param type Virtual interface type |
| * @param flags Flags for the virtual interface |
| * @param params A pointer to vif_params structure |
| * |
| * @return A pointer to wireless_dev -- success, otherwise null |
| */ |
| struct wireless_dev * |
| woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, const char *name, |
| unsigned char name_assign_type, |
| enum nl80211_iftype type, |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0) |
| u32 *flags, |
| #endif |
| struct vif_params *params) |
| #endif |
| #endif |
| { |
| struct net_device *ndev = NULL; |
| int ret = 0; |
| #if defined(WIFI_DIRECT_SUPPORT) |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) |
| u32 *flags = ¶ms->flags; |
| #endif |
| #endif |
| |
| ENTER(); |
| PRINTM(MIOCTL, "add virtual intf: %d name: %s\n", type, name); |
| switch (type) { |
| #ifdef WIFI_DIRECT_SUPPORT |
| #if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION |
| case NL80211_IFTYPE_P2P_CLIENT: |
| case NL80211_IFTYPE_P2P_GO: |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) |
| ret = woal_cfg80211_add_virt_if(wiphy, name, name_assign_type, |
| type, flags, params, &ndev); |
| #else |
| ret = woal_cfg80211_add_virt_if(wiphy, name, type, flags, |
| params, &ndev); |
| #endif |
| break; |
| #endif |
| #endif |
| case NL80211_IFTYPE_AP: |
| if (!woal_uap_interface_ready(wiphy, (char *)name, &ndev)) { |
| PRINTM(MMSG, |
| "Not support dynamically create %s UAP interface\n", |
| name); |
| ret = -EFAULT; |
| } |
| break; |
| default: |
| PRINTM(MWARN, "Not supported if type: %d\n", type); |
| ret = -EFAULT; |
| break; |
| } |
| LEAVE(); |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) |
| #if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37) |
| if (ret) |
| return ERR_PTR(ret); |
| else |
| return ndev; |
| #else |
| return ret; |
| #endif |
| #else |
| if (ret) |
| return ERR_PTR(ret); |
| else |
| return ndev->ieee80211_ptr; |
| #endif |
| } |
| |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) |
| /** |
| * @brief Request the driver to del a virtual interface |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev The pointer to net_device |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev) |
| #else |
| /** |
| * @brief Request the driver to del a virtual interface |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param wdev The pointer to wireless_dev |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) |
| #endif |
| { |
| int ret = 0; |
| |
| moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy); |
| int i; |
| moal_private *vir_priv = NULL; |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) |
| struct net_device *dev = wdev->netdev; |
| #endif |
| |
| ENTER(); |
| |
| PRINTM(MIOCTL, "del virtual intf %s\n", dev->name); |
| ASSERT_RTNL(); |
| |
| if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) { |
| for (i = 0; i < handle->priv_num; i++) { |
| vir_priv = handle->priv[i]; |
| if (vir_priv) { |
| if (vir_priv->netdev == dev) { |
| PRINTM(MMSG, |
| "Del virtual interface %s, index=%d\n", |
| dev->name, i); |
| break; |
| } |
| } |
| } |
| if (vir_priv && vir_priv->bss_type == MLAN_BSS_TYPE_UAP) { |
| woal_cfg80211_del_beacon(wiphy, dev); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) |
| vir_priv->wdev->beacon_interval = 0; |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) |
| memset(&vir_priv->wdev->chandef, 0, |
| sizeof(vir_priv->wdev->chandef)); |
| #endif |
| #endif |
| vir_priv->wdev->ssid_len = 0; |
| PRINTM(MMSG, "Skip del UAP virtual interface %s", |
| dev->name); |
| } |
| LEAVE(); |
| return ret; |
| } |
| #ifdef WIFI_DIRECT_SUPPORT |
| #if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION |
| ret = woal_cfg80211_del_virt_if(wiphy, dev); |
| #endif |
| #endif |
| LEAVE(); |
| return ret; |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| /** |
| * @brief initialize AP or GO parameters |
| |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param params A pointer to cfg80211_ap_settings structure |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, |
| struct cfg80211_ap_settings *params) |
| #else |
| /** |
| * @brief initialize AP or GO parameters |
| |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param params A pointer to beacon_parameters structure |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_add_beacon(struct wiphy *wiphy, struct net_device *dev, |
| struct beacon_parameters *params) |
| #endif |
| { |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| int ret = 0; |
| |
| t_u8 wait_option = MOAL_IOCTL_WAIT_TIMEOUT; |
| |
| ENTER(); |
| |
| PRINTM(MMSG, "wlan: Starting AP\n"); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39) |
| /* cancel previous remain on channel to avoid firmware hang */ |
| if (priv->phandle->remain_on_channel) { |
| t_u8 channel_status; |
| moal_private *remain_priv; |
| remain_priv = |
| priv->phandle->priv[priv->phandle->remain_bss_index]; |
| if (remain_priv) { |
| PRINTM(MCMND, |
| "Cancel Remain on Channel before Starting AP\n"); |
| if (woal_cfg80211_remain_on_channel_cfg |
| (remain_priv, MOAL_IOCTL_WAIT, MTRUE, |
| &channel_status, NULL, 0, 0)) |
| PRINTM(MERROR, |
| "add beacon: Fail to cancel remain on channel\n"); |
| if (priv->phandle->cookie) { |
| cfg80211_remain_on_channel_expired( |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0) |
| remain_priv-> |
| netdev, |
| #else |
| remain_priv-> |
| wdev, |
| #endif |
| priv-> |
| phandle-> |
| cookie, |
| &priv-> |
| phandle-> |
| chan, |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0) |
| priv-> |
| phandle-> |
| channel_type, |
| #endif |
| GFP_ATOMIC); |
| priv->phandle->cookie = 0; |
| } |
| priv->phandle->remain_on_channel = MFALSE; |
| } |
| } |
| #endif |
| |
| #ifdef STA_CFG80211 |
| /*** cancel pending scan */ |
| woal_cancel_scan(priv, MOAL_IOCTL_WAIT); |
| #endif |
| |
| if (!params) { |
| LEAVE(); |
| return -EFAULT; |
| } |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| priv->channel = |
| ieee80211_frequency_to_channel(params->chandef.chan-> |
| center_freq); |
| #else |
| priv->channel = |
| ieee80211_frequency_to_channel(params->channel->center_freq); |
| #endif |
| #endif |
| /* bss config */ |
| if (MLAN_STATUS_SUCCESS != woal_cfg80211_beacon_config(priv, params)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| /* set mgmt frame ies */ |
| ret = woal_cfg80211_mgmt_frame_ie(priv, |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 2, 0) |
| params->tail, params->tail_len, NULL, |
| 0, NULL, 0, NULL, 0, MGMT_MASK_BEACON |
| #else |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| params->beacon.tail, |
| params->beacon.tail_len, |
| params->beacon.proberesp_ies, |
| params->beacon.proberesp_ies_len, |
| params->beacon.assocresp_ies, |
| params->beacon.assocresp_ies_len, |
| #else |
| params->tail, params->tail_len, |
| params->proberesp_ies, |
| params->proberesp_ies_len, |
| params->assocresp_ies, |
| params->assocresp_ies_len, |
| #endif |
| NULL, 0, |
| MGMT_MASK_BEACON | |
| MGMT_MASK_PROBE_RESP | |
| MGMT_MASK_ASSOC_RESP |
| #endif |
| , MOAL_IOCTL_WAIT); |
| if (ret) |
| goto done; |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| if (params->beacon.beacon_ies && params->beacon.beacon_ies_len) { |
| ret = woal_cfg80211_mgmt_frame_ie(priv, |
| params->beacon.beacon_ies, |
| params->beacon.beacon_ies_len, |
| NULL, 0, NULL, 0, NULL, 0, |
| MGMT_MASK_BEACON_WPS_P2P, |
| MOAL_IOCTL_WAIT); |
| if (ret) { |
| PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); |
| goto done; |
| } |
| } |
| #else |
| if (params->beacon_ies && params->beacon_ies_len) { |
| ret = woal_cfg80211_mgmt_frame_ie(priv, params->beacon_ies, |
| params->beacon_ies_len, NULL, |
| 0, NULL, 0, NULL, 0, |
| MGMT_MASK_BEACON_WPS_P2P, |
| MOAL_IOCTL_WAIT); |
| if (ret) { |
| PRINTM(MERROR, "Failed to set beacon wps/p2p ie\n"); |
| goto done; |
| } |
| } |
| #endif |
| #endif |
| priv->uap_host_based = MTRUE; |
| |
| /* if the bss is stopped, then start it */ |
| if (priv->bss_started == MFALSE) { |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) |
| if (moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) |
| wait_option = MOAL_NO_WAIT; |
| #endif |
| if (MLAN_STATUS_SUCCESS != |
| woal_uap_bss_ctrl(priv, wait_option, UAP_BSS_START)) { |
| priv->uap_host_based = MFALSE; |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| PRINTM(MMSG, "wlan: AP started\n"); |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) |
| /** |
| * @brief set AP or GO parameter |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param params A pointer to cfg80211_beacon_data structure |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, |
| struct cfg80211_beacon_data *params) |
| #else |
| /** |
| * @brief set AP or GO parameter |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param params A pointer to beacon_parameters structure |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_set_beacon(struct wiphy *wiphy, struct net_device *dev, |
| struct beacon_parameters *params) |
| #endif |
| { |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| int ret = 0; |
| |
| ENTER(); |
| |
| PRINTM(MIOCTL, "set beacon\n"); |
| if (params != NULL) { |
| #if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 2, 0) |
| if (params->tail && params->tail_len) { |
| ret = woal_cfg80211_mgmt_frame_ie(priv, params->tail, |
| params->tail_len, |
| NULL, 0, NULL, 0, |
| NULL, 0, |
| MGMT_MASK_BEACON, |
| MOAL_IOCTL_WAIT); |
| if (ret) |
| goto done; |
| } |
| #else |
| t_u16 mask = 0; |
| if (params->tail && params->tail_len) |
| mask |= MGMT_MASK_BEACON; |
| if (params->proberesp_ies && params->proberesp_ies_len) |
| mask |= MGMT_MASK_PROBE_RESP; |
| if (params->assocresp_ies && params->assocresp_ies_len) |
| mask |= MGMT_MASK_ASSOC_RESP; |
| PRINTM(MIOCTL, "Set beacon: mask=0x%x\n", mask); |
| if (mask) { |
| ret = woal_cfg80211_mgmt_frame_ie(priv, params->tail, |
| params->tail_len, |
| params->proberesp_ies, |
| params-> |
| proberesp_ies_len, |
| params->assocresp_ies, |
| params-> |
| assocresp_ies_len, |
| NULL, 0, mask, |
| MOAL_IOCTL_WAIT); |
| if (ret) |
| goto done; |
| } |
| if (params->beacon_ies && params->beacon_ies_len) { |
| ret = woal_cfg80211_mgmt_frame_ie(priv, |
| params->beacon_ies, |
| params-> |
| beacon_ies_len, NULL, |
| 0, NULL, 0, NULL, 0, |
| MGMT_MASK_BEACON_WPS_P2P, |
| MOAL_IOCTL_WAIT); |
| if (ret) { |
| PRINTM(MERROR, |
| "Failed to set beacon wps/p2p ie\n"); |
| goto done; |
| } |
| } |
| #endif |
| } |
| |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief reset AP or GO parameters |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev) |
| { |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| int ret = 0; |
| #ifdef STA_SUPPORT |
| moal_private *pmpriv = NULL; |
| #endif |
| |
| ENTER(); |
| |
| if (priv->phandle->driver_status) { |
| PRINTM(MERROR, |
| "Block woal_cfg80211_del_beacon in abnormal driver state\n"); |
| LEAVE(); |
| return ret; |
| } |
| priv->uap_host_based = MFALSE; |
| PRINTM(MMSG, "wlan: Stoping AP\n"); |
| #ifdef STA_SUPPORT |
| woal_cancel_scan(priv, MOAL_IOCTL_WAIT); |
| #endif |
| memset(priv->dscp_map, 0xFF, sizeof(priv->dscp_map)); |
| woal_deauth_all_station(priv); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) |
| if (moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) |
| woal_cancel_cac_block(priv); |
| #endif |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) |
| memset(&priv->chan, 0, sizeof(struct cfg80211_chan_def)); |
| if (priv->phandle->is_cac_timer_set && |
| priv->bss_index == priv->phandle->cac_bss_index) { |
| woal_cancel_timer(&priv->phandle->cac_timer); |
| priv->phandle->is_cac_timer_set = MFALSE; |
| /* Make sure Chan Report is cancelled */ |
| woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) |
| cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, |
| NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); |
| #else |
| cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, |
| GFP_KERNEL); |
| #endif |
| memset(&priv->phandle->dfs_channel, 0, |
| sizeof(struct cfg80211_chan_def)); |
| priv->phandle->cac_bss_index = 0xff; |
| } |
| if (priv->csa_workqueue) |
| flush_workqueue(priv->csa_workqueue); |
| #endif |
| /* if the bss is still running, then stop it */ |
| if (priv->bss_started == MTRUE) { |
| if ((int)MLAN_STATUS_FAILURE == |
| woal_uap_bss_ctrl(priv, MOAL_NO_WAIT, UAP_BSS_STOP)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| if ((int)MLAN_STATUS_FAILURE == |
| woal_uap_bss_ctrl(priv, MOAL_NO_WAIT, UAP_BSS_RESET)) { |
| ret = -EFAULT; |
| goto done; |
| } |
| /* Set WLAN MAC addresses */ |
| if (MLAN_STATUS_FAILURE == |
| woal_request_set_mac_address(priv, MOAL_NO_WAIT)) { |
| PRINTM(MERROR, "Set MAC address failed\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| } |
| woal_clear_all_mgmt_ies(priv, MOAL_NO_WAIT); |
| #ifdef STA_SUPPORT |
| if (!woal_is_any_interface_active(priv->phandle)) { |
| pmpriv = woal_get_priv((moal_handle *)priv->phandle, |
| MLAN_BSS_ROLE_STA); |
| if (pmpriv) |
| woal_set_scan_time(pmpriv, ACTIVE_SCAN_CHAN_TIME, |
| PASSIVE_SCAN_CHAN_TIME, |
| SPECIFIC_SCAN_CHAN_TIME); |
| } |
| #endif |
| |
| priv->cipher = 0; |
| memset(priv->uap_wep_key, 0, sizeof(priv->uap_wep_key)); |
| priv->channel = 0; |
| PRINTM(MMSG, "wlan: AP stopped\n"); |
| done: |
| LEAVE(); |
| return ret; |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) |
| /** |
| * @brief change BSS |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param params A pointer to bss_parameters structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev, |
| struct bss_parameters *params) |
| { |
| int ret = 0; |
| t_u8 change = MFALSE; |
| mlan_uap_bss_param *sys_config = NULL; |
| u8 bss_started = MFALSE; |
| t_u8 pkt_forward_ctl; |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| |
| ENTER(); |
| PRINTM(MIOCTL, "isolate=%d\n", params->ap_isolate); |
| |
| sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); |
| if (!sys_config) { |
| PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); |
| LEAVE(); |
| return -EFAULT; |
| } |
| |
| if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, |
| MOAL_IOCTL_WAIT, |
| sys_config)) { |
| PRINTM(MERROR, "Error getting AP confiruration\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| pkt_forward_ctl = sys_config->pkt_forward_ctl; |
| if (params->ap_isolate) { |
| /** disable packet forwarding */ |
| sys_config->pkt_forward_ctl |= PKT_FWD_INTRA_BCAST; |
| sys_config->pkt_forward_ctl |= PKT_FWD_INTRA_UCAST; |
| } else { |
| sys_config->pkt_forward_ctl &= ~PKT_FWD_INTRA_BCAST; |
| sys_config->pkt_forward_ctl &= ~PKT_FWD_INTRA_UCAST; |
| } |
| if (pkt_forward_ctl != sys_config->pkt_forward_ctl) { |
| change = MTRUE; |
| PRINTM(MIOCTL, "ap_isolate=%xd\n", params->ap_isolate); |
| } |
| if (change) { |
| if (priv->bss_started == MTRUE) { |
| bss_started = MTRUE; |
| woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); |
| } |
| if (params->use_short_preamble == 1) |
| sys_config->preamble_type = 1; |
| else if (params->use_short_preamble == 0) |
| sys_config->preamble_type = 2; |
| else |
| sys_config->preamble_type = 0; |
| if (MLAN_STATUS_SUCCESS == |
| woal_set_get_sys_config(priv, MLAN_ACT_SET, MOAL_IOCTL_WAIT, |
| sys_config)) |
| ret = 0; |
| if (bss_started) |
| woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT_TIMEOUT, |
| UAP_BSS_START); |
| } |
| done: |
| kfree(sys_config); |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) |
| /** |
| * @brief del station |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param param A pointer tostation_del_parameters structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| #else |
| /** |
| * @brief del station |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param mac_addr A pointer to station mac address |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| #endif |
| int |
| woal_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev, |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) |
| struct station_del_parameters *param) |
| #else |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) |
| const u8 *mac_addr) |
| #else |
| u8 *mac_addr) |
| #endif |
| #endif |
| { |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) |
| const u8 *mac_addr = NULL; |
| #endif |
| u16 reason_code = REASON_CODE_DEAUTH_LEAVING; |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| ENTER(); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) |
| if (priv->phandle->is_cac_timer_set && |
| priv->bss_index == priv->phandle->cac_bss_index) { |
| woal_cancel_timer(&priv->phandle->cac_timer); |
| priv->phandle->is_cac_timer_set = MFALSE; |
| /* Make sure Chan Report is cancelled */ |
| woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) |
| cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, |
| NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); |
| #else |
| cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, |
| GFP_KERNEL); |
| #endif |
| memset(&priv->phandle->dfs_channel, 0, |
| sizeof(struct cfg80211_chan_def)); |
| priv->phandle->cac_bss_index = 0xff; |
| } |
| #endif |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) |
| if (moal_extflg_isset(priv->phandle, EXT_DFS_OFFLOAD)) |
| woal_cancel_cac_block(priv); |
| #endif |
| |
| if (priv->media_connected == MFALSE) { |
| PRINTM(MINFO, "cfg80211: Media not connected!\n"); |
| LEAVE(); |
| return 0; |
| } |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) |
| if (param) { |
| mac_addr = param->mac; |
| reason_code = param->reason_code; |
| } |
| #endif |
| /** we will not send deauth to p2p interface, it might cause WPS failure |
| */ |
| if (mac_addr) { |
| PRINTM(MMSG, "wlan: deauth station " MACSTR "\n", |
| MAC2STR(mac_addr)); |
| #ifdef WIFI_DIRECT_SUPPORT |
| if (!priv->phandle->is_go_timer_set) |
| #endif |
| woal_deauth_assoc_station(priv, (u8 *)mac_addr, |
| reason_code); |
| } else { |
| PRINTM(MIOCTL, "del all station\n"); |
| } |
| LEAVE(); |
| return 0; |
| } |
| |
| /** |
| * @brief Get station info |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param mac A pointer to station mac address |
| * @param stainfo A pointer to station_info structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_uap_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev, |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) |
| const u8 *mac, |
| #else |
| u8 *mac, |
| #endif |
| struct station_info *stainfo) |
| { |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| int ret = -EFAULT; |
| int i = 0; |
| mlan_ds_get_info *info = NULL; |
| mlan_ioctl_req *ioctl_req = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| mlan_ds_get_stats stats; |
| |
| ENTER(); |
| if (priv->media_connected == MFALSE) { |
| PRINTM(MINFO, "cfg80211: Media not connected!\n"); |
| LEAVE(); |
| return -ENOENT; |
| } |
| |
| /* Allocate an IOCTL request buffer */ |
| ioctl_req = |
| (mlan_ioctl_req *) |
| woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); |
| if (ioctl_req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| info = (mlan_ds_get_info *)ioctl_req->pbuf; |
| info->sub_command = MLAN_OID_UAP_STA_LIST; |
| ioctl_req->req_id = MLAN_IOCTL_GET_INFO; |
| ioctl_req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) |
| goto done; |
| for (i = 0; i < info->param.sta_list.sta_count; i++) { |
| if (!memcmp(info->param.sta_list.info[i].mac_address, mac, |
| ETH_ALEN)) { |
| PRINTM(MIOCTL, "Get station: " MACSTR " RSSI=%d\n", |
| MAC2STR(mac), |
| (int)info->param.sta_list.info[i].rssi); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) |
| stainfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME) | |
| BIT(NL80211_STA_INFO_RX_BYTES) | |
| BIT(NL80211_STA_INFO_TX_BYTES) | |
| BIT(NL80211_STA_INFO_RX_PACKETS) | |
| BIT(NL80211_STA_INFO_TX_PACKETS) | |
| BIT(NL80211_STA_INFO_SIGNAL); |
| |
| stainfo->rx_bytes = priv->stats.rx_bytes; |
| stainfo->tx_bytes = priv->stats.tx_bytes; |
| stainfo->rx_packets = priv->stats.rx_packets; |
| stainfo->tx_packets = priv->stats.tx_packets; |
| #else |
| stainfo->filled = STATION_INFO_INACTIVE_TIME | |
| STATION_INFO_SIGNAL; |
| #endif |
| stainfo->inactive_time = 0; |
| stainfo->signal = info->param.sta_list.info[i].rssi; |
| ret = 0; |
| break; |
| } |
| } |
| memset(&stats, 0, sizeof(mlan_ds_get_stats)); |
| if (MLAN_STATUS_SUCCESS != |
| woal_get_stats_info(priv, MOAL_IOCTL_WAIT, &stats)) { |
| PRINTM(MERROR, "Error getting stats information\n"); |
| ret = MLAN_STATUS_FAILURE; |
| goto done; |
| } |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) |
| stainfo->filled |= BIT(NL80211_STA_INFO_TX_RETRIES) | |
| BIT(NL80211_STA_INFO_TX_FAILED) | |
| BIT(NL80211_STA_INFO_RX_DROP_MISC); |
| stainfo->tx_failed = stats.failed; |
| stainfo->tx_retries = stats.retry; |
| stainfo->rx_dropped_misc = stats.fcs_error; |
| #endif |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(ioctl_req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief Request the driver to dump the station information |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param idx Station index |
| * @param mac MAC address of the station |
| * @param sinfo A pointer to station_info structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_uap_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *dev, |
| int idx, t_u8 *mac, struct station_info *sinfo) |
| { |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| int ret = -EFAULT; |
| mlan_ds_get_info *info = NULL; |
| mlan_ioctl_req *ioctl_req = NULL; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| t_u32 sec = 0, usec = 0; |
| t_u64 cur_msec = 0; |
| |
| ENTER(); |
| if (priv->media_connected == MFALSE) { |
| PRINTM(MINFO, "cfg80211: Media not connected!\n"); |
| LEAVE(); |
| return -ENOENT; |
| } |
| |
| /* Allocate an IOCTL request buffer */ |
| ioctl_req = |
| (mlan_ioctl_req *) |
| woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_get_info)); |
| if (ioctl_req == NULL) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| info = (mlan_ds_get_info *)ioctl_req->pbuf; |
| info->sub_command = MLAN_OID_UAP_STA_LIST; |
| ioctl_req->req_id = MLAN_IOCTL_GET_INFO; |
| ioctl_req->action = MLAN_ACT_GET; |
| |
| status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) |
| goto done; |
| if (idx >= info->param.sta_list.sta_count) { |
| ret = -EFAULT; |
| goto done; |
| } |
| ret = 0; |
| moal_memcpy_ext(priv->phandle, mac, |
| info->param.sta_list.info[idx].mac_address, ETH_ALEN, |
| ETH_ALEN); |
| PRINTM(MIOCTL, "Dump station: " MACSTR " RSSI=%d\n", MAC2STR(mac), |
| (int)info->param.sta_list.info[idx].rssi); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 0, 0) |
| sinfo->filled = BIT(NL80211_STA_INFO_INACTIVE_TIME) | |
| BIT(NL80211_STA_INFO_SIGNAL); |
| #else |
| sinfo->filled = STATION_INFO_INACTIVE_TIME | STATION_INFO_SIGNAL; |
| #endif |
| if (info->param.sta_list.info[idx].stats.last_rx_in_msec) { |
| moal_get_system_time(priv->phandle, &sec, &usec); |
| cur_msec = (t_u64)sec *1000 + (t_u64)usec / 1000; |
| sinfo->inactive_time = (t_u32)(cur_msec - |
| info->param.sta_list.info[idx]. |
| stats.last_rx_in_msec); |
| PRINTM(MIOCTL, |
| "cur:%llu - [%d].last_rx:%llu = inactive_time:%d\n", |
| cur_msec, idx, |
| info->param.sta_list.info[idx].stats.last_rx_in_msec, |
| sinfo->inactive_time); |
| } else |
| sinfo->inactive_time = 0; |
| sinfo->signal = info->param.sta_list.info[idx].rssi; |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(ioctl_req); |
| LEAVE(); |
| return ret; |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) |
| /** |
| * @brief set mac filter |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param params A pointer to cfg80211_acl_data structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_set_mac_acl(struct wiphy *wiphy, struct net_device *dev, |
| const struct cfg80211_acl_data *params) |
| { |
| int ret = -EFAULT; |
| mlan_uap_bss_param *sys_config = NULL; |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| u8 bss_started = MFALSE; |
| ENTER(); |
| |
| PRINTM(MIOCTL, "Set mac acl, entries=%d, policy=%d\n", |
| params->n_acl_entries, params->acl_policy); |
| sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); |
| if (!sys_config) { |
| PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); |
| LEAVE(); |
| return -EFAULT; |
| } |
| |
| /* Initialize the uap bss values which are uploaded from firmware */ |
| if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET, |
| MOAL_IOCTL_WAIT, |
| sys_config)) { |
| PRINTM(MERROR, "Error getting AP confiruration\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| memset(&sys_config->filter, 0, sizeof(mac_filter)); |
| if (params->n_acl_entries <= MAX_MAC_FILTER_NUM) |
| sys_config->filter.mac_count = params->n_acl_entries; |
| else |
| sys_config->filter.mac_count = MAX_MAC_FILTER_NUM; |
| |
| if (params->acl_policy == NL80211_ACL_POLICY_DENY_UNLESS_LISTED) |
| sys_config->filter.filter_mode = MAC_FILTER_MODE_ALLOW_MAC; |
| else if (params->acl_policy == NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) |
| sys_config->filter.filter_mode = MAC_FILTER_MODE_BLOCK_MAC; |
| moal_memcpy_ext(priv->phandle, sys_config->filter.mac_list, |
| params->mac_addrs, |
| sys_config->filter.mac_count * |
| sizeof(mlan_802_11_mac_addr), |
| sizeof(sys_config->filter.mac_list)); |
| if (priv->bss_started == MTRUE) { |
| bss_started = MTRUE; |
| woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT, UAP_BSS_STOP); |
| } |
| if (MLAN_STATUS_SUCCESS == woal_set_get_sys_config(priv, MLAN_ACT_SET, |
| MOAL_IOCTL_WAIT, |
| sys_config)) |
| ret = 0; |
| done: |
| kfree(sys_config); |
| if (bss_started) |
| woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT_TIMEOUT, UAP_BSS_START); |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 1, 0) |
| /** |
| * @brief Set txq parameters |
| |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param params A pointer to ieee80211_txq_params structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_set_txq_params(struct wiphy *wiphy, struct net_device *dev, |
| struct ieee80211_txq_params *params) |
| { |
| int ret = 0; |
| u8 ac = 0; |
| wmm_parameter_t ap_wmm_para; |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| |
| ENTER(); |
| |
| /* AC_BE: 0, AC_BK:1, AC_VI: 2, AC_VO:3 */ |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) |
| switch (params->ac) { |
| case NL80211_AC_VO: |
| ac = 3; |
| break; |
| case NL80211_AC_VI: |
| ac = 2; |
| break; |
| case NL80211_AC_BK: |
| ac = 1; |
| break; |
| case NL80211_AC_BE: |
| ac = 0; |
| break; |
| default: |
| break; |
| } |
| #else |
| switch (params->queue) { |
| case NL80211_TXQ_Q_VO: |
| ac = 3; |
| break; |
| case NL80211_TXQ_Q_VI: |
| ac = 2; |
| break; |
| case NL80211_TXQ_Q_BK: |
| ac = 1; |
| break; |
| case NL80211_TXQ_Q_BE: |
| ac = 0; |
| break; |
| default: |
| break; |
| } |
| #endif |
| |
| PRINTM(MMSG, "Set AC=%d, txop=%d cwmin=%d, cwmax=%d aifs=%d\n", ac, |
| params->txop, params->cwmin, params->cwmax, params->aifs); |
| |
| memset(&ap_wmm_para, 0, sizeof(wmm_parameter_t)); |
| |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_get_ap_wmm_para(priv, MLAN_ACT_GET, &ap_wmm_para)) { |
| PRINTM(MERROR, "wlan: We don't support AP WMM parameter\n"); |
| LEAVE(); |
| return ret; |
| } |
| ap_wmm_para.ac_params[ac].aci_aifsn.aifsn = params->aifs; |
| ap_wmm_para.ac_params[ac].ecw.ecw_max = ilog2(params->cwmax + 1); |
| ap_wmm_para.ac_params[ac].ecw.ecw_min = ilog2(params->cwmin + 1); |
| ap_wmm_para.ac_params[ac].tx_op_limit = params->txop; |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_get_ap_wmm_para(priv, MLAN_ACT_SET, &ap_wmm_para)) { |
| PRINTM(MERROR, "wlan: Fail to set AP WMM parameter\n"); |
| ret = -EFAULT; |
| } |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) |
| /** |
| * @brief cac timer call back function. |
| * |
| * @param context a pointer to moal_handle |
| * |
| * @return N/A |
| */ |
| void |
| woal_cac_timer_func(void *context) |
| { |
| moal_handle *handle = (moal_handle *)context; |
| moal_private *priv = handle->priv[handle->cac_bss_index]; |
| |
| PRINTM(MEVENT, "cac_timer fired.\n"); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) |
| cfg80211_cac_event(priv->netdev, &handle->dfs_channel, |
| NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); |
| #else |
| cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); |
| #endif |
| handle->is_cac_timer_set = MFALSE; |
| memset(&handle->dfs_channel, 0, sizeof(struct cfg80211_chan_def)); |
| handle->cac_bss_index = 0xff; |
| } |
| |
| /** |
| * @brief This function switch AP's channel |
| * 1. clear mgmt IEs 2. stop uAP |
| * 3. set beacon after 4. set new channel |
| * 5. start uAP 6. notify cfg80211 |
| * |
| * @param priv a pointer to moal_private |
| * @param wait_option wait option |
| * |
| * @return N/A |
| */ |
| static void |
| woal_switch_uap_channel(moal_private *priv, t_u8 wait_option) |
| { |
| chan_band_info uap_channel; |
| t_u8 chan2Offset = SEC_CHAN_NONE; |
| ENTER(); |
| woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); |
| if (MLAN_STATUS_SUCCESS != |
| woal_uap_bss_ctrl(priv, wait_option, UAP_BSS_STOP)) { |
| PRINTM(MERROR, "%s: stop uap failed \n", __func__); |
| goto done; |
| } |
| if (woal_cfg80211_set_beacon(priv->wdev->wiphy, priv->netdev, |
| &priv->beacon_after)) { |
| PRINTM(MERROR, "%s: set mgmt ies failed \n", __func__); |
| goto done; |
| } |
| |
| uap_channel.channel = |
| ieee80211_frequency_to_channel(priv->csa_chan.chan-> |
| center_freq); |
| switch (priv->csa_chan.width) { |
| case NL80211_CHAN_WIDTH_5: |
| case NL80211_CHAN_WIDTH_10: |
| case NL80211_CHAN_WIDTH_20_NOHT: |
| uap_channel.bandcfg.chanWidth = CHAN_BW_20MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_20: |
| uap_channel.bandcfg.chanWidth = CHAN_BW_20MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_40: |
| if (priv->csa_chan.center_freq1 < |
| priv->csa_chan.chan->center_freq) |
| chan2Offset = SEC_CHAN_BELOW; |
| else |
| chan2Offset = SEC_CHAN_ABOVE; |
| uap_channel.bandcfg.chanWidth = CHAN_BW_40MHZ; |
| break; |
| case NL80211_CHAN_WIDTH_80: |
| case NL80211_CHAN_WIDTH_80P80: |
| case NL80211_CHAN_WIDTH_160: |
| uap_channel.bandcfg.chanWidth = CHAN_BW_80MHZ; |
| chan2Offset = |
| woal_get_second_channel_offset(priv, |
| uap_channel.channel); |
| break; |
| default: |
| PRINTM(MWARN, "Unknown channel width: %d\n", |
| priv->csa_chan.width); |
| break; |
| } |
| if (priv->csa_chan.chan->band == IEEE80211_BAND_2GHZ) |
| uap_channel.bandcfg.chanBand = BAND_2GHZ; |
| else if (priv->csa_chan.chan->band == IEEE80211_BAND_5GHZ) |
| uap_channel.bandcfg.chanBand = BAND_5GHZ; |
| uap_channel.bandcfg.chan2Offset = chan2Offset; |
| if (MLAN_STATUS_SUCCESS != woal_set_get_ap_channel(priv, MLAN_ACT_SET, |
| wait_option, |
| &uap_channel)) { |
| PRINTM(MERROR, "Fail to set ap channel \n"); |
| goto done; |
| } |
| if (MLAN_STATUS_SUCCESS != |
| woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT_TIMEOUT, UAP_BSS_START)) { |
| PRINTM(MERROR, "%s: start uap failed \n", __func__); |
| goto done; |
| } |
| PRINTM(MMSG, "CSA: old chan %d => new chan %d \n", priv->channel, |
| uap_channel.channel); |
| priv->channel = uap_channel.channel; |
| moal_memcpy_ext(priv->phandle, &priv->chan, &priv->csa_chan, |
| sizeof(struct cfg80211_chan_def), sizeof(priv->chan)); |
| cfg80211_ch_switch_notify(priv->netdev, &priv->chan); |
| if (priv->uap_tx_blocked) { |
| if (!netif_carrier_ok(priv->netdev)) |
| netif_carrier_on(priv->netdev); |
| woal_start_queue(priv->netdev); |
| priv->uap_tx_blocked = MFALSE; |
| } |
| done: |
| LEAVE(); |
| return; |
| } |
| |
| /** |
| * @brief csa work handler |
| * |
| * @param work a pointer to work_struct |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| void |
| woal_csa_work_queue(struct work_struct *work) |
| { |
| struct delayed_work *delayed_work = |
| container_of(work, struct delayed_work, work); |
| moal_private *priv = container_of(delayed_work, moal_private, csa_work); |
| ENTER(); |
| if (priv->bss_started == MTRUE) |
| woal_switch_uap_channel(priv, MOAL_IOCTL_WAIT); |
| LEAVE(); |
| } |
| |
| /* |
| * @brief handle WOAL_EVENT_CANCEL_CHANRPT |
| * |
| * @param priv A pointer moal_private structure |
| * |
| * @return N/A |
| */ |
| void |
| woal_process_cancel_chanrpt_event(moal_private *priv) |
| { |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) |
| if (priv->phandle->is_cac_timer_set && |
| priv->bss_index == priv->phandle->cac_bss_index) { |
| woal_cancel_timer(&priv->phandle->cac_timer); |
| priv->phandle->is_cac_timer_set = MFALSE; |
| /* Make sure Chan Report is cancelled */ |
| woal_11h_cancel_chan_report_ioctl(priv, MOAL_IOCTL_WAIT); |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 14, 0) |
| cfg80211_cac_event(priv->netdev, &priv->phandle->dfs_channel, |
| NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); |
| #else |
| cfg80211_cac_event(priv->netdev, NL80211_RADAR_CAC_ABORTED, |
| GFP_KERNEL); |
| #endif |
| memset(&priv->phandle->dfs_channel, 0, |
| sizeof(struct cfg80211_chan_def)); |
| priv->phandle->cac_bss_index = 0xff; |
| } |
| #endif |
| } |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) |
| /** |
| * @brief start radar detection |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param chandef A pointer to cfg80211_chan_def structure |
| * @param cac_time_ms A cac dwell time |
| * @return 0 -- success, otherwise fail |
| */ |
| |
| int |
| woal_cfg80211_start_radar_detection(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_chan_def *chandef, |
| u32 cac_time_ms) |
| #else |
| /** |
| * @brief start radar detection |
| * |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param chandef A pointer to cfg80211_chan_def structure |
| * @return 0 -- success, otherwise fail |
| */ |
| |
| int |
| woal_cfg80211_start_radar_detection(struct wiphy *wiphy, |
| struct net_device *dev, |
| struct cfg80211_chan_def *chandef) |
| #endif |
| { |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| moal_handle *handle = priv->phandle; |
| mlan_ioctl_req *req = NULL; |
| mlan_ds_11h_chan_rep_req *pchan_rpt_req = NULL; |
| mlan_ds_11h_cfg *p11h_cfg = NULL; |
| int ret = 0; |
| mlan_status status = MLAN_STATUS_SUCCESS; |
| |
| ENTER(); |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) |
| PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d , Time %d \n", |
| chandef->chan->hw_value, chandef->width, cac_time_ms); |
| #else |
| PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d \n", |
| chandef->chan->hw_value, chandef->width); |
| #endif |
| |
| if (priv->bss_started == MTRUE) { |
| PRINTM(MERROR, "recv CAC request when bss already started \n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| if (priv->phandle->cac_period || handle->is_cac_timer_set) { |
| PRINTM(MERROR, |
| "Maybe other interface is doing CAC, please defer your oper\n"); |
| ret = -EBUSY; |
| goto done; |
| } |
| req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg)); |
| if (NULL == req) { |
| ret = -ENOMEM; |
| goto done; |
| } |
| |
| p11h_cfg = (mlan_ds_11h_cfg *)req->pbuf; |
| pchan_rpt_req = &p11h_cfg->param.chan_rpt_req; |
| pchan_rpt_req->startFreq = START_FREQ_11A_BAND; |
| pchan_rpt_req->chanNum = (t_u8)chandef->chan->hw_value; |
| woal_convert_chan_to_bandconfig(priv, &pchan_rpt_req->bandcfg, chandef); |
| pchan_rpt_req->host_based = MTRUE; |
| |
| #if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0) |
| pchan_rpt_req->millisec_dwell_time = cac_time_ms; |
| #else |
| pchan_rpt_req->millisec_dwell_time = IEEE80211_DFS_MIN_CAC_TIME_MS; |
| |
| if ((woal_is_etsi_country(priv->phandle->country_code) == MTRUE)) { |
| if (chandef->chan->hw_value == 120 || |
| chandef->chan->hw_value == 124 || |
| chandef->chan->hw_value == 128) { |
| pchan_rpt_req->millisec_dwell_time = |
| IEEE80211_DFS_MIN_CAC_TIME_MS * 10; |
| } |
| if (chandef->chan->hw_value == 116 && |
| ((chandef->width == NL80211_CHAN_WIDTH_40) || |
| (chandef->width == NL80211_CHAN_WIDTH_80))) { |
| pchan_rpt_req->millisec_dwell_time = |
| IEEE80211_DFS_MIN_CAC_TIME_MS * 10; |
| } |
| } |
| #endif |
| if (priv->user_cac_period_msec) { |
| pchan_rpt_req->millisec_dwell_time = priv->user_cac_period_msec; |
| PRINTM(MCMD_D, |
| "cfg80211 dfstesting: User CAC Period=%d (msec) \n", |
| pchan_rpt_req->millisec_dwell_time); |
| } |
| |
| p11h_cfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK; |
| req->req_id = MLAN_IOCTL_11H_CFG; |
| req->action = MLAN_ACT_SET; |
| |
| /* Send Channel Check command and wait until the report is ready */ |
| status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT); |
| if (status != MLAN_STATUS_SUCCESS) { |
| PRINTM(MERROR, "Fail to start radar detection\n"); |
| ret = -EFAULT; |
| } else { |
| moal_memcpy_ext(priv->phandle, &handle->dfs_channel, chandef, |
| sizeof(struct cfg80211_chan_def), |
| sizeof(handle->dfs_channel)); |
| handle->cac_bss_index = priv->bss_index; |
| handle->is_cac_timer_set = MTRUE; |
| /* avoid EVENT_CHANNEL_RAPORT_READY missing, add 1s gap */ |
| woal_mod_timer(&handle->cac_timer, |
| pchan_rpt_req->millisec_dwell_time + 1000); |
| } |
| done: |
| if (status != MLAN_STATUS_PENDING) |
| kfree(req); |
| LEAVE(); |
| return ret; |
| } |
| |
| /** |
| * @brief channel switch |
| |
| * @param wiphy A pointer to wiphy structure |
| * @param dev A pointer to net_device structure |
| * @param params A pointer to cfg80211_csa_settings structure |
| * |
| * @return 0 -- success, otherwise fail |
| */ |
| int |
| woal_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev, |
| struct cfg80211_csa_settings *params) |
| { |
| int ret = 0; |
| moal_private *priv = (moal_private *)woal_get_netdev_priv(dev); |
| t_u32 chsw_msec; |
| mlan_uap_bss_param *bss_cfg = NULL; |
| |
| ENTER(); |
| |
| if (!params) { |
| ret = -EINVAL; |
| goto done; |
| } |
| |
| /* TODO: support this case in next version */ |
| if (params->radar_required) { |
| PRINTM(MMSG, |
| " hostapd handle this case by disable and re-enable interface\n"); |
| ret = -ENOTSUPP; |
| goto done; |
| } |
| |
| /* actually hostapd would always choose one diff channel */ |
| if (cfg80211_chandef_identical(¶ms->chandef, &priv->chan)) { |
| PRINTM(MMSG, |
| "csa channel is same with current channel, invaild\n"); |
| ret = -EINVAL; |
| goto done; |
| } |
| bss_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC); |
| if (!bss_cfg) { |
| PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n"); |
| ret = -EFAULT; |
| goto done; |
| } |
| |
| if (params->block_tx) { |
| if (netif_carrier_ok(dev)) |
| netif_carrier_off(dev); |
| woal_stop_queue(dev); |
| priv->uap_tx_blocked = MTRUE; |
| } |
| |
| woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT); |
| if (woal_cfg80211_set_beacon(wiphy, dev, ¶ms->beacon_csa)) { |
| PRINTM(MERROR, "%s: setting csa mgmt ies failed\n", __func__); |
| goto done; |
| } |
| |
| moal_memcpy_ext(priv->phandle, &priv->csa_chan, ¶ms->chandef, |
| sizeof(struct cfg80211_chan_def), |
| sizeof(priv->csa_chan)); |
| moal_memcpy_ext(priv->phandle, &priv->beacon_after, |
| ¶ms->beacon_after, |
| sizeof(struct cfg80211_beacon_data), |
| sizeof(priv->beacon_after)); |
| |
| if (!priv->phandle->fw_ecsa_enable) { |
| if (MLAN_STATUS_SUCCESS != |
| woal_set_get_sys_config(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT, |
| bss_cfg)) { |
| PRINTM(MERROR, "%s: get uap config failed\n", __func__); |
| ret = -EFAULT; |
| goto done; |
| } |
| chsw_msec = params->count * bss_cfg->beacon_period; |
| queue_delayed_work(priv->csa_workqueue, &priv->csa_work, |
| msecs_to_jiffies(chsw_msec)); |
| } |
| done: |
| kfree(bss_cfg); |
| LEAVE(); |
| return ret; |
| } |
| #endif |
| |
| /** |
| * @brief Register the device with cfg80211 |
| * |
| * @param dev A pointer to net_device structure |
| * @param bss_type BSS type |
| * |
| * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE |
| */ |
| mlan_status |
| woal_register_uap_cfg80211(struct net_device *dev, t_u8 bss_type) |
| { |
| mlan_status ret = MLAN_STATUS_SUCCESS; |
| moal_private *priv = (moal_private *)netdev_priv(dev); |
| struct wireless_dev *wdev = NULL; |
| |
| ENTER(); |
| |
| wdev = (struct wireless_dev *)&priv->w_dev; |
| memset(wdev, 0, sizeof(struct wireless_dev)); |
| |
| wdev->wiphy = priv->phandle->wiphy; |
| if (!wdev->wiphy) { |
| LEAVE(); |
| return MLAN_STATUS_FAILURE; |
| } |
| |
| if (bss_type == MLAN_BSS_TYPE_UAP) |
| wdev->iftype = NL80211_IFTYPE_AP; |
| |
| dev_net_set(dev, wiphy_net(wdev->wiphy)); |
| dev->ieee80211_ptr = wdev; |
| SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy)); |
| priv->wdev = wdev; |
| |
| LEAVE(); |
| return ret; |
| } |