From: Viktor Barna <viktor.barna@xxxxxxxxxx> (Part of the split. Please, take a look at the cover letter for more details). Signed-off-by: Viktor Barna <viktor.barna@xxxxxxxxxx> --- drivers/net/wireless/celeno/cl8k/regdom.c | 301 ++++++++++++++++++++++ 1 file changed, 301 insertions(+) create mode 100644 drivers/net/wireless/celeno/cl8k/regdom.c diff --git a/drivers/net/wireless/celeno/cl8k/regdom.c b/drivers/net/wireless/celeno/cl8k/regdom.c new file mode 100644 index 000000000000..1b9d33a33d98 --- /dev/null +++ b/drivers/net/wireless/celeno/cl8k/regdom.c @@ -0,0 +1,301 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +/* Copyright(c) 2019-2022, Celeno Communications Ltd. */ + +#include "chip.h" +#include "dfs.h" +#include "core.h" +#include "debug.h" +#include "hw.h" +#include "utils.h" +#include "regdom.h" + +static struct ieee80211_regdomain cl_regdom_24g = { + .n_reg_rules = 2, + .alpha2 = "99", + .reg_rules = { + REG_RULE(2412 - 10, 2472 + 10, 40, 6, 20, 0), + REG_RULE(2484 - 10, 2484 + 10, 20, 6, 20, 0), + } +}; + +static struct ieee80211_regdomain cl_regdom_5g = { + .n_reg_rules = 1, + .alpha2 = "99", + .reg_rules = { + REG_RULE(5150 - 10, 5850 + 10, 80, 6, 30, 0), + } +}; + +static struct ieee80211_regdomain cl_regdom_6g = { + .n_reg_rules = 1, + .alpha2 = "99", + .reg_rules = { + REG_RULE(5935 - 10, 7115 + 10, 80, 6, 30, 0), + } +}; + +static int cl_regd_is_legal_bw(int freq_diff) +{ + int bw = 0; + + for (bw = CHNL_BW_20; bw < CHNL_BW_MAX; bw++) + if (freq_diff == BW_TO_KHZ(bw)) + return bw; + + return -EINVAL; +} + +static int cl_regd_domain_update_rule(struct cl_hw *cl_hw, struct ieee80211_regdomain *rd, + int freq, int power, u8 max_bw, u32 flags, u32 dfs_cac_ms) +{ + struct ieee80211_reg_rule *reg_rule = &rd->reg_rules[rd->n_reg_rules - 1]; + struct ieee80211_power_rule *power_rule = ®_rule->power_rule; + int bw, diff; + + reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(freq + 10); + if (power_rule->max_eirp < DBM_TO_MBM(power)) + power_rule->max_eirp = DBM_TO_MBM(power); + + diff = reg_rule->freq_range.end_freq_khz - reg_rule->freq_range.start_freq_khz; + /* if freq diff is equal to legal BW then update max_bandwidth_khz */ + bw = cl_regd_is_legal_bw(diff); + if (bw >= 0) + reg_rule->freq_range.max_bandwidth_khz = BW_TO_KHZ(min((u8)bw, max_bw)); + + reg_rule->flags |= flags; + reg_rule->dfs_cac_ms = max_t(u32, reg_rule->dfs_cac_ms, dfs_cac_ms); + + return diff; +} + +/* + * Add first rule with minimal BW and increase then in cl_regd_domain_update_rule + * if new freq range will added + */ +static void cl_regd_domain_add_rule(struct cl_hw *cl_hw, struct ieee80211_regdomain *rd, + int freq, int max_power, u8 min_bw, u32 flags, u32 dfs_cac_ms) +{ + struct ieee80211_reg_rule *reg_rule = &rd->reg_rules[rd->n_reg_rules]; + struct ieee80211_freq_range *freq_range = ®_rule->freq_range; + struct ieee80211_power_rule *power_rule = ®_rule->power_rule; + + freq_range->start_freq_khz = MHZ_TO_KHZ(freq - 10); + freq_range->end_freq_khz = MHZ_TO_KHZ(freq + 10); + freq_range->max_bandwidth_khz = BW_TO_KHZ(min_bw); + + power_rule->max_eirp = DBM_TO_MBM(max_power); + power_rule->max_antenna_gain = DBI_TO_MBI(3); + + reg_rule->flags |= flags; + reg_rule->dfs_cac_ms = dfs_cac_ms; + + rd->n_reg_rules++; +} + +static u32 cl_regd_map_reg_flags(u32 reg_flags) +{ + u32 flags = 0; + + if (reg_flags & IEEE80211_CHAN_NO_IR) + flags = NL80211_RRF_NO_IR; + + if (reg_flags & IEEE80211_CHAN_RADAR) + flags |= NL80211_RRF_DFS; + + if (reg_flags & IEEE80211_CHAN_NO_OFDM) + flags |= NL80211_RRF_NO_OFDM; + + if (reg_flags & IEEE80211_CHAN_INDOOR_ONLY) + flags |= NL80211_RRF_NO_OUTDOOR; + + if (reg_flags & (IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)) + flags |= NL80211_RRF_NO_HT40; + + if (reg_flags & IEEE80211_CHAN_NO_80MHZ) + flags |= NL80211_RRF_NO_80MHZ; + + if (reg_flags & IEEE80211_CHAN_NO_160MHZ) + flags |= NL80211_RRF_NO_160MHZ; + + return flags; +} + +void cl_regd_set(struct cl_hw *cl_hw, struct ieee80211_regdomain *rd, + struct regulatory_request *request) +{ + int j = 0; + int power = 0, prev_power = 0; + u8 bw = 0, prev_bw = 0; + int freq = 0, prev_freq = 0; + u8 chan = 0; + u32 flags = 0, prev_flags = 0; + u32 dfs_cac_ms = 0; + + spin_lock_bh(&cl_hw->channel_info_lock); + + memset(rd, 0, sizeof(*rd) + NL80211_MAX_SUPP_REG_RULES * sizeof(struct ieee80211_reg_rule)); + memcpy(rd->alpha2, request->alpha2, 2); + + rd->dfs_region = request->dfs_region; + + if (request->dfs_region == NL80211_DFS_FCC) + cl_hw->channel_info.standard = NL80211_DFS_FCC; + else if (request->dfs_region == NL80211_DFS_ETSI) + cl_hw->channel_info.standard = NL80211_DFS_ETSI; + else + cl_hw->channel_info.standard = NL80211_DFS_UNSET; + + for (j = 0; j < cl_channel_num(cl_hw); j++) { + struct cl_chan_info *chan_info = &cl_hw->channel_info.channels[CHNL_BW_20][j]; + + chan = chan_info->channel; + if (!chan) + continue; + + /* Translate from country_power (.25dBm) to max_power (1dBm) */ + power = cl_hw->channel_info.channels[CHNL_BW_20][j].country_max_power_q2 >> 2; + bw = cl_chan_info_get_max_bw(cl_hw, chan); + freq = ieee80211_channel_to_frequency(chan, cl_hw->nl_band); + flags = cl_regd_map_reg_flags(chan_info->flags); + dfs_cac_ms = chan_info->dfs_cac_ms; + if (freq - prev_freq > 20 || prev_power != power || prev_bw != bw || + prev_flags != flags) + cl_regd_domain_add_rule(cl_hw, rd, freq, power, + CHNL_BW_20, flags, dfs_cac_ms); + else + cl_regd_domain_update_rule(cl_hw, rd, freq, power, bw, flags, dfs_cac_ms); + + prev_freq = freq; + prev_power = power; + prev_bw = bw; + prev_flags = flags; + } + + spin_unlock_bh(&cl_hw->channel_info_lock); +} + +static void cl_regd_update_channels(struct cl_hw *cl_hw, struct wiphy *wiphy) +{ + enum nl80211_band band; + const struct ieee80211_supported_band *cfg_band = NULL; + + for (band = 0; band < NUM_NL80211_BANDS; band++) { + if (band != cl_hw->nl_band) + continue; + + cfg_band = wiphy->bands[band]; + if (!cfg_band) + continue; + + cl_chan_update_channels_info(cl_hw, cfg_band); + } +} + +static void cl_regd_set_by_user(struct cl_hw *cl_hw, struct wiphy *wiphy, + struct regulatory_request *request) +{ + if (!cl_hw->channel_info.use_channel_info) + return; + + cl_regd_update_channels(cl_hw, wiphy); + /* + * Here is updated cl_hw->channel_info, + * let's generates new regdom rules into cl_hw->channel_info.rd + */ + cl_regd_set(cl_hw, cl_hw->channel_info.rd, request); + if (cl_band_is_5g(cl_hw)) + cl_dfs_reinit(cl_hw); + /* TODO: calib callback for channels update */ +} + +static bool cl_regd_dyn_mode_enabled(struct cl_hw *cl_hw) +{ + return !strcmp(cl_hw->chip->conf->ci_regdom_mode, "auto"); +} + +static void cl_regd_notifier_apply(struct cl_hw *cl_hw, + struct wiphy *wiphy, + struct regulatory_request *request) +{ + if (!request) + return; + + switch (request->initiator) { + case NL80211_REGDOM_SET_BY_CORE: + break; + case NL80211_REGDOM_SET_BY_DRIVER: + break; + case NL80211_REGDOM_SET_BY_USER: + if (cl_regd_dyn_mode_enabled(cl_hw)) + cl_regd_set_by_user(cl_hw, wiphy, request); + break; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + break; + default: + break; + } +} + +static void cl_regd_notifier(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + + cl_regd_notifier_apply(hw->priv, wiphy, request); +} + +static int _cl_regd_init(struct cl_hw *cl_hw, struct wiphy *wiphy, + void (*reg_notifier)(struct wiphy *wiphy, + struct regulatory_request *request)) +{ + if (cl_regd_dyn_mode_enabled(cl_hw)) { + const struct ieee80211_regdomain *regd = cl_hw->channel_info.rd; + + wiphy->reg_notifier = reg_notifier; + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | + REGULATORY_DISABLE_BEACON_HINTS | + REGULATORY_COUNTRY_IE_IGNORE; + + wiphy_apply_custom_regulatory(wiphy, regd); + + return 0; + } + + /* default is self managed mode */ + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; + + return regulatory_set_wiphy_regd(wiphy, cl_hw->channel_info.rd); +} + +int cl_regd_init(struct cl_hw *cl_hw, struct wiphy *wiphy) +{ + if (cl_hw->channel_info.use_channel_info) { + cl_hw->channel_info.rd = kzalloc(sizeof(*cl_hw->channel_info.rd) + + NL80211_MAX_SUPP_REG_RULES * + sizeof(struct ieee80211_reg_rule), + GFP_KERNEL); + if (!cl_hw->channel_info.rd) { + cl_dbg_err(cl_hw, "memory allocation failed!\n"); + return -ENOMEM; + } + + struct regulatory_request request = { + .alpha2[0] = cl_hw->chip->conf->ci_country_code[0], + .alpha2[1] = cl_hw->chip->conf->ci_country_code[1], + .alpha2[2] = 0, + .dfs_region = cl_hw->channel_info.standard, + }; + + cl_regd_set(cl_hw, cl_hw->channel_info.rd, &request); + } else { + if (cl_band_is_6g(cl_hw)) + cl_hw->channel_info.rd = &cl_regdom_6g; + else if (cl_band_is_5g(cl_hw)) + cl_hw->channel_info.rd = &cl_regdom_5g; + else + cl_hw->channel_info.rd = &cl_regdom_24g; + } + + return _cl_regd_init(cl_hw, wiphy, cl_regd_notifier); +} + -- 2.36.1