From 27d602c9f1d925ec2866bf5e7421107ee02ba4ed Mon Sep 17 00:00:00 2001
From: "Adiga Ashwin (CSS ICW ENG SW WFSW 2)" <ashwin.adiga@infineon.com>
Date: Thu, 6 Jun 2024 13:52:50 +0000
Subject: [PATCH 280/296] non-upstream: IW vendor cmds for NAT keep-alive & tko
 param

IW vendor commands for NAT keep-alive (mkeep_alive hex data)
& tko param are being added here which used string based
parsing infrastructure.

Example for NAT keep alive cmd: wl -> iw

1. wl mkeep_alive 0 1000 0x080027b1050a00904c3104000800
4500001e000040004011c52a0a8830700a88302513c413c5000a00000a0d
2. echo 'mkeep_alive 0 1000 0x080027b1050a00904c3104000800
4500001e000040004011c52a0a8830700a88302513c413c5000a00000a0d ' |
iw dev wlan0 vendor send 0x000319 0x1C -

Example for tko param: wl -> iw

1. wl tko param 10 4 10 0
2. echo 'tko param 10 4 10 0 ' | iw dev wlan0 vendor
send 0x000319 0x1C -

Signed-off-by: Adiga Ashwin  (CSS ICW ENG SW WFSW 2) <ashwin.adiga@infineon.com>
---
 .../broadcom/brcm80211/brcmfmac/vendor_ifx.c  | 186 +++++++++++++-----
 .../broadcom/brcm80211/brcmfmac/vendor_ifx.h  |  11 +-
 2 files changed, 146 insertions(+), 51 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c
index 6709d81c0ac1..f9bc5830b51d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c
@@ -847,13 +847,13 @@ int ifx_vndr_cmdstr_offload_config(struct wiphy *wiphy, struct wireless_dev *wde
 	 * send 0x000319 0x1C -
 	 *
 	 */
-	if (cmd_str[1] && (strlen(cmd_str[1]) == 6) &&
+	if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 6) &&
 	    (memcmp(cmd_str[1], "Enable", 6)) == 0 &&
 	    (cmd_val[0] == 0 || cmd_val[0] == 1)) {
 		brcmf_generic_offload_enable(ifp, brcmf_offload_feat, cmd_val[0]);
-	} else if (cmd_str[1] && (strlen(cmd_str[1]) == 7) &&
+	} else if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 7) &&
 		  (memcmp(cmd_str[1], "Profile", 7)) == 0) {
-		if (cmd_str[2]) {
+		if (cmd_str[2][0] != '\0') {
 			unsigned int ol_prof;
 
 			if ((strlen(cmd_str[2]) == 6) &&
@@ -869,7 +869,7 @@ int ifx_vndr_cmdstr_offload_config(struct wiphy *wiphy, struct wireless_dev *wde
 				brcmf_err("unknown offload_config Profile attr\n");
 				return -EINVAL;
 			}
-			if (cmd_str[3] && (strlen(cmd_str[3]) == 2) &&
+			if (cmd_str[3][0] != '\0' && (strlen(cmd_str[3]) == 2) &&
 			    (memcmp(cmd_str[3], "-s", 2)) == 0)
 				brcmf_generic_offload_config(ifp, ~cmd_val[1], ol_prof, cmd_val[0]);
 			else
@@ -893,43 +893,76 @@ int ifx_vndr_cmdstr_mkeep_alive(struct wiphy *wiphy, struct wireless_dev *wdev,
 {
 	struct brcmf_cfg80211_vif *vif;
 	struct brcmf_if *ifp;
-	int ret = 0;
-	struct ifx_mkeep_alive mkeep_alive = {0};
+	int ret = 0, i = 0, j = 0;
+	struct ifx_mkeep_alive *mkeep_alive;
+	u8 buf[150] = {0};
 	bool immed_flag = 0;
 
 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 	ifp = vif->ifp;
 
-	/* echo 'mkeep_alive 0 1000 ' | iw dev wlan0 vendor
+	/* NULL Keep-Alive
+	 * echo 'mkeep_alive 0 1000 ' | iw dev wlan0 vendor
+	 * send 0x000319 0x1C -
+	 *
+	 * NAT Keep-Alive
+	 * echo 'mkeep_alive 0 1000 0x080027b1050a00904c3104
+	 * 0008004500001e000040004011c52a0a8830700a88302513c
+	 * 413c5000a00000a0d ' | iw dev wlan0 vendor
 	 * send 0x000319 0x1C -
 	 */
-
 	if (cmd_val[0] < 0 || cmd_val[0] > 4 || cmd_val[1] < 0) {
-		brcmf_err("Invalid command format\n");
-		return -EINVAL;
+		brcmf_err("Invalid command value\n");
+		ret = -EINVAL;
+		goto exit;
 	}
-	if (cmd_str[1] && (strlen(cmd_str[1]) == 9) &&
+	mkeep_alive = (struct ifx_mkeep_alive *)buf;
+
+	mkeep_alive->period_msec = cmd_val[1];
+	if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 9) &&
 	    (memcmp(cmd_str[1], "immediate", 9)) == 0) {
 		immed_flag = 1;
-	}
-	mkeep_alive.period_msec = cmd_val[1];
-	if (mkeep_alive.period_msec & WL_MKEEP_ALIVE_IMMEDIATE) {
-		brcmf_err("Period %d too large\n", mkeep_alive.period_msec);
-		return -EINVAL;
-	}
-	if (immed_flag && mkeep_alive.period_msec)
-		mkeep_alive.period_msec |= WL_MKEEP_ALIVE_IMMEDIATE;
 
-	mkeep_alive.version = WL_MKEEP_ALIVE_VERSION;
-	mkeep_alive.length = offsetof(struct ifx_mkeep_alive, data);
-	mkeep_alive.keep_alive_id = cmd_val[0];
-	mkeep_alive.len_bytes = 0;
+		if (mkeep_alive->period_msec & WL_MKEEP_ALIVE_IMMEDIATE) {
+			brcmf_err("Period %d too large\n", mkeep_alive->period_msec);
+			ret = -EINVAL;
+			goto exit;
+		}
+		if (immed_flag && mkeep_alive->period_msec)
+			mkeep_alive->period_msec |= WL_MKEEP_ALIVE_IMMEDIATE;
+	}
+	mkeep_alive->version = WL_MKEEP_ALIVE_VERSION;
+	mkeep_alive->keep_alive_id = cmd_val[0];
+	mkeep_alive->length = offsetof(struct ifx_mkeep_alive, data);
 
-	ret = brcmf_fil_bsscfg_data_set(ifp, "mkeep_alive", (void *)&mkeep_alive,
-					mkeep_alive.length);
+	/* If there is no hex value for pkt data, it is treated as NULL KA.
+	 * If there is hex value for pkt data, then copy hex as data and is
+	 * treated as NAT KA.
+	 */
+	if (mkeep_alive->period_msec > 0) {
+		j = 2;
+		if (cmd_val[j] < 0) {
+			mkeep_alive->len_bytes = 0;
+		} else if (cmd_val[j + 14] < 0) {
+			brcmf_err("Invalid pkt data. Required len bytes >= 14.\n");
+			ret = -EINVAL;
+			goto exit;
+		} else {
+			while (cmd_val[j] != ' ') {
+				if (j <= VNDR_CMD_VAL_NUM) {
+					mkeep_alive->data[i] = cmd_val[j];
+					j++;
+				}
+				i++;
+			}
+			mkeep_alive->len_bytes = i;
+		}
+	}
+	ret = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", buf, sizeof(buf));
 	if (ret)
 		brcmf_err("Failed to set mkeeplive params: %d\n", ret);
 
+exit:
 	return ret;
 }
 
@@ -940,33 +973,52 @@ int ifx_vndr_cmdstr_tko(struct wiphy *wiphy, struct wireless_dev *wdev,
 	struct brcmf_cfg80211_vif *vif;
 	struct brcmf_if *ifp;
 	int ret = 0;
-	struct ifx_tko tko = {0};
+	struct ifx_tko *tko;
+	struct ifx_tko_param *tko_param;
 	struct ifx_tko_enable *tko_enable;
+	u8 buf[128] = {0};
 	int length;
 
 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 	ifp = vif->ifp;
 
-	/* echo 'tko enable 1 ' | iw dev wlan0 vendor
+	tko = (struct ifx_tko *)buf;
+
+	/* echo 'tko param 10 4 10 0 ' | iw dev wlan0 vendor
 	 * send 0x000319 0x1C -
 	 */
-	if (cmd_str[1] && (strlen(cmd_str[1]) == 6) &&
-	    (memcmp(cmd_str[1], "enable", 6) == 0) &&
-	     (cmd_val[0] == 0 || cmd_val[0] == 1)) {
-		tko_enable = (struct ifx_tko_enable *)tko.data;
-		tko.subcmd_id = WL_TKO_SUBCMD_ENABLE;
-		tko.len = sizeof(*tko_enable);
+	if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 5) &&
+	    (memcmp(cmd_str[1], "param", 5) == 0) &&
+	    (cmd_val[0] >= 0 && cmd_val[1] >= 0 &&
+	     cmd_val[2] >= 0 && cmd_val[3] >= 0)) {
+		tko_param = (struct ifx_tko_param *)tko->data;
+		tko->subcmd_id = WL_TKO_SUBCMD_PARAM;
+		tko->len = sizeof(*tko_param);
+		tko_param->interval = cmd_val[0];
+		tko_param->retry_interval = cmd_val[1];
+		tko_param->retry_count = cmd_val[2];
+		tko_param->rst_delay = cmd_val[3];
+
+	} else if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 6) &&
+		   (memcmp(cmd_str[1], "enable", 6) == 0) &&
+		   (cmd_val[0] == 0 || cmd_val[0] == 1)) {
+		/* echo 'tko enable 1 ' | iw dev wlan0 vendor
+		 * send 0x000319 0x1C -
+		 */
+		tko_enable = (struct ifx_tko_enable *)tko->data;
+		tko->subcmd_id = WL_TKO_SUBCMD_ENABLE;
+		tko->len = sizeof(*tko_enable);
 		tko_enable->enable = cmd_val[0];
-
-		length = offsetof(struct ifx_tko, data) + tko.len;
-		ret = brcmf_fil_bsscfg_data_set(ifp, "tko", (void *)&tko, length);
-		if (ret)
-			brcmf_err("Failed to enable/disable tko: %d\n", ret);
 	} else {
 		brcmf_err("Invalid tko command format\n");
 		return -EINVAL;
 	}
 
+	length = offsetof(struct ifx_tko, data) + tko->len;
+	ret = brcmf_fil_iovar_data_set(ifp, "tko", buf, length);
+	if (ret)
+		brcmf_err("Failed to configure tko: %d\n", ret);
+
 	return ret;
 }
 
@@ -977,9 +1029,9 @@ int ifx_cfg80211_vndr_cmds_str(struct wiphy *wiphy, struct wireless_dev *wdev,
 	struct brcmf_if *ifp;
 	struct ifx_vndr_cmdstr_hashtbl *hash_entry;
 	u32 jhash_key;
-	int ret = 0, i = 0, j = 0;
+	int ret = 0, idx_str = 0, idx_val = 0;
 	unsigned long val;
-	char cmd_str[VNDR_CMD_STR_NUM][VNDR_CMD_STR_MAX_LEN] = {{""}};
+	char cmd_str[VNDR_CMD_STR_NUM][VNDR_CMD_STR_MAX_LEN];
 	long cmd_val[VNDR_CMD_VAL_NUM];
 	char *tok = NULL, *buf = NULL;
 
@@ -987,29 +1039,63 @@ int ifx_cfg80211_vndr_cmds_str(struct wiphy *wiphy, struct wireless_dev *wdev,
 	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
 	ifp = vif->ifp;
 
+	memset(cmd_str, '\0', VNDR_CMD_STR_NUM * VNDR_CMD_STR_MAX_LEN * sizeof(char));
 	memset(cmd_val, -1, VNDR_CMD_VAL_NUM * sizeof(*cmd_val));
 
-	while ((tok = strsep(&buf, " ")) != NULL) {
-		if (kstrtoul(tok, 10, &val) == 0) {
-			cmd_val[j] = val;
-			j++;
+	while (idx_str < VNDR_CMD_STR_NUM && idx_val < VNDR_CMD_VAL_NUM &&
+	       ((tok = strsep(&buf, " ")) != NULL)) {
+		if (kstrtol(tok, 10, &val) == 0) {
+			cmd_val[idx_val] = val;
+			idx_val++;
 		} else if ((strncmp(tok, "0x", 2) == 0) || (strncmp(tok, "0X", 2) == 0)) {
-			if (kstrtoul(tok, 16, &val) == 0) {
-				cmd_val[j] = val;
-				j++;
+			if (kstrtol(tok, 16, &val) == 0) {
+				cmd_val[idx_val] = val;
+				idx_val++;
+
+			} else if (strnlen(tok, VNDR_CMD_VAL_NUM) >= 20) {
+			/* For larger input hex, split the hex pattern into 2 bytes each
+			 * and store it individually.
+			 */
+				tok = tok + 2;/* Skip past 0x */
+				if (strlen(tok) % 2 != 0) {
+					brcmf_err("Data invalid format. Even length required\n");
+					return -EINVAL;
+				}
+				while (*tok != '\0') {
+					char num[3];
+
+					if (idx_val >= VNDR_CMD_VAL_NUM) {
+						brcmf_err("pkt header hex length exceeded\n");
+						return -EINVAL;
+					}
+					memcpy(num, tok, 2);
+					num[2] = '\0';
+					if (kstrtol(num, 16, &val) == 0) {
+						cmd_val[idx_val] = val;
+					} else {
+						brcmf_err("Invalid hex pkt data\n");
+						return -EINVAL;
+					}
+					tok += 2;
+					idx_val++;
+				}
+				cmd_val[idx_val] = ' ';
 			} else {
 				brcmf_err("Failed to parse hex token\n");
 				return -EINVAL;
 			}
 		} else if (strnlen(tok, VNDR_CMD_STR_MAX_LEN) <= VNDR_CMD_STR_MAX_LEN) {
-			strncpy(cmd_str[i], tok, strnlen(tok, VNDR_CMD_STR_MAX_LEN));
-			i++;
+			strncpy(cmd_str[idx_str], tok, strnlen(tok, VNDR_CMD_STR_MAX_LEN));
+			idx_str++;
 		} else {
 			brcmf_err("Failed to parse token\n");
 			return -EINVAL;
 		}
 	}
-
+	if (idx_str >= VNDR_CMD_STR_NUM || idx_val >= VNDR_CMD_VAL_NUM) {
+		brcmf_err("CMD parameter limit exceeded\n");
+		return -EINVAL;
+	}
 	/* Run the user cmd string input via Jenkins hash to pass and search the entry in
 	 * vendor cmd hashtable initialized at load time.
 	 */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h
index 55a41cff4044..3c4d2949bcab 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h
@@ -719,8 +719,17 @@ struct ifx_tko {
 };
 
 /* subcommand ids */
+#define WL_TKO_SUBCMD_PARAM		1	/* configure offload common parameters  */
 #define WL_TKO_SUBCMD_ENABLE		3	/* enable/disable */
 
+/* WL_TKO_SUBCMD_PARAM subcommand data */
+struct ifx_tko_param {
+	u16 interval;		/* keepalive tx interval (secs) */
+	u16 retry_interval;	/* keepalive retry interval (secs) */
+	u16 retry_count;	/* retry_count */
+	s16 rst_delay;		/* delay to delay a RST frame from reaching the host */
+};
+
 struct ifx_tko_enable {
 	u8 enable;		/* 1 - enable, 0 - disable */
 	u8 pad[3];		/* 4-byte struct alignment */
@@ -730,7 +739,7 @@ struct ifx_tko_enable {
  */
 #define VNDR_CMD_STR_NUM	15
 #define VNDR_CMD_STR_MAX_LEN	20
-#define VNDR_CMD_VAL_NUM	15
+#define VNDR_CMD_VAL_NUM	50
 #define VNDR_CMD_HASH_BITS	4
 
 struct ifx_vendor_cmdstr {
-- 
2.25.1

