Merge "diag: Move registration cleanup function to work queue" into msm-3.4
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index c0b82df..b70efe3 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -28,11 +28,6 @@
 	int signal_type;
 };
 
-#define DIAG_CON_APSS (0x0001)	/* Bit mask for APSS */
-#define DIAG_CON_MPSS (0x0002)	/* Bit mask for MPSS */
-#define DIAG_CON_LPASS (0x0004)	/* Bit mask for LPASS */
-#define DIAG_CON_WCNSS (0x0008)	/* Bit mask for WCNSS */
-
 enum {
 	DIAG_DCI_NO_ERROR = 1001,	/* No error */
 	DIAG_DCI_NO_REG,		/* Could not register */
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 2f356f0..95a85f2 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -58,6 +58,11 @@
 #define DIAG_CTRL_MSG_F3_MASK	11
 #define CONTROL_CHAR	0x7E
 
+#define DIAG_CON_APSS (0x0001)	/* Bit mask for APSS */
+#define DIAG_CON_MPSS (0x0002)	/* Bit mask for MPSS */
+#define DIAG_CON_LPASS (0x0004)	/* Bit mask for LPASS */
+#define DIAG_CON_WCNSS (0x0008)	/* Bit mask for WCNSS */
+
 /* Maximum number of pkt reg supported at initialization*/
 extern unsigned int diag_max_reg;
 extern unsigned int diag_threshold_reg;
@@ -224,6 +229,9 @@
 	struct work_struct diag_qdsp_mask_update_work;
 	struct work_struct diag_wcnss_mask_update_work;
 	struct work_struct diag_read_smd_dci_work;
+	struct work_struct diag_clean_modem_reg_work;
+	struct work_struct diag_clean_lpass_reg_work;
+	struct work_struct diag_clean_wcnss_reg_work;
 	uint8_t *msg_masks;
 	uint8_t *log_masks;
 	int log_masks_length;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 547f42f..240a514 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1268,6 +1268,12 @@
 			diag_read_smd_wcnss_cntl_work_fn);
 		INIT_WORK(&(driver->diag_read_smd_dci_work),
 						 diag_read_smd_dci_work_fn);
+		INIT_WORK(&(driver->diag_clean_modem_reg_work),
+						 diag_clean_modem_reg_fn);
+		INIT_WORK(&(driver->diag_clean_lpass_reg_work),
+						 diag_clean_lpass_reg_fn);
+		INIT_WORK(&(driver->diag_clean_wcnss_reg_work),
+						 diag_clean_wcnss_reg_fn);
 		diag_debugfs_init();
 		diagfwd_init();
 		diagfwd_cntl_init();
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 384c1bf..b228276 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1735,8 +1735,8 @@
 static void diag_smd_notify(void *ctxt, unsigned event)
 {
 	if (event == SMD_EVENT_CLOSE) {
-		pr_info("diag: clean modem registration\n");
-		diag_clear_reg(MODEM_PROC);
+		queue_work(driver->diag_cntl_wq,
+			 &(driver->diag_clean_modem_reg_work));
 		driver->ch = 0;
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
@@ -1750,8 +1750,8 @@
 static void diag_smd_qdsp_notify(void *ctxt, unsigned event)
 {
 	if (event == SMD_EVENT_CLOSE) {
-		pr_info("diag: clean lpass registration\n");
-		diag_clear_reg(QDSP_PROC);
+		queue_work(driver->diag_cntl_wq,
+			 &(driver->diag_clean_lpass_reg_work));
 		driver->chqdsp = 0;
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
@@ -1765,8 +1765,8 @@
 static void diag_smd_wcnss_notify(void *ctxt, unsigned event)
 {
 	if (event == SMD_EVENT_CLOSE) {
-		pr_info("diag: clean wcnss registration\n");
-		diag_clear_reg(WCNSS_PROC);
+		queue_work(driver->diag_cntl_wq,
+			 &(driver->diag_clean_wcnss_reg_work));
 		driver->ch_wcnss = 0;
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index de1a5b5..95abd21 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -20,9 +20,34 @@
 #ifdef CONFIG_DEBUG_FS
 #include <linux/debugfs.h>
 #endif
-
+/* tracks which peripheral is undergoing SSR */
+static uint16_t reg_dirty;
 #define HDR_SIZ 8
 
+void diag_clean_modem_reg_fn(struct work_struct *work)
+{
+	pr_debug("diag: clean modem registration\n");
+	reg_dirty |= DIAG_CON_MPSS;
+	diag_clear_reg(MODEM_PROC);
+	reg_dirty ^= DIAG_CON_MPSS;
+}
+
+void diag_clean_lpass_reg_fn(struct work_struct *work)
+{
+	pr_debug("diag: clean lpass registration\n");
+	reg_dirty |= DIAG_CON_LPASS;
+	diag_clear_reg(QDSP_PROC);
+	reg_dirty ^= DIAG_CON_LPASS;
+}
+
+void diag_clean_wcnss_reg_fn(struct work_struct *work)
+{
+	pr_debug("diag: clean wcnss registration\n");
+	reg_dirty |= DIAG_CON_WCNSS;
+	diag_clear_reg(WCNSS_PROC);
+	reg_dirty ^= DIAG_CON_WCNSS;
+}
+
 void diag_smd_cntl_notify(void *ctxt, unsigned event)
 {
 	int r1, r2;
@@ -105,6 +130,8 @@
 	struct bindpkt_params *temp;
 	void *buf = NULL;
 	smd_channel_t *smd_ch = NULL;
+	/* tracks which peripheral is sending registration */
+	uint16_t reg_mask = 0;
 
 	if (pkt_params == NULL) {
 		pr_alert("diag: Memory allocation failure\n");
@@ -114,12 +141,15 @@
 	if (proc_num == MODEM_PROC) {
 		buf = driver->buf_in_cntl;
 		smd_ch = driver->ch_cntl;
+		reg_mask = DIAG_CON_MPSS;
 	} else if (proc_num == QDSP_PROC) {
 		buf = driver->buf_in_qdsp_cntl;
 		smd_ch = driver->chqdsp_cntl;
+		reg_mask = DIAG_CON_LPASS;
 	} else if (proc_num == WCNSS_PROC) {
 		buf = driver->buf_in_wcnss_cntl;
 		smd_ch = driver->ch_wcnss_cntl;
+		reg_mask = DIAG_CON_WCNSS;
 	}
 
 	if (!smd_ch || !buf) {
@@ -180,8 +210,16 @@
 				temp -= pkt_params->count;
 				pkt_params->params = temp;
 				flag = 1;
-				diagchar_ioctl(NULL, DIAG_IOCTL_COMMAND_REG,
-						 (unsigned long)pkt_params);
+				/* peripheral undergoing SSR should not
+				 * record new registration
+				 */
+				if (!(reg_dirty & reg_mask))
+					diagchar_ioctl(NULL,
+					 DIAG_IOCTL_COMMAND_REG, (unsigned long)
+								pkt_params);
+				else
+					pr_err("diag: drop reg proc %d\n",
+								 proc_num);
 				kfree(temp);
 			}
 			buf = buf + HDR_SIZ + data_len;
@@ -275,6 +313,7 @@
 
 void diagfwd_cntl_init(void)
 {
+	reg_dirty = 0;
 	driver->polling_reg_flag = 0;
 	driver->diag_cntl_wq = create_singlethread_workqueue("diag_cntl_wq");
 	if (driver->buf_in_cntl == NULL) {
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 743ddc1..59e5e6b 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -85,7 +85,9 @@
 void diag_smd_cntl_notify(void *ctxt, unsigned event);
 void diag_smd_qdsp_cntl_notify(void *ctxt, unsigned event);
 void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event);
-
+void diag_clean_modem_reg_fn(struct work_struct *);
+void diag_clean_lpass_reg_fn(struct work_struct *);
+void diag_clean_wcnss_reg_fn(struct work_struct *);
 void diag_debugfs_init(void);
 void diag_debugfs_cleanup(void);