qcacld-3.0: Add debugfs for MIB statistics

Define debugfs node in driver to
export MIB counters to user app.

Define macro WLAN_FEATURE_MIB_STATS for this
feature that gets MIB statistics from fw.

Change-Id: Icae8826309094d17e8f6d4503f617a3a7116d3c9
CRs-Fixed: 2548241
diff --git a/Kbuild b/Kbuild
index 6a6ef37..5584c0c 100644
--- a/Kbuild
+++ b/Kbuild
@@ -98,6 +98,9 @@
 ifeq ($(CONFIG_WLAN_FEATURE_LINK_LAYER_STATS), y)
 HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_llstat.o
 endif
+ifeq ($(CONFIG_WLAN_FEATURE_MIB_STATS), y)
+HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_mibstat.o
+endif
 HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_csr.o
 HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_connect.o
 HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_debugfs_offload.o
@@ -2118,6 +2121,7 @@
 cppflags-$(CONFIG_QCA_SUPPORT_TX_THROTTLE) += -DQCA_SUPPORT_TX_THROTTLE
 cppflags-$(CONFIG_WMI_INTERFACE_EVENT_LOGGING) += -DWMI_INTERFACE_EVENT_LOGGING
 cppflags-$(CONFIG_WLAN_FEATURE_LINK_LAYER_STATS) += -DWLAN_FEATURE_LINK_LAYER_STATS
+cppflags-$(CONFIG_WLAN_FEATURE_MIB_STATS) += -DWLAN_FEATURE_MIB_STATS
 cppflags-$(CONFIG_FEATURE_WLAN_EXTSCAN) += -DFEATURE_WLAN_EXTSCAN
 cppflags-$(CONFIG_160MHZ_SUPPORT) += -DCONFIG_160MHZ_SUPPORT
 cppflags-$(CONFIG_MCL) += -DCONFIG_MCL
diff --git a/configs/default_defconfig b/configs/default_defconfig
index 4cbadc8..d556dee 100644
--- a/configs/default_defconfig
+++ b/configs/default_defconfig
@@ -816,6 +816,7 @@
 CONFIG_QCA_SUPPORT_TX_THROTTLE := y
 CONFIG_WMI_INTERFACE_EVENT_LOGGING := y
 CONFIG_WLAN_FEATURE_LINK_LAYER_STATS := y
+CONFIG_WLAN_FEATURE_MIB_STATS := y
 CONFIG_FEATURE_WLAN_EXTSCAN := n
 CONFIG_WMI_BCN_OFFLOAD := y
 CONFIG_160MHZ_SUPPORT := y
diff --git a/core/hdd/inc/wlan_hdd_debugfs_mibstat.h b/core/hdd/inc/wlan_hdd_debugfs_mibstat.h
new file mode 100644
index 0000000..e42236c
--- /dev/null
+++ b/core/hdd/inc/wlan_hdd_debugfs_mibstat.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_hdd_debugfs_mibstat.h
+ *
+ * WLAN Host Device Driver implementation to update
+ * debugfs with MIB statistics
+ */
+
+#ifndef _WLAN_HDD_DEBUGFS_MIBSTAT_H
+#define _WLAN_HDD_DEBUGFS_MIBSTAT_H
+
+#define DEBUGFS_MIBSTATS_BUF_SIZE 4096
+
+#include <wlan_hdd_main.h>
+
+#if defined(WLAN_FEATURE_MIB_STATS) && defined(WLAN_DEBUGFS)
+/**
+ * hdd_debugfs_process_mib_stats() - Process mib stats from fw
+ *
+ * This function is used to store mib stats to global variable mib_stats.
+ *
+ * Return: None
+ */
+void hdd_debugfs_process_mib_stats(struct hdd_adapter *adapter,
+				   struct stats_event *stats);
+
+/**
+ * wlan_hdd_create_mib_stats_file() - API to create MIB stats file
+ * @adapter: interface adapter pointer
+ *
+ * Return: 0 on success and errno on failure
+ */
+int wlan_hdd_create_mib_stats_file(struct hdd_adapter *adapter);
+
+/**
+ * wlan_hdd_destroy_mib_stats_lock() - API to destroy MIB stats lock
+ *
+ * Return: No return
+ */
+void wlan_hdd_destroy_mib_stats_lock(void);
+#else
+static inline int wlan_hdd_create_mib_stats_file(struct hdd_adapter *adapter)
+{
+	return 0;
+}
+
+static inline void wlan_hdd_destroy_mib_stats_lock(void)
+{
+}
+#endif
+#endif /* #ifndef _WLAN_HDD_DEBUGFS_MIBSTAT_H */
diff --git a/core/hdd/src/wlan_hdd_debugfs.c b/core/hdd/src/wlan_hdd_debugfs.c
index 05e6e16..41d5ede 100644
--- a/core/hdd/src/wlan_hdd_debugfs.c
+++ b/core/hdd/src/wlan_hdd_debugfs.c
@@ -33,6 +33,7 @@
 #include <wlan_hdd_wowl.h>
 #include <cds_sched.h>
 #include <wlan_hdd_debugfs_llstat.h>
+#include <wlan_hdd_debugfs_mibstat.h>
 
 #define MAX_USER_COMMAND_SIZE_WOWL_ENABLE 8
 #define MAX_USER_COMMAND_SIZE_WOWL_PATTERN 512
@@ -527,6 +528,9 @@
 					&fops_patterngen))
 		return QDF_STATUS_E_FAILURE;
 
+	if (wlan_hdd_create_mib_stats_file(adapter))
+		return QDF_STATUS_E_FAILURE;
+
 	if (wlan_hdd_create_ll_stats_file(adapter))
 		return QDF_STATUS_E_FAILURE;
 
@@ -544,5 +548,6 @@
 void hdd_debugfs_exit(struct hdd_adapter *adapter)
 {
 	debugfs_remove_recursive(adapter->debugfs_phy);
+	wlan_hdd_destroy_mib_stats_lock();
 }
 #endif /* #ifdef WLAN_OPEN_SOURCE */
diff --git a/core/hdd/src/wlan_hdd_debugfs_mibstat.c b/core/hdd/src/wlan_hdd_debugfs_mibstat.c
new file mode 100644
index 0000000..de8169a
--- /dev/null
+++ b/core/hdd/src/wlan_hdd_debugfs_mibstat.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for
+ * any purpose with or without fee is hereby granted, provided that the
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_hdd_debugfs_mibstat.c
+ *
+ * WLAN Host Device Driver implementation to update
+ * debugfs with MIB statistics
+ */
+
+#include <cds_sched.h>
+#include "osif_sync.h"
+#include <wlan_hdd_debugfs_mibstat.h>
+#include <wlan_hdd_stats.h>
+#include <wma_api.h>
+
+struct mib_stats_buf {
+	ssize_t len;
+	uint8_t *result;
+};
+
+static struct mib_stats_buf mib_stats;
+
+qdf_mutex_t mibstats_lock;
+
+void hdd_debugfs_process_mib_stats(struct hdd_adapter *adapter,
+				   struct stats_event *stats)
+{
+	ssize_t len = 0;
+	uint8_t *buffer;
+
+	hdd_enter();
+
+	qdf_mutex_acquire(&mibstats_lock);
+	if (!mib_stats.result) {
+		qdf_mutex_release(&mibstats_lock);
+		hdd_err("MIB statistics buffer is NULL");
+		return;
+	}
+
+	buffer = mib_stats.result;
+	buffer += mib_stats.len;
+
+	len = scnprintf(buffer, DEBUGFS_MIBSTATS_BUF_SIZE - mib_stats.len,
+			"dot11RTSSuccessCount %d "
+			"\ndot11RTSFailureCount %d "
+			"\ndot11QosFailedCount %d "
+			"\ndot11QosRetryCount %d "
+			"\ndot11QosTransmittedFrameCount %d "
+			"\ndot11QosMPDUsReceivedCount %d "
+			"\ndot11TransmittedAMPDUCount %d "
+			"\ndot11QosACKFailureCount %d",
+			stats->mib_stats->mib_mac_statistics.rts_success_cnt,
+			stats->mib_stats->mib_mac_statistics.rts_fail_cnt,
+			stats->mib_stats->mib_qos_counters.qos_failed_cnt,
+			stats->mib_stats->mib_qos_counters.qos_retry_cnt,
+			stats->mib_stats->mib_qos_counters.qos_tx_frame_cnt,
+			stats->mib_stats->mib_qos_counters.qos_mpdu_rx_cnt,
+			stats->mib_stats->mib_counters_group3.tx_ampdu_cnt,
+			stats->mib_stats->
+				mib_qos_counters.tx_qos_ack_fail_cnt_up
+			);
+
+	buffer += len;
+	mib_stats.len += len;
+	qdf_mutex_release(&mibstats_lock);
+
+	hdd_exit();
+}
+
+static inline void wlan_hdd_mibstats_free_buf(void)
+{
+	qdf_mutex_acquire(&mibstats_lock);
+	qdf_mem_free(mib_stats.result);
+	mib_stats.result = NULL;
+	mib_stats.len =  0;
+	qdf_mutex_release(&mibstats_lock);
+}
+
+static int wlan_hdd_mibstats_alloc_buf(void)
+{
+	qdf_mutex_acquire(&mibstats_lock);
+	if (mib_stats.result) {
+		qdf_mutex_release(&mibstats_lock);
+		hdd_err("Buffer is already allocated");
+		return 0;
+	}
+	mib_stats.len = 0;
+	mib_stats.result = qdf_mem_malloc(DEBUGFS_MIBSTATS_BUF_SIZE);
+	if (!mib_stats.result) {
+		qdf_mutex_release(&mibstats_lock);
+		return -EINVAL;
+	}
+	qdf_mutex_release(&mibstats_lock);
+	return 0;
+}
+
+/**
+ * hdd_debugfs_mib_stats_update() - Update userspace with local stats buffer
+ * @buf: userspace buffer (to which data is being copied into)
+ * @count: max data that can be copied into buf in bytes
+ * @pos: offset (where data should be copied into)
+ *
+ * This function copies mib statistics buffer into debugfs
+ * entry.
+ *
+ * Return: number of characters copied; 0 on no-copy
+ */
+static ssize_t hdd_debugfs_mib_stats_update(char __user *buf,
+					    size_t count, loff_t *pos)
+{
+	ssize_t ret_cnt;
+
+	hdd_enter();
+	qdf_mutex_acquire(&mibstats_lock);
+	if (!mib_stats.result) {
+		qdf_mutex_release(&mibstats_lock);
+		hdd_err("Trying to read from NULL buffer");
+		return 0;
+	}
+
+	ret_cnt = simple_read_from_buffer(buf, count, pos,
+					  mib_stats.result,
+					  mib_stats.len);
+	qdf_mutex_release(&mibstats_lock);
+	hdd_debug("mib stats read req: count: %zu, pos: %lld", count, *pos);
+
+	hdd_exit();
+	return ret_cnt;
+}
+
+/**
+ * __wlan_hdd_release_mib_stats_debugfs() - Function to free private
+ *                                          memory on release
+ * @net_dev: net_device context used to register the debugfs file
+ *
+ * Return: Errno
+ */
+static int __wlan_hdd_release_mib_stats_debugfs(struct net_device *net_dev)
+
+{
+	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev);
+	struct hdd_context *hdd_ctx;
+	int errno;
+
+	hdd_enter_dev(net_dev);
+
+	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	errno = wlan_hdd_validate_context(hdd_ctx);
+	if (errno)
+		return errno;
+
+	wlan_hdd_mibstats_free_buf();
+
+	hdd_exit();
+
+	return 0;
+}
+
+/**
+ * wlan_hdd_release_mib_stats_debugfs() - SSR wrapper function to free
+ *                                        private memory on release
+ * @inode: Pointer to inode structure
+ * @file: file pointer
+ *
+ * Return: Errno
+ */
+static int wlan_hdd_release_mib_stats_debugfs(struct inode *inode,
+					      struct file *file)
+{
+	struct net_device *net_dev = file_inode(file)->i_private;
+	struct osif_vdev_sync *vdev_sync;
+	int errno;
+
+	errno = osif_vdev_sync_op_start(net_dev, &vdev_sync);
+	if (errno)
+		return errno;
+
+	errno = __wlan_hdd_release_mib_stats_debugfs(net_dev);
+
+	osif_vdev_sync_op_stop(vdev_sync);
+
+	return errno;
+}
+
+/**
+ * __wlan_hdd_read_mib_stats_debugfs() - mib_stats debugfs handler
+ * @net_dev: net_device context used to register the debugfs file
+ * @buf: text being written to the debugfs
+ * @count: size of @buf
+ * @pos: (unused) offset into the virtual file system
+ *
+ * Return: Number of bytes read on success, error number otherwise
+ */
+static ssize_t __wlan_hdd_read_mib_stats_debugfs(struct net_device *net_dev,
+						 char __user *buf,
+						 size_t count, loff_t *pos)
+
+{
+	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev);
+	struct hdd_context *hdd_ctx;
+	ssize_t ret;
+
+	hdd_enter_dev(net_dev);
+
+	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (ret)
+		goto free_buf;
+
+	if (*pos == 0) {
+		ret = wlan_hdd_get_mib_stats(adapter);
+		if (ret)
+			goto free_buf;
+	}
+
+	/* All the events are received and buffer is populated */
+	ret = hdd_debugfs_mib_stats_update(buf, count, pos);
+	hdd_debug("%zu characters written into debugfs", ret);
+
+	hdd_exit();
+
+	return ret;
+
+free_buf:
+	wlan_hdd_mibstats_free_buf();
+
+	hdd_exit();
+
+	return ret;
+}
+
+/**
+ * wlan_hdd_read_mib_stats_debugfs() - SSR wrapper function to read
+ *                                     mib stats
+ * @file: file pointer
+ * @buf: buffer
+ * @count: count
+ * @pos: position pointer
+ *
+ * Return: Number of bytes read on success, error number otherwise
+ */
+static ssize_t wlan_hdd_read_mib_stats_debugfs(struct file *file,
+				   char __user *buf, size_t count,
+				   loff_t *pos)
+{
+	struct net_device *net_dev = file_inode(file)->i_private;
+	struct osif_vdev_sync *vdev_sync;
+	ssize_t err_size;
+
+	err_size = osif_vdev_sync_op_start(net_dev, &vdev_sync);
+	if (err_size)
+		return err_size;
+
+	err_size = __wlan_hdd_read_mib_stats_debugfs(net_dev, buf,
+						     count, pos);
+
+	osif_vdev_sync_op_stop(vdev_sync);
+
+	return err_size;
+}
+
+/**
+ * __wlan_hdd_open_mib_stats_debugfs() - Function to save private on open
+ * @net_dev: net_device context used to register the debugfs file
+ *
+ * Return: Errno
+ */
+static int __wlan_hdd_open_mib_stats_debugfs(struct net_device *net_dev)
+{
+	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(net_dev);
+	struct hdd_context *hdd_ctx;
+	int errno;
+
+	hdd_enter_dev(net_dev);
+
+	errno = hdd_validate_adapter(adapter);
+	if (errno)
+		return errno;
+
+	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	errno = wlan_hdd_validate_context(hdd_ctx);
+	if (errno)
+		return errno;
+
+	errno = wlan_hdd_mibstats_alloc_buf();
+	if (errno)
+		return errno;
+
+	hdd_exit();
+
+	return 0;
+}
+
+/**
+ * wlan_hdd_open_mib_stats_debugfs() - SSR wrapper to save private
+ *                                     on open
+ * @inode: Pointer to inode structure
+ * @file: file pointer
+ *
+ * Return: Errno
+ */
+static int wlan_hdd_open_mib_stats_debugfs(struct inode *inode,
+			       struct file *file)
+{
+	struct net_device *net_dev = inode->i_private;
+	struct osif_vdev_sync *vdev_sync;
+	int errno;
+
+	errno = osif_vdev_sync_op_start(net_dev, &vdev_sync);
+	if (errno)
+		return errno;
+
+	errno = __wlan_hdd_open_mib_stats_debugfs(net_dev);
+
+	osif_vdev_sync_op_stop(vdev_sync);
+
+	return errno;
+}
+
+static const struct file_operations fops_mib_stats = {
+	.read = wlan_hdd_read_mib_stats_debugfs,
+	.open = wlan_hdd_open_mib_stats_debugfs,
+	.release = wlan_hdd_release_mib_stats_debugfs,
+	.owner = THIS_MODULE,
+	.llseek = default_llseek,
+};
+
+int wlan_hdd_create_mib_stats_file(struct hdd_adapter *adapter)
+{
+	if (!debugfs_create_file("mib_stats", 0444, adapter->debugfs_phy,
+				 adapter->dev, &fops_mib_stats))
+		return -EINVAL;
+
+	if (QDF_IS_STATUS_ERROR(qdf_mutex_create(
+				&mibstats_lock))) {
+		hdd_err("mibstats lock init failed!");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	return 0;
+}
+
+void wlan_hdd_destroy_mib_stats_lock(void)
+{
+	qdf_mutex_destroy(&mibstats_lock);
+}
diff --git a/core/hdd/src/wlan_hdd_stats.c b/core/hdd/src/wlan_hdd_stats.c
index b19bf23..adcd9de 100644
--- a/core/hdd/src/wlan_hdd_stats.c
+++ b/core/hdd/src/wlan_hdd_stats.c
@@ -35,6 +35,7 @@
 #include "wlan_hdd_hostapd.h"
 #include "wlan_osif_request_manager.h"
 #include "wlan_hdd_debugfs_llstat.h"
+#include "wlan_hdd_debugfs_mibstat.h"
 #include "wlan_reg_services_api.h"
 #include <wlan_cfg80211_mc_cp_stats.h>
 #include "wlan_cp_stats_mc_ucfg_api.h"
@@ -5266,6 +5267,32 @@
 	return status;
 }
 
+#ifdef WLAN_FEATURE_MIB_STATS
+QDF_STATUS wlan_hdd_get_mib_stats(struct hdd_adapter *adapter)
+{
+	int ret = 0;
+	struct stats_event *stats;
+
+	if (!adapter) {
+		hdd_err("Invalid context, adapter");
+		return QDF_STATUS_E_FAULT;
+	}
+
+	stats = wlan_cfg80211_mc_cp_stats_get_mib_stats(
+			adapter->vdev,
+			&ret);
+	if (ret || !stats) {
+		wlan_cfg80211_mc_cp_stats_free_stats_event(stats);
+		return ret;
+	}
+
+	hdd_debugfs_process_mib_stats(adapter, stats);
+
+	wlan_cfg80211_mc_cp_stats_free_stats_event(stats);
+	return ret;
+}
+#endif
+
 QDF_STATUS wlan_hdd_get_rssi(struct hdd_adapter *adapter, int8_t *rssi_value)
 {
 	int ret = 0, i;
diff --git a/core/hdd/src/wlan_hdd_stats.h b/core/hdd/src/wlan_hdd_stats.h
index a6272db..88a9b58 100644
--- a/core/hdd/src/wlan_hdd_stats.h
+++ b/core/hdd/src/wlan_hdd_stats.h
@@ -352,6 +352,16 @@
 		      int32_t *rcpi_value,
 		      enum rcpi_measurement_type measurement_type);
 
+#ifdef WLAN_FEATURE_MIB_STATS
+/**
+ * wlan_hdd_get_mib_stats() - Get the mib statistics
+ * @adapter: adapter upon which the measurement is requested
+ *
+ * Return: QDF_STATUS_SUCCESS on success, QDF_STATUS_E_** on error
+ */
+QDF_STATUS wlan_hdd_get_mib_stats(struct hdd_adapter *adapter);
+#endif
+
 /**
  * wlan_hdd_get_rssi() - Get the current RSSI
  * @adapter: adapter upon which the measurement is requested