diff mbox series

[RFC,v2,62/96] cl8k: add regdom.c

Message ID 20220524113502.1094459-63-viktor.barna@celeno.com
State New
Headers show
Series wireless: cl8k driver for Celeno IEEE 802.11ax devices | expand

Commit Message

Viktor Barna May 24, 2022, 11:34 a.m. UTC
From: Viktor Barna <viktor.barna@celeno.com>

(Part of the split. Please, take a look at the cover letter for more
details).

Signed-off-by: Viktor Barna <viktor.barna@celeno.com>
---
 drivers/net/wireless/celeno/cl8k/regdom.c | 301 ++++++++++++++++++++++
 1 file changed, 301 insertions(+)
 create mode 100644 drivers/net/wireless/celeno/cl8k/regdom.c
diff mbox series

Patch

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 = &reg_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 = &reg_rule->freq_range;
+	struct ieee80211_power_rule *power_rule = &reg_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);
+}
+