libertas: Add auto deep sleep support for SD8385/SD8686/SD8688

Add timer based auto deep sleep feature in libertas driver which can be
configured using iwconfig command. This is tested on SD8688, SD8686 cards
with firmware versions 10.38.1.p25, 9.70.4.p0 respectively on 32-bit and 64-bit
platforms. Tests have been done for USB/CS cards to make sure that the patch
won't break USB/CS code. We didn't test the if_spi driver.

Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Acked-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
index be837a0..38a451e 100644
--- a/drivers/net/wireless/libertas/wext.c
+++ b/drivers/net/wireless/libertas/wext.c
@@ -45,6 +45,31 @@
 	priv->pending_assoc_req = NULL;
 }
 
+/**
+ *  @brief This function checks if the command is allowed.
+ *
+ *  @param priv         A pointer to lbs_private structure
+ *  @return             allowed or not allowed.
+ */
+
+int lbs_is_cmd_allowed(struct lbs_private *priv)
+{
+	int         ret = 1;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (!priv->is_auto_deep_sleep_enabled) {
+		if (priv->is_deep_sleep) {
+			lbs_deb_wext("IOCTLS called when station"
+					"is in deep sleep\n");
+			ret = 0;
+		}
+	}
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return ret;
+}
+
 
 /**
  *  @brief Find the channel frequency power info with specific channel
@@ -168,6 +193,11 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		lbs_deb_leave(LBS_DEB_WEXT);
+		return -EBUSY;
+	}
+
 	cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
 					   priv->curbssparams.channel);
 
@@ -278,6 +308,12 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (vwrq->disabled)
 		val = MRVDRV_RTS_MAX_VALUE;
 
@@ -299,6 +335,11 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
 	if (ret)
 		goto out;
@@ -321,6 +362,12 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (vwrq->disabled)
 		val = MRVDRV_FRAG_MAX_VALUE;
 
@@ -342,6 +389,11 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
 	if (ret)
 		goto out;
@@ -391,6 +443,11 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (!priv->radio_on) {
 		lbs_deb_wext("tx power off\n");
 		vwrq->value = 0;
@@ -424,6 +481,11 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
         if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
                 return -EOPNOTSUPP;
 
@@ -472,6 +534,11 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	vwrq->disabled = 0;
 
 	if (vwrq->flags & IW_RETRY_LONG) {
@@ -709,6 +776,7 @@
 			  struct iw_param *vwrq, char *extra)
 {
 	struct lbs_private *priv = dev->ml_priv;
+	int ret = 0;
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
@@ -737,8 +805,54 @@
 		       "setting power timeout is not supported\n");
 		return -EINVAL;
 	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
-		lbs_deb_wext("setting power period not supported\n");
-		return -EINVAL;
+		vwrq->value = vwrq->value / 1000;
+		if (!priv->enter_deep_sleep) {
+			lbs_pr_err("deep sleep feature is not implemented "
+					"for this interface driver\n");
+			return -EINVAL;
+		}
+
+		if (priv->connect_status == LBS_CONNECTED) {
+			if ((priv->is_auto_deep_sleep_enabled) &&
+						(vwrq->value == -1000)) {
+				lbs_exit_auto_deep_sleep(priv);
+				return 0;
+			} else {
+				lbs_pr_err("can't use deep sleep cmd in "
+						"connected state\n");
+				return -EINVAL;
+			}
+		}
+
+		if ((vwrq->value < 0) && (vwrq->value != -1000)) {
+			lbs_pr_err("unknown option\n");
+			return -EINVAL;
+		}
+
+		if (vwrq->value > 0) {
+			if (!priv->is_auto_deep_sleep_enabled) {
+				priv->is_activity_detected = 0;
+				priv->auto_deep_sleep_timeout = vwrq->value;
+				lbs_enter_auto_deep_sleep(priv);
+			} else {
+				priv->auto_deep_sleep_timeout = vwrq->value;
+				lbs_deb_debugfs("auto deep sleep: "
+						"already enabled\n");
+			}
+			return 0;
+		} else {
+			if (priv->is_auto_deep_sleep_enabled) {
+				lbs_exit_auto_deep_sleep(priv);
+				/* Try to exit deep sleep if auto */
+				/*deep sleep disabled */
+				ret = lbs_set_deep_sleep(priv, 0);
+			}
+			if (vwrq->value == 0)
+				ret = lbs_set_deep_sleep(priv, 1);
+			else if (vwrq->value == -1000)
+				ret = lbs_set_deep_sleep(priv, 0);
+			return ret;
+		}
 	}
 
 	if (priv->psmode != LBS802_11POWERMODECAM) {
@@ -752,6 +866,7 @@
 	}
 
 	lbs_deb_leave(LBS_DEB_WEXT);
+
 	return 0;
 }
 
@@ -792,6 +907,9 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv))
+		return NULL;
+
 	priv->wstats.status = priv->mode;
 
 	/* If we're not associated, all quality values are meaningless */
@@ -892,6 +1010,12 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1000,6 +1124,12 @@
 	u8 rates[MAX_RATES + 1];
 
 	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	lbs_deb_wext("vwrq->value %d\n", vwrq->value);
 	lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
 
@@ -1058,6 +1188,11 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		lbs_deb_leave(LBS_DEB_WEXT);
+		return -EBUSY;
+	}
+
 	if (priv->connect_status == LBS_CONNECTED) {
 		vwrq->value = priv->cur_rate * 500000;
 
@@ -1084,6 +1219,11 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (   (*uwrq != IW_MODE_ADHOC)
 	    && (*uwrq != IW_MODE_INFRA)
 	    && (*uwrq != IW_MODE_AUTO)) {
@@ -1325,6 +1465,12 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1508,6 +1654,12 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1720,6 +1872,12 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1822,6 +1980,12 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	switch (dwrq->flags & IW_AUTH_INDEX) {
 	case IW_AUTH_KEY_MGMT:
 		dwrq->value = priv->secinfo.key_mgmt;
@@ -1864,6 +2028,11 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (vwrq->disabled) {
 		lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
 		goto out;
@@ -1983,6 +2152,12 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (!priv->radio_on) {
 		ret = -EINVAL;
 		goto out;
@@ -2110,6 +2285,12 @@
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (!priv->radio_on)
 		return -EINVAL;