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

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

4275 lines
126 KiB
C

/** @file mlan_11h.c
*
* @brief This file contains functions for 802.11H.
*
*
* Copyright 2008-2021 NXP
*
* This software file (the File) is distributed by NXP
* under the terms of the GNU General Public License Version 2, June 1991
* (the License). You may use, redistribute and/or modify the File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
/*************************************************************
Change Log:
03/26/2009: initial version
************************************************************/
#include "mlan.h"
#include "mlan_join.h"
#include "mlan_util.h"
#include "mlan_fw.h"
#include "mlan_main.h"
#include "mlan_ioctl.h"
#include "mlan_11h.h"
#include "mlan_11n.h"
#ifdef UAP_SUPPORT
#include "mlan_uap.h"
#endif
/********************************************************
Local Variables
********************************************************/
/** Default IBSS DFS recovery interval (in TBTTs); used for adhoc start */
#define WLAN_11H_DEFAULT_DFS_RECOVERY_INTERVAL 100
/** Default 11h power constraint used to offset the maximum transmit power */
#define WLAN_11H_TPC_POWERCONSTRAINT 0
/** 11h TPC Power capability minimum setting, sent in TPC_INFO command to fw */
#define WLAN_11H_TPC_POWERCAPABILITY_MIN 5
/** 11h TPC Power capability maximum setting, sent in TPC_INFO command to fw */
#define WLAN_11H_TPC_POWERCAPABILITY_MAX 20
/** Regulatory requirement for the duration of a channel availability check */
#define WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION 60000 /* in ms */
/** Starting Frequency for 11A band */
#define START_FREQ_11A_BAND 5000 /* in MHz */
/** DFS Channel Move Time */
#define DFS_CHAN_MOVE_TIME 10 /* in sec */
/** Regulatory requirement for the duration of a non-occupancy period */
#define WLAN_11H_NON_OCCUPANCY_PERIOD 1800 /* in sec (30mins) */
/** Maximum allowable age (seconds) on DFS report data */
#define MAX_DFS_REPORT_USABLE_AGE_SEC (120) /* 2 minutes */
/** Minimum delay for CHAN_SW IE to broadcast by FW */
#define MIN_RDH_CHAN_SW_IE_PERIOD_MSEC (400) /* 4 beacons @ 100ms */
/** Maximum delay for CHAN_SW IE to broadcast by FW */
#define MAX_RDH_CHAN_SW_IE_PERIOD_MSEC (3000) /* 5 beacons @ 600ms */
/** Maximum retries on selecting new random channel */
#define MAX_RANDOM_CHANNEL_RETRIES (20)
/** Maximum retries on selecting new random non-dfs channel */
#define MAX_SWITCH_CHANNEL_RETRIES (30)
/** Value for undetermined priv_curr_idx on first entry to new RDH stage */
#define RDH_STAGE_FIRST_ENTRY_PRIV_IDX (0xff)
/** Region codes 0x10, 0x20: channels 1 thru 11 supported */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_2_4G_region_FCC = {1, 11};
/** Region codes 0x30, 0x32, 0x41, 0x50: channels 1 thru 13 supported */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_2_4G_region_EU = {1, 13};
/** Region code 0x40: only channel 14 supported */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_2_4G_region_JPN40 = {14,
1};
/** JPN sub-band config : Start Channel = 8, NumChans = 3 */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_JPN_bottom_band = {8, 3};
/** U-NII sub-band config : Start Channel = 36, NumChans = 4 */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_lower_band = {36, 4};
/** U-NII sub-band config : Start Channel = 52, NumChans = 4 */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_middle_band = {52,
4};
/** U-NII sub-band config : Start Channel = 100, NumChans = 11 */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_mid_upper_band = {
100, 11};
/** U-NII sub-band config : Start Channel = 100, NumChans = 5 */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_mid_upper_band_0 = {
100, 5};
/** U-NII sub-band config : Start Channel = 132, NumChans = 3 */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_mid_upper_band_1 = {
132, 3};
/** U-NII sub-band config : Start Channel = 149, NumChans = 5 */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_upper_band = {149,
5};
/** Internally passed structure used to send a CMD_802_11_TPC_INFO command */
typedef struct {
t_u8 chan; /**< Channel to which the power constraint applies */
t_u8 power_constraint; /**< Local power constraint to send to firmware
*/
} wlan_11h_tpc_info_param_t;
/********************************************************
Global Variables
********************************************************/
/********************************************************
Local Functions
********************************************************/
/**
* @brief Utility function to get a random number based on the underlying OS
*
* @param pmadapter Pointer to mlan_adapter
* @return random integer
*/
static t_u32 wlan_11h_get_random_num(pmlan_adapter pmadapter)
{
t_u32 sec, usec;
ENTER();
pmadapter->callbacks.moal_get_system_time(pmadapter->pmoal_handle, &sec,
&usec);
sec = (sec & 0xFFFF) + (sec >> 16);
usec = (usec & 0xFFFF) + (usec >> 16);
LEAVE();
return (usec << 16) | sec;
}
/**
* @brief Convert an IEEE formatted IE to 16-bit ID/Len NXP
* proprietary format
*
* @param pmadapter Pointer to mlan_adapter
* @param pout_buf Output parameter: Buffer to output NXP formatted IE
* @param pin_ie Pointer to IEEE IE to be converted to NXP format
*
* @return Number of bytes output to pout_buf parameter return
*/
static t_u32 wlan_11h_convert_ieee_to_mrvl_ie(mlan_adapter *pmadapter,
t_u8 *pout_buf,
const t_u8 *pin_ie)
{
MrvlIEtypesHeader_t mrvl_ie_hdr;
t_u8 *ptmp_buf = pout_buf;
ENTER();
/* Assign the Element Id and Len to the NXP struct attributes */
mrvl_ie_hdr.type = wlan_cpu_to_le16(pin_ie[0]);
mrvl_ie_hdr.len = wlan_cpu_to_le16(pin_ie[1]);
/* If the element ID is zero, return without doing any copying */
if (!mrvl_ie_hdr.type) {
LEAVE();
return 0;
}
/* Copy the header to the buffer pointer */
memcpy_ext(pmadapter, ptmp_buf, &mrvl_ie_hdr, sizeof(mrvl_ie_hdr),
sizeof(mrvl_ie_hdr));
/* Increment the temp buffer pointer by the size appended */
ptmp_buf += sizeof(mrvl_ie_hdr);
/* Append the data section of the IE; length given by the IEEE IE length
*/
memcpy_ext(pmadapter, ptmp_buf, pin_ie + 2, pin_ie[1], pin_ie[1]);
LEAVE();
/* Return the number of bytes appended to pout_buf */
return sizeof(mrvl_ie_hdr) + pin_ie[1];
}
#ifdef STA_SUPPORT
/**
* @brief Setup the IBSS DFS element passed to the firmware in adhoc start
* and join commands
*
* The DFS Owner and recovery fields are set to be our MAC address and
* a predetermined constant recovery value. If we are joining an adhoc
* network, these values are replaced with the existing IBSS values.
* They are valid only when starting a new IBSS.
*
* The IBSS DFS Element is variable in size based on the number of
* channels supported in our current region.
*
* @param priv Private driver information structure
* @param pdfs Output parameter: Pointer to the IBSS DFS element setup by
* this function.
*
* @return
* - Length of the returned element in pdfs output parameter
* - 0 if returned element is not setup
*/
static t_u32 wlan_11h_set_ibss_dfs_ie(mlan_private *priv,
IEEEtypes_IBSS_DFS_t *pdfs)
{
t_u8 num_chans = 0;
MeasRptBasicMap_t initial_map;
mlan_adapter *adapter = priv->adapter;
ENTER();
memset(adapter, pdfs, 0x00, sizeof(IEEEtypes_IBSS_DFS_t));
/*
* A basic measurement report is included with each channel in the
* map field. Initial value for the map for each supported channel
* is with only the unmeasured bit set.
*/
memset(adapter, &initial_map, 0x00, sizeof(initial_map));
initial_map.unmeasured = 1;
/* Set the DFS Owner and recovery interval fields */
memcpy_ext(adapter, pdfs->dfs_owner, priv->curr_addr,
sizeof(pdfs->dfs_owner), sizeof(pdfs->dfs_owner));
pdfs->dfs_recovery_interval = WLAN_11H_DEFAULT_DFS_RECOVERY_INTERVAL;
for (; (num_chans < adapter->parsed_region_chan.no_of_chan) &&
(num_chans < WLAN_11H_MAX_IBSS_DFS_CHANNELS);
num_chans++) {
pdfs->channel_map[num_chans].channel_number =
adapter->parsed_region_chan.chan_pwr[num_chans].chan;
/*
* Set the initial map field with a basic measurement
*/
pdfs->channel_map[num_chans].rpt_map = initial_map;
}
/*
* If we have an established channel map, include it and return
* a valid DFS element
*/
if (num_chans) {
PRINTM(MINFO, "11h: Added %d channels to IBSS DFS Map\n",
num_chans);
pdfs->element_id = IBSS_DFS;
pdfs->len = (sizeof(pdfs->dfs_owner) +
sizeof(pdfs->dfs_recovery_interval) +
num_chans * sizeof(IEEEtypes_ChannelMap_t));
LEAVE();
return pdfs->len + sizeof(pdfs->len) + sizeof(pdfs->element_id);
}
/* Ensure the element is zeroed out for an invalid return */
memset(adapter, pdfs, 0x00, sizeof(IEEEtypes_IBSS_DFS_t));
LEAVE();
return 0;
}
#endif
/**
* @brief Setup the Supported Channel IE sent in association requests
*
* The Supported Channels IE is required to be sent when the spectrum
* management capability (11h) is enabled. The element contains a
* starting channel and number of channels tuple for each sub-band
* the STA supports. This information is based on the operating region.
*
* @param priv Private driver information structure
* @param band Band in use
* @param psup_chan Output parameter: Pointer to the Supported Chan element
* setup by this function.
*
* @return
* - Length of the returned element in psup_chan output parameter
* - 0 if returned element is not setup
*/
static t_u16
wlan_11h_set_supp_channels_ie(mlan_private *priv, t_u8 band,
IEEEtypes_SupportedChannels_t *psup_chan)
{
t_u16 num_subbands = 0;
t_u16 ret_len = 0;
t_u8 cfp_bg, cfp_a;
ENTER();
memset(priv->adapter, psup_chan, 0x00,
sizeof(IEEEtypes_SupportedChannels_t));
cfp_bg = cfp_a = priv->adapter->region_code;
if (!priv->adapter->region_code) {
/* Invalid region code, use CFP code */
cfp_bg = priv->adapter->cfp_code_bg;
cfp_a = priv->adapter->cfp_code_a;
}
if ((band & BAND_B) || (band & BAND_G)) {
/*
* Channels are contiguous in 2.4GHz, usually only one subband.
*/
switch (cfp_bg) {
case 0x10: /* USA FCC */
case 0x20: /* Canada IC */
default:
psup_chan->subband[num_subbands++] =
wlan_11h_2_4G_region_FCC;
break;
case 0x30: /* Europe ETSI */
case 0x41: /* Japan */
case 0x50: /* China */
psup_chan->subband[num_subbands++] =
wlan_11h_2_4G_region_EU;
break;
case 0x40: /* Japan */
psup_chan->subband[num_subbands++] =
wlan_11h_2_4G_region_JPN40;
break;
case 0xff: /* Japan special */
psup_chan->subband[num_subbands++] =
wlan_11h_2_4G_region_EU;
psup_chan->subband[num_subbands++] =
wlan_11h_2_4G_region_JPN40;
break;
}
} else if (band & BAND_A) {
/*
* Set the supported channel elements based on the region code,
* incrementing num_subbands for each sub-band we append to the
* element.
*/
switch (cfp_a) {
case 0x10: /* USA FCC */
case 0x20: /* Canada IC */
case 0x30: /* Europe ETSI */
default:
psup_chan->subband[num_subbands++] =
wlan_11h_unii_lower_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_middle_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_mid_upper_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_upper_band;
break;
case 0x50: /* China */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_lower_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_middle_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_upper_band;
break;
case 0x40: /* Japan */
case 0x41: /* Japan */
case 0xff: /* Japan special */
psup_chan->subband[num_subbands++] =
wlan_11h_JPN_bottom_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_lower_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_middle_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_mid_upper_band;
break;
case 0x1: /* Low band (5150-5250 MHz) channels */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_lower_band;
break;
case 0x2: /* Lower middle band (5250-5350 MHz) channels */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_middle_band;
break;
case 0x3: /* Upper middle band (5470-5725 MHz) channels */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_mid_upper_band;
break;
case 0x4: /* High band (5725-5850 MHz) channels */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_upper_band;
break;
case 0x5: /* Low band (5150-5250 MHz) and High band (5725-5850
MHz) channels */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_lower_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_upper_band;
break;
case 0x6: /* Low band (5150-5250 MHz) and Lower middle band
(5250-5350 MHz) and High band (5725-5850 MHz)
channels */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_lower_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_middle_band;
psup_chan->subband[num_subbands++] =
wlan_11h_unii_upper_band;
break;
case 0x7:
/* 36-48 */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_lower_band;
/* 52-64 */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_middle_band;
/* 100-116 */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_mid_upper_band_0;
/* 132-140 */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_mid_upper_band_1;
/* 149-165 */
psup_chan->subband[num_subbands++] =
wlan_11h_unii_upper_band;
break;
}
}
/*
* If we have setup any supported subbands in the element, return a
* valid IE along with its size, else return 0.
*/
if (num_subbands) {
psup_chan->element_id = SUPPORTED_CHANNELS;
psup_chan->len =
num_subbands * sizeof(IEEEtypes_SupportChan_Subband_t);
ret_len = (t_u16)(psup_chan->len + sizeof(psup_chan->len) +
sizeof(psup_chan->element_id));
HEXDUMP("11h: SupChan", (t_u8 *)psup_chan, ret_len);
}
LEAVE();
return ret_len;
}
/**
* @brief Prepare CMD_802_11_TPC_ADAPT_REQ firmware command
*
* @param priv Private driver information structure
* @param pcmd_ptr Output parameter: Pointer to the command being prepared
* for the firmware
* @param pinfo_buf HostCmd_DS_802_11_TPC_ADAPT_REQ passed as void data block
*
* @return MLAN_STATUS_SUCCESS
*/
static mlan_status wlan_11h_cmd_tpc_request(mlan_private *priv,
HostCmd_DS_COMMAND *pcmd_ptr,
const t_void *pinfo_buf)
{
ENTER();
memcpy_ext(priv->adapter, &pcmd_ptr->params.tpc_req, pinfo_buf,
sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ),
sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ));
pcmd_ptr->params.tpc_req.req.timeout =
wlan_cpu_to_le16(pcmd_ptr->params.tpc_req.req.timeout);
/* Converted to little endian in wlan_11h_cmd_process */
pcmd_ptr->size = sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ) + S_DS_GEN;
HEXDUMP("11h: 11_TPC_ADAPT_REQ:", (t_u8 *)pcmd_ptr,
(t_u32)pcmd_ptr->size);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Prepare CMD_802_11_TPC_INFO firmware command
*
* @param priv Private driver information structure
* @param pcmd_ptr Output parameter: Pointer to the command being prepared
* for the firmware
* @param pinfo_buf wlan_11h_tpc_info_param_t passed as void data block
*
* @return MLAN_STATUS_SUCCESS
*/
static mlan_status wlan_11h_cmd_tpc_info(mlan_private *priv,
HostCmd_DS_COMMAND *pcmd_ptr,
const t_void *pinfo_buf)
{
HostCmd_DS_802_11_TPC_INFO *ptpc_info = &pcmd_ptr->params.tpc_info;
MrvlIEtypes_LocalPowerConstraint_t *pconstraint =
&ptpc_info->local_constraint;
MrvlIEtypes_PowerCapability_t *pcap = &ptpc_info->power_cap;
wlan_11h_device_state_t *pstate = &priv->adapter->state_11h;
const wlan_11h_tpc_info_param_t *ptpc_info_param =
(wlan_11h_tpc_info_param_t *)pinfo_buf;
ENTER();
pcap->min_power = pstate->min_tx_power_capability;
pcap->max_power = pstate->max_tx_power_capability;
pcap->header.len = wlan_cpu_to_le16(2);
pcap->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CAPABILITY);
pconstraint->chan = ptpc_info_param->chan;
pconstraint->constraint = ptpc_info_param->power_constraint;
pconstraint->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CONSTRAINT);
pconstraint->header.len = wlan_cpu_to_le16(2);
/* Converted to little endian in wlan_11h_cmd_process */
pcmd_ptr->size = sizeof(HostCmd_DS_802_11_TPC_INFO) + S_DS_GEN;
HEXDUMP("11h: TPC INFO", (t_u8 *)pcmd_ptr, (t_u32)pcmd_ptr->size);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Prepare CMD_802_11_CHAN_SW_ANN firmware command
*
* @param priv Private driver information structure
* @param pcmd_ptr Output parameter: Pointer to the command being
* prepared to for firmware
* @param pinfo_buf HostCmd_DS_802_11_CHAN_SW_ANN passed as void data block
*
* @return MLAN_STATUS_SUCCESS
*/
static mlan_status wlan_11h_cmd_chan_sw_ann(mlan_private *priv,
HostCmd_DS_COMMAND *pcmd_ptr,
const t_void *pinfo_buf)
{
const HostCmd_DS_802_11_CHAN_SW_ANN *pch_sw_ann =
(HostCmd_DS_802_11_CHAN_SW_ANN *)pinfo_buf;
ENTER();
/* Converted to little endian in wlan_11h_cmd_process */
pcmd_ptr->size = sizeof(HostCmd_DS_802_11_CHAN_SW_ANN) + S_DS_GEN;
memcpy_ext(priv->adapter, &pcmd_ptr->params.chan_sw_ann, pch_sw_ann,
sizeof(HostCmd_DS_802_11_CHAN_SW_ANN),
sizeof(HostCmd_DS_802_11_CHAN_SW_ANN));
PRINTM(MINFO, "11h: ChSwAnn: %#x-%u, Seq=%u, Ret=%u\n",
pcmd_ptr->command, pcmd_ptr->size, pcmd_ptr->seq_num,
pcmd_ptr->result);
PRINTM(MINFO, "11h: ChSwAnn: Ch=%d, Cnt=%d, Mode=%d\n",
pch_sw_ann->new_chan, pch_sw_ann->switch_count,
pch_sw_ann->switch_mode);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Prepare CMD_CHAN_REPORT_REQUEST firmware command
*
* @param priv Private driver information structure
* @param pcmd_ptr Output parameter: Pointer to the command being
* prepared to for firmware
* @param pinfo_buf HostCmd_DS_CHAN_RPT_REQ passed as void data block
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_PENDING
*/
static mlan_status wlan_11h_cmd_chan_rpt_req(mlan_private *priv,
HostCmd_DS_COMMAND *pcmd_ptr,
const t_void *pinfo_buf)
{
HostCmd_DS_CHAN_RPT_REQ *pchan_rpt_req =
(HostCmd_DS_CHAN_RPT_REQ *)pinfo_buf;
wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs;
MrvlIEtypes_ChanRpt11hBasic_t *ptlv_basic;
t_bool is_cancel_req = MFALSE;
t_u8 dfs53cfg = priv->adapter->dfs53cfg;
MrvlIEtypes_DfsW53Cfg_t *ptlv_dfs53cfg;
ENTER();
/*
* pchan_rpt_req->millisec_dwell_time would be zero if the chan_rpt_req
* is to cancel current ongoing report
*/
if (pchan_rpt_req->millisec_dwell_time == 0)
is_cancel_req = MTRUE;
if (pstate_dfs->dfs_check_pending && !is_cancel_req) {
PRINTM(MERROR,
"11h: ChanRptReq - previous CMD_CHAN_REPORT_REQUEST has"
" not returned its result yet (as EVENT_CHANNEL_READY)."
" This command will be dropped.\n");
LEAVE();
return MLAN_STATUS_PENDING;
}
/* Converted to little endian in wlan_11h_cmd_process */
pcmd_ptr->size = sizeof(HostCmd_DS_CHAN_RPT_REQ) + S_DS_GEN;
memcpy_ext(priv->adapter, &pcmd_ptr->params.chan_rpt_req, pchan_rpt_req,
sizeof(HostCmd_DS_CHAN_RPT_REQ),
sizeof(HostCmd_DS_CHAN_RPT_REQ));
pcmd_ptr->params.chan_rpt_req.chan_desc.startFreq =
wlan_cpu_to_le16(pchan_rpt_req->chan_desc.startFreq);
pcmd_ptr->params.chan_rpt_req.millisec_dwell_time =
wlan_cpu_to_le32(pchan_rpt_req->millisec_dwell_time);
/* if DFS channel, add BASIC report TLV, and set radar bit */
if (!is_cancel_req && wlan_11h_radar_detect_required(
priv, pchan_rpt_req->chan_desc.chanNum)) {
ptlv_basic =
(MrvlIEtypes_ChanRpt11hBasic_t *)(((t_u8 *)(pcmd_ptr)) +
pcmd_ptr->size);
ptlv_basic->Header.type =
wlan_cpu_to_le16(TLV_TYPE_CHANRPT_11H_BASIC);
ptlv_basic->Header.len =
wlan_cpu_to_le16(sizeof(MeasRptBasicMap_t));
memset(priv->adapter, &ptlv_basic->map, 0,
sizeof(MeasRptBasicMap_t));
ptlv_basic->map.radar = 1;
pcmd_ptr->size += sizeof(MrvlIEtypes_ChanRpt11hBasic_t);
}
if ((priv->adapter->region_code == COUNTRY_CODE_JP_40 ||
priv->adapter->region_code == COUNTRY_CODE_JP_FF) &&
(dfs53cfg != DFS_W53_DEFAULT_FW)) {
ptlv_dfs53cfg =
(MrvlIEtypes_DfsW53Cfg_t *)(((t_u8 *)(pcmd_ptr)) +
pcmd_ptr->size);
ptlv_dfs53cfg->Header.type =
wlan_cpu_to_le16(TLV_TYPE_DFS_W53_CFG);
ptlv_dfs53cfg->Header.len = wlan_cpu_to_le16(sizeof(t_u8));
ptlv_dfs53cfg->dfs53cfg = dfs53cfg;
pcmd_ptr->size += sizeof(MrvlIEtypes_DfsW53Cfg_t);
}
pcmd_ptr->size = wlan_cpu_to_le16(pcmd_ptr->size);
/* update dfs sturcture.
* dfs_check_pending is set when we receive CMD_RESP == SUCCESS */
pstate_dfs->dfs_check_pending = MFALSE;
pstate_dfs->dfs_radar_found = MFALSE;
pstate_dfs->dfs_check_priv = MNULL;
if (!is_cancel_req)
pstate_dfs->dfs_check_channel =
pchan_rpt_req->chan_desc.chanNum;
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Set the local power capability and constraint TLV
*
* @param ppbuffer The buffer to add these two TLVs
* @param channel Channel to which the power constraint applies
* @param power_constraint Power constraint to be applied on the channel
* @param min_tx_power_capability Min. Tx Power in Power Capability IE
* @param max_tx_power_capability Max. Tx Power in Power Capability IE
*
* @return The len increased
*/
static t_u32 wlan_11h_set_local_power_constraint_tlv(
t_u8 **ppbuffer, t_u8 channel, t_u8 power_constraint,
t_u8 min_tx_power_capability, t_u8 max_tx_power_capability)
{
MrvlIEtypes_PowerCapability_t *pcap;
MrvlIEtypes_LocalPowerConstraint_t *pconstraint;
t_u8 *start_ptr = MNULL;
ENTER();
/* Null Checks */
if ((ppbuffer == MNULL) || (((t_u8 *)(*ppbuffer)) == MNULL)) {
LEAVE();
return 0;
}
start_ptr = (t_u8 *)(*ppbuffer);
PRINTM(MINFO,
"11h: Set local power constraint = %d channel=%d min_tx_pwr=%d max_tx_pwr=%d\n",
power_constraint, channel, min_tx_power_capability,
max_tx_power_capability);
pcap = (MrvlIEtypes_PowerCapability_t *)*ppbuffer;
pcap->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CAPABILITY);
pcap->header.len = wlan_cpu_to_le16(2);
pcap->min_power = min_tx_power_capability;
pcap->max_power = max_tx_power_capability;
*ppbuffer += sizeof(MrvlIEtypesHeader_t) + 2;
pconstraint = (MrvlIEtypes_LocalPowerConstraint_t *)*ppbuffer;
pconstraint->header.type = wlan_cpu_to_le16(TLV_TYPE_POWER_CONSTRAINT);
pconstraint->header.len = wlan_cpu_to_le16(2);
pconstraint->chan = channel;
pconstraint->constraint = power_constraint;
*ppbuffer += sizeof(MrvlIEtypesHeader_t) + 2;
LEAVE();
return (t_u32)(*ppbuffer - start_ptr);
}
/**
* @brief Utility function to process a join to an infrastructure BSS
*
* @param priv Private driver information structure
* @param ppbuffer Output parameter: Pointer to the TLV output buffer,
* modified on return to point after the appended 11h TLVs
* @param band Band on which we are joining the BSS
* @param channel Channel on which we are joining the BSS
* @param p11h_bss_info Pointer to the 11h BSS information for this network
* that was parsed out of the scan response.
*
* @return Integer number of bytes appended to the TLV output
* buffer (ppbuffer)
*/
static t_u32 wlan_11h_process_infra_join(mlan_private *priv, t_u8 **ppbuffer,
t_u8 band, t_u32 channel,
wlan_11h_bss_info_t *p11h_bss_info)
{
MrvlIEtypesHeader_t ie_header;
IEEEtypes_SupportedChannels_t sup_chan_ie;
t_u32 ret_len = 0;
t_u16 sup_chan_len = 0;
ENTER();
/* Null Checks */
if ((ppbuffer == MNULL) || (((t_u8 *)(*ppbuffer)) == MNULL)) {
LEAVE();
return 0;
}
ret_len += wlan_11h_set_local_power_constraint_tlv(
ppbuffer, (t_u8)channel,
(t_u8)p11h_bss_info->power_constraint.local_constraint,
(t_u8)priv->adapter->state_11h.min_tx_power_capability,
(t_u8)priv->adapter->state_11h.max_tx_power_capability);
/* Setup the Supported Channels IE */
sup_chan_len = wlan_11h_set_supp_channels_ie(priv, band, &sup_chan_ie);
/*
* If we returned a valid Supported Channels IE, wrap and append it
*/
if (sup_chan_len) {
/* Wrap the supported channels IE with a passthrough TLV type */
ie_header.type = wlan_cpu_to_le16(TLV_TYPE_PASSTHROUGH);
ie_header.len = wlan_cpu_to_le16(sup_chan_len);
memcpy_ext(priv->adapter, *ppbuffer, &ie_header,
sizeof(ie_header), sizeof(ie_header));
/*
* Increment the return size and the return buffer
* pointer param
*/
*ppbuffer += sizeof(ie_header);
ret_len += sizeof(ie_header);
/*
* Copy the supported channels IE to the output buf,
* advance pointer
*/
memcpy_ext(priv->adapter, *ppbuffer, &sup_chan_ie, sup_chan_len,
sup_chan_len);
*ppbuffer += sup_chan_len;
ret_len += sup_chan_len;
}
LEAVE();
return ret_len;
}
/**
* @brief Utility function to process a start or join to an adhoc network
*
* Add the elements to the TLV buffer needed in the start/join adhoc commands:
* - IBSS DFS IE
* - Quiet IE
*
* Also send the local constraint to the firmware in a TPC_INFO command.
*
* @param priv Private driver information structure
* @param ppbuffer Output parameter: Pointer to the TLV output buffer,
* modified on return to point after the appended 11h TLVs
* @param channel Channel on which we are starting/joining the IBSS
* @param p11h_bss_info Pointer to the 11h BSS information for this network
* that was parsed out of the scan response. NULL
* indicates we are starting the adhoc network
*
* @return Integer number of bytes appended to the TLV output
* buffer (ppbuffer)
*/
static t_u32 wlan_11h_process_adhoc(mlan_private *priv, t_u8 **ppbuffer,
t_u32 channel,
wlan_11h_bss_info_t *p11h_bss_info)
{
IEEEtypes_IBSS_DFS_t dfs_elem;
t_u32 size_appended;
t_u32 ret_len = 0;
t_s8 local_constraint = 0;
mlan_adapter *adapter = priv->adapter;
ENTER();
#ifdef STA_SUPPORT
/* Format our own IBSS DFS Element. Include our channel map fields */
wlan_11h_set_ibss_dfs_ie(priv, &dfs_elem);
#endif
if (p11h_bss_info) {
/*
* Copy the DFS Owner/Recovery Interval from the BSS
* we are joining
*/
memcpy_ext(adapter, dfs_elem.dfs_owner,
p11h_bss_info->ibss_dfs.dfs_owner,
sizeof(dfs_elem.dfs_owner),
sizeof(dfs_elem.dfs_owner));
dfs_elem.dfs_recovery_interval =
p11h_bss_info->ibss_dfs.dfs_recovery_interval;
}
/* Append the dfs element to the TLV buffer */
size_appended = wlan_11h_convert_ieee_to_mrvl_ie(
adapter, (t_u8 *)*ppbuffer, (t_u8 *)&dfs_elem);
HEXDUMP("11h: IBSS-DFS", (t_u8 *)*ppbuffer, size_appended);
*ppbuffer += size_appended;
ret_len += size_appended;
/*
* Check to see if we are joining a network. Join is indicated by the
* BSS Info pointer being valid (not NULL)
*/
if (p11h_bss_info) {
/*
* If there was a quiet element, include it in
* adhoc join command
*/
if (p11h_bss_info->quiet.element_id == QUIET) {
size_appended = wlan_11h_convert_ieee_to_mrvl_ie(
adapter, (t_u8 *)*ppbuffer,
(t_u8 *)&p11h_bss_info->quiet);
HEXDUMP("11h: Quiet", (t_u8 *)*ppbuffer, size_appended);
*ppbuffer += size_appended;
ret_len += size_appended;
}
/* Copy the local constraint from the network */
local_constraint =
p11h_bss_info->power_constraint.local_constraint;
} else {
/*
* If we are the adhoc starter, we can add a quiet element
*/
if (adapter->state_11h.quiet_ie.quiet_period) {
size_appended = wlan_11h_convert_ieee_to_mrvl_ie(
adapter, (t_u8 *)*ppbuffer,
(t_u8 *)&adapter->state_11h.quiet_ie);
HEXDUMP("11h: Quiet", (t_u8 *)*ppbuffer, size_appended);
*ppbuffer += size_appended;
ret_len += size_appended;
}
/* Use the local_constraint configured in the driver state */
local_constraint = adapter->state_11h.usr_def_power_constraint;
}
PRINTM(MINFO, "WEILIE 1: ppbuffer = %p\n", *ppbuffer);
ret_len += wlan_11h_set_local_power_constraint_tlv(
ppbuffer, (t_u8)channel, (t_u8)local_constraint,
(t_u8)priv->adapter->state_11h.min_tx_power_capability,
(t_u8)priv->adapter->state_11h.max_tx_power_capability);
PRINTM(MINFO, "WEILIE 2: ppbuffer = %p\n", *ppbuffer);
LEAVE();
return ret_len;
}
/**
* @brief Return whether the driver has enabled 11h for the interface
*
* Association/Join commands are dynamic in that they enable 11h in the
* driver/firmware when they are detected in the existing BSS.
*
* @param priv Private driver information structure
*
* @return
* - MTRUE if 11h is enabled
* - MFALSE otherwise
*/
static t_bool wlan_11h_is_enabled(mlan_private *priv)
{
ENTER();
LEAVE();
return priv->intf_state_11h.is_11h_enabled;
}
/**
* @brief Return whether the device has activated slave radar detection.
*
* @param priv Private driver information structure
*
* @return
* - MTRUE if slave radar detection is enabled in firmware
* - MFALSE otherwise
*/
static t_bool wlan_11h_is_slave_radar_det_active(mlan_private *priv)
{
ENTER();
LEAVE();
return priv->adapter->state_11h.is_slave_radar_det_active;
}
/**
* @brief Return whether the slave interface is active, and on DFS channel.
* priv is assumed to already be a dfs slave interface, doesn't check this.
*
* @param priv Private driver information structure
*
* @return
* - MTRUE if priv is slave, and meets both conditions
* - MFALSE otherwise
*/
static t_bool wlan_11h_is_slave_active_on_dfs_chan(mlan_private *priv)
{
t_bool ret = MFALSE;
ENTER();
if ((priv->media_connected == MTRUE) &&
(priv->curr_bss_params.band & BAND_A) &&
wlan_11h_radar_detect_required(
priv, priv->curr_bss_params.bss_descriptor.channel))
ret = MTRUE;
LEAVE();
return ret;
}
/**
* @brief Return whether the master interface is active, and on DFS channel.
* priv is assumed to already be a dfs master interface, doesn't check this.
*
* @param priv Private driver information structure
*
* @return
* - MTRUE if priv is master, and meets both conditions
* - MFALSE otherwise
*/
static t_bool wlan_11h_is_master_active_on_dfs_chan(mlan_private *priv)
{
t_bool ret = MFALSE;
ENTER();
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) {
/* Ad-hoc creator */
if (((priv->media_connected == MTRUE) ||
(priv->adhoc_state == ADHOC_STARTING)) &&
(priv->adapter->adhoc_start_band & BAND_A) &&
wlan_11h_radar_detect_required(priv, priv->adhoc_channel))
ret = MTRUE;
} else if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP) {
/* UAP */
#ifdef UAP_SUPPORT
if ((priv->uap_bss_started == MTRUE) &&
(priv->uap_state_chan_cb.bandcfg.chanBand == BAND_5GHZ) &&
wlan_11h_radar_detect_required(
priv, priv->uap_state_chan_cb.channel))
ret = MTRUE;
#endif
}
LEAVE();
return ret;
}
/**
* @brief Determine if priv is DFS Master interface
*
* @param priv Pointer to mlan_private
*
* @return MTRUE or MFALSE
*/
static t_bool wlan_11h_is_dfs_master(mlan_private *priv)
{
t_bool ret = MFALSE;
ENTER();
/* UAP: all are master */
if (GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_UAP)
ret = MTRUE;
/* STA: only ad-hoc creator is master */
else if ((GET_BSS_ROLE(priv) == MLAN_BSS_ROLE_STA) &&
(priv->bss_mode == MLAN_BSS_MODE_IBSS) &&
(priv->adhoc_state == ADHOC_STARTED ||
priv->adhoc_state == ADHOC_STARTING))
ret = MTRUE;
/* all other cases = slave interface */
LEAVE();
return ret;
}
/* Need this as function to pass to wlan_count_priv_cond() */
/**
* @brief Determine if priv is DFS Slave interface
*
* @param priv Pointer to mlan_private
*
* @return MTRUE or MFALSE
*/
static t_bool wlan_11h_is_dfs_slave(mlan_private *priv)
{
t_bool ret = MFALSE;
ENTER();
ret = !wlan_11h_is_dfs_master(priv);
LEAVE();
return ret;
}
/**
* @brief This function checks if interface is active.
*
* @param pmpriv A pointer to mlan_private structure
*
* @return MTRUE or MFALSE
*/
t_bool wlan_is_intf_active(mlan_private *pmpriv)
{
t_bool ret = MFALSE;
ENTER();
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP)
/*
* NOTE: UAP's media_connected == true only after first STA
* associated. Need different variable to tell if UAP
* has been started.
*/
ret = pmpriv->uap_bss_started;
else
#endif
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA)
ret = pmpriv->media_connected;
LEAVE();
return ret;
}
/**
* @brief This function gets current radar detect flags
*
* @param pmadapter A pointer to mlan_adapter structure
*
* @return 11H MIB setting for radar detect
*/
static t_u32 wlan_11h_get_current_radar_detect_flags(mlan_adapter *pmadapter)
{
t_u32 radar_det_flags = 0;
ENTER();
if (pmadapter->state_11h.is_master_radar_det_active)
radar_det_flags |= MASTER_RADAR_DET_MASK;
if (pmadapter->state_11h.is_slave_radar_det_active)
radar_det_flags |= SLAVE_RADAR_DET_MASK;
PRINTM(MINFO, "%s: radar_det_state_curr=0x%x\n", __func__,
radar_det_flags);
LEAVE();
return radar_det_flags;
}
/**
* @brief This function checks if radar detect flags have/should be changed.
*
* @param pmadapter A pointer to mlan_adapter structure
* @param pnew_state Output param with new state, if return MTRUE.
*
* @return MTRUE (need update) or MFALSE (no change in flags)
*/
static t_bool wlan_11h_check_radar_det_state(mlan_adapter *pmadapter,
t_u32 *pnew_state)
{
t_u32 radar_det_state_new = 0;
t_bool ret;
ENTER();
PRINTM(MINFO,
"%s: master_radar_det_pending=%d, "
" slave_radar_det_pending=%d\n",
__func__, pmadapter->state_11h.master_radar_det_enable_pending,
pmadapter->state_11h.slave_radar_det_enable_pending);
/* new state comes from evaluating interface states & pending starts */
if (pmadapter->state_11h.master_radar_det_enable_pending ||
(wlan_count_priv_cond(pmadapter,
wlan_11h_is_master_active_on_dfs_chan,
wlan_11h_is_dfs_master) > 0))
radar_det_state_new |= MASTER_RADAR_DET_MASK;
if (pmadapter->state_11h.slave_radar_det_enable_pending ||
(wlan_count_priv_cond(pmadapter,
wlan_11h_is_slave_active_on_dfs_chan,
wlan_11h_is_dfs_slave) > 0))
radar_det_state_new |= SLAVE_RADAR_DET_MASK;
PRINTM(MINFO, "%s: radar_det_state_new=0x%x\n", __func__,
radar_det_state_new);
/* now compare flags with current state */
ret = (wlan_11h_get_current_radar_detect_flags(pmadapter) !=
radar_det_state_new) ?
MTRUE :
MFALSE;
if (ret)
*pnew_state = radar_det_state_new;
LEAVE();
return ret;
}
/**
* @brief generate the channel center frequency index
*
* @param channel_num channel number
*
* @return frenquency index
*/
static t_u8 wlan_11h_get_channel_freq_idx(t_u8 channel_num)
{
t_u8 index;
t_u8 center_freq[] = {42, 58, 106, 122, 138, 155};
t_u8 chan_idx, ret = 0;
chan_idx = channel_num - 100;
for (index = 0; index < sizeof(center_freq); index++) {
if ((chan_idx >= (center_freq[index] - 6)) &&
(chan_idx <= (center_freq[index] + 6))) {
ret = center_freq[index];
break;
}
}
return ret;
}
/**
* @brief Prepare ioctl for add/remove CHAN_SW IE - RADAR_DETECTED event
* handling
*
* @param pmadapter Pointer to mlan_adapter
* @param pioctl_req Pointer to completed mlan_ioctl_req (allocated
* inside)
* @param ppcust_chansw_ie Poniter to customer ie
* @param is_adding_ie CHAN_SW IE is to be added (MTRUE), or removed
* (MFALSE)
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status
wlan_11h_prepare_custom_ie_chansw(mlan_adapter *pmadapter,
mlan_ioctl_req **ppioctl_req,
t_bool is_adding_ie)
{
mlan_ioctl_req *pioctl_req = MNULL;
mlan_ds_misc_cfg *pds_misc_cfg = MNULL;
custom_ie *pcust_chansw_ie = MNULL;
IEEEtypes_ChanSwitchAnn_t *pchansw_ie = MNULL;
mlan_status ret;
IEEEtypes_Header_t *pChanSwWrap_ie = MNULL;
IEEEtypes_WideBWChanSwitch_t *pbwchansw_ie = MNULL;
IEEEtypes_VhtTpcEnvelope_t *pvhttpcEnv_ie = MNULL;
t_u8 index;
mlan_private *pmpriv = MNULL;
ENTER();
if (pmadapter == MNULL || ppioctl_req == MNULL) {
LEAVE();
return MLAN_STATUS_FAILURE;
}
/* allocate buffer for mlan_ioctl_req and mlan_ds_misc_cfg */
/* FYI - will be freed as part of cmd_response handler */
ret = pmadapter->callbacks.moal_malloc(
pmadapter->pmoal_handle,
sizeof(mlan_ioctl_req) + sizeof(mlan_ds_misc_cfg), MLAN_MEM_DEF,
(t_u8 **)&pioctl_req);
if ((ret != MLAN_STATUS_SUCCESS) || !pioctl_req) {
PRINTM(MERROR, "%s(): Could not allocate ioctl req\n",
__func__);
LEAVE();
return MLAN_STATUS_FAILURE;
}
pds_misc_cfg = (mlan_ds_misc_cfg *)((t_u8 *)pioctl_req +
sizeof(mlan_ioctl_req));
/* prepare mlan_ioctl_req */
memset(pmadapter, pioctl_req, 0x00, sizeof(mlan_ioctl_req));
pioctl_req->req_id = MLAN_IOCTL_MISC_CFG;
pioctl_req->action = MLAN_ACT_SET;
pioctl_req->pbuf = (t_u8 *)pds_misc_cfg;
pioctl_req->buf_len = sizeof(mlan_ds_misc_cfg);
/* prepare mlan_ds_misc_cfg */
memset(pmadapter, pds_misc_cfg, 0x00, sizeof(mlan_ds_misc_cfg));
pds_misc_cfg->sub_command = MLAN_OID_MISC_CUSTOM_IE;
pds_misc_cfg->param.cust_ie.type = TLV_TYPE_MGMT_IE;
pds_misc_cfg->param.cust_ie.len = (sizeof(custom_ie) - MAX_IE_SIZE);
/* configure custom_ie api settings */
pcust_chansw_ie =
(custom_ie *)&pds_misc_cfg->param.cust_ie.ie_data_list[0];
pcust_chansw_ie->ie_index = 0xffff; /* Auto index */
pcust_chansw_ie->ie_length = sizeof(IEEEtypes_ChanSwitchAnn_t);
pcust_chansw_ie->mgmt_subtype_mask =
(is_adding_ie) ? MBIT(8) | MBIT(5) /* add IE for BEACON |
PROBE_RSP */
:
0; /* remove IE */
/* prepare CHAN_SW IE inside ioctl */
pchansw_ie = (IEEEtypes_ChanSwitchAnn_t *)pcust_chansw_ie->ie_buffer;
pchansw_ie->element_id = CHANNEL_SWITCH_ANN;
pchansw_ie->len =
sizeof(IEEEtypes_ChanSwitchAnn_t) - sizeof(IEEEtypes_Header_t);
pchansw_ie->chan_switch_mode = 1; /* STA should not transmit */
pchansw_ie->new_channel_num = pmadapter->state_rdh.new_channel;
pchansw_ie->chan_switch_count = pmadapter->dfs_cs_count;
PRINTM(MCMD_D, "New Channel = %d Channel switch count = %d\n",
pmadapter->state_rdh.new_channel, pchansw_ie->chan_switch_count);
for (index = 0; index < pmadapter->state_rdh.priv_list_count; index++) {
pmpriv = pmadapter->state_rdh.priv_list[index];
/*find the first AP interface*/
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
if (pmpriv->is_11ac_enabled) {
pChanSwWrap_ie =
(IEEEtypes_Header_t
*)((t_u8 *)pchansw_ie +
sizeof(IEEEtypes_ChanSwitchAnn_t));
pChanSwWrap_ie->element_id = EXT_POWER_CONSTR;
/*will have multiple sub IEs*/
pChanSwWrap_ie->len = 0;
/* prepare the Wide Bandwidth Channel Switch IE
* Channel Switch IE */
pbwchansw_ie =
(IEEEtypes_WideBWChanSwitch_t
*)((t_u8 *)pChanSwWrap_ie +
sizeof(IEEEtypes_Header_t));
pbwchansw_ie->ieee_hdr.element_id =
BW_CHANNEL_SWITCH;
pbwchansw_ie->ieee_hdr.len =
sizeof(IEEEtypes_WideBWChanSwitch_t) -
sizeof(IEEEtypes_Header_t);
/*fix 80MHZ now*/
pbwchansw_ie->new_channel_width =
VHT_OPER_CHWD_80MHZ;
pbwchansw_ie->new_channel_center_freq0 =
wlan_11h_get_channel_freq_idx(
pmadapter->state_rdh
.new_channel);
pbwchansw_ie->new_channel_center_freq1 =
wlan_11h_get_channel_freq_idx(
pmadapter->state_rdh
.new_channel);
pChanSwWrap_ie->len +=
sizeof(IEEEtypes_WideBWChanSwitch_t);
/*prepare the VHT Transmit Power Envelope IE*/
pvhttpcEnv_ie =
(IEEEtypes_VhtTpcEnvelope_t
*)((t_u8 *)pChanSwWrap_ie +
sizeof(IEEEtypes_Header_t) +
sizeof(IEEEtypes_WideBWChanSwitch_t));
pvhttpcEnv_ie->ieee_hdr.element_id =
VHT_TX_POWER_ENV;
pvhttpcEnv_ie->ieee_hdr.len =
sizeof(IEEEtypes_VhtTpcEnvelope_t) -
sizeof(IEEEtypes_Header_t);
/* Local Max TX Power Count= 3,
* Local TX Power Unit Inter=EIP(0) */
pvhttpcEnv_ie->tpc_info = 3;
pvhttpcEnv_ie->local_max_tp_20mhz = 0xff;
pvhttpcEnv_ie->local_max_tp_40mhz = 0xff;
pvhttpcEnv_ie->local_max_tp_80mhz = 0xff;
pvhttpcEnv_ie->local_max_tp_160mhz_80_80mhz =
0xff;
pChanSwWrap_ie->len +=
sizeof(IEEEtypes_VhtTpcEnvelope_t);
pcust_chansw_ie->ie_length +=
sizeof(IEEEtypes_WideBWChanSwitch_t) +
sizeof(IEEEtypes_VhtTpcEnvelope_t) +
sizeof(IEEEtypes_Header_t);
PRINTM(MINFO,
"Append Wide Bandwidth Channel Switch IE\n");
break;
}
}
}
pds_misc_cfg->param.cust_ie.len += pcust_chansw_ie->ie_length;
DBG_HEXDUMP(MCMD_D, "11h: custom_ie containing CHAN_SW IE",
(t_u8 *)pcust_chansw_ie, pds_misc_cfg->param.cust_ie.len);
/* assign output pointer before returning */
*ppioctl_req = pioctl_req;
LEAVE();
return MLAN_STATUS_SUCCESS;
}
#ifdef UAP_SUPPORT
/**
* @brief Check if start channel 165 is allowed to operate in
* previous uAP channel's band config
*
* @param start_chn Random Start channel choosen after radar detection
* @param uap_band_cfg Private driver uAP band configuration information
* structure
*
* @return MFALSE if the channel is not allowed in given band
*/
static t_bool wlan_11h_is_band_valid(t_u8 start_chn, Band_Config_t uap_band_cfg)
{
/* if band width is not 20MHZ (either 40 or 80MHz)
* return MFALSE, 165 is not allowed in bands other than 20MHZ
*/
if (start_chn == 165 && (uap_band_cfg.chanWidth != CHAN_BW_20MHZ)) {
return MFALSE;
}
return MTRUE;
}
/**
* @brief Retrieve a randomly selected starting channel if needed for 11h
*
* If 11h is enabled and 5GHz band is selected in band_config
* return a random channel in A band, else one from BG band.
*
* @param priv Private driver information structure
* @param uap_band_cfg Private driver information structure
*
* @return Starting channel
*/
static t_u8 wlan_11h_get_uap_start_channel(mlan_private *priv,
Band_Config_t uap_band_cfg)
{
t_u8 start_chn;
mlan_adapter *adapter = priv->adapter;
t_u32 region;
t_u32 rand_entry;
region_chan_t *chn_tbl;
t_u8 rand_tries = 0;
/*TODO: right now mostly a copy of wlan_11h_get_adhoc_start_channel.
* Improve to be more specfic to UAP, e.g.
* 1. take into account COUNTRY_CODE -> region_code
* 2. check domain_info for value channels
*/
ENTER();
/*
* Set start_chn to the Default.
* Used if 11h is disabled or the band
* does not require 11h support.
*/
start_chn = DEFAULT_AD_HOC_CHANNEL;
/*
* Check that we are looking for a channel in the A Band
*/
if (uap_band_cfg.chanBand == BAND_5GHZ) {
/*
* Set default to the A Band default.
* Used if random selection fails
* or if 11h is not enabled
*/
start_chn = DEFAULT_AD_HOC_CHANNEL_A;
/*
* Check that 11h is enabled in the driver
*/
if (wlan_11h_is_enabled(priv)) {
/*
* Search the region_channel tables for a channel table
* that is marked for the A Band.
*/
for (region = 0; (region < MAX_REGION_CHANNEL_NUM);
region++) {
chn_tbl = &adapter->region_channel[region];
/* Check if table is valid and marked for A Band
*/
if (chn_tbl->valid &&
chn_tbl->region == adapter->region_code &&
chn_tbl->band & BAND_A) {
/*
* Set the start channel. Get a random
* number and use it to pick an entry
* in the table between 0 and the number
* of channels in the table (NumCFP).
*/
rand_entry = wlan_11h_get_random_num(
adapter) %
chn_tbl->num_cfp;
start_chn =
(t_u8)chn_tbl->pcfp[rand_entry]
.channel;
/* Loop until a non-dfs channel is found
* with compatible band bounded by
* chn_tbl->num_cfp entries in the
* channel table
*/
while (((chn_tbl->pcfp[rand_entry]
.dynamic.flags &
NXP_CHANNEL_DISABLED) ||
(wlan_11h_is_channel_under_nop(
adapter, start_chn) ||
((adapter->state_rdh.stage ==
RDH_GET_INFO_CHANNEL) &&
wlan_11h_radar_detect_required(
priv, start_chn)) ||
!(wlan_11h_is_band_valid(
start_chn,
uap_band_cfg)))) &&
(++rand_tries <
chn_tbl->num_cfp)) {
rand_entry++;
rand_entry = rand_entry %
chn_tbl->num_cfp;
start_chn =
(t_u8)chn_tbl
->pcfp[rand_entry]
.channel;
PRINTM(MINFO,
"start chan=%d rand_entry=%d\n",
start_chn, rand_entry);
}
if (rand_tries == chn_tbl->num_cfp) {
PRINTM(MERROR,
"Failed to get UAP start channel\n");
start_chn = 0;
}
}
}
}
}
PRINTM(MCMD_D, "11h: UAP Get Start Channel %d\n", start_chn);
LEAVE();
return start_chn;
}
#endif /* UAP_SUPPORT */
#ifdef DEBUG_LEVEL1
static const char *DFS_TS_REPR_STRINGS[] = {"", "NOP_start", "CAC_completed"};
#endif
/**
* @brief Search for a dfs timestamp in the list with desired channel.
*
* Assumes there will only be one timestamp per channel in the list.
*
* @param pmadapter Pointer to mlan_adapter
* @param channel Channel number
*
* @return Pointer to timestamp if found, or MNULL
*/
static wlan_dfs_timestamp_t *
wlan_11h_find_dfs_timestamp(mlan_adapter *pmadapter, t_u8 channel)
{
wlan_dfs_timestamp_t *pts = MNULL, *pts_found = MNULL;
ENTER();
pts = (wlan_dfs_timestamp_t *)util_peek_list(
pmadapter->pmoal_handle, &pmadapter->state_dfs.dfs_ts_head,
MNULL, MNULL);
while (pts && pts != (wlan_dfs_timestamp_t *)&pmadapter->state_dfs
.dfs_ts_head) {
PRINTM(MINFO,
"dfs_timestamp(@ %p) - chan=%d, repr=%d(%s),"
" time(sec.usec)=%lu.%06lu\n",
pts, pts->channel, pts->represents,
DFS_TS_REPR_STRINGS[pts->represents], pts->ts_sec,
pts->ts_usec);
if (pts->channel == channel) {
pts_found = pts;
break;
}
pts = pts->pnext;
}
LEAVE();
return pts_found;
}
/**
* @brief Removes dfs timestamp from list.
*
* @param pmadapter Pointer to mlan_adapter
* @param pdfs_ts Pointer to dfs_timestamp to remove
*/
static t_void wlan_11h_remove_dfs_timestamp(mlan_adapter *pmadapter,
wlan_dfs_timestamp_t *pdfs_ts)
{
ENTER();
/* dequeue and delete timestamp */
util_unlink_list(pmadapter->pmoal_handle,
&pmadapter->state_dfs.dfs_ts_head,
(pmlan_linked_list)pdfs_ts, MNULL, MNULL);
pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle,
(t_u8 *)pdfs_ts);
LEAVE();
}
/**
* @brief Add a dfs timestamp to the list
*
* Assumes there will only be one timestamp per channel in the list,
* and that timestamp modes (represents) are mutually exclusive.
*
* @param pmadapter Pointer to mlan_adapter
* @param repr Timestamp 'represents' value (see _dfs_timestamp_repr_e)
* @param channel Channel number
*
* @return Pointer to timestamp if found, or MNULL
*/
static mlan_status wlan_11h_add_dfs_timestamp(mlan_adapter *pmadapter,
t_u8 repr, t_u8 channel)
{
wlan_dfs_timestamp_t *pdfs_ts = MNULL;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
pdfs_ts = wlan_11h_find_dfs_timestamp(pmadapter, channel);
if (!pdfs_ts) {
/* need to allocate new timestamp */
ret = pmadapter->callbacks.moal_malloc(
pmadapter->pmoal_handle, sizeof(wlan_dfs_timestamp_t),
MLAN_MEM_DEF, (t_u8 **)&pdfs_ts);
if ((ret != MLAN_STATUS_SUCCESS) || !pdfs_ts) {
PRINTM(MERROR, "%s(): Could not allocate dfs_ts\n",
__func__);
LEAVE();
return MLAN_STATUS_FAILURE;
}
memset(pmadapter, (t_u8 *)pdfs_ts, 0,
sizeof(wlan_dfs_timestamp_t));
util_enqueue_list_tail(pmadapter->pmoal_handle,
&pmadapter->state_dfs.dfs_ts_head,
(pmlan_linked_list)pdfs_ts, MNULL,
MNULL);
pdfs_ts->channel = channel;
}
/* (else, use existing timestamp for channel; see assumptions above) */
/* update params */
pmadapter->callbacks.moal_get_system_time(
pmadapter->pmoal_handle, &pdfs_ts->ts_sec, &pdfs_ts->ts_usec);
pdfs_ts->represents = repr;
PRINTM(MCMD_D,
"11h: add/update dfs_timestamp - chan=%d, repr=%d(%s),"
" time(sec.usec)=%lu.%06lu\n",
pdfs_ts->channel, pdfs_ts->represents,
DFS_TS_REPR_STRINGS[pdfs_ts->represents], pdfs_ts->ts_sec,
pdfs_ts->ts_usec);
LEAVE();
return ret;
}
/********************************************************
Global functions
********************************************************/
/**
* @brief Return whether the device has activated master radar detection.
*
* @param priv Private driver information structure
*
* @return
* - MTRUE if master radar detection is enabled in firmware
* - MFALSE otherwise
*/
t_bool wlan_11h_is_master_radar_det_active(mlan_private *priv)
{
ENTER();
LEAVE();
return priv->adapter->state_11h.is_master_radar_det_active;
}
/**
* @brief Configure master radar detection.
* Call wlan_11h_check_update_radar_det_state() afterwards
* to push this to firmware.
*
* @param priv Private driver information structure
* @param enable Whether to enable or disable master radar detection
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*
* @sa wlan_11h_check_update_radar_det_state
*/
mlan_status wlan_11h_config_master_radar_det(mlan_private *priv, t_bool enable)
{
mlan_status ret = MLAN_STATUS_FAILURE;
/* Force disable master radar detection on in-AP interfaces */
if (priv->adapter->dfs_repeater)
enable = MFALSE;
ENTER();
if (wlan_11h_is_dfs_master(priv) &&
priv->adapter->init_para.dfs_master_radar_det_en) {
priv->adapter->state_11h.master_radar_det_enable_pending =
enable;
ret = MLAN_STATUS_SUCCESS;
}
LEAVE();
return ret;
}
/**
* @brief Configure slave radar detection.
* Call wlan_11h_check_update_radar_det_state() afterwards
* to push this to firmware.
*
* @param priv Private driver information structure
* @param enable Whether to enable or disable slave radar detection
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*
* @sa wlan_11h_check_update_radar_det_state
*/
mlan_status wlan_11h_config_slave_radar_det(mlan_private *priv, t_bool enable)
{
mlan_status ret = MLAN_STATUS_FAILURE;
/* Force disable radar detection on STA interfaces */
if (priv->adapter->dfs_repeater)
enable = MFALSE;
ENTER();
if (wlan_11h_is_dfs_slave(priv) &&
priv->adapter->init_para.dfs_slave_radar_det_en) {
priv->adapter->state_11h.slave_radar_det_enable_pending =
enable;
ret = MLAN_STATUS_SUCCESS;
}
LEAVE();
return ret;
}
/**
* @brief Checks all interfaces and determines if radar_detect flag states
* have/should be changed. If so, sends SNMP_MIB 11H command to FW.
* Call this function on any interface enable/disable/channel change.
*
* @param pmpriv Pointer to mlan_private structure
*
* @return MLAN_STATUS_SUCCESS (update or not)
* or MLAN_STATUS_FAILURE (cmd failure)
*
* @sa wlan_11h_check_radar_det_state
*/
mlan_status wlan_11h_check_update_radar_det_state(mlan_private *pmpriv)
{
t_u32 new_radar_det_state = 0;
t_u32 mib_11h = 0;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
if (wlan_11h_check_radar_det_state(pmpriv->adapter,
&new_radar_det_state)) {
PRINTM(MCMD_D, "%s: radar_det_state being updated.\n",
__func__);
mib_11h |= new_radar_det_state;
/* keep priv's existing 11h state */
if (pmpriv->intf_state_11h.is_11h_active)
mib_11h |= ENABLE_11H_MASK;
/* Send cmd to FW to enable/disable 11h function in firmware */
ret = wlan_prepare_cmd(pmpriv, HostCmd_CMD_802_11_SNMP_MIB,
HostCmd_ACT_GEN_SET, Dot11H_i, MNULL,
&mib_11h);
if (ret)
ret = MLAN_STATUS_FAILURE;
}
/* updated state sent OR no change, thus no longer pending */
pmpriv->adapter->state_11h.master_radar_det_enable_pending = MFALSE;
pmpriv->adapter->state_11h.slave_radar_det_enable_pending = MFALSE;
LEAVE();
return ret;
}
/**
* @brief Query 11h firmware enabled state.
*
* Return whether the firmware currently has 11h extensions enabled
*
* @param priv Private driver information structure
*
* @return
* - MTRUE if 11h has been activated in the firmware
* - MFALSE otherwise
*
* @sa wlan_11h_activate
*/
t_bool wlan_11h_is_active(mlan_private *priv)
{
ENTER();
LEAVE();
return priv->intf_state_11h.is_11h_active;
}
/**
* @brief Enable the transmit interface and record the state.
*
* @param priv Private driver information structure
*
* @return N/A
*/
t_void wlan_11h_tx_enable(mlan_private *priv)
{
ENTER();
if (priv->intf_state_11h.tx_disabled) {
if (priv->media_connected == MTRUE) {
wlan_recv_event(priv, MLAN_EVENT_ID_FW_START_TX, MNULL);
priv->intf_state_11h.tx_disabled = MFALSE;
}
}
LEAVE();
}
/**
* @brief Disable the transmit interface and record the state.
*
* @param priv Private driver information structure
*
* @return N/A
*/
t_void wlan_11h_tx_disable(mlan_private *priv)
{
ENTER();
if (!priv->intf_state_11h.tx_disabled) {
if (priv->media_connected == MTRUE) {
priv->intf_state_11h.tx_disabled = MTRUE;
wlan_recv_event(priv, MLAN_EVENT_ID_FW_STOP_TX, MNULL);
}
}
LEAVE();
}
/**
* @brief Enable or Disable the 11h extensions in the firmware
*
* @param priv Private driver information structure
* @param pioctl_buf A pointer to MLAN IOCTL Request buffer
* @param flag Enable 11h if MTRUE, disable otherwise
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_activate(mlan_private *priv, t_void *pioctl_buf,
t_bool flag)
{
t_u32 enable = flag & ENABLE_11H_MASK;
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
/* add bits for master/slave radar detect into enable. */
enable |= wlan_11h_get_current_radar_detect_flags(priv->adapter);
/* Whenever repeater mode is on make sure
* we do not enable master or slave radar det mode.
* HW will not detect radar in dfs_repeater mode.
*/
if (priv->adapter->dfs_repeater) {
enable &= ~(MASTER_RADAR_DET_MASK | SLAVE_RADAR_DET_MASK);
}
/*
* Send cmd to FW to enable/disable 11h function in firmware
*/
ret = wlan_prepare_cmd(priv, HostCmd_CMD_802_11_SNMP_MIB,
HostCmd_ACT_GEN_SET, Dot11H_i,
(t_void *)pioctl_buf, &enable);
if (ret)
ret = MLAN_STATUS_FAILURE;
else
/* Set boolean flag in driver 11h state */
priv->intf_state_11h.is_11h_active = flag;
PRINTM(MINFO, "11h: %s\n", flag ? "Activate" : "Deactivate");
LEAVE();
return ret;
}
/**
* @brief Initialize the 11h parameters and enable 11h when starting an IBSS
*
* @param adapter mlan_adapter structure
*
* @return N/A
*/
t_void wlan_11h_init(mlan_adapter *adapter)
{
wlan_11h_device_state_t *pstate_11h = &adapter->state_11h;
IEEEtypes_Quiet_t *pquiet = &adapter->state_11h.quiet_ie;
wlan_dfs_device_state_t *pstate_dfs = &adapter->state_dfs;
wlan_radar_det_hndlg_state_t *pstate_rdh = &adapter->state_rdh;
wlan_dfs_testing_settings_t *pdfs_test = &adapter->dfs_test_params;
ENTER();
/* Initialize 11H struct */
pstate_11h->usr_def_power_constraint = WLAN_11H_TPC_POWERCONSTRAINT;
pstate_11h->min_tx_power_capability = WLAN_11H_TPC_POWERCAPABILITY_MIN;
pstate_11h->max_tx_power_capability = WLAN_11H_TPC_POWERCAPABILITY_MAX;
pstate_11h->recvd_chanswann_event = MFALSE;
pstate_11h->master_radar_det_enable_pending = MFALSE;
pstate_11h->slave_radar_det_enable_pending = MFALSE;
pstate_11h->is_master_radar_det_active = MFALSE;
pstate_11h->is_slave_radar_det_active = MFALSE;
/*Initialize quiet_ie*/
memset(adapter, pquiet, 0, sizeof(IEEEtypes_Quiet_t));
pquiet->element_id = QUIET;
pquiet->len =
(sizeof(pquiet->quiet_count) + sizeof(pquiet->quiet_period) +
sizeof(pquiet->quiet_duration) + sizeof(pquiet->quiet_offset));
/* Initialize DFS struct */
pstate_dfs->dfs_check_pending = MFALSE;
pstate_dfs->dfs_radar_found = MFALSE;
pstate_dfs->dfs_check_channel = 0;
pstate_dfs->dfs_report_time_sec = 0;
util_init_list((pmlan_linked_list)&pstate_dfs->dfs_ts_head);
/* Initialize RDH struct */
pstate_rdh->stage = RDH_OFF;
pstate_rdh->priv_list_count = 0;
pstate_rdh->priv_curr_idx = 0;
pstate_rdh->curr_channel = 0;
pstate_rdh->new_channel = 0;
memset(adapter, &(pstate_rdh->uap_band_cfg), 0,
sizeof(pstate_rdh->uap_band_cfg));
pstate_rdh->max_bcn_dtim_ms = 0;
memset(adapter, pstate_rdh->priv_list, 0,
sizeof(pstate_rdh->priv_list));
/* Initialize dfs channel switch count */
#define DFS_CS_COUNT 5
adapter->dfs_cs_count = DFS_CS_COUNT;
/* Initialize DFS testing struct */
pdfs_test->user_cac_period_msec = 0;
pdfs_test->user_nop_period_sec = 0;
pdfs_test->no_channel_change_on_radar = MFALSE;
pdfs_test->fixed_new_channel_on_radar = 0;
pdfs_test->cac_restart = 0;
pdfs_test->millisec_dwell_time = 0;
adapter->dfs53cfg = adapter->init_para.dfs53cfg;
LEAVE();
}
/**
* @brief Cleanup for the 11h parameters that allocated memory, etc.
*
* @param adapter mlan_adapter structure
*
* @return N/A
*/
t_void wlan_11h_cleanup(mlan_adapter *adapter)
{
wlan_dfs_device_state_t *pstate_dfs = &adapter->state_dfs;
wlan_dfs_timestamp_t *pdfs_ts;
ENTER();
/* cleanup dfs_timestamp list */
pdfs_ts = (wlan_dfs_timestamp_t *)util_peek_list(
adapter->pmoal_handle, &pstate_dfs->dfs_ts_head, MNULL, MNULL);
while (pdfs_ts) {
util_unlink_list(adapter->pmoal_handle,
&pstate_dfs->dfs_ts_head,
(pmlan_linked_list)pdfs_ts, MNULL, MNULL);
adapter->callbacks.moal_mfree(adapter->pmoal_handle,
(t_u8 *)pdfs_ts);
pdfs_ts = (wlan_dfs_timestamp_t *)util_peek_list(
adapter->pmoal_handle, &pstate_dfs->dfs_ts_head, MNULL,
MNULL);
}
LEAVE();
}
/**
* @brief Initialize the 11h parameters and enable 11h when starting an IBSS
*
* @param pmpriv Pointer to mlan_private structure
*
* @return N/A
*/
t_void wlan_11h_priv_init(mlan_private *pmpriv)
{
wlan_11h_interface_state_t *pistate_11h = &pmpriv->intf_state_11h;
ENTER();
pistate_11h->is_11h_enabled = MTRUE;
pistate_11h->is_11h_active = MFALSE;
pistate_11h->adhoc_auto_sel_chan = MTRUE;
pistate_11h->tx_disabled = MFALSE;
pistate_11h->dfs_slave_csa_chan = 0;
pistate_11h->dfs_slave_csa_expire_at_sec = 0;
LEAVE();
}
/**
* @brief Retrieve a randomly selected starting channel if needed for 11h
*
* If 11h is enabled and an A-Band channel start band preference
* configured in the driver, the start channel must be random in order
* to meet with
*
* @param priv Private driver information structure
*
* @return Starting channel
*/
t_u8 wlan_11h_get_adhoc_start_channel(mlan_private *priv)
{
t_u8 start_chn;
mlan_adapter *adapter = priv->adapter;
t_u32 region;
t_u32 rand_entry;
region_chan_t *chn_tbl;
t_u8 rand_tries = 0;
ENTER();
/*
* Set start_chn to the Default. Used if 11h is disabled or the band
* does not require 11h support.
*/
start_chn = DEFAULT_AD_HOC_CHANNEL;
/*
* Check that we are looking for a channel in the A Band
*/
if ((adapter->adhoc_start_band & BAND_A)) {
/*
* Set default to the A Band default.
* Used if random selection fails
* or if 11h is not enabled
*/
start_chn = DEFAULT_AD_HOC_CHANNEL_A;
/*
* Check that 11h is enabled in the driver
*/
if (wlan_11h_is_enabled(priv)) {
/*
* Search the region_channel tables for a channel table
* that is marked for the A Band.
*/
for (region = 0; (region < MAX_REGION_CHANNEL_NUM);
region++) {
chn_tbl = &adapter->region_channel[region];
/* Check if table is valid and marked for A Band
*/
if (chn_tbl->valid &&
chn_tbl->region == adapter->region_code &&
chn_tbl->band & BAND_A) {
/*
* Set the start channel. Get a random
* number and use it to pick an entry
* in the table between 0 and the number
* of channels in the table (NumCFP).
*/
do {
rand_entry =
wlan_11h_get_random_num(
adapter) %
chn_tbl->num_cfp;
start_chn =
(t_u8)chn_tbl
->pcfp[rand_entry]
.channel;
} while (
(wlan_11h_is_channel_under_nop(
adapter, start_chn) ||
((adapter->state_rdh.stage ==
RDH_GET_INFO_CHANNEL) &&
wlan_11h_radar_detect_required(
priv, start_chn))) &&
(++rand_tries <
MAX_RANDOM_CHANNEL_RETRIES));
}
}
}
}
PRINTM(MINFO, "11h: %s: AdHoc Channel set to %u\n",
wlan_11h_is_enabled(priv) ? "Enabled" : "Disabled", start_chn);
LEAVE();
return start_chn;
}
/**
* @brief Retrieve channel closed for operation by Channel Switch Announcement
*
* After receiving CSA, we must not transmit in any form on the original
* channel for a certain duration. This checks the time, and returns
* the channel if valid.
*
* @param priv Private driver information structure
*
* @return Closed channel, else 0
*/
t_u8 wlan_11h_get_csa_closed_channel(mlan_private *priv)
{
t_u32 sec, usec;
ENTER();
if (!priv->intf_state_11h.dfs_slave_csa_chan) {
LEAVE();
return 0;
}
/* have csa channel, check if expired or not */
priv->adapter->callbacks.moal_get_system_time(
priv->adapter->pmoal_handle, &sec, &usec);
if (sec > priv->intf_state_11h.dfs_slave_csa_expire_at_sec) {
/* expired: remove channel from blacklist table, and clear vars
*/
wlan_set_chan_blacklist(priv, BAND_A,
priv->intf_state_11h.dfs_slave_csa_chan,
MFALSE);
priv->intf_state_11h.dfs_slave_csa_chan = 0;
priv->intf_state_11h.dfs_slave_csa_expire_at_sec = 0;
}
LEAVE();
return priv->intf_state_11h.dfs_slave_csa_chan;
}
/**
* @brief Check if the current region's regulations require the input channel
* to be scanned for radar.
*
* Based on statically defined requirements for sub-bands per regulatory
* agency requirements.
*
* Used in adhoc start to determine if channel availability check is required
*
* @param priv Private driver information structure
* @param channel Channel to determine radar detection requirements
*
* @return
* - MTRUE if radar detection is required
* - MFALSE otherwise
*/
/** @sa wlan_11h_issue_radar_detect
*/
t_bool wlan_11h_radar_detect_required(mlan_private *priv, t_u8 channel)
{
t_bool required = MFALSE;
ENTER();
/*
* No checks for 11h or measurement code being enabled is placed here
* since regulatory requirements exist whether we support them or not.
*/
required = wlan_get_cfp_radar_detect(priv, channel);
if (!priv->adapter->region_code) {
PRINTM(MINFO,
"11h: Radar detection in CFP code BG:%#x "
", A:%#x "
"is %srequired for channel %d\n",
priv->adapter->cfp_code_bg, priv->adapter->cfp_code_a,
(required ? "" : "not "), channel);
} else
PRINTM(MINFO,
"11h: Radar detection in region %#02x "
"is %srequired for channel %d\n",
priv->adapter->region_code, (required ? "" : "not "),
channel);
if (required == MTRUE && priv->media_connected == MTRUE &&
priv->curr_bss_params.bss_descriptor.channel == channel) {
required = MFALSE;
PRINTM(MINFO, "11h: Radar detection not required. "
"Already operating on the channel\n");
}
LEAVE();
return required;
}
t_s32 wlan_11h_cancel_radar_detect(mlan_private *priv)
{
t_s32 ret;
HostCmd_DS_CHAN_RPT_REQ chan_rpt_req;
memset(priv->adapter, &chan_rpt_req, 0x00, sizeof(chan_rpt_req));
ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST,
HostCmd_ACT_GEN_SET, 0, (t_void *)MNULL,
(t_void *)&chan_rpt_req);
return ret;
}
/**
* @brief Perform a radar measurement if required on given channel
*
* Check to see if the provided channel requires a channel availability
* check (60 second radar detection measurement). If required, perform
* measurement, stalling calling thread until the measurement completes
* and then report result.
*
* Used when starting an adhoc or AP network.
*
* @param priv Private driver information structure
* @param pioctl_req Pointer to IOCTL request buffer
* @param channel Channel on which to perform radar measurement
* @param bandcfg Channel Band config structure
*
* @return
* - MTRUE if radar measurement request was successfully issued
* - MFALSE if radar detection is not required
* - < 0 for error during radar detection (if performed)
*
* @sa wlan_11h_radar_detect_required
*/
t_s32 wlan_11h_issue_radar_detect(mlan_private *priv,
pmlan_ioctl_req pioctl_req, t_u8 channel,
Band_Config_t bandcfg)
{
t_s32 ret;
HostCmd_DS_CHAN_RPT_REQ chan_rpt_req;
mlan_adapter *pmadapter = priv->adapter;
mlan_ds_11h_cfg *ds_11hcfg = MNULL;
ENTER();
ret = wlan_11h_radar_detect_required(priv, channel);
if (ret) {
/* Prepare and issue CMD_CHAN_RPT_REQ. */
memset(priv->adapter, &chan_rpt_req, 0x00,
sizeof(chan_rpt_req));
chan_rpt_req.chan_desc.startFreq = START_FREQ_11A_BAND;
if (pmadapter->chanrpt_param_bandcfg) {
chan_rpt_req.chan_desc.bandcfg = bandcfg;
} else {
*((t_u8 *)&chan_rpt_req.chan_desc.bandcfg) =
(t_u8)bandcfg.chanWidth;
}
chan_rpt_req.chan_desc.chanNum = channel;
chan_rpt_req.millisec_dwell_time =
WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION;
/* ETSI new requirement for ch 120, 124 and 128 */
if (wlan_is_etsi_country(pmadapter, pmadapter->country_code)) {
if (channel == 120 || channel == 124 ||
channel == 128) {
chan_rpt_req.millisec_dwell_time =
WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION *
10;
}
if (channel == 116 &&
((bandcfg.chanWidth == CHAN_BW_40MHZ) ||
(bandcfg.chanWidth == CHAN_BW_80MHZ))) {
chan_rpt_req.millisec_dwell_time =
WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION *
10;
}
}
/* Save dwell time information to be used later in moal */
if (pioctl_req) {
ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf;
if (!ds_11hcfg->param.chan_rpt_req.host_based) {
ds_11hcfg->param.chan_rpt_req
.millisec_dwell_time =
chan_rpt_req.millisec_dwell_time;
}
}
if (priv->adapter->dfs_test_params.user_cac_period_msec) {
PRINTM(MCMD_D,
"dfs_testing - user CAC period=%d (msec)\n",
priv->adapter->dfs_test_params
.user_cac_period_msec);
chan_rpt_req.millisec_dwell_time =
priv->adapter->dfs_test_params
.user_cac_period_msec;
}
if (priv->adapter->dfs_test_params.cac_restart) {
priv->adapter->dfs_test_params.chan =
chan_rpt_req.chan_desc.chanNum;
if (chan_rpt_req.millisec_dwell_time)
priv->adapter->dfs_test_params
.millisec_dwell_time =
chan_rpt_req.millisec_dwell_time;
else
chan_rpt_req.millisec_dwell_time =
priv->adapter->dfs_test_params
.millisec_dwell_time;
memcpy_ext(priv->adapter,
&priv->adapter->dfs_test_params.bandcfg,
&bandcfg, sizeof(bandcfg), sizeof(bandcfg));
}
PRINTM(MMSG,
"11h: issuing DFS Radar check for channel=%d."
" Please wait for response...\n",
channel);
ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST,
HostCmd_ACT_GEN_SET, 0,
(t_void *)pioctl_req,
(t_void *)&chan_rpt_req);
}
LEAVE();
return ret;
}
/**
* @brief Checks if a radar measurement was performed on channel,
* and if so, whether radar was detected on it.
*
* Used when starting an adhoc network.
*
* @param priv Private driver information structure
* @param chan Channel to check upon
*
* @return
* - MLAN_STATUS_SUCCESS if no radar on channel
* - MLAN_STATUS_FAILURE if radar was found on channel
* - (TBD??) MLAN_STATUS_PENDING if radar report NEEDS TO BE REISSUED
*
* @sa wlan_11h_issue_radar_detect
* @sa wlan_11h_process_start
*/
mlan_status wlan_11h_check_chan_report(mlan_private *priv, t_u8 chan)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs;
t_u32 sec, usec;
ENTER();
/* check report we hold is valid or not */
priv->adapter->callbacks.moal_get_system_time(
priv->adapter->pmoal_handle, &sec, &usec);
PRINTM(MINFO, "11h: %s()\n", __func__);
PRINTM(MINFO, "- sec_now=%d, sec_report=%d.\n", sec,
pstate_dfs->dfs_report_time_sec);
PRINTM(MINFO, "- rpt_channel=%d, rpt_radar=%d.\n",
pstate_dfs->dfs_check_channel, pstate_dfs->dfs_radar_found);
if ((!pstate_dfs->dfs_check_pending) &&
(chan == pstate_dfs->dfs_check_channel) &&
((sec - pstate_dfs->dfs_report_time_sec) <
MAX_DFS_REPORT_USABLE_AGE_SEC)) {
/* valid and not out-dated, check if radar */
if (pstate_dfs->dfs_radar_found) {
PRINTM(MMSG, "Radar was detected on channel %d.\n",
chan);
ret = MLAN_STATUS_FAILURE;
}
} else {
/* When Cache is not valid. This is required during extending
* cache validity during bss_stop
*/
pstate_dfs->dfs_check_channel = 0;
/*TODO: reissue report request if not pending.
* BUT HOW to make the code wait for it???
* For now, just fail since we don't have the info. */
ret = MLAN_STATUS_PENDING;
}
LEAVE();
return ret;
}
/**
* @brief Process an TLV buffer for a pending BSS Adhoc start command.
*
* Activate 11h functionality in the firmware if driver has is enabled
* for 11h (configured by the application via IOCTL).
*
* @param priv Private driver information structure
* @param ppbuffer Output parameter: Pointer to the TLV output buffer,
* modified on return to point after the appended 11h TLVs
* @param pcap_info Pointer to the capability info for the BSS to join
* @param channel Channel on which we are starting the IBSS
* @param p11h_bss_info Input/Output parameter: Pointer to the 11h BSS
* information for this network that we are establishing.
* 11h sensed flag set on output if warranted.
*
* @return
* - MLAN_STATUS_SUCCESS if 11h is disabled
* - Integer number of bytes appended to the TLV output buffer (ppbuffer)
* - < 0 for error (e.g. radar detected on channel)
*/
t_s32 wlan_11h_process_start(mlan_private *priv, t_u8 **ppbuffer,
IEEEtypes_CapInfo_t *pcap_info, t_u32 channel,
wlan_11h_bss_info_t *p11h_bss_info)
{
mlan_adapter *adapter = priv->adapter;
t_s32 ret = MLAN_STATUS_SUCCESS;
t_bool is_dfs_chan = MFALSE;
ENTER();
if (wlan_11h_is_enabled(priv) &&
((adapter->adhoc_start_band & BAND_A))) {
if (!wlan_fw_11d_is_enabled(priv)) {
/* No use having 11h enabled without 11d enabled */
wlan_11d_enable(priv, MNULL, ENABLE_11D);
#ifdef STA_SUPPORT
wlan_11d_create_dnld_countryinfo(
priv, adapter->adhoc_start_band);
#endif
}
/*
* Activate 11h functions in firmware,
* turns on capability bit
*/
wlan_11h_activate(priv, MNULL, MTRUE);
pcap_info->spectrum_mgmt = MTRUE;
/* If using a DFS channel, enable radar detection. */
is_dfs_chan = wlan_11h_radar_detect_required(priv, channel);
if (is_dfs_chan) {
if (!wlan_11h_is_master_radar_det_active(priv))
wlan_11h_config_master_radar_det(priv, MTRUE);
}
wlan_11h_check_update_radar_det_state(priv);
/* Set flag indicating this BSS we are starting is using 11h */
p11h_bss_info->sensed_11h = MTRUE;
if (is_dfs_chan) {
/* check if this channel is under NOP */
if (wlan_11h_is_channel_under_nop(adapter, channel))
ret = MLAN_STATUS_FAILURE;
/* check last channel report, if this channel is free of
* radar */
if (ret == MLAN_STATUS_SUCCESS)
ret = wlan_11h_check_chan_report(priv, channel);
}
if (ret == MLAN_STATUS_SUCCESS)
ret = wlan_11h_process_adhoc(priv, ppbuffer, channel,
MNULL);
else
ret = MLAN_STATUS_FAILURE;
} else {
/* Deactivate 11h functions in the firmware */
wlan_11h_activate(priv, MNULL, MFALSE);
pcap_info->spectrum_mgmt = MFALSE;
wlan_11h_check_update_radar_det_state(priv);
}
LEAVE();
return ret;
}
/**
* @brief Process an TLV buffer for a pending BSS Join command for
* both adhoc and infra networks
*
* The TLV command processing for a BSS join for either adhoc or
* infrastructure network is performed with this function. The
* capability bits are inspected for the IBSS flag and the appropriate
* local routines are called to setup the necessary TLVs.
*
* Activate 11h functionality in the firmware if the spectrum management
* capability bit is found in the network information for the BSS we are
* joining.
*
* @param priv Private driver information structure
* @param ppbuffer Output parameter: Pointer to the TLV output buffer,
* modified on return to point after the appended 11h TLVs
* @param pcap_info Pointer to the capability info for the BSS to join
* @param band Band on which we are joining the BSS
* @param channel Channel on which we are joining the BSS
* @param p11h_bss_info Pointer to the 11h BSS information for this
* network that was parsed out of the scan response.
*
* @return Integer number of bytes appended to the TLV output
* buffer (ppbuffer), MLAN_STATUS_FAILURE (-1),
* or MLAN_STATUS_SUCCESS (0)
*/
t_s32 wlan_11h_process_join(mlan_private *priv, t_u8 **ppbuffer,
IEEEtypes_CapInfo_t *pcap_info, t_u8 band,
t_u32 channel, wlan_11h_bss_info_t *p11h_bss_info)
{
t_s32 ret = 0;
ENTER();
if (priv->media_connected == MTRUE) {
if (wlan_11h_is_active(priv) == p11h_bss_info->sensed_11h) {
/*
* Assume DFS parameters are the same for roaming as
* long as the current & next APs have the same spectrum
* mgmt capability bit setting
*/
ret = MLAN_STATUS_SUCCESS;
} else {
/* No support for roaming between DFS/non-DFS yet */
ret = MLAN_STATUS_FAILURE;
}
LEAVE();
return ret;
}
if (p11h_bss_info->sensed_11h) {
if (!wlan_fw_11d_is_enabled(priv)) {
/* No use having 11h enabled without 11d enabled */
wlan_11d_enable(priv, MNULL, ENABLE_11D);
#ifdef STA_SUPPORT
wlan_11d_parse_dnld_countryinfo(
priv, priv->pattempted_bss_desc);
#endif
}
/*
* Activate 11h functions in firmware,
* turns on capability bit
*/
wlan_11h_activate(priv, MNULL, MTRUE);
pcap_info->spectrum_mgmt = MTRUE;
/* If using a DFS channel, enable radar detection. */
if ((band & BAND_A) &&
wlan_11h_radar_detect_required(priv, channel)) {
if (!wlan_11h_is_slave_radar_det_active(priv))
wlan_11h_config_slave_radar_det(priv, MTRUE);
}
wlan_11h_check_update_radar_det_state(priv);
if (pcap_info->ibss) {
PRINTM(MINFO, "11h: Adhoc join: Sensed\n");
ret = wlan_11h_process_adhoc(priv, ppbuffer, channel,
p11h_bss_info);
} else {
PRINTM(MINFO, "11h: Infra join: Sensed\n");
ret = wlan_11h_process_infra_join(
priv, ppbuffer, band, channel, p11h_bss_info);
}
} else {
/* Deactivate 11h functions in the firmware */
wlan_11h_activate(priv, MNULL, MFALSE);
pcap_info->spectrum_mgmt = MFALSE;
wlan_11h_check_update_radar_det_state(priv);
}
LEAVE();
return ret;
}
/**
*
* @brief Prepare the HostCmd_DS_Command structure for an 11h command.
*
* Use the Command field to determine if the command being set up is for
* 11h and call one of the local command handlers accordingly for:
*
* - HostCmd_CMD_802_11_TPC_ADAPT_REQ
* - HostCmd_CMD_802_11_TPC_INFO
* - HostCmd_CMD_802_11_CHAN_SW_ANN
*/
/** - HostCmd_CMD_CHAN_REPORT_REQUEST
*/
/**
* @param priv Private driver information structure
* @param pcmd_ptr Output parameter: Pointer to the command being prepared
* for the firmware
* @param pinfo_buf Void buffer pass through with data necessary for a
* specific command type
*/
/** @return MLAN_STATUS_SUCCESS, MLAN_STATUS_FAILURE
* or MLAN_STATUS_PENDING
*/
/** @sa wlan_11h_cmd_tpc_request
* @sa wlan_11h_cmd_tpc_info
* @sa wlan_11h_cmd_chan_sw_ann
*/
/** @sa wlan_11h_cmd_chan_report_req
*/
mlan_status wlan_11h_cmd_process(mlan_private *priv,
HostCmd_DS_COMMAND *pcmd_ptr,
const t_void *pinfo_buf)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
switch (pcmd_ptr->command) {
case HostCmd_CMD_802_11_TPC_ADAPT_REQ:
ret = wlan_11h_cmd_tpc_request(priv, pcmd_ptr, pinfo_buf);
break;
case HostCmd_CMD_802_11_TPC_INFO:
ret = wlan_11h_cmd_tpc_info(priv, pcmd_ptr, pinfo_buf);
break;
case HostCmd_CMD_802_11_CHAN_SW_ANN:
ret = wlan_11h_cmd_chan_sw_ann(priv, pcmd_ptr, pinfo_buf);
break;
case HostCmd_CMD_CHAN_REPORT_REQUEST:
ret = wlan_11h_cmd_chan_rpt_req(priv, pcmd_ptr, pinfo_buf);
break;
default:
ret = MLAN_STATUS_FAILURE;
}
pcmd_ptr->command = wlan_cpu_to_le16(pcmd_ptr->command);
pcmd_ptr->size = wlan_cpu_to_le16(pcmd_ptr->size);
LEAVE();
return ret;
}
/**
* @brief Handle the command response from the firmware if from an 11h command
*
* Use the Command field to determine if the command response being
* is for 11h. Call the local command response handler accordingly for:
*
* - HostCmd_CMD_802_11_TPC_ADAPT_REQ
* - HostCmd_CMD_802_11_TPC_INFO
* - HostCmd_CMD_802_11_CHAN_SW_ANN
*/
/** - HostCmd_CMD_CHAN_REPORT_REQUEST
*/
/**
* @param priv Private driver information structure
* @param resp HostCmd_DS_COMMAND struct returned from the firmware
* command
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_cmdresp_process(mlan_private *priv,
const HostCmd_DS_COMMAND *resp)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
ENTER();
switch (resp->command) {
case HostCmd_CMD_802_11_TPC_ADAPT_REQ:
HEXDUMP("11h: TPC REQUEST Rsp:", (t_u8 *)resp,
(t_u32)resp->size);
memcpy_ext(priv->adapter, priv->adapter->curr_cmd->pdata_buf,
&resp->params.tpc_req,
sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ),
sizeof(HostCmd_DS_802_11_TPC_ADAPT_REQ));
break;
case HostCmd_CMD_802_11_TPC_INFO:
HEXDUMP("11h: TPC INFO Rsp Data:", (t_u8 *)resp,
(t_u32)resp->size);
break;
case HostCmd_CMD_802_11_CHAN_SW_ANN:
PRINTM(MINFO, "11h: Ret ChSwAnn: Sz=%u, Seq=%u, Ret=%u\n",
resp->size, resp->seq_num, resp->result);
break;
case HostCmd_CMD_CHAN_REPORT_REQUEST:
priv->adapter->state_dfs.dfs_check_priv = priv;
priv->adapter->state_dfs.dfs_check_pending = MTRUE;
if (resp->params.chan_rpt_req.millisec_dwell_time == 0) {
/* from wlan_11h_ioctl_dfs_chan_report */
priv->adapter->state_dfs.dfs_check_pending = MFALSE;
priv->adapter->state_dfs.dfs_check_priv = MNULL;
priv->adapter->state_dfs.dfs_check_channel = 0;
PRINTM(MINFO, "11h: Cancelling Chan Report \n");
} else {
PRINTM(MERROR,
"11h: Ret ChanRptReq. Set dfs_check_pending and wait"
" for EVENT_CHANNEL_REPORT.\n");
}
break;
default:
ret = MLAN_STATUS_FAILURE;
}
LEAVE();
return ret;
}
/**
* @brief Process an element from a scan response, copy relevant info for 11h
*
* @param pmadapter Pointer to mlan_adapter
* @param p11h_bss_info Output parameter: Pointer to the 11h BSS information
* for the network that is being processed
* @param pelement Pointer to the current IE we are inspecting for 11h
* relevance
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_process_bss_elem(mlan_adapter *pmadapter,
wlan_11h_bss_info_t *p11h_bss_info,
const t_u8 *pelement)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
t_u8 element_len = *((t_u8 *)pelement + 1);
ENTER();
switch (*pelement) {
case POWER_CONSTRAINT:
PRINTM(MINFO, "11h: Power Constraint IE Found\n");
p11h_bss_info->sensed_11h = MTRUE;
memcpy_ext(pmadapter, &p11h_bss_info->power_constraint,
pelement, element_len + sizeof(IEEEtypes_Header_t),
sizeof(IEEEtypes_PowerConstraint_t));
p11h_bss_info->power_constraint.len =
MIN(element_len, (sizeof(IEEEtypes_PowerConstraint_t) -
sizeof(IEEEtypes_Header_t)));
break;
case POWER_CAPABILITY:
PRINTM(MINFO, "11h: Power Capability IE Found\n");
p11h_bss_info->sensed_11h = MTRUE;
memcpy_ext(pmadapter, &p11h_bss_info->power_capability,
pelement, element_len + sizeof(IEEEtypes_Header_t),
sizeof(IEEEtypes_PowerCapability_t));
p11h_bss_info->power_capability.len =
MIN(element_len, (sizeof(IEEEtypes_PowerCapability_t) -
sizeof(IEEEtypes_Header_t)));
break;
case TPC_REPORT:
PRINTM(MINFO, "11h: Tpc Report IE Found\n");
p11h_bss_info->sensed_11h = MTRUE;
memcpy_ext(pmadapter, &p11h_bss_info->tpc_report, pelement,
element_len + sizeof(IEEEtypes_Header_t),
sizeof(IEEEtypes_TPCReport_t));
p11h_bss_info->tpc_report.len =
MIN(element_len, (sizeof(IEEEtypes_TPCReport_t) -
sizeof(IEEEtypes_Header_t)));
break;
case CHANNEL_SWITCH_ANN:
PRINTM(MINFO, "11h: Channel Switch Ann IE Found\n");
p11h_bss_info->sensed_11h = MTRUE;
memcpy_ext(pmadapter, &p11h_bss_info->chan_switch_ann, pelement,
element_len + sizeof(IEEEtypes_Header_t),
sizeof(IEEEtypes_ChanSwitchAnn_t));
p11h_bss_info->chan_switch_ann.len =
MIN(element_len, (sizeof(IEEEtypes_ChanSwitchAnn_t) -
sizeof(IEEEtypes_Header_t)));
break;
case QUIET:
PRINTM(MINFO, "11h: Quiet IE Found\n");
p11h_bss_info->sensed_11h = MTRUE;
memcpy_ext(pmadapter, &p11h_bss_info->quiet, pelement,
element_len + sizeof(IEEEtypes_Header_t),
sizeof(IEEEtypes_Quiet_t));
p11h_bss_info->quiet.len =
MIN(element_len, (sizeof(IEEEtypes_Quiet_t) -
sizeof(IEEEtypes_Header_t)));
break;
case IBSS_DFS:
PRINTM(MINFO, "11h: Ibss Dfs IE Found\n");
p11h_bss_info->sensed_11h = MTRUE;
memcpy_ext(pmadapter, &p11h_bss_info->ibss_dfs, pelement,
element_len + sizeof(IEEEtypes_Header_t),
sizeof(IEEEtypes_IBSS_DFS_t));
p11h_bss_info->ibss_dfs.len =
MIN(element_len, (sizeof(IEEEtypes_IBSS_DFS_t) -
sizeof(IEEEtypes_Header_t)));
break;
case SUPPORTED_CHANNELS:
case TPC_REQUEST:
/*
* These elements are not in beacons/probe responses.
* Included here to cover set of enumerated 11h elements.
*/
break;
default:
ret = MLAN_STATUS_FAILURE;
}
LEAVE();
return ret;
}
/**
* @brief Driver handling for CHANNEL_SWITCH_ANN event
*
* @param priv Pointer to mlan_private
*
* @return MLAN_STATUS_SUCCESS, MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING
*/
mlan_status wlan_11h_handle_event_chanswann(mlan_private *priv)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
#ifdef STA_SUPPORT
mlan_deauth_param deauth_param;
#endif
t_u32 sec, usec;
#ifdef UAP_SUPPORT
mlan_adapter *pmadapter = priv->adapter;
int i;
t_u8 radar_detected = MFALSE;
mlan_private *pmpriv = MNULL;
#endif
ENTER();
#ifdef UAP_SUPPORT
if (priv->adapter->state_11h.is_master_radar_det_active) {
for (i = 0; i < MIN(pmadapter->priv_num, MLAN_MAX_BSS_NUM);
i++) {
if (pmadapter->priv[i] &&
(pmadapter->priv[i]->bss_role ==
MLAN_BSS_ROLE_UAP) &&
pmadapter->priv[i]->uap_bss_started &&
(priv->curr_bss_params.bss_descriptor.channel ==
pmadapter->priv[i]->uap_channel)) {
PRINTM(MCMND,
"Receive channel switch Ann event on uap_channel=%d\n",
pmadapter->priv[i]->uap_channel);
radar_detected = MTRUE;
pmpriv = pmadapter->priv[i];
break;
}
}
if (radar_detected) {
if (!pmpriv->intf_state_11h.is_11h_host) {
if (pmadapter->state_rdh.stage == RDH_OFF) {
pmadapter->state_rdh.stage =
RDH_CHK_INTFS;
wlan_11h_radar_detected_handling(
pmadapter, pmpriv);
if (pmpriv->uap_host_based)
wlan_recv_event(
pmpriv,
MLAN_EVENT_ID_FW_RADAR_DETECTED,
MNULL);
} else {
PRINTM(MEVENT,
"Ignore Event Radar Detected - handling already in progress.\n");
}
} else {
if (pmpriv->adapter->dfs_test_params
.no_channel_change_on_radar ||
pmpriv->adapter->dfs_test_params
.fixed_new_channel_on_radar) {
if (pmadapter->state_rdh.stage ==
RDH_OFF ||
pmadapter->state_rdh.stage ==
RDH_SET_CUSTOM_IE) {
pmadapter->state_rdh.stage =
RDH_CHK_INTFS;
wlan_11h_radar_detected_handling(
pmadapter, pmpriv);
} else
PRINTM(MEVENT,
"Ignore Event Radar Detected - handling already in progress.\n");
} else {
pmpriv->intf_state_11h.tx_disabled =
MTRUE;
wlan_recv_event(
pmpriv,
MLAN_EVENT_ID_FW_RADAR_DETECTED,
MNULL);
}
}
}
}
if (pmadapter->ecsa_enable) {
t_u8 stop_tx = *(t_u8 *)pmadapter->event_body;
if (stop_tx)
pmadapter->state_rdh.tx_block = MTRUE;
LEAVE();
return ret;
}
#endif
priv->adapter->state_11h.recvd_chanswann_event = MTRUE;
/* unlikely: clean up previous csa if still on-going */
if (priv->intf_state_11h.dfs_slave_csa_chan) {
wlan_set_chan_blacklist(priv, BAND_A,
priv->intf_state_11h.dfs_slave_csa_chan,
MFALSE);
}
/* record channel and time of occurence */
priv->intf_state_11h.dfs_slave_csa_chan =
priv->curr_bss_params.bss_descriptor.channel;
priv->adapter->callbacks.moal_get_system_time(
priv->adapter->pmoal_handle, &sec, &usec);
priv->intf_state_11h.dfs_slave_csa_expire_at_sec =
sec + DFS_CHAN_MOVE_TIME;
#ifdef STA_SUPPORT
/* do directed deauth. recvd_chanswann_event flag will cause different
* reason code */
PRINTM(MINFO, "11h: handle_event_chanswann() - sending deauth\n");
memcpy_ext(priv->adapter, deauth_param.mac_addr,
&priv->curr_bss_params.bss_descriptor.mac_address,
MLAN_MAC_ADDR_LENGTH, MLAN_MAC_ADDR_LENGTH);
deauth_param.reason_code = DEF_DEAUTH_REASON_CODE;
ret = wlan_disconnect(priv, MNULL, &deauth_param);
/* clear region table so next scan will be all passive */
PRINTM(MINFO, "11h: handle_event_chanswann() - clear region table\n");
wlan_11d_clear_parsedtable(priv);
/* add channel to blacklist table */
PRINTM(MINFO,
"11h: handle_event_chanswann() - scan blacklist csa channel\n");
wlan_set_chan_blacklist(priv, BAND_A,
priv->intf_state_11h.dfs_slave_csa_chan, MTRUE);
#endif
priv->adapter->state_11h.recvd_chanswann_event = MFALSE;
LEAVE();
return ret;
}
/**
* @brief 802.11h DFS Testing configuration
*
* @param pmadapter Pointer to mlan_adapter
* @param pioctl_req Pointer to mlan_ioctl_req
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_ioctl_dfs_testing(pmlan_adapter pmadapter,
pmlan_ioctl_req pioctl_req)
{
mlan_ds_11h_cfg *ds_11hcfg = MNULL;
mlan_ds_11h_dfs_testing *dfs_test = MNULL;
wlan_dfs_testing_settings_t *pdfs_test_params = MNULL;
ENTER();
ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf;
dfs_test = &ds_11hcfg->param.dfs_testing;
pdfs_test_params = &pmadapter->dfs_test_params;
if (pioctl_req->action == MLAN_ACT_GET) {
dfs_test->usr_cac_period_msec =
pdfs_test_params->user_cac_period_msec;
dfs_test->usr_nop_period_sec =
pdfs_test_params->user_nop_period_sec;
dfs_test->usr_no_chan_change =
pdfs_test_params->no_channel_change_on_radar;
dfs_test->usr_fixed_new_chan =
pdfs_test_params->fixed_new_channel_on_radar;
dfs_test->usr_cac_restart = pdfs_test_params->cac_restart;
} else {
pdfs_test_params->user_cac_period_msec =
dfs_test->usr_cac_period_msec;
pdfs_test_params->user_nop_period_sec =
dfs_test->usr_nop_period_sec;
pdfs_test_params->no_channel_change_on_radar =
dfs_test->usr_no_chan_change;
pdfs_test_params->fixed_new_channel_on_radar =
dfs_test->usr_fixed_new_chan;
pdfs_test_params->cac_restart = dfs_test->usr_cac_restart;
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief 802.11h IOCTL to handle channel NOP status check
* @brief If given channel is under NOP, return a new non-dfs
* @brief channel
*
* @param pmadapter Pointer to mlan_adapter
* @param pioctl_req Pointer to mlan_ioctl_req
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_ioctl_get_channel_nop_info(pmlan_adapter pmadapter,
pmlan_ioctl_req pioctl_req)
{
pmlan_private pmpriv = MNULL;
mlan_ds_11h_cfg *ds_11hcfg = MNULL;
t_s32 ret = MLAN_STATUS_FAILURE;
mlan_ds_11h_chan_nop_info *ch_nop_info = MNULL;
ENTER();
if (pioctl_req) {
pmpriv = pmadapter->priv[pioctl_req->bss_index];
ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf;
ch_nop_info = &ds_11hcfg->param.ch_nop_info;
if (pioctl_req->action == MLAN_ACT_GET) {
ch_nop_info->chan_under_nop =
wlan_11h_is_channel_under_nop(
pmadapter, ch_nop_info->curr_chan);
if (ch_nop_info->chan_under_nop) {
wlan_11h_switch_non_dfs_chan(
pmpriv, &ch_nop_info->new_chan.channel);
if (ch_nop_info->chan_width == CHAN_BW_80MHZ ||
ch_nop_info->chan_width == CHAN_BW_40MHZ)
wlan_11h_update_bandcfg(
&ch_nop_info->new_chan.bandcfg,
ch_nop_info->new_chan.channel);
if (ch_nop_info->chan_width == CHAN_BW_80MHZ)
ch_nop_info->new_chan.center_chan =
wlan_get_center_freq_idx(
pmpriv, BAND_AAC,
ch_nop_info->new_chan
.channel,
ch_nop_info->chan_width);
}
}
ret = MLAN_STATUS_SUCCESS;
}
LEAVE();
return ret;
}
/**
* @brief 802.11h DFS Channel Switch Count Configuration
*
* @param pmadapter Pointer to mlan_adapter
* @param pioctl_req Pointer to mlan_ioctl_req
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_ioctl_chan_switch_count(pmlan_adapter pmadapter,
pmlan_ioctl_req pioctl_req)
{
mlan_ds_11h_cfg *ds_11hcfg = MNULL;
t_s32 ret = MLAN_STATUS_FAILURE;
ENTER();
if (pioctl_req) {
ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf;
if (pioctl_req->action == MLAN_ACT_GET) {
ds_11hcfg->param.cs_count = pmadapter->dfs_cs_count;
} else {
pmadapter->dfs_cs_count = ds_11hcfg->param.cs_count;
}
ret = MLAN_STATUS_SUCCESS;
}
LEAVE();
return ret;
}
/**
* @brief 802.11h DFS chan report
*
* @param priv Pointer to mlan_private
* @param pioctl_req Pointer to mlan_ioctl_req
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_ioctl_dfs_chan_report(mlan_private *priv,
pmlan_ioctl_req pioctl_req)
{
mlan_ds_11h_cfg *ds_11hcfg = MNULL;
HostCmd_DS_CHAN_RPT_REQ *chan_rpt_req = MNULL;
t_s32 ret;
ENTER();
ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf;
chan_rpt_req =
(HostCmd_DS_CHAN_RPT_REQ *)&ds_11hcfg->param.chan_rpt_req;
ret = wlan_prepare_cmd(priv, HostCmd_CMD_CHAN_REPORT_REQUEST,
HostCmd_ACT_GEN_SET, 0, (t_void *)pioctl_req,
(t_void *)chan_rpt_req);
if (ret == MLAN_STATUS_SUCCESS)
ret = MLAN_STATUS_PENDING;
LEAVE();
return ret;
}
/**
* @brief Check if channel is under NOP (Non-Occupancy Period)
* If so, the channel should not be used until the period expires.
*
* @param pmadapter Pointer to mlan_adapter
* @param channel Channel number
*
* @return MTRUE or MFALSE
*/
t_bool wlan_11h_is_channel_under_nop(mlan_adapter *pmadapter, t_u8 channel)
{
wlan_dfs_timestamp_t *pdfs_ts = MNULL;
t_u32 now_sec, now_usec;
t_bool ret = MFALSE;
ENTER();
pdfs_ts = wlan_11h_find_dfs_timestamp(pmadapter, channel);
if (pdfs_ts && (pdfs_ts->channel == channel) &&
(pdfs_ts->represents == DFS_TS_REPR_NOP_START)) {
/* found NOP_start timestamp entry on channel */
pmadapter->callbacks.moal_get_system_time(
pmadapter->pmoal_handle, &now_sec, &now_usec);
if (pmadapter->dfs_test_params.user_nop_period_sec) {
PRINTM(MCMD_D,
"dfs_testing - user NOP period=%d (sec)\n",
pmadapter->dfs_test_params.user_nop_period_sec);
if ((now_sec - pdfs_ts->ts_sec) <=
pmadapter->dfs_test_params.user_nop_period_sec) {
ret = MTRUE;
}
} else {
if ((now_sec - pdfs_ts->ts_sec) <=
WLAN_11H_NON_OCCUPANCY_PERIOD)
ret = MTRUE;
}
/* if entry is expired, remove it */
if (!ret) {
wlan_11h_remove_dfs_timestamp(pmadapter, pdfs_ts);
} else
PRINTM(MMSG,
"11h: channel %d is under NOP - can't use.\n",
channel);
}
LEAVE();
return ret;
}
/**
* @brief Driver handling for CHANNEL_REPORT_RDY event
* This event will have the channel report data appended.
*
* @param priv Pointer to mlan_private
* @param pevent Pointer to mlan_event
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_handle_event_chanrpt_ready(mlan_private *priv,
mlan_event *pevent,
t_u8 *radar_chan)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
HostCmd_DS_CHAN_RPT_RSP *pchan_rpt_rsp;
MrvlIEtypes_Data_t *ptlv;
MeasRptBasicMap_t *pmeas_rpt_basic;
t_u8 *pbuffer;
t_s32 evt_len;
t_u16 tlv_len;
t_u32 sec, usec;
wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs;
t_u8 dfs_radar_found = MFALSE;
t_u8 dfs_check_channel = pstate_dfs->dfs_check_channel;
ENTER();
pchan_rpt_rsp = (HostCmd_DS_CHAN_RPT_RSP *)&pevent->event_buf;
DBG_HEXDUMP(MCMD_D, "11h: Event ChanRptReady (HostCmd_DS_CHAN_RPT_RSP)",
(t_u8 *)pchan_rpt_rsp, pevent->event_len);
if (wlan_le32_to_cpu(pchan_rpt_rsp->cmd_result) ==
MLAN_CMD_RESULT_SUCCESS) {
pbuffer = (t_u8 *)&pchan_rpt_rsp->tlv_buffer;
evt_len = pevent->event_len;
evt_len -= sizeof(HostCmd_DS_CHAN_RPT_RSP) -
sizeof(pchan_rpt_rsp->tlv_buffer);
while (evt_len >= (t_s32)sizeof(MrvlIEtypesHeader_t)) {
ptlv = (MrvlIEtypes_Data_t *)pbuffer;
tlv_len = wlan_le16_to_cpu(ptlv->header.len);
switch (wlan_le16_to_cpu(ptlv->header.type)) {
case TLV_TYPE_CHANRPT_11H_BASIC:
pmeas_rpt_basic =
(MeasRptBasicMap_t *)&ptlv->data;
if (pmeas_rpt_basic->radar)
dfs_radar_found = MTRUE;
break;
default:
break;
}
pbuffer += (tlv_len + sizeof(ptlv->header));
evt_len -= (tlv_len + sizeof(ptlv->header));
evt_len = (evt_len > 0) ? evt_len : 0;
}
} else {
ret = MLAN_STATUS_FAILURE;
}
if (dfs_radar_found) {
PRINTM(MMSG, "RADAR Detected on channel %d!\n",
dfs_check_channel);
/* add channel to NOP list */
wlan_11h_add_dfs_timestamp(priv->adapter, DFS_TS_REPR_NOP_START,
dfs_check_channel);
}
*radar_chan = dfs_check_channel;
pstate_dfs->dfs_radar_found = dfs_radar_found;
/* Update DFS structure. */
priv->adapter->callbacks.moal_get_system_time(
priv->adapter->pmoal_handle, &sec, &usec);
pstate_dfs->dfs_report_time_sec = sec;
pstate_dfs->dfs_check_pending = MFALSE;
pstate_dfs->dfs_check_priv = MNULL;
LEAVE();
return ret;
}
/**
* @brief Print debug info in RADAR_DETECTED event
* This event may have the dfs record data appended.
*
* @param priv Pointer to mlan_private
* @param pevent Pointer to mlan_event
* @param radar_chan Pointer to radar channel
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_print_event_radar_detected(mlan_private *priv,
mlan_event *pevent,
t_u8 *radar_chan)
{
wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs;
ENTER();
*radar_chan = pstate_dfs->dfs_check_channel;
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @brief Check if RADAR_DETECTED handling is blocking data tx
*
* @param pmadapter Pointer to mlan_adapter
*
* @return MTRUE or MFALSE
*/
t_bool wlan_11h_radar_detected_tx_blocked(mlan_adapter *pmadapter)
{
if (pmadapter->state_rdh.tx_block)
return MTRUE;
switch (pmadapter->state_rdh.stage) {
case RDH_OFF:
case RDH_CHK_INTFS:
case RDH_STOP_TRAFFIC:
return MFALSE;
}
return MTRUE;
}
/**
* @brief Callback for RADAR_DETECTED event driver handling
*
* @param priv Void pointer to mlan_private
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_radar_detected_callback(t_void *priv)
{
mlan_status ret;
ENTER();
ret = wlan_11h_radar_detected_handling(
((mlan_private *)(priv))->adapter, (mlan_private *)priv);
LEAVE();
return ret;
}
#ifdef UAP_SUPPORT
/**
* @brief Function for handling sta disconnect event in dfs_repeater mode
*
* @param pmadapter pointer to mlan_adapter
*
* @return NONE
*/
void wlan_dfs_rep_disconnect(mlan_adapter *pmadapter)
{
mlan_private *priv_list[MLAN_MAX_BSS_NUM];
mlan_private *pmpriv = MNULL;
t_u8 pcount, i;
memset(pmadapter, priv_list, 0x00, sizeof(priv_list));
pcount = wlan_get_privs_by_cond(pmadapter, wlan_is_intf_active,
priv_list);
/* Stop all the active BSSes */
for (i = 0; i < pcount; i++) {
pmpriv = priv_list[i];
if (GET_BSS_ROLE(pmpriv) != MLAN_BSS_ROLE_UAP)
continue;
if (wlan_11h_radar_detect_required(pmpriv,
pmadapter->dfsr_channel)) {
wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_STOP,
HostCmd_ACT_GEN_SET, 0, MNULL, MNULL);
}
}
}
/**
* @brief Function for handling sta BW change event in dfs_repeater mode
*
* @param pmadapter pointer to mlan_adapter
*
* @return NONE
*/
void wlan_dfs_rep_bw_change(mlan_adapter *pmadapter)
{
mlan_private *priv_list[MLAN_MAX_BSS_NUM];
mlan_private *pmpriv = MNULL;
t_u8 pcount, i;
memset(pmadapter, priv_list, 0x00, sizeof(priv_list));
pcount = wlan_get_privs_by_cond(pmadapter, wlan_is_intf_active,
priv_list);
if (pcount == 1) {
pmpriv = priv_list[0];
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) {
PRINTM(MMSG,
"dfs-repeater: BW change detected\n"
"no active priv's, skip event handling.\n");
return;
}
}
/* Stop all the active BSSes */
for (i = 0; i < pcount; i++) {
pmpriv = priv_list[i];
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
/* Check if uAPs running on non-dfs channel. If they do
* then there is no need to restart the uAPs
*/
if (!wlan_11h_radar_detect_required(
pmpriv, pmadapter->dfsr_channel))
return;
wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_STOP,
HostCmd_ACT_GEN_SET, 0, MNULL, MNULL);
}
}
/* Start all old active BSSes */
for (i = 0; i < pcount; i++) {
pmpriv = priv_list[i];
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
wlan_prepare_cmd(pmpriv, HOST_CMD_APCMD_BSS_START,
HostCmd_ACT_GEN_SET, 0, MNULL, MNULL);
}
}
}
#endif
/**
* @brief Update band config for the new channel
*
* @param uap_band_cfg uap's old channel's band configuration
* @param new_channel new channel that the device is switching to
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING
*/
void wlan_11h_update_bandcfg(Band_Config_t *uap_band_cfg, t_u8 new_channel)
{
t_u8 chan_offset;
ENTER();
/* Update the channel offset for 20MHz, 40MHz and 80MHz
* Clear the channel bandwidth for 20MHz
* since channel switch could be happening from 40/80MHz to 20MHz
*/
chan_offset = wlan_get_second_channel_offset(new_channel);
uap_band_cfg->chan2Offset = chan_offset;
if (!chan_offset) { /* 40MHz/80MHz */
PRINTM(MCMD_D, "20MHz channel, clear channel bandwidth\n");
uap_band_cfg->chanWidth = CHAN_BW_20MHZ;
}
LEAVE();
}
/**
* @brief Get priv current index -- this is used to enter correct rdh_state
* during radar handling
*
* @param pmpriv Pointer to mlan_private
* @param pstate_rdh Pointer to radar detected state handler
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status
wlan_11h_get_priv_curr_idx(mlan_private *pmpriv,
wlan_radar_det_hndlg_state_t *pstate_rdh)
{
t_bool found = MFALSE;
ENTER();
PRINTM(MINFO, "%s:pmpriv =%p\n", __func__, pmpriv);
while ((++pstate_rdh->priv_curr_idx) < pstate_rdh->priv_list_count) {
if (pmpriv ==
pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]) {
PRINTM(MINFO, "found matching priv: priv_idx=%d\n",
pstate_rdh->priv_curr_idx);
found = MTRUE;
break;
}
}
return (found == MTRUE) ? MLAN_STATUS_SUCCESS : MLAN_STATUS_FAILURE;
}
/**
* @brief Driver handling for remove customeie
*
* @param pmadapter Pointer to mlan_adapter
* @param pmpriv Pointer to mlan_private
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING
*/
mlan_status wlan_11h_remove_custom_ie(mlan_adapter *pmadapter,
mlan_private *pmpriv)
{
mlan_status ret = MLAN_STATUS_SUCCESS;
wlan_radar_det_hndlg_state_t *pstate_rdh = &pmadapter->state_rdh;
mlan_ioctl_req *pioctl_req = MNULL;
ENTER();
if (pstate_rdh->stage == RDH_SET_CUSTOM_IE) {
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
PRINTM(MMSG, "Removing CHAN_SW IE from interfaces.\n");
while ((++pstate_rdh->priv_curr_idx) <
pstate_rdh->priv_list_count) {
pmpriv =
pstate_rdh->priv_list[pstate_rdh->priv_curr_idx];
if (!wlan_11h_is_dfs_master(pmpriv))
continue;
ret = wlan_11h_prepare_custom_ie_chansw(
pmadapter, &pioctl_req, MFALSE);
if ((ret != MLAN_STATUS_SUCCESS) || !pioctl_req) {
PRINTM(MERROR,
"%s(): Error in preparing CHAN_SW IE.\n",
__func__);
goto done;
}
pioctl_req->bss_index = pmpriv->bss_index;
ret = wlan_misc_ioctl_custom_ie_list(
pmadapter, pioctl_req, MFALSE);
if (ret != MLAN_STATUS_SUCCESS &&
ret != MLAN_STATUS_PENDING) {
PRINTM(MERROR,
"%s(): Could not remove IE for priv=%p [priv_bss_idx=%d]!\n",
__func__, pmpriv, pmpriv->bss_index);
/* TODO: hiow to handle this error case??
* ignore & continue? */
}
/* free ioctl buffer memory before we leave */
pmadapter->callbacks.moal_mfree(pmadapter->pmoal_handle,
(t_u8 *)pioctl_req);
}
}
done:
LEAVE();
return ret;
}
/**
* @brief Driver handling for RADAR_DETECTED event
*
* @param pmadapter Pointer to mlan_adapter
* @param pmpriv Pointer to mlan_private
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING
*/
mlan_status wlan_11h_radar_detected_handling(mlan_adapter *pmadapter,
mlan_private *pmpriv)
{
#ifdef DEBUG_LEVEL1
const char *rdh_stage_str[] = {"RDH_OFF",
"RDH_CHK_INTFS",
"RDH_STOP_TRAFFIC",
"RDH_GET_INFO_CHANNEL",
"RDH_GET_INFO_BEACON_DTIM",
"RDH_SET_CUSTOM_IE",
"RDH_REM_CUSTOM_IE",
"RDH_STOP_INTFS",
"RDH_SET_NEW_CHANNEL",
"RDH_RESTART_INTFS",
"RDH_RESTART_TRAFFIC"};
#endif
mlan_status ret = MLAN_STATUS_SUCCESS;
t_u32 i;
wlan_radar_det_hndlg_state_t *pstate_rdh = &pmadapter->state_rdh;
ENTER();
if (!pmpriv) {
PRINTM(MERROR, "Invalid radar priv -- Exit radar handling\n");
LEAVE();
return MLAN_STATUS_FAILURE;
}
switch (pstate_rdh->stage) {
case RDH_CHK_INTFS:
PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]);
/* get active interfaces */
memset(pmadapter, pstate_rdh->priv_list, 0x00,
sizeof(pstate_rdh->priv_list));
pstate_rdh->priv_list_count = wlan_get_privs_by_cond(
pmadapter, wlan_is_intf_active, pstate_rdh->priv_list);
PRINTM(MCMD_D, "%s(): priv_list_count = %d\n", __func__,
pstate_rdh->priv_list_count);
for (i = 0; i < pstate_rdh->priv_list_count; i++)
PRINTM(MINFO, "%s(): priv_list[%d] = %p\n", __func__,
i, pstate_rdh->priv_list[i]);
if (pstate_rdh->priv_list_count == 0) {
/* no interfaces active... nothing to do */
PRINTM(MMSG, "11h: Radar Detected - no active priv's,"
" skip event handling.\n");
pstate_rdh->stage = RDH_OFF;
PRINTM(MCMD_D, "%s(): finished - stage(%d)=%s\n",
__func__, pstate_rdh->stage,
rdh_stage_str[pstate_rdh->stage]);
break; /* EXIT CASE */
}
/* else: start handling */
pstate_rdh->curr_channel = 0;
pstate_rdh->new_channel = 0;
memset(pmadapter, &(pstate_rdh->uap_band_cfg), 0,
sizeof(pstate_rdh->uap_band_cfg));
pstate_rdh->max_bcn_dtim_ms = 0;
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_STOP_TRAFFIC;
/* fall through */
case RDH_STOP_TRAFFIC:
PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]);
PRINTM(MMSG,
"11h: Radar Detected - stopping host tx traffic.\n");
for (i = 0; i < pstate_rdh->priv_list_count; i++)
wlan_11h_tx_disable(pstate_rdh->priv_list[i]);
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_GET_INFO_CHANNEL;
/* fall through */
case RDH_GET_INFO_CHANNEL:
PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage],
pstate_rdh->priv_curr_idx);
/* here, prefer STA info over UAP info - one less CMD to send */
if (pstate_rdh->priv_curr_idx ==
RDH_STAGE_FIRST_ENTRY_PRIV_IDX) {
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
ret = wlan_11h_get_priv_curr_idx(pmpriv,
pstate_rdh);
if (ret != MLAN_STATUS_SUCCESS) {
PRINTM(MERROR,
"Unable to locate pmpriv in current active priv_list\n");
break; /* EXIT CASE */
}
/* send cmd to get first UAP's info */
pmpriv->uap_state_chan_cb.pioctl_req_curr =
MNULL;
pmpriv->uap_state_chan_cb.get_chan_callback =
wlan_11h_radar_detected_callback;
ret = wlan_uap_get_channel(pmpriv);
break; /* EXIT CASE */
} else
#endif
{
/* Assume all STAs on same channel, find first
* STA */
MASSERT(pstate_rdh->priv_list_count > 0);
for (i = 0; i < pstate_rdh->priv_list_count;
i++) {
pmpriv = pstate_rdh->priv_list[i];
if (GET_BSS_ROLE(pmpriv) ==
MLAN_BSS_ROLE_STA)
break;
}
/* STA info kept in driver, just copy */
pstate_rdh->curr_channel =
pmpriv->curr_bss_params.bss_descriptor
.channel;
}
}
#ifdef UAP_SUPPORT
else if (pstate_rdh->priv_curr_idx <
pstate_rdh->priv_list_count) {
/* repeat entry: UAP return with info */
pstate_rdh->curr_channel =
pmpriv->uap_state_chan_cb.channel;
pstate_rdh->uap_band_cfg =
pmpriv->uap_state_chan_cb.bandcfg;
PRINTM(MCMD_D,
"%s(): uap_band_cfg=0x%02x curr_chan=%d, curr_idx=%d bss_role=%d\n",
__func__, pstate_rdh->uap_band_cfg,
pstate_rdh->curr_channel,
pstate_rdh->priv_curr_idx, GET_BSS_ROLE(pmpriv));
}
#endif
/* add channel to NOP list */
wlan_11h_add_dfs_timestamp(pmadapter, DFS_TS_REPR_NOP_START,
pstate_rdh->curr_channel);
/* choose new channel (!= curr channel) and move on */
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP)
pstate_rdh->new_channel =
wlan_11h_get_uap_start_channel(
pmpriv,
pmpriv->uap_state_chan_cb.bandcfg);
else
#endif
pstate_rdh->new_channel =
wlan_11h_get_adhoc_start_channel(pmpriv);
if (!pstate_rdh->new_channel ||
(pstate_rdh->new_channel ==
pstate_rdh->curr_channel)) { /* report error */
PRINTM(MERROR,
"%s(): ERROR - Failed to choose new_chan"
" (!= curr_chan) !!\n",
__func__);
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
ret = wlan_prepare_cmd(pmpriv,
HOST_CMD_APCMD_BSS_STOP,
HostCmd_ACT_GEN_SET, 0,
MNULL, MNULL);
PRINTM(MERROR,
"STOP UAP and exit radar handling...\n");
pstate_rdh->stage = RDH_OFF;
break; /* leads to exit case */
}
#endif
}
if (!pmadapter->dfs_test_params.no_channel_change_on_radar &&
pmadapter->dfs_test_params.fixed_new_channel_on_radar) {
PRINTM(MCMD_D, "dfs_testing - user fixed new_chan=%d\n",
pmadapter->dfs_test_params
.fixed_new_channel_on_radar);
pstate_rdh->new_channel =
pmadapter->dfs_test_params
.fixed_new_channel_on_radar;
}
/* applies to DFS with ECSA support */
if (pmadapter->dfs_test_params.no_channel_change_on_radar) {
pstate_rdh->new_channel = pstate_rdh->curr_channel;
}
PRINTM(MCMD_D, "%s(): curr_chan=%d, new_chan=%d\n", __func__,
pstate_rdh->curr_channel, pstate_rdh->new_channel);
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_GET_INFO_BEACON_DTIM;
/* fall through */
case RDH_GET_INFO_BEACON_DTIM:
PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage],
pstate_rdh->priv_curr_idx);
#ifdef UAP_SUPPORT
/* check all intfs in this stage to find longest period */
/* UAP intf callback returning with info */
if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count) {
t_u16 bcn_dtim_msec;
pmpriv =
pstate_rdh->priv_list[pstate_rdh->priv_curr_idx];
PRINTM(MCMD_D, "%s(): uap.bcn_pd=%d, uap.dtim_pd=%d\n",
__func__,
pmpriv->uap_state_chan_cb.beacon_period,
pmpriv->uap_state_chan_cb.dtim_period);
bcn_dtim_msec =
(pmpriv->uap_state_chan_cb.beacon_period *
pmpriv->uap_state_chan_cb.dtim_period);
if (bcn_dtim_msec > pstate_rdh->max_bcn_dtim_ms)
pstate_rdh->max_bcn_dtim_ms = bcn_dtim_msec;
}
#endif
/* check next intf */
while ((++pstate_rdh->priv_curr_idx) <
pstate_rdh->priv_list_count) {
pmpriv =
pstate_rdh->priv_list[pstate_rdh->priv_curr_idx];
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
pmpriv->uap_state_chan_cb.pioctl_req_curr =
MNULL;
pmpriv->uap_state_chan_cb.get_chan_callback =
wlan_11h_radar_detected_callback;
ret = wlan_uap_get_beacon_dtim(pmpriv);
break; /* leads to exit case */
} else
#endif
{ /* get STA info from driver and compare here */
t_u16 bcn_pd_msec = 100;
t_u16 dtim_pd_msec = 1;
t_u16 bcn_dtim_msec;
/* adhoc creator */
if (wlan_11h_is_dfs_master(pmpriv)) {
bcn_pd_msec = pmpriv->beacon_period;
} else {
bcn_pd_msec = pmpriv->curr_bss_params
.bss_descriptor
.beacon_period;
/* if (priv->bss_mode !=
* MLAN_BSS_MODE_IBSS) */
/* TODO: mlan_scan.c needs to parse TLV
* 0x05 (TIM) for dtim_period */
}
PRINTM(MCMD_D,
"%s(): sta.bcn_pd=%d, sta.dtim_pd=%d\n",
__func__, bcn_pd_msec, dtim_pd_msec);
bcn_dtim_msec = (bcn_pd_msec * dtim_pd_msec);
if (bcn_dtim_msec > pstate_rdh->max_bcn_dtim_ms)
pstate_rdh->max_bcn_dtim_ms =
bcn_dtim_msec;
}
}
if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count)
break; /* EXIT CASE (for UAP) */
/* else */
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_SET_CUSTOM_IE;
/* fall through */
case RDH_SET_CUSTOM_IE:
PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage],
pstate_rdh->priv_curr_idx);
/* add CHAN_SW IE - Need apply on each interface */
if (pstate_rdh->priv_curr_idx ==
RDH_STAGE_FIRST_ENTRY_PRIV_IDX) {
mlan_ioctl_req *pioctl_req = MNULL;
PRINTM(MMSG,
"11h: Radar Detected - adding CHAN_SW IE to interfaces.\n");
while ((++pstate_rdh->priv_curr_idx) <
pstate_rdh->priv_list_count) {
pmpriv = pstate_rdh->priv_list
[pstate_rdh->priv_curr_idx];
if (!wlan_11h_is_dfs_master(pmpriv))
continue;
ret = wlan_11h_prepare_custom_ie_chansw(
pmadapter, &pioctl_req, MTRUE);
if ((ret != MLAN_STATUS_SUCCESS) ||
!pioctl_req) {
PRINTM(MERROR,
"%s(): Error in preparing CHAN_SW IE.\n",
__func__);
break; /* EXIT CASE */
}
pioctl_req->bss_index = pmpriv->bss_index;
ret = wlan_misc_ioctl_custom_ie_list(
pmadapter, pioctl_req, MFALSE);
if (ret != MLAN_STATUS_SUCCESS &&
ret != MLAN_STATUS_PENDING) {
PRINTM(MERROR,
"%s(): Could not set IE for priv=%p [priv_bss_idx=%d]!\n",
__func__, pmpriv,
pmpriv->bss_index);
/* TODO: how to handle this error case??
* ignore & continue? */
}
/* free ioctl buffer memory before we leave */
pmadapter->callbacks.moal_mfree(
pmadapter->pmoal_handle,
(t_u8 *)pioctl_req);
}
break; /* EXIT CASE */
}
/* else */
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_REM_CUSTOM_IE;
/* fall through */
case RDH_REM_CUSTOM_IE:
PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage],
pstate_rdh->priv_curr_idx);
/* remove CHAN_SW IE - Need apply on each interface */
if (pstate_rdh->priv_curr_idx ==
RDH_STAGE_FIRST_ENTRY_PRIV_IDX) {
mlan_ioctl_req *pioctl_req = MNULL;
/*
* first entry to this stage, do delay
* DFS requires a minimum of 5 chances for clients to
* hear this IE. Use delay: 5 beacons <=
* (BCN_DTIM_MSEC*5) <= 3 seconds).
*/
t_u16 delay_ms =
MAX(MIN_RDH_CHAN_SW_IE_PERIOD_MSEC,
MIN((4 * pstate_rdh->max_bcn_dtim_ms),
MAX_RDH_CHAN_SW_IE_PERIOD_MSEC));
PRINTM(MMSG,
"11h: Radar Detected - delay %d ms for FW to"
" broadcast CHAN_SW IE.\n",
delay_ms);
wlan_mdelay(pmadapter, delay_ms);
PRINTM(MMSG,
"11h: Radar Detected - delay over, removing"
" CHAN_SW IE from interfaces.\n");
while ((++pstate_rdh->priv_curr_idx) <
pstate_rdh->priv_list_count) {
pmpriv = pstate_rdh->priv_list
[pstate_rdh->priv_curr_idx];
if (!wlan_11h_is_dfs_master(pmpriv))
continue;
ret = wlan_11h_prepare_custom_ie_chansw(
pmadapter, &pioctl_req, MFALSE);
if ((ret != MLAN_STATUS_SUCCESS) ||
!pioctl_req) {
PRINTM(MERROR,
"%s(): Error in preparing CHAN_SW IE.\n",
__func__);
break; /* EXIT CASE */
}
pioctl_req->bss_index = pmpriv->bss_index;
ret = wlan_misc_ioctl_custom_ie_list(
pmadapter, pioctl_req, MFALSE);
if (ret != MLAN_STATUS_SUCCESS &&
ret != MLAN_STATUS_PENDING) {
PRINTM(MERROR,
"%s(): Could not remove IE for priv=%p [priv_bss_idx=%d]!\n",
__func__, pmpriv,
pmpriv->bss_index);
/* TODO: hiow to handle this error
* case?? ignore & continue? */
}
/* free ioctl buffer memory before we leave */
pmadapter->callbacks.moal_mfree(
pmadapter->pmoal_handle,
(t_u8 *)pioctl_req);
}
break; /* EXIT CASE */
}
/* else */
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_STOP_INTFS;
/* fall through */
case RDH_STOP_INTFS:
PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage],
pstate_rdh->priv_curr_idx);
/* issues one cmd (DEAUTH/ADHOC_STOP/BSS_STOP) to each intf */
while ((++pstate_rdh->priv_curr_idx) <
pstate_rdh->priv_list_count) {
pmpriv =
pstate_rdh->priv_list[pstate_rdh->priv_curr_idx];
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
ret = wlan_prepare_cmd(pmpriv,
HOST_CMD_APCMD_BSS_STOP,
HostCmd_ACT_GEN_SET, 0,
MNULL, MNULL);
break; /* leads to exit case */
}
#endif
#ifdef STA_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) {
if (wlan_11h_is_dfs_master(pmpriv)) {
/* Save ad-hoc creator state before stop
* clears it */
pmpriv->adhoc_state_prev =
pmpriv->adhoc_state;
}
if (pmpriv->media_connected == MTRUE) {
wlan_disconnect(pmpriv, MNULL, MNULL);
break; /* leads to exit case */
}
}
#endif
}
if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count ||
ret == MLAN_STATUS_FAILURE)
break; /* EXIT CASE */
/* else */
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_SET_NEW_CHANNEL;
if (pmadapter->dfs_test_params.no_channel_change_on_radar) {
PRINTM(MCMD_D,
"dfs_testing - no channel change on radar."
" Overwrite new_chan = curr_chan.\n");
pstate_rdh->new_channel = pstate_rdh->curr_channel;
pstate_rdh->priv_curr_idx =
RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_RESTART_INTFS;
goto rdh_restart_intfs; /* skip next stage */
}
/* fall through */
case RDH_SET_NEW_CHANNEL:
PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage],
pstate_rdh->priv_curr_idx);
/* only set new channel for UAP intfs */
while ((++pstate_rdh->priv_curr_idx) <
pstate_rdh->priv_list_count) {
pmpriv =
pstate_rdh->priv_list[pstate_rdh->priv_curr_idx];
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
pmpriv->uap_state_chan_cb.pioctl_req_curr =
MNULL;
pmpriv->uap_state_chan_cb.get_chan_callback =
wlan_11h_radar_detected_callback;
/* DFS only in 5GHz */
wlan_11h_update_bandcfg(
&pstate_rdh->uap_band_cfg,
pstate_rdh->new_channel);
PRINTM(MCMD_D,
"RDH_SET_NEW_CHANNEL: uAP band config = 0x%x channel=%d\n",
pstate_rdh->uap_band_cfg,
pstate_rdh->new_channel);
ret = wlan_uap_set_channel(
pmpriv, pstate_rdh->uap_band_cfg,
pstate_rdh->new_channel);
break; /* leads to exit case */
}
#endif
}
if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count ||
ret == MLAN_STATUS_FAILURE)
break; /* EXIT CASE (for UAP) */
/* else */
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_RESTART_INTFS;
/* fall through */
case RDH_RESTART_INTFS:
rdh_restart_intfs:
PRINTM(MCMD_D, "%s(): stage(%d)=%s, priv_idx=%d\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage],
pstate_rdh->priv_curr_idx);
/* can only restart master intfs */
while ((++pstate_rdh->priv_curr_idx) <
pstate_rdh->priv_list_count) {
pmpriv =
pstate_rdh->priv_list[pstate_rdh->priv_curr_idx];
#ifdef UAP_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
if (wlan_11h_radar_detect_required(
pmpriv, pstate_rdh->new_channel)) {
/* Radar detection is required for this
channel, make sure 11h is activated
in the firmware */
ret = wlan_11h_activate(pmpriv, MNULL,
MTRUE);
ret = wlan_11h_config_master_radar_det(
pmpriv, MTRUE);
ret = wlan_11h_check_update_radar_det_state(
pmpriv);
}
ret = wlan_prepare_cmd(pmpriv,
HOST_CMD_APCMD_BSS_START,
HostCmd_ACT_GEN_SET, 0,
MNULL, MNULL);
break; /* leads to exit case */
}
#endif
#ifdef STA_SUPPORT
if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) {
/* Check previous state to find former
* Ad-hoc creator interface. Set new
* state to Starting, so it'll be seen
* as a DFS master. */
if (pmpriv->adhoc_state_prev == ADHOC_STARTED) {
pmpriv->adhoc_state = ADHOC_STARTING;
pmpriv->adhoc_state_prev = ADHOC_IDLE;
}
if (wlan_11h_is_dfs_master(pmpriv)) {
/* set new adhoc channel here */
pmpriv->adhoc_channel =
pstate_rdh->new_channel;
if (wlan_11h_radar_detect_required(
pmpriv,
pstate_rdh->new_channel)) {
/* Radar detection is required
for this channel, make sure
11h is activated in the
firmware */
ret = wlan_11h_activate(
pmpriv, MNULL, MTRUE);
if (ret)
break;
ret = wlan_11h_config_master_radar_det(
pmpriv, MTRUE);
if (ret)
break;
ret = wlan_11h_check_update_radar_det_state(
pmpriv);
if (ret)
break;
}
ret = wlan_prepare_cmd(
pmpriv,
HostCmd_CMD_802_11_AD_HOC_START,
HostCmd_ACT_GEN_SET, 0, MNULL,
&pmpriv->adhoc_last_start_ssid);
break; /* leads to exit case */
}
/* NOTE: DON'T reconnect slave STA intfs -
* infra/adhoc_joiner Do we want to return to
* same AP/network (on radar channel)? If want
* to connect back, depend on either:
* 1. driver's reassoc thread
* 2. wpa_supplicant, or other user-space
* app
*/
}
#endif
}
if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count ||
ret == MLAN_STATUS_FAILURE)
break; /* EXIT CASE (for UAP) */
/* else */
pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
pstate_rdh->stage = RDH_RESTART_TRAFFIC;
/* fall through */
case RDH_RESTART_TRAFFIC:
PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]);
/* remove custome ie */
if (pmadapter->ecsa_enable) {
mlan_ioctl_req *pioctl_req = MNULL;
pstate_rdh->priv_curr_idx =
RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
while ((++pstate_rdh->priv_curr_idx) <
pstate_rdh->priv_list_count) {
pmpriv = pstate_rdh->priv_list
[pstate_rdh->priv_curr_idx];
if (!wlan_11h_is_dfs_master(pmpriv))
continue;
ret = wlan_11h_prepare_custom_ie_chansw(
pmadapter, &pioctl_req, MFALSE);
if ((ret != MLAN_STATUS_SUCCESS) ||
!pioctl_req) {
PRINTM(MERROR,
"%s(): Error in preparing CHAN_SW IE.\n",
__func__);
break; /* EXIT CASE */
}
pioctl_req->bss_index = pmpriv->bss_index;
ret = wlan_misc_ioctl_custom_ie_list(
pmadapter, pioctl_req, MFALSE);
if (ret != MLAN_STATUS_SUCCESS &&
ret != MLAN_STATUS_PENDING) {
PRINTM(MERROR,
"%s(): Could not remove IE for priv=%p [priv_bss_idx=%d]!\n",
__func__, pmpriv,
pmpriv->bss_index);
/* TODO: hiow to handle this error
* case?? ignore & continue? */
}
/* free ioctl buffer memory before we leave */
pmadapter->callbacks.moal_mfree(
pmadapter->pmoal_handle,
(t_u8 *)pioctl_req);
}
}
/* continue traffic for reactivated interfaces */
PRINTM(MMSG,
"11h: Radar Detected - restarting host tx traffic.\n");
for (i = 0; i < pstate_rdh->priv_list_count; i++)
wlan_11h_tx_enable(pstate_rdh->priv_list[i]);
pstate_rdh->stage = RDH_OFF; /* DONE! */
PRINTM(MCMD_D, "%s(): finished - stage(%d)=%s\n", __func__,
pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]);
break;
default:
pstate_rdh->stage = RDH_OFF; /* cancel RDH to unblock Tx packets
*/
break;
}
LEAVE();
return ret;
}
/**
* @brief DFS Event Preprocessing.
* Operates directly on pmadapter variables.
*
* 1. EVENT_RADAR_DETECTED comes from firmware without specific
* bss_num/bss_type. Find it an appropriate interface and
* update event_cause field in event_buf.
*
* @param pmadapter Pointer to mlan_adapter
*
* @return MLAN_STATUS_SUCCESS (update successful)
* or MLAN_STATUS_FAILURE (no change)
*/
mlan_status wlan_11h_dfs_event_preprocessing(mlan_adapter *pmadapter)
{
mlan_status ret = MLAN_STATUS_FAILURE;
mlan_private *pmpriv = MNULL;
mlan_private *priv_list[MLAN_MAX_BSS_NUM] = {MNULL};
ENTER();
switch (pmadapter->event_cause & EVENT_ID_MASK) {
case EVENT_RADAR_DETECTED:
/* find active intf: prefer dfs_master over dfs_slave */
if (wlan_get_privs_by_two_cond(
pmadapter, wlan_11h_is_master_active_on_dfs_chan,
wlan_11h_is_dfs_master, MTRUE, priv_list)) {
pmpriv = priv_list[0];
PRINTM(MINFO, "%s: found dfs_master priv=%p\n",
__func__, pmpriv);
} else if (wlan_get_privs_by_two_cond(
pmadapter,
wlan_11h_is_slave_active_on_dfs_chan,
wlan_11h_is_dfs_slave, MTRUE, priv_list)) {
pmpriv = priv_list[0];
PRINTM(MINFO, "%s: found dfs_slave priv=%p\n", __func__,
pmpriv);
} else if (pmadapter->state_dfs.dfs_check_pending) {
pmpriv = (mlan_private *)(pmadapter->state_dfs
.dfs_check_priv);
PRINTM(MINFO, "%s: found dfs priv=%p\n", __func__,
pmpriv);
}
/* update event_cause if we found an appropriate priv */
if (pmpriv) {
pmlan_buffer pmevbuf = pmadapter->pmlan_buffer_event;
t_u32 new_event_cause =
pmadapter->event_cause & EVENT_ID_MASK;
new_event_cause |=
((GET_BSS_NUM(pmpriv) & 0xff) << 16) |
((pmpriv->bss_type & 0xff) << 24);
PRINTM(MINFO, "%s: priv - bss_num=%d, bss_type=%d\n",
__func__, GET_BSS_NUM(pmpriv), pmpriv->bss_type);
memcpy_ext(pmadapter,
pmevbuf->pbuf + pmevbuf->data_offset,
&new_event_cause, sizeof(new_event_cause),
sizeof(new_event_cause));
ret = MLAN_STATUS_SUCCESS;
} else {
PRINTM(MERROR,
"Failed to find dfs master/slave priv\n");
ret = MLAN_STATUS_FAILURE;
}
break;
}
LEAVE();
return ret;
}
/**
* @brief try to switch to a non-dfs channel
*
* @param priv Void pointer to mlan_private
*
* @param chan pointer to channel
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING
*/
mlan_status wlan_11h_switch_non_dfs_chan(mlan_private *priv, t_u8 *chan)
{
mlan_status ret = MLAN_STATUS_FAILURE;
t_u32 i;
t_u32 rand_entry;
t_u8 def_chan;
t_u8 rand_tries = 0;
region_chan_t *chn_tbl = MNULL;
pmlan_adapter pmadapter = priv->adapter;
ENTER();
if (!pmadapter->dfs_test_params.no_channel_change_on_radar &&
pmadapter->dfs_test_params.fixed_new_channel_on_radar) {
PRINTM(MCMD_D, "dfs_testing - user fixed new_chan=%d\n",
pmadapter->dfs_test_params.fixed_new_channel_on_radar);
*chan = pmadapter->dfs_test_params.fixed_new_channel_on_radar;
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/*get the channel table first*/
for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
if (pmadapter->region_channel[i].band == BAND_A &&
pmadapter->region_channel[i].valid) {
chn_tbl = &pmadapter->region_channel[i];
break;
}
}
if (!chn_tbl || !chn_tbl->pcfp)
goto done;
do {
rand_entry =
wlan_11h_get_random_num(pmadapter) % chn_tbl->num_cfp;
def_chan = (t_u8)chn_tbl->pcfp[rand_entry].channel;
rand_tries++;
} while ((wlan_11h_is_channel_under_nop(pmadapter, def_chan) ||
chn_tbl->pcfp[rand_entry].passive_scan_or_radar_detect ==
MTRUE) &&
(rand_tries < MAX_SWITCH_CHANNEL_RETRIES));
/* meet max retries, use the lowest non-dfs channel */
if (rand_tries == MAX_SWITCH_CHANNEL_RETRIES) {
for (i = 0; i < chn_tbl->num_cfp; i++) {
if (chn_tbl->pcfp[i].passive_scan_or_radar_detect ==
MFALSE &&
!wlan_11h_is_channel_under_nop(
pmadapter,
(t_u8)chn_tbl->pcfp[i].channel)) {
def_chan = (t_u8)chn_tbl->pcfp[i].channel;
break;
}
}
if (i == chn_tbl->num_cfp)
goto done;
}
*chan = def_chan;
ret = MLAN_STATUS_SUCCESS;
done:
LEAVE();
return ret;
}
/**
* @brief set dfs check channel
*
* @param priv Void pointer to mlan_private
*
* @param chan pointer to channel
*
* @return N/A
*/
void wlan_11h_set_dfs_check_chan(mlan_private *priv, t_u8 chan)
{
wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs;
ENTER();
pstate_dfs->dfs_check_channel = chan;
PRINTM(MCMND, "Set dfs_check_channel=%d\n", chan);
LEAVE();
}
/**
* @brief 802.11h DFS W53 configuration
*
* @param pmadapter Pointer to mlan_adapter
* @param pioctl_req Pointer to mlan_ioctl_req
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
mlan_status wlan_11h_ioctl_dfs_w53_cfg(pmlan_adapter pmadapter,
pmlan_ioctl_req pioctl_req)
{
mlan_ds_11h_cfg *ds_11hcfg = MNULL;
mlan_ds_11h_dfs_w53_cfg *dfs_w53_cfg = MNULL;
ENTER();
ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf;
dfs_w53_cfg = &ds_11hcfg->param.dfs_w53_cfg;
if (pioctl_req->action == MLAN_ACT_GET) {
dfs_w53_cfg->dfs53cfg = pmadapter->dfs53cfg;
} else {
pmadapter->dfs53cfg = dfs_w53_cfg->dfs53cfg;
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}