summaryrefslogtreecommitdiff
path: root/drivers/char
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/diag/diag_dci.c70
-rw-r--r--drivers/char/diag/diag_dci.h10
-rw-r--r--drivers/char/diag/diag_debugfs.c47
-rw-r--r--drivers/char/diag/diagchar.h32
-rw-r--r--drivers/char/diag/diagchar_core.c242
-rw-r--r--drivers/char/diag/diagfwd.c169
-rw-r--r--drivers/char/diag/diagfwd.h5
-rw-r--r--drivers/char/diag/diagfwd_cntl.c1
-rw-r--r--drivers/char/diag/diagfwd_hsic.c6
-rw-r--r--drivers/char/diag/diagmem.c13
10 files changed, 387 insertions, 208 deletions
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 91ac7b07914f..05a1403bd1dc 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -190,6 +190,12 @@ static void dci_add_buffer_to_list(struct diag_dci_client_tbl *client,
mutex_lock(&client->write_buf_mutex);
list_add_tail(&buf->buf_track, &client->list_write_buf);
+ /*
+ * In the case of DCI, there can be multiple packets in one read. To
+ * calculate the wakeup source reference count, we must account for each
+ * packet in a single read.
+ */
+ diag_ws_on_read(DIAG_WS_DCI, buf->data_len);
mutex_lock(&buf->data_mutex);
buf->in_busy = 1;
buf->in_list = 1;
@@ -470,7 +476,6 @@ end:
/* wake up all sleeping DCI clients which have some data */
diag_dci_wakeup_clients();
dci_check_drain_timer();
- diag_dci_try_deactivate_wakeup_source();
return 0;
}
@@ -491,7 +496,7 @@ int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
* process DCI data
*/
if (driver->num_dci_client == 0) {
- diag_dci_try_deactivate_wakeup_source();
+ diag_ws_reset(DIAG_WS_DCI);
return 0;
}
@@ -511,7 +516,7 @@ int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
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();
+ diag_ws_release();
return 0;
}
/*
@@ -520,8 +525,10 @@ int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
*/
err = diag_process_single_dci_pkt(buf + 4, dci_pkt_len,
smd_info->peripheral, DCI_LOCAL_PROC);
- if (err)
+ if (err) {
+ diag_ws_release();
break;
+ }
read_bytes += 5 + dci_pkt_len;
buf += 5 + dci_pkt_len; /* advance to next DCI pkt */
}
@@ -529,7 +536,6 @@ int diag_process_smd_dci_read_data(struct diag_smd_info *smd_info, void *buf,
/* wake up all sleeping DCI clients which have some data */
diag_dci_wakeup_clients();
dci_check_drain_timer();
- diag_dci_try_deactivate_wakeup_source();
return 0;
}
@@ -723,6 +729,7 @@ void extract_dci_ctrl_pkt(unsigned char *buf, int len, int token)
return;
}
+ diag_ws_on_read(DIAG_WS_DCI, len);
header = (struct diag_ctrl_dci_status *)temp;
temp += sizeof(struct diag_ctrl_dci_status);
read_len += sizeof(struct diag_ctrl_dci_status);
@@ -731,7 +738,7 @@ void extract_dci_ctrl_pkt(unsigned char *buf, int len, int token)
if (read_len > len) {
pr_err("diag: Invalid length len: %d in %s\n", len,
__func__);
- return;
+ goto err;
}
switch (*(uint8_t *)temp) {
@@ -747,7 +754,7 @@ void extract_dci_ctrl_pkt(unsigned char *buf, int len, int token)
default:
pr_err("diag: In %s, unknown peripheral, peripheral: %d\n",
__func__, *(uint8_t *)temp);
- return;
+ goto err;
}
temp += sizeof(uint8_t);
read_len += sizeof(uint8_t);
@@ -758,6 +765,13 @@ void extract_dci_ctrl_pkt(unsigned char *buf, int len, int token)
read_len += sizeof(uint8_t);
diag_dci_notify_client(peripheral_mask, status, token);
}
+err:
+ /*
+ * DCI control packets are not consumed by the clients. Mimic client
+ * consumption by setting and clearing the wakeup source copy_count
+ * explicitly.
+ */
+ diag_ws_on_copy_fail(DIAG_WS_DCI);
}
void extract_dci_pkt_rsp(unsigned char *buf, int len, int data_source,
@@ -771,6 +785,8 @@ void extract_dci_pkt_rsp(unsigned char *buf, int len, int data_source,
struct diag_dci_buffer_t *rsp_buf = NULL;
struct dci_pkt_req_entry_t *req_entry = NULL;
unsigned char *temp = buf;
+ int save_req_uid = 0;
+ struct diag_dci_pkt_rsp_header_t pkt_rsp_header;
if (!buf) {
pr_err("diag: Invalid pointer in %s\n", __func__);
@@ -814,6 +830,7 @@ void extract_dci_pkt_rsp(unsigned char *buf, int len, int data_source,
return;
}
+ save_req_uid = req_entry->uid;
/* Remove the headers and send only the response to this function */
delete_flag = diag_dci_remove_req_entry(temp, rsp_len, req_entry);
if (delete_flag < 0)
@@ -840,15 +857,16 @@ void extract_dci_pkt_rsp(unsigned char *buf, int len, int data_source,
rsp_buf->data = temp_buf;
}
}
- *(int *)(rsp_buf->data + rsp_buf->data_len) = DCI_PKT_RSP_TYPE;
- rsp_buf->data_len += sizeof(int);
+
+ /* Fill in packet response header information */
+ pkt_rsp_header.type = DCI_PKT_RSP_TYPE;
/* 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);
+ pkt_rsp_header.length = rsp_len + sizeof(int);
+ pkt_rsp_header.delete_flag = delete_flag;
+ pkt_rsp_header.uid = save_req_uid;
+ memcpy(rsp_buf->data, &pkt_rsp_header,
+ sizeof(struct diag_dci_pkt_rsp_header_t));
+ rsp_buf->data_len += sizeof(struct diag_dci_pkt_rsp_header_t);
memcpy(rsp_buf->data + rsp_buf->data_len, temp, rsp_len);
rsp_buf->data_len += rsp_len;
rsp_buf->data_source = data_source;
@@ -2570,21 +2588,6 @@ int diag_dci_set_real_time(struct diag_dci_client_tbl *entry, uint8_t 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()
-{
- spin_lock_irqsave(&ws_lock, ws_lock_flags);
- 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;
@@ -2806,7 +2809,12 @@ int diag_dci_deinit_client(struct diag_dci_client_tbl *entry)
smd_info->in_busy_1 = 0;
mutex_unlock(&buf_entry->data_mutex);
}
- diag_dci_try_deactivate_wakeup_source();
+ /*
+ * These are buffers that can't be written to the client which
+ * means that the copy cannot be completed. Make sure that we
+ * remove those references in DCI wakeup source.
+ */
+ diag_ws_on_copy_fail(DIAG_WS_DCI);
}
mutex_unlock(&entry->write_buf_mutex);
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index 6cf28f435fab..aa6d7de6d440 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -159,6 +159,13 @@ struct diag_log_event_stats {
int is_set;
} __packed;
+struct diag_dci_pkt_rsp_header_t {
+ int type;
+ int length;
+ uint8_t delete_flag;
+ int uid;
+} __packed;
+
struct diag_dci_pkt_header_t {
uint8_t start;
uint8_t version;
@@ -251,9 +258,6 @@ uint8_t diag_dci_get_cumulative_real_time(int token);
int diag_dci_set_real_time(struct diag_dci_client_tbl *entry,
uint8_t real_time);
int diag_dci_copy_health_stats(struct diag_dci_health_stats_proc *stats_proc);
-/* Functions related to DCI wakeup sources */
-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);
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c
index 7abc2cade6e6..cdad688e95fa 100644
--- a/drivers/char/diag/diag_debugfs.c
+++ b/drivers/char/diag/diag_debugfs.c
@@ -288,6 +288,44 @@ static ssize_t diag_dbgfs_read_dcistats(struct file *file,
return bytes_written;
}
+static ssize_t diag_dbgfs_read_power(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ char *buf;
+ int ret;
+ unsigned int buf_size;
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (!buf) {
+ pr_err("diag: %s, Error allocating memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ buf_size = ksize(buf);
+ ret = scnprintf(buf, buf_size,
+ "DCI reference count: %d\n"
+ "DCI copy count: %d\n"
+ "DCI Client Count: %d\n\n"
+ "Memory Device reference count: %d\n"
+ "Memory Device copy count: %d\n"
+ "Logging mode: %d\n\n"
+ "Wakeup source active count: %lu\n"
+ "Wakeup source relax count: %lu\n\n",
+ driver->dci_ws.ref_count,
+ driver->dci_ws.copy_count,
+ driver->num_dci_client,
+ driver->md_ws.ref_count,
+ driver->md_ws.copy_count,
+ driver->logging_mode,
+ driver->diag_dev->power.wakeup->active_count,
+ driver->diag_dev->power.wakeup->relax_count);
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, ret);
+
+ kfree(buf);
+ return ret;
+}
+
static ssize_t diag_dbgfs_read_workpending(struct file *file,
char __user *ubuf, size_t count, loff_t *ppos)
{
@@ -786,6 +824,10 @@ const struct file_operations diag_dbgfs_dcistats_ops = {
.read = diag_dbgfs_read_dcistats,
};
+const struct file_operations diag_dbgfs_power_ops = {
+ .read = diag_dbgfs_read_power,
+};
+
int diag_debugfs_init(void)
{
struct dentry *entry = NULL;
@@ -819,6 +861,11 @@ int diag_debugfs_init(void)
if (!entry)
goto err;
+ entry = debugfs_create_file("power", 0444, diag_dbgfs_dent, 0,
+ &diag_dbgfs_power_ops);
+ if (!entry)
+ goto err;
+
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
entry = debugfs_create_file("bridge", 0444, diag_dbgfs_dent, 0,
&diag_dbgfs_bridge_ops);
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 9bca95856789..b20b5990954c 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -191,6 +191,9 @@
#define DIAG_NUM_PROC 1
#endif
+#define DIAG_WS_DCI 0
+#define DIAG_WS_MD 1
+
/* Maximum number of pkt reg supported at initialization*/
extern int diag_max_reg;
extern int diag_threshold_reg;
@@ -254,14 +257,6 @@ struct diag_client_map {
int pid;
};
-struct diag_nrt_wake_lock {
- int enabled;
- int ref_count;
- int copy_count;
- struct wake_lock read_lock;
- spinlock_t read_spinlock;
-};
-
struct real_time_vote_t {
int client_id;
uint16_t proc;
@@ -273,6 +268,12 @@ struct real_time_query_t {
int proc;
} __packed;
+struct diag_ws_ref_t {
+ int ref_count;
+ int copy_count;
+ spinlock_t lock;
+};
+
/* This structure is defined in USB header file */
#ifndef CONFIG_DIAG_OVER_USB
struct diag_request {
@@ -314,8 +315,6 @@ struct diag_smd_info {
struct diag_request *write_ptr_1;
struct diag_request *write_ptr_2;
- struct diag_nrt_wake_lock nrt_lock;
-
struct workqueue_struct *wq;
struct work_struct diag_read_smd_work;
@@ -475,6 +474,10 @@ struct diagchar_dev {
int logging_process_id;
struct task_struct *socket_process;
struct task_struct *callback_process;
+ /* Power related variables */
+ struct diag_ws_ref_t dci_ws;
+ struct diag_ws_ref_t md_ws;
+ spinlock_t ws_lock;
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
/* common for all bridges */
@@ -503,4 +506,13 @@ void diag_get_timestamp(char *time_str);
int diag_find_polling_reg(int i);
void check_drain_timer(void);
+void diag_ws_init(void);
+void diag_ws_on_notify(void);
+void diag_ws_on_read(int type, int pkt_len);
+void diag_ws_on_copy(int type);
+void diag_ws_on_copy_fail(int type);
+void diag_ws_on_copy_complete(int type);
+void diag_ws_reset(int type);
+void diag_ws_release(void);
+
#endif
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 27713809cd79..741dd622b30b 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -117,6 +117,8 @@ static uint16_t diag_get_next_delayed_rsp_id(void)
return rsp_id;
}
+static int diag_switch_logging(int requested_mode);
+
#define COPY_USER_SPACE_OR_EXIT(buf, data, length) \
do { \
if ((count < ret+length) || (copy_to_user(buf, \
@@ -164,6 +166,20 @@ void check_drain_timer(void)
}
}
+static void diag_clear_local_tbl(void)
+{
+ int i;
+
+ for (i = 0; i < driver->buf_tbl_size; i++) {
+ if (driver->buf_tbl[i].buf) {
+ diagmem_free(driver, (unsigned char *)
+ driver->buf_tbl[i].buf, POOL_TYPE_HDLC);
+ driver->buf_tbl[i].buf = 0;
+ }
+ driver->buf_tbl[i].length = 0;
+ }
+}
+
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
void diag_clear_hsic_tbl(void)
{
@@ -311,15 +327,10 @@ static int diagchar_close(struct inode *inode, struct file *file)
#ifdef CONFIG_DIAG_OVER_USB
/* If the SD logging process exits, change logging to USB mode */
if (driver->logging_process_id == current->tgid) {
- driver->logging_mode = USB_MODE;
diag_update_proc_vote(DIAG_PROC_MEMORY_DEVICE, VOTE_DOWN,
ALL_PROC);
- diagfwd_connect();
-#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
- diag_clear_hsic_tbl();
- diagfwd_cancel_hsic(REOPEN_HSIC);
- diagfwd_connect_bridge(0);
-#endif
+ diag_switch_logging(USB_MODE);
+ diag_ws_reset(DIAG_WS_MD);
}
#endif /* DIAG over USB */
/* Delete the pkt response table entry for the exiting process */
@@ -482,6 +493,7 @@ int diag_copy_remote(char __user *buf, size_t count, int *pret, int *pnum_data)
int ret = *pret;
int num_data = *pnum_data;
int remote_token;
+ int copy_data = 0;
unsigned long spin_lock_flags;
struct diag_write_device hsic_buf_tbl[NUM_HSIC_BUF_TBL_ENTRIES];
@@ -513,6 +525,9 @@ int diag_copy_remote(char __user *buf, size_t count, int *pret, int *pnum_data)
hsic_buf_tbl[i].length);
num_data++;
+ diag_ws_on_copy(DIAG_WS_MD);
+ copy_data = 1;
+
/* Copy the negative token */
if (copy_to_user(buf+ret,
&remote_token, 4)) {
@@ -569,6 +584,8 @@ drop_hsic:
driver->in_busy_smux = 0;
}
exit_stat = 0;
+ if (copy_data)
+ diag_ws_on_copy_complete(DIAG_WS_MD);
exit:
*pret = ret;
*pnum_data = num_data;
@@ -610,6 +627,7 @@ static int diag_copy_dci(char __user *buf, size_t count,
goto drop;
ret += buf_entry->data_len;
total_data_len += buf_entry->data_len;
+ diag_ws_on_copy(DIAG_WS_DCI);
drop:
buf_entry->in_busy = 0;
buf_entry->data_len = 0;
@@ -647,6 +665,13 @@ drop:
/* Copy the total data length */
COPY_USER_SPACE_OR_EXIT(buf+8, total_data_len, 4);
ret -= 4;
+ /*
+ * Flush any read that is currently pending on DCI data and
+ * command channnels. This will ensure that the next read is not
+ * missed.
+ */
+ flush_workqueue(driver->diag_dci_wq);
+ diag_ws_on_copy_complete(DIAG_WS_DCI);
} else {
pr_debug("diag: In %s, Trying to copy ZERO bytes, total_data_len: %d\n",
__func__, total_data_len);
@@ -793,6 +818,7 @@ void diag_cmp_logging_modes_diagfwd_bridge(int old_mode, int new_mode)
if (old_mode == MEMORY_DEVICE_MODE && new_mode
== NO_LOGGING_MODE) {
diagfwd_disconnect_bridge(0);
+ diag_clear_local_tbl();
diag_clear_hsic_tbl();
} else if (old_mode == NO_LOGGING_MODE && new_mode
== MEMORY_DEVICE_MODE) {
@@ -817,6 +843,7 @@ void diag_cmp_logging_modes_diagfwd_bridge(int old_mode, int new_mode)
diagfwd_connect_bridge(0);
} else if (old_mode == MEMORY_DEVICE_MODE && new_mode
== USB_MODE) {
+ diag_clear_local_tbl();
diag_clear_hsic_tbl();
diagfwd_cancel_hsic(REOPEN_HSIC);
diagfwd_connect_bridge(0);
@@ -856,10 +883,12 @@ static int diag_switch_logging(int requested_mode)
return 0;
}
- diag_update_proc_vote(DIAG_PROC_MEMORY_DEVICE, VOTE_UP, ALL_PROC);
if (requested_mode != MEMORY_DEVICE_MODE)
diag_update_real_time_vote(DIAG_PROC_MEMORY_DEVICE,
MODE_REALTIME, ALL_PROC);
+ else
+ diag_update_proc_vote(DIAG_PROC_MEMORY_DEVICE, VOTE_UP,
+ ALL_PROC);
if (!(requested_mode == MEMORY_DEVICE_MODE &&
driver->logging_mode == USB_MODE))
@@ -871,6 +900,7 @@ static int diag_switch_logging(int requested_mode)
driver->logging_mode = requested_mode;
if (driver->logging_mode == MEMORY_DEVICE_MODE) {
+ diag_clear_local_tbl();
diag_clear_hsic_tbl();
driver->mask_check = 1;
if (driver->socket_process) {
@@ -896,13 +926,13 @@ static int diag_switch_logging(int requested_mode)
if (driver->logging_mode == UART_MODE ||
driver->logging_mode == SOCKET_MODE ||
driver->logging_mode == CALLBACK_MODE) {
+ diag_clear_local_tbl();
diag_clear_hsic_tbl();
driver->mask_check = 0;
driver->logging_mode = MEMORY_DEVICE_MODE;
}
driver->logging_process_id = current->tgid;
- mutex_unlock(&driver->diagchar_mutex);
if (temp == MEMORY_DEVICE_MODE && driver->logging_mode
== NO_LOGGING_MODE) {
@@ -936,6 +966,7 @@ static int diag_switch_logging(int requested_mode)
diag_cmp_logging_modes_diagfwd_bridge(temp,
driver->logging_mode);
}
+ mutex_unlock(&driver->diagchar_mutex);
success = 1;
return success;
}
@@ -1325,7 +1356,7 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count,
int num_data = 0, data_type;
int remote_token;
int exit_stat;
- int clear_read_wakelock;
+ int copy_data = 0;
unsigned long flags;
for (i = 0; i < driver->num_clients; i++)
@@ -1344,7 +1375,6 @@ static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count,
mutex_lock(&driver->diagchar_mutex);
- clear_read_wakelock = 0;
if ((driver->data_ready[index] & USER_SPACE_DATA_TYPE) && (driver->
logging_mode == MEMORY_DEVICE_MODE)) {
remote_token = 0;
@@ -1426,14 +1456,12 @@ drop:
COPY_USER_SPACE_OR_EXIT(buf+ret,
*(data->buf_in_1),
data->write_ptr_1->length);
- if (!driver->real_time_mode) {
- process_lock_on_copy(&data->nrt_lock);
- clear_read_wakelock++;
- }
spin_lock_irqsave(&data->in_busy_lock, flags);
data->in_busy_1 = 0;
spin_unlock_irqrestore(&data->in_busy_lock,
flags);
+ diag_ws_on_copy(DIAG_WS_MD);
+ copy_data = 1;
}
if (data->in_busy_2 == 1) {
num_data++;
@@ -1444,14 +1472,12 @@ drop:
COPY_USER_SPACE_OR_EXIT(buf+ret,
*(data->buf_in_2),
data->write_ptr_2->length);
- if (!driver->real_time_mode) {
- process_lock_on_copy(&data->nrt_lock);
- clear_read_wakelock++;
- }
spin_lock_irqsave(&data->in_busy_lock, flags);
data->in_busy_2 = 0;
spin_unlock_irqrestore(&data->in_busy_lock,
flags);
+ diag_ws_on_copy(DIAG_WS_MD);
+ copy_data = 1;
}
}
if (driver->supports_separate_cmdrsp) {
@@ -1618,10 +1644,15 @@ drop:
goto exit;
}
exit:
- if (clear_read_wakelock) {
+ if (copy_data) {
+ /*
+ * Flush any work that is currently pending on the data
+ * channels. This will ensure that the next read is not missed.
+ */
for (i = 0; i < NUM_SMD_DATA_CHANNELS; i++)
- process_lock_on_copy_complete(
- &driver->smd_data[i].nrt_lock);
+ flush_workqueue(driver->smd_data[i].wq);
+ wake_up(&driver->smd_wait_q);
+ diag_ws_on_copy_complete(DIAG_WS_MD);
}
mutex_unlock(&driver->diagchar_mutex);
return ret;
@@ -2096,6 +2127,172 @@ fail_free_copy:
return ret;
}
+void diag_ws_init()
+{
+ driver->dci_ws.ref_count = 0;
+ driver->dci_ws.copy_count = 0;
+ spin_lock_init(&driver->dci_ws.lock);
+
+ driver->md_ws.ref_count = 0;
+ driver->md_ws.copy_count = 0;
+ spin_lock_init(&driver->md_ws.lock);
+
+ spin_lock_init(&driver->ws_lock);
+}
+
+void diag_ws_on_notify()
+{
+ /*
+ * Do not deal with reference count here as there can be spurious
+ * interrupts.
+ */
+ pm_stay_awake(driver->diag_dev);
+}
+
+void diag_ws_on_read(int type, int pkt_len)
+{
+ unsigned long flags;
+ struct diag_ws_ref_t *ws_ref = NULL;
+
+ switch (type) {
+ case DIAG_WS_DCI:
+ ws_ref = &driver->dci_ws;
+ break;
+ case DIAG_WS_MD:
+ ws_ref = &driver->md_ws;
+ break;
+ default:
+ pr_err_ratelimited("diag: In %s, invalid type: %d\n",
+ __func__, type);
+ return;
+ }
+
+ spin_lock_irqsave(&ws_ref->lock, flags);
+ if (pkt_len > 0) {
+ ws_ref->ref_count++;
+ } else {
+ if (ws_ref->ref_count < 1) {
+ ws_ref->ref_count = 0;
+ ws_ref->copy_count = 0;
+ }
+ diag_ws_release();
+ }
+ spin_unlock_irqrestore(&ws_ref->lock, flags);
+}
+
+
+void diag_ws_on_copy(int type)
+{
+ unsigned long flags;
+ struct diag_ws_ref_t *ws_ref = NULL;
+
+ switch (type) {
+ case DIAG_WS_DCI:
+ ws_ref = &driver->dci_ws;
+ break;
+ case DIAG_WS_MD:
+ ws_ref = &driver->md_ws;
+ break;
+ default:
+ pr_err_ratelimited("diag: In %s, invalid type: %d\n",
+ __func__, type);
+ return;
+ }
+
+ spin_lock_irqsave(&ws_ref->lock, flags);
+ ws_ref->copy_count++;
+ spin_unlock_irqrestore(&ws_ref->lock, flags);
+}
+
+void diag_ws_on_copy_fail(int type)
+{
+ unsigned long flags;
+ struct diag_ws_ref_t *ws_ref = NULL;
+
+ switch (type) {
+ case DIAG_WS_DCI:
+ ws_ref = &driver->dci_ws;
+ break;
+ case DIAG_WS_MD:
+ ws_ref = &driver->md_ws;
+ break;
+ default:
+ pr_err_ratelimited("diag: In %s, invalid type: %d\n",
+ __func__, type);
+ return;
+ }
+
+ spin_lock_irqsave(&ws_ref->lock, flags);
+ ws_ref->ref_count--;
+ spin_unlock_irqrestore(&ws_ref->lock, flags);
+
+ diag_ws_release();
+}
+
+void diag_ws_on_copy_complete(int type)
+{
+ unsigned long flags;
+ struct diag_ws_ref_t *ws_ref = NULL;
+
+ switch (type) {
+ case DIAG_WS_DCI:
+ ws_ref = &driver->dci_ws;
+ break;
+ case DIAG_WS_MD:
+ ws_ref = &driver->md_ws;
+ break;
+ default:
+ pr_err_ratelimited("diag: In %s, invalid type: %d\n",
+ __func__, type);
+ return;
+ }
+
+ spin_lock_irqsave(&ws_ref->lock, flags);
+ ws_ref->ref_count -= ws_ref->copy_count;
+ if (ws_ref->ref_count < 1)
+ ws_ref->ref_count = 0;
+ ws_ref->copy_count = 0;
+ spin_unlock_irqrestore(&ws_ref->lock, flags);
+
+ diag_ws_release();
+}
+
+void diag_ws_reset(int type)
+{
+ unsigned long flags;
+ struct diag_ws_ref_t *ws_ref = NULL;
+
+ switch (type) {
+ case DIAG_WS_DCI:
+ ws_ref = &driver->dci_ws;
+ break;
+ case DIAG_WS_MD:
+ ws_ref = &driver->md_ws;
+ break;
+ default:
+ pr_err_ratelimited("diag: In %s, invalid type: %d\n",
+ __func__, type);
+ return;
+ }
+
+ spin_lock_irqsave(&ws_ref->lock, flags);
+ ws_ref->ref_count = 0;
+ ws_ref->copy_count = 0;
+ spin_unlock_irqrestore(&ws_ref->lock, flags);
+
+ diag_ws_release();
+}
+
+void diag_ws_release()
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&driver->ws_lock, flags);
+ if (driver->dci_ws.ref_count == 0 && driver->md_ws.ref_count == 0)
+ pm_relax(driver->diag_dev);
+ spin_unlock_irqrestore(&driver->ws_lock, flags);
+}
+
static int diag_real_time_info_init(void)
{
int i;
@@ -2336,6 +2533,7 @@ static int __init diagchar_init(void)
init_waitqueue_head(&driver->wait_q);
init_waitqueue_head(&driver->smd_wait_q);
INIT_WORK(&(driver->diag_drain_work), diag_drain_work_fn);
+ diag_ws_init();
ret = diag_real_time_info_init();
if (ret)
goto fail;
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 3f915390ac0a..8f44514b21ba 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -103,6 +103,8 @@ int chk_config_get_id(void)
return APQ8084_TOOLS_ID;
case MSM_CPU_8916:
return MSM8916_TOOLS_ID;
+ case MSM_CPU_8939:
+ return MSM8939_TOOLS_ID;
default:
if (driver->use_device_tree) {
if (machine_is_msm8974())
@@ -331,93 +333,6 @@ static int check_bufsize_for_encoding(struct diag_smd_info *smd_info, void *buf,
return buf_size;
}
-void process_lock_enabling(struct diag_nrt_wake_lock *lock, int real_time)
-{
- unsigned long read_lock_flags;
-
- spin_lock_irqsave(&lock->read_spinlock, read_lock_flags);
- if (real_time)
- lock->enabled = 0;
- else
- lock->enabled = 1;
- lock->ref_count = 0;
- lock->copy_count = 0;
- wake_unlock(&lock->read_lock);
- spin_unlock_irqrestore(&lock->read_spinlock, read_lock_flags);
-}
-
-void process_lock_on_notify(struct diag_nrt_wake_lock *lock)
-{
- unsigned long read_lock_flags;
-
- spin_lock_irqsave(&lock->read_spinlock, read_lock_flags);
- /*
- * Do not work with ref_count here in case
- * of spurious interrupt
- */
- if (lock->enabled && !wake_lock_active(&lock->read_lock))
- wake_lock(&lock->read_lock);
- spin_unlock_irqrestore(&lock->read_spinlock, read_lock_flags);
-}
-
-void process_lock_on_read(struct diag_nrt_wake_lock *lock, int pkt_len)
-{
- unsigned long read_lock_flags;
-
- spin_lock_irqsave(&lock->read_spinlock, read_lock_flags);
- if (lock->enabled) {
- if (pkt_len > 0) {
- /*
- * We have an data that is read that
- * needs to be processed, make sure the
- * processor does not go to sleep
- */
- lock->ref_count++;
- if (!wake_lock_active(&lock->read_lock))
- wake_lock(&lock->read_lock);
- } else {
- /*
- * There was no data associated with the
- * read from the smd, unlock the wake lock
- * if it is not needed.
- */
- if (lock->ref_count < 1) {
- if (wake_lock_active(&lock->read_lock))
- wake_unlock(&lock->read_lock);
- lock->ref_count = 0;
- lock->copy_count = 0;
- }
- }
- }
- spin_unlock_irqrestore(&lock->read_spinlock, read_lock_flags);
-}
-
-void process_lock_on_copy(struct diag_nrt_wake_lock *lock)
-{
- unsigned long read_lock_flags;
-
- spin_lock_irqsave(&lock->read_spinlock, read_lock_flags);
- if (lock->enabled)
- lock->copy_count++;
- spin_unlock_irqrestore(&lock->read_spinlock, read_lock_flags);
-}
-
-void process_lock_on_copy_complete(struct diag_nrt_wake_lock *lock)
-{
- unsigned long read_lock_flags;
-
- spin_lock_irqsave(&lock->read_spinlock, read_lock_flags);
- if (lock->enabled) {
- lock->ref_count -= lock->copy_count;
- if (lock->ref_count < 1) {
- wake_unlock(&lock->read_lock);
- lock->ref_count = 0;
- }
- lock->copy_count = 0;
- }
- spin_unlock_irqrestore(&lock->read_spinlock, read_lock_flags);
-}
-
/* Process the data read from the smd data channel */
int diag_process_smd_read_data(struct diag_smd_info *smd_info, void *buf,
int total_recd)
@@ -433,10 +348,9 @@ int diag_process_smd_read_data(struct diag_smd_info *smd_info, void *buf,
*/
if ((smd_info->type == SMD_CMD_TYPE) &&
!driver->separate_cmdrsp[smd_info->peripheral]) {
- /* This print is for debugging */
- pr_err("diag, In %s, received data on non-designated command channel: %d\n",
+ pr_debug("diag, In %s, received data on non-designated command channel: %d\n",
__func__, smd_info->peripheral);
- return 0;
+ goto fail_return;
}
/* If the data is already hdlc encoded */
@@ -509,6 +423,14 @@ int diag_process_smd_read_data(struct diag_smd_info *smd_info, void *buf,
}
}
+ if (err) {
+fail_return:
+ if (smd_info->type == SMD_DATA_TYPE &&
+ driver->logging_mode == MEMORY_DEVICE_MODE) {
+ diag_ws_on_copy_fail(DIAG_WS_MD);
+ }
+ }
+
return 0;
}
@@ -647,9 +569,8 @@ void diag_smd_send_req(struct diag_smd_info *smd_info)
buf_size = smd_info->buf_in_1_size;
}
- if (!buf && (smd_info->type == SMD_DCI_TYPE ||
- smd_info->type == SMD_DCI_CMD_TYPE))
- diag_dci_try_deactivate_wakeup_source();
+ if (!buf)
+ goto fail_return;
if (smd_info->ch && buf) {
int required_size = 0;
@@ -741,14 +662,11 @@ void diag_smd_send_req(struct diag_smd_info *smd_info)
}
- if (pkt_len == 0 && (smd_info->type == SMD_DCI_TYPE ||
- smd_info->type == SMD_DCI_CMD_TYPE))
- diag_dci_try_deactivate_wakeup_source();
-
- if (!driver->real_time_mode && smd_info->type == SMD_DATA_TYPE)
- process_lock_on_read(&smd_info->nrt_lock, pkt_len);
-
if (total_recd > 0) {
+ if (smd_info->type == SMD_DATA_TYPE &&
+ driver->logging_mode == MEMORY_DEVICE_MODE)
+ diag_ws_on_read(DIAG_WS_MD, total_recd);
+
if (!buf) {
pr_err("diag: In %s, SMD peripheral: %d, Out of diagmem for Modem\n",
__func__, smd_info->peripheral);
@@ -767,6 +685,8 @@ void diag_smd_send_req(struct diag_smd_info *smd_info)
diag_smd_notify(smd_info,
SMD_EVENT_DATA);
}
+ } else {
+ goto fail_return;
}
} else if (smd_info->ch && !buf &&
(driver->logging_mode == MEMORY_DEVICE_MODE)) {
@@ -776,8 +696,9 @@ void diag_smd_send_req(struct diag_smd_info *smd_info)
fail_return:
if (smd_info->type == SMD_DCI_TYPE ||
- smd_info->type == SMD_DCI_CMD_TYPE)
- diag_dci_try_deactivate_wakeup_source();
+ smd_info->type == SMD_DCI_CMD_TYPE ||
+ driver->logging_mode == MEMORY_DEVICE_MODE)
+ diag_ws_release();
return;
}
@@ -1476,7 +1397,9 @@ int diag_process_apps_pkt(unsigned char *buf, int len)
*(uint16_t *)(driver->apps_rsp_buf + 94) = MSG_SSID_21_LAST;
*(uint16_t *)(driver->apps_rsp_buf + 96) = MSG_SSID_22;
*(uint16_t *)(driver->apps_rsp_buf + 98) = MSG_SSID_22_LAST;
- encode_rsp_and_send(99);
+ *(uint16_t *)(driver->apps_rsp_buf + 100) = MSG_SSID_23;
+ *(uint16_t *)(driver->apps_rsp_buf + 102) = MSG_SSID_23_LAST;
+ encode_rsp_and_send(103);
return 0;
}
/* Check for Apps Only Respond to Get Subsys Build mask */
@@ -2183,17 +2106,19 @@ void diag_smd_notify(void *ctxt, unsigned event)
diag_dci_notify_client(smd_info->peripheral_mask,
DIAG_STATUS_OPEN, DCI_LOCAL_PROC);
}
- } else if (event == SMD_EVENT_DATA && !driver->real_time_mode &&
- smd_info->type == SMD_DATA_TYPE) {
- process_lock_on_notify(&smd_info->nrt_lock);
+ } else if (event == SMD_EVENT_DATA) {
+ if ((smd_info->type == SMD_DCI_TYPE) ||
+ (smd_info->type == SMD_DCI_CMD_TYPE) ||
+ (smd_info->type == SMD_DATA_TYPE &&
+ driver->logging_mode == MEMORY_DEVICE_MODE)) {
+ diag_ws_on_notify();
+ }
}
wake_up(&driver->smd_wait_q);
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();
queue_work(driver->diag_dci_wq,
&(smd_info->diag_read_smd_work));
} else if (smd_info->type == SMD_DATA_TYPE) {
@@ -2329,10 +2254,8 @@ int device_supports_separate_cmdrsp(void)
void diag_smd_destructor(struct diag_smd_info *smd_info)
{
- if (smd_info->type == SMD_DATA_TYPE) {
- wake_lock_destroy(&smd_info->nrt_lock.read_lock);
+ if (smd_info->type == SMD_DATA_TYPE)
destroy_workqueue(smd_info->wq);
- }
if (smd_info->ch)
smd_close(smd_info->ch);
@@ -2555,30 +2478,6 @@ int diag_smd_constructor(struct diag_smd_info *smd_info, int peripheral,
goto err;
}
- smd_info->nrt_lock.enabled = 0;
- smd_info->nrt_lock.ref_count = 0;
- smd_info->nrt_lock.copy_count = 0;
- if (type == SMD_DATA_TYPE) {
- spin_lock_init(&smd_info->nrt_lock.read_spinlock);
-
- switch (peripheral) {
- case MODEM_DATA:
- wake_lock_init(&smd_info->nrt_lock.read_lock,
- WAKE_LOCK_SUSPEND, "diag_nrt_modem_read");
- break;
- case LPASS_DATA:
- wake_lock_init(&smd_info->nrt_lock.read_lock,
- WAKE_LOCK_SUSPEND, "diag_nrt_lpass_read");
- break;
- case WCNSS_DATA:
- wake_lock_init(&smd_info->nrt_lock.read_lock,
- WAKE_LOCK_SUSPEND, "diag_nrt_wcnss_read");
- break;
- default:
- break;
- }
- }
-
return 0;
err:
if (smd_info->wq)
diff --git a/drivers/char/diag/diagfwd.h b/drivers/char/diag/diagfwd.h
index 2e701fc94ec1..94856697f64b 100644
--- a/drivers/char/diag/diagfwd.h
+++ b/drivers/char/diag/diagfwd.h
@@ -26,11 +26,6 @@ int diagfwd_init(void);
void diagfwd_exit(void);
void diag_process_hdlc(void *data, unsigned len);
void diag_smd_send_req(struct diag_smd_info *smd_info);
-void process_lock_enabling(struct diag_nrt_wake_lock *lock, int real_time);
-void process_lock_on_notify(struct diag_nrt_wake_lock *lock);
-void process_lock_on_read(struct diag_nrt_wake_lock *lock, int pkt_len);
-void process_lock_on_copy(struct diag_nrt_wake_lock *lock);
-void process_lock_on_copy_complete(struct diag_nrt_wake_lock *lock);
void diag_usb_legacy_notifier(void *, unsigned, struct diag_request *);
long diagchar_ioctl(struct file *, unsigned int, unsigned long);
int diag_device_write(void *, int, struct diag_request *);
diff --git a/drivers/char/diag/diagfwd_cntl.c b/drivers/char/diag/diagfwd_cntl.c
index 6c8096bb5aa7..2e946798294d 100644
--- a/drivers/char/diag/diagfwd_cntl.c
+++ b/drivers/char/diag/diagfwd_cntl.c
@@ -549,7 +549,6 @@ void diag_send_diag_mode_update_by_smd(struct diag_smd_info *smd_info,
pr_err("diag: ch invalid, feature update on proc %d\n",
smd_info->peripheral);
}
- process_lock_enabling(&data->nrt_lock, real_time);
mutex_unlock(&driver->diag_cntl_mutex);
}
diff --git a/drivers/char/diag/diagfwd_hsic.c b/drivers/char/diag/diagfwd_hsic.c
index 20e6a8ebd3b7..0c17b711f4d9 100644
--- a/drivers/char/diag/diagfwd_hsic.c
+++ b/drivers/char/diag/diagfwd_hsic.c
@@ -287,10 +287,14 @@ static void diag_hsic_read_complete_callback(void *ctxt, char *buf,
* appropriate device, e.g. USB MDM channel
*/
diag_bridge[index].write_len = actual_size;
+ if (driver->logging_mode == MEMORY_DEVICE_MODE)
+ diag_ws_on_notify();
err = diag_device_write((void *)buf, index+HSIC_DATA,
NULL);
/* If an error, return buffer to the pool */
if (err) {
+ if (driver->logging_mode == MEMORY_DEVICE_MODE)
+ diag_ws_release();
diagmem_free(driver, buf, index +
POOL_TYPE_HSIC);
if (__ratelimit(&rl))
@@ -344,7 +348,7 @@ static void diag_hsic_dci_read_complete_callback(void *ctxt, char *buf,
if (!buf) {
pr_err("diag: Out of diagmem for HSIC\n");
} else {
- diag_dci_try_activate_wakeup_source();
+ diag_ws_on_notify();
diag_hsic_dci[index].data_len = actual_size;
diag_hsic_dci[index].data_buf = buf;
memcpy(diag_hsic_dci[index].data, buf, actual_size);
diff --git a/drivers/char/diag/diagmem.c b/drivers/char/diag/diagmem.c
index 27c284833787..3020b768fa7c 100644
--- a/drivers/char/diag/diagmem.c
+++ b/drivers/char/diag/diagmem.c
@@ -16,6 +16,10 @@
#include <linux/mempool.h>
#include <linux/mutex.h>
#include <asm/atomic.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/kmemleak.h>
+
#include "diagchar.h"
#include "diagfwd_bridge.h"
#include "diagfwd_hsic.h"
@@ -37,6 +41,7 @@ void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
atomic_add(1, (atomic_t *)&driver->count);
buf = mempool_alloc(driver->diagpool,
GFP_ATOMIC);
+ kmemleak_not_leak(buf);
}
}
} else if (pool_type == POOL_TYPE_HDLC) {
@@ -47,6 +52,7 @@ void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
(atomic_t *)&driver->count_hdlc_pool);
buf = mempool_alloc(driver->diag_hdlc_pool,
GFP_ATOMIC);
+ kmemleak_not_leak(buf);
}
}
} else if (pool_type == POOL_TYPE_USER) {
@@ -57,6 +63,7 @@ void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
(atomic_t *)&driver->count_user_pool);
buf = mempool_alloc(driver->diag_user_pool,
GFP_ATOMIC);
+ kmemleak_not_leak(buf);
}
}
} else if (pool_type == POOL_TYPE_WRITE_STRUCT) {
@@ -68,6 +75,7 @@ void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
(atomic_t *)&driver->count_write_struct_pool);
buf = mempool_alloc(
driver->diag_write_struct_pool, GFP_ATOMIC);
+ kmemleak_not_leak(buf);
}
}
} else if (pool_type == POOL_TYPE_DCI) {
@@ -78,6 +86,7 @@ void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
(atomic_t *)&driver->count_dci_pool);
buf = mempool_alloc(driver->diag_dci_pool,
GFP_ATOMIC);
+ kmemleak_not_leak(buf);
}
}
#ifdef CONFIG_DIAGFWD_BRIDGE_CODE
@@ -93,6 +102,7 @@ void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
buf = mempool_alloc(
diag_hsic[index].diag_hsic_pool,
GFP_ATOMIC);
+ kmemleak_not_leak(buf);
}
}
} else if (pool_type == POOL_TYPE_HSIC_WRITE ||
@@ -108,6 +118,7 @@ void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
buf = mempool_alloc(
diag_hsic[index].diag_hsic_write_pool,
GFP_ATOMIC);
+ kmemleak_not_leak(buf);
}
}
} else if (pool_type == POOL_TYPE_HSIC_DCI ||
@@ -122,6 +133,7 @@ void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
buf = mempool_alloc(
diag_hsic_dci[index].diag_hsic_pool,
GFP_ATOMIC);
+ kmemleak_not_leak(buf);
}
}
} else if (pool_type == POOL_TYPE_HSIC_DCI_WRITE ||
@@ -137,6 +149,7 @@ void *diagmem_alloc(struct diagchar_dev *driver, int size, int pool_type)
buf = mempool_alloc(
diag_hsic_dci[index].diag_hsic_write_pool,
GFP_ATOMIC);
+ kmemleak_not_leak(buf);
}
}
#endif