mirror of
https://github.com/nxp-imx/mwifiex.git
synced 2024-11-14 11:05:34 +00:00
5cda905576
Driver Bug Fixes: ----------------- 1. LF-6972: Updated the License to GPL-2.0 2. Incremented the driver version Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
3621 lines
104 KiB
C
3621 lines
104 KiB
C
/** @file moal_uap_cfg80211.c
|
|
*
|
|
* @brief This file contains the functions for uAP CFG80211.
|
|
*
|
|
*
|
|
* Copyright 2011-2022 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)) {
|
|
PRINTM(MMSG, "wlan: deauth station " MACSTR "\n",
|
|
MAC2STR(mac_addr));
|
|
ret = woal_deauth_station(priv, mac_addr, reason_code);
|
|
#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE
|
|
if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME))
|
|
cfg80211_del_sta(priv->netdev, mac_addr,
|
|
GFP_KERNEL);
|
|
#endif
|
|
break;
|
|
}
|
|
}
|
|
|
|
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();
|
|
memset(bandcfg, 0, sizeof(Band_Config_t));
|
|
switch (chandef->chan->band) {
|
|
case NL80211_BAND_2GHZ:
|
|
bandcfg->chanBand = BAND_2GHZ;
|
|
break;
|
|
case NL80211_BAND_5GHZ:
|
|
bandcfg->chanBand = BAND_5GHZ;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
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;
|
|
}
|
|
PRINTM(MCMND,
|
|
"cfg80211 AP: channel=%d, chanBand=0x%x chanWidth=0x%x chan2Offset=0x%x\n",
|
|
chandef->chan->hw_value, bandcfg->chanBand, bandcfg->chanWidth,
|
|
bandcfg->chan2Offset);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
/**
|
|
* @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)
|
|
Band_Config_t bandcfg;
|
|
#endif
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
mlan_ds_11h_chan_nop_info chan_nop_info;
|
|
#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)
|
|
woal_convert_chan_to_bandconfig(priv, &bandcfg, ¶ms->chandef);
|
|
#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);
|
|
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, 12, 0)
|
|
moal_memcpy_ext(priv->phandle, &sys_config->bandcfg, &bandcfg,
|
|
sizeof(bandcfg), sizeof(bandcfg));
|
|
#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;
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
/**
|
|
* @brief Request the driver to add a monitor interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param name_assign_type Interface name assignment type
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
* @param new_dev Netdevice to be passed out
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_cfg80211_add_mon_if(struct wiphy *wiphy, const char *name,
|
|
unsigned char name_assign_type, u32 *flags,
|
|
struct vif_params *params,
|
|
struct net_device **new_dev)
|
|
#else
|
|
/**
|
|
* @brief Request the driver to add a monitor interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
* @param new_dev Netdevice to be passed out
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_cfg80211_add_mon_if(struct wiphy *wiphy,
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
|
|
const
|
|
#endif
|
|
char *name,
|
|
u32 *flags, struct vif_params *params,
|
|
struct net_device **new_dev)
|
|
#endif
|
|
{
|
|
int ret = 0;
|
|
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
|
|
moal_private *priv =
|
|
(moal_private *)woal_get_priv(handle, MLAN_BSS_ROLE_STA);
|
|
monitor_iface *mon_if = NULL;
|
|
struct net_device *ndev = NULL;
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
chan_band_info chan_info;
|
|
#endif
|
|
unsigned char name_assign_type_tmp = 0;
|
|
|
|
ENTER();
|
|
|
|
ASSERT_RTNL();
|
|
|
|
if (handle->mon_if) {
|
|
PRINTM(MERROR, "%s: monitor interface exist: %s basedev %s\n",
|
|
__func__, handle->mon_if->mon_ndev->name,
|
|
handle->mon_if->base_ndev->name);
|
|
ret = -EFAULT;
|
|
goto fail;
|
|
}
|
|
if (!priv) {
|
|
PRINTM(MERROR, "add_mon_if: priv is NULL\n");
|
|
ret = -EFAULT;
|
|
goto fail;
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
name_assign_type_tmp = name_assign_type;
|
|
#endif
|
|
mon_if = woal_prepare_mon_if(priv, name, name_assign_type_tmp,
|
|
CHANNEL_SPEC_SNIFFER_MODE);
|
|
if (!mon_if) {
|
|
PRINTM(MFATAL, "Prepare mon_if fail.\n");
|
|
goto fail;
|
|
}
|
|
ndev = mon_if->mon_ndev;
|
|
dev_net_set(ndev, wiphy_net(wiphy));
|
|
|
|
moal_memcpy_ext(priv->phandle, ndev->perm_addr, wiphy->perm_addr,
|
|
ETH_ALEN, sizeof(ndev->perm_addr));
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
|
|
eth_hw_addr_set(ndev, ndev->perm_addr);
|
|
#else
|
|
moal_memcpy_ext(priv->phandle, ndev->dev_addr, ndev->perm_addr,
|
|
ETH_ALEN, MAX_ADDR_LEN);
|
|
#endif
|
|
SET_NETDEV_DEV(ndev, wiphy_dev(wiphy));
|
|
ndev->ieee80211_ptr = &mon_if->wdev;
|
|
mon_if->wdev.iftype = NL80211_IFTYPE_MONITOR;
|
|
mon_if->wdev.wiphy = wiphy;
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
/* Set default band channel config */
|
|
mon_if->band_chan_cfg.band = BAND_B | BAND_G;
|
|
mon_if->band_chan_cfg.band |= BAND_GN;
|
|
mon_if->band_chan_cfg.channel = 1;
|
|
mon_if->band_chan_cfg.chan_bandwidth = CHANNEL_BW_20MHZ;
|
|
memset(&chan_info, 0x00, sizeof(chan_info));
|
|
chan_info.channel = 1;
|
|
chan_info.is_11n_enabled = MTRUE;
|
|
if (MLAN_STATUS_FAILURE ==
|
|
woal_chandef_create(priv, &mon_if->chandef, &chan_info)) {
|
|
ret = -EFAULT;
|
|
goto fail;
|
|
}
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_net_monitor(priv, MOAL_IOCTL_WAIT,
|
|
CHANNEL_SPEC_SNIFFER_MODE, 0x7,
|
|
&mon_if->band_chan_cfg)) {
|
|
PRINTM(MERROR, "%s: woal_set_net_monitor fail\n", __func__);
|
|
ret = -EFAULT;
|
|
goto fail;
|
|
}
|
|
#endif
|
|
|
|
ret = register_netdevice(ndev);
|
|
if (ret) {
|
|
PRINTM(MFATAL, "register net_device failed, ret=%d\n", ret);
|
|
free_netdev(ndev);
|
|
goto fail;
|
|
}
|
|
|
|
handle->mon_if = mon_if;
|
|
|
|
if (new_dev)
|
|
*new_dev = ndev;
|
|
|
|
fail:
|
|
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);
|
|
INIT_LIST_HEAD(&priv->mcast_list);
|
|
spin_lock_init(&priv->mcast_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 CFG80211_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 CFG80211_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);
|
|
woal_flush_mcast_list(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 CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
|
cfg80211_unregister_netdevice(dev);
|
|
#else
|
|
unregister_netdevice(dev);
|
|
#endif
|
|
}
|
|
return ret;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/**
|
|
* @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 CFG80211_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
|
|
if (handle->mon_if) {
|
|
netif_device_detach(handle->mon_if->mon_ndev);
|
|
if (handle->mon_if->mon_ndev->reg_state == NETREG_REGISTERED)
|
|
unregister_netdevice(handle->mon_if->mon_ndev);
|
|
handle->mon_if = NULL;
|
|
}
|
|
rtnl_unlock();
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
handle->priv_num -= vir_intf;
|
|
#endif
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @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 CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
|
|
u32 *flags = ¶ms->flags;
|
|
#endif
|
|
|
|
ENTER();
|
|
PRINTM(MIOCTL, "add virtual intf: %d name: %s\n", type, name);
|
|
switch (type) {
|
|
case NL80211_IFTYPE_MONITOR:
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
ret = woal_cfg80211_add_mon_if(wiphy, name, name_assign_type,
|
|
flags, params, &ndev);
|
|
#else
|
|
ret = woal_cfg80211_add_mon_if(wiphy, name, flags, params,
|
|
&ndev);
|
|
#endif
|
|
break;
|
|
#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_MONITOR) {
|
|
if ((handle->mon_if) && (handle->mon_if->mon_ndev == dev)) {
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_net_monitor(handle->mon_if->priv,
|
|
MOAL_IOCTL_WAIT, MFALSE, 0,
|
|
NULL)) {
|
|
PRINTM(MERROR,
|
|
"%s: woal_set_net_monitor fail\n",
|
|
__func__);
|
|
ret = -EFAULT;
|
|
}
|
|
#endif
|
|
handle->mon_if = NULL;
|
|
}
|
|
unregister_netdevice(dev);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
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) {
|
|
#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;
|
|
}
|
|
uap_channel.bandcfg.chanBand =
|
|
woal_ieee_band_to_radio_type(priv->csa_chan.chan->band);
|
|
uap_channel.bandcfg.chan2Offset = chan2Offset;
|
|
if (MLAN_STATUS_SUCCESS != woal_set_get_ap_channel(priv, MLAN_ACT_SET,
|
|
wait_option,
|
|
&uap_channel)) {
|
|
PRINTM(MERROR, "Fail to set ap channel \n");
|
|
goto done;
|
|
}
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_uap_bss_ctrl(priv, MOAL_IOCTL_WAIT_TIMEOUT, UAP_BSS_START)) {
|
|
PRINTM(MERROR, "%s: start uap failed \n", __func__);
|
|
goto done;
|
|
}
|
|
PRINTM(MMSG, "CSA: old chan %d => new chan %d \n", priv->channel,
|
|
uap_channel.channel);
|
|
priv->channel = uap_channel.channel;
|
|
moal_memcpy_ext(priv->phandle, &priv->chan, &priv->csa_chan,
|
|
sizeof(struct cfg80211_chan_def), sizeof(priv->chan));
|
|
cfg80211_ch_switch_notify(priv->netdev, &priv->chan);
|
|
if (priv->uap_tx_blocked) {
|
|
if (!netif_carrier_ok(priv->netdev))
|
|
netif_carrier_on(priv->netdev);
|
|
woal_start_queue(priv->netdev);
|
|
priv->uap_tx_blocked = MFALSE;
|
|
}
|
|
done:
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief csa work handler
|
|
*
|
|
* @param work a pointer to work_struct
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
void woal_csa_work_queue(struct work_struct *work)
|
|
{
|
|
struct delayed_work *delayed_work =
|
|
container_of(work, struct delayed_work, work);
|
|
moal_private *priv = container_of(delayed_work, moal_private, csa_work);
|
|
ENTER();
|
|
if (priv->bss_started == MTRUE)
|
|
woal_switch_uap_channel(priv, MOAL_IOCTL_WAIT);
|
|
LEAVE();
|
|
}
|
|
|
|
#if KERNEL_VERSION(3, 12, 0) <= CFG80211_VERSION_CODE
|
|
/*
|
|
* @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 (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;
|
|
#endif
|
|
/* Since kernel doesn't support 600sec cac_timer for channels 120, 124,
|
|
* and 128 (weather channels) in ETSI region, overwrite kernel's
|
|
* cac_timer.
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
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;
|
|
}
|