qcacmn: Implement WMI/Credit History log print APIs

Create output agnostic log print APIs for WMI and Credit History to
enhance the debugging experience.

Change-Id: Ie89a9cb3b54c373ac2610d62003e940da17696d8
CRs-Fixed: 2028762
diff --git a/htc/htc_api.h b/htc/htc_api.h
index 0fcc2c9..2b8f3b1 100644
--- a/htc/htc_api.h
+++ b/htc/htc_api.h
@@ -802,4 +802,10 @@
  * return: WMI enpoint count
  */
 uint8_t  htc_get_wmi_endpoint_count(HTC_HANDLE htc_handle);
+
+#ifdef WMI_INTERFACE_EVENT_LOGGING
+void htc_print_credit_history(HTC_HANDLE htc, uint32_t count,
+			      qdf_abstract_print * print, void *print_priv);
+#endif
+
 #endif /* _HTC_API_H_ */
diff --git a/htc/htc_internal.h b/htc/htc_internal.h
index c37008c..1d5bfc0 100644
--- a/htc/htc_internal.h
+++ b/htc/htc_internal.h
@@ -92,6 +92,25 @@
 	HTC_INITIAL_WAKE_UP,
 } htc_credit_exchange_type;
 
+static inline const char*
+htc_credit_exchange_type_str(htc_credit_exchange_type type)
+{
+	switch (type) {
+	case HTC_REQUEST_CREDIT:
+		return "HTC_REQUEST_CREDIT";
+	case HTC_PROCESS_CREDIT_REPORT:
+		return "HTC_PROCESS_CREDIT_REPORT";
+	case HTC_SUSPEND_ACK:
+		return "HTC_SUSPEND_ACK";
+	case HTC_SUSPEND_NACK:
+		return "HTC_SUSPEND_NACK";
+	case HTC_INITIAL_WAKE_UP:
+		return "HTC_INITIAL_WAKE_UP";
+	default:
+		return "Unknown htc_credit_exchange_type";
+	}
+}
+
 typedef struct {
 	htc_credit_exchange_type type;
 	uint64_t time;
diff --git a/htc/htc_send.c b/htc/htc_send.c
index a65e0ce..79f6f47 100644
--- a/htc/htc_send.c
+++ b/htc/htc_send.c
@@ -55,6 +55,7 @@
 
 /* HTC Control Path Credit History */
 uint32_t g_htc_credit_history_idx = 0;
+uint32_t g_htc_credit_history_length;
 HTC_CREDIT_HISTORY htc_credit_history_buffer[HTC_CREDIT_HISTORY_MAX];
 
 /**
@@ -85,9 +86,53 @@
 		tx_credit;
 	htc_credit_history_buffer[g_htc_credit_history_idx].htc_tx_queue_depth =
 		htc_tx_queue_depth;
+
 	g_htc_credit_history_idx++;
+	g_htc_credit_history_length++;
 }
 
+#ifdef WMI_INTERFACE_EVENT_LOGGING
+void htc_print_credit_history(HTC_HANDLE htc, uint32_t count,
+			      qdf_abstract_print *print, void *print_priv)
+{
+	uint32_t idx;
+	HTC_TARGET *target;
+
+	target = GET_HTC_TARGET_FROM_HANDLE(htc);
+	LOCK_HTC_CREDIT(target);
+
+	if (count > HTC_CREDIT_HISTORY_MAX)
+		count = HTC_CREDIT_HISTORY_MAX;
+	if (count > g_htc_credit_history_length)
+		count = g_htc_credit_history_length;
+
+	/* subtract count from index, and wrap if necessary */
+	idx = HTC_CREDIT_HISTORY_MAX + g_htc_credit_history_idx - count;
+	idx %= HTC_CREDIT_HISTORY_MAX;
+
+	print(print_priv,
+	      "Time (seconds)     Type                         Credits    Queue Depth");
+	while (count) {
+		HTC_CREDIT_HISTORY *hist = &htc_credit_history_buffer[idx];
+		long long us = qdf_log_timestamp_to_usecs(hist->time);
+
+		print(print_priv, "% 8lld.%06lld    %-25s    %-7.d    %d",
+		      us / 1000000,
+		      us % 1000000,
+		      htc_credit_exchange_type_str(hist->type),
+		      hist->tx_credit,
+		      hist->htc_tx_queue_depth);
+
+		--count;
+		++idx;
+		if (idx >= HTC_CREDIT_HISTORY_MAX)
+			idx = 0;
+	}
+
+	UNLOCK_HTC_CREDIT(target);
+}
+#endif /* WMI_INTERFACE_EVENT_LOGGING */
+
 void htc_dump_counter_info(HTC_HANDLE HTCHandle)
 {
 	HTC_TARGET *target = GET_HTC_TARGET_FROM_HANDLE(HTCHandle);
diff --git a/qdf/inc/qdf_trace.h b/qdf/inc/qdf_trace.h
index 44d0bff..884f4d3 100644
--- a/qdf/inc/qdf_trace.h
+++ b/qdf/inc/qdf_trace.h
@@ -53,6 +53,8 @@
 #define QDF_DEFAULT_TRACE_LEVEL (1 << QDF_TRACE_LEVEL_INFO)
 #endif
 
+typedef int (qdf_abstract_print)(void *priv, const char *fmt, ...);
+
 /*
  * Log levels
  */
diff --git a/wmi/inc/wmi_unified_api.h b/wmi/inc/wmi_unified_api.h
index e080005..a57e6e6 100644
--- a/wmi/inc/wmi_unified_api.h
+++ b/wmi/inc/wmi_unified_api.h
@@ -1435,4 +1435,28 @@
  */
 QDF_STATUS wmi_unified_dfs_phyerr_offload_dis_cmd(void *wmi_hdl,
 		uint32_t pdev_id);
+
+#ifdef WMI_INTERFACE_EVENT_LOGGING
+void wmi_print_cmd_log(wmi_unified_t wmi, uint32_t count,
+		       qdf_abstract_print *print, void *print_priv);
+
+void wmi_print_cmd_tx_cmp_log(wmi_unified_t wmi, uint32_t count,
+			      qdf_abstract_print *print, void *print_priv);
+
+void wmi_print_mgmt_cmd_log(wmi_unified_t wmi, uint32_t count,
+			    qdf_abstract_print *print, void *print_priv);
+
+void wmi_print_mgmt_cmd_tx_cmp_log(wmi_unified_t wmi, uint32_t count,
+				   qdf_abstract_print *print, void *print_priv);
+
+void wmi_print_event_log(wmi_unified_t wmi, uint32_t count,
+			 qdf_abstract_print *print, void *print_priv);
+
+void wmi_print_rx_event_log(wmi_unified_t wmi, uint32_t count,
+			    qdf_abstract_print *print, void *print_priv);
+
+void wmi_print_mgmt_event_log(wmi_unified_t wmi, uint32_t count,
+			      qdf_abstract_print *print, void *print_priv);
+#endif /* WMI_INTERFACE_EVENT_LOGGING */
+
 #endif /* _WMI_UNIFIED_API_H_ */
diff --git a/wmi/inc/wmi_unified_priv.h b/wmi/inc/wmi_unified_priv.h
index 5aaa06b..c58664d 100644
--- a/wmi/inc/wmi_unified_priv.h
+++ b/wmi/inc/wmi_unified_priv.h
@@ -113,12 +113,14 @@
  * @ buf_tail_idx - Tail index of buffer
  * @ p_buf_tail_idx - refernce to buffer tail index. It is added to accommodate
  * unified design since MCL uses global variable for buffer tail index
+ * @ size - the size of the buffer in number of entries
  */
 struct wmi_log_buf_t {
 	void *buf;
 	uint32_t length;
 	uint32_t buf_tail_idx;
 	uint32_t *p_buf_tail_idx;
+	uint32_t size;
 };
 
 /**
diff --git a/wmi/src/wmi_unified.c b/wmi/src/wmi_unified.c
index f0c8a06..48c69b3 100644
--- a/wmi/src/wmi_unified.c
+++ b/wmi/src/wmi_unified.c
@@ -342,30 +342,35 @@
 	cmd_log_buf->buf_tail_idx = 0;
 	cmd_log_buf->buf = wmi_command_log_buffer;
 	cmd_log_buf->p_buf_tail_idx = &g_wmi_command_buf_idx;
+	cmd_log_buf->size = WMI_EVENT_DEBUG_MAX_ENTRY;
 
 	/* WMI commands TX completed */
 	cmd_tx_cmpl_log_buf->length = 0;
 	cmd_tx_cmpl_log_buf->buf_tail_idx = 0;
 	cmd_tx_cmpl_log_buf->buf = wmi_command_tx_cmp_log_buffer;
 	cmd_tx_cmpl_log_buf->p_buf_tail_idx = &g_wmi_command_tx_cmp_buf_idx;
+	cmd_tx_cmpl_log_buf->size = WMI_EVENT_DEBUG_MAX_ENTRY;
 
 	/* WMI events when processed */
 	event_log_buf->length = 0;
 	event_log_buf->buf_tail_idx = 0;
 	event_log_buf->buf = wmi_event_log_buffer;
 	event_log_buf->p_buf_tail_idx = &g_wmi_event_buf_idx;
+	event_log_buf->size = WMI_EVENT_DEBUG_MAX_ENTRY;
 
 	/* WMI events when queued */
 	rx_event_log_buf->length = 0;
 	rx_event_log_buf->buf_tail_idx = 0;
 	rx_event_log_buf->buf = wmi_rx_event_log_buffer;
 	rx_event_log_buf->p_buf_tail_idx = &g_wmi_rx_event_buf_idx;
+	rx_event_log_buf->size = WMI_EVENT_DEBUG_MAX_ENTRY;
 
 	/* WMI Management commands */
 	mgmt_cmd_log_buf->length = 0;
 	mgmt_cmd_log_buf->buf_tail_idx = 0;
 	mgmt_cmd_log_buf->buf = wmi_mgmt_command_log_buffer;
 	mgmt_cmd_log_buf->p_buf_tail_idx = &g_wmi_mgmt_command_buf_idx;
+	mgmt_cmd_log_buf->size = WMI_MGMT_EVENT_DEBUG_MAX_ENTRY;
 
 	/* WMI Management commands Tx completed*/
 	mgmt_cmd_tx_cmp_log_buf->length = 0;
@@ -373,12 +378,14 @@
 	mgmt_cmd_tx_cmp_log_buf->buf = wmi_mgmt_command_tx_cmp_log_buffer;
 	mgmt_cmd_tx_cmp_log_buf->p_buf_tail_idx =
 		&g_wmi_mgmt_command_tx_cmp_buf_idx;
+	mgmt_cmd_tx_cmp_log_buf->size = WMI_MGMT_EVENT_DEBUG_MAX_ENTRY;
 
 	/* WMI Management events when processed*/
 	mgmt_event_log_buf->length = 0;
 	mgmt_event_log_buf->buf_tail_idx = 0;
 	mgmt_event_log_buf->buf = wmi_mgmt_event_log_buffer;
 	mgmt_event_log_buf->p_buf_tail_idx = &g_wmi_mgmt_event_buf_idx;
+	mgmt_event_log_buf->size = WMI_MGMT_EVENT_DEBUG_MAX_ENTRY;
 
 	qdf_spinlock_create(&wmi_handle->log_info.wmi_record_lock);
 	wmi_handle->log_info.wmi_logging_enable = 1;
@@ -412,6 +419,7 @@
 	cmd_log_buf->buf_tail_idx = 0;
 	cmd_log_buf->buf = (struct wmi_command_debug *) qdf_mem_malloc(
 		wmi_log_max_entry * sizeof(struct wmi_command_debug));
+	cmd_log_buf->size = wmi_log_max_entry;
 
 	if (!cmd_log_buf->buf) {
 		qdf_print("no memory for WMI command log buffer..\n");
@@ -424,6 +432,7 @@
 	cmd_tx_cmpl_log_buf->buf_tail_idx = 0;
 	cmd_tx_cmpl_log_buf->buf = (struct wmi_command_debug *) qdf_mem_malloc(
 		wmi_log_max_entry * sizeof(struct wmi_command_debug));
+	cmd_tx_cmpl_log_buf->size = wmi_log_max_entry;
 
 	if (!cmd_tx_cmpl_log_buf->buf) {
 		qdf_print("no memory for WMI Command Tx Complete log buffer..\n");
@@ -437,6 +446,7 @@
 	event_log_buf->buf_tail_idx = 0;
 	event_log_buf->buf = (struct wmi_event_debug *) qdf_mem_malloc(
 		wmi_log_max_entry * sizeof(struct wmi_event_debug));
+	event_log_buf->size = wmi_log_max_entry;
 
 	if (!event_log_buf->buf) {
 		qdf_print("no memory for WMI Event log buffer..\n");
@@ -449,6 +459,7 @@
 	rx_event_log_buf->buf_tail_idx = 0;
 	rx_event_log_buf->buf = (struct wmi_event_debug *) qdf_mem_malloc(
 		wmi_log_max_entry * sizeof(struct wmi_event_debug));
+	rx_event_log_buf->size = wmi_log_max_entry;
 
 	if (!rx_event_log_buf->buf) {
 		qdf_print("no memory for WMI Event Rx log buffer..\n");
@@ -460,8 +471,8 @@
 	mgmt_cmd_log_buf->length = 0;
 	mgmt_cmd_log_buf->buf_tail_idx = 0;
 	mgmt_cmd_log_buf->buf = (struct wmi_command_debug *) qdf_mem_malloc(
-		wmi_mgmt_log_max_entry *
-		sizeof(struct wmi_command_debug));
+		wmi_mgmt_log_max_entry * sizeof(struct wmi_command_debug));
+	mgmt_cmd_log_buf->size = wmi_mgmt_log_max_entry;
 
 	if (!mgmt_cmd_log_buf->buf) {
 		qdf_print("no memory for WMI Management Command log buffer..\n");
@@ -476,6 +487,7 @@
 		qdf_mem_malloc(
 		wmi_mgmt_log_max_entry *
 		sizeof(struct wmi_command_debug));
+	mgmt_cmd_tx_cmp_log_buf->size = wmi_mgmt_log_max_entry;
 
 	if (!mgmt_cmd_tx_cmp_log_buf->buf) {
 		qdf_print("no memory for WMI Management Command Tx complete log buffer..\n");
@@ -491,6 +503,7 @@
 	mgmt_event_log_buf->buf = (struct wmi_event_debug *) qdf_mem_malloc(
 		wmi_mgmt_log_max_entry *
 		sizeof(struct wmi_event_debug));
+	mgmt_event_log_buf->size = wmi_mgmt_log_max_entry;
 
 	if (!mgmt_event_log_buf->buf) {
 		qdf_print("no memory for WMI Management Event log buffer..\n");
@@ -544,6 +557,175 @@
 }
 #endif
 
+/**
+ * wmi_print_cmd_log_buffer() - an output agnostic wmi command log printer
+ * @log_buffer: the command log buffer metadata of the buffer to print
+ * @count: the maximum number of entries to print
+ * @print: an abstract print method, e.g. a qdf_print() or seq_printf() wrapper
+ * @print_priv: any data required by the print method, e.g. a file handle
+ *
+ * Return: None
+ */
+static void
+wmi_print_cmd_log_buffer(struct wmi_log_buf_t *log_buffer, uint32_t count,
+			 qdf_abstract_print *print, void *print_priv)
+{
+	static const int data_len =
+		WMI_EVENT_DEBUG_ENTRY_MAX_LENGTH / sizeof(uint32_t);
+	char str[128];
+	uint32_t idx;
+
+	if (count > log_buffer->size)
+		count = log_buffer->size;
+	if (count > log_buffer->length)
+		count = log_buffer->length;
+
+	/* subtract count from index, and wrap if necessary */
+	idx = log_buffer->size + *log_buffer->p_buf_tail_idx - count;
+	idx %= log_buffer->size;
+
+	print(print_priv, "Time (seconds)      Cmd Id              Payload");
+	while (count) {
+		struct wmi_command_debug *cmd_log = (struct wmi_command_debug *)
+			&((struct wmi_command_debug *)log_buffer->buf)[idx];
+		long long us = qdf_log_timestamp_to_usecs(cmd_log->time);
+		int len = 0;
+		int i;
+
+		len += scnprintf(str + len, sizeof(str) - len,
+				 "% 8lld.%06lld    %6u (0x%06x)    ",
+				 us / 1000000, us % 1000000,
+				 cmd_log->command, cmd_log->command);
+		for (i = 0; i < data_len; ++i) {
+			len += scnprintf(str + len, sizeof(str) - len,
+					 "0x%08x ", cmd_log->data[i]);
+		}
+
+		print(print_priv, str);
+
+		--count;
+		++idx;
+		if (idx >= log_buffer->size)
+			idx = 0;
+	}
+}
+
+/**
+ * wmi_print_event_log_buffer() - an output agnostic wmi event log printer
+ * @log_buffer: the event log buffer metadata of the buffer to print
+ * @count: the maximum number of entries to print
+ * @print: an abstract print method, e.g. a qdf_print() or seq_printf() wrapper
+ * @print_priv: any data required by the print method, e.g. a file handle
+ *
+ * Return: None
+ */
+static void
+wmi_print_event_log_buffer(struct wmi_log_buf_t *log_buffer, uint32_t count,
+			   qdf_abstract_print *print, void *print_priv)
+{
+	static const int data_len =
+		WMI_EVENT_DEBUG_ENTRY_MAX_LENGTH / sizeof(uint32_t);
+	char str[128];
+	uint32_t idx;
+
+	if (count > log_buffer->size)
+		count = log_buffer->size;
+	if (count > log_buffer->length)
+		count = log_buffer->length;
+
+	/* subtract count from index, and wrap if necessary */
+	idx = log_buffer->size + *log_buffer->p_buf_tail_idx - count;
+	idx %= log_buffer->size;
+
+	print(print_priv, "Time (seconds)      Event Id             Payload");
+	while (count) {
+		struct wmi_event_debug *event_log = (struct wmi_event_debug *)
+			&((struct wmi_event_debug *)log_buffer->buf)[idx];
+		long long us = qdf_log_timestamp_to_usecs(event_log->time);
+		int len = 0;
+		int i;
+
+		len += scnprintf(str + len, sizeof(str) - len,
+				 "% 8lld.%06lld    %6u (0x%06x)    ",
+				 us / 1000000, us % 1000000,
+				 event_log->event, event_log->event);
+		for (i = 0; i < data_len; ++i) {
+			len += scnprintf(str + len, sizeof(str) - len,
+					 "0x%08x ", event_log->data[i]);
+		}
+
+		print(print_priv, str);
+
+		--count;
+		++idx;
+		if (idx >= log_buffer->size)
+			idx = 0;
+	}
+}
+
+inline void
+wmi_print_cmd_log(wmi_unified_t wmi, uint32_t count,
+		  qdf_abstract_print *print, void *print_priv)
+{
+	wmi_print_cmd_log_buffer(
+		&wmi->log_info.wmi_command_log_buf_info,
+		count, print, print_priv);
+}
+
+inline void
+wmi_print_cmd_tx_cmp_log(wmi_unified_t wmi, uint32_t count,
+			 qdf_abstract_print *print, void *print_priv)
+{
+	wmi_print_cmd_log_buffer(
+		&wmi->log_info.wmi_command_tx_cmp_log_buf_info,
+		count, print, print_priv);
+}
+
+inline void
+wmi_print_mgmt_cmd_log(wmi_unified_t wmi, uint32_t count,
+		       qdf_abstract_print *print, void *print_priv)
+{
+	wmi_print_cmd_log_buffer(
+		&wmi->log_info.wmi_mgmt_command_log_buf_info,
+		count, print, print_priv);
+}
+
+inline void
+wmi_print_mgmt_cmd_tx_cmp_log(wmi_unified_t wmi, uint32_t count,
+			      qdf_abstract_print *print, void *print_priv)
+{
+	wmi_print_cmd_log_buffer(
+		&wmi->log_info.wmi_mgmt_command_tx_cmp_log_buf_info,
+		count, print, print_priv);
+}
+
+inline void
+wmi_print_event_log(wmi_unified_t wmi, uint32_t count,
+		    qdf_abstract_print *print, void *print_priv)
+{
+	wmi_print_event_log_buffer(
+		&wmi->log_info.wmi_event_log_buf_info,
+		count, print, print_priv);
+}
+
+inline void
+wmi_print_rx_event_log(wmi_unified_t wmi, uint32_t count,
+		       qdf_abstract_print *print, void *print_priv)
+{
+	wmi_print_event_log_buffer(
+		&wmi->log_info.wmi_rx_event_log_buf_info,
+		count, print, print_priv);
+}
+
+inline void
+wmi_print_mgmt_event_log(wmi_unified_t wmi, uint32_t count,
+			 qdf_abstract_print *print, void *print_priv)
+{
+	wmi_print_event_log_buffer(
+		&wmi->log_info.wmi_mgmt_event_log_buf_info,
+		count, print, print_priv);
+}
+
 #ifdef CONFIG_MCL
 const int8_t * const debugfs_dir[MAX_WMI_INSTANCES] = {"WMI0"};
 #else