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