blob: 445d84f5877632d36eb210102b691d5e4075f65f [file] [log] [blame]
/** @file mlan_11d.c
*
* @brief This file contains functions for 802.11D.
*
*
* Copyright 2008-2020 NXP
*
* This software file (the File) is distributed by NXP
* under the terms of the GNU General Public License Version 2, June 1991
* (the License). You may use, redistribute and/or modify the File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
/********************************************************
Change log:
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
********************************************************/
#ifdef STA_SUPPORT
/** 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 ", 0xFE}, /* Japan */
{"JP ", 0xFF}, /* Japan special */
{"NE ", 0x30}, /* New Zeland */
};
/** Universal region code */
#define UNIVERSAL_REGION_CODE 0xff
#endif
/** Default Tx power */
#define TX_PWR_DEFAULT 10
/* 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, MFALSE, {0x1c, 0, 0}},
{2, 2417, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{3, 2422, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{4, 2427, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{5, 2432, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{6, 2437, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{7, 2442, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{8, 2447, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{9, 2452, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{10, 2457, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{11, 2462, TX_PWR_DEFAULT, MFALSE, {0x1c, 0, 0}},
{12, 2467, TX_PWR_DEFAULT, MFALSE, {0x1d, 0, 0}},
{13, 2472, TX_PWR_DEFAULT, MFALSE, {0x1d, 0, 0}},
{14, 2484, TX_PWR_DEFAULT, MFALSE, {0x1d, 0, 0}},
};
/** Channels for 802.11a/j */
static chan_freq_power_t channel_freq_power_UN_AJ[] = {
{8, 5040, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{12, 5060, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{16, 5080, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{34, 5170, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{36, 5180, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{38, 5190, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{40, 5200, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{42, 5210, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{44, 5220, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{46, 5230, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{48, 5240, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{52, 5260, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{56, 5280, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{60, 5300, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{64, 5320, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{100, 5500, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{104, 5520, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{108, 5540, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{112, 5560, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{116, 5580, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{120, 5600, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{124, 5620, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{128, 5640, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{132, 5660, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{136, 5680, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{140, 5700, TX_PWR_DEFAULT, MTRUE, {0x13, 0, 0}},
{149, 5745, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{153, 5765, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{157, 5785, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{161, 5805, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}},
{165, 5825, TX_PWR_DEFAULT, MFALSE, {0x10, 0, 0}}
/* {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 */
};
/********************************************************
Global Variables
********************************************************/
/********************************************************
Local Functions
********************************************************/
#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;
ENTER();
/* Look for code in mapping table */
for (i = 0; i < NELEMENTS(region_code_mapping); 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 */
pmpriv = wlan_get_priv(pmadapter,
MLAN_BSS_ROLE_STA);
if (pmpriv &&
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_ext(pmadapter, domain_info->country_code,
wlan_11d_code_2_region(pmadapter,
(t_u8)pmadapter->region_code),
COUNTRY_CODE_LEN, 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 | BAND_GAC)) {
cfp = channel_freq_power_UN_BG;
cfp_no = NELEMENTS(channel_freq_power_UN_BG);
} else if (band & (BAND_A | BAND_AN | BAND_AAC)) {
cfp = channel_freq_power_UN_AJ;
cfp_no = NELEMENTS(channel_freq_power_UN_AJ);
} 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 + num_chan_added) < 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_ext(pmadapter, parsed_region_chan, &region_chan,
sizeof(parsed_region_chan_11d_t),
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_ext(pmadapter, pdomain->country_code, country_code,
COUNTRY_CODE_LEN, COUNTRY_CODE_LEN);
pdomain->band = band;
pdomain->no_of_sub_band = num_sub_band;
memcpy_ext(pmadapter, pdomain->sub_band, sub_band_list,
num_sub_band * sizeof(IEEEtypes_SubbandSet_t),
MRVDRV_MAX_SUBBAND_802_11D * 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 &&
pmpriv->state_11d.user_enable_11d == ENABLE_11D) ?
MTRUE : MFALSE;
}
/**
* @brief This function gets if 11D is enabled in FW
*
* @param pmpriv Pointer to mlan_private structure
*
* @return MTRUE or MFALSE
*/
t_bool
wlan_fw_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;
t_u8 i;
ENTER();
PRINTM(MCMND, "11D:Country=%c%c band=%d sub-band=5d\n",
pmadapter->domain_reg.country_code[0],
pmadapter->domain_reg.country_code[1],
pmadapter->domain_reg.band, no_of_sub_band);
for (i = 0; i < no_of_sub_band; i++) {
PRINTM(MCMND,
"11D: first chan=%d no_of_chan=%d, max_tx_pwr=%d\n",
pmadapter->domain_reg.sub_band[i].first_chan,
pmadapter->domain_reg.sub_band[i].no_of_chan,
pmadapter->domain_reg.sub_band[i].max_tx_pwr);
}
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_ext(pmadapter, domain->country_code,
pmadapter->domain_reg.country_code,
sizeof(domain->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_ext(pmadapter, domain->sub_band,
pmadapter->domain_reg.sub_band,
no_of_sub_band * sizeof(IEEEtypes_SubbandSet_t),
MRVDRV_MAX_SUBBAND_802_11D *
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;
}
/**
* @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 | BAND_AAC)) {
cf = channel_freq_power_UN_AJ;
cnt = NELEMENTS(channel_freq_power_UN_AJ);
} else {
cf = channel_freq_power_UN_BG;
cnt = NELEMENTS(channel_freq_power_UN_BG);
}
/* Locate channel and return corresponding frequency */
for (i = 0; i < cnt; i++) {
if (chan == cf[i].channel)
freq = cf[i].freq;
}
LEAVE();
return freq;
}
#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 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 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 =
NELEMENTS(channel_freq_power_UN_BG);
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 | BAND_AAC)) {
/* If band A */
/* Set channel-frequency-power */
pmadapter->universal_channel[i].num_cfp =
NELEMENTS(channel_freq_power_UN_AJ);
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:
case BAND_A | BAND_AN | BAND_AAC:
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:
case BAND_B | BAND_G | BAND_GN | BAND_GAC:
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_ext(pmadapter, &region_chan,
&pmadapter->parsed_region_chan,
sizeof(parsed_region_chan_11d_t),
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;
}
#endif /* STA_SUPPORT */
/**
* @brief This function checks country code and maps it when needed
*
* @param pmadapter A pointer to mlan_adapter structure
* @param pcountry_code Pointer to the country code string
*
* @return Pointer to the mapped country code string
*/
static t_u8 *
wlan_11d_map_country_code(pmlan_adapter pmadapter, t_u8 *pcountry_code)
{
/* Since firmware can only recognize EU as ETSI domain and there is no
* memory left for some devices to convert it in firmware, driver need
* to convert it before passing country code to firmware through tlv
*/
if (wlan_is_etsi_country(pmadapter, pcountry_code))
return ("EU ");
else
return pcountry_code;
}
/**
* @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(pmlan_adapter pmadapter, 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 cfp_bg = 0, cfp_a = 0;
ENTER();
if (pmadapter->otp_region && pmadapter->otp_region->force_reg) {
PRINTM(MERROR,
"ForceRegionRule is set in the on-chip OTP memory\n");
ret = MLAN_STATUS_FAILURE;
goto done;
}
if (!wlan_fw_11d_is_enabled(pmpriv))
wlan_11d_enable(pmpriv, MNULL, ENABLE_11D);
cfg_11d = (mlan_ds_11d_cfg *)pioctl_req->pbuf;
domain_info = &cfg_11d->param.domain_info;
memcpy_ext(pmadapter, pmadapter->country_code,
domain_info->country_code, COUNTRY_CODE_LEN,
COUNTRY_CODE_LEN);
wlan_11d_set_domain_info(pmpriv, domain_info->band,
wlan_11d_map_country_code(pmadapter,
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;
/* Update region code and table based on country code */
if (wlan_misc_country_2_cfp_table_code
(pmadapter, domain_info->country_code, &cfp_bg, &cfp_a)) {
PRINTM(MIOCTL, "Country code %c%c not found!\n",
domain_info->country_code[0],
domain_info->country_code[1]);
goto done;
}
pmadapter->cfp_code_bg = cfp_bg;
pmadapter->cfp_code_a = cfp_a;
if (cfp_a)
pmadapter->region_code = cfp_a;
else if (cfp_bg)
pmadapter->region_code = cfp_bg;
else
pmadapter->region_code = 0;
if (wlan_set_regiontable(pmpriv, pmadapter->region_code,
pmadapter->config_bands |
pmadapter->adhoc_start_band)) {
PRINTM(MIOCTL, "Fail to set regiontabl\n");
goto done;
}
done:
LEAVE();
return ret;
}
#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 cfp_bg = 0, cfp_a = 0;
ENTER();
pdomain_tlv = (MrvlIEtypes_DomainParamSet_t *)domain_tlv;
/* update region code & table based on country string */
if (wlan_misc_country_2_cfp_table_code
(pmadapter, pdomain_tlv->country_code, &cfp_bg,
&cfp_a) == MLAN_STATUS_SUCCESS) {
pmadapter->cfp_code_bg = cfp_bg;
pmadapter->cfp_code_a = cfp_a;
if (cfp_a)
pmadapter->region_code = cfp_a;
else if (cfp_bg)
pmadapter->region_code = cfp_bg;
else
pmadapter->region_code = 0;
if (wlan_set_regiontable(pmpriv, pmadapter->region_code,
pmadapter->config_bands |
pmadapter->adhoc_start_band)) {
ret = MLAN_STATUS_FAILURE;
goto done;
}
}
memcpy_ext(pmadapter, pmadapter->country_code,
pdomain_tlv->country_code, COUNTRY_CODE_LEN,
COUNTRY_CODE_LEN);
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);
done:
LEAVE();
return ret;
}
#endif