qcacld-3.0: Add bss color collision detection support

Add support for bss color collision detection.

Change-Id: Idd616ca902469f5dc446d35e63fce7fe7eb0d327
CRs-Fixed: 2130127
diff --git a/components/pmo/core/src/wlan_pmo_wow.c b/components/pmo/core/src/wlan_pmo_wow.c
index 97a125c..10e1081 100644
--- a/components/pmo/core/src/wlan_pmo_wow.c
+++ b/components/pmo/core/src/wlan_pmo_wow.c
@@ -427,4 +427,7 @@
 	pmo_set_wow_event_bitmap(WOW_SAP_OBSS_DETECTION_EVENT,
 				 wow_bitmap_size,
 				 bitmask);
+	pmo_set_wow_event_bitmap(WOW_BSS_COLOR_COLLISION_DETECT_EVENT,
+				 wow_bitmap_size,
+				 bitmask);
 }
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index fa6b5f4..8837452 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -2015,6 +2015,12 @@
 				 cfg->obss_detection_offloaded);
 	if (QDF_IS_STATUS_ERROR(status))
 		hdd_err("Couldn't pass WNI_CFG_OBSS_DETECTION_OFFLOAD to CFG");
+
+	status = sme_cfg_set_int(hdd_ctx->hHal,
+				 WNI_CFG_OBSS_COLOR_COLLISION_OFFLOAD,
+				 cfg->obss_color_collision_offloaded);
+	if (QDF_IS_STATUS_ERROR(status))
+		hdd_err("Failed to set WNI_CFG_OBSS_COLOR_COLLISION_OFFLOAD");
 }
 
 bool hdd_dfs_indicate_radar(struct hdd_context *hdd_ctx)
diff --git a/core/mac/inc/ani_global.h b/core/mac/inc/ani_global.h
index 476319a..c251278 100644
--- a/core/mac/inc/ani_global.h
+++ b/core/mac/inc/ani_global.h
@@ -832,6 +832,7 @@
 	uint8_t beacon_probe_rsp_cnt_per_scan;
 	wlan_scan_requester req_id;
 	bool global_obss_offload_enabled;
+	bool global_obss_color_collision_det_offload;
 } tAniSirLim, *tpAniSirLim;
 
 struct mgmt_frm_reg_info {
diff --git a/core/mac/inc/wni_cfg.h b/core/mac/inc/wni_cfg.h
index 4c6bf4f..a99897f 100644
--- a/core/mac/inc/wni_cfg.h
+++ b/core/mac/inc/wni_cfg.h
@@ -354,6 +354,7 @@
 	WNI_CFG_EDCA_ETSI_ACVI,
 	WNI_CFG_EDCA_ETSI_ACVO,
 	WNI_CFG_OBSS_DETECTION_OFFLOAD,
+	WNI_CFG_OBSS_COLOR_COLLISION_OFFLOAD,
 	/* Any new items to be added should be above this strictly */
 	CFG_PARAM_MAX_NUM
 };
diff --git a/core/mac/src/cfg/cfg_param_name.c b/core/mac/src/cfg/cfg_param_name.c
index 857ea17..a646122 100644
--- a/core/mac/src/cfg/cfg_param_name.c
+++ b/core/mac/src/cfg/cfg_param_name.c
@@ -356,6 +356,7 @@
 	CASE_RETURN_STRING(WNI_CFG_EDCA_ETSI_ACVI);
 	CASE_RETURN_STRING(WNI_CFG_EDCA_ETSI_ACVO);
 	CASE_RETURN_STRING(WNI_CFG_OBSS_DETECTION_OFFLOAD);
+	CASE_RETURN_STRING(WNI_CFG_OBSS_COLOR_COLLISION_OFFLOAD);
 	}
 
 	return "invalid";
diff --git a/core/mac/src/cfg/cfg_proc_msg.c b/core/mac/src/cfg/cfg_proc_msg.c
index 0644b84..c1aae42 100644
--- a/core/mac/src/cfg/cfg_proc_msg.c
+++ b/core/mac/src/cfg/cfg_proc_msg.c
@@ -1584,6 +1584,9 @@
 	{WNI_CFG_OBSS_DETECTION_OFFLOAD,
 	 CFG_CTL_VALID | CFG_CTL_RE | CFG_CTL_WE | CFG_CTL_INT,
 	 0, 1, 0},
+	{WNI_CFG_OBSS_COLOR_COLLISION_OFFLOAD,
+	 CFG_CTL_VALID | CFG_CTL_RE | CFG_CTL_WE | CFG_CTL_INT,
+	 0, 1, 0},
 };
 
 
diff --git a/core/mac/src/include/sir_params.h b/core/mac/src/include/sir_params.h
index 472992f..ad84a0f 100644
--- a/core/mac/src/include/sir_params.h
+++ b/core/mac/src/include/sir_params.h
@@ -694,6 +694,8 @@
 #define SIR_HAL_OBSS_DETECTION_REQ          (SIR_HAL_ITC_MSG_TYPES_BEGIN + 393)
 #define SIR_HAL_OBSS_DETECTION_INFO         (SIR_HAL_ITC_MSG_TYPES_BEGIN + 394)
 #define SIR_HAL_INVOKE_NEIGHBOR_REPORT      (SIR_HAL_ITC_MSG_TYPES_BEGIN + 395)
+#define SIR_HAL_OBSS_COLOR_COLLISION_REQ    (SIR_HAL_ITC_MSG_TYPES_BEGIN + 396)
+#define SIR_HAL_OBSS_COLOR_COLLISION_INFO   (SIR_HAL_ITC_MSG_TYPES_BEGIN + 397)
 #define SIR_HAL_MSG_TYPES_END               (SIR_HAL_MSG_TYPES_BEGIN + 0x1FF)
 
 /* CFG message types */
diff --git a/core/mac/src/pe/include/lim_session.h b/core/mac/src/pe/include/lim_session.h
index c637d5c..6f79b35 100644
--- a/core/mac/src/pe/include/lim_session.h
+++ b/core/mac/src/pe/include/lim_session.h
@@ -100,6 +100,10 @@
 #define MAX_BSS_COLOR_VALUE 63
 #define TIME_BEACON_NOT_UPDATED 30000
 #define BSS_COLOR_SWITCH_COUNTDOWN 5
+#define OBSS_COLOR_COLLISION_DETECTION_STA_PERIOD_MS 10000
+#define OBSS_COLOR_COLLISION_DETECTION_AP_PERIOD_MS 5000
+#define OBSS_COLOR_COLLISION_SCAN_PERIOD_MS 200
+#define OBSS_COLOR_COLLISION_FREE_SLOT_EXPIRY_MS 50000
 struct bss_color_info {
 	qdf_time_t timestamp;
 	uint64_t seen_count;
@@ -555,6 +559,8 @@
 	bool recvd_deauth_while_roaming;
 	bool recvd_disassoc_while_roaming;
 	bool deauth_disassoc_rc;
+	enum wmi_obss_color_collision_evt_type obss_color_collision_dec_evt;
+	bool is_session_obss_color_collision_det_enabled;
 } tPESession, *tpPESession;
 
 /*-------------------------------------------------------------------------
diff --git a/core/mac/src/pe/lim/lim_api.c b/core/mac/src/pe/lim/lim_api.c
index 4ea1362..6387ea7 100644
--- a/core/mac/src/pe/lim/lim_api.c
+++ b/core/mac/src/pe/lim/lim_api.c
@@ -476,6 +476,14 @@
 		return eSIR_FAILURE;
 	}
 
+	if (eSIR_SUCCESS !=
+	    wlan_cfg_get_int(pMac, WNI_CFG_OBSS_COLOR_COLLISION_OFFLOAD,
+			     (uint32_t *) &pMac->lim.
+			     global_obss_color_collision_det_offload)) {
+		pe_err("cfg get obss_color_collision_offload failed");
+		return eSIR_FAILURE;
+	}
+
 	return eSIR_SUCCESS;
 }
 
diff --git a/core/mac/src/pe/lim/lim_process_message_queue.c b/core/mac/src/pe/lim/lim_process_message_queue.c
index c22a695..898547a 100644
--- a/core/mac/src/pe/lim/lim_process_message_queue.c
+++ b/core/mac/src/pe/lim/lim_process_message_queue.c
@@ -1921,6 +1921,11 @@
 		qdf_mem_free((void *)msg->bodyptr);
 		msg->bodyptr = NULL;
 		break;
+	case WMA_OBSS_COLOR_COLLISION_INFO:
+		lim_process_obss_color_collision_info(mac_ctx, msg->bodyptr);
+		qdf_mem_free((void *)msg->bodyptr);
+		msg->bodyptr = NULL;
+		break;
 	default:
 		qdf_mem_free((void *)msg->bodyptr);
 		msg->bodyptr = NULL;
diff --git a/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c b/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c
index 7977e1b..29ae301 100644
--- a/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c
+++ b/core/mac/src/pe/lim/lim_process_mlm_rsp_messages.c
@@ -255,7 +255,9 @@
 					psessionEntry->ssId.ssId,
 					psessionEntry->currentOperChannel);
 			lim_send_beacon_ind(pMac, psessionEntry);
-			lim_enable_obss_detection_config (pMac, psessionEntry);
+			lim_enable_obss_detection_config(pMac, psessionEntry);
+			lim_send_obss_color_collision_cfg(pMac, psessionEntry,
+					OBSS_COLOR_COLLISION_DETECTION);
 		}
 	}
 }
@@ -1564,6 +1566,8 @@
 		 * be passed in here
 		 */
 		mlm_assoc_cnf.resultCode = (tSirResultCodes) eSIR_SME_SUCCESS;
+		lim_send_obss_color_collision_cfg(mac_ctx, session_entry,
+					OBSS_COLOR_COLLISION_DETECTION);
 	} else {
 		pe_err("ADD_STA failed!");
 		if (session_entry->limSmeState == eLIM_SME_WT_REASSOC_STATE)
diff --git a/core/mac/src/pe/lim/lim_process_sme_req_messages.c b/core/mac/src/pe/lim/lim_process_sme_req_messages.c
index f9aea2f..2a39975 100644
--- a/core/mac/src/pe/lim/lim_process_sme_req_messages.c
+++ b/core/mac/src/pe/lim/lim_process_sme_req_messages.c
@@ -5095,6 +5095,8 @@
 			  psessionEntry->currentOperChannel);
 		lim_send_beacon_ind(pMac, psessionEntry);
 		lim_enable_obss_detection_config(pMac, psessionEntry);
+		lim_send_obss_color_collision_cfg(pMac, psessionEntry,
+					OBSS_COLOR_COLLISION_DETECTION);
 	} else {
 		pe_err("Invalid Beacon Start Indication");
 		return;
@@ -5979,6 +5981,163 @@
 }
 
 #ifdef WLAN_FEATURE_11AX_BSS_COLOR
+
+/**
+ * obss_color_collision_process_color_disable() - Disable bss color
+ * @mac_ctx: Pointer to Global MAC structure
+ * @session: pointer to session
+ *
+ * This function will disbale bss color.
+ *
+ * Return: None
+ */
+static void obss_color_collision_process_color_disable(tpAniSirGlobal mac_ctx,
+						       tpPESession session)
+{
+	tUpdateBeaconParams beacon_params;
+
+	if (!session) {
+		pe_err("Invalid session");
+		return;
+	}
+
+	if (session->valid && !LIM_IS_AP_ROLE(session)) {
+		pe_err("Invalid SystemRole %d",
+		       GET_LIM_SYSTEM_ROLE(session));
+		return;
+	}
+
+	if (session->bss_color_changing == 1) {
+		pe_warn("%d: color change in progress", session->smeSessionId);
+		/* Continue color collision detection */
+		lim_send_obss_color_collision_cfg(mac_ctx, session,
+				OBSS_COLOR_COLLISION_DETECTION);
+		return;
+	}
+
+	if (session->he_op.bss_col_disabled == 1) {
+		pe_warn("%d: bss color already disabled",
+			session->smeSessionId);
+		/* Continue free color detection */
+		lim_send_obss_color_collision_cfg(mac_ctx, session,
+				OBSS_COLOR_FREE_SLOT_AVAILABLE);
+		return;
+	}
+
+	qdf_mem_zero(&beacon_params, sizeof(beacon_params));
+	beacon_params.paramChangeBitmap |= PARAM_BSS_COLOR_CHANGED;
+	session->he_op.bss_col_disabled = 1;
+	session->he_bss_color_change.new_color = 0;
+	session->he_op.bss_color = 0;
+	beacon_params.bss_color = 0;
+	beacon_params.bss_color_disabled = 1;
+
+	if (sch_set_fixed_beacon_fields(mac_ctx, session) !=
+	    eSIR_SUCCESS) {
+		pe_err("Unable to set op mode IE in beacon");
+		return;
+	}
+
+	lim_send_beacon_params(mac_ctx, &beacon_params, session);
+	lim_send_obss_color_collision_cfg(mac_ctx, session,
+					  OBSS_COLOR_FREE_SLOT_AVAILABLE);
+}
+
+/**
+ * obss_color_collision_process_color_change() - Process bss color change
+ * @mac_ctx: Pointer to Global MAC structure
+ * @session: pointer to session
+ * @obss_color_info: obss color collision/free slot indication info
+ *
+ * This function selects new color ib case of bss color collision.
+ *
+ * Return: None
+ */
+static void obss_color_collision_process_color_change(tpAniSirGlobal mac_ctx,
+		tpPESession session,
+		struct wmi_obss_color_collision_info *obss_color_info)
+{
+	int i, num_bss_color = 0;
+	uint32_t bss_color_bitmap;
+	uint8_t bss_color_index_array[MAX_BSS_COLOR_VALUE];
+	uint32_t rand_byte = 0;
+	struct sir_set_he_bss_color he_bss_color;
+	bool is_color_collision = false;
+
+
+	if (session->bss_color_changing == 1) {
+		pe_err("%d: color change in progress", session->smeSessionId);
+		return;
+	}
+
+	if (!session->he_op.bss_col_disabled) {
+		if (session->he_op.bss_color < 32)
+			is_color_collision = (obss_color_info->
+					     obss_color_bitmap_bit0to31 >>
+					     session->he_op.bss_color) & 0x01;
+		else
+			is_color_collision = (obss_color_info->
+					     obss_color_bitmap_bit32to63 >>
+					     (session->he_op.bss_color -
+					      31)) & 0x01;
+		if (!is_color_collision) {
+			pe_err("%d: color collision not found, curr_color: %d",
+			       session->smeSessionId,
+			       session->he_op.bss_color);
+			return;
+		}
+	}
+
+	bss_color_bitmap = obss_color_info->obss_color_bitmap_bit0to31;
+
+	/* Skip color zero */
+	bss_color_bitmap = bss_color_bitmap >> 1;
+	for (i = 0; (i < 31) && (num_bss_color < MAX_BSS_COLOR_VALUE); i++) {
+		if (!(bss_color_bitmap & 0x01)) {
+			bss_color_index_array[num_bss_color] = i + 1;
+			num_bss_color++;
+		}
+		bss_color_bitmap = bss_color_bitmap >> 1;
+	}
+
+	bss_color_bitmap = obss_color_info->obss_color_bitmap_bit32to63;
+	for (i = 0; (i < 32) && (num_bss_color < MAX_BSS_COLOR_VALUE); i++) {
+		if (!(bss_color_bitmap & 0x01)) {
+			bss_color_index_array[num_bss_color] = i + 32;
+			num_bss_color++;
+		}
+		bss_color_bitmap = bss_color_bitmap >> 1;
+	}
+
+	if (num_bss_color) {
+		qdf_get_random_bytes((void *) &rand_byte, 1);
+		i = (rand_byte + qdf_mc_timer_get_system_ticks()) %
+		    num_bss_color;
+		pe_debug("New bss color = %d", bss_color_index_array[i]);
+		he_bss_color.session_id = obss_color_info->vdev_id;
+		he_bss_color.bss_color = bss_color_index_array[i];
+		lim_process_set_he_bss_color(mac_ctx,
+					     (uint32_t *)&he_bss_color);
+	} else {
+		pe_err("Unable to find bss color from bitmasp");
+		if (obss_color_info->evt_type ==
+		    OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY &&
+		    session->obss_color_collision_dec_evt ==
+		    OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY)
+			/* In dot11BSSColorCollisionAPPeriod and
+			 * timer expired, time to disable bss color.
+			 */
+			obss_color_collision_process_color_disable(mac_ctx,
+								   session);
+		else
+			/*
+			 * Enter dot11BSSColorCollisionAPPeriod period.
+			 */
+			lim_send_obss_color_collision_cfg(mac_ctx, session,
+					OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY);
+	}
+}
+
 void lim_process_set_he_bss_color(tpAniSirGlobal mac_ctx, uint32_t *msg_buf)
 {
 	struct sir_set_he_bss_color *bss_color;
@@ -6016,9 +6175,6 @@
 	session_entry->he_bss_color_change.countdown =
 		BSS_COLOR_SWITCH_COUNTDOWN;
 	session_entry->he_bss_color_change.new_color = bss_color->bss_color;
-	session_entry->he_op.bss_color =
-		session_entry->he_bss_color_change.new_color;
-	beacon_params.bss_color = session_entry->he_op.bss_color;
 	beacon_params.bss_color_disabled =
 		session_entry->he_op.bss_col_disabled;
 	session_entry->bss_color_changing = 1;
@@ -6030,5 +6186,132 @@
 	}
 
 	lim_send_beacon_params(mac_ctx, &beacon_params, session_entry);
+	lim_send_obss_color_collision_cfg(mac_ctx, session_entry,
+			OBSS_COLOR_COLLISION_DETECTION_DISABLE);
+}
+
+void lim_send_obss_color_collision_cfg(tpAniSirGlobal mac_ctx,
+				       tpPESession session,
+				       enum wmi_obss_color_collision_evt_type
+				       event_type)
+{
+	struct wmi_obss_color_collision_cfg_param *cfg_param;
+	struct scheduler_msg msg = {0};
+
+	if (!session) {
+		pe_err("Invalid session");
+		return;
+	}
+
+	if (!session->he_capable ||
+	    !session->is_session_obss_color_collision_det_enabled) {
+		pe_debug("%d: obss color det not enabled, he_cap:%d, sup:%d:%d",
+			 session->smeSessionId, session->he_capable,
+			 session->is_session_obss_color_collision_det_enabled,
+			 mac_ctx->lim.global_obss_color_collision_det_offload);
+		return;
+	}
+
+	cfg_param = qdf_mem_malloc(sizeof(*cfg_param));
+	if (!cfg_param) {
+		pe_err("Failed to allocate memory");
+		return;
+	}
+
+	pe_debug("%d: sending event:%d", session->smeSessionId, event_type);
+	qdf_mem_zero(cfg_param, sizeof(*cfg_param));
+	cfg_param->vdev_id = session->smeSessionId;
+	cfg_param->evt_type = event_type;
+	if (LIM_IS_AP_ROLE(session))
+		cfg_param->detection_period_ms =
+			OBSS_COLOR_COLLISION_DETECTION_AP_PERIOD_MS;
+	else
+		cfg_param->detection_period_ms =
+			OBSS_COLOR_COLLISION_DETECTION_STA_PERIOD_MS;
+
+	cfg_param->scan_period_ms = OBSS_COLOR_COLLISION_SCAN_PERIOD_MS;
+	if (event_type == OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY)
+		cfg_param->free_slot_expiry_time_ms =
+			OBSS_COLOR_COLLISION_FREE_SLOT_EXPIRY_MS;
+
+	msg.type = WMA_OBSS_COLOR_COLLISION_REQ;
+	msg.bodyptr = cfg_param;
+	msg.reserved = 0;
+
+	if (QDF_IS_STATUS_ERROR(scheduler_post_msg(QDF_MODULE_ID_WMA, &msg))) {
+		pe_err("Failed to post WMA_OBSS_COLOR_COLLISION_REQ to WMA");
+		qdf_mem_free(cfg_param);
+	} else {
+		session->obss_color_collision_dec_evt = event_type;
+	}
+}
+
+void lim_process_obss_color_collision_info(tpAniSirGlobal mac_ctx,
+					   uint32_t *msg_buf)
+{
+	struct wmi_obss_color_collision_info *obss_color_info;
+	tpPESession session;
+
+	if (!msg_buf) {
+		pe_err("Buffer is Pointing to NULL");
+		return;
+	}
+
+	obss_color_info = (struct wmi_obss_color_collision_info *)msg_buf;
+	session = pe_find_session_by_sme_session_id(mac_ctx,
+						    obss_color_info->vdev_id);
+	if (!session) {
+		pe_err("Session not found for given session_id %d",
+			obss_color_info->vdev_id);
+		return;
+	}
+
+	pe_debug("vdev_id:%d, evt:%d:%d, 0to31:0x%x, 32to63:0x%x, cap:%d:%d:%d",
+		 obss_color_info->vdev_id,
+		 obss_color_info->evt_type,
+		 session->obss_color_collision_dec_evt,
+		 obss_color_info->obss_color_bitmap_bit0to31,
+		 obss_color_info->obss_color_bitmap_bit32to63,
+		 session->he_capable,
+		 session->is_session_obss_color_collision_det_enabled,
+		 mac_ctx->lim.global_obss_color_collision_det_offload);
+
+	if (!session->he_capable ||
+	    !session->is_session_obss_color_collision_det_enabled) {
+		return;
+	}
+
+	switch (obss_color_info->evt_type) {
+	case OBSS_COLOR_COLLISION_DETECTION_DISABLE:
+		pe_err("%d: FW disabled obss color det. he_cap:%d, sup:%d:%d",
+		       session->smeSessionId, session->he_capable,
+		       session->is_session_obss_color_collision_det_enabled,
+		       mac_ctx->lim.global_obss_color_collision_det_offload);
+		session->is_session_obss_color_collision_det_enabled = false;
+		return;
+	case OBSS_COLOR_FREE_SLOT_AVAILABLE:
+	case OBSS_COLOR_COLLISION_DETECTION:
+	case OBSS_COLOR_FREE_SLOT_TIMER_EXPIRY:
+		if (session->valid && !LIM_IS_AP_ROLE(session)) {
+			pe_debug("Invalid System Role %d",
+				 GET_LIM_SYSTEM_ROLE(session));
+			return;
+		}
+
+		if (session->obss_color_collision_dec_evt !=
+		    obss_color_info->evt_type) {
+			pe_debug("%d: Wrong event: %d, skiping",
+				 obss_color_info->vdev_id,
+				 obss_color_info->evt_type);
+			return;
+		}
+		obss_color_collision_process_color_change(mac_ctx, session,
+							  obss_color_info);
+		break;
+	default:
+		pe_err("%d: Invalid event type %d",
+		       obss_color_info->vdev_id, obss_color_info->evt_type);
+		return;
+	}
 }
 #endif
diff --git a/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c b/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c
index 93757ca..ce4d45f 100644
--- a/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c
+++ b/core/mac/src/pe/lim/lim_send_sme_rsp_messages.c
@@ -2535,8 +2535,14 @@
 			session->he_bss_color_change.countdown--;
 		} else {
 			session->bss_color_changing = 0;
-			if (session->he_bss_color_change.new_color != 0)
+			if (session->he_bss_color_change.new_color != 0) {
 				session->he_op.bss_col_disabled = 0;
+				session->he_op.bss_color =
+					session->he_bss_color_change.new_color;
+				lim_send_obss_color_collision_cfg(mac_ctx,
+						session,
+						OBSS_COLOR_COLLISION_DETECTION);
+			}
 		}
 
 		lim_send_bss_color_change_ie_update(mac_ctx, session);
diff --git a/core/mac/src/pe/lim/lim_session.c b/core/mac/src/pe/lim/lim_session.c
index e30a5ee..2656571 100644
--- a/core/mac/src/pe/lim/lim_session.c
+++ b/core/mac/src/pe/lim/lim_session.c
@@ -473,6 +473,8 @@
 	session_ptr->fWaitForProbeRsp = 0;
 	session_ptr->fIgnoreCapsChange = 0;
 	session_ptr->ignore_assoc_disallowed = pMac->ignore_assoc_disallowed;
+	session_ptr->is_session_obss_color_collision_det_enabled =
+		pMac->lim.global_obss_color_collision_det_offload;
 
 	pe_debug("Create a new PE session: %d BSSID: "MAC_ADDRESS_STR" Max No of STA: %d",
 		*sessionId, MAC_ADDR_ARRAY(bssid), numSta);
diff --git a/core/mac/src/pe/lim/lim_types.h b/core/mac/src/pe/lim/lim_types.h
index d6e47ab..ed0cb5f 100644
--- a/core/mac/src/pe/lim/lim_types.h
+++ b/core/mac/src/pe/lim/lim_types.h
@@ -567,10 +567,41 @@
  * Return: void
  */
 void lim_process_set_he_bss_color(tpAniSirGlobal mac_ctx, uint32_t *msg_buf);
+
+/**
+ * lim_process_obss_color_collision_info() - Process the obss color collision
+ *  request.
+ * @mac_ctx: global mac context pointer
+ * @msg_buf: message buffer pointer
+ *
+ * Return: void
+ */
+void lim_process_obss_color_collision_info(tpAniSirGlobal mac_ctx,
+					   uint32_t *msg_buf);
+
+/**
+ * lim_send_obss_color_collision_cfg() - Send obss color collision cfg.
+ * @mac_ctx: global mac context pointer
+ * @session: Pointer to session
+ * @event_type: obss color collision detection type
+ *
+ * Return: void
+ */
+void lim_send_obss_color_collision_cfg(tpAniSirGlobal mac_ctx,
+				       tpPESession session,
+				       enum wmi_obss_color_collision_evt_type
+				       event_type);
 #else
 static inline void lim_process_set_he_bss_color(tpAniSirGlobal mac_ctx,
 		uint32_t *msg_buf)
 {}
+static inline void lim_process_obss_color_collision_info(tpAniSirGlobal mac_ctx,
+							 uint32_t *msg_buf)
+{}
+static inline void lim_send_obss_color_collision_cfg(tpAniSirGlobal mac_ctx,
+			tpPESession session,
+			enum wmi_obss_color_collision_evt_type event_type)
+{}
 #endif
 void lim_send_delts_req_action_frame(tpAniSirGlobal pMac, tSirMacAddr peer,
 				     uint8_t wmmTspecPresent,
diff --git a/core/mac/src/pe/sch/sch_beacon_process.c b/core/mac/src/pe/sch/sch_beacon_process.c
index 7c4b489..9339266 100644
--- a/core/mac/src/pe/sch/sch_beacon_process.c
+++ b/core/mac/src/pe/sch/sch_beacon_process.c
@@ -492,6 +492,9 @@
 	uint8_t session_bss_col_disabled_flag;
 	bool anything_changed = false;
 
+	if (session->is_session_obss_color_collision_det_enabled)
+		return;
+
 	if (session->he_op.present && bcn->he_op.present) {
 		if (bcn->vendor_he_bss_color_change.present &&
 				(session->he_op.bss_color !=
@@ -1055,7 +1058,9 @@
 
 		bcn_prm.bssIdx = ap_session->bssIdx;
 
-		sch_check_bss_color_ie(mac_ctx, ap_session, &bcn, &bcn_prm);
+		if (!ap_session->is_session_obss_color_collision_det_enabled)
+			sch_check_bss_color_ie(mac_ctx, ap_session,
+					       &bcn, &bcn_prm);
 
 		if ((ap_session->gLimProtectionControl !=
 		     WNI_CFG_FORCE_POLICY_PROTECTION_DISABLE) &&
diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h
index 1e322a8..9b5d088 100644
--- a/core/wma/inc/wma_internal.h
+++ b/core/wma/inc/wma_internal.h
@@ -1360,4 +1360,21 @@
  */
 int wma_vdev_obss_detection_info_handler(void *handle, uint8_t *event,
 					 uint32_t len);
+
+/**
+ * wma_vdev_bss_color_collision_info_handler - event handler to
+ *  handle obss color collision detection.
+ * @handle: the wma handle
+ * @event: buffer with event
+ * @len: buffer length
+ *
+ * This function receives obss color collision detection info from firmware
+ * which is used to select new bss color.
+ *
+ * Return: 0 on success
+ */
+int wma_vdev_bss_color_collision_info_handler(void *handle,
+					      uint8_t *event,
+					      uint32_t len);
+
 #endif
diff --git a/core/wma/inc/wma_tgt_cfg.h b/core/wma/inc/wma_tgt_cfg.h
index 90b0f06..8556b3c 100644
--- a/core/wma/inc/wma_tgt_cfg.h
+++ b/core/wma/inc/wma_tgt_cfg.h
@@ -161,6 +161,7 @@
  * @tx_bfee_8ss_enabled: Tx Beamformee support for 8x8
  * @rcpi_enabled: for checking rcpi support
  * @obss_detection_offloaded: obss detection offloaded to firmware
+ * @obss_color_collision_offloaded: obss color collision offloaded to firmware
  */
 struct wma_tgt_cfg {
 	uint32_t target_fw_version;
@@ -196,5 +197,6 @@
 	bool tx_bfee_8ss_enabled;
 	bool rcpi_enabled;
 	bool obss_detection_offloaded;
+	bool obss_color_collision_offloaded;
 };
 #endif /* WMA_TGT_CFG_H */
diff --git a/core/wma/inc/wma_types.h b/core/wma/inc/wma_types.h
index e0a8e02..7ad9665 100644
--- a/core/wma/inc/wma_types.h
+++ b/core/wma/inc/wma_types.h
@@ -474,6 +474,8 @@
 #define WMA_OBSS_DETECTION_REQ               SIR_HAL_OBSS_DETECTION_REQ
 #define WMA_OBSS_DETECTION_INFO              SIR_HAL_OBSS_DETECTION_INFO
 #define WMA_INVOKE_NEIGHBOR_REPORT           SIR_HAL_INVOKE_NEIGHBOR_REPORT
+#define WMA_OBSS_COLOR_COLLISION_REQ         SIR_HAL_OBSS_COLOR_COLLISION_REQ
+#define WMA_OBSS_COLOR_COLLISION_INFO        SIR_HAL_OBSS_COLOR_COLLISION_INFO
 
 /* Bit 6 will be used to control BD rate for Management frames */
 #define HAL_USE_BD_RATE2_FOR_MANAGEMENT_FRAME 0x40
diff --git a/core/wma/src/wma_dev_if.c b/core/wma/src/wma_dev_if.c
index 876cb84..296032d 100644
--- a/core/wma/src/wma_dev_if.c
+++ b/core/wma/src/wma_dev_if.c
@@ -4585,9 +4585,49 @@
 #endif
 
 /**
+ * wma_send_bss_color_change_enable() - send bss color chnage enable cmd.
+ * @wma: wma handle
+ * @params: add sta params
+ *
+ * Send bss color change command to firmware, to enable firmware to update
+ * internally if any change in bss color in advertised by associated AP.
+ *
+ * Return: none
+ */
+#ifdef WLAN_FEATURE_11AX
+static void wma_send_bss_color_change_enable(tp_wma_handle wma,
+					     tpAddStaParams params)
+{
+	QDF_STATUS status;
+	uint32_t vdev_id = params->smesessionId;
+
+	if (!params->he_capable) {
+		WMA_LOGD("%s: he_capable is not set for vdev_id:%d",
+			 __func__, vdev_id);
+		return;
+	}
+
+	status = wmi_unified_send_bss_color_change_enable_cmd(wma->wmi_handle,
+							      vdev_id,
+							      true);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		WMA_LOGE("Failed to enable bss color change offload, vdev:%d",
+			 vdev_id);
+	}
+
+	return;
+}
+#else
+static void wma_send_bss_color_change_enable(tp_wma_handle wma,
+					     tpAddStaParams params)
+{
+}
+#endif
+
+/**
  * wma_add_sta_req_sta_mode() - process add sta request in sta mode
  * @wma: wma handle
- * @add_sta: add sta params
+ * @params: add sta params
  *
  * Return: none
  */
@@ -4796,6 +4836,8 @@
 		}
 	}
 
+	wma_send_bss_color_change_enable(wma, params);
+
 	/* Partial AID match power save, enable when SU bformee */
 	if (params->enableVhtpAid && params->vhtTxBFCapable)
 		wma_set_ppsconfig(params->smesessionId,
diff --git a/core/wma/src/wma_features.c b/core/wma/src/wma_features.c
index 4705b2e..db0d7d0 100644
--- a/core/wma/src/wma_features.c
+++ b/core/wma/src/wma_features.c
@@ -1612,6 +1612,10 @@
 		return "CHIP_POWER_FAILURE_DETECT";
 	case WOW_REASON_11D_SCAN:
 		return "11D_SCAN";
+	case WOW_REASON_SAP_OBSS_DETECTION:
+		return "SAP_OBSS_DETECTION";
+	case WOW_REASON_BSS_COLOR_COLLISION_DETECT:
+		return "BSS_COLOR_COLLISION_DETECT";
 	default:
 		return "unknown";
 	}
@@ -5332,3 +5336,44 @@
 
 	return 0;
 }
+
+int wma_vdev_bss_color_collision_info_handler(void *handle,
+					      uint8_t *event,
+					      uint32_t len)
+{
+	tp_wma_handle wma = (tp_wma_handle) handle;
+	struct wmi_obss_color_collision_info *obss_color_info;
+	QDF_STATUS status;
+
+	if (!event) {
+		WMA_LOGE("Invalid obss_color_collision event buffer");
+		return -EINVAL;
+	}
+
+	obss_color_info = qdf_mem_malloc(sizeof(*obss_color_info));
+	if (!obss_color_info) {
+		WMA_LOGE("%s: Failed to malloc", __func__);
+		return -ENOMEM;
+	}
+
+	status = wmi_unified_extract_obss_color_collision_info(wma->wmi_handle,
+							       event,
+							       obss_color_info);
+
+	if (QDF_IS_STATUS_ERROR(status)) {
+		WMA_LOGE("%s: Failed to extract obss color info", __func__);
+		qdf_mem_free(obss_color_info);
+		return -EINVAL;
+	}
+
+	if (!wma_is_vdev_valid(obss_color_info->vdev_id)) {
+		WMA_LOGE("%s: Invalid vdev id %d", __func__,
+			 obss_color_info->vdev_id);
+		qdf_mem_free(obss_color_info);
+		return -EINVAL;
+	}
+
+	wma_send_msg(wma, WMA_OBSS_COLOR_COLLISION_INFO, obss_color_info, 0);
+
+	return 0;
+}
diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c
index 36d858e..0d19c69 100644
--- a/core/wma/src/wma_main.c
+++ b/core/wma/src/wma_main.c
@@ -3304,6 +3304,12 @@
 			wma_vdev_obss_detection_info_handler,
 			WMA_RX_SERIALIZER_CTX);
 
+	wmi_unified_register_event_handler(wma_handle->wmi_handle,
+			wmi_obss_color_collision_report_event_id,
+			wma_vdev_bss_color_collision_info_handler,
+			WMA_RX_WORK_CTX);
+
+
 	return QDF_STATUS_SUCCESS;
 
 err_dbglog_init:
@@ -5068,15 +5074,33 @@
 static void wma_update_obss_detection_support(tp_wma_handle wh,
 					      struct wma_tgt_cfg *tgt_cfg)
 {
-	if (WMI_SERVICE_EXT_IS_ENABLED(wh->wmi_service_bitmap,
-				       wh->wmi_service_ext_bitmap,
-				       WMI_SERVICE_AP_OBSS_DETECTION_OFFLOAD))
+	if (wmi_service_enabled(wh->wmi_handle,
+				wmi_service_ap_obss_detection_offload))
 		tgt_cfg->obss_detection_offloaded = true;
 	else
 		tgt_cfg->obss_detection_offloaded = false;
 }
 
 /**
+ * wma_update_obss_color_collision_support() - update obss color collision
+ *   offload support
+ * @wh: wma handle
+ * @tgt_cfg: target configuration to be updated
+ *
+ * Update obss color collision offload support based on service bit.
+ *
+ * Return: None
+ */
+static void wma_update_obss_color_collision_support(tp_wma_handle wh,
+						    struct wma_tgt_cfg *tgt_cfg)
+{
+	if (wmi_service_enabled(wh->wmi_handle, wmi_service_bss_color_offload))
+		tgt_cfg->obss_color_collision_offloaded = true;
+	else
+		tgt_cfg->obss_color_collision_offloaded = false;
+}
+
+/**
  * wma_update_hdd_cfg() - update HDD config
  * @wma_handle: wma handle
  *
@@ -5152,6 +5176,7 @@
 			      - WMI_TLV_HEADROOM;
 	tgt_cfg.tx_bfee_8ss_enabled = wma_handle->tx_bfee_8ss_enabled;
 	wma_update_obss_detection_support(wma_handle, &tgt_cfg);
+	wma_update_obss_color_collision_support(wma_handle, &tgt_cfg);
 	wma_update_hdd_cfg_ndp(wma_handle, &tgt_cfg);
 	wma_handle->tgt_cfg_update_cb(hdd_ctx, &tgt_cfg);
 	target_if_store_pdev_target_if_ctx(wma_get_pdev_from_scn_handle);
@@ -7300,6 +7325,29 @@
 	return QDF_STATUS_SUCCESS;
 }
 
+static QDF_STATUS wma_process_obss_color_collision_req(tp_wma_handle wma_handle,
+		struct wmi_obss_color_collision_cfg_param *cfg)
+{
+	QDF_STATUS status;
+
+	if (cfg->vdev_id >= wma_handle->max_bssid) {
+		WMA_LOGE(FL("Invalid vdev_id: %d"), cfg->vdev_id);
+		return QDF_STATUS_E_INVAL;
+	}
+	if (!wma_is_vdev_up(cfg->vdev_id)) {
+		WMA_LOGE("vdev %d is not up skipping obss color collision req",
+			 cfg->vdev_id);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	status = wmi_unified_send_obss_color_collision_cfg_cmd(wma_handle->
+							       wmi_handle, cfg);
+	if (QDF_IS_STATUS_ERROR(status))
+		WMA_LOGE("Failed to send obss color collision cfg");
+
+	return status;
+}
+
 /**
  * wma_send_obss_detection_cfg() - send obss detection cfg to firmware
  * @wma_handle: pointer to wma handle
@@ -8090,6 +8138,10 @@
 		wma_send_invoke_neighbor_report(wma_handle, msg->bodyptr);
 		qdf_mem_free(msg->bodyptr);
 		break;
+	case WMA_OBSS_COLOR_COLLISION_REQ:
+		wma_process_obss_color_collision_req(wma_handle, msg->bodyptr);
+		qdf_mem_free(msg->bodyptr);
+		break;
 	default:
 		WMA_LOGE("Unhandled WMA message of type %d", msg->type);
 		if (msg->bodyptr)