qcacld-3.0: Set hw mode during channel switch in STA/P2P-CLI case

Set the hw mode, if needed, during channel switch in STA and
P2P-CLI scenario. The STA/P2P-CLI on receiving the channel change
event may need to do a hw mode change to get the best out of the
hw capabilities.

e.g.1, In a STA+SAP concurrency, the STA and SAP are doing SCC on
channel 6. When the STA interface receives a channel event for
channel 36, it is better the driver moves to a DBS scenario.

e.g.2, In a STA+SAP concurrency, the STA is on channel 6 and the
SAP is on channel 36. When the STA interface receives a channel
event for channel 36, it is better the driver moves from a DBS
scenario to a SCC scenario.

For MCC upgrade, the following steps are taken
1. Opportunistic timer is started
2. vdev restart is initiated on the new channel
3. PM will check if MCC upgrade can be done on timer expiry

For DBS downgrade, the following steps are taken
1. PM will initiate HW mode change to DBS right away
2. vdev restart is initiated on the new channel

Change-Id: I202842bf28c3117e8cc91954cdfd3b39a0062f4e
CRs-Fixed: 972184
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 2591138..664c523 100644
--- a/core/mac/src/pe/lim/lim_process_message_queue.c
+++ b/core/mac/src/pe/lim/lim_process_message_queue.c
@@ -62,6 +62,7 @@
 #include "qdf_types.h"
 #include "cds_packet.h"
 #include "qdf_mem.h"
+#include "cds_concurrency.h"
 
 void lim_log_session_states(tpAniSirGlobal pMac);
 static void lim_process_normal_hdd_msg(tpAniSirGlobal mac_ctx,
@@ -1147,6 +1148,210 @@
 #endif
 
 /**
+ * lim_handle_hw_mode_change_on_csa() - Do HW mode change on CSA for STA mode
+ * @mac_ctx: Global MAC context
+ * @msg: Received message
+ *
+ * Checks if hw mode change is required for the new channel.
+ * If MCC upgrade is required, this function will start the opportunistic
+ * timer and the caller will invoke the other APIs to perform vdev restart on
+ * the new channel.
+ *
+ * If DBS downgrade is required, this function will initiate the hw mode
+ * change and vdev restart will happen on the new channel after getting hw
+ * mode response
+ *
+ * Return: QDF_STATUS_SUCCESS if processing of csa params (and hence vdev
+ * restart) needs to happen or if no hw mode change is required,
+ * QDF_STATUS_E_FAILURE otherwise.
+ */
+static QDF_STATUS lim_handle_hw_mode_change_on_csa(tpAniSirGlobal mac_ctx,
+							tpSirMsgQ msg)
+{
+	tpPESession session_entry;
+	struct csa_offload_params *csa_params =
+				(struct csa_offload_params *) (msg->bodyptr);
+	tpDphHashNode sta_ds = NULL;
+	uint8_t session_id;
+	uint16_t aid = 0;
+	enum cds_conc_next_action action;
+	QDF_STATUS status = QDF_STATUS_E_FAILURE;
+
+	lim_log(mac_ctx, LOG1, FL("handle hw mode change for csa"));
+
+	if (!csa_params) {
+		lim_log(mac_ctx, LOGE, FL("limMsgQ body ptr is NULL"));
+		/* qdf_mem_free() can handle NULL values */
+		goto err;
+	}
+
+	session_entry = pe_find_session_by_bssid(mac_ctx,
+					csa_params->bssId, &session_id);
+	if (!session_entry) {
+		lim_log(mac_ctx, LOGE, FL("Session does not exist"));
+		goto err;
+	}
+
+	sta_ds = dph_lookup_hash_entry(mac_ctx, session_entry->bssId, &aid,
+			&session_entry->dph.dphHashTable);
+
+	if (!sta_ds) {
+		lim_log(mac_ctx, LOGE, FL("sta_ds does not exist"));
+		goto err;
+	}
+
+	/* Since all the write to the policy manager table happens in the
+	 * MC thread context and this channel change event is also processed
+	 * in the MC thread context, explicit lock/unlock of qdf_conc_list_lock
+	 * is not done here
+	 */
+	action = cds_get_pref_hw_mode_for_chan(session_entry->smeSessionId,
+				csa_params->channel);
+
+	if (action == CDS_NOP) {
+		lim_log(mac_ctx, LOG1, FL("no need for hw mode change"));
+		/* Proceed with processing csa params. So, not freeing it */
+		return QDF_STATUS_SUCCESS;
+	}
+
+	lim_log(mac_ctx, LOG1, FL("session:%d action:%d"),
+		session_entry->smeSessionId, action);
+
+	/*     1. Start opportunistic timer
+	 *     2. Do vdev restart on the new channel (by the caller)
+	 *     3. PM will check if MCC upgrade can be done after timer expiry
+	 */
+	if (action == CDS_MCC_UPGRADE) {
+		status = cds_stop_start_opportunistic_timer();
+		if (QDF_IS_STATUS_SUCCESS(status))
+			lim_log(mac_ctx, LOG1,
+				FL("opportunistic timer for MCC upgrade"));
+
+		/* After opportunistic timer is triggered, we can go ahead
+		 * with processing the csa params. So, not freeing the memory
+		 * through 'err' label.
+		 */
+		return QDF_STATUS_SUCCESS;
+	}
+
+	/*     CDS_DBS_DOWNGRADE:
+	 *     1. PM will initiate HW mode change to DBS rightaway
+	 *     2. Do vdev restart on the new channel (on getting hw mode resp)
+	 */
+	status = cds_next_actions(session_entry->smeSessionId, action,
+				CDS_UPDATE_REASON_CHANNEL_SWITCH_STA);
+	if (!QDF_IS_STATUS_SUCCESS(status)) {
+		lim_log(mac_ctx, LOGE, FL("no set hw mode command was issued"));
+		/* Proceed with processing csa params. So, not freeing it */
+		return QDF_STATUS_SUCCESS;
+	} else {
+		/* Save the csa params to be used after DBS downgrade */
+		qdf_mem_copy(&session_entry->saved_csa_params, csa_params,
+				sizeof(session_entry->saved_csa_params));
+
+		lim_log(mac_ctx, LOG1,
+			FL("saved csa params for dbs downgrade for %pM"),
+			session_entry->saved_csa_params.bssId);
+
+		/* Returning error so that csa params are not processed here */
+		status = QDF_STATUS_E_FAILURE;
+	}
+
+err:
+	qdf_mem_free(csa_params);
+	return status;
+}
+
+/**
+ * lim_handle_hw_mode_change_on_csa_event() - Handle hw mode change on csa
+ * @mac_ctx: Pointer to the Global Mac Context
+ * @msg: Received message
+ *
+ * Checks if a hw mode change is required for the received csa event. Processes
+ * csa params and do vdev restart immediately if the there is no need for a hw
+ * mode change or if MCC upgrade is required
+ *
+ * Return: None
+ */
+static void lim_handle_hw_mode_change_on_csa_event(tpAniSirGlobal mac_ctx,
+							tpSirMsgQ msg)
+{
+	QDF_STATUS qdf_status;
+
+	lim_log(mac_ctx, LOG1, FL("lim received csa offload event"));
+	if (mac_ctx->policy_manager_enabled &&
+			wma_is_hw_dbs_capable() == true) {
+		/* Check if a hw mode change is required */
+		qdf_status = lim_handle_hw_mode_change_on_csa(mac_ctx,
+				msg);
+		/* Process csa params and do vdev restart immediately if
+		 * there is no need for a hw mode change or if MCC upgrade is
+		 * required.
+		 */
+		if (QDF_IS_STATUS_SUCCESS(qdf_status))
+			lim_handle_csa_offload_msg(mac_ctx, msg);
+	} else {
+		lim_handle_csa_offload_msg(mac_ctx, msg);
+	}
+}
+
+/**
+ * lim_handle_csa_event_post_dbs_downgrade() - Process csa event post dbs
+ * downgrade
+ * @mac_ctx: Pointer to the Global Mac Context
+ * @msg: Received message
+ *
+ * Process the csa event to do vdev restart on the new channel after the dbs
+ * downgrade. If there was a DBS downgrade as part of the event
+ * WMA_CSA_OFFLOAD_EVENT, SIR_LIM_CSA_POST_HW_MODE_CHANGE will be received after
+ * receiving the set hw mode response, where this processing will happen.
+ *
+ * Return: None
+ */
+static void lim_handle_csa_event_post_dbs_downgrade(tpAniSirGlobal mac_ctx,
+							tpSirMsgQ msg)
+{
+	tSirMsgQ csa_msg;
+	tpPESession session_entry;
+
+	struct sir_saved_csa_params *buf =
+		(struct sir_saved_csa_params *)msg->bodyptr;
+
+	/* Null check for 'msg' already done before coming here in the caller */
+
+	session_entry = pe_find_session_by_sme_session_id(mac_ctx,
+				buf->session_id);
+	if (!session_entry) {
+		lim_log(mac_ctx, LOGE, FL("Invalid session id:%d"),
+			buf->session_id);
+		return;
+	}
+
+	lim_log(mac_ctx, LOG1,
+			FL("received csa offload event post hw change for %pM"),
+			session_entry->saved_csa_params.bssId);
+
+	csa_msg.bodyptr = qdf_mem_malloc(
+			sizeof(struct csa_offload_params));
+	if (!csa_msg.bodyptr) {
+		lim_log(mac_ctx, LOGE, FL("malloc failed for csa msg"));
+		goto clean_msg_body;
+	}
+
+	qdf_mem_copy((void *)csa_msg.bodyptr,
+			(void *)&session_entry->saved_csa_params,
+			sizeof(struct csa_offload_params));
+	/* If there was a DBS downgrade as part of the event
+	 * WMA_CSA_OFFLOAD_EVENT, SIR_LIM_CSA_POST_HW_MODE_CHANGE will
+	 * be received after receiving the set hw mode response.
+	 */
+	lim_handle_csa_offload_msg(mac_ctx, &csa_msg);
+clean_msg_body:
+	if (msg->bodyptr)
+		qdf_mem_free(msg->bodyptr);
+}
+
+/**
  * lim_process_messages() - Process messages from upper layers.
  *
  * @mac_ctx:          Pointer to the Global Mac Context.
@@ -1605,7 +1810,10 @@
 		lim_handle_delete_bss_rsp(mac_ctx, msg);
 		break;
 	case WMA_CSA_OFFLOAD_EVENT:
-		lim_handle_csa_offload_msg(mac_ctx, msg);
+		lim_handle_hw_mode_change_on_csa_event(mac_ctx, msg);
+		break;
+	case SIR_LIM_CSA_POST_HW_MODE_CHANGE:
+		lim_handle_csa_event_post_dbs_downgrade(mac_ctx, msg);
 		break;
 	case WMA_SET_BSSKEY_RSP:
 	case WMA_SET_STA_BCASTKEY_RSP: