summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/sd8797/mlan/mlan_11d.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/sd8797/mlan/mlan_11d.c')
-rw-r--r--drivers/net/wireless/sd8797/mlan/mlan_11d.c1514
1 files changed, 1514 insertions, 0 deletions
diff --git a/drivers/net/wireless/sd8797/mlan/mlan_11d.c b/drivers/net/wireless/sd8797/mlan/mlan_11d.c
new file mode 100644
index 000000000000..809de4cc9ede
--- /dev/null
+++ b/drivers/net/wireless/sd8797/mlan/mlan_11d.c
@@ -0,0 +1,1514 @@
+/** @file mlan_11d.c
+ *
+ * @brief This file contains functions for 802.11D.
+ *
+ * Copyright (C) 2008-2011, Marvell International Ltd.
+ *
+ * This software file (the "File") is distributed by Marvell International
+ * Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ * (the "License"). You may use, redistribute and/or modify this File in
+ * accordance with the terms and conditions of the License, a copy of which
+ * is available by writing to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
+ * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
+ *
+ * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ * ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ * this warranty disclaimer.
+ *
+ */
+/********************************************************
+Change log:
+ 10/21/2008: initial version
+********************************************************/
+
+#include "mlan.h"
+#include "mlan_join.h"
+#include "mlan_util.h"
+#include "mlan_fw.h"
+#include "mlan_main.h"
+#include "mlan_11h.h"
+
+/********************************************************
+ Local Variables
+********************************************************/
+
+/** Region code mapping */
+typedef struct _region_code_mapping
+{
+ /** Region */
+ t_u8 region[COUNTRY_CODE_LEN];
+ /** Code */
+ t_u8 code;
+} region_code_mapping_t;
+
+/** Region code mapping table */
+static region_code_mapping_t region_code_mapping[] = {
+ {"US ", 0x10}, /* US FCC */
+ {"CA ", 0x20}, /* IC Canada */
+ {"SG ", 0x10}, /* Singapore */
+ {"EU ", 0x30}, /* ETSI */
+ {"AU ", 0x30}, /* Australia */
+ {"KR ", 0x30}, /* Republic Of Korea */
+ {"FR ", 0x32}, /* France */
+ {"JP ", 0x40}, /* Japan */
+ {"JP ", 0x41}, /* Japan */
+ {"CN ", 0x50}, /* China */
+ {"JP ", 0xFF}, /* Japan special */
+};
+
+#ifdef STA_SUPPORT
+/** Default Tx power */
+#define TX_PWR_DEFAULT 10
+
+/** Universal region code */
+#define UNIVERSAL_REGION_CODE 0xff
+
+/* Following two structures define the supported channels */
+/** Channels for 802.11b/g */
+static chan_freq_power_t channel_freq_power_UN_BG[] = {
+ {1, 2412, TX_PWR_DEFAULT},
+ {2, 2417, TX_PWR_DEFAULT},
+ {3, 2422, TX_PWR_DEFAULT},
+ {4, 2427, TX_PWR_DEFAULT},
+ {5, 2432, TX_PWR_DEFAULT},
+ {6, 2437, TX_PWR_DEFAULT},
+ {7, 2442, TX_PWR_DEFAULT},
+ {8, 2447, TX_PWR_DEFAULT},
+ {9, 2452, TX_PWR_DEFAULT},
+ {10, 2457, TX_PWR_DEFAULT},
+ {11, 2462, TX_PWR_DEFAULT},
+ {12, 2467, TX_PWR_DEFAULT},
+ {13, 2472, TX_PWR_DEFAULT},
+ {14, 2484, TX_PWR_DEFAULT}
+};
+
+/** Channels for 802.11a/j */
+static chan_freq_power_t channel_freq_power_UN_AJ[] = {
+ {8, 5040, TX_PWR_DEFAULT},
+ {12, 5060, TX_PWR_DEFAULT},
+ {16, 5080, TX_PWR_DEFAULT},
+ {34, 5170, TX_PWR_DEFAULT},
+ {38, 5190, TX_PWR_DEFAULT},
+ {42, 5210, TX_PWR_DEFAULT},
+ {46, 5230, TX_PWR_DEFAULT},
+ {36, 5180, TX_PWR_DEFAULT},
+ {40, 5200, TX_PWR_DEFAULT},
+ {44, 5220, TX_PWR_DEFAULT},
+ {48, 5240, TX_PWR_DEFAULT},
+ {52, 5260, TX_PWR_DEFAULT},
+ {56, 5280, TX_PWR_DEFAULT},
+ {60, 5300, TX_PWR_DEFAULT},
+ {64, 5320, TX_PWR_DEFAULT},
+ {100, 5500, TX_PWR_DEFAULT},
+ {104, 5520, TX_PWR_DEFAULT},
+ {108, 5540, TX_PWR_DEFAULT},
+ {112, 5560, TX_PWR_DEFAULT},
+ {116, 5580, TX_PWR_DEFAULT},
+ {120, 5600, TX_PWR_DEFAULT},
+ {124, 5620, TX_PWR_DEFAULT},
+ {128, 5640, TX_PWR_DEFAULT},
+ {132, 5660, TX_PWR_DEFAULT},
+ {136, 5680, TX_PWR_DEFAULT},
+ {140, 5700, TX_PWR_DEFAULT},
+ {149, 5745, TX_PWR_DEFAULT},
+ {153, 5765, TX_PWR_DEFAULT},
+ {157, 5785, TX_PWR_DEFAULT},
+ {161, 5805, TX_PWR_DEFAULT},
+ {165, 5825, TX_PWR_DEFAULT},
+/* {240, 4920, TX_PWR_DEFAULT},
+ {244, 4940, TX_PWR_DEFAULT},
+ {248, 4960, TX_PWR_DEFAULT},
+ {252, 4980, TX_PWR_DEFAULT},
+channels for 11J JP 10M channel gap */
+};
+#endif /* STA_SUPPORT */
+
+/********************************************************
+ Global Variables
+********************************************************/
+
+/********************************************************
+ Local Functions
+********************************************************/
+
+/**
+ * @brief This function converts region string to integer code
+ *
+ * @param pmadapter A pointer to mlan_adapter structure
+ * @param region Region string
+ * @param code [output] Region code
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static mlan_status
+wlan_11d_region_2_code(pmlan_adapter pmadapter, t_u8 * region, OUT t_u8 * code)
+{
+ t_u8 i;
+ t_u8 size = sizeof(region_code_mapping) / sizeof(region_code_mapping_t);
+
+ ENTER();
+
+ /* Look for code in mapping table */
+ for (i = 0; i < size; i++) {
+ if (!memcmp(pmadapter, region_code_mapping[i].region,
+ region, COUNTRY_CODE_LEN)) {
+ *code = region_code_mapping[i].code;
+ LEAVE();
+ return MLAN_STATUS_SUCCESS;
+ }
+ }
+
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+}
+
+#ifdef STA_SUPPORT
+/**
+ * @brief This function converts integer code to region string
+ *
+ * @param pmadapter A pointer to mlan_adapter structure
+ * @param code Region code
+ *
+ * @return Region string
+ */
+static t_u8 *
+wlan_11d_code_2_region(pmlan_adapter pmadapter, t_u8 code)
+{
+ t_u8 i;
+ t_u8 size = sizeof(region_code_mapping) / sizeof(region_code_mapping_t);
+
+ ENTER();
+
+ /* Look for code in mapping table */
+ for (i = 0; i < size; i++) {
+ if (region_code_mapping[i].code == code) {
+ LEAVE();
+ return (region_code_mapping[i].region);
+ }
+ }
+
+ LEAVE();
+ /* Default is US */
+ return (region_code_mapping[0].region);
+}
+
+/**
+ * @brief This function Checks if channel txpwr is learned from AP/IBSS
+ *
+ * @param pmadapter A pointer to mlan_adapter structure
+ * @param band Band number
+ * @param chan Channel number
+ * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
+ *
+ * @return MTRUE or MFALSE
+ */
+static t_u8
+wlan_11d_channel_known(pmlan_adapter pmadapter,
+ t_u8 band,
+ t_u8 chan, parsed_region_chan_11d_t * parsed_region_chan)
+{
+ chan_power_11d_t *pchan_pwr = parsed_region_chan->chan_pwr;
+ t_u8 no_of_chan = parsed_region_chan->no_of_chan;
+ t_u8 i = 0;
+ t_u8 ret = MFALSE;
+ mlan_private *pmpriv;
+
+ ENTER();
+
+ HEXDUMP("11D: parsed_region_chan", (t_u8 *) pchan_pwr,
+ sizeof(chan_power_11d_t) * no_of_chan);
+
+ /* Search channel */
+ for (i = 0; i < no_of_chan; i++) {
+ if (chan == pchan_pwr[i].chan && band == pchan_pwr[i].band) {
+ PRINTM(MINFO, "11D: Found channel:%d (band:%d)\n", chan, band);
+ ret = MTRUE;
+
+ if (band & BAND_A) {
+ /* If chan is a DFS channel, we need to see an AP on it */
+ if ((pmpriv = wlan_get_priv(pmadapter, MLAN_BSS_ROLE_STA))
+ && wlan_11h_radar_detect_required(pmpriv, chan)) {
+ PRINTM(MINFO, "11H: DFS channel %d, and ap_seen=%d\n",
+ chan, pchan_pwr[i].ap_seen);
+ ret = pchan_pwr[i].ap_seen;
+ }
+ }
+
+ LEAVE();
+ return ret;
+ }
+ }
+
+ PRINTM(MINFO, "11D: Could not find channel:%d (band:%d)\n", chan, band);
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function generates parsed_region_chan from Domain Info
+ * learned from AP/IBSS
+ *
+ * @param pmadapter Pointer to mlan_adapter structure
+ * @param region_chan Pointer to region_chan_t
+ * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
+ *
+ * @return N/A
+ */
+static t_void
+wlan_11d_generate_parsed_region_chan(pmlan_adapter pmadapter,
+ region_chan_t * region_chan,
+ parsed_region_chan_11d_t *
+ parsed_region_chan)
+{
+ chan_freq_power_t *cfp;
+ t_u8 i;
+
+ ENTER();
+
+ /* Region channel must be provided */
+ if (!region_chan) {
+ PRINTM(MWARN, "11D: region_chan is MNULL\n");
+ LEAVE();
+ return;
+ }
+
+ /* Get channel-frequency-power trio */
+ cfp = region_chan->pcfp;
+ if (!cfp) {
+ PRINTM(MWARN, "11D: cfp equal MNULL\n");
+ LEAVE();
+ return;
+ }
+
+ /* Set channel, band and power */
+ for (i = 0; i < region_chan->num_cfp; i++, cfp++) {
+ parsed_region_chan->chan_pwr[i].chan = (t_u8) cfp->channel;
+ parsed_region_chan->chan_pwr[i].band = region_chan->band;
+ parsed_region_chan->chan_pwr[i].pwr = (t_u8) cfp->max_tx_power;
+ PRINTM(MINFO, "11D: Chan[%d] Band[%d] Pwr[%d]\n",
+ parsed_region_chan->chan_pwr[i].chan,
+ parsed_region_chan->chan_pwr[i].band,
+ parsed_region_chan->chan_pwr[i].pwr);
+ }
+ parsed_region_chan->no_of_chan = region_chan->num_cfp;
+
+ PRINTM(MINFO, "11D: no_of_chan[%d]\n", parsed_region_chan->no_of_chan);
+
+ LEAVE();
+ return;
+}
+
+/**
+ * @brief This function generates domain_info from parsed_region_chan
+ *
+ * @param pmadapter Pointer to mlan_adapter structure
+ * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
+ *
+ * @return MLAN_STATUS_SUCCESS
+ */
+static mlan_status
+wlan_11d_generate_domain_info(pmlan_adapter pmadapter,
+ parsed_region_chan_11d_t * parsed_region_chan)
+{
+ t_u8 no_of_sub_band = 0;
+ t_u8 no_of_chan = parsed_region_chan->no_of_chan;
+ t_u8 no_of_parsed_chan = 0;
+ t_u8 first_chan = 0, next_chan = 0, max_pwr = 0;
+ t_u8 i, flag = MFALSE;
+ wlan_802_11d_domain_reg_t *domain_info = &pmadapter->domain_reg;
+
+ ENTER();
+
+ /* Should be only place that clear domain_reg (besides init) */
+ memset(pmadapter, domain_info, 0, sizeof(wlan_802_11d_domain_reg_t));
+
+ /* Set country code */
+ memcpy(pmadapter, domain_info->country_code,
+ wlan_11d_code_2_region(pmadapter, (t_u8) pmadapter->region_code),
+ COUNTRY_CODE_LEN);
+
+ PRINTM(MINFO, "11D: Number of channel = %d\n", no_of_chan);
+ HEXDUMP("11D: parsed_region_chan", (t_u8 *) parsed_region_chan,
+ sizeof(parsed_region_chan_11d_t));
+
+ /* Set channel and power */
+ for (i = 0; i < no_of_chan; i++) {
+ if (!flag) {
+ flag = MTRUE;
+ next_chan = first_chan = parsed_region_chan->chan_pwr[i].chan;
+ max_pwr = parsed_region_chan->chan_pwr[i].pwr;
+ no_of_parsed_chan = 1;
+ continue;
+ }
+
+ if (parsed_region_chan->chan_pwr[i].chan == next_chan + 1 &&
+ parsed_region_chan->chan_pwr[i].pwr == max_pwr) {
+ next_chan++;
+ no_of_parsed_chan++;
+ } else {
+ domain_info->sub_band[no_of_sub_band].first_chan = first_chan;
+ domain_info->sub_band[no_of_sub_band].no_of_chan =
+ no_of_parsed_chan;
+ domain_info->sub_band[no_of_sub_band].max_tx_pwr = max_pwr;
+ no_of_sub_band++;
+ no_of_parsed_chan = 1;
+ next_chan = first_chan = parsed_region_chan->chan_pwr[i].chan;
+ max_pwr = parsed_region_chan->chan_pwr[i].pwr;
+ }
+ }
+
+ if (flag) {
+ domain_info->sub_band[no_of_sub_band].first_chan = first_chan;
+ domain_info->sub_band[no_of_sub_band].no_of_chan = no_of_parsed_chan;
+ domain_info->sub_band[no_of_sub_band].max_tx_pwr = max_pwr;
+ no_of_sub_band++;
+ }
+ domain_info->no_of_sub_band = no_of_sub_band;
+
+ PRINTM(MINFO, "11D: Number of sub-band =0x%x\n",
+ domain_info->no_of_sub_band);
+ HEXDUMP("11D: domain_info", (t_u8 *) domain_info,
+ COUNTRY_CODE_LEN + 1 +
+ sizeof(IEEEtypes_SubbandSet_t) * no_of_sub_band);
+ LEAVE();
+ return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function updates the channel power table with the channel
+ * present in BSSDescriptor.
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ * @param pbss_desc A pointer to BSSDescriptor_t
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static mlan_status
+wlan_11d_update_chan_pwr_table(mlan_private * pmpriv,
+ BSSDescriptor_t * pbss_desc)
+{
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ parsed_region_chan_11d_t *parsed_region_chan =
+ &pmadapter->parsed_region_chan;
+ t_u16 i;
+ t_u8 tx_power = 0;
+ t_u8 chan;
+
+ ENTER();
+
+ chan = pbss_desc->phy_param_set.ds_param_set.current_chan;
+
+ tx_power = wlan_get_txpwr_of_chan_from_cfp(pmpriv, chan);
+
+ if (!tx_power) {
+ PRINTM(MMSG, "11D: Invalid channel\n");
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+ }
+
+ /* Check whether the channel already exists in channel power table of
+ parsed region */
+ for (i = 0; ((i < parsed_region_chan->no_of_chan) &&
+ (i < MAX_NO_OF_CHAN)); i++) {
+ if (parsed_region_chan->chan_pwr[i].chan == chan
+ && parsed_region_chan->chan_pwr[i].band == pbss_desc->bss_band) {
+ /* Channel already exists, use minimum of existing and tx_power */
+ parsed_region_chan->chan_pwr[i].pwr =
+ MIN(parsed_region_chan->chan_pwr[i].pwr, tx_power);
+ parsed_region_chan->chan_pwr[i].ap_seen = MTRUE;
+ break;
+ }
+ }
+
+ if (i == parsed_region_chan->no_of_chan && i < MAX_NO_OF_CHAN) {
+ /* Channel not found. Update the channel in the channel-power table */
+ parsed_region_chan->chan_pwr[i].chan = chan;
+ parsed_region_chan->chan_pwr[i].band = (t_u8) pbss_desc->bss_band;
+ parsed_region_chan->chan_pwr[i].pwr = tx_power;
+ parsed_region_chan->chan_pwr[i].ap_seen = MTRUE;
+ parsed_region_chan->no_of_chan++;
+ }
+
+ LEAVE();
+ return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function finds the no_of_chan-th chan after the first_chan
+ *
+ * @param pmadapter A pointer to mlan_adapter structure
+ * @param band Band
+ * @param first_chan First channel number
+ * @param no_of_chan Number of channels
+ * @param chan Pointer to the returned no_of_chan-th chan number
+ *
+ * @return MTRUE or MFALSE
+ */
+static t_u8
+wlan_11d_get_chan(pmlan_adapter pmadapter, t_u8 band, t_u8 first_chan,
+ t_u8 no_of_chan, t_u8 * chan)
+{
+ chan_freq_power_t *cfp = MNULL;
+ t_u8 i;
+ t_u8 cfp_no = 0;
+
+ ENTER();
+ if (band & (BAND_B | BAND_G | BAND_GN)) {
+ cfp = channel_freq_power_UN_BG;
+ cfp_no = sizeof(channel_freq_power_UN_BG) / sizeof(chan_freq_power_t);
+ } else if (band & (BAND_A | BAND_AN)) {
+ cfp = channel_freq_power_UN_AJ;
+ cfp_no = sizeof(channel_freq_power_UN_AJ) / sizeof(chan_freq_power_t);
+ } else {
+ PRINTM(MERROR, "11D: Wrong Band[%d]\n", band);
+ LEAVE();
+ return MFALSE;
+ }
+ /* Locate the first_chan */
+ for (i = 0; i < cfp_no; i++) {
+ if (cfp && ((cfp + i)->channel == first_chan)) {
+ PRINTM(MINFO, "11D: first_chan found\n");
+ break;
+ }
+ }
+
+ if (i < cfp_no) {
+ /* Check if beyond the boundary */
+ if (i + no_of_chan < cfp_no) {
+ /* Get first_chan + no_of_chan */
+ *chan = (t_u8) (cfp + i + no_of_chan)->channel;
+ LEAVE();
+ return MTRUE;
+ }
+ }
+
+ LEAVE();
+ return MFALSE;
+}
+
+/**
+ * @brief This function processes the country info present in BSSDescriptor.
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ * @param pbss_desc A pointer to BSSDescriptor_t
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static mlan_status
+wlan_11d_process_country_info(mlan_private * pmpriv,
+ BSSDescriptor_t * pbss_desc)
+{
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ parsed_region_chan_11d_t region_chan;
+ parsed_region_chan_11d_t *parsed_region_chan =
+ &pmadapter->parsed_region_chan;
+ t_u16 i, j, num_chan_added = 0;
+
+ ENTER();
+
+ memset(pmadapter, &region_chan, 0, sizeof(parsed_region_chan_11d_t));
+
+ /* Parse 11D country info */
+ if (wlan_11d_parse_domain_info(pmadapter, &pbss_desc->country_info,
+ (t_u8) pbss_desc->bss_band,
+ &region_chan) != MLAN_STATUS_SUCCESS) {
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+ }
+
+ if (parsed_region_chan->no_of_chan != 0) {
+ /*
+ * Check if the channel number already exists in the
+ * chan-power table of parsed_region_chan
+ */
+ for (i = 0; (i < region_chan.no_of_chan && i < MAX_NO_OF_CHAN); i++) {
+ for (j = 0; (j < parsed_region_chan->no_of_chan &&
+ j < MAX_NO_OF_CHAN); j++) {
+ /*
+ * Channel already exists, update the tx power with new tx
+ * power, since country IE is valid here.
+ */
+ if (region_chan.chan_pwr[i].chan ==
+ parsed_region_chan->chan_pwr[j].chan &&
+ region_chan.chan_pwr[i].band ==
+ parsed_region_chan->chan_pwr[j].band) {
+ parsed_region_chan->chan_pwr[j].pwr =
+ region_chan.chan_pwr[i].pwr;
+ break;
+ }
+ }
+
+ if (j == parsed_region_chan->no_of_chan && j < MAX_NO_OF_CHAN) {
+ /*
+ * Channel does not exist in the channel power table,
+ * update this new chan and tx_power to the channel power table
+ */
+ parsed_region_chan->chan_pwr[parsed_region_chan->no_of_chan +
+ num_chan_added].chan =
+ region_chan.chan_pwr[i].chan;
+ parsed_region_chan->chan_pwr[parsed_region_chan->no_of_chan +
+ num_chan_added].band =
+ region_chan.chan_pwr[i].band;
+ parsed_region_chan->chan_pwr[parsed_region_chan->no_of_chan +
+ num_chan_added].pwr =
+ region_chan.chan_pwr[i].pwr;
+ parsed_region_chan->chan_pwr[parsed_region_chan->no_of_chan +
+ num_chan_added].ap_seen = MFALSE;
+ num_chan_added++;
+ }
+ }
+ parsed_region_chan->no_of_chan += num_chan_added;
+ } else {
+ /* Parsed region is empty, copy the first one */
+ memcpy(pmadapter, parsed_region_chan,
+ &region_chan, sizeof(parsed_region_chan_11d_t));
+ }
+
+ LEAVE();
+ return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This helper function copies chan_power_11d_t element
+ *
+ * @param chan_dst Pointer to destination of chan_power
+ * @param chan_src Pointer to source of chan_power
+ *
+ * @return N/A
+ */
+static t_void
+wlan_11d_copy_chan_power(chan_power_11d_t * chan_dst,
+ chan_power_11d_t * chan_src)
+{
+ ENTER();
+
+ chan_dst->chan = chan_src->chan;
+ chan_dst->band = chan_src->band;
+ chan_dst->pwr = chan_src->pwr;
+ chan_dst->ap_seen = chan_src->ap_seen;
+
+ LEAVE();
+ return;
+}
+
+/**
+ * @brief This function sorts parsed_region_chan in ascending
+ * channel number.
+ *
+ * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
+ *
+ * @return N/A
+ */
+static t_void
+wlan_11d_sort_parsed_region_chan(parsed_region_chan_11d_t * parsed_region_chan)
+{
+ int i, j;
+ chan_power_11d_t temp;
+ chan_power_11d_t *pchan_power = parsed_region_chan->chan_pwr;
+
+ ENTER();
+
+ PRINTM(MINFO, "11D: Number of channel = %d\n",
+ parsed_region_chan->no_of_chan);
+
+ // Use insertion sort method
+ for (i = 1; i < parsed_region_chan->no_of_chan; i++) {
+ wlan_11d_copy_chan_power(&temp, pchan_power + i);
+ for (j = i; j > 0 && (pchan_power + j - 1)->chan > temp.chan; j--)
+ wlan_11d_copy_chan_power(pchan_power + j, pchan_power + j - 1);
+ wlan_11d_copy_chan_power(pchan_power + j, &temp);
+ }
+
+ HEXDUMP("11D: parsed_region_chan", (t_u8 *) parsed_region_chan,
+ sizeof(parsed_region_chan_11d_t));
+
+ LEAVE();
+ return;
+}
+#endif /* STA_SUPPORT */
+
+/**
+ * @brief This function sends domain info to FW
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ * @param pioctl_buf A pointer to MLAN IOCTL Request buffer
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+static mlan_status
+wlan_11d_send_domain_info(mlan_private * pmpriv, t_void * pioctl_buf)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ /* Send cmd to FW to set domain info */
+ ret = wlan_prepare_cmd(pmpriv,
+ HostCmd_CMD_802_11D_DOMAIN_INFO,
+ HostCmd_ACT_GEN_SET,
+ 0, (t_void *) pioctl_buf, MNULL);
+ if (ret)
+ PRINTM(MERROR, "11D: Failed to download domain Info\n");
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function overwrites domain_info
+ *
+ * @param pmadapter Pointer to mlan_adapter structure
+ * @param band Intended operating band
+ * @param country_code Intended country code
+ * @param num_sub_band Count of tuples in list below
+ * @param sub_band_list List of sub_band tuples
+ *
+ * @return MLAN_STATUS_SUCCESS
+ */
+static mlan_status
+wlan_11d_set_domain_info(mlan_private * pmpriv,
+ t_u8 band,
+ t_u8 country_code[COUNTRY_CODE_LEN],
+ t_u8 num_sub_band,
+ IEEEtypes_SubbandSet_t * sub_band_list)
+{
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ wlan_802_11d_domain_reg_t *pdomain = &pmadapter->domain_reg;
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ memset(pmadapter, pdomain, 0, sizeof(wlan_802_11d_domain_reg_t));
+ memcpy(pmadapter, pdomain->country_code, country_code, COUNTRY_CODE_LEN);
+ pdomain->band = band;
+ pdomain->no_of_sub_band = num_sub_band;
+ memcpy(pmadapter, pdomain->sub_band, sub_band_list,
+ MIN(MRVDRV_MAX_SUBBAND_802_11D,
+ num_sub_band) * sizeof(IEEEtypes_SubbandSet_t));
+
+ LEAVE();
+ return ret;
+}
+
+/********************************************************
+ Global functions
+********************************************************/
+
+/**
+ * @brief This function gets if priv is a station (STA)
+ *
+ * @param pmpriv Pointer to mlan_private structure
+ *
+ * @return MTRUE or MFALSE
+ */
+t_bool
+wlan_is_station(mlan_private * pmpriv)
+{
+ ENTER();
+ LEAVE();
+ return (GET_BSS_ROLE(pmpriv) == MLAN_BSS_ROLE_STA) ? MTRUE : MFALSE;
+}
+
+/**
+ * @brief This function gets if 11D is enabled
+ *
+ * @param pmpriv Pointer to mlan_private structure
+ *
+ * @return MTRUE or MFALSE
+ */
+t_bool
+wlan_11d_is_enabled(mlan_private * pmpriv)
+{
+ ENTER();
+ LEAVE();
+ return (pmpriv->state_11d.enable_11d == ENABLE_11D) ? MTRUE : MFALSE;
+}
+
+/**
+ * @brief Initialize interface variable for 11D
+ *
+ * @param pmpriv Pointer to mlan_private structure
+ *
+ * @return N/A
+ */
+t_void
+wlan_11d_priv_init(mlan_private * pmpriv)
+{
+ wlan_802_11d_state_t *state = &pmpriv->state_11d;
+
+ ENTER();
+
+ /* Start in disabled mode */
+ state->enable_11d = DISABLE_11D;
+ if (!pmpriv->adapter->init_para.cfg_11d)
+ state->user_enable_11d = DEFAULT_11D_STATE;
+ else
+ state->user_enable_11d =
+ (pmpriv->adapter->init_para.cfg_11d == MLAN_INIT_PARA_DISABLED) ?
+ DISABLE_11D : ENABLE_11D;
+
+ LEAVE();
+ return;
+}
+
+/**
+ * @brief Initialize device variable for 11D
+ *
+ * @param pmadapter Pointer to mlan_adapter structure
+ *
+ * @return N/A
+ */
+t_void
+wlan_11d_init(mlan_adapter * pmadapter)
+{
+ ENTER();
+
+#ifdef STA_SUPPORT
+ memset(pmadapter, &(pmadapter->parsed_region_chan), 0,
+ sizeof(parsed_region_chan_11d_t));
+ memset(pmadapter, &(pmadapter->universal_channel), 0,
+ sizeof(region_chan_t));
+#endif
+ memset(pmadapter, &(pmadapter->domain_reg), 0,
+ sizeof(wlan_802_11d_domain_reg_t));
+
+ LEAVE();
+ return;
+}
+
+/**
+ * @brief This function enable/disable 11D
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ * @param pioctl_buf A pointer to MLAN IOCTL Request buffer
+ * @param flag 11D status
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+wlan_11d_enable(mlan_private * pmpriv, t_void * pioctl_buf, state_11d_t flag)
+{
+#ifdef STA_SUPPORT
+ mlan_adapter *pmadapter = pmpriv->adapter;
+#endif
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ state_11d_t enable = flag;
+
+ ENTER();
+
+ /* Send cmd to FW to enable/disable 11D function */
+ ret = wlan_prepare_cmd(pmpriv,
+ HostCmd_CMD_802_11_SNMP_MIB,
+ HostCmd_ACT_GEN_SET,
+ Dot11D_i, (t_void *) pioctl_buf, &enable);
+
+ if (ret) {
+ PRINTM(MERROR, "11D: Failed to %s 11D\n",
+ (flag) ? "enable" : "disable");
+ }
+#ifdef STA_SUPPORT
+ else {
+ /* clear parsed table regardless of flag */
+ memset(pmadapter, &(pmadapter->parsed_region_chan), 0,
+ sizeof(parsed_region_chan_11d_t));
+ }
+#endif
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function implements command CMD_802_11D_DOMAIN_INFO
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ * @param pcmd A pointer to HostCmd_DS_COMMAND structure of
+ * command buffer
+ * @param cmd_action Command action
+ *
+ * @return MLAN_STATUS_SUCCESS
+ */
+mlan_status
+wlan_cmd_802_11d_domain_info(mlan_private * pmpriv,
+ HostCmd_DS_COMMAND * pcmd, t_u16 cmd_action)
+{
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ HostCmd_DS_802_11D_DOMAIN_INFO *pdomain_info = &pcmd->params.domain_info;
+ MrvlIEtypes_DomainParamSet_t *domain = &pdomain_info->domain;
+ t_u8 no_of_sub_band = pmadapter->domain_reg.no_of_sub_band;
+
+ ENTER();
+
+ PRINTM(MINFO, "11D: number of sub-band=0x%x\n", no_of_sub_band);
+
+ pcmd->command = wlan_cpu_to_le16(HostCmd_CMD_802_11D_DOMAIN_INFO);
+ pdomain_info->action = wlan_cpu_to_le16(cmd_action);
+ if (cmd_action == HostCmd_ACT_GEN_GET) {
+ /* Dump domain info */
+ pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) + S_DS_GEN);
+ HEXDUMP("11D: 802_11D_DOMAIN_INFO", (t_u8 *) pcmd,
+ wlan_le16_to_cpu(pcmd->size));
+ LEAVE();
+ return MLAN_STATUS_SUCCESS;
+ }
+
+ /* Set domain info fields */
+ domain->header.type = wlan_cpu_to_le16(TLV_TYPE_DOMAIN);
+ memcpy(pmadapter, domain->country_code,
+ pmadapter->domain_reg.country_code, sizeof(domain->country_code));
+
+ domain->header.len = ((no_of_sub_band * sizeof(IEEEtypes_SubbandSet_t)) +
+ sizeof(domain->country_code));
+
+ if (no_of_sub_band) {
+ memcpy(pmadapter, domain->sub_band,
+ pmadapter->domain_reg.sub_band,
+ MIN(MRVDRV_MAX_SUBBAND_802_11D,
+ no_of_sub_band) * sizeof(IEEEtypes_SubbandSet_t));
+
+ pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) +
+ domain->header.len +
+ sizeof(MrvlIEtypesHeader_t) + S_DS_GEN);
+ } else {
+ pcmd->size = wlan_cpu_to_le16(sizeof(pdomain_info->action) + S_DS_GEN);
+ }
+ domain->header.len = wlan_cpu_to_le16(domain->header.len);
+
+ HEXDUMP("11D: 802_11D_DOMAIN_INFO", (t_u8 *) pcmd,
+ wlan_le16_to_cpu(pcmd->size));
+
+ LEAVE();
+ return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function handle response of CMD_802_11D_DOMAIN_INFO
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ * @param resp Pointer to command response buffer
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+wlan_ret_802_11d_domain_info(mlan_private * pmpriv, HostCmd_DS_COMMAND * resp)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ HostCmd_DS_802_11D_DOMAIN_INFO_RSP *domain_info =
+ &resp->params.domain_info_resp;
+ MrvlIEtypes_DomainParamSet_t *domain = &domain_info->domain;
+ t_u16 action = wlan_le16_to_cpu(domain_info->action);
+ t_u8 no_of_sub_band = 0;
+
+ ENTER();
+
+ /* Dump domain info response data */
+ HEXDUMP("11D: DOMAIN Info Rsp Data", (t_u8 *) resp, resp->size);
+
+ no_of_sub_band =
+ (t_u8) ((wlan_le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN)
+ / sizeof(IEEEtypes_SubbandSet_t));
+
+ PRINTM(MINFO, "11D Domain Info Resp: number of sub-band=%d\n",
+ no_of_sub_band);
+
+ if (no_of_sub_band > MRVDRV_MAX_SUBBAND_802_11D) {
+ PRINTM(MWARN, "11D: Invalid number of subbands %d returned!!\n",
+ no_of_sub_band);
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+ }
+
+ switch (action) {
+ case HostCmd_ACT_GEN_SET: /* Proc Set Action */
+ break;
+ case HostCmd_ACT_GEN_GET:
+ break;
+ default:
+ PRINTM(MERROR, "11D: Invalid Action:%d\n", domain_info->action);
+ ret = MLAN_STATUS_FAILURE;
+ break;
+ }
+
+ LEAVE();
+ return ret;
+}
+
+#ifdef STA_SUPPORT
+/**
+ * @brief This function parses country information for region channel
+ *
+ * @param pmadapter Pointer to mlan_adapter structure
+ * @param country_info Country information
+ * @param band Chan band
+ * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+wlan_11d_parse_domain_info(pmlan_adapter pmadapter,
+ IEEEtypes_CountryInfoFullSet_t * country_info,
+ t_u8 band,
+ parsed_region_chan_11d_t * parsed_region_chan)
+{
+ t_u8 no_of_sub_band, no_of_chan;
+ t_u8 last_chan, first_chan, cur_chan = 0;
+ t_u8 idx = 0;
+ t_u8 j, i;
+
+ ENTER();
+
+ /*
+ * Validation Rules:
+ * 1. Valid Region Code
+ * 2. First Chan increment
+ * 3. Channel range no overlap
+ * 4. Channel is valid?
+ * 5. Channel is supported by Region?
+ * 6. Others
+ */
+
+ HEXDUMP("country_info", (t_u8 *) country_info, 30);
+
+ /* Step 1: Check region_code */
+ if (!(*(country_info->country_code)) ||
+ (country_info->len <= COUNTRY_CODE_LEN)) {
+ /* No region info or wrong region info: treat as no 11D info */
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+ }
+
+ no_of_sub_band = (country_info->len - COUNTRY_CODE_LEN) /
+ sizeof(IEEEtypes_SubbandSet_t);
+
+ for (j = 0, last_chan = 0; j < no_of_sub_band; j++) {
+
+ if (country_info->sub_band[j].first_chan <= last_chan) {
+ /* Step2&3: Check First Chan Num increment and no overlap */
+ PRINTM(MINFO, "11D: Chan[%d>%d] Overlap\n",
+ country_info->sub_band[j].first_chan, last_chan);
+ continue;
+ }
+
+ first_chan = country_info->sub_band[j].first_chan;
+ no_of_chan = country_info->sub_band[j].no_of_chan;
+
+ for (i = 0; idx < MAX_NO_OF_CHAN && i < no_of_chan; i++) {
+ /* Step 4 : Channel is supported? */
+ if (wlan_11d_get_chan(pmadapter, band, first_chan, i, &cur_chan) ==
+ MFALSE) {
+ /* Chan is not found in UN table */
+ PRINTM(MWARN, "11D: channel is not supported: %d\n", i);
+ break;
+ }
+
+ last_chan = cur_chan;
+
+ /* Step 5: We don't need to check if cur_chan is supported by mrvl
+ in region */
+ parsed_region_chan->chan_pwr[idx].chan = cur_chan;
+ parsed_region_chan->chan_pwr[idx].band = band;
+ parsed_region_chan->chan_pwr[idx].pwr =
+ country_info->sub_band[j].max_tx_pwr;
+ idx++;
+ }
+
+ /* Step 6: Add other checking if any */
+ }
+
+ parsed_region_chan->no_of_chan = idx;
+
+ PRINTM(MINFO, "11D: number of channel=0x%x\n",
+ parsed_region_chan->no_of_chan);
+ HEXDUMP("11D: parsed_region_chan", (t_u8 *) parsed_region_chan->chan_pwr,
+ sizeof(chan_power_11d_t) * idx);
+
+ LEAVE();
+ return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function converts channel to frequency
+ *
+ * @param pmadapter A pointer to mlan_adapter structure
+ * @param chan Channel number
+ * @param band Band
+ *
+ * @return Channel frequency
+ */
+t_u32
+wlan_11d_chan_2_freq(pmlan_adapter pmadapter, t_u8 chan, t_u8 band)
+{
+ chan_freq_power_t *cf;
+ t_u16 cnt;
+ t_u16 i;
+ t_u32 freq = 0;
+
+ ENTER();
+
+ /* Get channel-frequency-power trios */
+ if (band & (BAND_A | BAND_AN)) {
+ cf = channel_freq_power_UN_AJ;
+ cnt = sizeof(channel_freq_power_UN_AJ) / sizeof(chan_freq_power_t);
+ } else {
+ cf = channel_freq_power_UN_BG;
+ cnt = sizeof(channel_freq_power_UN_BG) / sizeof(chan_freq_power_t);
+ }
+
+ /* Locate channel and return corresponding frequency */
+ for (i = 0; i < cnt; i++) {
+ if (chan == cf[i].channel)
+ freq = cf[i].freq;
+ }
+
+ LEAVE();
+ return freq;
+}
+
+/**
+ * @brief This function setups scan channels
+ *
+ * @param pmpriv Pointer to mlan_private structure
+ * @param band Band
+ *
+ * @return MLAN_STATUS_SUCCESS
+ */
+mlan_status
+wlan_11d_set_universaltable(mlan_private * pmpriv, t_u8 band)
+{
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ t_u16 size = sizeof(chan_freq_power_t);
+ t_u16 i = 0;
+
+ ENTER();
+
+ memset(pmadapter, pmadapter->universal_channel, 0,
+ sizeof(pmadapter->universal_channel));
+
+ if (band & (BAND_B | BAND_G | BAND_GN))
+ /* If band B, G or N */
+ {
+ /* Set channel-frequency-power */
+ pmadapter->universal_channel[i].num_cfp =
+ (t_u8) (sizeof(channel_freq_power_UN_BG) / size);
+ PRINTM(MINFO, "11D: BG-band num_cfp=%d\n",
+ pmadapter->universal_channel[i].num_cfp);
+
+ pmadapter->universal_channel[i].pcfp = channel_freq_power_UN_BG;
+ pmadapter->universal_channel[i].valid = MTRUE;
+
+ /* Set region code */
+ pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
+
+ /* Set band */
+ if (band & BAND_GN)
+ pmadapter->universal_channel[i].band = BAND_G;
+ else
+ pmadapter->universal_channel[i].band =
+ (band & BAND_G) ? BAND_G : BAND_B;
+ i++;
+ }
+
+ if (band & (BAND_A | BAND_AN)) {
+ /* If band A */
+
+ /* Set channel-frequency-power */
+ pmadapter->universal_channel[i].num_cfp =
+ sizeof(channel_freq_power_UN_AJ) / size;
+ PRINTM(MINFO, "11D: AJ-band num_cfp=%d\n",
+ pmadapter->universal_channel[i].num_cfp);
+
+ pmadapter->universal_channel[i].pcfp = channel_freq_power_UN_AJ;
+
+ pmadapter->universal_channel[i].valid = MTRUE;
+
+ /* Set region code */
+ pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
+
+ /* Set band */
+ pmadapter->universal_channel[i].band = BAND_A;
+ i++;
+ }
+
+ LEAVE();
+ return MLAN_STATUS_SUCCESS;
+}
+
+/**
+ * @brief This function calculates the scan type for channels
+ *
+ * @param pmadapter A pointer to mlan_adapter structure
+ * @param band Band number
+ * @param chan Chan number
+ * @param parsed_region_chan Pointer to parsed_region_chan_11d_t
+ *
+ * @return PASSIVE if chan is unknown; ACTIVE if chan is known
+ */
+t_u8
+wlan_11d_get_scan_type(pmlan_adapter pmadapter,
+ t_u8 band,
+ t_u8 chan, parsed_region_chan_11d_t * parsed_region_chan)
+{
+ t_u8 scan_type = MLAN_SCAN_TYPE_PASSIVE;
+
+ ENTER();
+
+ if (wlan_11d_channel_known(pmadapter, band, chan, parsed_region_chan)) {
+ /* Channel found */
+ PRINTM(MINFO, "11D: Channel found and doing Active Scan\n");
+ scan_type = MLAN_SCAN_TYPE_ACTIVE;
+ } else
+ PRINTM(MINFO, "11D: Channel not found and doing Passive Scan\n");
+
+ LEAVE();
+ return scan_type;
+}
+
+/**
+ * @brief This function clears the parsed region table, if 11D is enabled
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+wlan_11d_clear_parsedtable(mlan_private * pmpriv)
+{
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+
+ ENTER();
+
+ if (wlan_11d_is_enabled(pmpriv))
+ memset(pmadapter, &(pmadapter->parsed_region_chan), 0,
+ sizeof(parsed_region_chan_11d_t));
+ else
+ ret = MLAN_STATUS_FAILURE;
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function generates 11D info from user specified regioncode
+ * and download to FW
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ * @param band Band to create
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+wlan_11d_create_dnld_countryinfo(mlan_private * pmpriv, t_u8 band)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ region_chan_t *region_chan;
+ parsed_region_chan_11d_t parsed_region_chan;
+ t_u8 j;
+
+ ENTER();
+
+ /* Only valid if 11D is enabled */
+ if (wlan_11d_is_enabled(pmpriv)) {
+
+ PRINTM(MINFO, "11D: Band[%d]\n", band);
+
+ /* Update parsed_region_chan; download domain info to FW */
+
+ /* Find region channel */
+ for (j = 0; j < MAX_REGION_CHANNEL_NUM; j++) {
+ region_chan = &pmadapter->region_channel[j];
+
+ PRINTM(MINFO, "11D: [%d] region_chan->Band[%d]\n", j,
+ region_chan->band);
+
+ if (!region_chan || !region_chan->valid || !region_chan->pcfp)
+ continue;
+ switch (region_chan->band) {
+ case BAND_A:
+ switch (band) {
+ case BAND_A:
+ case BAND_AN:
+ case BAND_A | BAND_AN:
+ break;
+ default:
+ continue;
+ }
+ break;
+ case BAND_B:
+ case BAND_G:
+ switch (band) {
+ case BAND_B:
+ case BAND_G:
+ case BAND_G | BAND_B:
+ case BAND_GN:
+ case BAND_G | BAND_GN:
+ case BAND_B | BAND_G | BAND_GN:
+ break;
+ default:
+ continue;
+ }
+ break;
+ default:
+ continue;
+ }
+ break;
+ }
+
+ /* Check if region channel found */
+ if (j >= MAX_REGION_CHANNEL_NUM) {
+ PRINTM(MERROR, "11D: region_chan not found. Band[%d]\n", band);
+ LEAVE();
+ return MLAN_STATUS_FAILURE;
+ }
+
+ /* Generate parsed region channel info from region channel */
+ memset(pmadapter, &parsed_region_chan, 0,
+ sizeof(parsed_region_chan_11d_t));
+ wlan_11d_generate_parsed_region_chan(pmadapter, region_chan,
+ &parsed_region_chan);
+
+ /* Generate domain info from parsed region channel info */
+ wlan_11d_generate_domain_info(pmadapter, &parsed_region_chan);
+
+ /* Set domain info */
+ ret = wlan_11d_send_domain_info(pmpriv, MNULL);
+ if (ret) {
+ PRINTM(MERROR, "11D: Error sending domain info to FW\n");
+ }
+ }
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function parses country info from AP and
+ * download country info to FW
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ * @param pbss_desc A pointer to BSS descriptor
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+wlan_11d_parse_dnld_countryinfo(mlan_private * pmpriv,
+ BSSDescriptor_t * pbss_desc)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ parsed_region_chan_11d_t region_chan;
+ parsed_region_chan_11d_t bssdesc_region_chan;
+ t_u32 i, j;
+
+ ENTER();
+
+ /* Only valid if 11D is enabled */
+ if (wlan_11d_is_enabled(pmpriv)) {
+
+ memset(pmadapter, &region_chan, 0, sizeof(parsed_region_chan_11d_t));
+ memset(pmadapter, &bssdesc_region_chan, 0,
+ sizeof(parsed_region_chan_11d_t));
+
+ memcpy(pmadapter, &region_chan,
+ &pmadapter->parsed_region_chan,
+ sizeof(parsed_region_chan_11d_t));
+
+ if (pbss_desc) {
+ /* Parse domain info if available */
+ ret =
+ wlan_11d_parse_domain_info(pmadapter, &pbss_desc->country_info,
+ (t_u8) pbss_desc->bss_band,
+ &bssdesc_region_chan);
+
+ if (ret == MLAN_STATUS_SUCCESS) {
+ /* Update the channel-power table */
+ for (i = 0; ((i < bssdesc_region_chan.no_of_chan)
+ && (i < MAX_NO_OF_CHAN)); i++) {
+
+ for (j = 0; ((j < region_chan.no_of_chan)
+ && (j < MAX_NO_OF_CHAN)); j++) {
+ /*
+ * Channel already exists, use minimum of existing
+ * tx power and tx_power received from
+ * country info of the current AP
+ */
+ if (region_chan.chan_pwr[i].chan ==
+ bssdesc_region_chan.chan_pwr[j].chan &&
+ region_chan.chan_pwr[i].band ==
+ bssdesc_region_chan.chan_pwr[j].band) {
+ region_chan.chan_pwr[j].pwr =
+ MIN(region_chan.chan_pwr[j].pwr,
+ bssdesc_region_chan.chan_pwr[i].pwr);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Generate domain info */
+ wlan_11d_generate_domain_info(pmadapter, &region_chan);
+
+ /* Set domain info */
+ ret = wlan_11d_send_domain_info(pmpriv, MNULL);
+ if (ret) {
+ PRINTM(MERROR, "11D: Error sending domain info to FW\n");
+ }
+ }
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function prepares domain info from scan table and
+ * downloads the domain info command to the FW.
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+wlan_11d_prepare_dnld_domain_info_cmd(mlan_private * pmpriv)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ IEEEtypes_CountryInfoFullSet_t *pcountry_full = MNULL;
+ t_u32 idx;
+
+ ENTER();
+
+ /* Only valid if 11D is enabled */
+ if (wlan_11d_is_enabled(pmpriv) && pmadapter->num_in_scan_table != 0) {
+ for (idx = 0; idx < pmadapter->num_in_scan_table; idx++) {
+ pcountry_full = &pmadapter->pscan_table[idx].country_info;
+
+ ret =
+ wlan_11d_update_chan_pwr_table(pmpriv,
+ &pmadapter->pscan_table[idx]);
+
+ if (*(pcountry_full->country_code) != 0 &&
+ (pcountry_full->len > COUNTRY_CODE_LEN)) {
+ /* Country info found in the BSS Descriptor */
+ ret =
+ wlan_11d_process_country_info(pmpriv,
+ &pmadapter->pscan_table[idx]);
+ }
+ }
+
+ /* Sort parsed_region_chan in ascending channel number */
+ wlan_11d_sort_parsed_region_chan(&pmadapter->parsed_region_chan);
+
+ /* Check if connected */
+ if (pmpriv->media_connected == MTRUE) {
+ ret =
+ wlan_11d_parse_dnld_countryinfo(pmpriv,
+ &pmpriv->curr_bss_params.
+ bss_descriptor);
+ } else {
+ ret = wlan_11d_parse_dnld_countryinfo(pmpriv, MNULL);
+ }
+ }
+
+ LEAVE();
+ return ret;
+}
+
+/**
+ * @brief This function sets up domain_reg and downloads CMD to FW
+ *
+ * @param pmadapter A pointer to mlan_adapter structure
+ * @param pioctl_req Pointer to the IOCTL request buffer
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+wlan_11d_cfg_domain_info(IN pmlan_adapter pmadapter,
+ IN mlan_ioctl_req * pioctl_req)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ mlan_private *pmpriv = pmadapter->priv[pioctl_req->bss_index];
+ mlan_ds_11d_domain_info *domain_info = MNULL;
+ mlan_ds_11d_cfg *cfg_11d = MNULL;
+ t_u8 region_code = 0;
+
+ ENTER();
+
+ cfg_11d = (mlan_ds_11d_cfg *) pioctl_req->pbuf;
+ domain_info = &cfg_11d->param.domain_info;
+
+ /* Update region code and table based on country code */
+ if (wlan_11d_region_2_code(pmadapter, domain_info->country_code,
+ &region_code) == MLAN_STATUS_SUCCESS) {
+ pmadapter->region_code = region_code;
+ ret = wlan_set_regiontable(pmpriv, region_code, pmadapter->fw_bands);
+ if (ret != MLAN_STATUS_SUCCESS)
+ goto done;
+ }
+
+ wlan_11d_set_domain_info(pmpriv, domain_info->band,
+ domain_info->country_code,
+ domain_info->no_of_sub_band,
+ (IEEEtypes_SubbandSet_t *) domain_info->sub_band);
+ ret = wlan_11d_send_domain_info(pmpriv, pioctl_req);
+
+ if (ret == MLAN_STATUS_SUCCESS)
+ ret = MLAN_STATUS_PENDING;
+
+ done:
+ LEAVE();
+ return ret;
+}
+#endif /* STA_SUPPORT */
+
+#if defined(UAP_SUPPORT)
+/**
+ * @brief This function handles domain info data from UAP interface.
+ * Checks conditions, sets up domain_reg, then downloads CMD.
+ *
+ * @param pmpriv A pointer to mlan_private structure
+ * @param band Band interface is operating on
+ * @param domain_tlv Pointer to domain_info tlv
+ * @param pioctl_buf Pointer to the IOCTL buffer
+ *
+ * @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
+ */
+mlan_status
+wlan_11d_handle_uap_domain_info(mlan_private * pmpriv,
+ t_u8 band,
+ t_u8 * domain_tlv, t_void * pioctl_buf)
+{
+ mlan_status ret = MLAN_STATUS_SUCCESS;
+ mlan_adapter *pmadapter = pmpriv->adapter;
+ MrvlIEtypes_DomainParamSet_t *pdomain_tlv;
+ t_u8 num_sub_band = 0;
+ t_u8 region_code = 0;
+
+ ENTER();
+
+ pdomain_tlv = (MrvlIEtypes_DomainParamSet_t *) domain_tlv;
+
+ // update region code & table based on country string
+ if (wlan_11d_region_2_code(pmadapter, pdomain_tlv->country_code,
+ &region_code) == MLAN_STATUS_SUCCESS) {
+ pmadapter->region_code = region_code;
+ ret = wlan_set_regiontable(pmpriv, region_code, pmadapter->fw_bands);
+ }
+
+ num_sub_band = ((pdomain_tlv->header.len - COUNTRY_CODE_LEN) /
+ sizeof(IEEEtypes_SubbandSet_t));
+
+ // TODO: don't just clobber pmadapter->domain_reg.
+ // Add some checking or merging between STA & UAP domain_info
+
+ wlan_11d_set_domain_info(pmpriv, band, pdomain_tlv->country_code,
+ num_sub_band, pdomain_tlv->sub_band);
+ ret = wlan_11d_send_domain_info(pmpriv, pioctl_buf);
+
+ LEAVE();
+ return ret;
+}
+#endif