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