Merge 85f79a60c6e6429f9ee6550d2894e050dc052c2d on remote branch

Change-Id: I38b542b53e12ce6715e951ff7eb04f1c7ab9a145
diff --git a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c
index e599c80..f95a8fb 100644
--- a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c
+++ b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c
@@ -182,6 +182,10 @@
 #define TWT_RESUME_RESP_LEN      strlen(TWT_RESUME_RESP)
 #define TWT_NOTIFY_RESP_LEN      strlen(TWT_NOTIFY_RESP)
 
+#define OPM_MODE_DISABLE         0
+#define OPM_MODE_ENABLE          1
+#define OPM_MODE_USER_DEFINED    2
+
 static int twt_async_support = -1;
 
 struct twt_setup_parameters {
@@ -2689,31 +2693,35 @@
 
 static u64 get_u64_from_string(char *cmd_string, int *ret)
 {
-	u64 val = 0;
+	long long val = 0;
+	char *endptr = NULL;
 
 	*ret = 0;
 	errno = 0;
-	val = strtoll(cmd_string, NULL, 10);
-	if (errno == ERANGE || (errno != 0 && val == 0)) {
+	val = strtoll(cmd_string, &endptr, 10);
+	if (errno == ERANGE || (errno != 0 && val == 0) ||
+	    *cmd_string == *endptr || val < 0) {
 		wpa_printf(MSG_ERROR, "invalid value");
 		*ret = -EINVAL;
-        }
-	return val;
+	}
+	return (u64)val;
 }
 
 
 static u32 get_u32_from_string(char *cmd_string, int *ret)
 {
-	u32 val = 0;
+	long val = 0;
+	char *endptr = NULL;
 
 	*ret = 0;
 	errno = 0;
-	val = strtol(cmd_string, NULL, 10);
-	if (errno == ERANGE || (errno != 0 && val == 0)) {
+	val = strtol(cmd_string, &endptr, 10);
+	if (errno == ERANGE || (errno != 0 && val == 0) ||
+	    *cmd_string == *endptr || val < 0) {
 		wpa_printf(MSG_ERROR, "invalid value");
 		*ret = -EINVAL;
-        }
-	return val;
+	}
+	return (u32)val;
 }
 
 static s32 get_s32_from_string(char *cmd_string, int *ret)
@@ -2739,16 +2747,34 @@
 
 static u8 get_u8_from_string(char *cmd_string, int *ret)
 {
-	u8 val = 0;
+	long val = 0;
+	char *endptr = NULL;
 
 	*ret = 0;
 	errno = 0;
-	val = strtol(cmd_string, NULL, 10) & 0xFF;
-	if (errno == ERANGE || (errno != 0 && val == 0)) {
+	val = strtol(cmd_string, &endptr, 10);
+	if (errno == ERANGE || (errno != 0 && val == 0) ||
+	    *cmd_string == *endptr || val < 0) {
 		wpa_printf(MSG_ERROR, "invalid value");
 		*ret = -EINVAL;
-        }
-	return val;
+	}
+	return (u8)(val & 0xFF);
+}
+
+static u16 get_u16_from_string(char *cmd_string, int *ret)
+{
+	long val = 0;
+	char *endptr = NULL;
+
+	*ret = 0;
+	errno = 0;
+	val = strtol(cmd_string, &endptr, 10);
+	if (errno == ERANGE || (errno != 0 && val == 0) ||
+	    *cmd_string == *endptr || val < 0) {
+		wpa_printf(MSG_ERROR, "Invalid input to get u16 value");
+		*ret = -EINVAL;
+	}
+	return (u16)(val & 0xFFFF);
 }
 
 char *move_to_next_str(char *cmd)
@@ -6401,6 +6427,116 @@
 	return ret;
 }
 
+static uint8_t wpa_driver_convert_opm_mode(uint8_t opm_mode)
+{
+	switch (opm_mode) {
+	case 0:
+		return QCA_WLAN_VENDOR_OPM_MODE_DISABLE;
+	case 1:
+		return QCA_WLAN_VENDOR_OPM_MODE_ENABLE;
+	case 2:
+		return QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED;
+	default:
+		return opm_mode;
+	}
+}
+
+static int wpa_driver_ps_config_cmd(struct i802_bss *bss, char *cmd)
+{
+	struct wpa_driver_nl80211_data *drv;
+	struct nl_msg *nlmsg;
+	struct nlattr *attr;
+	u8 opm_mode;
+	u16 ps_ito, spec_wake;
+	int ret;
+
+	drv = bss->drv;
+	cmd = skip_white_space(cmd);
+	if (*cmd == '\0') {
+		wpa_printf(MSG_ERROR, "mode and config values are missing");
+		return -EINVAL;
+	}
+	opm_mode = get_u8_from_string(cmd, &ret);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, "ps_config: Invalid opm_mode");
+		return -EINVAL;
+	}
+
+	if (opm_mode == OPM_MODE_USER_DEFINED) {
+		cmd = move_to_next_str(cmd);
+		if (*cmd == '\0') {
+			wpa_printf(MSG_ERROR, "ps_ito is missing in command");
+			return -EINVAL;
+		}
+		ps_ito = get_u16_from_string(cmd, &ret);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "Invalid ps_ito value");
+			return -EINVAL;
+		}
+		cmd = move_to_next_str(cmd);
+		if (*cmd == '\0') {
+			wpa_printf(MSG_ERROR,
+				   "spec_wake is missing in command");
+			return -EINVAL;
+		}
+		spec_wake = get_u16_from_string(cmd, &ret);
+		if (ret < 0) {
+			wpa_printf(MSG_ERROR, "Invalid spec_wake value");
+			return -EINVAL;
+		}
+	}
+
+	nlmsg = prepare_vendor_nlmsg(drv, bss->ifname,
+				     QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION);
+	if (!nlmsg) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to allocate nlmsg for set_opm_mode cmd");
+		return -ENOMEM;
+	}
+
+	attr = nla_nest_start(nlmsg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr) {
+		ret = -ENOMEM;
+		wpa_printf(MSG_ERROR,
+			   "Failed to create nl attr for set_opm_mode cmd");
+		goto nlmsg_fail;
+	}
+	if (nla_put_u8(nlmsg,
+		       QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT,
+		       wpa_driver_convert_opm_mode(opm_mode))) {
+		ret = -ENOMEM;
+		wpa_printf(MSG_ERROR, "Failed to put power_save_mode value");
+		goto nlmsg_fail;
+	}
+	if (opm_mode == OPM_MODE_USER_DEFINED) {
+		if (nla_put_u16(nlmsg, QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO,
+				ps_ito)) {
+			ret = -ENOMEM;
+			wpa_printf(MSG_ERROR, "Failed to put ps_ito value");
+			goto nlmsg_fail;
+		}
+		if (nla_put_u16(nlmsg,
+				QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL,
+				spec_wake)) {
+			ret = -ENOMEM;
+			wpa_printf(MSG_ERROR, "Failed to put spec_wake value");
+			goto nlmsg_fail;
+		}
+	}
+	nla_nest_end(nlmsg, attr);
+
+	ret = send_nlmsg((struct nl_sock *)drv->global->nl, nlmsg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to send set_opm_mode nlmsg, error:%d", ret);
+		return ret;
+	}
+	return 0;
+nlmsg_fail:
+	nlmsg_free(nlmsg);
+	return ret;
+}
+
 int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
 				  size_t buf_len )
 {
@@ -6750,6 +6886,17 @@
 		 */
 		cmd += 17;
 		return wpa_driver_set_ul_mu_cfg(bss, cmd);
+	} else if (os_strncasecmp(cmd, "SET_PS_CONFIG ", 14) == 0) {
+		/* DRIVER SET_PS_CONFIG <opm_mode> <ps_ito> <spec_wake>
+		 * opm_mode  - Optimized power management Mode
+		 *     value 0 - Disable OPM
+		 *     value 1 - Enable OPM
+		 *     value 2 - User defined OPM
+		 * ps_ito    - Power save inactivity timeout
+		 * spec_wake - Speculative wake interval
+		 */
+		cmd += 14;
+		return wpa_driver_ps_config_cmd(bss, cmd);
 	} else { /* Use private command */
 		memset(&ifr, 0, sizeof(ifr));
 		memset(&priv_cmd, 0, sizeof(priv_cmd));
diff --git a/qcwcn/wpa_supplicant_8_lib/qca-vendor_copy.h b/qcwcn/wpa_supplicant_8_lib/qca-vendor_copy.h
index b776082..aade3ae 100644
--- a/qcwcn/wpa_supplicant_8_lib/qca-vendor_copy.h
+++ b/qcwcn/wpa_supplicant_8_lib/qca-vendor_copy.h
@@ -2833,8 +2833,13 @@
 	 * configure the asymmetric NSS configuration (such as 1X2).
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_NSS = 70,
-	/* 8-bit unsigned value to trigger Optimized Power Management:
-	 * 1-Enable, 0-Disable
+	/* 8-bit unsigned value to configure Optimized Power Management mode:
+	 * Modes are defined by enum qca_wlan_vendor_opm_mode.
+	 *
+	 * This attribute shall be configured along with
+	 * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and
+	 * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL attributes
+	 * when its value is set to %QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED.
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT = 71,
 
@@ -3202,6 +3207,34 @@
 	 */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_MLO_LINKS = 100,
 
+	/* 16-bit unsigned value to configure power save inactivity timeout in
+	 * milliseconds.
+	 *
+	 * STA enters into power save mode (PM=1) after TX/RX inactivity of time
+	 * duration specified by %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO.
+	 *
+	 * This attribute shall be configured along with
+	 * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL when
+	 * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT
+	 * is set to %QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED mode.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO = 101,
+
+	/* 16-bit unsigned value to configure speculative wake interval in
+	 * milliseconds.
+	 *
+	 * STA speculatively wakes up to look for buffered data by AP at
+	 * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL interval after
+	 * entering into power save. If configured zero, STA wakes up at
+	 * upcoming DTIM beacon.
+	 *
+	 * This attribute shall be configured along with
+	 * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and
+	 * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT
+	 * to %QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED mode.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL = 102,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -15997,4 +16030,22 @@
 	QCA_WLAN_VENDOR_ATTR_TDLS_STATE_AFTER_LAST - 1,
 };
 
+/*
+ * enum qca_wlan_vendor_opm_mode - Modes supported by
+ * %QCA_WLAN_VENDOR_ATTR_CONFIG_OPTIMIZED_POWER_MANAGEMENT vendor attribute.
+ *
+ * @QCA_WLAN_VENDOR_OPM_MODE_DISABLE: OPM Disabled
+ * @QCA_WLAN_VENDOR_OPM_MODE_ENABLE: OPM Enabled
+ * @QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED: User defined mode which allows user
+ * 	to configure power save inactivity timeout and speculative wake up
+ * 	interval through %QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_ITO and
+ * 	%QCA_WLAN_VENDOR_ATTR_CONFIG_OPM_SPEC_WAKE_INTERVAL attributes.
+ */
+
+enum qca_wlan_vendor_opm_mode {
+	QCA_WLAN_VENDOR_OPM_MODE_DISABLE = 0,
+	QCA_WLAN_VENDOR_OPM_MODE_ENABLE = 1,
+	QCA_WLAN_VENDOR_OPM_MODE_USER_DEFINED = 2,
+};
+
 #endif /* QCA_VENDOR_H */