Merge "diag: dci: Add DCI support for APSS logs and events and cmd/rsp"
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 0edfdad..653e299 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -22,7 +22,10 @@
 #include <linux/platform_device.h>
 #include <linux/pm_wakeup.h>
 #include <linux/spinlock.h>
+#include <linux/ratelimit.h>
+#include <linux/reboot.h>
 #include <asm/current.h>
+#include <mach/restart.h>
 #ifdef CONFIG_DIAG_OVER_USB
 #include <mach/usbdiag.h>
 #endif
@@ -33,6 +36,10 @@
 #include "diagfwd_cntl.h"
 #include "diag_dci.h"
 
+static struct timer_list dci_drain_timer;
+static int dci_timer_in_progress;
+static struct work_struct dci_data_drain_work;
+
 unsigned int dci_max_reg = 100;
 unsigned int dci_max_clients = 10;
 unsigned char dci_cumulative_log_mask[DCI_LOG_MASK_SIZE];
@@ -47,14 +54,15 @@
 /* Number of milliseconds anticipated to process the DCI data */
 #define DCI_WAKEUP_TIMEOUT 1
 
-#define DCI_CHK_CAPACITY(entry, new_data_len)				\
-((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0)	\
+#define DCI_CAN_ADD_BUF_TO_LIST(buf)					\
+	(buf && buf->data && !buf->in_busy && buf->data_len > 0)	\
 
 #ifdef CONFIG_DEBUG_FS
 struct diag_dci_data_info *dci_data_smd;
 struct mutex dci_stat_mutex;
 
-void diag_dci_smd_record_info(int read_bytes, uint8_t ch_type)
+void diag_dci_smd_record_info(int read_bytes, uint8_t ch_type,
+			      uint8_t peripheral)
 {
 	static int curr_dci_data_smd;
 	static unsigned long iteration;
@@ -67,6 +75,7 @@
 	temp_data += curr_dci_data_smd;
 	temp_data->iteration = iteration + 1;
 	temp_data->data_size = read_bytes;
+	temp_data->peripheral = peripheral;
 	temp_data->ch_type = ch_type;
 	diag_get_timestamp(temp_data->time_stamp);
 	curr_dci_data_smd++;
@@ -74,50 +83,393 @@
 	mutex_unlock(&dci_stat_mutex);
 }
 #else
-void diag_dci_smd_record_info(int read_bytes, uint8_t ch_type) { }
+void diag_dci_smd_record_info(int read_bytes, uint8_t ch_type,
+			      uint8_t peripheral) { }
 #endif
 
+static void dci_drain_data(unsigned long data)
+{
+	queue_work(driver->diag_dci_wq, &dci_data_drain_work);
+}
+
+static void dci_check_drain_timer(void)
+{
+	if (!dci_timer_in_progress) {
+		dci_timer_in_progress = 1;
+		 mod_timer(&dci_drain_timer, jiffies + msecs_to_jiffies(500));
+	}
+}
+
+static int diag_dci_init_buffer(struct diag_dci_buffer_t *buffer, int type)
+{
+	if (!buffer || buffer->data)
+		return -EINVAL;
+
+	switch (type) {
+	case DCI_BUF_PRIMARY:
+		buffer->data = kzalloc(IN_BUF_SIZE, GFP_KERNEL);
+		if (!buffer->data)
+			return -ENOMEM;
+		buffer->capacity = IN_BUF_SIZE;
+		break;
+	case DCI_BUF_SECONDARY:
+		buffer->data = NULL;
+		buffer->capacity = IN_BUF_SIZE;
+		break;
+	case DCI_BUF_CMD:
+		buffer->data = kzalloc(PKT_SIZE, GFP_KERNEL);
+		if (!buffer->data)
+			return -ENOMEM;
+		buffer->capacity = PKT_SIZE;
+		break;
+	default:
+		pr_err("diag: In %s, unknown type %d", __func__, type);
+		return -EINVAL;
+	}
+
+	buffer->data_len = 0;
+	buffer->in_busy = 0;
+	buffer->buf_type = type;
+	mutex_init(&buffer->data_mutex);
+
+	return 0;
+}
+
+static inline int diag_dci_check_buffer(struct diag_dci_buffer_t *buf, int len)
+{
+	if (!buf)
+		return -EINVAL;
+
+	/* Return 1 if the buffer is not busy and can hold new data */
+	if ((buf->data_len + len < buf->capacity) && !buf->in_busy)
+		return 1;
+
+	return 0;
+}
+
+static void dci_add_buffer_to_list(struct diag_dci_client_tbl *client,
+				   struct diag_dci_buffer_t *buf)
+{
+	if (!buf || !client || !buf->data)
+		return;
+
+	if (buf->in_list || buf->data_len == 0)
+		return;
+
+	mutex_lock(&client->write_buf_mutex);
+	list_add_tail(&buf->buf_track, &client->list_write_buf);
+	mutex_lock(&buf->data_mutex);
+	buf->in_busy = 1;
+	buf->in_list = 1;
+	mutex_unlock(&buf->data_mutex);
+	mutex_unlock(&client->write_buf_mutex);
+}
+
+static int diag_dci_get_buffer(struct diag_dci_client_tbl *client,
+			       int data_source, int len)
+{
+	struct diag_dci_buffer_t *buf_primary = NULL;
+	struct diag_dci_buffer_t *buf_temp = NULL;
+	struct diag_dci_buffer_t *curr = NULL;
+
+	if (!client)
+		return -EINVAL;
+	if (len < 0 || len > IN_BUF_SIZE)
+		return -EINVAL;
+
+	curr = client->buffers[data_source].buf_curr;
+	buf_primary = client->buffers[data_source].buf_primary;
+
+	if (curr && diag_dci_check_buffer(curr, len) == 1)
+		return 0;
+
+	dci_add_buffer_to_list(client, curr);
+	client->buffers[data_source].buf_curr = NULL;
+
+	if (diag_dci_check_buffer(buf_primary, len) == 1) {
+		client->buffers[data_source].buf_curr = buf_primary;
+		return 0;
+	}
+
+	buf_temp = kzalloc(sizeof(struct diag_dci_buffer_t), GFP_KERNEL);
+	if (!buf_temp)
+		return -EIO;
+
+	if (!diag_dci_init_buffer(buf_temp, DCI_BUF_SECONDARY)) {
+		buf_temp->data = diagmem_alloc(driver, driver->itemsize_dci,
+					       POOL_TYPE_DCI);
+		if (!buf_temp->data) {
+			kfree(buf_temp);
+			buf_temp = NULL;
+			return -ENOMEM;
+		}
+		client->buffers[data_source].buf_curr = buf_temp;
+		return 0;
+	}
+
+	kfree(buf_temp);
+	buf_temp = NULL;
+	return -EIO;
+}
+
+void diag_dci_wakeup_clients()
+{
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
+
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+
+		/*
+		 * Don't wake up the client when there is no pending buffer to
+		 * write or when it is writing to user space
+		 */
+		if (!list_empty(&entry->list_write_buf) && !entry->in_service) {
+			mutex_lock(&entry->write_buf_mutex);
+			entry->in_service = 1;
+			mutex_unlock(&entry->write_buf_mutex);
+			diag_update_sleeping_process(entry->client->tgid,
+						     DCI_DATA_TYPE);
+		}
+	}
+}
+
+void dci_data_drain_work_fn(struct work_struct *work)
+{
+	int i;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
+	struct diag_dci_buf_peripheral_t *proc_buf = NULL;
+	struct diag_dci_buffer_t *buf_temp = NULL;
+
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		for (i = 0; i < NUM_DCI_PROC; i++) {
+			proc_buf = &entry->buffers[i];
+
+			buf_temp = proc_buf->buf_primary;
+			if (DCI_CAN_ADD_BUF_TO_LIST(buf_temp))
+				dci_add_buffer_to_list(entry, buf_temp);
+
+			buf_temp = proc_buf->buf_cmd;
+			if (DCI_CAN_ADD_BUF_TO_LIST(buf_temp))
+				dci_add_buffer_to_list(entry, buf_temp);
+
+			buf_temp = proc_buf->buf_curr;
+			if (DCI_CAN_ADD_BUF_TO_LIST(buf_temp)) {
+				dci_add_buffer_to_list(entry, buf_temp);
+				mutex_lock(&proc_buf->buf_mutex);
+				proc_buf->buf_curr = NULL;
+				mutex_unlock(&proc_buf->buf_mutex);
+			}
+		}
+		if (!list_empty(&entry->list_write_buf) && !entry->in_service) {
+			mutex_lock(&entry->write_buf_mutex);
+			entry->in_service = 1;
+			mutex_unlock(&entry->write_buf_mutex);
+			diag_update_sleeping_process(entry->client->tgid,
+						     DCI_DATA_TYPE);
+		}
+	}
+	dci_timer_in_progress = 0;
+}
+
+/* Process the data read from apps userspace client */
+void diag_process_apps_dci_read_data(int data_type, void *buf, int recd_bytes)
+{
+	uint8_t cmd_code;
+
+	if (!buf) {
+		pr_err_ratelimited("diag: In %s, Null buf pointer\n", __func__);
+		return;
+	}
+
+	if (data_type != DATA_TYPE_DCI_LOG && data_type != DATA_TYPE_DCI_EVENT
+						&& data_type != DCI_PKT_TYPE) {
+		pr_err("diag: In %s, unsupported data_type: 0x%x\n",
+				__func__, (unsigned int)data_type);
+		return;
+	}
+
+	cmd_code = *(uint8_t *)buf;
+
+	switch (cmd_code) {
+	case LOG_CMD_CODE:
+		extract_dci_log(buf, recd_bytes, APPS_DATA);
+		break;
+	case EVENT_CMD_CODE:
+		extract_dci_events(buf, recd_bytes, APPS_DATA);
+		break;
+	case DCI_PKT_RSP_CODE:
+	case DCI_DELAYED_RSP_CODE:
+		extract_dci_pkt_rsp(buf, recd_bytes, APPS_DATA, NULL);
+		break;
+	default:
+		pr_err("diag: In %s, unsupported command code: 0x%x, not log or event\n",
+		       __func__, cmd_code);
+		return;
+
+	}
+
+	/* wake up all sleeping DCI clients which have some data */
+	diag_dci_wakeup_clients();
+	dci_check_drain_timer();
+}
+
 /* 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)
 {
-	int read_bytes, dci_pkt_len, i;
+	int read_bytes, dci_pkt_len;
 	uint8_t recv_pkt_cmd_code;
 
-	diag_dci_smd_record_info(recd_bytes, (uint8_t)smd_info->type);
+	/*
+	 * Release wakeup source when there are no more clients to
+	 * process DCI data
+	 */
+	if (driver->num_dci_client == 0) {
+		diag_dci_try_deactivate_wakeup_source();
+		return 0;
+	}
+
+	diag_dci_smd_record_info(recd_bytes, (uint8_t)smd_info->type,
+				 (uint8_t)smd_info->peripheral);
 	/* 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);
+
+		/* Check if the length of the current packet is lesser than the
+		 * remaining bytes in the received buffer. This includes space
+		 * for the Start byte (1), Version byte (1), length bytes (2)
+		 * and End byte (1)
+		 */
+		if ((dci_pkt_len+5) > (recd_bytes-read_bytes)) {
+			pr_err("diag: Invalid length in %s, len: %d, dci_pkt_len: %d",
+					__func__, recd_bytes, dci_pkt_len);
+			diag_dci_try_deactivate_wakeup_source();
+			return 0;
+		}
 		/* process one dci packet */
-		pr_debug("diag: bytes read = %d, single dci pkt len = %d\n",
-			read_bytes, dci_pkt_len);
+		pr_debug("diag: dci: peripheral = %d bytes read = %d, single dci pkt len = %d\n",
+			 smd_info->peripheral, 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(smd_info, buf); /* pkt response */
+		if (recv_pkt_cmd_code == LOG_CMD_CODE) {
+			/* Don't include the 4 bytes for command code */
+			extract_dci_log(buf + 4, recd_bytes - 4,
+					smd_info->peripheral);
+		} else if (recv_pkt_cmd_code == EVENT_CMD_CODE) {
+			/* Don't include the 4 bytes for command code */
+			extract_dci_events(buf + 4, recd_bytes - 4,
+					   smd_info->peripheral);
+		} else
+			extract_dci_pkt_rsp(buf + 4, dci_pkt_len,
+					    smd_info->peripheral, smd_info);
 		read_bytes += 5 + dci_pkt_len;
 		buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
 	}
-	/* Release wakeup source when there are no more clients to
-	   process DCI data */
-	if (driver->num_dci_client == 0)
-		diag_dci_try_deactivate_wakeup_source(smd_info->ch);
 
 	/* 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);
+	diag_dci_wakeup_clients();
+	dci_check_drain_timer();
+	diag_dci_try_deactivate_wakeup_source();
+	return 0;
+}
+
+static inline struct diag_dci_client_tbl *__diag_dci_get_client_entry(
+								int client_id)
+{
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		if (entry->client->tgid == client_id)
+			return entry;
+	}
+	return NULL;
+}
+
+static inline int __diag_dci_query_log_mask(struct diag_dci_client_tbl *entry,
+							uint16_t log_code)
+{
+	uint16_t item_num;
+	uint8_t equip_id, *log_mask_ptr, byte_mask;
+	int byte_index, offset;
+
+	if (!entry) {
+		pr_err("diag: In %s, invalid client entry\n", __func__);
+		return 0;
+	}
+
+	equip_id = LOG_GET_EQUIP_ID(log_code);
+	item_num = LOG_GET_ITEM_NUM(log_code);
+	byte_index = item_num/8 + 2;
+	byte_mask = 0x01 << (item_num % 8);
+	offset = equip_id * 514;
+
+	if (offset + byte_index > DCI_LOG_MASK_SIZE) {
+		pr_err("diag: In %s, invalid offset: %d, log_code: %d, byte_index: %d\n",
+				__func__, offset, log_code, byte_index);
+		return 0;
+	}
+
+	log_mask_ptr = entry->dci_log_mask;
+	log_mask_ptr = log_mask_ptr + offset + byte_index;
+	return ((*log_mask_ptr & byte_mask) == byte_mask) ? 1 : 0;
+
+}
+
+static inline int __diag_dci_query_event_mask(struct diag_dci_client_tbl *entry,
+							uint16_t event_id)
+{
+	uint8_t *event_mask_ptr, byte_mask;
+	int byte_index, bit_index;
+
+	if (!entry) {
+		pr_err("diag: In %s, invalid client entry\n", __func__);
+		return 0;
+	}
+
+	byte_index = event_id/8;
+	bit_index = event_id % 8;
+	byte_mask = 0x1 << bit_index;
+
+	if (byte_index > DCI_EVENT_MASK_SIZE) {
+		pr_err("diag: In %s, invalid, event_id: %d, byte_index: %d\n",
+				__func__, event_id, byte_index);
+		return 0;
+	}
+
+	event_mask_ptr = entry->dci_event_mask;
+	event_mask_ptr = event_mask_ptr + byte_index;
+	return ((*event_mask_ptr & byte_mask) == byte_mask) ? 1 : 0;
+}
+
+static int diag_dci_filter_commands(struct diag_pkt_header_t *header)
+{
+	if (!header)
+		return -ENOMEM;
+
+	switch (header->cmd_code) {
+	case 0x7d: /* Msg Mask Configuration */
+	case 0x73: /* Log Mask Configuration */
+	case 0x81: /* Event Mask Configuration */
+	case 0x82: /* Event Mask Change */
+	case 0x60: /* Event Mask Toggle */
+		return 1;
+	}
+
+	if (header->cmd_code == 0x4b && header->subsys_id == 0x12) {
+		switch (header->subsys_cmd_code) {
+		case 0x60: /* Extended Event Mask Config */
+		case 0x61: /* Extended Msg Mask Config */
+		case 0x62: /* Extended Log Mask Config */
+		case 0x20C: /* Set current Preset ID */
+		case 0x20D: /* Get current Preset ID */
+			return 1;
 		}
 	}
 
@@ -204,96 +556,181 @@
 	return 0;
 }
 
-void extract_dci_pkt_rsp(struct diag_smd_info *smd_info, unsigned char *buf)
+void extract_dci_pkt_rsp(unsigned char *buf, int len, int data_source,
+			 struct diag_smd_info *smd_info)
 {
-	int i = 0, cmd_code_len = 1;
-	int curr_client_pid = 0, write_len, *tag = NULL;
-	struct diag_dci_client_tbl *entry;
+	int tag, curr_client_pid = 0;
+	struct diag_dci_client_tbl *entry = NULL;
 	void *temp_buf = NULL;
-	uint8_t recv_pkt_cmd_code, delete_flag = 0;
+	uint8_t dci_cmd_code, cmd_code_len, delete_flag = 0;
+	uint32_t rsp_len = 0;
+	struct diag_dci_buffer_t *rsp_buf = NULL;
 	struct dci_pkt_req_entry_t *req_entry = NULL;
-	recv_pkt_cmd_code = *(uint8_t *)(buf+4);
-	if (recv_pkt_cmd_code != DCI_PKT_RSP_CODE)
-		cmd_code_len = 4; /* delayed response */
-	write_len = (int)(*(uint16_t *)(buf+2)) - cmd_code_len;
-	if (write_len <= 0) {
-		pr_err("diag: Invalid length in %s, write_len: %d",
-					__func__, write_len);
+	unsigned char *temp = buf;
+
+	if (!buf) {
+		pr_err("diag: Invalid pointer in %s\n", __func__);
 		return;
 	}
-	pr_debug("diag: len = %d\n", write_len);
-	tag = (int *)(buf + (4 + cmd_code_len)); /* Retrieve the Tag field */
-	req_entry = diag_dci_get_request_entry(*tag);
+	dci_cmd_code = *(uint8_t *)(temp);
+	if (dci_cmd_code == DCI_PKT_RSP_CODE) {
+		cmd_code_len = sizeof(uint8_t);
+	} else if (dci_cmd_code == DCI_DELAYED_RSP_CODE) {
+		cmd_code_len = sizeof(uint32_t);
+	} else {
+		pr_err("diag: In %s, invalid command code %d\n", __func__,
+								dci_cmd_code);
+		return;
+	}
+	temp += cmd_code_len;
+	tag = *(int *)temp;
+	temp += sizeof(int);
+
+	/*
+	 * The size of the response is (total length) - (length of the command
+	 * code, the tag (int)
+	 */
+	rsp_len = len - (cmd_code_len + sizeof(int));
+	/*
+	 * Check if the length embedded in the packet is correct.
+	 * Include the start (1), version (1), length (2) and the end
+	 * (1) bytes while checking. Total = 5 bytes
+	 */
+	if ((rsp_len == 0) || (rsp_len > (len - 5))) {
+		pr_err("diag: Invalid length in %s, len: %d, rsp_len: %d",
+						__func__, len, rsp_len);
+		return;
+	}
+
+	req_entry = diag_dci_get_request_entry(tag);
 	if (!req_entry) {
-		pr_alert("diag: No matching PID for DCI data\n");
+		pr_err("diag: No matching PID for DCI data\n");
 		return;
 	}
-	*tag = req_entry->uid;
 	curr_client_pid = req_entry->pid;
 
 	/* Remove the headers and send only the response to this function */
-	delete_flag = diag_dci_remove_req_entry(buf + 8 + cmd_code_len,
-						write_len - 4,
-						req_entry);
+	delete_flag = diag_dci_remove_req_entry(temp, rsp_len, req_entry);
 	if (delete_flag < 0)
 		return;
 
-	/* Using PID of client process, find client buffer */
-	i = diag_dci_find_client_index(curr_client_pid);
-	if (i != DCI_CLIENT_INDEX_INVALID) {
-		/* copy pkt rsp in client buf */
-		entry = &(driver->dci_client_tbl[i]);
-		mutex_lock(&entry->data_mutex);
-		/*
-		 * Check if we can fit the data in the rsp buffer. The total
-		 * length of the rsp is the rsp length (write_len) +
-		 * DCI_PKT_RSP_TYPE header (int) + field for length (int) +
-		 * delete_flag (uint8_t)
-		 */
-		if (DCI_CHK_CAPACITY(entry, 9+write_len)) {
-			pr_alert("diag: create capacity for pkt rsp\n");
-			entry->total_capacity += 9+write_len;
-			temp_buf = krealloc(entry->dci_data,
-			entry->total_capacity, GFP_KERNEL);
-			if (!temp_buf) {
-				pr_err("diag: DCI realloc failed\n");
-				mutex_unlock(&entry->data_mutex);
-				return;
-			} else {
-				entry->dci_data = temp_buf;
-			}
-		}
-		*(int *)(entry->dci_data+entry->data_len) =
-					DCI_PKT_RSP_TYPE;
-		entry->data_len += 4;
-		*(int *)(entry->dci_data+entry->data_len)
-						= write_len;
-		entry->data_len += 4;
-		*(uint8_t *)(entry->dci_data + entry->data_len) = delete_flag;
-		entry->data_len += sizeof(uint8_t);
-		memcpy(entry->dci_data+entry->data_len,
-			buf+4+cmd_code_len, write_len);
-		entry->data_len += write_len;
-		mutex_unlock(&entry->data_mutex);
+	entry = __diag_dci_get_client_entry(curr_client_pid);
+	if (!entry) {
+		pr_err("diag: In %s, couldn't find entry\n", __func__);
+		return;
 	}
+
+	rsp_buf = entry->buffers[data_source].buf_cmd;
+
+	mutex_lock(&rsp_buf->data_mutex);
+	/*
+	 * Check if we can fit the data in the rsp buffer. The total length of
+	 * the rsp is the rsp length (write_len) + DCI_PKT_RSP_TYPE header (int)
+	 * + field for length (int) + delete_flag (uint8_t)
+	 */
+	if ((rsp_buf->data_len + 9 + rsp_len) > rsp_buf->capacity) {
+		pr_alert("diag: create capacity for pkt rsp\n");
+		rsp_buf->capacity += 9 + rsp_len;
+		temp_buf = krealloc(rsp_buf->data, rsp_buf->capacity,
+				    GFP_KERNEL);
+		if (!temp_buf) {
+			pr_err("diag: DCI realloc failed\n");
+			mutex_unlock(&rsp_buf->data_mutex);
+			return;
+		} else {
+			rsp_buf->data = temp_buf;
+		}
+	}
+
+	/* Fill in packet response header information */
+	*(int *)(rsp_buf->data + rsp_buf->data_len) = DCI_PKT_RSP_TYPE;
+	rsp_buf->data_len += sizeof(int);
+	/* Packet Length = Response Length + Length of uid field (int) */
+	*(int *)(rsp_buf->data + rsp_buf->data_len) = rsp_len + sizeof(int);
+	rsp_buf->data_len += sizeof(int);
+	*(uint8_t *)(rsp_buf->data + rsp_buf->data_len) = delete_flag;
+	rsp_buf->data_len += sizeof(uint8_t);
+	*(int *)(rsp_buf->data + rsp_buf->data_len) = req_entry->uid;
+	rsp_buf->data_len += sizeof(int);
+	memcpy(rsp_buf->data + rsp_buf->data_len, temp, rsp_len);
+	rsp_buf->data_len += rsp_len;
+	rsp_buf->data_source = data_source;
+	if (smd_info)
+		smd_info->in_busy_1 = 1;
+	mutex_unlock(&rsp_buf->data_mutex);
+
+
+	/*
+	 * Add directly to the list for writing responses to the
+	 * userspace as these shouldn't be buffered and shouldn't wait
+	 * for log and event buffers to be full
+	 */
+	dci_add_buffer_to_list(entry, rsp_buf);
 }
 
-void extract_dci_events(unsigned char *buf)
+static void copy_dci_event(unsigned char *buf, int len,
+			   struct diag_dci_client_tbl *client, int data_source)
+{
+	struct diag_dci_buffer_t *data_buffer = NULL;
+	struct diag_dci_buf_peripheral_t *proc_buf = NULL;
+	int err = 0, total_len = 0;
+
+	if (!buf || !client) {
+		pr_err("diag: Invalid pointers in %s", __func__);
+		return;
+	}
+
+	total_len = sizeof(int) + len;
+
+	proc_buf = &client->buffers[data_source];
+	mutex_lock(&proc_buf->buf_mutex);
+	mutex_lock(&proc_buf->health_mutex);
+	err = diag_dci_get_buffer(client, data_source, total_len);
+	if (err) {
+		if (err == -ENOMEM)
+			proc_buf->health.dropped_events++;
+		else
+			pr_err("diag: In %s, invalid packet\n", __func__);
+		mutex_unlock(&proc_buf->health_mutex);
+		mutex_unlock(&proc_buf->buf_mutex);
+		return;
+	}
+
+	data_buffer = proc_buf->buf_curr;
+
+	proc_buf->health.received_events++;
+	mutex_unlock(&proc_buf->health_mutex);
+	mutex_unlock(&proc_buf->buf_mutex);
+
+	mutex_lock(&data_buffer->data_mutex);
+	*(int *)(data_buffer->data + data_buffer->data_len) = DCI_EVENT_TYPE;
+	data_buffer->data_len += sizeof(int);
+	memcpy(data_buffer->data + data_buffer->data_len, buf, len);
+	data_buffer->data_len += len;
+	data_buffer->data_source = data_source;
+	mutex_unlock(&data_buffer->data_mutex);
+
+}
+
+void extract_dci_events(unsigned char *buf, int len, int data_source)
 {
 	uint16_t event_id, event_id_packet, length, temp_len;
-	uint8_t *event_mask_ptr, byte_mask, payload_len, payload_len_field;
-	uint8_t timestamp[8] = {0}, bit_index, timestamp_len;
-	uint8_t event_data[MAX_EVENT_SIZE];
-	unsigned int byte_index, total_event_len, i;
-	struct diag_dci_client_tbl *entry;
+	uint8_t payload_len, payload_len_field;
+	uint8_t timestamp[8], timestamp_len;
+	unsigned char event_data[MAX_EVENT_SIZE];
+	unsigned int total_event_len;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
 
 	length =  *(uint16_t *)(buf + 1); /* total length of event series */
 	if (length == 0) {
 		pr_err("diag: Incoming dci event length is invalid\n");
 		return;
 	}
-	temp_len = 0;
-	buf = buf + 3; /* start of event series */
+	/* Move directly to the start of the event series. 1 byte for
+	 * event code and 2 bytes for the length field.
+	 */
+	temp_len = 3;
 	while (temp_len < (length - 1)) {
 		event_id_packet = *(uint16_t *)(buf + temp_len);
 		event_id = event_id_packet & 0x0FFF; /* extract 12 bits */
@@ -334,6 +771,22 @@
 			memcpy(event_data + 12, buf + temp_len + 2 +
 						timestamp_len, payload_len);
 		}
+
+		/* Before copying the data to userspace, check if we are still
+		 * within the buffer limit. This is an error case, don't count
+		 * it towards the health statistics.
+		 *
+		 * Here, the offset of 2 bytes(uint16_t) is for the
+		 * event_id_packet length
+		 */
+		temp_len += sizeof(uint16_t) + timestamp_len +
+						payload_len_field + payload_len;
+		if (temp_len > len) {
+			pr_err("diag: Invalid length in %s, len: %d, read: %d",
+						__func__, len, temp_len);
+			return;
+		}
+
 		/* 2 bytes for the event id & timestamp len is hard coded to 8,
 		   as individual events have full timestamp */
 		*(uint16_t *)(event_data) = 10 +
@@ -343,108 +796,114 @@
 		/* 2 bytes for the event length field which is added to
 		   the event data */
 		total_event_len = 2 + 10 + payload_len_field + payload_len;
-		byte_index = event_id / 8;
-		bit_index = event_id % 8;
-		byte_mask = 0x1 << bit_index;
 		/* parse through event mask tbl of each client and check mask */
-		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-			if (driver->dci_client_tbl[i].client) {
-				entry = &(driver->dci_client_tbl[i]);
-				event_mask_ptr = entry->dci_event_mask +
-								 byte_index;
-				mutex_lock(&dci_health_mutex);
-				mutex_lock(&entry->data_mutex);
-				if (*event_mask_ptr & byte_mask) {
-					/* copy to client buffer */
-					if (DCI_CHK_CAPACITY(entry,
-							 4 + total_event_len)) {
-						pr_err("diag: DCI event drop\n");
-						driver->dci_client_tbl[i].
-							dropped_events++;
-						mutex_unlock(
-							&entry->data_mutex);
-						mutex_unlock(
-							&dci_health_mutex);
-						break;
-					}
-					driver->dci_client_tbl[i].
-							received_events++;
-					*(int *)(entry->dci_data+
-					entry->data_len) = DCI_EVENT_TYPE;
-					/* 4 bytes for DCI_EVENT_TYPE */
-					memcpy(entry->dci_data +
-						entry->data_len + 4, event_data
-						, total_event_len);
-					entry->data_len += 4 + total_event_len;
-				}
-				mutex_unlock(&entry->data_mutex);
-				mutex_unlock(&dci_health_mutex);
+		list_for_each_safe(start, temp, &driver->dci_client_list) {
+			entry = list_entry(start, struct diag_dci_client_tbl,
+									track);
+			if (__diag_dci_query_event_mask(entry, event_id)) {
+				/* copy to client buffer */
+				copy_dci_event(event_data, total_event_len,
+					       entry, data_source);
 			}
 		}
-		temp_len += 2 + timestamp_len + payload_len_field + payload_len;
 	}
 }
 
-void extract_dci_log(unsigned char *buf)
+static void copy_dci_log(unsigned char *buf, int len,
+			 struct diag_dci_client_tbl *client, int data_source)
 {
-	uint16_t log_code, item_num, log_length;
-	uint8_t equip_id, *log_mask_ptr, byte_mask;
-	unsigned int i, byte_index, byte_offset = 0;
-	struct diag_dci_client_tbl *entry;
+	uint16_t log_length = 0;
+	struct diag_dci_buffer_t *data_buffer = NULL;
+	struct diag_dci_buf_peripheral_t *proc_buf = NULL;
+	int err = 0, total_len = 0;
+
+	if (!buf || !client) {
+		pr_err("diag: Invalid pointers in %s", __func__);
+		return;
+	}
 
 	log_length = *(uint16_t *)(buf + 2);
-	log_code = *(uint16_t *)(buf + 6);
-	equip_id = LOG_GET_EQUIP_ID(log_code);
-	item_num = LOG_GET_ITEM_NUM(log_code);
-	byte_index = item_num/8 + 2;
-	byte_mask = 0x01 << (item_num % 8);
-
 	if (log_length > USHRT_MAX - 4) {
-		pr_err("diag: Integer overflow in %s, log_len:%d",
+		pr_err("diag: Integer overflow in %s, log_len: %d",
 				__func__, log_length);
 		return;
 	}
-	byte_offset = (equip_id * 514) + byte_index;
-	if (byte_offset >=  DCI_LOG_MASK_SIZE) {
-		pr_err("diag: Invalid byte_offset %d in dci log\n",
-							byte_offset);
+	total_len = sizeof(int) + log_length;
+
+	/* Check if we are within the len. The check should include the
+	 * first 4 bytes for the Log code(2) and the length bytes (2)
+	 */
+	if ((log_length + sizeof(uint16_t) + 2) > len) {
+		pr_err("diag: Invalid length in %s, log_len: %d, len: %d",
+						__func__, log_length, len);
+		return;
+	}
+
+	proc_buf = &client->buffers[data_source];
+	mutex_lock(&proc_buf->buf_mutex);
+	mutex_lock(&proc_buf->health_mutex);
+	err = diag_dci_get_buffer(client, data_source, total_len);
+	if (err) {
+		if (err == -ENOMEM)
+			proc_buf->health.dropped_logs++;
+		else
+			pr_err("diag: In %s, invalid packet\n", __func__);
+		mutex_unlock(&proc_buf->health_mutex);
+		mutex_unlock(&proc_buf->buf_mutex);
+		return;
+	}
+
+	data_buffer = proc_buf->buf_curr;
+	proc_buf->health.received_logs++;
+	mutex_unlock(&proc_buf->health_mutex);
+	mutex_unlock(&proc_buf->buf_mutex);
+
+	mutex_lock(&data_buffer->data_mutex);
+	if (!data_buffer->data) {
+		mutex_unlock(&data_buffer->data_mutex);
+		return;
+	}
+
+	*(int *)(data_buffer->data + data_buffer->data_len) = DCI_LOG_TYPE;
+	data_buffer->data_len += sizeof(int);
+	memcpy(data_buffer->data + data_buffer->data_len, buf + sizeof(int),
+	       log_length);
+	data_buffer->data_len += log_length;
+	data_buffer->data_source = data_source;
+	mutex_unlock(&data_buffer->data_mutex);
+}
+
+void extract_dci_log(unsigned char *buf, int len, int data_source)
+{
+	uint16_t log_code, read_bytes = 0;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
+
+	if (!buf) {
+		pr_err("diag: In %s buffer is NULL\n", __func__);
+		return;
+	}
+
+	/* The first six bytes for the incoming log packet contains
+	 * Command code (2), the length of the packet (2) and the length
+	 * of the log (2)
+	 */
+	log_code = *(uint16_t *)(buf + 6);
+	read_bytes += sizeof(uint16_t) + 6;
+	if (read_bytes > len) {
+		pr_err("diag: Invalid length in %s, len: %d, read: %d",
+						__func__, len, read_bytes);
 		return;
 	}
 
 	/* parse through log mask table of each client and check mask */
-	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-		if (driver->dci_client_tbl[i].client) {
-			entry = &(driver->dci_client_tbl[i]);
-			log_mask_ptr = entry->dci_log_mask;
-			if (!log_mask_ptr)
-				return;
-			log_mask_ptr = log_mask_ptr + byte_offset;
-			mutex_lock(&dci_health_mutex);
-			mutex_lock(&entry->data_mutex);
-			if (*log_mask_ptr & byte_mask) {
-				pr_debug("\t log code %x needed by client %d",
-					 log_code, entry->client->tgid);
-				/* copy to client buffer */
-				if (DCI_CHK_CAPACITY(entry,
-						 4 + *(uint16_t *)(buf + 2))) {
-						pr_err("diag: DCI log drop\n");
-						driver->dci_client_tbl[i].
-								dropped_logs++;
-						mutex_unlock(
-							&entry->data_mutex);
-						mutex_unlock(
-							&dci_health_mutex);
-						return;
-				}
-				driver->dci_client_tbl[i].received_logs++;
-				*(int *)(entry->dci_data+entry->data_len) =
-								DCI_LOG_TYPE;
-				memcpy(entry->dci_data + entry->data_len + 4,
-					    buf + 4, log_length);
-				entry->data_len += 4 + log_length;
-			}
-			mutex_unlock(&entry->data_mutex);
-			mutex_unlock(&dci_health_mutex);
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		if (__diag_dci_query_log_mask(entry, log_code)) {
+			pr_debug("\t log code %x needed by client %d",
+				 log_code, entry->client->tgid);
+			/* copy to client buffer */
+			copy_dci_log(buf, len, entry, data_source);
 		}
 	}
 }
@@ -459,14 +918,10 @@
 	uint8_t *client_log_mask_ptr;
 	uint8_t *log_mask_ptr;
 	int ret;
-	int index = smd_info->peripheral;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
 
-	/* 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->smd_cntl[index].ch)
-		return;
-
+	/* Update apps and peripheral(s) with the dci log and event masks */
 	memset(dirty_bits, 0, 16 * sizeof(uint8_t));
 
 	/*
@@ -474,15 +929,13 @@
 	 * which log entries in the cumulative logs that need
 	 * to be updated on the peripheral.
 	 */
-	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-		if (driver->dci_client_tbl[i].client) {
-			client_log_mask_ptr =
-				driver->dci_client_tbl[i].dci_log_mask;
-			for (j = 0; j < 16; j++) {
-				if (*(client_log_mask_ptr+1))
-					dirty_bits[j] = 1;
-				client_log_mask_ptr += 514;
-			}
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		client_log_mask_ptr = entry->dci_log_mask;
+		for (j = 0; j < 16; j++) {
+			if (*(client_log_mask_ptr+1))
+				dirty_bits[j] = 1;
+			client_log_mask_ptr += 514;
 		}
 	}
 
@@ -497,41 +950,48 @@
 	}
 	mutex_unlock(&dci_log_mask_mutex);
 
-	ret = diag_send_dci_log_mask(&driver->smd_cntl[index]);
+	/* Send updated mask to userspace clients */
+	diag_update_userspace_clients(DCI_LOG_MASKS_TYPE);
+	/* Send updated log mask to peripherals */
+	ret = diag_send_dci_log_mask();
 
-	ret = diag_send_dci_event_mask(&driver->smd_cntl[index]);
+	/* Send updated event mask to userspace clients */
+	diag_update_userspace_clients(DCI_EVENT_MASKS_TYPE);
+	/* Send updated event mask to peripheral */
+	ret = diag_send_dci_event_mask();
 
 	smd_info->notify_context = 0;
 }
 
 void diag_dci_notify_client(int peripheral_mask, int data)
 {
-	int i, stat;
+	int stat;
 	struct siginfo info;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
+
 	memset(&info, 0, sizeof(struct siginfo));
 	info.si_code = SI_QUEUE;
 	info.si_int = (peripheral_mask | data);
 
 	/* Notify the DCI process that the peripheral DCI Channel is up */
-	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-		if (!driver->dci_client_tbl[i].client)
-			continue;
-		if (driver->dci_client_tbl[i].list & peripheral_mask) {
-			info.si_signo = driver->dci_client_tbl[i].signal_type;
-			stat = send_sig_info(
-				driver->dci_client_tbl[i].signal_type,
-				&info, driver->dci_client_tbl[i].client);
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		if (entry->client_info.notification_list & peripheral_mask) {
+			info.si_signo = entry->client_info.signal_type;
+			stat = send_sig_info(entry->client_info.signal_type,
+					     &info, entry->client);
 			if (stat)
 				pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n",
-				info.si_int, stat);
+							info.si_int, stat);
 		}
-	} /* end of loop for all DCI clients */
+	}
 }
 
-static int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
-					 int len, int tag)
+static int diag_send_dci_pkt(struct diag_master_table entry,
+			     unsigned char *buf, int len, int tag)
 {
-	int i, status = 0;
+	int i, status = DIAG_DCI_NO_ERROR;
 	unsigned int read_len = 0;
 
 	/* The first 4 bytes is the uid tag and the next four bytes is
@@ -565,48 +1025,289 @@
 		mutex_unlock(&driver->dci_mutex);
 		return -EIO;
 	}
-
-	for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
-		struct diag_smd_info *smd_info = driver->separate_cmdrsp[i] ?
-					&driver->smd_dci_cmd[i] :
-					&driver->smd_dci[i];
-		if (entry.client_id == smd_info->peripheral) {
-			if (smd_info->ch) {
-				mutex_lock(&smd_info->smd_ch_mutex);
-				smd_write(smd_info->ch,
-					driver->apps_dci_buf, len + 10);
-				mutex_unlock(&smd_info->smd_ch_mutex);
-				status = DIAG_DCI_NO_ERROR;
-			}
-			break;
-		}
+	/* This command is registered locally on the Apps */
+	if (entry.client_id == APPS_DATA) {
+		driver->dci_pkt_length = len + 10;
+		diag_update_pkt_buffer(driver->apps_dci_buf, DCI_PKT_TYPE);
+		diag_update_sleeping_process(entry.process_id, DCI_PKT_TYPE);
+		mutex_unlock(&driver->dci_mutex);
+		return DIAG_DCI_NO_ERROR;
 	}
 
-	if (status != DIAG_DCI_NO_ERROR) {
-		pr_alert("diag: check DCI channel\n");
+	for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
+		if (entry.client_id == i) {
+			status = 1;
+			break;
+		}
+
+	if (status) {
+		status = diag_dci_write_proc(entry.client_id,
+					     DIAG_DATA_TYPE,
+					     driver->apps_dci_buf,
+					     len + 10);
+	} else {
+		pr_err("diag: Cannot send packet to peripheral %d",
+		       entry.client_id);
 		status = DIAG_DCI_SEND_DATA_FAIL;
 	}
 	mutex_unlock(&driver->dci_mutex);
 	return status;
 }
 
+static int diag_dci_process_apps_pkt(struct diag_pkt_header_t *pkt_header,
+				     unsigned char *req_buf, int tag)
+{
+	uint8_t cmd_code, subsys_id, i, goto_download = 0;
+	uint8_t header_len = sizeof(struct diag_dci_pkt_header_t);
+	uint16_t ss_cmd_code;
+	uint32_t write_len = 0;
+	unsigned char *dest_buf = driver->apps_dci_buf;
+	unsigned char *payload_ptr = driver->apps_dci_buf + header_len;
+	struct diag_dci_pkt_header_t dci_header;
+
+	if (!pkt_header || !req_buf || tag < 0)
+		return -EIO;
+
+	cmd_code = pkt_header->cmd_code;
+	subsys_id = pkt_header->subsys_id;
+	ss_cmd_code = pkt_header->subsys_cmd_code;
+
+	if (cmd_code == DIAG_CMD_DOWNLOAD) {
+		*payload_ptr = DIAG_CMD_DOWNLOAD;
+		write_len = sizeof(uint8_t);
+		goto_download = 1;
+		goto fill_buffer;
+	} else if (cmd_code == DIAG_CMD_VERSION) {
+		if (chk_polling_response()) {
+			for (i = 0; i < 55; i++, write_len++, payload_ptr++)
+				*(payload_ptr) = 0;
+			goto fill_buffer;
+		}
+	} else if (cmd_code == DIAG_CMD_EXT_BUILD) {
+		if (chk_polling_response()) {
+			*payload_ptr = DIAG_CMD_EXT_BUILD;
+			write_len = sizeof(uint8_t);
+			payload_ptr += sizeof(uint8_t);
+			for (i = 0; i < 8; i++, write_len++, payload_ptr++)
+				*(payload_ptr) = 0;
+			*(int *)(payload_ptr) = chk_config_get_id();
+			write_len += sizeof(int);
+			goto fill_buffer;
+		}
+	} else if (cmd_code == DIAG_CMD_LOG_ON_DMND) {
+		if (driver->log_on_demand_support) {
+			*payload_ptr = DIAG_CMD_LOG_ON_DMND;
+			write_len = sizeof(uint8_t);
+			payload_ptr += sizeof(uint8_t);
+			*(uint16_t *)(payload_ptr) = *(uint16_t *)(req_buf + 1);
+			write_len += sizeof(uint16_t);
+			payload_ptr += sizeof(uint16_t);
+			*payload_ptr = 0x1; /* Unknown */
+			write_len += sizeof(uint8_t);
+			goto fill_buffer;
+		}
+	} else if (cmd_code != DIAG_CMD_DIAG_SUBSYS) {
+		return DIAG_DCI_TABLE_ERR;
+	}
+
+	if (subsys_id == DIAG_SS_DIAG) {
+		if (ss_cmd_code == DIAG_DIAG_MAX_PKT_SZ) {
+			memcpy(payload_ptr, pkt_header,
+					sizeof(struct diag_pkt_header_t));
+			write_len = sizeof(struct diag_pkt_header_t);
+			*(uint32_t *)(payload_ptr + write_len) = PKT_SIZE;
+			write_len += sizeof(uint32_t);
+		} else if (ss_cmd_code == DIAG_DIAG_STM) {
+			write_len = diag_process_stm_cmd(req_buf, payload_ptr);
+		}
+	} else if (subsys_id == DIAG_SS_PARAMS) {
+		if (ss_cmd_code == DIAG_DIAG_POLL) {
+			if (chk_polling_response()) {
+				memcpy(payload_ptr, pkt_header,
+					sizeof(struct diag_pkt_header_t));
+				write_len = sizeof(struct diag_pkt_header_t);
+				payload_ptr += write_len;
+				for (i = 0; i < 12; i++, write_len++) {
+					*(payload_ptr) = 0;
+					payload_ptr++;
+				}
+			}
+		} else if (ss_cmd_code == DIAG_DEL_RSP_WRAP) {
+			memcpy(payload_ptr, pkt_header,
+					sizeof(struct diag_pkt_header_t));
+			write_len = sizeof(struct diag_pkt_header_t);
+			*(int *)(payload_ptr + write_len) = wrap_enabled;
+			write_len += sizeof(int);
+		} else if (ss_cmd_code == DIAG_DEL_RSP_WRAP_CNT) {
+			wrap_enabled = true;
+			memcpy(payload_ptr, pkt_header,
+					sizeof(struct diag_pkt_header_t));
+			write_len = sizeof(struct diag_pkt_header_t);
+			*(uint16_t *)(payload_ptr + write_len) = wrap_count;
+			write_len += sizeof(uint16_t);
+		}
+	}
+
+fill_buffer:
+	if (write_len > 0) {
+		/* Check if we are within the range of the buffer*/
+		if (write_len + header_len > PKT_SIZE) {
+			pr_err("diag: In %s, invalid length %d\n", __func__,
+						write_len + header_len);
+			return -ENOMEM;
+		}
+		dci_header.start = CONTROL_CHAR;
+		dci_header.version = 1;
+		/*
+		 * Length of the rsp pkt = actual data len + pkt rsp code
+		 * (uint8_t) + tag (int)
+		 */
+		dci_header.len = write_len + sizeof(uint8_t) + sizeof(int);
+		dci_header.pkt_code = DCI_PKT_RSP_CODE;
+		dci_header.tag = tag;
+		driver->in_busy_dcipktdata = 1;
+		memcpy(dest_buf, &dci_header, header_len);
+		diag_process_apps_dci_read_data(DCI_PKT_TYPE, dest_buf + 4,
+						dci_header.len);
+		driver->in_busy_dcipktdata = 0;
+
+		if (goto_download) {
+			/*
+			 * Sleep for sometime so that the response reaches the
+			 * client. The value 5000 empirically as an optimum
+			 * time for the response to reach the client.
+			 */
+			usleep_range(5000, 5100);
+			/* call download API */
+			msm_set_restart_mode(RESTART_DLOAD);
+			pr_alert("diag: download mode set, Rebooting SoC..\n");
+			kernel_restart(NULL);
+		}
+		return DIAG_DCI_NO_ERROR;
+	}
+
+	return DIAG_DCI_TABLE_ERR;
+}
+
+static int diag_process_dci_pkt_rsp(unsigned char *buf, int len)
+{
+	int req_uid, ret = DIAG_DCI_TABLE_ERR, i;
+	struct diag_pkt_header_t *header = NULL;
+	unsigned char *temp = buf;
+	unsigned char *req_buf = NULL;
+	uint8_t retry_count = 0, max_retries = 3, found = 0;
+	uint32_t read_len = 0;
+	struct diag_master_table entry;
+	struct dci_pkt_req_entry_t *req_entry = NULL;
+
+	if (!buf)
+		return -EIO;
+
+	if (len < DCI_PKT_REQ_MIN_LEN || len > USER_SPACE_DATA) {
+		pr_err("diag: dci: Invalid length %d len in %s", len, __func__);
+		return -EIO;
+	}
+
+	req_uid = *(int *)temp; /* UID of the request */
+	temp += sizeof(int);
+	req_buf = temp; /* Start of the Request */
+	header = (struct diag_pkt_header_t *)temp;
+	temp += sizeof(struct diag_pkt_header_t);
+	read_len = sizeof(int) + sizeof(struct diag_pkt_header_t);
+	if (read_len >= USER_SPACE_DATA) {
+		pr_err("diag: dci: Invalid length in %s\n", __func__);
+		return -EIO;
+	}
+
+	/* Check if the command is allowed on DCI */
+	if (diag_dci_filter_commands(header)) {
+		pr_debug("diag: command not supported %d %d %d",
+			 header->cmd_code, header->subsys_id,
+			 header->subsys_cmd_code);
+		return DIAG_DCI_SEND_DATA_FAIL;
+	}
+
+	/*
+	 * Previous packet is yet to be consumed by the client. Wait
+	 * till the buffer is free.
+	 */
+	while (retry_count < max_retries) {
+		retry_count++;
+		if (driver->in_busy_dcipktdata)
+			usleep_range(10000, 10100);
+		else
+			break;
+	}
+	/* The buffer is still busy */
+	if (driver->in_busy_dcipktdata) {
+		pr_err("diag: In %s, apps dci buffer is still busy. Dropping packet\n",
+								__func__);
+		return -EAGAIN;
+	}
+
+	/* Register this new DCI packet */
+	req_entry = diag_register_dci_transaction(req_uid);
+	if (!req_entry) {
+		pr_alert("diag: registering new DCI transaction failed\n");
+		return DIAG_DCI_NO_REG;
+	}
+
+	/* Check if it is a dedicated Apps command */
+	ret = diag_dci_process_apps_pkt(header, req_buf, req_entry->tag);
+	if (ret == DIAG_DCI_NO_ERROR || ret < 0)
+		return ret;
+
+	/* Check the registration table for command entries */
+	for (i = 0; i < diag_max_reg && !found; i++) {
+		entry = driver->table[i];
+		if (entry.process_id == NO_PROCESS)
+			continue;
+		if (entry.cmd_code == header->cmd_code &&
+			    entry.subsys_id == header->subsys_id &&
+			    entry.cmd_code_lo <= header->subsys_cmd_code &&
+			    entry.cmd_code_hi >= header->subsys_cmd_code) {
+			ret = diag_send_dci_pkt(entry, buf, len,
+						req_entry->tag);
+			found = 1;
+		} else if (entry.cmd_code == 255 && header->cmd_code == 75) {
+			if (entry.subsys_id == header->subsys_id &&
+			    entry.cmd_code_lo <= header->subsys_cmd_code &&
+			    entry.cmd_code_hi >= header->subsys_cmd_code) {
+				ret = diag_send_dci_pkt(entry, buf, len,
+							req_entry->tag);
+				found = 1;
+			}
+		} else if (entry.cmd_code == 255 && entry.subsys_id == 255) {
+			if (entry.cmd_code_lo <= header->cmd_code &&
+			    entry.cmd_code_hi >= header->cmd_code) {
+				/*
+				 * If its a Mode reset command, make sure it is
+				 * registered on the Apps Processor
+				 */
+				if (entry.cmd_code_lo == MODE_CMD &&
+				    entry.cmd_code_hi == MODE_CMD)
+					if (entry.client_id != APPS_DATA)
+						continue;
+					ret = diag_send_dci_pkt(entry, buf, len,
+								req_entry->tag);
+					found = 1;
+			}
+		}
+	}
+
+	return ret;
+}
+
 int diag_process_dci_transaction(unsigned char *buf, int len)
 {
 	unsigned char *temp = buf;
-	uint16_t subsys_cmd_code, log_code, item_num;
-	int subsys_id, cmd_code, ret = -1, found = 0;
-	struct diag_master_table entry;
-	int count, set_mask, num_codes, bit_index, event_id, offset = 0, i;
+	uint16_t log_code, item_num;
+	int ret = -1, found = 0;
+	int count, set_mask, num_codes, bit_index, event_id, offset = 0;
 	unsigned int byte_index, read_len = 0;
 	uint8_t equip_id, *log_mask_ptr, *head_log_mask_ptr, byte_mask;
 	uint8_t *event_mask_ptr;
-	struct dci_pkt_req_entry_t *req_entry = NULL;
-
-	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;
-	}
+	struct diag_dci_client_tbl *dci_entry = NULL;
 
 	if (!temp) {
 		pr_err("diag: Invalid buffer in %s\n", __func__);
@@ -615,68 +1316,7 @@
 
 	/* This is Pkt request/response transaction */
 	if (*(int *)temp > 0) {
-		if (len < DCI_PKT_REQ_MIN_LEN || len > USER_SPACE_DATA) {
-			pr_err("diag: dci: Invalid length %d len in %s", len,
-								__func__);
-			return -EIO;
-		}
-		/* enter this UID into kernel table and return index */
-		req_entry = diag_register_dci_transaction(*(int *)temp);
-		if (!req_entry) {
-			pr_alert("diag: registering new DCI transaction failed\n");
-			return DIAG_DCI_NO_REG;
-		}
-		temp += sizeof(int);
-		/*
-		 * Check for registered peripheral and fwd pkt to
-		 * appropriate proc
-		 */
-		cmd_code = (int)(*(char *)temp);
-		temp++;
-		subsys_id = (int)(*(char *)temp);
-		temp++;
-		subsys_cmd_code = *(uint16_t *)temp;
-		temp += sizeof(uint16_t);
-		read_len += sizeof(int) + 2 + sizeof(uint16_t);
-		if (read_len >= USER_SPACE_DATA) {
-			pr_err("diag: dci: Invalid length in %s\n", __func__);
-			return -EIO;
-		}
-		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,
-								req_entry->tag);
-				} 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,
-							req_entry->tag);
-					}
-				} 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,
-							req_entry->tag);
-					}
-				}
-			}
-		}
+		return diag_process_dci_pkt_rsp(buf, len);
 	} else if (*(int *)temp == DCI_LOG_TYPE) {
 		/* Minimum length of a log mask config is 12 + 2 bytes for
 		   atleast one log code to be set or reset */
@@ -684,12 +1324,13 @@
 			pr_err("diag: dci: Invalid length in %s\n", __func__);
 			return -EIO;
 		}
-		/* find client id and table */
-		i = diag_dci_find_client_index(current->tgid);
-		if (i == DCI_CLIENT_INDEX_INVALID) {
-			pr_err("diag: dci client not registered/found\n");
+		/* find client table entry */
+		dci_entry = diag_dci_get_client_entry();
+		if (!dci_entry) {
+			pr_err("diag: In %s, invalid client\n", __func__);
 			return ret;
 		}
+
 		/* Extract each log code and put in client table */
 		temp += sizeof(int);
 		read_len += sizeof(int);
@@ -706,7 +1347,7 @@
 			return -EIO;
 		}
 
-		head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
+		head_log_mask_ptr = dci_entry->dci_log_mask;
 		if (!head_log_mask_ptr) {
 			pr_err("diag: dci: Invalid Log mask pointer in %s\n",
 								__func__);
@@ -768,8 +1409,10 @@
 			count++;
 			ret = DIAG_DCI_NO_ERROR;
 		}
+		/* send updated mask to userspace clients */
+		diag_update_userspace_clients(DCI_LOG_MASKS_TYPE);
 		/* send updated mask to peripherals */
-		ret = diag_send_dci_log_mask(&driver->smd_cntl[MODEM_DATA]);
+		ret = diag_send_dci_log_mask();
 	} else if (*(int *)temp == DCI_EVENT_TYPE) {
 		/* Minimum length of a event mask config is 12 + 4 bytes for
 		  atleast one event id to be set or reset. */
@@ -777,10 +1420,10 @@
 			pr_err("diag: dci: Invalid length in %s\n", __func__);
 			return -EIO;
 		}
-		/* find client id and table */
-		i = diag_dci_find_client_index(current->tgid);
-		if (i == DCI_CLIENT_INDEX_INVALID) {
-			pr_err("diag: dci client not registered/found\n");
+		/* find client table entry */
+		dci_entry = diag_dci_get_client_entry();
+		if (!dci_entry) {
+			pr_err("diag: In %s, invalid client\n", __func__);
 			return ret;
 		}
 		/* Extract each log code and put in client table */
@@ -802,7 +1445,7 @@
 			return -EIO;
 		}
 
-		event_mask_ptr = driver->dci_client_tbl[i].dci_event_mask;
+		event_mask_ptr = dci_entry->dci_event_mask;
 		if (!event_mask_ptr) {
 			pr_err("diag: dci: Invalid event mask pointer in %s\n",
 								__func__);
@@ -839,58 +1482,35 @@
 			count++;
 			ret = DIAG_DCI_NO_ERROR;
 		}
+		/* send updated mask to userspace clients */
+		diag_update_userspace_clients(DCI_EVENT_MASKS_TYPE);
 		/* send updated mask to peripherals */
-		ret = diag_send_dci_event_mask(&driver->smd_cntl[MODEM_DATA]);
+		ret = diag_send_dci_event_mask();
 	} else {
 		pr_alert("diag: Incorrect DCI transaction\n");
 	}
 	return ret;
 }
 
-int diag_dci_find_client_index_health(int client_id)
+
+struct diag_dci_client_tbl *diag_dci_get_client_entry()
 {
-	int i, ret = DCI_CLIENT_INDEX_INVALID;
-
-	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-		if (driver->dci_client_tbl[i].client != NULL) {
-			if (driver->dci_client_tbl[i].client_id ==
-					client_id) {
-				ret = i;
-				break;
-			}
-		}
-	}
-	return ret;
-}
-
-int diag_dci_find_client_index(int client_id)
-{
-	int i, ret = DCI_CLIENT_INDEX_INVALID;
-
-	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-		if (driver->dci_client_tbl[i].client != NULL) {
-			if (driver->dci_client_tbl[i].client->tgid ==
-					client_id) {
-				ret = i;
-				break;
-			}
-		}
-	}
-	return ret;
+	return __diag_dci_get_client_entry(current->tgid);
 }
 
 void update_dci_cumulative_event_mask(int offset, uint8_t byte_mask)
 {
-	int i;
 	uint8_t *event_mask_ptr;
 	uint8_t *update_ptr = dci_cumulative_event_mask;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
 	bool is_set = false;
 
 	mutex_lock(&dci_event_mask_mutex);
 	update_ptr += offset;
-	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-		event_mask_ptr =
-			driver->dci_client_tbl[i].dci_event_mask;
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		event_mask_ptr = entry->dci_event_mask;
 		event_mask_ptr += offset;
 		if ((*event_mask_ptr & byte_mask) == byte_mask) {
 			is_set = true;
@@ -905,75 +1525,29 @@
 	mutex_unlock(&dci_event_mask_mutex);
 }
 
-void clear_client_dci_cumulative_event_mask(int client_index)
+void diag_dci_invalidate_cumulative_event_mask()
 {
-	int i, j;
-	uint8_t *update_ptr = dci_cumulative_event_mask;
-	uint8_t *event_mask_ptr, *client_event_mask_ptr, byte_mask = 0;
-	bool is_set = false;
-
-	event_mask_ptr =
-		(driver->dci_client_tbl[client_index].dci_event_mask);
+	int i = 0;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
+	uint8_t *update_ptr, *event_mask_ptr;
+	update_ptr = dci_cumulative_event_mask;
 
 	mutex_lock(&dci_event_mask_mutex);
-	for (i = 0; i < DCI_EVENT_MASK_SIZE; i++) {
-		is_set = false;
-		/* Already cleared event masks need not to be considered */
-		if (*event_mask_ptr != 0) {
-			byte_mask = *event_mask_ptr;
-		} else {
-			update_ptr++;
-			event_mask_ptr++;
-			continue;
-		}
-		for (j = 0; j < MAX_DCI_CLIENTS; j++) {
-			/* continue searching for valid client */
-			if (driver->dci_client_tbl[j].client == NULL ||
-				client_index == j)
-				continue;
-			client_event_mask_ptr =
-				(driver->dci_client_tbl[j].dci_event_mask);
-			client_event_mask_ptr += i;
-			if (*client_event_mask_ptr & byte_mask) {
-				/*
-				* Break if another client has same
-				* event mask set
-				*/
-				if ((*client_event_mask_ptr &
-					byte_mask) == byte_mask) {
-					is_set = true;
-					break;
-				} else {
-					byte_mask =
-					(~(*client_event_mask_ptr) &
-					byte_mask);
-					is_set = false;
-				}
-			}
-		}
-		/*
-		* Clear only if this client has event mask set else
-		* don't update cumulative event mask ptr
-		*/
-		if (is_set == false)
-			*update_ptr &= ~byte_mask;
-
-		update_ptr++;
-		event_mask_ptr++;
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		event_mask_ptr = entry->dci_event_mask;
+		for (i = 0; i < DCI_EVENT_MASK_SIZE; i++)
+			*(update_ptr+i) |= *(event_mask_ptr+i);
 	}
-	event_mask_ptr =
-		(driver->dci_client_tbl[client_index].dci_event_mask);
-	memset(event_mask_ptr, 0, DCI_EVENT_MASK_SIZE);
 	mutex_unlock(&dci_event_mask_mutex);
 }
 
-
-int diag_send_dci_event_mask(struct diag_smd_info *smd_info)
+int diag_send_dci_event_mask()
 {
 	void *buf = driver->buf_event_mask_update;
 	int header_size = sizeof(struct diag_ctrl_event_mask);
-	int wr_size = -ENOMEM, retry_count = 0, timer;
-	int ret = DIAG_DCI_NO_ERROR, i;
+	int ret = DIAG_DCI_NO_ERROR, err = DIAG_DCI_NO_ERROR, i;
 
 	mutex_lock(&driver->diag_cntl_mutex);
 	/* send event mask update */
@@ -991,28 +1565,18 @@
 	}
 	memcpy(buf, driver->event_mask, header_size);
 	memcpy(buf+header_size, dci_cumulative_event_mask, DCI_EVENT_MASK_SIZE);
-	if (smd_info && smd_info->ch) {
-		while (retry_count < 3) {
-			mutex_lock(&smd_info->smd_ch_mutex);
-			wr_size = smd_write(smd_info->ch, buf,
-					 header_size + DCI_EVENT_MASK_SIZE);
-			mutex_unlock(&smd_info->smd_ch_mutex);
-			if (wr_size == -ENOMEM) {
-				retry_count++;
-				for (timer = 0; timer < 5; timer++)
-					udelay(2000);
-			} else {
-				break;
-			}
-		}
-		if (wr_size != header_size + DCI_EVENT_MASK_SIZE) {
-			pr_err("diag: error writing dci event mask %d, tried %d\n",
-				 wr_size, header_size + DCI_EVENT_MASK_SIZE);
+	for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
+		/*
+		 * Don't send to peripheral if its regular channel
+		 * is down. It may also mean that the peripheral doesn't
+		 * support DCI.
+		 */
+		if (!driver->smd_dci[i].ch)
+			continue;
+		err = diag_dci_write_proc(i, DIAG_CNTL_TYPE, buf,
+					  header_size + DCI_EVENT_MASK_SIZE);
+		if (err != DIAG_DCI_NO_ERROR)
 			ret = DIAG_DCI_SEND_DATA_FAIL;
-		}
-	} else {
-		pr_err("diag: ch not valid for dci event mask update\n");
-		ret = DIAG_DCI_SEND_DATA_FAIL;
 	}
 	mutex_unlock(&driver->diag_cntl_mutex);
 
@@ -1026,6 +1590,8 @@
 	uint8_t *update_ptr = dci_cumulative_log_mask;
 	uint8_t *log_mask_ptr;
 	bool is_set = false;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
 
 	mutex_lock(&dci_log_mask_mutex);
 	*update_ptr = 0;
@@ -1037,9 +1603,9 @@
 	/* update the dirty bit */
 	*(update_ptr+1) = 1;
 	update_ptr = update_ptr + byte_index;
-	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-		log_mask_ptr =
-			(driver->dci_client_tbl[i].dci_log_mask);
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		log_mask_ptr = entry->dci_log_mask;
 		log_mask_ptr = log_mask_ptr + offset + byte_index;
 		if ((*log_mask_ptr & byte_mask) == byte_mask) {
 			is_set = true;
@@ -1055,103 +1621,35 @@
 	mutex_unlock(&dci_log_mask_mutex);
 }
 
-void clear_client_dci_cumulative_log_mask(int client_index)
+void diag_dci_invalidate_cumulative_log_mask()
 {
-	int i, j, k;
-	uint8_t *update_ptr = dci_cumulative_log_mask;
-	uint8_t *log_mask_ptr, *client_log_mask_ptr, byte_mask = 0;
-	bool is_set = false;
-
-	log_mask_ptr = driver->dci_client_tbl[client_index].dci_log_mask;
+	int i = 0;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
+	uint8_t *update_ptr, *log_mask_ptr;
+	update_ptr = dci_cumulative_log_mask;
 
 	mutex_lock(&dci_log_mask_mutex);
-	*update_ptr = 0;
-	/* set the equipment IDs */
-	for (i = 0; i < 16; i++)
-		*(update_ptr + (i*514)) = i;
-
-	/* update cumulative log mask ptr*/
-	update_ptr += 2;
-	log_mask_ptr += 2;
-	for (i = 0; i < 16; i++) {
-		for (j = 0; j < 512; j++) {
-			is_set = false;
-			/*
-			* Already cleared log masks need
-			* not to be considered
-			*/
-			if (*log_mask_ptr != 0) {
-				byte_mask = *log_mask_ptr;
-			} else {
-				update_ptr++;
-				log_mask_ptr++;
-				continue;
-			}
-			for (k = 0; k < MAX_DCI_CLIENTS; k++) {
-				/* continue searching for valid client */
-				if (driver->dci_client_tbl[k].client == NULL ||
-					client_index == k)
-					continue;
-				client_log_mask_ptr =
-				 (driver->dci_client_tbl[k].dci_log_mask);
-				client_log_mask_ptr += (i*514) + 2 + j;
-				if (*client_log_mask_ptr & byte_mask) {
-					/*
-					* Break if another client has same
-					* log mask set
-					*/
-					if ((*client_log_mask_ptr &
-						byte_mask) == byte_mask) {
-						is_set = true;
-						break;
-					} else {
-						byte_mask =
-						 (~(*client_log_mask_ptr) &
-						 byte_mask);
-						is_set = false;
-					}
-				}
-			}
-			/*
-			* Clear only if this client has log mask set else
-			* don't update cumulative log mask ptr
-			*/
-			if (is_set == false) {
-				/*
-				* Update the dirty bit for the equipment
-				* whose mask is changing
-				*/
-				dci_cumulative_log_mask[1+(i*514)] = 1;
-				*update_ptr &= ~byte_mask;
-			}
-
-			update_ptr++;
-			log_mask_ptr++;
-		}
-		update_ptr += 2;
-		log_mask_ptr += 2;
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		log_mask_ptr = entry->dci_log_mask;
+		for (i = 0; i < DCI_LOG_MASK_SIZE; i++)
+			*(update_ptr+i) |= *(log_mask_ptr+i);
 	}
-	log_mask_ptr = driver->dci_client_tbl[client_index].dci_log_mask;
-	memset(log_mask_ptr, 0, DCI_LOG_MASK_SIZE);
 	mutex_unlock(&dci_log_mask_mutex);
 }
 
-int diag_send_dci_log_mask(struct diag_smd_info *smd_info)
+int diag_send_dci_log_mask()
 {
 	void *buf = driver->buf_log_mask_update;
 	int header_size = sizeof(struct diag_ctrl_log_mask);
 	uint8_t *log_mask_ptr = dci_cumulative_log_mask;
-	int i, wr_size = -ENOMEM, retry_count = 0, timer;
-	int ret = DIAG_DCI_NO_ERROR;
-
-	if (!smd_info || !smd_info->ch) {
-		pr_err("diag: ch not valid for dci log mask update\n");
-		return DIAG_DCI_SEND_DATA_FAIL;
-	}
+	int i, j, ret = DIAG_DCI_NO_ERROR, err = DIAG_DCI_NO_ERROR;
+	int updated;
 
 	mutex_lock(&driver->diag_cntl_mutex);
 	for (i = 0; i < 16; i++) {
-		retry_count = 0;
+		updated = 1;
 		driver->log_mask->cmd_type = DIAG_CTRL_MSG_LOG_MASK;
 		driver->log_mask->num_items = 512;
 		driver->log_mask->data_len  = 11 + 512;
@@ -1162,31 +1660,26 @@
 		memcpy(buf, driver->log_mask, header_size);
 		memcpy(buf+header_size, log_mask_ptr+2, 512);
 		/* if dirty byte is set and channel is valid */
-		if (smd_info->ch && *(log_mask_ptr+1)) {
-			while (retry_count < 3) {
-				mutex_lock(&smd_info->smd_ch_mutex);
-				wr_size = smd_write(smd_info->ch, buf,
-							header_size + 512);
-				mutex_unlock(&smd_info->smd_ch_mutex);
-				if (wr_size == -ENOMEM) {
-					retry_count++;
-					for (timer = 0; timer < 5; timer++)
-						udelay(2000);
-				} else
-					break;
-			}
-			if (wr_size != header_size + 512) {
-				pr_err("diag: dci log mask update failed %d, tried %d for equip_id %d\n",
-					wr_size, header_size + 512,
-					driver->log_mask->equip_id);
-				ret = DIAG_DCI_SEND_DATA_FAIL;
+		for (j = 0; j < NUM_SMD_DCI_CHANNELS; j++) {
+			/*
+			 * Don't send to peripheral if its regular channel
+			 * is down. It may also mean that the peripheral
+			 * doesn't support DCI.
+			 */
+			if (!driver->smd_dci[j].ch)
+				continue;
 
-			} else {
-				*(log_mask_ptr+1) = 0; /* clear dirty byte */
-				pr_debug("diag: updated dci log equip ID %d\n",
-						 *log_mask_ptr);
+			if (!(*(log_mask_ptr+1)))
+				continue;
+			err = diag_dci_write_proc(j, DIAG_CNTL_TYPE, buf,
+				header_size + DCI_MAX_ITEMS_PER_LOG_CODE);
+			if (err != DIAG_DCI_NO_ERROR) {
+				updated = 0;
+				ret = DIAG_DCI_SEND_DATA_FAIL;
 			}
 		}
+		if (updated)
+			*(log_mask_ptr+1) = 0; /* clear dirty byte */
 		log_mask_ptr += 514;
 	}
 	mutex_unlock(&driver->diag_cntl_mutex);
@@ -1198,6 +1691,9 @@
 {
 	uint8_t i; int count = 0;
 
+	if (!tbl_buf)
+		return;
+
 	/* create hard coded table for log mask with 16 categories */
 	for (i = 0; i < 16; i++) {
 		*(uint8_t *)tbl_buf = i;
@@ -1220,17 +1716,15 @@
 
 	if (pdev->id == SMD_APPS_MODEM) {
 		index = MODEM_DATA;
-		err = smd_open("DIAG_2",
+		err = smd_named_open_on_edge("DIAG_2",
+			SMD_APPS_MODEM,
 			&driver->smd_dci[index].ch,
 			&driver->smd_dci[index],
 			diag_smd_notify);
 		driver->smd_dci[index].ch_save =
 			driver->smd_dci[index].ch;
-		driver->dci_device = &pdev->dev;
-		driver->dci_device->power.wakeup = wakeup_source_register
-							("DIAG_DCI_WS");
 		if (err)
-			pr_err("diag: In %s, cannot open DCI port, Id = %d, err: %d\n",
+			pr_err("diag: In %s, cannot open DCI Modem port, Id = %d, err: %d\n",
 				__func__, pdev->id, err);
 	}
 
@@ -1251,11 +1745,8 @@
 			diag_smd_notify);
 		driver->smd_dci_cmd[index].ch_save =
 			driver->smd_dci_cmd[index].ch;
-		driver->dci_cmd_device = &pdev->dev;
-		driver->dci_cmd_device->power.wakeup = wakeup_source_register
-							("DIAG_DCI_CMD_WS");
 		if (err)
-			pr_err("diag: In %s, cannot open DCI port, Id = %d, err: %d\n",
+			pr_err("diag: In %s, cannot open DCI Modem CMD port, Id = %d, err: %d\n",
 				__func__, pdev->id, err);
 	}
 
@@ -1305,8 +1796,6 @@
 	driver->dci_tag = 0;
 	driver->dci_client_id = 0;
 	driver->num_dci_client = 0;
-	driver->dci_device = NULL;
-	driver->dci_cmd_device = NULL;
 	mutex_init(&driver->dci_mutex);
 	mutex_init(&dci_log_mask_mutex);
 	mutex_init(&dci_event_mask_mutex);
@@ -1328,19 +1817,17 @@
 				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;
 	}
-	if (driver->dci_client_tbl == NULL) {
-		driver->dci_client_tbl = kzalloc(MAX_DCI_CLIENTS *
-			sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
-		if (driver->dci_client_tbl == NULL)
-			goto err;
-	}
-	driver->diag_dci_wq = create_singlethread_workqueue("diag_dci_wq");
+	INIT_LIST_HEAD(&driver->dci_client_list);
 	INIT_LIST_HEAD(&driver->dci_req_list);
+
+	driver->diag_dci_wq = create_singlethread_workqueue("diag_dci_wq");
+	INIT_WORK(&dci_data_drain_work, dci_data_drain_work_fn);
 	success = platform_driver_register(&msm_diag_dci_driver);
 	if (success) {
 		pr_err("diag: Could not register DCI driver\n");
@@ -1353,10 +1840,10 @@
 			goto err;
 		}
 	}
+	setup_timer(&dci_drain_timer, dci_drain_data, 0);
 	return DIAG_DCI_NO_ERROR;
 err:
 	pr_err("diag: Could not initialize diag DCI buffers");
-	kfree(driver->dci_client_tbl);
 	kfree(driver->apps_dci_buf);
 	for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
 		diag_smd_destructor(&driver->smd_dci[i]);
@@ -1383,21 +1870,12 @@
 
 	platform_driver_unregister(&msm_diag_dci_driver);
 
-	if (driver->dci_client_tbl) {
-		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-			kfree(driver->dci_client_tbl[i].dci_data);
-			mutex_destroy(&driver->dci_client_tbl[i].data_mutex);
-		}
-	}
-
 	if (driver->supports_separate_cmdrsp) {
 		for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++)
 			diag_smd_destructor(&driver->smd_dci_cmd[i]);
 
 		platform_driver_unregister(&msm_diag_dci_cmd_driver);
 	}
-
-	kfree(driver->dci_client_tbl);
 	kfree(driver->apps_dci_buf);
 	mutex_destroy(&driver->dci_mutex);
 	mutex_destroy(&dci_log_mask_mutex);
@@ -1408,153 +1886,483 @@
 
 int diag_dci_clear_log_mask()
 {
-	int i, j, k, err = DIAG_DCI_NO_ERROR;
+	int j, k, err = DIAG_DCI_NO_ERROR;
 	uint8_t *log_mask_ptr, *update_ptr;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
 
-	i = diag_dci_find_client_index(current->tgid);
-	if (i == DCI_CLIENT_INDEX_INVALID)
+	entry = diag_dci_get_client_entry();
+	if (!entry) {
+		pr_err("diag: In %s, invalid client entry\n", __func__);
 		return DIAG_DCI_TABLE_ERR;
+	}
 
 	mutex_lock(&dci_log_mask_mutex);
-	create_dci_log_mask_tbl(
-			driver->dci_client_tbl[i].dci_log_mask);
-	memset(dci_cumulative_log_mask,
-				0x0, DCI_LOG_MASK_SIZE);
-	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+	create_dci_log_mask_tbl(entry->dci_log_mask);
+	memset(dci_cumulative_log_mask, 0x0, DCI_LOG_MASK_SIZE);
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
 		update_ptr = dci_cumulative_log_mask;
-		if (driver->dci_client_tbl[i].client) {
-			log_mask_ptr =
-				driver->dci_client_tbl[i].dci_log_mask;
-			for (j = 0; j < 16; j++) {
-				*update_ptr = j;
-				*(update_ptr + 1) = 1;
-				update_ptr += 2;
-				log_mask_ptr += 2;
-				for (k = 0; k < 513; k++) {
-					*update_ptr |= *log_mask_ptr;
-					update_ptr++;
-					log_mask_ptr++;
-				}
+		log_mask_ptr = entry->dci_log_mask;
+		for (j = 0; j < 16; j++) {
+			*update_ptr = j;
+			*(update_ptr + 1) = 1;
+			update_ptr += 2;
+			log_mask_ptr += 2;
+			for (k = 0; k < 513; k++) {
+				*update_ptr |= *log_mask_ptr;
+				update_ptr++;
+				log_mask_ptr++;
 			}
 		}
 	}
 	mutex_unlock(&dci_log_mask_mutex);
-	err = diag_send_dci_log_mask(&driver->smd_cntl[MODEM_DATA]);
+	/* send updated mask to userspace clients */
+	diag_update_userspace_clients(DCI_LOG_MASKS_TYPE);
+	/* Send updated mask to peripherals */
+	err = diag_send_dci_log_mask();
 	return err;
 }
 
 int diag_dci_clear_event_mask()
 {
-	int i, j, err = DIAG_DCI_NO_ERROR;
+	int j, err = DIAG_DCI_NO_ERROR;
 	uint8_t *event_mask_ptr, *update_ptr;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
 
-	i = diag_dci_find_client_index(current->tgid);
-	if (i == DCI_CLIENT_INDEX_INVALID)
+	entry = diag_dci_get_client_entry();
+	if (!entry) {
+		pr_err("diag: In %s, invalid client entry\n", __func__);
 		return DIAG_DCI_TABLE_ERR;
+	}
 
 	mutex_lock(&dci_event_mask_mutex);
-	memset(driver->dci_client_tbl[i].dci_event_mask,
-			0x0, DCI_EVENT_MASK_SIZE);
-	memset(dci_cumulative_event_mask,
-			0x0, DCI_EVENT_MASK_SIZE);
+	memset(entry->dci_event_mask, 0x0, DCI_EVENT_MASK_SIZE);
+	memset(dci_cumulative_event_mask, 0x0, DCI_EVENT_MASK_SIZE);
 	update_ptr = dci_cumulative_event_mask;
-	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-		event_mask_ptr =
-			driver->dci_client_tbl[i].dci_event_mask;
+
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		event_mask_ptr = entry->dci_event_mask;
 		for (j = 0; j < DCI_EVENT_MASK_SIZE; j++)
 			*(update_ptr + j) |= *(event_mask_ptr + j);
 	}
 	mutex_unlock(&dci_event_mask_mutex);
-	err = diag_send_dci_event_mask(&driver->smd_cntl[MODEM_DATA]);
+	/* send updated mask to userspace clients */
+	diag_update_userspace_clients(DCI_EVENT_MASKS_TYPE);
+	/* Send updated mask to peripherals */
+	err = diag_send_dci_event_mask();
 	return err;
 }
 
 int diag_dci_query_log_mask(uint16_t log_code)
 {
-	uint16_t item_num;
-	uint8_t equip_id, *log_mask_ptr, byte_mask;
-	int i, byte_index, offset;
-
-	equip_id = LOG_GET_EQUIP_ID(log_code);
-	item_num = LOG_GET_ITEM_NUM(log_code);
-	byte_index = item_num/8 + 2;
-	byte_mask = 0x01 << (item_num % 8);
-	offset = equip_id * 514;
-
-	i = diag_dci_find_client_index(current->tgid);
-	if (i != DCI_CLIENT_INDEX_INVALID) {
-		log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
-		log_mask_ptr = log_mask_ptr + offset + byte_index;
-		return ((*log_mask_ptr & byte_mask) == byte_mask) ?
-								1 : 0;
-	}
-	return 0;
+	return __diag_dci_query_log_mask(diag_dci_get_client_entry(),
+					 log_code);
 }
 
-
 int diag_dci_query_event_mask(uint16_t event_id)
 {
-	uint8_t *event_mask_ptr, byte_mask;
-	int i, byte_index, bit_index;
-	byte_index = event_id/8;
-	bit_index = event_id % 8;
-	byte_mask = 0x1 << bit_index;
-
-	i = diag_dci_find_client_index(current->tgid);
-	if (i != DCI_CLIENT_INDEX_INVALID) {
-		event_mask_ptr =
-		driver->dci_client_tbl[i].dci_event_mask;
-		event_mask_ptr = event_mask_ptr + byte_index;
-		if ((*event_mask_ptr & byte_mask) == byte_mask)
-			return 1;
-		else
-			return 0;
-	}
-	return 0;
+	return __diag_dci_query_event_mask(diag_dci_get_client_entry(),
+					   event_id);
 }
 
 uint8_t diag_dci_get_cumulative_real_time()
 {
-	uint8_t real_time = MODE_NONREALTIME, i;
-	for (i = 0; i < MAX_DCI_CLIENTS; i++)
-		if (driver->dci_client_tbl[i].client &&
-				driver->dci_client_tbl[i].real_time ==
-				MODE_REALTIME) {
+	uint8_t real_time = MODE_NONREALTIME;
+	struct list_head *start, *temp;
+	struct diag_dci_client_tbl *entry = NULL;
+
+	list_for_each_safe(start, temp, &driver->dci_client_list) {
+		entry = list_entry(start, struct diag_dci_client_tbl, track);
+		if (entry->real_time == MODE_REALTIME) {
 			real_time = 1;
 			break;
 		}
+	}
 	return real_time;
 }
 
-int diag_dci_set_real_time(int client_id, uint8_t real_time)
+int diag_dci_set_real_time(uint8_t real_time)
 {
-	int i = DCI_CLIENT_INDEX_INVALID;
-	i = diag_dci_find_client_index(client_id);
-
-	if (i != DCI_CLIENT_INDEX_INVALID)
-		driver->dci_client_tbl[i].real_time = real_time;
-	return i;
-}
-
-void diag_dci_try_activate_wakeup_source(smd_channel_t *channel)
-{
-	spin_lock_irqsave(&ws_lock, ws_lock_flags);
-	if (channel == driver->smd_dci[MODEM_DATA].ch) {
-		pm_wakeup_event(driver->dci_device, DCI_WAKEUP_TIMEOUT);
-		pm_stay_awake(driver->dci_device);
-	} else if (channel == driver->smd_dci_cmd[MODEM_DATA].ch) {
-		pm_wakeup_event(driver->dci_cmd_device, DCI_WAKEUP_TIMEOUT);
-		pm_stay_awake(driver->dci_cmd_device);
+	struct diag_dci_client_tbl *entry = NULL;
+	entry = diag_dci_get_client_entry();
+	if (!entry) {
+		pr_err("diag: In %s, invalid client entry\n", __func__);
+		return 0;
 	}
+	entry->real_time = real_time;
+	return 1;
+}
+
+void diag_dci_try_activate_wakeup_source()
+{
+	spin_lock_irqsave(&ws_lock, ws_lock_flags);
+	pm_wakeup_event(driver->diag_dev, DCI_WAKEUP_TIMEOUT);
+	pm_stay_awake(driver->diag_dev);
 	spin_unlock_irqrestore(&ws_lock, ws_lock_flags);
 }
 
-void diag_dci_try_deactivate_wakeup_source(smd_channel_t *channel)
+void diag_dci_try_deactivate_wakeup_source()
 {
 	spin_lock_irqsave(&ws_lock, ws_lock_flags);
-	if (channel == driver->smd_dci[MODEM_DATA].ch)
-		pm_relax(driver->dci_device);
-	else if (channel == driver->smd_dci_cmd[MODEM_DATA].ch)
-		pm_relax(driver->dci_cmd_device);
+	pm_relax(driver->diag_dev);
 	spin_unlock_irqrestore(&ws_lock, ws_lock_flags);
 }
+
+int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry)
+{
+	int i, err = 0;
+	struct diag_dci_client_tbl *new_entry = NULL;
+	struct diag_dci_buf_peripheral_t *proc_buf = NULL;
+
+	if (!reg_entry)
+		return DIAG_DCI_NO_REG;
+
+	if (driver->dci_state == DIAG_DCI_NO_REG)
+		return DIAG_DCI_NO_REG;
+
+	if (driver->num_dci_client >= MAX_DCI_CLIENTS)
+		return DIAG_DCI_NO_REG;
+
+	new_entry = kzalloc(sizeof(struct diag_dci_client_tbl), GFP_KERNEL);
+	if (new_entry == NULL) {
+		pr_err("diag: unable to alloc memory\n");
+		return DIAG_DCI_NO_REG;
+	}
+
+	mutex_lock(&driver->dci_mutex);
+	if (!(driver->num_dci_client)) {
+		for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
+			driver->smd_dci[i].in_busy_1 = 0;
+		if (driver->supports_separate_cmdrsp)
+			for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++)
+				driver->smd_dci_cmd[i].in_busy_1 = 0;
+	}
+
+	new_entry->client = current;
+	new_entry->client_info.notification_list =
+				reg_entry->notification_list;
+	new_entry->client_info.signal_type =
+				reg_entry->signal_type;
+	new_entry->real_time = MODE_REALTIME;
+	new_entry->in_service = 0;
+	INIT_LIST_HEAD(&new_entry->list_write_buf);
+	mutex_init(&new_entry->write_buf_mutex);
+	new_entry->dci_log_mask =  kzalloc(DCI_LOG_MASK_SIZE, GFP_KERNEL);
+	if (!new_entry->dci_log_mask) {
+		pr_err("diag: Unable to create log mask for client, %d",
+							driver->dci_client_id);
+		goto fail_alloc;
+	}
+	create_dci_log_mask_tbl(new_entry->dci_log_mask);
+
+	new_entry->dci_event_mask =  kzalloc(DCI_EVENT_MASK_SIZE, GFP_KERNEL);
+	if (!new_entry->dci_event_mask) {
+		pr_err("diag: Unable to create event mask for client, %d",
+							driver->dci_client_id);
+		goto fail_alloc;
+	}
+	create_dci_event_mask_tbl(new_entry->dci_event_mask);
+
+	for (i = 0; i < NUM_DCI_PROC; i++) {
+		proc_buf = &new_entry->buffers[i];
+		if (!proc_buf)
+			goto fail_alloc;
+
+		mutex_init(&proc_buf->health_mutex);
+		mutex_init(&proc_buf->buf_mutex);
+		proc_buf->health.dropped_events = 0;
+		proc_buf->health.dropped_logs = 0;
+		proc_buf->health.received_events = 0;
+		proc_buf->health.received_logs = 0;
+		proc_buf->buf_primary = kzalloc(
+					sizeof(struct diag_dci_buffer_t),
+					GFP_KERNEL);
+		if (!proc_buf->buf_primary)
+			goto fail_alloc;
+		proc_buf->buf_cmd = kzalloc(sizeof(struct diag_dci_buffer_t),
+					    GFP_KERNEL);
+		if (!proc_buf->buf_cmd)
+			goto fail_alloc;
+		err = diag_dci_init_buffer(proc_buf->buf_primary,
+					   DCI_BUF_PRIMARY);
+		if (err)
+			goto fail_alloc;
+		err = diag_dci_init_buffer(proc_buf->buf_cmd, DCI_BUF_CMD);
+		if (err)
+			goto fail_alloc;
+		proc_buf->buf_curr = proc_buf->buf_primary;
+	}
+
+	list_add_tail(&new_entry->track, &driver->dci_client_list);
+	driver->dci_client_id++;
+	new_entry->client_info.client_id = driver->dci_client_id;
+	reg_entry->client_id = driver->dci_client_id;
+	driver->num_dci_client++;
+	if (driver->num_dci_client == 1)
+		diag_update_proc_vote(DIAG_PROC_DCI, VOTE_UP);
+	queue_work(driver->diag_real_time_wq, &driver->diag_real_time_work);
+	mutex_unlock(&driver->dci_mutex);
+
+	return driver->dci_client_id;
+
+fail_alloc:
+	if (new_entry) {
+		for (i = 0; i < NUM_DCI_PROC; i++) {
+			proc_buf = &new_entry->buffers[i];
+			mutex_destroy(&proc_buf->health_mutex);
+			mutex_destroy(&proc_buf->buf_primary->data_mutex);
+			mutex_destroy(&proc_buf->buf_cmd->data_mutex);
+			if (proc_buf->buf_primary)
+				kfree(proc_buf->buf_primary->data);
+			kfree(proc_buf->buf_primary);
+			if (proc_buf->buf_cmd)
+				kfree(proc_buf->buf_cmd->data);
+			kfree(proc_buf->buf_cmd);
+		}
+		kfree(new_entry->dci_event_mask);
+		kfree(new_entry->dci_log_mask);
+	}
+	kfree(new_entry);
+	mutex_unlock(&driver->dci_mutex);
+	return DIAG_DCI_NO_REG;
+}
+
+int diag_dci_deinit_client()
+{
+	int ret = DIAG_DCI_NO_ERROR, real_time = MODE_REALTIME, i, peripheral;
+	struct diag_dci_buf_peripheral_t *proc_buf = NULL;
+	struct diag_dci_client_tbl *entry = diag_dci_get_client_entry();
+	struct diag_dci_buffer_t *buf_entry, *temp;
+	struct list_head *start, *req_temp;
+	struct dci_pkt_req_entry_t *req_entry = NULL;
+	struct diag_smd_info *smd_info = NULL;
+
+	if (!entry)
+		return DIAG_DCI_NOT_SUPPORTED;
+
+	mutex_lock(&driver->dci_mutex);
+	/*
+	 * Remove the entry from the list before freeing the buffers
+	 * to ensure that we don't have any invalid access.
+	 */
+	list_del(&entry->track);
+	driver->num_dci_client--;
+	/*
+	 * Clear the client's log and event masks, update the cumulative
+	 * masks and send the masks to peripherals
+	 */
+	kfree(entry->dci_log_mask);
+	diag_update_userspace_clients(DCI_LOG_MASKS_TYPE);
+	diag_dci_invalidate_cumulative_log_mask();
+	ret = diag_send_dci_event_mask();
+	if (ret != DIAG_DCI_NO_ERROR) {
+		mutex_unlock(&driver->dci_mutex);
+		return ret;
+	}
+	kfree(entry->dci_event_mask);
+	diag_update_userspace_clients(DCI_EVENT_MASKS_TYPE);
+	diag_dci_invalidate_cumulative_event_mask();
+	ret = diag_send_dci_log_mask();
+	if (ret != DIAG_DCI_NO_ERROR) {
+		mutex_unlock(&driver->dci_mutex);
+		return ret;
+	}
+
+	list_for_each_safe(start, req_temp, &driver->dci_req_list) {
+		req_entry = list_entry(start, struct dci_pkt_req_entry_t,
+				       track);
+		if (req_entry->pid == current->tgid) {
+			list_del(&req_entry->track);
+			kfree(req_entry);
+		}
+	}
+
+	/* Clean up any buffer that is pending write */
+	mutex_lock(&entry->write_buf_mutex);
+	list_for_each_entry_safe(buf_entry, temp, &entry->list_write_buf,
+							buf_track) {
+		list_del(&buf_entry->buf_track);
+		if (buf_entry->buf_type == DCI_BUF_SECONDARY) {
+			mutex_lock(&buf_entry->data_mutex);
+			diagmem_free(driver, buf_entry->data, POOL_TYPE_DCI);
+			buf_entry->data = NULL;
+			mutex_unlock(&buf_entry->data_mutex);
+			kfree(buf_entry);
+		} else if (buf_entry->buf_type == DCI_BUF_CMD) {
+			peripheral = buf_entry->data_source;
+			if (peripheral == APPS_DATA)
+				continue;
+			mutex_lock(&buf_entry->data_mutex);
+			smd_info = driver->separate_cmdrsp[peripheral] ?
+					&driver->smd_dci_cmd[peripheral] :
+					&driver->smd_dci[peripheral];
+			smd_info->in_busy_1 = 0;
+			mutex_unlock(&buf_entry->data_mutex);
+		}
+		diag_dci_try_deactivate_wakeup_source();
+	}
+	mutex_unlock(&entry->write_buf_mutex);
+
+	for (i = 0; i < NUM_DCI_PROC; i++) {
+		proc_buf = &entry->buffers[i];
+		buf_entry = proc_buf->buf_curr;
+		mutex_lock(&proc_buf->buf_mutex);
+		/* Clean up secondary buffer from mempool that is active */
+		if (buf_entry && buf_entry->buf_type == DCI_BUF_SECONDARY) {
+			mutex_lock(&buf_entry->data_mutex);
+			diagmem_free(driver, buf_entry->data, POOL_TYPE_DCI);
+			buf_entry->data = NULL;
+			mutex_unlock(&buf_entry->data_mutex);
+			mutex_destroy(&buf_entry->data_mutex);
+			kfree(buf_entry);
+		}
+
+		mutex_lock(&proc_buf->buf_primary->data_mutex);
+		kfree(proc_buf->buf_primary->data);
+		mutex_unlock(&proc_buf->buf_primary->data_mutex);
+
+		mutex_lock(&proc_buf->buf_cmd->data_mutex);
+		kfree(proc_buf->buf_cmd->data);
+		mutex_unlock(&proc_buf->buf_cmd->data_mutex);
+
+		mutex_destroy(&proc_buf->health_mutex);
+		mutex_destroy(&proc_buf->buf_primary->data_mutex);
+		mutex_destroy(&proc_buf->buf_cmd->data_mutex);
+
+		kfree(proc_buf->buf_primary);
+		kfree(proc_buf->buf_cmd);
+		mutex_unlock(&proc_buf->buf_mutex);
+	}
+	mutex_destroy(&entry->write_buf_mutex);
+
+	kfree(entry);
+
+	if (driver->num_dci_client == 0) {
+		diag_update_proc_vote(DIAG_PROC_DCI, VOTE_DOWN);
+	} else {
+		real_time = diag_dci_get_cumulative_real_time();
+		diag_update_real_time_vote(DIAG_PROC_DCI, real_time);
+	}
+	queue_work(driver->diag_real_time_wq, &driver->diag_real_time_work);
+
+	mutex_unlock(&driver->dci_mutex);
+
+	return DIAG_DCI_NO_ERROR;
+}
+
+int diag_dci_write_proc(int peripheral, int pkt_type, char *buf, int len)
+{
+	struct diag_smd_info *smd_info = NULL;
+	int wr_size = 0, retry = 0, err = -EAGAIN, timer = 0, i;
+
+	if (!buf || (peripheral < 0 || peripheral > NUM_SMD_DCI_CHANNELS)
+								|| len < 0) {
+		pr_err("diag: In %s, invalid data 0x%p, peripheral: %d, len: %d\n",
+				__func__, buf, peripheral, len);
+		return -EINVAL;
+	}
+
+	if (pkt_type == DIAG_DATA_TYPE) {
+		for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++)
+			if (peripheral == i)
+				smd_info = &driver->smd_dci_cmd[peripheral];
+		/*
+		 * This peripheral doesn't support separate channel for
+		 * command response.
+		 */
+		if (!smd_info)
+			smd_info = &driver->smd_dci[peripheral];
+	} else if (pkt_type == DIAG_CNTL_TYPE) {
+		smd_info = &driver->smd_cntl[peripheral];
+	} else {
+		pr_err("diag: Invalid DCI pkt type in %s", __func__);
+		return -EINVAL;
+	}
+
+	if (!smd_info || !smd_info->ch)
+		return -EINVAL;
+
+	while (retry < 3) {
+		mutex_lock(&smd_info->smd_ch_mutex);
+		wr_size = smd_write(smd_info->ch, buf, len);
+		if (wr_size == len) {
+			pr_debug("diag: successfully wrote pkt_type %d of len %d to %d in trial %d",
+					pkt_type, len, peripheral, (retry+1));
+			err = DIAG_DCI_NO_ERROR;
+			mutex_unlock(&smd_info->smd_ch_mutex);
+			break;
+		}
+		pr_debug("diag: cannot write pkt_type %d of len %d to %d in trial %d",
+					pkt_type, len, peripheral, (retry+1));
+		retry++;
+		mutex_unlock(&smd_info->smd_ch_mutex);
+
+		/*
+		 * Sleep for sometime before retrying. The delay of 2000 was
+		 * determined empirically as best value to use.
+		 */
+		for (timer = 0; timer < 5; timer++)
+			usleep(2000);
+	}
+	return err;
+}
+
+int diag_dci_copy_health_stats(struct diag_dci_health_stats *stats, int proc)
+{
+	struct diag_dci_client_tbl *entry = NULL;
+	struct diag_dci_health_t *health = NULL;
+	int i;
+
+	if (!stats)
+		return -EINVAL;
+
+	if (proc < ALL_PROC || proc > APPS_DATA)
+		return -EINVAL;
+
+	entry = diag_dci_get_client_entry();
+	if (!entry)
+		return DIAG_DCI_NOT_SUPPORTED;
+
+	stats->stats.dropped_logs = 0;
+	stats->stats.dropped_events = 0;
+	stats->stats.received_logs = 0;
+	stats->stats.received_events = 0;
+
+	if (proc != ALL_PROC) {
+		health = &entry->buffers[proc].health;
+		stats->stats.dropped_logs = health->dropped_logs;
+		stats->stats.dropped_events = health->dropped_events;
+		stats->stats.received_logs = health->received_logs;
+		stats->stats.received_events = health->received_events;
+		if (stats->reset_status) {
+			mutex_lock(&entry->buffers[proc].health_mutex);
+			health->dropped_logs = 0;
+			health->dropped_events = 0;
+			health->received_logs = 0;
+			health->received_events = 0;
+			mutex_unlock(&entry->buffers[proc].health_mutex);
+		}
+		return DIAG_DCI_NO_ERROR;
+	}
+
+
+	for (i = 0; i < NUM_DCI_PROC; i++) {
+		health = &entry->buffers[i].health;
+		stats->stats.dropped_logs += health->dropped_logs;
+		stats->stats.dropped_events += health->dropped_events;
+		stats->stats.received_logs += health->received_logs;
+		stats->stats.received_events += health->received_events;
+		if (stats->reset_status) {
+			mutex_lock(&entry->buffers[i].health_mutex);
+			health->dropped_logs = 0;
+			health->dropped_events = 0;
+			health->received_logs = 0;
+			health->received_events = 0;
+			mutex_unlock(&entry->buffers[i].health_mutex);
+		}
+	}
+	return DIAG_DCI_NO_ERROR;
+}
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 870b0f3..ccd1a71 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -28,6 +28,13 @@
 #define DCI_LOG_CON_MIN_LEN		14
 #define DCI_EVENT_CON_MIN_LEN		16
 
+#define DIAG_DATA_TYPE		1
+#define DIAG_CNTL_TYPE		2
+
+#define DCI_BUF_PRIMARY		1
+#define DCI_BUF_SECONDARY	2
+#define DCI_BUF_CMD		3
+
 #ifdef CONFIG_DEBUG_FS
 #define DIAG_DCI_DEBUG_CNT	100
 #define DIAG_DCI_DEBUG_LEN	100
@@ -46,6 +53,8 @@
 
 extern unsigned int dci_max_reg;
 extern unsigned int dci_max_clients;
+extern unsigned char dci_cumulative_log_mask[DCI_LOG_MASK_SIZE];
+extern unsigned char dci_cumulative_event_mask[DCI_EVENT_MASK_SIZE];
 extern struct mutex dci_health_mutex;
 
 struct dci_pkt_req_entry_t {
@@ -55,34 +64,63 @@
 	struct list_head track;
 } __packed;
 
-struct diag_dci_client_tbl {
+struct diag_dci_reg_tbl_t {
 	uint32_t client_id;
-	struct task_struct *client;
-	uint16_t list; /* bit mask */
+	uint16_t notification_list;
 	int signal_type;
-	unsigned char dci_log_mask[DCI_LOG_MASK_SIZE];
-	unsigned char dci_event_mask[DCI_EVENT_MASK_SIZE];
-	unsigned char *dci_data;
-	int data_len;
-	int total_capacity;
-	int dropped_logs;
-	int dropped_events;
-	int received_logs;
-	int received_events;
-	struct mutex data_mutex;
-	uint8_t real_time;
 };
 
-/* This is used for DCI health stats */
-struct diag_dci_health_stats {
-	int client_id;
+struct diag_dci_health_t {
 	int dropped_logs;
 	int dropped_events;
 	int received_logs;
 	int received_events;
+};
+
+struct diag_dci_buffer_t {
+	unsigned char *data;
+	unsigned int data_len;
+	struct mutex data_mutex;
+	uint8_t in_busy;
+	uint8_t buf_type;
+	int data_source;
+	int capacity;
+	uint8_t in_list;
+	struct list_head buf_track;
+};
+
+struct diag_dci_buf_peripheral_t {
+	struct diag_dci_buffer_t *buf_curr;
+	struct diag_dci_buffer_t *buf_primary;
+	struct diag_dci_buffer_t *buf_cmd;
+	struct diag_dci_health_t health;
+	struct mutex health_mutex;
+	struct mutex buf_mutex;
+};
+
+struct diag_dci_client_tbl {
+	struct diag_dci_reg_tbl_t client_info;
+	struct task_struct *client;
+	unsigned char *dci_log_mask;
+	unsigned char *dci_event_mask;
+	uint8_t real_time;
+	struct list_head track;
+	struct diag_dci_buf_peripheral_t buffers[NUM_DCI_PROC];
+	uint8_t in_service;
+	struct list_head list_write_buf;
+	struct mutex write_buf_mutex;
+};
+
+struct diag_dci_health_stats {
+	struct diag_dci_health_t stats;
 	int reset_status;
 };
 
+struct diag_dci_health_stats_proc {
+	struct diag_dci_health_stats *health;
+	int proc;
+};
+
 /* This is used for querying DCI Log
    or Event Mask */
 struct diag_log_event_stats {
@@ -90,6 +128,14 @@
 	int is_set;
 };
 
+struct diag_dci_pkt_header_t {
+	uint8_t start;
+	uint8_t version;
+	uint16_t len;
+	uint8_t pkt_code;
+	int tag;
+} __packed;
+
 enum {
 	DIAG_DCI_NO_ERROR = 1001,	/* No error */
 	DIAG_DCI_NO_REG,		/* Could not register */
@@ -106,6 +152,7 @@
 	unsigned long iteration;
 	int data_size;
 	char time_stamp[DIAG_TS_SIZE];
+	uint8_t peripheral;
 	uint8_t ch_type;
 };
 
@@ -115,36 +162,42 @@
 
 int diag_dci_init(void);
 void diag_dci_exit(void);
+int diag_dci_register_client(struct diag_dci_reg_tbl_t *reg_entry);
+int diag_dci_deinit_client(void);
 void diag_update_smd_dci_work_fn(struct work_struct *);
 void diag_dci_notify_client(int peripheral_mask, int data);
+void diag_dci_wakeup_clients(void);
+void diag_process_apps_dci_read_data(int data_type, void *buf, int recd_bytes);
 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);
-void extract_dci_pkt_rsp(struct diag_smd_info *smd_info, unsigned char *buf);
-
-int diag_dci_find_client_index_health(int client_id);
-int diag_dci_find_client_index(int client_id);
+void extract_dci_pkt_rsp(unsigned char *buf, int len, int data_source,
+			 struct diag_smd_info *smd_info);
+struct diag_dci_client_tbl *diag_dci_get_client_entry(void);
 /* DCI Log streaming functions */
 void create_dci_log_mask_tbl(unsigned char *tbl_buf);
 void update_dci_cumulative_log_mask(int offset, unsigned int byte_index,
 						uint8_t byte_mask);
-void clear_client_dci_cumulative_log_mask(int client_index);
-int diag_send_dci_log_mask(struct diag_smd_info *smd_info);
-void extract_dci_log(unsigned char *buf);
+void diag_dci_invalidate_cumulative_log_mask(void);
+int diag_send_dci_log_mask(void);
+void extract_dci_log(unsigned char *buf, int len, int data_source);
 int diag_dci_clear_log_mask(void);
 int diag_dci_query_log_mask(uint16_t log_code);
 /* DCI event streaming functions */
 void update_dci_cumulative_event_mask(int offset, uint8_t byte_mask);
-void clear_client_dci_cumulative_event_mask(int client_index);
-int diag_send_dci_event_mask(struct diag_smd_info *smd_info);
-void extract_dci_events(unsigned char *buf);
+void diag_dci_invalidate_cumulative_event_mask(void);
+int diag_send_dci_event_mask(void);
+void extract_dci_events(unsigned char *buf, int len, int data_source);
 void create_dci_event_mask_tbl(unsigned char *tbl_buf);
 int diag_dci_clear_event_mask(void);
 int diag_dci_query_event_mask(uint16_t event_id);
-void diag_dci_smd_record_info(int read_bytes, uint8_t ch_type);
+void diag_dci_smd_record_info(int read_bytes, uint8_t ch_type,
+			      uint8_t peripheral);
 uint8_t diag_dci_get_cumulative_real_time(void);
-int diag_dci_set_real_time(int client_id, uint8_t real_time);
+int diag_dci_set_real_time(uint8_t real_time);
+int diag_dci_copy_health_stats(struct diag_dci_health_stats *stats, int proc);
 /* Functions related to DCI wakeup sources */
-void diag_dci_try_activate_wakeup_source(smd_channel_t *channel);
-void diag_dci_try_deactivate_wakeup_source(smd_channel_t *channel);
+void diag_dci_try_activate_wakeup_source(void);
+void diag_dci_try_deactivate_wakeup_source(void);
+int diag_dci_write_proc(int peripheral, int pkt_type, char *buf, int len);
 #endif
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 3a1c96b..4dd0845 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -233,38 +233,30 @@
 		bytes_in_buf += bytes_written;
 		bytes_remaining -= bytes_written;
 #endif
-		if (driver->dci_device) {
-			bytes_written = scnprintf(buf+bytes_in_buf,
-						  bytes_remaining,
-				"dci power active, relax: %lu, %lu\n",
-				driver->dci_device->power.wakeup->active_count,
-				driver->dci_device->power.wakeup->relax_count);
-			bytes_in_buf += bytes_written;
-			bytes_remaining -= bytes_written;
-		}
-		if (driver->dci_cmd_device) {
-			bytes_written = scnprintf(buf+bytes_in_buf,
-						  bytes_remaining,
-				"dci cmd power active, relax: %lu, %lu\n",
-				driver->dci_cmd_device->power.wakeup->
-						  active_count,
-				driver->dci_cmd_device->power.wakeup->
-						  relax_count);
-			bytes_in_buf += bytes_written;
-			bytes_remaining -= bytes_written;
-		}
+		bytes_written = scnprintf(buf+bytes_in_buf,
+					  bytes_remaining,
+					  "dci power: active, relax: %lu, %lu\n",
+					  driver->diag_dev->power.wakeup->
+						active_count,
+					  driver->diag_dev->
+						power.wakeup->relax_count);
+		bytes_in_buf += bytes_written;
+		bytes_remaining -= bytes_written;
+
 	}
 	temp_data += diag_dbgfs_dci_data_index;
 	for (i = diag_dbgfs_dci_data_index; i < DIAG_DCI_DEBUG_CNT; i++) {
 		if (temp_data->iteration != 0) {
 			bytes_written = scnprintf(
 				buf + bytes_in_buf, bytes_remaining,
-				"i %-10ld\t"
-				"s %-10d\t"
-				"c %-10d\t"
+				"i %-5ld\t"
+				"s %-5d\t"
+				"p %-5d\t"
+				"c %-5d\t"
 				"t %-15s\n",
 				temp_data->iteration,
 				temp_data->data_size,
+				temp_data->peripheral,
 				temp_data->ch_type,
 				temp_data->time_stamp);
 			bytes_in_buf += bytes_written;
@@ -446,7 +438,8 @@
 		"POOL_TYPE_COPY: [0x%p : 0x%p] count = %d\n"
 		"POOL_TYPE_HDLC: [0x%p : 0x%p] count = %d\n"
 		"POOL_TYPE_USER: [0x%p : 0x%p] count = %d\n"
-		"POOL_TYPE_WRITE_STRUCT: [0x%p : 0x%p] count = %d\n",
+		"POOL_TYPE_WRITE_STRUCT: [0x%p : 0x%p] count = %d\n"
+		"POOL_TYPE_DCI: [0x%p : 0x%p] count = %d\n",
 		driver->diagpool,
 		diag_pools_array[POOL_COPY_IDX],
 		driver->count,
@@ -458,7 +451,10 @@
 		driver->count_user_pool,
 		driver->diag_write_struct_pool,
 		diag_pools_array[POOL_WRITE_STRUCT_IDX],
-		driver->count_write_struct_pool);
+		driver->count_write_struct_pool,
+		driver->diag_dci_pool,
+		diag_pools_array[POOL_DCI_IDX],
+		driver->count_dci_pool);
 
 	for (i = 0; i < MAX_HSIC_CH; i++) {
 		if (!diag_hsic[i].hsic_inited)
@@ -506,7 +502,8 @@
 		"POOL_TYPE_COPY: [0x%p : 0x%p] count = %d\n"
 		"POOL_TYPE_HDLC: [0x%p : 0x%p] count = %d\n"
 		"POOL_TYPE_USER: [0x%p : 0x%p] count = %d\n"
-		"POOL_TYPE_WRITE_STRUCT: [0x%p : 0x%p] count = %d\n",
+		"POOL_TYPE_WRITE_STRUCT: [0x%p : 0x%p] count = %d\n"
+		"POOL_TYPE_DCI: [0x%p : 0x%p] count = %d\n",
 		driver->diagpool,
 		diag_pools_array[POOL_COPY_IDX],
 		driver->count,
@@ -518,7 +515,10 @@
 		driver->count_user_pool,
 		driver->diag_write_struct_pool,
 		diag_pools_array[POOL_WRITE_STRUCT_IDX],
-		driver->count_write_struct_pool);
+		driver->count_write_struct_pool,
+		driver->diag_dci_pool,
+		diag_pools_array[POOL_DCI_IDX],
+		driver->count_dci_pool);
 
 	ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
 
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 95d90b3..eda745f 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, 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
@@ -17,13 +17,13 @@
 #include <linux/module.h>
 #include <linux/mempool.h>
 #include <linux/mutex.h>
+#include <linux/list.h>
 #include <linux/spinlock.h>
 #include <linux/workqueue.h>
 #include <linux/sched.h>
 #include <linux/wakelock.h>
 #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
@@ -44,28 +44,32 @@
 #define POOL_TYPE_HSIC_WRITE	11
 #define POOL_TYPE_HSIC_2_WRITE	12
 #define POOL_TYPE_ALL		10
+#define POOL_TYPE_DCI		20
 
 #define POOL_COPY_IDX		0
 #define POOL_HDLC_IDX		1
 #define POOL_USER_IDX		2
 #define POOL_WRITE_STRUCT_IDX	3
-#define POOL_HSIC_IDX		4
-#define POOL_HSIC_2_IDX		5
-#define POOL_HSIC_3_IDX		6
-#define POOL_HSIC_4_IDX		7
-#define POOL_HSIC_WRITE_IDX	8
-#define POOL_HSIC_2_WRITE_IDX	9
-#define POOL_HSIC_3_WRITE_IDX	10
-#define POOL_HSIC_4_WRITE_IDX	11
+#define POOL_DCI_IDX		4
+#define POOL_BRIDGE_BASE	POOL_DCI_IDX
+#define POOL_HSIC_IDX		(POOL_BRIDGE_BASE + 1)
+#define POOL_HSIC_2_IDX		(POOL_BRIDGE_BASE + 2)
+#define POOL_HSIC_3_IDX		(POOL_BRIDGE_BASE + 3)
+#define POOL_HSIC_4_IDX		(POOL_BRIDGE_BASE + 4)
+#define POOL_HSIC_WRITE_IDX	(POOL_BRIDGE_BASE + 5)
+#define POOL_HSIC_2_WRITE_IDX	(POOL_BRIDGE_BASE + 6)
+#define POOL_HSIC_3_WRITE_IDX	(POOL_BRIDGE_BASE + 7)
+#define POOL_HSIC_4_WRITE_IDX	(POOL_BRIDGE_BASE + 8)
 
 #ifdef CONFIG_DIAGFWD_BRIDGE_CODE
-#define NUM_MEMORY_POOLS	12
+#define NUM_MEMORY_POOLS	13
 #else
-#define NUM_MEMORY_POOLS	4
+#define NUM_MEMORY_POOLS	5
 #endif
 
 #define MAX_SSID_PER_RANGE	200
 
+#define ALL_PROC		-1
 #define MODEM_DATA		0
 #define LPASS_DATA		1
 #define WCNSS_DATA		2
@@ -107,10 +111,26 @@
 #define DIAG_STM_WCNSS	0x04
 #define DIAG_STM_APPS	0x08
 
-#define DIAG_DIAG_STM		0x214
-
 #define BAD_PARAM_RESPONSE_MESSAGE 20
 
+#define DIAG_CMD_VERSION	0
+#define DIAG_CMD_DOWNLOAD	0x3A
+#define DIAG_CMD_DIAG_SUBSYS	0x4B
+#define DIAG_CMD_LOG_ON_DMND	0x78
+#define DIAG_CMD_EXT_BUILD	0x7c
+
+#define DIAG_SS_DIAG		0x12
+#define DIAG_SS_PARAMS		0x32
+
+#define DIAG_DIAG_MAX_PKT_SZ	0x55
+#define DIAG_DIAG_STM		0x20E
+#define DIAG_DIAG_POLL		0x03
+#define DIAG_DEL_RSP_WRAP	0x04
+#define DIAG_DEL_RSP_WRAP_CNT	0x05
+
+#define MODE_CMD	41
+#define RESET_ID	2
+
 /*
  * The status bit masks when received in a signal handler are to be
  * used in conjunction with the peripheral list bit mask to determine the
@@ -129,6 +149,13 @@
 #define NUM_SMD_CMD_CHANNELS 1
 #define NUM_SMD_DCI_CMD_CHANNELS 1
 
+/*
+ * Indicates number of peripherals that can support DCI and Apps
+ * processor. This doesn't mean that a peripheral has the
+ * feature.
+ */
+#define NUM_DCI_PROC	(NUM_SMD_DATA_CHANNELS + 1)
+
 #define SMD_DATA_TYPE 0
 #define SMD_CNTL_TYPE 1
 #define SMD_DCI_TYPE 2
@@ -165,6 +192,12 @@
 	QSC = 5,
 };
 
+struct diag_pkt_header_t {
+	uint8_t cmd_code;
+	uint8_t subsys_id;
+	uint16_t subsys_cmd_code;
+} __packed;
+
 struct diag_master_table {
 	uint16_t cmd_code;
 	uint16_t subsys_id;
@@ -286,6 +319,7 @@
 	char *name;
 	int dropped_count;
 	struct class *diagchar_class;
+	struct device *diag_dev;
 	int ref_count;
 	struct mutex diagchar_mutex;
 	wait_queue_head_t wait_q;
@@ -307,7 +341,7 @@
 	int peripheral_supports_stm[NUM_SMD_CONTROL_CHANNELS];
 	/* DCI related variables */
 	struct list_head dci_req_list;
-	struct diag_dci_client_tbl *dci_client_tbl;
+	struct list_head dci_client_list;
 	int dci_tag;
 	int dci_client_id;
 	struct mutex dci_mutex;
@@ -324,17 +358,21 @@
 	unsigned int poolsize_user;
 	unsigned int itemsize_write_struct;
 	unsigned int poolsize_write_struct;
+	unsigned int itemsize_dci;
+	unsigned int poolsize_dci;
 	unsigned int debug_flag;
 	/* State for the mempool for the char driver */
 	mempool_t *diagpool;
 	mempool_t *diag_hdlc_pool;
 	mempool_t *diag_user_pool;
 	mempool_t *diag_write_struct_pool;
+	mempool_t *diag_dci_pool;
 	spinlock_t diag_mem_lock;
 	int count;
 	int count_hdlc_pool;
 	int count_user_pool;
 	int count_write_struct_pool;
+	int count_dci_pool;
 	int used;
 	/* Buffers for masks */
 	struct mutex diag_cntl_mutex;
@@ -365,8 +403,6 @@
 	unsigned hdlc_count;
 	unsigned hdlc_escape;
 	int in_busy_pktdata;
-	struct device *dci_device;
-	struct device *dci_cmd_device;
 	/* Variables for non real time mode */
 	int real_time_mode;
 	int real_time_update_busy;
@@ -397,6 +433,9 @@
 	struct diag_master_table *table;
 	uint8_t *pkt_buf;
 	int pkt_length;
+	uint8_t *dci_pkt_buf; /* For Apps DCI packets */
+	uint32_t dci_pkt_length;
+	int in_busy_dcipktdata;
 	struct diag_request *usb_read_ptr;
 	struct diag_request *write_ptr_svc;
 	int logging_mode;
@@ -441,5 +480,6 @@
 
 void diag_get_timestamp(char *time_str);
 int diag_find_polling_reg(int i);
+void check_drain_timer(void);
 
 #endif
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 93e932a..263715d 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -71,6 +71,9 @@
 /* for write structure buffer */
 static unsigned int itemsize_write_struct = 20; /*Size of item in the mempool */
 static unsigned int poolsize_write_struct = 10;/* Num of items in the mempool */
+/* For the dci memory pool */
+static unsigned int itemsize_dci = IN_BUF_SIZE; /*Size of item in the mempool */
+static unsigned int poolsize_dci = 10;  /*Number of items in the mempool */
 /* This is the max number of user-space clients supported at initialization*/
 static unsigned int max_clients = 15;
 static unsigned int threshold_client_limit = 30;
@@ -144,6 +147,16 @@
 	mutex_unlock(&driver->diagchar_mutex);
 }
 
+void check_drain_timer(void)
+{
+	int ret = 0;
+
+	if (!timer_in_progress) {
+		timer_in_progress = 1;
+		ret = mod_timer(&drain_timer, jiffies + msecs_to_jiffies(500));
+	}
+}
+
 #ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 void diag_clear_hsic_tbl(void)
 {
@@ -266,9 +279,7 @@
 	* This will specially help in case of ungraceful exit of any DCI client
 	* This call will remove any pending registrations of such client
 	*/
-	if (diag_dci_find_client_index(current->tgid) !=
-		 DCI_CLIENT_INDEX_INVALID)
-		diagchar_ioctl(NULL, DIAG_IOCTL_DCI_DEINIT, 0);
+	diag_dci_deinit_client();
 	/* If the exiting process is the socket process */
 	mutex_lock(&driver->diagchar_mutex);
 	if (driver->socket_process &&
@@ -303,11 +314,12 @@
 
 	mutex_lock(&driver->diagchar_mutex);
 	driver->ref_count--;
-	/* On Client exit, try to destroy all 4 pools */
+	/* On Client exit, try to destroy all 5 pools */
 	diagmem_exit(driver, POOL_TYPE_COPY);
 	diagmem_exit(driver, POOL_TYPE_HDLC);
 	diagmem_exit(driver, POOL_TYPE_USER);
 	diagmem_exit(driver, POOL_TYPE_WRITE_STRUCT);
+	diagmem_exit(driver, POOL_TYPE_DCI);
 	for (i = 0; i < driver->num_clients; i++) {
 		if (NULL != diagpriv_data && diagpriv_data->pid ==
 						driver->client_map[i].pid) {
@@ -552,6 +564,87 @@
 			    int *pnum_data) { return 0; }
 #endif
 
+static int diag_copy_dci(char __user *buf, size_t count,
+			struct diag_dci_client_tbl *entry, int *pret)
+{
+	int total_data_len = 0;
+	int ret = 0;
+	int exit_stat = 1;
+	struct diag_dci_buffer_t *buf_entry, *temp;
+	struct diag_smd_info *smd_info = NULL;
+
+	if (!buf || !entry || !pret)
+		return exit_stat;
+
+	ret = *pret;
+
+	ret += 4;
+
+	mutex_lock(&entry->write_buf_mutex);
+	list_for_each_entry_safe(buf_entry, temp, &entry->list_write_buf,
+								buf_track) {
+		list_del(&buf_entry->buf_track);
+		mutex_lock(&buf_entry->data_mutex);
+		if ((buf_entry->data_len > 0) &&
+		    (buf_entry->in_busy) &&
+		    (buf_entry->data)) {
+			if (copy_to_user(buf+ret, (void *)buf_entry->data,
+					 buf_entry->data_len))
+				goto drop;
+			ret += buf_entry->data_len;
+			total_data_len += buf_entry->data_len;
+drop:
+			buf_entry->in_busy = 0;
+			buf_entry->data_len = 0;
+			buf_entry->in_list = 0;
+			if (buf_entry->buf_type == DCI_BUF_CMD) {
+				if (buf_entry->data_source == APPS_DATA) {
+					mutex_unlock(&buf_entry->data_mutex);
+					continue;
+				}
+				if (driver->separate_cmdrsp[
+						buf_entry->data_source]) {
+					smd_info = &driver->smd_dci_cmd[
+						buf_entry->data_source];
+				} else {
+					smd_info = &driver->smd_dci[
+						buf_entry->data_source];
+				}
+				smd_info->in_busy_1 = 0;
+				mutex_unlock(&buf_entry->data_mutex);
+				continue;
+			} else if (buf_entry->buf_type == DCI_BUF_SECONDARY) {
+				diagmem_free(driver, buf_entry->data,
+					     POOL_TYPE_DCI);
+				buf_entry->data = NULL;
+				mutex_unlock(&buf_entry->data_mutex);
+				kfree(buf_entry);
+				continue;
+			}
+
+		}
+		mutex_unlock(&buf_entry->data_mutex);
+	}
+
+	if (total_data_len > 0) {
+		/* Copy the total data length */
+		COPY_USER_SPACE_OR_EXIT(buf+4, total_data_len, 4);
+		ret -= 4;
+	} else {
+		pr_debug("diag: In %s, Trying to copy ZERO bytes, total_data_len: %d\n",
+			__func__, total_data_len);
+	}
+
+	entry->in_service = 0;
+	mutex_unlock(&entry->write_buf_mutex);
+
+	exit_stat = 0;
+exit:
+	*pret = ret;
+
+	return exit_stat;
+}
+
 int diag_command_reg(unsigned long ioarg)
 {
 	int i = 0, success = -EINVAL, j;
@@ -877,13 +970,11 @@
 	int i, result = -EINVAL, interim_size = 0, client_id = 0, real_time = 0;
 	int retry_count = 0, timer = 0;
 	uint16_t support_list = 0, interim_rsp_id, remote_dev;
-	struct diag_dci_client_tbl *dci_params;
-	struct diag_dci_health_stats stats;
+	struct diag_dci_reg_tbl_t *dci_reg_params;
+	struct diag_dci_health_stats_proc stats;
 	struct diag_log_event_stats le_stats;
 	struct diagpkt_delay_params delay_params;
 	struct real_time_vote_t rt_vote;
-	struct list_head *start, *req_temp;
-	struct dci_pkt_req_entry_t *req_entry = NULL;
 
 	switch (iocmd) {
 	case DIAG_IOCTL_COMMAND_REG:
@@ -909,127 +1000,25 @@
 		}
 		break;
 	case DIAG_IOCTL_DCI_REG:
-		if (driver->dci_state == DIAG_DCI_NO_REG)
-			return DIAG_DCI_NO_REG;
-		if (driver->num_dci_client >= MAX_DCI_CLIENTS)
-			return DIAG_DCI_NO_REG;
-		dci_params = kzalloc(sizeof(struct diag_dci_client_tbl),
+		dci_reg_params = kzalloc(sizeof(struct diag_dci_reg_tbl_t),
 								 GFP_KERNEL);
-		if (dci_params == NULL) {
+		if (dci_reg_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))) {
-			kfree(dci_params);
+		if (copy_from_user(dci_reg_params, (void *)ioarg,
+				 sizeof(struct diag_dci_reg_tbl_t))) {
+			kfree(dci_reg_params);
 			return -EFAULT;
 		}
-		mutex_lock(&driver->dci_mutex);
-		if (!(driver->num_dci_client)) {
-			for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++)
-				driver->smd_dci[i].in_busy_1 = 0;
-			if (driver->supports_separate_cmdrsp)
-				for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++)
-					driver->smd_dci_cmd[i].in_busy_1 = 0;
-		}
-		driver->num_dci_client++;
-		if (driver->num_dci_client == 1)
-			diag_update_proc_vote(DIAG_PROC_DCI, VOTE_UP);
-		queue_work(driver->diag_real_time_wq,
-				 &driver->diag_real_time_work);
-		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].client_id =
-							driver->dci_client_id;
-				driver->dci_client_tbl[i].list =
-							 dci_params->list;
-				driver->dci_client_tbl[i].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->
-					dci_client_tbl[i].dci_event_mask);
-				driver->dci_client_tbl[i].data_len = 0;
-				driver->dci_client_tbl[i].dci_data =
-					 kzalloc(IN_BUF_SIZE, GFP_KERNEL);
-				driver->dci_client_tbl[i].total_capacity =
-								 IN_BUF_SIZE;
-				driver->dci_client_tbl[i].dropped_logs = 0;
-				driver->dci_client_tbl[i].dropped_events = 0;
-				driver->dci_client_tbl[i].received_logs = 0;
-				driver->dci_client_tbl[i].received_events = 0;
-				driver->dci_client_tbl[i].real_time = 1;
-				mutex_init(&driver->dci_client_tbl[i].
-								data_mutex);
-				break;
-			}
-		}
-		kfree(dci_params);
-		mutex_unlock(&driver->dci_mutex);
-		result = driver->dci_client_id;
+		result = diag_dci_register_client(dci_reg_params);
+		kfree(dci_reg_params);
 		break;
 	case DIAG_IOCTL_DCI_DEINIT:
-		result = -EIO;
-		/* Delete this process from DCI table */
-		mutex_lock(&driver->dci_mutex);
-		i = diag_dci_find_client_index(current->tgid);
-		if (i == DCI_CLIENT_INDEX_INVALID) {
-			result = DIAG_DCI_NOT_SUPPORTED;
-		} else {
-			/* clear respective cumulative log masks */
-			clear_client_dci_cumulative_log_mask(i);
-			/* send updated log mask to peripherals */
-			result =
-			diag_send_dci_log_mask(&driver->smd_cntl[MODEM_DATA]);
-			if (result != DIAG_DCI_NO_ERROR) {
-				mutex_unlock(&driver->dci_mutex);
-				return result;
-			}
-			/* clear respective cumulative event masks */
-			clear_client_dci_cumulative_event_mask(i);
-			/* send updated event mask to peripherals */
-			result =
-			diag_send_dci_event_mask(
-				&driver->smd_cntl[MODEM_DATA]);
-			if (result != DIAG_DCI_NO_ERROR) {
-				mutex_unlock(&driver->dci_mutex);
-				return result;
-			}
-			result = i;
-			/* Delete this process from DCI table */
-			list_for_each_safe(start, req_temp,
-							&driver->dci_req_list) {
-				req_entry = list_entry(start,
-						struct dci_pkt_req_entry_t,
-						track);
-				if (req_entry->pid == current->tgid) {
-					list_del(&req_entry->track);
-					kfree(req_entry);
-				}
-			}
-			driver->dci_client_tbl[result].client = NULL;
-			kfree(driver->dci_client_tbl[result].dci_data);
-			driver->dci_client_tbl[result].dci_data = NULL;
-			mutex_destroy(&driver->dci_client_tbl[result].
-								data_mutex);
-			driver->num_dci_client--;
-			if (driver->num_dci_client == 0) {
-				diag_update_proc_vote(DIAG_PROC_DCI, VOTE_DOWN);
-			} else {
-				real_time = diag_dci_get_cumulative_real_time();
-				diag_update_real_time_vote(DIAG_PROC_DCI,
-								real_time);
-			}
-			queue_work(driver->diag_real_time_wq,
-				   &driver->diag_real_time_work);
-		}
-		mutex_unlock(&driver->dci_mutex);
+		result = diag_dci_deinit_client();
 		break;
 	case DIAG_IOCTL_DCI_SUPPORT:
+		support_list |= DIAG_CON_APSS;
 		for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
 			if (driver->smd_dci[i].ch)
 				support_list |=
@@ -1042,29 +1031,16 @@
 		break;
 	case DIAG_IOCTL_DCI_HEALTH_STATS:
 		if (copy_from_user(&stats, (void *)ioarg,
-				 sizeof(struct diag_dci_health_stats)))
+				 sizeof(struct diag_dci_health_stats_proc)))
 			return -EFAULT;
-		mutex_lock(&dci_health_mutex);
-		i = diag_dci_find_client_index_health(stats.client_id);
-		if (i != DCI_CLIENT_INDEX_INVALID) {
-			dci_params = &(driver->dci_client_tbl[i]);
-			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) {
-				dci_params->dropped_logs = 0;
-				dci_params->dropped_events = 0;
-				dci_params->received_logs = 0;
-				dci_params->received_events = 0;
-			}
-		}
-		mutex_unlock(&dci_health_mutex);
+
+		result = diag_dci_copy_health_stats(stats.health,
+						    stats.proc);
+		if (result != DIAG_DCI_NO_ERROR)
+			break;
+
 		if (copy_to_user((void *)ioarg, &stats,
-				   sizeof(struct diag_dci_health_stats)))
+				   sizeof(struct diag_dci_health_stats_proc)))
 			return -EFAULT;
 		result = DIAG_DCI_NO_ERROR;
 		break;
@@ -1126,8 +1102,7 @@
 			return -EFAULT;
 		driver->real_time_update_busy++;
 		if (rt_vote.proc == DIAG_PROC_DCI) {
-			diag_dci_set_real_time(current->tgid,
-						rt_vote.real_time_vote);
+			diag_dci_set_real_time(rt_vote.real_time_vote);
 			real_time = diag_dci_get_cumulative_real_time();
 		} else {
 			real_time = rt_vote.real_time_vote;
@@ -1181,7 +1156,10 @@
 		pr_err("diag: Client PID not found in table");
 		return -EINVAL;
 	}
-
+	if (!buf) {
+		pr_err("diag: bad address from user side\n");
+		return -EFAULT;
+	}
 	wait_event_interruptible(driver->wait_q, driver->data_ready[index]);
 
 	mutex_lock(&driver->diagchar_mutex);
@@ -1389,32 +1367,55 @@
 		goto exit;
 	}
 
+	if (driver->data_ready[index] & DCI_PKT_TYPE) {
+		/* Copy the type of data being passed */
+		data_type = driver->data_ready[index] & DCI_PKT_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->dci_pkt_buf),
+					driver->dci_pkt_length);
+		driver->data_ready[index] ^= DCI_PKT_TYPE;
+		driver->in_busy_dcipktdata = 0;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & DCI_EVENT_MASKS_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & DCI_EVENT_MASKS_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, driver->num_dci_client, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+8, *(dci_cumulative_event_mask),
+							DCI_EVENT_MASK_SIZE);
+		driver->data_ready[index] ^= DCI_EVENT_MASKS_TYPE;
+		goto exit;
+	}
+
+	if (driver->data_ready[index] & DCI_LOG_MASKS_TYPE) {
+		/*Copy the type of data being passed*/
+		data_type = driver->data_ready[index] & DCI_LOG_MASKS_TYPE;
+		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+4, driver->num_dci_client, 4);
+		COPY_USER_SPACE_OR_EXIT(buf+8, *(dci_cumulative_log_mask),
+							DCI_LOG_MASK_SIZE);
+		driver->data_ready[index] ^= DCI_LOG_MASKS_TYPE;
+		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);
-		/* check the current client and copy its data */
-		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
-			entry = &(driver->dci_client_tbl[i]);
-			if (entry && entry->client) {
-				if (current->tgid == entry->client->tgid) {
-					mutex_lock(&entry->data_mutex);
-					COPY_USER_SPACE_OR_EXIT(buf+4,
-							entry->data_len, 4);
-					COPY_USER_SPACE_OR_EXIT(buf+8,
-					*(entry->dci_data), entry->data_len);
-					entry->data_len = 0;
-					mutex_unlock(&entry->data_mutex);
-					break;
-				}
-			}
-		}
 		driver->data_ready[index] ^= DCI_DATA_TYPE;
+		/* check the current client and copy its data */
+		entry = diag_dci_get_client_entry();
+		if (entry) {
+			if (!entry->in_service)
+				goto exit;
+			COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
+			exit_stat = diag_copy_dci(buf, count, entry, &ret);
+			if (exit_stat == 1)
+				goto exit;
+		}
 		for (i = 0; i < NUM_SMD_DCI_CHANNELS; i++) {
-			driver->smd_dci[i].in_busy_1 = 0;
 			if (driver->smd_dci[i].ch) {
-				diag_dci_try_deactivate_wakeup_source(
-						driver->smd_dci[i].ch);
 				queue_work(driver->diag_dci_wq,
 				&(driver->smd_dci[i].diag_read_smd_work));
 			}
@@ -1423,10 +1424,7 @@
 			for (i = 0; i < NUM_SMD_DCI_CMD_CHANNELS; i++) {
 				if (!driver->separate_cmdrsp[i])
 					continue;
-				driver->smd_dci_cmd[i].in_busy_1 = 0;
 				if (driver->smd_dci_cmd[i].ch) {
-					diag_dci_try_deactivate_wakeup_source(
-						driver->smd_dci_cmd[i].ch);
 					queue_work(driver->diag_dci_wq,
 						&(driver->smd_dci_cmd[i].
 							diag_read_smd_work));
@@ -1449,7 +1447,7 @@
 				size_t count, loff_t *ppos)
 {
 	int err, ret = 0, pkt_type, token_offset = 0;
-	int remote_proc = 0;
+	int remote_proc = 0, data_type;
 	uint8_t index;
 #ifdef DIAG_DEBUG
 	int length = 0, i;
@@ -1480,9 +1478,11 @@
 		return -EBADMSG;
 	}
 #ifdef CONFIG_DIAG_OVER_USB
-	if (((pkt_type != DCI_DATA_TYPE) && (driver->logging_mode == USB_MODE)
-				&& (!driver->usb_connected)) ||
-				(driver->logging_mode == NO_LOGGING_MODE)) {
+	if (driver->logging_mode == NO_LOGGING_MODE ||
+	    (!((pkt_type == DCI_DATA_TYPE) ||
+	       ((pkt_type & (DATA_TYPE_DCI_LOG | DATA_TYPE_DCI_EVENT)) == 0))
+		&& (driver->logging_mode == USB_MODE) &&
+		(!driver->usb_connected))) {
 		/*Drop the diag payload */
 		return -EIO;
 	}
@@ -1541,9 +1541,9 @@
 		}
 		/* The packet is for the remote processor */
 		if (payload_size <= MIN_SIZ_ALLOW) {
-				pr_err("diag: Integer underflow in %s, payload size: %d",
-							__func__, payload_size);
-		return -EBADMSG;
+			pr_err("diag: Integer underflow in %s, payload size: %d",
+					__func__, payload_size);
+			return -EBADMSG;
 		}
 		token_offset = 4;
 		payload_size -= 4;
@@ -1761,20 +1761,47 @@
 		return -EBADMSG;
 	}
 
-	mutex_lock(&driver->diagchar_mutex);
 	buf_copy = diagmem_alloc(driver, payload_size, POOL_TYPE_COPY);
 	if (!buf_copy) {
 		driver->dropped_count++;
-		mutex_unlock(&driver->diagchar_mutex);
 		return -ENOMEM;
 	}
 
 	err = copy_from_user(buf_copy, buf + 4, payload_size);
 	if (err) {
 		printk(KERN_INFO "diagchar : copy_from_user failed\n");
-		ret = -EFAULT;
-		goto fail_free_copy;
+		diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+		buf_copy = NULL;
+		return -EFAULT;
 	}
+
+	data_type = pkt_type &
+		    (DATA_TYPE_DCI_LOG | DATA_TYPE_DCI_EVENT | DCI_PKT_TYPE);
+	if (data_type) {
+		diag_process_apps_dci_read_data(data_type, buf_copy,
+						payload_size);
+		if (pkt_type & DATA_TYPE_DCI_LOG)
+			pkt_type ^= DATA_TYPE_DCI_LOG;
+		else if (pkt_type & DATA_TYPE_DCI_EVENT) {
+			pkt_type ^= DATA_TYPE_DCI_EVENT;
+		} else {
+			pkt_type ^= DCI_PKT_TYPE;
+			diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+			return 0;
+		}
+
+		/*
+		 * If the data is not headed for normal processing or the usb
+		 * is unplugged and we are in usb mode
+		 */
+		if ((pkt_type != DATA_TYPE_LOG && pkt_type != DATA_TYPE_EVENT)
+			|| ((driver->logging_mode == USB_MODE) &&
+			(!driver->usb_connected))) {
+			diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
+			return 0;
+		}
+	}
+
 	if (driver->stm_state[APPS_DATA] &&
 		(pkt_type >= DATA_TYPE_EVENT && pkt_type <= DATA_TYPE_LOG)) {
 		int stm_size = 0;
@@ -1809,6 +1836,7 @@
 			length++;
 	}
 #endif
+	mutex_lock(&driver->diagchar_mutex);
 	if (!buf_hdlc)
 		buf_hdlc = diagmem_alloc(driver, HDLC_OUT_BUF_SIZE,
 						 POOL_TYPE_HDLC);
@@ -1882,10 +1910,9 @@
 	diagmem_free(driver, buf_copy, POOL_TYPE_COPY);
 	buf_copy = NULL;
 	mutex_unlock(&driver->diagchar_mutex);
-	if (!timer_in_progress)	{
-		timer_in_progress = 1;
-		ret = mod_timer(&drain_timer, jiffies + msecs_to_jiffies(500));
-	}
+
+	check_drain_timer();
+
 	return 0;
 
 fail_free_hdlc:
@@ -2017,9 +2044,13 @@
 		return -1;
 	}
 
-	device_create(driver->diagchar_class, NULL, devno,
-				  (void *)driver, "diag");
+	driver->diag_dev = device_create(driver->diagchar_class, NULL, devno,
+					 (void *)driver, "diag");
 
+	if (!driver->diag_dev)
+		return -EIO;
+
+	driver->diag_dev->power.wakeup = wakeup_source_register("DIAG_WS");
 	return 0;
 
 }
@@ -2110,12 +2141,15 @@
 		driver->poolsize_user = poolsize_user;
 		driver->itemsize_write_struct = itemsize_write_struct;
 		driver->poolsize_write_struct = poolsize_write_struct;
+		driver->itemsize_dci = itemsize_dci;
+		driver->poolsize_dci = poolsize_dci;
 		driver->num_clients = max_clients;
 		driver->logging_mode = USB_MODE;
 		driver->socket_process = NULL;
 		driver->callback_process = NULL;
 		driver->mask_check = 0;
 		driver->in_busy_pktdata = 0;
+		driver->in_busy_dcipktdata = 0;
 		mutex_init(&driver->diagchar_mutex);
 		init_waitqueue_head(&driver->wait_q);
 		init_waitqueue_head(&driver->smd_wait_q);
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index f7e720f..eb8a75b 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, 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
@@ -43,9 +43,6 @@
 #include "diag_masks.h"
 #include "diagfwd_bridge.h"
 
-#define MODE_CMD		41
-#define RESET_ID		2
-
 #define STM_CMD_VERSION_OFFSET	4
 #define STM_CMD_MASK_OFFSET	5
 #define STM_CMD_DATA_OFFSET	6
@@ -695,15 +692,14 @@
 
 	if (!buf && (smd_info->type == SMD_DCI_TYPE ||
 					smd_info->type == SMD_DCI_CMD_TYPE))
-		diag_dci_try_deactivate_wakeup_source(smd_info->ch);
+		diag_dci_try_deactivate_wakeup_source();
 
 	if (smd_info->ch && buf) {
-		pkt_len = smd_cur_packet_size(smd_info->ch);
 
+		pkt_len = smd_cur_packet_size(smd_info->ch);
 		if (pkt_len == 0 && (smd_info->type == SMD_DCI_TYPE ||
 					smd_info->type == SMD_DCI_CMD_TYPE))
-			diag_dci_try_deactivate_wakeup_source(smd_info->ch);
-
+			diag_dci_try_deactivate_wakeup_source();
 		if (pkt_len > buf_size)
 			resize_success = diag_smd_resize_buf(smd_info, &buf,
 							&buf_size, pkt_len);
@@ -811,7 +807,7 @@
 fail_return:
 	if (smd_info->type == SMD_DCI_TYPE ||
 					smd_info->type == SMD_DCI_CMD_TYPE)
-		diag_dci_try_deactivate_wakeup_source(smd_info->ch);
+		diag_dci_try_deactivate_wakeup_source();
 	return;
 }
 
@@ -1049,17 +1045,46 @@
     return err;
 }
 
-static void diag_update_pkt_buffer(unsigned char *buf)
+void diag_update_pkt_buffer(unsigned char *buf, int type)
 {
-	unsigned char *ptr = driver->pkt_buf;
+	unsigned char *ptr = NULL;
 	unsigned char *temp = buf;
+	unsigned int length;
+	int *in_busy = NULL;
 
+	if (!buf) {
+		pr_err("diag: Invalid buffer in %s\n", __func__);
+		return;
+	}
+
+	switch (type) {
+	case PKT_TYPE:
+		ptr = driver->pkt_buf;
+		length = driver->pkt_length;
+		in_busy = &driver->in_busy_pktdata;
+		break;
+	case DCI_PKT_TYPE:
+		ptr = driver->dci_pkt_buf;
+		length = driver->dci_pkt_length;
+		in_busy = &driver->in_busy_dcipktdata;
+		break;
+	default:
+		pr_err("diag: Invalid type %d in %s\n", type, __func__);
+		return;
+	}
+
+	if (!ptr || length == 0) {
+		pr_err("diag: Invalid ptr %p and length %d in %s",
+						ptr, length, __func__);
+		return;
+	}
 	mutex_lock(&driver->diagchar_mutex);
-	if (CHK_OVERFLOW(ptr, ptr, ptr + PKT_SIZE, driver->pkt_length)) {
-		memcpy(ptr, temp , driver->pkt_length);
-		driver->in_busy_pktdata = 1;
-	} else
+	if (CHK_OVERFLOW(ptr, ptr, ptr + PKT_SIZE, length)) {
+		memcpy(ptr, temp , length);
+		*in_busy = 1;
+	} else {
 		printk(KERN_CRIT " Not enough buffer space for PKT_RESP\n");
+	}
 	mutex_unlock(&driver->diagchar_mutex);
 }
 
@@ -1108,7 +1133,7 @@
 	if (entry.process_id != NON_APPS_PROC) {
 		/* If the message is to be sent to the apps process */
 		if (type != MODEM_DATA) {
-			diag_update_pkt_buffer(buf);
+			diag_update_pkt_buffer(buf, PKT_TYPE);
 			diag_update_sleeping_process(entry.process_id,
 							PKT_TYPE);
 		}
@@ -2191,7 +2216,7 @@
 	if (smd_info->type == SMD_DCI_TYPE ||
 					smd_info->type == SMD_DCI_CMD_TYPE) {
 		if (event == SMD_EVENT_DATA)
-			diag_dci_try_activate_wakeup_source(smd_info->ch);
+			diag_dci_try_activate_wakeup_source();
 		queue_work(driver->diag_dci_wq,
 				&(smd_info->diag_read_smd_work));
 	} else if (smd_info->type == SMD_DATA_TYPE) {
@@ -2659,6 +2684,12 @@
 			 GFP_KERNEL)) == NULL)
 		goto err;
 	kmemleak_not_leak(driver->pkt_buf);
+	if (driver->dci_pkt_buf == NULL) {
+		driver->dci_pkt_buf = kzalloc(PKT_SIZE, GFP_KERNEL);
+		if (!driver->dci_pkt_buf)
+			goto err;
+	}
+	kmemleak_not_leak(driver->dci_pkt_buf);
 	if (driver->apps_rsp_buf == NULL) {
 		driver->apps_rsp_buf = kzalloc(APPS_BUF_SIZE, GFP_KERNEL);
 		if (driver->apps_rsp_buf == NULL)
@@ -2709,6 +2740,7 @@
 	kfree(driver->data_ready);
 	kfree(driver->table);
 	kfree(driver->pkt_buf);
+	kfree(driver->dci_pkt_buf);
 	kfree(driver->usb_read_ptr);
 	kfree(driver->apps_rsp_buf);
 	kfree(driver->user_space_data_buf);
@@ -2749,6 +2781,7 @@
 	kfree(driver->data_ready);
 	kfree(driver->table);
 	kfree(driver->pkt_buf);
+	kfree(driver->dci_pkt_buf);
 	kfree(driver->usb_read_ptr);
 	kfree(driver->apps_rsp_buf);
 	kfree(driver->user_space_data_buf);
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index 1a4601a..7b2ded3 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, 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
@@ -54,6 +54,8 @@
 int diag_process_apps_pkt(unsigned char *buf, int len);
 void diag_reset_smd_data(int queue);
 int diag_apps_responds(void);
+void diag_update_pkt_buffer(unsigned char *buf, int type);
+int diag_process_stm_cmd(unsigned char *buf, unsigned char *dest_buf);
 /* State for diag forwarding */
 #ifdef CONFIG_DIAG_OVER_USB
 int diagfwd_connect(void);
diff --git a/drivers/char/diag/diagmem.c b/drivers/char/diag/diagmem.c
index 4ceca4f..db01e9b 100644
--- a/drivers/char/diag/diagmem.c
+++ b/drivers/char/diag/diagmem.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, 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
@@ -70,6 +70,16 @@
 				driver->diag_write_struct_pool, GFP_ATOMIC);
 			}
 		}
+	} else if (pool_type == POOL_TYPE_DCI) {
+		if (driver->diag_dci_pool) {
+			if ((driver->count_dci_pool < driver->poolsize_dci) &&
+				(size <= driver->itemsize_dci)) {
+				atomic_add(1,
+					(atomic_t *)&driver->count_dci_pool);
+				buf = mempool_alloc(driver->diag_dci_pool,
+								GFP_ATOMIC);
+			}
+		}
 #ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 	} else if (pool_type == POOL_TYPE_HSIC ||
 				pool_type == POOL_TYPE_HSIC_2) {
@@ -155,6 +165,16 @@
 			pr_err("diag: Unable to destroy STRUCT mempool");
 		}
 	}
+
+	if (driver->diag_dci_pool) {
+		if (driver->count_dci_pool == 0 && driver->ref_count == 0) {
+			mempool_destroy(driver->diag_dci_pool);
+			driver->diag_dci_pool = NULL;
+		} else if (driver->ref_count == 0 && pool_type ==
+							POOL_TYPE_ALL) {
+				pr_err("diag: Unable to destroy DCI mempool");
+		}
+	}
 #ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 	for (index = 0; index < MAX_HSIC_CH; index++) {
 		if (diag_hsic[index].diag_hsic_pool &&
@@ -231,6 +251,15 @@
 		} else
 			pr_err("diag: Attempt to free up DIAG driver USB structure mempool which is already free %d ",
 					driver->count_write_struct_pool);
+	} else if (pool_type == POOL_TYPE_DCI) {
+		if (driver->diag_dci_pool != NULL &&
+			driver->count_dci_pool > 0) {
+				mempool_free(buf, driver->diag_dci_pool);
+				atomic_add(-1,
+					(atomic_t *)&driver->count_dci_pool);
+		} else
+			pr_err("diag: Attempt to free up DIAG driver DCI mempool which is already free %d ",
+					driver->count_dci_pool);
 #ifdef CONFIG_DIAGFWD_BRIDGE_CODE
 	} else if (pool_type == POOL_TYPE_HSIC ||
 				pool_type == POOL_TYPE_HSIC_2) {
@@ -294,6 +323,12 @@
 						driver->diag_write_struct_pool;
 	}
 
+	if (driver->count_dci_pool == 0) {
+		driver->diag_dci_pool = mempool_create_kmalloc_pool(
+			driver->poolsize_dci, driver->itemsize_dci);
+		diag_pools_array[POOL_DCI_IDX] = driver->diag_dci_pool;
+	}
+
 	if (!driver->diagpool)
 		pr_err("diag: Cannot allocate diag mempool\n");
 
@@ -305,6 +340,10 @@
 
 	if (!driver->diag_write_struct_pool)
 		pr_err("diag: Cannot allocate diag USB struct mempool\n");
+
+	if (!driver->diag_dci_pool)
+		pr_err("diag: Cannot allocate diag DCI mempool\n");
+
 }
 
 #ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index f78d418..1e15415 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2013, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2008-2014, 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
@@ -13,14 +13,18 @@
 #ifndef DIAGCHAR_SHARED
 #define DIAGCHAR_SHARED
 
-#define MSG_MASKS_TYPE			1
-#define LOG_MASKS_TYPE			2
-#define EVENT_MASKS_TYPE		4
-#define PKT_TYPE			8
-#define DEINIT_TYPE			16
-#define USER_SPACE_DATA_TYPE		32
-#define DCI_DATA_TYPE			64
-#define CALLBACK_DATA_TYPE		128
+#define MSG_MASKS_TYPE		0x00000001
+#define LOG_MASKS_TYPE		0x00000002
+#define EVENT_MASKS_TYPE	0x00000004
+#define PKT_TYPE		0x00000008
+#define DEINIT_TYPE		0x00000010
+#define USER_SPACE_DATA_TYPE	0x00000020
+#define DCI_DATA_TYPE		0x00000040
+#define CALLBACK_DATA_TYPE	0x00000080
+#define DCI_LOG_MASKS_TYPE	0x00000100
+#define DCI_EVENT_MASKS_TYPE	0x00000200
+#define DCI_PKT_TYPE		0x00000400
+
 #define USB_MODE			1
 #define MEMORY_DEVICE_MODE		2
 #define NO_LOGGING_MODE			3
@@ -32,6 +36,8 @@
 #define DATA_TYPE_F3            	1
 #define DATA_TYPE_LOG           	2
 #define DATA_TYPE_RESPONSE      	3
+#define DATA_TYPE_DCI_LOG		0x00000100
+#define DATA_TYPE_DCI_EVENT		0x00000200
 
 /* Different IOCTL values */
 #define DIAG_IOCTL_COMMAND_REG  	0