summaryrefslogtreecommitdiff
path: root/drivers/char
diff options
context:
space:
mode:
authorRavi Aravamudhan <aravamud@codeaurora.org>2014-02-20 19:08:22 -0800
committerRavi Aravamudhan <aravamud@codeaurora.org>2014-02-26 16:50:42 -0800
commit9d92106b2bb66231b6d82327c36d35fb656a5798 (patch)
treed85d6708dea58c06e52e4873c359fab1538c0b46 /drivers/char
parent78025e56f077f7a11e7de978dc514228e6262a3c (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.c6
-rw-r--r--drivers/char/diag/diagchar.h5
-rw-r--r--drivers/char/diag/diagfwd.c124
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);