wpa_supplicant_8_lib: Add ratemask vendor command

Add driver command to configure tx ratemask for different
hw mode on specific interface.

Change-Id: I291fe65687ff1891a2414db0965a93de03269cfe
CRs-Fixed: 3390531
diff --git a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c
index 553d714..5b83142 100644
--- a/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c
+++ b/qcwcn/wpa_supplicant_8_lib/driver_cmd_nl80211.c
@@ -6089,6 +6089,208 @@
 	return ret;
 }
 
+static int string_to_bitmap(u32 *dest, size_t dlen, char *src)
+{
+	int i, idx;
+	u8 rem;
+	size_t slen = 0;
+	size_t max_len_per_idx = sizeof(u32) * 2;
+	char buff[(sizeof(u32) * 2) + 1] = {0};
+	char *sptr;
+	char *endptr = NULL;
+
+	if (strncasecmp(src, "0X", 2) == 0)
+		src += 2;
+
+	sptr = src;
+
+	while (*sptr && *sptr != ' ') {
+		sptr++;
+		slen++;
+	}
+
+	idx = (slen / max_len_per_idx) + ((slen % max_len_per_idx) > 0 ? 1 : 0);
+
+	if (idx > dlen) {
+		wpa_printf(MSG_ERROR, "rate_mask : src is too long");
+		return -EINVAL;
+	}
+
+	for (i = (idx - 1); i >= 0 ; i--) {
+		rem = (slen % max_len_per_idx);
+
+		if (rem > 0) {
+			memcpy(buff, src, rem);
+			src += rem;
+			slen -= rem;
+		} else if ((slen / max_len_per_idx) > 0) {
+			memcpy(buff, src, max_len_per_idx);
+			src += max_len_per_idx;
+			slen -= max_len_per_idx;
+		}
+
+		dest[i] = strtol(buff, &endptr, 16);
+		if (errno == ERANGE || (errno != 0 && dest[i] == 0) ||
+		    *buff == *endptr) {
+			wpa_printf(MSG_ERROR,
+				   "rate_mask:invalid value\n");
+			return -EINVAL;
+		}
+
+		memset(buff, 0, sizeof(buff));
+	}
+
+	return 0;
+}
+
+/**
+ *wpa_driver_rate_mask_config()- Sends the ratemask params to the driver.
+ *
+ * @bss: nl data
+ * @cmd: Ratemask vendor command
+ *
+ * Return: returns 0 on Success, error code on invalid response.
+ */
+static int wpa_driver_rate_mask_config(struct i802_bss *bss, char *cmd)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *nlmsg = NULL;
+	struct nlattr *attr, *mask_attr_list;
+	struct nlattr *mask_list;
+	int ret = -EINVAL, status = 0;
+	u32 buffer[RATEMASK_PARAMS_TYPE_MAX] = {0};
+	u8 type, value;
+	int i = 0;
+
+	cmd = skip_white_space(cmd);
+
+	nlmsg = prepare_vendor_nlmsg(drv, bss->ifname,
+				     QCA_NL80211_VENDOR_SUBCMD_RATEMASK_CONFIG);
+	if (!nlmsg) {
+		wpa_printf(MSG_ERROR,
+			   "rate_mask: Failed to allocate nl message");
+		return -ENOMEM;
+	}
+
+	attr = nla_nest_start(nlmsg, NL80211_ATTR_VENDOR_DATA);
+	if (!attr) {
+		wpa_printf(MSG_ERROR, "rate_mask: Failed to alloc nlattr");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	mask_attr_list =
+	       nla_nest_start(nlmsg, QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_LIST);
+	if (!mask_attr_list) {
+		wpa_printf(MSG_ERROR, "rate_mask: Failed alloc mask_attr_list");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	do {
+		mask_list =
+		      nla_nest_start(nlmsg, i++);
+		if (!mask_list) {
+			wpa_printf(MSG_ERROR,
+				   "rate_mask: Failed alloc mask_list");
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		if (os_strncasecmp(cmd, "phymode ", 8) == 0) {
+			cmd += 8;
+			cmd = skip_white_space(cmd);
+		} else {
+			wpa_printf(MSG_ERROR, "rate_mask:Invalid phymode");
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		value = get_u8_from_string(cmd, &status);
+
+		if (status < 0) {
+			wpa_printf(MSG_ERROR, "rate_mask: Invalid type");
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		switch (value) {
+		case 0:
+			type = QCA_WLAN_RATEMASK_PARAMS_TYPE_CCK_OFDM;
+			break;
+		case 1:
+			type = QCA_WLAN_RATEMASK_PARAMS_TYPE_HT;
+			break;
+		case 2:
+			type = QCA_WLAN_RATEMASK_PARAMS_TYPE_VHT;
+			break;
+		case 3:
+			type = QCA_WLAN_RATEMASK_PARAMS_TYPE_HE;
+			break;
+		default:
+			wpa_printf(MSG_ERROR,
+				   "rate_mask: Invalid rate_mask type");
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		cmd = move_to_next_str(cmd);
+
+		if (os_strncasecmp(cmd, "ratemask ", 9) == 0) {
+			cmd += 9;
+			cmd = skip_white_space(cmd);
+			ret = string_to_bitmap(buffer, RATEMASK_PARAMS_TYPE_MAX,
+					       cmd);
+			if (ret != 0) {
+				wpa_printf(MSG_ERROR,
+					   "rate_mask:str to bitmap conv fail");
+				goto fail;
+			}
+		} else {
+			wpa_printf(MSG_ERROR, "rate_mask:Invalid ratemask");
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		ret = nla_put_u8(nlmsg,
+				 QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_TYPE,
+				 type);
+		if (ret) {
+			wpa_printf(MSG_ERROR,
+				   "Failed to add rate_mask_type attr %d", ret);
+			goto fail;
+		}
+
+		ret = nla_put(nlmsg,
+			      QCA_WLAN_VENDOR_ATTR_RATEMASK_PARAMS_BITMAP,
+			      sizeof(buffer), buffer);
+		if (ret) {
+			wpa_printf(MSG_ERROR,
+				   "rate_mask: Failed bitmap attr %d", ret);
+			goto fail;
+		}
+
+		cmd = move_to_next_str(cmd);
+		memset(buffer, 0, sizeof(buffer));
+
+		nla_nest_end(nlmsg, mask_list);
+
+	} while (os_strncasecmp(cmd, "phymode ", 8) == 0);
+
+	nla_nest_end(nlmsg, mask_attr_list);
+	nla_nest_end(nlmsg, attr);
+
+	ret = send_nlmsg((struct nl_sock *)drv->global->nl, nlmsg, NULL, NULL);
+
+	if (ret)
+		wpa_printf(MSG_ERROR, "rate_mask: Error sending nlmsg %d", ret);
+
+	return ret;
+fail:
+	nlmsg_free(nlmsg);
+	return ret;
+}
+
 int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
 				  size_t buf_len )
 {
@@ -6420,6 +6622,13 @@
 		 */
 		cmd += 11;
 		return wpa_driver_tsf_cmd(priv, cmd, buf, buf_len);
+	} else if (os_strncasecmp(cmd, "SET_TX_RATEMASK ", 16) == 0) {
+		/*
+		 * DRIVER SET_TX_RATEMASK phymode <phy_mode> ratemask
+		 * <txrate_mask>…phymode <phy_mode> ratemask <tx_rate_mask>
+		 */
+		cmd += 16;
+		return wpa_driver_rate_mask_config(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/wpa_driver_common_lib.h b/qcwcn/wpa_supplicant_8_lib/wpa_driver_common_lib.h
index 9ec58d2..e623b19 100644
--- a/qcwcn/wpa_supplicant_8_lib/wpa_driver_common_lib.h
+++ b/qcwcn/wpa_supplicant_8_lib/wpa_driver_common_lib.h
@@ -122,6 +122,8 @@
 #define GET_STA_INFO_MAX \
 	QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_MAX
 
+#define RATEMASK_PARAMS_TYPE_MAX  4
+
 struct bss_info {
 	uint8_t oui[OUI_LEN];
 	char ssid[MAX_SSID_LEN + 1];