diag: Add logs & events streaming support for DCI

Diag driver is adding a new feature to stream logs & events to a
user space application on Linux processor. This will eliminate
need to connect QXDM to receive diag traffic.
This is known as Diag Consumer Interface (DCI).

Change-Id: I7983bb7d9b8a0429ebbee290868c8bd0e562953f
Signed-off-by: Shalabh Jain <shalabhj@codeaurora.org>
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 2860055..7c0c0b9 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -28,17 +28,23 @@
 #include "diagmem.h"
 #include "diagchar.h"
 #include "diagfwd.h"
+#include "diagfwd_cntl.h"
 #include "diag_dci.h"
 
 unsigned int dci_max_reg = 100;
 unsigned int dci_max_clients = 10;
+unsigned char dci_cumulative_log_mask[DCI_LOG_MASK_SIZE];
+unsigned char dci_cumulative_event_mask[DCI_EVENT_MASK_SIZE];
+
+#define DCI_CHK_CAPACITY(entry, new_data_len)				\
+((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0)	\
 
 static void diag_smd_dci_send_req(int proc_num)
 {
 	void *buf = NULL;
 	smd_channel_t *smd_ch = NULL;
-	int i, r, found = 1;
-	int cmd_code_len = 1;
+	int recd_bytes, read_bytes, dci_pkt_len, i;
+	uint8_t recv_pkt_cmd_code;
 
 	if (driver->in_busy_dci)
 		return;
@@ -51,58 +57,237 @@
 	if (!smd_ch || !buf)
 		return;
 
-	r = smd_read_avail(smd_ch);
-	if (r > IN_BUF_SIZE) {
-		if (r < MAX_IN_BUF_SIZE) {
-			pr_err("diag: SMD DCI sending pkt upto %d bytes", r);
-			buf = krealloc(buf, r, GFP_KERNEL);
+	recd_bytes = smd_read_avail(smd_ch);
+	if (recd_bytes > IN_BUF_SIZE) {
+		if (recd_bytes < MAX_IN_BUF_SIZE) {
+			pr_err("diag: SMD DCI sending pkt upto %d bytes",
+				recd_bytes);
+			buf = krealloc(buf, recd_bytes, GFP_KERNEL);
 		} else {
 			pr_err("diag: DCI pkt > %d bytes", MAX_IN_BUF_SIZE);
 			return;
 		}
 	}
-	if (buf && r > 0) {
-		smd_read(smd_ch, buf, r);
-		pr_debug("diag: data received ---\n");
-		for (i = 0; i < r; i++)
-			pr_debug("\t %x \t", *(((unsigned char *)buf)+i));
+	if (buf && recd_bytes > 0) {
+		smd_read(smd_ch, buf, recd_bytes);
+		pr_debug("diag: data received %d bytes\n", recd_bytes);
+		/* Each SMD read can have multiple DCI packets */
+		read_bytes = 0;
+		while (read_bytes < recd_bytes) {
+			/* read actual length of dci pkt */
+			dci_pkt_len = *(uint16_t *)(buf+2);
+			/* process one dci packet */
+			pr_debug("diag: bytes read = %d, single dci pkt len = %d\n",
+				read_bytes, dci_pkt_len);
+			/* print_hex_dump(KERN_DEBUG, "Single DCI packet :",
+			 DUMP_PREFIX_ADDRESS, 16, 1, buf, 5 + dci_pkt_len, 1);*/
+			recv_pkt_cmd_code = *(uint8_t *)(buf+4);
+			if (recv_pkt_cmd_code == LOG_CMD_CODE)
+				extract_dci_log(buf+4);
+			else if (recv_pkt_cmd_code == EVENT_CMD_CODE)
+				extract_dci_events(buf+4);
+			else
+				extract_dci_pkt_rsp(buf); /* pkt response */
+			read_bytes += 5 + dci_pkt_len;
+			buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
+		}
+		driver->in_busy_dci = 1;
+		/* 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)
+				diag_update_sleeping_process(
+					driver->dci_client_tbl[i].client->tgid,
+						 DCI_DATA_TYPE);
+	}
+}
 
-		if (*(uint8_t *)(buf+4) != DCI_CMD_CODE)
-			cmd_code_len = 4; /* delayed response */
-		driver->write_ptr_dci->length =
-			 (int)(*(uint16_t *)(buf+2)) - (4+cmd_code_len);
-		pr_debug("diag: len = %d\n", (int)(*(uint16_t *)(buf+2))
-							 - (4+cmd_code_len));
-		/* look up DCI client with tag */
-		for (i = 0; i < dci_max_reg; i++) {
-			if (driver->dci_tbl[i].tag ==
-			    *(int *)(buf+(4+cmd_code_len))) {
-				found = 0;
-				break;
+void extract_dci_pkt_rsp(unsigned char *buf)
+{
+	int i = 0, index = -1, cmd_code_len = 1;
+	int curr_client_pid = 0, write_len;
+	struct diag_dci_client_tbl *entry;
+	void *temp_buf = NULL;
+	uint8_t recv_pkt_cmd_code;
+
+	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;
+	pr_debug("diag: len = %d\n", write_len);
+	/* look up DCI client with tag */
+	for (i = 0; i < dci_max_reg; i++) {
+		if (driver->req_tracking_tbl[i].tag ==
+					 *(int *)(buf+(4+cmd_code_len))) {
+			*(int *)(buf+4+cmd_code_len) =
+					driver->req_tracking_tbl[i].uid;
+			curr_client_pid =
+					 driver->req_tracking_tbl[i].pid;
+			index = i;
+			break;
+		}
+	}
+	if (index == -1)
+		pr_alert("diag: No matching PID for DCI data\n");
+	/* Using PID of client process, find client buffer */
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		if (curr_client_pid == driver->dci_client_tbl[i].client->tgid) {
+			/* copy pkt rsp in client buf */
+			entry = &(driver->dci_client_tbl[i]);
+			if (DCI_CHK_CAPACITY(entry, 8+write_len)) {
+				pr_alert("diag: create capacity for pkt rsp\n");
+				entry->total_capacity += 8+write_len;
+				temp_buf = krealloc(entry->dci_data,
+					 entry->total_capacity, GFP_KERNEL);
+				if (!temp_buf) {
+					pr_err("diag: DCI realloc failed\n");
+					break;
+				} 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;
+			memcpy(entry->dci_data+entry->data_len,
+				 buf+4+cmd_code_len, write_len);
+			entry->data_len += write_len;
+			/* delete immediate response entry */
+			if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
+				driver->req_tracking_tbl[index].pid = 0;
+			break;
+		}
+	}
+}
+
+void extract_dci_events(unsigned char *buf)
+{
+	uint16_t event_id, event_id_packet;
+	uint8_t *event_mask_ptr, byte_mask, payload_len;
+	uint8_t event_data[MAX_EVENT_SIZE], timestamp[8];
+	int i, byte_index, bit_index, length, temp_len;
+	int total_event_len, payload_len_field, timestamp_len;
+	struct diag_dci_client_tbl *entry;
+
+	length =  *(uint16_t *)(buf+1); /* total length of event series */
+	temp_len = 0;
+	buf = buf + 3; /* start of event series */
+	while (temp_len < length-1) {
+		*event_data = EVENT_CMD_CODE;
+		event_id_packet = *(uint16_t *)(buf+temp_len);
+		event_id = event_id_packet & 0x0FFF; /* extract 12 bits */
+		if (event_id_packet & 0x8000) {
+			timestamp_len = 2;
+		} else {
+			timestamp_len = 8;
+			memcpy(timestamp, buf+temp_len+2, 8);
+		}
+		if (((event_id_packet & 0x6000) >> 13) == 3) {
+			payload_len_field = 1;
+			payload_len = *(uint8_t *)
+					(buf+temp_len+2+timestamp_len);
+			memcpy(event_data+13, buf+temp_len+2+timestamp_len, 1);
+			memcpy(event_data+14, buf+temp_len+2+timestamp_len+1,
+								 payload_len);
+		} else {
+			payload_len_field = 0;
+			payload_len = (event_id_packet & 0x6000) >> 13;
+			if (payload_len < MAX_EVENT_SIZE)
+				memcpy(event_data+13,
+				 buf+temp_len+2+timestamp_len, payload_len);
+			else
+				pr_alert("diag: event > %d\n", MAX_EVENT_SIZE);
+		}
+		/* 2 bytes for the event id & timestamp len is hard coded to 8,
+		   as individual events have full timestamp */
+		*(uint16_t *)(event_data+1) = 10+payload_len_field+payload_len;
+		*(uint16_t *)(event_data+3) = event_id_packet & 0x7FFF;
+		memcpy(event_data+5, timestamp, 8);
+		total_event_len = 3 + 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;
+				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++;
+						return;
+					}
+					*(int *)(entry->dci_data+
+					entry->data_len) = DCI_EVENT_TYPE;
+					memcpy(entry->dci_data+
+				entry->data_len+4, event_data, total_event_len);
+					entry->data_len += 4 + total_event_len;
+				}
 			}
 		}
-		if (found)
-			pr_alert("diag: No matching PID for DCI data\n");
-		pr_debug("\n diag PID = %d", driver->dci_tbl[i].pid);
-		if (driver->dci_tbl[i].pid == 0)
-			pr_alert("diag: Receiving DCI process deleted\n");
-		*(int *)(buf+4+cmd_code_len) = driver->dci_tbl[i].uid;
-		/* update len after adding UID */
-		driver->write_ptr_dci->length =
-			driver->write_ptr_dci->length + 4;
-		pr_debug("diag: data receivd, wake process\n");
-		driver->in_busy_dci = 1;
-		diag_update_sleeping_process(driver->dci_tbl[i].pid,
-							DCI_DATA_TYPE);
-		/* delete immediate response entry */
-		if (driver->buf_in_dci[8+cmd_code_len] != 0x80)
-			driver->dci_tbl[i].pid = 0;
-		for (i = 0; i < dci_max_reg; i++)
-			if (driver->dci_tbl[i].pid != 0)
-				pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
-				driver->dci_tbl[i].pid, driver->dci_tbl[i].uid,
-				 driver->dci_tbl[i].tag);
-		pr_debug("diag: completed clearing table\n");
+		temp_len += 2 + timestamp_len + payload_len_field + payload_len;
+	}
+}
+
+void extract_dci_log(unsigned char *buf)
+{
+	uint16_t log_code, item_num;
+	uint8_t equip_id, *log_mask_ptr, byte_mask;
+	int i, byte_index, found = 0;
+	struct diag_dci_client_tbl *entry;
+
+	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);
+
+	/* 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;
+			found = 0;
+			while (log_mask_ptr) {
+				if (*log_mask_ptr == equip_id) {
+					found = 1;
+					pr_debug("diag: find equip id = %x at %p\n",
+					equip_id, log_mask_ptr);
+					break;
+				} else {
+					pr_debug("diag: did not find equip id = %x at %p\n",
+						 equip_id, log_mask_ptr);
+					log_mask_ptr += 514;
+				}
+			}
+			if (!found)
+				pr_err("diag: dci equip id not found\n");
+			log_mask_ptr = log_mask_ptr + byte_index;
+			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++;
+						return;
+				}
+				*(int *)(entry->dci_data+entry->data_len) =
+								DCI_LOG_TYPE;
+				memcpy(entry->dci_data+entry->data_len+4, buf+4,
+						 *(uint16_t *)(buf+2));
+				entry->data_len += 4 + *(uint16_t *)(buf+2);
+			}
+		}
 	}
 }
 
@@ -113,7 +298,7 @@
 
 static void diag_smd_dci_notify(void *ctxt, unsigned event)
 {
-	queue_work(driver->diag_wq, &(driver->diag_read_smd_dci_work));
+	queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
 }
 
 void diag_dci_notify_client(int peripheral_mask)
@@ -121,11 +306,11 @@
 	int i, stat;
 
 	/* Notify the DCI process that the peripheral DCI Channel is up */
-	for (i = 0; i < MAX_DCI_CLIENT; i++) {
-		if (driver->dci_notify_tbl[i].list & peripheral_mask) {
-			pr_debug("diag: sending signal now\n");
-			stat = send_sig(driver->dci_notify_tbl[i].signal_type,
-					 driver->dci_notify_tbl[i].client, 0);
+	for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+		if (driver->dci_client_tbl[i].list & peripheral_mask) {
+			pr_info("diag: sending signal now\n");
+			stat = send_sig(driver->dci_client_tbl[i].signal_type,
+					 driver->dci_client_tbl[i].client, 0);
 			if (stat)
 				pr_err("diag: Err send sig stat: %d\n", stat);
 			break;
@@ -139,7 +324,7 @@
 
 	if (pdev->id == SMD_APPS_MODEM) {
 		err = smd_open("DIAG_2", &driver->ch_dci, driver,
-					    diag_smd_dci_notify);
+						diag_smd_dci_notify);
 		if (err)
 			pr_err("diag: cannot open DCI port, Id = %d, err ="
 				" %d\n", pdev->id, err);
@@ -163,10 +348,12 @@
 	driver->apps_dci_buf[0] = CONTROL_CHAR; /* start */
 	driver->apps_dci_buf[1] = 1; /* version */
 	*(uint16_t *)(driver->apps_dci_buf + 2) = len + 4 + 1; /* length */
-	driver->apps_dci_buf[4] = DCI_CMD_CODE; /* DCI ID */
-	*(int *)(driver->apps_dci_buf + 5) = driver->dci_tbl[index].tag;
+	driver->apps_dci_buf[4] = DCI_PKT_RSP_CODE;
+	*(int *)(driver->apps_dci_buf + 5) =
+		driver->req_tracking_tbl[index].tag;
 	for (i = 0; i < len; i++)
 		driver->apps_dci_buf[i+9] = *(buf+i);
+
 	driver->apps_dci_buf[9+len] = CONTROL_CHAR; /* end */
 
 	if (entry.client_id == MODEM_PROC && driver->ch_dci) {
@@ -185,7 +372,7 @@
 	int i, new_dci_client = 1, ret = -1;
 
 	for (i = 0; i < dci_max_reg; i++) {
-		if (driver->dci_tbl[i].pid == current->tgid) {
+		if (driver->req_tracking_tbl[i].pid == current->tgid) {
 			new_dci_client = 0;
 			break;
 		}
@@ -193,7 +380,7 @@
 	mutex_lock(&driver->dci_mutex);
 	if (new_dci_client)
 		driver->num_dci_client++;
-	if (driver->num_dci_client > MAX_DCI_CLIENT) {
+	if (driver->num_dci_client > MAX_DCI_CLIENTS) {
 		pr_info("diag: Max DCI Client limit reached\n");
 		driver->num_dci_client--;
 		mutex_unlock(&driver->dci_mutex);
@@ -202,10 +389,10 @@
 	/* Make an entry in kernel DCI table */
 	driver->dci_tag++;
 	for (i = 0; i < dci_max_reg; i++) {
-		if (driver->dci_tbl[i].pid == 0) {
-			driver->dci_tbl[i].pid = current->tgid;
-			driver->dci_tbl[i].uid = uid;
-			driver->dci_tbl[i].tag = driver->dci_tag;
+		if (driver->req_tracking_tbl[i].pid == 0) {
+			driver->req_tracking_tbl[i].pid = current->tgid;
+			driver->req_tracking_tbl[i].uid = uid;
+			driver->req_tracking_tbl[i].tag = driver->dci_tag;
 			ret = i;
 			break;
 		}
@@ -214,62 +401,313 @@
 	return ret;
 }
 
-int diag_process_dci_client(unsigned char *buf, int len)
+int diag_process_dci_transaction(unsigned char *buf, int len)
 {
 	unsigned char *temp = buf;
-	uint16_t subsys_cmd_code;
-	int subsys_id, cmd_code, i, ret = -1, index = -1;
+	uint16_t subsys_cmd_code, log_code, item_num;
+	int subsys_id, cmd_code, i, ret = -1, index = -1, found = 0;
 	struct diag_master_table entry;
+	int count, set_mask, num_codes, byte_index, bit_index, event_id;
+	uint8_t equip_id, *log_mask_ptr, *head_log_mask_ptr, byte_mask;
+	uint8_t *event_mask_ptr;
 
-	/* enter this UID into kernel table and return index */
-	index = diag_register_dci_transaction(*(int *)temp);
-	if (index < 0) {
-		pr_alert("diag: registering new DCI transaction failed\n");
-		return DIAG_DCI_NO_REG;
-	}
-	temp += 4;
-	/* Check for registered peripheral and fwd pkt to apropriate proc */
-	cmd_code = (int)(*(char *)buf);
-	temp++;
-	subsys_id = (int)(*(char *)temp);
-	temp++;
-	subsys_cmd_code = *(uint16_t *)temp;
-	temp += 2;
-	pr_debug("diag: %d %d %d", cmd_code, subsys_id, subsys_cmd_code);
-	for (i = 0; i < diag_max_reg; i++) {
-		entry = driver->table[i];
-		if (entry.process_id != NO_PROCESS) {
-			if (entry.cmd_code == cmd_code && entry.subsys_id ==
-				 subsys_id && entry.cmd_code_lo <=
-							 subsys_cmd_code &&
-				  entry.cmd_code_hi >= subsys_cmd_code) {
-				ret = diag_send_dci_pkt(entry, buf, len, index);
-			} else if (entry.cmd_code == 255
-				  && cmd_code == 75) {
-				if (entry.subsys_id ==
-					subsys_id &&
-				   entry.cmd_code_lo <=
-					subsys_cmd_code &&
-					 entry.cmd_code_hi >=
-					subsys_cmd_code) {
-					ret = diag_send_dci_pkt(entry, buf, len,
-								 index);
-				}
-			} else if (entry.cmd_code == 255 &&
-				  entry.subsys_id == 255) {
-				if (entry.cmd_code_lo <=
-						 cmd_code &&
-						 entry.
-						cmd_code_hi >= cmd_code) {
-					ret = diag_send_dci_pkt(entry, buf, len,
-								 index);
+	/* This is Pkt request/response transaction */
+	if (*(int *)temp > 0) {
+		/* enter this UID into kernel table and return index */
+		index = diag_register_dci_transaction(*(int *)temp);
+		if (index < 0) {
+			pr_alert("diag: registering new DCI transaction failed\n");
+			return DIAG_DCI_NO_REG;
+		}
+		temp += 4;
+		/*
+		 * Check for registered peripheral and fwd pkt to
+		 * appropriate proc
+		 */
+		cmd_code = (int)(*(char *)temp);
+		temp++;
+		subsys_id = (int)(*(char *)temp);
+		temp++;
+		subsys_cmd_code = *(uint16_t *)temp;
+		temp += 2;
+		pr_debug("diag: %d %d %d", cmd_code, subsys_id,
+			subsys_cmd_code);
+		for (i = 0; i < diag_max_reg; i++) {
+			entry = driver->table[i];
+			if (entry.process_id != NO_PROCESS) {
+				if (entry.cmd_code == cmd_code &&
+					entry.subsys_id == subsys_id &&
+					entry.cmd_code_lo <= subsys_cmd_code &&
+					entry.cmd_code_hi >= subsys_cmd_code) {
+					ret = diag_send_dci_pkt(entry, buf,
+								len, index);
+				} else if (entry.cmd_code == 255
+					  && cmd_code == 75) {
+					if (entry.subsys_id == subsys_id &&
+						entry.cmd_code_lo <=
+						subsys_cmd_code &&
+						entry.cmd_code_hi >=
+						subsys_cmd_code) {
+						ret = diag_send_dci_pkt(entry,
+							buf, len, index);
+					}
+				} else if (entry.cmd_code == 255 &&
+					entry.subsys_id == 255) {
+					if (entry.cmd_code_lo <= cmd_code &&
+						entry.cmd_code_hi >=
+							cmd_code) {
+						ret = diag_send_dci_pkt(entry,
+							buf, len, index);
+					}
 				}
 			}
 		}
+	} else if (*(int *)temp == DCI_LOG_TYPE) {
+		/* find client id and table */
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			if (driver->dci_client_tbl[i].client->tgid ==
+							 current->tgid) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			pr_err("diag: dci client not registered/found\n");
+			return ret;
+		}
+		/* Extract each log code and put in client table */
+		temp += 4;
+		set_mask = *(int *)temp;
+		temp += 4;
+		num_codes = *(int *)temp;
+		temp += 4;
+
+		head_log_mask_ptr = driver->dci_client_tbl[i].dci_log_mask;
+		pr_info("diag: head of dci log mask %p\n", head_log_mask_ptr);
+		count = 0; /* iterator for extracting log codes */
+		while (count < num_codes) {
+			log_code = *(uint16_t *)temp;
+			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);
+			/*
+			 * Parse through log mask table and find
+			 * relevant range
+			 */
+			log_mask_ptr = head_log_mask_ptr;
+			found = 0;
+			while (log_mask_ptr) {
+				if (*log_mask_ptr == equip_id) {
+					found = 1;
+					pr_info("diag: find equip id = %x at %p\n",
+						 equip_id, log_mask_ptr);
+					break;
+				} else {
+					pr_info("diag: did not find equip id = %x at %p\n",
+						 equip_id, log_mask_ptr);
+					log_mask_ptr += 514;
+				}
+			}
+			if (!found) {
+				pr_err("diag: dci equip id not found\n");
+				return ret;
+			}
+			*(log_mask_ptr+1) = 1; /* set the dirty byte */
+			log_mask_ptr = log_mask_ptr + byte_index;
+			if (set_mask)
+				*log_mask_ptr |= byte_mask;
+			else
+				*log_mask_ptr &= ~byte_mask;
+			temp += 2;
+			count++;
+			ret = DIAG_DCI_NO_ERROR;
+		}
+		/* add to cumulative mask */
+		update_dci_cumulative_log_mask(i);
+		/* send updated mask to peripherals */
+		diag_send_dci_log_mask(driver->ch_cntl);
+	} else if (*(int *)temp == DCI_EVENT_TYPE) {
+		/* find client id and table */
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			if (driver->dci_client_tbl[i].client->tgid ==
+							 current->tgid) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			pr_err("diag: dci client not registered/found\n");
+			return ret;
+		}
+		/* Extract each log code and put in client table */
+		temp += 4;
+		set_mask = *(int *)temp;
+		temp += 4;
+		num_codes = *(int *)temp;
+		temp += 4;
+
+		event_mask_ptr = driver->dci_client_tbl[i].dci_event_mask;
+		pr_debug("diag: head of dci event mask %p\n", event_mask_ptr);
+		count = 0; /* iterator for extracting log codes */
+		while (count < num_codes) {
+			event_id = *(int *)temp;
+			byte_index = event_id/8;
+			bit_index = event_id % 8;
+			byte_mask = 0x1 << bit_index;
+			/*
+			 * Parse through event mask table and set
+			 * relevant byte & bit combination
+			 */
+			if (set_mask)
+				*(event_mask_ptr + byte_index) |= byte_mask;
+			else
+				*(event_mask_ptr + byte_index) &= ~byte_mask;
+			temp += sizeof(int);
+			count++;
+			ret = DIAG_DCI_NO_ERROR;
+		}
+		/* add to cumulative mask */
+		update_dci_cumulative_event_mask(i);
+		/* send updated mask to peripherals */
+		diag_send_dci_event_mask(driver->ch_cntl);
+	} else {
+		pr_alert("diag: Incorrect DCI transaction\n");
 	}
 	return ret;
 }
 
+void update_dci_cumulative_event_mask(int client_index)
+{
+	int i;
+	uint8_t *update_ptr = dci_cumulative_event_mask;
+	uint8_t *event_mask_ptr;
+
+	event_mask_ptr = driver->dci_client_tbl[client_index].dci_event_mask;
+	for (i = 0; i < DCI_EVENT_MASK_SIZE; i++)
+		*(update_ptr+i) |= *(event_mask_ptr+i);
+}
+
+void diag_send_dci_event_mask(smd_channel_t *ch)
+{
+	void *buf = driver->buf_event_mask_update;
+	int header_size = sizeof(struct diag_ctrl_event_mask);
+	int wr_size = -ENOMEM, retry_count = 0, timer;
+
+	mutex_lock(&driver->diag_cntl_mutex);
+	/* send event mask update */
+	driver->event_mask->cmd_type = DIAG_CTRL_MSG_EVENT_MASK;
+	driver->event_mask->data_len = 7 + DCI_EVENT_MASK_SIZE;
+	driver->event_mask->stream_id = DCI_MASK_STREAM;
+	driver->event_mask->status = 3; /* status for valid mask */
+	driver->event_mask->event_config = 1; /* event config */
+	driver->event_mask->event_mask_size = DCI_EVENT_MASK_SIZE;
+	memcpy(buf, driver->event_mask, header_size);
+	memcpy(buf+header_size, dci_cumulative_event_mask, DCI_EVENT_MASK_SIZE);
+	if (ch) {
+		while (retry_count < 3) {
+			wr_size = smd_write(ch, buf,
+					 header_size + DCI_EVENT_MASK_SIZE);
+			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);
+	} else
+		pr_err("diag: ch not valid for dci event mask update\n");
+	mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void update_dci_cumulative_log_mask(int client_index)
+{
+	int i, j;
+	uint8_t *update_ptr = dci_cumulative_log_mask;
+	uint8_t *log_mask_ptr =
+	driver->dci_client_tbl[client_index].dci_log_mask;
+
+	*update_ptr = 0; /* add first equip id */
+	/* skip the first equip id */
+	update_ptr++; log_mask_ptr++;
+	for (i = 0; i < 16; i++) {
+		for (j = 0; j < 513; j++) {
+			*update_ptr |= *log_mask_ptr;
+			update_ptr++;
+			log_mask_ptr++;
+		}
+		*update_ptr = i+1;
+		update_ptr++;
+		log_mask_ptr++;
+	}
+}
+
+void diag_send_dci_log_mask(smd_channel_t *ch)
+{
+	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;
+
+	mutex_lock(&driver->diag_cntl_mutex);
+	for (i = 0; i < 16; i++) {
+		driver->log_mask->cmd_type = DIAG_CTRL_MSG_LOG_MASK;
+		driver->log_mask->num_items = 512;
+		driver->log_mask->data_len  = 11 + 512;
+		driver->log_mask->stream_id = DCI_MASK_STREAM;
+		driver->log_mask->status = 3; /* status for valid mask */
+		driver->log_mask->equip_id = *log_mask_ptr;
+		driver->log_mask->log_mask_size = 512;
+		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 (ch && *(log_mask_ptr+1)) {
+			while (retry_count < 3) {
+				wr_size = smd_write(ch, buf, header_size + 512);
+				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",
+					 wr_size, header_size + 512);
+			else {
+				*(log_mask_ptr+1) = 0; /* clear dirty byte */
+				pr_debug("diag: updated dci log equip ID %d\n",
+						 *log_mask_ptr);
+			}
+		}
+		log_mask_ptr += 514;
+	}
+	mutex_unlock(&driver->diag_cntl_mutex);
+}
+
+void create_dci_log_mask_tbl(unsigned char *tbl_buf)
+{
+	uint8_t i; int count = 0;
+
+	/* create hard coded table for log mask with 16 categories */
+	for (i = 0; i < 16; i++) {
+		*(uint8_t *)tbl_buf = i;
+		pr_debug("diag: put value %x at %p\n", i, tbl_buf);
+		memset(tbl_buf+1, 0, 513); /* set dirty bit as 0 */
+		tbl_buf += 514;
+		count += 514;
+	}
+}
+
+void create_dci_event_mask_tbl(unsigned char *tbl_buf)
+{
+	memset(tbl_buf, 0, 512);
+}
+
 static int diag_dci_runtime_suspend(struct device *dev)
 {
 	dev_dbg(dev, "pm_runtime: suspending...\n");
@@ -290,10 +728,10 @@
 struct platform_driver msm_diag_dci_driver = {
 	.probe = diag_dci_probe,
 	.driver = {
-		   .name = "DIAG_2",
-		   .owner = THIS_MODULE,
-		   .pm   = &diag_dci_dev_pm_ops,
-		   },
+			.name = "DIAG_2",
+			.owner = THIS_MODULE,
+			.pm   = &diag_dci_dev_pm_ops,
+	},
 };
 
 int diag_dci_init(void)
@@ -316,16 +754,10 @@
 		if (driver->write_ptr_dci == NULL)
 			goto err;
 	}
-	if (driver->dci_tbl == NULL) {
-		driver->dci_tbl = kzalloc(dci_max_reg *
-			sizeof(struct diag_dci_tbl), GFP_KERNEL);
-		if (driver->dci_tbl == NULL)
-			goto err;
-	}
-	if (driver->dci_notify_tbl == NULL) {
-		driver->dci_notify_tbl = kzalloc(MAX_DCI_CLIENT *
-			sizeof(struct dci_notification_tbl), GFP_KERNEL);
-		if (driver->dci_notify_tbl == NULL)
+	if (driver->req_tracking_tbl == NULL) {
+		driver->req_tracking_tbl = kzalloc(dci_max_reg *
+			sizeof(struct dci_pkt_req_tracking_tbl), GFP_KERNEL);
+		if (driver->req_tracking_tbl == NULL)
 			goto err;
 	}
 	if (driver->apps_dci_buf == NULL) {
@@ -333,6 +765,13 @@
 		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");
 	success = platform_driver_register(&msm_diag_dci_driver);
 	if (success) {
 		pr_err("diag: Could not register DCI driver\n");
@@ -341,11 +780,13 @@
 	return DIAG_DCI_NO_ERROR;
 err:
 	pr_err("diag: Could not initialize diag DCI buffers");
-	kfree(driver->dci_tbl);
-	kfree(driver->dci_notify_tbl);
+	kfree(driver->req_tracking_tbl);
+	kfree(driver->dci_client_tbl);
 	kfree(driver->apps_dci_buf);
 	kfree(driver->buf_in_dci);
 	kfree(driver->write_ptr_dci);
+	if (driver->diag_dci_wq)
+		destroy_workqueue(driver->diag_dci_wq);
 	return DIAG_DCI_NO_REG;
 }
 
@@ -354,10 +795,10 @@
 	smd_close(driver->ch_dci);
 	driver->ch_dci = 0;
 	platform_driver_unregister(&msm_diag_dci_driver);
-	kfree(driver->dci_tbl);
-	kfree(driver->dci_notify_tbl);
+	kfree(driver->req_tracking_tbl);
+	kfree(driver->dci_client_tbl);
 	kfree(driver->apps_dci_buf);
 	kfree(driver->buf_in_dci);
 	kfree(driver->write_ptr_dci);
+	destroy_workqueue(driver->diag_dci_wq);
 }
-
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index b70efe3..97a285c 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -11,21 +11,48 @@
  */
 #ifndef DIAG_DCI_H
 #define DIAG_DCI_H
-#define MAX_DCI_CLIENT 10
-#define DCI_CMD_CODE 0x93
+
+#define MAX_DCI_CLIENTS		10
+#define DCI_PKT_RSP_CODE	0x93
+#define DCI_DELAYED_RSP_CODE	0x94
+#define LOG_CMD_CODE		0x10
+#define EVENT_CMD_CODE		0x60
+#define DCI_PKT_RSP_TYPE	0
+#define DCI_LOG_TYPE		-1
+#define DCI_EVENT_TYPE		-2
+#define SET_LOG_MASK		1
+#define DISABLE_LOG_MASK	0
+#define MAX_EVENT_SIZE		100
+
+/* 16 log code categories, each has:
+ * 1 bytes equip id + 1 dirty byte + 512 byte max log mask
+ */
+#define DCI_LOG_MASK_SIZE		(16*514)
+#define DCI_EVENT_MASK_SIZE		512
+#define DCI_MASK_STREAM			2
+#define DCI_MAX_LOG_CODES		16
+#define DCI_MAX_ITEMS_PER_LOG_CODE	512
 
 extern unsigned int dci_max_reg;
 extern unsigned int dci_max_clients;
-struct diag_dci_tbl {
+
+struct dci_pkt_req_tracking_tbl {
 	int pid;
 	int uid;
 	int tag;
 };
 
-struct dci_notification_tbl {
+struct diag_dci_client_tbl {
 	struct task_struct *client;
 	uint16_t list; /* bit mask */
 	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;
 };
 
 enum {
@@ -41,7 +68,18 @@
 int diag_dci_init(void);
 void diag_dci_exit(void);
 void diag_read_smd_dci_work_fn(struct work_struct *);
-int diag_process_dci_client(unsigned char *buf, int len);
+int diag_process_dci_transaction(unsigned char *buf, int len);
 int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
 							 int len, int index);
+void extract_dci_pkt_rsp(unsigned char *buf);
+/* DCI Log streaming functions */
+void create_dci_log_mask_tbl(unsigned char *tbl_buf);
+void update_dci_cumulative_log_mask(int client_index);
+void diag_send_dci_log_mask(smd_channel_t *ch);
+void extract_dci_log(unsigned char *buf);
+/* DCI event streaming functions */
+void update_dci_cumulative_event_mask(int client_index);
+void diag_send_dci_event_mask(smd_channel_t *ch);
+void extract_dci_events(unsigned char *buf);
+void create_dci_event_mask_tbl(unsigned char *tbl_buf);
 #endif
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index b18d709..2a77a7f 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -147,14 +147,15 @@
 	struct diag_write_device *buf_tbl;
 	int use_device_tree;
 	/* DCI related variables */
-	struct diag_dci_tbl *dci_tbl;
-	struct dci_notification_tbl *dci_notify_tbl;
+	struct dci_pkt_req_tracking_tbl *req_tracking_tbl;
+	struct diag_dci_client_tbl *dci_client_tbl;
 	int dci_tag;
 	int dci_client_id;
 	struct mutex dci_mutex;
 	int num_dci_client;
 	unsigned char *apps_dci_buf;
 	int dci_state;
+	struct workqueue_struct *diag_dci_wq;
 	/* Memory pool parameters */
 	unsigned int itemsize;
 	unsigned int poolsize;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 7e827b9..100c56f 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -372,7 +372,7 @@
 	int success = -1;
 	void *temp_buf;
 	uint16_t support_list = 0;
-	struct dci_notification_tbl *notify_params;
+	struct diag_dci_client_tbl *notify_params;
 	int status;
 
 	if (iocmd == DIAG_IOCTL_COMMAND_REG) {
@@ -444,20 +444,31 @@
 	} else if (iocmd == DIAG_IOCTL_DCI_REG) {
 		if (driver->dci_state == DIAG_DCI_NO_REG)
 			return DIAG_DCI_NO_REG;
-		if (driver->num_dci_client >= MAX_DCI_CLIENT)
+		if (driver->num_dci_client >= MAX_DCI_CLIENTS)
 			return DIAG_DCI_NO_REG;
-		notify_params = (struct dci_notification_tbl *) ioarg;
+		notify_params = (struct diag_dci_client_tbl *) ioarg;
 		mutex_lock(&driver->dci_mutex);
 		driver->num_dci_client++;
 		pr_debug("diag: id = %d\n", driver->dci_client_id);
 		driver->dci_client_id++;
-		for (i = 0; i < MAX_DCI_CLIENT; i++) {
-			if (driver->dci_notify_tbl[i].client == NULL) {
-				driver->dci_notify_tbl[i].client = current;
-				driver->dci_notify_tbl[i].list =
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			if (driver->dci_client_tbl[i].client == NULL) {
+				driver->dci_client_tbl[i].client = current;
+				driver->dci_client_tbl[i].list =
 							 notify_params->list;
-				driver->dci_notify_tbl[i].signal_type =
+				driver->dci_client_tbl[i].signal_type =
 					 notify_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;
 				break;
 			}
 		}
@@ -468,15 +479,15 @@
 		/* Delete this process from DCI table */
 		mutex_lock(&driver->dci_mutex);
 		for (i = 0; i < dci_max_reg; i++) {
-			if (driver->dci_tbl[i].pid == current->tgid) {
+			if (driver->req_tracking_tbl[i].pid == current->tgid) {
 				pr_debug("diag: delete %d\n", current->tgid);
-				driver->dci_tbl[i].pid = 0;
+				driver->req_tracking_tbl[i].pid = 0;
 				success = i;
 			}
 		}
-		for (i = 0; i < MAX_DCI_CLIENT; i++) {
-			if (driver->dci_notify_tbl[i].client == current) {
-				driver->dci_notify_tbl[i].client = NULL;
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			if (driver->dci_client_tbl[i].client == current) {
+				driver->dci_client_tbl[i].client = NULL;
 				break;
 			}
 		}
@@ -486,10 +497,6 @@
 			driver->num_dci_client--;
 		driver->num_dci_client--;
 		mutex_unlock(&driver->dci_mutex);
-		for (i = 0; i < dci_max_reg; i++)
-			if (driver->dci_tbl[i].pid != 0)
-				pr_debug("diag: PID = %d, UID = %d, tag = %d\n",
-	driver->dci_tbl[i].pid, driver->dci_tbl[i].uid, driver->dci_tbl[i].tag);
 		pr_debug("diag: complete deleting registrations\n");
 		return success;
 	} else if (iocmd == DIAG_IOCTL_DCI_SUPPORT) {
@@ -648,6 +655,7 @@
 static int diagchar_read(struct file *file, char __user *buf, size_t count,
 			  loff_t *ppos)
 {
+	struct diag_dci_client_tbl *entry;
 	int index = -1, i = 0, ret = 0;
 	int num_data = 0, data_type;
 	for (i = 0; i < driver->num_clients; i++)
@@ -922,23 +930,26 @@
 	}
 
 	if (driver->data_ready[index] & DCI_DATA_TYPE) {
-		/*Copy the type of data being passed*/
+		/* Copy the type of data being passed */
 		data_type = driver->data_ready[index] & DCI_DATA_TYPE;
 		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
-		COPY_USER_SPACE_OR_EXIT(buf+4,
-			 driver->write_ptr_dci->length, 4);
-		/* check delayed vs immediate response */
-		if (*(uint8_t *)(driver->buf_in_dci+4) == DCI_CMD_CODE)
-			COPY_USER_SPACE_OR_EXIT(buf+8,
-		*(driver->buf_in_dci + 5), driver->write_ptr_dci->length);
-		else
-			COPY_USER_SPACE_OR_EXIT(buf+8,
-		*(driver->buf_in_dci + 8), driver->write_ptr_dci->length);
-		driver->in_busy_dci = 0;
+		/* check the current client and copy its data */
+		for (i = 0; i < MAX_DCI_CLIENTS; i++) {
+			entry = &(driver->dci_client_tbl[i]);
+			if (entry && (current->tgid == entry->client->tgid)) {
+				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;
+				break;
+			}
+		}
 		driver->data_ready[index] ^= DCI_DATA_TYPE;
+		driver->in_busy_dci = 0;
 		if (driver->ch_dci)
-			queue_work(driver->diag_wq,
-				 &(driver->diag_read_smd_dci_work));
+			queue_work(driver->diag_dci_wq,
+				&(driver->diag_read_smd_dci_work));
 		goto exit;
 	}
 exit:
@@ -947,7 +958,7 @@
 }
 
 static int diagchar_write(struct file *file, const char __user *buf,
-			      size_t count, loff_t *ppos)
+				size_t count, loff_t *ppos)
 {
 	int err, ret = 0, pkt_type;
 #ifdef DIAG_DEBUG
@@ -976,7 +987,7 @@
 			pr_alert("diag: copy failed for DCI data\n");
 			return DIAG_DCI_SEND_DATA_FAIL;
 		}
-		err = diag_process_dci_client(driver->user_space_data,
+		err = diag_process_dci_transaction(driver->user_space_data,
 							payload_size);
 		return err;
 	}
diff --git a/include/linux/diagchar.h b/include/linux/diagchar.h
index c953613..e3e6a22 100644
--- a/include/linux/diagchar.h
+++ b/include/linux/diagchar.h
@@ -41,6 +41,7 @@
 #define DIAG_IOCTL_DCI_DEINIT		21
 #define DIAG_IOCTL_DCI_SUPPORT		22
 #define DIAG_IOCTL_DCI_REG		23
+#define DIAG_IOCTL_DCI_STREAM_INIT	24
 
 /* PC Tools IDs */
 #define APQ8060_TOOLS_ID	4062
@@ -706,5 +707,6 @@
 #define LOG_15	0x0
 
 #define LOG_GET_ITEM_NUM(xx_code) (xx_code & 0x0FFF)
+#define LOG_GET_EQUIP_ID(xx_code) ((xx_code & 0xF000) >> 12)
 
 #endif