diag: Add debugfs support to assist in debug analysis

Add debugfs support to allow for runtime analysis of
diag's current state.

Change-Id: I8b4b69ea7510b701863e297c34da1ee154367cbe
Signed-off-by: Dixon Peterson <dixonp@codeaurora.org>
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index f1ab127..34640c3 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1249,6 +1249,7 @@
 			diag_read_smd_wcnss_cntl_work_fn);
 		INIT_WORK(&(driver->diag_read_smd_dci_work),
 						 diag_read_smd_dci_work_fn);
+		diag_debugfs_init();
 		diagfwd_init();
 		diagfwd_cntl_init();
 		driver->dci_state = diag_dci_init();
@@ -1282,6 +1283,7 @@
 	return 0;
 
 fail:
+	diag_debugfs_cleanup();
 	diagchar_cleanup();
 	diagfwd_exit();
 	diagfwd_cntl_exit();
@@ -1300,6 +1302,7 @@
 	diagfwd_cntl_exit();
 	diag_sdio_fn(EXIT);
 	diag_hsic_fn(EXIT);
+	diag_debugfs_cleanup();
 	diagchar_cleanup();
 	printk(KERN_INFO "done diagchar exit\n");
 }
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 340524b..a63e344 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -183,7 +183,7 @@
 		return 0;
 }
 
-inline int chk_polling_response(void)
+int chk_polling_response(void)
 {
 	if (!(driver->polling_reg_flag) && chk_apps_master())
 		/*
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index 0780a8e..f5de2ac 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -27,7 +27,10 @@
 int diag_device_write(void *, int, struct diag_request *);
 int mask_request_validate(unsigned char mask_buf[]);
 void diag_clear_reg(int);
+int chk_config_get_id(void);
 int chk_apps_only(void);
+int chk_apps_master(void);
+int chk_polling_response(void);
 void diag_send_event_mask_update(smd_channel_t *, int num_bytes);
 void diag_send_msg_mask_update(smd_channel_t *, int ssid_first,
 					 int ssid_last, int proc);
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 171168f..8efe10b 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -16,6 +16,9 @@
 #include "diagchar.h"
 #include "diagfwd.h"
 #include "diagfwd_cntl.h"
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
 
 #define HDR_SIZ 8
 
@@ -317,3 +320,281 @@
 	kfree(driver->buf_in_qdsp_cntl);
 	kfree(driver->buf_in_wcnss_cntl);
 }
+
+#ifdef CONFIG_DEBUG_FS
+#define DEBUG_BUF_SIZE	4096
+static struct dentry *diag_dbgfs_dent;
+static int diag_dbgfs_table_index;
+
+static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf,
+				      size_t count, loff_t *ppos)
+{
+	char *buf;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf) {
+		pr_err("diag: %s, Error allocating memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = scnprintf(buf, DEBUG_BUF_SIZE,
+		"modem ch: 0x%x\n"
+		"lpass ch: 0x%x\n"
+		"riva ch: 0x%x\n"
+		"dci ch: 0x%x\n"
+		"modem cntl_ch: 0x%x\n"
+		"lpass cntl_ch: 0x%x\n"
+		"riva cntl_ch: 0x%x\n"
+		"CPU Tools id: %d\n"
+		"Apps only: %d\n"
+		"Apps master: %d\n"
+		"Check Polling Response: %d\n"
+		"polling_reg_flag: %d\n"
+		"uses device tree: %d\n"
+		"in_busy_1: %d\n"
+		"in_busy_2: %d\n"
+		"in_busy_qdsp_1: %d\n"
+		"in_busy_qdsp_2: %d\n"
+		"in_busy_wcnss_1: %d\n"
+		"in_busy_wcnss_2: %d\n"
+		"in_busy_dci: %d\n",
+		(unsigned int)driver->ch,
+		(unsigned int)driver->chqdsp,
+		(unsigned int)driver->ch_wcnss,
+		(unsigned int)driver->ch_dci,
+		(unsigned int)driver->ch_cntl,
+		(unsigned int)driver->chqdsp_cntl,
+		(unsigned int)driver->ch_wcnss_cntl,
+		chk_config_get_id(),
+		chk_apps_only(),
+		chk_apps_master(),
+		chk_polling_response(),
+		driver->polling_reg_flag,
+		driver->use_device_tree,
+		driver->in_busy_1,
+		driver->in_busy_2,
+		driver->in_busy_qdsp_1,
+		driver->in_busy_qdsp_2,
+		driver->in_busy_wcnss_1,
+		driver->in_busy_wcnss_2,
+		driver->in_busy_dci);
+
+#ifdef CONFIG_DIAG_OVER_USB
+	ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
+		"usb_connected: %d\n",
+		driver->usb_connected);
+#endif
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t diag_dbgfs_read_workpending(struct file *file,
+				char __user *ubuf, size_t count, loff_t *ppos)
+{
+	char *buf;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf) {
+		pr_err("diag: %s, Error allocating memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = scnprintf(buf, DEBUG_BUF_SIZE,
+		"Pending status for work_stucts:\n"
+		"diag_drain_work: %d\n"
+		"diag_read_smd_work: %d\n"
+		"diag_read_smd_cntl_work: %d\n"
+		"diag_read_smd_qdsp_work: %d\n"
+		"diag_read_smd_qdsp_cntl_work: %d\n"
+		"diag_read_smd_wcnss_work: %d\n"
+		"diag_read_smd_wcnss_cntl_work: %d\n"
+		"diag_modem_mask_update_work: %d\n"
+		"diag_qdsp_mask_update_work: %d\n"
+		"diag_wcnss_mask_update_work: %d\n"
+		"diag_read_smd_dci_work: %d\n",
+		work_pending(&(driver->diag_drain_work)),
+		work_pending(&(driver->diag_read_smd_work)),
+		work_pending(&(driver->diag_read_smd_cntl_work)),
+		work_pending(&(driver->diag_read_smd_qdsp_work)),
+		work_pending(&(driver->diag_read_smd_qdsp_cntl_work)),
+		work_pending(&(driver->diag_read_smd_wcnss_work)),
+		work_pending(&(driver->diag_read_smd_wcnss_cntl_work)),
+		work_pending(&(driver->diag_modem_mask_update_work)),
+		work_pending(&(driver->diag_qdsp_mask_update_work)),
+		work_pending(&(driver->diag_wcnss_mask_update_work)),
+		work_pending(&(driver->diag_read_smd_dci_work)));
+
+#ifdef CONFIG_DIAG_OVER_USB
+	ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
+		"diag_proc_hdlc_work: %d\n"
+		"diag_read_work: %d\n",
+		work_pending(&(driver->diag_proc_hdlc_work)),
+		work_pending(&(driver->diag_read_work)));
+#endif
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+	return ret;
+}
+
+static ssize_t diag_dbgfs_read_table(struct file *file, char __user *ubuf,
+				     size_t count, loff_t *ppos)
+{
+	char *buf;
+	int ret = 0;
+	int i;
+	int bytes_remaining;
+	int bytes_in_buffer = 0;
+	int bytes_written;
+	int buf_size = (DEBUG_BUF_SIZE < count) ? DEBUG_BUF_SIZE : count;
+
+	if (diag_dbgfs_table_index >= diag_max_reg) {
+		/* Done. Reset to prepare for future requests */
+		diag_dbgfs_table_index = 0;
+		return 0;
+	}
+
+	buf = kzalloc(sizeof(char) * buf_size, GFP_KERNEL);
+	if (!buf) {
+		pr_err("diag: %s, Error allocating memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	bytes_remaining = buf_size;
+	for (i = diag_dbgfs_table_index; i < diag_max_reg; i++) {
+		/* Do not process empty entries in the table */
+		if (driver->table[i].process_id == 0)
+			continue;
+
+		bytes_written = scnprintf(buf+bytes_in_buffer, bytes_remaining,
+			"i: %3d, cmd_code: %4x, subsys_id: %4x, "
+			"client: %2d, cmd_code_lo: %4x, "
+			"cmd_code_hi: %4x, process_id: %5d\n",
+			i,
+			driver->table[i].cmd_code,
+			driver->table[i].subsys_id,
+			driver->table[i].client_id,
+			driver->table[i].cmd_code_lo,
+			driver->table[i].cmd_code_hi,
+			driver->table[i].process_id);
+
+		bytes_in_buffer += bytes_written;
+
+		/* Check if there is room to add another table entry */
+		bytes_remaining = buf_size - bytes_in_buffer;
+		if (bytes_remaining < bytes_written)
+			break;
+	}
+	diag_dbgfs_table_index = i;
+
+	*ppos = 0;
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, bytes_in_buffer);
+
+	kfree(buf);
+	return ret;
+}
+
+#ifdef CONFIG_DIAG_HSIC_PIPE
+static ssize_t diag_dbgfs_read_hsic(struct file *file, char __user *ubuf,
+				    size_t count, loff_t *ppos)
+{
+	char *buf;
+	int ret;
+
+	buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+	if (!buf) {
+		pr_err("diag: %s, Error allocating memory\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret = scnprintf(buf, DEBUG_BUF_SIZE,
+		"hsic initialized: %d\n"
+		"hsic ch: %d\n"
+		"hsic enabled: %d\n"
+		"hsic_opened: %d\n"
+		"hisc_suspend: %d\n"
+		"in_busy_hsic_read_on_mdm: %d\n"
+		"in_busy_hsic_write_on_mdm: %d\n"
+		"in_busy_hsic_write: %d\n"
+		"in_busy_hsic_read: %d\n"
+		"usb_mdm_connected: %d\n"
+		"diag_read_mdm_work: %d\n"
+		"diag_read_hsic_work: %d\n"
+		"diag_disconnect_work: %d\n"
+		"diag_usb_read_complete_work: %d\n",
+		driver->hsic_initialized,
+		driver->hsic_ch,
+		driver->hsic_device_enabled,
+		driver->hsic_device_opened,
+		driver->hsic_suspend,
+		driver->in_busy_hsic_read_on_device,
+		driver->in_busy_hsic_write_on_device,
+		driver->in_busy_hsic_write,
+		driver->in_busy_hsic_read,
+		driver->usb_mdm_connected,
+		work_pending(&(driver->diag_read_mdm_work)),
+		work_pending(&(driver->diag_read_hsic_work)),
+		work_pending(&(driver->diag_disconnect_work)),
+		work_pending(&(driver->diag_usb_read_complete_work)));
+
+	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+	kfree(buf);
+	return ret;
+}
+
+const struct file_operations diag_dbgfs_hsic_ops = {
+	.read = diag_dbgfs_read_hsic,
+};
+#endif
+
+const struct file_operations diag_dbgfs_status_ops = {
+	.read = diag_dbgfs_read_status,
+};
+
+const struct file_operations diag_dbgfs_table_ops = {
+	.read = diag_dbgfs_read_table,
+};
+
+const struct file_operations diag_dbgfs_workpending_ops = {
+	.read = diag_dbgfs_read_workpending,
+};
+
+void diag_debugfs_init(void)
+{
+	diag_dbgfs_dent = debugfs_create_dir("diag", 0);
+	if (IS_ERR(diag_dbgfs_dent))
+		return;
+
+	debugfs_create_file("status", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_status_ops);
+
+	debugfs_create_file("table", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_table_ops);
+
+	debugfs_create_file("work_pending", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_workpending_ops);
+
+#ifdef CONFIG_DIAG_HSIC_PIPE
+	debugfs_create_file("hsic", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_hsic_ops);
+#endif
+
+	diag_dbgfs_table_index = 0;
+}
+
+void diag_debugfs_cleanup(void)
+{
+	if (diag_dbgfs_dent) {
+		debugfs_remove_recursive(diag_dbgfs_dent);
+		diag_dbgfs_dent = NULL;
+	}
+}
+#else
+void diag_debugfs_init(void) { }
+void diag_debugfs_cleanup(void) { }
+#endif
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index ad1fec9..743ddc1 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -86,4 +86,7 @@
 void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event);
 void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event);
 
+void diag_debugfs_init(void);
+void diag_debugfs_cleanup(void);
+
 #endif