diff options
author | Ravi Aravamudhan <aravamud@codeaurora.org> | 2014-02-20 19:08:22 -0800 |
---|---|---|
committer | Ravi Aravamudhan <aravamud@codeaurora.org> | 2014-02-26 16:50:42 -0800 |
commit | 9d92106b2bb66231b6d82327c36d35fb656a5798 (patch) | |
tree | d85d6708dea58c06e52e4873c359fab1538c0b46 /drivers/char | |
parent | 78025e56f077f7a11e7de978dc514228e6262a3c (diff) |
diag: Create separate buffer for command responses
Diag driver was using an exisiting SMD Modem buffer for encoding
command responses. Make changes to add a new buffer to avoid
stomping on modem data.
Change-Id: I88ad0a2785dd8eef74f7a866bf9b3525d371e1f7
Signed-off-by: Ravi Aravamudhan <aravamud@codeaurora.org>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/diag/diag_debugfs.c | 6 | ||||
-rw-r--r-- | drivers/char/diag/diagchar.h | 5 | ||||
-rw-r--r-- | drivers/char/diag/diagfwd.c | 124 |
3 files changed, 94 insertions, 41 deletions
diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c index 94b30240bae2..7abc2cade6e6 100644 --- a/drivers/char/diag/diag_debugfs.c +++ b/drivers/char/diag/diag_debugfs.c @@ -109,7 +109,8 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf, "Received Feature mask from Modem: %d\n" "Received Feature mask from LPASS: %d\n" "Received Feature mask from WCNSS: %d\n" - "logging_mode: %d\n", + "logging_mode: %d\n" + "rsp_in_busy: %d\n", driver->smd_data[MODEM_DATA].ch, driver->smd_data[LPASS_DATA].ch, driver->smd_data[WCNSS_DATA].ch, @@ -178,7 +179,8 @@ static ssize_t diag_dbgfs_read_status(struct file *file, char __user *ubuf, driver->rcvd_feature_mask[MODEM_DATA], driver->rcvd_feature_mask[LPASS_DATA], driver->rcvd_feature_mask[WCNSS_DATA], - driver->logging_mode); + driver->logging_mode, + driver->rsp_buf_busy); #ifdef CONFIG_DIAG_OVER_USB ret += scnprintf(buf+ret, buf_size-ret, diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h index 5d3a5882350c..53921ed8b42c 100644 --- a/drivers/char/diag/diagchar.h +++ b/drivers/char/diag/diagchar.h @@ -404,6 +404,11 @@ struct diagchar_dev { struct diag_ctrl_msg_mask *msg_mask; struct diag_ctrl_feature_mask *feature_mask; struct mutex log_mask_mutex; + /* Members for Sending response */ + unsigned char *encoded_rsp_buf; + uint8_t rsp_buf_busy; + struct diag_request *rsp_write_ptr; + spinlock_t rsp_buf_busy_lock; /* State for diag forwarding */ struct diag_smd_info smd_data[NUM_SMD_DATA_CHANNELS]; struct diag_smd_info smd_cntl[NUM_SMD_CONTROL_CHANNELS]; diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c index 7c444fada73d..915568442e69 100644 --- a/drivers/char/diag/diagfwd.c +++ b/drivers/char/diag/diagfwd.c @@ -62,45 +62,6 @@ struct diag_master_table entry; int wrap_enabled; uint16_t wrap_count; -void encode_rsp_and_send(int buf_length) -{ - struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 }; - struct diag_hdlc_dest_type enc = { NULL, NULL, 0 }; - struct diag_smd_info *data = &(driver->smd_data[MODEM_DATA]); - int err; - unsigned long flags; - - if (buf_length > APPS_BUF_SIZE) { - pr_err("diag: In %s, invalid len %d, permissible len %d\n", - __func__, buf_length, APPS_BUF_SIZE); - return; - } - - send.state = DIAG_STATE_START; - send.pkt = driver->apps_rsp_buf; - send.last = (void *)(driver->apps_rsp_buf + buf_length); - send.terminate = 1; - if (!data->in_busy_1) { - spin_lock_irqsave(&data->in_busy_lock, flags); - enc.dest = data->buf_in_1; - enc.dest_last = (void *)(data->buf_in_1 + APPS_BUF_SIZE - 1); - diag_hdlc_encode(&send, &enc); - data->write_ptr_1->buf = data->buf_in_1; - data->write_ptr_1->length = (int)(enc.dest - - (void *)(data->buf_in_1)); - data->in_busy_1 = 1; - err = diag_device_write(data->buf_in_1, data->peripheral, - data->write_ptr_1); - if (err) { - pr_err("diag: In %s, Unable to write to device, err: %d\n", - __func__, err); - data->in_busy_1 = 0; - } - memset(driver->apps_rsp_buf, '\0', APPS_BUF_SIZE); - spin_unlock_irqrestore(&data->in_busy_lock, flags); - } -} - /* Determine if this device uses a device tree */ #ifdef CONFIG_OF static int has_device_tree(void) @@ -868,6 +829,68 @@ static int diag_write_to_usb(struct usb_diag_ch *ch, } #endif +void encode_rsp_and_send(int buf_length) +{ + struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 }; + struct diag_hdlc_dest_type enc = { NULL, NULL, 0 }; + unsigned char *rsp_ptr = driver->encoded_rsp_buf; + int err, retry_count = 0; + unsigned long flags; + + if (!rsp_ptr) + return; + + if (buf_length > APPS_BUF_SIZE || buf_length <= 0) { + pr_err("diag: In %s, invalid len %d, permissible len %d\n", + __func__, buf_length, APPS_BUF_SIZE); + return; + } + + /* + * Keep trying till we get the buffer back. It should probably + * take one or two iterations. When this loops till UINT_MAX, it + * means we did not get a write complete for the previous + * response. + */ + while (retry_count < UINT_MAX) { + if (!driver->rsp_buf_busy) + break; + /* + * Wait for sometime and try again. The value 10000 was chosen + * empirically as an optimum value for USB to complete a write + */ + usleep_range(10000, 10100); + retry_count++; + } + if (driver->rsp_buf_busy) { + pr_err("diag: unable to get hold of response buffer\n"); + return; + } + + spin_lock_irqsave(&driver->rsp_buf_busy_lock, flags); + driver->rsp_buf_busy = 1; + spin_unlock_irqrestore(&driver->rsp_buf_busy_lock, flags); + send.state = DIAG_STATE_START; + send.pkt = driver->apps_rsp_buf; + send.last = (void *)(driver->apps_rsp_buf + buf_length); + send.terminate = 1; + enc.dest = rsp_ptr; + enc.dest_last = (void *)(rsp_ptr + HDLC_OUT_BUF_SIZE - 1); + diag_hdlc_encode(&send, &enc); + driver->rsp_write_ptr->buf = rsp_ptr; + driver->rsp_write_ptr->length = (int)(enc.dest - (void *)rsp_ptr); + err = diag_write_to_usb(driver->legacy_ch, driver->rsp_write_ptr); + if (err) { + pr_err("diag: In %s, Unable to write to device, err: %d\n", + __func__, err); + spin_lock_irqsave(&driver->rsp_buf_busy_lock, flags); + driver->rsp_buf_busy = 0; + spin_unlock_irqrestore(&driver->rsp_buf_busy_lock, flags); + } + memset(driver->apps_rsp_buf, '\0', APPS_BUF_SIZE); + +} + int diag_device_write(void *buf, int data_type, struct diag_request *write_ptr) { int i, err = 0, index; @@ -2035,6 +2058,7 @@ int diagfwd_write_complete(struct diag_request *diag_write_ptr) { unsigned char *buf = diag_write_ptr->buf; int found_it = 0; + unsigned long flags; /* Determine if the write complete is for data from modem/apps/q6 */ found_it = diagfwd_check_buf_match(NUM_SMD_DATA_CHANNELS, @@ -2044,6 +2068,13 @@ int diagfwd_write_complete(struct diag_request *diag_write_ptr) found_it = diagfwd_check_buf_match(NUM_SMD_CMD_CHANNELS, driver->smd_cmd, buf); + if (!found_it && buf == (void *)driver->rsp_write_ptr->buf) { + found_it = 1; + spin_lock_irqsave(&driver->rsp_buf_busy_lock, flags); + driver->rsp_buf_busy = 0; + spin_unlock_irqrestore(&driver->rsp_buf_busy_lock, flags); + } + if (!found_it) { if (driver->logging_mode != USB_MODE) pr_debug("diag: freeing buffer when not in usb mode\n"); @@ -2573,6 +2604,17 @@ int diagfwd_init(void) driver->supports_apps_hdlc_encoding = 1; mutex_init(&driver->diag_hdlc_mutex); mutex_init(&driver->diag_cntl_mutex); + driver->encoded_rsp_buf = kzalloc(HDLC_OUT_BUF_SIZE, GFP_KERNEL); + if (!driver->encoded_rsp_buf) + goto err; + kmemleak_not_leak(driver->encoded_rsp_buf); + driver->rsp_buf_busy = 0; + driver->rsp_write_ptr = kzalloc(sizeof(struct diag_request), + GFP_KERNEL); + if (!driver->rsp_write_ptr) + goto err; + kmemleak_not_leak(driver->rsp_write_ptr); + spin_lock_init(&driver->rsp_buf_busy_lock); for (i = 0; i < NUM_SMD_CONTROL_CHANNELS; i++) { driver->separate_cmdrsp[i] = 0; @@ -2703,6 +2745,8 @@ err: for (i = 0; i < NUM_SMD_CMD_CHANNELS; i++) diag_smd_destructor(&driver->smd_cmd[i]); + kfree(driver->encoded_rsp_buf); + kfree(driver->rsp_write_ptr); kfree(driver->buf_msg_mask_update); kfree(driver->buf_log_mask_update); kfree(driver->buf_event_mask_update); @@ -2745,6 +2789,8 @@ void diagfwd_exit(void) } } + kfree(driver->encoded_rsp_buf); + kfree(driver->rsp_write_ptr); kfree(driver->buf_msg_mask_update); kfree(driver->buf_log_mask_update); kfree(driver->buf_event_mask_update); |