diag: Add support for diag dci restart
The diag implementation of dci does not properly
handle the closing/re-opening of the dci channel.
Add support for closing/re-opening of the dci channel
including appropriate handling of client dci requests while
the channel is closed and resumption upon re-opening of
the channel.
Change-Id: Ied29fe99be5fee34cfe27cc5bdeacd4365fa136b
Signed-off-by: Ravi Aravamudhan <aravamud@codeaurora.org>
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index e78a2aa..24fc99a 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -38,6 +38,8 @@
struct mutex dci_log_mask_mutex;
struct mutex dci_event_mask_mutex;
+smd_channel_t *ch_dci_temp;
+
#define DCI_CHK_CAPACITY(entry, new_data_len) \
((entry->data_len + new_data_len > entry->total_capacity) ? 1 : 0) \
@@ -306,28 +308,100 @@
diag_smd_dci_send_req(MODEM_PROC);
}
-static void diag_smd_dci_notify(void *ctxt, unsigned event)
+void diag_update_smd_dci_work_fn(struct work_struct *work)
{
- queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
+ int i, j;
+ char dirty_bits[16];
+ uint8_t *client_log_mask_ptr;
+ uint8_t *log_mask_ptr;
+ int ret;
+
+ /* Update the peripheral(s) with the dci log and event masks */
+
+ /* If the cntl channel is not up, we can't update logs and events */
+ if (!driver->ch_cntl)
+ return;
+
+ memset(dirty_bits, 0, 16 * sizeof(uint8_t));
+
+ /*
+ * From each log entry used by each client, determine
+ * 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;
+ }
+ }
+ }
+
+ mutex_lock(&dci_log_mask_mutex);
+ /* Update the appropriate dirty bits in the cumulative mask */
+ log_mask_ptr = dci_cumulative_log_mask;
+ for (i = 0; i < 16; i++) {
+ if (dirty_bits[i])
+ *(log_mask_ptr+1) = dirty_bits[i];
+
+ log_mask_ptr += 514;
+ }
+ mutex_unlock(&dci_log_mask_mutex);
+
+ ret = diag_send_dci_log_mask(driver->ch_cntl);
+
+ ret = diag_send_dci_event_mask(driver->ch_cntl);
}
-void diag_dci_notify_client(int peripheral_mask)
+void diag_dci_notify_client(int peripheral_mask, int data)
{
int i, stat;
+ struct siginfo info;
+ 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].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);
+ 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);
if (stat)
- pr_err("diag: Err send sig stat: %d\n", stat);
+ pr_err("diag: Err sending dci signal to client, signal data: 0x%x, stat: %d\n",
+ info.si_int, stat);
break;
}
} /* end of loop for all DCI clients */
}
+static void diag_smd_dci_notify(void *ctxt, unsigned event)
+{
+ if (event == SMD_EVENT_CLOSE) {
+ driver->ch_dci = 0;
+ /* Notify the clients of the close */
+ diag_dci_notify_client(DIAG_CON_MPSS, DIAG_STATUS_CLOSED);
+ return;
+ } else if (event == SMD_EVENT_OPEN) {
+
+ if (ch_dci_temp)
+ driver->ch_dci = ch_dci_temp;
+
+ queue_work(driver->diag_dci_wq,
+ &(driver->diag_update_smd_dci_work));
+
+ /* Notify the clients of the open */
+ diag_dci_notify_client(DIAG_CON_MPSS, DIAG_STATUS_OPEN);
+ }
+
+ queue_work(driver->diag_dci_wq, &(driver->diag_read_smd_dci_work));
+}
+
static int diag_dci_probe(struct platform_device *pdev)
{
int err = 0;
@@ -339,12 +413,11 @@
pr_err("diag: cannot open DCI port, Id = %d, err ="
" %d\n", pdev->id, err);
else
- diag_dci_notify_client(DIAG_CON_MPSS);
+ ch_dci_temp = driver->ch_dci;
}
return err;
}
-
int diag_send_dci_pkt(struct diag_master_table entry, unsigned char *buf,
int len, int index)
{
@@ -414,6 +487,11 @@
uint8_t *event_mask_ptr;
int offset = 0;
+ if (!driver->ch_dci) {
+ pr_err("diag: ch_dci not valid for dci updates\n");
+ return DIAG_DCI_SEND_DATA_FAIL;
+ }
+
/* This is Pkt request/response transaction */
if (*(int *)temp > 0) {
/* enter this UID into kernel table and return index */
@@ -535,7 +613,7 @@
ret = DIAG_DCI_NO_ERROR;
}
/* send updated mask to peripherals */
- diag_send_dci_log_mask(driver->ch_cntl);
+ ret = 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++) {
@@ -581,7 +659,7 @@
ret = DIAG_DCI_NO_ERROR;
}
/* send updated mask to peripherals */
- diag_send_dci_event_mask(driver->ch_cntl);
+ ret = diag_send_dci_event_mask(driver->ch_cntl);
} else {
pr_alert("diag: Incorrect DCI transaction\n");
}
@@ -614,11 +692,12 @@
mutex_unlock(&dci_event_mask_mutex);
}
-void diag_send_dci_event_mask(smd_channel_t *ch)
+int 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;
+ int ret = DIAG_DCI_NO_ERROR;
mutex_lock(&driver->diag_cntl_mutex);
/* send event mask update */
@@ -642,12 +721,18 @@
break;
}
}
- if (wr_size != header_size + DCI_EVENT_MASK_SIZE)
+ 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
+ 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);
+
+ return ret;
}
void update_dci_cumulative_log_mask(int offset, int byte_index,
@@ -686,12 +771,18 @@
mutex_unlock(&dci_log_mask_mutex);
}
-void diag_send_dci_log_mask(smd_channel_t *ch)
+int 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;
+ int ret = DIAG_DCI_NO_ERROR;
+
+ if (!ch) {
+ pr_err("diag: ch not valid for dci log mask update\n");
+ return DIAG_DCI_SEND_DATA_FAIL;
+ }
mutex_lock(&driver->diag_cntl_mutex);
for (i = 0; i < 16; i++) {
@@ -715,10 +806,12 @@
} else
break;
}
- if (wr_size != header_size + 512)
+ if (wr_size != header_size + 512) {
pr_err("diag: dci log mask update failed %d, tried %d",
wr_size, header_size + 512);
- else {
+ ret = DIAG_DCI_SEND_DATA_FAIL;
+
+ } else {
*(log_mask_ptr+1) = 0; /* clear dirty byte */
pr_debug("diag: updated dci log equip ID %d\n",
*log_mask_ptr);
@@ -727,6 +820,8 @@
log_mask_ptr += 514;
}
mutex_unlock(&driver->diag_cntl_mutex);
+
+ return ret;
}
void create_dci_log_mask_tbl(unsigned char *tbl_buf)
@@ -778,6 +873,8 @@
{
int success = 0;
+ ch_dci_temp = NULL;
+
driver->dci_tag = 0;
driver->dci_client_id = 0;
driver->num_dci_client = 0;
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 435c750..3f62e5e 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -79,6 +79,7 @@
int diag_dci_init(void);
void diag_dci_exit(void);
void diag_read_smd_dci_work_fn(struct work_struct *);
+void diag_update_smd_dci_work_fn(struct work_struct *);
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);
@@ -87,11 +88,11 @@
void create_dci_log_mask_tbl(unsigned char *tbl_buf);
void update_dci_cumulative_log_mask(int offset, int byte_index,
uint8_t byte_mask);
-void diag_send_dci_log_mask(smd_channel_t *ch);
+int 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 offset, uint8_t byte_mask);
-void diag_send_dci_event_mask(smd_channel_t *ch);
+int 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/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index ed0f08e..0048007 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -112,7 +112,8 @@
"diag_modem_mask_update_work: %d\n"
"diag_lpass_mask_update_work: %d\n"
"diag_wcnss_mask_update_work: %d\n"
- "diag_read_smd_dci_work: %d\n",
+ "diag_read_smd_dci_work: %d\n"
+ "diag_update_smd_dci_work: %d\n",
work_pending(&(driver->diag_drain_work)),
work_pending(&(driver->diag_read_smd_work)),
work_pending(&(driver->diag_read_smd_cntl_work)),
@@ -123,7 +124,8 @@
work_pending(&(driver->diag_modem_mask_update_work)),
work_pending(&(driver->diag_lpass_mask_update_work)),
work_pending(&(driver->diag_wcnss_mask_update_work)),
- work_pending(&(driver->diag_read_smd_dci_work)));
+ work_pending(&(driver->diag_read_smd_dci_work)),
+ work_pending(&(driver->diag_update_smd_dci_work)));
#ifdef CONFIG_DIAG_OVER_USB
ret += scnprintf(buf+ret, DEBUG_BUF_SIZE,
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index b535d53..cb895d8 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -66,6 +66,15 @@
#define DIAG_CON_LPASS (0x0004) /* Bit mask for LPASS */
#define DIAG_CON_WCNSS (0x0008) /* Bit mask for WCNSS */
+/*
+ * 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
+ * status for a peripheral. For instance, 0x00010002 would denote an open
+ * status on the MPSS
+ */
+#define DIAG_STATUS_OPEN (0x00010000) /* DCI channel open status mask */
+#define DIAG_STATUS_CLOSED (0x00020000) /* DCI channel closed status mask */
+
/* Maximum number of pkt reg supported at initialization*/
extern unsigned int diag_max_reg;
extern unsigned int diag_threshold_reg;
@@ -233,6 +242,7 @@
struct work_struct diag_lpass_mask_update_work;
struct work_struct diag_wcnss_mask_update_work;
struct work_struct diag_read_smd_dci_work;
+ struct work_struct diag_update_smd_dci_work;
struct work_struct diag_clean_modem_reg_work;
struct work_struct diag_clean_lpass_reg_work;
struct work_struct diag_clean_wcnss_reg_work;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index bce7eee..6c9e5ab 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -1410,7 +1410,9 @@
INIT_WORK(&(driver->diag_read_smd_wcnss_cntl_work),
diag_read_smd_wcnss_cntl_work_fn);
INIT_WORK(&(driver->diag_read_smd_dci_work),
- diag_read_smd_dci_work_fn);
+ diag_read_smd_dci_work_fn);
+ INIT_WORK(&(driver->diag_update_smd_dci_work),
+ diag_update_smd_dci_work_fn);
INIT_WORK(&(driver->diag_clean_modem_reg_work),
diag_clean_modem_reg_fn);
INIT_WORK(&(driver->diag_clean_lpass_reg_work),