diff options
Diffstat (limited to 'drivers/net/wireless/sd8797/mlan/mlan_11d.c')
-rw-r--r-- | drivers/net/wireless/sd8797/mlan/mlan_11d.c | 1514 |
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, ®ion_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, + ®ion_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, + ®ion_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, ®ion_chan, 0, sizeof(parsed_region_chan_11d_t)); + memset(pmadapter, &bssdesc_region_chan, 0, + sizeof(parsed_region_chan_11d_t)); + + memcpy(pmadapter, ®ion_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, ®ion_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, + ®ion_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, + ®ion_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 |