qcacmn: Add support to send regulatory sync event

Add support to send regulatory rules info in regulatory sync
event to user space for self managed regulatory when regulatory
info is updated.

Change-Id: Iac8704598fd181e47cb023405dfe592c4c93f51b
CRs-Fixed: 2242701
diff --git a/os_if/linux/qca_vendor.h b/os_if/linux/qca_vendor.h
index ece4802..10bf652 100644
--- a/os_if/linux/qca_vendor.h
+++ b/os_if/linux/qca_vendor.h
@@ -2971,6 +2971,8 @@
  *	QCA_WLAN_VENDOR_FEATURE_OCE_AP, the userspace shall assume that
  *	this Device may not support all OCE AP functionalities but can support
  *	only OCE STA-CFON functionalities.
+ * @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self
+ *	managed regulatory.
  */
 enum qca_wlan_vendor_features {
 	QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD = 0,
@@ -2980,6 +2982,7 @@
 	QCA_WLAN_VENDOR_FEATURE_OCE_STA = 4,
 	QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5,
 	QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6,
+	QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
 	/* Additional features need to be added above this */
 	NUM_QCA_WLAN_VENDOR_FEATURES
 };
diff --git a/umac/regulatory/core/src/reg_priv.h b/umac/regulatory/core/src/reg_priv.h
index 0dd737f..1b2d4bd 100644
--- a/umac/regulatory/core/src/reg_priv.h
+++ b/umac/regulatory/core/src/reg_priv.h
@@ -108,6 +108,7 @@
 	struct ch_avoid_ind_type freq_avoid_list;
 	bool force_ssc_disable_indoor_channel;
 	bool sap_state;
+	struct reg_rule_info reg_rules;
 };
 
 #endif
diff --git a/umac/regulatory/core/src/reg_services.c b/umac/regulatory/core/src/reg_services.c
index 6730170..4859adc 100644
--- a/umac/regulatory/core/src/reg_services.c
+++ b/umac/regulatory/core/src/reg_services.c
@@ -2496,7 +2496,7 @@
 		qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
 		if (callback != NULL)
 			callback(psoc, pdev, cur_chan_list, avoid_freq_ind,
-					cbk_list[ctr].arg);
+				 cbk_list[ctr].arg);
 	}
 	qdf_mem_free(cur_chan_list);
 	if (avoid_freq_ind)
@@ -2745,6 +2745,44 @@
 }
 #endif
 
+void reg_reset_reg_rules(struct reg_rule_info *reg_rules)
+{
+	if (reg_rules->reg_rules_ptr)
+		qdf_mem_free(reg_rules->reg_rules_ptr);
+	qdf_mem_zero(reg_rules, sizeof(*reg_rules));
+}
+
+static 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;
+
+	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) {
+		reg_debug("no reg rules in psoc");
+		return;
+	}
+	reg_rule_len = pdev_reg_rules->num_of_reg_rules *
+		       sizeof(struct cur_reg_rule);
+	pdev_reg_rules->reg_rules_ptr = qdf_mem_malloc(reg_rule_len);
+	if (!pdev_reg_rules->reg_rules_ptr) {
+		reg_err("mem alloc failed for pdev reg rules");
+		return;
+	}
+	qdf_mem_copy(pdev_reg_rules->reg_rules_ptr,
+		     psoc_reg_rules->reg_rules_ptr,
+		     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;
+	reg_debug("num pdev reg rules saved %d",
+		  pdev_reg_rules->num_of_reg_rules);
+}
+
 static void reg_propagate_mas_chan_list_to_pdev(struct wlan_objmgr_psoc *psoc,
 						void *object, void *arg)
 {
@@ -2754,6 +2792,7 @@
 	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,
@@ -2774,7 +2813,8 @@
 	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_compute_pdev_current_chan_list(pdev_priv_obj);
 
 	reg_tx_ops = reg_get_psoc_tx_ops(psoc);
@@ -2832,6 +2872,7 @@
 	uint8_t phy_id;
 	struct wlan_objmgr_pdev *pdev;
 	struct wlan_lmac_if_reg_tx_ops *tx_ops;
+	struct reg_rule_info *reg_rules;
 
 	reg_debug("process reg master chan list");
 
@@ -2918,6 +2959,30 @@
 	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) {
+		reg_rules->reg_rules_ptr =
+			qdf_mem_malloc(reg_rules->num_of_reg_rules *
+					sizeof(struct cur_reg_rule));
+		if (!reg_rules->reg_rules_ptr) {
+			reg_err("mem alloc failed for reg_rules");
+		} else {
+			if (num_2g_reg_rules)
+				qdf_mem_copy(reg_rules->reg_rules_ptr,
+					     reg_rule_2g, num_2g_reg_rules *
+					     sizeof(struct cur_reg_rule));
+			if (num_5g_reg_rules)
+				qdf_mem_copy(reg_rules->reg_rules_ptr +
+					     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);
@@ -2979,6 +3044,7 @@
 	if (pdev != NULL) {
 		reg_propagate_mas_chan_list_to_pdev(psoc, pdev, &dir);
 		wlan_objmgr_pdev_release_ref(pdev, dbg_id);
+		reg_reset_reg_rules(reg_rules);
 	}
 
 	return QDF_STATUS_SUCCESS;
@@ -3277,6 +3343,7 @@
 	uint32_t range_2g_low, range_2g_high;
 	uint32_t range_5g_low, range_5g_high;
 	QDF_STATUS status;
+	struct reg_rule_info *psoc_reg_rules;
 
 	pdev_priv_obj = qdf_mem_malloc(sizeof(*pdev_priv_obj));
 	if (NULL == pdev_priv_obj) {
@@ -3343,6 +3410,11 @@
 
 	reg_compute_pdev_current_chan_list(pdev_priv_obj);
 
+	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_reset_reg_rules(psoc_reg_rules);
+
 	status = wlan_objmgr_pdev_component_obj_attach(pdev,
 						     WLAN_UMAC_COMP_REGULATORY,
 						     pdev_priv_obj,
@@ -3377,6 +3449,7 @@
 
 	reg_debug("reg pdev obj deleted with status %d", status);
 
+	reg_reset_reg_rules(&pdev_priv_obj->reg_rules);
 	qdf_mem_free(pdev_priv_obj);
 
 	return status;
@@ -3900,6 +3973,24 @@
 	return QDF_STATUS_SUCCESS;
 }
 
+struct reg_rule_info *reg_get_regd_rules(struct wlan_objmgr_pdev *pdev)
+{
+	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
+
+	if (!pdev) {
+		reg_err("pdev is NULL");
+		return NULL;
+	}
+	pdev_priv_obj = reg_get_pdev_obj(pdev);
+
+	if (!pdev_priv_obj) {
+		reg_err("pdev priv obj is NULL");
+		return NULL;
+	}
+
+	return &pdev_priv_obj->reg_rules;
+}
+
 QDF_STATUS reg_program_chan_list(struct wlan_objmgr_pdev *pdev,
 				 struct cc_regdmn_s *rd)
 {
diff --git a/umac/regulatory/core/src/reg_services.h b/umac/regulatory/core/src/reg_services.h
index cf1ca43..d73f50c 100644
--- a/umac/regulatory/core/src/reg_services.h
+++ b/umac/regulatory/core/src/reg_services.h
@@ -344,6 +344,22 @@
 			       uint8_t *alpha2,
 			       enum dfs_reg dfs_region);
 
+/**
+ * reg_get_regd_rules() - provides the reg domain rules info
+ * @pdev: pdev pointer
+ *
+ * Return: reg_rule_info pointer
+ */
+struct reg_rule_info *reg_get_regd_rules(struct wlan_objmgr_pdev *pdev);
+
+/**
+ * reg_reset_reg_rules() - provides the reg domain rules info
+ * @reg_rules: reg rules pointer
+ *
+ * Return: None
+ */
+void reg_reset_reg_rules(struct reg_rule_info *reg_rules);
+
 QDF_STATUS reg_program_default_cc(struct wlan_objmgr_pdev *pdev,
 				  uint16_t regdmn);
 
diff --git a/umac/regulatory/dispatcher/inc/reg_services_public_struct.h b/umac/regulatory/dispatcher/inc/reg_services_public_struct.h
index 72269d0..1c1f864 100644
--- a/umac/regulatory/dispatcher/inc/reg_services_public_struct.h
+++ b/umac/regulatory/dispatcher/inc/reg_services_public_struct.h
@@ -736,6 +736,20 @@
 };
 
 /**
+ * struct reg_rule_info
+ * @alpha2: alpha2 of reg rules
+ * @dfs_region: dfs region
+ * @num_of_reg_rules: number of reg rules
+ * @reg_rules_ptr: regulatory rules pointer
+ */
+struct reg_rule_info {
+	uint8_t alpha2[REG_ALPHA2_LEN + 1];
+	enum dfs_reg dfs_region;
+	uint8_t num_of_reg_rules;
+	struct cur_reg_rule *reg_rules_ptr;
+};
+
+/**
  * enum band_info
  * @BAND_ALL:all bands
  * @BAND_2G: 2G band
@@ -828,6 +842,7 @@
  * @def_country_code: default country code
  * @reg_dmn_pair: reg domain pair
  * @ctry_code: country code
+ * @reg_rules: regulatory rules
  */
 struct mas_chan_params {
 	enum dfs_reg dfs_region;
@@ -839,6 +854,7 @@
 	uint16_t def_country_code;
 	uint16_t reg_dmn_pair;
 	uint16_t ctry_code;
+	struct reg_rule_info reg_rules;
 };
 
 /**
diff --git a/umac/regulatory/dispatcher/inc/wlan_reg_ucfg_api.h b/umac/regulatory/dispatcher/inc/wlan_reg_ucfg_api.h
index 04b1916..0fb420b 100644
--- a/umac/regulatory/dispatcher/inc/wlan_reg_ucfg_api.h
+++ b/umac/regulatory/dispatcher/inc/wlan_reg_ucfg_api.h
@@ -220,6 +220,14 @@
 				    enum dfs_reg dfs_region);
 
 /**
+ * ucfg_reg_get_regd_rules() - provides the reg domain rules info pointer
+ * @pdev: pdev ptr
+ *
+ * Return: reg_rule_info pointer
+ */
+struct reg_rule_info *ucfg_reg_get_regd_rules(struct wlan_objmgr_pdev *pdev);
+
+/**
  * ucfg_reg_register_chan_change_callback () - add chan change cbk
  * @psoc: psoc ptr
  * @cbk: callback
diff --git a/umac/regulatory/dispatcher/src/wlan_reg_services_api.c b/umac/regulatory/dispatcher/src/wlan_reg_services_api.c
index 069e916..419096e 100644
--- a/umac/regulatory/dispatcher/src/wlan_reg_services_api.c
+++ b/umac/regulatory/dispatcher/src/wlan_reg_services_api.c
@@ -377,6 +377,8 @@
 QDF_STATUS regulatory_psoc_close(struct wlan_objmgr_psoc *psoc)
 {
 	struct wlan_lmac_if_reg_tx_ops *tx_ops;
+	struct wlan_regulatory_psoc_priv_obj *soc_reg;
+	uint8_t i;
 
 	tx_ops = reg_get_psoc_tx_ops(psoc);
 	if (tx_ops->unregister_11d_new_cc_handler)
@@ -386,6 +388,15 @@
 	if (tx_ops->unregister_ch_avoid_event_handler)
 		tx_ops->unregister_ch_avoid_event_handler(psoc, NULL);
 
+	soc_reg = reg_get_psoc_obj(psoc);
+
+	if (!soc_reg) {
+		reg_err("reg psoc private obj is NULL");
+		return QDF_STATUS_E_FAULT;
+	}
+	for (i = 0; i < PSOC_MAX_PHY_REG_CAP; i++)
+		reg_reset_reg_rules(&soc_reg->mas_chan_params[i].reg_rules);
+
 	return QDF_STATUS_SUCCESS;
 }
 
diff --git a/umac/regulatory/dispatcher/src/wlan_reg_ucfg_api.c b/umac/regulatory/dispatcher/src/wlan_reg_ucfg_api.c
index 97c34a8..4ae7b03 100644
--- a/umac/regulatory/dispatcher/src/wlan_reg_ucfg_api.c
+++ b/umac/regulatory/dispatcher/src/wlan_reg_ucfg_api.c
@@ -82,6 +82,11 @@
 	reg_program_mas_chan_list(psoc, reg_channels, alpha2, dfs_region);
 }
 
+struct reg_rule_info *ucfg_reg_get_regd_rules(struct wlan_objmgr_pdev *pdev)
+{
+	return reg_get_regd_rules(pdev);
+}
+
 QDF_STATUS ucfg_reg_program_default_cc(struct wlan_objmgr_pdev *pdev,
 				       uint16_t regdmn)
 {