aboutsummaryrefslogtreecommitdiff
path: root/drivers/leds
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/leds')
-rw-r--r--drivers/leds/leds-qpnp-flash.c147
1 files changed, 104 insertions, 43 deletions
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
index 392238f20be..bda73cb85d6 100644
--- a/drivers/leds/leds-qpnp-flash.c
+++ b/drivers/leds/leds-qpnp-flash.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -213,10 +213,13 @@ struct flash_led_platform_data {
};
struct qpnp_flash_led_buffer {
- size_t rpos;
- size_t wpos;
- size_t len;
- char data[0];
+ struct mutex debugfs_lock; /* Prevent thread concurrency */
+ size_t rpos;
+ size_t wpos;
+ size_t len;
+ struct qpnp_flash_led *led;
+ u32 buffer_cnt;
+ char data[0];
};
/*
@@ -233,10 +236,8 @@ struct qpnp_flash_led {
struct workqueue_struct *ordered_workq;
struct qpnp_vadc_chip *vadc_dev;
struct mutex flash_led_lock;
- struct qpnp_flash_led_buffer *log;
struct dentry *dbgfs_root;
int num_leds;
- u32 buffer_cnt;
u16 base;
u16 current_addr;
u16 current2_addr;
@@ -266,10 +267,11 @@ static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led,
log->rpos = 0;
log->wpos = 0;
log->len = logbufsize - sizeof(*log);
- led->log = log;
+ mutex_init(&log->debugfs_lock);
+ log->led = led;
- led->buffer_cnt = 1;
- file->private_data = led;
+ log->buffer_cnt = 1;
+ file->private_data = log;
return 0;
}
@@ -282,24 +284,30 @@ static int flash_led_dfs_open(struct inode *inode, struct file *file)
static int flash_led_dfs_close(struct inode *inode, struct file *file)
{
- struct qpnp_flash_led *led = file->private_data;
+ struct qpnp_flash_led_buffer *log = file->private_data;
- if (led && led->log) {
+ if (log) {
file->private_data = NULL;
- kfree(led->log);
+ mutex_destroy(&log->debugfs_lock);
+ kfree(log);
}
return 0;
}
+#define MIN_BUFFER_WRITE_LEN 20
static int print_to_log(struct qpnp_flash_led_buffer *log,
const char *fmt, ...)
{
va_list args;
int cnt;
- char *log_buf = &log->data[log->wpos];
+ char *log_buf;
size_t size = log->len - log->wpos;
+ if (size < MIN_BUFFER_WRITE_LEN)
+ return 0; /* not enough buffer left */
+
+ log_buf = &log->data[log->wpos];
va_start(args, fmt);
cnt = vscnprintf(log_buf, size, fmt, args);
va_end(args);
@@ -310,15 +318,23 @@ static int print_to_log(struct qpnp_flash_led_buffer *log,
static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
size_t count, loff_t *ppos) {
- struct qpnp_flash_led *led = fp->private_data;
- struct qpnp_flash_led_buffer *log = led->log;
+ struct qpnp_flash_led_buffer *log = fp->private_data;
+ struct qpnp_flash_led *led;
u8 val;
- int rc;
+ int rc = 0;
size_t len;
size_t ret;
- if (log->rpos >= log->wpos && led->buffer_cnt == 0)
- return 0;
+ if (!log) {
+ pr_err("error: file private data is NULL\n");
+ return -EFAULT;
+ }
+ led = log->led;
+
+ mutex_lock(&log->debugfs_lock);
+ if ((log->rpos >= log->wpos && log->buffer_cnt == 0) ||
+ ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
+ goto unlock_mutex;
rc = spmi_ext_register_readl(led->spmi_dev->ctrl,
led->spmi_dev->sid, INT_LATCHED_STS(led->base), &val, 1);
@@ -326,17 +342,17 @@ static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
dev_err(&led->spmi_dev->dev,
"Unable to read from address %x, rc(%d)\n",
INT_LATCHED_STS(led->base), rc);
- return -EINVAL;
+ goto unlock_mutex;
}
- led->buffer_cnt--;
+ log->buffer_cnt--;
rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base));
if (rc == 0)
- return rc;
+ goto unlock_mutex;
rc = print_to_log(log, "0x%02X ", val);
if (rc == 0)
- return rc;
+ goto unlock_mutex;
if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
log->data[log->wpos - 1] = '\n';
@@ -346,36 +362,49 @@ static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
ret = copy_to_user(buf, &log->data[log->rpos], len);
if (ret) {
pr_err("error copy register value to user\n");
- return -EFAULT;
+ rc = -EFAULT;
+ goto unlock_mutex;
}
len -= ret;
*ppos += len;
log->rpos += len;
- return len;
+ rc = len;
+
+unlock_mutex:
+ mutex_unlock(&log->debugfs_lock);
+ return rc;
}
static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
size_t count, loff_t *ppos) {
- struct qpnp_flash_led *led = fp->private_data;
- struct qpnp_flash_led_buffer *log = led->log;
- int rc;
+ struct qpnp_flash_led_buffer *log = fp->private_data;
+ struct qpnp_flash_led *led;
+ int rc = 0;
size_t len;
size_t ret;
- if (log->rpos >= log->wpos && led->buffer_cnt == 0)
- return 0;
+ if (!log) {
+ pr_err("error: file private data is NULL\n");
+ return -EFAULT;
+ }
+ led = log->led;
+
+ mutex_lock(&log->debugfs_lock);
+ if ((log->rpos >= log->wpos && log->buffer_cnt == 0) ||
+ ((log->len - log->wpos) < MIN_BUFFER_WRITE_LEN))
+ goto unlock_mutex;
- led->buffer_cnt--;
+ log->buffer_cnt--;
rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base));
if (rc == 0)
- return rc;
+ goto unlock_mutex;
rc = print_to_log(log, "0x%02X ", led->fault_reg);
if (rc == 0)
- return rc;
+ goto unlock_mutex;
if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
log->data[log->wpos - 1] = '\n';
@@ -385,14 +414,19 @@ static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
ret = copy_to_user(buf, &log->data[log->rpos], len);
if (ret) {
pr_err("error copy register value to user\n");
- return -EFAULT;
+ rc = -EFAULT;
+ goto unlock_mutex;
}
len -= ret;
*ppos += len;
log->rpos += len;
- return len;
+ rc = len;
+
+unlock_mutex:
+ mutex_unlock(&log->debugfs_lock);
+ return rc;
}
static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
@@ -404,10 +438,22 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
int data;
size_t ret = 0;
- struct qpnp_flash_led *led = file->private_data;
- char *kbuf = kmalloc(count + 1, GFP_KERNEL);
- if (!kbuf)
- return -ENOMEM;
+ struct qpnp_flash_led_buffer *log = file->private_data;
+ struct qpnp_flash_led *led;
+ char *kbuf;
+
+ if (!log) {
+ pr_err("error: file private data is NULL\n");
+ return -EFAULT;
+ }
+ led = log->led;
+
+ mutex_lock(&log->debugfs_lock);
+ kbuf = kmalloc(count + 1, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto unlock_mutex;
+ }
ret = copy_from_user(kbuf, buf, count);
if (!ret) {
@@ -436,6 +482,8 @@ static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
free_buf:
kfree(kbuf);
+unlock_mutex:
+ mutex_unlock(&log->debugfs_lock);
return ret;
}
@@ -447,11 +495,22 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file,
int cnt = 0;
int data;
size_t ret = 0;
+ struct qpnp_flash_led_buffer *log = file->private_data;
+ struct qpnp_flash_led *led;
+ char *kbuf;
- struct qpnp_flash_led *led = file->private_data;
- char *kbuf = kmalloc(count + 1, GFP_KERNEL);
- if (!kbuf)
- return -ENOMEM;
+ if (!log) {
+ pr_err("error: file private data is NULL\n");
+ return -EFAULT;
+ }
+ led = log->led;
+
+ mutex_lock(&log->debugfs_lock);
+ kbuf = kmalloc(count + 1, GFP_KERNEL);
+ if (!kbuf) {
+ ret = -ENOMEM;
+ goto unlock_mutex;
+ }
ret = copy_from_user(kbuf, buf, count);
if (ret == count) {
@@ -479,6 +538,8 @@ static ssize_t flash_led_dfs_dbg_enable(struct file *file,
free_buf:
kfree(kbuf);
+unlock_mutex:
+ mutex_unlock(&log->debugfs_lock);
return ret;
}