Kaynağa Gözat

qcacld-3.0: Extend debug stats

Extend to collect connectivity check stats other
than ARP such as DNS, TCP SYN/SYN-ACK/ACK, and Ipv4.
Add extra attributes in existing vendor command(set/reset
and get NUD debug stats) for above  connectvitiy stats.

CRs-Fixed: 2161889
Change-Id: I037c4ec29b181a0b3117ae2abbc7a2229b373ac2
Poddar, Siddarth 7 yıl önce
ebeveyn
işleme
31797fa6a8

+ 7 - 1
core/cds/inc/cds_api.h

@@ -590,7 +590,13 @@ bool cds_is_group_addr(uint8_t *mac_addr)
 		return false;
 }
 
-uint32_t cds_get_arp_stats_gw_ip(void);
+/**
+ * cds_get_arp_stats_gw_ip() - get arp stats track IP
+ * @context: osif dev
+ *
+ * Return: ARP stats IP to track.
+ */
+uint32_t cds_get_arp_stats_gw_ip(void *context);
 void cds_incr_arp_stats_tx_tgt_delivered(void);
 void cds_incr_arp_stats_tx_tgt_acked(void);
 

+ 7 - 11
core/cds/src/cds_api.c

@@ -2776,22 +2776,18 @@ cds_print_htc_credit_history(uint32_t count, qdf_abstract_print *print,
 }
 #endif
 
-/**
- * cds_get_arp_stats_gw_ip() - get arp stats track IP
- *
- * Return: ARP stats IP to track
- */
-uint32_t cds_get_arp_stats_gw_ip(void)
+uint32_t cds_get_arp_stats_gw_ip(void *context)
 {
-	struct hdd_context *hdd_ctx;
+	struct hdd_adapter *adapter = (struct hdd_adapter *)context;
 
-	hdd_ctx = (struct hdd_context *) (gp_cds_context->pHDDContext);
-	if (!hdd_ctx) {
-		cds_err("Hdd Context is Null");
+	if (unlikely(adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) {
+		QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR,
+			  "Magic cookie(%x) for adapter sanity verification is invalid",
+			  adapter->magic);
 		return 0;
 	}
 
-	return hdd_ctx->track_arp_ip;
+	return adapter->track_arp_ip;
 }
 
 /**

+ 10 - 4
core/dp/txrx/ol_tx_send.c

@@ -631,15 +631,21 @@ static inline void ol_tx_timestamp(ol_txrx_pdev_handle pdev,
 
 /**
  * ol_tx_update_arp_stats() - update ARP packet TX stats
+ * @tx_desc: tx desc
  * @netbuf:  buffer
+ * @status: htt status
  *
  *
  * Return: none
  */
-static void ol_tx_update_arp_stats(qdf_nbuf_t netbuf,
-					enum htt_tx_status status)
+static void ol_tx_update_arp_stats(struct ol_tx_desc_t *tx_desc,
+				   qdf_nbuf_t netbuf,
+				   enum htt_tx_status status)
 {
-	uint32_t tgt_ip = cds_get_arp_stats_gw_ip();
+	uint32_t tgt_ip;
+
+	qdf_assert(tx_desc);
+	tgt_ip = cds_get_arp_stats_gw_ip(tx_desc->vdev->osif_dev);
 
 	if (tgt_ip == qdf_nbuf_get_arp_tgt_ip(netbuf)) {
 		if (status != htt_tx_status_download_fail)
@@ -703,7 +709,7 @@ ol_tx_completion_handler(ol_txrx_pdev_handle pdev,
 		if (QDF_NBUF_CB_GET_PACKET_TYPE(netbuf) ==
 		    QDF_NBUF_CB_PACKET_TYPE_ARP) {
 			if (qdf_nbuf_data_is_arp_req(netbuf))
-				ol_tx_update_arp_stats(netbuf, status);
+				ol_tx_update_arp_stats(tx_desc, netbuf, status);
 		}
 
 		if (tx_desc->pkt_type != OL_TX_FRM_TSO) {

+ 106 - 7
core/hdd/inc/wlan_hdd_main.h

@@ -307,6 +307,9 @@
 #endif
 
 #define MAX_USER_COMMAND_SIZE 4096
+#define DNS_DOMAIN_NAME_MAX_LEN 255
+#define ICMPv6_ADDR_LEN 16
+
 
 #define HDD_MIN_TX_POWER (-100) /* minimum tx power */
 #define HDD_MAX_TX_POWER (+100) /* maximum tx power */
@@ -464,6 +467,19 @@ struct hdd_pmf_stats {
 };
 #endif
 
+/**
+ * struct hdd_arp_stats_s - arp debug stats count
+ * @tx_arp_req_count: no. of arp req received from network stack
+ * @rx_arp_rsp_count: no. of arp res received from FW
+ * @tx_dropped: no. of arp req dropped at hdd layer
+ * @rx_dropped: no. of arp res dropped
+ * @rx_delivered: no. of arp res delivered to network stack
+ * @rx_refused: no of arp rsp refused (not delivered) to network stack
+ * @tx_host_fw_sent: no of arp req sent by FW OTA
+ * @rx_host_drop_reorder: no of arp res dropped by host
+ * @rx_fw_cnt: no of arp res received by FW
+ * @tx_ack_cnt: no of arp req acked by FW
+ */
 struct hdd_arp_stats_s {
 	uint16_t tx_arp_req_count;
 	uint16_t rx_arp_rsp_count;
@@ -478,14 +494,87 @@ struct hdd_arp_stats_s {
 };
 
 /**
- * struct hdd_stats - per-adapter statistics
- * @summary_stat: Summary stats reported by firmware
- * @class_a_stat: "Class A" stats reported by firmware
- * @class_d_stat: "Class D" stats reported by firmware
- * @per_chain_rssi_stats: Per-chain RSSI stats
- * @tx_rx_stats: Tx & Rx stats
- * @hdd_pmf_stats: Protected Management Frame stats
+ * struct hdd_dns_stats_s - dns debug stats count
+ * @tx_dns_req_count: no. of dns query received from network stack
+ * @rx_dns_rsp_count: no. of dns res received from FW
+ * @tx_dropped: no. of dns query dropped at hdd layer
+ * @rx_delivered: no. of dns res delivered to network stack
+ * @rx_refused: no of dns res refused (not delivered) to network stack
+ * @tx_host_fw_sent: no of dns query sent by FW OTA
+ * @rx_host_drop: no of dns res dropped by host
+ * @tx_ack_cnt: no of dns req acked by FW
  */
+struct hdd_dns_stats_s {
+	uint16_t tx_dns_req_count;
+	uint16_t rx_dns_rsp_count;
+	uint16_t tx_dropped;
+	uint16_t rx_delivered;
+	uint16_t rx_refused;
+	uint16_t tx_host_fw_sent;
+	uint16_t rx_host_drop;
+	uint16_t tx_ack_cnt;
+};
+
+/**
+ * struct hdd_tcp_stats_s - tcp debug stats count
+ * @tx_tcp_syn_count: no. of tcp syn received from network stack
+ * @@tx_tcp_ack_count: no. of tcp ack received from network stack
+ * @rx_tcp_syn_ack_count: no. of tcp syn ack received from FW
+ * @tx_tcp_syn_dropped: no. of tcp syn dropped at hdd layer
+ * @tx_tcp_ack_dropped: no. of tcp ack dropped at hdd layer
+ * @rx_delivered: no. of tcp syn ack delivered to network stack
+ * @rx_refused: no of tcp syn ack refused (not delivered) to network stack
+ * @tx_tcp_syn_host_fw_sent: no of tcp syn sent by FW OTA
+ * @@tx_tcp_ack_host_fw_sent: no of tcp ack sent by FW OTA
+ * @rx_host_drop: no of tcp syn ack dropped by host
+ * @tx_tcp_syn_ack_cnt: no of tcp syn acked by FW
+ * @tx_tcp_syn_ack_cnt: no of tcp ack acked by FW
+ * @is_tcp_syn_ack_rcv: flag to check tcp syn ack received or not
+ * @is_tcp_ack_sent: flag to check tcp ack sent or not
+ */
+struct hdd_tcp_stats_s {
+	uint16_t tx_tcp_syn_count;
+	uint16_t tx_tcp_ack_count;
+	uint16_t rx_tcp_syn_ack_count;
+	uint16_t tx_tcp_syn_dropped;
+	uint16_t tx_tcp_ack_dropped;
+	uint16_t rx_delivered;
+	uint16_t rx_refused;
+	uint16_t tx_tcp_syn_host_fw_sent;
+	uint16_t tx_tcp_ack_host_fw_sent;
+	uint16_t rx_host_drop;
+	uint16_t rx_fw_cnt;
+	uint16_t tx_tcp_syn_ack_cnt;
+	uint16_t tx_tcp_ack_ack_cnt;
+	bool is_tcp_syn_ack_rcv;
+	bool is_tcp_ack_sent;
+
+};
+
+/**
+ * struct hdd_icmpv4_stats_s - icmpv4 debug stats count
+ * @tx_icmpv4_req_count: no. of icmpv4 req received from network stack
+ * @rx_icmpv4_rsp_count: no. of icmpv4 res received from FW
+ * @tx_dropped: no. of icmpv4 req dropped at hdd layer
+ * @rx_delivered: no. of icmpv4 res delivered to network stack
+ * @rx_refused: no of icmpv4 res refused (not delivered) to network stack
+ * @tx_host_fw_sent: no of icmpv4 req sent by FW OTA
+ * @rx_host_drop: no of icmpv4 res dropped by host
+ * @rx_fw_cnt: no of icmpv4 res received by FW
+ * @tx_ack_cnt: no of icmpv4 req acked by FW
+ */
+struct hdd_icmpv4_stats_s {
+	uint16_t tx_icmpv4_req_count;
+	uint16_t rx_icmpv4_rsp_count;
+	uint16_t tx_dropped;
+	uint16_t rx_delivered;
+	uint16_t rx_refused;
+	uint16_t tx_host_fw_sent;
+	uint16_t rx_host_drop;
+	uint16_t rx_fw_cnt;
+	uint16_t tx_ack_cnt;
+};
+
 struct hdd_stats {
 	tCsrSummaryStatsInfo summary_stat;
 	tCsrGlobalClassAStatsInfo class_a_stat;
@@ -493,6 +582,9 @@ struct hdd_stats {
 	struct csr_per_chain_rssi_stats_info  per_chain_rssi_stats;
 	struct hdd_tx_rx_stats tx_rx_stats;
 	struct hdd_arp_stats_s hdd_arp_stats;
+	struct hdd_dns_stats_s hdd_dns_stats;
+	struct hdd_tcp_stats_s hdd_tcp_stats;
+	struct hdd_icmpv4_stats_s hdd_icmpv4_stats;
 #ifdef WLAN_FEATURE_11W
 	struct hdd_pmf_stats hdd_pmf_stats;
 #endif
@@ -1211,6 +1303,13 @@ struct hdd_adapter {
 	bool con_status;
 	bool dad;
 	uint8_t active_ac;
+	uint32_t pkt_type_bitmap;
+	uint32_t track_arp_ip;
+	uint8_t dns_payload[256];
+	uint32_t track_dns_domain_len;
+	uint32_t track_src_port;
+	uint32_t track_dest_port;
+	uint32_t track_dest_ipv4;
 	uint32_t mon_chan;
 	uint32_t mon_bandwidth;
 

+ 12 - 0
core/hdd/inc/wlan_hdd_tx_rx.h

@@ -65,6 +65,18 @@ QDF_STATUS hdd_rx_packet_cbk(void *context, qdf_nbuf_t rxBuf);
 QDF_STATUS hdd_get_peer_sta_id(struct hdd_station_ctx *sta_ctx,
 				struct qdf_mac_addr *peer_mac_addr,
 				uint8_t *sta_id);
+/**
+ * hdd_tx_rx_collect_connectivity_stats_info() - collect connectivity stats
+ * @skb: pointer to skb data
+ * @adapter: pointer to vdev apdapter
+ * @action: action done on pkt.
+ * @pkt_type: data pkt type
+ *
+ * Return: None
+ */
+void hdd_tx_rx_collect_connectivity_stats_info(struct sk_buff *skb,
+		void *adapter, enum connectivity_stats_pkt_status action,
+		uint8_t *pkt_type);
 
 /**
  * hdd_tx_queue_cb() - Disable/Enable the Transmit Queues

+ 2 - 0
core/hdd/src/wlan_hdd_assoc.c

@@ -1985,6 +1985,7 @@ QDF_STATUS hdd_roam_register_sta(struct hdd_adapter *adapter,
 	/* Register the vdev transmit and receive functions */
 	qdf_mem_zero(&txrx_ops, sizeof(txrx_ops));
 	txrx_ops.rx.rx = hdd_rx_packet_cbk;
+	txrx_ops.rx.stats_rx = hdd_tx_rx_collect_connectivity_stats_info;
 
 	adapter->txrx_vdev = (void *)cdp_get_vdev_from_vdev_id(soc,
 				(struct cdp_pdev *)pdev,
@@ -3826,6 +3827,7 @@ QDF_STATUS hdd_roam_register_tdlssta(struct hdd_adapter *adapter,
 		 (struct cdp_pdev *)pdev, adapter->session_id),
 		 adapter, &txrx_ops);
 	adapter->tx_fn = txrx_ops.tx.tx;
+	txrx_ops.rx.stats_rx = hdd_tx_rx_collect_connectivity_stats_info;
 
 	/* Register the Station with TL...  */
 	qdf_status = cdp_peer_register(soc,

+ 628 - 35
core/hdd/src/wlan_hdd_cfg80211.c

@@ -12409,9 +12409,266 @@ static int wlan_hdd_cfg80211_set_fast_roaming(struct wiphy *wiphy,
 	return ret;
 }
 
+/*
+ * define short names for the global vendor params
+ * used by wlan_hdd_cfg80211_setarp_stats_cmd()
+ */
+#define STATS_SET_INVALID \
+	QCA_ATTR_NUD_STATS_SET_INVALID
+#define STATS_SET_START \
+	QCA_ATTR_NUD_STATS_SET_START
+#define STATS_GW_IPV4 \
+	QCA_ATTR_NUD_STATS_GW_IPV4
+#define STATS_SET_DATA_PKT_INFO \
+		QCA_ATTR_NUD_STATS_SET_DATA_PKT_INFO
+#define STATS_SET_MAX \
+	QCA_ATTR_NUD_STATS_SET_MAX
+
+const struct nla_policy
+qca_wlan_vendor_set_nud_stats[STATS_SET_MAX + 1] = {
+	[STATS_SET_START] = {.type = NLA_FLAG },
+	[STATS_GW_IPV4] = {.type = NLA_U32 },
+	[STATS_SET_DATA_PKT_INFO] = {.type = NLA_U32 },
+};
+
+/* define short names for the global vendor params */
+#define CONNECTIVITY_STATS_SET_INVALID \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_SET_INVALID
+#define STATS_PKT_INFO_TYPE \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_STATS_PKT_INFO_TYPE
+#define STATS_DNS_DOMAIN_NAME \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_DNS_DOMAIN_NAME
+#define STATS_SRC_PORT \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_SRC_PORT
+#define STATS_DEST_PORT \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_PORT
+#define STATS_DEST_IPV4 \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_IPV4
+#define STATS_DEST_IPV6 \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_DEST_IPV6
+#define CONNECTIVITY_STATS_SET_MAX \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_SET_MAX
+
+const struct nla_policy
+qca_wlan_vendor_set_connectivity_check_stats[CONNECTIVITY_STATS_SET_MAX + 1] = {
+	[STATS_PKT_INFO_TYPE] = {.type = NLA_U32 },
+	[STATS_DNS_DOMAIN_NAME] = {.type = NLA_BINARY,
+					.len = DNS_DOMAIN_NAME_MAX_LEN },
+	[STATS_SRC_PORT] = {.type = NLA_U32 },
+	[STATS_DEST_PORT] = {.type = NLA_U32 },
+	[STATS_DEST_IPV4] = {.type = NLA_U32 },
+	[STATS_DEST_IPV6] = {.type = NLA_BINARY,
+					.len = ICMPv6_ADDR_LEN },
+};
+
+/**
+ * hdd_dns_unmake_name_query() - Convert an uncompressed DNS name to a
+ *			     NUL-terminated string
+ * @name: DNS name
+ *
+ * Return: Produce a printable version of a DNS name.
+ */
+static inline uint8_t *hdd_dns_unmake_name_query(uint8_t *name)
+{
+	uint8_t *p;
+	unsigned int len;
+
+	p = name;
+	while ((len = *p)) {
+		*(p++) = '.';
+		p += len;
+	}
+
+	return name + 1;
+}
+
+/**
+ * hdd_dns_make_name_query() - Convert a standard NUL-terminated string
+ *				to DNS name
+ * @string: Name as a NUL-terminated string
+ * @buf: Buffer in which to place DNS name
+ *
+ * DNS names consist of "<length>element" pairs.
+ *
+ * Return: Byte following constructed DNS name
+ */
+static uint8_t *hdd_dns_make_name_query(const uint8_t *string, uint8_t *buf)
+{
+	uint8_t *length_byte = buf++;
+	uint8_t c;
+
+	while ((c = *(string++))) {
+		if (c == '.') {
+			*length_byte = buf - length_byte - 1;
+			length_byte = buf;
+		}
+		*(buf++) = c;
+	}
+	*length_byte = buf - length_byte - 1;
+	*(buf++) = '\0';
+	return buf;
+}
+
+/**
+ * hdd_set_clear_connectivity_check_stats_info() - set/clear stats info
+ * @adapter: Pointer to hdd adapter
+ * @arp_stats_params: arp stats structure to be sent to FW
+ * @tb: nl attribute
+ * @is_set_stats: set/clear stats
+ *
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int hdd_set_clear_connectivity_check_stats_info(
+		struct hdd_adapter *adapter,
+		struct set_arp_stats_params *arp_stats_params,
+		struct nlattr **tb, bool is_set_stats)
+{
+	struct nlattr *tb2[CONNECTIVITY_STATS_SET_MAX + 1];
+	struct nlattr *curr_attr = NULL;
+	int err = 0;
+	uint32_t pkt_bitmap;
+	int rem;
+
+	/* Clear All Stats command has come */
+	if (!is_set_stats) {
+		arp_stats_params->pkt_type_bitmap = adapter->pkt_type_bitmap;
+		/* DNS tracking is not supported in FW. */
+		arp_stats_params->pkt_type_bitmap &=
+						~CONNECTIVITY_CHECK_SET_DNS;
+		arp_stats_params->flag = false;
+		arp_stats_params->pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE;
+		qdf_mem_zero(&adapter->hdd_stats.hdd_arp_stats,
+			     sizeof(adapter->hdd_stats.hdd_arp_stats));
+		qdf_mem_zero(&adapter->hdd_stats.hdd_dns_stats,
+			     sizeof(adapter->hdd_stats.hdd_dns_stats));
+		qdf_mem_zero(&adapter->hdd_stats.hdd_tcp_stats,
+			     sizeof(adapter->hdd_stats.hdd_tcp_stats));
+		qdf_mem_zero(&adapter->hdd_stats.hdd_icmpv4_stats,
+			     sizeof(adapter->hdd_stats.hdd_icmpv4_stats));
+		adapter->track_arp_ip = 0;
+		qdf_mem_zero(adapter->dns_payload,
+			     adapter->track_dns_domain_len);
+		adapter->track_dns_domain_len = 0;
+		adapter->track_src_port = 0;
+		adapter->track_dest_port = 0;
+		adapter->track_dest_ipv4 = 0;
+		adapter->pkt_type_bitmap = 0;
+		goto end;
+	}
+
+	/* Set NUD command for start tracking is received. */
+	nla_for_each_nested(curr_attr,
+			    tb[STATS_SET_DATA_PKT_INFO],
+			    rem) {
+
+		if (wlan_cfg80211_nla_parse(tb2,
+				CONNECTIVITY_STATS_SET_MAX,
+				nla_data(curr_attr), nla_len(curr_attr),
+				qca_wlan_vendor_set_connectivity_check_stats)) {
+			hdd_err("nla_parse failed");
+			err = -EINVAL;
+			goto end;
+		}
+
+		if (tb2[STATS_PKT_INFO_TYPE]) {
+			pkt_bitmap = nla_get_u32(tb2[STATS_PKT_INFO_TYPE]);
+			if (!pkt_bitmap) {
+				hdd_err("pkt tracking bitmap is empty");
+				err = -EINVAL;
+				goto end;
+			}
+			arp_stats_params->pkt_type_bitmap = pkt_bitmap;
+			arp_stats_params->flag = true;
+			adapter->pkt_type_bitmap |=
+					arp_stats_params->pkt_type_bitmap;
+
+			if (pkt_bitmap & CONNECTIVITY_CHECK_SET_ARP) {
+				if (!tb[STATS_GW_IPV4]) {
+					hdd_err("GW ipv4 address is not present");
+					err = -EINVAL;
+					goto end;
+				}
+				arp_stats_params->ip_addr =
+						nla_get_u32(tb[STATS_GW_IPV4]);
+				arp_stats_params->pkt_type =
+						WLAN_NUD_STATS_ARP_PKT_TYPE;
+				adapter->track_arp_ip =
+						arp_stats_params->ip_addr;
+				qdf_mem_zero(&adapter->hdd_stats.hdd_arp_stats,
+					sizeof(adapter->hdd_stats.
+					       hdd_arp_stats));
+			}
+
+			if (pkt_bitmap & CONNECTIVITY_CHECK_SET_DNS) {
+				uint8_t *domain_name;
+
+				if (!tb2[STATS_DNS_DOMAIN_NAME]) {
+					hdd_err("DNS domain id is not present");
+					err = -EINVAL;
+					goto end;
+				}
+				domain_name =
+					nla_data(tb2[STATS_DNS_DOMAIN_NAME]);
+				adapter->track_dns_domain_len =
+					nla_len(tb2[STATS_DNS_DOMAIN_NAME]);
+				hdd_dns_make_name_query(domain_name,
+						    adapter->dns_payload);
+				/* DNS tracking is not supported in FW. */
+				arp_stats_params->pkt_type_bitmap &=
+						~CONNECTIVITY_CHECK_SET_DNS;
+				qdf_mem_zero(&adapter->hdd_stats.hdd_dns_stats,
+					sizeof(adapter->hdd_stats.
+					       hdd_dns_stats));
+			}
+
+			if (pkt_bitmap & CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) {
+				if (!tb2[STATS_SRC_PORT] ||
+				    !tb2[STATS_DEST_PORT]) {
+					hdd_err("Source/Dest port is not present");
+					err = -EINVAL;
+					goto end;
+				}
+				arp_stats_params->tcp_src_port =
+					nla_get_u32(tb2[STATS_SRC_PORT]);
+				arp_stats_params->tcp_dst_port =
+					nla_get_u32(tb2[STATS_DEST_PORT]);
+				adapter->track_src_port =
+						arp_stats_params->tcp_src_port;
+				adapter->track_dest_port =
+						arp_stats_params->tcp_dst_port;
+				qdf_mem_zero(&adapter->hdd_stats.hdd_tcp_stats,
+					sizeof(adapter->hdd_stats.
+					       hdd_tcp_stats));
+			}
+
+			if (pkt_bitmap & CONNECTIVITY_CHECK_SET_ICMPV4) {
+				if (!tb2[STATS_DEST_IPV4]) {
+					hdd_err("destination ipv4 address to track ping packets is not present");
+					err = -EINVAL;
+					goto end;
+				}
+				arp_stats_params->icmp_ipv4 =
+					nla_get_u32(tb2[STATS_DEST_IPV4]);
+				adapter->track_dest_ipv4 =
+						arp_stats_params->icmp_ipv4;
+				qdf_mem_zero(&adapter->hdd_stats.hdd_tcp_stats,
+					sizeof(adapter->hdd_stats.
+							hdd_tcp_stats));
+			}
+		} else {
+			hdd_err("stats list empty");
+			err = -EINVAL;
+			goto end;
+		}
+	}
+
+end:
+	return err;
+}
 
 void hdd_update_cca_info_cb(void *context, uint32_t congestion,
-			uint32_t vdev_id)
+			    uint32_t vdev_id)
 {
 	struct hdd_context *hdd_ctx = (struct hdd_context *)context;
 	int status;
@@ -12556,25 +12813,6 @@ static int wlan_hdd_cfg80211_set_trace_level(struct wiphy *wiphy,
 	return ret;
 }
 
-/*
- * define short names for the global vendor params
- * used by wlan_hdd_cfg80211_setarp_stats_cmd()
- */
-#define STATS_SET_INVALID \
-	QCA_ATTR_NUD_STATS_SET_INVALID
-#define STATS_SET_START \
-	QCA_ATTR_NUD_STATS_SET_START
-#define STATS_GW_IPV4 \
-	QCA_ATTR_NUD_STATS_GW_IPV4
-#define STATS_SET_MAX \
-	QCA_ATTR_NUD_STATS_SET_MAX
-
-const struct nla_policy
-qca_wlan_vendor_set_nud_stats[STATS_SET_MAX + 1] = {
-	[STATS_SET_START] = {.type = NLA_FLAG },
-	[STATS_GW_IPV4] = {.type = NLA_U32 },
-};
-
 /**
  * __wlan_hdd_cfg80211_set_nud_stats() - set arp stats command to firmware
  * @wiphy: pointer to wireless wiphy structure.
@@ -12595,7 +12833,7 @@ static int __wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy,
 	struct net_device   *dev = wdev->netdev;
 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
-	struct set_arp_stats_params arp_stats_params;
+	struct set_arp_stats_params arp_stats_params = {0};
 	int err = 0;
 
 	hdd_enter();
@@ -12623,25 +12861,65 @@ static int __wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy,
 	}
 
 	if (tb[STATS_SET_START]) {
-		if (!tb[STATS_GW_IPV4]) {
-			QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
-				  "%s STATS_SET_START CMD", __func__);
-			return -EINVAL;
+		/* tracking is enabled for stats other than arp. */
+		if (tb[STATS_SET_DATA_PKT_INFO]) {
+			err = hdd_set_clear_connectivity_check_stats_info(
+						adapter,
+						&arp_stats_params, tb, true);
+			if (err)
+				return -EINVAL;
+
+			/*
+			 * if only tracking dns, then don't send
+			 * wmi command to FW.
+			 */
+			if (!arp_stats_params.pkt_type_bitmap)
+				return err;
+		} else {
+			if (!tb[STATS_GW_IPV4]) {
+				QDF_TRACE(QDF_MODULE_ID_HDD,
+					  QDF_TRACE_LEVEL_ERROR,
+					  "%s STATS_SET_START CMD", __func__);
+				return -EINVAL;
+			}
+			arp_stats_params.flag = true;
+			arp_stats_params.ip_addr =
+					nla_get_u32(tb[STATS_GW_IPV4]);
+			adapter->track_arp_ip = arp_stats_params.ip_addr;
+
+			qdf_mem_zero(&adapter->hdd_stats.hdd_arp_stats,
+			     sizeof(adapter->hdd_stats.hdd_arp_stats));
+
+			arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE;
 		}
-		arp_stats_params.flag = true;
-		arp_stats_params.ip_addr = nla_get_u32(tb[STATS_GW_IPV4]);
-		hdd_ctx->track_arp_ip = arp_stats_params.ip_addr;
 	} else {
-		arp_stats_params.flag = false;
+		/* clear tracking stats of other data types as well*/
+		if (adapter->pkt_type_bitmap) {
+			err = hdd_set_clear_connectivity_check_stats_info(
+						adapter,
+						&arp_stats_params, tb, false);
+			if (err)
+				return -EINVAL;
+
+			/*
+			 * if only tracking dns, then don't send
+			 * wmi command to FW.
+			 */
+			if (!arp_stats_params.pkt_type_bitmap)
+				return err;
+		} else {
+			arp_stats_params.flag = false;
+			qdf_mem_zero(&adapter->hdd_stats.hdd_arp_stats,
+				     sizeof(adapter->hdd_stats.hdd_arp_stats));
+			arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE;
+		}
 	}
-	if (arp_stats_params.flag)
+
+	if (arp_stats_params.flag) {
 		QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_INFO,
 			  "%s STATS_SET_START Cleared!!", __func__);
+	}
 
-	qdf_mem_zero(&adapter->hdd_stats.hdd_arp_stats,
-		     sizeof(adapter->hdd_stats.hdd_arp_stats));
-
-	arp_stats_params.pkt_type = WLAN_NUD_STATS_ARP_PKT_TYPE;
 	arp_stats_params.vdev_id = adapter->session_id;
 
 	if (QDF_STATUS_SUCCESS !=
@@ -12712,9 +12990,45 @@ static int wlan_hdd_cfg80211_set_nud_stats(struct wiphy *wiphy,
 	QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE
 #define AP_LINK_DAD \
 	QCA_ATTR_NUD_STATS_IS_DAD
+#define DATA_PKT_STATS \
+	QCA_ATTR_NUD_STATS_DATA_PKT_STATS
 #define STATS_GET_MAX \
 	QCA_ATTR_NUD_STATS_GET_MAX
 
+#define CHECK_STATS_INVALID \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_INVALID
+#define CHECK_STATS_PKT_TYPE \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_TYPE
+#define CHECK_STATS_PKT_DNS_DOMAIN_NAME \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DNS_DOMAIN_NAME
+#define CHECK_STATS_PKT_SRC_PORT \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_SRC_PORT
+#define CHECK_STATS_PKT_DEST_PORT \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_PORT
+#define CHECK_STATS_PKT_DEST_IPV4 \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_IPV4
+#define CHECK_STATS_PKT_DEST_IPV6 \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_DEST_IPV6
+#define CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV
+#define CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC
+#define CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC
+#define CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS
+#define CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC
+#define CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC
+#define CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV
+#define CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP \
+	QCA_ATTR_CONNECTIVITY_CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP
+#define CHECK_DATA_STATS_MAX \
+	QCA_ATTR_CONNECTIVITY_CHECK_DATA_STATS_MAX
+
+
 const struct nla_policy
 qca_wlan_vendor_get_nud_stats[STATS_GET_MAX + 1] = {
 	[COUNT_FROM_NETDEV] = {.type = NLA_U16 },
@@ -12727,8 +13041,278 @@ qca_wlan_vendor_get_nud_stats[STATS_GET_MAX + 1] = {
 	[RSP_COUNT_OUT_OF_ORDER_DROP] = {.type = NLA_U16 },
 	[AP_LINK_ACTIVE] = {.type = NLA_FLAG },
 	[AP_LINK_DAD] = {.type = NLA_FLAG },
+	[DATA_PKT_STATS] = {.type = NLA_U16 },
 };
 
+/**
+ * hdd_populate_dns_stats_info() - send dns stats info to network stack
+ * @adapter: pointer to adapter context
+ * @skb: pointer to skb
+ *
+ *
+ * Return: An error code or 0 on success.
+ */
+static int hdd_populate_dns_stats_info(struct hdd_adapter *adapter,
+				       struct sk_buff *skb)
+{
+	uint8_t *dns_query;
+
+	dns_query = qdf_mem_malloc(adapter->track_dns_domain_len + 1);
+	if (!dns_query) {
+		QDF_TRACE(QDF_MODULE_ID_HDD, QDF_TRACE_LEVEL_ERROR,
+			  "%s: mem alloc fail.", __func__);
+		return -EINVAL;
+	}
+
+	qdf_mem_copy(dns_query, adapter->dns_payload,
+		     adapter->track_dns_domain_len);
+
+	if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
+		CONNECTIVITY_CHECK_SET_DNS) ||
+	    nla_put(skb, CHECK_STATS_PKT_DNS_DOMAIN_NAME,
+		adapter->track_dns_domain_len,
+		hdd_dns_unmake_name_query(dns_query)) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV,
+		adapter->hdd_stats.hdd_dns_stats.tx_dns_req_count) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC,
+		adapter->hdd_stats.hdd_dns_stats.tx_host_fw_sent) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC,
+		adapter->hdd_stats.hdd_dns_stats.tx_host_fw_sent) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS,
+		adapter->hdd_stats.hdd_dns_stats.tx_ack_cnt) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC,
+		adapter->hdd_stats.hdd_dns_stats.rx_dns_rsp_count) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV,
+		adapter->hdd_stats.hdd_dns_stats.rx_delivered) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP,
+		adapter->hdd_stats.hdd_dns_stats.rx_host_drop)) {
+		hdd_err("nla put fail");
+		qdf_mem_free(dns_query);
+		kfree_skb(skb);
+		return -EINVAL;
+	}
+	qdf_mem_free(dns_query);
+	return 0;
+}
+
+/**
+ * hdd_populate_tcp_stats_info() - send tcp stats info to network stack
+ * @adapter: pointer to adapter context
+ * @skb: pointer to skb
+ * @pkt_type: tcp pkt type
+ *
+ * Return: An error code or 0 on success.
+ */
+static int hdd_populate_tcp_stats_info(struct hdd_adapter *adapter,
+				       struct sk_buff *skb,
+				       uint8_t pkt_type)
+{
+	switch (pkt_type) {
+	case CONNECTIVITY_CHECK_SET_TCP_SYN:
+		/* Fill info for tcp syn packets (tx packet) */
+		if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
+			CONNECTIVITY_CHECK_SET_TCP_SYN) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT,
+			adapter->track_src_port) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT,
+			adapter->track_dest_port) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV,
+			adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_count) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC,
+			adapter->hdd_stats.hdd_tcp_stats.
+						tx_tcp_syn_host_fw_sent) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC,
+			adapter->hdd_stats.hdd_tcp_stats.
+						tx_tcp_syn_host_fw_sent) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS,
+			adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_ack_cnt)) {
+			hdd_err("nla put fail");
+			kfree_skb(skb);
+			return -EINVAL;
+		}
+		break;
+	case CONNECTIVITY_CHECK_SET_TCP_SYN_ACK:
+		/* Fill info for tcp syn-ack packets (rx packet) */
+		if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
+			CONNECTIVITY_CHECK_SET_TCP_SYN_ACK) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT,
+			adapter->track_src_port) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT,
+			adapter->track_dest_port) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC,
+			adapter->hdd_stats.hdd_tcp_stats.rx_fw_cnt) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC,
+			adapter->hdd_stats.hdd_tcp_stats.
+							rx_tcp_syn_ack_count) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV,
+			adapter->hdd_stats.hdd_tcp_stats.rx_delivered) ||
+		    nla_put_u16(skb,
+			CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP,
+			adapter->hdd_stats.hdd_tcp_stats.rx_host_drop)) {
+			hdd_err("nla put fail");
+			kfree_skb(skb);
+			return -EINVAL;
+		}
+		break;
+	case CONNECTIVITY_CHECK_SET_TCP_ACK:
+		/* Fill info for tcp ack packets (tx packet) */
+		if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
+			CONNECTIVITY_CHECK_SET_TCP_ACK) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_SRC_PORT,
+			adapter->track_src_port) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_DEST_PORT,
+			adapter->track_dest_port) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV,
+			adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_count) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC,
+			adapter->hdd_stats.hdd_tcp_stats.
+						tx_tcp_ack_host_fw_sent) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC,
+			adapter->hdd_stats.hdd_tcp_stats.
+						tx_tcp_ack_host_fw_sent) ||
+		    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS,
+			adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_ack_cnt)) {
+			hdd_err("nla put fail");
+			kfree_skb(skb);
+			return -EINVAL;
+		}
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+/**
+ * hdd_populate_icmpv4_stats_info() - send icmpv4 stats info to network stack
+ * @adapter: pointer to adapter context
+ * @skb: pointer to skb
+ *
+ *
+ * Return: An error code or 0 on success.
+ */
+static int hdd_populate_icmpv4_stats_info(struct hdd_adapter *adapter,
+					  struct sk_buff *skb)
+{
+	if (nla_put_u16(skb, CHECK_STATS_PKT_TYPE,
+		CONNECTIVITY_CHECK_SET_ICMPV4) ||
+	    nla_put_u32(skb, CHECK_STATS_PKT_DEST_IPV4,
+		adapter->track_dest_ipv4) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_FROM_NETDEV,
+		adapter->hdd_stats.hdd_icmpv4_stats.tx_icmpv4_req_count) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TO_LOWER_MAC,
+		adapter->hdd_stats.hdd_icmpv4_stats.tx_host_fw_sent) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_REQ_RX_COUNT_BY_LOWER_MAC,
+		adapter->hdd_stats.hdd_icmpv4_stats.tx_host_fw_sent) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_REQ_COUNT_TX_SUCCESS,
+		adapter->hdd_stats.hdd_icmpv4_stats.tx_ack_cnt) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_LOWER_MAC,
+		adapter->hdd_stats.hdd_icmpv4_stats.rx_fw_cnt) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_RSP_RX_COUNT_BY_UPPER_MAC,
+		adapter->hdd_stats.hdd_icmpv4_stats.rx_icmpv4_rsp_count) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_TO_NETDEV,
+		adapter->hdd_stats.hdd_icmpv4_stats.rx_delivered) ||
+	    nla_put_u16(skb, CHECK_STATS_PKT_RSP_COUNT_OUT_OF_ORDER_DROP,
+		adapter->hdd_stats.hdd_icmpv4_stats.rx_host_drop)) {
+		hdd_err("nla put fail");
+		kfree_skb(skb);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/**
+ * hdd_populate_connectivity_check_stats_info() - send connectivity stats info
+ *						  to network stack
+ * @adapter: pointer to adapter context
+ * @skb: pointer to skb
+ *
+ *
+ * Return: An error code or 0 on success.
+ */
+
+static int hdd_populate_connectivity_check_stats_info(
+	struct hdd_adapter *adapter, struct sk_buff *skb)
+{
+	struct nlattr *connect_stats, *connect_info;
+	uint32_t count = 0;
+
+	connect_stats = nla_nest_start(skb, DATA_PKT_STATS);
+	if (connect_stats == NULL) {
+		hdd_err("nla_nest_start failed");
+		return -EINVAL;
+	}
+
+	if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_DNS) {
+		connect_info = nla_nest_start(skb, count);
+		if (connect_info == NULL) {
+			hdd_err("nla_nest_start failed count %u", count);
+			return -EINVAL;
+		}
+
+		if (hdd_populate_dns_stats_info(adapter, skb))
+			goto put_attr_fail;
+		nla_nest_end(skb, connect_info);
+		count++;
+	}
+
+	if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE) {
+		connect_info = nla_nest_start(skb, count);
+		if (connect_info == NULL) {
+			hdd_err("nla_nest_start failed count %u", count);
+			return -EINVAL;
+		}
+		if (hdd_populate_tcp_stats_info(adapter, skb,
+					CONNECTIVITY_CHECK_SET_TCP_SYN))
+			goto put_attr_fail;
+		nla_nest_end(skb, connect_info);
+		count++;
+
+		connect_info = nla_nest_start(skb, count);
+		if (connect_info == NULL) {
+			hdd_err("nla_nest_start failed count %u", count);
+			return -EINVAL;
+		}
+		if (hdd_populate_tcp_stats_info(adapter, skb,
+					CONNECTIVITY_CHECK_SET_TCP_SYN_ACK))
+			goto put_attr_fail;
+		nla_nest_end(skb, connect_info);
+		count++;
+
+		connect_info = nla_nest_start(skb, count);
+		if (connect_info == NULL) {
+			hdd_err("nla_nest_start failed count %u", count);
+			return -EINVAL;
+		}
+		if (hdd_populate_tcp_stats_info(adapter, skb,
+					CONNECTIVITY_CHECK_SET_TCP_ACK))
+			goto put_attr_fail;
+		nla_nest_end(skb, connect_info);
+		count++;
+	}
+
+	if (adapter->pkt_type_bitmap & CONNECTIVITY_CHECK_SET_ICMPV4) {
+		connect_info = nla_nest_start(skb, count);
+		if (connect_info == NULL) {
+			hdd_err("nla_nest_start failed count %u", count);
+			return -EINVAL;
+		}
+
+		if (hdd_populate_icmpv4_stats_info(adapter, skb))
+			goto put_attr_fail;
+		nla_nest_end(skb, connect_info);
+		count++;
+	}
+
+	nla_nest_end(skb, connect_stats);
+	return 0;
+
+put_attr_fail:
+	hdd_err("QCA_WLAN_VENDOR_ATTR put fail. count %u", count);
+	return -EINVAL;
+}
+
+
 /**
  * __wlan_hdd_cfg80211_get_nud_stats() - get arp stats command to firmware
  * @wiphy: pointer to wireless wiphy structure.
@@ -12753,6 +13337,7 @@ static int __wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy,
 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
 	struct get_arp_stats_params arp_stats_params;
 	void *soc = cds_get_context(QDF_MODULE_ID_SOC);
+	uint32_t pkt_type_bitmap;
 	struct sk_buff *skb;
 
 	hdd_enter();
@@ -12834,7 +13419,15 @@ static int __wlan_hdd_cfg80211_get_nud_stats(struct wiphy *wiphy,
 	if (adapter->dad)
 		nla_put_flag(skb, AP_LINK_DAD);
 
-	hdd_ctx->track_arp_ip = 0;
+	pkt_type_bitmap = adapter->pkt_type_bitmap;
+	/* ARP tracking is done above. */
+	pkt_type_bitmap &= ~CONNECTIVITY_CHECK_SET_ARP;
+
+	if (pkt_type_bitmap) {
+		if (hdd_populate_connectivity_check_stats_info(adapter, skb))
+			return -EINVAL;
+	}
+
 	cfg80211_vendor_cmd_reply(skb);
 	return err;
 }

+ 19 - 0
core/hdd/src/wlan_hdd_cfg80211.h

@@ -37,6 +37,7 @@
 #include <wlan_cfg80211_scan.h>
 #include <wlan_cfg80211.h>
 #include <wlan_cfg80211_tdls.h>
+#include <qca_vendor.h>
 
 struct hdd_context;
 
@@ -227,6 +228,24 @@ struct cfg80211_bss *
 wlan_hdd_cfg80211_update_bss_db(struct hdd_adapter *adapter,
 				struct csr_roam_info *roam_info);
 
+#define CONNECTIVITY_CHECK_SET_ARP \
+	QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_ARP
+#define CONNECTIVITY_CHECK_SET_DNS \
+	QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_DNS
+#define CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE \
+	QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_TCP_HANDSHAKE
+#define CONNECTIVITY_CHECK_SET_ICMPV4 \
+	QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_ICMPV4
+#define CONNECTIVITY_CHECK_SET_ICMPV6 \
+	QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_ICMPV6
+#define CONNECTIVITY_CHECK_SET_TCP_SYN \
+	QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_TCP_SYN
+#define CONNECTIVITY_CHECK_SET_TCP_SYN_ACK \
+	QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_TCP_SYN_ACK
+#define CONNECTIVITY_CHECK_SET_TCP_ACK \
+	QCA_WLAN_VENDOR_CONNECTIVITY_CHECK_SET_TCP_ACK
+
+
 int wlan_hdd_cfg80211_pmksa_candidate_notify(struct hdd_adapter *adapter,
 					struct csr_roam_info *roam_info,
 					int index, bool preauth);

+ 9 - 0
core/hdd/src/wlan_hdd_main.c

@@ -10763,6 +10763,15 @@ static void hdd_get_nud_stats_cb(void *data, struct rsp_stats *rsp)
 	adapter->dad |= rsp->dad_detected;
 	adapter->con_status = rsp->connect_status;
 
+	/* Flag true indicates connectivity check stats present. */
+	if (rsp->connect_stats_present) {
+		hdd_info("rsp->tcp_ack_recvd :%x", rsp->tcp_ack_recvd);
+		hdd_info("rsp->icmpv4_rsp_recvd :%x", rsp->icmpv4_rsp_recvd);
+		adapter->hdd_stats.hdd_tcp_stats.rx_fw_cnt = rsp->tcp_ack_recvd;
+		adapter->hdd_stats.hdd_icmpv4_stats.rx_fw_cnt =
+							rsp->icmpv4_rsp_recvd;
+	}
+
 	spin_lock(&hdd_context_lock);
 	context = &hdd_ctx->nud_stats_context;
 	complete(&context->response_event);

+ 1 - 0
core/hdd/src/wlan_hdd_ocb.c

@@ -266,6 +266,7 @@ static int hdd_ocb_register_sta(struct hdd_adapter *adapter)
 		(struct cdp_vdev *)cdp_get_vdev_from_vdev_id(soc,
 		(struct cdp_pdev *)pdev, adapter->session_id),
 		adapter, &txrx_ops);
+	txrx_ops.rx.stats_rx = hdd_tx_rx_collect_connectivity_stats_info;
 	adapter->tx_fn = txrx_ops.tx.tx;
 
 	qdf_status = cdp_peer_register(soc,

+ 290 - 1
core/hdd/src/wlan_hdd_tx_rx.c

@@ -63,6 +63,7 @@
 #include <cdp_txrx_misc.h>
 #include "wlan_hdd_rx_monitor.h"
 #include "wlan_hdd_power.h"
+#include "wlan_hdd_cfg80211.h"
 #include <wlan_hdd_tsf.h>
 
 #ifdef QCA_LL_TX_FLOW_CONTROL_V2
@@ -548,6 +549,263 @@ static inline bool hdd_is_tx_allowed(struct sk_buff *skb, uint8_t peer_id)
 		  FL("Invalid peer state for Tx: %d"), peer_state);
 	return false;
 }
+/**
+ * hdd_tx_rx_is_dns_domain_name_match() - function to check whether dns
+ * domain name in the received skb matches with the tracking dns domain
+ * name or not
+ *
+ * @skb: pointer to skb
+ * @adapter: pointer to adapter
+ *
+ * Returns: true if matches else false
+ */
+static bool hdd_tx_rx_is_dns_domain_name_match(struct sk_buff *skb,
+					       struct hdd_adapter *adapter)
+{
+	uint8_t *domain_name;
+
+	if (adapter->track_dns_domain_len == 0)
+		return false;
+
+	domain_name = qdf_nbuf_get_dns_domain_name(skb,
+						adapter->track_dns_domain_len);
+	if (strncmp(domain_name, adapter->dns_payload,
+		    adapter->track_dns_domain_len) == 0)
+		return true;
+	else
+		return false;
+}
+
+void hdd_tx_rx_collect_connectivity_stats_info(struct sk_buff *skb,
+			void *context,
+			enum connectivity_stats_pkt_status action,
+			uint8_t *pkt_type)
+{
+	uint32_t pkt_type_bitmap;
+	struct hdd_adapter *adapter = NULL;
+
+	adapter = (struct hdd_adapter *)context;
+	if (unlikely(adapter->magic != WLAN_HDD_ADAPTER_MAGIC)) {
+		QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_ERROR,
+			  "Magic cookie(%x) for adapter sanity verification is invalid",
+			  adapter->magic);
+		return;
+	}
+
+	/* ARP tracking is done already. */
+	pkt_type_bitmap = adapter->pkt_type_bitmap;
+	pkt_type_bitmap &= ~CONNECTIVITY_CHECK_SET_ARP;
+
+	if (!pkt_type_bitmap)
+		return;
+
+	switch (action) {
+	case PKT_TYPE_REQ:
+	case PKT_TYPE_TX_HOST_FW_SENT:
+		if (qdf_nbuf_is_icmp_pkt(skb)) {
+			if (qdf_nbuf_data_is_icmpv4_req(skb) &&
+			    (adapter->track_dest_ipv4 ==
+					qdf_nbuf_get_icmpv4_tgt_ip(skb))) {
+				*pkt_type = CONNECTIVITY_CHECK_SET_ICMPV4;
+				if (action == PKT_TYPE_REQ) {
+					++adapter->hdd_stats.hdd_icmpv4_stats.
+							tx_icmpv4_req_count;
+					QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+						  QDF_TRACE_LEVEL_INFO_HIGH,
+						  "%s : ICMPv4 Req packet",
+						  __func__);
+				} else
+					/* host receives tx completion */
+					++adapter->hdd_stats.hdd_icmpv4_stats.
+								tx_host_fw_sent;
+			}
+		} else if (qdf_nbuf_is_ipv4_tcp_pkt(skb)) {
+			if (qdf_nbuf_data_is_tcp_syn(skb) &&
+			    (adapter->track_dest_port ==
+					qdf_nbuf_data_get_tcp_dst_port(skb))) {
+				*pkt_type = CONNECTIVITY_CHECK_SET_TCP_SYN;
+				if (action == PKT_TYPE_REQ) {
+					++adapter->hdd_stats.hdd_tcp_stats.
+							tx_tcp_syn_count;
+					QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+						  QDF_TRACE_LEVEL_INFO_HIGH,
+						  "%s : TCP Syn packet",
+						  __func__);
+				} else
+					/* host receives tx completion */
+					++adapter->hdd_stats.hdd_tcp_stats.
+							tx_tcp_syn_host_fw_sent;
+			} else if ((adapter->hdd_stats.hdd_tcp_stats.
+				    is_tcp_syn_ack_rcv || adapter->hdd_stats.
+					hdd_tcp_stats.is_tcp_ack_sent) &&
+				   qdf_nbuf_data_is_tcp_ack(skb) &&
+				   (adapter->track_dest_port ==
+				    qdf_nbuf_data_get_tcp_dst_port(skb))) {
+				*pkt_type = CONNECTIVITY_CHECK_SET_TCP_ACK;
+				if (action == PKT_TYPE_REQ &&
+					adapter->hdd_stats.hdd_tcp_stats.
+							is_tcp_syn_ack_rcv) {
+					++adapter->hdd_stats.hdd_tcp_stats.
+							tx_tcp_ack_count;
+					adapter->hdd_stats.hdd_tcp_stats.
+						is_tcp_syn_ack_rcv = false;
+					adapter->hdd_stats.hdd_tcp_stats.
+						is_tcp_ack_sent = true;
+					QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+						  QDF_TRACE_LEVEL_INFO_HIGH,
+						  "%s : TCP Ack packet",
+						  __func__);
+				} else if (action == PKT_TYPE_TX_HOST_FW_SENT &&
+					adapter->hdd_stats.hdd_tcp_stats.
+							is_tcp_ack_sent) {
+				/* host receives tx completion */
+				++adapter->hdd_stats.hdd_tcp_stats.
+							tx_tcp_ack_host_fw_sent;
+				adapter->hdd_stats.hdd_tcp_stats.
+							is_tcp_ack_sent = false;
+				}
+			}
+		} else if (qdf_nbuf_is_ipv4_udp_pkt(skb)) {
+			if (qdf_nbuf_data_is_dns_query(skb) &&
+			    hdd_tx_rx_is_dns_domain_name_match(skb, adapter)) {
+				*pkt_type = CONNECTIVITY_CHECK_SET_DNS;
+				if (action == PKT_TYPE_REQ) {
+					++adapter->hdd_stats.hdd_dns_stats.
+							tx_dns_req_count;
+					QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+						  QDF_TRACE_LEVEL_INFO_HIGH,
+						  "%s : DNS query packet",
+						  __func__);
+				} else
+					/* host receives tx completion */
+					++adapter->hdd_stats.hdd_dns_stats.
+								tx_host_fw_sent;
+			}
+		}
+		break;
+
+	case PKT_TYPE_RSP:
+		if (qdf_nbuf_is_icmp_pkt(skb)) {
+			if (qdf_nbuf_data_is_icmpv4_rsp(skb) &&
+			    (adapter->track_dest_ipv4 ==
+					qdf_nbuf_get_icmpv4_src_ip(skb))) {
+				++adapter->hdd_stats.hdd_icmpv4_stats.
+							rx_icmpv4_rsp_count;
+				*pkt_type =
+				CONNECTIVITY_CHECK_SET_ICMPV4;
+				QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+					  QDF_TRACE_LEVEL_INFO_HIGH,
+					  "%s : ICMPv4 Res packet", __func__);
+			}
+		} else if (qdf_nbuf_is_ipv4_tcp_pkt(skb)) {
+			if (qdf_nbuf_data_is_tcp_syn_ack(skb) &&
+			    (adapter->track_dest_port ==
+					qdf_nbuf_data_get_tcp_src_port(skb))) {
+				++adapter->hdd_stats.hdd_tcp_stats.
+							rx_tcp_syn_ack_count;
+				adapter->hdd_stats.hdd_tcp_stats.
+					is_tcp_syn_ack_rcv = true;
+				*pkt_type =
+				CONNECTIVITY_CHECK_SET_TCP_SYN_ACK;
+				QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+					  QDF_TRACE_LEVEL_INFO_HIGH,
+					  "%s : TCP Syn ack packet", __func__);
+			}
+		} else if (qdf_nbuf_is_ipv4_udp_pkt(skb)) {
+			if (qdf_nbuf_data_is_dns_response(skb) &&
+			    hdd_tx_rx_is_dns_domain_name_match(skb, adapter)) {
+				++adapter->hdd_stats.hdd_dns_stats.
+							rx_dns_rsp_count;
+				*pkt_type = CONNECTIVITY_CHECK_SET_DNS;
+				QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+					  QDF_TRACE_LEVEL_INFO_HIGH,
+					  "%s : DNS response packet", __func__);
+			}
+		}
+		break;
+
+	case PKT_TYPE_TX_DROPPED:
+		switch (*pkt_type) {
+		case CONNECTIVITY_CHECK_SET_ICMPV4:
+			++adapter->hdd_stats.hdd_icmpv4_stats.tx_dropped;
+			QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+				  QDF_TRACE_LEVEL_INFO_HIGH,
+				  "%s : ICMPv4 Req packet dropped", __func__);
+			break;
+		case CONNECTIVITY_CHECK_SET_TCP_SYN:
+			++adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_dropped;
+			QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+				  QDF_TRACE_LEVEL_INFO_HIGH,
+				  "%s : TCP syn packet dropped", __func__);
+			break;
+		case CONNECTIVITY_CHECK_SET_TCP_ACK:
+			++adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_dropped;
+			QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+				  QDF_TRACE_LEVEL_INFO_HIGH,
+				  "%s : TCP ack packet dropped", __func__);
+			break;
+		case CONNECTIVITY_CHECK_SET_DNS:
+			++adapter->hdd_stats.hdd_dns_stats.tx_dropped;
+			QDF_TRACE(QDF_MODULE_ID_HDD_DATA,
+				  QDF_TRACE_LEVEL_INFO_HIGH,
+				  "%s : DNS query packet dropped", __func__);
+			break;
+		default:
+			break;
+		}
+		break;
+	case PKT_TYPE_RX_DELIVERED:
+		switch (*pkt_type) {
+		case CONNECTIVITY_CHECK_SET_ICMPV4:
+			++adapter->hdd_stats.hdd_icmpv4_stats.rx_delivered;
+			break;
+		case CONNECTIVITY_CHECK_SET_TCP_SYN_ACK:
+			++adapter->hdd_stats.hdd_tcp_stats.rx_delivered;
+			break;
+		case CONNECTIVITY_CHECK_SET_DNS:
+			++adapter->hdd_stats.hdd_dns_stats.rx_delivered;
+			break;
+		default:
+			break;
+		}
+		break;
+	case PKT_TYPE_RX_REFUSED:
+		switch (*pkt_type) {
+		case CONNECTIVITY_CHECK_SET_ICMPV4:
+			++adapter->hdd_stats.hdd_icmpv4_stats.rx_refused;
+			break;
+		case CONNECTIVITY_CHECK_SET_TCP_SYN_ACK:
+			++adapter->hdd_stats.hdd_tcp_stats.rx_refused;
+			break;
+		case CONNECTIVITY_CHECK_SET_DNS:
+			++adapter->hdd_stats.hdd_dns_stats.rx_refused;
+			break;
+		default:
+			break;
+		}
+		break;
+	case PKT_TYPE_TX_ACK_CNT:
+		switch (*pkt_type) {
+		case CONNECTIVITY_CHECK_SET_ICMPV4:
+			++adapter->hdd_stats.hdd_icmpv4_stats.tx_ack_cnt;
+			break;
+		case CONNECTIVITY_CHECK_SET_TCP_SYN:
+			++adapter->hdd_stats.hdd_tcp_stats.tx_tcp_syn_ack_cnt;
+			break;
+		case CONNECTIVITY_CHECK_SET_TCP_ACK:
+			++adapter->hdd_stats.hdd_tcp_stats.tx_tcp_ack_ack_cnt;
+			break;
+		case CONNECTIVITY_CHECK_SET_DNS:
+			++adapter->hdd_stats.hdd_dns_stats.tx_ack_cnt;
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+}
 
 /**
  * __hdd_hard_start_xmit() - Transmit a frame
@@ -573,6 +831,7 @@ static int __hdd_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	struct hdd_station_ctx *sta_ctx = &adapter->session.station;
 	struct qdf_mac_addr *mac_addr;
 	bool pkt_proto_logged = false;
+	uint8_t pkt_type = 0;
 #ifdef QCA_PKT_PROTO_TRACE
 	uint8_t proto_type = 0;
 #endif
@@ -607,6 +866,10 @@ static int __hdd_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 					"%s : ARP packet", __func__);
 		}
 	}
+	/* track connectivity stats */
+	if (adapter->pkt_type_bitmap)
+		hdd_tx_rx_collect_connectivity_stats_info(skb, adapter,
+						PKT_TYPE_REQ, &pkt_type);
 
 	if (cds_is_driver_recovering()) {
 		QDF_TRACE(QDF_MODULE_ID_HDD_DATA, QDF_TRACE_LEVEL_WARN,
@@ -828,6 +1091,11 @@ drop_pkt_accounting:
 			"%s : ARP packet dropped", __func__);
 	}
 
+	/* track connectivity stats */
+	if (adapter->pkt_type_bitmap)
+		hdd_tx_rx_collect_connectivity_stats_info(skb, adapter,
+						PKT_TYPE_TX_DROPPED, &pkt_type);
+
 	return NETDEV_TX_OK;
 }
 
@@ -1240,6 +1508,7 @@ QDF_STATUS hdd_rx_packet_cbk(void *context, qdf_nbuf_t rxBuf)
 	unsigned int cpu_index;
 	struct qdf_mac_addr *mac_addr;
 	bool wake_lock = false;
+	uint8_t pkt_type = 0;
 	bool proto_pkt_logged = false;
 	bool track_arp = false;
 
@@ -1292,6 +1561,10 @@ QDF_STATUS hdd_rx_packet_cbk(void *context, qdf_nbuf_t rxBuf)
 				track_arp = true;
 			}
 		}
+		/* track connectivity stats */
+		if (adapter->pkt_type_bitmap)
+			hdd_tx_rx_collect_connectivity_stats_info(skb, adapter,
+						PKT_TYPE_RSP, &pkt_type);
 
 		sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
 		if ((sta_ctx->conn_info.proxyARPService) &&
@@ -1396,19 +1669,35 @@ QDF_STATUS hdd_rx_packet_cbk(void *context, qdf_nbuf_t rxBuf)
 				if (track_arp)
 					++adapter->hdd_stats.hdd_arp_stats.
 						rx_delivered;
+				/* track connectivity stats */
+				if (adapter->pkt_type_bitmap)
+					hdd_tx_rx_collect_connectivity_stats_info(
+						skb, adapter,
+						PKT_TYPE_RX_DELIVERED,
+						&pkt_type);
 			} else {
 				++adapter->hdd_stats.tx_rx_stats.
 					 rx_refused[cpu_index];
 				if (track_arp)
 					++adapter->hdd_stats.hdd_arp_stats.
 						rx_refused;
+				/* track connectivity stats */
+				if (adapter->pkt_type_bitmap)
+					hdd_tx_rx_collect_connectivity_stats_info(
+						skb, adapter,
+						PKT_TYPE_RX_REFUSED, &pkt_type);
 			}
 		} else {
 			++adapter->hdd_stats.tx_rx_stats.
 						rx_delivered[cpu_index];
 			if (track_arp)
 				++adapter->hdd_stats.hdd_arp_stats.
-						rx_delivered;
+							rx_delivered;
+			/* track connectivity stats */
+			if (adapter->pkt_type_bitmap)
+				hdd_tx_rx_collect_connectivity_stats_info(
+					skb, adapter,
+					PKT_TYPE_RX_DELIVERED, &pkt_type);
 		}
 	}