wpa_supplicant_8_lib: Add support for SR PD thresholds & SR params

 - Add support to configure both SRG and Non-SRG PD threshold
   values with SR enable command, below are the supported commands.
    driver spatial_reuse enable,
    driver spatial_reuse enable srg_pd_threshold <val>,
    driver spatial_reuse enable non_srg_pd_threshold <val>,
    driver spatial_reuse enable srg_pd_threshold <val>
    non_srg_pd_threshold <val>,
    driver spatial_reuse enable non_srg_pd_threshold <val>
    srg_pd_threshold <val>

 - Add support to get SR params.
    driver spatial_reuse getparams

Change-Id: Id4466339c6abfe8a0eb8042a9f3443e8caaba425
CRs-Fixed: 3580884
(cherry picked from commit ffeb90e52daf6bbcd31ad78812614adb016a3f06)
diff --git a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c
index 8055ce2..7cbc288 100644
--- a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c
+++ b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c
@@ -265,7 +265,7 @@
 };
 
 
-static char *get_next_arg(char *cmd)
+char *get_next_arg(char *cmd)
 {
 	char *pos = cmd;
 
@@ -2723,7 +2723,7 @@
 	return (u32)val;
 }
 
-static s32 get_s32_from_string(char *cmd_string, int *ret)
+s32 get_s32_from_string(char *cmd_string, int *ret)
 {
 	s64 val64 = 0;
 	s32 val = 0;
diff --git a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211_common.h b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211_common.h
index 1fe2743..25b5f60 100644
--- a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211_common.h
+++ b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211_common.h
@@ -49,6 +49,9 @@
 #include <netlink/object-api.h>
 #include <linux/pkt_sched.h>
 
+#define OBSS_PD_THRESHOLD_MIN -82
+#define OBSS_PD_THRESHOLD_MAX -62
+
 struct wpa_driver_nl80211_data *drv;
 struct i802_bss *bss;
 struct nl_msg *prepare_vendor_nlmsg(struct wpa_driver_nl80211_data *drv,
@@ -64,3 +67,5 @@
 int wpa_driver_sr_event(struct wpa_driver_nl80211_data *drv,
 		        u32 vendor_id, u32 subcmd, u8 *data, size_t len);
 char *skip_white_space(char *cmd);
+char *get_next_arg(char *cmd);
+s32 get_s32_from_string(char *cmd_string, int *ret);
diff --git a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211_sr.c b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211_sr.c
index 8d8acd6..d0cb989 100644
--- a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211_sr.c
+++ b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211_sr.c
@@ -80,7 +80,14 @@
 		*sr_cmd = QCA_WLAN_SR_OPERATION_GET_STATS;
 	else if (os_strncasecmp(cmd, "clearstats", 10) == 0)
 		*sr_cmd = QCA_WLAN_SR_OPERATION_CLEAR_STATS;
-	else {
+	else if (os_strncasecmp(cmd, "getparams", 9) == 0) {
+		cmd += 9;
+		cmd = skip_white_space(cmd);
+		if (*cmd != '\0')
+			return -EINVAL;
+		else
+			*sr_cmd = QCA_WLAN_SR_OPERATION_GET_PARAMS;
+	} else {
 		wpa_printf(MSG_ERROR, "Unknown SR command:%s\n", cmd);
 		return -EINVAL;
 	}
@@ -159,6 +166,65 @@
 	return 0;
 }
 
+static int parse_sr_get_params_response(struct resp_info *info,
+					struct nlattr *vendata, int datalen)
+{
+	int cmd_id, ret;
+	u8 srg_pd_offset_min = 0, srg_pd_offset_max = 0,
+	non_srg_pd_offset_max = 0, hesiga_val15_enable = 1,
+	non_srg_pd_disallow = 1;
+	struct nlattr *sr_attr[QCA_WLAN_VENDOR_ATTR_SR_PARAMS_MAX + 1];
+
+	ret = nla_parse_nested(sr_attr, QCA_WLAN_VENDOR_ATTR_SR_PARAMS_MAX,
+			       vendata, NULL);
+	if (ret) {
+		wpa_printf(MSG_ERROR, "SR params: nla_parse fail, error: %d", ret);
+		return ret;
+	}
+
+	cmd_id = QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MIN_OFFSET;
+	if (sr_attr[cmd_id])
+		srg_pd_offset_min = nla_get_u8(sr_attr[cmd_id]);
+	else
+		wpa_printf(MSG_INFO, "SR params: SRG PD min offset not found");
+
+	cmd_id = QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MAX_OFFSET;
+	if (sr_attr[cmd_id])
+		srg_pd_offset_max = nla_get_u8(sr_attr[cmd_id]);
+	else
+		wpa_printf(MSG_INFO, "SR params: SRG PD max offset not found");
+
+	cmd_id = QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_MAX_OFFSET;
+	if (sr_attr[cmd_id])
+		non_srg_pd_offset_max = nla_get_u8(sr_attr[cmd_id]);
+	else
+		wpa_printf(MSG_INFO, "SR params: Non SRG PD max offset not found");
+
+	cmd_id = QCA_WLAN_VENDOR_ATTR_SR_PARAMS_HESIGA_VAL15_ENABLE;
+	if (!sr_attr[cmd_id]) {
+		wpa_printf(MSG_INFO, "SR params: Hesiga Val15 is not enabled by AP");
+		hesiga_val15_enable = 0;
+	}
+
+	cmd_id = QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_DISALLOW;
+	if (!sr_attr[cmd_id]) {
+		wpa_printf(MSG_INFO, "SR params: non SRG PD is not allowed by AP");
+		non_srg_pd_disallow = 0;
+	}
+
+	ret = os_snprintf(info->reply_buf, info->reply_buf_len,
+			"srg_obss_pd_min_offset: %u\nsrg_obss_pd_max_offset: %u\n"
+			"non_srg_obss_pd_max_offset: %u\nhesiga_val15_enable: %u\n"
+			"non_srg_pd_disallow: %u", srg_pd_offset_min,
+			srg_pd_offset_max, non_srg_pd_offset_max,
+			hesiga_val15_enable, non_srg_pd_disallow);
+	if (os_snprintf_error(info->reply_buf_len, ret)) {
+		wpa_printf(MSG_ERROR, "SR params: Failed to put in buffer, error: %d", ret);
+		return ret;
+	}
+	return 0;
+}
+
 /**
  * sr_response_unpack() - unpack the spatial reuse command response received from driver
  *
@@ -179,16 +245,19 @@
 	switch (info->cmd_oper) {
 	case QCA_WLAN_SR_OPERATION_GET_STATS:
 		ret = parse_sr_get_stats_response(info, vendata, datalen);
-		if (ret) {
-			wpa_printf(MSG_ERROR, "Unpacking SR stats failed, error:%d\n",ret);
-			return ret;
-		}
+		if (ret)
+			wpa_printf(MSG_ERROR, "Unpacking SR stats failed, error:%d", ret);
+		break;
+	case QCA_WLAN_SR_OPERATION_GET_PARAMS:
+		ret = parse_sr_get_params_response(info, vendata, datalen);
+		if (ret)
+			wpa_printf(MSG_ERROR, "Unpacking SR params failed, error:%d", ret);
 		break;
 	default:
 		ret = -EINVAL;
-		wpa_printf(MSG_ERROR, "Unsupported SR command:%d, error:%d\n",
+		wpa_printf(MSG_ERROR, "Unsupported SR command:%d, error:%d",
 			   info->cmd_oper, ret);
-		return ret;
+		break;
 	}
 	return ret;
 }
@@ -216,15 +285,94 @@
 	drv = info->drv;
 
 	ret = sr_response_unpack(info, vendata, datalen);
-	if (!ret)
-		wpa_msg(drv->ctx, MSG_INFO, "CTRL-EVENT-SR STATS RESPONSE\n" "%s", info->reply_buf);
-	else
-		wpa_msg(drv->ctx, MSG_INFO, "CTRL-EVENT-SR STATS RESPONSE\n" " %s : Error = %d",
-			info->reply_buf, ret);
-
+	switch (info->cmd_oper) {
+	case QCA_WLAN_SR_OPERATION_GET_STATS:
+		if (!ret)
+			wpa_msg(drv->ctx, MSG_INFO, "CTRL-EVENT-SR STATS RESPONSE\n"
+				"%s", info->reply_buf);
+		else
+			wpa_msg(drv->ctx, MSG_ERROR, "CTRL-EVENT-SR STATS RESPONSE\n"
+				" %s : Error = %d", info->reply_buf, ret);
+		break;
+	case QCA_WLAN_SR_OPERATION_GET_PARAMS:
+		if (!ret)
+			wpa_msg(drv->ctx, MSG_INFO, "CTRL-EVENT-SR PARAMS RESPONSE\n"
+				"%s", info->reply_buf);
+		else
+			wpa_msg(drv->ctx, MSG_ERROR, "CTRL-EVENT-SR PARAMS RESPONSE\n"
+				" %s : Error = %d", info->reply_buf, ret);
+		break;
+	}
 	return ret;
 }
 
+static int pack_sr_enable_nlmsg(struct nl_msg *nlmsg, char *cmd)
+{
+
+	struct nlattr *sr_attr;
+	s8 is_srg_pd_cmd = 0, is_non_srg_pd_cmd = 0;
+	s32 pd_thres = 0, cmd_id;
+	int ret;
+
+	cmd += 6;
+	cmd = skip_white_space(cmd);
+	if (*cmd == '\0')
+		return 0;
+
+	sr_attr = nla_nest_start(nlmsg, QCA_WLAN_VENDOR_ATTR_SR_PARAMS);
+	if (!sr_attr)
+		return -ENOMEM;
+
+	for (int i = 0; i < 2; i++) {
+		if (os_strncasecmp(cmd, "srg_pd_threshold ", 17) == 0) {
+			cmd += 17;
+			cmd = skip_white_space(cmd);
+			pd_thres = get_s32_from_string(cmd, &ret);
+			if (ret < 0 || pd_thres < OBSS_PD_THRESHOLD_MIN ||
+			    pd_thres > OBSS_PD_THRESHOLD_MAX) {
+				wpa_printf(MSG_ERROR, "Invalid SRG PD threshold: %d", pd_thres);
+				return -EINVAL;
+			}
+			cmd_id = QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_PD_THRESHOLD;
+			if (nla_put_s32(nlmsg, cmd_id, pd_thres)) {
+				wpa_printf(MSG_ERROR, "Failed to put SRG PD threshold");
+				return -ENOMEM;
+			}
+			is_srg_pd_cmd++;
+		} else if (os_strncasecmp(cmd, "non_srg_pd_threshold ", 21) == 0) {
+			cmd += 21;
+			cmd = skip_white_space(cmd);
+			pd_thres = get_s32_from_string(cmd, &ret);
+			/**
+			 * For non-SRG OBSS, allowed range for PD threshold
+			 * is -62 to -81 as -82 is fixed as min offset.
+			 **/
+			if (ret < 0 || pd_thres <= OBSS_PD_THRESHOLD_MIN ||
+			    pd_thres > OBSS_PD_THRESHOLD_MAX) {
+				wpa_printf(MSG_ERROR, "Invalid Non-SRG PD threshold: %d", pd_thres);
+				return -EINVAL;
+			}
+			cmd_id = QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_PD_THRESHOLD;
+			if (nla_put_s32(nlmsg, cmd_id, pd_thres)) {
+				wpa_printf(MSG_ERROR, "Failed to put Non-SRG PD threshold");
+				return -ENOMEM;
+			}
+			is_non_srg_pd_cmd++;
+		} else if (*cmd == '\0')
+			break;
+		else
+			return -EINVAL;
+
+		if (is_srg_pd_cmd > 1 || is_non_srg_pd_cmd > 1)
+			return -EINVAL;
+
+		cmd = get_next_arg(cmd);
+		cmd = skip_white_space(cmd);
+	}
+	nla_nest_end(nlmsg, sr_attr);
+	return 0;
+}
+
 /**
  * wpa_driver_sr_cmd() - handle the spatial reuse commands
  *
@@ -288,11 +436,18 @@
 	}
 	switch (sr_cmd) {
 		case QCA_WLAN_SR_OPERATION_SR_ENABLE:
+			status = pack_sr_enable_nlmsg(nlmsg, cmd);
+			if (status < 0) {
+				wpa_printf(MSG_ERROR, "SR enable command failed: %d,"
+					   "error:%d", sr_cmd, status);
+				goto nlmsg_fail;
+			}
 		case QCA_WLAN_SR_OPERATION_SR_DISABLE:
 		case QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_PROHIBIT:
 		case QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_ALLOW:
 		case QCA_WLAN_SR_OPERATION_GET_STATS:
 		case QCA_WLAN_SR_OPERATION_CLEAR_STATS:
+		case QCA_WLAN_SR_OPERATION_GET_PARAMS:
 			status = nla_put_u8(nlmsg, QCA_WLAN_VENDOR_ATTR_SR_OPERATION, sr_cmd);
 			if (status) {
 				wpa_printf(MSG_ERROR, "Fail to put SR command:%d, error:%d\n",
@@ -307,9 +462,14 @@
 				   sr_cmd, status);
 			goto nlmsg_fail;
 	}
-	status = send_nlmsg((struct nl_sock *)drv->global->nl, nlmsg,
-			    (sr_cmd == QCA_WLAN_SR_OPERATION_GET_STATS) ? response_handler : NULL,
-			    (sr_cmd == QCA_WLAN_SR_OPERATION_GET_STATS) ? &info : NULL);
+	if (sr_cmd == QCA_WLAN_SR_OPERATION_GET_STATS ||
+	    sr_cmd == QCA_WLAN_SR_OPERATION_GET_PARAMS)
+		status = send_nlmsg((struct nl_sock *)drv->global->nl,
+				    nlmsg, response_handler, &info);
+	else
+		status = send_nlmsg((struct nl_sock *)drv->global->nl,
+				    nlmsg, NULL, NULL);
+
 	if (status) {
 		wpa_printf(MSG_ERROR, "Fail to send nlmsg SR command:%d to driver, error:%d\n",
 			   sr_cmd, status);