qcacld-3.0: Add NAPI statistics to dumpstats

Currently NAPI stats are retrieved as a part of iwpriv getStats command.
The buffer available for this command is limited and NAPI stats get
truncated.

Add a new dumpStats parameter (9) to dump NAPI stats.
The following command will dump NAPI Stats into kmsg.
>iwpriv wlan0 dumpStats 9

Cleanup existing getStats

Change-Id: I0d804295d96a495b4afa576c8258f08c9c2b9bbb
CRs-Fixed: 1076563
diff --git a/core/dp/txrx/ol_txrx.c b/core/dp/txrx/ol_txrx.c
index 5e6336b..5a4d55a 100644
--- a/core/dp/txrx/ol_txrx.c
+++ b/core/dp/txrx/ol_txrx.c
@@ -3919,7 +3919,7 @@
 	}
 
 	len = scnprintf(buffer, buf_len,
-			"\nTXRX stats:\n\nllQueue State : %s\n pause %u unpause %u\n overflow %u\n llQueue timer state : %s\n",
+			"\n\nTXRX stats:\nllQueue State : %s\npause %u unpause %u\noverflow %u\nllQueue timer state : %s",
 			((vdev->ll_pause.is_q_paused == false) ?
 			 "UNPAUSED" : "PAUSED"),
 			vdev->ll_pause.q_pause_cnt,
@@ -4383,44 +4383,21 @@
 #endif /* IPA_UC_OFFLOAD */
 
 /**
- * ol_txrx_display_stats_help() - print statistics help
+ * ol_txrx_display_stats() - Display OL TXRX display stats
+ * @value: Module id for which stats needs to be displayed
  *
- * Return: none
+ * Return: None
  */
-static void ol_txrx_display_stats_help(void)
-{
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"iwpriv wlan0 dumpStats [option] - dump statistics");
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"iwpriv wlan0 clearStats [option] - clear statistics");
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"options:");
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"  1 -- TXRX Layer statistics");
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"  2 -- Bandwidth compute timer stats");
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"  3 -- TSO statistics");
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"  4 -- Network queue statistics");
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"  5 -- Flow control statistics");
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"  6 -- Per Layer statistics");
-	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-				"  7 -- Copy engine interrupt statistics");
-
-}
-
-static void ol_txrx_display_stats(uint16_t value)
+static QDF_STATUS ol_txrx_display_stats(uint16_t value)
 {
 	ol_txrx_pdev_handle pdev;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
 
 	pdev = cds_get_context(QDF_MODULE_ID_TXRX);
 	if (!pdev) {
 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
 			  "%s: pdev is NULL", __func__);
-		return;
+		return QDF_STATUS_E_NULL_VALUE;
 	}
 
 	switch (value) {
@@ -4457,14 +4434,22 @@
 #endif
 #endif
 	default:
-		ol_txrx_display_stats_help();
+		status = QDF_STATUS_E_INVAL;
 		break;
 	}
+	return status;
 }
 
+/**
+ * ol_txrx_clear_stats() - Clear OL TXRX stats
+ * @value: Module id for which stats needs to be cleared
+ *
+ * Return: None
+ */
 static void ol_txrx_clear_stats(uint16_t value)
 {
 	ol_txrx_pdev_handle pdev;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
 
 	pdev = cds_get_context(QDF_MODULE_ID_TXRX);
 	if (!pdev) {
@@ -4500,9 +4485,11 @@
 		break;
 #endif
 	default:
-		ol_txrx_display_stats_help();
+		status = QDF_STATUS_E_INVAL;
 		break;
 	}
+
+	return;
 }
 
 /**
diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h
index ecbe851..b9cf5ee 100644
--- a/core/hdd/inc/wlan_hdd_main.h
+++ b/core/hdd/inc/wlan_hdd_main.h
@@ -322,7 +322,7 @@
 #define WLAN_SAP_HDD_TX_FLOW_CONTROL_OS_Q_BLOCK_TIME 100
 #define WLAN_HDD_TX_FLOW_CONTROL_MAX_24BAND_CH   14
 
-#define NUM_TX_RX_HISTOGRAM 1024
+#define NUM_TX_RX_HISTOGRAM 128
 #define NUM_TX_RX_HISTOGRAM_MASK (NUM_TX_RX_HISTOGRAM - 1)
 
 /**
@@ -1984,7 +1984,7 @@
 void wlan_hdd_txrx_pause_cb(uint8_t vdev_id,
 	enum netif_action_type action, enum netif_reason_type reason);
 
-void hdd_wlan_dump_stats(hdd_adapter_t *adapter, int value);
+int hdd_wlan_dump_stats(hdd_adapter_t *adapter, int value);
 void wlan_hdd_deinit_tx_rx_histogram(hdd_context_t *hdd_ctx);
 void wlan_hdd_display_tx_rx_histogram(hdd_context_t *pHddCtx);
 void wlan_hdd_clear_tx_rx_histogram(hdd_context_t *pHddCtx);
diff --git a/core/hdd/inc/wlan_hdd_napi.h b/core/hdd/inc/wlan_hdd_napi.h
index 81f9734..67f4b06 100644
--- a/core/hdd/inc/wlan_hdd_napi.h
+++ b/core/hdd/inc/wlan_hdd_napi.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -48,11 +48,7 @@
 int hdd_napi_enabled(int id);
 int hdd_napi_create(void);
 int hdd_napi_destroy(int force);
-
-int hdd_napi_stats(char   *buf,
-		   int     max,
-		   char   *indp,
-		   struct qca_napi_data *napid);
+int hdd_display_napi_stats(void);
 
 /* the following triggers napi_enable/disable as required */
 int hdd_napi_event(enum qca_napi_event event, void *data);
@@ -91,8 +87,7 @@
 static inline int hdd_napi_enabled(int id) { return 0; }
 static inline int hdd_napi_create(void) { return 0; }
 static inline int hdd_napi_destroy(int force) { return 0; }
-static inline int hdd_napi_stats(char *buf, int max, char *indp,
-				 struct qca_napi_data *napid) { return 0; }
+static inline int hdd_display_napi_stats(void) { return 0; }
 static inline int hdd_napi_event(enum qca_napi_event event, void *data)
 {
 	return 0;
diff --git a/core/hdd/inc/wlan_hdd_wext.h b/core/hdd/inc/wlan_hdd_wext.h
index 80f1f37..b9b0796 100644
--- a/core/hdd/inc/wlan_hdd_wext.h
+++ b/core/hdd/inc/wlan_hdd_wext.h
@@ -301,6 +301,7 @@
 extern int hdd_unregister_wext(struct net_device *dev);
 extern int hdd_register_wext(struct net_device *dev);
 extern int hdd_wlan_get_freq(uint32_t chan, uint32_t *freq);
+extern void hdd_display_stats_help(void);
 extern void hdd_wlan_get_version(hdd_context_t *hdd_ctx,
 				 union iwreq_data *wrqu, char *extra);
 
diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c
index 2d6bb6f..5007fcb 100644
--- a/core/hdd/src/wlan_hdd_hostapd.c
+++ b/core/hdd/src/wlan_hdd_hostapd.c
@@ -3297,7 +3297,7 @@
 	case QCASAP_DUMP_STATS:
 	{
 		hdd_notice("QCASAP_DUMP_STATS val %d", set_value);
-		hdd_wlan_dump_stats(pHostapdAdapter, set_value);
+		ret = hdd_wlan_dump_stats(pHostapdAdapter, set_value);
 		break;
 	}
 	case QCASAP_CLEAR_STATS:
diff --git a/core/hdd/src/wlan_hdd_ioctl.c b/core/hdd/src/wlan_hdd_ioctl.c
index 4801fab..ec8d5b7 100644
--- a/core/hdd/src/wlan_hdd_ioctl.c
+++ b/core/hdd/src/wlan_hdd_ioctl.c
@@ -6102,303 +6102,6 @@
 	return ret;
 }
 
-#ifdef FEATURE_NAPI
-/**
- * hdd_parse_napi() - helper functions to drv_cmd_napi
- * @str : source string to parse
- * @cmd : pointer to cmd part after parsing
- * @sub : pointer to subcmd part after parsing
- * @aux : pointer to optional aux part after parsing
- *
- * Example:
- * NAPI SCALE <n>  +-- IN  str
- *  |    |     +------ OUT aux
- *  |    +------------ OUT subcmd
- *  +----------------- OUT cmd
- *
- * Return: ==0: success; !=0: failure
- */
-static int hdd_parse_napi(char **str, char **cmd, char **sub, char **aux)
-{
-	int rc;
-	char *token, *lcmd = NULL, *lsub = NULL, *laux = NULL;
-
-	NAPI_DEBUG("-->\n");
-
-	token = strsep(str, " \t");
-	if (NULL == token) {
-		hdd_err("cannot parse cmd");
-		goto parse_end;
-	}
-	lcmd = token;
-
-	token = strsep(str, " \t");
-	if (NULL == token) {
-		hdd_err("cannot parse subcmd");
-		goto parse_end;
-	}
-	lsub = token;
-
-	token = strsep(str, " \t");
-	if (NULL == token)
-		hdd_warn("cannot parse aux\n");
-	else
-		laux = token;
-
-parse_end:
-	if ((NULL == lcmd) || (NULL == lsub))
-		rc = -EINVAL;
-	else {
-		rc = 0;
-		*cmd = lcmd;
-		*sub = lsub;
-		if (NULL != aux)
-			*aux = laux;
-	}
-	NAPI_DEBUG("<--[rc=%d]\n", rc);
-	return rc;
-}
-
-
-/**
- * hdd_parse_stats() - print NAPI stats into a buffer
- * @buf : buffer to write stats into
- * @max : "size of buffer"
- * @idp : NULL: all stats, otherwise, ptr to the NAPI instance
- * @napid: binary structure to retrieve the stats from
- *
- * Return: number of bytes written into the buffer
- */
-int hdd_napi_stats(char   *buf,
-		   int     max,
-		   char   *indp,
-		   struct qca_napi_data *napid)
-{
-	int n = 0;
-	int i, j, k; /* NAPI, CPU, bucket indices */
-	int from, to;
-	struct qca_napi_info *napii;
-	struct qca_napi_stat *napis;
-
-	NAPI_DEBUG("-->\n");
-
-	if (NULL == napid)
-		return n;
-	if (NULL == indp) {
-		from = 0;
-		to = CE_COUNT_MAX;
-	} else {
-		if (0 > kstrtoint(indp, 10, &to)) {
-			from = 0;
-			to = CE_COUNT_MAX;
-		} else
-			from = to;
-	}
-
-	for (i = from; i < to; i++)
-		if (napid->ce_map & (0x01 << i)) {
-			napii = &(napid->napis[i]);
-			for (j = 0; j < NR_CPUS; j++) {
-				napis = &(napii->stats[j]);
-				n += scnprintf(buf + n, max - n,
-					       "STATS: NAPI[%d] CPU: %d scheds: %d polls: %d completes: %d done: %d ",
-					       i, j,
-					       napis->napi_schedules,
-					       napis->napi_polls,
-					       napis->napi_completes,
-					       napis->napi_workdone);
-
-				for (k = 0; k < QCA_NAPI_NUM_BUCKETS; k++) {
-					n += scnprintf(
-						buf + n, max - n,
-						" %d",
-						napis->napi_budget_uses[k]);
-				}
-				n += scnprintf(buf+n, max - n, "\n");
-			}
-		}
-
-	NAPI_DEBUG("<--[n=%d]\n", n);
-	return n;
-}
-
-/**
- * napi_set_scale() - sets the scale attribute in all NAPI entries
- * @sc : scale to set
- *
- * Return: void
- */
-static void napi_set_scale(uint8_t sc)
-{
-	uint32_t  i;
-	struct qca_napi_data *napi_data;
-
-	napi_data = hdd_napi_get_all();
-	if (likely(NULL != napi_data))
-	    for (i = 0; i < CE_COUNT_MAX; i++)
-		    if (napi_data->ce_map & (0x01 << i))
-			    napi_data->napis[i].scale = sc;
-
-	return;
-}
-/**
- * drv_cmd_napi() - processes NAPI commands
- * @adapter    : net_device
- * @hdd_ctx    : HDD context
- * @command    : command string from user command (including "NAPI")
- * @command_len: length of command
- * @priv_data  : ifr_data
- *
- * Commands supported:
- * NAPI ENABLE      : enables NAPI administratively. Note that this may not
- *                    enable NAPI functionally, as some other conditions
- *                    may not have been satisfied yet
- * NAPI DISABLE     : reverse operation of "enable"
- * NAPI STATUS      : get global status of NAPI instances
- * NAPI STATS [<n>] : get the stats for a given NAPI instance
- * NAPI SCALE <n>   : set the scale factor
- *
- * Return: 0: success; !0: failure
- */
-static int drv_cmd_napi(hdd_adapter_t *adapter,
-			hdd_context_t *hdd_ctx,
-			uint8_t *command,
-			uint8_t command_len,
-			hdd_priv_data_t *priv_data)
-{
-	int  rc = 0;
-	int  n, l;
-	char *cmd = NULL, *subcmd = NULL, *aux = NULL;
-	char *synopsis = "NAPI ENABLE\n"
-		"NAPI DISABLE\n"
-		"NAPI STATUS\n"
-		"NAPI STATS [<n>] -- if no <n> then all\n"
-		"NAPI SCALE <n>   -- set the scale\n";
-	char *reply = NULL;
-
-	/* make a local copy, as strsep modifies the str in place */
-	char *str = NULL;
-
-	NAPI_DEBUG("-->\n");
-
-	/**
-	 * NOTE TO MAINTAINER: from this point to the end of the function,
-	 * please do not return anywhere in the code except the very end
-	 * to avoid memory leakage (goto end_drv_napi instead)
-	 * or make sure that reply+str is freed
-	 */
-	reply = kmalloc(MAX_USER_COMMAND_SIZE, GFP_KERNEL);
-	if (NULL == reply) {
-		hdd_err("could not allocate reply buffer");
-		rc = -ENOMEM;
-		goto end_drv_napi;
-	}
-
-	str = kmalloc(strlen(command) + 1, GFP_KERNEL);
-	if (NULL == str) {
-		hdd_err("could not allocate copy of input buffer");
-		rc = -ENOMEM;
-		goto end_drv_napi;
-	}
-
-	strlcpy(str, command, strlen(command) + 1);
-	hdd_debug("parsing command into cmd=0x%p sub=0x%p aux=0x%p\n",
-		  cmd, subcmd, aux);
-
-
-	rc = hdd_parse_napi(&str, &cmd, &subcmd, &aux);
-
-	if (0 != rc) {
-		const char *msg = "unknown or badly formatted cmd\n%s";
-		l = QDF_MIN(MAX_USER_COMMAND_SIZE,
-			    strlen(msg)+strlen(synopsis));
-		n = scnprintf(reply, l, msg, synopsis);
-
-		if (copy_to_user(priv_data->buf, reply,
-				 QDF_MIN(priv_data->total_len, l)))
-			hdd_err("failed to copy data to user buffer");
-		hdd_debug("reply: %s", reply);
-
-		rc = -EINVAL;
-	} else {
-		hdd_debug("cmd=(%s) subcmd=(%s) aux=(%s)\n",
-			cmd, subcmd, aux);
-		if (!strcmp(subcmd, "ENABLE"))
-			hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)1);
-		else if (!strcmp(subcmd, "DISABLE"))
-			hdd_napi_event(NAPI_EVT_CMD_STATE, (void *)0);
-		else if (!strcmp(subcmd, "STATUS")) {
-			int n = 0;
-			uint32_t  i;
-			struct qca_napi_data *napi_data;
-
-			napi_data = hdd_napi_get_all();
-			if (unlikely(NULL == napi_data))
-				goto status_end;
-			n += scnprintf(reply+n, MAX_USER_COMMAND_SIZE - n,
-				       "NAPI state: 0x%08x map: 0x%08x\n",
-				       napi_data->state,
-				       napi_data->ce_map);
-
-			for (i = 0; i < CE_COUNT_MAX; i++)
-				if (napi_data->ce_map & (0x01 << i)) {
-					n += scnprintf(
-						reply + n,
-						MAX_USER_COMMAND_SIZE - n,
-						"#%d: id: %d, scale=%d\n",
-						i,
-						napi_data->napis[i].id,
-						napi_data->napis[i].scale);
-				}
-		status_end:
-			hdd_info("wlan: STATUS DATA:\n%s", reply);
-			if (copy_to_user(priv_data->buf, reply,
-					 QDF_MIN(n, priv_data->total_len)))
-				rc = -EINVAL;
-		} else if (!strcmp(subcmd, "STATS")) {
-			int n = 0;
-			struct qca_napi_data *napi_data;
-
-			napi_data = hdd_napi_get_all();
-			if (NULL != napi_data) {
-				n = hdd_napi_stats(reply, MAX_USER_COMMAND_SIZE,
-						   aux, napi_data);
-				NAPI_DEBUG("STATS: returns %d\n", n);
-			}
-			if (n > 0) {
-				if (copy_to_user(priv_data->buf, reply,
-						 QDF_MIN(priv_data->total_len,
-							 n)))
-					rc = -EINVAL;
-				hdd_info("wlan: STATS_DATA\n%s\n", reply);
-			} else
-				rc = -EINVAL;
-		} else if (!strcmp(subcmd, "SCALE")) {
-			if (NULL == aux) {
-				rc = -EINVAL;
-				hdd_err("wlan: SCALE cmd requires <n>");
-			} else {
-				uint8_t sc;
-				rc = kstrtou8(aux, 10, &sc);
-				if (rc) {
-					hdd_err("wlan: bad scale (%s)", aux);
-					rc = -EINVAL;
-				} else
-					napi_set_scale(sc);
-			}
-		} /* SCALE */
-	}
-end_drv_napi:
-	if (NULL != str)
-		kfree(str);
-	if (NULL != reply)
-		kfree(reply);
-
-	NAPI_DEBUG("<--[rc=%d]\n", rc);
-	return rc;
-}
-#endif /* FEATURE_NAPI */
-
 /**
  * hdd_set_rx_filter() - set RX filter
  * @adapter: Pointer to adapter
@@ -7172,9 +6875,6 @@
 #endif
 	{"RSSI",                      drv_cmd_get_rssi},
 	{"LINKSPEED",                 drv_cmd_get_linkspeed},
-#ifdef FEATURE_NAPI
-	{"NAPI",                      drv_cmd_napi},
-#endif /* FEATURE_NAPI */
 	{"RXFILTER-REMOVE",           drv_cmd_rx_filter_remove},
 	{"RXFILTER-ADD",              drv_cmd_rx_filter_add},
 	{"SET_FCC_CHANNEL",           drv_cmd_set_fcc_channel},
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index bb573a1..65a3825 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -6150,17 +6150,24 @@
 	hdd_err("index, total_rx, interval_rx, total_tx, interval_tx, bus_bw_level, RX TP Level, TX TP Level");
 
 	for (i = 0; i < NUM_TX_RX_HISTOGRAM; i++) {
-		hdd_err("%d: %llu, %llu, %llu, %llu, %s, %s, %s",
-			i, hdd_ctx->hdd_txrx_hist[i].total_rx,
-			hdd_ctx->hdd_txrx_hist[i].interval_rx,
-			hdd_ctx->hdd_txrx_hist[i].total_tx,
-			hdd_ctx->hdd_txrx_hist[i].interval_tx,
-			convert_level_to_string(
-				hdd_ctx->hdd_txrx_hist[i].next_vote_level),
-			convert_level_to_string(
-				hdd_ctx->hdd_txrx_hist[i].next_rx_level),
-			convert_level_to_string(
-				hdd_ctx->hdd_txrx_hist[i].next_tx_level));
+		/* using hdd_log to avoid printing function name */
+		if (hdd_ctx->hdd_txrx_hist[i].total_rx != 0 ||
+			hdd_ctx->hdd_txrx_hist[i].total_tx != 0)
+			hdd_log(QDF_TRACE_LEVEL_ERROR,
+				"%d: %llu, %llu, %llu, %llu, %s, %s, %s",
+				i, hdd_ctx->hdd_txrx_hist[i].total_rx,
+				hdd_ctx->hdd_txrx_hist[i].interval_rx,
+				hdd_ctx->hdd_txrx_hist[i].total_tx,
+				hdd_ctx->hdd_txrx_hist[i].interval_tx,
+				convert_level_to_string(
+					hdd_ctx->hdd_txrx_hist[i].
+						next_vote_level),
+				convert_level_to_string(
+					hdd_ctx->hdd_txrx_hist[i].
+						next_rx_level),
+				convert_level_to_string(
+					hdd_ctx->hdd_txrx_hist[i].
+						next_tx_level));
 	}
 	return;
 }
@@ -6197,7 +6204,7 @@
 	while (NULL != adapter_node && QDF_STATUS_SUCCESS == status) {
 		adapter = adapter_node->pAdapter;
 
-		hdd_err("\nNetif queue operation statistics:");
+		hdd_err("Netif queue operation statistics:");
 		hdd_err("Session_id %d device mode %d",
 			adapter->sessionId, adapter->device_mode);
 		hdd_err("Current pause_map value %x", adapter->pause_map);
@@ -6223,7 +6230,9 @@
 			if (adapter->pause_map & (1 << i))
 				pause_delta = delta;
 
-			hdd_err("%s: %d: %d: %ums",
+			/* using hdd_log to avoid printing function name */
+			hdd_log(QDF_TRACE_LEVEL_ERROR,
+				"%s: %d: %d: %ums",
 				hdd_reason_type_to_string(i),
 				adapter->queue_oper_stats[i].pause_count,
 				adapter->queue_oper_stats[i].unpause_count,
@@ -6232,14 +6241,18 @@
 				pause_delta));
 		}
 
-		hdd_err("\nNetif queue operation history:");
+		hdd_err("Netif queue operation history:");
 		hdd_err("Total entries: %d current index %d",
 			WLAN_HDD_MAX_HISTORY_ENTRY, adapter->history_index);
 
 		hdd_err("index: time: action_type: reason_type: pause_map");
 
 		for (i = 0; i < WLAN_HDD_MAX_HISTORY_ENTRY; i++) {
-			hdd_err("%d: %u: %s: %s: %x",
+			/* using hdd_log to avoid printing function name */
+			if (adapter->queue_oper_history[i].time == 0)
+				continue;
+			hdd_log(QDF_TRACE_LEVEL_ERROR,
+				"%d: %u: %s: %s: %x",
 				i, qdf_system_ticks_to_msecs(
 					adapter->queue_oper_history[i].time),
 				hdd_action_type_to_string(
diff --git a/core/hdd/src/wlan_hdd_napi.c b/core/hdd/src/wlan_hdd_napi.c
index ea72cca..614d8b3 100644
--- a/core/hdd/src/wlan_hdd_napi.c
+++ b/core/hdd/src/wlan_hdd_napi.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -373,3 +373,62 @@
 {
 	return hif_napi_poll(cds_get_context(QDF_MODULE_ID_HIF), napi, budget);
 }
+
+/**
+ * hdd_display_napi_stats() - print NAPI stats
+ *
+ * Return: == 0: success; !=0: failure
+ */
+int hdd_display_napi_stats(void)
+{
+	int i, j, k, n; /* NAPI, CPU, bucket indices, bucket buf write index*/
+	int max;
+	struct qca_napi_data *napid;
+	struct qca_napi_info *napii;
+	struct qca_napi_stat *napis;
+	/*
+	 * Expecting each NAPI bucket item to need at max 5 numerals + space for
+	 * formatting. For example "10000 " Thus the array needs to have
+	 * (5 + 1) * QCA_NAPI_NUM_BUCKETS bytes of space. Leaving one space at
+	 * the end of the "buf" arrary for end of string char.
+	 */
+	char buf[6 * QCA_NAPI_NUM_BUCKETS + 1] = {'\0'};
+
+	napid = hdd_napi_get_all();
+	if (NULL == napid) {
+		hdd_err("%s unable to retrieve napi structure", __func__);
+		return -EFAULT;
+	}
+	qdf_print("[NAPI -- STATS]:  scheds   polls   comps    done time-lim pkt-lim napi-buckets(%d)", QCA_NAPI_NUM_BUCKETS);
+
+	for (i = 0; i < CE_COUNT_MAX; i++)
+		if (napid->ce_map & (0x01 << i)) {
+			napii = &(napid->napis[i]);
+			for (j = 0; j < NR_CPUS; j++) {
+				napis = &(napii->stats[j]);
+				n = 0;
+				max = sizeof(buf);
+				for (k = 0; k < QCA_NAPI_NUM_BUCKETS; k++) {
+					n += scnprintf(
+						buf + n, max - n,
+						" %d",
+						napis->napi_budget_uses[k]);
+				}
+
+				if (napis->napi_schedules != 0)
+					qdf_print("NAPI[%2d]CPU[%2d]: %7d %7d %7d %7d %8d %7d %s",
+						  i, j,
+						  napis->napi_schedules,
+						  napis->napi_polls,
+						  napis->napi_completes,
+						  napis->napi_workdone,
+						  napis->time_limit_reached,
+						  napis->rxpkt_thresh_reached,
+						  buf);
+			}
+		}
+
+	hif_napi_stats(napid);
+	return 0;
+}
+
diff --git a/core/hdd/src/wlan_hdd_wext.c b/core/hdd/src/wlan_hdd_wext.c
index 44a5071..8592bf5 100644
--- a/core/hdd/src/wlan_hdd_wext.c
+++ b/core/hdd/src/wlan_hdd_wext.c
@@ -2931,14 +2931,14 @@
 	}
 
 	len = scnprintf(buffer, buf_len,
-		"\nTransmit"
-		"\ncalled %u, dropped %u,"
-		"\n      dropped BK %u, BE %u, VI %u, VO %u"
-		"\n   classified BK %u, BE %u, VI %u, VO %u"
-		"\ncompleted %u,"
-		"\n\nReceive  Total"
-		"\n packets %u, dropped %u, delivered %u, refused %u"
+		"\nTransmit[%lu] - "
+		"called %u, dropped %u,"
+		"\n[dropped]    BK %u, BE %u, VI %u, VO %u"
+		"\n[classified] BK %u, BE %u, VI %u, VO %u"
+		"\n\nReceive[%lu] - "
+		"packets %u, dropped %u, delivered %u, refused %u"
 		"\n",
+		qdf_system_ticks(),
 		pStats->txXmitCalled,
 		pStats->txXmitDropped,
 
@@ -2951,21 +2951,22 @@
 		pStats->txXmitClassifiedAC[SME_AC_BE],
 		pStats->txXmitClassifiedAC[SME_AC_VI],
 		pStats->txXmitClassifiedAC[SME_AC_VO],
-
-		pStats->txCompleted,
+		qdf_system_ticks(),
 		total_rx_pkt, total_rx_dropped, total_rx_delv, total_rx_refused
 		);
 
 	for (i = 0; i < NUM_CPUS; i++) {
+		if (pStats->rxPackets[i] == 0)
+			continue;
 		len += scnprintf(buffer + len, buf_len - len,
-			"\nReceive CPU: %d"
-			"\n  packets %u, dropped %u, delivered %u, refused %u",
+			"Rx CPU[%d]:"
+			"packets %u, dropped %u, delivered %u, refused %u\n",
 			i, pStats->rxPackets[i], pStats->rxDropped[i],
 			pStats->rxDelivered[i], pStats->rxRefused[i]);
 	}
 
 	len += scnprintf(buffer + len, buf_len - len,
-		"\n\nTX_FLOW"
+		"\nTX_FLOW"
 		"\nCurrent status: %s"
 		"\ntx-flow timer start count %u"
 		"\npause count %u, unpause count %u",
@@ -2976,10 +2977,6 @@
 
 	len += cdp_stats(cds_get_context(QDF_MODULE_ID_SOC),
 		pAdapter->sessionId, &buffer[len], (buf_len - len));
-
-	len += hdd_napi_stats(buffer + len, buf_len - len,
-			   NULL, hdd_napi_get_all());
-
 	*length = len + 1;
 }
 
@@ -3092,20 +3089,41 @@
 
 	*length = len + 1;
 }
+/**
+ * hdd_display_stats_help() - print statistics help
+ *
+ * Return: none
+ */
+void hdd_display_stats_help(void)
+{
+	hdd_err("iwpriv wlan0 dumpStats [option] - dump statistics");
+	hdd_err("iwpriv wlan0 clearStats [option] - clear statistics");
+	hdd_err("options:");
+	hdd_err("  1 -- TXRX Layer statistics");
+	hdd_err("  2 -- Bandwidth compute timer stats");
+	hdd_err("  3 -- TSO statistics");
+	hdd_err("  4 -- Network queue statistics");
+	hdd_err("  5 -- Flow control statistics");
+	hdd_err("  6 -- Per Layer statistics");
+	hdd_err("  7 -- Copy engine interrupt statistics");
+	hdd_err("  8 -- LRO statistics");
+	hdd_err("  9 -- NAPI statistics");
+}
 
 /**
  * hdd_wlan_dump_stats() - display dump Stats
  * @adapter: adapter handle
  * @value: value from user
  *
- * Return: none
+ * Return: 0 => success, error code on failure
  */
-void hdd_wlan_dump_stats(hdd_adapter_t *adapter, int value)
+int hdd_wlan_dump_stats(hdd_adapter_t *adapter, int value)
 {
+	int ret = 0;
+	QDF_STATUS status;
 	hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
 
 	switch (value) {
-
 	case WLAN_TXRX_HIST_STATS:
 		wlan_hdd_display_tx_rx_histogram(hdd_ctx);
 		break;
@@ -3117,11 +3135,22 @@
 		break;
 	case WLAN_LRO_STATS:
 		hdd_lro_display_stats(hdd_ctx);
+	case WLAN_NAPI_STATS:
+		if (hdd_display_napi_stats()) {
+			hdd_err("error displaying napi stats");
+			ret = EFAULT;
+		}
 		break;
 	default:
-		cdp_display_stats(cds_get_context(QDF_MODULE_ID_SOC), value);
+		status = cdp_display_stats(cds_get_context(QDF_MODULE_ID_SOC),
+							value);
+		if (status == QDF_STATUS_E_INVAL) {
+			hdd_display_stats_help();
+			ret = EINVAL;
+		}
 		break;
 	}
+	return ret;
 }
 
 /**
@@ -8369,7 +8398,7 @@
 	case WE_DUMP_STATS:
 	{
 		hdd_notice("WE_DUMP_STATS val %d", set_value);
-		hdd_wlan_dump_stats(pAdapter, set_value);
+		ret = hdd_wlan_dump_stats(pAdapter, set_value);
 		break;
 	}