diag: Add diag changes to bring diag up to date

diag on the A-Family mainline is out of date. Create a commit
to bring diag up to date.

Change-Id: Ie24780b4b83a3cb378326357d70785a590167447
Signed-off-by: Dixon Peterson <dixonp@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/usbdiag.h b/arch/arm/mach-msm/include/mach/usbdiag.h
index d1e3605..d9320c3 100644
--- a/arch/arm/mach-msm/include/mach/usbdiag.h
+++ b/arch/arm/mach-msm/include/mach/usbdiag.h
@@ -1,6 +1,6 @@
 /* include/asm-arm/arch-msm/usbdiag.h
  *
- * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved.
+ * Copyright (c) 2008-2010, 2012, The Linux Foundation. All rights reserved.
  *
  * All source code in this file is licensed under the following license except
  * where indicated.
@@ -21,8 +21,11 @@
 #ifndef _DRIVERS_USB_DIAG_H_
 #define _DRIVERS_USB_DIAG_H_
 
+#include <linux/err.h>
+
 #define DIAG_LEGACY		"diag"
 #define DIAG_MDM		"diag_mdm"
+#define DIAG_QSC		"diag_qsc"
 
 #define USB_DIAG_CONNECT	0
 #define USB_DIAG_DISCONNECT	1
@@ -45,6 +48,7 @@
 	void *priv_usb;
 };
 
+#ifdef CONFIG_USB_G_ANDROID
 struct usb_diag_ch *usb_diag_open(const char *name, void *priv,
 		void (*notify)(void *, unsigned, struct diag_request *));
 void usb_diag_close(struct usb_diag_ch *ch);
@@ -52,7 +56,32 @@
 void usb_diag_free_req(struct usb_diag_ch *ch);
 int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req);
 int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req);
-
-int diag_read_from_cb(unsigned char * , int);
-
+#else
+static inline struct usb_diag_ch *usb_diag_open(const char *name, void *priv,
+		void (*notify)(void *, unsigned, struct diag_request *))
+{
+	return ERR_PTR(-ENODEV);
+}
+static inline void usb_diag_close(struct usb_diag_ch *ch)
+{
+}
+static inline
+int usb_diag_alloc_req(struct usb_diag_ch *ch, int n_write, int n_read)
+{
+	return -ENODEV;
+}
+static inline void usb_diag_free_req(struct usb_diag_ch *ch)
+{
+}
+static inline
+int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+	return -ENODEV;
+}
+static inline
+int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req)
+{
+	return -ENODEV;
+}
+#endif /* CONFIG_USB_G_ANDROID */
 #endif /* _DRIVERS_USB_DIAG_H_ */
diff --git a/drivers/char/diag/Kconfig b/drivers/char/diag/Kconfig
index 8f8707f..91fcdfc 100644
--- a/drivers/char/diag/Kconfig
+++ b/drivers/char/diag/Kconfig
@@ -30,9 +30,9 @@
 	 SDIO Transport Layer for DIAG Router
 endmenu
 
-menu "HSIC support for DIAG"
+menu "HSIC/SMUX support for DIAG"
 
-config DIAG_BRIDGE_CODE
+config DIAGFWD_BRIDGE_CODE
 	depends on USB_QCOM_DIAG_BRIDGE
 	default y
 	bool "Enable QSC/9K DIAG traffic over SMUX/HSIC"
diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile
index 6ecc970..c9204ea 100644
--- a/drivers/char/diag/Makefile
+++ b/drivers/char/diag/Makefile
@@ -1,5 +1,6 @@
 obj-$(CONFIG_DIAG_CHAR) := diagchar.o
 obj-$(CONFIG_DIAG_SDIO_PIPE) += diagfwd_sdio.o
-obj-$(CONFIG_DIAG_BRIDGE_CODE) += diagfwd_hsic.o
-obj-$(CONFIG_DIAG_BRIDGE_CODE) += diagfwd_smux.o
+obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_bridge.o
+obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_hsic.o
+obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_smux.o
 diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagmem.o diagfwd_cntl.o diag_dci.o diag_masks.o diag_debugfs.o
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 24fc99a..d4b1856 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -38,73 +38,48 @@
 struct mutex dci_log_mask_mutex;
 struct mutex dci_event_mask_mutex;
 
-smd_channel_t *ch_dci_temp;
-
 #define DCI_CHK_CAPACITY(entry, new_data_len)				\
 ((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0)	\
 
-static void diag_smd_dci_send_req(int proc_num)
+/* Process the data read from the smd dci channel */
+int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
+								int recd_bytes)
 {
-	void *buf = NULL;
-	smd_channel_t *smd_ch = NULL;
-	int recd_bytes, read_bytes, dci_pkt_len, i;
+	int read_bytes, dci_pkt_len, i;
 	uint8_t recv_pkt_cmd_code;
 
-	if (driver->in_busy_dci)
-		return;
-
-	if (proc_num == MODEM_PROC) {
-		buf = driver->buf_in_dci;
-		smd_ch = driver->ch_dci;
+	/* Each SMD read can have multiple DCI packets */
+	read_bytes = 0;
+	while (read_bytes < recd_bytes) {
+		/* read actual length of dci pkt */
+		dci_pkt_len = *(uint16_t *)(buf+2);
+		/* process one dci packet */
+		pr_debug("diag: bytes read = %d, single dci pkt len = %d\n",
+			read_bytes, dci_pkt_len);
+		/* print_hex_dump(KERN_DEBUG, "Single DCI packet :",
+		 DUMP_PREFIX_ADDRESS, 16, 1, buf, 5 + dci_pkt_len, 1);*/
+		recv_pkt_cmd_code = *(uint8_t *)(buf+4);
+		if (recv_pkt_cmd_code == LOG_CMD_CODE)
+			extract_dci_log(buf+4);
+		else if (recv_pkt_cmd_code == EVENT_CMD_CODE)
+			extract_dci_events(buf+4);
+		else
+			extract_dci_pkt_rsp(buf); /* pkt response */
+		read_bytes += 5 + dci_pkt_len;
+		buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
 	}
-
-	if (!smd_ch || !buf)
-		return;
-
-	recd_bytes = smd_read_avail(smd_ch);
-	if (recd_bytes > IN_BUF_SIZE) {
-		if (recd_bytes < MAX_IN_BUF_SIZE) {
-			pr_err("diag: SMD DCI sending pkt upto %d bytes",
-				recd_bytes);
-			buf = krealloc(buf, recd_bytes, GFP_KERNEL);
-		} else {
-			pr_err("diag: DCI pkt > %d bytes", MAX_IN_BUF_SIZE);
-			return;
+	/* wake up all sleeping DCI clients which have some data */
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		if (driver->dci_client_tbl[i].client &&
+			driver->dci_client_tbl[i].data_len) {
+			smd_info->in_busy_1 = 1;
+			diag_update_sleeping_process(
+				driver->dci_client_tbl[i].client->tgid,
+					 DCI_DATA_TYPE);
 		}
 	}
-	if (buf && recd_bytes > 0) {
-		smd_read(smd_ch, buf, recd_bytes);
-		pr_debug("diag: data received %d bytes\n", recd_bytes);
-		/* Each SMD read can have multiple DCI packets */
-		read_bytes = 0;
-		while (read_bytes < recd_bytes) {
-			/* read actual length of dci pkt */
-			dci_pkt_len = *(uint16_t *)(buf+2);
-			/* process one dci packet */
-			pr_debug("diag: bytes read = %d, single dci pkt len = %d\n",
-				read_bytes, dci_pkt_len);
-			/* print_hex_dump(KERN_DEBUG, "Single DCI packet :",
-			 DUMP_PREFIX_ADDRESS, 16, 1, buf, 5 + dci_pkt_len, 1);*/
-			recv_pkt_cmd_code = *(uint8_t *)(buf+4);
-			if (recv_pkt_cmd_code == LOG_CMD_CODE)
-				extract_dci_log(buf+4);
-			else if (recv_pkt_cmd_code == EVENT_CMD_CODE)
-				extract_dci_events(buf+4);
-			else
-				extract_dci_pkt_rsp(buf); /* pkt response */
-			read_bytes += 5 + dci_pkt_len;
-			buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
-		}
-		/* wake up all sleeping DCI clients which have some data */
-		for (i = 0; i < MAX_DCI_CLIENTS; i++)
-			if (driver->dci_client_tbl[i].client &&
-				driver->dci_client_tbl[i].data_len) {
-				driver->in_busy_dci = 1;
-				diag_update_sleeping_process(
-					driver->dci_client_tbl[i].client->tgid,
-						 DCI_DATA_TYPE);
-			}
-	}
+
+	return 0;
 }
 
 void extract_dci_pkt_rsp(unsigned char *buf)
@@ -163,7 +138,8 @@
 					buf+4+cmd_code_len, write_len);
 				entry->data_len += write_len;
 				/* delete immediate response entry */
-				if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
+				if (driver->smd_dci[MODEM_DATA].
+					buf_in_1[8+cmd_code_len] != 0x80)
 					driver->req_tracking_tbl[index].pid = 0;
 				break;
 			}
@@ -303,23 +279,22 @@
 	}
 }
 
-void diag_read_smd_dci_work_fn(struct work_struct *work)
-{
-	diag_smd_dci_send_req(MODEM_PROC);
-}
-
 void diag_update_smd_dci_work_fn(struct work_struct *work)
 {
+	struct diag_smd_info *smd_info = container_of(work,
+						struct diag_smd_info,
+						diag_notify_update_smd_work);
 	int i, j;
 	char dirty_bits[16];
 	uint8_t *client_log_mask_ptr;
 	uint8_t *log_mask_ptr;
 	int ret;
+	int index = smd_info->peripheral;
 
 	/* Update the peripheral(s) with the dci log and event masks */
 
 	/* If the cntl channel is not up, we can't update logs and events */
-	if (!driver->ch_cntl)
+	if (!driver->smd_cntl[index].ch)
 		return;
 
 	memset(dirty_bits, 0, 16 * sizeof(uint8_t));
@@ -352,9 +327,11 @@
 	}
 	mutex_unlock(&dci_log_mask_mutex);
 
-	ret = diag_send_dci_log_mask(driver->ch_cntl);
+	ret = diag_send_dci_log_mask(driver->smd_cntl[index].ch);
 
-	ret = diag_send_dci_event_mask(driver->ch_cntl);
+	ret = diag_send_dci_event_mask(driver->smd_cntl[index].ch);
+
+	smd_info->notify_context = 0;
 }
 
 void diag_dci_notify_client(int peripheral_mask, int data)
@@ -380,41 +357,23 @@
 	} /* end of loop for all DCI clients */
 }
 
-static void diag_smd_dci_notify(void *ctxt, unsigned event)
-{
-	if (event == SMD_EVENT_CLOSE) {
-		driver->ch_dci = 0;
-		/* Notify the clients of the close */
-		diag_dci_notify_client(DIAG_CON_MPSS, DIAG_STATUS_CLOSED);
-		return;
-	} else if (event == SMD_EVENT_OPEN) {
-
-		if (ch_dci_temp)
-			driver->ch_dci = ch_dci_temp;
-
-		queue_work(driver->diag_dci_wq,
-			&(driver->diag_update_smd_dci_work));
-
-		/* Notify the clients of the open */
-		diag_dci_notify_client(DIAG_CON_MPSS, DIAG_STATUS_OPEN);
-	}
-
-	queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
-}
-
 static int diag_dci_probe(struct platform_device *pdev)
 {
 	int err = 0;
+	int index;
 
 	if (pdev->id == SMD_APPS_MODEM) {
-		err = smd_open("DIAG_2", &driver->ch_dci, driver,
-						diag_smd_dci_notify);
+		index = MODEM_DATA;
+		err = smd_open("DIAG_2", &driver->smd_dci[index].ch,
+					&driver->smd_dci[index],
+					diag_smd_notify);
+		driver->smd_dci[index].ch_save =
+					driver->smd_dci[index].ch;
 		if (err)
-			pr_err("diag: cannot open DCI port, Id = %d, err ="
-				" %d\n", pdev->id, err);
-		else
-			ch_dci_temp = driver->ch_dci;
+			pr_err("diag: In %s, cannot open DCI port, Id = %d, err: %d\n",
+				__func__, pdev->id, err);
 	}
+
 	return err;
 }
 
@@ -422,6 +381,7 @@
 					 int len, int index)
 {
 	int i;
+	int status = 0;
 
 	/* remove UID from user space pkt before sending to peripheral */
 	buf = buf + 4;
@@ -439,15 +399,23 @@
 
 	driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */
 
-	if (entry.client_id == MODEM_PROC && driver->ch_dci) {
-		smd_write(driver->ch_dci, driver->apps_dci_buf, len + 10);
-		i = DIAG_DCI_NO_ERROR;
-	} else {
+	for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
+		if (entry.client_id == driver->smd_dci[i].peripheral) {
+			if (driver->smd_dci[i].ch) {
+				smd_write(driver->smd_dci[i].ch,
+					driver->apps_dci_buf, len + 10);
+				status = DIAG_DCI_NO_ERROR;
+			}
+			break;
+		}
+	}
+
+	if (status != DIAG_DCI_NO_ERROR) {
 		pr_alert("diag: check DCI channel\n");
-		i = DIAG_DCI_SEND_DATA_FAIL;
+		status = DIAG_DCI_SEND_DATA_FAIL;
 	}
 	mutex_unlock(&driver->dci_mutex);
-	return i;
+	return status;
 }
 
 int diag_register_dci_transaction(int uid)
@@ -487,8 +455,9 @@
 	uint8_t *event_mask_ptr;
 	int offset = 0;
 
-	if (!driver->ch_dci) {
-		pr_err("diag: ch_dci not valid for dci updates\n");
+	if (!driver->smd_dci[MODEM_DATA].ch) {
+		pr_err("diag: DCI smd channel for peripheral %d not valid for dci updates\n",
+			driver->smd_dci[MODEM_DATA].peripheral);
 		return DIAG_DCI_SEND_DATA_FAIL;
 	}
 
@@ -613,7 +582,7 @@
 			ret = DIAG_DCI_NO_ERROR;
 		}
 		/* send updated mask to peripherals */
-		ret = diag_send_dci_log_mask(driver->ch_cntl);
+		ret = diag_send_dci_log_mask(driver->smd_cntl[MODEM_DATA].ch);
 	} else if (*(int *)temp == DCI_EVENT_TYPE) {
 		/* find client id and table */
 		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
@@ -659,7 +628,7 @@
 			ret = DIAG_DCI_NO_ERROR;
 		}
 		/* send updated mask to peripherals */
-		ret = diag_send_dci_event_mask(driver->ch_cntl);
+		ret = diag_send_dci_event_mask(driver->smd_cntl[MODEM_DATA].ch);
 	} else {
 		pr_alert("diag: Incorrect DCI transaction\n");
 	}
@@ -872,27 +841,19 @@
 int diag_dci_init(void)
 {
 	int success = 0;
-
-	ch_dci_temp = NULL;
+	int i;
 
 	driver->dci_tag = 0;
 	driver->dci_client_id = 0;
 	driver->num_dci_client = 0;
-	driver->in_busy_dci = 0;
 	mutex_init(&driver->dci_mutex);
 	mutex_init(&dci_log_mask_mutex);
 	mutex_init(&dci_event_mask_mutex);
-	if (driver->buf_in_dci == NULL) {
-		driver->buf_in_dci = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_dci == NULL)
-			goto err;
-	}
-	if (driver->write_ptr_dci == NULL) {
-		driver->write_ptr_dci = kzalloc(
-			sizeof(struct diag_write_device), GFP_KERNEL);
-		if (driver->write_ptr_dci == NULL)
-			goto err;
-	}
+	success = diag_smd_constructor(&driver->smd_dci[MODEM_DATA],
+					MODEM_DATA, SMD_DCI_TYPE);
+	if (!success)
+		goto err;
+
 	if (driver->req_tracking_tbl == NULL) {
 		driver->req_tracking_tbl = kzalloc(dci_max_reg *
 			sizeof(struct dci_pkt_req_tracking_tbl), GFP_KERNEL);
@@ -922,8 +883,8 @@
 	kfree(driver->req_tracking_tbl);
 	kfree(driver->dci_client_tbl);
 	kfree(driver->apps_dci_buf);
-	kfree(driver->buf_in_dci);
-	kfree(driver->write_ptr_dci);
+	for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
+		diag_smd_destructor(&driver->smd_dci[i]);
 	if (driver->diag_dci_wq)
 		destroy_workqueue(driver->diag_dci_wq);
 	return DIAG_DCI_NO_REG;
@@ -931,13 +892,14 @@
 
 void diag_dci_exit(void)
 {
-	smd_close(driver->ch_dci);
-	driver->ch_dci = 0;
+	int i;
+
+	for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
+		diag_smd_destructor(&driver->smd_dci[i]);
+
 	platform_driver_unregister(&msm_diag_dci_driver);
 	kfree(driver->req_tracking_tbl);
 	kfree(driver->dci_client_tbl);
 	kfree(driver->apps_dci_buf);
-	kfree(driver->buf_in_dci);
-	kfree(driver->write_ptr_dci);
 	destroy_workqueue(driver->diag_dci_wq);
 }
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 3f62e5e..af89ac8 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -78,8 +78,10 @@
 
 int diag_dci_init(void);
 void diag_dci_exit(void);
-void diag_read_smd_dci_work_fn(struct work_struct *);
 void diag_update_smd_dci_work_fn(struct work_struct *);
+void diag_dci_notify_client(int peripheral_mask, int data);
+int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
+								int recd_bytes);
 int diag_process_dci_transaction(unsigned char *buf, int len);
 int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
 							 int len, int index);
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 0048007..d852d75 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -16,6 +16,7 @@
 #include <linux/debugfs.h>
 #include "diagchar.h"
 #include "diagfwd.h"
+#include "diagfwd_bridge.h"
 
 #define DEBUG_BUF_SIZE	4096
 static struct dentry *diag_dbgfs_dent;
@@ -47,34 +48,34 @@
 		"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_lpass_1: %d\n"
-		"in_busy_lpass_2: %d\n"
-		"in_busy_wcnss_1: %d\n"
-		"in_busy_wcnss_2: %d\n"
-		"in_busy_dci: %d\n"
+		"Modem in_busy_1: %d\n"
+		"Modem in_busy_2: %d\n"
+		"LPASS in_busy_1: %d\n"
+		"LPASS in_busy_2: %d\n"
+		"RIVA in_busy_1: %d\n"
+		"RIVA in_busy_2: %d\n"
+		"DCI Modem in_busy_1: %d\n"
 		"logging_mode: %d\n",
-		(unsigned int)driver->ch,
-		(unsigned int)driver->chlpass,
-		(unsigned int)driver->ch_wcnss,
-		(unsigned int)driver->ch_dci,
-		(unsigned int)driver->ch_cntl,
-		(unsigned int)driver->chlpass_cntl,
-		(unsigned int)driver->ch_wcnss_cntl,
+		(unsigned int)driver->smd_data[MODEM_DATA].ch,
+		(unsigned int)driver->smd_data[LPASS_DATA].ch,
+		(unsigned int)driver->smd_data[WCNSS_DATA].ch,
+		(unsigned int)driver->smd_dci[MODEM_DATA].ch,
+		(unsigned int)driver->smd_cntl[MODEM_DATA].ch,
+		(unsigned int)driver->smd_cntl[LPASS_DATA].ch,
+		(unsigned int)driver->smd_cntl[WCNSS_DATA].ch,
 		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_lpass_1,
-		driver->in_busy_lpass_2,
-		driver->in_busy_wcnss_1,
-		driver->in_busy_wcnss_2,
-		driver->in_busy_dci,
+		driver->smd_data[MODEM_DATA].in_busy_1,
+		driver->smd_data[MODEM_DATA].in_busy_2,
+		driver->smd_data[LPASS_DATA].in_busy_1,
+		driver->smd_data[LPASS_DATA].in_busy_2,
+		driver->smd_data[WCNSS_DATA].in_busy_1,
+		driver->smd_data[WCNSS_DATA].in_busy_2,
+		driver->smd_dci[MODEM_DATA].in_busy_1,
 		driver->logging_mode);
 
 #ifdef CONFIG_DIAG_OVER_USB
@@ -103,29 +104,49 @@
 	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_lpass_work: %d\n"
-		"diag_read_smd_lpass_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_lpass_mask_update_work: %d\n"
-		"diag_wcnss_mask_update_work: %d\n"
-		"diag_read_smd_dci_work: %d\n"
-		"diag_update_smd_dci_work: %d\n",
+		"Modem data diag_read_smd_work: %d\n"
+		"LPASS data diag_read_smd_work: %d\n"
+		"RIVA data diag_read_smd_work: %d\n"
+		"Modem cntl diag_read_smd_work: %d\n"
+		"LPASS cntl diag_read_smd_work: %d\n"
+		"RIVA cntl diag_read_smd_work: %d\n"
+		"Modem dci diag_read_smd_work: %d\n"
+		"Modem data diag_notify_update_smd_work: %d\n"
+		"LPASS data diag_notify_update_smd_work: %d\n"
+		"RIVA data diag_notify_update_smd_work: %d\n"
+		"Modem cntl diag_notify_update_smd_work: %d\n"
+		"LPASS cntl diag_notify_update_smd_work: %d\n"
+		"RIVA cntl diag_notify_update_smd_work: %d\n"
+		"Modem dci diag_notify_update_smd_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_lpass_work)),
-		work_pending(&(driver->diag_read_smd_lpass_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_lpass_mask_update_work)),
-		work_pending(&(driver->diag_wcnss_mask_update_work)),
-		work_pending(&(driver->diag_read_smd_dci_work)),
-		work_pending(&(driver->diag_update_smd_dci_work)));
+		work_pending(&(driver->smd_data[MODEM_DATA].
+							diag_read_smd_work)),
+		work_pending(&(driver->smd_data[LPASS_DATA].
+							diag_read_smd_work)),
+		work_pending(&(driver->smd_data[WCNSS_DATA].
+							diag_read_smd_work)),
+		work_pending(&(driver->smd_cntl[MODEM_DATA].
+							diag_read_smd_work)),
+		work_pending(&(driver->smd_cntl[LPASS_DATA].
+							diag_read_smd_work)),
+		work_pending(&(driver->smd_cntl[WCNSS_DATA].
+							diag_read_smd_work)),
+		work_pending(&(driver->smd_dci[MODEM_DATA].
+							diag_read_smd_work)),
+		work_pending(&(driver->smd_data[MODEM_DATA].
+						diag_notify_update_smd_work)),
+		work_pending(&(driver->smd_data[LPASS_DATA].
+						diag_notify_update_smd_work)),
+		work_pending(&(driver->smd_data[WCNSS_DATA].
+						diag_notify_update_smd_work)),
+		work_pending(&(driver->smd_cntl[MODEM_DATA].
+						diag_notify_update_smd_work)),
+		work_pending(&(driver->smd_cntl[LPASS_DATA].
+						diag_notify_update_smd_work)),
+		work_pending(&(driver->smd_cntl[WCNSS_DATA].
+						diag_notify_update_smd_work)),
+		work_pending(&(driver->smd_dci[MODEM_DATA].
+						diag_notify_update_smd_work)));
 
 #ifdef CONFIG_DIAG_OVER_USB
 	ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
@@ -164,6 +185,15 @@
 	}
 
 	bytes_remaining = buf_size;
+
+	if (diag_dbgfs_table_index == 0) {
+		bytes_written = scnprintf(buf+bytes_in_buffer, bytes_remaining,
+			"Client ids: Modem: %d, LPASS: %d, "
+			"WCNSS: %d, APPS: %d\n",
+			MODEM_DATA, LPASS_DATA, WCNSS_DATA, APPS_DATA);
+		bytes_in_buffer += bytes_written;
+	}
+
 	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)
@@ -188,7 +218,7 @@
 		if (bytes_remaining < bytes_written)
 			break;
 	}
-	diag_dbgfs_table_index = i;
+	diag_dbgfs_table_index = i+1;
 
 	*ppos = 0;
 	ret = simple_read_from_buffer(ubuf, count, ppos, buf, bytes_in_buffer);
@@ -197,8 +227,8 @@
 	return ret;
 }
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-static ssize_t diag_dbgfs_read_hsic(struct file *file, char __user *ubuf,
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+static ssize_t diag_dbgfs_read_bridge(struct file *file, char __user *ubuf,
 				    size_t count, loff_t *ppos)
 {
 	char *buf;
@@ -222,13 +252,17 @@
 		"count_hsic_write_pool: %d\n"
 		"diag_hsic_pool: %x\n"
 		"diag_hsic_write_pool: %x\n"
-		"write_len_mdm: %d\n"
+		"HSIC write_len: %d\n"
 		"num_hsic_buf_tbl_entries: %d\n"
-		"usb_mdm_connected: %d\n"
-		"diag_read_mdm_work: %d\n"
+		"HSIC usb_connected: %d\n"
+		"HSIC diag_read_work: %d\n"
 		"diag_read_hsic_work: %d\n"
 		"diag_disconnect_work: %d\n"
-		"diag_usb_read_complete_work: %d\n",
+		"diag_usb_read_complete_work: %d\n"
+		"smux ch: %d\n"
+		"smux enabled %d\n"
+		"smux in busy %d\n"
+		"smux connected %d\n",
 		driver->hsic_ch,
 		driver->hsic_inited,
 		driver->hsic_device_enabled,
@@ -240,13 +274,17 @@
 		driver->count_hsic_write_pool,
 		(unsigned int)driver->diag_hsic_pool,
 		(unsigned int)driver->diag_hsic_write_pool,
-		driver->write_len_mdm,
+			diag_bridge[HSIC].write_len,
 		driver->num_hsic_buf_tbl_entries,
-		driver->usb_mdm_connected,
-		work_pending(&(driver->diag_read_mdm_work)),
+			diag_bridge[HSIC].usb_connected,
+			work_pending(&(diag_bridge[HSIC].diag_read_work)),
 		work_pending(&(driver->diag_read_hsic_work)),
 		work_pending(&(driver->diag_disconnect_work)),
-		work_pending(&(driver->diag_usb_read_complete_work)));
+		work_pending(&(diag_bridge[HSIC].usb_read_complete_work)),
+		driver->lcid,
+		driver->diag_smux_enabled,
+		driver->in_busy_smux,
+		driver->smux_connected);
 
 	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
 
@@ -254,8 +292,8 @@
 	return ret;
 }
 
-const struct file_operations diag_dbgfs_hsic_ops = {
-	.read = diag_dbgfs_read_hsic,
+const struct file_operations diag_dbgfs_bridge_ops = {
+	.read = diag_dbgfs_read_bridge,
 };
 #endif
 
@@ -286,9 +324,9 @@
 	debugfs_create_file("work_pending", 0444, diag_dbgfs_dent, 0,
 		&diag_dbgfs_workpending_ops);
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-	debugfs_create_file("hsic", 0444, diag_dbgfs_dent, 0,
-		&diag_dbgfs_hsic_ops);
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+	debugfs_create_file("bridge", 0444, diag_dbgfs_dent, 0,
+		&diag_dbgfs_bridge_ops);
 #endif
 
 	diag_dbgfs_table_index = 0;
diff --git a/drivers/char/diag/diag_masks.c b/drivers/char/diag/diag_masks.c
index 5316548..e8567db 100644
--- a/drivers/char/diag/diag_masks.c
+++ b/drivers/char/diag/diag_masks.c
@@ -26,6 +26,9 @@
 #define ALL_SSID		-1
 #define MAX_SSID_PER_RANGE	100
 
+#define FEATURE_MASK_LEN_BYTES		1
+#define APPS_RESPOND_LOG_ON_DEMAND	0x04
+
 struct mask_info {
 	int equip_id;
 	int num_items;
@@ -294,29 +297,24 @@
 	mutex_unlock(&driver->diagchar_mutex);
 }
 
-void diag_modem_mask_update_fn(struct work_struct *work)
+void diag_mask_update_fn(struct work_struct *work)
 {
-	diag_send_msg_mask_update(driver->ch_cntl, ALL_SSID,
-					   ALL_SSID, MODEM_PROC);
-	diag_send_log_mask_update(driver->ch_cntl, ALL_EQUIP_ID);
-	diag_send_event_mask_update(driver->ch_cntl, diag_event_num_bytes);
-}
+	struct diag_smd_info *smd_info = container_of(work,
+						struct diag_smd_info,
+						diag_notify_update_smd_work);
+	if (!smd_info) {
+		pr_err("diag: In %s, smd info is null, cannot update masks for the peripheral\n",
+			__func__);
+		return;
+	}
 
-void diag_lpass_mask_update_fn(struct work_struct *work)
-{
-	diag_send_msg_mask_update(driver->chlpass_cntl, ALL_SSID,
-						   ALL_SSID, LPASS_PROC);
-	diag_send_log_mask_update(driver->chlpass_cntl, ALL_EQUIP_ID);
-	diag_send_event_mask_update(driver->chlpass_cntl, diag_event_num_bytes);
-}
+	diag_send_msg_mask_update(smd_info->ch, ALL_SSID, ALL_SSID,
+						smd_info->peripheral);
+	diag_send_log_mask_update(smd_info->ch, ALL_EQUIP_ID);
+	diag_send_event_mask_update(smd_info->ch, diag_event_num_bytes);
+	diag_send_feature_mask_update(smd_info->ch, smd_info->peripheral);
 
-void diag_wcnss_mask_update_fn(struct work_struct *work)
-{
-	diag_send_msg_mask_update(driver->ch_wcnss_cntl, ALL_SSID,
-						   ALL_SSID, WCNSS_PROC);
-	diag_send_log_mask_update(driver->ch_wcnss_cntl, ALL_EQUIP_ID);
-	diag_send_event_mask_update(driver->ch_wcnss_cntl,
-						 diag_event_num_bytes);
+	smd_info->notify_context = 0;
 }
 
 void diag_send_log_mask_update(smd_channel_t *ch, int equip_id)
@@ -467,6 +465,42 @@
 	mutex_unlock(&driver->diag_cntl_mutex);
 }
 
+void diag_send_feature_mask_update(smd_channel_t *ch, int proc)
+{
+	void *buf = driver->buf_feature_mask_update;
+	int header_size = sizeof(struct diag_ctrl_feature_mask);
+	int wr_size = -ENOMEM, retry_count = 0, timer;
+	uint8_t feature_byte = 0;
+
+	mutex_lock(&driver->diag_cntl_mutex);
+	/* send feature mask update */
+	driver->feature_mask->ctrl_pkt_id = DIAG_CTRL_MSG_FEATURE;
+	driver->feature_mask->ctrl_pkt_data_len = 4 + FEATURE_MASK_LEN_BYTES;
+	driver->feature_mask->feature_mask_len = FEATURE_MASK_LEN_BYTES;
+	memcpy(buf, driver->feature_mask, header_size);
+	feature_byte |= APPS_RESPOND_LOG_ON_DEMAND;
+	memcpy(buf+header_size, &feature_byte, FEATURE_MASK_LEN_BYTES);
+
+	if (ch) {
+		while (retry_count < 3) {
+			wr_size = smd_write(ch, buf, header_size +
+						FEATURE_MASK_LEN_BYTES);
+			if (wr_size == -ENOMEM) {
+				retry_count++;
+				for (timer = 0; timer < 5; timer++)
+					udelay(2000);
+			} else
+				break;
+		}
+		if (wr_size != header_size + FEATURE_MASK_LEN_BYTES)
+			pr_err("diag: proc %d fail feature update %d, tried %d",
+			   proc, wr_size, header_size + FEATURE_MASK_LEN_BYTES);
+	} else
+		pr_err("diag: ch invalid, feature update on proc %d\n", proc);
+	mutex_unlock(&driver->diag_cntl_mutex);
+
+}
+
 int diag_process_apps_masks(unsigned char *buf, int len)
 {
 	int packet_type = 1;
@@ -493,15 +527,13 @@
 			payload_length = 8 + ((*(int *)(buf + 4)) + 7)/8;
 			for (i = 0; i < payload_length; i++)
 				*(int *)(driver->apps_rsp_buf+12+i) = *(buf+i);
-			if (driver->ch_cntl)
-				diag_send_log_mask_update(driver->ch_cntl,
-				*(int *)buf);
-			if (driver->chlpass_cntl)
-				diag_send_log_mask_update(driver->chlpass_cntl,
-				*(int *)buf);
-			if (driver->ch_wcnss_cntl)
-				diag_send_log_mask_update(driver->ch_wcnss_cntl,
-				*(int *)buf);
+
+			for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) {
+				if (driver->smd_cntl[i].ch)
+					diag_send_log_mask_update(
+						driver->smd_cntl[i].ch,
+						*(int *)buf);
+			}
 			encode_rsp_and_send(12 + payload_length - 1);
 			return 0;
 		}
@@ -509,7 +541,8 @@
 	} /* Get log masks */
 	else if (*buf == 0x73 && *(int *)(buf+4) == 4) {
 #if defined(CONFIG_DIAG_OVER_USB)
-		if (!(driver->ch) && chk_apps_only()) {
+		if (!(driver->smd_data[MODEM_DATA].ch) &&
+						chk_apps_only()) {
 			equip_id = *(int *)(buf + 8);
 			num_items = *(int *)(buf + 12);
 			driver->apps_rsp_buf[0] = 0x73;
@@ -541,15 +574,13 @@
 			driver->apps_rsp_buf[3] = 0x0;
 			*(int *)(driver->apps_rsp_buf + 4) = 0x0;
 			*(int *)(driver->apps_rsp_buf + 8) = 0x0; /* status */
-			if (driver->ch_cntl)
-				diag_send_log_mask_update(driver->ch_cntl,
-				ALL_EQUIP_ID);
-			if (driver->chlpass_cntl)
-				diag_send_log_mask_update(driver->chlpass_cntl,
-				ALL_EQUIP_ID);
-			if (driver->ch_wcnss_cntl)
-				diag_send_log_mask_update(driver->ch_wcnss_cntl,
-				ALL_EQUIP_ID);
+			for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) {
+				if (driver->smd_cntl[i].ch)
+					diag_send_log_mask_update(
+						driver->smd_cntl[i].ch,
+						ALL_EQUIP_ID);
+
+			}
 			encode_rsp_and_send(11);
 			return 0;
 		}
@@ -559,7 +590,8 @@
 		ssid_first = *(uint16_t *)(buf + 2);
 		ssid_last = *(uint16_t *)(buf + 4);
 #if defined(CONFIG_DIAG_OVER_USB)
-		if (!(driver->ch) && chk_apps_only()) {
+		if (!(driver->smd_data[MODEM_DATA].ch) &&
+						chk_apps_only()) {
 			driver->apps_rsp_buf[0] = 0x7d;
 			driver->apps_rsp_buf[1] = 0x3;
 			*(uint16_t *)(driver->apps_rsp_buf+2) = ssid_first;
@@ -599,15 +631,14 @@
 			for (i = 0; i < 8 + ssid_range; i++)
 				*(driver->apps_rsp_buf + i) = *(buf+i);
 			*(driver->apps_rsp_buf + 6) = 0x1;
-			if (driver->ch_cntl)
-				diag_send_msg_mask_update(driver->ch_cntl,
-				ssid_first, ssid_last, MODEM_PROC);
-			if (driver->chlpass_cntl)
-				diag_send_msg_mask_update(driver->chlpass_cntl,
-				ssid_first, ssid_last, LPASS_PROC);
-			if (driver->ch_wcnss_cntl)
-				diag_send_msg_mask_update(driver->ch_wcnss_cntl,
-				ssid_first, ssid_last, WCNSS_PROC);
+			for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) {
+				if (driver->smd_cntl[i].ch)
+					diag_send_msg_mask_update(
+						driver->smd_cntl[i].ch,
+						ssid_first, ssid_last,
+						driver->smd_cntl[i].peripheral);
+
+			}
 			encode_rsp_and_send(8 + ssid_range - 1);
 			return 0;
 		}
@@ -625,15 +656,14 @@
 			driver->apps_rsp_buf[3] = 0; /* rsvd */
 			*(int *)(driver->apps_rsp_buf + 4) = rt_mask;
 			/* send msg mask update to peripheral */
-			if (driver->ch_cntl)
-				diag_send_msg_mask_update(driver->ch_cntl,
-				ALL_SSID, ALL_SSID, MODEM_PROC);
-			if (driver->chlpass_cntl)
-				diag_send_msg_mask_update(driver->chlpass_cntl,
-				ALL_SSID, ALL_SSID, LPASS_PROC);
-			if (driver->ch_wcnss_cntl)
-				diag_send_msg_mask_update(driver->ch_wcnss_cntl,
-				ALL_SSID, ALL_SSID, WCNSS_PROC);
+			for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) {
+				if (driver->smd_cntl[i].ch)
+					diag_send_msg_mask_update(
+						driver->smd_cntl[i].ch,
+						ALL_SSID, ALL_SSID,
+						driver->smd_cntl[i].peripheral);
+
+			}
 			encode_rsp_and_send(7);
 			return 0;
 		}
@@ -652,16 +682,12 @@
 				EVENT_LAST_ID + 1;
 			memcpy(driver->apps_rsp_buf+6, driver->event_masks,
 				EVENT_LAST_ID/8+1);
-			if (driver->ch_cntl)
-				diag_send_event_mask_update(driver->ch_cntl,
-				diag_event_num_bytes);
-			if (driver->chlpass_cntl)
-				diag_send_event_mask_update(
-				driver->chlpass_cntl,
-				diag_event_num_bytes);
-			if (driver->ch_wcnss_cntl)
-				diag_send_event_mask_update(
-				driver->ch_wcnss_cntl, diag_event_num_bytes);
+			for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) {
+				if (driver->smd_cntl[i].ch)
+					diag_send_event_mask_update(
+						driver->smd_cntl[i].ch,
+						diag_event_num_bytes);
+			}
 			encode_rsp_and_send(6 + EVENT_LAST_ID/8);
 			return 0;
 		}
@@ -675,20 +701,26 @@
 			driver->apps_rsp_buf[0] = 0x60;
 			driver->apps_rsp_buf[1] = 0x0;
 			driver->apps_rsp_buf[2] = 0x0;
-			if (driver->ch_cntl)
-				diag_send_event_mask_update(driver->ch_cntl,
-				diag_event_num_bytes);
-			if (driver->chlpass_cntl)
-				diag_send_event_mask_update(
-				driver->chlpass_cntl,
-				diag_event_num_bytes);
-			if (driver->ch_wcnss_cntl)
-				diag_send_event_mask_update(
-				driver->ch_wcnss_cntl, diag_event_num_bytes);
+			for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) {
+				if (driver->smd_cntl[i].ch)
+					diag_send_event_mask_update(
+						driver->smd_cntl[i].ch,
+						diag_event_num_bytes);
+			}
 			encode_rsp_and_send(2);
 			return 0;
 		}
 #endif
+	} else if (*buf == 0x78) {
+		if (!(driver->smd_cntl[MODEM_DATA].ch) ||
+					(driver->log_on_demand_support)) {
+			driver->apps_rsp_buf[0] = 0x78;
+			/* Copy log code received */
+			*(uint16_t *)(driver->apps_rsp_buf+1) =
+							 *(uint16_t *)buf;
+			driver->apps_rsp_buf[3] = 0x1;/* Unknown */
+			encode_rsp_and_send(3);
+		}
 	}
 
 	return  packet_type;
@@ -745,6 +777,21 @@
 			goto err;
 		kmemleak_not_leak(driver->msg_masks);
 	}
+	if (driver->buf_feature_mask_update == NULL) {
+		driver->buf_feature_mask_update = kzalloc(sizeof(
+					struct diag_ctrl_feature_mask) +
+					FEATURE_MASK_LEN_BYTES, GFP_KERNEL);
+		if (driver->buf_feature_mask_update == NULL)
+			goto err;
+		kmemleak_not_leak(driver->buf_feature_mask_update);
+	}
+	if (driver->feature_mask == NULL) {
+		driver->feature_mask = kzalloc(sizeof(
+			struct diag_ctrl_feature_mask), GFP_KERNEL);
+		if (driver->feature_mask == NULL)
+			goto err;
+		kmemleak_not_leak(driver->feature_mask);
+	}
 	diag_create_msg_mask_table();
 	diag_event_num_bytes = 0;
 	if (driver->log_masks == NULL) {
@@ -760,14 +807,6 @@
 			goto err;
 		kmemleak_not_leak(driver->event_masks);
 	}
-#ifdef CONFIG_DIAG_OVER_USB
-	INIT_WORK(&(driver->diag_modem_mask_update_work),
-						 diag_modem_mask_update_fn);
-	INIT_WORK(&(driver->diag_lpass_mask_update_work),
-						 diag_lpass_mask_update_fn);
-	INIT_WORK(&(driver->diag_wcnss_mask_update_work),
-						 diag_wcnss_mask_update_fn);
-#endif
 	return;
 err:
 	pr_err("diag: Could not initialize diag mask buffers");
@@ -777,6 +816,8 @@
 	kfree(driver->msg_masks);
 	kfree(driver->log_masks);
 	kfree(driver->event_masks);
+	kfree(driver->feature_mask);
+	kfree(driver->buf_feature_mask_update);
 }
 
 void diag_masks_exit(void)
@@ -787,4 +828,6 @@
 	kfree(driver->msg_masks);
 	kfree(driver->log_masks);
 	kfree(driver->event_masks);
+	kfree(driver->feature_mask);
+	kfree(driver->buf_feature_mask_update);
 }
diff --git a/drivers/char/diag/diag_masks.h b/drivers/char/diag/diag_masks.h
index bcf5bc2..53f72e8 100644
--- a/drivers/char/diag/diag_masks.h
+++ b/drivers/char/diag/diag_masks.h
@@ -20,6 +20,8 @@
 void diag_send_msg_mask_update(smd_channel_t *, int ssid_first,
 					 int ssid_last, int proc);
 void diag_send_log_mask_update(smd_channel_t *, int);
+void diag_mask_update_fn(struct work_struct *work);
+void diag_send_feature_mask_update(smd_channel_t *ch, int proc);
 int diag_process_apps_masks(unsigned char *buf, int len);
 void diag_masks_init(void);
 void diag_masks_exit(void);
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index cb895d8..0d5ad6a 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -23,12 +23,14 @@
 #include <mach/msm_smd.h>
 #include <asm/atomic.h>
 #include <asm/mach-types.h>
+
 /* Size of the USB buffers used for read and write*/
 #define USB_MAX_OUT_BUF 4096
 #define APPS_BUF_SIZE	2000
 #define IN_BUF_SIZE		16384
 #define MAX_IN_BUF_SIZE	32768
 #define MAX_SYNC_OBJ_NAME_SIZE	32
+#define UINT32_MAX	UINT_MAX
 /* Size of the buffer used for deframing a packet
   reveived from the PC tool*/
 #define HDLC_MAX 4096
@@ -39,17 +41,14 @@
 #define POOL_TYPE_HSIC		8
 #define POOL_TYPE_HSIC_WRITE	16
 #define POOL_TYPE_ALL		7
-#define MODEM_DATA		1
-#define LPASS_DATA		2
+#define MODEM_DATA		0
+#define LPASS_DATA		1
+#define WCNSS_DATA		2
 #define APPS_DATA		3
 #define SDIO_DATA		4
-#define WCNSS_DATA		5
-#define HSIC_DATA		6
-#define SMUX_DATA		7
-#define MODEM_PROC		0
+#define HSIC_DATA		5
+#define SMUX_DATA		6
 #define APPS_PROC		1
-#define LPASS_PROC		2
-#define WCNSS_PROC		3
 #define MSG_MASK_SIZE 10000
 #define LOG_MASK_SIZE 8000
 #define EVENT_MASK_SIZE 1000
@@ -75,6 +74,14 @@
 #define DIAG_STATUS_OPEN (0x00010000)	/* DCI channel open status mask   */
 #define DIAG_STATUS_CLOSED (0x00020000)	/* DCI channel closed status mask */
 
+#define NUM_SMD_DATA_CHANNELS 3
+#define NUM_SMD_CONTROL_CHANNELS 3
+#define NUM_SMD_DCI_CHANNELS 1
+
+#define SMD_DATA_TYPE 0
+#define SMD_CNTL_TYPE 1
+#define SMD_DCI_TYPE 2
+
 /* Maximum number of pkt reg supported at initialization*/
 extern unsigned int diag_max_reg;
 extern unsigned int diag_threshold_reg;
@@ -86,6 +93,12 @@
 	(diag_debug_buf_idx++) : (diag_debug_buf_idx = 0); \
 } while (0)
 
+/* List of remote processor supported */
+enum remote_procs {
+	MDM = 1,
+	QSC = 2,
+};
+
 struct diag_master_table {
 	uint16_t cmd_code;
 	uint16_t subsys_id;
@@ -136,6 +149,35 @@
 };
 #endif
 
+struct diag_smd_info {
+	int peripheral;	/* The peripheral this smd channel communicates with */
+	int type;	/* The type of smd channel (data, control, dci) */
+	uint16_t peripheral_mask;
+
+	smd_channel_t *ch;
+	smd_channel_t *ch_save;
+
+	int in_busy_1;
+	int in_busy_2;
+
+	unsigned char *buf_in_1;
+	unsigned char *buf_in_2;
+
+	struct diag_request *write_ptr_1;
+	struct diag_request *write_ptr_2;
+
+	struct work_struct diag_read_smd_work;
+	struct work_struct diag_notify_update_smd_work;
+	int notify_context;
+
+	/*
+	 * Function ptr for function to call to process the data that
+	 * was just read from the smd channel
+	 */
+	int (*process_smd_read_data)(struct diag_smd_info *smd_info,
+						void *buf, int num_bytes);
+};
+
 struct diagchar_dev {
 
 	/* State for the char driver */
@@ -149,6 +191,7 @@
 	int ref_count;
 	struct mutex diagchar_mutex;
 	wait_queue_head_t wait_q;
+	wait_queue_head_t smd_wait_q;
 	struct diag_client_map *client_map;
 	int *data_ready;
 	int num_clients;
@@ -187,17 +230,11 @@
 	struct diag_ctrl_event_mask *event_mask;
 	struct diag_ctrl_log_mask *log_mask;
 	struct diag_ctrl_msg_mask *msg_mask;
+	struct diag_ctrl_feature_mask *feature_mask;
 	/* State for diag forwarding */
-	unsigned char *buf_in_1;
-	unsigned char *buf_in_2;
-	unsigned char *buf_in_cntl;
-	unsigned char *buf_in_lpass_1;
-	unsigned char *buf_in_lpass_2;
-	unsigned char *buf_in_lpass_cntl;
-	unsigned char *buf_in_wcnss_1;
-	unsigned char *buf_in_wcnss_2;
-	unsigned char *buf_in_wcnss_cntl;
-	unsigned char *buf_in_dci;
+	struct diag_smd_info smd_data[NUM_SMD_DATA_CHANNELS];
+	struct diag_smd_info smd_cntl[NUM_SMD_CONTROL_CHANNELS];
+	struct diag_smd_info smd_dci[NUM_SMD_DCI_CHANNELS];
 	unsigned char *usb_buf_out;
 	unsigned char *apps_rsp_buf;
 	unsigned char *user_space_data;
@@ -205,20 +242,7 @@
 	unsigned char *buf_msg_mask_update;
 	unsigned char *buf_log_mask_update;
 	unsigned char *buf_event_mask_update;
-	smd_channel_t *ch;
-	smd_channel_t *ch_cntl;
-	smd_channel_t *ch_dci;
-	smd_channel_t *chlpass;
-	smd_channel_t *chlpass_cntl;
-	smd_channel_t *ch_wcnss;
-	smd_channel_t *ch_wcnss_cntl;
-	int in_busy_1;
-	int in_busy_2;
-	int in_busy_lpass_1;
-	int in_busy_lpass_2;
-	int in_busy_wcnss_1;
-	int in_busy_wcnss_2;
-	int in_busy_dci;
+	unsigned char *buf_feature_mask_update;
 	int read_len_legacy;
 	unsigned char *hdlc_buf;
 	unsigned hdlc_count;
@@ -231,41 +255,22 @@
 #endif
 	struct workqueue_struct *diag_wq;
 	struct work_struct diag_drain_work;
-	struct work_struct diag_read_smd_work;
-	struct work_struct diag_read_smd_cntl_work;
-	struct work_struct diag_read_smd_lpass_work;
-	struct work_struct diag_read_smd_lpass_cntl_work;
-	struct work_struct diag_read_smd_wcnss_work;
-	struct work_struct diag_read_smd_wcnss_cntl_work;
 	struct workqueue_struct *diag_cntl_wq;
-	struct work_struct diag_modem_mask_update_work;
-	struct work_struct diag_lpass_mask_update_work;
-	struct work_struct diag_wcnss_mask_update_work;
-	struct work_struct diag_read_smd_dci_work;
-	struct work_struct diag_update_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;
 	uint8_t *event_masks;
+	uint8_t log_on_demand_support;
 	struct diag_master_table *table;
 	uint8_t *pkt_buf;
 	int pkt_length;
-	struct diag_request *write_ptr_1;
-	struct diag_request *write_ptr_2;
 	struct diag_request *usb_read_ptr;
 	struct diag_request *write_ptr_svc;
-	struct diag_request *write_ptr_lpass_1;
-	struct diag_request *write_ptr_lpass_2;
-	struct diag_request *write_ptr_wcnss_1;
-	struct diag_request *write_ptr_wcnss_2;
-	struct diag_write_device *write_ptr_dci;
 	int logging_mode;
 	int mask_check;
 	int logging_process_id;
 	struct task_struct *socket_process;
+	struct task_struct *callback_process;
 #ifdef CONFIG_DIAG_SDIO_PIPE
 	unsigned char *buf_in_sdio;
 	unsigned char *usb_buf_mdm_out;
@@ -280,7 +285,9 @@
 	struct diag_request *usb_read_mdm_ptr;
 	struct diag_request *write_ptr_mdm;
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+	/* common for all bridges */
+	struct work_struct diag_disconnect_work;
 	/* SGLTE variables */
 	int lcid;
 	unsigned char *buf_in_smux;
@@ -297,18 +304,6 @@
 	int in_busy_hsic_read_on_device;
 	int in_busy_hsic_write;
 	struct work_struct diag_read_hsic_work;
-	struct mutex bridge_mutex;
-	/* USB MDM channel variables */
-	int usb_mdm_connected;
-	int read_len_mdm;
-	int write_len_mdm;
-	unsigned char *usb_buf_mdm_out;
-	struct usb_diag_ch *mdm_ch;
-	struct workqueue_struct *diag_bridge_wq;
-	struct work_struct diag_read_mdm_work;
-	struct work_struct diag_disconnect_work;
-	struct work_struct diag_usb_read_complete_work;
-	struct diag_request *usb_read_mdm_ptr;
 	int count_hsic_pool;
 	int count_hsic_write_pool;
 	unsigned int poolsize_hsic;
@@ -323,5 +318,10 @@
 #endif
 };
 
+extern struct diag_bridge_dev *diag_bridge;
 extern struct diagchar_dev *driver;
+
+extern int wrap_enabled;
+extern uint16_t wrap_count;
+
 #endif
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 6c9e5ab..d2454f4 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -32,13 +32,14 @@
 #ifdef CONFIG_DIAG_SDIO_PIPE
 #include "diagfwd_sdio.h"
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 #include "diagfwd_hsic.h"
 #include "diagfwd_smux.h"
 #endif
 #include <linux/timer.h>
 #include "diag_debugfs.h"
 #include "diag_masks.h"
+#include "diagfwd_bridge.h"
 
 MODULE_DESCRIPTION("Diag Char Driver");
 MODULE_LICENSE("GPL v2");
@@ -78,12 +79,25 @@
 /* delayed_rsp_id 0 represents no delay in the response. Any other number
     means that the diag packet has a delayed response. */
 static uint16_t delayed_rsp_id = 1;
-#define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
-/* This macro gets the next delayed respose id. Once it reaches
- DIAGPKT_MAX_DELAYED_RSP, it stays at DIAGPKT_MAX_DELAYED_RSP */
 
-#define DIAGPKT_NEXT_DELAYED_RSP_ID(x) 				\
-((x < DIAGPKT_MAX_DELAYED_RSP) ? x++ : DIAGPKT_MAX_DELAYED_RSP)
+#define DIAGPKT_MAX_DELAYED_RSP 0xFFFF
+
+/* returns the next delayed rsp id - rollsover the id if wrapping is
+   enabled. */
+uint16_t diagpkt_next_delayed_rsp_id(uint16_t rspid)
+{
+	if (rspid < DIAGPKT_MAX_DELAYED_RSP)
+		rspid++;
+	else {
+		if (wrap_enabled) {
+			rspid = 1;
+			wrap_count++;
+		} else
+			rspid = DIAGPKT_MAX_DELAYED_RSP;
+	}
+	delayed_rsp_id = rspid;
+	return delayed_rsp_id;
+}
 
 #define COPY_USER_SPACE_OR_EXIT(buf, data, length)		\
 do {								\
@@ -124,7 +138,7 @@
 	mutex_unlock(&driver->diagchar_mutex);
 }
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 void diag_clear_hsic_tbl(void)
 {
 	int i;
@@ -145,21 +159,6 @@
 void diag_clear_hsic_tbl(void) { }
 #endif
 
-void diag_read_smd_work_fn(struct work_struct *work)
-{
-	__diag_smd_send_req();
-}
-
-void diag_read_smd_lpass_work_fn(struct work_struct *work)
-{
-	__diag_smd_lpass_send_req();
-}
-
-void diag_read_smd_wcnss_work_fn(struct work_struct *work)
-{
-	__diag_smd_wcnss_send_req();
-}
-
 void diag_add_client(int i, struct file *file)
 {
 	struct diagchar_priv *diagpriv_data;
@@ -248,6 +247,10 @@
 		pr_alert("diag: Invalid file pointer");
 		return -ENOMEM;
 	}
+
+	if (!driver)
+		return -ENOMEM;
+
 	/* clean up any DCI registrations, if this is a DCI client
 	* This will specially help in case of ungraceful exit of any DCI client
 	* This call will remove any pending registrations of such client
@@ -265,13 +268,17 @@
 		(driver->socket_process->tgid == current->tgid)) {
 		driver->socket_process = NULL;
 	}
+	if (driver->callback_process &&
+		(driver->callback_process->tgid == current->tgid)) {
+		driver->callback_process = NULL;
+	}
 
 #ifdef CONFIG_DIAG_OVER_USB
 	/* If the SD logging process exits, change logging to USB mode */
 	if (driver->logging_process_id == current->tgid) {
 		driver->logging_mode = USB_MODE;
 		diagfwd_connect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		diag_clear_hsic_tbl();
 		diagfwd_cancel_hsic();
 		diagfwd_connect_bridge(0);
@@ -283,26 +290,23 @@
 			if (driver->table[i].process_id == current->tgid)
 					driver->table[i].process_id = 0;
 
-	if (driver) {
-		mutex_lock(&driver->diagchar_mutex);
-		driver->ref_count--;
-		/* On Client exit, try to destroy all 3 pools */
-		diagmem_exit(driver, POOL_TYPE_COPY);
-		diagmem_exit(driver, POOL_TYPE_HDLC);
-		diagmem_exit(driver, POOL_TYPE_WRITE_STRUCT);
-		for (i = 0; i < driver->num_clients; i++) {
-			if (NULL != diagpriv_data && diagpriv_data->pid ==
-				 driver->client_map[i].pid) {
-				driver->client_map[i].pid = 0;
-				kfree(diagpriv_data);
-				diagpriv_data = NULL;
-				break;
-			}
+	mutex_lock(&driver->diagchar_mutex);
+	driver->ref_count--;
+	/* On Client exit, try to destroy all 3 pools */
+	diagmem_exit(driver, POOL_TYPE_COPY);
+	diagmem_exit(driver, POOL_TYPE_HDLC);
+	diagmem_exit(driver, POOL_TYPE_WRITE_STRUCT);
+	for (i = 0; i < driver->num_clients; i++) {
+		if (NULL != diagpriv_data && diagpriv_data->pid ==
+			 driver->client_map[i].pid) {
+			driver->client_map[i].pid = 0;
+			kfree(diagpriv_data);
+			diagpriv_data = NULL;
+			break;
 		}
-		mutex_unlock(&driver->diagchar_mutex);
-		return 0;
 	}
-	return -ENOMEM;
+	mutex_unlock(&driver->diagchar_mutex);
+	return 0;
 }
 
 int diag_find_polling_reg(int i)
@@ -328,7 +332,7 @@
 	return 0;
 }
 
-void diag_clear_reg(int proc_num)
+void diag_clear_reg(int peripheral)
 {
 	int i;
 
@@ -336,9 +340,8 @@
 	/* reset polling flag */
 	driver->polling_reg_flag = 0;
 	for (i = 0; i < diag_max_reg; i++) {
-		if (driver->table[i].client_id == proc_num) {
+		if (driver->table[i].client_id == peripheral)
 			driver->table[i].process_id = 0;
-		}
 	}
 	/* re-scan the registration table */
 	for (i = 0; i < diag_max_reg; i++) {
@@ -351,7 +354,7 @@
 }
 
 void diag_add_reg(int j, struct bindpkt_params *params,
-					  int *success, int *count_entries)
+				  int *success, unsigned int *count_entries)
 {
 	*success = 1;
 	driver->table[j].cmd_code = params->cmd_code;
@@ -365,7 +368,7 @@
 			driver->polling_reg_flag = 1;
 	if (params->proc_id == APPS_PROC) {
 		driver->table[j].process_id = current->tgid;
-		driver->table[j].client_id = APPS_PROC;
+		driver->table[j].client_id = APPS_DATA;
 	} else {
 		driver->table[j].process_id = NON_APPS_PROC;
 		driver->table[j].client_id = params->client_id;
@@ -373,82 +376,193 @@
 	(*count_entries)++;
 }
 
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+uint16_t diag_get_remote_device_mask(void)
+{
+	uint16_t remote_dev = 0;
+
+	/* Check for MDM processor */
+	if (driver->hsic_inited)
+		remote_dev |= 1 << 0;
+
+	/* Check for QSC processor */
+	if (driver->diag_smux_enabled)
+		remote_dev |= 1 << 1;
+
+	return remote_dev;
+}
+#else
+inline uint16_t diag_get_remote_device_mask(void) { return 0; }
+#endif
+
+static int diag_get_remote(int remote_info)
+{
+	int val = (remote_info < 0) ? -remote_info : remote_info;
+	int remote_val;
+
+	switch (val) {
+	case MDM:
+	case QSC:
+		remote_val = -remote_info;
+		break;
+	default:
+		remote_val = 0;
+		break;
+	}
+
+	return remote_val;
+}
+
 long diagchar_ioctl(struct file *filp,
 			   unsigned int iocmd, unsigned long ioarg)
 {
-	int i, j, count_entries = 0, temp;
-	int success = -1;
+	int i, j, temp, success = -1, status;
+	unsigned int count_entries = 0, interim_count = 0;
 	void *temp_buf;
 	uint16_t support_list = 0;
-	struct diag_dci_client_tbl *params =
-		kzalloc(sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
+	struct diag_dci_client_tbl *dci_params;
 	struct diag_dci_health_stats stats;
-	int status;
 
 	if (iocmd == DIAG_IOCTL_COMMAND_REG) {
-		struct bindpkt_params_per_process *pkt_params =
-			 (struct bindpkt_params_per_process *) ioarg;
+		struct bindpkt_params_per_process pkt_params;
+		struct bindpkt_params *params;
+		struct bindpkt_params *head_params;
+		if (copy_from_user(&pkt_params, (void *)ioarg,
+			   sizeof(struct bindpkt_params_per_process))) {
+			return -EFAULT;
+		}
+		if ((UINT32_MAX/sizeof(struct bindpkt_params)) <
+							 pkt_params.count) {
+			pr_warning("diag: integer overflow while multiply\n");
+			return -EFAULT;
+		}
+		params = kzalloc(pkt_params.count*sizeof(
+			struct bindpkt_params), GFP_KERNEL);
+		if (!params) {
+			pr_err("diag: unable to alloc memory\n");
+			return -ENOMEM;
+		} else
+			head_params = params;
+
+		if (copy_from_user(params, pkt_params.params,
+			   pkt_params.count*sizeof(struct bindpkt_params))) {
+			kfree(head_params);
+			return -EFAULT;
+		}
 		mutex_lock(&driver->diagchar_mutex);
 		for (i = 0; i < diag_max_reg; i++) {
 			if (driver->table[i].process_id == 0) {
-				diag_add_reg(i, pkt_params->params,
-						&success, &count_entries);
-				if (pkt_params->count > count_entries) {
-					pkt_params->params++;
+				diag_add_reg(i, params, &success,
+							 &count_entries);
+				if (pkt_params.count > count_entries) {
+					params++;
 				} else {
 					mutex_unlock(&driver->diagchar_mutex);
+					kfree(head_params);
 					return success;
 				}
 			}
 		}
 		if (i < diag_threshold_reg) {
 			/* Increase table size by amount required */
-			diag_max_reg += pkt_params->count -
+			if (pkt_params.count >= count_entries) {
+				interim_count = pkt_params.count -
 							 count_entries;
+			} else {
+				pr_warning("diag: error in params count\n");
+				kfree(head_params);
+				mutex_unlock(&driver->diagchar_mutex);
+				return -EFAULT;
+			}
+			if (UINT32_MAX - diag_max_reg >=
+							interim_count) {
+				diag_max_reg += interim_count;
+			} else {
+				pr_warning("diag: Integer overflow\n");
+				kfree(head_params);
+				mutex_unlock(&driver->diagchar_mutex);
+				return -EFAULT;
+			}
 			/* Make sure size doesnt go beyond threshold */
 			if (diag_max_reg > diag_threshold_reg) {
 				diag_max_reg = diag_threshold_reg;
 				pr_info("diag: best case memory allocation\n");
 			}
+			if (UINT32_MAX/sizeof(struct diag_master_table) <
+								 diag_max_reg) {
+				pr_warning("diag: integer overflow\n");
+				kfree(head_params);
+				mutex_unlock(&driver->diagchar_mutex);
+				return -EFAULT;
+			}
 			temp_buf = krealloc(driver->table,
 					 diag_max_reg*sizeof(struct
 					 diag_master_table), GFP_KERNEL);
 			if (!temp_buf) {
-				diag_max_reg -= pkt_params->count -
-							 count_entries;
-				pr_alert("diag: Insufficient memory for reg.");
+				pr_alert("diag: Insufficient memory for reg.\n");
 				mutex_unlock(&driver->diagchar_mutex);
+
+				if (pkt_params.count >= count_entries) {
+					interim_count = pkt_params.count -
+								 count_entries;
+				} else {
+					pr_warning("diag: params count error\n");
+					mutex_unlock(&driver->diagchar_mutex);
+					kfree(head_params);
+					return -EFAULT;
+				}
+				if (diag_max_reg >= interim_count) {
+					diag_max_reg -= interim_count;
+				} else {
+					pr_warning("diag: Integer underflow\n");
+					mutex_unlock(&driver->diagchar_mutex);
+					kfree(head_params);
+					return -EFAULT;
+				}
+				kfree(head_params);
 				return 0;
 			} else {
 				driver->table = temp_buf;
 			}
 			for (j = i; j < diag_max_reg; j++) {
-				diag_add_reg(j, pkt_params->params,
-						&success, &count_entries);
-				if (pkt_params->count > count_entries) {
-					pkt_params->params++;
+				diag_add_reg(j, params, &success,
+							 &count_entries);
+				if (pkt_params.count > count_entries) {
+					params++;
 				} else {
 					mutex_unlock(&driver->diagchar_mutex);
+					kfree(head_params);
 					return success;
 				}
 			}
+			kfree(head_params);
 			mutex_unlock(&driver->diagchar_mutex);
 		} else {
 			mutex_unlock(&driver->diagchar_mutex);
+			kfree(head_params);
 			pr_err("Max size reached, Pkt Registration failed for"
 						" Process %d", current->tgid);
 		}
 		success = 0;
 	} else if (iocmd == DIAG_IOCTL_GET_DELAYED_RSP_ID) {
-		struct diagpkt_delay_params *delay_params =
-					(struct diagpkt_delay_params *) ioarg;
-
-		if ((delay_params->rsp_ptr) &&
-		 (delay_params->size == sizeof(delayed_rsp_id)) &&
-				 (delay_params->num_bytes_ptr)) {
-			*((uint16_t *)delay_params->rsp_ptr) =
-				DIAGPKT_NEXT_DELAYED_RSP_ID(delayed_rsp_id);
-			*(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
+		struct diagpkt_delay_params delay_params;
+		uint16_t interim_rsp_id;
+		int interim_size;
+		if (copy_from_user(&delay_params, (void *)ioarg,
+					   sizeof(struct diagpkt_delay_params)))
+			return -EFAULT;
+		if ((delay_params.rsp_ptr) &&
+		 (delay_params.size == sizeof(delayed_rsp_id)) &&
+				 (delay_params.num_bytes_ptr)) {
+			interim_rsp_id = diagpkt_next_delayed_rsp_id(
+							delayed_rsp_id);
+			if (copy_to_user((void *)delay_params.rsp_ptr,
+					 &interim_rsp_id, sizeof(uint16_t)))
+				return -EFAULT;
+			interim_size = sizeof(delayed_rsp_id);
+			if (copy_to_user((void *)delay_params.num_bytes_ptr,
+						 &interim_size, sizeof(int)))
+				return -EFAULT;
 			success = 0;
 		}
 	} else if (iocmd == DIAG_IOCTL_DCI_REG) {
@@ -456,22 +570,30 @@
 			return DIAG_DCI_NO_REG;
 		if (driver->num_dci_client >= MAX_DCI_CLIENTS)
 			return DIAG_DCI_NO_REG;
-		if (copy_from_user(params, (void *)ioarg,
+		dci_params = kzalloc(sizeof(struct diag_dci_client_tbl),
+								 GFP_KERNEL);
+		if (dci_params == NULL) {
+			pr_err("diag: unable to alloc memory\n");
+			return -ENOMEM;
+		}
+		if (copy_from_user(dci_params, (void *)ioarg,
 				 sizeof(struct diag_dci_client_tbl)))
 			return -EFAULT;
 		mutex_lock(&driver->dci_mutex);
 		if (!(driver->num_dci_client))
-			driver->in_busy_dci = 0;
+			for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
+				driver->smd_dci[i].in_busy_1 = 0;
 		driver->num_dci_client++;
-		pr_debug("diag: id = %d\n", driver->dci_client_id);
+		pr_debug("diag: In %s, id = %d\n",
+				__func__, driver->dci_client_id);
 		driver->dci_client_id++;
 		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
 			if (driver->dci_client_tbl[i].client == NULL) {
 				driver->dci_client_tbl[i].client = current;
 				driver->dci_client_tbl[i].list =
-							 params->list;
+							 dci_params->list;
 				driver->dci_client_tbl[i].signal_type =
-					 params->signal_type;
+					 dci_params->signal_type;
 				create_dci_log_mask_tbl(driver->
 					dci_client_tbl[i].dci_log_mask);
 				create_dci_event_mask_tbl(driver->
@@ -489,6 +611,7 @@
 			}
 		}
 		mutex_unlock(&driver->dci_mutex);
+		kfree(dci_params);
 		return driver->dci_client_id;
 	} else if (iocmd == DIAG_IOCTL_DCI_DEINIT) {
 		success = -1;
@@ -511,27 +634,34 @@
 		mutex_unlock(&driver->dci_mutex);
 		return success;
 	} else if (iocmd == DIAG_IOCTL_DCI_SUPPORT) {
-		if (driver->ch_dci)
-			support_list = support_list | DIAG_CON_MPSS;
-		*(uint16_t *)ioarg = support_list;
+		for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
+			if (driver->smd_dci[i].ch)
+				support_list |=
+				driver->smd_dci[i].peripheral_mask;
+		}
+		if (copy_to_user((void *)ioarg, &support_list,
+							 sizeof(uint16_t)))
+			return -EFAULT;
 		return DIAG_DCI_NO_ERROR;
 	} else if (iocmd == DIAG_IOCTL_DCI_HEALTH_STATS) {
 		if (copy_from_user(&stats, (void *)ioarg,
 				 sizeof(struct diag_dci_health_stats)))
 			return -EFAULT;
 		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-			params = &(driver->dci_client_tbl[i]);
-			if (params->client &&
-				params->client->tgid == current->tgid) {
-				stats.dropped_logs = params->dropped_logs;
-				stats.dropped_events = params->dropped_events;
-				stats.received_logs = params->received_logs;
-				stats.received_events = params->received_events;
+			dci_params = &(driver->dci_client_tbl[i]);
+			if (dci_params->client &&
+				dci_params->client->tgid == current->tgid) {
+				stats.dropped_logs = dci_params->dropped_logs;
+				stats.dropped_events =
+						 dci_params->dropped_events;
+				stats.received_logs = dci_params->received_logs;
+				stats.received_events =
+						 dci_params->received_events;
 				if (stats.reset_status) {
-					params->dropped_logs = 0;
-					params->dropped_events = 0;
-					params->received_logs = 0;
-					params->received_events = 0;
+					dci_params->dropped_logs = 0;
+					dci_params->dropped_events = 0;
+					dci_params->received_logs = 0;
+					dci_params->received_events = 0;
 				}
 				break;
 			}
@@ -544,7 +674,7 @@
 		for (i = 0; i < driver->num_clients; i++)
 			if (driver->client_map[i].pid == current->tgid)
 				break;
-		if (i == -1)
+		if (i == driver->num_clients)
 			return -EINVAL;
 		driver->data_ready[i] |= DEINIT_TYPE;
 		wake_up_interruptible(&driver->wait_q);
@@ -553,6 +683,11 @@
 		mutex_lock(&driver->diagchar_mutex);
 		temp = driver->logging_mode;
 		driver->logging_mode = (int)ioarg;
+		if (temp == driver->logging_mode) {
+			mutex_unlock(&driver->diagchar_mutex);
+			pr_alert("diag: forbidden logging change requested\n");
+			return 0;
+		}
 		if (driver->logging_mode == MEMORY_DEVICE_MODE) {
 			diag_clear_hsic_tbl();
 			driver->mask_check = 1;
@@ -571,14 +706,14 @@
 				}
 			}
 		}
-		if (driver->logging_mode == UART_MODE) {
-			diag_clear_hsic_tbl();
-			driver->mask_check = 0;
-			driver->logging_mode = MEMORY_DEVICE_MODE;
-		}
-		if (driver->logging_mode == SOCKET_MODE) {
-			diag_clear_hsic_tbl();
+		if (driver->logging_mode == SOCKET_MODE)
 			driver->socket_process = current;
+		if (driver->logging_mode == CALLBACK_MODE)
+			driver->callback_process = current;
+		if (driver->logging_mode == UART_MODE ||
+			driver->logging_mode == SOCKET_MODE ||
+			driver->logging_mode == CALLBACK_MODE) {
+			diag_clear_hsic_tbl();
 			driver->mask_check = 0;
 			driver->logging_mode = MEMORY_DEVICE_MODE;
 		}
@@ -586,37 +721,28 @@
 		mutex_unlock(&driver->diagchar_mutex);
 		if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
 							== NO_LOGGING_MODE) {
-			driver->in_busy_1 = 1;
-			driver->in_busy_2 = 1;
-			driver->in_busy_lpass_1 = 1;
-			driver->in_busy_lpass_2 = 1;
-			driver->in_busy_wcnss_1 = 1;
-			driver->in_busy_wcnss_2 = 1;
+			for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+				driver->smd_data[i].in_busy_1 = 0;
+				driver->smd_data[i].in_busy_2 = 0;
+			}
 #ifdef CONFIG_DIAG_SDIO_PIPE
 			driver->in_busy_sdio = 1;
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_disconnect_bridge(0);
 			diag_clear_hsic_tbl();
 #endif
 		} else if (temp == NO_LOGGING_MODE && driver->logging_mode
 							== MEMORY_DEVICE_MODE) {
-			driver->in_busy_1 = 0;
-			driver->in_busy_2 = 0;
-			driver->in_busy_lpass_1 = 0;
-			driver->in_busy_lpass_2 = 0;
-			driver->in_busy_wcnss_1 = 0;
-			driver->in_busy_wcnss_2 = 0;
-			/* Poll SMD channels to check for data*/
-			if (driver->ch)
-				queue_work(driver->diag_wq,
-					&(driver->diag_read_smd_work));
-			if (driver->chlpass)
-				queue_work(driver->diag_wq,
-					&(driver->diag_read_smd_lpass_work));
-			if (driver->ch_wcnss)
-				queue_work(driver->diag_wq,
-					&(driver->diag_read_smd_wcnss_work));
+			for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+				driver->smd_data[i].in_busy_1 = 0;
+				driver->smd_data[i].in_busy_2 = 0;
+				/* Poll SMD channels to check for data*/
+				if (driver->smd_data[i].ch)
+					queue_work(driver->diag_wq,
+						&(driver->smd_data[i].
+							diag_read_smd_work));
+			}
 #ifdef CONFIG_DIAG_SDIO_PIPE
 			driver->in_busy_sdio = 0;
 			/* Poll SDIO channel to check for data */
@@ -624,7 +750,7 @@
 				queue_work(driver->diag_sdio_wq,
 					&(driver->diag_read_sdio_work));
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_connect_bridge(0);
 #endif
 		}
@@ -632,35 +758,27 @@
 		else if (temp == USB_MODE && driver->logging_mode
 							 == NO_LOGGING_MODE) {
 			diagfwd_disconnect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_disconnect_bridge(0);
 #endif
 		} else if (temp == NO_LOGGING_MODE && driver->logging_mode
 								== USB_MODE) {
 			diagfwd_connect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_connect_bridge(0);
 #endif
 		} else if (temp == USB_MODE && driver->logging_mode
 							== MEMORY_DEVICE_MODE) {
 			diagfwd_disconnect();
-			driver->in_busy_1 = 0;
-			driver->in_busy_2 = 0;
-			driver->in_busy_lpass_1 = 0;
-			driver->in_busy_lpass_2 = 0;
-			driver->in_busy_wcnss_1 = 0;
-			driver->in_busy_wcnss_2 = 0;
-
-			/* Poll SMD channels to check for data*/
-			if (driver->ch)
-				queue_work(driver->diag_wq,
-					 &(driver->diag_read_smd_work));
-			if (driver->chlpass)
-				queue_work(driver->diag_wq,
-					&(driver->diag_read_smd_lpass_work));
-			if (driver->ch_wcnss)
-				queue_work(driver->diag_wq,
-					&(driver->diag_read_smd_wcnss_work));
+			for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+				driver->smd_data[i].in_busy_1 = 0;
+				driver->smd_data[i].in_busy_2 = 0;
+				/* Poll SMD channels to check for data*/
+				if (driver->smd_data[i].ch)
+					queue_work(driver->diag_wq,
+						&(driver->smd_data[i].
+							diag_read_smd_work));
+			}
 #ifdef CONFIG_DIAG_SDIO_PIPE
 			driver->in_busy_sdio = 0;
 			/* Poll SDIO channel to check for data */
@@ -668,14 +786,14 @@
 				queue_work(driver->diag_sdio_wq,
 					&(driver->diag_read_sdio_work));
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diagfwd_cancel_hsic();
 			diagfwd_connect_bridge(0);
 #endif
 		} else if (temp == MEMORY_DEVICE_MODE &&
 				 driver->logging_mode == USB_MODE) {
 			diagfwd_connect();
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 			diag_clear_hsic_tbl();
 			diagfwd_cancel_hsic();
 			diagfwd_connect_bridge(0);
@@ -683,6 +801,12 @@
 		}
 #endif /* DIAG over USB */
 		success = 1;
+	} else if (iocmd == DIAG_IOCTL_REMOTE_DEV) {
+		uint16_t remote_dev = diag_get_remote_device_mask();
+		if (copy_to_user((void *)ioarg, &remote_dev, sizeof(uint16_t)))
+			success = -EFAULT;
+		else
+			success = 1;
 	}
 
 	return success;
@@ -694,6 +818,8 @@
 	struct diag_dci_client_tbl *entry;
 	int index = -1, i = 0, ret = 0;
 	int num_data = 0, data_type;
+	int remote_token;
+
 	for (i = 0; i < driver->num_clients; i++)
 		if (driver->client_map[i].pid == current->tgid)
 			index = i;
@@ -707,16 +833,16 @@
 				  driver->data_ready[index]);
 	mutex_lock(&driver->diagchar_mutex);
 
-	if ((driver->data_ready[index] & USER_SPACE_LOG_TYPE) && (driver->
+	if ((driver->data_ready[index] & USER_SPACE_DATA_TYPE) && (driver->
 					logging_mode == MEMORY_DEVICE_MODE)) {
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		unsigned long spin_lock_flags;
 		struct diag_write_device hsic_buf_tbl[NUM_HSIC_BUF_TBL_ENTRIES];
 #endif
-
+		remote_token = 0;
 		pr_debug("diag: process woken up\n");
 		/*Copy the type of data being passed*/
-		data_type = driver->data_ready[index] & USER_SPACE_LOG_TYPE;
+		data_type = driver->data_ready[index] & USER_SPACE_DATA_TYPE;
 		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
 		/* place holder for number of data field */
 		ret += 4;
@@ -761,78 +887,40 @@
 		}
 
 		/* copy modem data */
-		if (driver->in_busy_1 == 1) {
-			num_data++;
-			/*Copy the length of data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret,
-					 (driver->write_ptr_1->length), 4);
-			/*Copy the actual data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret,
-					*(driver->buf_in_1),
-					 driver->write_ptr_1->length);
-			driver->in_busy_1 = 0;
-		}
-		if (driver->in_busy_2 == 1) {
-			num_data++;
-			/*Copy the length of data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret,
-					 (driver->write_ptr_2->length), 4);
-			/*Copy the actual data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret,
-					 *(driver->buf_in_2),
-					 driver->write_ptr_2->length);
-			driver->in_busy_2 = 0;
-		}
-		/* copy lpass data */
-		if (driver->in_busy_lpass_1 == 1) {
-			num_data++;
-			/*Copy the length of data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret,
-				 (driver->write_ptr_lpass_1->length), 4);
-			/*Copy the actual data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
-							buf_in_lpass_1),
-					 driver->write_ptr_lpass_1->length);
-			driver->in_busy_lpass_1 = 0;
-		}
-		if (driver->in_busy_lpass_2 == 1) {
-			num_data++;
-			/*Copy the length of data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret,
-				 (driver->write_ptr_lpass_2->length), 4);
-			/*Copy the actual data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
-				buf_in_lpass_2), driver->
-					write_ptr_lpass_2->length);
-			driver->in_busy_lpass_2 = 0;
-		}
-		/* copy wncss data */
-		if (driver->in_busy_wcnss_1 == 1) {
-			num_data++;
-			/*Copy the length of data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret,
-				 (driver->write_ptr_wcnss_1->length), 4);
-			/*Copy the actual data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
-							buf_in_wcnss_1),
-					 driver->write_ptr_wcnss_1->length);
-			driver->in_busy_wcnss_1 = 0;
-		}
-		if (driver->in_busy_wcnss_2 == 1) {
-			num_data++;
-			/*Copy the length of data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret,
-				 (driver->write_ptr_wcnss_2->length), 4);
-			/*Copy the actual data being passed*/
-			COPY_USER_SPACE_OR_EXIT(buf+ret, *(driver->
-							buf_in_wcnss_2),
-					 driver->write_ptr_wcnss_2->length);
-			driver->in_busy_wcnss_2 = 0;
+		for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+			struct diag_smd_info *data = &driver->smd_data[i];
+			if (data->in_busy_1 == 1) {
+				num_data++;
+				/*Copy the length of data being passed*/
+				COPY_USER_SPACE_OR_EXIT(buf+ret,
+					(data->write_ptr_1->length), 4);
+				/*Copy the actual data being passed*/
+				COPY_USER_SPACE_OR_EXIT(buf+ret,
+					*(data->buf_in_1),
+					data->write_ptr_1->length);
+				data->in_busy_1 = 0;
+			}
+			if (data->in_busy_2 == 1) {
+				num_data++;
+				/*Copy the length of data being passed*/
+				COPY_USER_SPACE_OR_EXIT(buf+ret,
+					(data->write_ptr_2->length), 4);
+				/*Copy the actual data being passed*/
+				COPY_USER_SPACE_OR_EXIT(buf+ret,
+					*(data->buf_in_2),
+					data->write_ptr_2->length);
+				data->in_busy_2 = 0;
+			}
 		}
 #ifdef CONFIG_DIAG_SDIO_PIPE
 		/* copy 9K data over SDIO */
 		if (driver->in_busy_sdio == 1) {
+			remote_token = diag_get_remote(MDM);
 			num_data++;
+
+			/*Copy the negative token of data being passed*/
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+						remote_token, 4);
 			/*Copy the length of data being passed*/
 			COPY_USER_SPACE_OR_EXIT(buf+ret,
 				 (driver->write_ptr_mdm->length), 4);
@@ -843,7 +931,7 @@
 			driver->in_busy_sdio = 0;
 		}
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		spin_lock_irqsave(&driver->hsic_spinlock, spin_lock_flags);
 		for (i = 0; i < driver->poolsize_hsic_write; i++) {
 			hsic_buf_tbl[i].buf = driver->hsic_buf_tbl[i].buf;
@@ -856,12 +944,22 @@
 		spin_unlock_irqrestore(&driver->hsic_spinlock,
 					spin_lock_flags);
 
+		remote_token = diag_get_remote(MDM);
 		for (i = 0; i < driver->poolsize_hsic_write; i++) {
 			if (hsic_buf_tbl[i].length > 0) {
 				pr_debug("diag: HSIC copy to user, i: %d, buf: %x, len: %d\n",
 					 i, (unsigned int)hsic_buf_tbl[i].buf,
 					hsic_buf_tbl[i].length);
 				num_data++;
+
+				/* Copy the negative token */
+				if (copy_to_user(buf+ret,
+						&remote_token, 4)) {
+					num_data--;
+					goto drop_hsic;
+				}
+				ret += 4;
+
 				/* Copy the length of data being passed */
 				if (copy_to_user(buf+ret,
 					(void *)&(hsic_buf_tbl[i].length),
@@ -890,20 +988,33 @@
 				diagfwd_write_complete_hsic(NULL);
 			}
 		}
+		if (driver->in_busy_smux == 1) {
+			remote_token = diag_get_remote(QSC);
+			num_data++;
+
+			/* Copy the negative  token of data being passed */
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+						remote_token, 4);
+			/* Copy the length of data being passed */
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					(driver->write_ptr_mdm->length), 4);
+			/* Copy the actual data being passed */
+			COPY_USER_SPACE_OR_EXIT(buf+ret,
+					*(driver->buf_in_smux),
+					driver->write_ptr_mdm->length);
+			pr_debug("diag: SMUX  data copied\n");
+			driver->in_busy_smux = 0;
+		}
 #endif
 		/* copy number of data fields */
 		COPY_USER_SPACE_OR_EXIT(buf+4, num_data, 4);
 		ret -= 4;
-		driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
-		if (driver->ch)
-			queue_work(driver->diag_wq,
-					 &(driver->diag_read_smd_work));
-		if (driver->chlpass)
-			queue_work(driver->diag_wq,
-					 &(driver->diag_read_smd_lpass_work));
-		if (driver->ch_wcnss)
-			queue_work(driver->diag_wq,
-					 &(driver->diag_read_smd_wcnss_work));
+		driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
+		for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+			if (driver->smd_data[i].ch)
+				queue_work(driver->diag_wq,
+				&(driver->smd_data[i].diag_read_smd_work));
+		}
 #ifdef CONFIG_DIAG_SDIO_PIPE
 		if (driver->sdio_ch)
 			queue_work(driver->diag_sdio_wq,
@@ -911,10 +1022,10 @@
 #endif
 		APPEND_DEBUG('n');
 		goto exit;
-	} else if (driver->data_ready[index] & USER_SPACE_LOG_TYPE) {
+	} else if (driver->data_ready[index] & USER_SPACE_DATA_TYPE) {
 		/* In case, the thread wakes up and the logging mode is
 		not memory device any more, the condition needs to be cleared */
-		driver->data_ready[index] ^= USER_SPACE_LOG_TYPE;
+		driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
 	}
 
 	if (driver->data_ready[index] & DEINIT_TYPE) {
@@ -971,10 +1082,9 @@
 		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
 		/* check the current client and copy its data */
 		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-			if (driver->dci_client_tbl[i].client != NULL) {
-				entry = &(driver->dci_client_tbl[i]);
-				if (entry && (current->tgid ==
-						entry->client->tgid)) {
+			entry = &(driver->dci_client_tbl[i]);
+			if (entry && entry->client) {
+				if (current->tgid == entry->client->tgid) {
 					COPY_USER_SPACE_OR_EXIT(buf+4,
 							entry->data_len, 4);
 					COPY_USER_SPACE_OR_EXIT(buf+8,
@@ -985,10 +1095,12 @@
 			}
 		}
 		driver->data_ready[index] ^= DCI_DATA_TYPE;
-		driver->in_busy_dci = 0;
-		if (driver->ch_dci)
-			queue_work(driver->diag_dci_wq,
-				&(driver->diag_read_smd_dci_work));
+		for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
+			driver->smd_dci[i].in_busy_1 = 0;
+			if (driver->smd_dci[i].ch)
+				queue_work(driver->diag_dci_wq,
+				&(driver->smd_dci[i].diag_read_smd_work));
+		}
 		goto exit;
 	}
 exit:
@@ -999,14 +1111,15 @@
 static int diagchar_write(struct file *file, const char __user *buf,
 				size_t count, loff_t *ppos)
 {
-	int err, ret = 0, pkt_type;
+	int err, ret = 0, pkt_type, token_offset = 0;
+	int remote_proc = 0;
 #ifdef DIAG_DEBUG
 	int length = 0, i;
 #endif
 	struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
 	struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
 	void *buf_copy = NULL;
-	int payload_size;
+	unsigned int payload_size;
 #ifdef CONFIG_DIAG_OVER_USB
 	if (((driver->logging_mode == USB_MODE) && (!driver->usb_connected)) ||
 				(driver->logging_mode == NO_LOGGING_MODE)) {
@@ -1017,8 +1130,17 @@
 	/* Get the packet type F3/log/event/Pkt response */
 	err = copy_from_user((&pkt_type), buf, 4);
 	/* First 4 bytes indicate the type of payload - ignore these */
+	if (count < 4) {
+		pr_err("diag: Client sending short data\n");
+		return -EBADMSG;
+	}
 	payload_size = count - 4;
-
+	if (payload_size > USER_SPACE_DATA) {
+		pr_err("diag: Dropping packet, packet payload size crosses 8KB limit. Current payload size %d\n",
+				payload_size);
+		driver->dropped_count++;
+		return -EBADMSG;
+	}
 	if (pkt_type == DCI_DATA_TYPE) {
 		err = copy_from_user(driver->user_space_data, buf + 4,
 							 payload_size);
@@ -1030,12 +1152,22 @@
 							payload_size);
 		return err;
 	}
-	if (pkt_type == USER_SPACE_LOG_TYPE) {
+	if (pkt_type == USER_SPACE_DATA_TYPE) {
 		err = copy_from_user(driver->user_space_data, buf + 4,
 							 payload_size);
+		/* Check for proc_type */
+		remote_proc = diag_get_remote(*(int *)driver->user_space_data);
+
+		if (remote_proc) {
+			token_offset = 4;
+			payload_size -= 4;
+			buf += 4;
+		}
+
 		/* Check masks for On-Device logging */
 		if (driver->mask_check) {
-			if (!mask_request_validate(driver->user_space_data)) {
+			if (!mask_request_validate(driver->user_space_data +
+							 token_offset)) {
 				pr_alert("diag: mask request Invalid\n");
 				return -EFAULT;
 			}
@@ -1044,31 +1176,35 @@
 #ifdef DIAG_DEBUG
 		pr_debug("diag: user space data %d\n", payload_size);
 		for (i = 0; i < payload_size; i++)
-			pr_debug("\t %x", *((driver->user_space_data)+i));
+			pr_debug("\t %x", *((driver->user_space_data
+						+ token_offset)+i));
 #endif
 #ifdef CONFIG_DIAG_SDIO_PIPE
 		/* send masks to 9k too */
-		if (driver->sdio_ch) {
+		if (driver->sdio_ch && (remote_proc == MDM)) {
 			wait_event_interruptible(driver->wait_q,
 				 (sdio_write_avail(driver->sdio_ch) >=
 					 payload_size));
 			if (driver->sdio_ch && (payload_size > 0)) {
 				sdio_write(driver->sdio_ch, (void *)
-				   (driver->user_space_data), payload_size);
+				   (driver->user_space_data + token_offset),
+				   payload_size);
 			}
 		}
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 		/* send masks to 9k too */
-		if (driver->hsic_ch && (payload_size > 0)) {
+		if (driver->hsic_ch && (payload_size > 0) &&
+						(remote_proc == MDM)) {
 			/* wait sending mask updates if HSIC ch not ready */
 			if (driver->in_busy_hsic_write)
 				wait_event_interruptible(driver->wait_q,
 					(driver->in_busy_hsic_write != 1));
 			driver->in_busy_hsic_write = 1;
 			driver->in_busy_hsic_read_on_device = 0;
-			err = diag_bridge_write(driver->user_space_data,
-							 payload_size);
+			err = diag_bridge_write(
+					driver->user_space_data + token_offset,
+					payload_size);
 			if (err) {
 				pr_err("diag: err sending mask to MDM: %d\n",
 									 err);
@@ -1082,10 +1218,25 @@
 					driver->in_busy_hsic_write = 0;
 			}
 		}
+		if (driver->diag_smux_enabled && (remote_proc == QSC)
+						&& driver->lcid) {
+			if (payload_size > 0) {
+				err = msm_smux_write(driver->lcid, NULL,
+					driver->user_space_data + token_offset,
+					payload_size);
+				if (err) {
+					pr_err("diag:send mask to MDM err %d",
+							err);
+					return err;
+				}
+			}
+		}
 #endif
 		/* send masks to 8k now */
-		diag_process_hdlc((void *)(driver->user_space_data),
-							 payload_size);
+		if (!remote_proc)
+			diag_process_hdlc((void *)
+				(driver->user_space_data + token_offset),
+				 payload_size);
 		return 0;
 	}
 
@@ -1136,13 +1287,21 @@
 		ret = -ENOMEM;
 		goto fail_free_hdlc;
 	}
+	if (HDLC_OUT_BUF_SIZE < (2*payload_size) + 3) {
+		pr_err("diag: Dropping packet, HDLC encoded packet payload size crosses buffer limit. Current payload size %d\n",
+				((2*payload_size) + 3));
+		driver->dropped_count++;
+		ret = -EBADMSG;
+		goto fail_free_hdlc;
+	}
 	if (HDLC_OUT_BUF_SIZE - driver->used <= (2*payload_size) + 3) {
 		err = diag_device_write(buf_hdlc, APPS_DATA, NULL);
 		if (err) {
 			/*Free the buffer right away if write failed */
 			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
-			diagmem_free(driver, (unsigned char *)driver->
-				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			if (driver->logging_mode == USB_MODE)
+				diagmem_free(driver, (unsigned char *)driver->
+					write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
 			ret = -EIO;
 			goto fail_free_hdlc;
 		}
@@ -1169,8 +1328,9 @@
 		if (err) {
 			/*Free the buffer right away if write failed */
 			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
-			diagmem_free(driver, (unsigned char *)driver->
-				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			if (driver->logging_mode == USB_MODE)
+				diagmem_free(driver, (unsigned char *)driver->
+					write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
 			ret = -EIO;
 			goto fail_free_hdlc;
 		}
@@ -1194,8 +1354,9 @@
 		if (err) {
 			/*Free the buffer right away if write failed */
 			diagmem_free(driver, buf_hdlc, POOL_TYPE_HDLC);
-			diagmem_free(driver, (unsigned char *)driver->
-				 write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
+			if (driver->logging_mode == USB_MODE)
+				diagmem_free(driver, (unsigned char *)driver->
+					write_ptr_svc, POOL_TYPE_WRITE_STRUCT);
 			ret = -EIO;
 			goto fail_free_hdlc;
 		}
@@ -1203,8 +1364,8 @@
 		driver->used = 0;
 	}
 
-	mutex_unlock(&driver->diagchar_mutex);
 	diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+	mutex_unlock(&driver->diagchar_mutex);
 	if (!timer_in_progress)	{
 		timer_in_progress = 1;
 		ret = mod_timer(&drain_timer, jiffies + msecs_to_jiffies(500));
@@ -1345,6 +1506,13 @@
 	return 0;
 }
 
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+static void diag_disconnect_work_fn(struct work_struct *w)
+{
+	diagfwd_disconnect_bridge(1);
+}
+#endif
+
 #ifdef CONFIG_DIAG_SDIO_PIPE
 void diag_sdio_fn(int type)
 {
@@ -1359,16 +1527,14 @@
 inline void diag_sdio_fn(int type) {}
 #endif
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-void diag_bridge_fn(int type)
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+void diagfwd_bridge_fn(int type)
 {
-	if (type == INIT)
-		diagfwd_bridge_init();
-	else if (type == EXIT)
+	if (type == EXIT)
 		diagfwd_bridge_exit();
 }
 #else
-inline void diag_bridge_fn(int type) {}
+inline void diagfwd_bridge_fn(int type) { }
 #endif
 
 static int __init diagchar_init(void)
@@ -1378,6 +1544,12 @@
 
 	pr_debug("diagfwd initializing ..\n");
 	driver = kzalloc(sizeof(struct diagchar_dev) + 5, GFP_KERNEL);
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+	diag_bridge = kzalloc(MAX_BRIDGES * sizeof(struct diag_bridge_dev),
+								 GFP_KERNEL);
+	if (!diag_bridge)
+		pr_warning("diag: could not allocate memory for bridge\n");
+#endif
 
 	if (driver) {
 		driver->used = 0;
@@ -1394,38 +1566,25 @@
 		driver->num_clients = max_clients;
 		driver->logging_mode = USB_MODE;
 		driver->socket_process = NULL;
+		driver->callback_process = NULL;
 		driver->mask_check = 0;
 		mutex_init(&driver->diagchar_mutex);
 		init_waitqueue_head(&driver->wait_q);
+		init_waitqueue_head(&driver->smd_wait_q);
 		INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
-		INIT_WORK(&(driver->diag_read_smd_work), diag_read_smd_work_fn);
-		INIT_WORK(&(driver->diag_read_smd_cntl_work),
-						 diag_read_smd_cntl_work_fn);
-		INIT_WORK(&(driver->diag_read_smd_lpass_work),
-			   diag_read_smd_lpass_work_fn);
-		INIT_WORK(&(driver->diag_read_smd_lpass_cntl_work),
-			   diag_read_smd_lpass_cntl_work_fn);
-		INIT_WORK(&(driver->diag_read_smd_wcnss_work),
-			diag_read_smd_wcnss_work_fn);
-		INIT_WORK(&(driver->diag_read_smd_wcnss_cntl_work),
-			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_update_smd_dci_work),
-						diag_update_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();
 		diag_masks_init();
+		diagfwd_init();
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+		diagfwd_bridge_init(HSIC);
+		diagfwd_bridge_init(SMUX);
+		INIT_WORK(&(driver->diag_disconnect_work),
+						 diag_disconnect_work_fn);
+#endif
+		diagfwd_cntl_init();
 		driver->dci_state = diag_dci_init();
 		diag_sdio_fn(INIT);
-		diag_bridge_fn(INIT);
+
 		pr_debug("diagchar initializing ..\n");
 		driver->num = 1;
 		driver->name = ((void *)driver) + sizeof(struct diagchar_dev);
@@ -1460,7 +1619,7 @@
 	diagfwd_cntl_exit();
 	diag_masks_exit();
 	diag_sdio_fn(EXIT);
-	diag_bridge_fn(EXIT);
+	diagfwd_bridge_fn(EXIT);
 	return -1;
 }
 
@@ -1474,7 +1633,7 @@
 	diagfwd_cntl_exit();
 	diag_masks_exit();
 	diag_sdio_fn(EXIT);
-	diag_bridge_fn(EXIT);
+	diagfwd_bridge_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 602ca67..0bd26cf 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -40,6 +40,7 @@
 #endif
 #include "diag_dci.h"
 #include "diag_masks.h"
+#include "diagfwd_bridge.h"
 
 #define MODE_CMD		41
 #define RESET_ID		2
@@ -48,26 +49,28 @@
 unsigned char diag_debug_buf[1024];
 static unsigned int buf_tbl_size = 8; /*Number of entries in table of buffers */
 struct diag_master_table entry;
-smd_channel_t *ch_temp = NULL, *chlpass_temp = NULL, *ch_wcnss_temp = NULL;
 struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
 struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
+int wrap_enabled;
+uint16_t wrap_count;
 
 void encode_rsp_and_send(int buf_length)
 {
+	struct diag_smd_info *data = &(driver->smd_data[MODEM_DATA]);
 	send.state = DIAG_STATE_START;
 	send.pkt = driver->apps_rsp_buf;
 	send.last = (void *)(driver->apps_rsp_buf + buf_length);
 	send.terminate = 1;
-	if (!driver->in_busy_1) {
-		enc.dest = driver->buf_in_1;
-		enc.dest_last = (void *)(driver->buf_in_1 + APPS_BUF_SIZE - 1);
+	if (!data->in_busy_1) {
+		enc.dest = data->buf_in_1;
+		enc.dest_last = (void *)(data->buf_in_1 + APPS_BUF_SIZE - 1);
 		diag_hdlc_encode(&send, &enc);
-		driver->write_ptr_1->buf = driver->buf_in_1;
-		driver->write_ptr_1->length = (int)(enc.dest -
-						(void *)(driver->buf_in_1));
-		driver->in_busy_1 = 1;
-		diag_device_write(driver->buf_in_1, MODEM_DATA,
-					driver->write_ptr_1);
+		data->write_ptr_1->buf = data->buf_in_1;
+		data->write_ptr_1->length = (int)(enc.dest -
+						(void *)(data->buf_in_1));
+		data->in_busy_1 = 1;
+		diag_device_write(data->buf_in_1, data->peripheral,
+					data->write_ptr_1);
 		memset(driver->apps_rsp_buf, '\0', APPS_BUF_SIZE);
 	}
 }
@@ -177,7 +180,8 @@
 		 * has registered to respond for polling
 		 */
 		return 1;
-	else if (!(driver->ch) && !(chk_apps_master()))
+	else if (!(driver->smd_data[MODEM_DATA].ch) &&
+					!(chk_apps_master()))
 		/*
 		 * If the apps processor is not the master and the modem
 		 * is not up
@@ -212,69 +216,144 @@
 		 * have their data read/logged.  Detect and remedy this
 		 * situation.
 		 */
-		if ((driver->data_ready[i] & USER_SPACE_LOG_TYPE) == 0) {
-			driver->data_ready[i] |= USER_SPACE_LOG_TYPE;
+		if ((driver->data_ready[i] & USER_SPACE_DATA_TYPE) == 0) {
+			driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
 			pr_debug("diag: Force wakeup of logging process\n");
 			wake_up_interruptible(&driver->wait_q);
 		}
 	}
 }
 
-void __diag_smd_send_req(void)
+/* Process the data read from the smd data channel */
+int diag_process_smd_read_data(struct diag_smd_info *smd_info, void *buf,
+								int total_recd)
 {
-	void *buf = NULL;
-	int *in_busy_ptr = NULL;
 	struct diag_request *write_ptr_modem = NULL;
+	int *in_busy_ptr = 0;
 
-	if (!driver->in_busy_1) {
-		buf = driver->buf_in_1;
-		write_ptr_modem = driver->write_ptr_1;
-		in_busy_ptr = &(driver->in_busy_1);
-	} else if (!driver->in_busy_2) {
-		buf = driver->buf_in_2;
-		write_ptr_modem = driver->write_ptr_2;
-		in_busy_ptr = &(driver->in_busy_2);
+	if (smd_info->buf_in_1 == buf) {
+		write_ptr_modem = smd_info->write_ptr_1;
+		in_busy_ptr = &smd_info->in_busy_1;
+	} else if (smd_info->buf_in_2 == buf) {
+		write_ptr_modem = smd_info->write_ptr_2;
+		in_busy_ptr = &smd_info->in_busy_2;
+	} else {
+		pr_err("diag: In %s, no match for in_busy_1\n", __func__);
 	}
 
-	if (driver->ch && buf) {
-		int r = smd_read_avail(driver->ch);
+	if (write_ptr_modem) {
+		write_ptr_modem->length = total_recd;
+		*in_busy_ptr = 1;
+		diag_device_write(buf, smd_info->peripheral, write_ptr_modem);
+	}
 
-		if (r > IN_BUF_SIZE) {
-			if (r < MAX_IN_BUF_SIZE) {
-				pr_err("diag: SMD sending in "
-						   "packets upto %d bytes", r);
-				buf = krealloc(buf, r, GFP_KERNEL);
-			} else {
-				pr_err("diag: SMD sending in "
-				"packets more than %d bytes", MAX_IN_BUF_SIZE);
+	return 0;
+}
+
+void diag_smd_send_req(struct diag_smd_info *smd_info)
+{
+	void *buf = NULL, *temp_buf = NULL;
+	int total_recd = 0, r = 0, pkt_len;
+	int loop_count = 0;
+	int notify = 0;
+
+	if (!smd_info) {
+		pr_err("diag: In %s, no smd info. Not able to read.\n",
+			__func__);
+		return;
+	}
+
+	if (!smd_info->in_busy_1)
+		buf = smd_info->buf_in_1;
+	else if ((smd_info->type == SMD_DATA_TYPE) && !smd_info->in_busy_2)
+		buf = smd_info->buf_in_2;
+
+	if (smd_info->ch && buf) {
+		temp_buf = buf;
+		pkt_len = smd_cur_packet_size(smd_info->ch);
+
+		while (pkt_len && (pkt_len != total_recd)) {
+			loop_count++;
+			r = smd_read_avail(smd_info->ch);
+			pr_debug("diag: In %s, received pkt %d %d\n",
+				__func__, r, total_recd);
+			if (!r) {
+				/* Nothing to read from SMD */
+				wait_event(driver->smd_wait_q,
+					((smd_info->ch == 0) ||
+					smd_read_avail(smd_info->ch)));
+				/* If the smd channel is open */
+				if (smd_info->ch) {
+					pr_debug("diag: In %s, return from wait_event\n",
+						__func__);
+					continue;
+				} else {
+					pr_debug("diag: In %s, return from wait_event ch closed\n",
+						__func__);
+					return;
+				}
+			}
+			total_recd += r;
+			if (total_recd > IN_BUF_SIZE) {
+				if (total_recd < MAX_IN_BUF_SIZE) {
+					pr_err("diag: In %s, SMD sending in packets up to %d bytes\n",
+						__func__, total_recd);
+					buf = krealloc(buf, total_recd,
+						GFP_KERNEL);
+				} else {
+					pr_err("diag: In %s, SMD sending in packets more than %d bytes\n",
+						__func__, MAX_IN_BUF_SIZE);
+					return;
+				}
+			}
+			if (pkt_len < r) {
+				pr_err("diag: In %s, SMD sending incorrect pkt\n",
+					__func__);
 				return;
 			}
+			if (pkt_len > r) {
+				pr_err("diag: In %s, SMD sending partial pkt %d %d %d %d %d %d\n",
+				__func__, pkt_len, r, total_recd, loop_count,
+				smd_info->peripheral, smd_info->type);
+			}
+
+			/* keep reading for complete packet */
+			smd_read(smd_info->ch, temp_buf, r);
+			temp_buf += r;
 		}
-		if (r > 0) {
-			if (!buf)
-				pr_info("Out of diagmem for Modem\n");
-			else {
-				APPEND_DEBUG('i');
-				smd_read(driver->ch, buf, r);
-				APPEND_DEBUG('j');
-				write_ptr_modem->length = r;
-				*in_busy_ptr = 1;
-				diag_device_write(buf, MODEM_DATA,
-							 write_ptr_modem);
+
+		if (total_recd > 0) {
+			if (!buf) {
+				pr_err("diag: Out of diagmem for Modem\n");
+			} else if (smd_info->process_smd_read_data) {
+				notify = smd_info->process_smd_read_data(
+						smd_info, buf, total_recd);
+				/* Poll SMD channels to check for data */
+				if (notify)
+					diag_smd_notify(smd_info,
+							SMD_EVENT_DATA);
 			}
 		}
-	} else if (driver->ch && !buf &&
+	} else if (smd_info->ch && !buf &&
 		(driver->logging_mode == MEMORY_DEVICE_MODE)) {
-		chk_logging_wakeup();
+			chk_logging_wakeup();
 	}
 }
 
-int diag_device_write(void *buf, int proc_num, struct diag_request *write_ptr)
+void diag_read_smd_work_fn(struct work_struct *work)
+{
+	struct diag_smd_info *smd_info = container_of(work,
+							struct diag_smd_info,
+							diag_read_smd_work);
+	diag_smd_send_req(smd_info);
+}
+
+int diag_device_write(void *buf, int data_type, struct diag_request *write_ptr)
 {
 	int i, err = 0;
 
 	if (driver->logging_mode == MEMORY_DEVICE_MODE) {
-		if (proc_num == APPS_DATA) {
+		if (data_type == APPS_DATA) {
 			for (i = 0; i < driver->poolsize_write_struct; i++)
 				if (driver->buf_tbl[i].length == 0) {
 					driver->buf_tbl[i].buf = buf;
@@ -290,8 +369,8 @@
 				}
 		}
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-		else if (proc_num == HSIC_DATA) {
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+		else if (data_type == HSIC_DATA) {
 			unsigned long flags;
 			int foundIndex = -1;
 
@@ -300,7 +379,7 @@
 				if (driver->hsic_buf_tbl[i].length == 0) {
 					driver->hsic_buf_tbl[i].buf = buf;
 					driver->hsic_buf_tbl[i].length =
-							driver->write_len_mdm;
+						diag_bridge[HSIC].write_len;
 					driver->num_hsic_buf_tbl_entries++;
 					foundIndex = i;
 					break;
@@ -312,7 +391,7 @@
 			else
 				pr_debug("diag: ENQUEUE HSIC buf ptr and length is %x , %d\n",
 					(unsigned int)buf,
-					driver->write_len_mdm);
+					 diag_bridge[HSIC].write_len);
 		}
 #endif
 		for (i = 0; i < driver->num_clients; i++)
@@ -320,39 +399,30 @@
 						 driver->logging_process_id)
 				break;
 		if (i < driver->num_clients) {
-			driver->data_ready[i] |= USER_SPACE_LOG_TYPE;
+			driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
 			pr_debug("diag: wake up logging process\n");
 			wake_up_interruptible(&driver->wait_q);
 		} else
 			return -EINVAL;
 	} else if (driver->logging_mode == NO_LOGGING_MODE) {
-		if (proc_num == MODEM_DATA) {
-			driver->in_busy_1 = 0;
-			driver->in_busy_2 = 0;
-			queue_work(driver->diag_wq, &(driver->
+		if ((data_type >= 0) && (data_type < NUM_SMD_DATA_CHANNELS)) {
+			driver->smd_data[data_type].in_busy_1 = 0;
+			driver->smd_data[data_type].in_busy_2 = 0;
+			queue_work(driver->diag_wq,
+				&(driver->smd_data[data_type].
 							diag_read_smd_work));
-		} else if (proc_num == LPASS_DATA) {
-			driver->in_busy_lpass_1 = 0;
-			driver->in_busy_lpass_2 = 0;
-			queue_work(driver->diag_wq, &(driver->
-						diag_read_smd_lpass_work));
-		}  else if (proc_num == WCNSS_DATA) {
-			driver->in_busy_wcnss_1 = 0;
-			driver->in_busy_wcnss_2 = 0;
-			queue_work(driver->diag_wq, &(driver->
-				diag_read_smd_wcnss_work));
 		}
 #ifdef CONFIG_DIAG_SDIO_PIPE
-		else if (proc_num == SDIO_DATA) {
+		else if (data_type == SDIO_DATA) {
 			driver->in_busy_sdio = 0;
 			queue_work(driver->diag_sdio_wq,
 				&(driver->diag_read_sdio_work));
 		}
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-		else if (proc_num == HSIC_DATA) {
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+		else if (data_type == HSIC_DATA) {
 			if (driver->hsic_ch)
-				queue_work(driver->diag_bridge_wq,
+				queue_work(diag_bridge[HSIC].wq,
 					&(driver->diag_read_hsic_work));
 		}
 #endif
@@ -360,7 +430,7 @@
 	}
 #ifdef CONFIG_DIAG_OVER_USB
 	else if (driver->logging_mode == USB_MODE) {
-		if (proc_num == APPS_DATA) {
+		if (data_type == APPS_DATA) {
 			driver->write_ptr_svc = (struct diag_request *)
 			(diagmem_alloc(driver, sizeof(struct diag_request),
 				 POOL_TYPE_WRITE_STRUCT));
@@ -371,7 +441,8 @@
 						driver->write_ptr_svc);
 			} else
 				err = -1;
-		} else if (proc_num == MODEM_DATA) {
+		} else if ((data_type >= 0) &&
+				(data_type < NUM_SMD_DATA_CHANNELS)) {
 			write_ptr->buf = buf;
 #ifdef DIAG_DEBUG
 			printk(KERN_INFO "writing data to USB,"
@@ -381,15 +452,9 @@
 					    buf, write_ptr->length, 1);
 #endif /* DIAG DEBUG */
 			err = usb_diag_write(driver->legacy_ch, write_ptr);
-		} else if (proc_num == LPASS_DATA) {
-			write_ptr->buf = buf;
-			err = usb_diag_write(driver->legacy_ch, write_ptr);
-		} else if (proc_num == WCNSS_DATA) {
-			write_ptr->buf = buf;
-			err = usb_diag_write(driver->legacy_ch, write_ptr);
 		}
 #ifdef CONFIG_DIAG_SDIO_PIPE
-		else if (proc_num == SDIO_DATA) {
+		else if (data_type == SDIO_DATA) {
 			if (machine_is_msm8x60_fusion() ||
 					 machine_is_msm8x60_fusn_ffa()) {
 				write_ptr->buf = buf;
@@ -399,8 +464,8 @@
 						"while USB write\n");
 		}
 #endif
-#ifdef CONFIG_DIAG_BRIDGE_CODE
-		else if (proc_num == HSIC_DATA) {
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
+		else if (data_type == HSIC_DATA) {
 			if (driver->hsic_device_enabled) {
 				struct diag_request *write_ptr_mdm;
 				write_ptr_mdm = (struct diag_request *)
@@ -410,9 +475,10 @@
 				if (write_ptr_mdm) {
 					write_ptr_mdm->buf = buf;
 					write_ptr_mdm->length =
-						driver->write_len_mdm;
-					err = usb_diag_write(driver->mdm_ch,
-								write_ptr_mdm);
+					   diag_bridge[HSIC].write_len;
+					write_ptr_mdm->context = (void *)HSIC;
+					err = usb_diag_write(
+					diag_bridge[HSIC].ch, write_ptr_mdm);
 					/* Return to the pool immediately */
 					if (err) {
 						diagmem_free(driver,
@@ -426,14 +492,16 @@
 					err = -1;
 				}
 			} else {
-				pr_err("diag: Incorrect hsic data "
+				pr_err("diag: Incorrect HSIC data "
 						"while USB write\n");
 				err = -1;
 			}
-		} else if (proc_num == SMUX_DATA) {
+		} else if (data_type == SMUX_DATA) {
 				write_ptr->buf = buf;
+				write_ptr->context = (void *)SMUX;
 				pr_debug("diag: writing SMUX data\n");
-				err = usb_diag_write(driver->mdm_ch, write_ptr);
+				err = usb_diag_write(diag_bridge[SMUX].ch,
+								 write_ptr);
 		}
 #endif
 		APPEND_DEBUG('d');
@@ -442,101 +510,6 @@
     return err;
 }
 
-void __diag_smd_wcnss_send_req(void)
-{
-	void *buf = NULL;
-	int *in_busy_wcnss_ptr = NULL;
-	struct diag_request *write_ptr_wcnss = NULL;
-
-	if (!driver->in_busy_wcnss_1) {
-		buf = driver->buf_in_wcnss_1;
-		write_ptr_wcnss = driver->write_ptr_wcnss_1;
-		in_busy_wcnss_ptr = &(driver->in_busy_wcnss_1);
-	} else if (!driver->in_busy_wcnss_2) {
-		buf = driver->buf_in_wcnss_2;
-		write_ptr_wcnss = driver->write_ptr_wcnss_2;
-		in_busy_wcnss_ptr = &(driver->in_busy_wcnss_2);
-	}
-
-	if (driver->ch_wcnss && buf) {
-		int r = smd_read_avail(driver->ch_wcnss);
-		if (r > IN_BUF_SIZE) {
-			if (r < MAX_IN_BUF_SIZE) {
-				pr_err("diag: wcnss packets > %d bytes", r);
-				buf = krealloc(buf, r, GFP_KERNEL);
-			} else {
-				pr_err("diag: wcnss pkt > %d", MAX_IN_BUF_SIZE);
-				return;
-			}
-		}
-		if (r > 0) {
-			if (!buf) {
-				pr_err("Out of diagmem for wcnss\n");
-			} else {
-				APPEND_DEBUG('i');
-				smd_read(driver->ch_wcnss, buf, r);
-				APPEND_DEBUG('j');
-				write_ptr_wcnss->length = r;
-				*in_busy_wcnss_ptr = 1;
-				diag_device_write(buf, WCNSS_DATA,
-					 write_ptr_wcnss);
-			}
-		}
-	} else if (driver->ch_wcnss && !buf &&
-		(driver->logging_mode == MEMORY_DEVICE_MODE)) {
-		chk_logging_wakeup();
-	}
-}
-
-void __diag_smd_lpass_send_req(void)
-{
-	void *buf = NULL;
-	int *in_busy_lpass_ptr = NULL;
-	struct diag_request *write_ptr_lpass = NULL;
-
-	if (!driver->in_busy_lpass_1) {
-		buf = driver->buf_in_lpass_1;
-		write_ptr_lpass = driver->write_ptr_lpass_1;
-		in_busy_lpass_ptr = &(driver->in_busy_lpass_1);
-	} else if (!driver->in_busy_lpass_2) {
-		buf = driver->buf_in_lpass_2;
-		write_ptr_lpass = driver->write_ptr_lpass_2;
-		in_busy_lpass_ptr = &(driver->in_busy_lpass_2);
-	}
-
-	if (driver->chlpass && buf) {
-		int r = smd_read_avail(driver->chlpass);
-
-		if (r > IN_BUF_SIZE) {
-			if (r < MAX_IN_BUF_SIZE) {
-				pr_err("diag: SMD sending in "
-						   "packets upto %d bytes", r);
-				buf = krealloc(buf, r, GFP_KERNEL);
-			} else {
-				pr_err("diag: SMD sending in "
-				"packets more than %d bytes", MAX_IN_BUF_SIZE);
-				return;
-			}
-		}
-		if (r > 0) {
-			if (!buf)
-				printk(KERN_INFO "Out of diagmem for LPASS\n");
-			else {
-				APPEND_DEBUG('i');
-				smd_read(driver->chlpass, buf, r);
-				APPEND_DEBUG('j');
-				write_ptr_lpass->length = r;
-				*in_busy_lpass_ptr = 1;
-				diag_device_write(buf, LPASS_DATA,
-							 write_ptr_lpass);
-			}
-		}
-	} else if (driver->chlpass && !buf &&
-		(driver->logging_mode == MEMORY_DEVICE_MODE)) {
-		chk_logging_wakeup();
-	}
-}
-
 static void diag_update_pkt_buffer(unsigned char *buf)
 {
 	unsigned char *ptr = driver->pkt_buf;
@@ -576,6 +549,15 @@
 	mutex_unlock(&driver->diagchar_mutex);
 }
 
+static int diag_check_mode_reset(unsigned char *buf)
+{
+	int is_mode_reset = 0;
+	if (chk_apps_master() && (int)(*(char *)buf) == MODE_CMD)
+		if ((int)(*(char *)(buf+1)) == RESET_ID)
+			is_mode_reset = 1;
+	return is_mode_reset;
+}
+
 void diag_send_data(struct diag_master_table entry, unsigned char *buf,
 					 int len, int type)
 {
@@ -585,21 +567,23 @@
 		diag_update_sleeping_process(entry.process_id, PKT_TYPE);
 	} else {
 		if (len > 0) {
-			if (entry.client_id == MODEM_PROC && driver->ch) {
-				if (chk_apps_master() &&
-					 (int)(*(char *)buf) == MODE_CMD)
-					if ((int)(*(char *)(buf+1)) ==
-						RESET_ID)
+			if ((entry.client_id >= 0) &&
+				(entry.client_id < NUM_SMD_DATA_CHANNELS)) {
+				int index = entry.client_id;
+				if (driver->smd_data[index].ch) {
+					if ((index == MODEM_DATA) &&
+						diag_check_mode_reset(buf)) {
 						return;
-				smd_write(driver->ch, buf, len);
-			} else if (entry.client_id == LPASS_PROC &&
-							 driver->chlpass) {
-				smd_write(driver->chlpass, buf, len);
-			} else if (entry.client_id == WCNSS_PROC &&
-							 driver->ch_wcnss) {
-				smd_write(driver->ch_wcnss, buf, len);
+					}
+					smd_write(driver->smd_data[index].ch,
+							buf, len);
+				} else {
+					pr_err("diag: In %s, smd channel %d not open\n",
+						__func__, index);
+				}
 			} else {
-				pr_alert("diag: incorrect channel");
+				pr_alert("diag: In %s, incorrect channel: %d",
+					__func__, entry.client_id);
 			}
 		}
 	}
@@ -680,7 +664,8 @@
 		return 0;
 	}
 	/* Check for Apps Only & get event mask request */
-	else if (!(driver->ch) && chk_apps_only() && *buf == 0x81) {
+	else if (!(driver->smd_data[MODEM_DATA].ch) && chk_apps_only() &&
+								*buf == 0x81) {
 		driver->apps_rsp_buf[0] = 0x81;
 		driver->apps_rsp_buf[1] = 0x0;
 		*(uint16_t *)(driver->apps_rsp_buf + 2) = 0x0;
@@ -691,7 +676,7 @@
 		return 0;
 	}
 	/* Get log ID range & Check for Apps Only */
-	else if (!(driver->ch) && chk_apps_only()
+	else if (!(driver->smd_data[MODEM_DATA].ch) && chk_apps_only()
 			  && (*buf == 0x73) && *(int *)(buf+4) == 1) {
 		driver->apps_rsp_buf[0] = 0x73;
 		*(int *)(driver->apps_rsp_buf + 4) = 0x1; /* operation ID */
@@ -716,7 +701,7 @@
 		return 0;
 	}
 	/* Respond to Get SSID Range request message */
-	else if (!(driver->ch) && chk_apps_only()
+	else if (!(driver->smd_data[MODEM_DATA].ch) && chk_apps_only()
 			 && (*buf == 0x7d) && (*(buf+1) == 0x1)) {
 		driver->apps_rsp_buf[0] = 0x7d;
 		driver->apps_rsp_buf[1] = 0x1;
@@ -774,7 +759,7 @@
 		return 0;
 	}
 	/* Check for Apps Only Respond to Get Subsys Build mask */
-	else if (!(driver->ch) && chk_apps_only()
+	else if (!(driver->smd_data[MODEM_DATA].ch) && chk_apps_only()
 			 && (*buf == 0x7d) && (*(buf+1) == 0x2)) {
 		ssid_first = *(uint16_t *)(buf + 2);
 		ssid_last = *(uint16_t *)(buf + 4);
@@ -913,6 +898,23 @@
 			return 0;
 		}
 	}
+	/* Return the Delayed Response Wrap Status */
+	else if ((*buf == 0x4b) && (*(buf+1) == 0x32) &&
+		(*(buf+2) == 0x04) && (*(buf+3) == 0x0)) {
+		memcpy(driver->apps_rsp_buf, buf, 4);
+		driver->apps_rsp_buf[4] = wrap_enabled;
+		encode_rsp_and_send(4);
+		return 0;
+	}
+	/* Wrap the Delayed Rsp ID */
+	else if ((*buf == 0x4b) && (*(buf+1) == 0x32) &&
+		(*(buf+2) == 0x05) && (*(buf+3) == 0x0)) {
+		wrap_enabled = true;
+		memcpy(driver->apps_rsp_buf, buf, 4);
+		driver->apps_rsp_buf[4] = wrap_count;
+		encode_rsp_and_send(5);
+		return 0;
+	}
 	 /* Check for ID for NO MODEM present */
 	else if (chk_polling_response()) {
 		/* respond to 0x0 command */
@@ -991,13 +993,14 @@
 		type = 0;
 	}
 	/* implies this packet is NOT meant for apps */
-	if (!(driver->ch) && type == 1) {
+	if (!(driver->smd_data[MODEM_DATA].ch) && type == 1) {
 		if (chk_apps_only()) {
 			diag_send_error_rsp(hdlc.dest_idx);
 		} else { /* APQ 8060, Let Q6 respond */
-			if (driver->chlpass)
-				smd_write(driver->chlpass, driver->hdlc_buf,
-						  hdlc.dest_idx - 3);
+			if (driver->smd_data[LPASS_DATA].ch)
+				smd_write(driver->smd_data[LPASS_DATA].ch,
+						driver->hdlc_buf,
+						hdlc.dest_idx - 3);
 		}
 		type = 0;
 	}
@@ -1009,9 +1012,11 @@
 							driver->hdlc_buf)+i));
 #endif /* DIAG DEBUG */
 	/* ignore 2 bytes for CRC, one for 7E and send */
-	if ((driver->ch) && (ret) && (type) && (hdlc.dest_idx > 3)) {
+	if ((driver->smd_data[MODEM_DATA].ch) && (ret) && (type) &&
+						(hdlc.dest_idx > 3)) {
 		APPEND_DEBUG('g');
-		smd_write(driver->ch, driver->hdlc_buf, hdlc.dest_idx - 3);
+		smd_write(driver->smd_data[MODEM_DATA].ch,
+					driver->hdlc_buf, hdlc.dest_idx - 3);
 		APPEND_DEBUG('h');
 #ifdef DIAG_DEBUG
 		printk(KERN_INFO "writing data to SMD, pkt length %d\n", len);
@@ -1029,6 +1034,7 @@
 int diagfwd_connect(void)
 {
 	int err;
+	int i;
 
 	printk(KERN_DEBUG "diag: USB connected\n");
 	err = usb_diag_alloc_req(driver->legacy_ch, N_LEGACY_WRITE,
@@ -1037,21 +1043,16 @@
 		printk(KERN_ERR "diag: unable to alloc USB req on legacy ch");
 
 	driver->usb_connected = 1;
-	driver->in_busy_1 = 0;
-	driver->in_busy_2 = 0;
-	driver->in_busy_lpass_1 = 0;
-	driver->in_busy_lpass_2 = 0;
-	driver->in_busy_wcnss_1 = 0;
-	driver->in_busy_wcnss_2 = 0;
+	for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+		driver->smd_data[i].in_busy_1 = 0;
+		driver->smd_data[i].in_busy_2 = 0;
+		/* Poll SMD data channels to check for data */
+		queue_work(driver->diag_wq,
+			&(driver->smd_data[i].diag_read_smd_work));
+		/* Poll SMD CNTL channels to check for data */
+		diag_smd_notify(&(driver->smd_cntl[i]), SMD_EVENT_DATA);
+	}
 
-	/* Poll SMD channels to check for data*/
-	queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
-	queue_work(driver->diag_wq, &(driver->diag_read_smd_lpass_work));
-	queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work));
-	/* Poll SMD CNTL channels to check for data */
-	diag_smd_cntl_notify(NULL, SMD_EVENT_DATA);
-	diag_smd_lpass_cntl_notify(NULL, SMD_EVENT_DATA);
-	diag_smd_wcnss_cntl_notify(NULL, SMD_EVENT_DATA);
 	/* Poll USB channel to check for data*/
 	queue_work(driver->diag_wq, &(driver->diag_read_work));
 #ifdef CONFIG_DIAG_SDIO_PIPE
@@ -1067,17 +1068,17 @@
 
 int diagfwd_disconnect(void)
 {
+	int i;
+
 	printk(KERN_DEBUG "diag: USB disconnected\n");
 	driver->usb_connected = 0;
 	driver->debug_flag = 1;
 	usb_diag_free_req(driver->legacy_ch);
 	if (driver->logging_mode == USB_MODE) {
-		driver->in_busy_1 = 1;
-		driver->in_busy_2 = 1;
-		driver->in_busy_lpass_1 = 1;
-		driver->in_busy_lpass_2 = 1;
-		driver->in_busy_wcnss_1 = 1;
-		driver->in_busy_wcnss_2 = 1;
+		for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+			driver->smd_data[i].in_busy_1 = 1;
+			driver->smd_data[i].in_busy_2 = 1;
+		}
 	}
 #ifdef CONFIG_DIAG_SDIO_PIPE
 	if (machine_is_msm8x60_fusion() || machine_is_msm8x60_fusn_ffa())
@@ -1091,50 +1092,44 @@
 int diagfwd_write_complete(struct diag_request *diag_write_ptr)
 {
 	unsigned char *buf = diag_write_ptr->buf;
-	/*Determine if the write complete is for data from modem/apps/q6 */
+	int found_it = 0;
+	int i;
+
+	/* Determine if the write complete is for data from modem/apps/q6 */
 	/* Need a context variable here instead */
-	if (buf == (void *)driver->buf_in_1) {
-		driver->in_busy_1 = 0;
-		APPEND_DEBUG('o');
-		queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
-	} else if (buf == (void *)driver->buf_in_2) {
-		driver->in_busy_2 = 0;
-		APPEND_DEBUG('O');
-		queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
-	} else if (buf == (void *)driver->buf_in_lpass_1) {
-		driver->in_busy_lpass_1 = 0;
-		APPEND_DEBUG('p');
-		queue_work(driver->diag_wq,
-				&(driver->diag_read_smd_lpass_work));
-	} else if (buf == (void *)driver->buf_in_lpass_2) {
-		driver->in_busy_lpass_2 = 0;
-		APPEND_DEBUG('P');
-		queue_work(driver->diag_wq,
-				&(driver->diag_read_smd_lpass_work));
-	} else if (buf == driver->buf_in_wcnss_1) {
-		driver->in_busy_wcnss_1 = 0;
-		APPEND_DEBUG('r');
-		queue_work(driver->diag_wq,
-			 &(driver->diag_read_smd_wcnss_work));
-	} else if (buf == driver->buf_in_wcnss_2) {
-		driver->in_busy_wcnss_2 = 0;
-		APPEND_DEBUG('R');
-		queue_work(driver->diag_wq,
-			 &(driver->diag_read_smd_wcnss_work));
+	for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++) {
+		struct diag_smd_info *data = &(driver->smd_data[i]);
+		if (buf == (void *)data->buf_in_1) {
+			data->in_busy_1 = 0;
+			queue_work(driver->diag_wq,
+				&(data->diag_read_smd_work));
+			found_it = 1;
+			break;
+		} else if (buf == (void *)data->buf_in_2) {
+			data->in_busy_2 = 0;
+			queue_work(driver->diag_wq,
+				&(data->diag_read_smd_work));
+			found_it = 1;
+			break;
+		}
 	}
 #ifdef CONFIG_DIAG_SDIO_PIPE
-	else if (buf == (void *)driver->buf_in_sdio)
-		if (machine_is_msm8x60_fusion() ||
-			 machine_is_msm8x60_fusn_ffa())
-			diagfwd_write_complete_sdio();
-		else
-			pr_err("diag: Incorrect buffer pointer while WRITE");
+	if (!found_it) {
+		if (buf == (void *)driver->buf_in_sdio) {
+			if (machine_is_msm8x60_fusion() ||
+				 machine_is_msm8x60_fusn_ffa())
+				diagfwd_write_complete_sdio();
+			else
+				pr_err("diag: Incorrect buffer pointer while WRITE");
+			found_it = 1;
+		}
+	}
 #endif
-	else {
-		diagmem_free(driver, (unsigned char *)buf, POOL_TYPE_HDLC);
+	if (!found_it) {
+		diagmem_free(driver, (unsigned char *)buf,
+						POOL_TYPE_HDLC);
 		diagmem_free(driver, (unsigned char *)diag_write_ptr,
-						 POOL_TYPE_WRITE_STRUCT);
-		APPEND_DEBUG('q');
+						POOL_TYPE_WRITE_STRUCT);
 	}
 	return 0;
 }
@@ -1221,70 +1216,87 @@
 
 #endif /* DIAG OVER USB */
 
-static void diag_smd_notify(void *ctxt, unsigned event)
+void diag_smd_notify(void *ctxt, unsigned event)
 {
-	if (event == SMD_EVENT_CLOSE) {
-		queue_work(driver->diag_cntl_wq,
-			 &(driver->diag_clean_modem_reg_work));
-		driver->ch = 0;
+	struct diag_smd_info *smd_info = (struct diag_smd_info *)ctxt;
+	if (!smd_info)
 		return;
-	} else if (event == SMD_EVENT_OPEN) {
-		if (ch_temp)
-			driver->ch = ch_temp;
-	}
-	queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
-}
 
-#if defined(CONFIG_MSM_N_WAY_SMD)
-static void diag_smd_lpass_notify(void *ctxt, unsigned event)
-{
 	if (event == SMD_EVENT_CLOSE) {
-		queue_work(driver->diag_cntl_wq,
-			 &(driver->diag_clean_lpass_reg_work));
-		driver->chlpass = 0;
+		smd_info->ch = 0;
+		wake_up(&driver->smd_wait_q);
+		if (smd_info->type == SMD_DATA_TYPE) {
+			smd_info->notify_context = event;
+			queue_work(driver->diag_cntl_wq,
+				 &(smd_info->diag_notify_update_smd_work));
+		} else if (smd_info->type == SMD_DCI_TYPE) {
+			/* Notify the clients of the close */
+			diag_dci_notify_client(smd_info->peripheral_mask,
+							DIAG_STATUS_CLOSED);
+		}
 		return;
 	} else if (event == SMD_EVENT_OPEN) {
-		if (chlpass_temp)
-			driver->chlpass = chlpass_temp;
-	}
-	queue_work(driver->diag_wq, &(driver->diag_read_smd_lpass_work));
-}
-#endif
+		if (smd_info->ch_save)
+			smd_info->ch = smd_info->ch_save;
 
-static void diag_smd_wcnss_notify(void *ctxt, unsigned event)
-{
-	if (event == SMD_EVENT_CLOSE) {
-		queue_work(driver->diag_cntl_wq,
-			 &(driver->diag_clean_wcnss_reg_work));
-		driver->ch_wcnss = 0;
-		return;
-	} else if (event == SMD_EVENT_OPEN) {
-		if (ch_wcnss_temp)
-			driver->ch_wcnss = ch_wcnss_temp;
+		if (smd_info->type == SMD_CNTL_TYPE) {
+			smd_info->notify_context = event;
+			queue_work(driver->diag_cntl_wq,
+				&(smd_info->diag_notify_update_smd_work));
+		} else if (smd_info->type == SMD_DCI_TYPE) {
+			smd_info->notify_context = event;
+			queue_work(driver->diag_dci_wq,
+				&(smd_info->diag_notify_update_smd_work));
+			/* Notify the clients of the open */
+			diag_dci_notify_client(smd_info->peripheral_mask,
+							DIAG_STATUS_OPEN);
+		}
 	}
-	queue_work(driver->diag_wq, &(driver->diag_read_smd_wcnss_work));
+
+	wake_up(&driver->smd_wait_q);
+
+	if (smd_info->type == SMD_DCI_TYPE)
+		queue_work(driver->diag_dci_wq,
+				&(smd_info->diag_read_smd_work));
+	else
+		queue_work(driver->diag_wq, &(smd_info->diag_read_smd_work));
 }
 
 static int diag_smd_probe(struct platform_device *pdev)
 {
 	int r = 0;
+	int index = -1;
 
 	if (pdev->id == SMD_APPS_MODEM) {
-		r = smd_open("DIAG", &driver->ch, driver, diag_smd_notify);
-		ch_temp = driver->ch;
+		index = MODEM_DATA;
+		r = smd_open("DIAG", &driver->smd_data[index].ch,
+					&driver->smd_data[index],
+					diag_smd_notify);
+		driver->smd_data[index].ch_save =
+					driver->smd_data[index].ch;
 	}
 #if defined(CONFIG_MSM_N_WAY_SMD)
 	if (pdev->id == SMD_APPS_QDSP) {
+		index = LPASS_DATA;
 		r = smd_named_open_on_edge("DIAG", SMD_APPS_QDSP,
-			&driver->chlpass, driver, diag_smd_lpass_notify);
-		chlpass_temp = driver->chlpass;
+					&driver->smd_data[index].ch,
+					&driver->smd_data[index],
+					diag_smd_notify);
+		driver->smd_data[index].ch_save =
+					driver->smd_data[index].ch;
 	}
 #endif
 	if (pdev->id == SMD_APPS_WCNSS) {
-		r = smd_named_open_on_edge("APPS_RIVA_DATA", SMD_APPS_WCNSS
-			, &driver->ch_wcnss, driver, diag_smd_wcnss_notify);
-		ch_wcnss_temp = driver->ch_wcnss;
+		index = WCNSS_DATA;
+		r = smd_named_open_on_edge("APPS_RIVA_DATA",
+					SMD_APPS_WCNSS,
+					&driver->smd_data[index].ch,
+					&driver->smd_data[index],
+					diag_smd_notify);
+		driver->smd_data[index].ch_save =
+					driver->smd_data[index].ch;
 	}
+
 	pm_runtime_set_active(&pdev->dev);
 	pm_runtime_enable(&pdev->dev);
 	pr_debug("diag: open SMD port, Id = %d, r = %d\n", pdev->id, r);
@@ -1292,86 +1304,192 @@
 	return 0;
 }
 
-static int diagfwd_runtime_suspend(struct device *dev)
+static int diag_smd_runtime_suspend(struct device *dev)
 {
 	dev_dbg(dev, "pm_runtime: suspending...\n");
 	return 0;
 }
 
-static int diagfwd_runtime_resume(struct device *dev)
+static int diag_smd_runtime_resume(struct device *dev)
 {
 	dev_dbg(dev, "pm_runtime: resuming...\n");
 	return 0;
 }
 
-static const struct dev_pm_ops diagfwd_dev_pm_ops = {
-	.runtime_suspend = diagfwd_runtime_suspend,
-	.runtime_resume = diagfwd_runtime_resume,
+static const struct dev_pm_ops diag_smd_dev_pm_ops = {
+	.runtime_suspend = diag_smd_runtime_suspend,
+	.runtime_resume = diag_smd_runtime_resume,
 };
 
 static struct platform_driver msm_smd_ch1_driver = {
 
 	.probe = diag_smd_probe,
 	.driver = {
-		   .name = "DIAG",
-		   .owner = THIS_MODULE,
-		   .pm   = &diagfwd_dev_pm_ops,
-		   },
+		.name = "DIAG",
+		.owner = THIS_MODULE,
+		.pm   = &diag_smd_dev_pm_ops,
+	},
 };
 
 static struct platform_driver diag_smd_lite_driver = {
 
 	.probe = diag_smd_probe,
 	.driver = {
-		   .name = "APPS_RIVA_DATA",
-		   .owner = THIS_MODULE,
-		   .pm   = &diagfwd_dev_pm_ops,
-		   },
+		.name = "APPS_RIVA_DATA",
+		.owner = THIS_MODULE,
+		.pm   = &diag_smd_dev_pm_ops,
+	},
 };
 
+void diag_smd_destructor(struct diag_smd_info *smd_info)
+{
+	if (smd_info->ch)
+		smd_close(smd_info->ch);
+
+	smd_info->ch = 0;
+	smd_info->ch_save = 0;
+	kfree(smd_info->buf_in_1);
+	kfree(smd_info->buf_in_2);
+	kfree(smd_info->write_ptr_1);
+	kfree(smd_info->write_ptr_2);
+}
+
+int diag_smd_constructor(struct diag_smd_info *smd_info, int peripheral,
+			  int type)
+{
+	smd_info->peripheral = peripheral;
+	smd_info->type = type;
+
+	switch (peripheral) {
+	case MODEM_DATA:
+		smd_info->peripheral_mask = DIAG_CON_MPSS;
+		break;
+	case LPASS_DATA:
+		smd_info->peripheral_mask = DIAG_CON_LPASS;
+		break;
+	case WCNSS_DATA:
+		smd_info->peripheral_mask = DIAG_CON_WCNSS;
+		break;
+	default:
+		pr_err("diag: In %s, unknown peripheral, peripheral: %d\n",
+			__func__, peripheral);
+		goto err;
+	}
+
+	smd_info->ch = 0;
+	smd_info->ch_save = 0;
+
+	if (smd_info->buf_in_1 == NULL) {
+		smd_info->buf_in_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (smd_info->buf_in_1 == NULL)
+			goto err;
+		kmemleak_not_leak(smd_info->buf_in_1);
+	}
+
+	if (smd_info->write_ptr_1 == NULL) {
+		smd_info->write_ptr_1 = kzalloc(sizeof(struct diag_request),
+								GFP_KERNEL);
+		if (smd_info->write_ptr_1 == NULL)
+			goto err;
+		kmemleak_not_leak(smd_info->write_ptr_1);
+	}
+
+	/* The smd data type needs two buffers */
+	if (smd_info->type == SMD_DATA_TYPE) {
+		if (smd_info->buf_in_2 == NULL) {
+			smd_info->buf_in_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+			if (smd_info->buf_in_2 == NULL)
+				goto err;
+			kmemleak_not_leak(smd_info->buf_in_2);
+		}
+		if (smd_info->write_ptr_2 == NULL) {
+			smd_info->write_ptr_2 =
+				kzalloc(sizeof(struct diag_request),
+				GFP_KERNEL);
+			if (smd_info->write_ptr_2 == NULL)
+				goto err;
+			kmemleak_not_leak(smd_info->write_ptr_2);
+		}
+	}
+
+	INIT_WORK(&(smd_info->diag_read_smd_work), diag_read_smd_work_fn);
+
+	/*
+	 * The update function assigned to the diag_notify_update_smd_work
+	 * work_struct is meant to be used for updating that is not to
+	 * be done in the context of the smd notify function. The
+	 * notify_context variable can be used for passing additional
+	 * information to the update function.
+	 */
+	smd_info->notify_context = 0;
+	if (type == SMD_DATA_TYPE)
+		INIT_WORK(&(smd_info->diag_notify_update_smd_work),
+							diag_clean_reg_fn);
+	else if (type == SMD_CNTL_TYPE)
+		INIT_WORK(&(smd_info->diag_notify_update_smd_work),
+							diag_mask_update_fn);
+	else if (type == SMD_DCI_TYPE)
+		INIT_WORK(&(smd_info->diag_notify_update_smd_work),
+						diag_update_smd_dci_work_fn);
+	else {
+		pr_err("diag: In %s, unknown type, type: %d\n", __func__, type);
+		goto err;
+	}
+
+	/*
+	 * Set function ptr for function to call to process the data that
+	 * was just read from the smd channel
+	 */
+	if (type == SMD_DATA_TYPE)
+		smd_info->process_smd_read_data = diag_process_smd_read_data;
+	else if (type == SMD_CNTL_TYPE)
+		smd_info->process_smd_read_data =
+						diag_process_smd_cntl_read_data;
+	else if (type == SMD_DCI_TYPE)
+		smd_info->process_smd_read_data =
+						diag_process_smd_dci_read_data;
+	else {
+		pr_err("diag: In %s, unknown type, type: %d\n", __func__, type);
+		goto err;
+	}
+
+	return 1;
+err:
+	kfree(smd_info->buf_in_1);
+	kfree(smd_info->buf_in_2);
+	kfree(smd_info->write_ptr_1);
+	kfree(smd_info->write_ptr_2);
+
+	return 0;
+}
+
 void diagfwd_init(void)
 {
+	int success;
+	int i;
+
+	wrap_enabled = 0;
+	wrap_count = 0;
 	diag_debug_buf_idx = 0;
 	driver->read_len_legacy = 0;
 	driver->use_device_tree = has_device_tree();
 	mutex_init(&driver->diag_cntl_mutex);
 
-	if (driver->buf_in_1 == NULL) {
-		driver->buf_in_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_1 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->buf_in_1);
-	}
-	if (driver->buf_in_2 == NULL) {
-		driver->buf_in_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_2 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->buf_in_2);
-	}
-	if (driver->buf_in_lpass_1 == NULL) {
-		driver->buf_in_lpass_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_lpass_1 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->buf_in_lpass_1);
-	}
-	if (driver->buf_in_lpass_2 == NULL) {
-		driver->buf_in_lpass_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_lpass_2 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->buf_in_lpass_2);
-	}
-	if (driver->buf_in_wcnss_1 == NULL) {
-		driver->buf_in_wcnss_1 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_wcnss_1 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->buf_in_wcnss_1);
-	}
-	if (driver->buf_in_wcnss_2 == NULL) {
-		driver->buf_in_wcnss_2 = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_wcnss_2 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->buf_in_wcnss_2);
-	}
+	success = diag_smd_constructor(&driver->smd_data[MODEM_DATA],
+					MODEM_DATA, SMD_DATA_TYPE);
+	if (!success)
+		goto err;
+
+	success = diag_smd_constructor(&driver->smd_data[LPASS_DATA],
+					LPASS_DATA, SMD_DATA_TYPE);
+	if (!success)
+		goto err;
+
+	success = diag_smd_constructor(&driver->smd_data[WCNSS_DATA],
+					WCNSS_DATA, SMD_DATA_TYPE);
+	if (!success)
+		goto err;
+
 	if (driver->usb_buf_out  == NULL &&
 	     (driver->usb_buf_out = kzalloc(USB_MAX_OUT_BUF,
 					 GFP_KERNEL)) == NULL)
@@ -1409,48 +1527,6 @@
 		       GFP_KERNEL)) == NULL)
 		goto err;
 	kmemleak_not_leak(driver->table);
-	if (driver->write_ptr_1 == NULL) {
-		driver->write_ptr_1 = kzalloc(
-			sizeof(struct diag_request), GFP_KERNEL);
-		if (driver->write_ptr_1 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->write_ptr_1);
-	}
-	if (driver->write_ptr_2 == NULL) {
-		driver->write_ptr_2 = kzalloc(
-			sizeof(struct diag_request), GFP_KERNEL);
-		if (driver->write_ptr_2 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->write_ptr_2);
-	}
-	if (driver->write_ptr_lpass_1 == NULL) {
-		driver->write_ptr_lpass_1 = kzalloc(
-			sizeof(struct diag_request), GFP_KERNEL);
-		if (driver->write_ptr_lpass_1 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->write_ptr_lpass_1);
-	}
-	if (driver->write_ptr_lpass_2 == NULL) {
-		driver->write_ptr_lpass_2 = kzalloc(
-			sizeof(struct diag_request), GFP_KERNEL);
-		if (driver->write_ptr_lpass_2 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->write_ptr_lpass_2);
-	}
-	if (driver->write_ptr_wcnss_1 == NULL) {
-		driver->write_ptr_wcnss_1 = kzalloc(
-			sizeof(struct diag_request), GFP_KERNEL);
-		if (driver->write_ptr_wcnss_1 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->write_ptr_wcnss_1);
-	}
-	if (driver->write_ptr_wcnss_2 == NULL) {
-		driver->write_ptr_wcnss_2 = kzalloc(
-			sizeof(struct diag_request), GFP_KERNEL);
-		if (driver->write_ptr_wcnss_2 == NULL)
-			goto err;
-		kmemleak_not_leak(driver->write_ptr_wcnss_2);
-	}
 
 	if (driver->usb_read_ptr == NULL) {
 		driver->usb_read_ptr = kzalloc(
@@ -1483,16 +1559,13 @@
 #endif
 	platform_driver_register(&msm_smd_ch1_driver);
 	platform_driver_register(&diag_smd_lite_driver);
-
 	return;
 err:
 	pr_err("diag: Could not initialize diag buffers");
-	kfree(driver->buf_in_1);
-	kfree(driver->buf_in_2);
-	kfree(driver->buf_in_lpass_1);
-	kfree(driver->buf_in_lpass_2);
-	kfree(driver->buf_in_wcnss_1);
-	kfree(driver->buf_in_wcnss_2);
+
+	for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++)
+		diag_smd_destructor(&driver->smd_data[i]);
+
 	kfree(driver->buf_msg_mask_update);
 	kfree(driver->buf_log_mask_update);
 	kfree(driver->buf_event_mask_update);
@@ -1503,12 +1576,6 @@
 	kfree(driver->data_ready);
 	kfree(driver->table);
 	kfree(driver->pkt_buf);
-	kfree(driver->write_ptr_1);
-	kfree(driver->write_ptr_2);
-	kfree(driver->write_ptr_lpass_1);
-	kfree(driver->write_ptr_lpass_2);
-	kfree(driver->write_ptr_wcnss_1);
-	kfree(driver->write_ptr_wcnss_2);
 	kfree(driver->usb_read_ptr);
 	kfree(driver->apps_rsp_buf);
 	kfree(driver->user_space_data);
@@ -1518,12 +1585,11 @@
 
 void diagfwd_exit(void)
 {
-	smd_close(driver->ch);
-	smd_close(driver->chlpass);
-	smd_close(driver->ch_wcnss);
-	driver->ch = 0;		/* SMD can make this NULL */
-	driver->chlpass = 0;
-	driver->ch_wcnss = 0;
+	int i;
+
+	for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++)
+		diag_smd_destructor(&driver->smd_data[i]);
+
 #ifdef CONFIG_DIAG_OVER_USB
 	if (driver->usb_connected)
 		usb_diag_free_req(driver->legacy_ch);
@@ -1532,12 +1598,7 @@
 	platform_driver_unregister(&msm_smd_ch1_driver);
 	platform_driver_unregister(&msm_diag_dci_driver);
 	platform_driver_unregister(&diag_smd_lite_driver);
-	kfree(driver->buf_in_1);
-	kfree(driver->buf_in_2);
-	kfree(driver->buf_in_lpass_1);
-	kfree(driver->buf_in_lpass_2);
-	kfree(driver->buf_in_wcnss_1);
-	kfree(driver->buf_in_wcnss_2);
+
 	kfree(driver->buf_msg_mask_update);
 	kfree(driver->buf_log_mask_update);
 	kfree(driver->buf_event_mask_update);
@@ -1548,12 +1609,6 @@
 	kfree(driver->data_ready);
 	kfree(driver->table);
 	kfree(driver->pkt_buf);
-	kfree(driver->write_ptr_1);
-	kfree(driver->write_ptr_2);
-	kfree(driver->write_ptr_lpass_1);
-	kfree(driver->write_ptr_lpass_2);
-	kfree(driver->write_ptr_wcnss_1);
-	kfree(driver->write_ptr_wcnss_2);
 	kfree(driver->usb_read_ptr);
 	kfree(driver->apps_rsp_buf);
 	kfree(driver->user_space_data);
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index a0631d6..14e2dd5 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -22,9 +22,7 @@
 void diagfwd_init(void);
 void diagfwd_exit(void);
 void diag_process_hdlc(void *data, unsigned len);
-void __diag_smd_send_req(void);
-void __diag_smd_lpass_send_req(void);
-void __diag_smd_wcnss_send_req(void);
+void diag_smd_send_req(struct diag_smd_info *smd_info);
 void diag_usb_legacy_notifier(void *, unsigned, struct diag_request *);
 long diagchar_ioctl(struct file *, unsigned int, unsigned long);
 int diag_device_write(void *, int, struct diag_request *);
@@ -37,6 +35,10 @@
 void diag_update_userspace_clients(unsigned int type);
 void diag_update_sleeping_process(int process_id, int data_type);
 void encode_rsp_and_send(int buf_length);
+void diag_smd_notify(void *ctxt, unsigned event);
+int diag_smd_constructor(struct diag_smd_info *smd_info, int peripheral,
+			 int type);
+void diag_smd_destructor(struct diag_smd_info *smd_info);
 /* State for diag forwarding */
 #ifdef CONFIG_DIAG_OVER_USB
 int diagfwd_connect(void);
diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c
new file mode 100644
index 0000000..75fdeb4
--- /dev/null
+++ b/drivers/char/diag/diagfwd_bridge.c
@@ -0,0 +1,355 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/diagchar.h>
+#include <linux/kmemleak.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/ratelimit.h>
+#include <linux/platform_device.h>
+#include <linux/smux.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include "diagchar.h"
+#include "diagmem.h"
+#include "diagfwd_cntl.h"
+#include "diagfwd_smux.h"
+#include "diagfwd_hsic.h"
+#include "diag_masks.h"
+#include "diagfwd_bridge.h"
+
+struct diag_bridge_dev *diag_bridge;
+
+/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
+int diagfwd_connect_bridge(int process_cable)
+{
+	int i;
+
+	pr_debug("diag: in %s\n", __func__);
+
+	for (i = 0; i < MAX_BRIDGES; i++)
+		if (diag_bridge[i].enabled)
+			connect_bridge(process_cable, i);
+	return 0;
+}
+
+void connect_bridge(int process_cable, int index)
+{
+	int err;
+
+	mutex_lock(&diag_bridge[index].bridge_mutex);
+	/* If the usb cable is being connected */
+	if (process_cable) {
+		err = usb_diag_alloc_req(diag_bridge[index].ch, N_MDM_WRITE,
+			       N_MDM_READ);
+		if (err)
+			pr_err("diag: unable to alloc USB req on mdm ch err:%d\n",
+							 err);
+
+		diag_bridge[index].usb_connected = 1;
+	}
+
+	if (index == SMUX && driver->diag_smux_enabled) {
+		driver->in_busy_smux = 0;
+		diagfwd_connect_smux();
+	} else if (index == HSIC && driver->hsic_device_enabled) {
+		driver->in_busy_hsic_read_on_device = 0;
+		driver->in_busy_hsic_write = 0;
+		/* If the HSIC (diag_bridge) platform device is not open */
+		if (!driver->hsic_device_opened) {
+			err = diag_bridge_open(&hsic_diag_bridge_ops);
+			if (err) {
+				pr_err("diag: HSIC channel open error: %d\n",
+					  err);
+			} else {
+				pr_debug("diag: opened HSIC channel\n");
+				driver->hsic_device_opened = 1;
+			}
+		} else {
+			pr_debug("diag: HSIC channel already open\n");
+		}
+		/*
+		 * Turn on communication over usb mdm and HSIC, if the HSIC
+		 * device driver is enabled and opened
+		 */
+		if (driver->hsic_device_opened) {
+			driver->hsic_ch = 1;
+			/* Poll USB mdm channel to check for data */
+			if (driver->logging_mode == USB_MODE)
+				queue_work(diag_bridge[HSIC].wq,
+					  &diag_bridge[HSIC].diag_read_work);
+			/* Poll HSIC channel to check for data */
+			queue_work(diag_bridge[HSIC].wq,
+				   &driver->diag_read_hsic_work);
+		}
+	}
+	mutex_unlock(&diag_bridge[index].bridge_mutex);
+}
+
+/*
+ * diagfwd_disconnect_bridge is called when the USB mdm channel
+ * is disconnected. So disconnect should happen for all bridges
+ */
+int diagfwd_disconnect_bridge(int process_cable)
+{
+	int i;
+	pr_debug("diag: In %s, process_cable: %d\n", __func__, process_cable);
+
+	for (i = 0; i < MAX_BRIDGES; i++) {
+		if (diag_bridge[i].enabled) {
+			mutex_lock(&diag_bridge[i].bridge_mutex);
+			/* If the usb cable is being disconnected */
+			if (process_cable) {
+				diag_bridge[i].usb_connected = 0;
+				usb_diag_free_req(diag_bridge[i].ch);
+			}
+
+			if (i == HSIC && driver->hsic_device_enabled &&
+				 driver->logging_mode != MEMORY_DEVICE_MODE) {
+				driver->in_busy_hsic_read_on_device = 1;
+				driver->in_busy_hsic_write = 1;
+				/* Turn off communication over usb and HSIC */
+				diag_hsic_close();
+			} else if (i == SMUX && driver->diag_smux_enabled &&
+					driver->logging_mode == USB_MODE) {
+				driver->in_busy_smux = 1;
+				driver->lcid = LCID_INVALID;
+				driver->smux_connected = 0;
+				/* Turn off communication over usb and smux */
+				msm_smux_close(LCID_VALID);
+			}
+			mutex_unlock(&diag_bridge[i].bridge_mutex);
+		}
+	}
+	return 0;
+}
+
+/* Called after the asychronous usb_diag_read() on mdm channel is complete */
+int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
+{
+	 int index = (int)(diag_read_ptr->context);
+
+	/* The read of the usb on the mdm (not HSIC/SMUX) has completed */
+	diag_bridge[index].read_len = diag_read_ptr->actual;
+
+	if (index == SMUX) {
+		if (driver->diag_smux_enabled) {
+			diagfwd_read_complete_smux();
+			return 0;
+		} else {
+			pr_warning("diag: incorrect callback for smux\n");
+		}
+	}
+
+	/* If SMUX not enabled, check for HSIC */
+	driver->in_busy_hsic_read_on_device = 0;
+	if (!driver->hsic_ch) {
+		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
+		return 0;
+	}
+
+	/*
+	 * The read of the usb driver on the mdm channel has completed.
+	 * If there is no write on the HSIC in progress, check if the
+	 * read has data to pass on to the HSIC. If so, pass the usb
+	 * mdm data on to the HSIC.
+	 */
+	if (!driver->in_busy_hsic_write && diag_bridge[HSIC].usb_buf_out &&
+		(diag_bridge[HSIC].read_len > 0)) {
+
+		/*
+		 * Initiate the HSIC write. The HSIC write is
+		 * asynchronous. When complete the write
+		 * complete callback function will be called
+		 */
+		int err;
+		driver->in_busy_hsic_write = 1;
+		err = diag_bridge_write(diag_bridge[HSIC].usb_buf_out,
+					diag_bridge[HSIC].read_len);
+		if (err) {
+			pr_err_ratelimited("diag: mdm data on HSIC write err: %d\n",
+					err);
+			/*
+			 * If the error is recoverable, then clear
+			 * the write flag, so we will resubmit a
+			 * write on the next frame.  Otherwise, don't
+			 * resubmit a write on the next frame.
+			 */
+			if ((-ENODEV) != err)
+				driver->in_busy_hsic_write = 0;
+		}
+	}
+
+	/*
+	 * If there is no write of the usb mdm data on the
+	 * HSIC channel
+	 */
+	if (!driver->in_busy_hsic_write)
+		queue_work(diag_bridge[HSIC].wq,
+			 &diag_bridge[HSIC].diag_read_work);
+
+	return 0;
+}
+
+static void diagfwd_bridge_notifier(void *priv, unsigned event,
+					struct diag_request *d_req)
+{
+	int index;
+
+	switch (event) {
+	case USB_DIAG_CONNECT:
+		diagfwd_connect_bridge(1);
+		break;
+	case USB_DIAG_DISCONNECT:
+		queue_work(driver->diag_wq,
+			 &driver->diag_disconnect_work);
+		break;
+	case USB_DIAG_READ_DONE:
+		index = (int)(d_req->context);
+		queue_work(diag_bridge[index].wq,
+		&diag_bridge[index].usb_read_complete_work);
+		break;
+	case USB_DIAG_WRITE_DONE:
+		index = (int)(d_req->context);
+		if (index == HSIC &&  driver->hsic_device_enabled)
+			diagfwd_write_complete_hsic(d_req);
+		else if (index == SMUX && driver->diag_smux_enabled)
+			diagfwd_write_complete_smux();
+		break;
+	default:
+		pr_err("diag: in %s: Unknown event from USB diag:%u\n",
+			__func__, event);
+		break;
+	}
+}
+
+void diagfwd_bridge_init(int index)
+{
+	int ret;
+	unsigned char name[20];
+
+	if (index == HSIC)
+		strlcpy(name, "hsic", sizeof(name));
+	else
+		strlcpy(name, "smux", sizeof(name));
+
+	strlcpy(diag_bridge[index].name, name, sizeof(diag_bridge[index].name));
+	strlcat(name, "_diag_wq", sizeof(diag_bridge[index].name));
+	diag_bridge[index].enabled = 1;
+	diag_bridge[index].wq = create_singlethread_workqueue(name);
+	diag_bridge[index].read_len = 0;
+	diag_bridge[index].write_len = 0;
+	if (diag_bridge[index].usb_buf_out == NULL)
+		diag_bridge[index].usb_buf_out =
+				 kzalloc(USB_MAX_OUT_BUF, GFP_KERNEL);
+	if (diag_bridge[index].usb_buf_out == NULL)
+		goto err;
+	if (diag_bridge[index].usb_read_ptr == NULL)
+		diag_bridge[index].usb_read_ptr =
+			 kzalloc(sizeof(struct diag_request), GFP_KERNEL);
+	if (diag_bridge[index].usb_read_ptr == NULL)
+		goto err;
+	if (diag_bridge[index].usb_read_ptr->context == NULL)
+		diag_bridge[index].usb_read_ptr->context =
+					 kzalloc(sizeof(int), GFP_KERNEL);
+	if (diag_bridge[index].usb_read_ptr->context == NULL)
+		goto err;
+	mutex_init(&diag_bridge[index].bridge_mutex);
+
+	if (index == HSIC) {
+		INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
+				 diag_usb_read_complete_hsic_fn);
+#ifdef CONFIG_DIAG_OVER_USB
+		INIT_WORK(&(diag_bridge[index].diag_read_work),
+		      diag_read_usb_hsic_work_fn);
+		diag_bridge[index].ch = usb_diag_open(DIAG_MDM, (void *)index,
+						  diagfwd_bridge_notifier);
+		if (IS_ERR(diag_bridge[index].ch)) {
+			pr_err("diag: Unable to open USB diag MDM channel\n");
+			goto err;
+		}
+#endif
+		/* register HSIC device */
+		ret = platform_driver_register(&msm_hsic_ch_driver);
+		if (ret)
+			pr_err("diag: could not register HSIC device, ret: %d\n",
+									 ret);
+	} else if (index == SMUX) {
+		INIT_WORK(&(diag_bridge[index].usb_read_complete_work),
+					 diag_usb_read_complete_smux_fn);
+#ifdef CONFIG_DIAG_OVER_USB
+		INIT_WORK(&(diag_bridge[index].diag_read_work),
+					 diag_read_usb_smux_work_fn);
+		diag_bridge[index].ch = usb_diag_open(DIAG_QSC, (void *)index,
+					     diagfwd_bridge_notifier);
+		if (IS_ERR(diag_bridge[index].ch)) {
+			pr_err("diag: Unable to open USB diag QSC channel\n");
+			goto err;
+		}
+#endif
+		ret = platform_driver_register(&msm_diagfwd_smux_driver);
+		if (ret)
+			pr_err("diag: could not register SMUX device, ret: %d\n",
+									 ret);
+	}
+	 return;
+err:
+	pr_err("diag: Could not initialize for bridge forwarding\n");
+	kfree(diag_bridge[index].usb_buf_out);
+	kfree(driver->hsic_buf_tbl);
+	kfree(driver->write_ptr_mdm);
+	kfree(diag_bridge[index].usb_read_ptr);
+	if (diag_bridge[index].wq)
+		destroy_workqueue(diag_bridge[index].wq);
+	return;
+}
+
+void diagfwd_bridge_exit(void)
+{
+	int i;
+	pr_debug("diag: in %s\n", __func__);
+
+	if (driver->hsic_device_enabled) {
+		diag_hsic_close();
+		driver->hsic_device_enabled = 0;
+		diag_bridge[HSIC].enabled = 0;
+	}
+	driver->hsic_inited = 0;
+	diagmem_exit(driver, POOL_TYPE_ALL);
+	if (driver->diag_smux_enabled) {
+		driver->lcid = LCID_INVALID;
+		kfree(driver->buf_in_smux);
+		driver->diag_smux_enabled = 0;
+		diag_bridge[SMUX].enabled = 0;
+	}
+	platform_driver_unregister(&msm_hsic_ch_driver);
+	platform_driver_unregister(&msm_diagfwd_smux_driver);
+	/* destroy USB MDM specific variables */
+	for (i = 0; i < MAX_BRIDGES; i++) {
+		if (diag_bridge[i].enabled) {
+#ifdef CONFIG_DIAG_OVER_USB
+			if (diag_bridge[i].usb_connected)
+				usb_diag_free_req(diag_bridge[i].ch);
+			usb_diag_close(diag_bridge[i].ch);
+#endif
+			kfree(diag_bridge[i].usb_buf_out);
+			kfree(diag_bridge[i].usb_read_ptr);
+			destroy_workqueue(diag_bridge[i].wq);
+			diag_bridge[i].enabled = 0;
+		}
+	}
+	kfree(driver->hsic_buf_tbl);
+	kfree(driver->write_ptr_mdm);
+}
diff --git a/drivers/char/diag/diagfwd_bridge.h b/drivers/char/diag/diagfwd_bridge.h
new file mode 100644
index 0000000..06e6a96
--- /dev/null
+++ b/drivers/char/diag/diagfwd_bridge.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DIAGFWD_BRIDGE_H
+#define DIAGFWD_BRIDGE_H
+
+#include "diagfwd.h"
+
+#define MAX_BRIDGES	5
+#define HSIC	0
+#define SMUX	1
+
+int diagfwd_connect_bridge(int);
+void connect_bridge(int, int);
+int diagfwd_disconnect_bridge(int);
+void diagfwd_bridge_init(int index);
+void diagfwd_bridge_exit(void);
+int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr);
+
+/* Diag-Bridge structure, n bridges can be used at same time
+ * for instance SMUX, HSIC working at same time
+ */
+struct diag_bridge_dev {
+	char name[20];
+	int enabled;
+	struct mutex bridge_mutex;
+	int usb_connected;
+	int read_len;
+	int write_len;
+	unsigned char *usb_buf_out;
+	struct usb_diag_ch *ch;
+	struct workqueue_struct *wq;
+	struct work_struct diag_read_work;
+	struct diag_request *usb_read_ptr;
+	struct work_struct usb_read_complete_work;
+};
+
+#endif
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 9e5a8d0..acac2fb 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -21,251 +21,151 @@
 static uint16_t reg_dirty;
 #define HDR_SIZ 8
 
-void diag_clean_modem_reg_fn(struct work_struct *work)
+void diag_clean_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(LPASS_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;
-
-	if (!(driver->ch_cntl))
+	struct diag_smd_info *smd_info = container_of(work,
+						struct diag_smd_info,
+						diag_notify_update_smd_work);
+	if (!smd_info)
 		return;
 
-	switch (event) {
-	case SMD_EVENT_DATA:
-		r1 = smd_read_avail(driver->ch_cntl);
-		r2 = smd_cur_packet_size(driver->ch_cntl);
-		if (r1 > 0 && r1 == r2)
-			queue_work(driver->diag_wq,
-				 &(driver->diag_read_smd_cntl_work));
-		else
-			pr_debug("diag: incomplete pkt on Modem CNTL ch\n");
-		break;
-	case SMD_EVENT_OPEN:
-		queue_work(driver->diag_cntl_wq,
-			 &(driver->diag_modem_mask_update_work));
-		break;
-	}
+	pr_debug("diag: clean registration for peripheral: %d\n",
+		smd_info->peripheral);
+
+	reg_dirty |= smd_info->peripheral_mask;
+	diag_clear_reg(smd_info->peripheral);
+	reg_dirty ^= smd_info->peripheral_mask;
+
+	smd_info->notify_context = 0;
 }
 
-void diag_smd_lpass_cntl_notify(void *ctxt, unsigned event)
+/* Process the data read from the smd control channel */
+int diag_process_smd_cntl_read_data(struct diag_smd_info *smd_info, void *buf,
+								int total_recd)
 {
-	int r1, r2;
-
-	if (!(driver->chlpass_cntl))
-		return;
-
-	switch (event) {
-	case SMD_EVENT_DATA:
-		r1 = smd_read_avail(driver->chlpass_cntl);
-		r2 = smd_cur_packet_size(driver->chlpass_cntl);
-		if (r1 > 0 && r1 == r2)
-			queue_work(driver->diag_wq,
-				 &(driver->diag_read_smd_lpass_cntl_work));
-		else
-			pr_debug("diag: incomplete pkt on LPASS CNTL ch\n");
-		break;
-	case SMD_EVENT_OPEN:
-		queue_work(driver->diag_cntl_wq,
-			 &(driver->diag_lpass_mask_update_work));
-		break;
-	}
-}
-
-void diag_smd_wcnss_cntl_notify(void *ctxt, unsigned event)
-{
-	int r1, r2;
-
-	if (!(driver->ch_wcnss_cntl))
-		return;
-
-	switch (event) {
-	case SMD_EVENT_DATA:
-		r1 = smd_read_avail(driver->ch_wcnss_cntl);
-		r2 = smd_cur_packet_size(driver->ch_wcnss_cntl);
-		if (r1 > 0 && r1 == r2)
-			queue_work(driver->diag_wq,
-				 &(driver->diag_read_smd_wcnss_cntl_work));
-		else
-			pr_debug("diag: incomplete pkt on WCNSS CNTL ch\n");
-		break;
-	case SMD_EVENT_OPEN:
-		queue_work(driver->diag_cntl_wq,
-			 &(driver->diag_wcnss_mask_update_work));
-		break;
-	}
-}
-
-static void diag_smd_cntl_send_req(int proc_num)
-{
-	int data_len = 0, type = -1, count_bytes = 0, j, r, flag = 0;
+	int data_len = 0, type = -1, count_bytes = 0, j, flag = 0;
+	int feature_mask_len;
 	struct bindpkt_params_per_process *pkt_params =
-		 kzalloc(sizeof(struct bindpkt_params_per_process), GFP_KERNEL);
+		kzalloc(sizeof(struct bindpkt_params_per_process), GFP_KERNEL);
 	struct diag_ctrl_msg *msg;
 	struct cmd_code_range *range;
 	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");
-		return;
+		pr_alert("diag: In %s, Memory allocation failure\n",
+			__func__);
+		return 0;
 	}
 
-	if (proc_num == MODEM_PROC) {
-		buf = driver->buf_in_cntl;
-		smd_ch = driver->ch_cntl;
-		reg_mask = DIAG_CON_MPSS;
-	} else if (proc_num == LPASS_PROC) {
-		buf = driver->buf_in_lpass_cntl;
-		smd_ch = driver->chlpass_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) {
+	if (!smd_info) {
+		pr_err("diag: In %s, No smd info. Not able to read.\n",
+			__func__);
 		kfree(pkt_params);
-		return;
+		return 0;
 	}
 
-	r = smd_read_avail(smd_ch);
-	if (r > IN_BUF_SIZE) {
-		if (r < MAX_IN_BUF_SIZE) {
-			pr_err("diag: SMD CNTL sending pkt upto %d bytes", r);
-			buf = krealloc(buf, r, GFP_KERNEL);
-		} else {
-			pr_err("diag: CNTL pkt > %d bytes", MAX_IN_BUF_SIZE);
-			kfree(pkt_params);
-			return;
+	while (count_bytes + HDR_SIZ <= total_recd) {
+		type = *(uint32_t *)(buf);
+		data_len = *(uint32_t *)(buf + 4);
+		if (type < DIAG_CTRL_MSG_REG ||
+				 type > DIAG_CTRL_MSG_F3_MASK_V2) {
+			pr_alert("diag: In %s, Invalid Msg type %d proc %d",
+				 __func__, type, smd_info->peripheral);
+			break;
 		}
-	}
-	if (buf && r > 0) {
-		smd_read(smd_ch, buf, r);
-		while (count_bytes + HDR_SIZ <= r) {
-			type = *(uint32_t *)(buf);
-			data_len = *(uint32_t *)(buf + 4);
-			if (type < DIAG_CTRL_MSG_REG ||
-					 type > DIAG_CTRL_MSG_F3_MASK_V2) {
-				pr_alert("diag: Invalid Msg type %d proc %d",
-					 type, proc_num);
-				break;
-			}
-			if (data_len < 0 || data_len > r) {
-				pr_alert("diag: Invalid data len %d proc %d",
-					 data_len, proc_num);
-				break;
-			}
-			count_bytes = count_bytes+HDR_SIZ+data_len;
-			if (type == DIAG_CTRL_MSG_REG && r >= count_bytes) {
-				msg = buf+HDR_SIZ;
-				range = buf+HDR_SIZ+
-						sizeof(struct diag_ctrl_msg);
-				pkt_params->count = msg->count_entries;
-				temp = kzalloc(pkt_params->count * sizeof(struct
-						 bindpkt_params), GFP_KERNEL);
-				if (temp == NULL) {
-					pr_alert("diag: Memory alloc fail\n");
-					kfree(pkt_params);
-					return;
-				}
-				for (j = 0; j < pkt_params->count; j++) {
-					temp->cmd_code = msg->cmd_code;
-					temp->subsys_id = msg->subsysid;
-					temp->client_id = proc_num;
-					temp->proc_id = proc_num;
-					temp->cmd_code_lo = range->cmd_code_lo;
-					temp->cmd_code_hi = range->cmd_code_hi;
-					range++;
-					temp++;
-				}
-				temp -= pkt_params->count;
-				pkt_params->params = temp;
-				flag = 1;
-				/* 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;
+		if (data_len < 0 || data_len > total_recd) {
+			pr_alert("diag: In %s, Invalid data len %d, total_recd: %d, proc %d",
+				 __func__, data_len, total_recd,
+				 smd_info->peripheral);
+			break;
 		}
+		count_bytes = count_bytes+HDR_SIZ+data_len;
+		if (type == DIAG_CTRL_MSG_REG && total_recd >= count_bytes) {
+			msg = buf+HDR_SIZ;
+			range = buf+HDR_SIZ+
+					sizeof(struct diag_ctrl_msg);
+			pkt_params->count = msg->count_entries;
+			temp = kzalloc(pkt_params->count * sizeof(struct
+					 bindpkt_params), GFP_KERNEL);
+			if (temp == NULL) {
+				pr_alert("diag: In %s, Memory alloc fail\n",
+					__func__);
+				kfree(pkt_params);
+				return flag;
+			}
+			for (j = 0; j < pkt_params->count; j++) {
+				temp->cmd_code = msg->cmd_code;
+				temp->subsys_id = msg->subsysid;
+				temp->client_id = smd_info->peripheral;
+				temp->proc_id = NON_APPS_PROC;
+				temp->cmd_code_lo = range->cmd_code_lo;
+				temp->cmd_code_hi = range->cmd_code_hi;
+				range++;
+				temp++;
+			}
+			temp -= pkt_params->count;
+			pkt_params->params = temp;
+			flag = 1;
+			/* peripheral undergoing SSR should not
+			 * record new registration
+			 */
+			if (!(reg_dirty & smd_info->peripheral_mask))
+				diagchar_ioctl(NULL, DIAG_IOCTL_COMMAND_REG,
+						(unsigned long)pkt_params);
+			else
+				pr_err("diag: drop reg proc %d\n",
+						smd_info->peripheral);
+			kfree(temp);
+		} else if ((type == DIAG_CTRL_MSG_FEATURE) &&
+				(smd_info->peripheral == MODEM_DATA)) {
+			feature_mask_len = *(int *)(buf + 8);
+			driver->log_on_demand_support = (*(uint8_t *)
+							 (buf + 12)) & 0x04;
+		} else if (type != DIAG_CTRL_MSG_REG) {
+			flag = 1;
+		}
+		buf = buf + HDR_SIZ + data_len;
 	}
 	kfree(pkt_params);
-	if (flag) {
-		/* Poll SMD CNTL channels to check for data */
-		if (proc_num == MODEM_PROC)
-			diag_smd_cntl_notify(NULL, SMD_EVENT_DATA);
-		else if (proc_num == LPASS_PROC)
-			diag_smd_lpass_cntl_notify(NULL, SMD_EVENT_DATA);
-		else if (proc_num == WCNSS_PROC)
-			diag_smd_wcnss_cntl_notify(NULL, SMD_EVENT_DATA);
-	}
-}
 
-void diag_read_smd_cntl_work_fn(struct work_struct *work)
-{
-	diag_smd_cntl_send_req(MODEM_PROC);
-}
-
-void diag_read_smd_lpass_cntl_work_fn(struct work_struct *work)
-{
-	diag_smd_cntl_send_req(LPASS_PROC);
-}
-
-void diag_read_smd_wcnss_cntl_work_fn(struct work_struct *work)
-{
-	diag_smd_cntl_send_req(WCNSS_PROC);
+	return flag;
 }
 
 static int diag_smd_cntl_probe(struct platform_device *pdev)
 {
 	int r = 0;
+	int index = -1;
 
 	/* open control ports only on 8960 & newer targets */
 	if (chk_apps_only()) {
-		if (pdev->id == SMD_APPS_MODEM)
-			r = smd_open("DIAG_CNTL", &driver->ch_cntl, driver,
-							diag_smd_cntl_notify);
-		if (pdev->id == SMD_APPS_QDSP)
-			r = smd_named_open_on_edge("DIAG_CNTL", SMD_APPS_QDSP
-					, &driver->chlpass_cntl, driver,
-					diag_smd_lpass_cntl_notify);
-		if (pdev->id == SMD_APPS_WCNSS)
+		if (pdev->id == SMD_APPS_MODEM) {
+			index = MODEM_DATA;
+			r = smd_open("DIAG_CNTL",
+					&driver->smd_cntl[index].ch,
+					&driver->smd_cntl[index],
+					diag_smd_notify);
+			driver->smd_cntl[index].ch_save =
+					driver->smd_cntl[index].ch;
+		} else if (pdev->id == SMD_APPS_QDSP) {
+			index = LPASS_DATA;
+			r = smd_named_open_on_edge("DIAG_CNTL",
+					SMD_APPS_QDSP,
+					&driver->smd_cntl[index].ch,
+					&driver->smd_cntl[index],
+					diag_smd_notify);
+			driver->smd_cntl[index].ch_save =
+					driver->smd_cntl[index].ch;
+		} else if (pdev->id == SMD_APPS_WCNSS) {
+			index = WCNSS_DATA;
 			r = smd_named_open_on_edge("APPS_RIVA_CTRL",
-				SMD_APPS_WCNSS, &driver->ch_wcnss_cntl,
-					driver, diag_smd_wcnss_cntl_notify);
+					SMD_APPS_WCNSS,
+					&driver->smd_cntl[index].ch,
+					&driver->smd_cntl[index],
+					diag_smd_notify);
+			driver->smd_cntl[index].ch_save =
+					driver->smd_cntl[index].ch;
+		}
+
 		pr_debug("diag: open CNTL port, ID = %d,r = %d\n", pdev->id, r);
 	}
 	return 0;
@@ -310,53 +210,52 @@
 
 void diagfwd_cntl_init(void)
 {
+	int success;
+	int i;
+
 	reg_dirty = 0;
 	driver->polling_reg_flag = 0;
+	driver->log_on_demand_support = 1;
 	driver->diag_cntl_wq = create_singlethread_workqueue("diag_cntl_wq");
-	if (driver->buf_in_cntl == NULL) {
-		driver->buf_in_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_cntl == NULL)
-			goto err;
-		kmemleak_not_leak(driver->buf_in_cntl);
-	}
-	if (driver->buf_in_lpass_cntl == NULL) {
-		driver->buf_in_lpass_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_lpass_cntl == NULL)
-			goto err;
-		kmemleak_not_leak(driver->buf_in_lpass_cntl);
-	}
-	if (driver->buf_in_wcnss_cntl == NULL) {
-		driver->buf_in_wcnss_cntl = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-		if (driver->buf_in_wcnss_cntl == NULL)
-			goto err;
-		kmemleak_not_leak(driver->buf_in_wcnss_cntl);
-	}
+
+	success = diag_smd_constructor(&driver->smd_cntl[MODEM_DATA],
+					MODEM_DATA, SMD_CNTL_TYPE);
+	if (!success)
+		goto err;
+
+	success = diag_smd_constructor(&driver->smd_cntl[LPASS_DATA],
+					LPASS_DATA, SMD_CNTL_TYPE);
+	if (!success)
+		goto err;
+
+	success = diag_smd_constructor(&driver->smd_cntl[WCNSS_DATA],
+					WCNSS_DATA, SMD_CNTL_TYPE);
+	if (!success)
+		goto err;
+
 	platform_driver_register(&msm_smd_ch1_cntl_driver);
 	platform_driver_register(&diag_smd_lite_cntl_driver);
 
 	return;
 err:
-		pr_err("diag: Could not initialize diag buffers");
-		kfree(driver->buf_in_cntl);
-		kfree(driver->buf_in_lpass_cntl);
-		kfree(driver->buf_in_wcnss_cntl);
-		if (driver->diag_cntl_wq)
-			destroy_workqueue(driver->diag_cntl_wq);
+	pr_err("diag: Could not initialize diag buffers");
+
+	for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
+		diag_smd_destructor(&driver->smd_cntl[i]);
+
+	if (driver->diag_cntl_wq)
+		destroy_workqueue(driver->diag_cntl_wq);
 }
 
 void diagfwd_cntl_exit(void)
 {
-	smd_close(driver->ch_cntl);
-	smd_close(driver->chlpass_cntl);
-	smd_close(driver->ch_wcnss_cntl);
-	driver->ch_cntl = 0;
-	driver->chlpass_cntl = 0;
-	driver->ch_wcnss_cntl = 0;
+	int i;
+
+	for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++)
+		diag_smd_destructor(&driver->smd_cntl[i]);
+
 	destroy_workqueue(driver->diag_cntl_wq);
+
 	platform_driver_unregister(&msm_smd_ch1_cntl_driver);
 	platform_driver_unregister(&diag_smd_lite_cntl_driver);
-
-	kfree(driver->buf_in_cntl);
-	kfree(driver->buf_in_lpass_cntl);
-	kfree(driver->buf_in_wcnss_cntl);
 }
diff --git a/drivers/char/diag/diagfwd_cntl.h b/drivers/char/diag/diagfwd_cntl.h
index 8a0ec3f..8d262c4 100644
--- a/drivers/char/diag/diagfwd_cntl.h
+++ b/drivers/char/diag/diagfwd_cntl.h
@@ -77,16 +77,18 @@
 	/* Copy msg mask here */
 } __packed;
 
+struct diag_ctrl_feature_mask {
+	uint32_t ctrl_pkt_id;
+	uint32_t ctrl_pkt_data_len;
+	uint32_t feature_mask_len;
+	/* Copy feature mask here */
+} __packed;
+
 void diagfwd_cntl_init(void);
 void diagfwd_cntl_exit(void);
 void diag_read_smd_cntl_work_fn(struct work_struct *);
-void diag_read_smd_lpass_cntl_work_fn(struct work_struct *);
-void diag_read_smd_wcnss_cntl_work_fn(struct work_struct *);
-void diag_smd_cntl_notify(void *ctxt, unsigned event);
-void diag_smd_lpass_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_clean_reg_fn(struct work_struct *work);
+int diag_process_smd_cntl_read_data(struct diag_smd_info *smd_info, void *buf,
+								int total_recd);
 
 #endif
diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c
index 56f2fae..3d5eea5 100644
--- a/drivers/char/diag/diagfwd_hsic.c
+++ b/drivers/char/diag/diagfwd_hsic.c
@@ -31,6 +31,7 @@
 #include "diagfwd.h"
 #include "diagfwd_hsic.h"
 #include "diagfwd_smux.h"
+#include "diagfwd_bridge.h"
 
 #define READ_HSIC_BUF_SIZE 2048
 
@@ -72,7 +73,7 @@
 		write_ptrs_available--;
 
 		/*
-		 * No sense queuing a read if the hsic bridge was
+		 * No sense queuing a read if the HSIC bridge was
 		 * closed in another thread
 		 */
 		if (!driver->hsic_ch)
@@ -82,7 +83,7 @@
 							POOL_TYPE_HSIC);
 		if (buf_in_hsic) {
 			/*
-			 * Initiate the read from the hsic.  The hsic read is
+			 * Initiate the read from the HSIC.  The HSIC read is
 			 * asynchronous.  Once the read is complete the read
 			 * callback function will be called.
 			 */
@@ -116,7 +117,7 @@
 	if ((driver->count_hsic_pool < driver->poolsize_hsic) &&
 		(num_reads_submitted == 0) && (err != -ENODEV) &&
 		(driver->hsic_ch != 0))
-		queue_work(driver->diag_bridge_wq,
+		queue_work(diag_bridge[HSIC].wq,
 				 &driver->diag_read_hsic_work);
 }
 
@@ -127,7 +128,7 @@
 
 	if (!driver->hsic_ch) {
 		/*
-		 * The hsic channel is closed. Return the buffer to
+		 * The HSIC channel is closed. Return the buffer to
 		 * the pool.  Do not send it on.
 		 */
 		diagmem_free(driver, buf, POOL_TYPE_HSIC);
@@ -149,7 +150,7 @@
 			 * Send data in buf to be written on the
 			 * appropriate device, e.g. USB MDM channel
 			 */
-			driver->write_len_mdm = actual_size;
+			diag_bridge[HSIC].write_len = actual_size;
 			err = diag_device_write((void *)buf, HSIC_DATA, NULL);
 			/* If an error, return buffer to the pool */
 			if (err) {
@@ -170,13 +171,13 @@
 	}
 
 	/*
-	 * If for some reason there was no hsic data to write to the
+	 * If for some reason there was no HSIC data to write to the
 	 * mdm channel, set up another read
 	 */
 	if (err &&
 		((driver->logging_mode == MEMORY_DEVICE_MODE) ||
-		(driver->usb_mdm_connected && !driver->hsic_suspend))) {
-		queue_work(driver->diag_bridge_wq,
+		(diag_bridge[HSIC].usb_connected && !driver->hsic_suspend))) {
+		queue_work(diag_bridge[HSIC].wq,
 				 &driver->diag_read_hsic_work);
 	}
 }
@@ -195,8 +196,10 @@
 	if (actual_size < 0)
 		pr_err("DIAG in %s: actual_size: %d\n", __func__, actual_size);
 
-	if (driver->usb_mdm_connected)
-		queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
+	if (diag_bridge[HSIC].usb_connected &&
+				 (driver->logging_mode == USB_MODE))
+		queue_work(diag_bridge[HSIC].wq,
+				 &diag_bridge[HSIC].diag_read_work);
 }
 
 static int diag_hsic_suspend(void *ctxt)
@@ -223,12 +226,12 @@
 
 	if ((driver->count_hsic_pool < driver->poolsize_hsic) &&
 		((driver->logging_mode == MEMORY_DEVICE_MODE) ||
-				(driver->usb_mdm_connected)))
-		queue_work(driver->diag_bridge_wq,
+				(diag_bridge[HSIC].usb_connected)))
+		queue_work(diag_bridge[HSIC].wq,
 			 &driver->diag_read_hsic_work);
 }
 
-static struct diag_bridge_ops hsic_diag_bridge_ops = {
+struct diag_bridge_ops hsic_diag_bridge_ops = {
 	.ctxt = NULL,
 	.read_complete_cb = diag_hsic_read_complete_callback,
 	.write_complete_cb = diag_hsic_write_complete_callback,
@@ -236,7 +239,7 @@
 	.resume = diag_hsic_resume,
 };
 
-static void diag_hsic_close(void)
+void diag_hsic_close(void)
 {
 	if (driver->hsic_device_enabled) {
 		driver->hsic_ch = 0;
@@ -257,7 +260,7 @@
 {
 	int err;
 
-	mutex_lock(&driver->bridge_mutex);
+	mutex_lock(&diag_bridge[HSIC].bridge_mutex);
 	if (driver->hsic_device_enabled) {
 		if (driver->hsic_device_opened) {
 			driver->hsic_ch = 0;
@@ -274,112 +277,7 @@
 			}
 		}
 	}
-
-	mutex_unlock(&driver->bridge_mutex);
-	return 0;
-}
-
-/* diagfwd_connect_bridge is called when the USB mdm channel is connected */
-int diagfwd_connect_bridge(int process_cable)
-{
-	int err;
-
-	pr_debug("diag: in %s\n", __func__);
-
-	mutex_lock(&driver->bridge_mutex);
-	/* If the usb cable is being connected */
-	if (process_cable) {
-		err = usb_diag_alloc_req(driver->mdm_ch, N_MDM_WRITE,
-			N_MDM_READ);
-		if (err)
-			pr_err("diag: unable to alloc USB req on mdm"
-				" ch err:%d\n", err);
-
-		driver->usb_mdm_connected = 1;
-	}
-
-	if (driver->hsic_device_enabled) {
-		driver->in_busy_hsic_read_on_device = 0;
-		driver->in_busy_hsic_write = 0;
-	} else if (driver->diag_smux_enabled) {
-		driver->in_busy_smux = 0;
-		diagfwd_connect_smux();
-		mutex_unlock(&driver->bridge_mutex);
-		return 0;
-	}
-
-	/* If the hsic (diag_bridge) platform device is not open */
-	if (driver->hsic_device_enabled) {
-		if (!driver->hsic_device_opened) {
-			err = diag_bridge_open(&hsic_diag_bridge_ops);
-			if (err) {
-				pr_err("diag: HSIC channel open error: %d\n",
-					err);
-			} else {
-				pr_debug("diag: opened HSIC channel\n");
-				driver->hsic_device_opened = 1;
-			}
-		} else {
-			pr_debug("diag: HSIC channel already open\n");
-		}
-
-		/*
-		 * Turn on communication over usb mdm and hsic, if the hsic
-		 * device driver is enabled and opened
-		 */
-		if (driver->hsic_device_opened) {
-			driver->hsic_ch = 1;
-
-			/* Poll USB mdm channel to check for data */
-			if (driver->logging_mode == USB_MODE)
-				queue_work(driver->diag_bridge_wq,
-						&driver->diag_read_mdm_work);
-
-			/* Poll HSIC channel to check for data */
-			queue_work(driver->diag_bridge_wq,
-					 &driver->diag_read_hsic_work);
-		}
-	} else {
-		/* The hsic device driver has not yet been enabled */
-		pr_info("diag: HSIC channel not yet enabled\n");
-	}
-
-	mutex_unlock(&driver->bridge_mutex);
-	return 0;
-}
-
-/*
- * diagfwd_disconnect_bridge is called when the USB mdm channel
- * is disconnected
- */
-int diagfwd_disconnect_bridge(int process_cable)
-{
-	pr_debug("diag: In %s, process_cable: %d\n", __func__, process_cable);
-
-	mutex_lock(&driver->bridge_mutex);
-
-	/* If the usb cable is being disconnected */
-	if (process_cable) {
-		driver->usb_mdm_connected = 0;
-		usb_diag_free_req(driver->mdm_ch);
-	}
-
-	if (driver->hsic_device_enabled &&
-		driver->logging_mode != MEMORY_DEVICE_MODE) {
-		driver->in_busy_hsic_read_on_device = 1;
-		driver->in_busy_hsic_write = 1;
-		/* Turn off communication over usb mdm and hsic */
-		diag_hsic_close();
-	} else if (driver->diag_smux_enabled &&
-		driver->logging_mode == USB_MODE) {
-		driver->in_busy_smux = 1;
-		driver->lcid = LCID_INVALID;
-		driver->smux_connected = 0;
-		/* Turn off communication over usb mdm and smux */
-		msm_smux_close(LCID_VALID);
-	}
-
-	mutex_unlock(&driver->bridge_mutex);
+	mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
 	return 0;
 }
 
@@ -403,225 +301,128 @@
 		return 0;
 	}
 
-	/* Read data from the hsic */
-	queue_work(driver->diag_bridge_wq, &driver->diag_read_hsic_work);
+	/* Read data from the HSIC */
+	queue_work(diag_bridge[HSIC].wq, &driver->diag_read_hsic_work);
 
 	return 0;
 }
 
-/* Called after the asychronous usb_diag_read() on mdm channel is complete */
-static int diagfwd_read_complete_bridge(struct diag_request *diag_read_ptr)
+void diag_usb_read_complete_hsic_fn(struct work_struct *w)
 {
-	/* The read of the usb driver on the mdm (not hsic) has completed */
-	driver->in_busy_hsic_read_on_device = 0;
-	driver->read_len_mdm = diag_read_ptr->actual;
+	diagfwd_read_complete_bridge(diag_bridge[HSIC].usb_read_ptr);
+}
 
-	if (driver->diag_smux_enabled) {
-		diagfwd_read_complete_smux();
-		return 0;
-	}
-	/* If SMUX not enabled, check for HSIC */
+
+void diag_read_usb_hsic_work_fn(struct work_struct *work)
+{
 	if (!driver->hsic_ch) {
-		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
-		return 0;
-	}
-
-	/*
-	 * The read of the usb driver on the mdm channel has completed.
-	 * If there is no write on the hsic in progress, check if the
-	 * read has data to pass on to the hsic. If so, pass the usb
-	 * mdm data on to the hsic.
-	 */
-	if (!driver->in_busy_hsic_write && driver->usb_buf_mdm_out &&
-		(driver->read_len_mdm > 0)) {
-
-		/*
-		 * Initiate the hsic write. The hsic write is
-		 * asynchronous. When complete the write
-		 * complete callback function will be called
-		 */
-		int err;
-		driver->in_busy_hsic_write = 1;
-		err = diag_bridge_write(driver->usb_buf_mdm_out,
-					driver->read_len_mdm);
-		if (err) {
-			pr_err_ratelimited("diag: mdm data on hsic write err: %d\n",
-					err);
-			/*
-			 * If the error is recoverable, then clear
-			 * the write flag, so we will resubmit a
-			 * write on the next frame.  Otherwise, don't
-			 * resubmit a write on the next frame.
-			 */
-			if ((-ENODEV) != err)
-				driver->in_busy_hsic_write = 0;
-		}
-	}
-
-	/*
-	 * If there is no write of the usb mdm data on the
-	 * hsic channel
-	 */
-	if (!driver->in_busy_hsic_write)
-		queue_work(driver->diag_bridge_wq, &driver->diag_read_mdm_work);
-
-	return 0;
-}
-
-static void diagfwd_bridge_notifier(void *priv, unsigned event,
-					struct diag_request *d_req)
-{
-	switch (event) {
-	case USB_DIAG_CONNECT:
-		diagfwd_connect_bridge(1);
-		break;
-	case USB_DIAG_DISCONNECT:
-		queue_work(driver->diag_bridge_wq,
-			 &driver->diag_disconnect_work);
-		break;
-	case USB_DIAG_READ_DONE:
-		queue_work(driver->diag_bridge_wq,
-				&driver->diag_usb_read_complete_work);
-		break;
-	case USB_DIAG_WRITE_DONE:
-		if (driver->hsic_device_enabled)
-			diagfwd_write_complete_hsic(d_req);
-		else if (driver->diag_smux_enabled)
-			diagfwd_write_complete_smux();
-		break;
-	default:
-		pr_err("diag: in %s: Unknown event from USB diag:%u\n",
-			__func__, event);
-		break;
-	}
-}
-
-static void diag_usb_read_complete_fn(struct work_struct *w)
-{
-	diagfwd_read_complete_bridge(driver->usb_read_mdm_ptr);
-}
-
-static void diag_disconnect_work_fn(struct work_struct *w)
-{
-	diagfwd_disconnect_bridge(1);
-}
-
-static void diag_read_mdm_work_fn(struct work_struct *work)
-{
-	int ret;
-	if (driver->diag_smux_enabled) {
-		if (driver->lcid && driver->usb_buf_mdm_out &&
-				(driver->read_len_mdm > 0) &&
-				driver->smux_connected) {
-			ret = msm_smux_write(driver->lcid,  NULL,
-		 driver->usb_buf_mdm_out, driver->read_len_mdm);
-			if (ret)
-				pr_err("diag: writing to SMUX ch, r = %d,"
-					"lcid = %d\n", ret, driver->lcid);
-		}
-		driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
-		driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
-		usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+		pr_err("diag: in %s: driver->hsic_ch == 0\n", __func__);
 		return;
 	}
-
-	/* if SMUX not enabled, check for HSIC */
-	if (!driver->hsic_ch) {
-		pr_err("DIAG in %s: driver->hsic_ch == 0\n", __func__);
-		return;
-	}
-
 	/*
 	 * If there is no data being read from the usb mdm channel
 	 * and there is no mdm channel data currently being written
-	 * to the hsic
+	 * to the HSIC
 	 */
 	if (!driver->in_busy_hsic_read_on_device &&
-				 !driver->in_busy_hsic_write) {
+	     !driver->in_busy_hsic_write) {
 		APPEND_DEBUG('x');
-
 		/* Setup the next read from usb mdm channel */
 		driver->in_busy_hsic_read_on_device = 1;
-		driver->usb_read_mdm_ptr->buf = driver->usb_buf_mdm_out;
-		driver->usb_read_mdm_ptr->length = USB_MAX_OUT_BUF;
-		usb_diag_read(driver->mdm_ch, driver->usb_read_mdm_ptr);
+		diag_bridge[HSIC].usb_read_ptr->buf =
+				 diag_bridge[HSIC].usb_buf_out;
+		diag_bridge[HSIC].usb_read_ptr->length = USB_MAX_OUT_BUF;
+		diag_bridge[HSIC].usb_read_ptr->context = (void *)HSIC;
+		usb_diag_read(diag_bridge[HSIC].ch,
+				 diag_bridge[HSIC].usb_read_ptr);
 		APPEND_DEBUG('y');
 	}
-
-	/*
-	 * If for some reason there was no mdm channel read initiated,
+	/* If for some reason there was no mdm channel read initiated,
 	 * queue up the reading of data from the mdm channel
 	 */
-	if (!driver->in_busy_hsic_read_on_device)
-		queue_work(driver->diag_bridge_wq,
-			 &driver->diag_read_mdm_work);
+
+	if (!driver->in_busy_hsic_read_on_device &&
+		(driver->logging_mode == USB_MODE))
+		queue_work(diag_bridge[HSIC].wq,
+			 &(diag_bridge[HSIC].diag_read_work));
 }
 
 static int diag_hsic_probe(struct platform_device *pdev)
 {
 	int err = 0;
+
 	pr_debug("diag: in %s\n", __func__);
+	mutex_lock(&diag_bridge[HSIC].bridge_mutex);
 	if (!driver->hsic_inited) {
+		spin_lock_init(&driver->hsic_spinlock);
+		driver->num_hsic_buf_tbl_entries = 0;
+		if (driver->hsic_buf_tbl == NULL)
+			driver->hsic_buf_tbl = kzalloc(NUM_HSIC_BUF_TBL_ENTRIES
+				* sizeof(struct diag_write_device), GFP_KERNEL);
+		if (driver->hsic_buf_tbl == NULL) {
+			mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
+			return -ENOMEM;
+		}
+		driver->count_hsic_pool = 0;
+		driver->count_hsic_write_pool = 0;
+		driver->itemsize_hsic = READ_HSIC_BUF_SIZE;
+		driver->poolsize_hsic = N_MDM_WRITE;
+		driver->itemsize_hsic_write = sizeof(struct diag_request);
+		driver->poolsize_hsic_write = N_MDM_WRITE;
 		diagmem_hsic_init(driver);
 		INIT_WORK(&(driver->diag_read_hsic_work),
-					 diag_read_hsic_work_fn);
+			    diag_read_hsic_work_fn);
 		driver->hsic_inited = 1;
 	}
-
-	mutex_lock(&driver->bridge_mutex);
-
 	/*
 	 * The probe function was called after the usb was connected
 	 * on the legacy channel OR ODL is turned on. Communication over usb
-	 * mdm and hsic needs to be turned on.
+	 * mdm and HSIC needs to be turned on.
 	 */
-	if (driver->usb_mdm_connected || (driver->logging_mode ==
-							 MEMORY_DEVICE_MODE)) {
+	if (diag_bridge[HSIC].usb_connected || (driver->logging_mode ==
+						   MEMORY_DEVICE_MODE)) {
 		if (driver->hsic_device_opened) {
 			/* should not happen. close it before re-opening */
 			pr_warn("diag: HSIC channel already opened in probe\n");
 			diag_bridge_close();
 		}
-
 		err = diag_bridge_open(&hsic_diag_bridge_ops);
 		if (err) {
 			pr_err("diag: could not open HSIC, err: %d\n", err);
 			driver->hsic_device_opened = 0;
-			mutex_unlock(&driver->bridge_mutex);
+			mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
 			return err;
 		}
 
 		pr_info("diag: opened HSIC channel\n");
 		driver->hsic_device_opened = 1;
 		driver->hsic_ch = 1;
-
 		driver->in_busy_hsic_read_on_device = 0;
 		driver->in_busy_hsic_write = 0;
 
-		if (driver->usb_mdm_connected) {
+		if (diag_bridge[HSIC].usb_connected) {
 			/* Poll USB mdm channel to check for data */
-			queue_work(driver->diag_bridge_wq,
-					 &driver->diag_read_mdm_work);
+			queue_work(diag_bridge[HSIC].wq,
+			     &diag_bridge[HSIC].diag_read_work);
 		}
-
 		/* Poll HSIC channel to check for data */
-		queue_work(driver->diag_bridge_wq,
-				 &driver->diag_read_hsic_work);
+		queue_work(diag_bridge[HSIC].wq,
+			  &driver->diag_read_hsic_work);
 	}
-
-	/* The hsic (diag_bridge) platform device driver is enabled */
+	/* The HSIC (diag_bridge) platform device driver is enabled */
 	driver->hsic_device_enabled = 1;
-	mutex_unlock(&driver->bridge_mutex);
+	mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
 	return err;
 }
 
 static int diag_hsic_remove(struct platform_device *pdev)
 {
 	pr_debug("diag: %s called\n", __func__);
-	mutex_lock(&driver->bridge_mutex);
+	mutex_lock(&diag_bridge[HSIC].bridge_mutex);
 	diag_hsic_close();
 	driver->hsic_device_enabled = 0;
-	mutex_unlock(&driver->bridge_mutex);
+	mutex_unlock(&diag_bridge[HSIC].bridge_mutex);
+
 	return 0;
 }
 
@@ -642,7 +443,7 @@
 	.runtime_resume = diagfwd_hsic_runtime_resume,
 };
 
-static struct platform_driver msm_hsic_ch_driver = {
+struct platform_driver msm_hsic_ch_driver = {
 	.probe = diag_hsic_probe,
 	.remove = diag_hsic_remove,
 	.driver = {
@@ -651,112 +452,3 @@
 		   .pm   = &diagfwd_hsic_dev_pm_ops,
 		   },
 };
-
-void diagfwd_bridge_init(void)
-{
-	int ret;
-
-	pr_debug("diag: in %s\n", __func__);
-	driver->diag_bridge_wq = create_singlethread_workqueue(
-							"diag_bridge_wq");
-	driver->read_len_mdm = 0;
-	driver->write_len_mdm = 0;
-	driver->num_hsic_buf_tbl_entries = 0;
-	spin_lock_init(&driver->hsic_spinlock);
-	if (driver->usb_buf_mdm_out  == NULL)
-		driver->usb_buf_mdm_out = kzalloc(USB_MAX_OUT_BUF,
-							 GFP_KERNEL);
-	if (driver->usb_buf_mdm_out == NULL)
-		goto err;
-	/* Only used by smux move to smux probe function */
-	if (driver->write_ptr_mdm == NULL)
-		driver->write_ptr_mdm = kzalloc(
-		sizeof(struct diag_request), GFP_KERNEL);
-	if (driver->write_ptr_mdm == NULL)
-		goto err;
-	if (driver->usb_read_mdm_ptr == NULL)
-		driver->usb_read_mdm_ptr = kzalloc(
-		sizeof(struct diag_request), GFP_KERNEL);
-	if (driver->usb_read_mdm_ptr == NULL)
-		goto err;
-
-	if (driver->hsic_buf_tbl == NULL)
-		driver->hsic_buf_tbl = kzalloc(NUM_HSIC_BUF_TBL_ENTRIES *
-				sizeof(struct diag_write_device), GFP_KERNEL);
-	if (driver->hsic_buf_tbl == NULL)
-		goto err;
-
-	driver->count_hsic_pool = 0;
-	driver->count_hsic_write_pool = 0;
-
-	driver->itemsize_hsic = READ_HSIC_BUF_SIZE;
-	driver->poolsize_hsic = N_MDM_WRITE;
-	driver->itemsize_hsic_write = sizeof(struct diag_request);
-	driver->poolsize_hsic_write = N_MDM_WRITE;
-
-	mutex_init(&driver->bridge_mutex);
-#ifdef CONFIG_DIAG_OVER_USB
-	INIT_WORK(&(driver->diag_read_mdm_work), diag_read_mdm_work_fn);
-#endif
-	INIT_WORK(&(driver->diag_disconnect_work), diag_disconnect_work_fn);
-	INIT_WORK(&(driver->diag_usb_read_complete_work),
-			diag_usb_read_complete_fn);
-#ifdef CONFIG_DIAG_OVER_USB
-	driver->mdm_ch = usb_diag_open(DIAG_MDM, driver,
-						 diagfwd_bridge_notifier);
-	if (IS_ERR(driver->mdm_ch)) {
-		pr_err("diag: Unable to open USB diag MDM channel\n");
-		goto err;
-	}
-#endif
-	/* register HSIC device */
-	ret = platform_driver_register(&msm_hsic_ch_driver);
-	if (ret)
-		pr_err("diag: could not register HSIC device, ret: %d\n", ret);
-	/* register SMUX device */
-	ret = platform_driver_register(&msm_diagfwd_smux_driver);
-	if (ret)
-		pr_err("diag: could not register SMUX device, ret: %d\n", ret);
-
-	return;
-err:
-	pr_err("diag: Could not initialize for bridge forwarding\n");
-	kfree(driver->usb_buf_mdm_out);
-	kfree(driver->hsic_buf_tbl);
-	kfree(driver->write_ptr_mdm);
-	kfree(driver->usb_read_mdm_ptr);
-	if (driver->diag_bridge_wq)
-		destroy_workqueue(driver->diag_bridge_wq);
-
-	return;
-}
-
-void diagfwd_bridge_exit(void)
-{
-	pr_debug("diag: in %s\n", __func__);
-
-	if (driver->hsic_device_enabled) {
-		diag_hsic_close();
-		driver->hsic_device_enabled = 0;
-	}
-	driver->hsic_inited = 0;
-	diagmem_exit(driver, POOL_TYPE_ALL);
-	if (driver->diag_smux_enabled) {
-		driver->lcid = LCID_INVALID;
-		kfree(driver->buf_in_smux);
-		driver->diag_smux_enabled = 0;
-	}
-	platform_driver_unregister(&msm_hsic_ch_driver);
-	platform_driver_unregister(&msm_diagfwd_smux_driver);
-	/* destroy USB MDM specific variables */
-#ifdef CONFIG_DIAG_OVER_USB
-	if (driver->usb_mdm_connected)
-		usb_diag_free_req(driver->mdm_ch);
-	usb_diag_close(driver->mdm_ch);
-#endif
-	kfree(driver->usb_buf_mdm_out);
-	kfree(driver->hsic_buf_tbl);
-	kfree(driver->write_ptr_mdm);
-	kfree(driver->usb_read_mdm_ptr);
-	destroy_workqueue(driver->diag_bridge_wq);
-}
diff --git a/drivers/char/diag/diagfwd_hsic.h b/drivers/char/diag/diagfwd_hsic.h
index 19ed3c7..2190fff 100644
--- a/drivers/char/diag/diagfwd_hsic.h
+++ b/drivers/char/diag/diagfwd_hsic.h
@@ -17,14 +17,14 @@
 
 #define N_MDM_WRITE	8
 #define N_MDM_READ	1
-
 #define NUM_HSIC_BUF_TBL_ENTRIES N_MDM_WRITE
 
-int diagfwd_connect_bridge(int);
-int diagfwd_disconnect_bridge(int);
 int diagfwd_write_complete_hsic(struct diag_request *);
 int diagfwd_cancel_hsic(void);
-void diagfwd_bridge_init(void);
-void diagfwd_bridge_exit(void);
+void diag_read_usb_hsic_work_fn(struct work_struct *work);
+void diag_usb_read_complete_hsic_fn(struct work_struct *w);
+extern struct diag_bridge_ops hsic_diag_bridge_ops;
+extern struct platform_driver msm_hsic_ch_driver;
+void diag_hsic_close(void);
 
 #endif
diff --git a/drivers/char/diag/diagfwd_smux.c b/drivers/char/diag/diagfwd_smux.c
index ae90686..0a97baf 100644
--- a/drivers/char/diag/diagfwd_smux.c
+++ b/drivers/char/diag/diagfwd_smux.c
@@ -18,6 +18,8 @@
 #include "diagchar.h"
 #include "diagfwd.h"
 #include "diagfwd_smux.h"
+#include "diagfwd_hsic.h"
+#include "diagfwd_bridge.h"
 
 void diag_smux_event(void *priv, int event_type, const void *metadata)
 {
@@ -30,8 +32,8 @@
 		driver->smux_connected = 1;
 		driver->in_busy_smux = 0;
 		/* read data from USB MDM channel & Initiate first write */
-		queue_work(driver->diag_bridge_wq,
-				 &(driver->diag_read_mdm_work));
+		queue_work(diag_bridge[SMUX].wq,
+			   &diag_bridge[SMUX].diag_read_work);
 		break;
 	case SMUX_DISCONNECTED:
 		driver->smux_connected = 0;
@@ -67,7 +69,7 @@
 
 int diagfwd_read_complete_smux(void)
 {
-	queue_work(driver->diag_bridge_wq, &(driver->diag_read_mdm_work));
+	queue_work(diag_bridge[SMUX].wq, &diag_bridge[SMUX].diag_read_work);
 	return 0;
 }
 
@@ -85,6 +87,36 @@
 	return 0;
 }
 
+void diag_usb_read_complete_smux_fn(struct work_struct *w)
+{
+	diagfwd_read_complete_bridge(diag_bridge[SMUX].usb_read_ptr);
+}
+
+void diag_read_usb_smux_work_fn(struct work_struct *work)
+{
+	int ret;
+
+	if (driver->diag_smux_enabled) {
+		if (driver->lcid && diag_bridge[SMUX].usb_buf_out &&
+			(diag_bridge[SMUX].read_len > 0) &&
+				driver->smux_connected) {
+			ret = msm_smux_write(driver->lcid, NULL,
+			      diag_bridge[SMUX].usb_buf_out,
+				 diag_bridge[SMUX].read_len);
+			if (ret)
+				pr_err("diag: writing to SMUX ch, r = %d, lcid = %d\n",
+						 ret, driver->lcid);
+		}
+		diag_bridge[SMUX].usb_read_ptr->buf =
+					 diag_bridge[SMUX].usb_buf_out;
+		diag_bridge[SMUX].usb_read_ptr->length = USB_MAX_OUT_BUF;
+		diag_bridge[SMUX].usb_read_ptr->context = (void *)SMUX;
+		usb_diag_read(diag_bridge[SMUX].ch,
+					 diag_bridge[SMUX].usb_read_ptr);
+		return;
+	}
+}
+
 static int diagfwd_smux_runtime_suspend(struct device *dev)
 {
 	dev_dbg(dev, "pm_runtime: suspending...\n");
@@ -120,7 +152,7 @@
 		}
 	}
 	/* Poll USB channel to check for data*/
-	queue_work(driver->diag_bridge_wq, &(driver->diag_read_mdm_work));
+	queue_work(diag_bridge[SMUX].wq, &(diag_bridge[SMUX].diag_read_work));
 	return ret;
 }
 
@@ -142,6 +174,11 @@
 	 * if (ret)
 	 *	pr_err("diag: error setting SMUX ch option, r = %d\n", ret);
 	 */
+	if (driver->write_ptr_mdm == NULL)
+		driver->write_ptr_mdm = kzalloc(sizeof(struct diag_request),
+								 GFP_KERNEL);
+	if (driver->write_ptr_mdm == NULL)
+		goto err;
 	ret = diagfwd_connect_smux();
 	return ret;
 
diff --git a/drivers/char/diag/diagfwd_smux.h b/drivers/char/diag/diagfwd_smux.h
index e78b7ed..b45fd5d 100644
--- a/drivers/char/diag/diagfwd_smux.h
+++ b/drivers/char/diag/diagfwd_smux.h
@@ -20,6 +20,8 @@
 int diagfwd_read_complete_smux(void);
 int diagfwd_write_complete_smux(void);
 int diagfwd_connect_smux(void);
+void diag_usb_read_complete_smux_fn(struct work_struct *w);
+void diag_read_usb_smux_work_fn(struct work_struct *work);
 extern struct platform_driver msm_diagfwd_smux_driver;
 
 #endif
diff --git a/drivers/char/diag/diagmem.c b/drivers/char/diag/diagmem.c
index 1a522d5..ab1aa75 100644
--- a/drivers/char/diag/diagmem.c
+++ b/drivers/char/diag/diagmem.c
@@ -51,7 +51,7 @@
 				driver->diag_write_struct_pool, GFP_ATOMIC);
 			}
 		}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 	} else if (pool_type == POOL_TYPE_HSIC) {
 		if (driver->diag_hsic_pool) {
 			if (driver->count_hsic_pool < driver->poolsize_hsic) {
@@ -105,7 +105,7 @@
 		} else if (driver->ref_count == 0 && pool_type == POOL_TYPE_ALL)
 			printk(KERN_ALERT "Unable to destroy STRUCT mempool");
 	}
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 	if (driver->diag_hsic_pool && (driver->hsic_inited == 0)) {
 		if (driver->count_hsic_pool == 0) {
 			mempool_destroy(driver->diag_hdlc_pool);
@@ -156,7 +156,7 @@
 			pr_err("diag: Attempt to free up DIAG driver "
 			   "USB structure mempool which is already free %d ",
 				    driver->count_write_struct_pool);
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 	} else if (pool_type == POOL_TYPE_HSIC) {
 		if (driver->diag_hsic_pool != NULL &&
 			driver->count_hsic_pool > 0) {
@@ -210,7 +210,7 @@
 		printk(KERN_INFO "Cannot allocate diag USB struct mempool\n");
 }
 
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 void diagmem_hsic_init(struct diagchar_dev *driver)
 {
 	if (driver->count_hsic_pool == 0)
diff --git a/drivers/char/diag/diagmem.h b/drivers/char/diag/diagmem.h
index 8665c75..36def72 100644
--- a/drivers/char/diag/diagmem.h
+++ b/drivers/char/diag/diagmem.h
@@ -18,7 +18,7 @@
 void diagmem_free(struct diagchar_dev *driver, void *buf, int pool_type);
 void diagmem_init(struct diagchar_dev *driver);
 void diagmem_exit(struct diagchar_dev *driver, int pool_type);
-#ifdef CONFIG_DIAG_BRIDGE_CODE
+#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 void diagmem_hsic_init(struct diagchar_dev *driver);
 #endif
 #endif
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index 03c69d0..4bd8885 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -18,14 +18,14 @@
 #define EVENT_MASKS_TYPE		4
 #define PKT_TYPE			8
 #define DEINIT_TYPE			16
-#define USER_SPACE_LOG_TYPE		32
+#define USER_SPACE_DATA_TYPE		32
 #define DCI_DATA_TYPE			64
 #define USB_MODE			1
 #define MEMORY_DEVICE_MODE		2
 #define NO_LOGGING_MODE			3
 #define UART_MODE			4
 #define SOCKET_MODE			5
-
+#define CALLBACK_MODE			6
 /* different values that go in for diag_data_type */
 #define DATA_TYPE_EVENT         	0
 #define DATA_TYPE_F3            	1
@@ -43,6 +43,7 @@
 #define DIAG_IOCTL_DCI_REG		23
 #define DIAG_IOCTL_DCI_STREAM_INIT	24
 #define DIAG_IOCTL_DCI_HEALTH_STATS	25
+#define DIAG_IOCTL_REMOTE_DEV		32
 
 /* PC Tools IDs */
 #define APQ8060_TOOLS_ID	4062
@@ -111,7 +112,7 @@
 /* This needs to be modified manually now, when we add
  a new RANGE of SSIDs to the msg_mask_tbl */
 #define MSG_MASK_TBL_CNT		24
-#define EVENT_LAST_ID			0x08AD
+#define EVENT_LAST_ID			0x099F
 
 #define MSG_SSID_0			0
 #define MSG_SSID_0_LAST			93
@@ -674,24 +675,24 @@
 };
 
 static const uint32_t msg_bld_masks_22[] = {
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH,
-	MSG_LVL_HIGH
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW,
+	MSG_LVL_LOW
 };
 
 /* LOG CODES */
 
 #define LOG_0	0x0
-#define LOG_1	0x15A7
+#define LOG_1	0x1750
 #define LOG_2	0x0
 #define LOG_3	0x0
 #define LOG_4	0x4910