diff options
Diffstat (limited to 'drivers/leds')
| -rw-r--r-- | drivers/leds/leds-qpnp-flash.c | 147 |
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; } |
