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