mirror of
https://github.com/nxp-imx/mwifiex.git
synced 2024-11-14 19:15:35 +00:00
3a50175cae
After merge AOSP kernel added these two patches: BACKPORT: FROMGIT: wifi: cfg80211: do some rework towards MLO link APIs BACKPORT: FROMLIST: cfg80211: Indicate MLO connection info in connect and roam callbacks The patches has been merged into an upstream maintainer tree, but has not yet been merged into Linux mainline. More importantly, the patches changed the structure, resulting in build errors. Signed-off-by: Zhipeng Wang <zhipeng.wang_1@nxp.com>
3609 lines
104 KiB
C
3609 lines
104 KiB
C
/** @file moal_uap_cfg80211.c
|
|
*
|
|
* @brief This file contains the functions for uAP CFG80211.
|
|
*
|
|
*
|
|
* Copyright 2011-2022 NXP
|
|
*
|
|
* NXP CONFIDENTIAL
|
|
* The source code contained or described herein and all documents related to
|
|
* the source code (Materials) are owned by NXP, its
|
|
* suppliers and/or its licensors. Title to the Materials remains with NXP,
|
|
* its suppliers and/or its licensors. The Materials contain
|
|
* trade secrets and proprietary and confidential information of NXP, its
|
|
* suppliers and/or its licensors. The Materials are protected by worldwide
|
|
* copyright and trade secret laws and treaty provisions. No part of the
|
|
* Materials may be used, copied, reproduced, modified, published, uploaded,
|
|
* posted, transmitted, distributed, or disclosed in any way without NXP's prior
|
|
* express written permission.
|
|
*
|
|
* No license under any patent, copyright, trade secret or other intellectual
|
|
* property right is granted to or conferred upon you by disclosure or delivery
|
|
* of the Materials, either expressly, by implication, inducement, estoppel or
|
|
* otherwise. Any license under such intellectual property rights must be
|
|
* express and approved by NXP in writing.
|
|
*
|
|
* Alternatively, this software may be distributed under the terms of GPL v2.
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*
|
|
*/
|
|
|
|
#include "moal_cfg80211.h"
|
|
#include "moal_uap_cfg80211.h"
|
|
/** deauth reason code */
|
|
#define REASON_CODE_DEAUTH_LEAVING 3
|
|
/********************************************************
|
|
Local Variables
|
|
********************************************************/
|
|
|
|
/********************************************************
|
|
Global Variables
|
|
********************************************************/
|
|
/********************************************************
|
|
Local Functions
|
|
********************************************************/
|
|
|
|
/********************************************************
|
|
Global Functions
|
|
********************************************************/
|
|
/**
|
|
* @brief send deauth to station
|
|
*
|
|
* @param A pointer to moal_private
|
|
* @param mac A pointer to station mac address
|
|
* @param reason_code ieee deauth reason code
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_deauth_station(moal_private *priv, u8 *mac_addr,
|
|
u16 reason_code)
|
|
{
|
|
mlan_ioctl_req *ioctl_req = NULL;
|
|
mlan_ds_bss *bss = NULL;
|
|
int ret = 0;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
ioctl_req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
|
|
if (ioctl_req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
bss = (mlan_ds_bss *)ioctl_req->pbuf;
|
|
bss->sub_command = MLAN_OID_UAP_DEAUTH_STA;
|
|
ioctl_req->req_id = MLAN_IOCTL_BSS;
|
|
ioctl_req->action = MLAN_ACT_SET;
|
|
|
|
moal_memcpy_ext(priv->phandle, bss->param.deauth_param.mac_addr,
|
|
mac_addr, MLAN_MAC_ADDR_LENGTH,
|
|
sizeof(bss->param.deauth_param.mac_addr));
|
|
bss->param.deauth_param.reason_code = reason_code;
|
|
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(ioctl_req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief send deauth to station, that has been added and associated
|
|
*
|
|
* @param A pointer to moal_private
|
|
* @param mac A pointer to station mac address
|
|
* @param reason_code ieee deauth reason code
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_deauth_assoc_station(moal_private *priv, u8 *mac_addr,
|
|
u16 reason_code)
|
|
{
|
|
int ret = -EFAULT;
|
|
int i = 0;
|
|
mlan_ds_get_info *info = NULL;
|
|
mlan_ioctl_req *ioctl_req = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
|
|
if (!mac_addr) {
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
ioctl_req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(
|
|
sizeof(mlan_ds_get_info));
|
|
if (ioctl_req == NULL) {
|
|
LEAVE();
|
|
return -ENOMEM;
|
|
}
|
|
|
|
info = (mlan_ds_get_info *)ioctl_req->pbuf;
|
|
info->sub_command = MLAN_OID_UAP_STA_LIST;
|
|
ioctl_req->req_id = MLAN_IOCTL_GET_INFO;
|
|
ioctl_req->action = MLAN_ACT_GET;
|
|
|
|
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS)
|
|
goto done;
|
|
if (!info->param.sta_list.sta_count) {
|
|
PRINTM(MCMND, "wlan: skip deauth to station " MACSTR "\n",
|
|
MAC2STR(mac_addr));
|
|
goto done;
|
|
}
|
|
|
|
for (i = 0; i < info->param.sta_list.sta_count; i++) {
|
|
if (!memcmp(info->param.sta_list.info[i].mac_address, mac_addr,
|
|
ETH_ALEN))
|
|
ret = woal_deauth_station(priv, mac_addr, reason_code);
|
|
}
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(ioctl_req);
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief send deauth to all station
|
|
*
|
|
* @param A pointer to moal_private
|
|
* @param mac A pointer to station mac address
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_deauth_all_station(moal_private *priv)
|
|
{
|
|
int ret = -EFAULT;
|
|
int i = 0;
|
|
mlan_ds_get_info *info = NULL;
|
|
mlan_ioctl_req *ioctl_req = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
|
|
ENTER();
|
|
if (priv->media_connected == MFALSE) {
|
|
PRINTM(MINFO, "cfg80211: Media not connected!\n");
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
PRINTM(MIOCTL, "del all station\n");
|
|
/* Allocate an IOCTL request buffer */
|
|
ioctl_req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(
|
|
sizeof(mlan_ds_get_info));
|
|
if (ioctl_req == NULL) {
|
|
ret = -ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
info = (mlan_ds_get_info *)ioctl_req->pbuf;
|
|
info->sub_command = MLAN_OID_UAP_STA_LIST;
|
|
ioctl_req->req_id = MLAN_IOCTL_GET_INFO;
|
|
ioctl_req->action = MLAN_ACT_GET;
|
|
|
|
status = woal_request_ioctl(priv, ioctl_req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS)
|
|
goto done;
|
|
if (!info->param.sta_list.sta_count)
|
|
goto done;
|
|
for (i = 0; i < info->param.sta_list.sta_count; i++) {
|
|
PRINTM(MIOCTL, "deauth station " MACSTR "\n",
|
|
MAC2STR(info->param.sta_list.info[i].mac_address));
|
|
ret = woal_deauth_station(
|
|
priv, info->param.sta_list.info[i].mac_address,
|
|
REASON_CODE_DEAUTH_LEAVING);
|
|
}
|
|
woal_sched_timeout(200);
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(ioctl_req);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Verify RSN IE
|
|
*
|
|
* @param rsn_ie Pointer RSN IE
|
|
* @param sys_config Pointer to mlan_uap_bss_param structure
|
|
*
|
|
* @return MTRUE/MFALSE
|
|
*/
|
|
static t_u8 woal_check_rsn_ie(IEEEtypes_Rsn_t *rsn_ie,
|
|
mlan_uap_bss_param *sys_config)
|
|
{
|
|
int left = 0;
|
|
int count = 0;
|
|
int i = 0;
|
|
wpa_suite_auth_key_mgmt_t *key_mgmt = NULL;
|
|
left = rsn_ie->len + 2;
|
|
if (left < (int)sizeof(IEEEtypes_Rsn_t))
|
|
return MFALSE;
|
|
sys_config->wpa_cfg.group_cipher = 0;
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa2 = 0;
|
|
sys_config->key_mgmt = 0;
|
|
/* check the group cipher */
|
|
switch (rsn_ie->group_cipher.type) {
|
|
case WPA_CIPHER_TKIP:
|
|
sys_config->wpa_cfg.group_cipher = CIPHER_TKIP;
|
|
break;
|
|
case WPA_CIPHER_AES_CCM:
|
|
sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
count = woal_le16_to_cpu(rsn_ie->pairwise_cipher.count);
|
|
for (i = 0; i < count; i++) {
|
|
switch (rsn_ie->pairwise_cipher.list[i].type) {
|
|
case WPA_CIPHER_TKIP:
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa2 |= CIPHER_TKIP;
|
|
break;
|
|
case WPA_CIPHER_AES_CCM:
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa2 |=
|
|
CIPHER_AES_CCMP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
left -= sizeof(IEEEtypes_Rsn_t) + (count - 1) * sizeof(wpa_suite);
|
|
if (left < (int)sizeof(wpa_suite_auth_key_mgmt_t))
|
|
return MFALSE;
|
|
key_mgmt =
|
|
(wpa_suite_auth_key_mgmt_t *)((u8 *)rsn_ie +
|
|
sizeof(IEEEtypes_Rsn_t) +
|
|
(count - 1) * sizeof(wpa_suite));
|
|
count = woal_le16_to_cpu(key_mgmt->count);
|
|
if (left < (int)(sizeof(wpa_suite_auth_key_mgmt_t) +
|
|
(count - 1) * sizeof(wpa_suite)))
|
|
return MFALSE;
|
|
for (i = 0; i < count; i++) {
|
|
switch (key_mgmt->list[i].type) {
|
|
case RSN_AKM_8021X:
|
|
sys_config->key_mgmt |= KEY_MGMT_EAP;
|
|
break;
|
|
case RSN_AKM_PSK:
|
|
sys_config->key_mgmt |= KEY_MGMT_PSK;
|
|
break;
|
|
case RSN_AKM_PSK_SHA256:
|
|
sys_config->key_mgmt |= KEY_MGMT_PSK_SHA256;
|
|
break;
|
|
case RSN_AKM_SAE:
|
|
sys_config->key_mgmt |= KEY_MGMT_SAE;
|
|
break;
|
|
case RSN_AKM_OWE:
|
|
sys_config->key_mgmt |= KEY_MGMT_OWE;
|
|
break;
|
|
}
|
|
}
|
|
return MTRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Verify WPA IE
|
|
*
|
|
* @param wpa_ie Pointer WPA IE
|
|
* @param sys_config Pointer to mlan_uap_bss_param structure
|
|
*
|
|
* @return MTRUE/MFALSE
|
|
*/
|
|
static t_u8 woal_check_wpa_ie(IEEEtypes_Wpa_t *wpa_ie,
|
|
mlan_uap_bss_param *sys_config)
|
|
{
|
|
int left = 0;
|
|
int count = 0;
|
|
int i = 0;
|
|
wpa_suite_auth_key_mgmt_t *key_mgmt = NULL;
|
|
left = wpa_ie->len + 2;
|
|
if (left < (int)sizeof(IEEEtypes_Wpa_t))
|
|
return MFALSE;
|
|
sys_config->wpa_cfg.group_cipher = 0;
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa = 0;
|
|
switch (wpa_ie->group_cipher.type) {
|
|
case WPA_CIPHER_TKIP:
|
|
sys_config->wpa_cfg.group_cipher = CIPHER_TKIP;
|
|
break;
|
|
case WPA_CIPHER_AES_CCM:
|
|
sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
count = woal_le16_to_cpu(wpa_ie->pairwise_cipher.count);
|
|
for (i = 0; i < count; i++) {
|
|
switch (wpa_ie->pairwise_cipher.list[i].type) {
|
|
case WPA_CIPHER_TKIP:
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa |= CIPHER_TKIP;
|
|
break;
|
|
case WPA_CIPHER_AES_CCM:
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa |=
|
|
CIPHER_AES_CCMP;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
left -= sizeof(IEEEtypes_Wpa_t) + (count - 1) * sizeof(wpa_suite);
|
|
if (left < (int)sizeof(wpa_suite_auth_key_mgmt_t))
|
|
return MFALSE;
|
|
key_mgmt =
|
|
(wpa_suite_auth_key_mgmt_t *)((u8 *)wpa_ie +
|
|
sizeof(IEEEtypes_Wpa_t) +
|
|
(count - 1) * sizeof(wpa_suite));
|
|
count = woal_le16_to_cpu(key_mgmt->count);
|
|
if (left < (int)(sizeof(wpa_suite_auth_key_mgmt_t) +
|
|
(count - 1) * sizeof(wpa_suite)))
|
|
return MFALSE;
|
|
for (i = 0; i < count; i++) {
|
|
switch (key_mgmt->list[i].type) {
|
|
case RSN_AKM_8021X:
|
|
sys_config->key_mgmt = KEY_MGMT_EAP;
|
|
break;
|
|
case RSN_AKM_PSK:
|
|
sys_config->key_mgmt = KEY_MGMT_PSK;
|
|
break;
|
|
}
|
|
}
|
|
return MTRUE;
|
|
}
|
|
|
|
/**
|
|
* @brief Find RSN/WPA IES
|
|
*
|
|
* @param ie Pointer IE buffer
|
|
* @param sys_config Pointer to mlan_uap_bss_param structure
|
|
*
|
|
* @return MTRUE/MFALSE
|
|
*/
|
|
static t_u8 woal_find_wpa_ies(const t_u8 *ie, int len,
|
|
mlan_uap_bss_param *sys_config)
|
|
{
|
|
int bytes_left = len;
|
|
const t_u8 *pcurrent_ptr = ie;
|
|
t_u16 total_ie_len;
|
|
t_u8 element_len;
|
|
t_u8 wpa2 = 0;
|
|
t_u8 wpa = 0;
|
|
t_u8 ret = MFALSE;
|
|
IEEEtypes_ElementId_e element_id;
|
|
IEEEtypes_VendorSpecific_t *pvendor_ie;
|
|
const t_u8 wpa_oui[4] = {0x00, 0x50, 0xf2, 0x01};
|
|
|
|
while (bytes_left >= 2) {
|
|
element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr));
|
|
element_len = *((t_u8 *)pcurrent_ptr + 1);
|
|
total_ie_len = element_len + sizeof(IEEEtypes_Header_t);
|
|
if (bytes_left < total_ie_len) {
|
|
PRINTM(MERROR,
|
|
"InterpretIE: Error in processing IE, bytes left < IE length\n");
|
|
bytes_left = 0;
|
|
continue;
|
|
}
|
|
switch (element_id) {
|
|
case RSN_IE:
|
|
wpa2 = woal_check_rsn_ie(
|
|
(IEEEtypes_Rsn_t *)pcurrent_ptr, sys_config);
|
|
break;
|
|
case VENDOR_SPECIFIC_221:
|
|
pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr;
|
|
if (!memcmp(pvendor_ie->vend_hdr.oui, wpa_oui,
|
|
sizeof(pvendor_ie->vend_hdr.oui)) &&
|
|
(pvendor_ie->vend_hdr.oui_type == wpa_oui[3])) {
|
|
wpa = woal_check_wpa_ie(
|
|
(IEEEtypes_Wpa_t *)pcurrent_ptr,
|
|
sys_config);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
pcurrent_ptr += element_len + 2;
|
|
/* Need to account for IE ID and IE Len */
|
|
bytes_left -= (element_len + 2);
|
|
}
|
|
if (wpa && wpa2) {
|
|
sys_config->protocol = PROTOCOL_WPA | PROTOCOL_WPA2;
|
|
ret = MTRUE;
|
|
} else if (wpa2) {
|
|
sys_config->protocol = PROTOCOL_WPA2;
|
|
ret = MTRUE;
|
|
} else if (wpa) {
|
|
sys_config->protocol = PROTOCOL_WPA;
|
|
ret = MTRUE;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Find and set WMM IES
|
|
*
|
|
* @param priv Pointer to moal_private
|
|
* @param ie Pointer IE buffer
|
|
* @param sys_config Pointer to mlan_uap_bss_param structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static t_void woal_set_wmm_ies(moal_private *priv, const t_u8 *ie, int len,
|
|
mlan_uap_bss_param *sys_config)
|
|
{
|
|
int bytes_left = len;
|
|
const t_u8 *pcurrent_ptr = ie;
|
|
t_u16 total_ie_len;
|
|
t_u8 element_len;
|
|
IEEEtypes_VendorSpecific_t *pvendor_ie;
|
|
IEEEtypes_ElementId_e element_id;
|
|
const t_u8 wmm_oui[4] = {0x00, 0x50, 0xf2, 0x02};
|
|
|
|
while (bytes_left >= 2) {
|
|
element_id = (IEEEtypes_ElementId_e)(*((t_u8 *)pcurrent_ptr));
|
|
element_len = *((t_u8 *)pcurrent_ptr + 1);
|
|
total_ie_len = element_len + sizeof(IEEEtypes_Header_t);
|
|
if (bytes_left < total_ie_len) {
|
|
PRINTM(MERROR,
|
|
"InterpretIE: Error in processing IE, bytes left < IE length\n");
|
|
bytes_left = 0;
|
|
continue;
|
|
}
|
|
switch (element_id) {
|
|
case VENDOR_SPECIFIC_221:
|
|
pvendor_ie = (IEEEtypes_VendorSpecific_t *)pcurrent_ptr;
|
|
if (!memcmp(pvendor_ie->vend_hdr.oui, wmm_oui,
|
|
sizeof(pvendor_ie->vend_hdr.oui)) &&
|
|
pvendor_ie->vend_hdr.oui_type == wmm_oui[3]) {
|
|
if (total_ie_len ==
|
|
sizeof(IEEEtypes_WmmParameter_t)) {
|
|
/*
|
|
* Only accept and copy the WMM IE if
|
|
* it matches the size expected for the
|
|
* WMM Parameter IE.
|
|
*/
|
|
moal_memcpy_ext(
|
|
priv->phandle,
|
|
&sys_config->wmm_para,
|
|
pcurrent_ptr +
|
|
sizeof(IEEEtypes_Header_t),
|
|
element_len,
|
|
sizeof(sys_config->wmm_para));
|
|
/** set uap_host_based_config to true */
|
|
sys_config->uap_host_based_config =
|
|
MTRUE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
pcurrent_ptr += element_len + 2;
|
|
/* Need to account for IE ID and IE Len */
|
|
bytes_left -= (element_len + 2);
|
|
}
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
/**
|
|
* @brief initialize AP or GO bss config
|
|
* @param priv A pointer to moal private structure
|
|
* @param band BAND_2GHZ/BAND_5GHZ
|
|
* @param params A pointer to cfg80211_ap_settings structure
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static t_u8 woal_check_11ac_capability(moal_private *priv, t_u8 band,
|
|
struct cfg80211_ap_settings *params)
|
|
#else
|
|
/**
|
|
* @brief initialize AP or GO bss config
|
|
* @param band BAND_2GHZ/BAND_5GHZ
|
|
* @param priv A pointer to moal private structure
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static t_u8 woal_check_11ac_capability(moal_private *priv, t_u8 band)
|
|
#endif
|
|
{
|
|
mlan_fw_info fw_info;
|
|
t_u8 enable_11ac = MFALSE;
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
const u8 *vht_ie = NULL;
|
|
#endif
|
|
ENTER();
|
|
memset(&fw_info, 0, sizeof(mlan_fw_info));
|
|
woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info);
|
|
if ((band == BAND_5GHZ) && !(fw_info.fw_bands & BAND_AAC)) {
|
|
PRINTM(MCMND, "FW don't support 5G AC");
|
|
LEAVE();
|
|
return enable_11ac;
|
|
}
|
|
if ((band == BAND_2GHZ) && !(fw_info.fw_bands & BAND_GAC)) {
|
|
PRINTM(MCMND, "FW don't support 2G AC");
|
|
LEAVE();
|
|
return enable_11ac;
|
|
}
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
vht_ie = cfg80211_find_ie(WLAN_EID_VHT_CAPABILITY, params->beacon.tail,
|
|
params->beacon.tail_len);
|
|
if (vht_ie)
|
|
enable_11ac = MTRUE;
|
|
else
|
|
enable_11ac = MFALSE;
|
|
#else
|
|
enable_11ac = MTRUE;
|
|
#endif
|
|
LEAVE();
|
|
return enable_11ac;
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
/**
|
|
* @brief initialize AP or GO bss config
|
|
* @param priv A pointer to moal private structure
|
|
* @param band BAND_5G/BAND_2GHZ
|
|
* @param params A pointer to cfg80211_ap_settings structure
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static t_u8 woal_check_11ax_capability(moal_private *priv, t_u8 band,
|
|
struct cfg80211_ap_settings *params)
|
|
{
|
|
mlan_fw_info fw_info;
|
|
t_u8 enable_11ax = MFALSE;
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 20, 0)
|
|
mlan_ds_11ax_he_cfg he_cfg;
|
|
t_u8 he_txrx_mcs_support[4] = {0xff, 0xff, 0xff, 0xff};
|
|
#endif
|
|
ENTER();
|
|
memset(&fw_info, 0, sizeof(mlan_fw_info));
|
|
woal_request_get_fw_info(priv, MOAL_IOCTL_WAIT, &fw_info);
|
|
if ((band == BAND_5GHZ) && !(fw_info.fw_bands & BAND_AAX)) {
|
|
PRINTM(MCMND, "FW don't support 5G AX\n");
|
|
LEAVE();
|
|
return enable_11ax;
|
|
}
|
|
if ((band == BAND_2GHZ) && !(fw_info.fw_bands & BAND_GAX)) {
|
|
PRINTM(MCMND, "FW don't support 2G AX");
|
|
LEAVE();
|
|
return enable_11ax;
|
|
}
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
|
if (params->he_cap)
|
|
enable_11ax = MTRUE;
|
|
else
|
|
enable_11ax = MFALSE;
|
|
#else
|
|
memset(&he_cfg, 0, sizeof(he_cfg));
|
|
if (band == BAND_5GHZ)
|
|
he_cfg.band = MBIT(1);
|
|
else if (band == BAND_2GHZ)
|
|
he_cfg.band = MBIT(0);
|
|
if (0 == woal_11ax_cfg(priv, MLAN_ACT_GET, &he_cfg)) {
|
|
if (he_cfg.he_cap.len &&
|
|
(he_cfg.he_cap.ext_id == HE_CAPABILITY)) {
|
|
if (memcmp(he_cfg.he_cap.he_txrx_mcs_support,
|
|
he_txrx_mcs_support,
|
|
sizeof(he_txrx_mcs_support)))
|
|
enable_11ax = MTRUE;
|
|
}
|
|
}
|
|
#endif
|
|
PRINTM(MCMND, "enable_11ax=%d\n", enable_11ax);
|
|
LEAVE();
|
|
return enable_11ax;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief get ht_cap from beacon ie
|
|
*
|
|
* @param ie Pointer to IEs
|
|
* @param len Total length of ie
|
|
*
|
|
* @return ht_cap
|
|
*/
|
|
static t_u16 woal_get_htcap_info(const t_u8 *ie, int len)
|
|
{
|
|
t_u16 ht_cap_info = 0;
|
|
IEEEtypes_HTCap_t *htcap_ie = NULL;
|
|
htcap_ie =
|
|
(IEEEtypes_HTCap_t *)woal_parse_ie_tlv(ie, len, HT_CAPABILITY);
|
|
if (htcap_ie) {
|
|
/* hostap has converted ht_cap_info to little endian, here
|
|
* conver to host endian */
|
|
ht_cap_info = woal_le16_to_cpu(htcap_ie->ht_cap.ht_cap_info);
|
|
PRINTM(MMSG, "Get ht_cap from beacon ies: 0x%x\n", ht_cap_info);
|
|
}
|
|
return ht_cap_info;
|
|
}
|
|
|
|
/**
|
|
* @brief get vht_cap from beacon ie
|
|
*
|
|
* @param ie Pointer to IEs
|
|
* @param len Total length of ie
|
|
*
|
|
* @return Pointer to vht_cap ie
|
|
*/
|
|
static IEEEtypes_VHTCap_t *woal_get_vhtcap_info(const t_u8 *ie, int len)
|
|
{
|
|
IEEEtypes_VHTCap_t *vhtcap_ie = NULL;
|
|
vhtcap_ie = (IEEEtypes_VHTCap_t *)woal_parse_ie_tlv(ie, len,
|
|
VHT_CAPABILITY);
|
|
if (vhtcap_ie)
|
|
PRINTM(MMSG, "Get vht_cap from beacon ies: 0x%x\n",
|
|
vhtcap_ie->vht_cap.vht_cap_info);
|
|
return vhtcap_ie;
|
|
}
|
|
|
|
/**
|
|
* @brief get vht_oper from beacon ie
|
|
*
|
|
* @param ie Pointer to IEs
|
|
* @param len Total length of ie
|
|
*
|
|
* @return Pointer to vht_opr ie
|
|
*/
|
|
static IEEEtypes_VHTOprat_t *woal_get_vht_oprat_ie(const t_u8 *ie, int len)
|
|
{
|
|
IEEEtypes_VHTOprat_t *vht_oprat_ie = NULL;
|
|
vht_oprat_ie = (IEEEtypes_VHTOprat_t *)woal_parse_ie_tlv(ie, len,
|
|
VHT_OPERATION);
|
|
if (vht_oprat_ie)
|
|
PRINTM(MMSG,
|
|
"Get vht_oprat_ie from beacon ies: chan_width=%d\n",
|
|
vht_oprat_ie->chan_width);
|
|
return vht_oprat_ie;
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
/** Starting Frequency for 11A band */
|
|
#define START_FREQ_11A_BAND 5000 /* in MHz */
|
|
/**
|
|
* @brief convert cfg80211_chan_def to Band_Config
|
|
*
|
|
* @param priv A pointer to moal private structure
|
|
* @param bandcfg A pointer to (Band_Config_t structure
|
|
* @param chandef A pointer to cfg80211_chan_def structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void woal_convert_chan_to_bandconfig(moal_private *priv,
|
|
Band_Config_t *bandcfg,
|
|
struct cfg80211_chan_def *chandef)
|
|
{
|
|
ENTER();
|
|
|
|
if (chandef->chan->hw_value <= MAX_BG_CHANNEL)
|
|
bandcfg->chanBand = BAND_2GHZ;
|
|
else
|
|
bandcfg->chanBand = BAND_5GHZ;
|
|
switch (chandef->width) {
|
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
|
case NL80211_CHAN_WIDTH_20:
|
|
bandcfg->chanWidth = CHAN_BW_20MHZ;
|
|
break;
|
|
case NL80211_CHAN_WIDTH_40:
|
|
bandcfg->chanWidth = CHAN_BW_40MHZ;
|
|
if (chandef->center_freq1 > chandef->chan->center_freq)
|
|
bandcfg->chan2Offset = SEC_CHAN_ABOVE;
|
|
else
|
|
bandcfg->chan2Offset = SEC_CHAN_BELOW;
|
|
break;
|
|
case NL80211_CHAN_WIDTH_80:
|
|
bandcfg->chan2Offset = woal_get_second_channel_offset(
|
|
priv, chandef->chan->hw_value);
|
|
bandcfg->chanWidth = CHAN_BW_80MHZ;
|
|
break;
|
|
case NL80211_CHAN_WIDTH_80P80:
|
|
case NL80211_CHAN_WIDTH_160:
|
|
default:
|
|
break;
|
|
}
|
|
LEAVE();
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Enable radar detect for DFS channel
|
|
*
|
|
* @param priv A pointer to moal private structure
|
|
* @param chandef A pointer to cfg80211_chan_def structure
|
|
* @return N/A
|
|
*/
|
|
static void woal_enable_dfs_support(moal_private *priv,
|
|
struct cfg80211_chan_def *chandef)
|
|
{
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_11h_chan_rep_req *pchan_rpt_req = NULL;
|
|
mlan_ds_11h_cfg *p11h_cfg = NULL;
|
|
mlan_status status = MLAN_STATUS_SUCCESS;
|
|
ENTER();
|
|
if (!(chandef->chan->flags & IEEE80211_CHAN_RADAR)) {
|
|
PRINTM(MIOCTL, "No radar channel\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
PRINTM(MIOCTL, "start Radar detect, chan %d , Bw %d \n",
|
|
chandef->chan->hw_value, chandef->width);
|
|
|
|
req = woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_11h_cfg));
|
|
if (NULL == req) {
|
|
PRINTM(MIOCTL, "No Memory to allocate ioctl buffer\n");
|
|
LEAVE();
|
|
return;
|
|
}
|
|
p11h_cfg = (mlan_ds_11h_cfg *)req->pbuf;
|
|
pchan_rpt_req = &p11h_cfg->param.chan_rpt_req;
|
|
pchan_rpt_req->startFreq = 5000;
|
|
pchan_rpt_req->chanNum = (t_u8)chandef->chan->hw_value;
|
|
woal_convert_chan_to_bandconfig(priv, &pchan_rpt_req->bandcfg, chandef);
|
|
pchan_rpt_req->host_based = MTRUE;
|
|
pchan_rpt_req->millisec_dwell_time = 0;
|
|
|
|
p11h_cfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK;
|
|
req->req_id = MLAN_IOCTL_11H_CFG;
|
|
req->action = MLAN_ACT_SET;
|
|
/* Send Channel Check command and wait until the report is ready */
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief Prase supported rates from beacon data, set bss cfg accordingly
|
|
*
|
|
* @param priv A pointer to moal_private
|
|
* @param bss_cfg A pointer to bss configuration structure
|
|
* @param head_ie A pointer to beacon head IE buffer
|
|
* @param head_len head IE buffer length
|
|
* @param tail_ie A pointer to beacon tail IE buffer
|
|
* @param tail_len tail IE buffer length *
|
|
* @return N/A
|
|
*/
|
|
static void woal_set_uap_rates(moal_private *priv, mlan_uap_bss_param *bss_cfg,
|
|
const t_u8 *head_ie, int head_len,
|
|
const t_u8 *tail_ie, int tail_len)
|
|
{
|
|
pIEEEtypes_Header_t rate_ie;
|
|
pIEEEtypes_Header_t ext_rate_ie;
|
|
int var_offset = offsetof(struct ieee80211_mgmt, u.beacon.variable);
|
|
const u8 *var_pos = head_ie + var_offset;
|
|
int len = head_len - var_offset;
|
|
int rate_len = 0;
|
|
|
|
rate_ie = (void *)woal_parse_ie_tlv(var_pos, len, WLAN_EID_SUPP_RATES);
|
|
if (rate_ie) {
|
|
memset(bss_cfg->rates, 0, sizeof(bss_cfg->rates));
|
|
moal_memcpy_ext(priv->phandle, bss_cfg->rates, rate_ie + 1,
|
|
rate_ie->len, sizeof(bss_cfg->rates));
|
|
rate_len = MIN(rate_ie->len, sizeof(bss_cfg->rates));
|
|
}
|
|
ext_rate_ie = (void *)woal_parse_ie_tlv(tail_ie, tail_len,
|
|
WLAN_EID_EXT_SUPP_RATES);
|
|
if (ext_rate_ie) {
|
|
moal_memcpy_ext(priv->phandle, &bss_cfg->rates[rate_len],
|
|
ext_rate_ie + 1, ext_rate_ie->len,
|
|
sizeof(bss_cfg->rates) - rate_len);
|
|
rate_len += MIN(ext_rate_ie->len,
|
|
(sizeof(bss_cfg->rates) - rate_len));
|
|
}
|
|
DBG_HEXDUMP(MCMD_D, "rates", bss_cfg->rates, sizeof(bss_cfg->rates));
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
/**
|
|
* @brief initialize AP or GO bss config
|
|
*
|
|
* @param priv A pointer to moal private structure
|
|
* @param params A pointer to cfg80211_ap_settings structure
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_cfg80211_beacon_config(moal_private *priv,
|
|
struct cfg80211_ap_settings *params)
|
|
#else
|
|
/**
|
|
* @brief initialize AP or GO bss config
|
|
*
|
|
* @param priv A pointer to moal private structure
|
|
* @param params A pointer to beacon_parameters structure
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_cfg80211_beacon_config(moal_private *priv,
|
|
struct beacon_parameters *params)
|
|
#endif
|
|
{
|
|
struct wiphy *wiphy = NULL;
|
|
const t_u8 *ie = NULL;
|
|
int ret = 0, ie_len;
|
|
mlan_uap_bss_param *sys_config = NULL;
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
|
|
int i = 0;
|
|
#else
|
|
t_u8 wpa_ies;
|
|
const t_u8 *ssid_ie = NULL;
|
|
struct ieee80211_mgmt *head = NULL;
|
|
t_u16 capab_info = 0;
|
|
#endif
|
|
t_u8 rates_bg[13] = {0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18,
|
|
0x24, 0x30, 0x48, 0x60, 0x6c, 0x00};
|
|
t_u8 rates_a[9] = {0x8c, 0x12, 0x98, 0x24, 0xb0,
|
|
0x48, 0x60, 0x6c, 0x00};
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
t_u8 rates_wfd[9] = {0x8c, 0x12, 0x18, 0x24, 0x30,
|
|
0x48, 0x60, 0x6c, 0x00};
|
|
#endif
|
|
t_u8 chan2Offset = SEC_CHAN_NONE;
|
|
t_u8 enable_11n = MTRUE;
|
|
t_u16 ht_cap = 0;
|
|
t_u8 enable_11ac = MFALSE;
|
|
t_u8 vht20_40 = MFALSE;
|
|
IEEEtypes_VHTCap_t *vhtcap_ie = NULL;
|
|
IEEEtypes_VHTOprat_t *vhtopr_ie = NULL;
|
|
IEEEtypes_HECap_t *hecap_ie = NULL;
|
|
t_u8 enable_11ax = MFALSE;
|
|
t_u8 *wapi_ie = NULL;
|
|
int wapi_ie_len = 0;
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
int GoAgeoutTime = priv->phandle->params.GoAgeoutTime;
|
|
#endif
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
mlan_ds_11h_chan_nop_info chan_nop_info;
|
|
Band_Config_t bandcfg;
|
|
#endif
|
|
ENTER();
|
|
|
|
if (!params) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
ie = ((struct cfg80211_ap_settings *)params)->beacon.tail;
|
|
ie_len = ((struct cfg80211_ap_settings *)params)->beacon.tail_len;
|
|
#else
|
|
ie = ((struct beacon_parameters *)params)->tail;
|
|
ie_len = ((struct beacon_parameters *)params)->tail_len;
|
|
#endif
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
wapi_ie = (t_u8 *)woal_parse_ie_tlv(params->beacon.tail,
|
|
params->beacon.tail_len, WAPI_IE);
|
|
#else
|
|
wapi_ie = (t_u8 *)woal_parse_ie_tlv(params->tail, params->tail_len,
|
|
WAPI_IE);
|
|
#endif
|
|
if (wapi_ie) {
|
|
wapi_ie_len = *(wapi_ie + 1) + 2;
|
|
if (MLAN_STATUS_FAILURE ==
|
|
woal_set_get_gen_ie(priv, MLAN_ACT_SET, wapi_ie,
|
|
&wapi_ie_len, MOAL_IOCTL_WAIT)) {
|
|
PRINTM(MERROR, "Failed to set wapi ie\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
wiphy = priv->phandle->wiphy;
|
|
if (priv->bss_type != MLAN_BSS_TYPE_UAP
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
&& priv->bss_type != MLAN_BSS_TYPE_WIFIDIRECT
|
|
#endif
|
|
) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
sys_config = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
|
|
if (!sys_config) {
|
|
PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
/* Initialize the uap bss values which are uploaded from firmware */
|
|
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_GET,
|
|
MOAL_IOCTL_WAIT,
|
|
sys_config)) {
|
|
PRINTM(MERROR, "Error getting AP confiruration\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (priv->phandle->params.uap_max_sta)
|
|
sys_config->max_sta_count = priv->phandle->params.uap_max_sta;
|
|
|
|
/* Setting the default values */
|
|
sys_config->channel = 6;
|
|
sys_config->preamble_type = 0;
|
|
sys_config->mgmt_ie_passthru_mask = priv->mgmt_subtype_mask;
|
|
moal_memcpy_ext(priv->phandle, sys_config->mac_addr, priv->current_addr,
|
|
ETH_ALEN, sizeof(sys_config->mac_addr));
|
|
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT && GoAgeoutTime) {
|
|
sys_config->sta_ageout_timer = GoAgeoutTime;
|
|
sys_config->ps_sta_ageout_timer = GoAgeoutTime;
|
|
}
|
|
#endif
|
|
/* Set frag_threshold, rts_threshold, and retry limit */
|
|
sys_config->frag_threshold = wiphy->frag_threshold;
|
|
sys_config->rts_threshold = wiphy->rts_threshold;
|
|
sys_config->retry_limit = wiphy->retry_long;
|
|
if (sys_config->frag_threshold == (t_u16)MLAN_FRAG_RTS_DISABLED) {
|
|
sys_config->frag_threshold = MLAN_FRAG_MAX_VALUE;
|
|
}
|
|
if (sys_config->rts_threshold == (t_u16)MLAN_FRAG_RTS_DISABLED) {
|
|
sys_config->rts_threshold = MLAN_RTS_MAX_VALUE;
|
|
}
|
|
|
|
if (priv->bss_type == MLAN_BSS_TYPE_UAP) {
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
if (params->beacon_interval)
|
|
sys_config->beacon_period = params->beacon_interval;
|
|
#else
|
|
if (params->interval)
|
|
sys_config->beacon_period = params->interval;
|
|
#endif
|
|
if (params->dtim_period)
|
|
sys_config->dtim_period = params->dtim_period;
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
/** back up ap's channel */
|
|
moal_memcpy_ext(priv->phandle, &priv->chan, ¶ms->chandef,
|
|
sizeof(struct cfg80211_chan_def), sizeof(priv->chan));
|
|
#endif
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
if (priv->phandle->usr_nop_period_sec) {
|
|
PRINTM(MCMND, "Checking if AP's channel %d is under NOP\n",
|
|
priv->channel);
|
|
woal_convert_chan_to_bandconfig(priv, &bandcfg,
|
|
¶ms->chandef);
|
|
memset(&chan_nop_info, 0, sizeof(chan_nop_info));
|
|
chan_nop_info.curr_chan = priv->channel;
|
|
chan_nop_info.chan_width = bandcfg.chanWidth;
|
|
if (params->chandef.width >= NL80211_CHAN_WIDTH_20)
|
|
chan_nop_info.new_chan.is_11n_enabled = MTRUE;
|
|
chan_nop_info.new_chan.bandcfg = bandcfg;
|
|
woal_uap_get_channel_nop_info(priv, MOAL_IOCTL_WAIT,
|
|
&chan_nop_info);
|
|
if (chan_nop_info.chan_under_nop) {
|
|
PRINTM(MCMND,
|
|
"cfg80211: Channel %d is under NOP, New channel=%d\n",
|
|
priv->channel, chan_nop_info.new_chan.channel);
|
|
priv->chan_under_nop = chan_nop_info.chan_under_nop;
|
|
priv->channel = chan_nop_info.new_chan.channel;
|
|
woal_chandef_create(priv, &priv->chan,
|
|
&chan_nop_info.new_chan);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (priv->channel) {
|
|
memset(sys_config->rates, 0, sizeof(sys_config->rates));
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
switch (priv->chan.width) {
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)
|
|
case NL80211_CHAN_WIDTH_5:
|
|
case NL80211_CHAN_WIDTH_10:
|
|
#endif
|
|
case NL80211_CHAN_WIDTH_20_NOHT:
|
|
enable_11n = MFALSE;
|
|
break;
|
|
case NL80211_CHAN_WIDTH_20:
|
|
break;
|
|
case NL80211_CHAN_WIDTH_40:
|
|
if (priv->chan.center_freq1 <
|
|
priv->chan.chan->center_freq)
|
|
chan2Offset = SEC_CHAN_BELOW;
|
|
else
|
|
chan2Offset = SEC_CHAN_ABOVE;
|
|
break;
|
|
case NL80211_CHAN_WIDTH_80:
|
|
case NL80211_CHAN_WIDTH_80P80:
|
|
case NL80211_CHAN_WIDTH_160:
|
|
chan2Offset = woal_get_second_channel_offset(
|
|
priv, priv->channel);
|
|
break;
|
|
default:
|
|
PRINTM(MWARN, "Unknown channel width: %d\n",
|
|
priv->chan.width);
|
|
break;
|
|
}
|
|
#else
|
|
switch (params->channel_type) {
|
|
case NL80211_CHAN_NO_HT:
|
|
enable_11n = MFALSE;
|
|
break;
|
|
case NL80211_CHAN_HT20:
|
|
break;
|
|
case NL80211_CHAN_HT40PLUS:
|
|
chan2Offset = SEC_CHAN_ABOVE;
|
|
break;
|
|
case NL80211_CHAN_HT40MINUS:
|
|
chan2Offset = SEC_CHAN_BELOW;
|
|
break;
|
|
default:
|
|
PRINTM(MWARN, "Unknown channel type: %d\n",
|
|
params->channel_type);
|
|
break;
|
|
}
|
|
#endif
|
|
#endif /* CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) */
|
|
|
|
sys_config->channel = priv->channel;
|
|
if (priv->channel <= MAX_BG_CHANNEL) {
|
|
sys_config->bandcfg.chanBand = BAND_2GHZ;
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT)
|
|
moal_memcpy_ext(priv->phandle,
|
|
sys_config->rates, rates_wfd,
|
|
sizeof(rates_wfd),
|
|
sizeof(sys_config->rates));
|
|
else
|
|
#endif
|
|
moal_memcpy_ext(priv->phandle,
|
|
sys_config->rates, rates_bg,
|
|
sizeof(rates_bg),
|
|
sizeof(sys_config->rates));
|
|
} else {
|
|
sys_config->bandcfg.chanBand = BAND_5GHZ;
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
|
|
chan2Offset = woal_get_second_channel_offset(
|
|
priv, priv->channel);
|
|
#endif
|
|
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
/* Force enable 40MHZ on WFD interface */
|
|
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT)
|
|
chan2Offset = woal_get_second_channel_offset(
|
|
priv, priv->channel);
|
|
#endif
|
|
#endif
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
if (priv->bss_type == MLAN_BSS_TYPE_WIFIDIRECT)
|
|
moal_memcpy_ext(priv->phandle,
|
|
sys_config->rates, rates_wfd,
|
|
sizeof(rates_wfd),
|
|
sizeof(sys_config->rates));
|
|
else
|
|
#endif
|
|
moal_memcpy_ext(priv->phandle,
|
|
sys_config->rates, rates_a,
|
|
sizeof(rates_a),
|
|
sizeof(sys_config->rates));
|
|
}
|
|
|
|
/* Replaced with rate from userspace, if exist */
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
woal_set_uap_rates(priv, sys_config, params->beacon.head,
|
|
params->beacon.head_len, params->beacon.tail,
|
|
params->beacon.tail_len);
|
|
#else
|
|
woal_set_uap_rates(priv, sys_config, params->head,
|
|
params->head_len, params->tail,
|
|
params->tail_len);
|
|
#endif
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
enable_11ac = woal_check_11ac_capability(
|
|
priv, sys_config->bandcfg.chanBand, params);
|
|
if (enable_11ac &&
|
|
((priv->chan.width == NL80211_CHAN_WIDTH_20) ||
|
|
(priv->chan.width == NL80211_CHAN_WIDTH_40)))
|
|
vht20_40 = MTRUE;
|
|
#else
|
|
enable_11ac = woal_check_11ac_capability(
|
|
priv, sys_config->bandcfg.chanBand);
|
|
#endif
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
enable_11ax = woal_check_11ax_capability(
|
|
priv, sys_config->bandcfg.chanBand, params);
|
|
#endif
|
|
|
|
/* Disable GreenField by default */
|
|
sys_config->ht_cap_info = 0x10c;
|
|
if (enable_11n)
|
|
sys_config->ht_cap_info |= 0x20;
|
|
if (chan2Offset) {
|
|
sys_config->bandcfg.chan2Offset = chan2Offset;
|
|
sys_config->ht_cap_info |= 0x1042;
|
|
sys_config->ampdu_param = 3;
|
|
} else {
|
|
sys_config->bandcfg.chan2Offset = 0;
|
|
}
|
|
ht_cap = woal_get_htcap_info(ie, ie_len);
|
|
if (ht_cap) {
|
|
if (sys_config->bandcfg.chanBand == BAND_2GHZ)
|
|
sys_config->ht_cap_info =
|
|
(ht_cap &
|
|
(wiphy->bands[IEEE80211_BAND_2GHZ]
|
|
->ht_cap.cap &
|
|
0x13ff)) |
|
|
0x0c;
|
|
else
|
|
sys_config->ht_cap_info =
|
|
(ht_cap &
|
|
(wiphy->bands[IEEE80211_BAND_5GHZ]
|
|
->ht_cap.cap &
|
|
0x13ff)) |
|
|
0x0c;
|
|
}
|
|
PRINTM(MCMND,
|
|
"11n=%d, ht_cap=0x%x, channel=%d, bandcfg:chanBand=0x%x chanWidth=0x%x chan2Offset=0x%x scanMode=0x%x\n",
|
|
enable_11n, sys_config->ht_cap_info, priv->channel,
|
|
sys_config->bandcfg.chanBand,
|
|
sys_config->bandcfg.chanWidth,
|
|
sys_config->bandcfg.chan2Offset,
|
|
sys_config->bandcfg.scanMode);
|
|
}
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
|
|
if (!params->ssid || !params->ssid_len) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
moal_memcpy_ext(priv->phandle, sys_config->ssid.ssid, params->ssid,
|
|
MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len),
|
|
sizeof(sys_config->ssid.ssid));
|
|
sys_config->ssid.ssid_len = MIN(MLAN_MAX_SSID_LENGTH, params->ssid_len);
|
|
/**
|
|
* hidden_ssid=0: broadcast SSID in beacons.
|
|
* hidden_ssid=1: send empty SSID (length=0) in beacon.
|
|
* hidden_ssid=2: clear SSID (ACSII 0), but keep the original length
|
|
*/
|
|
if (!params->hidden_ssid)
|
|
sys_config->bcast_ssid_ctl = 1;
|
|
else if (params->hidden_ssid == 1)
|
|
sys_config->bcast_ssid_ctl = 0;
|
|
else if (params->hidden_ssid == 2)
|
|
sys_config->bcast_ssid_ctl = 2;
|
|
switch (params->auth_type) {
|
|
case NL80211_AUTHTYPE_SHARED_KEY:
|
|
sys_config->auth_mode = MLAN_AUTH_MODE_SHARED;
|
|
break;
|
|
case NL80211_AUTHTYPE_AUTOMATIC:
|
|
sys_config->auth_mode = MLAN_AUTH_MODE_AUTO;
|
|
break;
|
|
case NL80211_AUTHTYPE_OPEN_SYSTEM:
|
|
default:
|
|
sys_config->auth_mode = MLAN_AUTH_MODE_OPEN;
|
|
break;
|
|
}
|
|
|
|
sys_config->protocol = PROTOCOL_NO_SECURITY;
|
|
if ((params->crypto.wpa_versions & NL80211_WPA_VERSION_1) &&
|
|
(params->crypto.wpa_versions & NL80211_WPA_VERSION_2))
|
|
sys_config->protocol = PROTOCOL_WPA | PROTOCOL_WPA2;
|
|
else if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
|
|
sys_config->protocol = PROTOCOL_WPA2;
|
|
else if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
|
|
sys_config->protocol = PROTOCOL_WPA;
|
|
if (params->crypto.n_akm_suites ||
|
|
(params->privacy && params->crypto.wpa_versions))
|
|
woal_find_wpa_ies(ie, ie_len, sys_config);
|
|
for (i = 0; i < params->crypto.n_akm_suites; i++) {
|
|
switch (params->crypto.akm_suites[i]) {
|
|
case WLAN_AKM_SUITE_8021X:
|
|
sys_config->key_mgmt |= KEY_MGMT_EAP;
|
|
break;
|
|
case WLAN_AKM_SUITE_PSK:
|
|
sys_config->key_mgmt |= KEY_MGMT_PSK;
|
|
break;
|
|
}
|
|
}
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa = 0;
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa2 = 0;
|
|
for (i = 0; i < params->crypto.n_ciphers_pairwise; i++) {
|
|
switch (params->crypto.ciphers_pairwise[i]) {
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
break;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa |=
|
|
CIPHER_TKIP;
|
|
if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa2 |=
|
|
CIPHER_TKIP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
if (params->crypto.wpa_versions & NL80211_WPA_VERSION_1)
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa |=
|
|
CIPHER_AES_CCMP;
|
|
if (params->crypto.wpa_versions & NL80211_WPA_VERSION_2)
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa2 |=
|
|
CIPHER_AES_CCMP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
sys_config->protocol = PROTOCOL_WAPI;
|
|
break;
|
|
}
|
|
}
|
|
switch (params->crypto.cipher_group) {
|
|
case WLAN_CIPHER_SUITE_WEP40:
|
|
case WLAN_CIPHER_SUITE_WEP104:
|
|
if ((priv->cipher == WLAN_CIPHER_SUITE_WEP40) ||
|
|
(priv->cipher == WLAN_CIPHER_SUITE_WEP104)) {
|
|
sys_config->protocol = PROTOCOL_STATIC_WEP;
|
|
sys_config->key_mgmt = KEY_MGMT_NONE;
|
|
sys_config->wpa_cfg.length = 0;
|
|
moal_memcpy_ext(priv->phandle,
|
|
&sys_config->wep_cfg.key0,
|
|
&priv->uap_wep_key[0], sizeof(wep_key),
|
|
sizeof(sys_config->wep_cfg.key0));
|
|
moal_memcpy_ext(priv->phandle,
|
|
&sys_config->wep_cfg.key1,
|
|
&priv->uap_wep_key[1], sizeof(wep_key),
|
|
sizeof(sys_config->wep_cfg.key1));
|
|
moal_memcpy_ext(priv->phandle,
|
|
&sys_config->wep_cfg.key2,
|
|
&priv->uap_wep_key[2], sizeof(wep_key),
|
|
sizeof(sys_config->wep_cfg.key2));
|
|
moal_memcpy_ext(priv->phandle,
|
|
&sys_config->wep_cfg.key3,
|
|
&priv->uap_wep_key[3], sizeof(wep_key),
|
|
sizeof(sys_config->wep_cfg.key3));
|
|
}
|
|
break;
|
|
case WLAN_CIPHER_SUITE_TKIP:
|
|
sys_config->wpa_cfg.group_cipher = CIPHER_TKIP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_CCMP:
|
|
sys_config->wpa_cfg.group_cipher = CIPHER_AES_CCMP;
|
|
break;
|
|
case WLAN_CIPHER_SUITE_SMS4:
|
|
sys_config->protocol = PROTOCOL_WAPI;
|
|
break;
|
|
}
|
|
#else
|
|
/* Since in Android ICS 4.0.1's wpa_supplicant, there is no way to set ssid
|
|
* when GO (AP) starts up, so get it from beacon head parameter
|
|
* TODO: right now use hard code
|
|
* 24 -- ieee80211 header lenth, 12 -- fixed element length for beacon
|
|
*/
|
|
#define BEACON_IE_OFFSET 36
|
|
/* Find SSID in head
|
|
* SSID IE id: 0, right now use hard code
|
|
*/
|
|
ssid_ie = woal_parse_ie_tlv(params->head + BEACON_IE_OFFSET,
|
|
params->head_len - BEACON_IE_OFFSET, 0);
|
|
|
|
if (!ssid_ie) {
|
|
PRINTM(MERROR, "No ssid IE found.\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
if (*(ssid_ie + 1) > 32) {
|
|
PRINTM(MERROR, "ssid len error: %d\n", *(ssid_ie + 1));
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
moal_memcpy_ext(priv->phandle, sys_config->ssid.ssid, ssid_ie + 2,
|
|
*(ssid_ie + 1), sizeof(sys_config->ssid.ssid));
|
|
sys_config->ssid.ssid_len = *(ssid_ie + 1);
|
|
head = (struct ieee80211_mgmt *)params->head;
|
|
|
|
capab_info = le16_to_cpu(head->u.beacon.capab_info);
|
|
PRINTM(MIOCTL, "capab_info=0x%x\n", head->u.beacon.capab_info);
|
|
sys_config->auth_mode = MLAN_AUTH_MODE_OPEN;
|
|
/** For ICS, we don't support OPEN mode */
|
|
if ((priv->cipher == WLAN_CIPHER_SUITE_WEP40) ||
|
|
(priv->cipher == WLAN_CIPHER_SUITE_WEP104)) {
|
|
sys_config->protocol = PROTOCOL_STATIC_WEP;
|
|
sys_config->key_mgmt = KEY_MGMT_NONE;
|
|
sys_config->.wpa_cfg.length = 0;
|
|
moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key0,
|
|
&priv->uap_wep_key[0], sizeof(wep_key),
|
|
sizeof(sys_config->wep_cfg.key0));
|
|
moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key1,
|
|
&priv->uap_wep_key[1], sizeof(wep_key),
|
|
sizeof(sys_config->wep_cfg.key1));
|
|
moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key2,
|
|
&priv->uap_wep_key[2], sizeof(wep_key),
|
|
sizeof(sys_config->wep_cfg.key2));
|
|
moal_memcpy_ext(priv->phandle, &sys_config->wep_cfg.key3,
|
|
&priv->uap_wep_key[3], sizeof(wep_key),
|
|
sizeof(sys_config->wep_cfg.key3));
|
|
} else {
|
|
/** Get cipher and key_mgmt from RSN/WPA IE */
|
|
if (capab_info & WLAN_CAPABILITY_PRIVACY) {
|
|
wpa_ies = woal_find_wpa_ies(
|
|
params->tail, params->tail_len, sys_config);
|
|
if (wpa_ies == MFALSE) {
|
|
/* hard code setting to wpa2-psk */
|
|
sys_config->protocol = PROTOCOL_WPA2;
|
|
sys_config->key_mgmt = KEY_MGMT_PSK;
|
|
sys_config->wpa_cfg.pairwise_cipher_wpa2 =
|
|
CIPHER_AES_CCMP;
|
|
sys_config->wpa_cfg.group_cipher =
|
|
CIPHER_AES_CCMP;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
|
|
/*find and set wmm ie*/
|
|
woal_set_wmm_ies(priv, ie, ie_len, sys_config);
|
|
}
|
|
/* If the security mode is configured as WEP or WPA-PSK,
|
|
* it will disable 11n automatically, and if configured as
|
|
* open(off) or wpa2-psk, it will automatically enable 11n */
|
|
if ((sys_config->protocol == PROTOCOL_STATIC_WEP) ||
|
|
(sys_config->protocol == PROTOCOL_WPA))
|
|
enable_11n = MFALSE;
|
|
if (!enable_11n) {
|
|
woal_set_uap_ht_tx_cfg(priv, sys_config->bandcfg, ht_cap,
|
|
MFALSE);
|
|
woal_uap_set_11n_status(priv, sys_config, MLAN_ACT_DISABLE);
|
|
} else {
|
|
woal_set_uap_ht_tx_cfg(priv, sys_config->bandcfg, ht_cap,
|
|
MTRUE);
|
|
woal_uap_set_11n_status(priv, sys_config, MLAN_ACT_ENABLE);
|
|
woal_set_get_tx_bf_cap(priv, MLAN_ACT_GET,
|
|
&sys_config->tx_bf_cap);
|
|
}
|
|
if (enable_11ac && enable_11n) {
|
|
vhtcap_ie = woal_get_vhtcap_info(ie, ie_len);
|
|
vhtopr_ie = woal_get_vht_oprat_ie(ie, ie_len);
|
|
// Enable VHT80
|
|
if (vhtopr_ie && vhtopr_ie->chan_width)
|
|
vht20_40 = 0;
|
|
woal_uap_set_11ac_status(priv, MLAN_ACT_ENABLE, vht20_40,
|
|
vhtcap_ie);
|
|
} else {
|
|
woal_uap_set_11ac_status(priv, MLAN_ACT_DISABLE, vht20_40,
|
|
NULL);
|
|
}
|
|
if (enable_11ax && enable_11n) {
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
|
hecap_ie = (IEEEtypes_HECap_t *)woal_parse_ext_ie_tlv(
|
|
ie, ie_len, HE_CAPABILITY);
|
|
#endif
|
|
woal_uap_set_11ax_status(priv, MLAN_ACT_ENABLE,
|
|
sys_config->bandcfg.chanBand,
|
|
hecap_ie);
|
|
} else
|
|
woal_uap_set_11ax_status(priv, MLAN_ACT_DISABLE,
|
|
sys_config->bandcfg.chanBand, NULL);
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
|
|
if (params->inactivity_timeout) {
|
|
sys_config->sta_ageout_timer = params->inactivity_timeout * 10;
|
|
sys_config->ps_sta_ageout_timer =
|
|
params->inactivity_timeout * 10;
|
|
}
|
|
PRINTM(MIOCTL, "inactivity_timeout=%d\n", params->inactivity_timeout);
|
|
PRINTM(MIOCTL, "sta_ageout_timer=%d ps_sta_ageout_timer=%d\n",
|
|
sys_config->sta_ageout_timer, sys_config->ps_sta_ageout_timer);
|
|
#endif
|
|
|
|
if (MLAN_STATUS_SUCCESS != woal_set_get_sys_config(priv, MLAN_ACT_SET,
|
|
MOAL_IOCTL_WAIT,
|
|
sys_config)) {
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
/** Set wacp_mode for uAP/P2P-GO */
|
|
if (priv->phandle->params.wacp_mode) {
|
|
PRINTM(MIOCTL, "wacp_mode: %d\n",
|
|
priv->phandle->params.wacp_mode);
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_wacp_mode(priv, MOAL_IOCTL_WAIT)) {
|
|
PRINTM(MERROR, "Set wacp_mode failed\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
|
|
woal_enable_dfs_support(priv, &priv->chan);
|
|
#endif
|
|
done:
|
|
kfree(sys_config);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
/**
|
|
* @brief Request the driver to add a monitor interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param name_assign_type Interface name assignment type
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
* @param new_dev Netdevice to be passed out
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_cfg80211_add_mon_if(struct wiphy *wiphy, const char *name,
|
|
unsigned char name_assign_type, u32 *flags,
|
|
struct vif_params *params,
|
|
struct net_device **new_dev)
|
|
#else
|
|
/**
|
|
* @brief Request the driver to add a monitor interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
* @param new_dev Netdevice to be passed out
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_cfg80211_add_mon_if(struct wiphy *wiphy,
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
|
|
const
|
|
#endif
|
|
char *name,
|
|
u32 *flags, struct vif_params *params,
|
|
struct net_device **new_dev)
|
|
#endif
|
|
{
|
|
int ret = 0;
|
|
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
|
|
moal_private *priv =
|
|
(moal_private *)woal_get_priv(handle, MLAN_BSS_ROLE_STA);
|
|
monitor_iface *mon_if = NULL;
|
|
struct net_device *ndev = NULL;
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
chan_band_info chan_info;
|
|
#endif
|
|
unsigned char name_assign_type_tmp = 0;
|
|
|
|
ENTER();
|
|
|
|
ASSERT_RTNL();
|
|
|
|
if (handle->mon_if) {
|
|
PRINTM(MERROR, "%s: monitor interface exist: %s basedev %s\n",
|
|
__func__, handle->mon_if->mon_ndev->name,
|
|
handle->mon_if->base_ndev->name);
|
|
ret = -EFAULT;
|
|
goto fail;
|
|
}
|
|
if (!priv) {
|
|
PRINTM(MERROR, "add_mon_if: priv is NULL\n");
|
|
ret = -EFAULT;
|
|
goto fail;
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
name_assign_type_tmp = name_assign_type;
|
|
#endif
|
|
mon_if = woal_prepare_mon_if(priv, name, name_assign_type_tmp,
|
|
CHANNEL_SPEC_SNIFFER_MODE);
|
|
if (!mon_if) {
|
|
PRINTM(MFATAL, "Prepare mon_if fail.\n");
|
|
goto fail;
|
|
}
|
|
ndev = mon_if->mon_ndev;
|
|
dev_net_set(ndev, wiphy_net(wiphy));
|
|
|
|
moal_memcpy_ext(priv->phandle, ndev->perm_addr, wiphy->perm_addr,
|
|
ETH_ALEN, sizeof(ndev->perm_addr));
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
|
|
eth_hw_addr_set(ndev, ndev->perm_addr);
|
|
#else
|
|
moal_memcpy_ext(priv->phandle, ndev->dev_addr, ndev->perm_addr,
|
|
ETH_ALEN, MAX_ADDR_LEN);
|
|
#endif
|
|
SET_NETDEV_DEV(ndev, wiphy_dev(wiphy));
|
|
ndev->ieee80211_ptr = &mon_if->wdev;
|
|
mon_if->wdev.iftype = NL80211_IFTYPE_MONITOR;
|
|
mon_if->wdev.wiphy = wiphy;
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
/* Set default band channel config */
|
|
mon_if->band_chan_cfg.band = BAND_B | BAND_G;
|
|
mon_if->band_chan_cfg.band |= BAND_GN;
|
|
mon_if->band_chan_cfg.channel = 1;
|
|
mon_if->band_chan_cfg.chan_bandwidth = CHANNEL_BW_20MHZ;
|
|
memset(&chan_info, 0x00, sizeof(chan_info));
|
|
chan_info.channel = 1;
|
|
chan_info.is_11n_enabled = MTRUE;
|
|
if (MLAN_STATUS_FAILURE ==
|
|
woal_chandef_create(priv, &mon_if->chandef, &chan_info)) {
|
|
ret = -EFAULT;
|
|
goto fail;
|
|
}
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_net_monitor(priv, MOAL_IOCTL_WAIT,
|
|
CHANNEL_SPEC_SNIFFER_MODE, 0x7,
|
|
&mon_if->band_chan_cfg)) {
|
|
PRINTM(MERROR, "%s: woal_set_net_monitor fail\n", __func__);
|
|
ret = -EFAULT;
|
|
goto fail;
|
|
}
|
|
#endif
|
|
|
|
ret = register_netdevice(ndev);
|
|
if (ret) {
|
|
PRINTM(MFATAL, "register net_device failed, ret=%d\n", ret);
|
|
free_netdev(ndev);
|
|
goto fail;
|
|
}
|
|
|
|
handle->mon_if = mon_if;
|
|
|
|
if (new_dev)
|
|
*new_dev = ndev;
|
|
|
|
fail:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
|
|
/**
|
|
* @brief Callback function for virtual interface
|
|
* setup
|
|
*
|
|
* @param dev A pointer to structure net_device
|
|
*
|
|
* @return N/A
|
|
*/
|
|
static void woal_virt_if_setup(struct net_device *dev)
|
|
{
|
|
ENTER();
|
|
ether_setup(dev);
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 11, 9)
|
|
dev->needs_free_netdev = true;
|
|
#else
|
|
dev->destructor = free_netdev;
|
|
#endif
|
|
LEAVE();
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
/**
|
|
* @brief This function adds a new interface. It will
|
|
* allocate, initialize and register the device.
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
* @param bss_index BSS index number
|
|
* @param name_assign_type Interface name assignment type
|
|
* @param bss_type BSS type
|
|
*
|
|
* @return A pointer to the new priv structure
|
|
*/
|
|
static moal_private *woal_alloc_virt_interface(moal_handle *handle,
|
|
t_u8 bss_index,
|
|
unsigned char name_assign_type,
|
|
t_u8 bss_type, const char *name)
|
|
#else
|
|
/**
|
|
* @brief This function adds a new interface. It will
|
|
* allocate, initialize and register the device.
|
|
*
|
|
* @param handle A pointer to moal_handle structure
|
|
* @param bss_index BSS index number
|
|
* @param bss_type BSS type
|
|
*
|
|
* @return A pointer to the new priv structure
|
|
*/
|
|
moal_private *woal_alloc_virt_interface(moal_handle *handle, t_u8 bss_index,
|
|
t_u8 bss_type,
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
|
|
const
|
|
#endif
|
|
char *name)
|
|
#endif
|
|
{
|
|
struct net_device *dev = NULL;
|
|
moal_private *priv = NULL;
|
|
ENTER();
|
|
|
|
#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 29)
|
|
#ifndef MAX_WMM_QUEUE
|
|
#define MAX_WMM_QUEUE 4
|
|
#endif
|
|
/* Allocate an Ethernet device */
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
dev = alloc_netdev_mq(sizeof(moal_private), name, name_assign_type,
|
|
woal_virt_if_setup, MAX_WMM_QUEUE);
|
|
#else
|
|
dev = alloc_netdev_mq(sizeof(moal_private), name, NET_NAME_UNKNOWN,
|
|
woal_virt_if_setup, MAX_WMM_QUEUE);
|
|
#endif
|
|
#else
|
|
dev = alloc_netdev_mq(sizeof(moal_private), name, woal_virt_if_setup,
|
|
MAX_WMM_QUEUE);
|
|
#endif
|
|
#else
|
|
dev = alloc_netdev(sizeof(moal_private), name, woal_virt_if_setup);
|
|
#endif
|
|
if (!dev) {
|
|
PRINTM(MFATAL, "Init virtual ethernet device failed\n");
|
|
goto error;
|
|
}
|
|
/* Allocate device name */
|
|
if ((dev_alloc_name(dev, name) < 0)) {
|
|
PRINTM(MERROR, "Could not allocate device name\n");
|
|
goto error;
|
|
}
|
|
|
|
priv = (moal_private *)netdev_priv(dev);
|
|
/* Save the priv to handle */
|
|
handle->priv[bss_index] = priv;
|
|
|
|
/* Use the same handle structure */
|
|
priv->phandle = handle;
|
|
priv->netdev = dev;
|
|
priv->bss_index = bss_index;
|
|
priv->bss_type = bss_type;
|
|
priv->bss_role = MLAN_BSS_ROLE_STA;
|
|
|
|
INIT_LIST_HEAD(&priv->tcp_sess_queue);
|
|
spin_lock_init(&priv->tcp_sess_lock);
|
|
|
|
INIT_LIST_HEAD(&priv->tx_stat_queue);
|
|
spin_lock_init(&priv->tx_stat_lock);
|
|
INIT_LIST_HEAD(&priv->mcast_list);
|
|
spin_lock_init(&priv->mcast_lock);
|
|
|
|
spin_lock_init(&priv->connect_lock);
|
|
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
|
|
SET_MODULE_OWNER(dev);
|
|
#endif
|
|
|
|
PRINTM(MCMND, "Alloc virtual interface%s\n", dev->name);
|
|
|
|
LEAVE();
|
|
return priv;
|
|
error:
|
|
if (dev)
|
|
free_netdev(dev);
|
|
LEAVE();
|
|
return NULL;
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
/**
|
|
* @brief Request the driver to add a virtual interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param name_assign_type Interface name assignment type
|
|
* @param type Virtual interface type
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
* @param new_dev new net_device to return
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
static int woal_cfg80211_add_virt_if(struct wiphy *wiphy, const char *name,
|
|
unsigned char name_assign_type,
|
|
enum nl80211_iftype type, u32 *flags,
|
|
struct vif_params *params,
|
|
struct net_device **new_dev)
|
|
#else
|
|
/**
|
|
* @brief Request the driver to add a virtual interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param type Virtual interface type
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
* @param new_dev new net_device to return
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
int woal_cfg80211_add_virt_if(struct wiphy *wiphy,
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
|
|
const
|
|
#endif
|
|
char *name,
|
|
enum nl80211_iftype type, u32 *flags,
|
|
struct vif_params *params,
|
|
struct net_device **new_dev)
|
|
#endif
|
|
{
|
|
int ret = 0;
|
|
struct net_device *ndev = NULL;
|
|
moal_private *priv = NULL, *new_priv = NULL;
|
|
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
|
|
struct wireless_dev *wdev = NULL;
|
|
moal_private *vir_priv;
|
|
int i = 0;
|
|
|
|
ENTER();
|
|
ASSERT_RTNL();
|
|
priv = woal_get_vir_priv_bss_type(handle, MLAN_BSS_TYPE_WIFIDIRECT);
|
|
if (priv && priv->bss_role == MLAN_BSS_ROLE_UAP &&
|
|
priv->bss_started == MTRUE) {
|
|
if (handle->pref_mac)
|
|
handle = (moal_handle *)handle->pref_mac;
|
|
}
|
|
priv = (moal_private *)woal_get_priv_bss_type(handle,
|
|
MLAN_BSS_TYPE_WIFIDIRECT);
|
|
if (!priv || !priv->phandle) {
|
|
PRINTM(MERROR, "priv or handle is NULL\n");
|
|
LEAVE();
|
|
return -EFAULT;
|
|
}
|
|
if (priv->phandle->drv_mode.intf_num == priv->phandle->priv_num) {
|
|
PRINTM(MERROR, "max virtual interface limit reached\n");
|
|
for (i = 0; i < priv->phandle->priv_num; i++) {
|
|
vir_priv = priv->phandle->priv[i];
|
|
if (vir_priv->bss_virtual) {
|
|
woal_cfg80211_del_virt_if(wiphy,
|
|
vir_priv->netdev);
|
|
break;
|
|
}
|
|
}
|
|
if (priv->phandle->drv_mode.intf_num ==
|
|
priv->phandle->priv_num) {
|
|
LEAVE();
|
|
return -ENOMEM;
|
|
}
|
|
}
|
|
PRINTM(MMSG, "Add virtual interface %s\n", name);
|
|
if ((type != NL80211_IFTYPE_P2P_CLIENT) &&
|
|
(type != NL80211_IFTYPE_P2P_GO)) {
|
|
PRINTM(MERROR, "Invalid iftype: %d\n", type);
|
|
LEAVE();
|
|
return -EINVAL;
|
|
}
|
|
|
|
handle = priv->phandle;
|
|
/* Cancel previous scan req */
|
|
woal_cancel_scan(priv, MOAL_IOCTL_WAIT);
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
new_priv = woal_alloc_virt_interface(handle, handle->priv_num,
|
|
name_assign_type,
|
|
MLAN_BSS_TYPE_WIFIDIRECT, name);
|
|
#else
|
|
new_priv = woal_alloc_virt_interface(handle, handle->priv_num,
|
|
MLAN_BSS_TYPE_WIFIDIRECT, name);
|
|
#endif
|
|
if (!new_priv) {
|
|
PRINTM(MERROR, "Add virtual interface fail.");
|
|
LEAVE();
|
|
return -EFAULT;
|
|
}
|
|
handle->priv_num++;
|
|
|
|
wdev = (struct wireless_dev *)&new_priv->w_dev;
|
|
memset(wdev, 0, sizeof(struct wireless_dev));
|
|
ndev = new_priv->netdev;
|
|
SET_NETDEV_DEV(ndev, wiphy_dev(wiphy));
|
|
ndev->ieee80211_ptr = wdev;
|
|
wdev->iftype = type;
|
|
wdev->wiphy = wiphy;
|
|
new_priv->wdev = wdev;
|
|
new_priv->bss_virtual = MTRUE;
|
|
new_priv->pa_netdev = priv->netdev;
|
|
|
|
/* Create workqueue for main process */
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
|
|
new_priv->mclist_workqueue =
|
|
alloc_workqueue("MCLIST_WORK_QUEUE",
|
|
WQ_HIGHPRI | WQ_MEM_RECLAIM | WQ_UNBOUND, 1);
|
|
#else
|
|
new_priv->mclist_workqueue = create_workqueue("MCLIST_WORK_QUEUE");
|
|
#endif
|
|
if (!new_priv->mclist_workqueue) {
|
|
PRINTM(MERROR, "cannot alloc mclist workqueue \n");
|
|
return -EFAULT;
|
|
}
|
|
MLAN_INIT_WORK(&new_priv->mclist_work, woal_mclist_work_queue);
|
|
|
|
woal_init_sta_dev(ndev, new_priv);
|
|
|
|
/* Initialize priv structure */
|
|
woal_init_priv(new_priv, MOAL_IOCTL_WAIT);
|
|
/** Init to GO/CLIENT mode */
|
|
if (type == NL80211_IFTYPE_P2P_CLIENT)
|
|
woal_cfg80211_init_p2p_client(new_priv);
|
|
else if (type == NL80211_IFTYPE_P2P_GO)
|
|
woal_cfg80211_init_p2p_go(new_priv);
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
|
ret = cfg80211_register_netdevice(ndev);
|
|
#else
|
|
ret = register_netdevice(ndev);
|
|
#endif
|
|
if (ret) {
|
|
handle->priv[new_priv->bss_index] = NULL;
|
|
handle->priv_num--;
|
|
if (ndev->reg_state == NETREG_REGISTERED) {
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
|
cfg80211_unregister_netdevice(ndev);
|
|
#else
|
|
unregister_netdevice(ndev);
|
|
#endif
|
|
free_netdev(ndev);
|
|
ndev = NULL;
|
|
}
|
|
PRINTM(MFATAL, "register net_device failed, ret=%d\n", ret);
|
|
goto done;
|
|
}
|
|
netif_carrier_off(ndev);
|
|
woal_stop_queue(ndev);
|
|
if (new_dev)
|
|
*new_dev = ndev;
|
|
#ifdef CONFIG_PROC_FS
|
|
woal_create_proc_entry(new_priv);
|
|
woal_debug_entry(new_priv);
|
|
#endif /* CONFIG_PROC_FS */
|
|
done:
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Notify mlan BSS will be removed.
|
|
*
|
|
* @param priv A pointer to moal_private structure
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_PENDING -- success,
|
|
* otherwise fail
|
|
*/
|
|
static mlan_status woal_bss_remove(moal_private *priv)
|
|
{
|
|
mlan_ioctl_req *req = NULL;
|
|
mlan_ds_bss *bss = NULL;
|
|
mlan_status status;
|
|
|
|
ENTER();
|
|
|
|
/* Allocate an IOCTL request buffer */
|
|
req = (mlan_ioctl_req *)woal_alloc_mlan_ioctl_req(sizeof(mlan_ds_bss));
|
|
if (req == NULL) {
|
|
status = MLAN_STATUS_FAILURE;
|
|
goto done;
|
|
}
|
|
|
|
/* Fill request buffer */
|
|
bss = (mlan_ds_bss *)req->pbuf;
|
|
bss->sub_command = MLAN_OID_BSS_REMOVE;
|
|
req->req_id = MLAN_IOCTL_BSS;
|
|
req->action = MLAN_ACT_SET;
|
|
/* Send IOCTL request to MLAN */
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @brief This function removes an virtual interface.
|
|
*
|
|
* @param wiphy A pointer to the wiphy structure
|
|
* @param dev A pointer to the net_device structure
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
int woal_cfg80211_del_virt_if(struct wiphy *wiphy, struct net_device *dev)
|
|
{
|
|
int ret = 0;
|
|
int i = 0;
|
|
moal_private *priv = NULL;
|
|
moal_private *vir_priv = NULL;
|
|
moal_private *remain_priv = NULL;
|
|
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
|
|
t_u8 find_bss = MFALSE;
|
|
|
|
for (i = 0; i < handle->priv_num; i++) {
|
|
vir_priv = handle->priv[i];
|
|
if (vir_priv) {
|
|
if (vir_priv->netdev == dev) {
|
|
find_bss = MTRUE;
|
|
PRINTM(MMSG,
|
|
"Del virtual interface %s, index=%d\n",
|
|
dev->name, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!find_bss) {
|
|
/* Switch to the other MAC */
|
|
if (handle->pref_mac)
|
|
handle = (moal_handle *)handle->pref_mac;
|
|
for (i = 0; i < handle->priv_num; i++) {
|
|
vir_priv = handle->priv[i];
|
|
if (vir_priv) {
|
|
if (vir_priv->netdev == dev) {
|
|
find_bss = MTRUE;
|
|
PRINTM(MMSG,
|
|
"Del virtual interface %s, index=%d\n",
|
|
dev->name, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
priv = (moal_private *)woal_get_priv_bss_type(handle,
|
|
MLAN_BSS_TYPE_WIFIDIRECT);
|
|
if (!priv)
|
|
return ret;
|
|
if (vir_priv && vir_priv->netdev == dev) {
|
|
woal_stop_queue(dev);
|
|
netif_carrier_off(dev);
|
|
netif_device_detach(dev);
|
|
if (handle->is_remain_timer_set) {
|
|
woal_cancel_timer(&handle->remain_timer);
|
|
woal_remain_timer_func(handle);
|
|
}
|
|
|
|
/*** cancel pending scan */
|
|
woal_cancel_scan(vir_priv, MOAL_IOCTL_WAIT);
|
|
|
|
woal_flush_tx_stat_queue(vir_priv);
|
|
woal_flush_mcast_list(vir_priv);
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
|
|
/* cancel previous remain on channel to avoid firmware hang */
|
|
if (priv->phandle->remain_on_channel) {
|
|
t_u8 channel_status;
|
|
remain_priv =
|
|
priv->phandle
|
|
->priv[priv->phandle->remain_bss_index];
|
|
if (remain_priv) {
|
|
if (woal_cfg80211_remain_on_channel_cfg(
|
|
remain_priv, MOAL_IOCTL_WAIT, MTRUE,
|
|
&channel_status, NULL, 0, 0))
|
|
PRINTM(MERROR,
|
|
"del_virt_if: Fail to cancel remain on channel\n");
|
|
|
|
if (priv->phandle->cookie) {
|
|
cfg80211_remain_on_channel_expired(
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
|
|
remain_priv->netdev,
|
|
#else
|
|
remain_priv->wdev,
|
|
#endif
|
|
priv->phandle->cookie,
|
|
&priv->phandle->chan,
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
|
|
priv->phandle->channel_type,
|
|
#endif
|
|
GFP_ATOMIC);
|
|
priv->phandle->cookie = 0;
|
|
}
|
|
priv->phandle->remain_on_channel = MFALSE;
|
|
}
|
|
}
|
|
#endif
|
|
if (vir_priv->mclist_workqueue) {
|
|
flush_workqueue(vir_priv->mclist_workqueue);
|
|
destroy_workqueue(vir_priv->mclist_workqueue);
|
|
vir_priv->mclist_workqueue = NULL;
|
|
}
|
|
woal_clear_all_mgmt_ies(vir_priv, MOAL_IOCTL_WAIT);
|
|
woal_cfg80211_deinit_p2p(vir_priv);
|
|
woal_bss_remove(vir_priv);
|
|
#ifdef CONFIG_PROC_FS
|
|
/* Remove proc debug */
|
|
woal_debug_remove(vir_priv);
|
|
woal_proc_remove(vir_priv);
|
|
#endif /* CONFIG_PROC_FS */
|
|
/* Last reference is our one */
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
|
|
PRINTM(MINFO, "refcnt = %d\n", atomic_read(&dev->refcnt));
|
|
#else
|
|
PRINTM(MINFO, "refcnt = %d\n", netdev_refcnt_read(dev));
|
|
#endif
|
|
PRINTM(MINFO, "netdev_finish_unregister: %s\n", dev->name);
|
|
/* Clear the priv in handle */
|
|
vir_priv->phandle->priv[vir_priv->bss_index] = NULL;
|
|
priv->phandle->priv_num--;
|
|
if (dev->reg_state == NETREG_REGISTERED)
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
|
cfg80211_unregister_netdevice(dev);
|
|
#else
|
|
unregister_netdevice(dev);
|
|
#endif
|
|
}
|
|
return ret;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
/**
|
|
* @brief This function removes an virtual interface.
|
|
*
|
|
* @param handle A pointer to the moal_handle structure
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void woal_remove_virtual_interface(moal_handle *handle)
|
|
{
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
moal_private *priv = NULL;
|
|
int vir_intf = 0;
|
|
int i = 0;
|
|
#endif
|
|
ENTER();
|
|
rtnl_lock();
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
for (i = 0; i < handle->priv_num; i++) {
|
|
priv = handle->priv[i];
|
|
if (priv) {
|
|
if (priv->bss_virtual) {
|
|
PRINTM(MCMND, "Remove virtual interface %s\n",
|
|
priv->netdev->name);
|
|
#ifdef CONFIG_PROC_FS
|
|
/* Remove proc debug */
|
|
woal_debug_remove(priv);
|
|
woal_proc_remove(priv);
|
|
#endif /* CONFIG_PROC_FS */
|
|
netif_device_detach(priv->netdev);
|
|
if (priv->netdev->reg_state ==
|
|
NETREG_REGISTERED)
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
|
cfg80211_unregister_netdevice(
|
|
priv->netdev);
|
|
#else
|
|
unregister_netdevice(priv->netdev);
|
|
#endif
|
|
handle->priv[i] = NULL;
|
|
vir_intf++;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (handle->mon_if) {
|
|
netif_device_detach(handle->mon_if->mon_ndev);
|
|
if (handle->mon_if->mon_ndev->reg_state == NETREG_REGISTERED)
|
|
unregister_netdevice(handle->mon_if->mon_ndev);
|
|
handle->mon_if = NULL;
|
|
}
|
|
rtnl_unlock();
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
handle->priv_num -= vir_intf;
|
|
#endif
|
|
LEAVE();
|
|
}
|
|
|
|
/**
|
|
* @brief This function check if uap interface is ready
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
*
|
|
* @return MTRUE/MFALSE;
|
|
*/
|
|
static t_u8 woal_uap_interface_ready(struct wiphy *wiphy, char *name,
|
|
struct net_device **new_dev)
|
|
{
|
|
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
|
|
moal_private *priv = NULL;
|
|
int i;
|
|
|
|
for (i = 0; i < handle->priv_num; i++) {
|
|
priv = handle->priv[i];
|
|
if (priv && (priv->bss_type == MLAN_BSS_TYPE_UAP) &&
|
|
!strcmp(priv->netdev->name, name)) {
|
|
priv->wdev->iftype = NL80211_IFTYPE_AP;
|
|
*new_dev = priv->netdev;
|
|
break;
|
|
}
|
|
}
|
|
if (priv && *new_dev)
|
|
return MTRUE;
|
|
else
|
|
return MFALSE;
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
|
|
#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37)
|
|
/**
|
|
* @brief Request the driver to add a virtual interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param type Virtual interface type
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
*
|
|
* @return A pointer to net_device -- success, otherwise null
|
|
*/
|
|
struct net_device *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy,
|
|
char *name,
|
|
enum nl80211_iftype type,
|
|
u32 *flags,
|
|
struct vif_params *params)
|
|
#else
|
|
/**
|
|
* @brief Request the driver to add a virtual interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param type Virtual interface type
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
int woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, char *name,
|
|
enum nl80211_iftype type, u32 *flags,
|
|
struct vif_params *params)
|
|
#endif
|
|
#else
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 1, 0)
|
|
/**
|
|
* @brief Request the driver to add a virtual interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param type Virtual interface type
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
*
|
|
* @return A pointer to wireless_dev -- success, otherwise null
|
|
*/
|
|
struct wireless_dev *woal_cfg80211_add_virtual_intf(struct wiphy *wiphy,
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
|
|
const
|
|
#endif
|
|
char *name,
|
|
enum nl80211_iftype type,
|
|
u32 *flags,
|
|
struct vif_params *params)
|
|
#else
|
|
/**
|
|
* @brief Request the driver to add a virtual interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param name Virtual interface name
|
|
* @param name_assign_type Interface name assignment type
|
|
* @param type Virtual interface type
|
|
* @param flags Flags for the virtual interface
|
|
* @param params A pointer to vif_params structure
|
|
*
|
|
* @return A pointer to wireless_dev -- success, otherwise null
|
|
*/
|
|
struct wireless_dev *
|
|
woal_cfg80211_add_virtual_intf(struct wiphy *wiphy, const char *name,
|
|
unsigned char name_assign_type,
|
|
enum nl80211_iftype type,
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
|
|
u32 *flags,
|
|
#endif
|
|
struct vif_params *params)
|
|
#endif
|
|
#endif
|
|
{
|
|
struct net_device *ndev = NULL;
|
|
int ret = 0;
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
|
|
u32 *flags = ¶ms->flags;
|
|
#endif
|
|
|
|
ENTER();
|
|
PRINTM(MIOCTL, "add virtual intf: %d name: %s\n", type, name);
|
|
switch (type) {
|
|
case NL80211_IFTYPE_MONITOR:
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
ret = woal_cfg80211_add_mon_if(wiphy, name, name_assign_type,
|
|
flags, params, &ndev);
|
|
#else
|
|
ret = woal_cfg80211_add_mon_if(wiphy, name, flags, params,
|
|
&ndev);
|
|
#endif
|
|
break;
|
|
#ifdef WIFI_DIRECT_SUPPORT
|
|
#if CFG80211_VERSION_CODE >= WIFI_DIRECT_KERNEL_VERSION
|
|
case NL80211_IFTYPE_P2P_CLIENT:
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
|
ret = woal_cfg80211_add_virt_if(wiphy, name, name_assign_type,
|
|
type, flags, params, &ndev);
|
|
#else
|
|
ret = woal_cfg80211_add_virt_if(wiphy, name, type, flags,
|
|
params, &ndev);
|
|
#endif
|
|
break;
|
|
#endif
|
|
#endif
|
|
case NL80211_IFTYPE_AP:
|
|
if (!woal_uap_interface_ready(wiphy, (char *)name, &ndev)) {
|
|
PRINTM(MMSG,
|
|
"Not support dynamically create %s UAP interface\n",
|
|
name);
|
|
ret = -EFAULT;
|
|
}
|
|
break;
|
|
default:
|
|
PRINTM(MWARN, "Not supported if type: %d\n", type);
|
|
ret = -EFAULT;
|
|
break;
|
|
}
|
|
LEAVE();
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
|
|
#if CFG80211_VERSION_CODE > KERNEL_VERSION(2, 6, 37)
|
|
if (ret)
|
|
return ERR_PTR(ret);
|
|
else
|
|
return ndev;
|
|
#else
|
|
return ret;
|
|
#endif
|
|
#else
|
|
if (ret)
|
|
return ERR_PTR(ret);
|
|
else
|
|
return ndev->ieee80211_ptr;
|
|
#endif
|
|
}
|
|
|
|
#if CFG80211_VERSION_CODE < KERNEL_VERSION(3, 6, 0)
|
|
/**
|
|
* @brief Request the driver to del a virtual interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param dev The pointer to net_device
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy, struct net_device *dev)
|
|
#else
|
|
/**
|
|
* @brief Request the driver to del a virtual interface
|
|
*
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param wdev The pointer to wireless_dev
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
int woal_cfg80211_del_virtual_intf(struct wiphy *wiphy,
|
|
struct wireless_dev *wdev)
|
|
#endif
|
|
{
|
|
int ret = 0;
|
|
|
|
moal_handle *handle = (moal_handle *)woal_get_wiphy_priv(wiphy);
|
|
int i;
|
|
moal_private *vir_priv = NULL;
|
|
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
|
|
struct net_device *dev = wdev->netdev;
|
|
#endif
|
|
|
|
ENTER();
|
|
|
|
PRINTM(MIOCTL, "del virtual intf %s\n", dev->name);
|
|
ASSERT_RTNL();
|
|
if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_MONITOR) {
|
|
if ((handle->mon_if) && (handle->mon_if->mon_ndev == dev)) {
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_net_monitor(handle->mon_if->priv,
|
|
MOAL_IOCTL_WAIT, MFALSE, 0,
|
|
NULL)) {
|
|
PRINTM(MERROR,
|
|
"%s: woal_set_net_monitor fail\n",
|
|
__func__);
|
|
ret = -EFAULT;
|
|
}
|
|
#endif
|
|
handle->mon_if = NULL;
|
|
}
|
|
unregister_netdevice(dev);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) {
|
|
for (i = 0; i < handle->priv_num; i++) {
|
|
vir_priv = handle->priv[i];
|
|
if (vir_priv) {
|
|
if (vir_priv->netdev == dev) {
|
|
PRINTM(MMSG,
|
|
"Del virtual interface %s, index=%d\n",
|
|
dev->name, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (vir_priv && vir_priv->bss_type == MLAN_BSS_TYPE_UAP) {
|
|
woal_cfg80211_del_beacon(wiphy, dev, 0);
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
|
|
vir_priv->wdev->links[0].ap.beacon_interval = 0;
|
|
#if CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)
|
|
memset(&vir_priv->wdev->links[0].ap.chandef, 0,
|
|
sizeof(vir_priv->wdev->links[0].ap.chandef));
|
|
#endif
|
|
#endif
|
|
vir_priv->wdev->u.ap.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, unsigned int link_id)
|
|
{
|
|
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 CFG80211_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
|
if (param) {
|
|
mac_addr = param->mac;
|
|
reason_code = param->reason_code;
|
|
}
|
|
#endif
|
|
#if KERNEL_VERSION(3, 8, 0) <= CFG80211_VERSION_CODE
|
|
if (moal_extflg_isset(priv->phandle, EXT_HOST_MLME)) {
|
|
if (mac_addr)
|
|
cfg80211_del_sta(dev, mac_addr, GFP_KERNEL);
|
|
}
|
|
#endif
|
|
if (priv->media_connected == MFALSE) {
|
|
PRINTM(MINFO, "cfg80211: Media not connected!\n");
|
|
LEAVE();
|
|
return 0;
|
|
}
|
|
|
|
/** 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, 0);
|
|
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;
|
|
#endif
|
|
/* Since kernel doesn't support 600sec cac_timer for channels 120, 124,
|
|
* and 128 (weather channels) in ETSI region, overwrite kernel's
|
|
* cac_timer.
|
|
*/
|
|
if ((woal_is_etsi_country(priv->phandle->country_code) == MTRUE)) {
|
|
if (chandef->chan->hw_value == 120 ||
|
|
chandef->chan->hw_value == 124 ||
|
|
chandef->chan->hw_value == 128) {
|
|
pchan_rpt_req->millisec_dwell_time =
|
|
IEEE80211_DFS_MIN_CAC_TIME_MS * 10;
|
|
}
|
|
if (chandef->chan->hw_value == 116 &&
|
|
((chandef->width == NL80211_CHAN_WIDTH_40) ||
|
|
(chandef->width == NL80211_CHAN_WIDTH_80))) {
|
|
pchan_rpt_req->millisec_dwell_time =
|
|
IEEE80211_DFS_MIN_CAC_TIME_MS * 10;
|
|
}
|
|
}
|
|
if (priv->user_cac_period_msec) {
|
|
pchan_rpt_req->millisec_dwell_time = priv->user_cac_period_msec;
|
|
PRINTM(MCMD_D,
|
|
"cfg80211 dfstesting: User CAC Period=%d (msec) \n",
|
|
pchan_rpt_req->millisec_dwell_time);
|
|
}
|
|
|
|
p11h_cfg->sub_command = MLAN_OID_11H_CHANNEL_CHECK;
|
|
req->req_id = MLAN_IOCTL_11H_CFG;
|
|
req->action = MLAN_ACT_SET;
|
|
|
|
/* Send Channel Check command and wait until the report is ready */
|
|
status = woal_request_ioctl(priv, req, MOAL_IOCTL_WAIT);
|
|
if (status != MLAN_STATUS_SUCCESS) {
|
|
PRINTM(MERROR, "Fail to start radar detection\n");
|
|
ret = -EFAULT;
|
|
} else {
|
|
moal_memcpy_ext(priv->phandle, &handle->dfs_channel, chandef,
|
|
sizeof(struct cfg80211_chan_def),
|
|
sizeof(handle->dfs_channel));
|
|
handle->cac_bss_index = priv->bss_index;
|
|
handle->is_cac_timer_set = MTRUE;
|
|
/* avoid EVENT_CHANNEL_RAPORT_READY missing, add 1s gap */
|
|
woal_mod_timer(&handle->cac_timer,
|
|
pchan_rpt_req->millisec_dwell_time + 1000);
|
|
}
|
|
done:
|
|
if (status != MLAN_STATUS_PENDING)
|
|
kfree(req);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief channel switch
|
|
|
|
* @param wiphy A pointer to wiphy structure
|
|
* @param dev A pointer to net_device structure
|
|
* @param params A pointer to cfg80211_csa_settings structure
|
|
*
|
|
* @return 0 -- success, otherwise fail
|
|
*/
|
|
int woal_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
|
|
struct cfg80211_csa_settings *params)
|
|
{
|
|
int ret = 0;
|
|
moal_private *priv = (moal_private *)woal_get_netdev_priv(dev);
|
|
t_u32 chsw_msec;
|
|
mlan_uap_bss_param *bss_cfg = NULL;
|
|
|
|
ENTER();
|
|
|
|
if (!params) {
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
/* TODO: support this case in next version */
|
|
if (params->radar_required) {
|
|
PRINTM(MMSG,
|
|
" hostapd handle this case by disable and re-enable interface\n");
|
|
ret = -ENOTSUPP;
|
|
goto done;
|
|
}
|
|
|
|
/* actually hostapd would always choose one diff channel*/
|
|
if (cfg80211_chandef_identical(¶ms->chandef, &priv->chan)) {
|
|
PRINTM(MMSG,
|
|
"csa channel is same with current channel, invaild\n");
|
|
ret = -EINVAL;
|
|
goto done;
|
|
}
|
|
bss_cfg = kzalloc(sizeof(mlan_uap_bss_param), GFP_ATOMIC);
|
|
if (!bss_cfg) {
|
|
PRINTM(MERROR, "Fail to alloc memory for mlan_uap_bss_param\n");
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
|
|
if (params->block_tx) {
|
|
if (netif_carrier_ok(dev))
|
|
netif_carrier_off(dev);
|
|
woal_stop_queue(dev);
|
|
priv->uap_tx_blocked = MTRUE;
|
|
}
|
|
|
|
woal_clear_all_mgmt_ies(priv, MOAL_IOCTL_WAIT);
|
|
if (woal_cfg80211_set_beacon(wiphy, dev, ¶ms->beacon_csa)) {
|
|
PRINTM(MERROR, "%s: setting csa mgmt ies failed\n", __func__);
|
|
goto done;
|
|
}
|
|
|
|
moal_memcpy_ext(priv->phandle, &priv->csa_chan, ¶ms->chandef,
|
|
sizeof(struct cfg80211_chan_def),
|
|
sizeof(priv->csa_chan));
|
|
moal_memcpy_ext(priv->phandle, &priv->beacon_after,
|
|
¶ms->beacon_after,
|
|
sizeof(struct cfg80211_beacon_data),
|
|
sizeof(priv->beacon_after));
|
|
|
|
if (!priv->phandle->fw_ecsa_enable) {
|
|
if (MLAN_STATUS_SUCCESS !=
|
|
woal_set_get_sys_config(priv, MLAN_ACT_GET, MOAL_IOCTL_WAIT,
|
|
bss_cfg)) {
|
|
PRINTM(MERROR, "%s: get uap config failed\n", __func__);
|
|
ret = -EFAULT;
|
|
goto done;
|
|
}
|
|
chsw_msec = params->count * bss_cfg->beacon_period;
|
|
queue_delayed_work(priv->csa_workqueue, &priv->csa_work,
|
|
msecs_to_jiffies(chsw_msec));
|
|
}
|
|
done:
|
|
kfree(bss_cfg);
|
|
LEAVE();
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief Register the device with cfg80211
|
|
*
|
|
* @param dev A pointer to net_device structure
|
|
* @param bss_type BSS type
|
|
*
|
|
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
|
|
*/
|
|
mlan_status woal_register_uap_cfg80211(struct net_device *dev, t_u8 bss_type)
|
|
{
|
|
mlan_status ret = MLAN_STATUS_SUCCESS;
|
|
moal_private *priv = (moal_private *)netdev_priv(dev);
|
|
struct wireless_dev *wdev = NULL;
|
|
|
|
ENTER();
|
|
|
|
wdev = (struct wireless_dev *)&priv->w_dev;
|
|
memset(wdev, 0, sizeof(struct wireless_dev));
|
|
|
|
wdev->wiphy = priv->phandle->wiphy;
|
|
if (!wdev->wiphy) {
|
|
LEAVE();
|
|
return MLAN_STATUS_FAILURE;
|
|
}
|
|
|
|
if (bss_type == MLAN_BSS_TYPE_UAP)
|
|
wdev->iftype = NL80211_IFTYPE_AP;
|
|
|
|
dev_net_set(dev, wiphy_net(wdev->wiphy));
|
|
dev->ieee80211_ptr = wdev;
|
|
SET_NETDEV_DEV(dev, wiphy_dev(wdev->wiphy));
|
|
priv->wdev = wdev;
|
|
|
|
LEAVE();
|
|
return ret;
|
|
}
|