| /* |
| * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved. |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /** |
| * DOC: reg_build_chan_list.c |
| * This file defines the API to build master and current channel list. |
| */ |
| |
| #include <wlan_cmn.h> |
| #include <reg_services_public_struct.h> |
| #include <wlan_objmgr_psoc_obj.h> |
| #include <wlan_objmgr_pdev_obj.h> |
| #include "reg_priv_objs.h" |
| #include "reg_utils.h" |
| #include "reg_callbacks.h" |
| #include "reg_services_common.h" |
| #include "reg_db.h" |
| #include "reg_db_parser.h" |
| #include "reg_offload_11d_scan.h" |
| #include <scheduler_api.h> |
| #include "reg_build_chan_list.h" |
| #include <qdf_platform.h> |
| |
| #define MAX_PWR_FCC_CHAN_12 8 |
| #define MAX_PWR_FCC_CHAN_13 2 |
| #define CHAN_144_CENT_FREQ 5720 |
| |
| /** |
| * reg_fill_channel_info() - Populate TX power, antenna gain, channel state, |
| * channel flags, min and max bandwidth to master channel list. |
| * @chan_enum: Channel enum. |
| * @reg_rule: Pointer to regulatory rule which has tx power and antenna gain. |
| * @master_list: Pointer to master channel list. |
| * @min_bw: minimum bandwidth to be used for given channel. |
| */ |
| static void reg_fill_channel_info(enum channel_enum chan_enum, |
| struct cur_reg_rule *reg_rule, |
| struct regulatory_channel *master_list, |
| uint16_t min_bw) |
| { |
| master_list[chan_enum].chan_flags &= ~REGULATORY_CHAN_DISABLED; |
| |
| master_list[chan_enum].tx_power = reg_rule->reg_power; |
| master_list[chan_enum].ant_gain = reg_rule->ant_gain; |
| master_list[chan_enum].state = CHANNEL_STATE_ENABLE; |
| |
| if (reg_rule->flags & REGULATORY_CHAN_NO_IR) { |
| master_list[chan_enum].chan_flags |= REGULATORY_CHAN_NO_IR; |
| master_list[chan_enum].state = CHANNEL_STATE_DFS; |
| } |
| |
| if (reg_rule->flags & REGULATORY_CHAN_RADAR) { |
| master_list[chan_enum].chan_flags |= REGULATORY_CHAN_RADAR; |
| master_list[chan_enum].state = CHANNEL_STATE_DFS; |
| } |
| |
| if (reg_rule->flags & REGULATORY_CHAN_INDOOR_ONLY) |
| master_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_INDOOR_ONLY; |
| |
| if (reg_rule->flags & REGULATORY_CHAN_NO_OFDM) |
| master_list[chan_enum].chan_flags |= REGULATORY_CHAN_NO_OFDM; |
| |
| master_list[chan_enum].min_bw = min_bw; |
| if (master_list[chan_enum].max_bw == 20) |
| master_list[chan_enum].max_bw = reg_rule->max_bw; |
| } |
| |
| /** |
| * reg_populate_band_channels() - For all the valid regdb channels in the master |
| * channel list, find the regulatory rules and call reg_fill_channel_info() to |
| * populate master channel list with txpower, antennagain, BW info, etc. |
| * @start_chan: Start channel enum. |
| * @end_chan: End channel enum. |
| * @rule_start_ptr: Pointer to regulatory rules. |
| * @num_reg_rules: Number of regulatory rules. |
| * @min_reg_bw: Minimum regulatory bandwidth. |
| * @mas_chan_list: Pointer to master channel list. |
| */ |
| static void reg_populate_band_channels(enum channel_enum start_chan, |
| enum channel_enum end_chan, |
| struct cur_reg_rule *rule_start_ptr, |
| uint32_t num_reg_rules, |
| uint16_t min_reg_bw, |
| struct regulatory_channel *mas_chan_list) |
| { |
| struct cur_reg_rule *found_rule_ptr; |
| struct cur_reg_rule *cur_rule_ptr; |
| struct regulatory_channel; |
| enum channel_enum chan_enum; |
| uint32_t rule_num, bw; |
| uint16_t max_bw; |
| uint16_t min_bw; |
| |
| for (chan_enum = start_chan; chan_enum <= end_chan; chan_enum++) { |
| found_rule_ptr = NULL; |
| |
| max_bw = QDF_MIN((uint16_t)20, channel_map[chan_enum].max_bw); |
| min_bw = QDF_MAX(min_reg_bw, channel_map[chan_enum].min_bw); |
| |
| if (channel_map[chan_enum].chan_num == INVALID_CHANNEL_NUM) |
| continue; |
| |
| for (bw = max_bw; bw >= min_bw; bw = bw / 2) { |
| for (rule_num = 0, cur_rule_ptr = rule_start_ptr; |
| rule_num < num_reg_rules; |
| cur_rule_ptr++, rule_num++) { |
| if ((cur_rule_ptr->start_freq <= |
| mas_chan_list[chan_enum].center_freq - |
| bw / 2) && |
| (cur_rule_ptr->end_freq >= |
| mas_chan_list[chan_enum].center_freq + |
| bw / 2) && (min_bw <= bw)) { |
| found_rule_ptr = cur_rule_ptr; |
| break; |
| } |
| } |
| |
| if (found_rule_ptr) |
| break; |
| } |
| |
| if (found_rule_ptr) { |
| mas_chan_list[chan_enum].max_bw = bw; |
| reg_fill_channel_info(chan_enum, found_rule_ptr, |
| mas_chan_list, min_bw); |
| /* Disable 2.4 Ghz channels that dont have 20 mhz bw */ |
| if (start_chan == MIN_24GHZ_CHANNEL && |
| mas_chan_list[chan_enum].max_bw < 20) { |
| mas_chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_DISABLED; |
| mas_chan_list[chan_enum].state = |
| CHANNEL_STATE_DISABLE; |
| } |
| } |
| } |
| } |
| |
| /** |
| * reg_update_max_bw_per_rule() - Update max bandwidth value for given regrules. |
| * @num_reg_rules: Number of regulatory rules. |
| * @reg_rule_start: Pointer to regulatory rules. |
| * @max_bw: Maximum bandwidth |
| */ |
| static void reg_update_max_bw_per_rule(uint32_t num_reg_rules, |
| struct cur_reg_rule *reg_rule_start, |
| uint16_t max_bw) |
| { |
| uint32_t count; |
| |
| for (count = 0; count < num_reg_rules; count++) |
| reg_rule_start[count].max_bw = |
| min(reg_rule_start[count].max_bw, max_bw); |
| } |
| |
| /** |
| * reg_do_auto_bw_correction() - Calculate and update the maximum bandwidth |
| * value. |
| * @num_reg_rules: Number of regulatory rules. |
| * @reg_rule_ptr: Pointer to regulatory rules. |
| * @max_bw: Maximum bandwidth |
| */ |
| static void reg_do_auto_bw_correction(uint32_t num_reg_rules, |
| struct cur_reg_rule *reg_rule_ptr, |
| uint16_t max_bw) |
| { |
| uint32_t count; |
| uint16_t new_bw; |
| |
| for (count = 0; count < num_reg_rules - 1; count++) { |
| if ((reg_rule_ptr[count].end_freq == |
| reg_rule_ptr[count + 1].start_freq) && |
| ((reg_rule_ptr[count].max_bw + |
| reg_rule_ptr[count + 1].max_bw) <= max_bw)) { |
| new_bw = reg_rule_ptr[count].max_bw + |
| reg_rule_ptr[count + 1].max_bw; |
| reg_rule_ptr[count].max_bw = new_bw; |
| reg_rule_ptr[count + 1].max_bw = new_bw; |
| } |
| } |
| } |
| |
| /** |
| * reg_modify_chan_list_for_dfs_channels() - disable the DFS channels if |
| * dfs_enable set to false. |
| * @chan_list: Pointer to regulatory channel list. |
| * @dfs_enabled: if false, then disable the DFS channels. |
| */ |
| static void reg_modify_chan_list_for_dfs_channels( |
| struct regulatory_channel *chan_list, bool dfs_enabled) |
| { |
| enum channel_enum chan_enum; |
| |
| if (dfs_enabled) |
| return; |
| |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { |
| if (chan_list[chan_enum].state == CHANNEL_STATE_DFS) { |
| chan_list[chan_enum].state = CHANNEL_STATE_DISABLE; |
| chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_DISABLED; |
| } |
| } |
| } |
| |
| /** |
| * reg_modify_chan_list_for_indoor_channels() - Disable the indoor channels if |
| * indoor_chan_enabled flag is set to false. |
| * @pdev_priv_obj: Pointer to regulatory private pdev structure. |
| */ |
| static void reg_modify_chan_list_for_indoor_channels( |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) |
| { |
| enum channel_enum chan_enum; |
| struct regulatory_channel *chan_list = pdev_priv_obj->cur_chan_list; |
| |
| if (!pdev_priv_obj->indoor_chan_enabled) { |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { |
| if (REGULATORY_CHAN_INDOOR_ONLY & |
| chan_list[chan_enum].chan_flags) { |
| chan_list[chan_enum].state = |
| CHANNEL_STATE_DFS; |
| chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_NO_IR; |
| } |
| } |
| } |
| |
| if (pdev_priv_obj->force_ssc_disable_indoor_channel && |
| pdev_priv_obj->sap_state) { |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { |
| if (REGULATORY_CHAN_INDOOR_ONLY & |
| chan_list[chan_enum].chan_flags) { |
| chan_list[chan_enum].state = |
| CHANNEL_STATE_DISABLE; |
| chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_DISABLED; |
| } |
| } |
| } |
| } |
| |
| /** |
| * reg_modify_chan_list_for_band() - Based on the input band value, either |
| * disable 2GHz or 5GHz channels. |
| * @chan_list: Pointer to regulatory channel list. |
| * @band_val: Input band value. |
| */ |
| static void reg_modify_chan_list_for_band(struct regulatory_channel *chan_list, |
| enum band_info band_val) |
| { |
| enum channel_enum chan_enum; |
| |
| if (band_val == BAND_2G) { |
| for (chan_enum = MIN_5GHZ_CHANNEL; |
| chan_enum <= MAX_5GHZ_CHANNEL; chan_enum++) { |
| chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_DISABLED; |
| chan_list[chan_enum].state = CHANNEL_STATE_DISABLE; |
| } |
| } |
| |
| if (band_val == BAND_5G) { |
| for (chan_enum = MIN_24GHZ_CHANNEL; |
| chan_enum <= MAX_24GHZ_CHANNEL; chan_enum++) { |
| chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_DISABLED; |
| chan_list[chan_enum].state = CHANNEL_STATE_DISABLE; |
| } |
| } |
| } |
| |
| /** |
| * reg_modify_chan_list_for_fcc_channel() - Set maximum FCC txpower for channel |
| * 12 and 13 if set_fcc_channel flag is set to true. |
| * @chan_list: Pointer to regulatory channel list. |
| * @set_fcc_channel: If this flag is set to true, then set the max FCC txpower |
| * for channel 12 and 13. |
| */ |
| static void reg_modify_chan_list_for_fcc_channel( |
| struct regulatory_channel *chan_list, bool set_fcc_channel) |
| { |
| enum channel_enum chan_enum; |
| |
| if (!set_fcc_channel) |
| return; |
| |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { |
| if (chan_list[chan_enum].center_freq == CHAN_12_CENT_FREQ) |
| chan_list[chan_enum].tx_power = MAX_PWR_FCC_CHAN_12; |
| |
| if (chan_list[chan_enum].center_freq == CHAN_13_CENT_FREQ) |
| chan_list[chan_enum].tx_power = MAX_PWR_FCC_CHAN_13; |
| } |
| } |
| |
| /** |
| * reg_modify_chan_list_for_chan_144() - Disable channel 144 if en_chan_144 flag |
| * is set to false. |
| * @chan_list: Pointer to regulatory channel list. |
| * @en_chan_144: if false, then disable channel 144. |
| */ |
| static void reg_modify_chan_list_for_chan_144( |
| struct regulatory_channel *chan_list, bool en_chan_144) |
| { |
| enum channel_enum chan_enum; |
| |
| if (en_chan_144) |
| return; |
| |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { |
| if (chan_list[chan_enum].center_freq == CHAN_144_CENT_FREQ) { |
| chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_DISABLED; |
| chan_list[chan_enum].state = CHANNEL_STATE_DISABLE; |
| } |
| } |
| } |
| |
| /** |
| * reg_modify_chan_list_for_nol_list() - Disable the channel if nol_chan flag is |
| * set. |
| * @chan_list: Pointer to regulatory channel list. |
| */ |
| static void reg_modify_chan_list_for_nol_list( |
| struct regulatory_channel *chan_list) |
| { |
| enum channel_enum chan_enum; |
| |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { |
| if (chan_list[chan_enum].nol_chan) { |
| chan_list[chan_enum].state = CHANNEL_STATE_DISABLE; |
| chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_DISABLED; |
| } |
| } |
| } |
| |
| /** |
| * reg_find_low_limit_chan_enum() - Find low limit 2G and 5G channel enums. |
| * @chan_list: Pointer to regulatory channel list. |
| * @low_freq: low limit frequency. |
| * @low_limit: pointer to output low limit enum. |
| * |
| * Return: None |
| */ |
| static void reg_find_low_limit_chan_enum( |
| struct regulatory_channel *chan_list, uint32_t low_freq, |
| uint32_t *low_limit) |
| { |
| enum channel_enum chan_enum; |
| uint16_t min_bw; |
| uint16_t max_bw; |
| uint32_t center_freq; |
| |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { |
| min_bw = chan_list[chan_enum].min_bw; |
| max_bw = chan_list[chan_enum].max_bw; |
| center_freq = chan_list[chan_enum].center_freq; |
| |
| if ((center_freq - min_bw / 2) >= low_freq) { |
| if ((center_freq - max_bw / 2) < low_freq) { |
| if (max_bw <= 20) |
| max_bw = ((center_freq - low_freq) * 2); |
| if (max_bw < min_bw) |
| max_bw = min_bw; |
| chan_list[chan_enum].max_bw = max_bw; |
| } |
| *low_limit = chan_enum; |
| break; |
| } |
| } |
| } |
| |
| /** |
| * reg_find_high_limit_chan_enum() - Find high limit 2G and 5G channel enums. |
| * @chan_list: Pointer to regulatory channel list. |
| * @high_freq: high limit frequency. |
| * @high_limit: pointer to output high limit enum. |
| * |
| * Return: None |
| */ |
| static void reg_find_high_limit_chan_enum( |
| struct regulatory_channel *chan_list, uint32_t high_freq, |
| uint32_t *high_limit) |
| { |
| enum channel_enum chan_enum; |
| uint16_t min_bw; |
| uint16_t max_bw; |
| uint32_t center_freq; |
| |
| for (chan_enum = NUM_CHANNELS - 1; chan_enum >= 0; chan_enum--) { |
| min_bw = chan_list[chan_enum].min_bw; |
| max_bw = chan_list[chan_enum].max_bw; |
| center_freq = chan_list[chan_enum].center_freq; |
| |
| if (center_freq + min_bw / 2 <= high_freq) { |
| if ((center_freq + max_bw / 2) > high_freq) { |
| if (max_bw <= 20) |
| max_bw = ((high_freq - |
| center_freq) * 2); |
| if (max_bw < min_bw) |
| max_bw = min_bw; |
| chan_list[chan_enum].max_bw = max_bw; |
| } |
| *high_limit = chan_enum; |
| break; |
| } |
| |
| if (chan_enum == 0) |
| break; |
| } |
| } |
| |
| #ifdef REG_DISABLE_JP_CH144 |
| /** |
| * reg_modify_chan_list_for_japan() - Disable channel 144 for MKK17_MKKC |
| * regdomain by default. |
| * @pdev: Pointer to pdev |
| * |
| * Return: None |
| */ |
| static void |
| reg_modify_chan_list_for_japan(struct wlan_objmgr_pdev *pdev) |
| { |
| #define MKK17_MKKC 0xE1 |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("reg pdev priv obj is NULL"); |
| return; |
| } |
| |
| if (pdev_priv_obj->reg_dmn_pair == MKK17_MKKC) |
| pdev_priv_obj->en_chan_144 = false; |
| |
| #undef MKK17_MKKC |
| } |
| #else |
| static inline void |
| reg_modify_chan_list_for_japan(struct wlan_objmgr_pdev *pdev) |
| { |
| } |
| #endif |
| /** |
| * reg_modify_chan_list_for_freq_range() - Modify channel list for the given low |
| * and high frequency range. |
| * @chan_list: Pointer to regulatory channel list. |
| * @low_freq_2g: Low frequency 2G. |
| * @high_freq_2g: High frequency 2G. |
| * @low_freq_5g: Low frequency 5G. |
| * @high_freq_5g: High frequency 5G. |
| * |
| * Return: None |
| */ |
| static void |
| reg_modify_chan_list_for_freq_range(struct regulatory_channel *chan_list, |
| uint32_t low_freq_2g, |
| uint32_t high_freq_2g, |
| uint32_t low_freq_5g, |
| uint32_t high_freq_5g) |
| { |
| uint32_t low_limit_2g = NUM_CHANNELS; |
| uint32_t high_limit_2g = NUM_CHANNELS; |
| uint32_t low_limit_5g = NUM_CHANNELS; |
| uint32_t high_limit_5g = NUM_CHANNELS; |
| enum channel_enum chan_enum; |
| bool chan_in_range; |
| |
| reg_find_low_limit_chan_enum(chan_list, low_freq_2g, &low_limit_2g); |
| reg_find_low_limit_chan_enum(chan_list, low_freq_5g, &low_limit_5g); |
| reg_find_high_limit_chan_enum(chan_list, high_freq_2g, &high_limit_2g); |
| reg_find_high_limit_chan_enum(chan_list, high_freq_5g, &high_limit_5g); |
| |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { |
| chan_in_range = false; |
| if ((low_limit_2g <= chan_enum) && |
| (high_limit_2g >= chan_enum) && |
| (low_limit_2g != NUM_CHANNELS) && |
| (high_limit_2g != NUM_CHANNELS)) |
| chan_in_range = true; |
| |
| if ((low_limit_5g <= chan_enum) && |
| (high_limit_5g >= chan_enum) && |
| (low_limit_5g != NUM_CHANNELS) && |
| (high_limit_5g != NUM_CHANNELS)) |
| chan_in_range = true; |
| |
| if (!chan_in_range) { |
| chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_DISABLED; |
| chan_list[chan_enum].state = CHANNEL_STATE_DISABLE; |
| } |
| } |
| } |
| |
| void reg_init_pdev_mas_chan_list( |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj, |
| struct mas_chan_params *mas_chan_params) |
| { |
| qdf_mem_copy(pdev_priv_obj->mas_chan_list, |
| mas_chan_params->mas_chan_list, |
| NUM_CHANNELS * sizeof(struct regulatory_channel)); |
| |
| pdev_priv_obj->dfs_region = mas_chan_params->dfs_region; |
| |
| pdev_priv_obj->phybitmap = mas_chan_params->phybitmap; |
| |
| pdev_priv_obj->reg_dmn_pair = mas_chan_params->reg_dmn_pair; |
| pdev_priv_obj->ctry_code = mas_chan_params->ctry_code; |
| |
| pdev_priv_obj->def_region_domain = mas_chan_params->reg_dmn_pair; |
| pdev_priv_obj->def_country_code = mas_chan_params->ctry_code; |
| |
| qdf_mem_copy(pdev_priv_obj->default_country, |
| mas_chan_params->default_country, REG_ALPHA2_LEN + 1); |
| |
| qdf_mem_copy(pdev_priv_obj->current_country, |
| mas_chan_params->current_country, REG_ALPHA2_LEN + 1); |
| } |
| |
| /** |
| * reg_modify_chan_list_for_cached_channels() - If num_cache_channels are |
| * non-zero, then disable the pdev channels which is given in |
| * cache_disable_chan_list. |
| * @pdev_priv_obj: Pointer to regulatory pdev private object. |
| */ |
| #ifdef DISABLE_CHANNEL_LIST |
| static void reg_modify_chan_list_for_cached_channels( |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) |
| { |
| uint32_t i, j; |
| uint32_t num_cache_channels = pdev_priv_obj->num_cache_channels; |
| struct regulatory_channel *chan_list = pdev_priv_obj->cur_chan_list; |
| struct regulatory_channel *cache_chan_list = |
| pdev_priv_obj->cache_disable_chan_list; |
| |
| if (!num_cache_channels) |
| return; |
| |
| if (pdev_priv_obj->disable_cached_channels) { |
| for (i = 0; i < num_cache_channels; i++) |
| for (j = 0; j < NUM_CHANNELS; j++) |
| if (cache_chan_list[i].chan_num == |
| chan_list[j].chan_num) { |
| chan_list[j].state = |
| CHANNEL_STATE_DISABLE; |
| chan_list[j].chan_flags |= |
| REGULATORY_CHAN_DISABLED; |
| } |
| } |
| } |
| #else |
| static void reg_modify_chan_list_for_cached_channels( |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) |
| { |
| } |
| #endif |
| |
| #ifdef CONFIG_REG_CLIENT |
| /** |
| * reg_modify_chan_list_for_srd_channels() - Modify SRD channels in ETSI13 |
| * @pdev: Pointer to pdev object |
| * @chan_list: Current channel list |
| * |
| * This function converts SRD channels to passive in ETSI13 regulatory domain |
| * when enable_srd_chan_in_master_mode is not set. |
| */ |
| static void |
| reg_modify_chan_list_for_srd_channels(struct wlan_objmgr_pdev *pdev, |
| struct regulatory_channel *chan_list) |
| { |
| enum channel_enum chan_enum; |
| |
| if (!reg_is_etsi13_regdmn(pdev)) |
| return; |
| |
| if (reg_is_etsi13_srd_chan_allowed_master_mode(pdev)) |
| return; |
| |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; chan_enum++) { |
| if (chan_list[chan_enum].chan_flags & REGULATORY_CHAN_DISABLED) |
| continue; |
| |
| if (reg_is_etsi13_srd_chan(pdev, |
| chan_list[chan_enum].chan_num)) { |
| chan_list[chan_enum].state = |
| CHANNEL_STATE_DFS; |
| chan_list[chan_enum].chan_flags |= |
| REGULATORY_CHAN_NO_IR; |
| } |
| } |
| } |
| #else |
| static inline void |
| reg_modify_chan_list_for_srd_channels(struct wlan_objmgr_pdev *pdev, |
| struct regulatory_channel *chan_list) |
| { |
| } |
| #endif |
| |
| #ifdef DISABLE_UNII_SHARED_BANDS |
| /** |
| * reg_is_reg_unii_band_1_set() - Check UNII bitmap |
| * @unii_bitmap: 5G UNII band bitmap |
| * |
| * This function checks the input bitmap to disable UNII-1 band channels. |
| * |
| * Return: Return true if UNII-1 channels need to be disabled, |
| * else return false. |
| */ |
| static bool reg_is_reg_unii_band_1_set(uint8_t unii_bitmap) |
| { |
| return !!(unii_bitmap & BIT(REG_UNII_BAND_1)); |
| } |
| |
| /** |
| * reg_is_reg_unii_band_2a_set() - Check UNII bitmap |
| * @unii_bitmap: 5G UNII band bitmap |
| * |
| * This function checks the input bitmap to disable UNII-2A band channels. |
| * |
| * Return: Return true if UNII-2A channels need to be disabled, |
| * else return false. |
| */ |
| static bool reg_is_reg_unii_band_2a_set(uint8_t unii_bitmap) |
| { |
| return !!(unii_bitmap & BIT(REG_UNII_BAND_2A)); |
| } |
| |
| /** |
| * reg_is_5g_enum() - Check if channel enum is a 5G channel enum |
| * @chan_enum: channel enum |
| * |
| * Return: Return true if the input channel enum is 5G, else return false. |
| */ |
| static bool reg_is_5g_enum(enum channel_enum chan_enum) |
| { |
| return (chan_enum >= MIN_5GHZ_CHANNEL && chan_enum <= MAX_5GHZ_CHANNEL); |
| } |
| |
| /** |
| * reg_remove_unii_chan_from_chan_list() - Remove UNII band channels |
| * @chan_list: Pointer to current channel list |
| * @start_enum: starting enum value |
| * @end_enum: ending enum value |
| * |
| * Remove channels in a unii band based in on the input start_enum and end_enum. |
| * Disable the state and flags. Set disable_coex flag to true. |
| * |
| * return: void. |
| */ |
| static void |
| reg_remove_unii_chan_from_chan_list(struct regulatory_channel *chan_list, |
| enum channel_enum start_enum, |
| enum channel_enum end_enum) |
| { |
| enum channel_enum chan_enum; |
| |
| if (!(reg_is_5g_enum(start_enum) && reg_is_5g_enum(end_enum))) { |
| reg_err_rl("start_enum or end_enum is invalid"); |
| return; |
| } |
| |
| for (chan_enum = start_enum; chan_enum <= end_enum; chan_enum++) { |
| chan_list[chan_enum].state = CHANNEL_STATE_DISABLE; |
| chan_list[chan_enum].chan_flags |= REGULATORY_CHAN_DISABLED; |
| } |
| } |
| |
| /** |
| * reg_modify_disable_chan_list_for_unii1_and_unii2a() - Disable UNII-1 and |
| * UNII2A band |
| * @pdev_priv_obj: Pointer to pdev private object |
| * |
| * This function disables the UNII-1 and UNII-2A band channels |
| * based on input unii_5g_bitmap. |
| * |
| * Return: void. |
| */ |
| static void |
| reg_modify_disable_chan_list_for_unii1_and_unii2a( |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) |
| { |
| uint8_t unii_bitmap = pdev_priv_obj->unii_5g_bitmap; |
| struct regulatory_channel *chan_list = pdev_priv_obj->cur_chan_list; |
| |
| if (reg_is_reg_unii_band_1_set(unii_bitmap)) { |
| reg_remove_unii_chan_from_chan_list(chan_list, |
| MIN_UNII_1_BAND_CHANNEL, |
| MAX_UNII_1_BAND_CHANNEL); |
| } |
| |
| if (reg_is_reg_unii_band_2a_set(unii_bitmap)) { |
| reg_remove_unii_chan_from_chan_list(chan_list, |
| MIN_UNII_2A_BAND_CHANNEL, |
| MAX_UNII_2A_BAND_CHANNEL); |
| } |
| } |
| #else |
| static inline bool reg_is_reg_unii_band_1_set(uint8_t unii_bitmap) |
| { |
| return false; |
| } |
| |
| static inline bool reg_is_reg_unii_band_2a_set(uint8_t unii_bitmap) |
| { |
| return false; |
| } |
| |
| static inline bool reg_is_5g_enum(enum channel_enum chan_enum) |
| { |
| return false; |
| } |
| |
| static inline void |
| reg_remove_unii_chan_from_chan_list(struct regulatory_channel *chan_list, |
| enum channel_enum start_enum, |
| enum channel_enum end_enum) |
| { |
| } |
| |
| static inline void |
| reg_modify_disable_chan_list_for_unii1_and_unii2a( |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) |
| { |
| } |
| #endif |
| |
| void reg_compute_pdev_current_chan_list(struct wlan_regulatory_pdev_priv_obj |
| *pdev_priv_obj) |
| { |
| qdf_mem_copy(pdev_priv_obj->cur_chan_list, pdev_priv_obj->mas_chan_list, |
| NUM_CHANNELS * sizeof(struct regulatory_channel)); |
| |
| reg_modify_chan_list_for_freq_range(pdev_priv_obj->cur_chan_list, |
| pdev_priv_obj->range_2g_low, |
| pdev_priv_obj->range_2g_high, |
| pdev_priv_obj->range_5g_low, |
| pdev_priv_obj->range_5g_high); |
| |
| reg_modify_chan_list_for_band(pdev_priv_obj->cur_chan_list, |
| pdev_priv_obj->band_capability); |
| |
| reg_modify_disable_chan_list_for_unii1_and_unii2a(pdev_priv_obj); |
| |
| reg_modify_chan_list_for_dfs_channels(pdev_priv_obj->cur_chan_list, |
| pdev_priv_obj->dfs_enabled); |
| |
| reg_modify_chan_list_for_nol_list(pdev_priv_obj->cur_chan_list); |
| |
| reg_modify_chan_list_for_indoor_channels(pdev_priv_obj); |
| |
| reg_modify_chan_list_for_fcc_channel(pdev_priv_obj->cur_chan_list, |
| pdev_priv_obj->set_fcc_channel); |
| |
| reg_modify_chan_list_for_chan_144(pdev_priv_obj->cur_chan_list, |
| pdev_priv_obj->en_chan_144); |
| |
| reg_modify_chan_list_for_cached_channels(pdev_priv_obj); |
| |
| reg_modify_chan_list_for_srd_channels(pdev_priv_obj->pdev_ptr, |
| pdev_priv_obj->cur_chan_list); |
| } |
| |
| void reg_reset_reg_rules(struct reg_rule_info *reg_rules) |
| { |
| qdf_mem_zero(reg_rules, sizeof(*reg_rules)); |
| } |
| |
| void reg_save_reg_rules_to_pdev( |
| struct reg_rule_info *psoc_reg_rules, |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) |
| { |
| uint32_t reg_rule_len; |
| struct reg_rule_info *pdev_reg_rules; |
| |
| qdf_spin_lock_bh(&pdev_priv_obj->reg_rules_lock); |
| |
| pdev_reg_rules = &pdev_priv_obj->reg_rules; |
| reg_reset_reg_rules(pdev_reg_rules); |
| |
| pdev_reg_rules->num_of_reg_rules = psoc_reg_rules->num_of_reg_rules; |
| if (!pdev_reg_rules->num_of_reg_rules) { |
| qdf_spin_unlock_bh(&pdev_priv_obj->reg_rules_lock); |
| reg_err("no reg rules in psoc"); |
| return; |
| } |
| |
| reg_rule_len = pdev_reg_rules->num_of_reg_rules * |
| sizeof(struct cur_reg_rule); |
| qdf_mem_copy(pdev_reg_rules->reg_rules, psoc_reg_rules->reg_rules, |
| reg_rule_len); |
| |
| qdf_mem_copy(pdev_reg_rules->alpha2, pdev_priv_obj->current_country, |
| REG_ALPHA2_LEN + 1); |
| pdev_reg_rules->dfs_region = pdev_priv_obj->dfs_region; |
| |
| qdf_spin_unlock_bh(&pdev_priv_obj->reg_rules_lock); |
| } |
| |
| void reg_propagate_mas_chan_list_to_pdev(struct wlan_objmgr_psoc *psoc, |
| void *object, void *arg) |
| { |
| struct wlan_objmgr_pdev *pdev = (struct wlan_objmgr_pdev *)object; |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| enum direction *dir = arg; |
| uint32_t pdev_id; |
| struct wlan_lmac_if_reg_tx_ops *reg_tx_ops; |
| struct reg_rule_info *psoc_reg_rules; |
| |
| psoc_priv_obj = (struct wlan_regulatory_psoc_priv_obj *) |
| wlan_objmgr_psoc_get_comp_private_obj( |
| psoc, WLAN_UMAC_COMP_REGULATORY); |
| |
| if (!psoc_priv_obj) { |
| reg_err("psoc priv obj is NULL"); |
| return; |
| } |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("reg pdev priv obj is NULL"); |
| return; |
| } |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); |
| reg_init_pdev_mas_chan_list( |
| pdev_priv_obj, |
| &psoc_priv_obj->mas_chan_params[pdev_id]); |
| psoc_reg_rules = &psoc_priv_obj->mas_chan_params[pdev_id].reg_rules; |
| reg_save_reg_rules_to_pdev(psoc_reg_rules, pdev_priv_obj); |
| reg_modify_chan_list_for_japan(pdev); |
| pdev_priv_obj->chan_list_recvd = |
| psoc_priv_obj->chan_list_recvd[pdev_id]; |
| reg_compute_pdev_current_chan_list(pdev_priv_obj); |
| |
| reg_tx_ops = reg_get_psoc_tx_ops(psoc); |
| if (reg_tx_ops->fill_umac_legacy_chanlist) { |
| reg_tx_ops->fill_umac_legacy_chanlist( |
| pdev, pdev_priv_obj->cur_chan_list); |
| } else { |
| if (*dir == NORTHBOUND) |
| reg_send_scheduler_msg_nb(psoc, pdev); |
| else |
| reg_send_scheduler_msg_sb(psoc, pdev); |
| } |
| } |
| |
| /** |
| * reg_populate_6g_band_channels() - For all the valid 6GHz regdb channels |
| * in the master channel list, find the regulatory rules and call |
| * reg_fill_channel_info() to populate master channel list with txpower, |
| * antennagain, BW info, etc. |
| * @reg_rule_5g: Pointer to regulatory rule. |
| * @num_5g_reg_rules: Number of regulatory rules. |
| * @min_bw_5g: Minimum regulatory bandwidth. |
| * @mas_chan_list: Pointer to the master channel list. |
| */ |
| #ifdef CONFIG_BAND_6GHZ |
| static void |
| reg_populate_6g_band_channels(struct cur_reg_rule *reg_rule_5g, |
| uint32_t num_5g_reg_rules, |
| uint16_t min_bw_5g, |
| struct regulatory_channel *mas_chan_list) |
| { |
| reg_populate_band_channels(MIN_6GHZ_CHANNEL, |
| MAX_6GHZ_CHANNEL, |
| reg_rule_5g, |
| num_5g_reg_rules, |
| min_bw_5g, |
| mas_chan_list); |
| } |
| #else |
| static void |
| reg_populate_6g_band_channels(struct cur_reg_rule *reg_rule_5g, |
| uint32_t num_5g_reg_rules, |
| uint16_t min_bw_5g, |
| struct regulatory_channel *mas_chan_list) |
| { |
| } |
| #endif /* CONFIG_BAND_6GHZ */ |
| |
| QDF_STATUS reg_process_master_chan_list( |
| struct cur_regulatory_info *regulat_info) |
| { |
| struct wlan_regulatory_psoc_priv_obj *soc_reg; |
| uint32_t num_2g_reg_rules, num_5g_reg_rules; |
| struct cur_reg_rule *reg_rule_2g, *reg_rule_5g; |
| uint16_t min_bw_2g, max_bw_2g, min_bw_5g, max_bw_5g; |
| struct regulatory_channel *mas_chan_list; |
| struct wlan_objmgr_psoc *psoc; |
| enum channel_enum chan_enum; |
| wlan_objmgr_ref_dbgid dbg_id; |
| enum direction dir; |
| uint8_t phy_id; |
| struct wlan_objmgr_pdev *pdev; |
| struct wlan_lmac_if_reg_tx_ops *tx_ops; |
| struct reg_rule_info *reg_rules; |
| QDF_STATUS status; |
| |
| psoc = regulat_info->psoc; |
| soc_reg = reg_get_psoc_obj(psoc); |
| |
| if (!IS_VALID_PSOC_REG_OBJ(soc_reg)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| tx_ops = reg_get_psoc_tx_ops(psoc); |
| phy_id = regulat_info->phy_id; |
| |
| if (reg_ignore_default_country(soc_reg, regulat_info)) { |
| status = reg_set_curr_country(soc_reg, regulat_info, tx_ops); |
| if (QDF_IS_STATUS_SUCCESS(status)) { |
| reg_debug("WLAN restart - Ignore default CC for phy_id: %u", |
| phy_id); |
| return QDF_STATUS_SUCCESS; |
| } |
| } |
| |
| reg_debug("process reg master chan list"); |
| |
| if (soc_reg->offload_enabled) { |
| dbg_id = WLAN_REGULATORY_NB_ID; |
| dir = NORTHBOUND; |
| } else { |
| dbg_id = WLAN_REGULATORY_SB_ID; |
| dir = SOUTHBOUND; |
| } |
| |
| if (regulat_info->status_code != REG_SET_CC_STATUS_PASS) { |
| reg_err("Setting country code failed, status code is %d", |
| regulat_info->status_code); |
| |
| pdev = wlan_objmgr_get_pdev_by_id(psoc, phy_id, dbg_id); |
| if (!pdev) { |
| reg_err("pdev is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (tx_ops->set_country_failed) |
| tx_ops->set_country_failed(pdev); |
| |
| wlan_objmgr_pdev_release_ref(pdev, dbg_id); |
| |
| if (regulat_info->status_code != REG_CURRENT_ALPHA2_NOT_FOUND) |
| return QDF_STATUS_E_FAILURE; |
| |
| soc_reg->new_user_ctry_pending[phy_id] = false; |
| soc_reg->new_11d_ctry_pending[phy_id] = false; |
| soc_reg->world_country_pending[phy_id] = true; |
| } |
| |
| mas_chan_list = soc_reg->mas_chan_params[phy_id].mas_chan_list; |
| |
| reg_init_channel_map(regulat_info->dfs_region); |
| |
| for (chan_enum = 0; chan_enum < NUM_CHANNELS; |
| chan_enum++) { |
| mas_chan_list[chan_enum].chan_num = |
| channel_map[chan_enum].chan_num; |
| mas_chan_list[chan_enum].center_freq = |
| channel_map[chan_enum].center_freq; |
| mas_chan_list[chan_enum].chan_flags = |
| REGULATORY_CHAN_DISABLED; |
| mas_chan_list[chan_enum].state = |
| CHANNEL_STATE_DISABLE; |
| mas_chan_list[chan_enum].nol_chan = false; |
| } |
| |
| soc_reg->num_phy = regulat_info->num_phy; |
| soc_reg->mas_chan_params[phy_id].phybitmap = |
| regulat_info->phybitmap; |
| soc_reg->mas_chan_params[phy_id].dfs_region = |
| regulat_info->dfs_region; |
| soc_reg->mas_chan_params[phy_id].ctry_code = |
| regulat_info->ctry_code; |
| soc_reg->mas_chan_params[phy_id].reg_dmn_pair = |
| regulat_info->reg_dmn_pair; |
| qdf_mem_copy(soc_reg->mas_chan_params[phy_id].current_country, |
| regulat_info->alpha2, |
| REG_ALPHA2_LEN + 1); |
| qdf_mem_copy(soc_reg->cur_country, |
| regulat_info->alpha2, |
| REG_ALPHA2_LEN + 1); |
| reg_debug("set cur_country %.2s", soc_reg->cur_country); |
| |
| min_bw_2g = regulat_info->min_bw_2g; |
| max_bw_2g = regulat_info->max_bw_2g; |
| reg_rule_2g = regulat_info->reg_rules_2g_ptr; |
| num_2g_reg_rules = regulat_info->num_2g_reg_rules; |
| reg_update_max_bw_per_rule(num_2g_reg_rules, |
| reg_rule_2g, max_bw_2g); |
| |
| min_bw_5g = regulat_info->min_bw_5g; |
| max_bw_5g = regulat_info->max_bw_5g; |
| reg_rule_5g = regulat_info->reg_rules_5g_ptr; |
| num_5g_reg_rules = regulat_info->num_5g_reg_rules; |
| reg_update_max_bw_per_rule(num_5g_reg_rules, |
| reg_rule_5g, max_bw_5g); |
| |
| reg_rules = &soc_reg->mas_chan_params[phy_id].reg_rules; |
| reg_reset_reg_rules(reg_rules); |
| |
| reg_rules->num_of_reg_rules = num_5g_reg_rules + num_2g_reg_rules; |
| if (reg_rules->num_of_reg_rules > MAX_REG_RULES) { |
| reg_err("number of reg rules exceeds limit"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| if (reg_rules->num_of_reg_rules) { |
| if (num_2g_reg_rules) |
| qdf_mem_copy(reg_rules->reg_rules, |
| reg_rule_2g, num_2g_reg_rules * |
| sizeof(struct cur_reg_rule)); |
| if (num_5g_reg_rules) |
| qdf_mem_copy(reg_rules->reg_rules + |
| num_2g_reg_rules, reg_rule_5g, |
| num_5g_reg_rules * |
| sizeof(struct cur_reg_rule)); |
| } |
| |
| if (num_5g_reg_rules != 0) |
| reg_do_auto_bw_correction(num_5g_reg_rules, |
| reg_rule_5g, max_bw_5g); |
| |
| if (num_2g_reg_rules != 0) |
| reg_populate_band_channels(MIN_24GHZ_CHANNEL, MAX_24GHZ_CHANNEL, |
| reg_rule_2g, num_2g_reg_rules, |
| min_bw_2g, mas_chan_list); |
| |
| if (num_5g_reg_rules != 0) { |
| reg_populate_band_channels(MIN_5GHZ_CHANNEL, MAX_5GHZ_CHANNEL, |
| reg_rule_5g, num_5g_reg_rules, |
| min_bw_5g, mas_chan_list); |
| reg_populate_band_channels(MIN_49GHZ_CHANNEL, |
| MAX_49GHZ_CHANNEL, |
| reg_rule_5g, num_5g_reg_rules, |
| min_bw_5g, mas_chan_list); |
| reg_populate_6g_band_channels(reg_rule_5g, |
| num_5g_reg_rules, |
| min_bw_5g, |
| mas_chan_list); |
| } |
| |
| soc_reg->chan_list_recvd[phy_id] = true; |
| if (soc_reg->new_user_ctry_pending[phy_id]) { |
| soc_reg->new_user_ctry_pending[phy_id] = false; |
| soc_reg->cc_src = SOURCE_USERSPACE; |
| soc_reg->user_ctry_set = true; |
| reg_debug("new user country is set"); |
| reg_run_11d_state_machine(psoc); |
| } else if (soc_reg->new_init_ctry_pending[phy_id]) { |
| soc_reg->new_init_ctry_pending[phy_id] = false; |
| soc_reg->cc_src = SOURCE_USERSPACE; |
| reg_debug("new init country is set"); |
| } else if (soc_reg->new_11d_ctry_pending[phy_id]) { |
| soc_reg->new_11d_ctry_pending[phy_id] = false; |
| soc_reg->cc_src = SOURCE_11D; |
| soc_reg->user_ctry_set = false; |
| reg_run_11d_state_machine(psoc); |
| } else if (soc_reg->world_country_pending[phy_id]) { |
| soc_reg->world_country_pending[phy_id] = false; |
| soc_reg->cc_src = SOURCE_CORE; |
| soc_reg->user_ctry_set = false; |
| reg_run_11d_state_machine(psoc); |
| } else { |
| if (soc_reg->cc_src == SOURCE_UNKNOWN && |
| soc_reg->num_phy == phy_id + 1) |
| soc_reg->cc_src = SOURCE_DRIVER; |
| |
| qdf_mem_copy(soc_reg->mas_chan_params[phy_id].default_country, |
| regulat_info->alpha2, |
| REG_ALPHA2_LEN + 1); |
| |
| soc_reg->mas_chan_params[phy_id].def_country_code = |
| regulat_info->ctry_code; |
| soc_reg->mas_chan_params[phy_id].def_region_domain = |
| regulat_info->reg_dmn_pair; |
| |
| if (soc_reg->cc_src == SOURCE_DRIVER) { |
| qdf_mem_copy(soc_reg->def_country, |
| regulat_info->alpha2, |
| REG_ALPHA2_LEN + 1); |
| |
| soc_reg->def_country_code = regulat_info->ctry_code; |
| soc_reg->def_region_domain = |
| regulat_info->reg_dmn_pair; |
| |
| if (reg_is_world_alpha2(regulat_info->alpha2)) { |
| soc_reg->cc_src = SOURCE_CORE; |
| reg_run_11d_state_machine(psoc); |
| } |
| } |
| } |
| |
| pdev = wlan_objmgr_get_pdev_by_id(psoc, phy_id, dbg_id); |
| if (pdev) { |
| reg_propagate_mas_chan_list_to_pdev(psoc, pdev, &dir); |
| wlan_objmgr_pdev_release_ref(pdev, dbg_id); |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS reg_get_current_chan_list(struct wlan_objmgr_pdev *pdev, |
| struct regulatory_channel *chan_list) |
| { |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("reg pdev private obj is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| qdf_mem_copy(chan_list, pdev_priv_obj->cur_chan_list, |
| NUM_CHANNELS * sizeof(struct regulatory_channel)); |
| |
| return QDF_STATUS_SUCCESS; |
| } |