Parcourir la source

qcacmn: Add support to process channel avoidance event

Add support to process channel avoidance event

Change-Id: I19ce742406a4778ac0f279faf0bf853bc9069eb0
CRs-Fixed: 2080241
Kiran Kumar Lokere il y a 7 ans
Parent
commit
a78b4bfb67

+ 66 - 0
target_if/regulatory/src/target_if_reg.c

@@ -221,6 +221,48 @@ static int tgt_reg_11d_new_cc_handler(ol_scn_t handle,
 	return 0;
 }
 
+static int tgt_reg_ch_avoid_event_handler(ol_scn_t handle,
+		uint8_t *event_buf, uint32_t len)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_lmac_if_reg_rx_ops *reg_rx_ops;
+	struct ch_avoid_ind_type ch_avoid_event;
+	QDF_STATUS status;
+
+	TARGET_IF_ENTER();
+
+	psoc = target_if_get_psoc_from_scn_hdl(handle);
+	if (!psoc) {
+		target_if_err("psoc ptr is NULL");
+		return -EINVAL;
+	}
+
+	reg_rx_ops = target_if_regulatory_get_rx_ops(psoc);
+
+	if (!reg_rx_ops->reg_ch_avoid_event_handler) {
+		target_if_err("reg_ch_avoid_event_handler is NULL");
+		return -EINVAL;
+	}
+
+	if (wmi_extract_reg_ch_avoid_event(GET_WMI_HDL_FROM_PSOC(psoc),
+				event_buf, &ch_avoid_event, len) !=
+			QDF_STATUS_SUCCESS) {
+
+		target_if_err("Extraction of CH avoid event failed");
+		return -EFAULT;
+	}
+
+	status = reg_rx_ops->reg_ch_avoid_event_handler(psoc, &ch_avoid_event);
+	if (status != QDF_STATUS_SUCCESS) {
+		target_if_err("Failed to process CH avoid event");
+		return -EFAULT;
+	}
+
+	target_if_debug("processed CH avoid event");
+
+	return 0;
+}
+
 static QDF_STATUS tgt_if_regulatory_register_master_list_handler(
 	struct wlan_objmgr_psoc *psoc, void *arg)
 {
@@ -284,6 +326,24 @@ static QDF_STATUS tgt_if_regulatory_unregister_11d_new_cc_handler(
 					    wmi_11d_new_country_event_id);
 }
 
+static QDF_STATUS tgt_if_regulatory_register_ch_avoid_event_handler(
+	struct wlan_objmgr_psoc *psoc, void *arg)
+{
+	wmi_unified_t wmi_handle = GET_WMI_HDL_FROM_PSOC(psoc);
+
+	return wmi_unified_register_event(wmi_handle,
+					  wmi_wlan_freq_avoid_event_id,
+					  tgt_reg_ch_avoid_event_handler);
+}
+
+static QDF_STATUS tgt_if_regulatory_unregister_ch_avoid_event_handler(
+	struct wlan_objmgr_psoc *psoc, void *arg)
+{
+	wmi_unified_t wmi_handle = GET_WMI_HDL_FROM_PSOC(psoc);
+
+	return wmi_unified_unregister_event(wmi_handle,
+			wmi_wlan_freq_avoid_event_id);
+}
 static QDF_STATUS tgt_if_regulatory_start_11d_scan(
 		struct wlan_objmgr_psoc *psoc,
 		struct reg_start_11d_scan_req *reg_start_11d_scan_req)
@@ -338,6 +398,12 @@ QDF_STATUS target_if_register_regulatory_tx_ops(struct wlan_lmac_if_tx_ops
 	reg_ops->set_user_country_code =
 		tgt_if_regulatory_set_user_country_code;
 
+	reg_ops->register_ch_avoid_event_handler =
+		tgt_if_regulatory_register_ch_avoid_event_handler;
+
+	reg_ops->unregister_ch_avoid_event_handler =
+		tgt_if_regulatory_unregister_ch_avoid_event_handler;
+
 	return QDF_STATUS_SUCCESS;
 }
 

+ 6 - 0
umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h

@@ -422,6 +422,10 @@ struct wlan_lmac_if_reg_tx_ops {
 					    uint8_t pdev_id,
 					    struct cc_regdmn_s *rd);
 	QDF_STATUS (*set_country_failed)(struct wlan_objmgr_pdev *pdev);
+	QDF_STATUS (*register_ch_avoid_event_handler)(
+			struct wlan_objmgr_psoc *psoc, void *arg);
+	QDF_STATUS (*unregister_ch_avoid_event_handler)(
+			struct wlan_objmgr_psoc *psoc, void *arg);
 };
 
 /**
@@ -598,6 +602,8 @@ struct wlan_lmac_if_reg_rx_ops {
 			bool val);
 	QDF_STATUS (*get_dfs_region)(struct wlan_objmgr_pdev *pdev,
 			enum dfs_reg *dfs_reg);
+	QDF_STATUS (*reg_ch_avoid_event_handler)(struct wlan_objmgr_psoc *psoc,
+			struct ch_avoid_ind_type *ch_avoid_ind);
 };
 
 #ifdef CONVERGED_P2P_ENABLE

+ 3 - 0
umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c

@@ -208,6 +208,9 @@ static void wlan_lmac_if_umac_reg_rx_ops_register(
 
 	rx_ops->reg_rx_ops.get_dfs_region =
 		wlan_reg_get_dfs_region;
+
+	rx_ops->reg_rx_ops.reg_ch_avoid_event_handler =
+		tgt_reg_process_ch_avoid_event;
 }
 
 #ifdef CONVERGED_P2P_ENABLE

+ 4 - 0
umac/regulatory/core/src/reg_priv.h

@@ -69,6 +69,9 @@ struct wlan_regulatory_psoc_priv_obj {
 	bool user_ctry_set;
 	struct chan_change_cbk_entry cbk_list[REG_MAX_CHAN_CHANGE_CBKS];
 	uint8_t num_chan_change_cbks;
+	uint8_t ch_avoid_ind;
+	struct unsafe_ch_list unsafe_chan_list;
+	struct ch_avoid_ind_type avoid_freq_list;
 	qdf_spinlock_t cbk_list_lock;
 };
 
@@ -92,6 +95,7 @@ struct wlan_regulatory_pdev_priv_obj {
 	bool indoor_chan_enabled;
 	bool en_chan_144;
 	uint32_t wireless_modes;
+	struct ch_avoid_ind_type freq_avoid_list;
 };
 
 #endif

+ 248 - 3
umac/regulatory/core/src/reg_services.c

@@ -1946,6 +1946,38 @@ reg_modify_chan_list_for_nol_list(struct regulatory_channel *chan_list)
 	}
 }
 
+static void
+reg_modify_chan_list_for_unsafe_ch(struct wlan_regulatory_pdev_priv_obj
+		*pdev_priv_obj)
+{
+	enum channel_enum chan_enum;
+	struct regulatory_channel *chan_list;
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
+	struct unsafe_ch_list *unsafe_list;
+	uint8_t i;
+
+	psoc = wlan_pdev_get_psoc(pdev_priv_obj->pdev_ptr);
+	psoc_priv_obj = (struct wlan_regulatory_psoc_priv_obj *)
+		wlan_objmgr_psoc_get_comp_private_obj(psoc,
+					 WLAN_UMAC_COMP_REGULATORY);
+	if (!psoc_priv_obj) {
+		reg_err("psoc priv obj is NULL");
+		return;
+	}
+
+	chan_list = pdev_priv_obj->cur_chan_list;
+	unsafe_list = &psoc_priv_obj->unsafe_chan_list;
+
+	for (i = 0; i < unsafe_list->ch_cnt; i++) {
+		chan_enum = reg_get_chan_enum(unsafe_list->ch_list[i]);
+		if (chan_enum == INVALID_CHANNEL)
+			continue;
+		chan_list[chan_enum].state = CHANNEL_STATE_DISABLE;
+		chan_list[chan_enum].chan_flags |= REGULATORY_CHAN_DISABLED;
+	}
+}
+
 static void
 reg_modify_chan_list_for_freq_range(struct regulatory_channel *chan_list,
 				uint32_t low_freq_2g,
@@ -2066,6 +2098,8 @@ static void reg_compute_pdev_current_chan_list(struct wlan_regulatory_pdev_priv_
 
 	reg_modify_chan_list_for_chan_144(pdev_priv_obj->cur_chan_list,
 					  pdev_priv_obj->en_chan_144);
+
+	reg_modify_chan_list_for_unsafe_ch(pdev_priv_obj);
 }
 
 static void reg_call_chan_change_cbks(struct wlan_objmgr_psoc *psoc,
@@ -2076,6 +2110,7 @@ static void reg_call_chan_change_cbks(struct wlan_objmgr_psoc *psoc,
 	struct wlan_regulatory_pdev_priv_obj *pdev_priv_obj;
 	struct regulatory_channel *cur_chan_list;
 	uint32_t ctr;
+	struct avoid_freq_ind_data *avoid_freq_ind = NULL;
 	reg_chan_change_callback callback;
 
 	psoc_priv_obj = reg_get_psoc_obj(psoc);
@@ -2101,6 +2136,22 @@ static void reg_call_chan_change_cbks(struct wlan_objmgr_psoc *psoc,
 		     NUM_CHANNELS *
 		     sizeof(struct regulatory_channel));
 
+	if (psoc_priv_obj->ch_avoid_ind) {
+		avoid_freq_ind = qdf_mem_malloc(sizeof(*avoid_freq_ind));
+		if (!avoid_freq_ind) {
+			reg_alert("Mem alloc failed for avoid freq ind");
+			goto skip_ch_avoid_ind;
+		}
+		qdf_mem_copy(&avoid_freq_ind->freq_list,
+				&psoc_priv_obj->avoid_freq_list,
+				sizeof(struct ch_avoid_ind_type));
+		qdf_mem_copy(&avoid_freq_ind->chan_list,
+				&psoc_priv_obj->unsafe_chan_list,
+				sizeof(struct unsafe_ch_list));
+		psoc_priv_obj->ch_avoid_ind = false;
+	}
+
+skip_ch_avoid_ind:
 	cbk_list = psoc_priv_obj->cbk_list;
 
 	for (ctr = 0; ctr < REG_MAX_CHAN_CHANGE_CBKS; ctr++) {
@@ -2110,10 +2161,12 @@ static void reg_call_chan_change_cbks(struct wlan_objmgr_psoc *psoc,
 			callback = cbk_list[ctr].cbk;
 		qdf_spin_unlock_bh(&psoc_priv_obj->cbk_list_lock);
 		if (callback != NULL)
-			callback(psoc, pdev, cur_chan_list,
-				 cbk_list[ctr].arg);
+			callback(psoc, pdev, cur_chan_list, avoid_freq_ind,
+					cbk_list[ctr].arg);
 	}
 	qdf_mem_free(cur_chan_list);
+	if (!avoid_freq_ind)
+		qdf_mem_free(avoid_freq_ind);
 }
 
 static struct reg_sched_payload
@@ -3588,6 +3641,199 @@ QDF_STATUS reg_get_current_cc(struct wlan_objmgr_pdev *pdev,
 	return QDF_STATUS_SUCCESS;
 }
 
+static QDF_STATUS reg_process_ch_avoid_freq(struct wlan_objmgr_psoc *psoc,
+		struct wlan_objmgr_pdev *pdev)
+{
+	enum channel_enum ch_loop;
+	enum channel_enum start_ch_idx;
+	enum channel_enum end_ch_idx;
+	uint16_t start_channel;
+	uint16_t end_channel;
+	uint32_t i;
+	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
+
+	psoc_priv_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
+		WLAN_UMAC_COMP_REGULATORY);
+
+	if (!psoc_priv_obj) {
+		reg_err("reg psoc private obj is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	for (i = 0; i < psoc_priv_obj->avoid_freq_list.ch_avoid_range_cnt;
+		i++) {
+		if (psoc_priv_obj->unsafe_chan_list.ch_cnt >= NUM_CHANNELS) {
+			reg_warn("LTE Coex unsafe channel list full");
+			break;
+		}
+
+		start_ch_idx = INVALID_CHANNEL;
+		end_ch_idx = INVALID_CHANNEL;
+		start_channel = reg_freq_to_chan(pdev,
+			psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].start_freq);
+		end_channel = reg_freq_to_chan(pdev,
+			psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].end_freq);
+		reg_debug("start: freq %d, ch %d, end: freq %d, ch %d",
+			psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].start_freq,
+			start_channel,
+			psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].end_freq,
+			end_channel);
+
+		/* do not process frequency bands that are not mapped to
+		 * predefined channels
+		 */
+		if (start_channel == 0 || end_channel == 0)
+			continue;
+
+		for (ch_loop = 0; ch_loop < NUM_CHANNELS;
+			ch_loop++) {
+			if (REG_CH_TO_FREQ(ch_loop) >= psoc_priv_obj->avoid_freq_list.
+				avoid_freq_range[i].start_freq) {
+				start_ch_idx = ch_loop;
+				break;
+			}
+		}
+		for (ch_loop = 0; ch_loop < NUM_CHANNELS;
+			ch_loop++) {
+			if (REG_CH_TO_FREQ(ch_loop) >= psoc_priv_obj->avoid_freq_list.
+				avoid_freq_range[i].end_freq) {
+				end_ch_idx = ch_loop;
+				if (REG_CH_TO_FREQ(ch_loop) > psoc_priv_obj->avoid_freq_list.
+					avoid_freq_range[i].end_freq)
+					end_ch_idx--;
+				break;
+			}
+		}
+
+		if (start_ch_idx == INVALID_CHANNEL ||
+				end_ch_idx == INVALID_CHANNEL)
+			continue;
+
+		for (ch_loop = start_ch_idx; ch_loop <=	end_ch_idx;
+			ch_loop++) {
+			psoc_priv_obj->unsafe_chan_list.ch_list[
+				psoc_priv_obj->unsafe_chan_list.ch_cnt++] =
+				REG_CH_NUM(ch_loop);
+			if (psoc_priv_obj->unsafe_chan_list.ch_cnt >=
+				NUM_CHANNELS) {
+				reg_warn("LTECoex unsafe ch list full");
+				break;
+			}
+		}
+	}
+
+	reg_debug("number of unsafe channels is %d ",
+		psoc_priv_obj->unsafe_chan_list.ch_cnt);
+
+	if (!psoc_priv_obj->unsafe_chan_list.ch_cnt) {
+		reg_warn("No valid ch are present in avoid freq event");
+		psoc_priv_obj->ch_avoid_ind = false;
+		return QDF_STATUS_SUCCESS;
+	}
+
+	for (ch_loop = 0; ch_loop < psoc_priv_obj->unsafe_chan_list.ch_cnt;
+		ch_loop++) {
+		reg_debug("channel %d is not safe",
+			psoc_priv_obj->unsafe_chan_list.
+			ch_list[ch_loop]);
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * reg_update_unsafe_ch () - Updates unsafe channels in current channel list
+ * @pdev: pointer to pdev object
+ * @ch_avoid_list: pointer to unsafe channel list
+ *
+ * Return: None
+ */
+static void reg_update_unsafe_ch(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;
+	QDF_STATUS status;
+
+	psoc_priv_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
+						  WLAN_UMAC_COMP_REGULATORY);
+
+	if (!psoc_priv_obj) {
+		reg_err("reg psoc private 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 priv obj is NULL");
+		return;
+	}
+
+	if (psoc_priv_obj->ch_avoid_ind) {
+		status = reg_process_ch_avoid_freq(psoc, pdev);
+		if (QDF_IS_STATUS_ERROR(status))
+			psoc_priv_obj->ch_avoid_ind = false;
+	}
+
+	reg_compute_pdev_current_chan_list(pdev_priv_obj);
+	status = reg_send_scheduler_msg_nb(psoc, pdev);
+
+	if (QDF_IS_STATUS_ERROR(status))
+		reg_err("channel change msg schedule failed");
+
+}
+
+QDF_STATUS reg_process_ch_avoid_event(struct wlan_objmgr_psoc *psoc,
+		struct ch_avoid_ind_type *ch_avoid_event)
+{
+	uint32_t i;
+	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;
+	QDF_STATUS status;
+
+	psoc_priv_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc,
+			WLAN_UMAC_COMP_REGULATORY);
+	if (!psoc_priv_obj) {
+		reg_err("reg psoc private obj is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+	/* Make unsafe channel list */
+	reg_debug("band count %d", ch_avoid_event->ch_avoid_range_cnt);
+
+	/* generate vendor specific event */
+	qdf_mem_zero((void *)&psoc_priv_obj->avoid_freq_list,
+			sizeof(struct ch_avoid_ind_type));
+	qdf_mem_zero((void *)&psoc_priv_obj->unsafe_chan_list,
+			sizeof(struct unsafe_ch_list));
+
+	for (i = 0; i < ch_avoid_event->ch_avoid_range_cnt; i++) {
+		psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].start_freq =
+			ch_avoid_event->avoid_freq_range[i].start_freq;
+		psoc_priv_obj->avoid_freq_list.avoid_freq_range[i].end_freq =
+			ch_avoid_event->avoid_freq_range[i].end_freq;
+	}
+	psoc_priv_obj->avoid_freq_list.ch_avoid_range_cnt =
+		ch_avoid_event->ch_avoid_range_cnt;
+
+	psoc_priv_obj->ch_avoid_ind = true;
+
+	status = wlan_objmgr_psoc_try_get_ref(psoc, WLAN_REGULATORY_NB_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_update_unsafe_ch, NULL, 1,
+			WLAN_REGULATORY_NB_ID);
+
+	wlan_objmgr_psoc_release_ref(psoc, WLAN_REGULATORY_NB_ID);
+
+	return status;
+}
+
 QDF_STATUS reg_save_new_11d_country(struct wlan_objmgr_psoc *psoc,
 				    uint8_t *country)
 {
@@ -3607,7 +3853,6 @@ QDF_STATUS reg_save_new_11d_country(struct wlan_objmgr_psoc *psoc,
 	return QDF_STATUS_SUCCESS;
 }
 
-
 bool reg_11d_enabled_on_host(struct wlan_objmgr_psoc *psoc)
 {
 	struct wlan_regulatory_psoc_priv_obj *psoc_priv_obj;

+ 14 - 4
umac/regulatory/core/src/reg_services.h

@@ -280,10 +280,10 @@ QDF_STATUS reg_get_curr_band(struct wlan_objmgr_pdev *pdev,
 		enum band_info *band);
 
 typedef void (*reg_chan_change_callback)(struct wlan_objmgr_psoc *psoc,
-					      struct wlan_objmgr_pdev *pdev,
-					      struct regulatory_channel
-					      *chan_list,
-					      void *arg);
+		struct wlan_objmgr_pdev *pdev,
+		struct regulatory_channel *chan_list,
+		struct avoid_freq_ind_data *avoid_freq_ind,
+		void *arg);
 
 void reg_register_chan_change_callback(struct wlan_objmgr_psoc *psoc,
 				       reg_chan_change_callback cbk,
@@ -391,4 +391,14 @@ QDF_STATUS reg_modify_chan_144(struct wlan_objmgr_pdev *pdev,
  * Return: en_chan_144 flag value
  */
 bool reg_get_en_chan_144(struct wlan_objmgr_pdev *pdev);
+
+/**
+ * reg_process_ch_avoid_event() - Process channel avoid event
+ * @psoc: psoc for country information
+ * @ch_avoid_event: channel avoid event buffer
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS reg_process_ch_avoid_event(struct wlan_objmgr_psoc *psoc,
+		struct ch_avoid_ind_type *ch_avoid_event);
 #endif

+ 43 - 0
umac/regulatory/dispatcher/inc/reg_services_public_struct.h

@@ -33,8 +33,10 @@
 #define MAX_STA_VDEV_CNT 4
 #define INVALID_VDEV_ID 0xFF
 #define INVALID_CHANNEL_NUM 0xBAD
+#define CH_AVOID_MAX_RANGE   4
 
 #ifdef CONFIG_LEGACY_CHAN_ENUM
+
 /**
  * enum channel_enum - channel enumeration
  * @CHAN_ENUM_1:  channel number 1
@@ -846,4 +848,45 @@ struct cur_regdmn_info {
 	uint8_t ctl_5g;
 	uint8_t dfs_region;
 };
+
+/**
+ * struct ch_avoid_freq_type
+ * @start_freq: start freq
+ * @end_freq: end freq
+ */
+struct ch_avoid_freq_type {
+	uint32_t start_freq;
+	uint32_t end_freq;
+};
+
+/**
+ * struct ch_avoid_ind_type
+ * @ch_avoid_range_cnt: count
+ * @avoid_freq_range: avoid freq range array
+ */
+struct ch_avoid_ind_type {
+	uint32_t ch_avoid_range_cnt;
+	struct ch_avoid_freq_type avoid_freq_range[CH_AVOID_MAX_RANGE];
+};
+
+/**
+ * struct unsafe_ch_list
+ * @ch_cnt: no.of channels
+ * @ch_list: channel list
+ */
+struct unsafe_ch_list {
+	uint16_t ch_cnt;
+	uint16_t ch_list[NUM_CHANNELS];
+};
+
+/**
+ * struct avoid_freq_ind_data
+ * @freq_list: frequency list
+ * @chan_list: channel list
+ */
+struct avoid_freq_ind_data {
+	struct ch_avoid_ind_type freq_list;
+	struct unsafe_ch_list chan_list;
+};
+
 #endif

+ 9 - 0
umac/regulatory/dispatcher/inc/wlan_reg_tgt_api.h

@@ -60,4 +60,13 @@ QDF_STATUS tgt_reg_set_regdb_offloaded(struct wlan_objmgr_psoc *psoc,
  */
 QDF_STATUS tgt_reg_set_11d_offloaded(struct wlan_objmgr_psoc *psoc,
 		bool val);
+/**
+ * tgt_reg_process_ch_avoid_event() - process new ch avoid event
+ * @psoc: pointer to psoc
+ * @ch_avoid_evnt: channel avoid event
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS tgt_reg_process_ch_avoid_event(struct wlan_objmgr_psoc *psoc,
+		struct ch_avoid_ind_type *ch_avoid_evnt);
 #endif

+ 6 - 0
umac/regulatory/dispatcher/src/wlan_reg_tgt_api.c

@@ -56,3 +56,9 @@ QDF_STATUS tgt_reg_set_11d_offloaded(struct wlan_objmgr_psoc *psoc,
 {
 	return reg_set_11d_offloaded(psoc, val);
 }
+
+QDF_STATUS tgt_reg_process_ch_avoid_event(struct wlan_objmgr_psoc *psoc,
+		struct ch_avoid_ind_type *ch_avoid_evnt)
+{
+	return reg_process_ch_avoid_event(psoc, ch_avoid_evnt);
+}

+ 5 - 0
wmi/inc/wmi_unified_priv.h

@@ -1332,6 +1332,11 @@ QDF_STATUS (*extract_reg_11d_new_country_event)(wmi_unified_t wmi_handle,
 		struct reg_11d_new_country *reg_11d_country,
 		uint32_t len);
 
+QDF_STATUS (*extract_reg_ch_avoid_event)(wmi_unified_t wmi_handle,
+		uint8_t *evt_buf,
+		struct ch_avoid_ind_type *ch_avoid_event,
+		uint32_t len);
+
 QDF_STATUS (*extract_chainmask_tables)(wmi_unified_t wmi_handle,
 		uint8_t *evt_buf,
 		struct wlan_psoc_host_chainmask_table *chainmask_table);

+ 15 - 0
wmi/inc/wmi_unified_reg_api.h

@@ -82,4 +82,19 @@ QDF_STATUS wmi_extract_reg_11d_new_cc_event(void *wmi_hdl,
  */
 QDF_STATUS wmi_unified_set_user_country_code_cmd_send(void *wmi_hdl,
 		uint8_t pdev_id, struct cc_regdmn_s *rd);
+
+/**
+ * wmi_extract_reg_ch_avoid_event() - process freq avoid event
+ * @wmi_hdl: wmi handle.
+ * @evt_buf: event buffer
+ * @ch_avoid_ind: buffer pointer to save the event processed data
+ * @len: lenght of buffer
+ *
+ * Return: QDF_STATUS_SUCCESS on success and QDF_STATUS_E_FAILURE for failure
+ */
+QDF_STATUS wmi_extract_reg_ch_avoid_event(void *wmi_hdl,
+		uint8_t *evt_buf,
+		struct ch_avoid_ind_type *ch_avoid_ind,
+		uint32_t len);
+
 #endif /* _WMI_UNIFIED_REG_API_H_ */

+ 14 - 0
wmi/src/wmi_unified_reg_api.c

@@ -101,3 +101,17 @@ QDF_STATUS wmi_unified_set_user_country_code_cmd_send(void *wmi_hdl,
 
 	return QDF_STATUS_E_FAILURE;
 }
+
+QDF_STATUS wmi_extract_reg_ch_avoid_event(void *wmi_hdl,
+		uint8_t *evt_buf,
+		struct ch_avoid_ind_type *ch_avoid_ind,
+		uint32_t len)
+{
+	struct wmi_unified *wmi_handle = (struct wmi_unified *)wmi_hdl;
+
+	if (wmi_handle->ops->extract_reg_ch_avoid_event)
+		return wmi_handle->ops->extract_reg_ch_avoid_event(
+				wmi_handle, evt_buf, ch_avoid_ind, len);
+
+	return QDF_STATUS_E_FAILURE;
+}

+ 51 - 0
wmi/src/wmi_unified_tlv.c

@@ -18035,6 +18035,55 @@ static QDF_STATUS extract_reg_11d_new_country_event_tlv(
 
 	return QDF_STATUS_SUCCESS;
 }
+
+static QDF_STATUS extract_reg_ch_avoid_event_tlv(
+	wmi_unified_t wmi_handle, uint8_t *evt_buf,
+	struct ch_avoid_ind_type *ch_avoid_ind, uint32_t len)
+{
+	wmi_avoid_freq_ranges_event_fixed_param *afr_fixed_param;
+	wmi_avoid_freq_range_desc *afr_desc;
+	uint32_t num_freq_ranges, freq_range_idx;
+	WMI_WLAN_FREQ_AVOID_EVENTID_param_tlvs *param_buf =
+		(WMI_WLAN_FREQ_AVOID_EVENTID_param_tlvs *) evt_buf;
+
+	if (!param_buf) {
+		WMI_LOGE("Invalid channel avoid event buffer");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	afr_fixed_param = param_buf->fixed_param;
+	if (!afr_fixed_param) {
+		WMI_LOGE("Invalid channel avoid event fixed param buffer");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!ch_avoid_ind) {
+		WMI_LOGE("Invalid channel avoid indication buffer");
+		return QDF_STATUS_E_INVAL;
+	}
+	num_freq_ranges = (afr_fixed_param->num_freq_ranges >
+			CH_AVOID_MAX_RANGE) ? CH_AVOID_MAX_RANGE :
+			afr_fixed_param->num_freq_ranges;
+
+	WMI_LOGD("Channel avoid event received with %d ranges",
+		 num_freq_ranges);
+
+	ch_avoid_ind->ch_avoid_range_cnt = num_freq_ranges;
+	afr_desc = (wmi_avoid_freq_range_desc *)(param_buf->avd_freq_range);
+	for (freq_range_idx = 0; freq_range_idx < num_freq_ranges;
+	     freq_range_idx++) {
+		ch_avoid_ind->avoid_freq_range[freq_range_idx].start_freq =
+			afr_desc->start_freq;
+		ch_avoid_ind->avoid_freq_range[freq_range_idx].end_freq =
+			afr_desc->end_freq;
+		WMI_LOGD("range %d tlv id %u, start freq %u, end freq %u",
+				freq_range_idx, afr_desc->tlv_header,
+				afr_desc->start_freq, afr_desc->end_freq);
+		afr_desc++;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
 #ifdef DFS_COMPONENT_ENABLE
 /**
  * extract_dfs_cac_complete_event_tlv() - extract cac complete event
@@ -18678,6 +18727,8 @@ struct wmi_ops tlv_ops =  {
 	.send_user_country_code_cmd = send_user_country_code_cmd_tlv,
 	.send_limit_off_chan_cmd =
 		send_limit_off_chan_cmd_tlv,
+	.extract_reg_ch_avoid_event =
+		extract_reg_ch_avoid_event_tlv,
 };
 
 /**