blob: 49136d632e859f04662b5d8e2a97e1d31ee09b25 [file] [log] [blame]
/** @file mlan_11h.c
*
* @brief This file contains functions for 802.11H.
*
* Copyright (C) 2008-2014, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this 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"
#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 (500) /* 5 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 = 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 Marvell
* proprietary format
*
* @param pmadapter Pointer to mlan_adapter
* @param pout_buf Output parameter: Buffer to output Marvell formatted IE
* @param pin_ie Pointer to IEEE IE to be converted to Marvell 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 Marvell 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(pmadapter, ptmp_buf, &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(pmadapter, ptmp_buf, pin_ie + 2, 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(adapter, pdfs->dfs_owner, priv->curr_addr,
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 0x32: /* France */
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 0x32: /* France */
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 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;
break;
case 0x50: /* China */
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;
}
}
/*
* 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(priv->adapter, &pcmd_ptr->params.tpc_req, pinfo_buf,
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(priv->adapter, &pcmd_ptr->params.chan_sw_ann, pch_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)
{
const 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;
ENTER();
if (pstate_dfs->dfs_check_pending) {
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(priv->adapter, &pcmd_ptr->params.chan_rpt_req, pchan_rpt_req,
sizeof(HostCmd_DS_CHAN_RPT_REQ));
/* if DFS channel, add BASIC report TLV, and set radar bit */
if (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);
}
/* 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_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 = sup_chan_len;
memcpy(priv->adapter, *ppbuffer, &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(priv->adapter, *ppbuffer, &sup_chan_ie, 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(adapter, dfs_elem.dfs_owner,
p11h_bss_info->ibss_dfs.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.band_config & BAND_CONFIG_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
*/
static 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, OUT 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 Determine if mlan_private list only contains UAP interface(s)
*
* @param priv_list List of mlan_private pointers
* @param priv_list_count Number of mlan_privates in above list
*
* @return MTRUE or MFALSE
*/
static t_bool
wlan_only_uap_priv_in_list(mlan_private **priv_list, t_u8 priv_list_count)
{
#if defined(STA_SUPPORT) && !defined(UAP_SUPPORT)
return MFALSE;
#else
t_u8 uap_count = 0;
t_u8 sta_count = 0;
t_u8 i;
ENTER();
for (i = 0; i < priv_list_count; i++) {
if (GET_BSS_ROLE(priv_list[i]) == MLAN_BSS_ROLE_UAP)
uap_count++;
else
sta_count++;
}
LEAVE();
return ((uap_count > 0) && (sta_count == 0)) ? MTRUE : MFALSE;
#endif
}
/**
* @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 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(IN mlan_adapter *pmadapter,
OUT mlan_ioctl_req **ppioctl_req,
IN 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;
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 = 0; /* simplification */
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 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, t_u8 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 & BAND_CONFIG_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).
*/
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(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;
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;
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) {
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) {
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);
/*
* 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;
#ifdef DFS_TESTING_SUPPORT
wlan_dfs_testing_settings_t *pdfs_test = &adapter->dfs_test_params;
#endif
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;
pstate_rdh->uap_band_cfg = 0;
pstate_rdh->max_bcn_dtim_ms = 0;
memset(adapter, pstate_rdh->priv_list, 0,
sizeof(pstate_rdh->priv_list));
#ifdef DFS_TESTING_SUPPORT
/* 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;
#endif
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)
|| (adapter->adhoc_start_band & BAND_AN)
) {
/*
* 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;
}
/**
* @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 chan_width Width of channel on which to perform radar measurement
*
* @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, t_u8 chan_width)
{
t_s32 ret;
HostCmd_DS_CHAN_RPT_REQ chan_rpt_req;
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;
chan_rpt_req.chan_desc.chanWidth = chan_width;
chan_rpt_req.chan_desc.chanNum = channel;
chan_rpt_req.millisec_dwell_time =
WLAN_11H_CHANNEL_AVAIL_CHECK_DURATION;
#ifdef DFS_TESTING_SUPPORT
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;
}
#endif
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 {
/* 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)
|| (adapter->adhoc_start_band & BAND_AN)
)
) {
if (!wlan_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_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(priv->adapter, priv->adapter->curr_cmd->pdata_buf,
&resp->params.tpc_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:
PRINTM(MINFO,
"11h: Ret ChanRptReq. Set dfs_check_pending and wait"
" for EVENT_CHANNEL_REPORT.\n");
priv->adapter->state_dfs.dfs_check_pending = MTRUE;
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(pmadapter, &p11h_bss_info->power_constraint, pelement,
MIN((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(pmadapter, &p11h_bss_info->power_capability, pelement,
MIN((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(pmadapter, &p11h_bss_info->tpc_report, pelement,
MIN((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(pmadapter, &p11h_bss_info->chan_switch_ann, pelement,
MIN((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(pmadapter, &p11h_bss_info->quiet, pelement,
MIN((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(pmadapter, &p11h_bss_info->ibss_dfs, pelement,
MIN((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;
t_u32 sec, usec;
ENTER();
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");
ret = wlan_disconnect(priv, MNULL,
&priv->curr_bss_params.bss_descriptor.
mac_address);
/* 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;
}
#ifdef DFS_TESTING_SUPPORT
/**
* @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;
} 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;
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
#endif /* DFS_TESTING_SUPPORT */
/**
* @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);
#ifdef DFS_TESTING_SUPPORT
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
#endif
{
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)
{
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;
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, wlan_le32_to_cpu(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 = wlan_le32_to_cpu(pevent->event_len);
evt_len -=
sizeof(HostCmd_DS_CHAN_RPT_RSP) -
sizeof(pchan_rpt_rsp->tlv_buffer);
while (evt_len >= 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) {
pstate_dfs->dfs_radar_found = MTRUE;
PRINTM(MMSG,
"RADAR Detected on channel %d!\n",
pstate_dfs->dfs_check_channel);
/* add channel to NOP list */
wlan_11h_add_dfs_timestamp(priv->
adapter,
DFS_TS_REPR_NOP_START,
pstate_dfs->
dfs_check_channel);
}
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;
}
/* 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;
LEAVE();
return ret;
}
/**
* @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)
{
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);
LEAVE();
return ret;
}
/**
* @brief Driver handling for RADAR_DETECTED event
*
* @param pmadapter Pointer to mlan_adapter
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE or MLAN_STATUS_PENDING
*/
mlan_status
wlan_11h_radar_detected_handling(mlan_adapter *pmadapter)
{
#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;
mlan_private *pmpriv = MNULL;
t_u32 i;
wlan_radar_det_hndlg_state_t *pstate_rdh = &pmadapter->state_rdh;
ENTER();
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;
pstate_rdh->uap_band_cfg = 0;
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 TO NEXT STAGE */
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 TO NEXT STAGE */
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) {
if (wlan_only_uap_priv_in_list(pstate_rdh->priv_list,
pstate_rdh->
priv_list_count)) {
#ifdef UAP_SUPPORT
/* Assume all UAPs on same channel, use first
UAP */
pmpriv = pstate_rdh->priv_list[0];
pstate_rdh->priv_curr_idx = 0;
/* 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 */
#endif
} else {
/* 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 */
pmpriv = pstate_rdh->priv_list[pstate_rdh->
priv_curr_idx];
pstate_rdh->curr_channel =
pmpriv->uap_state_chan_cb.channel;
pstate_rdh->uap_band_cfg =
pmpriv->uap_state_chan_cb.band_config;
PRINTM(MCMD_D, "%s(): uap_band_cfg=0x%02x\n", __func__,
pstate_rdh->uap_band_cfg);
}
#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 */
i = 0;
do {
#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.
band_config);
else
#endif
pstate_rdh->new_channel =
wlan_11h_get_adhoc_start_channel
(pmpriv);
} while ((pstate_rdh->new_channel == pstate_rdh->curr_channel) && (++i < MAX_RANDOM_CHANNEL_RETRIES)); /* avoid
deadloop
*/
if (i >= MAX_RANDOM_CHANNEL_RETRIES) /* report error */
PRINTM(MERROR,
"%s(): ERROR - could not choose new_chan"
" (!= curr_chan) !!\n", __func__);
#ifdef DFS_TESTING_SUPPORT
if (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;
}
#endif
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 TO NEXT STAGE */
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 TO NEXT STAGE */
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 - firmware will accept on any interface, and
apply to all */
if (pstate_rdh->priv_curr_idx == RDH_STAGE_FIRST_ENTRY_PRIV_IDX) {
mlan_ioctl_req *pioctl_req = MNULL;
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 */
}
PRINTM(MMSG,
"11h: Radar Detected - adding CHAN_SW IE to interfaces.\n");
pmpriv = pstate_rdh->priv_list[0];
pstate_rdh->priv_curr_idx = 0;
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 TO NEXT STAGE */
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 - firmware will accept on any interface,
and apply to all */
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((5 *
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");
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 */
}
pmpriv = pstate_rdh->priv_list[0];
pstate_rdh->priv_curr_idx = 0;
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: 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 TO NEXT STAGE */
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;
#ifdef DFS_TESTING_SUPPORT
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 */
}
#endif
/* FALL THROUGH TO NEXT STAGE */
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 */
pstate_rdh->uap_band_cfg |= BAND_CONFIG_5GHZ;
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 TO NEXT STAGE */
case RDH_RESTART_INTFS:
#ifdef DFS_TESTING_SUPPORT
rdh_restart_intfs:
#endif
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 TO NEXT STAGE */
case RDH_RESTART_TRAFFIC:
PRINTM(MCMD_D, "%s(): stage(%d)=%s\n",
__func__, pstate_rdh->stage,
rdh_stage_str[pstate_rdh->stage]);
/* 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];
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);
}
/* 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(pmadapter, pmevbuf->pbuf + pmevbuf->data_offset,
&new_event_cause, sizeof(new_event_cause));
ret = MLAN_STATUS_SUCCESS;
}
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();
/* 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;
}