qcacld-3.0: Add support for changing LISTEN interval dynamically

Current method for calculating LISTEN INTERVAL are static
configuration a.ka. ini based. Now OEM / USER want to take
control of setting LISTEN INTERVAL as per their applications
need. Once if USER configure the LISTEN INTERVAL value using
vendor command then host should avoid changing the LI value
during each suspend/resume. User LI value will be override
configuration. Once USER will Disable the LI using vendor
command then host can fallback to current default method.

Change-Id: Ia9b412b073c059df0cdff7bcda8198f7581e796d
CRs-Fixed: 2040298
diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c
index 8a80165..293a7aa 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.c
+++ b/core/hdd/src/wlan_hdd_cfg80211.c
@@ -5426,6 +5426,7 @@
 		.type = NLA_UNSPEC,
 		.len = QDF_MAC_ADDR_SIZE},
 	[RX_BLOCKSIZE_WINLIMIT] = {.type = NLA_U32},
+	[QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL] = {.type = NLA_U32 },
 	[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA] = {.type = NLA_U32 },
 	[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN] = {.type = NLA_U32 },
 	[QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST] = {.type = NLA_U32 },
@@ -5735,7 +5736,7 @@
 	struct hdd_context *hdd_ctx  = wiphy_priv(wiphy);
 	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1];
 	int ret_val = 0;
-	u32 modulated_dtim;
+	u32 modulated_dtim, override_li;
 	u16 stats_avg_factor;
 	u32 guard_time;
 	uint8_t set_value;
@@ -5802,6 +5803,18 @@
 			ret_val = -EPERM;
 	}
 
+	if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL]) {
+		override_li = nla_get_u32(
+			tb[QCA_WLAN_VENDOR_ATTR_CONFIG_LISTEN_INTERVAL]);
+
+		status = sme_override_listen_interval(hdd_ctx->hHal,
+						      adapter->sessionId,
+						      override_li);
+
+		if (status != QDF_STATUS_SUCCESS)
+			ret_val = -EPERM;
+	}
+
 	if (tb[QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER]) {
 		qpower = nla_get_u8(
 			tb[QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER]);
diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h
index 9678fd0..33780e1 100644
--- a/core/sme/inc/sme_api.h
+++ b/core/sme/inc/sme_api.h
@@ -960,6 +960,9 @@
 QDF_STATUS sme_configure_modulated_dtim(tHalHandle hal, uint8_t session_id,
 				      uint32_t modulated_dtim);
 
+QDF_STATUS sme_override_listen_interval(tHalHandle h_hal, uint8_t session_id,
+		uint32_t override_li);
+
 QDF_STATUS sme_configure_stats_avg_factor(tHalHandle hal, uint8_t session_id,
 					  uint16_t stats_avg_factor);
 
diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c
index 6005e4d..34d8f04 100644
--- a/core/sme/src/common/sme_api.c
+++ b/core/sme/src/common/sme_api.c
@@ -13733,6 +13733,63 @@
 	return status;
 }
 
+/**
+ * sme_override_listen_interval() - function to override static LI
+ * @h_hal: SME API to override listen interval
+ * @session_id: session ID
+ * @override_li: new LI value passed by user
+ *
+ * This function override (enable/disable) static a.k.a ini based LI
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS sme_override_listen_interval(tHalHandle h_hal, uint8_t session_id,
+					uint32_t override_li)
+{
+	struct scheduler_msg msg = {0};
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	tpAniSirGlobal mac  = PMAC_STRUCT(h_hal);
+	wma_cli_set_cmd_t *iwcmd;
+
+	iwcmd = qdf_mem_malloc(sizeof(*iwcmd));
+	if (!iwcmd) {
+		QDF_TRACE(QDF_MODULE_ID_SME,
+			  QDF_TRACE_LEVEL_FATAL,
+			  "%s: qdf_mem_malloc failed", __func__);
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	status = sme_acquire_global_lock(&mac->sme);
+
+	if (status == QDF_STATUS_SUCCESS) {
+
+		iwcmd->param_value = override_li;
+		iwcmd->param_vdev_id = session_id;
+		iwcmd->param_id = GEN_PARAM_LISTEN_INTERVAL;
+		iwcmd->param_vp_dev = GEN_CMD;
+		msg.type = WMA_CLI_SET_CMD;
+		msg.reserved = 0;
+		msg.bodyptr = (void *)iwcmd;
+
+		if (!QDF_IS_STATUS_SUCCESS(
+			    scheduler_post_msg(QDF_MODULE_ID_WMA, &msg))) {
+			QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
+				  "%s: Can't post GEN_PARAM_LISTEN_INTERVAL",
+				  __func__);
+			qdf_mem_free(iwcmd);
+			status = QDF_STATUS_E_FAILURE;
+		}
+		sme_release_global_lock(&mac->sme);
+	} else {
+		QDF_TRACE(QDF_MODULE_ID_SME, QDF_TRACE_LEVEL_ERROR,
+			  "%s: sme_acquire_global_lock error!",
+			  __func__);
+		qdf_mem_free(iwcmd);
+	}
+
+	return status;
+}
+
 /*
  * sme_wifi_start_logger() - Send the start/stop logging command to WMA
  * to either start/stop logging
diff --git a/core/wma/inc/wma.h b/core/wma/inc/wma.h
index bb073ac..0c62e35 100644
--- a/core/wma/inc/wma.h
+++ b/core/wma/inc/wma.h
@@ -1029,6 +1029,7 @@
  * @plink_status_req: link status request
  * @psnr_req: snr request
  * @delay_before_vdev_stop: delay
+ * @override_li: dynamically user configured listen interval
  * @tx_streams: number of tx streams can be used by the vdev
  * @rx_streams: number of rx streams can be used by the vdev
  * @chain_mask: chain mask can be used by the vdev
@@ -1101,6 +1102,7 @@
 #endif
 	uint32_t alt_modulated_dtim;
 	bool alt_modulated_dtim_enabled;
+	uint32_t override_li;
 	uint32_t tx_streams;
 	uint32_t rx_streams;
 	uint32_t chain_mask;
diff --git a/core/wma/inc/wma_api.h b/core/wma/inc/wma_api.h
index 962d8c1..062bd40 100644
--- a/core/wma/inc/wma_api.h
+++ b/core/wma/inc/wma_api.h
@@ -53,6 +53,7 @@
  * @GEN_PARAM_CAPTURE_TSF: read tsf
  * @GEN_PARAM_RESET_TSF_GPIO: reset tsf gpio
  * @GEN_VDEV_ROAM_SYNCH_DELAY: roam sync delay
+ * @GEN_PARAM_LISTEN_INTERVAL: listen interval
  */
 enum GEN_PARAM {
 	GEN_VDEV_PARAM_AMPDU = 0x1,
@@ -62,6 +63,7 @@
 	GEN_PARAM_CAPTURE_TSF,
 	GEN_PARAM_RESET_TSF_GPIO,
 	GEN_VDEV_ROAM_SYNCH_DELAY,
+	GEN_PARAM_LISTEN_INTERVAL,
 };
 
 /**
diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c
index 05bbc46..5dfc60b 100644
--- a/core/wma/src/wma_main.c
+++ b/core/wma/src/wma_main.c
@@ -946,6 +946,70 @@
 	}
 }
 
+/**
+ * wma_override_listen_interval() - function to override static/ini based LI
+ * @wma: wma handle
+ * @privcmd: structure containing parameters
+ *
+ * This function override static/ini based LI in firmware
+ *
+ * Return: none
+ */
+static void wma_override_listen_interval(tp_wma_handle wma,
+				   wma_cli_set_cmd_t *privcmd)
+{
+	uint8_t vdev_id = privcmd->param_vdev_id;
+	struct wma_txrx_node *iface =
+		&wma->interfaces[vdev_id];
+	u32 old_override_li, new_override_li, listen_interval;
+	struct sAniSirGlobal *mac;
+	QDF_STATUS ret;
+
+	mac = cds_get_context(QDF_MODULE_ID_PE);
+	if (!mac) {
+		WMA_LOGE(FL("Failed to get mac context"));
+		return;
+	}
+
+	old_override_li = iface->override_li;
+	new_override_li = privcmd->param_value;
+	iface->override_li = new_override_li;
+
+	if (new_override_li &&
+	    (new_override_li != old_override_li)) {
+		listen_interval = new_override_li;
+	} else if (!new_override_li &&
+		   (new_override_li != old_override_li)) {
+		/* Configure default LI as we do on resume */
+		if ((wlan_cfg_get_int(mac, WNI_CFG_LISTEN_INTERVAL,
+				      &listen_interval) != eSIR_SUCCESS)) {
+			QDF_TRACE(QDF_MODULE_ID_WMA, QDF_TRACE_LEVEL_ERROR,
+				  "Failed to get value for listen interval");
+			listen_interval = POWERSAVE_DEFAULT_LISTEN_INTERVAL;
+		}
+	} else {
+		return;
+	}
+
+	ret = wma_vdev_set_param(wma->wmi_handle, vdev_id,
+			WMI_VDEV_PARAM_LISTEN_INTERVAL,
+			listen_interval);
+	if (QDF_IS_STATUS_ERROR(ret)) {
+		/* Even it fails continue Fw will take default LI */
+		WMA_LOGE("Failed to Set Listen Interval vdevId %d",
+			 vdev_id);
+	}
+	WMA_LOGD("%s: Set Listen Interval vdevId %d Listen Intv %d",
+			__func__, vdev_id, listen_interval);
+	ret = wma_vdev_set_param(wma->wmi_handle,
+			privcmd->param_vdev_id,
+			WMI_VDEV_PARAM_DTIM_POLICY,
+			NORMAL_DTIM);
+	if (QDF_IS_STATUS_ERROR(ret))
+		WMA_LOGE("Failed to Set to Normal DTIM policy");
+
+}
+
 
 /**
  * wma_process_cli_set_cmd() - set parameters to fw
@@ -1088,6 +1152,9 @@
 		case GEN_PARAM_MODULATED_DTIM:
 			wma_set_modulated_dtim(wma, privcmd);
 			break;
+		case GEN_PARAM_LISTEN_INTERVAL:
+			wma_override_listen_interval(wma, privcmd);
+			break;
 		default:
 			WMA_LOGE("Invalid param id 0x%x",
 				 privcmd->param_id);
diff --git a/core/wma/src/wma_power.c b/core/wma/src/wma_power.c
index 440ffe9..1b783ee 100644
--- a/core/wma/src/wma_power.c
+++ b/core/wma/src/wma_power.c
@@ -1906,8 +1906,8 @@
 				 vdev_id);
 		}
 
-		WMA_LOGD("Set Listen Interval vdevId %d Listen Intv %d",
-			 vdev_id, listen_interval);
+		WMA_LOGD("%s: Set Listen Interval vdevId %d Listen Intv %d",
+			 __func__, vdev_id, listen_interval);
 
 		iface->restore_dtim_setting = true;
 	}
@@ -1921,7 +1921,7 @@
  */
 static inline uint8_t wma_is_user_set_li_params(struct wma_txrx_node *iface)
 {
-       return iface->alt_modulated_dtim_enabled ? 1 : 0;
+	return iface->alt_modulated_dtim_enabled || iface->override_li ? 1 : 0;
 }
 
 /**
@@ -2027,8 +2027,8 @@
 				 vdev_id);
 		}
 
-		WMA_LOGD("Set Listen Interval vdevId %d Listen Intv %d",
-			 vdev_id, cfg_data_val);
+		WMA_LOGD("%s: Set Listen Interval vdevId %d Listen Intv %d",
+			 __func__, vdev_id, cfg_data_val);
 
 		iface->restore_dtim_setting = false;