diff options
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/diag/diag_dci.c | 70 | ||||
-rw-r--r-- | drivers/char/diag/diag_dci.h | 10 | ||||
-rw-r--r-- | drivers/char/diag/diag_debugfs.c | 47 | ||||
-rw-r--r-- | drivers/char/diag/diagchar.h | 32 | ||||
-rw-r--r-- | drivers/char/diag/diagchar_core.c | 242 | ||||
-rw-r--r-- | drivers/char/diag/diagfwd.c | 169 | ||||
-rw-r--r-- | drivers/char/diag/diagfwd.h | 5 | ||||
-rw-r--r-- | drivers/char/diag/diagfwd_cntl.c | 1 | ||||
-rw-r--r-- | drivers/char/diag/diagfwd_hsic.c | 6 | ||||
-rw-r--r-- | drivers/char/diag/diagmem.c | 13 |
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 |