mwifiex/mxm_wifiex/wlan_src/mlinux/moal_uap_cfg80211.c
Sherry Sun 8ffae47921 mxm_wifiex: update to mxm5x17283 release
changes:
1. WCSWREL-191: Fixed the error when loading module param from user config for SD8801
2. WCSWREL-186: Fixed the issue of mlanutl failing on kernel higher than L5.15
3. Fixed low throughput issue for WPA3 SAE
4. Added driver change for WLAN throughput improvement on 8997 SoC
5. Updated README to recommend not to use WEP/TKIP for all chipsets
6. WCSWREL-180: Fix P2P test fail on kernel higher than L5.12
7. WCSWREL-156: kernel_write/kernel_read not allowed by drivers for L5.10 kernel GKI buildou
8. Alternative for pm_qos_add_request/pm_qos_remove_request

Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
Approved-by: Tian Yang <yang.tian@nxp.com>
2021-10-12 12:16:50 +08:00

3422 lines
98 KiB
C

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