blob: f8086080b9eb85d279c5ce3ec31a6c8556800661 [file] [log] [blame]
/** @file mlan_11d.c
*
* @brief This file contains functions for 802.11D.
*
* Copyright (C) 2008-2009, Marvell International Ltd.
* All Rights Reserved
*/
/********************************************************
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"
/********************************************************
Local Variables
********************************************************/
/** Default Tx power */
#define TX_PWR_DEFAULT 10
/** Universal region code */
#define UNIVERSAL_REGION_CODE 0xff
/** 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 */
{"JP ", 0xFF}, /* Japan special */
};
/* 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 */
};
/********************************************************
Global Variables
********************************************************/
/********************************************************
Local Functions
********************************************************/
/**
* @brief This function converts a lowercase character to uppercase
*
* @param pmadapter A pointer to mlan_adapter structure
* @param c Input character
*
* @return Corresponding uppercase character
*/
static t_u8
wlan_11d_lower_to_upper(pmlan_adapter pmadapter, t_u8 c)
{
t_u8 upper;
ENTER();
if (c >= 'a' && c <= 'z')
upper = c - 0x20;
else
upper = c;
LEAVE();
return upper;
}
/**
* @brief This function convert region string to code integer
*
* @param pmadapter A pointer to mlan_adapter structure
* @param region Region string
*
* @return Region id
*/
static t_u8
wlan_11d_region_2_code(pmlan_adapter pmadapter, t_u8 * region)
{
t_u8 i;
t_u8 size = sizeof(region_code_mapping) / sizeof(region_code_mapping_t);
ENTER();
for (i = 0; i < COUNTRY_CODE_LEN && region[i]; i++)
region[i] = wlan_11d_lower_to_upper(pmadapter, region[i]);
/* Look for region in mapping table */
for (i = 0; i < size; i++) {
if (!memcmp(region, region_code_mapping[i].region, COUNTRY_CODE_LEN)) {
LEAVE();
return (region_code_mapping[i].code);
}
}
LEAVE();
/* Default is US */
return (region_code_mapping[0].code);
}
/**
* @brief This function converts interger 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 chan txpwr is learned from AP/IBSS
*
* @param pmadapter A pointer to mlan_adapter structure
* @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 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;
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) {
PRINTM(MINFO, "11D: Found chan:%d\n", chan);
LEAVE();
return MTRUE;
}
}
PRINTM(MERROR, "11D: Could not find chan:%d\n", chan);
LEAVE();
return MFALSE;
}
/**
* @brief This function sets domain info to FW
*
* @param pmpriv A pointer to mlan_private structure
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
static mlan_status
wlan_11d_set_domain_info(mlan_private * pmpriv)
{
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, MNULL, MNULL);
if (ret)
PRINTM(MERROR, "11D: Failed to download domain Info\n");
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(MINFO, "11D: region_chan is MNULL\n");
LEAVE();
return;
}
/* Get channel-frequecy-power trio */
cfp = region_chan->pcfp;
if (!cfp) {
PRINTM(MINFO, "11D: cfp equal MNULL \n");
LEAVE();
return;
}
/* Set band, region and country code */
parsed_region_chan->band = region_chan->band;
parsed_region_chan->region = region_chan->region;
memcpy(parsed_region_chan->country_code,
wlan_11d_code_2_region(pmadapter, region_chan->region),
COUNTRY_CODE_LEN);
PRINTM(MINFO, "11D: region[0x%x] band[%d]\n", parsed_region_chan->region,
parsed_region_chan->band);
/* Set channel 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].pwr = (t_u8) cfp->max_tx_power;
PRINTM(MINFO, "11D: Chan[%d] Pwr[%d]\n",
parsed_region_chan->chan_pwr[i].chan,
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
* @param domain_info Pointer to wlan_802_11d_domain_reg_t
*
* @return MLAN_STATUS_SUCCESS
*/
static mlan_status
wlan_11d_generate_domain_info(pmlan_adapter pmadapter,
parsed_region_chan_11d_t * parsed_region_chan,
wlan_802_11d_domain_reg_t * domain_info)
{
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 = 0;
ENTER();
/* Set country code */
memcpy(domain_info->country_code,
parsed_region_chan->country_code, COUNTRY_CODE_LEN);
PRINTM(MINFO, "11D: no_of_chan=%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 = 1;
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: no_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) {
/* Channel already exists update the tx_power */
parsed_region_chan->chan_pwr[i].pwr =
MIN(parsed_region_chan->chan_pwr[i].pwr, tx_power);
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].pwr = tx_power;
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 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);
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;
}
/* Step 1: Check region_code */
parsed_region_chan->region =
wlan_11d_region_2_code(pmadapter, country_info->country_code);
PRINTM(MINFO, "11D: region code=0x%x\n", (t_u8) parsed_region_chan->region);
HEXDUMP("11D: Country Code", (t_u8 *) country_info->country_code,
COUNTRY_CODE_LEN);
parsed_region_chan->band = band;
memcpy(parsed_region_chan->country_code,
country_info->country_code, COUNTRY_CODE_LEN);
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: chan 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].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: no_of_chan=0x%x\n", parsed_region_chan->no_of_chan);
HEXDUMP("11D: parsed_region_chan", (t_u8 *) parsed_region_chan,
2 + COUNTRY_CODE_LEN + sizeof(parsed_region_chan_11d_t) * idx);
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @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(&region_chan, 0, sizeof(parsed_region_chan_11d_t));
/* Parse 11D country info */
wlan_11d_parse_domain_info(pmadapter, &pbss_desc->country_info,
(t_u8) pbss_desc->bss_band, &region_chan);
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 minimum
* value among existing tx_power and new tx power
*/
if (region_chan.chan_pwr[i].chan ==
parsed_region_chan->chan_pwr[j].chan) {
parsed_region_chan->chan_pwr[j].pwr =
MIN(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].pwr =
region_chan.chan_pwr[i].pwr;
num_chan_added++;
}
}
parsed_region_chan->no_of_chan += num_chan_added;
} else {
/* Parsed region is empty, copy the first one */
memcpy(parsed_region_chan,
&region_chan, sizeof(parsed_region_chan_11d_t));
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/********************************************************
Global functions
********************************************************/
/**
* @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->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 gets if 11D is enabled
*
* @param pmpriv Pointer to mlan_private structure
*
* @return ENABLE_11D or DISABLE_11D
*/
state_11d_t
wlan_11d_get_state(mlan_private * pmpriv)
{
mlan_adapter *pmadapter = pmpriv->adapter;
wlan_802_11d_state_t *state = &pmadapter->state_11d;
ENTER();
LEAVE();
return (state->enable_11d);
}
/**
* @brief This function calculates the scan type for channels
*
* @param pmadapter A pointer to mlan_adapter structure
* @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 chan, parsed_region_chan_11d_t * parsed_region_chan)
{
t_u8 scan_type = HostCmd_SCAN_TYPE_PASSIVE;
ENTER();
if (wlan_11d_channel_known(pmadapter, chan, parsed_region_chan)) {
/* Channel found */
PRINTM(MINFO, "11D: Channel found and doing Active Scan\n");
scan_type = HostCmd_SCAN_TYPE_ACTIVE;
} else
PRINTM(MINFO, "11D: Channel not found and doing Passive Scan\n");
LEAVE();
return scan_type;
}
/**
* @brief Initialize internal variable for 11D
*
* @param pmadapter Pointer to mlan_adapter structure
*
* @return N/A
*/
t_void
wlan_11d_init(mlan_adapter * pmadapter)
{
wlan_802_11d_state_t *state = &pmadapter->state_11d;
ENTER();
/* Start in disabled mode */
state->enable_11d = DISABLE_11D;
state->user_enable_11d = DISABLE_11D;
memset(&(pmadapter->parsed_region_chan), 0,
sizeof(parsed_region_chan_11d_t));
memset(&(pmadapter->universal_channel), 0, sizeof(region_chan_t));
memset(&(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)
{
mlan_adapter *pmadapter = pmpriv->adapter;
wlan_802_11d_state_t *state = &pmadapter->state_11d;
mlan_status ret = MLAN_STATUS_SUCCESS;
state_11d_t enable = flag;
ENTER();
memset(&pmadapter->parsed_region_chan, 0, sizeof(parsed_region_chan_11d_t));
/* 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) {
if (flag)
PRINTM(MERROR, "11D: Failed to enable 11D\n");
else
PRINTM(MERROR, "11D: Failed to disable 11D\n");
} else {
state->enable_11d = flag;
/* Set user enable flag if called from ioctl */
if (pioctl_buf)
state->user_enable_11d = flag;
}
LEAVE();
return ret;
}
/**
* @brief This function setups scan channels
*
* @param pmpriv A pointer to mlan_private structure
* @param band band
*
* @return MLAN_STATUS_SUCCESS or MLAN_STATUS_FAILURE
*/
int
wlan_set_universal_table(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->universal_channel, 0,
sizeof(pmadapter->universal_channel));
if (band & (BAND_B | BAND_G | BAND_GN)) {
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;
pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
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)) {
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;
pmadapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
pmadapter->universal_channel[i].band = BAND_A;
i++;
}
LEAVE();
return MLAN_STATUS_SUCCESS;
}
/**
* @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 or MLAN_STATUS_FAILURE
*/
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: no_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(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(domain->sub_band,
pmadapter->domain_reg.sub_band,
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) -
3) / sizeof(IEEEtypes_SubbandSet_t));
/* Country code is 3 bytes */
PRINTM(MINFO, "11D Domain Info Resp: no_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 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();
PRINTM(MINFO, "11D: Band[%d]\n", band);
/* Update parsed_region_chan; download domain info to FW */
/* Find region channel */
for (j = 0;
j <
sizeof(pmadapter->region_channel) /
sizeof(pmadapter->region_channel[0]); 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 >= sizeof(pmadapter->region_channel) /
sizeof(pmadapter->region_channel[0])) {
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(&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 */
memset(&pmadapter->domain_reg, 0, sizeof(wlan_802_11d_domain_reg_t));
wlan_11d_generate_domain_info(pmadapter, &parsed_region_chan,
&pmadapter->domain_reg);
/* Set domain info */
ret = wlan_11d_set_domain_info(pmpriv);
if (ret) {
PRINTM(MERROR, "11D: Error setting domain info in 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_get_state(pmpriv) == ENABLE_11D) {
memset(&pmadapter->domain_reg, 0, sizeof(wlan_802_11d_domain_reg_t));
memset(&region_chan, 0, sizeof(parsed_region_chan_11d_t));
memset(&bssdesc_region_chan, 0, sizeof(parsed_region_chan_11d_t));
memcpy(&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,
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, so overwrite existing
* tx power with the 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[j].pwr =
bssdesc_region_chan.chan_pwr[i].pwr;
break;
}
}
}
}
}
/* Generate domain info */
wlan_11d_generate_domain_info(pmadapter, &region_chan,
&pmadapter->domain_reg);
/* Set domain info */
ret = wlan_11d_set_domain_info(pmpriv);
if (ret) {
PRINTM(MERROR, "11D: Error setting domain info in 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_get_state(pmpriv) == ENABLE_11D &&
pmadapter->num_in_scan_table != 0) {
for (idx = 0; idx < pmadapter->num_in_scan_table; idx++) {
pcountry_full = &pmadapter->pscan_table[idx].country_info;
if (*(pcountry_full->country_code) == 0 ||
(pcountry_full->len <= COUNTRY_CODE_LEN)) {
/* Country info not found in the BSS descriptor */
ret =
wlan_11d_update_chan_pwr_table(pmpriv,
&pmadapter->
pscan_table[idx]);
} else {
/* Country info found in the BSS Descriptor */
ret =
wlan_11d_process_country_info(pmpriv,
&pmadapter->pscan_table[idx]);
}
}
/* 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;
}