| /* |
| * 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_utils.c |
| * This file defines the APIs to set and get the regulatory variables. |
| */ |
| |
| #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_db.h" |
| #include "reg_db_parser.h" |
| #include "reg_host_11d.h" |
| #include <scheduler_api.h> |
| #include <wlan_reg_services_api.h> |
| #include <qdf_platform.h> |
| #include "reg_services_common.h" |
| #include "reg_build_chan_list.h" |
| |
| #define DEFAULT_WORLD_REGDMN 0x60 |
| |
| #define IS_VALID_PSOC_REG_OBJ(psoc_priv_obj) (psoc_priv_obj) |
| #define IS_VALID_PDEV_REG_OBJ(pdev_priv_obj) (pdev_priv_obj) |
| |
| #ifdef CONFIG_CHAN_NUM_API |
| bool reg_chan_has_dfs_attribute(struct wlan_objmgr_pdev *pdev, uint32_t ch) |
| { |
| enum channel_enum ch_idx; |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| |
| ch_idx = reg_get_chan_enum(ch); |
| |
| if (ch_idx == INVALID_CHANNEL) |
| return false; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("pdev reg obj is NULL"); |
| return false; |
| } |
| |
| if (pdev_priv_obj->cur_chan_list[ch_idx].chan_flags & |
| REGULATORY_CHAN_RADAR) |
| return true; |
| |
| return false; |
| } |
| #endif /* CONFIG_CHAN_NUM_API */ |
| |
| #ifdef CONFIG_CHAN_FREQ_API |
| bool reg_chan_has_dfs_attribute_for_freq(struct wlan_objmgr_pdev *pdev, |
| uint16_t freq) |
| { |
| enum channel_enum ch_idx; |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| |
| ch_idx = reg_get_chan_enum_for_freq(freq); |
| |
| if (ch_idx == INVALID_CHANNEL) |
| return false; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("pdev reg obj is NULL"); |
| return false; |
| } |
| |
| if (pdev_priv_obj->cur_chan_list[ch_idx].chan_flags & |
| REGULATORY_CHAN_RADAR) |
| return true; |
| |
| return false; |
| } |
| #endif /* CONFIG_CHAN_FREQ_API */ |
| |
| bool reg_is_world_ctry_code(uint16_t ctry_code) |
| { |
| if ((ctry_code & 0xFFF0) == DEFAULT_WORLD_REGDMN) |
| return true; |
| |
| return false; |
| } |
| |
| QDF_STATUS reg_read_current_country(struct wlan_objmgr_psoc *psoc, |
| uint8_t *country_code) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_reg; |
| |
| if (!country_code) { |
| reg_err("country_code is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc_reg = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_reg)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| qdf_mem_copy(country_code, psoc_reg->cur_country, REG_ALPHA2_LEN + 1); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| /** |
| * reg_set_default_country() - Read the default country for the regdomain |
| * @country: country code. |
| * |
| * Return: QDF_STATUS |
| */ |
| QDF_STATUS reg_set_default_country(struct wlan_objmgr_psoc *psoc, |
| uint8_t *country) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_reg; |
| |
| if (!country) { |
| reg_err("country is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc_reg = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_reg)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| reg_info("setting default_country: %s", country); |
| |
| qdf_mem_copy(psoc_reg->def_country, country, REG_ALPHA2_LEN + 1); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| bool reg_is_world_alpha2(uint8_t *alpha2) |
| { |
| if ((alpha2[0] == '0') && (alpha2[1] == '0')) |
| return true; |
| |
| return false; |
| } |
| |
| bool reg_is_us_alpha2(uint8_t *alpha2) |
| { |
| if ((alpha2[0] == 'U') && (alpha2[1] == 'S')) |
| return true; |
| |
| return false; |
| } |
| |
| QDF_STATUS reg_set_country(struct wlan_objmgr_pdev *pdev, |
| uint8_t *country) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_reg; |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| struct wlan_lmac_if_reg_tx_ops *tx_ops; |
| struct set_country cc; |
| struct wlan_objmgr_psoc *psoc; |
| struct cc_regdmn_s rd; |
| uint8_t pdev_id; |
| |
| if (!pdev) { |
| reg_err("pdev is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (!country) { |
| reg_err("country code is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| pdev_id = wlan_objmgr_pdev_get_pdev_id(pdev); |
| |
| psoc = wlan_pdev_get_psoc(pdev); |
| |
| psoc_reg = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_reg)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (!qdf_mem_cmp(psoc_reg->cur_country, country, REG_ALPHA2_LEN)) { |
| reg_err("country is not different"); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| reg_debug("programming new country: %s to firmware", country); |
| |
| qdf_mem_copy(cc.country, country, REG_ALPHA2_LEN + 1); |
| cc.pdev_id = pdev_id; |
| |
| if (!psoc_reg->offload_enabled && !reg_is_world_alpha2(country)) { |
| QDF_STATUS status; |
| |
| status = reg_is_country_code_valid(country); |
| if (!QDF_IS_STATUS_SUCCESS(status)) { |
| reg_err("Unable to set country code: %s\n", country); |
| reg_err("Restoring to world domain"); |
| qdf_mem_copy(cc.country, REG_WORLD_ALPHA2, |
| REG_ALPHA2_LEN + 1); |
| } |
| } |
| |
| |
| if (reg_is_world_alpha2(cc.country)) |
| psoc_reg->world_country_pending[pdev_id] = true; |
| else |
| psoc_reg->new_user_ctry_pending[pdev_id] = true; |
| |
| if (psoc_reg->offload_enabled) { |
| tx_ops = reg_get_psoc_tx_ops(psoc); |
| if (tx_ops->set_country_code) { |
| tx_ops->set_country_code(psoc, &cc); |
| } else { |
| reg_err("country set fw handler not present"); |
| psoc_reg->new_user_ctry_pending[pdev_id] = false; |
| return QDF_STATUS_E_FAULT; |
| } |
| } else { |
| if (reg_is_world_alpha2(cc.country)) { |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("reg component pdev priv is NULL"); |
| psoc_reg->world_country_pending[pdev_id] = |
| false; |
| return QDF_STATUS_E_INVAL; |
| } |
| if (reg_is_world_ctry_code( |
| pdev_priv_obj->def_region_domain)) |
| rd.cc.regdmn_id = |
| pdev_priv_obj->def_region_domain; |
| else |
| rd.cc.regdmn_id = DEFAULT_WORLD_REGDMN; |
| rd.flags = REGDMN_IS_SET; |
| } else { |
| qdf_mem_copy(rd.cc.alpha, cc.country, |
| REG_ALPHA2_LEN + 1); |
| rd.flags = ALPHA_IS_SET; |
| } |
| |
| reg_program_chan_list(pdev, &rd); |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS reg_reset_country(struct wlan_objmgr_psoc *psoc) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_reg; |
| |
| psoc_reg = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_reg)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| reg_info("re-setting user country to default"); |
| qdf_mem_copy(psoc_reg->cur_country, |
| psoc_reg->def_country, |
| REG_ALPHA2_LEN + 1); |
| reg_debug("set cur_country %.2s", psoc_reg->cur_country); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| QDF_STATUS reg_get_domain_from_country_code(v_REGDOMAIN_t *reg_domain_ptr, |
| const uint8_t *country_alpha2, |
| enum country_src source) |
| { |
| if (!reg_domain_ptr) { |
| reg_err("Invalid reg domain pointer"); |
| return QDF_STATUS_E_FAULT; |
| } |
| |
| *reg_domain_ptr = 0; |
| |
| if (!country_alpha2) { |
| reg_err("Country code array is NULL"); |
| return QDF_STATUS_E_FAULT; |
| } |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| #ifdef CONFIG_CHAN_NUM_API |
| bool reg_is_passive_or_disable_ch(struct wlan_objmgr_pdev *pdev, |
| uint32_t chan) |
| { |
| enum channel_state ch_state; |
| |
| ch_state = reg_get_channel_state(pdev, chan); |
| |
| return (ch_state == CHANNEL_STATE_DFS) || |
| (ch_state == CHANNEL_STATE_DISABLE); |
| } |
| #endif /* CONFIG_CHAN_NUM_API */ |
| |
| #ifdef CONFIG_CHAN_FREQ_API |
| bool reg_is_passive_or_disable_for_freq(struct wlan_objmgr_pdev *pdev, |
| uint16_t freq) |
| { |
| enum channel_state chan_state; |
| |
| chan_state = reg_get_channel_state_for_freq(pdev, freq); |
| |
| return (chan_state == CHANNEL_STATE_DFS) || |
| (chan_state == CHANNEL_STATE_DISABLE); |
| } |
| #endif /* CONFIG_CHAN_FREQ_API */ |
| |
| #ifdef WLAN_FEATURE_DSRC |
| #ifdef CONFIG_CHAN_FREQ_API |
| bool reg_is_dsrc_freq(uint16_t freq) |
| { |
| if (!REG_IS_5GHZ_FREQ(chan)) |
| return false; |
| |
| if (!(freq >= REG_DSRC_START_FREQ && freq <= REG_DSRC_END_FREQ)) |
| return false; |
| |
| return true; |
| } |
| #endif /*CONFIG_CHAN_FREQ_API*/ |
| |
| #ifdef CONFIG_CHAN_NUM_API |
| bool reg_is_dsrc_chan(struct wlan_objmgr_pdev *pdev, uint32_t chan) |
| { |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| uint32_t freq = 0; |
| |
| 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 false; |
| } |
| |
| if (!REG_IS_5GHZ_CH(chan)) |
| return false; |
| |
| freq = reg_chan_to_freq(pdev, chan); |
| |
| if (!(freq >= REG_DSRC_START_FREQ && freq <= REG_DSRC_END_FREQ)) |
| return false; |
| |
| return true; |
| } |
| #endif /* CONFIG_CHAN_NUM_API */ |
| |
| #else |
| |
| bool reg_is_etsi13_regdmn(struct wlan_objmgr_pdev *pdev) |
| { |
| struct cur_regdmn_info cur_reg_dmn; |
| QDF_STATUS status; |
| |
| status = reg_get_curr_regdomain(pdev, &cur_reg_dmn); |
| if (status != QDF_STATUS_SUCCESS) { |
| reg_err_rl("Failed to get reg domain"); |
| return false; |
| } |
| |
| return reg_etsi13_regdmn(cur_reg_dmn.dmn_id_5g); |
| } |
| |
| #ifdef CONFIG_CHAN_FREQ_API |
| bool reg_is_etsi13_srd_chan_for_freq(struct wlan_objmgr_pdev *pdev, |
| uint16_t freq) |
| { |
| if (!REG_IS_5GHZ_FREQ(freq)) |
| return false; |
| |
| if (!(freq >= REG_ETSI13_SRD_START_FREQ && |
| freq <= REG_ETSI13_SRD_END_FREQ)) |
| return false; |
| |
| return reg_is_etsi13_regdmn(pdev); |
| } |
| #endif /* CONFIG_CHAN_FREQ_API */ |
| |
| #ifdef CONFIG_CHAN_NUM_API |
| bool reg_is_etsi13_srd_chan(struct wlan_objmgr_pdev *pdev, uint32_t chan) |
| { |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| uint32_t freq = 0; |
| |
| 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 false; |
| } |
| |
| if (!REG_IS_5GHZ_CH(chan)) |
| return false; |
| |
| freq = reg_chan_to_freq(pdev, chan); |
| |
| if (!(freq >= REG_ETSI13_SRD_START_FREQ && |
| freq <= REG_ETSI13_SRD_END_FREQ)) |
| return false; |
| |
| return reg_is_etsi13_regdmn(pdev); |
| } |
| #endif /* CONFIG_CHAN_NUM_API */ |
| |
| bool reg_is_etsi13_srd_chan_allowed_master_mode(struct wlan_objmgr_pdev *pdev) |
| { |
| struct wlan_objmgr_psoc *psoc; |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| |
| if (!pdev) { |
| reg_alert("pdev is NULL"); |
| return true; |
| } |
| psoc = wlan_pdev_get_psoc(pdev); |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { |
| reg_alert("psoc reg component is NULL"); |
| return true; |
| } |
| |
| return psoc_priv_obj->enable_srd_chan_in_master_mode && |
| reg_is_etsi13_regdmn(pdev); |
| } |
| #endif |
| |
| QDF_STATUS reg_set_band(struct wlan_objmgr_pdev *pdev, |
| enum band_info band) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| struct wlan_objmgr_psoc *psoc; |
| QDF_STATUS status; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("pdev reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (pdev_priv_obj->band_capability == band) { |
| reg_info("band is already set to %d", band); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| psoc = wlan_pdev_get_psoc(pdev); |
| if (!psoc) { |
| reg_err("psoc is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| reg_info("setting band_info: %d", band); |
| pdev_priv_obj->band_capability = band; |
| |
| reg_compute_pdev_current_chan_list(pdev_priv_obj); |
| |
| status = reg_send_scheduler_msg_sb(psoc, pdev); |
| |
| return status; |
| } |
| |
| QDF_STATUS reg_get_band(struct wlan_objmgr_pdev *pdev, |
| enum band_info *band) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| struct wlan_objmgr_psoc *psoc; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("pdev reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc = wlan_pdev_get_psoc(pdev); |
| if (!psoc) { |
| reg_err("psoc is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| reg_debug("getting band_info: %d", pdev_priv_obj->band_capability); |
| *band = pdev_priv_obj->band_capability; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| #ifdef DISABLE_CHANNEL_LIST |
| QDF_STATUS reg_restore_cached_channels(struct wlan_objmgr_pdev *pdev) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| struct wlan_objmgr_psoc *psoc; |
| QDF_STATUS status; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("pdev reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc = wlan_pdev_get_psoc(pdev); |
| if (!psoc) { |
| reg_err("psoc is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| pdev_priv_obj->disable_cached_channels = false; |
| reg_compute_pdev_current_chan_list(pdev_priv_obj); |
| status = reg_send_scheduler_msg_sb(psoc, pdev); |
| return status; |
| } |
| |
| #ifdef CONFIG_CHAN_FREQ_API |
| QDF_STATUS reg_cache_channel_freq_state(struct wlan_objmgr_pdev *pdev, |
| uint32_t *channel_list, |
| uint32_t num_channels) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| struct wlan_objmgr_psoc *psoc; |
| uint16_t i, j; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("pdev reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc = wlan_pdev_get_psoc(pdev); |
| if (!psoc) { |
| reg_err("psoc is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (pdev_priv_obj->num_cache_channels > 0) { |
| pdev_priv_obj->num_cache_channels = 0; |
| qdf_mem_zero(&pdev_priv_obj->cache_disable_chan_list, |
| sizeof(pdev_priv_obj->cache_disable_chan_list)); |
| } |
| |
| for (i = 0; i < num_channels; i++) { |
| for (j = 0; j < NUM_CHANNELS; j++) { |
| if (channel_list[i] == pdev_priv_obj-> |
| cur_chan_list[j].center_freq) { |
| pdev_priv_obj-> |
| cache_disable_chan_list[i].center_freq = |
| channel_list[i]; |
| pdev_priv_obj-> |
| cache_disable_chan_list[i].state = |
| pdev_priv_obj->cur_chan_list[j].state; |
| pdev_priv_obj-> |
| cache_disable_chan_list[i].chan_flags = |
| pdev_priv_obj-> |
| cur_chan_list[j].chan_flags; |
| } |
| } |
| } |
| pdev_priv_obj->num_cache_channels = num_channels; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| #endif /* CONFIG_CHAN_FREQ_API */ |
| |
| #ifdef CONFIG_CHAN_NUM_API |
| QDF_STATUS reg_cache_channel_state(struct wlan_objmgr_pdev *pdev, |
| uint32_t *channel_list, |
| uint32_t num_channels) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| struct wlan_objmgr_psoc *psoc; |
| uint8_t i, j; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("pdev reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc = wlan_pdev_get_psoc(pdev); |
| if (!psoc) { |
| reg_err("psoc is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| if (pdev_priv_obj->num_cache_channels > 0) { |
| pdev_priv_obj->num_cache_channels = 0; |
| qdf_mem_zero(&pdev_priv_obj->cache_disable_chan_list, |
| sizeof(pdev_priv_obj->cache_disable_chan_list)); |
| } |
| |
| for (i = 0; i < num_channels; i++) { |
| for (j = 0; j < NUM_CHANNELS; j++) { |
| if (channel_list[i] == pdev_priv_obj-> |
| cur_chan_list[j].chan_num) { |
| pdev_priv_obj-> |
| cache_disable_chan_list[i].chan_num = |
| channel_list[i]; |
| pdev_priv_obj-> |
| cache_disable_chan_list[i].state = |
| pdev_priv_obj->cur_chan_list[j].state; |
| pdev_priv_obj-> |
| cache_disable_chan_list[i].chan_flags = |
| pdev_priv_obj-> |
| cur_chan_list[j].chan_flags; |
| } |
| } |
| } |
| pdev_priv_obj->num_cache_channels = num_channels; |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| #endif /* CONFIG_CHAN_NUM_API */ |
| void set_disable_channel_state( |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj) |
| { |
| pdev_priv_obj->disable_cached_channels = pdev_priv_obj->sap_state; |
| } |
| #endif |
| |
| #ifdef CONFIG_REG_CLIENT |
| |
| QDF_STATUS reg_set_fcc_constraint(struct wlan_objmgr_pdev *pdev, |
| bool fcc_constraint) |
| { |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| struct wlan_objmgr_psoc *psoc; |
| QDF_STATUS status; |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| if (!IS_VALID_PDEV_REG_OBJ(pdev_priv_obj)) { |
| reg_err("pdev reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| if (pdev_priv_obj->set_fcc_channel == fcc_constraint) { |
| reg_info("fcc_constraint is already set to %d", fcc_constraint); |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| reg_info("setting set_fcc_channel: %d", fcc_constraint); |
| pdev_priv_obj->set_fcc_channel = fcc_constraint; |
| |
| psoc = wlan_pdev_get_psoc(pdev); |
| if (!psoc) { |
| reg_err("psoc is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!IS_VALID_PSOC_REG_OBJ(psoc_priv_obj)) { |
| reg_err("psoc reg component is NULL"); |
| return QDF_STATUS_E_INVAL; |
| } |
| |
| reg_compute_pdev_current_chan_list(pdev_priv_obj); |
| |
| status = reg_send_scheduler_msg_sb(psoc, pdev); |
| |
| return status; |
| } |
| |
| bool reg_get_fcc_constraint(struct wlan_objmgr_pdev *pdev, uint32_t freq) |
| { |
| 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("pdev reg component is NULL"); |
| return false; |
| } |
| |
| if (freq != CHAN_12_CENT_FREQ && freq != CHAN_13_CENT_FREQ) |
| return false; |
| |
| if (!pdev_priv_obj->set_fcc_channel) |
| return false; |
| |
| return true; |
| } |
| |
| #endif /* CONFIG_REG_CLIENT */ |
| |
| /** |
| * reg_change_pdev_for_config() - Update user configuration in pdev private obj. |
| * @psoc: Pointer to global psoc structure. |
| * @object: Pointer to global pdev structure. |
| * @arg: Pointer to argument list. |
| */ |
| static void reg_change_pdev_for_config(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; |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| 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 private obj is NULL"); |
| return; |
| } |
| |
| pdev_priv_obj->dfs_enabled = psoc_priv_obj->dfs_enabled; |
| pdev_priv_obj->indoor_chan_enabled = psoc_priv_obj->indoor_chan_enabled; |
| pdev_priv_obj->force_ssc_disable_indoor_channel = |
| psoc_priv_obj->force_ssc_disable_indoor_channel; |
| pdev_priv_obj->band_capability = psoc_priv_obj->band_capability; |
| |
| reg_compute_pdev_current_chan_list(pdev_priv_obj); |
| |
| reg_send_scheduler_msg_sb(psoc, pdev); |
| } |
| |
| QDF_STATUS reg_set_config_vars(struct wlan_objmgr_psoc *psoc, |
| struct reg_config_vars config_vars) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| QDF_STATUS status; |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!psoc_priv_obj) { |
| reg_err("psoc priv obj is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| psoc_priv_obj->enable_11d_supp_original = |
| config_vars.enable_11d_support; |
| psoc_priv_obj->scan_11d_interval = config_vars.scan_11d_interval; |
| psoc_priv_obj->user_ctry_priority = config_vars.userspace_ctry_priority; |
| psoc_priv_obj->dfs_enabled = config_vars.dfs_enabled; |
| psoc_priv_obj->indoor_chan_enabled = config_vars.indoor_chan_enabled; |
| psoc_priv_obj->force_ssc_disable_indoor_channel = |
| config_vars.force_ssc_disable_indoor_channel; |
| psoc_priv_obj->band_capability = config_vars.band_capability; |
| psoc_priv_obj->restart_beaconing = config_vars.restart_beaconing; |
| psoc_priv_obj->enable_srd_chan_in_master_mode = |
| config_vars.enable_srd_chan_in_master_mode; |
| psoc_priv_obj->enable_11d_in_world_mode = |
| config_vars.enable_11d_in_world_mode; |
| |
| status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| reg_err("error taking psoc ref cnt"); |
| return status; |
| } |
| status = wlan_objmgr_iterate_obj_list(psoc, WLAN_PDEV_OP, |
| reg_change_pdev_for_config, |
| NULL, 1, WLAN_REGULATORY_SB_ID); |
| wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); |
| |
| return status; |
| } |
| |
| #ifdef CONFIG_CHAN_FREQ_API |
| bool reg_is_disable_for_freq(struct wlan_objmgr_pdev *pdev, uint16_t freq) |
| { |
| enum channel_state ch_state; |
| |
| ch_state = reg_get_channel_state_for_freq(pdev, freq); |
| |
| return ch_state == CHANNEL_STATE_DISABLE; |
| } |
| #endif /* CONFIG_CHAN_FREQ_API */ |
| |
| #ifdef CONFIG_CHAN_NUM_API |
| bool reg_is_disable_ch(struct wlan_objmgr_pdev *pdev, uint32_t chan) |
| { |
| enum channel_state ch_state; |
| |
| ch_state = reg_get_channel_state(pdev, chan); |
| |
| return ch_state == CHANNEL_STATE_DISABLE; |
| } |
| #endif /* CONFIG_CHAN_NUM_API */ |
| |
| bool reg_is_regdb_offloaded(struct wlan_objmgr_psoc *psoc) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!psoc_priv_obj) { |
| reg_err("reg psoc private obj is NULL"); |
| return false; |
| } |
| |
| return psoc_priv_obj->offload_enabled; |
| } |
| |
| void reg_program_mas_chan_list(struct wlan_objmgr_psoc *psoc, |
| struct regulatory_channel *reg_channels, |
| uint8_t *alpha2, |
| enum dfs_reg dfs_region) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| QDF_STATUS status; |
| uint32_t count; |
| enum direction dir; |
| uint32_t pdev_cnt; |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!psoc_priv_obj) { |
| reg_err("reg psoc private obj is NULL"); |
| return; |
| } |
| |
| qdf_mem_copy(psoc_priv_obj->cur_country, alpha2, |
| REG_ALPHA2_LEN); |
| reg_debug("set cur_country %.2s", psoc_priv_obj->cur_country); |
| for (count = 0; count < NUM_CHANNELS; count++) { |
| reg_channels[count].chan_num = channel_map[count].chan_num; |
| reg_channels[count].center_freq = |
| channel_map[count].center_freq; |
| reg_channels[count].nol_chan = false; |
| } |
| |
| for (pdev_cnt = 0; pdev_cnt < PSOC_MAX_PHY_REG_CAP; pdev_cnt++) { |
| qdf_mem_copy(psoc_priv_obj->mas_chan_params[pdev_cnt]. |
| mas_chan_list, reg_channels, |
| NUM_CHANNELS * sizeof(struct regulatory_channel)); |
| |
| psoc_priv_obj->mas_chan_params[pdev_cnt].dfs_region = |
| dfs_region; |
| } |
| |
| dir = SOUTHBOUND; |
| status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_SB_ID); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| reg_err("error taking psoc ref cnt"); |
| return; |
| } |
| status = wlan_objmgr_iterate_obj_list( |
| psoc, WLAN_PDEV_OP, reg_propagate_mas_chan_list_to_pdev, |
| &dir, 1, WLAN_REGULATORY_SB_ID); |
| wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_SB_ID); |
| } |
| |
| enum country_src reg_get_cc_and_src(struct wlan_objmgr_psoc *psoc, |
| uint8_t *alpha2) |
| { |
| struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj; |
| |
| psoc_priv_obj = reg_get_psoc_obj(psoc); |
| if (!psoc_priv_obj) { |
| reg_err("reg psoc private obj is NULL"); |
| return SOURCE_UNKNOWN; |
| } |
| |
| qdf_mem_copy(alpha2, psoc_priv_obj->cur_country, REG_ALPHA2_LEN + 1); |
| |
| return psoc_priv_obj->cc_src; |
| } |
| |
| QDF_STATUS reg_get_regd_rules(struct wlan_objmgr_pdev *pdev, |
| struct reg_rule_info *reg_rules) |
| { |
| struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj; |
| |
| if (!pdev) { |
| reg_err("pdev is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| pdev_priv_obj = reg_get_pdev_obj(pdev); |
| if (!pdev_priv_obj) { |
| reg_err("pdev priv obj is NULL"); |
| return QDF_STATUS_E_FAILURE; |
| } |
| |
| qdf_spin_lock_bh(&pdev_priv_obj->reg_rules_lock); |
| qdf_mem_copy(reg_rules, &pdev_priv_obj->reg_rules, |
| sizeof(struct reg_rule_info)); |
| qdf_spin_unlock_bh(&pdev_priv_obj->reg_rules_lock); |
| |
| return QDF_STATUS_SUCCESS; |
| } |
| |
| void reg_reset_ctry_pending_hints(struct wlan_regulatory_psoc_priv_obj |
| *soc_reg) |
| { |
| uint8_t ctr; |
| |
| if (!soc_reg->offload_enabled) |
| return; |
| |
| for (ctr = 0; ctr < PSOC_MAX_PHY_REG_CAP; ctr++) { |
| soc_reg->new_user_ctry_pending[ctr] = false; |
| soc_reg->new_init_ctry_pending[ctr] = false; |
| soc_reg->new_11d_ctry_pending[ctr] = false; |
| soc_reg->world_country_pending[ctr] = false; |
| } |
| } |
| |
| QDF_STATUS reg_set_curr_country(struct wlan_regulatory_psoc_priv_obj *soc_reg, |
| struct cur_regulatory_info *regulat_info, |
| struct wlan_lmac_if_reg_tx_ops *tx_ops) |
| { |
| struct wlan_objmgr_psoc *psoc = regulat_info->psoc; |
| uint8_t pdev_id; |
| uint8_t phy_num; |
| struct set_country country_code; |
| QDF_STATUS status; |
| |
| /* |
| * During SSR/WLAN restart ignore master channel list |
| * for all events and in the last event handling if |
| * current country and default country is different, send the last |
| * configured (soc_reg->cur_country) country. |
| */ |
| if ((regulat_info->num_phy != regulat_info->phy_id + 1) || |
| (!qdf_mem_cmp(soc_reg->cur_country, regulat_info->alpha2, |
| REG_ALPHA2_LEN))) |
| return QDF_STATUS_SUCCESS; |
| |
| /* |
| * Need firmware to send channel list event |
| * for all phys. Therefore set pdev_id to 0xFF |
| */ |
| pdev_id = 0xFF; |
| for (phy_num = 0; phy_num < regulat_info->num_phy; phy_num++) { |
| if (soc_reg->cc_src == SOURCE_USERSPACE) |
| soc_reg->new_user_ctry_pending[phy_num] = true; |
| else if (soc_reg->cc_src == SOURCE_11D) |
| soc_reg->new_11d_ctry_pending[phy_num] = true; |
| else |
| soc_reg->world_country_pending[phy_num] = true; |
| } |
| |
| qdf_mem_zero(&country_code, sizeof(country_code)); |
| qdf_mem_copy(country_code.country, soc_reg->cur_country, |
| sizeof(soc_reg->cur_country)); |
| country_code.pdev_id = pdev_id; |
| |
| if (!tx_ops || !tx_ops->set_country_code) { |
| reg_err("No regulatory tx_ops for set_country_code"); |
| status = QDF_STATUS_E_FAULT; |
| goto error; |
| } |
| |
| status = tx_ops->set_country_code(psoc, &country_code); |
| if (QDF_IS_STATUS_ERROR(status)) { |
| reg_err("Failed to send country code to firmware"); |
| goto error; |
| } |
| |
| reg_debug("Target CC: %.2s, Restore to Previous CC: %.2s", |
| regulat_info->alpha2, soc_reg->cur_country); |
| |
| return status; |
| |
| error: |
| reg_reset_ctry_pending_hints(soc_reg); |
| |
| return status; |
| } |
| |
| bool reg_ignore_default_country(struct wlan_regulatory_psoc_priv_obj *soc_reg, |
| struct cur_regulatory_info *regulat_info) |
| { |
| uint8_t phy_num; |
| |
| if (!soc_reg->offload_enabled) |
| return false; |
| |
| if (soc_reg->cc_src == SOURCE_UNKNOWN) |
| return false; |
| |
| phy_num = regulat_info->phy_id; |
| if (soc_reg->new_user_ctry_pending[phy_num] || |
| soc_reg->new_init_ctry_pending[phy_num] || |
| soc_reg->new_11d_ctry_pending[phy_num] || |
| soc_reg->world_country_pending[phy_num]) |
| return false; |
| |
| return true; |
| } |