162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause-Clear 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/rtnetlink.h> 762306a36Sopenharmony_ci#include "core.h" 862306a36Sopenharmony_ci#include "debug.h" 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* World regdom to be used in case default regd from fw is unavailable */ 1162306a36Sopenharmony_ci#define ATH12K_2GHZ_CH01_11 REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0) 1262306a36Sopenharmony_ci#define ATH12K_5GHZ_5150_5350 REG_RULE(5150 - 10, 5350 + 10, 80, 0, 30,\ 1362306a36Sopenharmony_ci NL80211_RRF_NO_IR) 1462306a36Sopenharmony_ci#define ATH12K_5GHZ_5725_5850 REG_RULE(5725 - 10, 5850 + 10, 80, 0, 30,\ 1562306a36Sopenharmony_ci NL80211_RRF_NO_IR) 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define ETSI_WEATHER_RADAR_BAND_LOW 5590 1862306a36Sopenharmony_ci#define ETSI_WEATHER_RADAR_BAND_HIGH 5650 1962306a36Sopenharmony_ci#define ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT 600000 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic const struct ieee80211_regdomain ath12k_world_regd = { 2262306a36Sopenharmony_ci .n_reg_rules = 3, 2362306a36Sopenharmony_ci .alpha2 = "00", 2462306a36Sopenharmony_ci .reg_rules = { 2562306a36Sopenharmony_ci ATH12K_2GHZ_CH01_11, 2662306a36Sopenharmony_ci ATH12K_5GHZ_5150_5350, 2762306a36Sopenharmony_ci ATH12K_5GHZ_5725_5850, 2862306a36Sopenharmony_ci } 2962306a36Sopenharmony_ci}; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic bool ath12k_regdom_changes(struct ath12k *ar, char *alpha2) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci const struct ieee80211_regdomain *regd; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci regd = rcu_dereference_rtnl(ar->hw->wiphy->regd); 3662306a36Sopenharmony_ci /* This can happen during wiphy registration where the previous 3762306a36Sopenharmony_ci * user request is received before we update the regd received 3862306a36Sopenharmony_ci * from firmware. 3962306a36Sopenharmony_ci */ 4062306a36Sopenharmony_ci if (!regd) 4162306a36Sopenharmony_ci return true; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return memcmp(regd->alpha2, alpha2, 2) != 0; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic void 4762306a36Sopenharmony_ciath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); 5062306a36Sopenharmony_ci struct ath12k_wmi_init_country_arg arg; 5162306a36Sopenharmony_ci struct ath12k *ar = hw->priv; 5262306a36Sopenharmony_ci int ret; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci ath12k_dbg(ar->ab, ATH12K_DBG_REG, 5562306a36Sopenharmony_ci "Regulatory Notification received for %s\n", wiphy_name(wiphy)); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci /* Currently supporting only General User Hints. Cell base user 5862306a36Sopenharmony_ci * hints to be handled later. 5962306a36Sopenharmony_ci * Hints from other sources like Core, Beacons are not expected for 6062306a36Sopenharmony_ci * self managed wiphy's 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci if (!(request->initiator == NL80211_REGDOM_SET_BY_USER && 6362306a36Sopenharmony_ci request->user_reg_hint_type == NL80211_USER_REG_HINT_USER)) { 6462306a36Sopenharmony_ci ath12k_warn(ar->ab, "Unexpected Regulatory event for this wiphy\n"); 6562306a36Sopenharmony_ci return; 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (!IS_ENABLED(CONFIG_ATH_REG_DYNAMIC_USER_REG_HINTS)) { 6962306a36Sopenharmony_ci ath12k_dbg(ar->ab, ATH12K_DBG_REG, 7062306a36Sopenharmony_ci "Country Setting is not allowed\n"); 7162306a36Sopenharmony_ci return; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (!ath12k_regdom_changes(ar, request->alpha2)) { 7562306a36Sopenharmony_ci ath12k_dbg(ar->ab, ATH12K_DBG_REG, "Country is already set\n"); 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Set the country code to the firmware and wait for 8062306a36Sopenharmony_ci * the WMI_REG_CHAN_LIST_CC EVENT for updating the 8162306a36Sopenharmony_ci * reg info 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci arg.flags = ALPHA_IS_SET; 8462306a36Sopenharmony_ci memcpy(&arg.cc_info.alpha2, request->alpha2, 2); 8562306a36Sopenharmony_ci arg.cc_info.alpha2[2] = 0; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci ret = ath12k_wmi_send_init_country_cmd(ar, &arg); 8862306a36Sopenharmony_ci if (ret) 8962306a36Sopenharmony_ci ath12k_warn(ar->ab, 9062306a36Sopenharmony_ci "INIT Country code set to fw failed : %d\n", ret); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ciint ath12k_reg_update_chan_list(struct ath12k *ar) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci struct ieee80211_supported_band **bands; 9662306a36Sopenharmony_ci struct ath12k_wmi_scan_chan_list_arg *arg; 9762306a36Sopenharmony_ci struct ieee80211_channel *channel; 9862306a36Sopenharmony_ci struct ieee80211_hw *hw = ar->hw; 9962306a36Sopenharmony_ci struct ath12k_wmi_channel_arg *ch; 10062306a36Sopenharmony_ci enum nl80211_band band; 10162306a36Sopenharmony_ci int num_channels = 0; 10262306a36Sopenharmony_ci int i, ret; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci bands = hw->wiphy->bands; 10562306a36Sopenharmony_ci for (band = 0; band < NUM_NL80211_BANDS; band++) { 10662306a36Sopenharmony_ci if (!(ar->mac.sbands[band].channels && bands[band])) 10762306a36Sopenharmony_ci continue; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci for (i = 0; i < bands[band]->n_channels; i++) { 11062306a36Sopenharmony_ci if (bands[band]->channels[i].flags & 11162306a36Sopenharmony_ci IEEE80211_CHAN_DISABLED) 11262306a36Sopenharmony_ci continue; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci num_channels++; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (WARN_ON(!num_channels)) 11962306a36Sopenharmony_ci return -EINVAL; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci arg = kzalloc(struct_size(arg, channel, num_channels), GFP_KERNEL); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (!arg) 12462306a36Sopenharmony_ci return -ENOMEM; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci arg->pdev_id = ar->pdev->pdev_id; 12762306a36Sopenharmony_ci arg->nallchans = num_channels; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci ch = arg->channel; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci for (band = 0; band < NUM_NL80211_BANDS; band++) { 13262306a36Sopenharmony_ci if (!(ar->mac.sbands[band].channels && bands[band])) 13362306a36Sopenharmony_ci continue; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci for (i = 0; i < bands[band]->n_channels; i++) { 13662306a36Sopenharmony_ci channel = &bands[band]->channels[i]; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (channel->flags & IEEE80211_CHAN_DISABLED) 13962306a36Sopenharmony_ci continue; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci /* TODO: Set to true/false based on some condition? */ 14262306a36Sopenharmony_ci ch->allow_ht = true; 14362306a36Sopenharmony_ci ch->allow_vht = true; 14462306a36Sopenharmony_ci ch->allow_he = true; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ch->dfs_set = 14762306a36Sopenharmony_ci !!(channel->flags & IEEE80211_CHAN_RADAR); 14862306a36Sopenharmony_ci ch->is_chan_passive = !!(channel->flags & 14962306a36Sopenharmony_ci IEEE80211_CHAN_NO_IR); 15062306a36Sopenharmony_ci ch->is_chan_passive |= ch->dfs_set; 15162306a36Sopenharmony_ci ch->mhz = channel->center_freq; 15262306a36Sopenharmony_ci ch->cfreq1 = channel->center_freq; 15362306a36Sopenharmony_ci ch->minpower = 0; 15462306a36Sopenharmony_ci ch->maxpower = channel->max_power * 2; 15562306a36Sopenharmony_ci ch->maxregpower = channel->max_reg_power * 2; 15662306a36Sopenharmony_ci ch->antennamax = channel->max_antenna_gain * 2; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci /* TODO: Use appropriate phymodes */ 15962306a36Sopenharmony_ci if (channel->band == NL80211_BAND_2GHZ) 16062306a36Sopenharmony_ci ch->phy_mode = MODE_11G; 16162306a36Sopenharmony_ci else 16262306a36Sopenharmony_ci ch->phy_mode = MODE_11A; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (channel->band == NL80211_BAND_6GHZ && 16562306a36Sopenharmony_ci cfg80211_channel_is_psc(channel)) 16662306a36Sopenharmony_ci ch->psc_channel = true; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci ath12k_dbg(ar->ab, ATH12K_DBG_WMI, 16962306a36Sopenharmony_ci "mac channel [%d/%d] freq %d maxpower %d regpower %d antenna %d mode %d\n", 17062306a36Sopenharmony_ci i, arg->nallchans, 17162306a36Sopenharmony_ci ch->mhz, ch->maxpower, ch->maxregpower, 17262306a36Sopenharmony_ci ch->antennamax, ch->phy_mode); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci ch++; 17562306a36Sopenharmony_ci /* TODO: use quarrter/half rate, cfreq12, dfs_cfreq2 17662306a36Sopenharmony_ci * set_agile, reg_class_idx 17762306a36Sopenharmony_ci */ 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci ret = ath12k_wmi_send_scan_chan_list_cmd(ar, arg); 18262306a36Sopenharmony_ci kfree(arg); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return ret; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void ath12k_copy_regd(struct ieee80211_regdomain *regd_orig, 18862306a36Sopenharmony_ci struct ieee80211_regdomain *regd_copy) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci u8 i; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* The caller should have checked error conditions */ 19362306a36Sopenharmony_ci memcpy(regd_copy, regd_orig, sizeof(*regd_orig)); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci for (i = 0; i < regd_orig->n_reg_rules; i++) 19662306a36Sopenharmony_ci memcpy(®d_copy->reg_rules[i], ®d_orig->reg_rules[i], 19762306a36Sopenharmony_ci sizeof(struct ieee80211_reg_rule)); 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ciint ath12k_regd_update(struct ath12k *ar, bool init) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci struct ieee80211_regdomain *regd, *regd_copy = NULL; 20362306a36Sopenharmony_ci int ret, regd_len, pdev_id; 20462306a36Sopenharmony_ci struct ath12k_base *ab; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci ab = ar->ab; 20762306a36Sopenharmony_ci pdev_id = ar->pdev_idx; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci spin_lock_bh(&ab->base_lock); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (init) { 21262306a36Sopenharmony_ci /* Apply the regd received during init through 21362306a36Sopenharmony_ci * WMI_REG_CHAN_LIST_CC event. In case of failure to 21462306a36Sopenharmony_ci * receive the regd, initialize with a default world 21562306a36Sopenharmony_ci * regulatory. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_ci if (ab->default_regd[pdev_id]) { 21862306a36Sopenharmony_ci regd = ab->default_regd[pdev_id]; 21962306a36Sopenharmony_ci } else { 22062306a36Sopenharmony_ci ath12k_warn(ab, 22162306a36Sopenharmony_ci "failed to receive default regd during init\n"); 22262306a36Sopenharmony_ci regd = (struct ieee80211_regdomain *)&ath12k_world_regd; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } else { 22562306a36Sopenharmony_ci regd = ab->new_regd[pdev_id]; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (!regd) { 22962306a36Sopenharmony_ci ret = -EINVAL; 23062306a36Sopenharmony_ci spin_unlock_bh(&ab->base_lock); 23162306a36Sopenharmony_ci goto err; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci regd_len = sizeof(*regd) + (regd->n_reg_rules * 23562306a36Sopenharmony_ci sizeof(struct ieee80211_reg_rule)); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci regd_copy = kzalloc(regd_len, GFP_ATOMIC); 23862306a36Sopenharmony_ci if (regd_copy) 23962306a36Sopenharmony_ci ath12k_copy_regd(regd, regd_copy); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spin_unlock_bh(&ab->base_lock); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci if (!regd_copy) { 24462306a36Sopenharmony_ci ret = -ENOMEM; 24562306a36Sopenharmony_ci goto err; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci rtnl_lock(); 24962306a36Sopenharmony_ci wiphy_lock(ar->hw->wiphy); 25062306a36Sopenharmony_ci ret = regulatory_set_wiphy_regd_sync(ar->hw->wiphy, regd_copy); 25162306a36Sopenharmony_ci wiphy_unlock(ar->hw->wiphy); 25262306a36Sopenharmony_ci rtnl_unlock(); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci kfree(regd_copy); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (ret) 25762306a36Sopenharmony_ci goto err; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (ar->state == ATH12K_STATE_ON) { 26062306a36Sopenharmony_ci ret = ath12k_reg_update_chan_list(ar); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci goto err; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_cierr: 26762306a36Sopenharmony_ci ath12k_warn(ab, "failed to perform regd update : %d\n", ret); 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic enum nl80211_dfs_regions 27262306a36Sopenharmony_ciath12k_map_fw_dfs_region(enum ath12k_dfs_region dfs_region) 27362306a36Sopenharmony_ci{ 27462306a36Sopenharmony_ci switch (dfs_region) { 27562306a36Sopenharmony_ci case ATH12K_DFS_REG_FCC: 27662306a36Sopenharmony_ci case ATH12K_DFS_REG_CN: 27762306a36Sopenharmony_ci return NL80211_DFS_FCC; 27862306a36Sopenharmony_ci case ATH12K_DFS_REG_ETSI: 27962306a36Sopenharmony_ci case ATH12K_DFS_REG_KR: 28062306a36Sopenharmony_ci return NL80211_DFS_ETSI; 28162306a36Sopenharmony_ci case ATH12K_DFS_REG_MKK: 28262306a36Sopenharmony_ci case ATH12K_DFS_REG_MKK_N: 28362306a36Sopenharmony_ci return NL80211_DFS_JP; 28462306a36Sopenharmony_ci default: 28562306a36Sopenharmony_ci return NL80211_DFS_UNSET; 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic u32 ath12k_map_fw_reg_flags(u16 reg_flags) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci u32 flags = 0; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (reg_flags & REGULATORY_CHAN_NO_IR) 29462306a36Sopenharmony_ci flags = NL80211_RRF_NO_IR; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci if (reg_flags & REGULATORY_CHAN_RADAR) 29762306a36Sopenharmony_ci flags |= NL80211_RRF_DFS; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (reg_flags & REGULATORY_CHAN_NO_OFDM) 30062306a36Sopenharmony_ci flags |= NL80211_RRF_NO_OFDM; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (reg_flags & REGULATORY_CHAN_INDOOR_ONLY) 30362306a36Sopenharmony_ci flags |= NL80211_RRF_NO_OUTDOOR; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (reg_flags & REGULATORY_CHAN_NO_HT40) 30662306a36Sopenharmony_ci flags |= NL80211_RRF_NO_HT40; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (reg_flags & REGULATORY_CHAN_NO_80MHZ) 30962306a36Sopenharmony_ci flags |= NL80211_RRF_NO_80MHZ; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (reg_flags & REGULATORY_CHAN_NO_160MHZ) 31262306a36Sopenharmony_ci flags |= NL80211_RRF_NO_160MHZ; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci return flags; 31562306a36Sopenharmony_ci} 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic bool 31862306a36Sopenharmony_ciath12k_reg_can_intersect(struct ieee80211_reg_rule *rule1, 31962306a36Sopenharmony_ci struct ieee80211_reg_rule *rule2) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci u32 start_freq1, end_freq1; 32262306a36Sopenharmony_ci u32 start_freq2, end_freq2; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci start_freq1 = rule1->freq_range.start_freq_khz; 32562306a36Sopenharmony_ci start_freq2 = rule2->freq_range.start_freq_khz; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci end_freq1 = rule1->freq_range.end_freq_khz; 32862306a36Sopenharmony_ci end_freq2 = rule2->freq_range.end_freq_khz; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci if ((start_freq1 >= start_freq2 && 33162306a36Sopenharmony_ci start_freq1 < end_freq2) || 33262306a36Sopenharmony_ci (start_freq2 > start_freq1 && 33362306a36Sopenharmony_ci start_freq2 < end_freq1)) 33462306a36Sopenharmony_ci return true; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* TODO: Should we restrict intersection feasibility 33762306a36Sopenharmony_ci * based on min bandwidth of the intersected region also, 33862306a36Sopenharmony_ci * say the intersected rule should have a min bandwidth 33962306a36Sopenharmony_ci * of 20MHz? 34062306a36Sopenharmony_ci */ 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return false; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void ath12k_reg_intersect_rules(struct ieee80211_reg_rule *rule1, 34662306a36Sopenharmony_ci struct ieee80211_reg_rule *rule2, 34762306a36Sopenharmony_ci struct ieee80211_reg_rule *new_rule) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci u32 start_freq1, end_freq1; 35062306a36Sopenharmony_ci u32 start_freq2, end_freq2; 35162306a36Sopenharmony_ci u32 freq_diff, max_bw; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci start_freq1 = rule1->freq_range.start_freq_khz; 35462306a36Sopenharmony_ci start_freq2 = rule2->freq_range.start_freq_khz; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci end_freq1 = rule1->freq_range.end_freq_khz; 35762306a36Sopenharmony_ci end_freq2 = rule2->freq_range.end_freq_khz; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci new_rule->freq_range.start_freq_khz = max_t(u32, start_freq1, 36062306a36Sopenharmony_ci start_freq2); 36162306a36Sopenharmony_ci new_rule->freq_range.end_freq_khz = min_t(u32, end_freq1, end_freq2); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci freq_diff = new_rule->freq_range.end_freq_khz - 36462306a36Sopenharmony_ci new_rule->freq_range.start_freq_khz; 36562306a36Sopenharmony_ci max_bw = min_t(u32, rule1->freq_range.max_bandwidth_khz, 36662306a36Sopenharmony_ci rule2->freq_range.max_bandwidth_khz); 36762306a36Sopenharmony_ci new_rule->freq_range.max_bandwidth_khz = min_t(u32, max_bw, freq_diff); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci new_rule->power_rule.max_antenna_gain = 37062306a36Sopenharmony_ci min_t(u32, rule1->power_rule.max_antenna_gain, 37162306a36Sopenharmony_ci rule2->power_rule.max_antenna_gain); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci new_rule->power_rule.max_eirp = min_t(u32, rule1->power_rule.max_eirp, 37462306a36Sopenharmony_ci rule2->power_rule.max_eirp); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* Use the flags of both the rules */ 37762306a36Sopenharmony_ci new_rule->flags = rule1->flags | rule2->flags; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* To be safe, lts use the max cac timeout of both rules */ 38062306a36Sopenharmony_ci new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms, 38162306a36Sopenharmony_ci rule2->dfs_cac_ms); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic struct ieee80211_regdomain * 38562306a36Sopenharmony_ciath12k_regd_intersect(struct ieee80211_regdomain *default_regd, 38662306a36Sopenharmony_ci struct ieee80211_regdomain *curr_regd) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci u8 num_old_regd_rules, num_curr_regd_rules, num_new_regd_rules; 38962306a36Sopenharmony_ci struct ieee80211_reg_rule *old_rule, *curr_rule, *new_rule; 39062306a36Sopenharmony_ci struct ieee80211_regdomain *new_regd = NULL; 39162306a36Sopenharmony_ci u8 i, j, k; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci num_old_regd_rules = default_regd->n_reg_rules; 39462306a36Sopenharmony_ci num_curr_regd_rules = curr_regd->n_reg_rules; 39562306a36Sopenharmony_ci num_new_regd_rules = 0; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Find the number of intersecting rules to allocate new regd memory */ 39862306a36Sopenharmony_ci for (i = 0; i < num_old_regd_rules; i++) { 39962306a36Sopenharmony_ci old_rule = default_regd->reg_rules + i; 40062306a36Sopenharmony_ci for (j = 0; j < num_curr_regd_rules; j++) { 40162306a36Sopenharmony_ci curr_rule = curr_regd->reg_rules + j; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci if (ath12k_reg_can_intersect(old_rule, curr_rule)) 40462306a36Sopenharmony_ci num_new_regd_rules++; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (!num_new_regd_rules) 40962306a36Sopenharmony_ci return NULL; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci new_regd = kzalloc(sizeof(*new_regd) + (num_new_regd_rules * 41262306a36Sopenharmony_ci sizeof(struct ieee80211_reg_rule)), 41362306a36Sopenharmony_ci GFP_ATOMIC); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (!new_regd) 41662306a36Sopenharmony_ci return NULL; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci /* We set the new country and dfs region directly and only trim 41962306a36Sopenharmony_ci * the freq, power, antenna gain by intersecting with the 42062306a36Sopenharmony_ci * default regdomain. Also MAX of the dfs cac timeout is selected. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci new_regd->n_reg_rules = num_new_regd_rules; 42362306a36Sopenharmony_ci memcpy(new_regd->alpha2, curr_regd->alpha2, sizeof(new_regd->alpha2)); 42462306a36Sopenharmony_ci new_regd->dfs_region = curr_regd->dfs_region; 42562306a36Sopenharmony_ci new_rule = new_regd->reg_rules; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci for (i = 0, k = 0; i < num_old_regd_rules; i++) { 42862306a36Sopenharmony_ci old_rule = default_regd->reg_rules + i; 42962306a36Sopenharmony_ci for (j = 0; j < num_curr_regd_rules; j++) { 43062306a36Sopenharmony_ci curr_rule = curr_regd->reg_rules + j; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci if (ath12k_reg_can_intersect(old_rule, curr_rule)) 43362306a36Sopenharmony_ci ath12k_reg_intersect_rules(old_rule, curr_rule, 43462306a36Sopenharmony_ci (new_rule + k++)); 43562306a36Sopenharmony_ci } 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci return new_regd; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic const char * 44162306a36Sopenharmony_ciath12k_reg_get_regdom_str(enum nl80211_dfs_regions dfs_region) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci switch (dfs_region) { 44462306a36Sopenharmony_ci case NL80211_DFS_FCC: 44562306a36Sopenharmony_ci return "FCC"; 44662306a36Sopenharmony_ci case NL80211_DFS_ETSI: 44762306a36Sopenharmony_ci return "ETSI"; 44862306a36Sopenharmony_ci case NL80211_DFS_JP: 44962306a36Sopenharmony_ci return "JP"; 45062306a36Sopenharmony_ci default: 45162306a36Sopenharmony_ci return "UNSET"; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic u16 45662306a36Sopenharmony_ciath12k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci u16 bw; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci bw = end_freq - start_freq; 46162306a36Sopenharmony_ci bw = min_t(u16, bw, max_bw); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci if (bw >= 80 && bw < 160) 46462306a36Sopenharmony_ci bw = 80; 46562306a36Sopenharmony_ci else if (bw >= 40 && bw < 80) 46662306a36Sopenharmony_ci bw = 40; 46762306a36Sopenharmony_ci else if (bw < 40) 46862306a36Sopenharmony_ci bw = 20; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci return bw; 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic void 47462306a36Sopenharmony_ciath12k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq, 47562306a36Sopenharmony_ci u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr, 47662306a36Sopenharmony_ci u32 reg_flags) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq); 47962306a36Sopenharmony_ci reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq); 48062306a36Sopenharmony_ci reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw); 48162306a36Sopenharmony_ci reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain); 48262306a36Sopenharmony_ci reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr); 48362306a36Sopenharmony_ci reg_rule->flags = reg_flags; 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_cistatic void 48762306a36Sopenharmony_ciath12k_reg_update_weather_radar_band(struct ath12k_base *ab, 48862306a36Sopenharmony_ci struct ieee80211_regdomain *regd, 48962306a36Sopenharmony_ci struct ath12k_reg_rule *reg_rule, 49062306a36Sopenharmony_ci u8 *rule_idx, u32 flags, u16 max_bw) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci u32 end_freq; 49362306a36Sopenharmony_ci u16 bw; 49462306a36Sopenharmony_ci u8 i; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci i = *rule_idx; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci bw = ath12k_reg_adjust_bw(reg_rule->start_freq, 49962306a36Sopenharmony_ci ETSI_WEATHER_RADAR_BAND_LOW, max_bw); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci ath12k_reg_update_rule(regd->reg_rules + i, reg_rule->start_freq, 50262306a36Sopenharmony_ci ETSI_WEATHER_RADAR_BAND_LOW, bw, 50362306a36Sopenharmony_ci reg_rule->ant_gain, reg_rule->reg_power, 50462306a36Sopenharmony_ci flags); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci ath12k_dbg(ab, ATH12K_DBG_REG, 50762306a36Sopenharmony_ci "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", 50862306a36Sopenharmony_ci i + 1, reg_rule->start_freq, ETSI_WEATHER_RADAR_BAND_LOW, 50962306a36Sopenharmony_ci bw, reg_rule->ant_gain, reg_rule->reg_power, 51062306a36Sopenharmony_ci regd->reg_rules[i].dfs_cac_ms, 51162306a36Sopenharmony_ci flags); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_HIGH) 51462306a36Sopenharmony_ci end_freq = ETSI_WEATHER_RADAR_BAND_HIGH; 51562306a36Sopenharmony_ci else 51662306a36Sopenharmony_ci end_freq = reg_rule->end_freq; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci bw = ath12k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_LOW, end_freq, 51962306a36Sopenharmony_ci max_bw); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci i++; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ath12k_reg_update_rule(regd->reg_rules + i, 52462306a36Sopenharmony_ci ETSI_WEATHER_RADAR_BAND_LOW, end_freq, bw, 52562306a36Sopenharmony_ci reg_rule->ant_gain, reg_rule->reg_power, 52662306a36Sopenharmony_ci flags); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci ath12k_dbg(ab, ATH12K_DBG_REG, 53162306a36Sopenharmony_ci "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", 53262306a36Sopenharmony_ci i + 1, ETSI_WEATHER_RADAR_BAND_LOW, end_freq, 53362306a36Sopenharmony_ci bw, reg_rule->ant_gain, reg_rule->reg_power, 53462306a36Sopenharmony_ci regd->reg_rules[i].dfs_cac_ms, 53562306a36Sopenharmony_ci flags); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (end_freq == reg_rule->end_freq) { 53862306a36Sopenharmony_ci regd->n_reg_rules--; 53962306a36Sopenharmony_ci *rule_idx = i; 54062306a36Sopenharmony_ci return; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci bw = ath12k_reg_adjust_bw(ETSI_WEATHER_RADAR_BAND_HIGH, 54462306a36Sopenharmony_ci reg_rule->end_freq, max_bw); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci i++; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ath12k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_HIGH, 54962306a36Sopenharmony_ci reg_rule->end_freq, bw, 55062306a36Sopenharmony_ci reg_rule->ant_gain, reg_rule->reg_power, 55162306a36Sopenharmony_ci flags); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci ath12k_dbg(ab, ATH12K_DBG_REG, 55462306a36Sopenharmony_ci "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", 55562306a36Sopenharmony_ci i + 1, ETSI_WEATHER_RADAR_BAND_HIGH, reg_rule->end_freq, 55662306a36Sopenharmony_ci bw, reg_rule->ant_gain, reg_rule->reg_power, 55762306a36Sopenharmony_ci regd->reg_rules[i].dfs_cac_ms, 55862306a36Sopenharmony_ci flags); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci *rule_idx = i; 56162306a36Sopenharmony_ci} 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_cistruct ieee80211_regdomain * 56462306a36Sopenharmony_ciath12k_reg_build_regd(struct ath12k_base *ab, 56562306a36Sopenharmony_ci struct ath12k_reg_info *reg_info, bool intersect) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL; 56862306a36Sopenharmony_ci struct ath12k_reg_rule *reg_rule; 56962306a36Sopenharmony_ci u8 i = 0, j = 0, k = 0; 57062306a36Sopenharmony_ci u8 num_rules; 57162306a36Sopenharmony_ci u16 max_bw; 57262306a36Sopenharmony_ci u32 flags; 57362306a36Sopenharmony_ci char alpha2[3]; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* FIXME: Currently taking reg rules for 6G only from Indoor AP mode list. 57862306a36Sopenharmony_ci * This can be updated to choose the combination dynamically based on AP 57962306a36Sopenharmony_ci * type and client type, after complete 6G regulatory support is added. 58062306a36Sopenharmony_ci */ 58162306a36Sopenharmony_ci if (reg_info->is_ext_reg_event) 58262306a36Sopenharmony_ci num_rules += reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP]; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (!num_rules) 58562306a36Sopenharmony_ci goto ret; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* Add max additional rules to accommodate weather radar band */ 58862306a36Sopenharmony_ci if (reg_info->dfs_region == ATH12K_DFS_REG_ETSI) 58962306a36Sopenharmony_ci num_rules += 2; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci tmp_regd = kzalloc(sizeof(*tmp_regd) + 59262306a36Sopenharmony_ci (num_rules * sizeof(struct ieee80211_reg_rule)), 59362306a36Sopenharmony_ci GFP_ATOMIC); 59462306a36Sopenharmony_ci if (!tmp_regd) 59562306a36Sopenharmony_ci goto ret; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci memcpy(tmp_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); 59862306a36Sopenharmony_ci memcpy(alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); 59962306a36Sopenharmony_ci alpha2[2] = '\0'; 60062306a36Sopenharmony_ci tmp_regd->dfs_region = ath12k_map_fw_dfs_region(reg_info->dfs_region); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci ath12k_dbg(ab, ATH12K_DBG_REG, 60362306a36Sopenharmony_ci "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", 60462306a36Sopenharmony_ci alpha2, ath12k_reg_get_regdom_str(tmp_regd->dfs_region), 60562306a36Sopenharmony_ci reg_info->dfs_region, num_rules); 60662306a36Sopenharmony_ci /* Update reg_rules[] below. Firmware is expected to 60762306a36Sopenharmony_ci * send these rules in order(2G rules first and then 5G) 60862306a36Sopenharmony_ci */ 60962306a36Sopenharmony_ci for (; i < num_rules; i++) { 61062306a36Sopenharmony_ci if (reg_info->num_2g_reg_rules && 61162306a36Sopenharmony_ci (i < reg_info->num_2g_reg_rules)) { 61262306a36Sopenharmony_ci reg_rule = reg_info->reg_rules_2g_ptr + i; 61362306a36Sopenharmony_ci max_bw = min_t(u16, reg_rule->max_bw, 61462306a36Sopenharmony_ci reg_info->max_bw_2g); 61562306a36Sopenharmony_ci flags = 0; 61662306a36Sopenharmony_ci } else if (reg_info->num_5g_reg_rules && 61762306a36Sopenharmony_ci (j < reg_info->num_5g_reg_rules)) { 61862306a36Sopenharmony_ci reg_rule = reg_info->reg_rules_5g_ptr + j++; 61962306a36Sopenharmony_ci max_bw = min_t(u16, reg_rule->max_bw, 62062306a36Sopenharmony_ci reg_info->max_bw_5g); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* FW doesn't pass NL80211_RRF_AUTO_BW flag for 62362306a36Sopenharmony_ci * BW Auto correction, we can enable this by default 62462306a36Sopenharmony_ci * for all 5G rules here. The regulatory core performs 62562306a36Sopenharmony_ci * BW correction if required and applies flags as 62662306a36Sopenharmony_ci * per other BW rule flags we pass from here 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_ci flags = NL80211_RRF_AUTO_BW; 62962306a36Sopenharmony_ci } else if (reg_info->is_ext_reg_event && 63062306a36Sopenharmony_ci reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP] && 63162306a36Sopenharmony_ci (k < reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP])) { 63262306a36Sopenharmony_ci reg_rule = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP] + k++; 63362306a36Sopenharmony_ci max_bw = min_t(u16, reg_rule->max_bw, 63462306a36Sopenharmony_ci reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]); 63562306a36Sopenharmony_ci flags = NL80211_RRF_AUTO_BW; 63662306a36Sopenharmony_ci } else { 63762306a36Sopenharmony_ci break; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci flags |= ath12k_map_fw_reg_flags(reg_rule->flags); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci ath12k_reg_update_rule(tmp_regd->reg_rules + i, 64362306a36Sopenharmony_ci reg_rule->start_freq, 64462306a36Sopenharmony_ci reg_rule->end_freq, max_bw, 64562306a36Sopenharmony_ci reg_rule->ant_gain, reg_rule->reg_power, 64662306a36Sopenharmony_ci flags); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* Update dfs cac timeout if the dfs domain is ETSI and the 64962306a36Sopenharmony_ci * new rule covers weather radar band. 65062306a36Sopenharmony_ci * Default value of '0' corresponds to 60s timeout, so no 65162306a36Sopenharmony_ci * need to update that for other rules. 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_ci if (flags & NL80211_RRF_DFS && 65462306a36Sopenharmony_ci reg_info->dfs_region == ATH12K_DFS_REG_ETSI && 65562306a36Sopenharmony_ci (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_LOW && 65662306a36Sopenharmony_ci reg_rule->start_freq < ETSI_WEATHER_RADAR_BAND_HIGH)){ 65762306a36Sopenharmony_ci ath12k_reg_update_weather_radar_band(ab, tmp_regd, 65862306a36Sopenharmony_ci reg_rule, &i, 65962306a36Sopenharmony_ci flags, max_bw); 66062306a36Sopenharmony_ci continue; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (reg_info->is_ext_reg_event) { 66462306a36Sopenharmony_ci ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n", 66562306a36Sopenharmony_ci i + 1, reg_rule->start_freq, reg_rule->end_freq, 66662306a36Sopenharmony_ci max_bw, reg_rule->ant_gain, reg_rule->reg_power, 66762306a36Sopenharmony_ci tmp_regd->reg_rules[i].dfs_cac_ms, 66862306a36Sopenharmony_ci flags, reg_rule->psd_flag, reg_rule->psd_eirp); 66962306a36Sopenharmony_ci } else { 67062306a36Sopenharmony_ci ath12k_dbg(ab, ATH12K_DBG_REG, 67162306a36Sopenharmony_ci "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", 67262306a36Sopenharmony_ci i + 1, reg_rule->start_freq, reg_rule->end_freq, 67362306a36Sopenharmony_ci max_bw, reg_rule->ant_gain, reg_rule->reg_power, 67462306a36Sopenharmony_ci tmp_regd->reg_rules[i].dfs_cac_ms, 67562306a36Sopenharmony_ci flags); 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci tmp_regd->n_reg_rules = i; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (intersect) { 68262306a36Sopenharmony_ci default_regd = ab->default_regd[reg_info->phy_id]; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci /* Get a new regd by intersecting the received regd with 68562306a36Sopenharmony_ci * our default regd. 68662306a36Sopenharmony_ci */ 68762306a36Sopenharmony_ci new_regd = ath12k_regd_intersect(default_regd, tmp_regd); 68862306a36Sopenharmony_ci kfree(tmp_regd); 68962306a36Sopenharmony_ci if (!new_regd) { 69062306a36Sopenharmony_ci ath12k_warn(ab, "Unable to create intersected regdomain\n"); 69162306a36Sopenharmony_ci goto ret; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci } else { 69462306a36Sopenharmony_ci new_regd = tmp_regd; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ciret: 69862306a36Sopenharmony_ci return new_regd; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_civoid ath12k_regd_update_work(struct work_struct *work) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci struct ath12k *ar = container_of(work, struct ath12k, 70462306a36Sopenharmony_ci regd_update_work); 70562306a36Sopenharmony_ci int ret; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci ret = ath12k_regd_update(ar, false); 70862306a36Sopenharmony_ci if (ret) { 70962306a36Sopenharmony_ci /* Firmware has already moved to the new regd. We need 71062306a36Sopenharmony_ci * to maintain channel consistency across FW, Host driver 71162306a36Sopenharmony_ci * and userspace. Hence as a fallback mechanism we can set 71262306a36Sopenharmony_ci * the prev or default country code to the firmware. 71362306a36Sopenharmony_ci */ 71462306a36Sopenharmony_ci /* TODO: Implement Fallback Mechanism */ 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_civoid ath12k_reg_init(struct ath12k *ar) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED; 72162306a36Sopenharmony_ci ar->hw->wiphy->reg_notifier = ath12k_reg_notifier; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_civoid ath12k_reg_free(struct ath12k_base *ab) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci int i; 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci for (i = 0; i < ab->hw_params->max_radios; i++) { 72962306a36Sopenharmony_ci kfree(ab->default_regd[i]); 73062306a36Sopenharmony_ci kfree(ab->new_regd[i]); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci} 733