From f27e9c80172d284a2d57cf303a04918269a85b1e Mon Sep 17 00:00:00 2001
From: Ashwin Adiga <ashwin.adiga@infineon.com>
Date: Fri, 14 Feb 2025 00:13:15 -0600
Subject: [PATCH 04/25] non-upstream: ICMP Echo Request offload implementation

ICMP Echo Request offload is implemented using IW vendor commands
which are being added here and uses string based parsing infrastructure.
ICMP Echo Request event is also handled here.

Example commands for ICMP Echo Request Offload:

1. echo 'icmp_echo_req enable 1 ' | iw dev wlan0 vendor send 0x000319
0x1C -

2. echo 'icmp_echo_req add 192.168.1.1 00:11:22:33:44:55 5 10 ' | iw
dev wlan0 vendor send 0x000319 0x1C -

3. echo 'icmp_echo_req del 192.168.1.1 ' | iw dev wlan0 vendor send
0x000319 0x1C -

4. echo 'icmp_echo_req start 192.168.1.1 ' | iw dev wlan0 vendor send
0x000319 0x1C -

5. echo 'icmp_echo_req stop 192.168.1.1 ' | iw dev wlan0 vendor send
0x000319 0x1C -

6. echo 'icmp_echo_req info 192.168.1.1 ' | iw dev wlan0 vendor recv
0x000319 0x1C -

7. echo 'icmp_echo_req info all ' | iw dev wlan0 vendor recv
0x000319 0x1C -

Fixes SWLINUX-5038

Signed-off-by: Ashwin Adiga <ashwin.adiga@infineon.com>
---
 .../broadcom/brcm80211/brcmfmac/cfg80211.c    |   4 +-
 .../broadcom/brcm80211/brcmfmac/fweh.h        |   5 +-
 .../broadcom/brcm80211/brcmfmac/fwil_types.h  |  78 ++++++
 .../broadcom/brcm80211/brcmfmac/vendor.c      |  65 +++++
 .../broadcom/brcm80211/brcmfmac/vendor.h      |   5 +-
 .../broadcom/brcm80211/brcmfmac/vendor_ifx.c  | 247 ++++++++++++++++++
 .../broadcom/brcm80211/brcmfmac/vendor_ifx.h  |  26 +-
 7 files changed, 423 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index a273b4fb9418..fe680fae727d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -8753,6 +8753,8 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
 			    brcmf_notify_assoc_req_ie);
 	brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_RESP_IE,
 			    brcmf_notify_assoc_resp_ie);
+	brcmf_fweh_register(cfg->pub, BRCMF_E_ICMP_ECHO_REQ,
+			    brcmf_wiphy_icmp_echo_req_event_handler);
 }
 
 static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
@@ -9992,7 +9994,7 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
 	wiphy->vendor_commands = brcmf_vendor_cmds;
 	wiphy->n_vendor_commands = get_brcmf_num_vndr_cmds();
 	wiphy->vendor_events = brcmf_vendor_events;
-	wiphy->n_vendor_events = BRCMF_VNDR_EVTS_LAST;
+	wiphy->n_vendor_events = get_brcmf_num_vndr_evts();
 	brcmf_fweh_register(cfg->pub, BRCMF_E_PHY_TEMP,
 			    brcmf_wiphy_phy_temp_evt_handler);
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
index bd6ac6219493..9b00c8f7213c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h
@@ -102,7 +102,8 @@ struct brcmf_cfg80211_info;
 	BRCMF_ENUM_DEF(MGMT_FRAME_TXSTATUS, 189) \
 	BRCMF_ENUM_DEF(MGMT_FRAME_OFF_CHAN_COMPLETE, 190) \
 	BRCMF_ENUM_DEF(TWT_TEARDOWN, 195) \
-	BRCMF_ENUM_DEF(EXT_ASSOC_FRAME_RX, 196)
+	BRCMF_ENUM_DEF(EXT_ASSOC_FRAME_RX, 196) \
+	BRCMF_ENUM_DEF(ICMP_ECHO_REQ, 202)
 
 #define BRCMF_ENUM_DEF(id, val) \
 	BRCMF_E_##id = (val),
@@ -114,7 +115,7 @@ enum brcmf_fweh_event_code {
 	 * minimum length check in device firmware so it is
 	 * hard-coded here.
 	 */
-	BRCMF_E_LAST = 197
+	BRCMF_E_LAST = 203
 };
 #undef BRCMF_ENUM_DEF
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index 318258567543..a3c0b29a76ab 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -1293,4 +1293,82 @@ struct brcmf_ol_cfg_v1 {
 	u32 offload_skip;				/* Bitmap of offload to be skipped */
 };
 
+#define WL_ICMP_ECHO_REQ_VER		1
+
+#define ICMP_ECHO_REQ_IP_BOTH		0
+#define ICMP_ECHO_REQ_IP_V4		1
+#define ICMP_ECHO_REQ_IP_V6		2
+
+/* ICMP Echo Request Sub commands */
+enum {
+	WL_ICMP_ECHO_REQ_ENAB,
+	WL_ICMP_ECHO_REQ_ADD,
+	WL_ICMP_ECHO_REQ_DEL,
+	WL_ICMP_ECHO_REQ_START,
+	WL_ICMP_ECHO_REQ_STOP,
+	WL_ICMP_ECHO_REQ_INFO
+};
+
+struct ifx_icmp_echo_req_peer_ip {
+	u16 version;
+	u16 length;
+	u8 ip_ver;				/* IP Version IPv4:1 IPv6:2 */
+	u8 pad[3];
+	union {
+		struct ipv4_addr ipv4;		/* Peer IPV4 Address */
+		struct ipv6_addr ipv6;		/* Peer IPV6 Address */
+	} u;
+};
+
+struct ifx_icmp_echo_req_peer_config {
+	u16 version;
+	u16 length;
+	u8 ip_ver;				/* IP Version IPv4:1 IPv6:2 */
+	u8 pad[3];
+	u32 periodicity;			/* Periodicty of Ping in sec */
+	u32 duration;				/* Duration in sec  */
+	union {
+		struct ipv4_addr ipv4;		/* Peer IPv4 Address */
+		struct ipv6_addr ipv6;		/* Peer IPv6 Address */
+	} u;
+	u8 mac_addr[ETH_ALEN];			/* Peer Mac Address */
+};
+
+/* ICMP Echo Req IOVAR Struct */
+struct ifx_icmp_echo_req_cmd {
+	u16 version;
+	u16 length;
+	u8 cmd_type;				/* ICMP Echo Req Cmd Type */
+	u8 pad[3];
+	u8 data[];				/* Data Pointing to Sub cmd structure */
+};
+
+/* ICMP Echo Request IOVAR INFO Struct */
+struct ifx_icmp_echo_req_get_peer_info {
+	u32 state;					/* State of the Peer */
+	struct ifx_icmp_echo_req_peer_config config;	/* Configuration of Peer */
+};
+
+struct ifx_icmp_echo_req_get_info {
+	u16 version;
+	u16 length;
+	u8 enable;				/* Offload Enable */
+	u8 count;				/* Peer Count */
+	u8 pad[2];
+	u8 data[];				/* Data Pointing to get peer info structure */
+};
+
+struct ifx_icmp_echo_req_event {
+	u16 version;
+	u16 length;
+	u8 ip_ver;			/* Peer IP Version IPv4:1 IPv6:2 */
+	u8 reason;			/* Event reason */
+	u8 pad[2];
+	u32 echo_req_cnt;		/* ICMP Echo Req Count */
+	union {
+		struct ipv4_addr ipv4;	/* Peer IPV4 Address */
+		struct ipv6_addr ipv6;	/* Peer IPV6 Address */
+	} u;
+};
+
 #endif /* FWIL_TYPES_H_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
index cfc60793d322..9bec90f7fc18 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.c
@@ -15,6 +15,7 @@
 #include "cfg80211.h"
 #include "vendor.h"
 #include "fwil.h"
+#include "common.h"
 #include "vendor_ifx.h"
 
 static int brcmf_cfg80211_vndr_cmds_dcmd_handler(struct wiphy *wiphy,
@@ -221,6 +222,55 @@ brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp,
 	return 0;
 }
 
+s32
+brcmf_wiphy_icmp_echo_req_event_handler(struct brcmf_if *ifp,
+					const struct brcmf_event_msg *e, void *data)
+{
+	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+	struct wiphy *wiphy = cfg_to_wiphy(cfg);
+	struct sk_buff *skb;
+	struct ifx_icmp_echo_req_event *echo_req_event;
+
+	echo_req_event = (struct ifx_icmp_echo_req_event *)data;
+
+	brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n",
+		  brcmf_fweh_event_name(e->event_code), e->event_code,
+		  e->status);
+
+	brcmf_dbg(INFO, "icmp_echo_req_event reason = %d icmp_echo_req_event count = %d\n",
+		  echo_req_event->reason, echo_req_event->echo_req_cnt);
+
+	if (echo_req_event->ip_ver == ICMP_ECHO_REQ_IP_V6) {
+		brcmf_dbg(INFO, "icmp_echo_req_event IPv6 address = %pI6",
+			  &echo_req_event->u.ipv6.addr);
+	} else if (echo_req_event->ip_ver == ICMP_ECHO_REQ_IP_V4) {
+		brcmf_dbg(INFO, "icmp_echo_req_event IPv4 address = %pI4",
+			  &echo_req_event->u.ipv6.addr);
+	} else {
+		brcmf_err("Invalid IP address\n");
+		return -EINVAL;
+	}
+
+	skb = cfg80211_vendor_event_alloc(wiphy, NULL,
+					  echo_req_event->length,
+					  IFX_VENDOR_EVTS_ICMP_ECHO_REQ,
+					  GFP_KERNEL);
+
+	if (!skb) {
+		brcmf_err("NO MEM: can't allocate skb for ICMP_ECHO_REQ_EVENT\n");
+		return -ENOMEM;
+	}
+
+	if (nla_put(skb, NL80211_ATTR_VENDOR_DATA, echo_req_event->length, data)) {
+		kfree_skb(skb);
+		brcmf_err("NO ROOM in skb for ICMP_ECHO_REQ_EVENT\n");
+		return -EMSGSIZE;
+	}
+
+	cfg80211_vendor_event(skb, GFP_KERNEL);
+	return 0;
+}
+
 const struct wiphy_vendor_command brcmf_vendor_cmds[] = {
 	{
 		{
@@ -386,6 +436,14 @@ const struct nl80211_vendor_cmd_info brcmf_vendor_events[] = {
 		.vendor_id = BROADCOM_OUI,
 		.subcmd = BRCMF_VNDR_EVTS_PHY_TEMP,
 	},
+	{
+		.vendor_id = OUI_IFX,
+		.subcmd = IFX_VENDOR_EVTS_RSV1,		/* Reserved for WLAN Sense */
+	},
+	{
+		.vendor_id = OUI_IFX,
+		.subcmd = IFX_VENDOR_EVTS_ICMP_ECHO_REQ,
+	},
 };
 
 int get_brcmf_num_vndr_cmds(void)
@@ -394,3 +452,10 @@ int get_brcmf_num_vndr_cmds(void)
 
 	return num;
 }
+
+int get_brcmf_num_vndr_evts(void)
+{
+	int num = ARRAY_SIZE(brcmf_vendor_events);
+
+	return num;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h
index adc559e12dae..3029cc5ec09f 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor.h
@@ -69,6 +69,9 @@ extern const struct nl80211_vendor_cmd_info brcmf_vendor_events[];
 s32 brcmf_wiphy_phy_temp_evt_handler(struct brcmf_if *ifp,
 				     const struct brcmf_event_msg *e,
 				     void *data);
+s32 brcmf_wiphy_icmp_echo_req_event_handler(struct brcmf_if *ifp,
+					    const struct brcmf_event_msg *e,
+					    void *data);
 int get_brcmf_num_vndr_cmds(void);
-
+int get_brcmf_num_vndr_evts(void);
 #endif /* _vendor_h_ */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c
index cc1f3bf2095e..0d2b16dd64f5 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.c
@@ -44,12 +44,14 @@
 #include <linux/kernel.h>
 #include <linux/jhash.h>
 #include <linux/hashtable.h>
+#include <linux/inet.h>
 #include "common.h"
 
 static const struct ifx_vendor_cmdstr ifx_vndr_cmdstr[] = {
 	{ "offload_config", ifx_vndr_cmdstr_offload_config},
 	{ "mkeep_alive", ifx_vndr_cmdstr_mkeep_alive},
 	{ "tko", ifx_vndr_cmdstr_tko},
+	{ "icmp_echo_req", ifx_vndr_cmdstr_icmp_echo_req},
 	{ NULL, NULL }
 };
 
@@ -1022,6 +1024,251 @@ int ifx_vndr_cmdstr_tko(struct wiphy *wiphy, struct wireless_dev *wdev,
 	return ret;
 }
 
+/* ifx_vndr_cmd_str_parse_ip()
+ * Get ip version. based on the ip version parse the command string into IP.
+ * In Param
+ * @cmd_str - String to be parsed.
+ * @ip_addr - Parsed IP address storage.
+ * Out Param
+ * @ip_ver -  Pointer to IP version.
+ * Return
+ * true - success.
+ * false - otherwise.
+ */
+static
+int ifx_vndr_cmdstr_parse_ip(char *cmd_str, u8 *ip_addr, u8 *ip_ver)
+{
+	if (in4_pton(cmd_str, strlen(cmd_str), ip_addr, -1, NULL)) {
+		*ip_ver = ICMP_ECHO_REQ_IP_V4;
+		brcmf_dbg(INFO, "Peer IP Version: %d Peer IPv4 Address: %pI4\n",
+			  *ip_ver, ip_addr);
+		return true;
+	} else if (in6_pton(cmd_str, strlen(cmd_str), ip_addr, -1, NULL)) {
+		*ip_ver = ICMP_ECHO_REQ_IP_V6;
+		brcmf_dbg(INFO, "Peer IP Version: %d Peer IPv6 Address: %pI6\n",
+			  *ip_ver, ip_addr);
+		return true;
+	}
+
+	return false;
+}
+
+/* ifx_vndr_icmp_echo_req_config()
+ * Prepare ICMP Echo Request IOVAR based on the ICMP Echo Request Parameters.
+ * In Param
+ * @ifp - Pointer to brcmf_if structure.
+ * @u8 - Sub Command Type.
+ * @enable - Enable.
+ * @ip_addr - IP Address to be filled.
+ * @ip_ver - IP version.
+ * @mac_addr - MAC Address to be filled.
+ * @periodicity - Periodicity of ping in sec.
+ * @duration - Duration in sec.
+ * Return
+ * 0 - success
+ * Non Zero  - otherwise
+ */
+static
+int ifx_vndr_icmp_echo_req_config(struct brcmf_if *ifp, u8 cmd_type,
+				  u8 enable, u8 *ip_addr, u8 ip_ver,
+				  u8 *mac_addr, u32 periodicity, u32 duration)
+{
+	struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
+	struct wiphy *wiphy = cfg_to_wiphy(cfg);
+	struct ifx_icmp_echo_req_cmd *icmp_echo_req_cmd;
+	struct ifx_icmp_echo_req_peer_config *icmp_echo_req_peer_config;
+	struct ifx_icmp_echo_req_peer_ip *icmp_echo_req_peer_ip;
+	struct ifx_icmp_echo_req_get_info *icmp_echo_req_get_info;
+	int ret = 0;
+
+	memset(cfg->extra_buf, '\0', WL_EXTRA_BUF_MAX);
+	icmp_echo_req_cmd = (struct ifx_icmp_echo_req_cmd *)cfg->extra_buf;
+
+	icmp_echo_req_cmd->version = WL_ICMP_ECHO_REQ_VER;
+	icmp_echo_req_cmd->cmd_type = cmd_type;
+
+	switch (icmp_echo_req_cmd->cmd_type) {
+	case WL_ICMP_ECHO_REQ_ENAB:
+		icmp_echo_req_cmd->data[0] = enable;
+		icmp_echo_req_cmd->length = sizeof(struct ifx_icmp_echo_req_cmd) +
+					    sizeof(u8);
+		break;
+	case WL_ICMP_ECHO_REQ_ADD:
+		icmp_echo_req_peer_config = (struct ifx_icmp_echo_req_peer_config *)
+					    icmp_echo_req_cmd->data;
+		icmp_echo_req_cmd->length = sizeof(*icmp_echo_req_peer_config) +
+					    sizeof(struct ifx_icmp_echo_req_cmd);
+		icmp_echo_req_peer_config->version = WL_ICMP_ECHO_REQ_VER;
+		icmp_echo_req_peer_config->ip_ver = ip_ver;
+		memcpy(icmp_echo_req_peer_config->u.ipv6.addr, ip_addr,
+		       (icmp_echo_req_peer_config->ip_ver == ICMP_ECHO_REQ_IP_V6) ?
+			BRCMF_IPV6_ADDR_LEN : BRCMF_IPV4_ADDR_LEN);
+		memcpy(icmp_echo_req_peer_config->mac_addr, mac_addr, ETH_ALEN);
+		icmp_echo_req_peer_config->periodicity = periodicity;
+		icmp_echo_req_peer_config->duration = duration;
+		icmp_echo_req_peer_config->length = sizeof(struct ifx_icmp_echo_req_peer_config);
+		break;
+	case WL_ICMP_ECHO_REQ_DEL:
+	case WL_ICMP_ECHO_REQ_START:
+	case WL_ICMP_ECHO_REQ_STOP:
+		icmp_echo_req_peer_ip = (struct ifx_icmp_echo_req_peer_ip *)
+					icmp_echo_req_cmd->data;
+		icmp_echo_req_cmd->length = sizeof(*icmp_echo_req_peer_ip) +
+					    sizeof(struct ifx_icmp_echo_req_cmd);
+		icmp_echo_req_peer_ip->version = WL_ICMP_ECHO_REQ_VER;
+		icmp_echo_req_peer_ip->ip_ver = ip_ver;
+		memcpy(icmp_echo_req_peer_ip->u.ipv6.addr, ip_addr,
+		       (icmp_echo_req_peer_ip->ip_ver == ICMP_ECHO_REQ_IP_V6) ?
+			BRCMF_IPV6_ADDR_LEN : BRCMF_IPV4_ADDR_LEN);
+		icmp_echo_req_peer_ip->length = sizeof(struct ifx_icmp_echo_req_peer_ip);
+		break;
+	case WL_ICMP_ECHO_REQ_INFO:
+		icmp_echo_req_peer_ip = (struct ifx_icmp_echo_req_peer_ip *)
+					icmp_echo_req_cmd->data;
+		icmp_echo_req_cmd->length = sizeof(*icmp_echo_req_peer_ip) +
+					    sizeof(struct ifx_icmp_echo_req_cmd);
+		icmp_echo_req_peer_ip->version = WL_ICMP_ECHO_REQ_VER;
+		icmp_echo_req_peer_ip->ip_ver = ip_ver;
+		if (ip_ver != ICMP_ECHO_REQ_IP_BOTH) {
+			memcpy(icmp_echo_req_peer_ip->u.ipv6.addr, ip_addr,
+			       (icmp_echo_req_peer_ip->ip_ver == ICMP_ECHO_REQ_IP_V6) ?
+				BRCMF_IPV6_ADDR_LEN : BRCMF_IPV4_ADDR_LEN);
+		}
+		icmp_echo_req_peer_ip->length = sizeof(struct ifx_icmp_echo_req_peer_ip);
+		break;
+	default:
+		brcmf_err("offload icmp_echo_req subcmd id %d not recognized",
+			  icmp_echo_req_cmd->cmd_type);
+		return -EOPNOTSUPP;
+	}
+
+	if (icmp_echo_req_cmd->cmd_type == WL_ICMP_ECHO_REQ_INFO) {
+		icmp_echo_req_get_info = (struct ifx_icmp_echo_req_get_info *)cfg->extra_buf;
+		ret = brcmf_fil_iovar_data_get(ifp, "icmp_echo_req", cfg->extra_buf,
+					       WL_EXTRA_BUF_MAX);
+		if (ret)
+			brcmf_err("Failed to get icmp_echo_req info: %d\n", ret);
+		else
+			ifx_cfg80211_vndr_send_cmd_reply(wiphy, (void *)icmp_echo_req_get_info,
+							 icmp_echo_req_get_info->length);
+	} else {
+		ret = brcmf_fil_iovar_data_set(ifp, "icmp_echo_req", (u8 *)icmp_echo_req_cmd,
+					       icmp_echo_req_cmd->length);
+		if (ret)
+			brcmf_err("Failed to configure icmp_echo_req: %d\n", ret);
+	}
+	return ret;
+}
+
+int ifx_vndr_cmdstr_icmp_echo_req(struct wiphy *wiphy, struct wireless_dev *wdev,
+				  char cmd_str[VNDR_CMD_STR_NUM][VNDR_CMD_STR_MAX_LEN],
+				  long *cmd_val)
+{
+	struct brcmf_cfg80211_vif *vif;
+	struct brcmf_if *ifp;
+	bool is_ip = false;
+	u8 cmd_type, enable, ip_ver;
+	u32 periodicity = 0, duration = 0;
+	u8 ip_addr[BRCMF_IPV6_ADDR_LEN];
+	u8 mac_addr[ETH_ALEN];
+
+	vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
+	ifp = vif->ifp;
+
+	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 'icmp_echo_req enable 0/1 ' | iw dev wlan0 vendor
+		 * send 0x000319 0x1C -
+		 */
+		cmd_type = WL_ICMP_ECHO_REQ_ENAB;
+		enable = cmd_val[0];
+		brcmf_dbg(INFO, "Cmd Type: %d enable: %d\n", cmd_type, enable);
+
+	} else if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 3) &&
+		   (memcmp(cmd_str[1], "add", 3)) == 0) {
+		/* echo 'icmp_echo_req add <peer_ip> <peer_mac>
+		 *  <periodicity> <duration> ' | iw dev wlan0 vendor
+		 * send 0x000319 0x1C -
+		 */
+		cmd_type = WL_ICMP_ECHO_REQ_ADD;
+		is_ip = true;
+
+		if (cmd_str[3][0] != '\0') {
+			if (!mac_pton(cmd_str[3], mac_addr)) {
+				brcmf_err("Invalid icmp_echo_req peer MAC address\n");
+				return -EINVAL;
+			}
+		}
+
+		if (cmd_val[0] >= 0 && cmd_val[1] >= 0) {
+			periodicity = cmd_val[0];
+			duration = cmd_val[1];
+		}
+		brcmf_dbg(INFO, "Cmd Type: %d MAC Address: %pM Periodicity: %d Duration: %d\n",
+			  cmd_type, mac_addr, periodicity, duration);
+
+	} else if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 3) &&
+		   (memcmp(cmd_str[1], "del", 3)) == 0) {
+		/* echo 'icmp_echo_req del <peer_ip> ' | iw dev wlan0 vendor
+		 * send 0x000319 0x1C -
+		 */
+		cmd_type = WL_ICMP_ECHO_REQ_DEL;
+		is_ip = true;
+		brcmf_dbg(INFO, "Cmd Type: %d\n", cmd_type);
+
+	} else if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 5) &&
+		   (memcmp(cmd_str[1], "start", 5)) == 0) {
+		/* echo 'icmp_echo_req start <peer_ip> ' | iw dev wlan0 vendor
+		 * send 0x000319 0x1C -
+		 */
+		cmd_type = WL_ICMP_ECHO_REQ_START;
+		is_ip = true;
+		brcmf_dbg(INFO, "Cmd Type: %d\n", cmd_type);
+
+	} else if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 4) &&
+		   (memcmp(cmd_str[1], "stop", 4)) == 0) {
+		/* echo 'icmp_echo_req stop <peer_ip> ' | iw dev wlan0 vendor
+		 * send 0x000319 0x1C -
+		 */
+		cmd_type = WL_ICMP_ECHO_REQ_STOP;
+		is_ip = true;
+		brcmf_dbg(INFO, "Cmd type: %d\n", cmd_type);
+
+	} else if (cmd_str[1][0] != '\0' && (strlen(cmd_str[1]) == 4) &&
+		   (memcmp(cmd_str[1], "info", 4)) == 0) {
+		 /* echo 'icmp_echo_req info <peer_ip> ' | iw dev wlan0 vendor
+		  * recv 0x000319 0x1C -
+		  *
+		  * echo 'icmp_echo_req info all ' | iw dev wlan0 vendor
+		  * recv 0x000319 0x1C -
+		  */
+		cmd_type = WL_ICMP_ECHO_REQ_INFO;
+
+		if (cmd_str[2][0] != '\0' && (strlen(cmd_str[2]) == 3) &&
+		    (memcmp(cmd_str[2], "all", 3)) == 0)
+			ip_ver = ICMP_ECHO_REQ_IP_BOTH;
+		else
+			is_ip = true;
+		brcmf_dbg(INFO, "Cmd Type: %d\n", cmd_type);
+
+	} else {
+		brcmf_err("Invalid icmp_echo_req command format\n");
+		return -EINVAL;
+	}
+
+	if (is_ip && (cmd_str[2][0] != '\0')) {
+		if (!ifx_vndr_cmdstr_parse_ip(cmd_str[2], ip_addr, &ip_ver)) {
+			brcmf_err("Invalid  peer IP address\n");
+			return -EINVAL;
+		}
+	}
+
+	return ifx_vndr_icmp_echo_req_config(ifp, cmd_type, enable, ip_addr,
+					     ip_ver, mac_addr, periodicity,
+					     duration);
+}
+
 int ifx_cfg80211_vndr_cmds_str(struct wiphy *wiphy, struct wireless_dev *wdev,
 			       const void *data, int len)
 {
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h
index 517ac7868024..224e2a979ddb 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/vendor_ifx.h
@@ -153,6 +153,24 @@ enum ifx_nl80211_vendor_subcmds {
 	SCMD(MAX)		= 31
 };
 
+/* enum ifx_vendor_evts - IFX nl80211 vendor events
+ *
+ * @IFX_VENDOR_EVTS_RSV0: Reserved Event
+ *
+ * @IFX_VENDOR_EVTS_RSV1: Reserved Event
+ *
+ * @IFX_VENDOR_EVTS_ICMP_ECHO_REQ: ICMP Echo Request Event
+ *
+ * @IFX_VENDOR_EVTS_LAST: This acts as a the tail of evts list.
+ *      Make sure it located at the end of the list.
+ */
+enum ifx_vendor_evts {
+	IFX_VENDOR_EVTS_RSV0,
+	IFX_VENDOR_EVTS_RSV1,
+	IFX_VENDOR_EVTS_ICMP_ECHO_REQ,
+	IFX_VENDOR_EVTS_LAST
+};
+
 /*
  * enum ifx_vendor_attr - IFX nl80211 vendor attributes
  *
@@ -752,10 +770,9 @@ struct ifx_tko_enable {
 	u8 pad[3];		/* 4-byte struct alignment */
 };
 
-/* String based vendor commands infra
- */
+/* String based vendor commands infra */
 #define VNDR_CMD_STR_NUM	15
-#define VNDR_CMD_STR_MAX_LEN	20
+#define VNDR_CMD_STR_MAX_LEN	50
 #define VNDR_CMD_VAL_NUM	50
 #define VNDR_CMD_HASH_BITS	4
 
@@ -818,6 +835,9 @@ int ifx_vndr_cmdstr_mkeep_alive(struct wiphy *wiphy, struct wireless_dev *wdev,
 int ifx_vndr_cmdstr_tko(struct wiphy *wiphy, struct wireless_dev *wdev,
 			char cmd_str[VNDR_CMD_STR_NUM][VNDR_CMD_STR_MAX_LEN],
 			long *cmd_val);
+int ifx_vndr_cmdstr_icmp_echo_req(struct wiphy *wiphy, struct wireless_dev *wdev,
+				  char cmd_str[VNDR_CMD_STR_NUM][VNDR_CMD_STR_MAX_LEN],
+				  long *cmd_val);
 int ifx_cfg80211_vndr_cmds_str(struct wiphy *wiphy, struct wireless_dev *wdev,
 			       const void *data, int len);
 int ifx_cfg80211_vndr_cmds_config_pfn(struct wiphy *wiphy,
-- 
2.25.0

