/** @file mlan_11h.c
 *
 *  @brief This file contains functions for 802.11H.
 *
 *
 *  Copyright 2008-2021 NXP
 *
 *  NXP CONFIDENTIAL
 *  The source code contained or described herein and all documents related to
 *  the source code (Materials) are owned by NXP, its
 *  suppliers and/or its licensors. Title to the Materials remains with NXP,
 *  its suppliers and/or its licensors. The Materials contain
 *  trade secrets and proprietary and confidential information of NXP, its
 *  suppliers and/or its licensors. The Materials are protected by worldwide
 *  copyright and trade secret laws and treaty provisions. No part of the
 *  Materials may be used, copied, reproduced, modified, published, uploaded,
 *  posted, transmitted, distributed, or disclosed in any way without NXP's
 *  prior express written permission.
 *
 *  No license under any patent, copyright, trade secret or other intellectual
 *  property right is granted to or conferred upon you by disclosure or delivery
 *  of the Materials, either expressly, by implication, inducement, estoppel or
 *  otherwise. Any license under such intellectual property rights must be
 *  express and approved by NXP in writing.
 *
 *  Alternatively, this software may be distributed under the terms of GPL v2.
 *  SPDX-License-Identifier:    GPL-2.0
 *
 *
 */

/*************************************************************
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};
/** U-NII sub-band config : Start Channel = 169, NumChans = 3 */
static const IEEEtypes_SupportChan_Subband_t wlan_11h_unii_4_band = {169, 3};

/** 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];
}

/**
 *  @brief find all bonded channel.
 *
 *  @param pri_chan   primary channel
 *  @param bw         channel bandwidth
 *  @param chan_list  buffer to return channel list.
 *
 *  @return           number of channel
 */
static t_u8 woal_get_bonded_channels(t_u8 pri_chan, t_u8 bw, t_u8 *chan_list)
{
	t_u8 ht40_plus[] = {52, 60, 100, 108, 116, 124, 132, 140};
	t_u8 ht40_minus[] = {56, 64, 104, 112, 120, 128, 136, 144};
	t_u8 vht80_dfs[4][4] = {{52, 56, 60, 64},
				{100, 104, 108, 112},
				{116, 120, 124, 128},
				{132, 136, 140, 144}};
	t_u8 find = MFALSE;
	int j;
	int i;
	t_u8 sec_chan = 0;
	t_u8 n_chan = 1;
	ENTER();

	if (bw == CHAN_BW_20MHZ) {
		chan_list[0] = pri_chan;
	} else if (bw == CHAN_BW_40MHZ) {
		chan_list[0] = pri_chan;
		for (i = 0; i < sizeof(ht40_minus); i++) {
			if (pri_chan == (t_u8)ht40_plus[i]) {
				sec_chan = pri_chan + 4;
				n_chan = 2;
				break;
			}
		}
		for (i = 0; i < sizeof(ht40_minus); i++) {
			if (pri_chan == (t_u8)ht40_minus[i]) {
				sec_chan = pri_chan - 4;
				n_chan = 2;
				break;
			}
		}
		chan_list[1] = sec_chan;
	} else if (bw == CHAN_BW_80MHZ) {
		for (i = 0; i < 4; i++) {
			for (j = 0; j < 4; j++) {
				if (pri_chan == (t_u8)vht80_dfs[i][j]) {
					find = MTRUE;
					break;
				}
			}
			if (find)
				break;
		}
		if (find) {
			n_chan = 4;
			for (j = 0; j < n_chan; j++) {
				chan_list[j] = (t_u8)vht80_dfs[i][j];
			}
		}
	}
	LEAVE();
	return n_chan;
}

/**
 *  @brief Set channel's dfs state
 *
 *  @param priv         Private driver information structure
 *  @param chan         primary channel
 *  @param bw           channel bandwidth
 *  @param dfs_state    dfs state
 *
 *  @return  N/A
 */
t_void wlan_11h_set_chan_dfs_state(mlan_private *priv, t_u8 chan, t_u8 bw,
				   dfs_state_t dfs_state)
{
	t_u8 n_chan;
	t_u8 chan_list[4] = {0};
	t_u8 i;
	n_chan = woal_get_bonded_channels(chan, bw, chan_list);
	for (i = 0; i < n_chan; i++)
		wlan_set_chan_dfs_state(priv, BAND_A, chan_list[i], dfs_state);
}

#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_u16 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   */
			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;
			psup_chan->subband[num_subbands++] =
				wlan_11h_unii_4_band;
			break;
		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;
	MrvlIEtypes_ZeroDfsOperation_t *ptlv_zero_dfs;
	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 &&
	    priv->bss_type != MLAN_BSS_TYPE_DFS) {
		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);
	}

	if (priv->bss_type == MLAN_BSS_TYPE_DFS) {
		memcpy_ext(priv->adapter, &priv->chan_rep_req, pchan_rpt_req,
			   sizeof(mlan_ds_11h_chan_rep_req),
			   sizeof(priv->chan_rep_req));
		ptlv_zero_dfs =
			(MrvlIEtypes_ZeroDfsOperation_t *)(((t_u8 *)(pcmd_ptr)) +
							   pcmd_ptr->size);
		ptlv_zero_dfs->Header.type =
			wlan_cpu_to_le16(TLV_TYPE_ZERO_DFS_OPERATION);
		ptlv_zero_dfs->Header.len = wlan_cpu_to_le16(sizeof(t_u8));
		if (!is_cancel_req) {
			ptlv_zero_dfs->zero_dfs_enbl = MTRUE;
			PRINTM(MCMND, "DFS: START: chan=%d bw=%d\n",
			       pchan_rpt_req->chan_desc.chanNum,
			       pchan_rpt_req->chan_desc.bandcfg.chanWidth);
		} else {
			ptlv_zero_dfs->zero_dfs_enbl = MFALSE;
			PRINTM(MCMND, "DFS: STOP\n");
		}
		pcmd_ptr->size += sizeof(MrvlIEtypes_ZeroDfsOperation_t);
		LEAVE();
		return MLAN_STATUS_SUCCESS;
	}

	/* 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;
		pstate_dfs->dfs_check_bandwidth =
			pchan_rpt_req->chan_desc.bandcfg.chanWidth;
	}

	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_u16 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 Check if the current input channel is on radar channel
 *
 *
 *  @param priv    Private driver information structure
 *  @param channel Channel to determine radar detection requirements
 *
 *  @return
 *    - MTRUE if radar detection is required
 *    - MFALSE otherwise
 */
t_bool wlan_11h_is_radar_channel(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);

	LEAVE();
	return required;
}

/**
 *  @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 */
	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 */
	pds_misc_cfg->sub_command = MLAN_OID_MISC_CUSTOM_IE;
	pds_misc_cfg->param.cust_ie.type = TLV_TYPE_MGMT_IE;
	pds_misc_cfg->param.cust_ie.len = (sizeof(custom_ie) - MAX_IE_SIZE);

	/* configure custom_ie api settings */
	pcust_chansw_ie =
		(custom_ie *)&pds_misc_cfg->param.cust_ie.ie_data_list[0];
	pcust_chansw_ie->ie_index = 0xffff; /* Auto index */
	pcust_chansw_ie->ie_length = sizeof(IEEEtypes_ChanSwitchAnn_t);
	pcust_chansw_ie->mgmt_subtype_mask =
		(is_adding_ie) ? MBIT(8) | MBIT(5) /* add IE for BEACON |
						      PROBE_RSP */
				 :
				 0; /* remove IE */

	/* prepare CHAN_SW IE inside ioctl */
	pchansw_ie = (IEEEtypes_ChanSwitchAnn_t *)pcust_chansw_ie->ie_buffer;
	pchansw_ie->element_id = CHANNEL_SWITCH_ANN;
	pchansw_ie->len =
		sizeof(IEEEtypes_ChanSwitchAnn_t) - sizeof(IEEEtypes_Header_t);
	pchansw_ie->chan_switch_mode = 1; /* STA should not transmit */
	pchansw_ie->new_channel_num = pmadapter->state_rdh.new_channel;

	pchansw_ie->chan_switch_count = pmadapter->dfs_cs_count;
	PRINTM(MCMD_D, "New Channel = %d Channel switch count = %d\n",
	       pmadapter->state_rdh.new_channel, pchansw_ie->chan_switch_count);

	for (index = 0; index < pmadapter->state_rdh.priv_list_count; index++) {
		pmpriv = pmadapter->state_rdh.priv_list[index];
		/*find the first AP interface*/
		if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
			if (pmpriv->is_11ac_enabled) {
				pChanSwWrap_ie =
					(IEEEtypes_Header_t
						 *)((t_u8 *)pchansw_ie +
						    sizeof(IEEEtypes_ChanSwitchAnn_t));
				pChanSwWrap_ie->element_id = EXT_POWER_CONSTR;
				/*will have multiple sub IEs*/
				pChanSwWrap_ie->len = 0;

				/* prepare the Wide Bandwidth Channel Switch IE
				 * Channel Switch IE */
				pbwchansw_ie =
					(IEEEtypes_WideBWChanSwitch_t
						 *)((t_u8 *)pChanSwWrap_ie +
						    sizeof(IEEEtypes_Header_t));
				pbwchansw_ie->ieee_hdr.element_id =
					BW_CHANNEL_SWITCH;
				pbwchansw_ie->ieee_hdr.len =
					sizeof(IEEEtypes_WideBWChanSwitch_t) -
					sizeof(IEEEtypes_Header_t);
				/*fix 80MHZ now*/
				pbwchansw_ie->new_channel_width =
					VHT_OPER_CHWD_80MHZ;
				pbwchansw_ie->new_channel_center_freq0 =
					wlan_11h_get_channel_freq_idx(
						pmadapter->state_rdh
							.new_channel);
				pbwchansw_ie->new_channel_center_freq1 =
					wlan_11h_get_channel_freq_idx(
						pmadapter->state_rdh
							.new_channel);
				pChanSwWrap_ie->len +=
					sizeof(IEEEtypes_WideBWChanSwitch_t);

				/*prepare the VHT Transmit Power Envelope IE*/
				pvhttpcEnv_ie =
					(IEEEtypes_VhtTpcEnvelope_t
						 *)((t_u8 *)pChanSwWrap_ie +
						    sizeof(IEEEtypes_Header_t) +
						    sizeof(IEEEtypes_WideBWChanSwitch_t));
				pvhttpcEnv_ie->ieee_hdr.element_id =
					VHT_TX_POWER_ENV;
				pvhttpcEnv_ie->ieee_hdr.len =
					sizeof(IEEEtypes_VhtTpcEnvelope_t) -
					sizeof(IEEEtypes_Header_t);
				/* Local Max TX Power Count= 3,
				 * Local TX Power Unit Inter=EIP(0) */
				pvhttpcEnv_ie->tpc_info = 3;
				pvhttpcEnv_ie->local_max_tp_20mhz = 0xff;
				pvhttpcEnv_ie->local_max_tp_40mhz = 0xff;
				pvhttpcEnv_ie->local_max_tp_80mhz = 0xff;
				pvhttpcEnv_ie->local_max_tp_160mhz_80_80mhz =
					0xff;
				pChanSwWrap_ie->len +=
					sizeof(IEEEtypes_VhtTpcEnvelope_t);

				pcust_chansw_ie->ie_length +=
					sizeof(IEEEtypes_WideBWChanSwitch_t) +
					sizeof(IEEEtypes_VhtTpcEnvelope_t) +
					sizeof(IEEEtypes_Header_t);

				PRINTM(MINFO,
				       "Append Wide Bandwidth Channel Switch IE\n");
				break;
			}
		}
	}

	pds_misc_cfg->param.cust_ie.len += pcust_chansw_ie->ie_length;
	DBG_HEXDUMP(MCMD_D, "11h: custom_ie containing CHAN_SW IE",
		    (t_u8 *)pcust_chansw_ie, pds_misc_cfg->param.cust_ie.len);

	/* assign output pointer before returning */
	*ppioctl_req = pioctl_req;
	LEAVE();
	return MLAN_STATUS_SUCCESS;
}

#ifdef UAP_SUPPORT
/**
 *  @brief Check if start channel 165 is allowed to operate in
 *  previous uAP channel's band config
 *
 *  @param priv          a pointer to mlan_private structure
 *  @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(mlan_private *priv, 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) {
		if (priv->adapter->region_code == COUNTRY_CODE_US)
			return MTRUE;
		if (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(
							 priv, 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;
		}

		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;
}

/**
 *  @brief Add all bonded channel's dfs timestamp to the list
 *
 *  @param pmadapter  Pointer to mlan_adapter
 *  @param repr       Timestamp 'represents' value (see _dfs_timestamp_repr_e)
 *  @param channel    Channel number
 *  @param bandwidth  Channel bandwidth
 *
 *  @return           Pointer to timestamp if found, or MNULL
 */
static void wlan_11h_add_all_dfs_timestamp(mlan_adapter *pmadapter, t_u8 repr,
					   t_u8 channel, t_u8 bandwidth)
{
	t_u8 n_chan;
	t_u8 chan_list[4] = {0};
	t_u8 i;
	n_chan = woal_get_bonded_channels(channel, bandwidth, chan_list);
	for (i = 0; i < n_chan; i++)
		wlan_11h_add_dfs_timestamp(pmadapter, repr, chan_list[i]);
}

/********************************************************
			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;
}

#ifdef UAP_SUPPORT
/**
 *  @brief Return whether the slave interface is 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_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_is_radar_channel(
		    priv, priv->curr_bss_params.bss_descriptor.channel))
		ret = MTRUE;

	LEAVE();
	return ret;
}

/**
 *  @brief check if dfs_master and dfs_slave are in same channel
 *
 *  @param pmadapter Pointer to mlan_adapter structure
 *
 *  @return        MTRUE-dfs_master and dfs_slave interface on same DFS channel
 *
 */
t_u8 static wlan_11h_check_dfs_channel(mlan_adapter *pmadapter)
{
	mlan_private *priv_master = MNULL;
	mlan_private *priv_slave = MNULL;
	mlan_private *priv_list[MLAN_MAX_BSS_NUM] = {MNULL};

	if (wlan_get_privs_by_two_cond(
		    pmadapter, wlan_11h_is_master_active_on_dfs_chan,
		    wlan_11h_is_dfs_master, MTRUE, priv_list)) {
		priv_master = priv_list[0];
		PRINTM(MINFO, "%s: found dfs_master priv=%p\n", __func__,
		       priv_master);
	}
	if (wlan_get_privs_by_two_cond(pmadapter, wlan_11h_is_slave_on_dfs_chan,
				       wlan_11h_is_dfs_slave, MTRUE,
				       priv_list)) {
		priv_slave = priv_list[0];
		PRINTM(MINFO, "%s: found dfs_slave priv=%p\n", __func__,
		       priv_slave);
	}
	if (!priv_slave || !priv_master)
		return MFALSE;
	if (priv_master->uap_state_chan_cb.channel !=
	    priv_slave->curr_bss_params.bss_descriptor.channel)
		return MFALSE;
	return MTRUE;
}

/**
 *   @brief disable 11h and DFS function
 *
 *  @param priv         Private driver information structure
 *  @param pioctl_buf   A pointer to MLAN IOCTL Request buffer
 *
 *  @return      MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
mlan_status static wlan_11h_disable_dfs(mlan_private *priv, t_void *pioctl_buf)
{
	t_u32 enable = 0;
	mlan_status ret = MLAN_STATUS_SUCCESS;

	ENTER();

	/*
	 * 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 = MFALSE;

	PRINTM(MINFO, "11h: DFS %s\n", "Deactivate");

	LEAVE();
	return ret;
}

/**
 *  @brief check if we need enable dfs_master
 *
 *  @param priv  Pointer to mlan_private structure
 *				 priv should be UAP priv
 *
 *  @return      N/A
 *
 */
void wlan_11h_update_dfs_master_state_by_uap(mlan_private *pmpriv)
{
	mlan_adapter *pmadapter = pmpriv->adapter;
	mlan_status ret = MLAN_STATUS_SUCCESS;
	if (pmadapter->dfs_mode && wlan_11h_check_dfs_channel(pmadapter)) {
		PRINTM(MCMND,
		       "11h: disable DFS master when AP+STA on same DFS channel\n");
		ret = wlan_11h_disable_dfs(pmpriv, MNULL);
		return;
	}
	if (!wlan_11h_is_active(pmpriv)) {
		/* active 11h extention in Fw */
		PRINTM(MCMND,
		       "11h: Enable DFS master after AP up or chan_switch\n");
		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);
	}
	if (pmpriv->uap_host_based && !pmpriv->adapter->init_para.dfs_offload)
		pmpriv->intf_state_11h.is_11h_host = MTRUE;
	wlan_11h_set_dfs_check_chan(pmpriv, pmpriv->uap_channel,
				    pmpriv->uap_bandwidth);
	return;
}

/**
 *  @brief check if dfs_master and dfs_slave are in same channel
 *
 *  @param pmadapter Pointer to mlan_adapter structure
 *
 *  @return        MTRUE-dfs_master and dfs_slave interface on same DFS channel
 *
 */
void wlan_11h_update_dfs_master_state_by_sta(mlan_private *pmpriv)
{
	mlan_private *priv_master = MNULL;
	mlan_private *priv_slave = MNULL;
	mlan_private *priv_list[MLAN_MAX_BSS_NUM] = {MNULL};
	mlan_adapter *pmadapter = pmpriv->adapter;
	mlan_status ret = MLAN_STATUS_SUCCESS;

	if (wlan_get_privs_by_two_cond(
		    pmadapter, wlan_11h_is_master_active_on_dfs_chan,
		    wlan_11h_is_dfs_master, MTRUE, priv_list)) {
		priv_master = priv_list[0];
		PRINTM(MINFO, "%s: found dfs_master priv=%p\n", __func__,
		       priv_master);
	}
	if (wlan_get_privs_by_two_cond(pmadapter, wlan_11h_is_slave_on_dfs_chan,
				       wlan_11h_is_dfs_slave, MTRUE,
				       priv_list)) {
		priv_slave = priv_list[0];
		PRINTM(MINFO, "%s: found dfs_slave priv=%p\n", __func__,
		       priv_slave);
	}
	if (!priv_slave || !priv_master)
		return;
	if (priv_master->uap_state_chan_cb.channel ==
	    priv_slave->curr_bss_params.bss_descriptor.channel) {
		PRINTM(MCMND,
		       "11h: disable DFS master when AP+STA on same DFS channel\n");
		ret = wlan_11h_disable_dfs(priv_master, MNULL);
	}
	return;
}

/**
 *  @brief update the dfs master state on station disconnect
 *
 *  @param priv  Pointer to mlan_private structure
 *				 priv should be UAP priv
 *
 *  @return      N/A
 *
 */
void wlan_11h_update_dfs_master_state_on_disconect(mlan_private *priv)
{
	mlan_private *priv_master = MNULL;
	mlan_private *priv_list[MLAN_MAX_BSS_NUM] = {MNULL};
	mlan_adapter *pmadapter = priv->adapter;
	mlan_status ret = MLAN_STATUS_SUCCESS;
	if (wlan_get_privs_by_two_cond(
		    pmadapter, wlan_11h_is_master_active_on_dfs_chan,
		    wlan_11h_is_dfs_master, MTRUE, priv_list)) {
		priv_master = priv_list[0];
		PRINTM(MINFO, "%s: found dfs_master priv=%p\n", __func__,
		       priv_master);
	}
	if (!priv_master) {
		wlan_11h_check_update_radar_det_state(priv);
		return;
	}
	if (!wlan_11h_is_active(priv_master)) {
		PRINTM(MCMND, "11h: Enable DFS master after STA disconnect\n");
		/* active 11h extention in Fw */
		ret = wlan_11h_activate(priv_master, MNULL, MTRUE);
		ret = wlan_11h_config_master_radar_det(priv_master, MTRUE);
		ret = wlan_11h_check_update_radar_det_state(priv_master);
	}
	if (priv_master->uap_host_based && !pmadapter->init_para.dfs_offload)
		priv_master->intf_state_11h.is_11h_host = MTRUE;
	wlan_11h_set_dfs_check_chan(priv_master, priv_master->uap_channel,
				    priv_master->uap_bandwidth);
	return;
}
#endif

/**
 *  @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 */
			if (wlan_11d_enable(priv, MNULL, ENABLE_11D)) {
				ret = MLAN_STATUS_FAILURE;
				LEAVE();
				return ret;
			}
#ifdef STA_SUPPORT
			if (wlan_11d_create_dnld_countryinfo(
				    priv, adapter->adhoc_start_band))
				PRINTM(MERROR, "Dnld_countryinfo_11d failed\n");
#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)
 */
t_s32 wlan_11h_process_join(mlan_private *priv, t_u8 **ppbuffer,
			    IEEEtypes_CapInfo_t *pcap_info, t_u16 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
			 */
			PRINTM(MINFO,
			       "Assume DFS parameters are the same for roaming\n");
		} else {
			/* No support for roaming between DFS/non-DFS yet */
			PRINTM(MINFO,
			       "No support for roaming between DFS/non-DFS yet\n");
		}

		LEAVE();
		return ret;
	}

	if (p11h_bss_info->sensed_11h) {
		if (!wlan_fw_11d_is_enabled(priv)) {
			/* No use having 11h enabled without 11d enabled */
			if (wlan_11d_enable(priv, MNULL, ENABLE_11D)) {
				PRINTM(MERROR, "Fail to enable 11D\n");
				LEAVE();
				return ret;
			}
#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 {
		if (pcap_info->ibss) {
			/* 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:
		if (priv->bss_type == MLAN_BSS_TYPE_DFS)
			break;
		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
	/** No need handle AP if mc_policy is disabled, FW will move the AP to
	 * client's new channel */
	if (pmadapter->mc_policy &&
	    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);
				}
			}
		}
	}
#endif /* UAP_SUPPORT */
	if (priv->adapter->ecsa_enable) {
		t_u8 stop_tx = *(t_u8 *)priv->adapter->event_body;
		if (stop_tx)
			priv->adapter->state_rdh.tx_block = MTRUE;
		LEAVE();
		return ret;
	}
	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 /* STA_SUPPORT */

	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 get nop channel list
 *
 *  @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_nop_channel_list(pmlan_adapter pmadapter,
					    pmlan_ioctl_req pioctl_req)
{
	mlan_ds_11h_cfg *ds_11hcfg = MNULL;
	int i, j;
	chan_freq_power_t *pcfp = MNULL;
	t_u8 num_chan = 0;
	ENTER();

	ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf;
	/*get the cfp table first */
	for (i = 0; i < MAX_REGION_CHANNEL_NUM; i++) {
		if (pmadapter->region_channel[i].band == BAND_A) {
			pcfp = pmadapter->region_channel[i].pcfp;
			break;
		}
	}
	if (!pcfp) {
		/* This means operation in BAND-A is not support, we can
		 * just return false here, it's harmless
		 */
		goto done;
	}
	/*get the radar detection requirements according to chan num */
	for (j = 0; j < pmadapter->region_channel[i].num_cfp; j++) {
		if (pcfp[j].passive_scan_or_radar_detect) {
			if (wlan_11h_is_channel_under_nop(pmadapter,
							  pcfp[j].channel)) {
				ds_11hcfg->param.nop_chan_list
					.chan_list[num_chan] = pcfp[j].channel;
				num_chan++;
			}
		}
	}
done:
	ds_11hcfg->param.nop_chan_list.num_chan = num_chan;
	return MLAN_STATUS_SUCCESS;
}

/**
 *  @brief 802.11h IOCTL to handle channel NOP status check/clear
 *  @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_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 &&
			    ch_nop_info->check_new_chan) {
				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(
						pmpriv,
						&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,
							ch_nop_info->new_chan
								.bandcfg
								.chanBand,
							ch_nop_info->new_chan
								.channel,
							ch_nop_info->chan_width);
			}
		} else if (pioctl_req->action == MLAN_ACT_CLEAR) {
			wlan_11h_cleanup(pmadapter);
			wlan_reset_all_chan_dfs_state(pmpriv, BAND_A,
						      DFS_USABLE);
		}
		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 Get/Set 802.11h  channel dfs state
 *
 *  @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_dfs_state(pmlan_adapter pmadapter,
					  pmlan_ioctl_req pioctl_req)
{
	mlan_ds_11h_cfg *ds_11hcfg = MNULL;
	t_s32 ret = MLAN_STATUS_FAILURE;
	pmlan_private priv = MNULL;

	ENTER();

	if (pioctl_req) {
		ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf;
		priv = pmadapter->priv[pioctl_req->bss_index];

		if (pioctl_req->action == MLAN_ACT_GET) {
			if (MFALSE ==
			    wlan_11h_is_channel_under_nop(
				    pmadapter,
				    ds_11hcfg->param.ch_dfs_state.channel))
				PRINTM(MINFO, "Channel is not in NOP\n");
			ds_11hcfg->param.ch_dfs_state.dfs_required =
				wlan_11h_radar_detect_required(
					priv,
					ds_11hcfg->param.ch_dfs_state.channel);
			if (ds_11hcfg->param.ch_dfs_state.dfs_required)
				ds_11hcfg->param.ch_dfs_state
					.dfs_state = wlan_get_chan_dfs_state(
					priv, BAND_A,
					ds_11hcfg->param.ch_dfs_state.channel);
		} else {
			wlan_set_chan_dfs_state(
				priv, BAND_A,
				ds_11hcfg->param.ch_dfs_state.channel,
				ds_11hcfg->param.ch_dfs_state.dfs_state);
		}
		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;
	mlan_private *priv;
	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);
			priv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_ANY);
			if (priv)
				wlan_set_chan_dfs_state(priv, BAND_A, channel,
							DFS_USABLE);
		} 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
 *  @param radar_chan 	Pointer to radar channel
 *  @param bandwidth    Pointer to band width
 *
 *  @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,
						t_u8 *bandwidth)
{
	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;
	t_u8 dfs_check_bandwidth = pstate_dfs->dfs_check_bandwidth;
	MrvlIEtypes_channel_band_t *tlv;

	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 (priv->bss_type == MLAN_BSS_TYPE_DFS) {
		dfs_check_channel = priv->chan_rep_req.chanNum;
		dfs_check_bandwidth = priv->chan_rep_req.bandcfg.chanWidth;
	}

	if (wlan_le32_to_cpu(pchan_rpt_rsp->cmd_result) ==
	    MLAN_CMD_RESULT_SUCCESS) {
		pbuffer = (t_u8 *)&pchan_rpt_rsp->tlv_buffer;
		evt_len = pevent->event_len;
		evt_len -= sizeof(HostCmd_DS_CHAN_RPT_RSP) -
			   sizeof(pchan_rpt_rsp->tlv_buffer);

		while (evt_len >= (t_s32)sizeof(MrvlIEtypesHeader_t)) {
			ptlv = (MrvlIEtypes_Data_t *)pbuffer;
			tlv_len = wlan_le16_to_cpu(ptlv->header.len);

			switch (wlan_le16_to_cpu(ptlv->header.type)) {
			case TLV_TYPE_CHANRPT_11H_BASIC:
				pmeas_rpt_basic =
					(MeasRptBasicMap_t *)&ptlv->data;
				if (pmeas_rpt_basic->radar)
					dfs_radar_found = MTRUE;
				break;
			case TLV_TYPE_CHANNELBANDLIST:
				tlv = (MrvlIEtypes_channel_band_t *)ptlv;
				dfs_check_channel = tlv->channel;
				dfs_check_bandwidth = tlv->bandcfg.chanWidth;
				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 bw=%d !\n",
		       dfs_check_channel, dfs_check_bandwidth);
		/* add channel to NOP list */
		wlan_11h_add_all_dfs_timestamp(priv->adapter,
					       DFS_TS_REPR_NOP_START,
					       dfs_check_channel,
					       dfs_check_bandwidth);
	}
	*radar_chan = dfs_check_channel;
	*bandwidth = dfs_check_bandwidth;
	if (dfs_radar_found)
		wlan_11h_set_chan_dfs_state(priv, dfs_check_channel,
					    dfs_check_bandwidth,
					    DFS_UNAVAILABLE);
	else
		wlan_11h_set_chan_dfs_state(priv, dfs_check_channel,
					    dfs_check_bandwidth, DFS_AVAILABLE);
	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;

	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
 *  @param bandwidth    Pointer to band width
 *
 *  @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,
						t_u8 *bandwidth)
{
	wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs;
	t_u8 dfs_check_bandwidth = pstate_dfs->dfs_check_bandwidth;
	MrvlIEtypes_channel_band_t *tlv;
	ENTER();
	*radar_chan = pstate_dfs->dfs_check_channel;
	if (pevent->event_len >= sizeof(MrvlIEtypes_channel_band_t)) {
		tlv = (MrvlIEtypes_channel_band_t *)&pevent->event_buf;
		*radar_chan = tlv->channel;
		dfs_check_bandwidth = tlv->bandcfg.chanWidth;
	} else {
		if (priv->bss_type == MLAN_BSS_TYPE_DFS) {
			*radar_chan = priv->chan_rep_req.chanNum;
			dfs_check_bandwidth =
				priv->chan_rep_req.bandcfg.chanWidth;
		}
	}
	*bandwidth = dfs_check_bandwidth;
	wlan_11h_add_all_dfs_timestamp(priv->adapter, DFS_TS_REPR_NOP_START,
				       *radar_chan, dfs_check_bandwidth);
	wlan_11h_set_chan_dfs_state(priv, *radar_chan, dfs_check_bandwidth,
				    DFS_UNAVAILABLE);
	PRINTM(MEVENT, "DFS: Radar detected on %d bw=%d\n", *radar_chan,
	       dfs_check_bandwidth);
	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)) {
			mlan_status ret = MLAN_STATUS_SUCCESS;
			ret = wlan_prepare_cmd(pmpriv,
					       HostCmd_CMD_APCMD_BSS_STOP,
					       HostCmd_ACT_GEN_SET, 0, MNULL,
					       MNULL);
			if (ret) {
				PRINTM(MMSG, "Error sending message to FW\n");
			}
		}
	}
}

/**
 *  @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;
	mlan_status ret = MLAN_STATUS_SUCCESS;

	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;

			ret = wlan_prepare_cmd(pmpriv,
					       HostCmd_CMD_APCMD_BSS_STOP,
					       HostCmd_ACT_GEN_SET, 0, MNULL,
					       MNULL);
			if (ret) {
				PRINTM(MERROR, "Error sending message to FW\n");
			}
		}
	}

	/* Start all old active BSSes */
	for (i = 0; i < pcount; i++) {
		pmpriv = priv_list[i];

		if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_UAP) {
			ret = wlan_prepare_cmd(pmpriv,
					       HostCmd_CMD_APCMD_BSS_START,
					       HostCmd_ACT_GEN_SET, 0, MNULL,
					       MNULL);
			if (ret) {
				PRINTM(MERROR, "Error sending message to FW\n");
			}
		}
	}
}
#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(mlan_private *pmpriv, 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(pmpriv, 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();
}

#ifdef UAP_SUPPORT
/**
 * @brief Get priv current index -- this is used to enter correct rdh_state
 * during radar handling
 *
 * @param pmpriv           Pointer to mlan_private
 * @param pstate_rdh       Pointer to radar detected state handler
 *
 * @return                 MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
 */
static mlan_status
wlan_11h_get_priv_curr_idx(mlan_private *pmpriv,
			   wlan_radar_det_hndlg_state_t *pstate_rdh)
{
	t_bool found = MFALSE;
	ENTER();

	PRINTM(MINFO, "%s:pmpriv =%p\n", __func__, pmpriv);
	while ((++pstate_rdh->priv_curr_idx) < pstate_rdh->priv_list_count) {
		if (pmpriv ==
		    pstate_rdh->priv_list[pstate_rdh->priv_curr_idx]) {
			PRINTM(MINFO, "found matching priv: priv_idx=%d\n",
			       pstate_rdh->priv_curr_idx);
			found = MTRUE;
			break;
		}
	}
	return (found == MTRUE) ? MLAN_STATUS_SUCCESS : MLAN_STATUS_FAILURE;
}
#endif

/**
 *  @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, HostCmd_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 &&
		       (pstate_rdh->priv_curr_idx < MLAN_MAX_BSS_NUM)) {
			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, HostCmd_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(
					pmpriv, &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, HostCmd_CMD_APCMD_BSS_START,
					HostCmd_ACT_GEN_SET, 0, MNULL, MNULL);
				break; /* leads to exit case */
			}
#endif
#ifdef STA_SUPPORT
			if (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) {
				/* Check previous state to find former
				 * Ad-hoc creator interface. Set new
				 * state to Starting, so it'll be seen
				 * as a DFS master. */
				if (pmpriv->adhoc_state_prev == ADHOC_STARTED) {
					pmpriv->adhoc_state = ADHOC_STARTING;
					pmpriv->adhoc_state_prev = ADHOC_IDLE;
				}
				if (wlan_11h_is_dfs_master(pmpriv)) {
					/* set new adhoc channel here */
					pmpriv->adhoc_channel =
						pstate_rdh->new_channel;
					if (wlan_11h_radar_detect_required(
						    pmpriv,
						    pstate_rdh->new_channel)) {
						/* Radar detection is required
						   for this channel, make sure
						   11h is activated in the
						   firmware */
						ret = wlan_11h_activate(
							pmpriv, MNULL, MTRUE);
						if (ret)
							break;
						ret = wlan_11h_config_master_radar_det(
							pmpriv, MTRUE);
						if (ret)
							break;
						ret = wlan_11h_check_update_radar_det_state(
							pmpriv);
						if (ret)
							break;
					}
					ret = wlan_prepare_cmd(
						pmpriv,
						HostCmd_CMD_802_11_AD_HOC_START,
						HostCmd_ACT_GEN_SET, 0, MNULL,
						&pmpriv->adhoc_last_start_ssid);
					break; /* leads to exit case */
				}

				/* NOTE:  DON'T reconnect slave STA intfs -
				 * infra/adhoc_joiner Do we want to return to
				 * same AP/network (on radar channel)? If want
				 * to connect back, depend on either:
				 *     1. driver's reassoc thread
				 *     2. wpa_supplicant, or other user-space
				 * app
				 */
			}
#endif
		}

		if (pstate_rdh->priv_curr_idx < pstate_rdh->priv_list_count ||
		    ret == MLAN_STATUS_FAILURE)
			break; /* EXIT CASE (for UAP) */
		/* else */
		pstate_rdh->priv_curr_idx = RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
		pstate_rdh->stage = RDH_RESTART_TRAFFIC;
		/* fall through */

	case RDH_RESTART_TRAFFIC:
		PRINTM(MCMD_D, "%s(): stage(%d)=%s\n", __func__,
		       pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]);
		/* remove custome ie */
		if (pmadapter->ecsa_enable) {
			mlan_ioctl_req *pioctl_req = MNULL;
			pstate_rdh->priv_curr_idx =
				RDH_STAGE_FIRST_ENTRY_PRIV_IDX;
			while ((++pstate_rdh->priv_curr_idx) <
			       pstate_rdh->priv_list_count) {
				pmpriv = pstate_rdh->priv_list
						 [pstate_rdh->priv_curr_idx];
				if (!wlan_11h_is_dfs_master(pmpriv))
					continue;
				ret = wlan_11h_prepare_custom_ie_chansw(
					pmadapter, &pioctl_req, MFALSE);
				if ((ret != MLAN_STATUS_SUCCESS) ||
				    !pioctl_req) {
					PRINTM(MERROR,
					       "%s(): Error in preparing CHAN_SW IE.\n",
					       __func__);
					break; /* EXIT CASE */
				}

				pioctl_req->bss_index = pmpriv->bss_index;

				ret = wlan_misc_ioctl_custom_ie_list(
					pmadapter, pioctl_req, MFALSE);
				if (ret != MLAN_STATUS_SUCCESS &&
				    ret != MLAN_STATUS_PENDING) {
					PRINTM(MERROR,
					       "%s(): Could not remove IE for priv=%p [priv_bss_idx=%d]!\n",
					       __func__, pmpriv,
					       pmpriv->bss_index);
					/* TODO: hiow to handle this error
					 * case??  ignore & continue? */
				}
				/* free ioctl buffer memory before we leave */
				pmadapter->callbacks.moal_mfree(
					pmadapter->pmoal_handle,
					(t_u8 *)pioctl_req);
			}
		}
		/* continue traffic for reactivated interfaces */
		PRINTM(MMSG,
		       "11h: Radar Detected - restarting host tx traffic.\n");
		for (i = 0; i < pstate_rdh->priv_list_count; i++)
			wlan_11h_tx_enable(pstate_rdh->priv_list[i]);

		pstate_rdh->stage = RDH_OFF; /* DONE! */
		PRINTM(MCMD_D, "%s(): finished - stage(%d)=%s\n", __func__,
		       pstate_rdh->stage, rdh_stage_str[pstate_rdh->stage]);

		break;

	default:
		pstate_rdh->stage = RDH_OFF; /* cancel RDH to unblock Tx packets
					      */
		break;
	}

	LEAVE();
	return ret;
}

/**
 *  @brief DFS Event Preprocessing.
 *  Operates directly on pmadapter variables.
 *
 *  1. EVENT_RADAR_DETECTED comes from firmware without specific
 *     bss_num/bss_type.  Find it an appropriate interface and
 *     update event_cause field in event_buf.
 *
 *  @param pmadapter    Pointer to mlan_adapter
 *
 *  @return    MLAN_STATUS_SUCCESS (update successful)
 *          or MLAN_STATUS_FAILURE (no change)
 */
mlan_status wlan_11h_dfs_event_preprocessing(mlan_adapter *pmadapter)
{
	mlan_status ret = MLAN_STATUS_FAILURE;
	mlan_private *pmpriv = MNULL;
	mlan_private *priv_list[MLAN_MAX_BSS_NUM] = {MNULL};

	ENTER();
	switch (pmadapter->event_cause & EVENT_ID_MASK) {
	case EVENT_RADAR_DETECTED:
		/* find active intf:  prefer dfs_master over dfs_slave */
		if (wlan_get_privs_by_two_cond(
			    pmadapter, wlan_11h_is_master_active_on_dfs_chan,
			    wlan_11h_is_dfs_master, MTRUE, priv_list)) {
			pmpriv = priv_list[0];
			PRINTM(MINFO, "%s: found dfs_master priv=%p\n",
			       __func__, pmpriv);
		} else if (wlan_get_privs_by_two_cond(
				   pmadapter,
				   wlan_11h_is_slave_active_on_dfs_chan,
				   wlan_11h_is_dfs_slave, MTRUE, priv_list)) {
			pmpriv = priv_list[0];
			PRINTM(MINFO, "%s: found dfs_slave priv=%p\n", __func__,
			       pmpriv);
		} else if (pmadapter->state_dfs.dfs_check_pending ||
			   pmadapter->state_dfs.dfs_check_channel) {
			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
 *  @param bandwidth    band width
 *
 *  @return  N/A
 */
void wlan_11h_set_dfs_check_chan(mlan_private *priv, t_u8 chan, t_u8 bandwidth)
{
	wlan_dfs_device_state_t *pstate_dfs = &priv->adapter->state_dfs;
	ENTER();
	pstate_dfs->dfs_check_channel = chan;
	pstate_dfs->dfs_check_bandwidth = bandwidth;
	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;
}

/**
 *  @brief 802.11h DFS mode 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_mode(pmlan_adapter pmadapter,
				    pmlan_ioctl_req pioctl_req)
{
	mlan_ds_11h_cfg *ds_11hcfg = MNULL;

	ENTER();

	ds_11hcfg = (mlan_ds_11h_cfg *)pioctl_req->pbuf;

	if (pioctl_req->action == MLAN_ACT_GET) {
		ds_11hcfg->param.dfs_mode = pmadapter->dfs_mode;
	} else {
		pmadapter->dfs_mode = ds_11hcfg->param.dfs_mode;
	}
	LEAVE();
	return MLAN_STATUS_SUCCESS;
}
