diag: Add DCI support

This change adds DIAG Consumer Interface (DCI). This will help
Android user space applications use DIAG to send command requests
to modem and receive responses back. Until now, this was done using
tools or scripts on PC. With this change, this functionality is available
on target itself.

Change-Id: I66c187e406fe93ae23c4ca0ead2cf0aed3502197
Signed-off-by: Shalabh Jain <shalabhj@codeaurora.org>
diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile
index c62b7fd..3181d29 100644
--- a/drivers/char/diag/Makefile
+++ b/drivers/char/diag/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_DIAG_CHAR) := diagchar.o
 obj-$(CONFIG_DIAG_SDIO_PIPE) += diagfwd_sdio.o
 obj-$(CONFIG_DIAG_HSIC_PIPE) += diagfwd_hsic.o
-diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagmem.o diagfwd_cntl.o
+diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagmem.o diagfwd_cntl.o diag_dci.o
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
new file mode 100644
index 0000000..5cbf888
--- /dev/null
+++ b/drivers/char/diag/diag_dci.c
@@ -0,0 +1,335 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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/init.h>
+#include <linux/uaccess.h>
+#include <linux/diagchar.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <asm/current.h>
+#ifdef CONFIG_DIAG_OVER_USB
+#include <mach/usbdiag.h>
+#endif
+#include "diagchar_hdlc.h"
+#include "diagmem.h"
+#include "diagchar.h"
+#include "diagfwd.h"
+#include "diag_dci.h"
+
+unsigned int dci_max_reg = 100;
+unsigned int dci_max_clients = 10;
+
+static void diag_smd_dci_send_req(int proc_num)
+{
+	void *buf = NULL;
+	smd_channel_t *smd_ch = NULL;
+	int i, r, found = 1;
+	int cmd_code_len = 1;
+
+	if (driver->in_busy_dci)
+		return;
+
+	if (proc_num == MODEM_PROC) {
+		buf = driver->buf_in_dci;
+		smd_ch = driver->ch_dci;
+	}
+
+	if (!smd_ch || !buf)
+		return;
+
+	r = smd_read_avail(smd_ch);
+	if (r > IN_BUF_SIZE) {
+		if (r < MAX_IN_BUF_SIZE) {
+			pr_err("diag: SMD DCI sending pkt upto %d bytes", r);
+			buf = krealloc(buf, r, GFP_KERNEL);
+		} else {
+			pr_err("diag: DCI pkt > %d bytes", MAX_IN_BUF_SIZE);
+			return;
+		}
+	}
+	if (buf && r > 0) {
+		smd_read(smd_ch, buf, r);
+		pr_debug("diag: data received ---\n");
+		for (i = 0; i < r; i++)
+			pr_debug("\t %x \t", *(((unsigned char *)buf)+i));
+
+		if (*(uint8_t *)(buf+4) != DCI_CMD_CODE)
+			cmd_code_len = 4; /* delayed response */
+		driver->write_ptr_dci->length =
+			 (int)(*(uint16_t *)(buf+2)) - (4+cmd_code_len);
+		pr_debug("diag: len = %d\n", (int)(*(uint16_t *)(buf+2))
+							 - (4+cmd_code_len));
+		/* look up DCI client with tag */
+		for (i = 0; i < dci_max_reg; i++) {
+			if (driver->dci_tbl[i].tag ==
+			    *(int *)(buf+(4+cmd_code_len))) {
+				found = 0;
+				break;
+			}
+		}
+		if (found)
+			pr_alert("diag: No matching PID for DCI data\n");
+		pr_debug("\n diag PID = %d", driver->dci_tbl[i].pid);
+		if (driver->dci_tbl[i].pid == 0)
+			pr_alert("diag: Receiving DCI process deleted\n");
+		*(int *)(buf+4+cmd_code_len) = driver->dci_tbl[i].uid;
+		/* update len after adding UID */
+		driver->write_ptr_dci->length =
+			driver->write_ptr_dci->length + 4;
+		pr_debug("diag: data receivd, wake process\n");
+		driver->in_busy_dci = 1;
+		diag_update_sleeping_process(driver->dci_tbl[i].pid,
+							DCI_DATA_TYPE);
+		/* delete immediate response entry */
+		if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
+			driver->dci_tbl[i].pid = 0;
+		for (i = 0; i < dci_max_reg; i++)
+			if (driver->dci_tbl[i].pid != 0)
+				pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
+				driver->dci_tbl[i].pid, driver->dci_tbl[i].uid,
+				 driver->dci_tbl[i].tag);
+		pr_debug("diag: completed clearing table\n");
+	}
+}
+
+void diag_read_smd_dci_work_fn(struct work_struct *work)
+{
+	diag_smd_dci_send_req(MODEM_PROC);
+}
+
+static void diag_smd_dci_notify(void *ctxt, unsigned event)
+{
+	queue_work(driver->diag_wq, &(driver->diag_read_smd_dci_work));
+}
+
+static int diag_dci_probe(struct platform_device *pdev)
+{
+	int err = 0;
+
+	if (pdev->id == SMD_APPS_MODEM) {
+		err = smd_open("DIAG_2", &driver->ch_dci, driver,
+					    diag_smd_dci_notify);
+		if (err)
+			pr_err("diag: cannot open DCI port, Id = %d, err ="
+				" %d\n", pdev->id, err);
+	}
+	return err;
+}
+
+
+int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
+					 int len, int index)
+{
+	int i;
+
+	/* remove UID from user space pkt before sending to peripheral */
+	buf = buf + 4;
+	len = len - 4;
+	mutex_lock(&driver->dci_mutex);
+	/* prepare DCI packet */
+	driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */
+	driver->apps_dci_buf[1] = 1; /* version */
+	*(uint16_t *)(driver->apps_dci_buf + 2) = len + 4 + 1; /* length */
+	driver->apps_dci_buf[4] = DCI_CMD_CODE; /* DCI ID */
+	*(int *)(driver->apps_dci_buf + 5) = driver->dci_tbl[index].tag;
+	for (i = 0; i < len; i++)
+		driver->apps_dci_buf[i+9] = *(buf+i);
+	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 {
+		pr_alert("diag: check DCI channel\n");
+		i = DIAG_DCI_SEND_DATA_FAIL;
+	}
+	mutex_unlock(&driver->dci_mutex);
+	return i;
+}
+
+int diag_register_dci_transaction(int uid)
+{
+	int i, new_dci_client = 1, ret = -1;
+
+	for (i = 0; i < dci_max_reg; i++) {
+		if (driver->dci_tbl[i].pid == current->tgid) {
+			new_dci_client = 0;
+			break;
+		}
+	}
+	mutex_lock(&driver->dci_mutex);
+	if (new_dci_client)
+		driver->num_dci_client++;
+	if (driver->num_dci_client > MAX_DCI_CLIENT) {
+		pr_info("diag: Max DCI Client limit reached\n");
+		driver->num_dci_client--;
+		mutex_unlock(&driver->dci_mutex);
+		return ret;
+	}
+	/* Make an entry in kernel DCI table */
+	driver->dci_tag++;
+	for (i = 0; i < dci_max_reg; i++) {
+		if (driver->dci_tbl[i].pid == 0) {
+			driver->dci_tbl[i].pid = current->tgid;
+			driver->dci_tbl[i].uid = uid;
+			driver->dci_tbl[i].tag = driver->dci_tag;
+			ret = i;
+			break;
+		}
+	}
+	mutex_unlock(&driver->dci_mutex);
+	return ret;
+}
+
+int diag_process_dci_client(unsigned char *buf, int len)
+{
+	unsigned char *temp = buf;
+	uint16_t subsys_cmd_code;
+	int subsys_id, cmd_code, i, ret = -1, index = -1;
+	struct diag_master_table entry;
+
+	/* enter this UID into kernel table and return index */
+	index = diag_register_dci_transaction(*(int *)temp);
+	if (index < 0) {
+		pr_alert("diag: registering new DCI transaction failed\n");
+		return DIAG_DCI_NO_REG;
+	}
+	temp += 4;
+	/* Check for registered peripheral and fwd pkt to apropriate proc */
+	cmd_code = (int)(*(char *)buf);
+	temp++;
+	subsys_id = (int)(*(char *)temp);
+	temp++;
+	subsys_cmd_code = *(uint16_t *)temp;
+	temp += 2;
+	pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code);
+	for (i = 0; i < diag_max_reg; i++) {
+		entry = driver->table[i];
+		if (entry.process_id != NO_PROCESS) {
+			if (entry.cmd_code == cmd_code && entry.subsys_id ==
+				 subsys_id && entry.cmd_code_lo <=
+							 subsys_cmd_code &&
+				  entry.cmd_code_hi >= subsys_cmd_code) {
+				ret = diag_send_dci_pkt(entry, buf, len, index);
+			} else if (entry.cmd_code == 255
+				  && cmd_code == 75) {
+				if (entry.subsys_id ==
+					subsys_id &&
+				   entry.cmd_code_lo <=
+					subsys_cmd_code &&
+					 entry.cmd_code_hi >=
+					subsys_cmd_code) {
+					ret = diag_send_dci_pkt(entry, buf, len,
+								 index);
+				}
+			} else if (entry.cmd_code == 255 &&
+				  entry.subsys_id == 255) {
+				if (entry.cmd_code_lo <=
+						 cmd_code &&
+						 entry.
+						cmd_code_hi >= cmd_code) {
+					ret = diag_send_dci_pkt(entry, buf, len,
+								 index);
+				}
+			}
+		}
+	}
+	return ret;
+}
+
+static int diag_dci_runtime_suspend(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: suspending...\n");
+	return 0;
+}
+
+static int diag_dci_runtime_resume(struct device *dev)
+{
+	dev_dbg(dev, "pm_runtime: resuming...\n");
+	return 0;
+}
+
+static const struct dev_pm_ops diag_dci_dev_pm_ops = {
+	.runtime_suspend = diag_dci_runtime_suspend,
+	.runtime_resume = diag_dci_runtime_resume,
+};
+
+struct platform_driver msm_diag_dci_driver = {
+	.probe = diag_dci_probe,
+	.driver = {
+		   .name = "DIAG_2",
+		   .owner = THIS_MODULE,
+		   .pm   = &diag_dci_dev_pm_ops,
+		   },
+};
+
+int diag_dci_init(void)
+{
+	int success = 0;
+
+	driver->dci_tag = 0;
+	driver->dci_client_id = 0;
+	driver->num_dci_client = 0;
+	driver->in_busy_dci = 0;
+	mutex_init(&driver->dci_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;
+	}
+	if (driver->dci_tbl == NULL) {
+		driver->dci_tbl = kzalloc(dci_max_reg *
+			sizeof(struct diag_dci_tbl), GFP_KERNEL);
+		if (driver->dci_tbl == NULL)
+			goto err;
+	}
+	if (driver->apps_dci_buf == NULL) {
+		driver->apps_dci_buf = kzalloc(APPS_BUF_SIZE, GFP_KERNEL);
+		if (driver->apps_dci_buf == NULL)
+			goto err;
+	}
+	success = platform_driver_register(&msm_diag_dci_driver);
+	if (success) {
+		pr_err("diag: Could not register DCI driver\n");
+		goto err;
+	}
+	return DIAG_DCI_NO_ERROR;
+err:
+	pr_err("diag: Could not initialize diag DCI buffers");
+	kfree(driver->dci_tbl);
+	kfree(driver->apps_dci_buf);
+	kfree(driver->buf_in_dci);
+	kfree(driver->write_ptr_dci);
+	return DIAG_DCI_NO_REG;
+}
+
+void diag_dci_exit(void)
+{
+	smd_close(driver->ch_dci);
+	driver->ch_dci = 0;
+	platform_driver_unregister(&msm_diag_dci_driver);
+	kfree(driver->dci_tbl);
+	kfree(driver->apps_dci_buf);
+	kfree(driver->buf_in_dci);
+	kfree(driver->write_ptr_dci);
+}
+
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
new file mode 100644
index 0000000..cc6e0cf
--- /dev/null
+++ b/drivers/char/diag/diag_dci.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 DIAG_DCI_H
+#define DIAG_DCI_H
+#define MAX_DCI_CLIENT 10
+#define DCI_CMD_CODE 0x93
+
+extern unsigned int dci_max_reg;
+extern unsigned int dci_max_clients;
+struct diag_dci_tbl {
+	int pid;
+	int uid;
+	int tag;
+};
+
+#define DIAG_CON_APSS (0x0001)	/* Bit mask for APSS */
+#define DIAG_CON_MPSS (0x0002)	/* Bit mask for MPSS */
+#define DIAG_CON_LPASS (0x0004)	/* Bit mask for LPASS */
+#define DIAG_CON_WCNSS (0x0008)	/* Bit mask for WCNSS */
+
+enum {
+	DIAG_DCI_NO_ERROR = 1001,	/* No error */
+	DIAG_DCI_NO_REG,		/* Could not register */
+	DIAG_DCI_NO_MEM,		/* Failed memory allocation */
+	DIAG_DCI_NOT_SUPPORTED,	/* This particular client is not supported */
+	DIAG_DCI_HUGE_PACKET,	/* Request/Response Packet too huge */
+	DIAG_DCI_SEND_DATA_FAIL,/* writing to kernel or peripheral fails */
+	DIAG_DCI_TABLE_ERR	/* Error dealing with registration tables */
+};
+
+int diag_dci_init(void);
+void diag_dci_exit(void);
+void diag_read_smd_dci_work_fn(struct work_struct *);
+int diag_process_dci_client(unsigned char *buf, int len);
+int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
+							 int len, int index);
+#endif
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 7b63610..d547121 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -54,6 +54,7 @@
 #define DIAG_CTRL_MSG_LOG_MASK	9
 #define DIAG_CTRL_MSG_EVENT_MASK	10
 #define DIAG_CTRL_MSG_F3_MASK	11
+#define CONTROL_CHAR	0x7E
 
 /* Maximum number of pkt reg supported at initialization*/
 extern unsigned int diag_max_reg;
@@ -135,7 +136,14 @@
 	int polling_reg_flag;
 	struct diag_write_device *buf_tbl;
 	int use_device_tree;
-
+	/* DCI related variables */
+	struct diag_dci_tbl *dci_tbl;
+	int dci_tag;
+	int dci_client_id;
+	struct mutex dci_mutex;
+	int num_dci_client;
+	unsigned char *apps_dci_buf;
+	int dci_state;
 	/* Memory pool parameters */
 	unsigned int itemsize;
 	unsigned int poolsize;
@@ -167,6 +175,7 @@
 	unsigned char *buf_in_wcnss_1;
 	unsigned char *buf_in_wcnss_2;
 	unsigned char *buf_in_wcnss_cntl;
+	unsigned char *buf_in_dci;
 	unsigned char *usb_buf_out;
 	unsigned char *apps_rsp_buf;
 	unsigned char *user_space_data;
@@ -176,6 +185,7 @@
 	unsigned char *buf_event_mask_update;
 	smd_channel_t *ch;
 	smd_channel_t *ch_cntl;
+	smd_channel_t *ch_dci;
 	smd_channel_t *chqdsp;
 	smd_channel_t *chqdsp_cntl;
 	smd_channel_t *ch_wcnss;
@@ -186,6 +196,7 @@
 	int in_busy_qdsp_2;
 	int in_busy_wcnss_1;
 	int in_busy_wcnss_2;
+	int in_busy_dci;
 	int read_len_legacy;
 	unsigned char *hdlc_buf;
 	unsigned hdlc_count;
@@ -208,6 +219,7 @@
 	struct work_struct diag_modem_mask_update_work;
 	struct work_struct diag_qdsp_mask_update_work;
 	struct work_struct diag_wcnss_mask_update_work;
+	struct work_struct diag_read_smd_dci_work;
 	uint8_t *msg_masks;
 	uint8_t *log_masks;
 	int log_masks_length;
@@ -223,6 +235,7 @@
 	struct diag_request *write_ptr_qdsp_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;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 7e2333a..0a156de 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -28,6 +28,7 @@
 #include "diagchar.h"
 #include "diagfwd.h"
 #include "diagfwd_cntl.h"
+#include "diag_dci.h"
 #ifdef CONFIG_DIAG_SDIO_PIPE
 #include "diagfwd_sdio.h"
 #endif
@@ -222,6 +223,11 @@
 		return -ENOMEM;
 	}
 
+	/* clean up any DCI registrations for this client
+	* This will specially help in case of ungraceful exit of any DCI client
+	* This call will remove any pending registrations of such client
+	*/
+	diagchar_ioctl(NULL, DIAG_IOCTL_DCI_DEINIT, 0);
 #ifdef CONFIG_DIAG_OVER_USB
 	/* If the SD logging process exits, change logging to USB mode */
 	if (driver->logging_process_id == current->tgid) {
@@ -330,6 +336,7 @@
 	int i, j, count_entries = 0, temp;
 	int success = -1;
 	void *temp_buf;
+	uint16_t support_list = 0;
 
 	if (iocmd == DIAG_IOCTL_COMMAND_REG) {
 		struct bindpkt_params_per_process *pkt_params =
@@ -397,6 +404,46 @@
 			*(delay_params->num_bytes_ptr) = sizeof(delayed_rsp_id);
 			success = 0;
 		}
+	} else if (iocmd == DIAG_IOCTL_DCI_REG) {
+		if (driver->dci_state == DIAG_DCI_NO_REG)
+			return DIAG_DCI_NO_REG;
+		/* use the 'list' later on to notify user space */
+		if (driver->num_dci_client >= MAX_DCI_CLIENT)
+			return DIAG_DCI_NO_REG;
+		mutex_lock(&driver->dci_mutex);
+		driver->num_dci_client++;
+		pr_debug("diag: id = %d\n", driver->dci_client_id);
+		driver->dci_client_id++;
+		mutex_unlock(&driver->dci_mutex);
+		return driver->dci_client_id;
+	} else if (iocmd == DIAG_IOCTL_DCI_DEINIT) {
+		success = -1;
+		/* Delete this process from DCI table */
+		mutex_lock(&driver->dci_mutex);
+		for (i = 0; i < dci_max_reg; i++) {
+			if (driver->dci_tbl[i].pid == current->tgid) {
+				pr_debug("diag: delete %d\n", current->tgid);
+				driver->dci_tbl[i].pid = 0;
+				success = i;
+			}
+		}
+		/* if any registrations were deleted successfully OR a valid
+		   client_id was sent in DEINIT call , then its DCI client */
+		if (success >= 0 || ioarg)
+			driver->num_dci_client--;
+		driver->num_dci_client--;
+		mutex_unlock(&driver->dci_mutex);
+		for (i = 0; i < dci_max_reg; i++)
+			if (driver->dci_tbl[i].pid != 0)
+				pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
+	driver->dci_tbl[i].pid, driver->dci_tbl[i].uid, driver->dci_tbl[i].tag);
+		pr_debug("diag: complete deleting registrations\n");
+		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;
+		return DIAG_DCI_NO_ERROR;
 	} else if (iocmd == DIAG_IOCTL_LSM_DEINIT) {
 		for (i = 0; i < driver->num_clients; i++)
 			if (driver->client_map[i].pid == current->tgid)
@@ -720,6 +767,26 @@
 		goto exit;
 	}
 
+	if (driver->data_ready[index] & DCI_DATA_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & DCI_DATA_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4,
+			 driver->write_ptr_dci->length, 4);
+		/* check delayed vs immediate response */
+		if (*(uint8_t *)(driver->buf_in_dci+4) == DCI_CMD_CODE)
+			COPY_USER_SPACE_OR_EXIT(buf+8,
+		*(driver->buf_in_dci + 5), driver->write_ptr_dci->length);
+		else
+			COPY_USER_SPACE_OR_EXIT(buf+8,
+		*(driver->buf_in_dci + 8), driver->write_ptr_dci->length);
+		driver->in_busy_dci = 0;
+		driver->data_ready[index] ^= DCI_DATA_TYPE;
+		if (driver->ch_dci)
+			queue_work(driver->diag_wq,
+				 &(driver->diag_read_smd_dci_work));
+		goto exit;
+	}
 exit:
 	mutex_unlock(&driver->diagchar_mutex);
 	return ret;
@@ -748,6 +815,17 @@
 	/* First 4 bytes indicate the type of payload - ignore these */
 	payload_size = count - 4;
 
+	if (pkt_type == DCI_DATA_TYPE) {
+		err = copy_from_user(driver->user_space_data, buf + 4,
+							 payload_size);
+		if (err) {
+			pr_alert("diag: copy failed for DCI data\n");
+			return DIAG_DCI_SEND_DATA_FAIL;
+		}
+		err = diag_process_dci_client(driver->user_space_data,
+							payload_size);
+		return err;
+	}
 	if (pkt_type == USER_SPACE_LOG_TYPE) {
 		err = copy_from_user(driver->user_space_data, buf + 4,
 							 payload_size);
@@ -1076,6 +1154,7 @@
 		driver->used = 0;
 		timer_in_progress = 0;
 		driver->debug_flag = 1;
+		driver->dci_state = DIAG_DCI_NO_ERROR;
 		setup_timer(&drain_timer, drain_timer_func, 1234);
 		driver->itemsize = itemsize;
 		driver->poolsize = poolsize;
@@ -1100,8 +1179,11 @@
 			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);
 		diagfwd_init();
 		diagfwd_cntl_init();
+		driver->dci_state = diag_dci_init();
 		diag_sdio_fn(INIT);
 		diag_hsic_fn(INIT);
 		pr_debug("diagchar initializing ..\n");
diff --git a/drivers/char/diag/diagchar_hdlc.c b/drivers/char/diag/diagchar_hdlc.c
index ef57d52..74dcb6b 100644
--- a/drivers/char/diag/diagchar_hdlc.c
+++ b/drivers/char/diag/diagchar_hdlc.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2009, 2012, Code Aurora Forum. 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
@@ -19,6 +19,7 @@
 #include <linux/uaccess.h>
 #include <linux/crc-ccitt.h>
 #include "diagchar_hdlc.h"
+#include "diagchar.h"
 
 
 MODULE_LICENSE("GPL v2");
diff --git a/drivers/char/diag/diagchar_hdlc.h b/drivers/char/diag/diagchar_hdlc.h
index 2df81de..116c980 100644
--- a/drivers/char/diag/diagchar_hdlc.h
+++ b/drivers/char/diag/diagchar_hdlc.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2009, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2009, 2012, Code Aurora Forum. 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
@@ -54,7 +54,6 @@
 int diag_hdlc_decode(struct diag_hdlc_decode_type *hdlc);
 
 #define ESC_CHAR     0x7D
-#define CONTROL_CHAR 0x7E
 #define ESC_MASK     0x20
 
 #endif
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index efe0241..7066141 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -37,6 +37,8 @@
 #ifdef CONFIG_DIAG_SDIO_PIPE
 #include "diagfwd_sdio.h"
 #endif
+#include "diag_dci.h"
+
 #define MODE_CMD		41
 #define RESET_ID		2
 #define ALL_EQUIP_ID		100
@@ -689,14 +691,14 @@
 	mutex_unlock(&driver->diagchar_mutex);
 }
 
-void diag_update_sleeping_process(int process_id)
+void diag_update_sleeping_process(int process_id, int data_type)
 {
 	int i;
 
 	mutex_lock(&driver->diagchar_mutex);
 	for (i = 0; i < driver->num_clients; i++)
 		if (driver->client_map[i].pid == process_id) {
-			driver->data_ready[i] |= PKT_TYPE;
+			driver->data_ready[i] |= data_type;
 			break;
 		}
 	wake_up_interruptible(&driver->wait_q);
@@ -709,7 +711,7 @@
 	driver->pkt_length = len;
 	if (entry.process_id != NON_APPS_PROC && type != MODEM_DATA) {
 		diag_update_pkt_buffer(buf);
-		diag_update_sleeping_process(entry.process_id);
+		diag_update_sleeping_process(entry.process_id, PKT_TYPE);
 	} else {
 		if (len > 0) {
 			if (entry.client_id == MODEM_PROC && driver->ch) {
@@ -2016,6 +2018,7 @@
 	usb_diag_close(driver->legacy_ch);
 #endif
 	platform_driver_unregister(&msm_smd_ch1_driver);
+	platform_driver_unregister(&msm_diag_dci_driver);
 	platform_driver_unregister(&diag_smd_lite_driver);
 	kfree(driver->event_mask);
 	kfree(driver->log_mask);
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index 5744459..0780a8e 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -32,6 +32,7 @@
 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_update_sleeping_process(int process_id, int data_type);
 /* State for diag forwarding */
 #ifdef CONFIG_DIAG_OVER_USB
 int diagfwd_connect(void);
@@ -40,4 +41,5 @@
 extern int diag_debug_buf_idx;
 extern unsigned char diag_debug_buf[1024];
 extern int diag_event_num_bytes;
+extern struct platform_driver msm_diag_dci_driver;
 #endif